summaryrefslogtreecommitdiff
path: root/qpid
diff options
context:
space:
mode:
Diffstat (limited to 'qpid')
-rw-r--r--qpid/cpp/bindings/qpid/ruby/.gitignore2
-rw-r--r--qpid/cpp/bindings/qpid/ruby/CMakeLists.txt25
-rw-r--r--qpid/cpp/bindings/qpid/ruby/LICENSE234
-rw-r--r--qpid/cpp/bindings/qpid/ruby/README.rdoc18
-rw-r--r--qpid/cpp/bindings/qpid/ruby/Rakefile100
-rw-r--r--qpid/cpp/bindings/qpid/ruby/TODO7
-rw-r--r--qpid/cpp/bindings/qpid/ruby/ext/cqpid/extconf.rb73
-rw-r--r--qpid/cpp/bindings/qpid/ruby/lib/qpid/version.rb2
-rw-r--r--qpid/cpp/docs/man/qpidd.121
-rw-r--r--qpid/cpp/include/qpid/amqp_0_10/Codecs.h20
-rw-r--r--qpid/cpp/include/qpid/console/ClassKey.h30
-rw-r--r--qpid/cpp/include/qpid/console/ObjectId.h8
-rw-r--r--qpid/cpp/src/CMakeLists.txt1
-rw-r--r--qpid/cpp/src/qmf/AgentSessionImpl.h1
-rw-r--r--qpid/cpp/src/qmf/ConsoleSessionImpl.h1
-rw-r--r--qpid/cpp/src/qpid/broker/Message.cpp6
-rw-r--r--qpid/cpp/src/qpid/broker/SessionState.cpp4
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp2
-rw-r--r--qpid/cpp/src/tests/MessagingSessionTests.cpp18
-rwxr-xr-xqpid/java/bdbstore/bin/backup.sh40
-rwxr-xr-xqpid/java/bdbstore/bin/storeUpgrade.sh43
-rw-r--r--qpid/java/bdbstore/build.xml84
-rwxr-xr-xqpid/java/bdbstore/etc/scripts/bdbbackuptest.sh44
-rwxr-xr-xqpid/java/bdbstore/etc/scripts/bdbtest.sh43
-rw-r--r--qpid/java/bdbstore/src/main/java/BDBStoreUpgrade.log4j.xml52
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/AMQShortStringEncoding.java59
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/AMQShortStringTB.java48
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBBackup.java344
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStore.java2124
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBStoreUpgrade.java1125
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BindingKey.java62
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/ContentTB.java52
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/DatabaseVisitor.java44
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/ExchangeTB.java58
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/FieldTableEncoding.java74
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/MessageContentKey.java42
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/QueueEntryKey.java49
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/keys/MessageContentKey_4.java44
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/keys/MessageContentKey_5.java44
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/records/ExchangeRecord.java53
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/records/QueueRecord.java66
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/testclient/BackupTestClient.java120
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/BindingTuple.java25
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/BindingTupleBindingFactory.java45
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/BindingTuple_4.java76
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/MessageContentKeyTB_4.java47
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/MessageContentKeyTB_5.java46
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/MessageContentKeyTupleBindingFactory.java45
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/MessageMetaDataTB_4.java162
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/MessageMetaDataTB_5.java77
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/MessageMetaDataTupleBindingFactory.java43
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/QueueEntryTB.java46
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/QueueTuple.java25
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/QueueTupleBindingFactory.java46
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/QueueTuple_4.java72
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/QueueTuple_5.java75
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/TupleBindingFactory.java35
-rw-r--r--qpid/java/bdbstore/src/resources/backup-log4j.xml65
-rw-r--r--qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/AMQShortStringEncodingTest.java88
-rw-r--r--qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreTest.java470
-rw-r--r--qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBStoreUpgradeTestPreparer.java232
-rw-r--r--qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBUpgradeTest.java540
-rw-r--r--qpid/java/bdbstore/src/test/resources/upgrade/bdbstore-to-upgrade/test-store/00000000.jdbbin0 -> 1330321 bytes
-rw-r--r--qpid/java/broker-plugins/experimental/info/build.xml9
-rwxr-xr-xqpid/java/broker/bin/qpid-server8
-rw-r--r--qpid/java/broker/build.xml4
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java2
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java2
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java27
-rw-r--r--qpid/java/build.deps5
-rw-r--r--[-rwxr-xr-x]qpid/java/build.overrides (renamed from qpid/java/test-profiles/clean-dir)13
-rw-r--r--qpid/java/build.xml5
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_8_0.java26
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java2
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java209
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java84
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_8.java34
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java145
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_10.java29
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java41
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_10.java7
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_8.java9
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java2
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate.java6
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate_0_10.java76
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate_0_8.java60
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractAMQMessageDelegate.java67
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java31
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java33
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java16
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpid/client/state/StateWaiter.java18
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpid/client/util/BlockingWaiter.java17
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpid/jms/MessageProducer.java3
-rw-r--r--qpid/java/client/src/test/java/org/apache/qpid/client/AMQSession_0_10Test.java4
-rw-r--r--qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseMethodHandlerNoCloseOk.java2
-rw-r--r--qpid/java/client/src/test/java/org/apache/qpid/test/unit/message/TestAMQSession.java30
-rw-r--r--qpid/java/common.xml1
-rwxr-xr-xqpid/java/common/bin/qpid-run13
-rw-r--r--qpid/java/common/src/main/java/org/apache/qpid/AMQInvalidArgumentException.java2
-rw-r--r--qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQConstant.java7
-rw-r--r--qpid/java/common/src/main/java/org/apache/qpid/transport/Connection.java18
-rw-r--r--qpid/java/common/src/main/java/org/apache/qpid/transport/Session.java52
-rw-r--r--qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoNetworkConnection.java2
-rw-r--r--qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoSender.java4
-rw-r--r--qpid/java/common/src/test/java/org/apache/qpid/test/utils/QpidTestCase.java6
-rw-r--r--qpid/java/lib/bdbstore/README.txt14
-rw-r--r--qpid/java/module.xml10
-rw-r--r--qpid/java/systests/build.xml10
-rw-r--r--qpid/java/systests/etc/config-systests-aclv2.xml2
-rw-r--r--qpid/java/systests/etc/config-systests-bdb-settings.xml26
-rw-r--r--qpid/java/systests/etc/config-systests-bdb.xml30
-rw-r--r--qpid/java/systests/etc/config-systests-derby.xml2
-rw-r--r--qpid/java/systests/etc/config-systests-firewall.xml2
-rw-r--r--qpid/java/systests/etc/config-systests.xml2
-rw-r--r--qpid/java/systests/etc/virtualhosts-systests-aclv2.xml2
-rw-r--r--qpid/java/systests/etc/virtualhosts-systests-bdb-settings.xml56
-rw-r--r--qpid/java/systests/etc/virtualhosts-systests-bdb.xml29
-rw-r--r--qpid/java/systests/etc/virtualhosts-systests-derby.xml2
-rw-r--r--qpid/java/systests/etc/virtualhosts-systests-firewall.xml2
-rw-r--r--qpid/java/systests/etc/virtualhosts-systests.xml2
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/client/failover/FailoverBehaviourTest.java924
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/client/ssl/SSLTest.java4
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagementActorLoggingTest.java22
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AbstractTestLogging.java2
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AlertingTest.java4
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/server/logging/BindingLoggingTest.java2
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/server/logging/BrokerLoggingTest.java2
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/server/logging/DerbyMessageStoreLoggingTest.java2
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/server/logging/DurableQueueLoggingTest.java2
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ExchangeLoggingTest.java6
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/server/logging/QueueLoggingTest.java2
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/server/logging/SubscriptionLoggingTest.java2
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ProducerFlowControlTest.java2
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/AbstractACLTestCase.java10
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLTest.java10
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/server/security/firewall/FirewallConfigTest.java14
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/test/client/destination/AddressBasedDestinationTest.java6
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/test/client/timeouts/SyncWaitTimeoutDelayTest.java2
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/ClientAcknowledgeTest.java82
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/RecoverTest.java9
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/test/unit/basic/close/CloseTest.java5
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/AMQConnectionTest.java10
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/AMQSSLConnectionTest.java4
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/message/ObjectMessageTest.java25
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/test/unit/publish/DirtyTransactedPublishTest.java403
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutConfigurationTest.java82
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutDisabledTest.java6
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutTest.java379
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutTestCase.java43
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java134
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitor.java141
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitorTest.java25
-rwxr-xr-xqpid/java/test-profiles/CPPExcludes1
-rw-r--r--qpid/java/test-profiles/CPPTransientExcludes3
-rwxr-xr-xqpid/java/test-profiles/Java010Excludes3
-rw-r--r--qpid/java/test-profiles/JavaBDBExcludes22
-rw-r--r--qpid/java/test-profiles/JavaDerbyExcludes21
-rw-r--r--qpid/java/test-profiles/JavaTransientExcludes4
-rw-r--r--qpid/java/test-profiles/cpp.testprofile2
-rw-r--r--qpid/java/test-profiles/java-bdb-spawn.0-10.testprofile31
-rw-r--r--qpid/java/test-profiles/java-bdb-spawn.0-9-1.testprofile36
-rw-r--r--qpid/java/test-profiles/java-bdb.0-10.testprofile32
-rw-r--r--qpid/java/test-profiles/java-bdb.0-9-1.testprofile37
-rw-r--r--qpid/java/test-profiles/java-dby-spawn.0-10.testprofile7
-rw-r--r--qpid/java/test-profiles/java-dby-spawn.0-9-1.testprofile7
-rw-r--r--qpid/java/test-profiles/java-dby.0-10.testprofile8
-rw-r--r--qpid/java/test-profiles/java-dby.0-9-1.testprofile8
-rw-r--r--qpid/java/test-profiles/java-mms-spawn.0-10.testprofile3
-rw-r--r--qpid/java/test-profiles/java-mms-spawn.0-9-1.testprofile3
-rw-r--r--qpid/java/test-profiles/java-mms.0-10.testprofile4
-rw-r--r--qpid/java/test-profiles/java-mms.0-9-1.testprofile3
-rw-r--r--qpid/java/test-profiles/testprofile.defaults3
-rwxr-xr-xqpid/tools/src/py/qmf-tool148
173 files changed, 9915 insertions, 1706 deletions
diff --git a/qpid/cpp/bindings/qpid/ruby/.gitignore b/qpid/cpp/bindings/qpid/ruby/.gitignore
new file mode 100644
index 0000000000..ab78513491
--- /dev/null
+++ b/qpid/cpp/bindings/qpid/ruby/.gitignore
@@ -0,0 +1,2 @@
+pkg
+html
diff --git a/qpid/cpp/bindings/qpid/ruby/CMakeLists.txt b/qpid/cpp/bindings/qpid/ruby/CMakeLists.txt
index 96c00154ba..25258cfc6a 100644
--- a/qpid/cpp/bindings/qpid/ruby/CMakeLists.txt
+++ b/qpid/cpp/bindings/qpid/ruby/CMakeLists.txt
@@ -17,6 +17,15 @@
# under the License.
#
+##--------------------------------------------------
+## Properties used for generating the Ruby bindings.
+##--------------------------------------------------
+set(GEM_BINDINGS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/cqpid)
+set(GEM_BINDINGS_SOURCE_FILE ${GEM_BINDINGS_SOURCE_DIR}/cqpid.cpp)
+set(GEM_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR})
+set(GEM_OUTPUT_FILE ${GEM_OUTPUT_PATH}/pkg/qpid-${qpidc_version}.0.gem)
+
+
##------------------------------------------------------
## Use Swig to generate a literal binding to the C++ API
##------------------------------------------------------
@@ -36,3 +45,19 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libcqpid.so
DESTINATION ${RUBY_SITEARCH_DIR}
COMPONENT ${QPID_COMPONENT_CLIENT}
)
+
+add_custom_command(OUTPUT ${GEM_BINDINGS_SOURCE_FILE}
+ COMMAND cp ${swig_generated_file_fullname} ${GEM_BINDINGS_SOURCE_FILE}
+ DEPENDS ${swig_generated_file_fullname}
+ )
+
+add_custom_command(OUTPUT ${GEM_OUTPUT_FILE}
+ COMMAND OUTPUT_DIR=${GEM_OUTPUT_PATH} rake clean clobber package
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ DEPENDS ${swig_generated_file_fullname} ${GEM_BINDINGS_SOURCE_FILE}
+ )
+
+add_custom_target(gemfile
+ DEPENDS ${GEM_OUTPUT_FILE}
+ )
+
diff --git a/qpid/cpp/bindings/qpid/ruby/LICENSE b/qpid/cpp/bindings/qpid/ruby/LICENSE
new file mode 100644
index 0000000000..cff2a5e25d
--- /dev/null
+++ b/qpid/cpp/bindings/qpid/ruby/LICENSE
@@ -0,0 +1,234 @@
+=========================================================================
+== Apache License ==
+=========================================================================
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+=========================================================================
+== Boost License ==
+=========================================================================
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
diff --git a/qpid/cpp/bindings/qpid/ruby/README.rdoc b/qpid/cpp/bindings/qpid/ruby/README.rdoc
index 960fdf6107..0ae7e5cbed 100644
--- a/qpid/cpp/bindings/qpid/ruby/README.rdoc
+++ b/qpid/cpp/bindings/qpid/ruby/README.rdoc
@@ -14,6 +14,20 @@ You can install Qpid with the following command.
$ gem install qpid
+== Building The Native Code
+
+The Qpid gem requires that you have available the Qpid libraries and
+development header files. To install them, please see:
+
+http://cwiki.apache.org/qpid/developer-pages.html
+
+If you are building the gem within the Qpid development environment
+itself, you can specify the location of the Qpid headers and
+libraries with:
+
+$ ruby extconfig.rb --with-qpid-lib=[path to libqpidclient.so, etc.]
+$ make
+
== Examples
Take a look at the integration tests for examples on how to leverage
@@ -24,4 +38,8 @@ the messaging capabilities of Qpid in your Ruby applications.
Licensed to the Apache Software Foundation (ASF) under one or more
contributor licensing agreements.
+Author:: Darryl L. Pierce (mailto:dpierce@redhat.com)
+Copyright:: Copyright (c) 2011, Red Hat, Inc.
+Homepage:: http://qpid.apache.org
+License:: Apache License 2.0 - http://www.apache.org/licenses/LICENSE-2.0.html
diff --git a/qpid/cpp/bindings/qpid/ruby/Rakefile b/qpid/cpp/bindings/qpid/ruby/Rakefile
index ef2b158eba..07cfff9844 100644
--- a/qpid/cpp/bindings/qpid/ruby/Rakefile
+++ b/qpid/cpp/bindings/qpid/ruby/Rakefile
@@ -20,24 +20,42 @@
task :noop
-require 'rubygems'
-require 'rake/clean'
-require 'rake/rdoctask'
-require 'rake/testtask'
+# look for a root directory for out-of-tree builds
-CLOBBER.include('pkg')
+OUTPUT_DIR=ENV["OUTPUT_DIR"] || "."
-load './lib/qpid/version.rb'
+require "rubygems"
+require "rubygems/package_task"
-desc 'Default: run all tests.'
-task :default => :'test:all'
+require "rake/clean"
+require "rake/extensiontask"
+require "rake/rdoctask"
+require "rake/testtask"
+
+CLOBBER.include("pkg")
+
+load "./lib/qpid/version.rb"
+
+#-------------
+# Gem Details.
+#-------------
+
+NAME = "qpid"
+# VERSION = Qpid::VERSION
+AUTHOR = "Darryl L. Pierce"
+EMAIL = "dpierce@redhat.com"
+HOMEPAGE = "http://qpid.apache.org"
+SUMMARY = "Qpid is an enterprise messaging framework."
+
+desc "Default: run all tests."
+task :default => :"test:all"
#---------------
# Testing tasks.
#---------------
-desc 'Run all tests (alias for test:all).'
-task :test => :'test:all'
+desc "Run all tests (alias for test:all)."
+task :test => :"test:all"
namespace :test do
desc "Run all tests (default)."
@@ -45,15 +63,15 @@ namespace :test do
desc "Run unit tests."
Rake::TestTask.new(:units) do |t|
- t.libs << '.'
- t.pattern = 'test/test*.rb'
+ t.libs << "."
+ t.pattern = "test/test*.rb"
t.verbose = true
end
desc "Run integration tests."
Rake::TestTask.new(:integrations) do |t|
- t.libs << '.'
- t.pattern = 'test/integration/*.rb'
+ t.libs << "."
+ t.pattern = "test/integration/*.rb"
t.verbose = true
end
@@ -63,12 +81,50 @@ end
# Documentation tasks.
#---------------------
-Rake::RDocTask.new(
- :rdoc => 'rdoc',
- :clobber_rdoc => 'rdoc:clean',
- :rerdoc => 'rdoc:force'
- ) do |rd|
- rd.main = 'README.rdoc'
- rd.options << '--all'
- rd.rdoc_files.include('README.rdoc', 'lib/**/*.rb')
+Rake::RDocTask.new(:rdoc => "rdoc",
+ :clobber_rdoc => "rdoc:clean",
+ :rerdoc => "rdoc:force") do |rd|
+ rd.main = "README.rdoc"
+ rd.options << "--all"
+ rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
end
+
+#-----------------
+# Package the gem.
+#-----------------
+
+spec = Gem::Specification.new do |s|
+ s.name = NAME
+ s.version = Qpid::VERSION
+ s.platform = Gem::Platform::RUBY
+ s.extra_rdoc_files = ["README.rdoc"]
+ s.summary = SUMMARY
+ s.description = s.summary
+ s.author = AUTHOR
+ s.email = EMAIL
+ s.homepage = HOMEPAGE
+
+ s.extensions = FileList["ext/**/extconf.rb"]
+
+ s.require_path = "lib"
+ # DEPRECATED s.autorequire = NAME
+ s.files = FileList["LICENSE",
+ "README.rdoc",
+ "Rakefile",
+ "TODO",
+ "lib/**/*.rb",
+ "test/**/*.rb",
+ "examples/**/*.rb",
+ "ext/**/*"]
+end
+
+Gem::PackageTask.new(spec) do |pkg|
+ pkg.package_dir = "#{OUTPUT_DIR}/pkg"
+end
+
+#------------------
+# Build native code
+#------------------
+
+Rake::ExtensionTask.new("cqpid", spec)
+
diff --git a/qpid/cpp/bindings/qpid/ruby/TODO b/qpid/cpp/bindings/qpid/ruby/TODO
new file mode 100644
index 0000000000..454aac9200
--- /dev/null
+++ b/qpid/cpp/bindings/qpid/ruby/TODO
@@ -0,0 +1,7 @@
+TODO Items
+-----------------------------------------------------------------------------
+
+Version 0.11.0:
+ * Deliver the Ruby bindings as a gem.
+ * Rework the blocking tasks to not bring the main thread to a halt.
+
diff --git a/qpid/cpp/bindings/qpid/ruby/ext/cqpid/extconf.rb b/qpid/cpp/bindings/qpid/ruby/ext/cqpid/extconf.rb
new file mode 100644
index 0000000000..90292d4bec
--- /dev/null
+++ b/qpid/cpp/bindings/qpid/ruby/ext/cqpid/extconf.rb
@@ -0,0 +1,73 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# To create the Makefile then you need to specify the location
+# of the Qpid shared libraries using the commandline:
+#
+# $ ruby extconf.rb --with-qpid-lib=[path to libraries]
+#
+
+require 'mkmf'
+
+# Setup the build environment.
+$CFLAGS = "-fPIC -fno-inline -x c++"
+
+REQUIRED_LIBRARIES = [
+ 'qpidclient',
+ 'qpidcommon',
+ 'qpidmessaging',
+ 'qpidtypes'
+ ]
+
+REQUIRED_HEADERS = [
+ 'qpid/messaging/Address.h',
+ 'qpid/messaging/Connection.h',
+ 'qpid/messaging/Duration.h',
+ 'qpid/messaging/exceptions.h',
+ 'qpid/messaging/FailoverUpdates.h',
+ 'qpid/messaging/Handle.h',
+ 'qpid/messaging/ImportExport.h',
+ 'qpid/messaging/Message.h',
+ 'qpid/messaging/Receiver.h',
+ 'qpid/messaging/Sender.h',
+ 'qpid/messaging/Session.h'
+ ]
+
+dir_config('qpid')
+
+def abort_build filetype, filename
+ abort "Missing required #{filetype}: #{filename}"
+end
+
+def require_library lib
+ abort_build "library", lib unless have_library lib
+end
+
+def require_header header
+ abort_build "header", header unless have_header header
+end
+
+have_library('stdc++')
+
+REQUIRED_LIBRARIES.each {|library| require_library library}
+
+REQUIRED_HEADERS.each {|header| require_header header}
+
+create_makefile('cqpid')
+
diff --git a/qpid/cpp/bindings/qpid/ruby/lib/qpid/version.rb b/qpid/cpp/bindings/qpid/ruby/lib/qpid/version.rb
index a791b1638c..f387ba98dc 100644
--- a/qpid/cpp/bindings/qpid/ruby/lib/qpid/version.rb
+++ b/qpid/cpp/bindings/qpid/ruby/lib/qpid/version.rb
@@ -22,7 +22,7 @@ module Qpid
module Version
NUMBERS = [MAJOR = 0,
- MINOR = 10,
+ MINOR = 13,
BUILD = 0]
end
diff --git a/qpid/cpp/docs/man/qpidd.1 b/qpid/cpp/docs/man/qpidd.1
index d2cff454cf..1e2d3aabee 100644
--- a/qpid/cpp/docs/man/qpidd.1
+++ b/qpid/cpp/docs/man/qpidd.1
@@ -1,5 +1,5 @@
-.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.38.2.
-.TH QPIDD "1" "March 2011" "qpidd (qpidc) version 0.11" "User Commands"
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.2.
+.TH QPIDD "1" "October 2011" "qpidd (qpidc) version 0.13" "User Commands"
.SH NAME
qpidd \- the Qpid AMQP Message Broker Daemon
@@ -24,11 +24,11 @@ Displays the help message
\fB\-v\fR [ \fB\-\-version\fR ]
Displays version information
.TP
-\fB\-\-config\fR FILE
+\fB\-\-config\fR FILE (/usr/local/etc/qpidd.conf)
Reads configuration from FILE
.SS "Module options:"
.TP
-\fB\-\-module\-dir\fR DIR
+\fB\-\-module\-dir\fR DIR (/usr/local/lib/qpid/daemon)
Load all shareable modules in this
directory
.TP
@@ -41,7 +41,7 @@ Don't load modules from module
directory
.SS "Broker Options:"
.TP
-\fB\-\-data\-dir\fR DIR
+\fB\-\-data\-dir\fR DIR (/home/kgiusti/.qpidd)
Directory to contain persistent data
generated by the broker
.TP
@@ -127,6 +127,11 @@ which flow control is de\-activated.
\fB\-\-default\-event\-threshold\-ratio\fR %age of limit (80)
The ratio of any specified queue limit
at which an event will be raised
+.TP
+\fB\-\-default\-message\-group\fR GROUP\-IDENTIFER (qpid.no\-group)
+Group identifier to assign to messages
+delivered to a message group queue that
+do not contain an identifier.
.SS "Logging options:"
.TP
\fB\-t\fR [ \fB\-\-trace\fR ]
@@ -164,6 +169,10 @@ Include thread ID in log messages
Include function signature in log
messages
.TP
+\fB\-\-log\-hires\-timestamp\fR yes|no (0)
+Use unformatted hi\-res timestamp in log
+messages
+.TP
\fB\-\-log\-prefix\fR STRING
Prefix to append to all log messages
.SS "Logging sink options:"
@@ -197,7 +206,7 @@ default in this mode.
The transport for which to return the
port
.TP
-\fB\-\-pid\-dir\fR DIR
+\fB\-\-pid\-dir\fR DIR (/home/kgiusti/.qpidd)
Directory where port\-specific PID file
is stored
.TP
diff --git a/qpid/cpp/include/qpid/amqp_0_10/Codecs.h b/qpid/cpp/include/qpid/amqp_0_10/Codecs.h
index 08275402fc..73846f33a8 100644
--- a/qpid/cpp/include/qpid/amqp_0_10/Codecs.h
+++ b/qpid/cpp/include/qpid/amqp_0_10/Codecs.h
@@ -34,14 +34,14 @@ namespace amqp_0_10 {
* Codec for encoding/decoding a map of Variants using the AMQP 0-10
* map encoding.
*/
-class QPID_COMMON_EXTERN MapCodec
+class QPID_COMMON_CLASS_EXTERN MapCodec
{
public:
typedef qpid::types::Variant::Map ObjectType;
- static void encode(const ObjectType&, std::string&);
- static void decode(const std::string&, ObjectType&);
- static size_t encodedSize(const ObjectType&);
- static const std::string contentType;
+ static void QPID_COMMON_EXTERN encode(const ObjectType&, std::string&);
+ static void QPID_COMMON_EXTERN decode(const std::string&, ObjectType&);
+ static size_t QPID_COMMON_EXTERN encodedSize(const ObjectType&);
+ static const QPID_COMMON_EXTERN std::string contentType;
private:
};
@@ -49,14 +49,14 @@ class QPID_COMMON_EXTERN MapCodec
* Codec for encoding/decoding a list of Variants using the AMQP 0-10
* list encoding.
*/
-class QPID_COMMON_EXTERN ListCodec
+class QPID_COMMON_CLASS_EXTERN ListCodec
{
public:
typedef qpid::types::Variant::List ObjectType;
- static void encode(const ObjectType&, std::string&);
- static void decode(const std::string&, ObjectType&);
- static size_t encodedSize(const ObjectType&);
- static const std::string contentType;
+ static void QPID_COMMON_EXTERN encode(const ObjectType&, std::string&);
+ static void QPID_COMMON_EXTERN decode(const std::string&, ObjectType&);
+ static size_t QPID_COMMON_EXTERN encodedSize(const ObjectType&);
+ static const QPID_COMMON_EXTERN std::string contentType;
private:
};
diff --git a/qpid/cpp/include/qpid/console/ClassKey.h b/qpid/cpp/include/qpid/console/ClassKey.h
index 95cd2627f1..5f7c50351a 100644
--- a/qpid/cpp/include/qpid/console/ClassKey.h
+++ b/qpid/cpp/include/qpid/console/ClassKey.h
@@ -33,24 +33,24 @@ namespace console {
*
* \ingroup qmfconsoleapi
*/
- class QPID_CONSOLE_EXTERN ClassKey {
+ class QPID_CONSOLE_CLASS_EXTERN ClassKey {
public:
- static const int HASH_SIZE = 16;
+ QPID_CONSOLE_EXTERN static const int HASH_SIZE = 16;
- ClassKey(const std::string& package, const std::string& name, const uint8_t* hash);
+ QPID_CONSOLE_EXTERN ClassKey(const std::string& package, const std::string& name, const uint8_t* hash);
- const std::string& getPackageName() const { return package; }
- const std::string& getClassName() const { return name; }
- const uint8_t* getHash() const { return hash; }
- std::string getHashString() const;
- std::string str() const;
- bool operator==(const ClassKey& other) const;
- bool operator!=(const ClassKey& other) const;
- bool operator<(const ClassKey& other) const;
- bool operator>(const ClassKey& other) const;
- bool operator<=(const ClassKey& other) const;
- bool operator>=(const ClassKey& other) const;
- void encode(framing::Buffer& buffer) const;
+ const QPID_CONSOLE_EXTERN std::string& getPackageName() const { return package; }
+ const QPID_CONSOLE_EXTERN std::string& getClassName() const { return name; }
+ const QPID_CONSOLE_EXTERN uint8_t* getHash() const { return hash; }
+ QPID_CONSOLE_EXTERN std::string getHashString() const;
+ QPID_CONSOLE_EXTERN std::string str() const;
+ QPID_CONSOLE_EXTERN bool operator==(const ClassKey& other) const;
+ QPID_CONSOLE_EXTERN bool operator!=(const ClassKey& other) const;
+ QPID_CONSOLE_EXTERN bool operator<(const ClassKey& other) const;
+ QPID_CONSOLE_EXTERN bool operator>(const ClassKey& other) const;
+ QPID_CONSOLE_EXTERN bool operator<=(const ClassKey& other) const;
+ QPID_CONSOLE_EXTERN bool operator>=(const ClassKey& other) const;
+ QPID_CONSOLE_EXTERN void encode(framing::Buffer& buffer) const;
private:
std::string package;
diff --git a/qpid/cpp/include/qpid/console/ObjectId.h b/qpid/cpp/include/qpid/console/ObjectId.h
index 7904c85598..0722eaebeb 100644
--- a/qpid/cpp/include/qpid/console/ObjectId.h
+++ b/qpid/cpp/include/qpid/console/ObjectId.h
@@ -40,10 +40,10 @@ namespace console {
ObjectId() : first(0), second(0) {}
ObjectId(framing::Buffer& buffer);
- uint8_t getFlags() const { return (first & 0xF000000000000000LL) >> 60; }
- uint16_t getSequence() const { return (first & 0x0FFF000000000000LL) >> 48; }
- uint32_t getBrokerBank() const { return (first & 0x0000FFFFF0000000LL) >> 28; }
- uint32_t getAgentBank() const { return first & 0x000000000FFFFFFFLL; }
+ uint8_t getFlags() const { return (uint8_t)((first & 0xF000000000000000LL) >> 60); }
+ uint16_t getSequence() const { return (uint16_t)((first & 0x0FFF000000000000LL) >> 48); }
+ uint32_t getBrokerBank() const { return (uint32_t)((first & 0x0000FFFFF0000000LL) >> 28); }
+ uint32_t getAgentBank() const { return (uint32_t) (first & 0x000000000FFFFFFFLL); }
uint64_t getObject() const { return second; }
bool isDurable() const { return getSequence() == 0; }
void decode(framing::Buffer& buffer);
diff --git a/qpid/cpp/src/CMakeLists.txt b/qpid/cpp/src/CMakeLists.txt
index f7b86d5f8a..da1f539108 100644
--- a/qpid/cpp/src/CMakeLists.txt
+++ b/qpid/cpp/src/CMakeLists.txt
@@ -598,6 +598,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL Windows)
/wd4244
/wd4800
/wd4355
+ /wd4267
)
if (SET_WIN32_WINNT)
add_definitions(/D "_WIN32_WINNT=0x0502")
diff --git a/qpid/cpp/src/qmf/AgentSessionImpl.h b/qpid/cpp/src/qmf/AgentSessionImpl.h
index 9039a5985f..ae512a4054 100644
--- a/qpid/cpp/src/qmf/AgentSessionImpl.h
+++ b/qpid/cpp/src/qmf/AgentSessionImpl.h
@@ -64,7 +64,6 @@ using namespace std;
using namespace qpid::messaging;
using namespace qmf;
using qpid::types::Variant;
-using namespace boost;
typedef qmf::PrivateImplRef<AgentSession> PI;
diff --git a/qpid/cpp/src/qmf/ConsoleSessionImpl.h b/qpid/cpp/src/qmf/ConsoleSessionImpl.h
index 2f1f631715..e2b30602fa 100644
--- a/qpid/cpp/src/qmf/ConsoleSessionImpl.h
+++ b/qpid/cpp/src/qmf/ConsoleSessionImpl.h
@@ -47,7 +47,6 @@
#include <map>
#include <queue>
-using namespace boost;
using namespace std;
namespace qmf {
diff --git a/qpid/cpp/src/qpid/broker/Message.cpp b/qpid/cpp/src/qpid/broker/Message.cpp
index 992a94f92e..5ea7143366 100644
--- a/qpid/cpp/src/qpid/broker/Message.cpp
+++ b/qpid/cpp/src/qpid/broker/Message.cpp
@@ -59,6 +59,7 @@ Message::~Message() {}
void Message::forcePersistent()
{
+ sys::Mutex::ScopedLock l(lock);
// only set forced bit if we actually need to force.
if (! getAdapter().isPersistent(frames) ){
forcePersistentPolicy = true;
@@ -95,16 +96,19 @@ bool Message::isImmediate() const
const FieldTable* Message::getApplicationHeaders() const
{
+ sys::Mutex::ScopedLock l(lock);
return getAdapter().getApplicationHeaders(frames);
}
std::string Message::getAppId() const
{
+ sys::Mutex::ScopedLock l(lock);
return getAdapter().getAppId(frames);
}
bool Message::isPersistent() const
{
+ sys::Mutex::ScopedLock l(lock);
return (getAdapter().isPersistent(frames) || forcePersistentPolicy);
}
@@ -319,6 +323,7 @@ const std::string X_QPID_TRACE("x-qpid.trace");
bool Message::isExcluded(const std::vector<std::string>& excludes) const
{
+ sys::Mutex::ScopedLock l(lock);
const FieldTable* headers = getApplicationHeaders();
if (headers) {
std::string traceStr = headers->getAsString(X_QPID_TRACE);
@@ -490,6 +495,7 @@ void Message::resetDequeueCompleteCallback() {
}
uint8_t Message::getPriority() const {
+ sys::Mutex::ScopedLock l(lock);
return getAdapter().getPriority(frames);
}
diff --git a/qpid/cpp/src/qpid/broker/SessionState.cpp b/qpid/cpp/src/qpid/broker/SessionState.cpp
index 742dbe9be8..ddd6ae3f5b 100644
--- a/qpid/cpp/src/qpid/broker/SessionState.cpp
+++ b/qpid/cpp/src/qpid/broker/SessionState.cpp
@@ -346,10 +346,8 @@ void SessionState::completeRcvMsg(SequenceNumber id,
}
// if the sender has requested immediate notification of the completion...
- if (requiresSync) {
+ if (requiresSync || callSendCompletion) {
sendAcceptAndCompletion();
- } else if (callSendCompletion) {
- sendCompletion();
}
}
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp b/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp
index 6687ba75e6..16e5fde075 100644
--- a/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp
+++ b/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp
@@ -583,7 +583,7 @@ void Subscription::cancel(qpid::client::AsyncSession& session, const std::string
{
linkBindings.unbind(session);
session.messageCancel(destination);
- if (reliable) session.queueDelete(arg::queue=queue, arg::ifUnused=true);
+ if (exclusiveQueue) session.queueDelete(arg::queue=queue, arg::ifUnused=true);
checkDelete(session, FOR_RECEIVER);
}
diff --git a/qpid/cpp/src/tests/MessagingSessionTests.cpp b/qpid/cpp/src/tests/MessagingSessionTests.cpp
index 418653978b..9d5db84bb4 100644
--- a/qpid/cpp/src/tests/MessagingSessionTests.cpp
+++ b/qpid/cpp/src/tests/MessagingSessionTests.cpp
@@ -1100,6 +1100,24 @@ QPID_AUTO_TEST_CASE(testCreateBindingsOnStandardExchange)
BOOST_CHECK_EQUAL(in.getSubject(), out.getSubject());
}
+QPID_AUTO_TEST_CASE(testUnsubscribeOnClose)
+{
+ MessagingFixture fix;
+ Sender sender = fix.session.createSender("my-exchange/my-subject; {create: always, delete:sender, node:{type:topic, x-declare:{alternate-exchange:amq.fanout}}}");
+ Receiver receiver = fix.session.createReceiver("my-exchange/my-subject");
+ Receiver deadletters = fix.session.createReceiver("amq.fanout");
+
+ sender.send(Message("first"));
+ Message in = receiver.fetch(Duration::SECOND);
+ BOOST_CHECK_EQUAL(in.getContent(), std::string("first"));
+ fix.session.acknowledge();
+ receiver.close();
+ sender.send(Message("second"));
+ in = deadletters.fetch(Duration::SECOND);
+ BOOST_CHECK_EQUAL(in.getContent(), std::string("second"));
+ fix.session.acknowledge();
+}
+
QPID_AUTO_TEST_SUITE_END()
}} // namespace qpid::tests
diff --git a/qpid/java/bdbstore/bin/backup.sh b/qpid/java/bdbstore/bin/backup.sh
new file mode 100755
index 0000000000..0e2f0fda09
--- /dev/null
+++ b/qpid/java/bdbstore/bin/backup.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Parse arguments taking all - prefixed args as JAVA_OPTS
+for arg in "$@"; do
+ if [[ $arg == -java:* ]]; then
+ JAVA_OPTS="${JAVA_OPTS}-`echo $arg|cut -d ':' -f 2` "
+ else
+ ARGS="${ARGS}$arg "
+ fi
+done
+
+WHEREAMI=`dirname $0`
+if [ -z "$QPID_HOME" ]; then
+ export QPID_HOME=`cd $WHEREAMI/../ && pwd`
+fi
+VERSION=0.13
+
+LIBS=$QPID_HOME/lib/je-4.0.103.jar:$QPID_HOME/lib/qpid-bdbstore-$VERSION.jar:$QPID_HOME/lib/qpid-all.jar
+
+
+echo "Starting Hot Backup Script"
+java -Dlog4j.configuration=backup-log4j.xml ${JAVA_OPTS} -cp $LIBS org.apache.qpid.server.store.berkeleydb.BDBBackup ${ARGS}
diff --git a/qpid/java/bdbstore/bin/storeUpgrade.sh b/qpid/java/bdbstore/bin/storeUpgrade.sh
new file mode 100755
index 0000000000..076b9d3f7e
--- /dev/null
+++ b/qpid/java/bdbstore/bin/storeUpgrade.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Parse arguements taking all - prefixed args as JAVA_OPTS
+for arg in "$@"; do
+ if [[ $arg == -java:* ]]; then
+ JAVA_OPTS="${JAVA_OPTS}-`echo $arg|cut -d ':' -f 2` "
+ else
+ ARGS="${ARGS}$arg "
+ fi
+done
+
+if [ -z "$QPID_HOME" ]; then
+ export QPID_HOME=$(dirname $(dirname $(readlink -f $0)))
+ export PATH=${PATH}:${QPID_HOME}/bin
+fi
+
+if [ -z "$BDB_HOME" ]; then
+ export BDB_HOME=$(dirname $(dirname $(readlink -f $0)))
+fi
+
+VERSION=0.13
+
+LIBS=$BDB_HOME/lib/je-4.0.103.jar:$BDB_HOME/lib/qpid-bdbstore-$VERSION.jar:$QPID_HOME/lib/qpid-all.jar
+
+java -Xms256m -Dlog4j.configuration=BDBStoreUpgrade.log4j.xml -Xmx256m -Damqj.logging.level=warn ${JAVA_OPTS} -cp $LIBS org.apache.qpid.server.store.berkeleydb.BDBStoreUpgrade ${ARGS}
diff --git a/qpid/java/bdbstore/build.xml b/qpid/java/bdbstore/build.xml
new file mode 100644
index 0000000000..9355358e6c
--- /dev/null
+++ b/qpid/java/bdbstore/build.xml
@@ -0,0 +1,84 @@
+<!--
+ - Licensed to the Apache Software Foundation (ASF) under one
+ - or more contributor license agreements. See the NOTICE file
+ - distributed with this work for additional information
+ - regarding copyright ownership. The ASF licenses this file
+ - to you under the Apache License, Version 2.0 (the
+ - "License"); you may not use this file except in compliance
+ - with the License. You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing,
+ - software distributed under the License is distributed on an
+ - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ - KIND, either express or implied. See the License for the
+ - specific language governing permissions and limitations
+ - under the License.
+ -->
+<project name="bdbstore" default="build">
+ <property name="module.depends" value="common client management/common broker perftests systests" />
+ <property name="module.test.depends" value="test common/test broker/test management/common perftests systests" />
+
+ <import file="../module.xml" />
+
+ <property name="bdb.lib.dir" value="${project.root}/lib/bdbstore" />
+ <property name="bdb.version" value="4.0.103" />
+ <property name="bdb.download.url" value="http://download.oracle.com/maven/com/sleepycat/je/${bdb.version}/je-${bdb.version}.jar" />
+ <property name="bdb.jar.file" value="${bdb.lib.dir}/je-${bdb.version}.jar" />
+
+ <!--check whether the BDB jar is present, possibly after download-->
+ <target name="check-bdb-jar">
+ <available file="${bdb.jar.file}" type="file" property="bdb.jar.available"/>
+ </target>
+
+ <!--echo that BDB is required if it isnt present, with associated licencing note-->
+ <target name="bdb-jar-required" depends="bdb-licence-note-optional" unless="bdb.jar.available">
+ <echo>The BDB JE library is required to use this optional module.
+
+The jar file may be downloaded by either:
+
+ Seperately running the following command from the qpid/java/bdbstore dir: ant download-bdb
+
+ OR
+
+ Adding -Ddownload-bdb=true to your regular build command.</echo>
+ <fail>The BDB JE library was not found</fail>
+ </target>
+
+ <!--issue BDB licencing note if BDB isnt already present-->
+ <target name="bdb-licence-note-optional" depends="check-bdb-jar" unless="bdb.jar.available">
+ <antcall target="bdb-licence-note"/>
+ </target>
+
+ <!--issue BDB licencing note-->
+ <target name="bdb-licence-note">
+ <echo>*NOTE* The BDB JE library required by this optional module is licensed under the Sleepycat Licence, which is not compatible with the Apache Licence v2.0.
+
+For a copy of the Sleepycat Licence, please see:
+http://www.oracle.com/technetwork/database/berkeleydb/downloads/jeoslicense-086837.html</echo>
+ </target>
+
+ <!--check if an inline BDB download was requested with the build-->
+ <target name="check-request-props" if="download-bdb">
+ <antcall target="download-bdb"/>
+ </target>
+
+ <!--download BDB, with licencing note-->
+ <target name="download-bdb" depends="bdb-licence-note">
+ <mkdir dir="${bdb.lib.dir}"/>
+ <echo>Downloading BDB JE</echo>
+ <get src="${bdb.download.url}" dest="${bdb.jar.file}" usetimestamp="true" />
+ </target>
+
+ <target name="build" depends="check-request-props, bdb-jar-required, module.build" />
+
+ <target name="postbuild" depends="copy-store-to-upgrade" />
+
+ <target name="copy-store-to-upgrade" description="copy the upgrade tool resource folder contents into the build tree">
+ <copy todir="${qpid.home}" failonerror="true">
+ <fileset dir="src/test/resources/upgrade"/>
+ </copy>
+ </target>
+
+</project>
diff --git a/qpid/java/bdbstore/etc/scripts/bdbbackuptest.sh b/qpid/java/bdbstore/etc/scripts/bdbbackuptest.sh
new file mode 100755
index 0000000000..2a0b72e5ad
--- /dev/null
+++ b/qpid/java/bdbstore/etc/scripts/bdbbackuptest.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+if [ -z "$QPID_HOME" ]; then
+ export QPID_HOME=$(dirname $(dirname $(readlink -f $0)))
+ export PATH=${PATH}:${QPID_HOME}/bin
+fi
+
+# Parse arguements taking all - prefixed args as JAVA_OPTS
+for arg in "$@"; do
+ if [[ $arg == -java:* ]]; then
+ JAVA_OPTS="${JAVA_OPTS}-`echo $arg|cut -d ':' -f 2` "
+ else
+ ARGS="${ARGS}$arg "
+ fi
+done
+
+VERSION=0.5
+
+# Set classpath to include Qpid jar with all required jars in manifest
+QPID_LIBS=$QPID_HOME/lib/qpid-all.jar:$QPID_HOME/lib/qpid-junit-toolkit-$VERSION.jar:$QPID_HOME/lib/junit-3.8.1.jar:$QPID_HOME/lib/log4j-1.2.12.jar:$QPID_HOME/lib/qpid-systests-$VERSION.jar:$QPID_HOME/lib/qpid-perftests-$VERSION.jar:$QPID_HOME/lib/slf4j-log4j12-1.4.0.jar:$QPID_HOME/lib/qpid-bdbstore-$VERSION.jar
+
+# Set other variables used by the qpid-run script before calling
+export JAVA=java JAVA_MEM=-Xmx256m QPID_CLASSPATH=$QPID_LIBS
+
+. qpid-run -Dlog4j.configuration=perftests.log4j -Dbadger.level=warn -Damqj.test.logging.level=warn -Damqj.logging.level=warn ${JAVA_OPTS} org.apache.qpid.server.store.berkeleydb.testclient.BackupTestClient -o $QPID_WORK/results numMessagesToAction=55 ${ARGS}
+
diff --git a/qpid/java/bdbstore/etc/scripts/bdbtest.sh b/qpid/java/bdbstore/etc/scripts/bdbtest.sh
new file mode 100755
index 0000000000..eafdae9710
--- /dev/null
+++ b/qpid/java/bdbstore/etc/scripts/bdbtest.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+if [ -z "$QPID_HOME" ]; then
+ export QPID_HOME=$(dirname $(dirname $(readlink -f $0)))
+ export PATH=${PATH}:${QPID_HOME}/bin
+fi
+
+# Parse arguements taking all - prefixed args as JAVA_OPTS
+for arg in "$@"; do
+ if [[ $arg == -java:* ]]; then
+ JAVA_OPTS="${JAVA_OPTS}-`echo $arg|cut -d ':' -f 2` "
+ else
+ ARGS="${ARGS}$arg "
+ fi
+done
+
+VERSION=0.5
+
+# Set classpath to include Qpid jar with all required jars in manifest
+QPID_LIBS=$QPID_HOME/lib/qpid-all.jar:$QPID_HOME/lib/qpid-junit-toolkit-$VERSION.jar:$QPID_HOME/lib/junit-3.8.1.jar:$QPID_HOME/lib/log4j-1.2.12.jar:$QPID_HOME/lib/qpid-systests-$VERSION.jar:$QPID_HOME/lib/qpid-perftests-$VERSION.jar:$QPID_HOME/lib/slf4j-log4j12-1.4.0.jar
+
+# Set other variables used by the qpid-run script before calling
+export JAVA=java JAVA_MEM=-Xmx256m QPID_CLASSPATH=$QPID_LIBS
+
+. qpid-run -Dlog4j.configuration=perftests.log4j -Dbadger.level=warn -Damqj.test.logging.level=warn -Damqj.logging.level=warn ${JAVA_OPTS} org.apache.qpid.ping.PingDurableClient -o $QPID_WORK/results ${ARGS}
diff --git a/qpid/java/bdbstore/src/main/java/BDBStoreUpgrade.log4j.xml b/qpid/java/bdbstore/src/main/java/BDBStoreUpgrade.log4j.xml
new file mode 100644
index 0000000000..4d71963ea7
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/BDBStoreUpgrade.log4j.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0"?>
+<!--
+ -
+ - Licensed to the Apache Software Foundation (ASF) under one
+ - or more contributor license agreements. See the NOTICE file
+ - distributed with this work for additional information
+ - regarding copyright ownership. The ASF licenses this file
+ - to you under the Apache License, Version 2.0 (the
+ - "License"); you may not use this file except in compliance
+ - with the License. You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing,
+ - software distributed under the License is distributed on an
+ - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ - KIND, either express or implied. See the License for the
+ - specific language governing permissions and limitations
+ - under the License.
+ -
+ -->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+
+ <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
+
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%d %-5p - %m%n"/>
+ </layout>
+ </appender>
+
+ <category name="org.apache.qpid.server.store.berkeleydb.BDBStoreUpgrade">
+ <priority value="info"/>
+ </category>
+
+ <!-- Only show errors from the BDB Store -->
+ <category name="org.apache.qpid.server.store.berkeleydb.berkeleydb.BDBMessageStore">
+ <priority value="error"/>
+ </category>
+
+ <!-- Provide warnings to standard output -->
+ <category name="org.apache.qpid">
+ <priority value="error"/>
+ </category>
+
+ <!-- Log all info events to file -->
+ <root>
+ <priority value="info"/>
+ <appender-ref ref="STDOUT"/>
+ </root>
+
+</log4j:configuration>
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/AMQShortStringEncoding.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/AMQShortStringEncoding.java
new file mode 100644
index 0000000000..8b887b1876
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/AMQShortStringEncoding.java
@@ -0,0 +1,59 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb;
+
+import org.apache.qpid.framing.AMQShortString;
+
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+
+public class AMQShortStringEncoding
+{
+ public static AMQShortString readShortString(TupleInput tupleInput)
+ {
+ int length = (int) tupleInput.readShort();
+ if (length < 0)
+ {
+ return null;
+ }
+ else
+ {
+ byte[] stringBytes = new byte[length];
+ tupleInput.readFast(stringBytes);
+ return new AMQShortString(stringBytes);
+ }
+
+ }
+
+ public static void writeShortString(AMQShortString shortString, TupleOutput tupleOutput)
+ {
+
+ if (shortString == null)
+ {
+ tupleOutput.writeShort(-1);
+ }
+ else
+ {
+ tupleOutput.writeShort(shortString.length());
+ tupleOutput.writeFast(shortString.getBytes(), 0, shortString.length());
+ }
+ }
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/AMQShortStringTB.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/AMQShortStringTB.java
new file mode 100644
index 0000000000..81ae315fe2
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/AMQShortStringTB.java
@@ -0,0 +1,48 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb;
+
+import com.sleepycat.bind.tuple.TupleBinding;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+import org.apache.log4j.Logger;
+import org.apache.qpid.framing.AMQShortString;
+
+public class AMQShortStringTB extends TupleBinding
+{
+ private static final Logger _log = Logger.getLogger(AMQShortStringTB.class);
+
+
+ public AMQShortStringTB()
+ {
+ }
+
+ public Object entryToObject(TupleInput tupleInput)
+ {
+ return AMQShortStringEncoding.readShortString(tupleInput);
+ }
+
+ public void objectToEntry(Object object, TupleOutput tupleOutput)
+ {
+ AMQShortStringEncoding.writeShortString((AMQShortString)object, tupleOutput);
+ }
+
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBBackup.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBBackup.java
new file mode 100644
index 0000000000..c515ca5d8e
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBBackup.java
@@ -0,0 +1,344 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.berkeleydb;
+
+import com.sleepycat.je.DatabaseException;
+import com.sleepycat.je.Environment;
+import com.sleepycat.je.EnvironmentConfig;
+import com.sleepycat.je.util.DbBackup;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.util.CommandLineParser;
+import org.apache.qpid.util.FileUtils;
+
+import java.io.*;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * BDBBackup is a utility for taking hot backups of the current state of a BDB transaction log database.
+ *
+ * <p/>This utility makes the following assumptions/performs the following actions:
+ *
+ * <p/><ul> <li>The from and to directory locations will already exist. This scripts does not create them. <li>If this
+ * script fails to complete in one minute it will terminate. <li>This script always exits with code 1 on error, code 0
+ * on success (standard unix convention). <li>This script will log out at info level, when it starts and ends and a list
+ * of all files backed up. <li>This script logs all errors at error level. <li>This script does not perform regular
+ * backups, wrap its calling script in a cron job or similar to do this. </ul>
+ *
+ * <p/>This utility is build around the BDB provided backup helper utility class, DbBackup. This utility class provides
+ * an ability to force BDB to stop writing to the current log file set, whilst the backup is taken, to ensure that a
+ * consistent snapshot is acquired. Preventing BDB from writing to the current log file set, does not stop BDB from
+ * continuing to run concurrently while the backup is running, it simply moves onto a new set of log files; this
+ * provides a 'hot' backup facility.
+ *
+ * <p/>DbBackup can also help with incremental backups, by providing the number of the last log file backed up.
+ * Subsequent backups can be taken, from later log files only. In a messaging application, messages are not expected to
+ * be long-lived in most cases, so the log files will usually have been completely turned over between backups. This
+ * utility does not support incremental backups for this reason.
+ *
+ * <p/>If the database is locked by BDB, as is required when using transactions, and therefore will always be the case
+ * in Qpid, this utility cannot make use of the DbBackup utility in a seperate process. DbBackup, needs to ensure that
+ * the BDB envinronment used to take the backup has exclusive write access to the log files. This utility can take a
+ * backup as a standalone utility against log files, when a broker is not running, using the {@link #takeBackup(String,
+ *String,com.sleepycat.je.Environment)} method.
+ *
+ * <p/>A seperate backup machanism is provided by the {@link #takeBackupNoLock(String,String)} method which can take a
+ * hot backup against a running broker. This works by finding out the set of files to copy, and then opening them all to
+ * read, and repeating this process until a consistent set of open files is obtained. This is done to avoid the
+ * situation where the BDB cleanup thread deletes a file, between the directory listing and opening of the file to copy.
+ * All consistently opened files are copied. This is the default mechanism the the {@link #main} method of this utility
+ * uses.
+ *
+ * <p/><table id="crc><caption>CRC Card</caption> <tr><th> Responsibilities <th> Collaborations <tr><td> Hot copy all
+ * BDB log files from one directory to another. </table>
+ */
+public class BDBBackup
+{
+ /** Used for debugging. */
+ private static final Logger log = Logger.getLogger(BDBBackup.class);
+
+ /** Used for communicating with the user. */
+ private static final Logger console = Logger.getLogger("Console");
+
+ /** Defines the suffix used to identify BDB log files. */
+ private static final String LOG_FILE_SUFFIX = ".jdb";
+
+ /** Defines the command line format for this utility. */
+ public static final String[][] COMMAND_LINE_SPEC =
+ new String[][]
+ {
+ { "fromdir", "The path to the directory to back the bdb log file from.", "dir", "true" },
+ { "todir", "The path to the directory to save the backed up bdb log files to.", "dir", "true" }
+ };
+
+ /** Defines the timeout to terminate the backup operation on if it fails to complete. One minte. */
+ public static final long TIMEOUT = 60000;
+
+ /**
+ * Runs a backup of the BDB log files in a specified directory, copying the backed up files to another specified
+ * directory.
+ *
+ * <p/>The following arguments must be specified:
+ *
+ * <p/><table><caption>Command Line</caption> <tr><th> Option <th> Comment <tr><td> -fromdir <td> The path to the
+ * directory to back the bdb log file from. <tr><td> -todir <td> The path to the directory to save the backed up
+ * bdb log files to. </table>
+ *
+ * @param args The command line arguments.
+ */
+ public static void main(String[] args)
+ {
+ // Process the command line using standard handling (errors and usage followed by System.exit when it is wrong).
+ Properties options =
+ CommandLineParser.processCommandLine(args, new CommandLineParser(COMMAND_LINE_SPEC), System.getProperties());
+
+ // Extract the from and to directory locations and perform a backup between them.
+ try
+ {
+ String fromDir = options.getProperty("fromdir");
+ String toDir = options.getProperty("todir");
+
+ log.info("BDBBackup Utility: Starting Hot Backup.");
+
+ BDBBackup bdbBackup = new BDBBackup();
+ String[] backedUpFiles = bdbBackup.takeBackupNoLock(fromDir, toDir);
+
+ if (log.isInfoEnabled())
+ {
+ log.info("BDBBackup Utility: Hot Backup Completed. Files backed up: " + backedUpFiles);
+ }
+ }
+ catch (Exception e)
+ {
+ console.info("Backup script encountered an error and has failed: " + e.getMessage());
+ log.error("Backup script got exception: " + e.getMessage(), e);
+ System.exit(1);
+ }
+ }
+
+ /**
+ * Creates a backup of the BDB log files in the source directory, copying them to the destination directory.
+ *
+ * @param fromdir The source directory path.
+ * @param todir The destination directory path.
+ * @param environment An open BDB environment to perform the back up.
+ *
+ * @throws DatabaseException Any underlying execeptions from BDB are allowed to fall through.
+ */
+ public void takeBackup(String fromdir, String todir, Environment environment) throws DatabaseException
+ {
+ DbBackup backupHelper = null;
+
+ try
+ {
+ backupHelper = new DbBackup(environment);
+
+ // Prevent BDB from writing to its log files while the backup it taken.
+ backupHelper.startBackup();
+
+ // Back up the BDB log files to the destination directory.
+ String[] filesForBackup = backupHelper.getLogFilesInBackupSet();
+
+ for (int i = 0; i < filesForBackup.length; i++)
+ {
+ File sourceFile = new File(fromdir + File.separator + filesForBackup[i]);
+ File destFile = new File(todir + File.separator + filesForBackup[i]);
+ FileUtils.copy(sourceFile, destFile);
+ }
+ }
+ finally
+ {
+ // Remember to exit backup mode, or all log files won't be cleaned and disk usage will bloat.
+ if (backupHelper != null)
+ {
+ backupHelper.endBackup();
+ }
+ }
+ }
+
+ /**
+ * Takes a hot backup when another process has locked the BDB database.
+ *
+ * @param fromdir The source directory path.
+ * @param todir The destination directory path.
+ *
+ * @return A list of all of the names of the files succesfully backed up.
+ */
+ public String[] takeBackupNoLock(String fromdir, String todir)
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug("public void takeBackupNoLock(String fromdir = " + fromdir + ", String todir = " + todir
+ + "): called");
+ }
+
+ File fromDirFile = new File(fromdir);
+
+ if (!fromDirFile.isDirectory())
+ {
+ throw new IllegalArgumentException("The specified fromdir(" + fromdir
+ + ") must be the directory containing your bdbstore.");
+ }
+
+ File toDirFile = new File(todir);
+
+ if (!toDirFile.exists())
+ {
+ // Create directory if it doesn't exist
+ toDirFile.mkdirs();
+
+ if (log.isDebugEnabled())
+ {
+ log.debug("Created backup directory:" + toDirFile);
+ }
+ }
+
+ if (!toDirFile.isDirectory())
+ {
+ throw new IllegalArgumentException("The specified todir(" + todir + ") must be a directory.");
+ }
+
+ // Repeat until manage to open consistent set of files for reading.
+ boolean consistentSet = false;
+ FileInputStream[] fileInputStreams = new FileInputStream[0];
+ File[] fileSet = new File[0];
+ long start = System.currentTimeMillis();
+
+ while (!consistentSet)
+ {
+ // List all .jdb files in the directory.
+ fileSet = fromDirFile.listFiles(new FilenameFilter()
+ {
+ public boolean accept(File dir, String name)
+ {
+ return name.endsWith(LOG_FILE_SUFFIX);
+ }
+ });
+
+ // Open them all for reading.
+ fileInputStreams = new FileInputStream[fileSet.length];
+
+ if (fileSet.length == 0)
+ {
+ throw new RuntimeException("There are no BDB log files to backup in the " + fromdir + " directory.");
+ }
+
+ for (int i = 0; i < fileSet.length; i++)
+ {
+ try
+ {
+ fileInputStreams[i] = new FileInputStream(fileSet[i]);
+ }
+ catch (FileNotFoundException e)
+ {
+ // Close any files opened for reading so far.
+ for (int j = 0; j < i; j++)
+ {
+ if (fileInputStreams[j] != null)
+ {
+ try
+ {
+ fileInputStreams[j].close();
+ }
+ catch (IOException ioEx)
+ {
+ // Rethrow this as a runtime exception, as something strange has happened.
+ throw new RuntimeException(ioEx);
+ }
+ }
+ }
+
+ // Could not open a consistent file set so try again.
+ break;
+ }
+
+ // A consistent set has been opened if all files were sucesfully opened for reading.
+ if (i == (fileSet.length - 1))
+ {
+ consistentSet = true;
+ }
+ }
+
+ // Check that the script has not timed out, and raise an error if it has.
+ long now = System.currentTimeMillis();
+ if ((now - start) > TIMEOUT)
+ {
+ throw new RuntimeException("Hot backup script failed to complete in " + (TIMEOUT / 1000) + " seconds.");
+ }
+ }
+
+ // Copy the consistent set of open files.
+ List<String> backedUpFileNames = new LinkedList<String>();
+
+ for (int j = 0; j < fileSet.length; j++)
+ {
+ File destFile = new File(todir + File.separator + fileSet[j].getName());
+ try
+ {
+ FileUtils.copy(fileSet[j], destFile);
+ }
+ catch (RuntimeException re)
+ {
+ Throwable cause = re.getCause();
+ if ((cause != null) && (cause instanceof IOException))
+ {
+ throw new RuntimeException(re.getMessage() + " fromDir:" + fromdir + " toDir:" + toDirFile, cause);
+ }
+ else
+ {
+ throw re;
+ }
+ }
+
+ backedUpFileNames.add(destFile.getName());
+
+ // Close all of the files.
+ try
+ {
+ fileInputStreams[j].close();
+ }
+ catch (IOException e)
+ {
+ // Rethrow this as a runtime exception, as something strange has happened.
+ throw new RuntimeException(e);
+ }
+ }
+
+ return backedUpFileNames.toArray(new String[backedUpFileNames.size()]);
+ }
+
+ /*
+ * Creates an environment for the bdb log files in the specified directory. This envrinonment can only be used
+ * to backup these files, if they are not locked by another database instance.
+ *
+ * @param fromdir The path to the directory to create the environment for.
+ *
+ * @throws DatabaseException Any underlying exceptions from BDB are allowed to fall through.
+ */
+ private Environment createSourceDirEnvironment(String fromdir) throws DatabaseException
+ {
+ // Initialize the BDB backup utility on the source directory.
+ return new Environment(new File(fromdir), new EnvironmentConfig());
+ }
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStore.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStore.java
new file mode 100644
index 0000000000..f900159808
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStore.java
@@ -0,0 +1,2124 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.berkeleydb;
+
+import java.io.File;
+import java.lang.ref.SoftReference;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQStoreException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.logging.LogSubject;
+import org.apache.qpid.server.logging.actors.CurrentActor;
+import org.apache.qpid.server.logging.messages.ConfigStoreMessages;
+import org.apache.qpid.server.logging.messages.MessageStoreMessages;
+import org.apache.qpid.server.logging.messages.TransactionLogMessages;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.store.ConfigurationRecoveryHandler;
+import org.apache.qpid.server.store.DurableConfigurationStore;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.store.MessageStoreRecoveryHandler;
+import org.apache.qpid.server.store.StorableMessageMetaData;
+import org.apache.qpid.server.store.StoredMemoryMessage;
+import org.apache.qpid.server.store.StoredMessage;
+import org.apache.qpid.server.store.TransactionLogRecoveryHandler;
+import org.apache.qpid.server.store.TransactionLogResource;
+import org.apache.qpid.server.store.ConfigurationRecoveryHandler.BindingRecoveryHandler;
+import org.apache.qpid.server.store.ConfigurationRecoveryHandler.ExchangeRecoveryHandler;
+import org.apache.qpid.server.store.ConfigurationRecoveryHandler.QueueRecoveryHandler;
+import org.apache.qpid.server.store.MessageStoreRecoveryHandler.StoredMessageRecoveryHandler;
+import org.apache.qpid.server.store.TransactionLogRecoveryHandler.QueueEntryRecoveryHandler;
+import org.apache.qpid.server.store.berkeleydb.keys.MessageContentKey_5;
+import org.apache.qpid.server.store.berkeleydb.records.ExchangeRecord;
+import org.apache.qpid.server.store.berkeleydb.records.QueueRecord;
+import org.apache.qpid.server.store.berkeleydb.tuples.BindingTupleBindingFactory;
+import org.apache.qpid.server.store.berkeleydb.tuples.MessageContentKeyTB_5;
+import org.apache.qpid.server.store.berkeleydb.tuples.MessageMetaDataTupleBindingFactory;
+import org.apache.qpid.server.store.berkeleydb.tuples.QueueEntryTB;
+import org.apache.qpid.server.store.berkeleydb.tuples.QueueTupleBindingFactory;
+
+import com.sleepycat.bind.EntryBinding;
+import com.sleepycat.bind.tuple.ByteBinding;
+import com.sleepycat.bind.tuple.TupleBinding;
+import com.sleepycat.je.CheckpointConfig;
+import com.sleepycat.je.Cursor;
+import com.sleepycat.je.Database;
+import com.sleepycat.je.DatabaseConfig;
+import com.sleepycat.je.DatabaseEntry;
+import com.sleepycat.je.DatabaseException;
+import com.sleepycat.je.Environment;
+import com.sleepycat.je.EnvironmentConfig;
+import com.sleepycat.je.LockMode;
+import com.sleepycat.je.OperationStatus;
+import com.sleepycat.je.TransactionConfig;
+
+/**
+ * BDBMessageStore implements a persistent {@link MessageStore} using the BDB high performance log.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption> <tr><th> Responsibilities <th> Collaborations <tr><td> Accept
+ * transaction boundary demarcations: Begin, Commit, Abort. <tr><td> Store and remove queues. <tr><td> Store and remove
+ * exchanges. <tr><td> Store and remove messages. <tr><td> Bind and unbind queues to exchanges. <tr><td> Enqueue and
+ * dequeue messages to queues. <tr><td> Generate message identifiers. </table>
+ */
+@SuppressWarnings({"unchecked"})
+public class BDBMessageStore implements MessageStore
+{
+ private static final Logger _log = Logger.getLogger(BDBMessageStore.class);
+
+ static final int DATABASE_FORMAT_VERSION = 5;
+ private static final String DATABASE_FORMAT_VERSION_PROPERTY = "version";
+ public static final String ENVIRONMENT_PATH_PROPERTY = "environment-path";
+
+ private Environment _environment;
+
+ private String MESSAGEMETADATADB_NAME = "messageMetaDataDb";
+ private String MESSAGECONTENTDB_NAME = "messageContentDb";
+ private String QUEUEBINDINGSDB_NAME = "queueBindingsDb";
+ private String DELIVERYDB_NAME = "deliveryDb";
+ private String EXCHANGEDB_NAME = "exchangeDb";
+ private String QUEUEDB_NAME = "queueDb";
+ private Database _messageMetaDataDb;
+ private Database _messageContentDb;
+ private Database _queueBindingsDb;
+ private Database _deliveryDb;
+ private Database _exchangeDb;
+ private Database _queueDb;
+
+ /* =======
+ * Schema:
+ * =======
+ *
+ * Queue:
+ * name(AMQShortString) - name(AMQShortString), owner(AMQShortString),
+ * arguments(FieldTable encoded as binary), exclusive (boolean)
+ *
+ * Exchange:
+ * name(AMQShortString) - name(AMQShortString), typeName(AMQShortString), autodelete (boolean)
+ *
+ * Binding:
+ * exchangeName(AMQShortString), queueName(AMQShortString), routingKey(AMQShortString),
+ * arguments (FieldTable encoded as binary) - 0 (zero)
+ *
+ * QueueEntry:
+ * queueName(AMQShortString), messageId (long) - 0 (zero)
+ *
+ * Message (MetaData):
+ * messageId (long) - bodySize (integer), metaData (MessageMetaData encoded as binary)
+ *
+ * Message (Content):
+ * messageId (long), byteOffset (integer) - dataLength(integer), data(binary);
+ */
+
+ private LogSubject _logSubject;
+
+ private final AtomicLong _messageId = new AtomicLong(0);
+
+ private final CommitThread _commitThread = new CommitThread("Commit-Thread");
+
+ // Factory Classes to create the TupleBinding objects that reflect the version instance of this BDBStore
+ private MessageMetaDataTupleBindingFactory _metaDataTupleBindingFactory;
+ private QueueTupleBindingFactory _queueTupleBindingFactory;
+ private BindingTupleBindingFactory _bindingTupleBindingFactory;
+
+ /** The data version this store should run with */
+ private int _version;
+ private enum State
+ {
+ INITIAL,
+ CONFIGURING,
+ CONFIGURED,
+ RECOVERING,
+ STARTED,
+ CLOSING,
+ CLOSED
+ }
+
+ private State _state = State.INITIAL;
+
+ private TransactionConfig _transactionConfig = new TransactionConfig();
+
+ private boolean _readOnly = false;
+
+ private boolean _configured;
+
+
+ public BDBMessageStore()
+ {
+ this(DATABASE_FORMAT_VERSION);
+ }
+
+ public BDBMessageStore(int version)
+ {
+ _version = version;
+ }
+
+ private void setDatabaseNames(int version)
+ {
+ if (version > 1)
+ {
+ MESSAGEMETADATADB_NAME += "_v" + version;
+
+ MESSAGECONTENTDB_NAME += "_v" + version;
+
+ QUEUEDB_NAME += "_v" + version;
+
+ DELIVERYDB_NAME += "_v" + version;
+
+ EXCHANGEDB_NAME += "_v" + version;
+
+ QUEUEBINDINGSDB_NAME += "_v" + version;
+ }
+ }
+
+ public void configureConfigStore(String name,
+ ConfigurationRecoveryHandler recoveryHandler,
+ Configuration storeConfiguration,
+ LogSubject logSubject) throws Exception
+ {
+ _logSubject = logSubject;
+ CurrentActor.get().message(_logSubject, ConfigStoreMessages.CREATED(this.getClass().getName()));
+
+ if(_configured)
+ {
+ throw new Exception("ConfigStore already configured");
+ }
+
+ configure(name,storeConfiguration);
+
+ _configured = true;
+ stateTransition(State.CONFIGURING, State.CONFIGURED);
+
+ recover(recoveryHandler);
+ stateTransition(State.RECOVERING, State.STARTED);
+ }
+
+ public void configureMessageStore(String name,
+ MessageStoreRecoveryHandler recoveryHandler,
+ Configuration storeConfiguration,
+ LogSubject logSubject) throws Exception
+ {
+ CurrentActor.get().message(_logSubject, MessageStoreMessages.CREATED(this.getClass().getName()));
+
+ if(!_configured)
+ {
+ throw new Exception("ConfigStore not configured");
+ }
+
+ recoverMessages(recoveryHandler);
+ }
+
+ public void configureTransactionLog(String name, TransactionLogRecoveryHandler recoveryHandler,
+ Configuration storeConfiguration, LogSubject logSubject) throws Exception
+ {
+ CurrentActor.get().message(_logSubject, TransactionLogMessages.CREATED(this.getClass().getName()));
+
+ if(!_configured)
+ {
+ throw new Exception("ConfigStore not configured");
+ }
+
+ recoverQueueEntries(recoveryHandler);
+
+
+ }
+
+ public org.apache.qpid.server.store.TransactionLog.Transaction newTransaction()
+ {
+ return new BDBTransaction();
+ }
+
+
+ /**
+ * Called after instantiation in order to configure the message store.
+ *
+ * @param name The name of the virtual host using this store
+ * @return whether a new store environment was created or not (to indicate whether recovery is necessary)
+ *
+ * @throws Exception If any error occurs that means the store is unable to configure itself.
+ */
+ public boolean configure(String name, Configuration storeConfig) throws Exception
+ {
+ File environmentPath = new File(storeConfig.getString(ENVIRONMENT_PATH_PROPERTY,
+ System.getProperty("QPID_WORK") + "/bdbstore/" + name));
+ if (!environmentPath.exists())
+ {
+ if (!environmentPath.mkdirs())
+ {
+ throw new IllegalArgumentException("Environment path " + environmentPath + " could not be read or created. "
+ + "Ensure the path is correct and that the permissions are correct.");
+ }
+ }
+
+ CurrentActor.get().message(_logSubject, MessageStoreMessages.STORE_LOCATION(environmentPath.getAbsolutePath()));
+
+ _version = storeConfig.getInt(DATABASE_FORMAT_VERSION_PROPERTY, DATABASE_FORMAT_VERSION);
+
+ return configure(environmentPath, false);
+ }
+
+ /**
+ * @param environmentPath location for the store to be created in/recovered from
+ * @param readonly if true then don't allow modifications to an existing store, and don't create a new store if none exists
+ * @return whether or not a new store environment was created
+ * @throws AMQStoreException
+ * @throws DatabaseException
+ */
+ protected boolean configure(File environmentPath, boolean readonly) throws AMQStoreException, DatabaseException
+ {
+ _readOnly = readonly;
+ stateTransition(State.INITIAL, State.CONFIGURING);
+
+ _log.info("Configuring BDB message store");
+
+ createTupleBindingFactories(_version);
+
+ setDatabaseNames(_version);
+
+ return setupStore(environmentPath, readonly);
+ }
+
+ private void createTupleBindingFactories(int version)
+ {
+ _bindingTupleBindingFactory = new BindingTupleBindingFactory(version);
+ _queueTupleBindingFactory = new QueueTupleBindingFactory(version);
+ _metaDataTupleBindingFactory = new MessageMetaDataTupleBindingFactory(version);
+ }
+
+ /**
+ * Move the store state from CONFIGURING to STARTED.
+ *
+ * This is required if you do not want to perform recovery of the store data
+ *
+ * @throws AMQStoreException if the store is not in the correct state
+ */
+ public void start() throws AMQStoreException
+ {
+ stateTransition(State.CONFIGURING, State.STARTED);
+ }
+
+ private boolean setupStore(File storePath, boolean readonly) throws DatabaseException, AMQStoreException
+ {
+ checkState(State.CONFIGURING);
+
+ boolean newEnvironment = createEnvironment(storePath, readonly);
+
+ verifyVersionByTables();
+
+ openDatabases(readonly);
+
+ if (!readonly)
+ {
+ _commitThread.start();
+ }
+
+ return newEnvironment;
+ }
+
+ private void verifyVersionByTables() throws DatabaseException
+ {
+ for (String s : _environment.getDatabaseNames())
+ {
+ int versionIndex = s.indexOf("_v");
+
+ // lack of _v index suggests DB is v1
+ // so if _version is not v1 then error
+ if (versionIndex == -1)
+ {
+ if (_version != 1)
+ {
+ closeEnvironment();
+ throw new IllegalArgumentException("Error: Unable to load BDBStore as version " + _version
+ + ". Store on disk contains version 1 data.");
+ }
+ else // DB is v1 and _version is v1
+ {
+ continue;
+ }
+ }
+
+ // Otherwise Check Versions
+ int version = Integer.parseInt(s.substring(versionIndex + 2));
+
+ if (version != _version)
+ {
+ closeEnvironment();
+ throw new IllegalArgumentException("Error: Unable to load BDBStore as version " + _version
+ + ". Store on disk contains version " + version + " data.");
+ }
+ }
+ }
+
+ private synchronized void stateTransition(State requiredState, State newState) throws AMQStoreException
+ {
+ if (_state != requiredState)
+ {
+ throw new AMQStoreException("Cannot transition to the state: " + newState + "; need to be in state: " + requiredState
+ + "; currently in state: " + _state);
+ }
+
+ _state = newState;
+ }
+
+ private void checkState(State requiredState) throws AMQStoreException
+ {
+ if (_state != requiredState)
+ {
+ throw new AMQStoreException("Unexpected state: " + _state + "; required state: " + requiredState);
+ }
+ }
+
+ private boolean createEnvironment(File environmentPath, boolean readonly) throws DatabaseException
+ {
+ _log.info("BDB message store using environment path " + environmentPath.getAbsolutePath());
+ EnvironmentConfig envConfig = new EnvironmentConfig();
+ // This is what allows the creation of the store if it does not already exist.
+ envConfig.setAllowCreate(true);
+ envConfig.setTransactional(true);
+ envConfig.setConfigParam("je.lock.nLockTables", "7");
+
+ // Restore 500,000 default timeout.
+ //envConfig.setLockTimeout(15000);
+
+ // Added to help diagnosis of Deadlock issue
+ // http://www.oracle.com/technology/products/berkeley-db/faq/je_faq.html#23
+ if (Boolean.getBoolean("qpid.bdb.lock.debug"))
+ {
+ envConfig.setConfigParam("je.txn.deadlockStackTrace", "true");
+ envConfig.setConfigParam("je.txn.dumpLocks", "true");
+ }
+
+ // Set transaction mode
+ _transactionConfig.setReadCommitted(true);
+
+ //This prevents background threads running which will potentially update the store.
+ envConfig.setReadOnly(readonly);
+ try
+ {
+ _environment = new Environment(environmentPath, envConfig);
+ return false;
+ }
+ catch (DatabaseException de)
+ {
+ if (de.getMessage().contains("Environment.setAllowCreate is false"))
+ {
+ //Allow the creation this time
+ envConfig.setAllowCreate(true);
+ if (_environment != null )
+ {
+ _environment.cleanLog();
+ _environment.close();
+ }
+ _environment = new Environment(environmentPath, envConfig);
+
+ return true;
+ }
+ else
+ {
+ throw de;
+ }
+ }
+ }
+
+ private void openDatabases(boolean readonly) throws DatabaseException
+ {
+ DatabaseConfig dbConfig = new DatabaseConfig();
+ dbConfig.setTransactional(true);
+ dbConfig.setAllowCreate(true);
+
+ //This is required if we are wanting read only access.
+ dbConfig.setReadOnly(readonly);
+
+ _messageMetaDataDb = _environment.openDatabase(null, MESSAGEMETADATADB_NAME, dbConfig);
+ _queueDb = _environment.openDatabase(null, QUEUEDB_NAME, dbConfig);
+ _exchangeDb = _environment.openDatabase(null, EXCHANGEDB_NAME, dbConfig);
+ _queueBindingsDb = _environment.openDatabase(null, QUEUEBINDINGSDB_NAME, dbConfig);
+ _messageContentDb = _environment.openDatabase(null, MESSAGECONTENTDB_NAME, dbConfig);
+ _deliveryDb = _environment.openDatabase(null, DELIVERYDB_NAME, dbConfig);
+
+ }
+
+ /**
+ * Called to close and cleanup any resources used by the message store.
+ *
+ * @throws Exception If the close fails.
+ */
+ public void close() throws Exception
+ {
+ if (_state != State.STARTED)
+ {
+ return;
+ }
+
+ _state = State.CLOSING;
+
+ _commitThread.close();
+ _commitThread.join();
+
+ if (_messageMetaDataDb != null)
+ {
+ _log.info("Closing message metadata database");
+ _messageMetaDataDb.close();
+ }
+
+ if (_messageContentDb != null)
+ {
+ _log.info("Closing message content database");
+ _messageContentDb.close();
+ }
+
+ if (_exchangeDb != null)
+ {
+ _log.info("Closing exchange database");
+ _exchangeDb.close();
+ }
+
+ if (_queueBindingsDb != null)
+ {
+ _log.info("Closing bindings database");
+ _queueBindingsDb.close();
+ }
+
+ if (_queueDb != null)
+ {
+ _log.info("Closing queue database");
+ _queueDb.close();
+ }
+
+ if (_deliveryDb != null)
+ {
+ _log.info("Close delivery database");
+ _deliveryDb.close();
+ }
+
+ closeEnvironment();
+
+ _state = State.CLOSED;
+
+ CurrentActor.get().message(_logSubject,MessageStoreMessages.CLOSED());
+ }
+
+ private void closeEnvironment() throws DatabaseException
+ {
+ if (_environment != null)
+ {
+ if(!_readOnly)
+ {
+ // Clean the log before closing. This makes sure it doesn't contain
+ // redundant data. Closing without doing this means the cleaner may not
+ // get a chance to finish.
+ _environment.cleanLog();
+ }
+ _environment.close();
+ }
+ }
+
+
+ public void recover(ConfigurationRecoveryHandler recoveryHandler) throws AMQStoreException
+ {
+ stateTransition(State.CONFIGURED, State.RECOVERING);
+
+ CurrentActor.get().message(_logSubject,MessageStoreMessages.RECOVERY_START());
+
+ try
+ {
+ QueueRecoveryHandler qrh = recoveryHandler.begin(this);
+ loadQueues(qrh);
+
+ ExchangeRecoveryHandler erh = qrh.completeQueueRecovery();
+ loadExchanges(erh);
+
+ BindingRecoveryHandler brh = erh.completeExchangeRecovery();
+ recoverBindings(brh);
+
+ brh.completeBindingRecovery();
+ }
+ catch (DatabaseException e)
+ {
+ throw new AMQStoreException("Error recovering persistent state: " + e.getMessage(), e);
+ }
+
+ }
+
+ private void loadQueues(QueueRecoveryHandler qrh) throws DatabaseException
+ {
+ Cursor cursor = null;
+
+ try
+ {
+ cursor = _queueDb.openCursor(null, null);
+ DatabaseEntry key = new DatabaseEntry();
+ DatabaseEntry value = new DatabaseEntry();
+ TupleBinding binding = _queueTupleBindingFactory.getInstance();
+ while (cursor.getNext(key, value, LockMode.RMW) == OperationStatus.SUCCESS)
+ {
+ QueueRecord queueRecord = (QueueRecord) binding.entryToObject(value);
+
+ String queueName = queueRecord.getNameShortString() == null ? null :
+ queueRecord.getNameShortString().asString();
+ String owner = queueRecord.getOwner() == null ? null :
+ queueRecord.getOwner().asString();
+ boolean exclusive = queueRecord.isExclusive();
+
+ FieldTable arguments = queueRecord.getArguments();
+
+ qrh.queue(queueName, owner, exclusive, arguments);
+ }
+
+ }
+ finally
+ {
+ if (cursor != null)
+ {
+ cursor.close();
+ }
+ }
+ }
+
+
+ private void loadExchanges(ExchangeRecoveryHandler erh) throws DatabaseException
+ {
+ Cursor cursor = null;
+
+ try
+ {
+ cursor = _exchangeDb.openCursor(null, null);
+ DatabaseEntry key = new DatabaseEntry();
+ DatabaseEntry value = new DatabaseEntry();
+ TupleBinding binding = new ExchangeTB();
+
+ while (cursor.getNext(key, value, LockMode.RMW) == OperationStatus.SUCCESS)
+ {
+ ExchangeRecord exchangeRec = (ExchangeRecord) binding.entryToObject(value);
+
+ String exchangeName = exchangeRec.getNameShortString() == null ? null :
+ exchangeRec.getNameShortString().asString();
+ String type = exchangeRec.getType() == null ? null :
+ exchangeRec.getType().asString();
+ boolean autoDelete = exchangeRec.isAutoDelete();
+
+ erh.exchange(exchangeName, type, autoDelete);
+ }
+ }
+ finally
+ {
+ if (cursor != null)
+ {
+ cursor.close();
+ }
+ }
+
+ }
+
+ private void recoverBindings(BindingRecoveryHandler brh) throws DatabaseException
+ {
+ Cursor cursor = null;
+ try
+ {
+ cursor = _queueBindingsDb.openCursor(null, null);
+ DatabaseEntry key = new DatabaseEntry();
+ DatabaseEntry value = new DatabaseEntry();
+ TupleBinding binding = _bindingTupleBindingFactory.getInstance();
+
+ while (cursor.getNext(key, value, LockMode.RMW) == OperationStatus.SUCCESS)
+ {
+ //yes, this is retrieving all the useful information from the key only.
+ //For table compatibility it shall currently be left as is
+ BindingKey bindingRecord = (BindingKey) binding.entryToObject(key);
+
+ String exchangeName = bindingRecord.getExchangeName() == null ? null :
+ bindingRecord.getExchangeName().asString();
+ String queueName = bindingRecord.getQueueName() == null ? null :
+ bindingRecord.getQueueName().asString();
+ String routingKey = bindingRecord.getRoutingKey() == null ? null :
+ bindingRecord.getRoutingKey().asString();
+ ByteBuffer argumentsBB = (bindingRecord.getArguments() == null ? null :
+ java.nio.ByteBuffer.wrap(bindingRecord.getArguments().getDataAsBytes()));
+
+ brh.binding(exchangeName, queueName, routingKey, argumentsBB);
+ }
+ }
+ finally
+ {
+ if (cursor != null)
+ {
+ cursor.close();
+ }
+ }
+
+ }
+
+ private void recoverMessages(MessageStoreRecoveryHandler msrh) throws DatabaseException
+ {
+ StoredMessageRecoveryHandler mrh = msrh.begin();
+
+ Cursor cursor = null;
+ try
+ {
+ cursor = _messageMetaDataDb.openCursor(null, null);
+ DatabaseEntry key = new DatabaseEntry();
+ EntryBinding keyBinding = TupleBinding.getPrimitiveBinding(Long.class);;
+
+ DatabaseEntry value = new DatabaseEntry();
+ EntryBinding valueBinding = _metaDataTupleBindingFactory.getInstance();
+
+ long maxId = 0;
+
+ while (cursor.getNext(key, value, LockMode.RMW) == OperationStatus.SUCCESS)
+ {
+ long messageId = (Long) keyBinding.entryToObject(key);
+ StorableMessageMetaData metaData = (StorableMessageMetaData) valueBinding.entryToObject(value);
+
+ StoredBDBMessage message = new StoredBDBMessage(messageId, metaData, false);
+ mrh.message(message);
+
+ maxId = Math.max(maxId, messageId);
+ }
+
+ _messageId.set(maxId);
+ }
+ catch (DatabaseException e)
+ {
+ _log.error("Database Error: " + e.getMessage(), e);
+ throw e;
+ }
+ finally
+ {
+ if (cursor != null)
+ {
+ cursor.close();
+ }
+ }
+ }
+
+ private void recoverQueueEntries(TransactionLogRecoveryHandler recoveryHandler)
+ throws DatabaseException
+ {
+ QueueEntryRecoveryHandler qerh = recoveryHandler.begin(this);
+
+ ArrayList<QueueEntryKey> entries = new ArrayList<QueueEntryKey>();
+
+ Cursor cursor = null;
+ try
+ {
+ cursor = _deliveryDb.openCursor(null, null);
+ DatabaseEntry key = new DatabaseEntry();
+ EntryBinding keyBinding = new QueueEntryTB();
+
+ DatabaseEntry value = new DatabaseEntry();
+
+ while (cursor.getNext(key, value, LockMode.RMW) == OperationStatus.SUCCESS)
+ {
+ QueueEntryKey qek = (QueueEntryKey) keyBinding.entryToObject(key);
+
+ entries.add(qek);
+ }
+
+ try
+ {
+ cursor.close();
+ }
+ finally
+ {
+ cursor = null;
+ }
+
+ for(QueueEntryKey entry : entries)
+ {
+ AMQShortString queueName = entry.getQueueName();
+ long messageId = entry.getMessageId();
+
+ qerh.queueEntry(queueName.asString(),messageId);
+ }
+ }
+ catch (DatabaseException e)
+ {
+ _log.error("Database Error: " + e.getMessage(), e);
+ throw e;
+ }
+ finally
+ {
+ if (cursor != null)
+ {
+ cursor.close();
+ }
+ }
+
+ qerh.completeQueueEntryRecovery();
+ }
+
+ /**
+ * Removes the specified message from the store.
+ *
+ * @param messageId Identifies the message to remove.
+ *
+ * @throws AMQInternalException If the operation fails for any reason.
+ */
+ public void removeMessage(Long messageId) throws AMQStoreException
+ {
+ // _log.debug("public void removeMessage(Long messageId = " + messageId): called");
+
+ com.sleepycat.je.Transaction tx = null;
+
+ Cursor cursor = null;
+ try
+ {
+ tx = _environment.beginTransaction(null, null);
+
+ //remove the message meta data from the store
+ DatabaseEntry key = new DatabaseEntry();
+ EntryBinding metaKeyBindingTuple = TupleBinding.getPrimitiveBinding(Long.class);
+ metaKeyBindingTuple.objectToEntry(messageId, key);
+
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Removing message id " + messageId);
+ }
+
+
+ OperationStatus status = _messageMetaDataDb.delete(tx, key);
+ if (status == OperationStatus.NOTFOUND)
+ {
+ tx.abort();
+
+ throw new AMQStoreException("Message metadata not found for message id " + messageId);
+ }
+
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Deleted metadata for message " + messageId);
+ }
+
+ //now remove the content data from the store if there is any.
+
+ DatabaseEntry contentKeyEntry = new DatabaseEntry();
+ MessageContentKey_5 mck = new MessageContentKey_5(messageId,0);
+
+ TupleBinding<MessageContentKey> contentKeyTupleBinding = new MessageContentKeyTB_5();
+ contentKeyTupleBinding.objectToEntry(mck, contentKeyEntry);
+
+ //Use a partial record for the value to prevent retrieving the
+ //data itself as we only need the key to identify what to remove.
+ DatabaseEntry value = new DatabaseEntry();
+ value.setPartial(0, 0, true);
+
+ cursor = _messageContentDb.openCursor(tx, null);
+
+ status = cursor.getSearchKeyRange(contentKeyEntry, value, LockMode.RMW);
+ while (status == OperationStatus.SUCCESS)
+ {
+ mck = (MessageContentKey_5) contentKeyTupleBinding.entryToObject(contentKeyEntry);
+
+ if(mck.getMessageId() != messageId)
+ {
+ //we have exhausted all chunks for this message id, break
+ break;
+ }
+ else
+ {
+ status = cursor.delete();
+
+ if(status == OperationStatus.NOTFOUND)
+ {
+ cursor.close();
+ cursor = null;
+
+ tx.abort();
+ throw new AMQStoreException("Content chunk offset" + mck.getOffset() + " not found for message " + messageId);
+ }
+
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Deleted content chunk offset " + mck.getOffset() + " for message " + messageId);
+ }
+ }
+
+ status = cursor.getNext(contentKeyEntry, value, LockMode.RMW);
+ }
+
+ cursor.close();
+ cursor = null;
+
+ commit(tx, true);
+ }
+ catch (DatabaseException e)
+ {
+ e.printStackTrace();
+
+ if (tx != null)
+ {
+ try
+ {
+ if(cursor != null)
+ {
+ cursor.close();
+ cursor = null;
+ }
+
+ tx.abort();
+ }
+ catch (DatabaseException e1)
+ {
+ throw new AMQStoreException("Error aborting transaction " + e1, e1);
+ }
+ }
+
+ throw new AMQStoreException("Error removing message with id " + messageId + " from database: " + e.getMessage(), e);
+ }
+ finally
+ {
+ if(cursor != null)
+ {
+ try
+ {
+ cursor.close();
+ }
+ catch (DatabaseException e)
+ {
+ throw new AMQStoreException("Error closing database connection: " + e.getMessage(), e);
+ }
+ }
+ }
+ }
+
+ /**
+ * @see DurableConfigurationStore#createExchange(Exchange)
+ */
+ public void createExchange(Exchange exchange) throws AMQStoreException
+ {
+ if (_state != State.RECOVERING)
+ {
+ ExchangeRecord exchangeRec = new ExchangeRecord(exchange.getNameShortString(),
+ exchange.getTypeShortString(), exchange.isAutoDelete());
+
+ DatabaseEntry key = new DatabaseEntry();
+ EntryBinding keyBinding = new AMQShortStringTB();
+ keyBinding.objectToEntry(exchange.getNameShortString(), key);
+
+ DatabaseEntry value = new DatabaseEntry();
+ TupleBinding exchangeBinding = new ExchangeTB();
+ exchangeBinding.objectToEntry(exchangeRec, value);
+
+ try
+ {
+ _exchangeDb.put(null, key, value);
+ }
+ catch (DatabaseException e)
+ {
+ throw new AMQStoreException("Error writing Exchange with name " + exchange.getName() + " to database: " + e.getMessage(), e);
+ }
+ }
+ }
+
+ /**
+ * @see DurableConfigurationStore#removeExchange(Exchange)
+ */
+ public void removeExchange(Exchange exchange) throws AMQStoreException
+ {
+ DatabaseEntry key = new DatabaseEntry();
+ EntryBinding keyBinding = new AMQShortStringTB();
+ keyBinding.objectToEntry(exchange.getNameShortString(), key);
+ try
+ {
+ OperationStatus status = _exchangeDb.delete(null, key);
+ if (status == OperationStatus.NOTFOUND)
+ {
+ throw new AMQStoreException("Exchange " + exchange.getName() + " not found");
+ }
+ }
+ catch (DatabaseException e)
+ {
+ throw new AMQStoreException("Error writing deleting with name " + exchange.getName() + " from database: " + e.getMessage(), e);
+ }
+ }
+
+
+
+
+ /**
+ * @see DurableConfigurationStore#bindQueue(Exchange, AMQShortString, AMQQueue, FieldTable)
+ */
+ public void bindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQStoreException
+ {
+ // _log.debug("public void bindQueue(Exchange exchange = " + exchange + ", AMQShortString routingKey = " + routingKey
+ // + ", AMQQueue queue = " + queue + ", FieldTable args = " + args + "): called");
+
+ if (_state != State.RECOVERING)
+ {
+ BindingKey bindingRecord = new BindingKey(exchange.getNameShortString(),
+ queue.getNameShortString(), routingKey, args);
+
+ DatabaseEntry key = new DatabaseEntry();
+ EntryBinding keyBinding = _bindingTupleBindingFactory.getInstance();
+
+ keyBinding.objectToEntry(bindingRecord, key);
+
+ //yes, this is writing out 0 as a value and putting all the
+ //useful info into the key, don't ask me why. For table
+ //compatibility it shall currently be left as is
+ DatabaseEntry value = new DatabaseEntry();
+ ByteBinding.byteToEntry((byte) 0, value);
+
+ try
+ {
+ _queueBindingsDb.put(null, key, value);
+ }
+ catch (DatabaseException e)
+ {
+ throw new AMQStoreException("Error writing binding for AMQQueue with name " + queue.getName() + " to exchange "
+ + exchange.getName() + " to database: " + e.getMessage(), e);
+ }
+ }
+ }
+
+ /**
+ * @see DurableConfigurationStore#unbindQueue(Exchange, AMQShortString, AMQQueue, FieldTable)
+ */
+ public void unbindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args)
+ throws AMQStoreException
+ {
+ DatabaseEntry key = new DatabaseEntry();
+ EntryBinding keyBinding = _bindingTupleBindingFactory.getInstance();
+ keyBinding.objectToEntry(new BindingKey(exchange.getNameShortString(), queue.getNameShortString(), routingKey, args), key);
+
+ try
+ {
+ OperationStatus status = _queueBindingsDb.delete(null, key);
+ if (status == OperationStatus.NOTFOUND)
+ {
+ throw new AMQStoreException("Queue binding for queue with name " + queue.getName() + " to exchange "
+ + exchange.getName() + " not found");
+ }
+ }
+ catch (DatabaseException e)
+ {
+ throw new AMQStoreException("Error deleting queue binding for queue with name " + queue.getName() + " to exchange "
+ + exchange.getName() + " from database: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * @see DurableConfigurationStore#createQueue(AMQQueue)
+ */
+ public void createQueue(AMQQueue queue) throws AMQStoreException
+ {
+ createQueue(queue, null);
+ }
+
+ /**
+ * @see DurableConfigurationStore#createQueue(AMQQueue, FieldTable)
+ */
+ public void createQueue(AMQQueue queue, FieldTable arguments) throws AMQStoreException
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("public void createQueue(AMQQueue queue(" + queue.getName() + ") = " + queue + "): called");
+ }
+
+ QueueRecord queueRecord= new QueueRecord(queue.getNameShortString(),
+ queue.getOwner(), queue.isExclusive(), arguments);
+
+ createQueue(queueRecord);
+ }
+
+ /**
+ * Makes the specified queue persistent.
+ *
+ * Only intended for direct use during store upgrades.
+ *
+ * @param queueRecord Details of the queue to store.
+ *
+ * @throws AMQStoreException If the operation fails for any reason.
+ */
+ protected void createQueue(QueueRecord queueRecord) throws AMQStoreException
+ {
+ if (_state != State.RECOVERING)
+ {
+ DatabaseEntry key = new DatabaseEntry();
+ EntryBinding keyBinding = new AMQShortStringTB();
+ keyBinding.objectToEntry(queueRecord.getNameShortString(), key);
+
+ DatabaseEntry value = new DatabaseEntry();
+ TupleBinding queueBinding = _queueTupleBindingFactory.getInstance();
+
+ queueBinding.objectToEntry(queueRecord, value);
+ try
+ {
+ _queueDb.put(null, key, value);
+ }
+ catch (DatabaseException e)
+ {
+ throw new AMQStoreException("Error writing AMQQueue with name " + queueRecord.getNameShortString().asString()
+ + " to database: " + e.getMessage(), e);
+ }
+ }
+ }
+
+ /**
+ * Updates the specified queue in the persistent store, IF it is already present. If the queue
+ * is not present in the store, it will not be added.
+ *
+ * NOTE: Currently only updates the exclusivity.
+ *
+ * @param queue The queue to update the entry for.
+ * @throws AMQStoreException If the operation fails for any reason.
+ */
+ public void updateQueue(final AMQQueue queue) throws AMQStoreException
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Updating queue: " + queue.getName());
+ }
+
+ try
+ {
+ DatabaseEntry key = new DatabaseEntry();
+ EntryBinding keyBinding = new AMQShortStringTB();
+ keyBinding.objectToEntry(queue.getNameShortString(), key);
+
+ DatabaseEntry value = new DatabaseEntry();
+ DatabaseEntry newValue = new DatabaseEntry();
+ TupleBinding queueBinding = _queueTupleBindingFactory.getInstance();
+
+ OperationStatus status = _queueDb.get(null, key, value, LockMode.DEFAULT);
+ if(status == OperationStatus.SUCCESS)
+ {
+ //read the existing record and apply the new exclusivity setting
+ QueueRecord queueRecord = (QueueRecord) queueBinding.entryToObject(value);
+ queueRecord.setExclusive(queue.isExclusive());
+
+ //write the updated entry to the store
+ queueBinding.objectToEntry(queueRecord, newValue);
+
+ _queueDb.put(null, key, newValue);
+ }
+ else if(status != OperationStatus.NOTFOUND)
+ {
+ throw new AMQStoreException("Error updating queue details within the store: " + status);
+ }
+ }
+ catch (DatabaseException e)
+ {
+ throw new AMQStoreException("Error updating queue details within the store: " + e,e);
+ }
+ }
+
+ /**
+ * Removes the specified queue from the persistent store.
+ *
+ * @param queue The queue to remove.
+ *
+ * @throws AMQStoreException If the operation fails for any reason.
+ */
+ public void removeQueue(final AMQQueue queue) throws AMQStoreException
+ {
+ AMQShortString name = queue.getNameShortString();
+
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("public void removeQueue(AMQShortString name = " + name + "): called");
+ }
+
+ DatabaseEntry key = new DatabaseEntry();
+ EntryBinding keyBinding = new AMQShortStringTB();
+ keyBinding.objectToEntry(name, key);
+ try
+ {
+ OperationStatus status = _queueDb.delete(null, key);
+ if (status == OperationStatus.NOTFOUND)
+ {
+ throw new AMQStoreException("Queue " + name + " not found");
+ }
+ }
+ catch (DatabaseException e)
+ {
+ throw new AMQStoreException("Error writing deleting with name " + name + " from database: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Places a message onto a specified queue, in a given transaction.
+ *
+ * @param tx The transaction for the operation.
+ * @param queue The the queue to place the message on.
+ * @param messageId The message to enqueue.
+ *
+ * @throws AMQStoreException If the operation fails for any reason.
+ */
+ public void enqueueMessage(final com.sleepycat.je.Transaction tx, final TransactionLogResource queue, Long messageId) throws AMQStoreException
+ {
+ // _log.debug("public void enqueueMessage(Transaction tx = " + tx + ", AMQShortString name = " + name + ", Long messageId): called");
+
+ AMQShortString name = new AMQShortString(queue.getResourceName());
+
+ DatabaseEntry key = new DatabaseEntry();
+ EntryBinding keyBinding = new QueueEntryTB();
+ QueueEntryKey dd = new QueueEntryKey(name, messageId);
+ keyBinding.objectToEntry(dd, key);
+ DatabaseEntry value = new DatabaseEntry();
+ ByteBinding.byteToEntry((byte) 0, value);
+
+ try
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Enqueuing message " + messageId + " on queue " + name + " [Transaction" + tx + "]");
+ }
+ _deliveryDb.put(tx, key, value);
+ }
+ catch (DatabaseException e)
+ {
+ _log.error("Failed to enqueue: " + e.getMessage(), e);
+ throw new AMQStoreException("Error writing enqueued message with id " + messageId + " for queue " + name
+ + " to database", e);
+ }
+ }
+
+ /**
+ * Extracts a message from a specified queue, in a given transaction.
+ *
+ * @param tx The transaction for the operation.
+ * @param queue The name queue to take the message from.
+ * @param messageId The message to dequeue.
+ *
+ * @throws AMQStoreException If the operation fails for any reason, or if the specified message does not exist.
+ */
+ public void dequeueMessage(final com.sleepycat.je.Transaction tx, final TransactionLogResource queue, Long messageId) throws AMQStoreException
+ {
+ AMQShortString name = new AMQShortString(queue.getResourceName());
+
+ DatabaseEntry key = new DatabaseEntry();
+ EntryBinding keyBinding = new QueueEntryTB();
+ QueueEntryKey dd = new QueueEntryKey(name, messageId);
+
+ keyBinding.objectToEntry(dd, key);
+
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Dequeue message id " + messageId);
+ }
+
+ try
+ {
+
+ OperationStatus status = _deliveryDb.delete(tx, key);
+ if (status == OperationStatus.NOTFOUND)
+ {
+ throw new AMQStoreException("Unable to find message with id " + messageId + " on queue " + name);
+ }
+ else if (status != OperationStatus.SUCCESS)
+ {
+ throw new AMQStoreException("Unable to remove message with id " + messageId + " on queue " + name);
+ }
+
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Removed message " + messageId + ", " + name + " from delivery db");
+
+ }
+ }
+ catch (DatabaseException e)
+ {
+
+ _log.error("Failed to dequeue message " + messageId + ": " + e.getMessage(), e);
+ _log.error(tx);
+
+ throw new AMQStoreException("Error accessing database while dequeuing message: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Commits all operations performed within a given transaction.
+ *
+ * @param tx The transaction to commit all operations for.
+ *
+ * @throws AMQStoreException If the operation fails for any reason.
+ */
+ private StoreFuture commitTranImpl(final com.sleepycat.je.Transaction tx, boolean syncCommit) throws AMQStoreException
+ {
+ //if (_log.isDebugEnabled())
+ //{
+ // _log.debug("public void commitTranImpl() called with (Transaction=" + tx + ", syncCommit= "+ syncCommit + ")");
+ //}
+
+ if (tx == null)
+ {
+ throw new AMQStoreException("Fatal internal error: transactional is null at commitTran");
+ }
+
+ StoreFuture result;
+ try
+ {
+ result = commit(tx, syncCommit);
+
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("commitTranImpl completed for [Transaction:" + tx + "]");
+ }
+ }
+ catch (DatabaseException e)
+ {
+ throw new AMQStoreException("Error commit tx: " + e.getMessage(), e);
+ }
+
+ return result;
+ }
+
+ /**
+ * Abandons all operations performed within a given transaction.
+ *
+ * @param tx The transaction to abandon.
+ *
+ * @throws AMQStoreException If the operation fails for any reason.
+ */
+ public void abortTran(final com.sleepycat.je.Transaction tx) throws AMQStoreException
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("abortTran called for [Transaction:" + tx + "]");
+ }
+
+ try
+ {
+ tx.abort();
+ }
+ catch (DatabaseException e)
+ {
+ throw new AMQStoreException("Error aborting transaction: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Primarily for testing purposes.
+ *
+ * @param queueName
+ *
+ * @return a list of message ids for messages enqueued for a particular queue
+ */
+ List<Long> getEnqueuedMessages(AMQShortString queueName) throws AMQStoreException
+ {
+ Cursor cursor = null;
+ try
+ {
+ cursor = _deliveryDb.openCursor(null, null);
+
+ DatabaseEntry key = new DatabaseEntry();
+
+ QueueEntryKey dd = new QueueEntryKey(queueName, 0);
+
+ EntryBinding keyBinding = new QueueEntryTB();
+ keyBinding.objectToEntry(dd, key);
+
+ DatabaseEntry value = new DatabaseEntry();
+
+ LinkedList<Long> messageIds = new LinkedList<Long>();
+
+ OperationStatus status = cursor.getSearchKeyRange(key, value, LockMode.DEFAULT);
+ dd = (QueueEntryKey) keyBinding.entryToObject(key);
+
+ while ((status == OperationStatus.SUCCESS) && dd.getQueueName().equals(queueName))
+ {
+
+ messageIds.add(dd.getMessageId());
+ status = cursor.getNext(key, value, LockMode.DEFAULT);
+ if (status == OperationStatus.SUCCESS)
+ {
+ dd = (QueueEntryKey) keyBinding.entryToObject(key);
+ }
+ }
+
+ return messageIds;
+ }
+ catch (DatabaseException e)
+ {
+ throw new AMQStoreException("Database error: " + e.getMessage(), e);
+ }
+ finally
+ {
+ if (cursor != null)
+ {
+ try
+ {
+ cursor.close();
+ }
+ catch (DatabaseException e)
+ {
+ throw new AMQStoreException("Error closing cursor: " + e.getMessage(), e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Return a valid, currently unused message id.
+ *
+ * @return A fresh message id.
+ */
+ public Long getNewMessageId()
+ {
+ return _messageId.incrementAndGet();
+ }
+
+ /**
+ * Stores a chunk of message data.
+ *
+ * @param tx The transaction for the operation.
+ * @param messageId The message to store the data for.
+ * @param offset The offset of the data chunk in the message.
+ * @param contentBody The content of the data chunk.
+ *
+ * @throws AMQStoreException If the operation fails for any reason, or if the specified message does not exist.
+ */
+ protected void addContent(final com.sleepycat.je.Transaction tx, Long messageId, int offset,
+ ByteBuffer contentBody) throws AMQStoreException
+ {
+ DatabaseEntry key = new DatabaseEntry();
+ TupleBinding<MessageContentKey> keyBinding = new MessageContentKeyTB_5();
+ keyBinding.objectToEntry(new MessageContentKey_5(messageId, offset), key);
+ DatabaseEntry value = new DatabaseEntry();
+ TupleBinding<ByteBuffer> messageBinding = new ContentTB();
+ messageBinding.objectToEntry(contentBody, value);
+ try
+ {
+ OperationStatus status = _messageContentDb.put(tx, key, value);
+ if (status != OperationStatus.SUCCESS)
+ {
+ throw new AMQStoreException("Error adding content chunk offset" + offset + " for message id " + messageId + ": "
+ + status);
+ }
+
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Storing content chunk offset" + offset + " for message " + messageId + "[Transaction" + tx + "]");
+ }
+ }
+ catch (DatabaseException e)
+ {
+ throw new AMQStoreException("Error writing AMQMessage with id " + messageId + " to database: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Stores message meta-data.
+ *
+ * @param tx The transaction for the operation.
+ * @param messageId The message to store the data for.
+ * @param messageMetaData The message meta data to store.
+ *
+ * @throws AMQStoreException If the operation fails for any reason, or if the specified message does not exist.
+ */
+ private void storeMetaData(final com.sleepycat.je.Transaction tx, Long messageId, StorableMessageMetaData messageMetaData)
+ throws AMQStoreException
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("public void storeMetaData(Txn tx = " + tx + ", Long messageId = "
+ + messageId + ", MessageMetaData messageMetaData = " + messageMetaData + "): called");
+ }
+
+ DatabaseEntry key = new DatabaseEntry();
+ EntryBinding keyBinding = TupleBinding.getPrimitiveBinding(Long.class);
+ keyBinding.objectToEntry(messageId, key);
+ DatabaseEntry value = new DatabaseEntry();
+
+ TupleBinding messageBinding = _metaDataTupleBindingFactory.getInstance();
+ messageBinding.objectToEntry(messageMetaData, value);
+ try
+ {
+ _messageMetaDataDb.put(tx, key, value);
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Storing message metadata for message id " + messageId + "[Transaction" + tx + "]");
+ }
+ }
+ catch (DatabaseException e)
+ {
+ throw new AMQStoreException("Error writing message metadata with id " + messageId + " to database: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Retrieves message meta-data.
+ *
+ * @param messageId The message to get the meta-data for.
+ *
+ * @return The message meta data.
+ *
+ * @throws AMQStoreException If the operation fails for any reason, or if the specified message does not exist.
+ */
+ public StorableMessageMetaData getMessageMetaData(Long messageId) throws AMQStoreException
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("public MessageMetaData getMessageMetaData(Long messageId = "
+ + messageId + "): called");
+ }
+
+ DatabaseEntry key = new DatabaseEntry();
+ EntryBinding keyBinding = TupleBinding.getPrimitiveBinding(Long.class);
+ keyBinding.objectToEntry(messageId, key);
+ DatabaseEntry value = new DatabaseEntry();
+ TupleBinding messageBinding = _metaDataTupleBindingFactory.getInstance();
+
+ try
+ {
+ OperationStatus status = _messageMetaDataDb.get(null, key, value, LockMode.READ_UNCOMMITTED);
+ if (status != OperationStatus.SUCCESS)
+ {
+ throw new AMQStoreException("Metadata not found for message with id " + messageId);
+ }
+
+ StorableMessageMetaData mdd = (StorableMessageMetaData) messageBinding.entryToObject(value);
+
+ return mdd;
+ }
+ catch (DatabaseException e)
+ {
+ throw new AMQStoreException("Error reading message metadata for message with id " + messageId + ": " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Fills the provided ByteBuffer with as much content for the specified message as possible, starting
+ * from the specified offset in the message.
+ *
+ * @param messageId The message to get the data for.
+ * @param offset The offset of the data within the message.
+ * @param dst The destination of the content read back
+ *
+ * @return The number of bytes inserted into the destination
+ *
+ * @throws AMQStoreException If the operation fails for any reason, or if the specified message does not exist.
+ */
+ public int getContent(Long messageId, int offset, ByteBuffer dst) throws AMQStoreException
+ {
+ DatabaseEntry contentKeyEntry = new DatabaseEntry();
+
+ //Start from 0 offset and search for the starting chunk.
+ MessageContentKey_5 mck = new MessageContentKey_5(messageId, 0);
+ TupleBinding<MessageContentKey> contentKeyTupleBinding = new MessageContentKeyTB_5();
+ contentKeyTupleBinding.objectToEntry(mck, contentKeyEntry);
+ DatabaseEntry value = new DatabaseEntry();
+ TupleBinding<ByteBuffer> contentTupleBinding = new ContentTB();
+
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Message Id: " + messageId + " Getting content body from offset: " + offset);
+ }
+
+ int written = 0;
+ int seenSoFar = 0;
+
+ Cursor cursor = null;
+ try
+ {
+ cursor = _messageContentDb.openCursor(null, null);
+
+ OperationStatus status = cursor.getSearchKeyRange(contentKeyEntry, value, LockMode.READ_UNCOMMITTED);
+
+ while (status == OperationStatus.SUCCESS)
+ {
+ mck = (MessageContentKey_5) contentKeyTupleBinding.entryToObject(contentKeyEntry);
+ long id = mck.getMessageId();
+
+ if(id != messageId)
+ {
+ //we have exhausted all chunks for this message id, break
+ break;
+ }
+
+ int offsetInMessage = mck.getOffset();
+ ByteBuffer buf = (ByteBuffer) contentTupleBinding.entryToObject(value);
+
+ final int size = (int) buf.limit();
+
+ seenSoFar += size;
+
+ if(seenSoFar >= offset)
+ {
+ byte[] dataAsBytes = buf.array();
+
+ int posInArray = offset + written - offsetInMessage;
+ int count = size - posInArray;
+ if(count > dst.remaining())
+ {
+ count = dst.remaining();
+ }
+ dst.put(dataAsBytes,posInArray,count);
+ written+=count;
+
+ if(dst.remaining() == 0)
+ {
+ break;
+ }
+ }
+
+ status = cursor.getNext(contentKeyEntry, value, LockMode.RMW);
+ }
+
+ return written;
+ }
+ catch (DatabaseException e)
+ {
+ throw new AMQStoreException("Error writing AMQMessage with id " + messageId + " to database: " + e.getMessage(), e);
+ }
+ finally
+ {
+ if(cursor != null)
+ {
+ try
+ {
+ cursor.close();
+ }
+ catch (DatabaseException e)
+ {
+ throw new AMQStoreException("Error writing AMQMessage with id " + messageId + " to database: " + e.getMessage(), e);
+ }
+ }
+ }
+ }
+
+ public boolean isPersistent()
+ {
+ return true;
+ }
+
+ public <T extends StorableMessageMetaData> StoredMessage<T> addMessage(T metaData)
+ {
+ if(metaData.isPersistent())
+ {
+ return new StoredBDBMessage(getNewMessageId(), metaData);
+ }
+ else
+ {
+ return new StoredMemoryMessage(getNewMessageId(), metaData);
+ }
+ }
+
+
+ //protected getters for the TupleBindingFactories
+
+ protected QueueTupleBindingFactory getQueueTupleBindingFactory()
+ {
+ return _queueTupleBindingFactory;
+ }
+
+ protected BindingTupleBindingFactory getBindingTupleBindingFactory()
+ {
+ return _bindingTupleBindingFactory;
+ }
+
+ protected MessageMetaDataTupleBindingFactory getMetaDataTupleBindingFactory()
+ {
+ return _metaDataTupleBindingFactory;
+ }
+
+ //Package getters for the various databases used by the Store
+
+ Database getMetaDataDb()
+ {
+ return _messageMetaDataDb;
+ }
+
+ Database getContentDb()
+ {
+ return _messageContentDb;
+ }
+
+ Database getQueuesDb()
+ {
+ return _queueDb;
+ }
+
+ Database getDeliveryDb()
+ {
+ return _deliveryDb;
+ }
+
+ Database getExchangesDb()
+ {
+ return _exchangeDb;
+ }
+
+ Database getBindingsDb()
+ {
+ return _queueBindingsDb;
+ }
+
+ void visitMetaDataDb(DatabaseVisitor visitor) throws DatabaseException, AMQStoreException
+ {
+ visitDatabase(_messageMetaDataDb, visitor);
+ }
+
+ void visitContentDb(DatabaseVisitor visitor) throws DatabaseException, AMQStoreException
+ {
+ visitDatabase(_messageContentDb, visitor);
+ }
+
+ void visitQueues(DatabaseVisitor visitor) throws DatabaseException, AMQStoreException
+ {
+ visitDatabase(_queueDb, visitor);
+ }
+
+ void visitDelivery(DatabaseVisitor visitor) throws DatabaseException, AMQStoreException
+ {
+ visitDatabase(_deliveryDb, visitor);
+ }
+
+ void visitExchanges(DatabaseVisitor visitor) throws DatabaseException, AMQStoreException
+ {
+ visitDatabase(_exchangeDb, visitor);
+ }
+
+ void visitBindings(DatabaseVisitor visitor) throws DatabaseException, AMQStoreException
+ {
+ visitDatabase(_queueBindingsDb, visitor);
+ }
+
+ /**
+ * Generic visitDatabase allows iteration through the specified database.
+ *
+ * @param database The database to visit
+ * @param visitor The visitor to give each entry to.
+ *
+ * @throws DatabaseException If there is a problem with the Database structure
+ * @throws AMQStoreException If there is a problem with the Database contents
+ */
+ void visitDatabase(Database database, DatabaseVisitor visitor) throws DatabaseException, AMQStoreException
+ {
+ Cursor cursor = database.openCursor(null, null);
+
+ try
+ {
+ DatabaseEntry key = new DatabaseEntry();
+ DatabaseEntry value = new DatabaseEntry();
+ while (cursor.getNext(key, value, LockMode.RMW) == OperationStatus.SUCCESS)
+ {
+ visitor.visit(key, value);
+ }
+ }
+ finally
+ {
+ if (cursor != null)
+ {
+ cursor.close();
+ }
+ }
+ }
+
+ private StoreFuture commit(com.sleepycat.je.Transaction tx, boolean syncCommit) throws DatabaseException
+ {
+ // _log.debug("void commit(Transaction tx = " + tx + ", sync = " + syncCommit + "): called");
+
+ tx.commitNoSync();
+
+ BDBCommitFuture commitFuture = new BDBCommitFuture(_commitThread, tx, syncCommit);
+ commitFuture.commit();
+
+ return commitFuture;
+ }
+
+ public void startCommitThread()
+ {
+ _commitThread.start();
+ }
+
+ private static final class BDBCommitFuture implements StoreFuture
+ {
+ // private static final Logger _log = Logger.getLogger(BDBCommitFuture.class);
+
+ private final CommitThread _commitThread;
+ private final com.sleepycat.je.Transaction _tx;
+ private DatabaseException _databaseException;
+ private boolean _complete;
+ private boolean _syncCommit;
+
+ public BDBCommitFuture(CommitThread commitThread, com.sleepycat.je.Transaction tx, boolean syncCommit)
+ {
+ // _log.debug("public Commit(CommitThread commitThread = " + commitThread + ", Transaction tx = " + tx
+ // + "): called");
+
+ _commitThread = commitThread;
+ _tx = tx;
+ _syncCommit = syncCommit;
+ }
+
+ public synchronized void complete()
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("public synchronized void complete(): called (Transaction = " + _tx + ")");
+ }
+
+ _complete = true;
+
+ notifyAll();
+ }
+
+ public synchronized void abort(DatabaseException databaseException)
+ {
+ // _log.debug("public synchronized void abort(DatabaseException databaseException = " + databaseException
+ // + "): called");
+
+ _complete = true;
+ _databaseException = databaseException;
+
+ notifyAll();
+ }
+
+ public void commit() throws DatabaseException
+ {
+ //_log.debug("public void commit(): called");
+
+ _commitThread.addJob(this);
+
+ if(!_syncCommit)
+ {
+ _log.debug("CommitAsync was requested, returning immediately.");
+ return;
+ }
+
+ synchronized (BDBCommitFuture.this)
+ {
+ while (!_complete)
+ {
+ try
+ {
+ wait(250);
+ }
+ catch (InterruptedException e)
+ {
+ // _log.error("Unexpected thread interruption: " + e, e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ // _log.debug("Commit completed, _databaseException = " + _databaseException);
+
+ if (_databaseException != null)
+ {
+ throw _databaseException;
+ }
+ }
+ }
+
+ public synchronized boolean isComplete()
+ {
+ return _complete;
+ }
+
+ public void waitForCompletion()
+ {
+ while (!isComplete())
+ {
+ try
+ {
+ wait(250);
+ }
+ catch (InterruptedException e)
+ {
+ //TODO Should we ignore, or throw a 'StoreException'?
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Implements a thread which batches and commits a queue of {@link BDBCommitFuture} operations. The commit operations
+ * themselves are responsible for adding themselves to the queue and waiting for the commit to happen before
+ * continuing, but it is the responsibility of this thread to tell the commit operations when they have been
+ * completed by calling back on their {@link BDBCommitFuture#complete()} and {@link BDBCommitFuture#abort} methods.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption> <tr><th> Responsibilities <th> Collarations </table>
+ */
+ private class CommitThread extends Thread
+ {
+ // private final Logger _log = Logger.getLogger(CommitThread.class);
+
+ private final AtomicBoolean _stopped = new AtomicBoolean(false);
+ private final AtomicReference<Queue<BDBCommitFuture>> _jobQueue = new AtomicReference<Queue<BDBCommitFuture>>(new ConcurrentLinkedQueue<BDBCommitFuture>());
+ private final CheckpointConfig _config = new CheckpointConfig();
+ private final Object _lock = new Object();
+
+ public CommitThread(String name)
+ {
+ super(name);
+ _config.setForce(true);
+
+ }
+
+ public void run()
+ {
+ while (!_stopped.get())
+ {
+ synchronized (_lock)
+ {
+ while (!_stopped.get() && !hasJobs())
+ {
+ try
+ {
+ // RHM-7 Periodically wake up and check, just in case we
+ // missed a notification. Don't want to lock the broker hard.
+ _lock.wait(250);
+ }
+ catch (InterruptedException e)
+ {
+ // _log.info(getName() + " interrupted. ");
+ }
+ }
+ }
+ processJobs();
+ }
+ }
+
+ private void processJobs()
+ {
+ // _log.debug("private void processJobs(): called");
+
+ // we replace the old queue atomically with a new one and this avoids any need to
+ // copy elements out of the queue
+ Queue<BDBCommitFuture> jobs = _jobQueue.getAndSet(new ConcurrentLinkedQueue<BDBCommitFuture>());
+
+ try
+ {
+ // _environment.checkpoint(_config);
+ _environment.sync();
+
+ for (BDBCommitFuture commit : jobs)
+ {
+ commit.complete();
+ }
+ }
+ catch (DatabaseException e)
+ {
+ for (BDBCommitFuture commit : jobs)
+ {
+ commit.abort(e);
+ }
+ }
+
+ }
+
+ private boolean hasJobs()
+ {
+ return !_jobQueue.get().isEmpty();
+ }
+
+ public void addJob(BDBCommitFuture commit)
+ {
+ synchronized (_lock)
+ {
+ _jobQueue.get().add(commit);
+ _lock.notifyAll();
+ }
+ }
+
+ public void close()
+ {
+ synchronized (_lock)
+ {
+ _stopped.set(true);
+ _lock.notifyAll();
+ }
+ }
+ }
+
+
+ private class StoredBDBMessage implements StoredMessage
+ {
+
+ private final long _messageId;
+ private volatile SoftReference<StorableMessageMetaData> _metaDataRef;
+ private com.sleepycat.je.Transaction _txn;
+
+ StoredBDBMessage(long messageId, StorableMessageMetaData metaData)
+ {
+ this(messageId, metaData, true);
+ }
+
+
+ StoredBDBMessage(long messageId,
+ StorableMessageMetaData metaData, boolean persist)
+ {
+ try
+ {
+ _messageId = messageId;
+
+ _metaDataRef = new SoftReference<StorableMessageMetaData>(metaData);
+ if(persist)
+ {
+ _txn = _environment.beginTransaction(null, null);
+ storeMetaData(_txn, messageId, metaData);
+ }
+ }
+ catch (DatabaseException e)
+ {
+ throw new RuntimeException(e);
+ }
+ catch (AMQStoreException e)
+ {
+ throw new RuntimeException(e);
+ }
+
+ }
+
+ public StorableMessageMetaData getMetaData()
+ {
+ StorableMessageMetaData metaData = _metaDataRef.get();
+ if(metaData == null)
+ {
+ try
+ {
+ metaData = BDBMessageStore.this.getMessageMetaData(_messageId);
+ }
+ catch (AMQStoreException e)
+ {
+ throw new RuntimeException(e);
+ }
+ _metaDataRef = new SoftReference<StorableMessageMetaData>(metaData);
+ }
+
+ return metaData;
+ }
+
+ public long getMessageNumber()
+ {
+ return _messageId;
+ }
+
+ public void addContent(int offsetInMessage, java.nio.ByteBuffer src)
+ {
+ try
+ {
+ BDBMessageStore.this.addContent(_txn, _messageId, offsetInMessage, src);
+ }
+ catch (AMQStoreException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public int getContent(int offsetInMessage, java.nio.ByteBuffer dst)
+ {
+ try
+ {
+ return BDBMessageStore.this.getContent(_messageId, offsetInMessage, dst);
+ }
+ catch (AMQStoreException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public StoreFuture flushToStore()
+ {
+ try
+ {
+ if(_txn != null)
+ {
+ //if(_log.isDebugEnabled())
+ //{
+ // _log.debug("Flushing message " + _messageId + " to store");
+ //}
+ BDBMessageStore.this.commitTranImpl(_txn, true);
+ }
+ }
+ catch (AMQStoreException e)
+ {
+ throw new RuntimeException(e);
+ }
+ finally
+ {
+ _txn = null;
+ }
+ return IMMEDIATE_FUTURE;
+ }
+
+ public void remove()
+ {
+ flushToStore();
+ try
+ {
+ BDBMessageStore.this.removeMessage(_messageId);
+ }
+ catch (AMQStoreException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private class BDBTransaction implements Transaction
+ {
+ private com.sleepycat.je.Transaction _txn;
+
+ private BDBTransaction()
+ {
+ try
+ {
+ _txn = _environment.beginTransaction(null, null);
+ }
+ catch (DatabaseException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void enqueueMessage(TransactionLogResource queue, Long messageId) throws AMQStoreException
+ {
+ BDBMessageStore.this.enqueueMessage(_txn, queue, messageId);
+ }
+
+ public void dequeueMessage(TransactionLogResource queue, Long messageId) throws AMQStoreException
+ {
+ BDBMessageStore.this.dequeueMessage(_txn, queue, messageId);
+
+ }
+
+ public void commitTran() throws AMQStoreException
+ {
+ BDBMessageStore.this.commitTranImpl(_txn, true);
+ }
+
+ public StoreFuture commitTranAsync() throws AMQStoreException
+ {
+ return BDBMessageStore.this.commitTranImpl(_txn, false);
+ }
+
+ public void abortTran() throws AMQStoreException
+ {
+ BDBMessageStore.this.abortTran(_txn);
+ }
+ }
+
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBStoreUpgrade.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBStoreUpgrade.java
new file mode 100644
index 0000000000..211c025dcd
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBStoreUpgrade.java
@@ -0,0 +1,1125 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.berkeleydb;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.store.berkeleydb.BDBMessageStore;
+import org.apache.qpid.server.store.berkeleydb.BindingKey;
+import org.apache.qpid.server.store.berkeleydb.ContentTB;
+import org.apache.qpid.server.store.berkeleydb.DatabaseVisitor;
+import org.apache.qpid.server.store.berkeleydb.ExchangeTB;
+import org.apache.qpid.server.store.berkeleydb.keys.MessageContentKey_4;
+import org.apache.qpid.server.store.berkeleydb.keys.MessageContentKey_5;
+import org.apache.qpid.server.store.berkeleydb.records.ExchangeRecord;
+import org.apache.qpid.server.store.berkeleydb.records.QueueRecord;
+import org.apache.qpid.server.store.berkeleydb.tuples.MessageContentKeyTB_4;
+import org.apache.qpid.server.store.berkeleydb.tuples.MessageContentKeyTB_5;
+import org.apache.qpid.server.store.berkeleydb.tuples.QueueEntryTB;
+import org.apache.qpid.server.logging.actors.CurrentActor;
+import org.apache.qpid.server.logging.actors.BrokerActor;
+import org.apache.qpid.server.logging.NullRootMessageLogger;
+import org.apache.qpid.server.message.MessageMetaData;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.AMQStoreException;
+import org.apache.qpid.util.FileUtils;
+import org.apache.commons.cli.PosixParser;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.ParseException;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionBuilder;
+
+import java.io.File;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.FileNotFoundException;
+import java.nio.ByteBuffer;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map.Entry;
+
+import com.sleepycat.je.DatabaseEntry;
+import com.sleepycat.je.DatabaseException;
+import com.sleepycat.je.Database;
+import com.sleepycat.je.Environment;
+import com.sleepycat.je.EnvironmentConfig;
+import com.sleepycat.bind.tuple.TupleBinding;
+
+/**
+ * This is a simple BerkeleyDB Store upgrade tool that will upgrade a V4 Store to a V5 Store.
+ *
+ * Currently upgrade is fixed from v4 -> v5
+ *
+ * Improvments:
+ * - Add List BDBMessageStore.getDatabases(); This can the be iterated to guard against new DBs being added.
+ * - A version in the store would allow automated upgrade or later with more available versions interactive upgrade.
+ * - Add process logging and disable all Store and Qpid logging.
+ */
+public class BDBStoreUpgrade
+{
+ private static final Logger _logger = LoggerFactory.getLogger(BDBStoreUpgrade.class);
+ /** The Store Directory that needs upgrading */
+ File _fromDir;
+ /** The Directory that will be made to contain the upgraded store */
+ File _toDir;
+ /** The Directory that will be made to backup the original store if required */
+ File _backupDir;
+
+ /** The Old Store */
+ BDBMessageStore _oldMessageStore;
+ /** The New Store */
+ BDBMessageStore _newMessageStore;
+ /** The file ending that is used by BDB Store Files */
+ private static final String BDB_FILE_ENDING = ".jdb";
+
+ static final Options _options = new Options();
+ static CommandLine _commandLine;
+ private boolean _interactive;
+ private boolean _force;
+
+ private static final String VERSION = "3.0";
+ private static final String USER_ABORTED_PROCESS = "User aborted process";
+ private static final int LOWEST_SUPPORTED_STORE_VERSION = 4;
+ private static final String PREVIOUS_STORE_VERSION_UNSUPPORTED = "Store upgrade from version {0} is not supported."
+ + " You must first run the previous store upgrade tool.";
+ private static final String FOLLOWING_STORE_VERSION_UNSUPPORTED = "Store version {0} is newer than this tool supports. "
+ + "You must use a newer version of the store upgrade tool";
+ private static final String STORE_ALREADY_UPGRADED = "Store has already been upgraded to version {0}.";
+
+ private static final String OPTION_INPUT_SHORT = "i";
+ private static final String OPTION_INPUT = "input";
+ private static final String OPTION_OUTPUT_SHORT = "o";
+ private static final String OPTION_OUTPUT = "output";
+ private static final String OPTION_BACKUP_SHORT = "b";
+ private static final String OPTION_BACKUP = "backup";
+ private static final String OPTION_QUIET_SHORT = "q";
+ private static final String OPTION_QUIET = "quiet";
+ private static final String OPTION_FORCE_SHORT = "f";
+ private static final String OPTION_FORCE = "force";
+ private boolean _inplace = false;
+
+ public BDBStoreUpgrade(String fromDir, String toDir, String backupDir, boolean interactive, boolean force)
+ {
+ _interactive = interactive;
+ _force = force;
+
+ _fromDir = new File(fromDir);
+ if (!_fromDir.exists())
+ {
+ throw new IllegalArgumentException("BDBStore path '" + fromDir + "' could not be read. "
+ + "Ensure the path is correct and that the permissions are correct.");
+ }
+
+ if (!isDirectoryAStoreDir(_fromDir))
+ {
+ throw new IllegalArgumentException("Specified directory '" + fromDir + "' does not contain a valid BDBMessageStore.");
+ }
+
+ if (toDir == null)
+ {
+ _inplace = true;
+ _toDir = new File(fromDir+"-Inplace");
+ }
+ else
+ {
+ _toDir = new File(toDir);
+ }
+
+ if (_toDir.exists())
+ {
+ if (_interactive)
+ {
+ if (toDir == null)
+ {
+ System.out.println("Upgrading in place:" + fromDir);
+ }
+ else
+ {
+ System.out.println("Upgrade destination: '" + toDir + "'");
+ }
+
+ if (userInteract("Upgrade destination exists do you wish to replace it?"))
+ {
+ if (!FileUtils.delete(_toDir, true))
+ {
+ throw new IllegalArgumentException("Unable to remove upgrade destination '" + _toDir + "'");
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException("Upgrade destination '" + _toDir + "' already exists. ");
+ }
+ }
+ else
+ {
+ if (_force)
+ {
+ if (!FileUtils.delete(_toDir, true))
+ {
+ throw new IllegalArgumentException("Unable to remove upgrade destination '" + _toDir + "'");
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException("Upgrade destination '" + _toDir + "' already exists. ");
+ }
+ }
+ }
+
+ if (!_toDir.mkdirs())
+ {
+ throw new IllegalArgumentException("Upgrade destination '" + _toDir + "' could not be created. "
+ + "Ensure the path is correct and that the permissions are correct.");
+ }
+
+ if (backupDir != null)
+ {
+ if (backupDir.equals(""))
+ {
+ _backupDir = new File(_fromDir.getAbsolutePath().toString() + "-Backup");
+ }
+ else
+ {
+ _backupDir = new File(backupDir);
+ }
+ }
+ else
+ {
+ _backupDir = null;
+ }
+ }
+
+ private static String ANSWER_OPTIONS = " Yes/No/Abort? ";
+ private static String ANSWER_NO = "no";
+ private static String ANSWER_N = "n";
+ private static String ANSWER_YES = "yes";
+ private static String ANSWER_Y = "y";
+ private static String ANSWER_ABORT = "abort";
+ private static String ANSWER_A = "a";
+
+ /**
+ * Interact with the user via System.in and System.out. If the user wishes to Abort then a RuntimeException is thrown.
+ * Otherwise the method will return based on their response true=yes false=no.
+ *
+ * @param message Message to print out
+ *
+ * @return boolean response from user if they wish to proceed
+ */
+ private boolean userInteract(String message)
+ {
+ System.out.print(message + ANSWER_OPTIONS);
+ BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
+
+ String input = "";
+ try
+ {
+ input = br.readLine();
+ }
+ catch (IOException e)
+ {
+ input = "";
+ }
+
+ if (input.equalsIgnoreCase(ANSWER_Y) || input.equalsIgnoreCase(ANSWER_YES))
+ {
+ return true;
+ }
+ else
+ {
+ if (input.equalsIgnoreCase(ANSWER_N) || input.equalsIgnoreCase(ANSWER_NO))
+ {
+ return false;
+ }
+ else
+ {
+ if (input.equalsIgnoreCase(ANSWER_A) || input.equalsIgnoreCase(ANSWER_ABORT))
+ {
+ throw new RuntimeException(USER_ABORTED_PROCESS);
+ }
+ }
+ }
+
+ return userInteract(message);
+ }
+
+ /**
+ * Upgrade a Store of a specified version to the latest version.
+ *
+ * @param version the version of the current store
+ *
+ * @throws Exception
+ */
+ public void upgradeFromVersion(int version) throws Exception
+ {
+ upgradeFromVersion(version, _fromDir, _toDir, _backupDir, _force,
+ _inplace);
+ }
+
+ /**
+ * Upgrade a Store of a specified version to the latest version.
+ *
+ * @param version the version of the current store
+ * @param fromDir the directory with the old Store
+ * @param toDir the directrory to hold the newly Upgraded Store
+ * @param backupDir the directrory to backup to if required
+ * @param force suppress all questions
+ * @param inplace replace the from dir with the upgraded result in toDir
+ *
+ * @throws Exception due to Virtualhost/MessageStore.close() being
+ * rather poor at exception handling
+ * @throws DatabaseException if there is a problem with the store formats
+ * @throws AMQException if there is an issue creating Qpid data structures
+ */
+ public void upgradeFromVersion(int version, File fromDir, File toDir,
+ File backupDir, boolean force,
+ boolean inplace) throws Exception
+ {
+ _logger.info("Located store to upgrade at '" + fromDir + "'");
+
+ // Verify user has created a backup, giving option to perform backup
+ if (_interactive)
+ {
+ if (!userInteract("Have you performed a DB backup of this store."))
+ {
+ File backup;
+ if (backupDir == null)
+ {
+ backup = new File(fromDir.getAbsolutePath().toString() + "-Backup");
+ }
+ else
+ {
+ backup = backupDir;
+ }
+
+ if (userInteract("Do you wish to perform a DB backup now? " +
+ "(Store will be backed up to '" + backup.getName() + "')"))
+ {
+ performDBBackup(fromDir, backup, force);
+ }
+ else
+ {
+ if (!userInteract("Are you sure wish to proceed with DB migration without backup? " +
+ "(For more details of the consequences check the Qpid/BDB Message Store Wiki)."))
+ {
+ throw new IllegalArgumentException("Upgrade stopped at user request as no DB Backup performed.");
+ }
+ }
+ }
+ else
+ {
+ if (!inplace)
+ {
+ _logger.info("Upgrade will create a new store at '" + toDir + "'");
+ }
+
+ _logger.info("Using the contents in the Message Store '" + fromDir + "'");
+
+ if (!userInteract("Do you wish to proceed?"))
+ {
+ throw new IllegalArgumentException("Upgrade stopped as did not wish to proceed");
+ }
+ }
+ }
+ else
+ {
+ if (backupDir != null)
+ {
+ performDBBackup(fromDir, backupDir, force);
+ }
+ }
+
+ CurrentActor.set(new BrokerActor(new NullRootMessageLogger()));
+
+ //Create a new messageStore
+ _newMessageStore = new BDBMessageStore();
+ _newMessageStore.configure(toDir, false);
+ _newMessageStore.start();
+
+ try
+ {
+ //Load the old MessageStore
+ switch (version)
+ {
+ default:
+ case 4:
+ _oldMessageStore = new BDBMessageStore(4);
+ _oldMessageStore.configure(fromDir, true);
+ _oldMessageStore.start();
+ upgradeFromVersion_4();
+ break;
+ case 3:
+ case 2:
+ case 1:
+ throw new IllegalArgumentException(MessageFormat.format(PREVIOUS_STORE_VERSION_UNSUPPORTED,
+ new Object[] { Integer.toString(version) }));
+ }
+ }
+ finally
+ {
+ _newMessageStore.close();
+ if (_oldMessageStore != null)
+ {
+ _oldMessageStore.close();
+ }
+ // if we are running inplace then swap fromDir and toDir
+ if (inplace)
+ {
+ // Remove original copy
+ if (FileUtils.delete(fromDir, true))
+ {
+ // Rename upgraded store
+ toDir.renameTo(fromDir);
+ }
+ else
+ {
+ throw new RuntimeException("Unable to upgrade inplace as " +
+ "unable to delete source '"
+ +fromDir+"', Store upgrade " +
+ "successfully performed to :"
+ +toDir);
+ }
+ }
+ }
+ }
+
+ private void upgradeFromVersion_4() throws AMQException, DatabaseException
+ {
+ _logger.info("Starting store upgrade from version 4");
+
+ //Migrate _exchangeDb;
+ _logger.info("Exchanges");
+
+ moveContents(_oldMessageStore.getExchangesDb(), _newMessageStore.getExchangesDb(), "Exchange");
+
+ final List<AMQShortString> topicExchanges = new ArrayList<AMQShortString>();
+ final TupleBinding exchangeTB = new ExchangeTB();
+
+ DatabaseVisitor exchangeListVisitor = new DatabaseVisitor()
+ {
+ public void visit(DatabaseEntry key, DatabaseEntry value) throws DatabaseException
+ {
+ ExchangeRecord exchangeRec = (ExchangeRecord) exchangeTB.entryToObject(value);
+ AMQShortString type = exchangeRec.getType();
+
+ if (ExchangeDefaults.TOPIC_EXCHANGE_CLASS.equals(type))
+ {
+ topicExchanges.add(exchangeRec.getNameShortString());
+ }
+ }
+ };
+ _oldMessageStore.visitExchanges(exchangeListVisitor);
+
+
+ //Migrate _queueBindingsDb;
+ _logger.info("Queue Bindings");
+ moveContents(_oldMessageStore.getBindingsDb(), _newMessageStore.getBindingsDb(), "Queue Binding");
+
+ //Inspect the bindings to gather a list of queues which are probably durable subscriptions, i.e. those
+ //which have a colon in their name and are bound to the Topic exchanges above
+ final List<AMQShortString> durableSubQueues = new ArrayList<AMQShortString>();
+ final TupleBinding<BindingKey> bindingTB = _oldMessageStore.getBindingTupleBindingFactory().getInstance();
+
+ DatabaseVisitor durSubQueueListVisitor = new DatabaseVisitor()
+ {
+ public void visit(DatabaseEntry key, DatabaseEntry value) throws DatabaseException
+ {
+ BindingKey bindingRec = (BindingKey) bindingTB.entryToObject(key);
+ AMQShortString queueName = bindingRec.getQueueName();
+ AMQShortString exchangeName = bindingRec.getExchangeName();
+
+ if (topicExchanges.contains(exchangeName) && queueName.asString().contains(":"))
+ {
+ durableSubQueues.add(queueName);
+ }
+ }
+ };
+ _oldMessageStore.visitBindings(durSubQueueListVisitor);
+
+
+ //Migrate _queueDb;
+ _logger.info("Queues");
+
+ // hold the list of existing queue names
+ final List<AMQShortString> existingQueues = new ArrayList<AMQShortString>();
+
+ final TupleBinding<QueueRecord> queueTupleBinding = _oldMessageStore.getQueueTupleBindingFactory().getInstance();
+
+ DatabaseVisitor queueVisitor = new DatabaseVisitor()
+ {
+ public void visit(DatabaseEntry key, DatabaseEntry value) throws AMQStoreException
+ {
+ QueueRecord queueRec = (QueueRecord) queueTupleBinding.entryToObject(value);
+ AMQShortString queueName = queueRec.getNameShortString();
+
+ //if the queue name is in the gathered list then set its exclusivity true
+ if (durableSubQueues.contains(queueName))
+ {
+ _logger.info("Marking as possible DurableSubscription backing queue: " + queueName);
+ queueRec.setExclusive(true);
+ }
+
+ //The simple call to createQueue with the QueueRecord object is sufficient for a v2->v3 upgrade as
+ //the extra 'exclusive' property in v3 will be defaulted to false in the record creation.
+ _newMessageStore.createQueue(queueRec);
+
+ _count++;
+ existingQueues.add(queueName);
+ }
+ };
+ _oldMessageStore.visitQueues(queueVisitor);
+
+ logCount(queueVisitor.getVisitedCount(), "Queue");
+
+
+ // Look for persistent messages stored for non-durable queues
+ _logger.info("Checking for messages previously sent to non-durable queues");
+
+ // track all message delivery to existing queues
+ final HashSet<Long> queueMessages = new HashSet<Long>();
+
+ // hold all non existing queues and their messages IDs
+ final HashMap<String, HashSet<Long>> phantomMessageQueues = new HashMap<String, HashSet<Long>>();
+
+ // delivery DB visitor to check message delivery and identify non existing queues
+ final QueueEntryTB queueEntryTB = new QueueEntryTB();
+ DatabaseVisitor messageDeliveryCheckVisitor = new DatabaseVisitor()
+ {
+ public void visit(DatabaseEntry key, DatabaseEntry value) throws DatabaseException
+ {
+ QueueEntryKey entryKey = (QueueEntryKey) queueEntryTB.entryToObject(key);
+ Long messageId = entryKey.getMessageId();
+ AMQShortString queueName = entryKey.getQueueName();
+ if (!existingQueues.contains(queueName))
+ {
+ String name = queueName.asString();
+ HashSet<Long> messages = phantomMessageQueues.get(name);
+ if (messages == null)
+ {
+ messages = new HashSet<Long>();
+ phantomMessageQueues.put(name, messages);
+ }
+ messages.add(messageId);
+ _count++;
+ }
+ else
+ {
+ queueMessages.add(messageId);
+ }
+ }
+ };
+ _oldMessageStore.visitDelivery(messageDeliveryCheckVisitor);
+
+ if (phantomMessageQueues.isEmpty())
+ {
+ _logger.info("No such messages were found");
+ }
+ else
+ {
+ _logger.info("Found " + messageDeliveryCheckVisitor.getVisitedCount()+ " such messages in total");
+
+ for (Entry<String, HashSet<Long>> phantomQueue : phantomMessageQueues.entrySet())
+ {
+ String queueName = phantomQueue.getKey();
+ HashSet<Long> messages = phantomQueue.getValue();
+
+ _logger.info(MessageFormat.format("There are {0} messages which were previously delivered to non-durable queue ''{1}''",messages.size(), queueName));
+
+ boolean createQueue;
+ if(!_interactive)
+ {
+ createQueue = true;
+ _logger.info("Running in batch-mode, marking queue as durable to ensure retention of the messages.");
+ }
+ else
+ {
+ createQueue = userInteract("Do you want to make this queue durable?\n"
+ + "NOTE: Answering No will result in these messages being discarded!");
+ }
+
+ if (createQueue)
+ {
+ for (Long messageId : messages)
+ {
+ queueMessages.add(messageId);
+ }
+ AMQShortString name = new AMQShortString(queueName);
+ existingQueues.add(name);
+ QueueRecord record = new QueueRecord(name, null, false, null);
+ _newMessageStore.createQueue(record);
+ }
+ }
+ }
+
+
+ //Migrate _messageMetaDataDb;
+ _logger.info("Message MetaData");
+
+ final Database newMetaDataDB = _newMessageStore.getMetaDataDb();
+ final TupleBinding<Object> oldMetaDataTupleBinding = _oldMessageStore.getMetaDataTupleBindingFactory().getInstance();
+ final TupleBinding<Object> newMetaDataTupleBinding = _newMessageStore.getMetaDataTupleBindingFactory().getInstance();
+
+ DatabaseVisitor metaDataVisitor = new DatabaseVisitor()
+ {
+ public void visit(DatabaseEntry key, DatabaseEntry value) throws DatabaseException
+ {
+ _count++;
+ MessageMetaData metaData = (MessageMetaData) oldMetaDataTupleBinding.entryToObject(value);
+
+ // get message id
+ Long messageId = TupleBinding.getPrimitiveBinding(Long.class).entryToObject(key);
+
+ // ONLY copy data if message is delivered to existing queue
+ if (!queueMessages.contains(messageId))
+ {
+ return;
+ }
+ DatabaseEntry newValue = new DatabaseEntry();
+ newMetaDataTupleBinding.objectToEntry(metaData, newValue);
+
+ newMetaDataDB.put(null, key, newValue);
+ }
+ };
+ _oldMessageStore.visitMetaDataDb(metaDataVisitor);
+
+ logCount(metaDataVisitor.getVisitedCount(), "Message MetaData");
+
+
+ //Migrate _messageContentDb;
+ _logger.info("Message Contents");
+ final Database newContentDB = _newMessageStore.getContentDb();
+
+ final TupleBinding<MessageContentKey> oldContentKeyTupleBinding = new MessageContentKeyTB_4();
+ final TupleBinding<MessageContentKey> newContentKeyTupleBinding = new MessageContentKeyTB_5();
+ final TupleBinding contentTB = new ContentTB();
+
+ DatabaseVisitor contentVisitor = new DatabaseVisitor()
+ {
+ long _prevMsgId = -1; //Initialise to invalid value
+ int _bytesSeenSoFar = 0;
+
+ public void visit(DatabaseEntry key, DatabaseEntry value) throws DatabaseException
+ {
+ _count++;
+
+ //determine the msgId of the current entry
+ MessageContentKey_4 contentKey = (MessageContentKey_4) oldContentKeyTupleBinding.entryToObject(key);
+ long msgId = contentKey.getMessageId();
+
+ // ONLY copy data if message is delivered to existing queue
+ if (!queueMessages.contains(msgId))
+ {
+ return;
+ }
+ //if this is a new message, restart the byte offset count.
+ if(_prevMsgId != msgId)
+ {
+ _bytesSeenSoFar = 0;
+ }
+
+ //determine the content size
+ ByteBuffer content = (ByteBuffer) contentTB.entryToObject(value);
+ int contentSize = content.limit();
+
+ //create the new key: id + previously seen data count
+ MessageContentKey_5 newKey = new MessageContentKey_5(msgId, _bytesSeenSoFar);
+ DatabaseEntry newKeyEntry = new DatabaseEntry();
+ newContentKeyTupleBinding.objectToEntry(newKey, newKeyEntry);
+
+ DatabaseEntry newValueEntry = new DatabaseEntry();
+ contentTB.objectToEntry(content, newValueEntry);
+
+ newContentDB.put(null, newKeyEntry, newValueEntry);
+
+ _prevMsgId = msgId;
+ _bytesSeenSoFar += contentSize;
+ }
+ };
+ _oldMessageStore.visitContentDb(contentVisitor);
+
+ logCount(contentVisitor.getVisitedCount(), "Message Content");
+
+
+ //Migrate _deliveryDb;
+ _logger.info("Delivery Records");
+ final Database deliveryDb =_newMessageStore.getDeliveryDb();
+ DatabaseVisitor deliveryDbVisitor = new DatabaseVisitor()
+ {
+
+ public void visit(DatabaseEntry key, DatabaseEntry value) throws DatabaseException
+ {
+ _count++;
+
+ // get message id from entry key
+ QueueEntryKey entryKey = (QueueEntryKey) queueEntryTB.entryToObject(key);
+ AMQShortString queueName = entryKey.getQueueName();
+
+ // ONLY copy data if message queue exists
+ if (existingQueues.contains(queueName))
+ {
+ deliveryDb.put(null, key, value);
+ }
+ }
+ };
+ _oldMessageStore.visitDelivery(deliveryDbVisitor);
+ logCount(contentVisitor.getVisitedCount(), "Delivery Record");
+ }
+
+ /**
+ * Log the specified count for item in a user friendly way.
+ *
+ * @param count of items to log
+ * @param item description of what is being logged.
+ */
+ private void logCount(int count, String item)
+ {
+ _logger.info(" " + count + " " + item + " " + (count == 1 ? "entry" : "entries"));
+ }
+
+ /**
+ * @param oldDatabase The old MessageStoreDB to perform the visit on
+ * @param newDatabase The new MessageStoreDB to copy the data to.
+ * @param contentName The string name of the content for display purposes.
+ *
+ * @throws AMQException Due to createQueue thorwing AMQException
+ * @throws DatabaseException If there is a problem with the loading of the data
+ */
+ private void moveContents(Database oldDatabase, final Database newDatabase, String contentName) throws AMQException, DatabaseException
+ {
+
+ DatabaseVisitor moveVisitor = new DatabaseVisitor()
+ {
+ public void visit(DatabaseEntry key, DatabaseEntry value) throws DatabaseException
+ {
+ _count++;
+ newDatabase.put(null, key, value);
+ }
+ };
+
+ _oldMessageStore.visitDatabase(oldDatabase, moveVisitor);
+
+ logCount(moveVisitor.getVisitedCount(), contentName);
+ }
+
+ private static void usage()
+ {
+ System.out.println("usage: BDBStoreUpgrade:\n [-h|--help] [-q|--quiet] [-f|--force] [-b|--backup <Path to backup-db>] " +
+ "-i|--input <Path to input-db> [-o|--output <Path to upgraded-db>]");
+ }
+
+ private static void help()
+ {
+ System.out.println("usage: BDBStoreUpgrade:");
+ System.out.println("Required:");
+ for (Object obj : _options.getOptions())
+ {
+ Option option = (Option) obj;
+ if (option.isRequired())
+ {
+ System.out.println("-" + option.getOpt() + "|--" + option.getLongOpt() + "\t\t-\t" + option.getDescription());
+ }
+ }
+
+ System.out.println("\nOptions:");
+ for (Object obj : _options.getOptions())
+ {
+ Option option = (Option) obj;
+ if (!option.isRequired())
+ {
+ System.out.println("--" + option.getLongOpt() + "|-" + option.getOpt() + "\t\t-\t" + option.getDescription());
+ }
+ }
+ }
+
+ static boolean isDirectoryAStoreDir(File directory)
+ {
+ if (directory.isFile())
+ {
+ return false;
+ }
+
+ for (File file : directory.listFiles())
+ {
+ if (file.isFile())
+ {
+ if (file.getName().endsWith(BDB_FILE_ENDING))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ static File[] discoverDBStores(File fromDir)
+ {
+ if (!fromDir.exists())
+ {
+ throw new IllegalArgumentException("'" + fromDir + "' does not exist unable to upgrade.");
+ }
+
+ // Ensure we are given a directory
+ if (fromDir.isFile())
+ {
+ throw new IllegalArgumentException("'" + fromDir + "' is not a directory unable to upgrade.");
+ }
+
+ // Check to see if we have been given a single directory
+ if (isDirectoryAStoreDir(fromDir))
+ {
+ return new File[]{fromDir};
+ }
+
+ // Check to see if we have been give a directory containing stores.
+ List<File> stores = new LinkedList<File>();
+
+ for (File directory : fromDir.listFiles())
+ {
+ if (directory.isDirectory())
+ {
+ if (isDirectoryAStoreDir(directory))
+ {
+ stores.add(directory);
+ }
+ }
+ }
+
+ return stores.toArray(new File[stores.size()]);
+ }
+
+ private static void performDBBackup(File source, File backup, boolean force) throws Exception
+ {
+ if (backup.exists())
+ {
+ if (force)
+ {
+ _logger.info("Backup location exists. Forced to remove.");
+ FileUtils.delete(backup, true);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Unable to perform backup a backup already exists.");
+ }
+ }
+
+ try
+ {
+ _logger.info("Backing up '" + source + "' to '" + backup + "'");
+ FileUtils.copyRecursive(source, backup);
+ }
+ catch (FileNotFoundException e)
+ {
+ //Throwing IAE here as this will be reported as a Backup not started
+ throw new IllegalArgumentException("Unable to perform backup:" + e.getMessage());
+ }
+ catch (FileUtils.UnableToCopyException e)
+ {
+ //Throwing exception here as this will be reported as a Failed Backup
+ throw new Exception("Unable to perform backup due to:" + e.getMessage());
+ }
+ }
+
+ public static void main(String[] args) throws ParseException
+ {
+ setOptions(_options);
+
+ final Options helpOptions = new Options();
+ setHelpOptions(helpOptions);
+
+ //Display help
+ boolean displayHelp = false;
+ try
+ {
+ if (new PosixParser().parse(helpOptions, args).hasOption("h"))
+ {
+ showHelp();
+ }
+ }
+ catch (ParseException pe)
+ {
+ displayHelp = true;
+ }
+
+ //Parse commandline for required arguments
+ try
+ {
+ _commandLine = new PosixParser().parse(_options, args);
+ }
+ catch (ParseException mae)
+ {
+ if (displayHelp)
+ {
+ showHelp();
+ }
+ else
+ {
+ fatalError(mae.getMessage());
+ }
+ }
+
+ String fromDir = _commandLine.getOptionValue(OPTION_INPUT_SHORT);
+ String toDir = _commandLine.getOptionValue(OPTION_OUTPUT_SHORT);
+ String backupDir = _commandLine.getOptionValue(OPTION_BACKUP_SHORT);
+
+ if (backupDir == null && _commandLine.hasOption(OPTION_BACKUP_SHORT))
+ {
+ backupDir = "";
+ }
+
+ //Attempt to locate possible Store to upgrade on input path
+ File[] stores = new File[0];
+ try
+ {
+ stores = discoverDBStores(new File(fromDir));
+ }
+ catch (IllegalArgumentException iae)
+ {
+ fatalError(iae.getMessage());
+ }
+
+ boolean interactive = !_commandLine.hasOption(OPTION_QUIET_SHORT);
+ boolean force = _commandLine.hasOption(OPTION_FORCE_SHORT);
+
+ try{
+ for (File store : stores)
+ {
+
+ // if toDir is null then we are upgrading inplace so we don't need
+ // to provide an upgraded toDir when upgrading multiple stores.
+ if (toDir == null ||
+ // Check to see if we are upgrading a store specified in
+ // fromDir or if the directories are nested.
+ (stores.length > 0
+ && stores[0].toString().length() == fromDir.length()))
+ {
+ upgrade(store, toDir, backupDir, interactive, force);
+ }
+ else
+ {
+ // Add the extra part of path from store to the toDir
+ upgrade(store, toDir + File.separator + store.toString().substring(fromDir.length()), backupDir, interactive, force);
+ }
+ }
+ }
+ catch (RuntimeException re)
+ {
+ if (!(USER_ABORTED_PROCESS).equals(re.getMessage()))
+ {
+ re.printStackTrace();
+ _logger.error("Upgrade Failed: " + re.getMessage());
+ }
+ else
+ {
+ _logger.error("Upgrade stopped : User aborted");
+ }
+ }
+
+ }
+
+ @SuppressWarnings("static-access")
+ private static void setOptions(Options options)
+ {
+ Option input =
+ OptionBuilder.isRequired().hasArg().withDescription("Location (Path) of store to upgrade.").withLongOpt(OPTION_INPUT)
+ .create(OPTION_INPUT_SHORT);
+
+ Option output =
+ OptionBuilder.hasArg().withDescription("Location (Path) for the upgraded-db to be written.").withLongOpt(OPTION_OUTPUT)
+ .create(OPTION_OUTPUT_SHORT);
+
+ Option quiet = new Option(OPTION_QUIET_SHORT, OPTION_QUIET, false, "Disable interactive options.");
+
+ Option force = new Option(OPTION_FORCE_SHORT, OPTION_FORCE, false, "Force upgrade removing any existing upgrade target.");
+ Option backup =
+ OptionBuilder.hasOptionalArg().withDescription("Location (Path) for the backup-db to be written.").withLongOpt(OPTION_BACKUP)
+ .create(OPTION_BACKUP_SHORT);
+
+ options.addOption(input);
+ options.addOption(output);
+ options.addOption(quiet);
+ options.addOption(force);
+ options.addOption(backup);
+ setHelpOptions(options);
+ }
+
+ private static void setHelpOptions(Options options)
+ {
+ options.addOption(new Option("h", "help", false, "Show this help."));
+ }
+
+ static void upgrade(File fromDir, String toDir, String backupDir, boolean interactive, boolean force)
+ {
+
+ _logger.info("Running BDB Message Store upgrade tool: v" + VERSION);
+ int version = getStoreVersion(fromDir);
+ if (!isVersionUpgradable(version))
+ {
+ return;
+ }
+ try
+ {
+ new BDBStoreUpgrade(fromDir.toString(), toDir, backupDir, interactive, force).upgradeFromVersion(version);
+
+ _logger.info("Upgrade complete.");
+ }
+ catch (IllegalArgumentException iae)
+ {
+ _logger.error("Upgrade not started due to: " + iae.getMessage());
+ }
+ catch (DatabaseException de)
+ {
+ de.printStackTrace();
+ _logger.error("Upgrade Failed: " + de.getMessage());
+ }
+ catch (RuntimeException re)
+ {
+ if (!(USER_ABORTED_PROCESS).equals(re.getMessage()))
+ {
+ re.printStackTrace();
+ _logger.error("Upgrade Failed: " + re.getMessage());
+ }
+ else
+ {
+ throw re;
+ }
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ _logger.error("Upgrade Failed: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Utility method to verify if store of given version can be upgraded.
+ *
+ * @param version
+ * store version to verify
+ * @return true if store can be upgraded, false otherwise
+ */
+ protected static boolean isVersionUpgradable(int version)
+ {
+ boolean storeUpgradable = false;
+ if (version == 0)
+ {
+ _logger.error("Existing store version is undefined!");
+ }
+ else if (version < LOWEST_SUPPORTED_STORE_VERSION)
+ {
+ _logger.error(MessageFormat.format(PREVIOUS_STORE_VERSION_UNSUPPORTED,
+ new Object[] { Integer.toString(version) }));
+ }
+ else if (version == BDBMessageStore.DATABASE_FORMAT_VERSION)
+ {
+ _logger.error(MessageFormat.format(STORE_ALREADY_UPGRADED, new Object[] { Integer.toString(version) }));
+ }
+ else if (version > BDBMessageStore.DATABASE_FORMAT_VERSION)
+ {
+ _logger.error(MessageFormat.format(FOLLOWING_STORE_VERSION_UNSUPPORTED,
+ new Object[] { Integer.toString(version) }));
+ }
+ else
+ {
+ _logger.info("Existing store version is " + version);
+ storeUpgradable = true;
+ }
+ return storeUpgradable;
+ }
+
+ /**
+ * Detects existing store version by checking list of database in store
+ * environment
+ *
+ * @param fromDir
+ * store folder
+ * @return version
+ */
+ public static int getStoreVersion(File fromDir)
+ {
+ int version = 0;
+ EnvironmentConfig envConfig = new EnvironmentConfig();
+ envConfig.setAllowCreate(false);
+ envConfig.setTransactional(false);
+ envConfig.setReadOnly(true);
+ Environment environment = null;
+ try
+ {
+
+ environment = new Environment(fromDir, envConfig);
+ List<String> databases = environment.getDatabaseNames();
+ for (String name : databases)
+ {
+ if (name.startsWith("exchangeDb"))
+ {
+ if (name.startsWith("exchangeDb_v"))
+ {
+ version = Integer.parseInt(name.substring(12));
+ }
+ else
+ {
+ version = 1;
+ }
+ break;
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ _logger.error("Failure to open existing database: " + e.getMessage());
+ }
+ finally
+ {
+ if (environment != null)
+ {
+ try
+ {
+ environment.close();
+ }
+ catch (Exception e)
+ {
+ // ignoring. It should never happen.
+ }
+ }
+ }
+ return version;
+ }
+
+ private static void fatalError(String message)
+ {
+ System.out.println(message);
+ usage();
+ System.exit(1);
+ }
+
+ private static void showHelp()
+ {
+ help();
+ System.exit(0);
+ }
+
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BindingKey.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BindingKey.java
new file mode 100644
index 0000000000..396f0ed817
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BindingKey.java
@@ -0,0 +1,62 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb;
+
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
+
+public class BindingKey extends Object
+{
+ private final AMQShortString _exchangeName;
+ private final AMQShortString _queueName;
+ private final AMQShortString _routingKey;
+ private final FieldTable _arguments;
+
+ public BindingKey(AMQShortString exchangeName, AMQShortString queueName, AMQShortString routingKey, FieldTable arguments)
+ {
+ _exchangeName = exchangeName;
+ _queueName = queueName;
+ _routingKey = routingKey;
+ _arguments = arguments;
+ }
+
+
+ public AMQShortString getExchangeName()
+ {
+ return _exchangeName;
+ }
+
+ public AMQShortString getQueueName()
+ {
+ return _queueName;
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return _routingKey;
+ }
+
+ public FieldTable getArguments()
+ {
+ return _arguments;
+ }
+
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/ContentTB.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/ContentTB.java
new file mode 100644
index 0000000000..5ea3e9c2e8
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/ContentTB.java
@@ -0,0 +1,52 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb;
+
+import java.nio.ByteBuffer;
+
+import com.sleepycat.bind.tuple.TupleBinding;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+
+public class ContentTB extends TupleBinding
+{
+ public Object entryToObject(TupleInput tupleInput)
+ {
+
+ final int size = tupleInput.readInt();
+ byte[] underlying = new byte[size];
+ tupleInput.readFast(underlying);
+ return ByteBuffer.wrap(underlying);
+ }
+
+ public void objectToEntry(Object object, TupleOutput tupleOutput)
+ {
+ ByteBuffer src = (ByteBuffer) object;
+
+ src = src.slice();
+
+ byte[] chunkData = new byte[src.limit()];
+ src.duplicate().get(chunkData);
+
+ tupleOutput.writeInt(chunkData.length);
+ tupleOutput.writeFast(chunkData);
+ }
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/DatabaseVisitor.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/DatabaseVisitor.java
new file mode 100644
index 0000000000..9bd879025f
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/DatabaseVisitor.java
@@ -0,0 +1,44 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb;
+
+import org.apache.qpid.AMQStoreException;
+
+import com.sleepycat.je.DatabaseEntry;
+import com.sleepycat.je.DatabaseException;
+
+/** Visitor Interface so that each DatabaseEntry for a database can easily be processed. */
+public abstract class DatabaseVisitor
+{
+ protected int _count;
+
+ abstract public void visit(DatabaseEntry entry, DatabaseEntry value) throws AMQStoreException, DatabaseException;
+
+ public int getVisitedCount()
+ {
+ return _count;
+ }
+
+ public void resetVisitCount()
+ {
+ _count = 0;
+ }
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/ExchangeTB.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/ExchangeTB.java
new file mode 100644
index 0000000000..f9c7828bef
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/ExchangeTB.java
@@ -0,0 +1,58 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb;
+
+import com.sleepycat.bind.tuple.TupleBinding;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+import org.apache.log4j.Logger;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.store.berkeleydb.records.ExchangeRecord;
+
+public class ExchangeTB extends TupleBinding
+{
+ private static final Logger _log = Logger.getLogger(ExchangeTB.class);
+
+ public ExchangeTB()
+ {
+ }
+
+ public Object entryToObject(TupleInput tupleInput)
+ {
+
+ AMQShortString name = AMQShortStringEncoding.readShortString(tupleInput);
+ AMQShortString typeName = AMQShortStringEncoding.readShortString(tupleInput);
+
+ boolean autoDelete = tupleInput.readBoolean();
+
+ return new ExchangeRecord(name, typeName, autoDelete);
+ }
+
+ public void objectToEntry(Object object, TupleOutput tupleOutput)
+ {
+ ExchangeRecord exchange = (ExchangeRecord) object;
+
+ AMQShortStringEncoding.writeShortString(exchange.getNameShortString(), tupleOutput);
+ AMQShortStringEncoding.writeShortString(exchange.getType(), tupleOutput);
+
+ tupleOutput.writeBoolean(exchange.isAutoDelete());
+ }
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/FieldTableEncoding.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/FieldTableEncoding.java
new file mode 100644
index 0000000000..c09498cce3
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/FieldTableEncoding.java
@@ -0,0 +1,74 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb;
+
+import org.apache.qpid.framing.FieldTable;
+
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+import com.sleepycat.je.DatabaseException;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+
+public class FieldTableEncoding
+{
+ public static FieldTable readFieldTable(TupleInput tupleInput) throws DatabaseException
+ {
+ long length = tupleInput.readLong();
+ if (length <= 0)
+ {
+ return null;
+ }
+ else
+ {
+
+ byte[] data = new byte[(int)length];
+ tupleInput.readFast(data);
+
+ try
+ {
+ return new FieldTable(new DataInputStream(new ByteArrayInputStream(data)),length);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+
+ }
+
+ }
+
+ public static void writeFieldTable(FieldTable fieldTable, TupleOutput tupleOutput)
+ {
+
+ if (fieldTable == null)
+ {
+ tupleOutput.writeLong(0);
+ }
+ else
+ {
+ tupleOutput.writeLong(fieldTable.getEncodedSize());
+ tupleOutput.writeFast(fieldTable.getDataAsBytes());
+ }
+ }
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/MessageContentKey.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/MessageContentKey.java
new file mode 100644
index 0000000000..005e8d4604
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/MessageContentKey.java
@@ -0,0 +1,42 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb;
+
+public class MessageContentKey
+{
+ private long _messageId;
+
+ public MessageContentKey(long messageId)
+ {
+ _messageId = messageId;
+ }
+
+
+ public long getMessageId()
+ {
+ return _messageId;
+ }
+
+ public void setMessageId(long messageId)
+ {
+ this._messageId = messageId;
+ }
+} \ No newline at end of file
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/QueueEntryKey.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/QueueEntryKey.java
new file mode 100644
index 0000000000..c274fdec8c
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/QueueEntryKey.java
@@ -0,0 +1,49 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb;
+
+import org.apache.qpid.framing.AMQShortString;
+
+public class QueueEntryKey
+{
+ private AMQShortString _queueName;
+ private long _messageId;
+
+
+ public QueueEntryKey(AMQShortString queueName, long messageId)
+ {
+ _queueName = queueName;
+ _messageId = messageId;
+ }
+
+
+ public AMQShortString getQueueName()
+ {
+ return _queueName;
+ }
+
+
+ public long getMessageId()
+ {
+ return _messageId;
+ }
+
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/keys/MessageContentKey_4.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/keys/MessageContentKey_4.java
new file mode 100644
index 0000000000..30357c97d4
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/keys/MessageContentKey_4.java
@@ -0,0 +1,44 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb.keys;
+
+import org.apache.qpid.server.store.berkeleydb.MessageContentKey;
+
+public class MessageContentKey_4 extends MessageContentKey
+{
+ private int _chunkNum;
+
+ public MessageContentKey_4(long messageId, int chunkNo)
+ {
+ super(messageId);
+ _chunkNum = chunkNo;
+ }
+
+ public int getChunk()
+ {
+ return _chunkNum;
+ }
+
+ public void setChunk(int chunk)
+ {
+ this._chunkNum = chunk;
+ }
+} \ No newline at end of file
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/keys/MessageContentKey_5.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/keys/MessageContentKey_5.java
new file mode 100644
index 0000000000..a1a7fe80b5
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/keys/MessageContentKey_5.java
@@ -0,0 +1,44 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb.keys;
+
+import org.apache.qpid.server.store.berkeleydb.MessageContentKey;
+
+public class MessageContentKey_5 extends MessageContentKey
+{
+ private int _offset;
+
+ public MessageContentKey_5(long messageId, int chunkNo)
+ {
+ super(messageId);
+ _offset = chunkNo;
+ }
+
+ public int getOffset()
+ {
+ return _offset;
+ }
+
+ public void setOffset(int chunk)
+ {
+ this._offset = chunk;
+ }
+} \ No newline at end of file
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/records/ExchangeRecord.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/records/ExchangeRecord.java
new file mode 100644
index 0000000000..f20367e33b
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/records/ExchangeRecord.java
@@ -0,0 +1,53 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb.records;
+
+import org.apache.qpid.framing.AMQShortString;
+
+public class ExchangeRecord extends Object
+{
+ private final AMQShortString _exchangeName;
+ private final AMQShortString _exchangeType;
+ private final boolean _autoDelete;
+
+ public ExchangeRecord(AMQShortString exchangeName, AMQShortString exchangeType, boolean autoDelete)
+ {
+ _exchangeName = exchangeName;
+ _exchangeType = exchangeType;
+ _autoDelete = autoDelete;
+ }
+
+ public AMQShortString getNameShortString()
+ {
+ return _exchangeName;
+ }
+
+ public AMQShortString getType()
+ {
+ return _exchangeType;
+ }
+
+ public boolean isAutoDelete()
+ {
+ return _autoDelete;
+ }
+
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/records/QueueRecord.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/records/QueueRecord.java
new file mode 100644
index 0000000000..fbe10433ca
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/records/QueueRecord.java
@@ -0,0 +1,66 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb.records;
+
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
+
+public class QueueRecord extends Object
+{
+ private final AMQShortString _queueName;
+ private final AMQShortString _owner;
+ private final FieldTable _arguments;
+ private boolean _exclusive;
+
+ public QueueRecord(AMQShortString queueName, AMQShortString owner, boolean exclusive, FieldTable arguments)
+ {
+ _queueName = queueName;
+ _owner = owner;
+ _exclusive = exclusive;
+ _arguments = arguments;
+ }
+
+ public AMQShortString getNameShortString()
+ {
+ return _queueName;
+ }
+
+ public AMQShortString getOwner()
+ {
+ return _owner;
+ }
+
+ public boolean isExclusive()
+ {
+ return _exclusive;
+ }
+
+ public void setExclusive(boolean exclusive)
+ {
+ _exclusive = exclusive;
+ }
+
+ public FieldTable getArguments()
+ {
+ return _arguments;
+ }
+
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/testclient/BackupTestClient.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/testclient/BackupTestClient.java
new file mode 100644
index 0000000000..f6344b3d7d
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/testclient/BackupTestClient.java
@@ -0,0 +1,120 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb.testclient;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.ping.PingDurableClient;
+import org.apache.qpid.server.store.berkeleydb.BDBBackup;
+import org.apache.qpid.util.CommandLineParser;
+
+import java.util.Properties;
+
+/**
+ * BackupTestClient extends {@link PingDurableClient} with an action that takes a BDB backup when a configurable
+ * message count is reached. This enables a test user to restore this beackup, knowing how many committed but undelivered
+ * messages were in the backup, in order to check that all are re-delivered when the backup is retored.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Perform BDB Backup on configurable message count.
+ * </table>
+ */
+public class BackupTestClient extends PingDurableClient
+{
+ /** Used for debugging. */
+ private static final Logger log = Logger.getLogger(BackupTestClient.class);
+
+ /** Holds the from directory to take backups from. */
+ private String fromDir;
+
+ /** Holds the to directory to store backups in. */
+ private String toDir;
+
+ /**
+ * Default constructor, passes all property overrides to the parent.
+ *
+ * @param overrides Any property overrides to apply to the defaults.
+ *
+ * @throws Exception Any underlying exception is allowed to fall through.
+ */
+ BackupTestClient(Properties overrides) throws Exception
+ {
+ super(overrides);
+ }
+
+ /**
+ * Starts the ping/wait/receive process. From and to directory locations for the BDB backups must be specified
+ * on the command line:
+ *
+ * <p/><table><caption>Command Line</caption>
+ * <tr><th> Option <th> Comment
+ * <tr><td> -fromdir <td> The path to the directory to back the bdb log file from.
+ * <tr><td> -todir <td> The path to the directory to save the backed up bdb log files to.
+ * </table>
+ *
+ * @param args The command line arguments.
+ */
+ public static void main(String[] args)
+ {
+ try
+ {
+ // Use the same command line format as BDBBackup utility, (compulsory from and to directories).
+ Properties options =
+ CommandLineParser.processCommandLine(args, new CommandLineParser(BDBBackup.COMMAND_LINE_SPEC),
+ System.getProperties());
+ BackupTestClient pingProducer = new BackupTestClient(options);
+
+ // Keep the from and to directories for backups.
+ pingProducer.fromDir = options.getProperty("fromdir");
+ pingProducer.toDir = options.getProperty("todir");
+
+ // Create a shutdown hook to terminate the ping-pong producer.
+ Runtime.getRuntime().addShutdownHook(pingProducer.getShutdownHook());
+
+ // Ensure that the ping pong producer is registered to listen for exceptions on the connection too.
+ // pingProducer.getConnection().setExceptionListener(pingProducer);
+
+ // Run the test procedure.
+ int sent = pingProducer.send();
+ pingProducer.waitForUser("Press return to begin receiving the pings.");
+ pingProducer.receive(sent);
+
+ System.exit(0);
+ }
+ catch (Exception e)
+ {
+ System.err.println(e.getMessage());
+ log.error("Top level handler caught execption.", e);
+ System.exit(1);
+ }
+ }
+
+ /**
+ * Supplies a triggered action extension, based on message count. This action takes a BDB log file backup.
+ */
+ public void takeAction()
+ {
+ BDBBackup backupUtil = new BDBBackup();
+ backupUtil.takeBackupNoLock(fromDir, toDir);
+ System.out.println("Took backup of BDB log files from directory: " + fromDir);
+ }
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/BindingTuple.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/BindingTuple.java
new file mode 100644
index 0000000000..301ee417c5
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/BindingTuple.java
@@ -0,0 +1,25 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.berkeleydb.tuples;
+
+public interface BindingTuple
+{
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/BindingTupleBindingFactory.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/BindingTupleBindingFactory.java
new file mode 100644
index 0000000000..8e17f074d7
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/BindingTupleBindingFactory.java
@@ -0,0 +1,45 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb.tuples;
+
+import org.apache.qpid.server.store.berkeleydb.BindingKey;
+
+import com.sleepycat.bind.tuple.TupleBinding;
+
+public class BindingTupleBindingFactory extends TupleBindingFactory<BindingKey>
+{
+ public BindingTupleBindingFactory(int version)
+ {
+ super(version);
+ }
+
+ public TupleBinding<BindingKey> getInstance()
+ {
+ switch (_version)
+ {
+ default:
+ case 5:
+ //no change from v4
+ case 4:
+ return new BindingTuple_4();
+ }
+ }
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/BindingTuple_4.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/BindingTuple_4.java
new file mode 100644
index 0000000000..52b131a7f2
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/BindingTuple_4.java
@@ -0,0 +1,76 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb.tuples;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.server.store.berkeleydb.AMQShortStringEncoding;
+import org.apache.qpid.server.store.berkeleydb.BindingKey;
+import org.apache.qpid.server.store.berkeleydb.FieldTableEncoding;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
+
+import com.sleepycat.bind.tuple.TupleBinding;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+import com.sleepycat.je.DatabaseException;
+
+public class BindingTuple_4 extends TupleBinding<BindingKey> implements BindingTuple
+{
+ protected static final Logger _log = Logger.getLogger(BindingTuple.class);
+
+ public BindingTuple_4()
+ {
+ super();
+ }
+
+ public BindingKey entryToObject(TupleInput tupleInput)
+ {
+ AMQShortString exchangeName = AMQShortStringEncoding.readShortString(tupleInput);
+ AMQShortString queueName = AMQShortStringEncoding.readShortString(tupleInput);
+ AMQShortString routingKey = AMQShortStringEncoding.readShortString(tupleInput);
+
+ FieldTable arguments;
+
+ // Addition for Version 2 of this table
+ try
+ {
+ arguments = FieldTableEncoding.readFieldTable(tupleInput);
+ }
+ catch (DatabaseException e)
+ {
+ _log.error("Unable to create binding: " + e, e);
+ return null;
+ }
+
+ return new BindingKey(exchangeName, queueName, routingKey, arguments);
+ }
+
+ public void objectToEntry(BindingKey binding, TupleOutput tupleOutput)
+ {
+ AMQShortStringEncoding.writeShortString(binding.getExchangeName(), tupleOutput);
+ AMQShortStringEncoding.writeShortString(binding.getQueueName(), tupleOutput);
+ AMQShortStringEncoding.writeShortString(binding.getRoutingKey(), tupleOutput);
+
+ // Addition for Version 2 of this table
+ FieldTableEncoding.writeFieldTable(binding.getArguments(), tupleOutput);
+ }
+
+} \ No newline at end of file
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/MessageContentKeyTB_4.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/MessageContentKeyTB_4.java
new file mode 100644
index 0000000000..f5ba6bbce3
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/MessageContentKeyTB_4.java
@@ -0,0 +1,47 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb.tuples;
+
+import org.apache.qpid.server.store.berkeleydb.MessageContentKey;
+import org.apache.qpid.server.store.berkeleydb.keys.MessageContentKey_4;
+
+import com.sleepycat.bind.tuple.TupleBinding;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+
+public class MessageContentKeyTB_4 extends TupleBinding<MessageContentKey>
+{
+
+ public MessageContentKey entryToObject(TupleInput tupleInput)
+ {
+ long messageId = tupleInput.readLong();
+ int chunk = tupleInput.readInt();
+ return new MessageContentKey_4(messageId, chunk);
+ }
+
+ public void objectToEntry(MessageContentKey object, TupleOutput tupleOutput)
+ {
+ final MessageContentKey_4 mk = (MessageContentKey_4) object;
+ tupleOutput.writeLong(mk.getMessageId());
+ tupleOutput.writeInt(mk.getChunk());
+ }
+
+} \ No newline at end of file
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/MessageContentKeyTB_5.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/MessageContentKeyTB_5.java
new file mode 100644
index 0000000000..e6a2fd23a8
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/MessageContentKeyTB_5.java
@@ -0,0 +1,46 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb.tuples;
+
+import org.apache.qpid.server.store.berkeleydb.MessageContentKey;
+import org.apache.qpid.server.store.berkeleydb.keys.MessageContentKey_5;
+
+import com.sleepycat.bind.tuple.TupleBinding;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+
+public class MessageContentKeyTB_5 extends TupleBinding<MessageContentKey>
+{
+ public MessageContentKey entryToObject(TupleInput tupleInput)
+ {
+ long messageId = tupleInput.readLong();
+ int offset = tupleInput.readInt();
+ return new MessageContentKey_5(messageId, offset);
+ }
+
+ public void objectToEntry(MessageContentKey object, TupleOutput tupleOutput)
+ {
+ final MessageContentKey_5 mk = (MessageContentKey_5) object;
+ tupleOutput.writeLong(mk.getMessageId());
+ tupleOutput.writeInt(mk.getOffset());
+ }
+
+} \ No newline at end of file
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/MessageContentKeyTupleBindingFactory.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/MessageContentKeyTupleBindingFactory.java
new file mode 100644
index 0000000000..76ee4f66e4
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/MessageContentKeyTupleBindingFactory.java
@@ -0,0 +1,45 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb.tuples;
+
+import org.apache.qpid.server.store.berkeleydb.MessageContentKey;
+
+import com.sleepycat.bind.tuple.TupleBinding;
+
+public class MessageContentKeyTupleBindingFactory extends TupleBindingFactory<MessageContentKey>
+{
+ public MessageContentKeyTupleBindingFactory(int version)
+ {
+ super(version);
+ }
+
+ public TupleBinding<MessageContentKey> getInstance()
+ {
+ switch (_version)
+ {
+ default:
+ case 5:
+ return new MessageContentKeyTB_5();
+ case 4:
+ return new MessageContentKeyTB_4();
+ }
+ }
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/MessageMetaDataTB_4.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/MessageMetaDataTB_4.java
new file mode 100644
index 0000000000..e26b544e38
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/MessageMetaDataTB_4.java
@@ -0,0 +1,162 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.berkeleydb.tuples;
+
+import com.sleepycat.bind.tuple.TupleBinding;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.*;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.server.message.MessageMetaData;
+import org.apache.qpid.server.store.berkeleydb.AMQShortStringEncoding;
+
+import java.io.*;
+
+/**
+ * Handles the mapping to and from 0-8/0-9 message meta data
+ */
+public class MessageMetaDataTB_4 extends TupleBinding<Object>
+{
+ private static final Logger _log = Logger.getLogger(MessageMetaDataTB_4.class);
+
+ public MessageMetaDataTB_4()
+ {
+ }
+
+ public Object entryToObject(TupleInput tupleInput)
+ {
+ try
+ {
+ final MessagePublishInfo publishBody = readMessagePublishInfo(tupleInput);
+ final ContentHeaderBody contentHeaderBody = readContentHeaderBody(tupleInput);
+ final int contentChunkCount = tupleInput.readInt();
+
+ return new MessageMetaData(publishBody, contentHeaderBody, contentChunkCount);
+ }
+ catch (Exception e)
+ {
+ _log.error("Error converting entry to object: " + e, e);
+ // annoyingly just have to return null since we cannot throw
+ return null;
+ }
+ }
+
+ public void objectToEntry(Object object, TupleOutput tupleOutput)
+ {
+ MessageMetaData message = (MessageMetaData) object;
+ try
+ {
+ writeMessagePublishInfo(message.getMessagePublishInfo(), tupleOutput);
+ }
+ catch (AMQException e)
+ {
+ // can't do anything else since the BDB interface precludes throwing any exceptions
+ // in practice we should never get an exception
+ throw new RuntimeException("Error converting object to entry: " + e, e);
+ }
+ writeContentHeader(message.getContentHeaderBody(), tupleOutput);
+ tupleOutput.writeInt(message.getContentChunkCount());
+ }
+
+ private MessagePublishInfo readMessagePublishInfo(TupleInput tupleInput)
+ {
+
+ final AMQShortString exchange = AMQShortStringEncoding.readShortString(tupleInput);
+ final AMQShortString routingKey = AMQShortStringEncoding.readShortString(tupleInput);
+ final boolean mandatory = tupleInput.readBoolean();
+ final boolean immediate = tupleInput.readBoolean();
+
+ return new MessagePublishInfo()
+ {
+
+ public AMQShortString getExchange()
+ {
+ return exchange;
+ }
+
+ public void setExchange(AMQShortString exchange)
+ {
+
+ }
+
+ public boolean isImmediate()
+ {
+ return immediate;
+ }
+
+ public boolean isMandatory()
+ {
+ return mandatory;
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return routingKey;
+ }
+ } ;
+
+ }
+
+ private ContentHeaderBody readContentHeaderBody(TupleInput tupleInput) throws AMQFrameDecodingException, AMQProtocolVersionException
+ {
+ int bodySize = tupleInput.readInt();
+ byte[] underlying = new byte[bodySize];
+ tupleInput.readFast(underlying);
+
+ try
+ {
+ return ContentHeaderBody.createFromBuffer(new DataInputStream(new ByteArrayInputStream(underlying)), bodySize);
+ }
+ catch (IOException e)
+ {
+ throw new AMQFrameDecodingException(null, e.getMessage(), e);
+ }
+ }
+
+ private void writeMessagePublishInfo(MessagePublishInfo publishBody, TupleOutput tupleOutput) throws AMQException
+ {
+
+ AMQShortStringEncoding.writeShortString(publishBody.getExchange(), tupleOutput);
+ AMQShortStringEncoding.writeShortString(publishBody.getRoutingKey(), tupleOutput);
+ tupleOutput.writeBoolean(publishBody.isMandatory());
+ tupleOutput.writeBoolean(publishBody.isImmediate());
+ }
+
+ private void writeContentHeader(ContentHeaderBody headerBody, TupleOutput tupleOutput)
+ {
+ // write out the content header body
+ final int bodySize = headerBody.getSize();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(bodySize);
+ try
+ {
+ headerBody.writePayload(new DataOutputStream(baos));
+ tupleOutput.writeInt(bodySize);
+ tupleOutput.writeFast(baos.toByteArray());
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+
+ }
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/MessageMetaDataTB_5.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/MessageMetaDataTB_5.java
new file mode 100644
index 0000000000..6dc041cb23
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/MessageMetaDataTB_5.java
@@ -0,0 +1,77 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb.tuples;
+
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+import org.apache.log4j.Logger;
+import org.apache.qpid.server.store.MessageMetaDataType;
+import org.apache.qpid.server.store.StorableMessageMetaData;
+
+/**
+ * Handles the mapping to and from message meta data
+ */
+public class MessageMetaDataTB_5 extends MessageMetaDataTB_4
+{
+ private static final Logger _log = Logger.getLogger(MessageMetaDataTB_5.class);
+
+ @Override
+ public Object entryToObject(TupleInput tupleInput)
+ {
+ try
+ {
+ final int bodySize = tupleInput.readInt();
+ byte[] dataAsBytes = new byte[bodySize];
+ tupleInput.readFast(dataAsBytes);
+
+ java.nio.ByteBuffer buf = java.nio.ByteBuffer.wrap(dataAsBytes);
+ buf.position(1);
+ buf = buf.slice();
+ MessageMetaDataType type = MessageMetaDataType.values()[dataAsBytes[0]];
+ StorableMessageMetaData metaData = type.getFactory().createMetaData(buf);
+
+ return metaData;
+ }
+ catch (Exception e)
+ {
+ _log.error("Error converting entry to object: " + e, e);
+ // annoyingly just have to return null since we cannot throw
+ return null;
+ }
+ }
+
+ @Override
+ public void objectToEntry(Object object, TupleOutput tupleOutput)
+ {
+ StorableMessageMetaData metaData = (StorableMessageMetaData) object;
+
+ final int bodySize = 1 + metaData.getStorableSize();
+ byte[] underlying = new byte[bodySize];
+ underlying[0] = (byte) metaData.getType().ordinal();
+ java.nio.ByteBuffer buf = java.nio.ByteBuffer.wrap(underlying);
+ buf.position(1);
+ buf = buf.slice();
+
+ metaData.writeToBuffer(0, buf);
+ tupleOutput.writeInt(bodySize);
+ tupleOutput.writeFast(underlying);
+ }
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/MessageMetaDataTupleBindingFactory.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/MessageMetaDataTupleBindingFactory.java
new file mode 100644
index 0000000000..40153c13ea
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/MessageMetaDataTupleBindingFactory.java
@@ -0,0 +1,43 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb.tuples;
+
+import com.sleepycat.bind.tuple.TupleBinding;
+
+public class MessageMetaDataTupleBindingFactory extends TupleBindingFactory<Object>
+{
+ public MessageMetaDataTupleBindingFactory(int version)
+ {
+ super(version);
+ }
+
+ public TupleBinding<Object> getInstance()
+ {
+ switch (_version)
+ {
+ default:
+ case 5:
+ return new MessageMetaDataTB_5();
+ case 4:
+ return new MessageMetaDataTB_4();
+ }
+ }
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/QueueEntryTB.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/QueueEntryTB.java
new file mode 100644
index 0000000000..975e558874
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/QueueEntryTB.java
@@ -0,0 +1,46 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb.tuples;
+
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.store.berkeleydb.AMQShortStringEncoding;
+import org.apache.qpid.server.store.berkeleydb.QueueEntryKey;
+
+import com.sleepycat.bind.tuple.TupleBinding;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+
+public class QueueEntryTB extends TupleBinding<QueueEntryKey>
+{
+ public QueueEntryKey entryToObject(TupleInput tupleInput)
+ {
+ AMQShortString queueName = AMQShortStringEncoding.readShortString(tupleInput);
+ Long messageId = tupleInput.readLong();
+
+ return new QueueEntryKey(queueName, messageId);
+ }
+
+ public void objectToEntry(QueueEntryKey mk, TupleOutput tupleOutput)
+ {
+ AMQShortStringEncoding.writeShortString(mk.getQueueName(),tupleOutput);
+ tupleOutput.writeLong(mk.getMessageId());
+ }
+} \ No newline at end of file
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/QueueTuple.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/QueueTuple.java
new file mode 100644
index 0000000000..affa9a271d
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/QueueTuple.java
@@ -0,0 +1,25 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.berkeleydb.tuples;
+
+public interface QueueTuple
+{
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/QueueTupleBindingFactory.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/QueueTupleBindingFactory.java
new file mode 100644
index 0000000000..512e319f96
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/QueueTupleBindingFactory.java
@@ -0,0 +1,46 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb.tuples;
+
+import org.apache.qpid.server.store.berkeleydb.records.QueueRecord;
+
+import com.sleepycat.bind.tuple.TupleBinding;
+
+public class QueueTupleBindingFactory extends TupleBindingFactory<QueueRecord>
+{
+
+ public QueueTupleBindingFactory(int version)
+ {
+ super(version);
+ }
+
+ public TupleBinding<QueueRecord> getInstance()
+ {
+ switch (_version)
+ {
+ default:
+ case 5:
+ return new QueueTuple_5();
+ case 4:
+ return new QueueTuple_4();
+ }
+ }
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/QueueTuple_4.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/QueueTuple_4.java
new file mode 100644
index 0000000000..347eecf08e
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/QueueTuple_4.java
@@ -0,0 +1,72 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb.tuples;
+
+import com.sleepycat.bind.tuple.TupleBinding;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+import com.sleepycat.je.DatabaseException;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.server.store.berkeleydb.AMQShortStringEncoding;
+import org.apache.qpid.server.store.berkeleydb.FieldTableEncoding;
+import org.apache.qpid.server.store.berkeleydb.records.QueueRecord;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
+
+public class QueueTuple_4 extends TupleBinding<QueueRecord> implements QueueTuple
+{
+ protected static final Logger _logger = Logger.getLogger(QueueTuple_4.class);
+
+ protected FieldTable _arguments;
+
+ public QueueTuple_4()
+ {
+ super();
+ }
+
+ public QueueRecord entryToObject(TupleInput tupleInput)
+ {
+ try
+ {
+ AMQShortString name = AMQShortStringEncoding.readShortString(tupleInput);
+ AMQShortString owner = AMQShortStringEncoding.readShortString(tupleInput);
+ // Addition for Version 2 of this table, read the queue arguments
+ FieldTable arguments = FieldTableEncoding.readFieldTable(tupleInput);
+
+ return new QueueRecord(name, owner, false, arguments);
+ }
+ catch (DatabaseException e)
+ {
+ _logger.error("Unable to create binding: " + e, e);
+ return null;
+ }
+
+ }
+
+ public void objectToEntry(QueueRecord queue, TupleOutput tupleOutput)
+ {
+ AMQShortStringEncoding.writeShortString(queue.getNameShortString(), tupleOutput);
+ AMQShortStringEncoding.writeShortString(queue.getOwner(), tupleOutput);
+ // Addition for Version 2 of this table, store the queue arguments
+ FieldTableEncoding.writeFieldTable(queue.getArguments(), tupleOutput);
+ }
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/QueueTuple_5.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/QueueTuple_5.java
new file mode 100644
index 0000000000..0f293b79b7
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/QueueTuple_5.java
@@ -0,0 +1,75 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.berkeleydb.tuples;
+
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+import com.sleepycat.je.DatabaseException;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.server.store.berkeleydb.AMQShortStringEncoding;
+import org.apache.qpid.server.store.berkeleydb.FieldTableEncoding;
+import org.apache.qpid.server.store.berkeleydb.records.QueueRecord;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
+
+public class QueueTuple_5 extends QueueTuple_4
+{
+ protected static final Logger _logger = Logger.getLogger(QueueTuple_5.class);
+
+ protected FieldTable _arguments;
+
+ public QueueTuple_5()
+ {
+ super();
+ }
+
+ public QueueRecord entryToObject(TupleInput tupleInput)
+ {
+ try
+ {
+ AMQShortString name = AMQShortStringEncoding.readShortString(tupleInput);
+ AMQShortString owner = AMQShortStringEncoding.readShortString(tupleInput);
+ // Addition for Version 2 of this table, read the queue arguments
+ FieldTable arguments = FieldTableEncoding.readFieldTable(tupleInput);
+ // Addition for Version 3 of this table, read the queue exclusivity
+ boolean exclusive = tupleInput.readBoolean();
+
+ return new QueueRecord(name, owner, exclusive, arguments);
+ }
+ catch (DatabaseException e)
+ {
+ _logger.error("Unable to create binding: " + e, e);
+ return null;
+ }
+
+ }
+
+ public void objectToEntry(QueueRecord queue, TupleOutput tupleOutput)
+ {
+ AMQShortStringEncoding.writeShortString(queue.getNameShortString(), tupleOutput);
+ AMQShortStringEncoding.writeShortString(queue.getOwner(), tupleOutput);
+ // Addition for Version 2 of this table, store the queue arguments
+ FieldTableEncoding.writeFieldTable(queue.getArguments(), tupleOutput);
+ // Addition for Version 3 of this table, store the queue exclusivity
+ tupleOutput.writeBoolean(queue.isExclusive());
+ }
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/TupleBindingFactory.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/TupleBindingFactory.java
new file mode 100644
index 0000000000..2adac1f9a3
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuples/TupleBindingFactory.java
@@ -0,0 +1,35 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb.tuples;
+
+import com.sleepycat.bind.tuple.TupleBinding;
+
+public abstract class TupleBindingFactory<E>
+{
+ protected int _version;
+
+ public TupleBindingFactory(int version)
+ {
+ _version = version;
+ }
+
+ public abstract TupleBinding<E> getInstance();
+}
diff --git a/qpid/java/bdbstore/src/resources/backup-log4j.xml b/qpid/java/bdbstore/src/resources/backup-log4j.xml
new file mode 100644
index 0000000000..6b0619f0b6
--- /dev/null
+++ b/qpid/java/bdbstore/src/resources/backup-log4j.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ -
+ - Licensed to the Apache Software Foundation (ASF) under one
+ - or more contributor license agreements. See the NOTICE file
+ - distributed with this work for additional information
+ - regarding copyright ownership. The ASF licenses this file
+ - to you under the Apache License, Version 2.0 (the
+ - "License"); you may not use this file except in compliance
+ - with the License. You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing,
+ - software distributed under the License is distributed on an
+ - "AS IS" BASIS, WITHOUT 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">
+
+<!-- =============================================================================== -->
+<!-- This is a Log4j configuration specially created for the BDB Backup utility, -->
+<!-- it outputs logging to the console for specifically designated console loggers -->
+<!-- at info level or above only. This avoids spamming the user with any internals -->
+<!-- of the Qpid code. -->
+<!-- Use a different logging set up to capture debugging output to diagnose errors. -->
+<!-- =============================================================================== -->
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
+
+ <!-- ====================================================== -->
+ <!-- Append messages to the console at info level or above. -->
+ <!-- ====================================================== -->
+
+ <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+ <param name="Target" value="System.out"/>
+ <param name="Threshold" value="info"/>
+
+ <layout class="org.apache.log4j.PatternLayout">
+ <!-- The default pattern: Date Priority [Category] Message\n -->
+ <param name="ConversionPattern" value="%m%n"/>
+ </layout>
+
+ </appender>
+
+ <!-- ================ -->
+ <!-- Limit categories -->
+ <!-- ================ -->
+
+ <category name="org.apache.qpid.server.store.berkeleydb.BDBBackup">
+ <priority value="info"/>
+ </category>
+
+ <!-- ======================= -->
+ <!-- Setup the Root category -->
+ <!-- ======================= -->
+
+ <root>
+ <appender-ref ref="CONSOLE"/>
+ </root>
+
+</log4j:configuration>
diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/AMQShortStringEncodingTest.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/AMQShortStringEncodingTest.java
new file mode 100644
index 0000000000..d076babc61
--- /dev/null
+++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/AMQShortStringEncodingTest.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.berkeleydb;
+
+import org.apache.qpid.framing.AMQShortString;
+
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for {@code AMQShortStringEncoding} including corner cases when string
+ * is null or over 127 characters in length
+ */
+public class AMQShortStringEncodingTest extends TestCase
+{
+
+ public void testWriteReadNullValues()
+ {
+ // write into tuple output
+ TupleOutput tupleOutput = new TupleOutput();
+ AMQShortStringEncoding.writeShortString(null, tupleOutput);
+ byte[] data = tupleOutput.getBufferBytes();
+
+ // read from tuple input
+ TupleInput tupleInput = new TupleInput(data);
+ AMQShortString result = AMQShortStringEncoding.readShortString(tupleInput);
+ assertNull("Expected null but got " + result, result);
+ }
+
+ public void testWriteReadShortStringWithLengthOver127()
+ {
+ AMQShortString value = createString('a', 128);
+
+ // write into tuple output
+ TupleOutput tupleOutput = new TupleOutput();
+ AMQShortStringEncoding.writeShortString(value, tupleOutput);
+ byte[] data = tupleOutput.getBufferBytes();
+
+ // read from tuple input
+ TupleInput tupleInput = new TupleInput(data);
+ AMQShortString result = AMQShortStringEncoding.readShortString(tupleInput);
+ assertEquals("Expected " + value + " but got " + result, value, result);
+ }
+
+ public void testWriteReadShortStringWithLengthLess127()
+ {
+ AMQShortString value = new AMQShortString("test");
+
+ // write into tuple output
+ TupleOutput tupleOutput = new TupleOutput();
+ AMQShortStringEncoding.writeShortString(value, tupleOutput);
+ byte[] data = tupleOutput.getBufferBytes();
+
+ // read from tuple input
+ TupleInput tupleInput = new TupleInput(data);
+ AMQShortString result = AMQShortStringEncoding.readShortString(tupleInput);
+ assertEquals("Expected " + value + " but got " + result, value, result);
+ }
+
+ private AMQShortString createString(char ch, int length)
+ {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < length; i++)
+ {
+ sb.append(ch);
+ }
+ return new AMQShortString(sb.toString());
+ }
+
+}
diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreTest.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreTest.java
new file mode 100644
index 0000000000..ef31b78cfe
--- /dev/null
+++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreTest.java
@@ -0,0 +1,470 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.berkeleydb;
+
+import java.io.File;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.qpid.AMQStoreException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.MethodRegistry;
+import org.apache.qpid.framing.ProtocolVersion;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.server.message.MessageMetaData;
+import org.apache.qpid.server.message.MessageMetaData_0_10;
+import org.apache.qpid.server.store.MessageMetaDataType;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.store.StorableMessageMetaData;
+import org.apache.qpid.server.store.StoredMessage;
+import org.apache.qpid.server.store.TransactionLog;
+import org.apache.qpid.server.store.TransactionLogResource;
+import org.apache.qpid.transport.DeliveryProperties;
+import org.apache.qpid.transport.Header;
+import org.apache.qpid.transport.MessageAcceptMode;
+import org.apache.qpid.transport.MessageAcquireMode;
+import org.apache.qpid.transport.MessageDeliveryMode;
+import org.apache.qpid.transport.MessageDeliveryPriority;
+import org.apache.qpid.transport.MessageProperties;
+import org.apache.qpid.transport.MessageTransfer;
+
+/**
+ * Subclass of MessageStoreTest which runs the standard tests from the superclass against
+ * the BDB Store as well as additional tests specific to the DBB store-implementation.
+ */
+public class BDBMessageStoreTest extends org.apache.qpid.server.store.MessageStoreTest
+{
+ /**
+ * Tests that message metadata and content are successfully read back from a
+ * store after it has been reloaded. Both 0-8 and 0-10 metadata is used to
+ * verify their ability to co-exist within the store and be successful retrieved.
+ */
+ public void testBDBMessagePersistence() throws Exception
+ {
+ MessageStore store = getVirtualHost().getMessageStore();
+
+ BDBMessageStore bdbStore = assertBDBStore(store);
+
+ // Create content ByteBuffers.
+ // Split the content into 2 chunks for the 0-8 message, as per broker behaviour.
+ // Use a single chunk for the 0-10 message as per broker behaviour.
+ String bodyText = "jfhdjsflsdhfjdshfjdslhfjdslhfsjlhfsjkhfdsjkhfdsjkfhdslkjf";
+
+ ByteBuffer firstContentBytes_0_8 = ByteBuffer.wrap(bodyText.substring(0, 10).getBytes());
+ ByteBuffer secondContentBytes_0_8 = ByteBuffer.wrap(bodyText.substring(10).getBytes());
+
+ ByteBuffer completeContentBody_0_10 = ByteBuffer.wrap(bodyText.getBytes());
+ int bodySize = completeContentBody_0_10.limit();
+
+ /*
+ * Create and insert a 0-8 message (metadata and multi-chunk content)
+ */
+ MessagePublishInfo pubInfoBody_0_8 = createPublishInfoBody_0_8();
+ BasicContentHeaderProperties props_0_8 = createContentHeaderProperties_0_8();
+
+ ContentHeaderBody chb_0_8 = createContentHeaderBody_0_8(props_0_8, bodySize);
+
+ MessageMetaData messageMetaData_0_8 = new MessageMetaData(pubInfoBody_0_8, chb_0_8, 0);
+ StoredMessage<MessageMetaData> storedMessage_0_8 = bdbStore.addMessage(messageMetaData_0_8);
+
+ long origArrivalTime_0_8 = messageMetaData_0_8.getArrivalTime();
+ long messageid_0_8 = storedMessage_0_8.getMessageNumber();
+
+ storedMessage_0_8.addContent(0, firstContentBytes_0_8);
+ storedMessage_0_8.addContent(firstContentBytes_0_8.limit(), secondContentBytes_0_8);
+ storedMessage_0_8.flushToStore();
+
+ /*
+ * Create and insert a 0-10 message (metadata and content)
+ */
+ MessageProperties msgProps_0_10 = createMessageProperties_0_10(bodySize);
+ DeliveryProperties delProps_0_10 = createDeliveryProperties_0_10();
+ Header header_0_10 = new Header(msgProps_0_10, delProps_0_10);
+
+ MessageTransfer xfr_0_10 = new MessageTransfer("destination", MessageAcceptMode.EXPLICIT,
+ MessageAcquireMode.PRE_ACQUIRED, header_0_10, completeContentBody_0_10);
+
+ MessageMetaData_0_10 messageMetaData_0_10 = new MessageMetaData_0_10(xfr_0_10);
+ StoredMessage<MessageMetaData_0_10> storedMessage_0_10 = bdbStore.addMessage(messageMetaData_0_10);
+
+ long origArrivalTime_0_10 = messageMetaData_0_10.getArrivalTime();
+ long messageid_0_10 = storedMessage_0_10.getMessageNumber();
+
+ storedMessage_0_10.addContent(0, completeContentBody_0_10);
+ storedMessage_0_10.flushToStore();
+
+ /*
+ * reload the store only (read-only)
+ */
+ bdbStore = reloadStoreReadOnly(bdbStore);
+
+ /*
+ * Read back and validate the 0-8 message metadata and content
+ */
+ StorableMessageMetaData storeableMMD_0_8 = bdbStore.getMessageMetaData(messageid_0_8);
+
+ assertEquals("Unexpected message type",MessageMetaDataType.META_DATA_0_8, storeableMMD_0_8.getType());
+ assertTrue("Unexpected instance type", storeableMMD_0_8 instanceof MessageMetaData);
+ MessageMetaData returnedMMD_0_8 = (MessageMetaData) storeableMMD_0_8;
+
+ assertEquals("Message arrival time has changed", origArrivalTime_0_8, returnedMMD_0_8.getArrivalTime());
+
+ MessagePublishInfo returnedPubBody_0_8 = returnedMMD_0_8.getMessagePublishInfo();
+ assertEquals("Message exchange has changed", pubInfoBody_0_8.getExchange(), returnedPubBody_0_8.getExchange());
+ assertEquals("Immediate flag has changed", pubInfoBody_0_8.isImmediate(), returnedPubBody_0_8.isImmediate());
+ assertEquals("Mandatory flag has changed", pubInfoBody_0_8.isMandatory(), returnedPubBody_0_8.isMandatory());
+ assertEquals("Routing key has changed", pubInfoBody_0_8.getRoutingKey(), returnedPubBody_0_8.getRoutingKey());
+
+ ContentHeaderBody returnedHeaderBody_0_8 = returnedMMD_0_8.getContentHeaderBody();
+ assertEquals("ContentHeader ClassID has changed", chb_0_8.classId, returnedHeaderBody_0_8.classId);
+ assertEquals("ContentHeader weight has changed", chb_0_8.weight, returnedHeaderBody_0_8.weight);
+ assertEquals("ContentHeader bodySize has changed", chb_0_8.bodySize, returnedHeaderBody_0_8.bodySize);
+
+ BasicContentHeaderProperties returnedProperties_0_8 = (BasicContentHeaderProperties) returnedHeaderBody_0_8.getProperties();
+ assertEquals("Property ContentType has changed", props_0_8.getContentTypeAsString(), returnedProperties_0_8.getContentTypeAsString());
+ assertEquals("Property MessageID has changed", props_0_8.getMessageIdAsString(), returnedProperties_0_8.getMessageIdAsString());
+
+ ByteBuffer recoveredContent_0_8 = ByteBuffer.allocate((int) chb_0_8.bodySize) ;
+ long recoveredCount_0_8 = bdbStore.getContent(messageid_0_8, 0, recoveredContent_0_8);
+ assertEquals("Incorrect amount of payload data recovered", chb_0_8.bodySize, recoveredCount_0_8);
+ String returnedPayloadString_0_8 = new String(recoveredContent_0_8.array());
+ assertEquals("Message Payload has changed", bodyText, returnedPayloadString_0_8);
+
+ /*
+ * Read back and validate the 0-10 message metadata and content
+ */
+ StorableMessageMetaData storeableMMD_0_10 = bdbStore.getMessageMetaData(messageid_0_10);
+
+ assertEquals("Unexpected message type",MessageMetaDataType.META_DATA_0_10, storeableMMD_0_10.getType());
+ assertTrue("Unexpected instance type", storeableMMD_0_10 instanceof MessageMetaData_0_10);
+ MessageMetaData_0_10 returnedMMD_0_10 = (MessageMetaData_0_10) storeableMMD_0_10;
+
+ assertEquals("Message arrival time has changed", origArrivalTime_0_10, returnedMMD_0_10.getArrivalTime());
+
+ DeliveryProperties returnedDelProps_0_10 = returnedMMD_0_10.getHeader().get(DeliveryProperties.class);
+ assertNotNull("DeliveryProperties were not returned", returnedDelProps_0_10);
+ assertEquals("Immediate flag has changed", delProps_0_10.getImmediate(), returnedDelProps_0_10.getImmediate());
+ assertEquals("Routing key has changed", delProps_0_10.getRoutingKey(), returnedDelProps_0_10.getRoutingKey());
+ assertEquals("Message exchange has changed", delProps_0_10.getExchange(), returnedDelProps_0_10.getExchange());
+ assertEquals("Message expiration has changed", delProps_0_10.getExpiration(), returnedDelProps_0_10.getExpiration());
+ assertEquals("Message delivery priority has changed", delProps_0_10.getPriority(), returnedDelProps_0_10.getPriority());
+
+ MessageProperties returnedMsgProps = returnedMMD_0_10.getHeader().get(MessageProperties.class);
+ assertNotNull("MessageProperties were not returned", returnedMsgProps);
+ assertTrue("Message correlationID has changed", Arrays.equals(msgProps_0_10.getCorrelationId(), returnedMsgProps.getCorrelationId()));
+ assertEquals("Message content length has changed", msgProps_0_10.getContentLength(), returnedMsgProps.getContentLength());
+ assertEquals("Message content type has changed", msgProps_0_10.getContentType(), returnedMsgProps.getContentType());
+
+ ByteBuffer recoveredContent = ByteBuffer.allocate((int) msgProps_0_10.getContentLength()) ;
+ long recoveredCount = bdbStore.getContent(messageid_0_10, 0, recoveredContent);
+ assertEquals("Incorrect amount of payload data recovered", msgProps_0_10.getContentLength(), recoveredCount);
+
+ String returnedPayloadString_0_10 = new String(recoveredContent.array());
+ assertEquals("Message Payload has changed", bodyText, returnedPayloadString_0_10);
+ }
+
+ private DeliveryProperties createDeliveryProperties_0_10()
+ {
+ DeliveryProperties delProps_0_10 = new DeliveryProperties();
+
+ delProps_0_10.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
+ delProps_0_10.setImmediate(true);
+ delProps_0_10.setExchange("exchange12345");
+ delProps_0_10.setRoutingKey("routingKey12345");
+ delProps_0_10.setExpiration(5);
+ delProps_0_10.setPriority(MessageDeliveryPriority.ABOVE_AVERAGE);
+
+ return delProps_0_10;
+ }
+
+ private MessageProperties createMessageProperties_0_10(int bodySize)
+ {
+ MessageProperties msgProps_0_10 = new MessageProperties();
+ msgProps_0_10.setContentLength(bodySize);
+ msgProps_0_10.setCorrelationId("qwerty".getBytes());
+ msgProps_0_10.setContentType("text/html");
+
+ return msgProps_0_10;
+ }
+
+ /**
+ * Close the provided store and create a new (read-only) store to read back the data.
+ *
+ * Use this method instead of reloading the virtual host like other tests in order
+ * to avoid the recovery handler deleting the message for not being on a queue.
+ */
+ private BDBMessageStore reloadStoreReadOnly(BDBMessageStore messageStore) throws Exception
+ {
+ messageStore.close();
+ File storePath = new File(String.valueOf(_config.getProperty("store.environment-path")));
+
+ BDBMessageStore newStore = new BDBMessageStore();
+ newStore.configure(storePath, false);
+ newStore.start();
+
+ return newStore;
+ }
+
+ private MessagePublishInfo createPublishInfoBody_0_8()
+ {
+ return new MessagePublishInfo()
+ {
+ public AMQShortString getExchange()
+ {
+ return new AMQShortString("exchange12345");
+ }
+
+ public void setExchange(AMQShortString exchange)
+ {
+ }
+
+ public boolean isImmediate()
+ {
+ return false;
+ }
+
+ public boolean isMandatory()
+ {
+ return true;
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return new AMQShortString("routingKey12345");
+ }
+ };
+
+ }
+
+ private ContentHeaderBody createContentHeaderBody_0_8(BasicContentHeaderProperties props, int length)
+ {
+ MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v0_9);
+ int classForBasic = methodRegistry.createBasicQosOkBody().getClazz();
+ return new ContentHeaderBody(classForBasic, 1, props, length);
+ }
+
+ private BasicContentHeaderProperties createContentHeaderProperties_0_8()
+ {
+ BasicContentHeaderProperties props = new BasicContentHeaderProperties();
+ props.setDeliveryMode(Integer.valueOf(BasicContentHeaderProperties.PERSISTENT).byteValue());
+ props.setContentType("text/html");
+ props.getHeaders().setString("Test", "MST");
+ return props;
+ }
+
+ /**
+ * Tests that messages which are added to the store and then removed using the
+ * public MessageStore interfaces are actually removed from the store by then
+ * interrogating the store with its own implementation methods and verifying
+ * expected exceptions are thrown to indicate the message is not present.
+ */
+ public void testMessageCreationAndRemoval() throws Exception
+ {
+ MessageStore store = getVirtualHost().getMessageStore();
+ BDBMessageStore bdbStore = assertBDBStore(store);
+
+ StoredMessage<MessageMetaData> storedMessage_0_8 = createAndStoreMultiChunkMessage_0_8(store);
+ long messageid_0_8 = storedMessage_0_8.getMessageNumber();
+
+ //remove the message in the fashion the broker normally would
+ storedMessage_0_8.remove();
+
+ //verify the removal using the BDB store implementation methods directly
+ try
+ {
+ // the next line should throw since the message id should not be found
+ bdbStore.getMessageMetaData(messageid_0_8);
+ fail("No exception thrown when message id not found getting metadata");
+ }
+ catch (AMQStoreException e)
+ {
+ // pass since exception expected
+ }
+
+ //expecting no content, allocate a 1 byte
+ ByteBuffer dst = ByteBuffer.allocate(1);
+
+ assertEquals("Retrieved content when none was expected",
+ 0, bdbStore.getContent(messageid_0_8, 0, dst));
+ }
+
+ private BDBMessageStore assertBDBStore(Object store)
+ {
+ if(!(store instanceof BDBMessageStore))
+ {
+ fail("Test requires an instance of BDBMessageStore to proceed");
+ }
+
+ return (BDBMessageStore) store;
+ }
+
+ private StoredMessage<MessageMetaData> createAndStoreMultiChunkMessage_0_8(MessageStore store)
+ {
+ byte[] body10Bytes = "0123456789".getBytes();
+ byte[] body5Bytes = "01234".getBytes();
+
+ ByteBuffer chunk1 = ByteBuffer.wrap(body10Bytes);
+ ByteBuffer chunk2 = ByteBuffer.wrap(body5Bytes);
+
+ int bodySize = body10Bytes.length + body5Bytes.length;
+
+ //create and store the message using the MessageStore interface
+ MessagePublishInfo pubInfoBody_0_8 = createPublishInfoBody_0_8();
+ BasicContentHeaderProperties props_0_8 = createContentHeaderProperties_0_8();
+
+ ContentHeaderBody chb_0_8 = createContentHeaderBody_0_8(props_0_8, bodySize);
+
+ MessageMetaData messageMetaData_0_8 = new MessageMetaData(pubInfoBody_0_8, chb_0_8, 0);
+ StoredMessage<MessageMetaData> storedMessage_0_8 = store.addMessage(messageMetaData_0_8);
+
+ storedMessage_0_8.addContent(0, chunk1);
+ storedMessage_0_8.addContent(chunk1.limit(), chunk2);
+ storedMessage_0_8.flushToStore();
+
+ return storedMessage_0_8;
+ }
+
+ /**
+ * Tests transaction commit by utilising the enqueue and dequeue methods available
+ * in the TransactionLog interface implemented by the store, and verifying the
+ * behaviour using BDB implementation methods.
+ */
+ public void testTranCommit() throws Exception
+ {
+ TransactionLog log = getVirtualHost().getTransactionLog();
+
+ BDBMessageStore bdbStore = assertBDBStore(log);
+
+ final AMQShortString mockQueueName = new AMQShortString("queueName");
+
+ TransactionLogResource mockQueue = new TransactionLogResource()
+ {
+ public String getResourceName()
+ {
+ return mockQueueName.asString();
+ }
+ };
+
+ TransactionLog.Transaction txn = log.newTransaction();
+
+ txn.enqueueMessage(mockQueue, 1L);
+ txn.enqueueMessage(mockQueue, 5L);
+ txn.commitTran();
+
+ List<Long> enqueuedIds = bdbStore.getEnqueuedMessages(mockQueueName);
+
+ assertEquals("Number of enqueued messages is incorrect", 2, enqueuedIds.size());
+ Long val = enqueuedIds.get(0);
+ assertEquals("First Message is incorrect", 1L, val.longValue());
+ val = enqueuedIds.get(1);
+ assertEquals("Second Message is incorrect", 5L, val.longValue());
+ }
+
+
+ /**
+ * Tests transaction rollback before a commit has occurred by utilising the
+ * enqueue and dequeue methods available in the TransactionLog interface
+ * implemented by the store, and verifying the behaviour using BDB
+ * implementation methods.
+ */
+ public void testTranRollbackBeforeCommit() throws Exception
+ {
+ TransactionLog log = getVirtualHost().getTransactionLog();
+
+ BDBMessageStore bdbStore = assertBDBStore(log);
+
+ final AMQShortString mockQueueName = new AMQShortString("queueName");
+
+ TransactionLogResource mockQueue = new TransactionLogResource()
+ {
+ public String getResourceName()
+ {
+ return mockQueueName.asString();
+ }
+ };
+
+ TransactionLog.Transaction txn = log.newTransaction();
+
+ txn.enqueueMessage(mockQueue, 21L);
+ txn.abortTran();
+
+ txn = log.newTransaction();
+ txn.enqueueMessage(mockQueue, 22L);
+ txn.enqueueMessage(mockQueue, 23L);
+ txn.commitTran();
+
+ List<Long> enqueuedIds = bdbStore.getEnqueuedMessages(mockQueueName);
+
+ assertEquals("Number of enqueued messages is incorrect", 2, enqueuedIds.size());
+ Long val = enqueuedIds.get(0);
+ assertEquals("First Message is incorrect", 22L, val.longValue());
+ val = enqueuedIds.get(1);
+ assertEquals("Second Message is incorrect", 23L, val.longValue());
+ }
+
+ /**
+ * Tests transaction rollback after a commit has occurred by utilising the
+ * enqueue and dequeue methods available in the TransactionLog interface
+ * implemented by the store, and verifying the behaviour using BDB
+ * implementation methods.
+ */
+ public void testTranRollbackAfterCommit() throws Exception
+ {
+ TransactionLog log = getVirtualHost().getTransactionLog();
+
+ BDBMessageStore bdbStore = assertBDBStore(log);
+
+ final AMQShortString mockQueueName = new AMQShortString("queueName");
+
+ TransactionLogResource mockQueue = new TransactionLogResource()
+ {
+ public String getResourceName()
+ {
+ return mockQueueName.asString();
+ }
+ };
+
+ TransactionLog.Transaction txn = log.newTransaction();
+
+ txn.enqueueMessage(mockQueue, 30L);
+ txn.commitTran();
+
+ txn = log.newTransaction();
+ txn.enqueueMessage(mockQueue, 31L);
+ txn.abortTran();
+
+ txn = log.newTransaction();
+ txn.enqueueMessage(mockQueue, 32L);
+ txn.commitTran();
+
+ List<Long> enqueuedIds = bdbStore.getEnqueuedMessages(mockQueueName);
+
+ assertEquals("Number of enqueued messages is incorrect", 2, enqueuedIds.size());
+ Long val = enqueuedIds.get(0);
+ assertEquals("First Message is incorrect", 30L, val.longValue());
+ val = enqueuedIds.get(1);
+ assertEquals("Second Message is incorrect", 32L, val.longValue());
+ }
+
+}
diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBStoreUpgradeTestPreparer.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBStoreUpgradeTestPreparer.java
new file mode 100644
index 0000000000..cc19bcf5d8
--- /dev/null
+++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBStoreUpgradeTestPreparer.java
@@ -0,0 +1,232 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb;
+
+import javax.jms.Connection;
+import javax.jms.DeliveryMode;
+import javax.jms.Destination;
+import javax.jms.ExceptionListener;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.Topic;
+import javax.jms.TopicConnection;
+import javax.jms.TopicPublisher;
+import javax.jms.TopicSession;
+import javax.jms.TopicSubscriber;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.AMQConnectionFactory;
+import org.apache.qpid.url.URLSyntaxException;
+
+/**
+ * Prepares an older version brokers BDB store with the required
+ * contents for use in the BDBStoreUpgradeTest.
+ *
+ * The store will then be used to verify that the upgraded is
+ * completed properly and that once upgraded it functions as
+ * expected with the new broker.
+ */
+public class BDBStoreUpgradeTestPreparer extends TestCase
+{
+ public static final String TOPIC_NAME="myUpgradeTopic";
+ public static final String SUB_NAME="myDurSubName";
+ public static final String QUEUE_NAME="myUpgradeQueue";
+
+ private static AMQConnectionFactory _connFac;
+ private static final String CONN_URL =
+ "amqp://guest:guest@clientid/test?brokerlist='tcp://localhost:5672'";
+
+ /**
+ * Create a BDBStoreUpgradeTestPreparer instance
+ */
+ public BDBStoreUpgradeTestPreparer () throws URLSyntaxException
+ {
+ _connFac = new AMQConnectionFactory(CONN_URL);
+ }
+
+ /**
+ * Utility test method to allow running the preparation tool
+ * using the test framework
+ */
+ public void testPrepareBroker() throws Exception
+ {
+ prepareBroker();
+ }
+
+ private void prepareBroker() throws Exception
+ {
+ prepareQueues();
+ prepareDurableSubscription();
+ }
+
+ /**
+ * Prepare a queue for use in testing message and binding recovery
+ * after the upgrade is performed.
+ *
+ * - Create a transacted session on the connection.
+ * - Use a consumer to create the (durable by default) queue.
+ * - Send 5 large messages to test (multi-frame) content recovery.
+ * - Send 1 small message to test (single-frame) content recovery.
+ * - Commit the session.
+ * - Send 5 small messages to test that uncommitted messages are not recovered.
+ * following the upgrade.
+ * - Close the session.
+ */
+ private void prepareQueues() throws Exception
+ {
+ // Create a connection
+ Connection connection = _connFac.createConnection();
+ connection.start();
+ connection.setExceptionListener(new ExceptionListener()
+ {
+ public void onException(JMSException e)
+ {
+ e.printStackTrace();
+ }
+ });
+ // Create a session on the connection, transacted to confirm delivery
+ Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
+ Queue queue = session.createQueue(QUEUE_NAME);
+ // Create a consumer to ensure the queue gets created
+ // (and enter it into the store, as queues are made durable by default)
+ MessageConsumer messageConsumer = session.createConsumer(queue);
+ messageConsumer.close();
+
+ // Create a Message producer
+ MessageProducer messageProducer = session.createProducer(queue);
+
+ // Publish 5 persistent messages, 256k chars to ensure they are multi-frame
+ sendMessages(session, messageProducer, queue, DeliveryMode.PERSISTENT, 256*1024, 5);
+ // Publish 5 persistent messages, 1k chars to ensure they are single-frame
+ sendMessages(session, messageProducer, queue, DeliveryMode.PERSISTENT, 1*1024, 5);
+
+ session.commit();
+
+ // Publish 5 persistent messages which will NOT be committed and so should be 'lost'
+ sendMessages(session, messageProducer, queue, DeliveryMode.PERSISTENT, 1*1024, 5);
+
+ session.close();
+ connection.close();
+ }
+
+ /**
+ * Prepare a DurableSubscription backing queue for use in testing selector
+ * recovery and queue exclusivity marking during the upgrade process.
+ *
+ * - Create a transacted session on the connection.
+ * - Open and close a DurableSubscription with selector to create the backing queue.
+ * - Send a message which matches the selector.
+ * - Send a message which does not match the selector.
+ * - Send a message which matches the selector but will remain uncommitted.
+ * - Close the session.
+ */
+ private void prepareDurableSubscription() throws Exception
+ {
+
+ // Create a connection
+ TopicConnection connection = _connFac.createTopicConnection();
+ connection.start();
+ connection.setExceptionListener(new ExceptionListener()
+ {
+ public void onException(JMSException e)
+ {
+ e.printStackTrace();
+ }
+ });
+ // Create a session on the connection, transacted to confirm delivery
+ Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
+ Topic topic = session.createTopic(TOPIC_NAME);
+
+ // Create and register a durable subscriber with selector and then close it
+ TopicSubscriber durSub1 = session.createDurableSubscriber(topic, SUB_NAME,"testprop='true'", false);
+ durSub1.close();
+
+ // Create a publisher and send a persistent message which matches the selector
+ // followed by one that does not match, and another which matches but is not
+ // committed and so should be 'lost'
+ TopicSession pubSession = connection.createTopicSession(true, Session.SESSION_TRANSACTED);
+ TopicPublisher publisher = pubSession.createPublisher(topic);
+
+ publishMessages(session, publisher, topic, DeliveryMode.PERSISTENT, 1*1024, 1, "true");
+ publishMessages(session, publisher, topic, DeliveryMode.PERSISTENT, 1*1024, 1, "false");
+ pubSession.commit();
+ publishMessages(session, publisher, topic, DeliveryMode.PERSISTENT, 1*1024, 1, "true");
+
+ publisher.close();
+ pubSession.close();
+
+ }
+
+ public static void sendMessages(Session session, MessageProducer messageProducer,
+ Destination dest, int deliveryMode, int length, int numMesages) throws JMSException
+ {
+ for (int i = 1; i <= numMesages; i++)
+ {
+ Message message = session.createTextMessage(generateString(length));
+ message.setIntProperty("ID", i);
+ messageProducer.send(message, deliveryMode, Message.DEFAULT_PRIORITY, Message.DEFAULT_TIME_TO_LIVE);
+ }
+ }
+
+ public static void publishMessages(Session session, TopicPublisher publisher,
+ Destination dest, int deliveryMode, int length, int numMesages, String selectorProperty) throws JMSException
+ {
+ for (int i = 1; i <= numMesages; i++)
+ {
+ Message message = session.createTextMessage(generateString(length));
+ message.setIntProperty("ID", i);
+ message.setStringProperty("testprop", selectorProperty);
+ publisher.publish(message, deliveryMode, Message.DEFAULT_PRIORITY, Message.DEFAULT_TIME_TO_LIVE);
+ }
+ }
+
+ /**
+ * Generates a string of a given length consisting of the sequence 0,1,2,..,9,0,1,2.
+ *
+ * @param length number of characters in the string
+ * @return string sequence of the given length
+ */
+ public static String generateString(int length)
+ {
+ char[] base_chars = new char[]{'0','1','2','3','4','5','6','7','8','9'};
+ char[] chars = new char[length];
+ for (int i = 0; i < (length); i++)
+ {
+ chars[i] = base_chars[i % 10];
+ }
+ return new String(chars);
+ }
+
+ /**
+ * Run the preparation tool.
+ * @param args Command line arguments.
+ */
+ public static void main(String[] args) throws Exception
+ {
+ BDBStoreUpgradeTestPreparer producer = new BDBStoreUpgradeTestPreparer();
+ producer.prepareBroker();
+ }
+} \ No newline at end of file
diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBUpgradeTest.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBUpgradeTest.java
new file mode 100644
index 0000000000..4861e007af
--- /dev/null
+++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBUpgradeTest.java
@@ -0,0 +1,540 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.berkeleydb;
+
+import static org.apache.qpid.server.store.berkeleydb.BDBStoreUpgradeTestPreparer.QUEUE_NAME;
+import static org.apache.qpid.server.store.berkeleydb.BDBStoreUpgradeTestPreparer.SUB_NAME;
+import static org.apache.qpid.server.store.berkeleydb.BDBStoreUpgradeTestPreparer.TOPIC_NAME;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+
+import javax.jms.Connection;
+import javax.jms.DeliveryMode;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.jms.Topic;
+import javax.jms.TopicConnection;
+import javax.jms.TopicPublisher;
+import javax.jms.TopicSession;
+import javax.jms.TopicSubscriber;
+
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.MethodRegistry;
+import org.apache.qpid.framing.ProtocolVersion;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.framing.abstraction.MessagePublishInfoImpl;
+import org.apache.qpid.management.common.mbeans.ManagedQueue;
+import org.apache.qpid.server.message.MessageMetaData;
+import org.apache.qpid.server.store.TransactionLog;
+import org.apache.qpid.server.store.TransactionLogResource;
+import org.apache.qpid.server.store.berkeleydb.keys.MessageContentKey_4;
+import org.apache.qpid.server.store.berkeleydb.tuples.MessageContentKeyTupleBindingFactory;
+import org.apache.qpid.server.store.berkeleydb.tuples.MessageMetaDataTupleBindingFactory;
+import org.apache.qpid.test.utils.JMXTestUtils;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+import org.apache.qpid.util.FileUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.sleepycat.bind.tuple.TupleBinding;
+import com.sleepycat.je.DatabaseEntry;
+
+/**
+ * Tests upgrading a BDB store and using it with the new broker
+ * after the required contents are entered into the store using
+ * an old broker with the BDBStoreUpgradeTestPreparer. The store
+ * will then be used to verify that the upgraded is completed
+ * properly and that once upgraded it functions as expected with
+ * the new broker.
+ */
+public class BDBUpgradeTest extends QpidBrokerTestCase
+{
+ protected static final Logger _logger = LoggerFactory.getLogger(BDBUpgradeTest.class);
+
+ private static final String STRING_1024 = BDBStoreUpgradeTestPreparer.generateString(1024);
+ private static final String STRING_1024_256 = BDBStoreUpgradeTestPreparer.generateString(1024*256);
+ private static final String QPID_WORK_ORIG = System.getProperty("QPID_WORK");
+ private static final String QPID_HOME = System.getProperty("QPID_HOME");
+ private static final int VERSION_4 = 4;
+
+ private String _fromDir;
+ private String _toDir;
+ private String _toDirTwice;
+
+ @Override
+ public void setUp() throws Exception
+ {
+ assertNotNull("QPID_WORK must be set", QPID_WORK_ORIG);
+ assertNotNull("QPID_HOME must be set", QPID_HOME);
+
+ if(! isExternalBroker())
+ {
+ //override QPID_WORK to add the InVM port used so the store
+ //output from the upgrade tool can be found by the broker
+ setSystemProperty("QPID_WORK", QPID_WORK_ORIG + "/" + getPort());
+ }
+
+ _fromDir = QPID_HOME + "/bdbstore-to-upgrade/test-store";
+ _toDir = getWorkDirBaseDir() + "/bdbstore/test-store";
+ _toDirTwice = getWorkDirBaseDir() + "/bdbstore-upgraded-twice";
+
+ //Clear the two target directories if they exist.
+ File directory = new File(_toDir);
+ if (directory.exists() && directory.isDirectory())
+ {
+ FileUtils.delete(directory, true);
+ }
+ directory = new File(_toDirTwice);
+ if (directory.exists() && directory.isDirectory())
+ {
+ FileUtils.delete(directory, true);
+ }
+
+ //Upgrade the test store.
+ upgradeBrokerStore(_fromDir, _toDir);
+
+ //override the broker config used and then start the broker with the updated store
+ _configFile = new File("build/etc/config-systests-bdb.xml");
+ setConfigurationProperty("management.enabled", "true");
+
+ super.setUp();
+ }
+
+ private String getWorkDirBaseDir()
+ {
+ return QPID_WORK_ORIG + (isInternalBroker() ? "" : "/" + getPort());
+ }
+
+ /**
+ * Tests that the core upgrade method of the store upgrade tool passes through the exception
+ * from the BDBMessageStore indicating that the data on disk can't be loaded as the previous
+ * version because it has already been upgraded.
+ * @throws Exception
+ */
+ public void testMultipleUpgrades() throws Exception
+ {
+ //stop the broker started by setUp() in order to allow the second upgrade attempt to proceed
+ stopBroker();
+
+ try
+ {
+ new BDBStoreUpgrade(_toDir, _toDirTwice, null, false, true).upgradeFromVersion(VERSION_4);
+ fail("Second Upgrade Succeeded");
+ }
+ catch (Exception e)
+ {
+ System.err.println("Showing stack trace, we are expecting an 'Unable to load BDBStore' error");
+ e.printStackTrace();
+ assertTrue("Incorrect Exception Thrown:" + e.getMessage(),
+ e.getMessage().contains("Unable to load BDBStore as version 4. Store on disk contains version 5 data"));
+ }
+ }
+
+ /**
+ * Test that the selector applied to the DurableSubscription was successfully
+ * transfered to the new store, and functions as expected with continued use
+ * by monitoring message count while sending new messages to the topic.
+ */
+ public void testSelectorDurability() throws Exception
+ {
+ JMXTestUtils jmxUtils = null;
+ try
+ {
+ jmxUtils = new JMXTestUtils(this, "guest", "guest");
+ jmxUtils.open();
+ }
+ catch (Exception e)
+ {
+ fail("Unable to establish JMX connection, test cannot proceed");
+ }
+
+ try
+ {
+ ManagedQueue dursubQueue = jmxUtils.getManagedQueue("clientid" + ":" + SUB_NAME);
+ assertEquals("DurableSubscription backing queue should have 1 message on it initially",
+ new Integer(1), dursubQueue.getMessageCount());
+
+ // Create a connection and start it
+ TopicConnection connection = (TopicConnection) getConnection();
+ connection.start();
+
+ // Send messages which don't match and do match the selector, checking message count
+ TopicSession pubSession = connection.createTopicSession(true, org.apache.qpid.jms.Session.SESSION_TRANSACTED);
+ Topic topic = pubSession.createTopic(TOPIC_NAME);
+ TopicPublisher publisher = pubSession.createPublisher(topic);
+
+ BDBStoreUpgradeTestPreparer.publishMessages(pubSession, publisher, topic, DeliveryMode.PERSISTENT, 1*1024, 1, "false");
+ pubSession.commit();
+ assertEquals("DurableSubscription backing queue should still have 1 message on it",
+ new Integer(1), dursubQueue.getMessageCount());
+
+ BDBStoreUpgradeTestPreparer.publishMessages(pubSession, publisher, topic, DeliveryMode.PERSISTENT, 1*1024, 1, "true");
+ pubSession.commit();
+ assertEquals("DurableSubscription backing queue should now have 2 messages on it",
+ new Integer(2), dursubQueue.getMessageCount());
+
+ dursubQueue.clearQueue();
+ pubSession.close();
+ }
+ finally
+ {
+ jmxUtils.close();
+ }
+ }
+
+ /**
+ * Test that the backing queue for the durable subscription created was successfully
+ * detected and set as being exclusive during the upgrade process, and that the
+ * regular queue was not.
+ */
+ public void testQueueExclusivity() throws Exception
+ {
+ JMXTestUtils jmxUtils = null;
+ try
+ {
+ jmxUtils = new JMXTestUtils(this, "guest", "guest");
+ jmxUtils.open();
+ }
+ catch (Exception e)
+ {
+ fail("Unable to establish JMX connection, test cannot proceed");
+ }
+
+ try
+ {
+ ManagedQueue queue = jmxUtils.getManagedQueue(QUEUE_NAME);
+ assertFalse("Queue should not have been marked as Exclusive during upgrade", queue.isExclusive());
+
+ ManagedQueue dursubQueue = jmxUtils.getManagedQueue("clientid" + ":" + SUB_NAME);
+ assertTrue("DurableSubscription backing queue should have been marked as Exclusive during upgrade", dursubQueue.isExclusive());
+ }
+ finally
+ {
+ jmxUtils.close();
+ }
+ }
+
+ /**
+ * Test that the upgraded queue continues to function properly when used
+ * for persistent messaging and restarting the broker.
+ *
+ * Sends the new messages to the queue BEFORE consuming those which were
+ * sent before the upgrade. In doing so, this also serves to test that
+ * the queue bindings were successfully transitioned during the upgrade.
+ */
+ public void testBindingAndMessageDurabability() throws Exception
+ {
+ // Create a connection and start it
+ TopicConnection connection = (TopicConnection) getConnection();
+ connection.start();
+
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Queue queue = session.createQueue(QUEUE_NAME);
+ MessageProducer messageProducer = session.createProducer(queue);
+
+ // Send a new message
+ BDBStoreUpgradeTestPreparer.sendMessages(session, messageProducer, queue, DeliveryMode.PERSISTENT, 256*1024, 1);
+
+ session.close();
+
+ // Restart the broker
+ restartBroker();
+
+ // Drain the queue of all messages
+ connection = (TopicConnection) getConnection();
+ connection.start();
+ consumeQueueMessages(connection, true);
+ }
+
+ /**
+ * Test that all of the committed persistent messages previously sent to
+ * the broker are properly received following update of the MetaData and
+ * Content entries during the store upgrade process.
+ */
+ public void testConsumptionOfUpgradedMessages() throws Exception
+ {
+ // Create a connection and start it
+ Connection connection = getConnection();
+ connection.start();
+
+ consumeDurableSubscriptionMessages(connection);
+ consumeQueueMessages(connection, false);
+ }
+
+ /**
+ * Tests store migration containing messages for non-existing queue.
+ *
+ * @throws Exception
+ */
+ public void testMigrationOfMessagesForNonExistingQueues() throws Exception
+ {
+ stopBroker();
+
+ // copy store data into a new location for adding of phantom message
+ File storeLocation = new File(_fromDir);
+ File target = new File(_toDirTwice);
+ if (!target.exists())
+ {
+ target.mkdirs();
+ }
+ FileUtils.copyRecursive(storeLocation, target);
+
+ // delete migrated data
+ File directory = new File(_toDir);
+ if (directory.exists() && directory.isDirectory())
+ {
+ FileUtils.delete(directory, true);
+ }
+
+ // test data
+ String nonExistingQueueName = getTestQueueName();
+ String messageText = "Test Phantom Message";
+
+ // add message
+ addMessageForNonExistingQueue(target, VERSION_4, nonExistingQueueName, messageText);
+
+ String[] inputs = { "Yes", "Yes", "Yes" };
+ upgradeBrokerStoreInInterractiveMode(_toDirTwice, _toDir, inputs);
+
+ // start broker
+ startBroker();
+
+ // Create a connection and start it
+ Connection connection = getConnection();
+ connection.start();
+
+ // consume a message for non-existing store
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Queue queue = session.createQueue(nonExistingQueueName);
+ MessageConsumer messageConsumer = session.createConsumer(queue);
+ Message message = messageConsumer.receive(1000);
+
+ // assert consumed message
+ assertNotNull("Message was not migrated!", message);
+ assertTrue("Unexpected message received!", message instanceof TextMessage);
+ String text = ((TextMessage) message).getText();
+ assertEquals("Message migration failed!", messageText, text);
+ }
+
+ /**
+ * An utility method to upgrade broker with simulation user interactions
+ *
+ * @param fromDir
+ * location of the store to migrate
+ * @param toDir
+ * location of where migrated data will be stored
+ * @param inputs
+ * user answers on upgrade tool questions
+ * @throws Exception
+ */
+ private void upgradeBrokerStoreInInterractiveMode(String fromDir, String toDir, final String[] inputs)
+ throws Exception
+ {
+ // save to restore system.in after data migration
+ InputStream stdin = System.in;
+
+ // set fake system in to simulate user interactions
+ // FIXME: it is a quite dirty simulator of system input but it does the job
+ System.setIn(new InputStream()
+ {
+
+ int counter = 0;
+
+ public synchronized int read(byte b[], int off, int len)
+ {
+ byte[] src = (inputs[counter] + "\n").getBytes();
+ System.arraycopy(src, 0, b, off, src.length);
+ counter++;
+ return src.length;
+ }
+
+ @Override
+ public int read() throws IOException
+ {
+ return -1;
+ }
+ });
+
+ try
+ {
+ // Upgrade the test store.
+ new BDBStoreUpgrade(fromDir, toDir, null, true, true).upgradeFromVersion(VERSION_4);
+ }
+ finally
+ {
+ // restore system in
+ System.setIn(stdin);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private void addMessageForNonExistingQueue(File storeLocation, int storeVersion, String nonExistingQueueName,
+ String messageText) throws Exception
+ {
+ final AMQShortString queueName = new AMQShortString(nonExistingQueueName);
+ BDBMessageStore store = new BDBMessageStore(storeVersion);
+ store.configure(storeLocation, false);
+ try
+ {
+ store.start();
+
+ // store message objects
+ ByteBuffer completeContentBody = ByteBuffer.wrap(messageText.getBytes("UTF-8"));
+ long bodySize = completeContentBody.limit();
+ MessagePublishInfo pubInfoBody = new MessagePublishInfoImpl(new AMQShortString("amq.direct"), false,
+ false, queueName);
+ BasicContentHeaderProperties props = new BasicContentHeaderProperties();
+ props.setDeliveryMode(Integer.valueOf(BasicContentHeaderProperties.PERSISTENT).byteValue());
+ props.setContentType("text/plain");
+ props.setType("text/plain");
+ props.setMessageId("whatever");
+ props.setEncoding("UTF-8");
+ props.getHeaders().setString("Test", "MST");
+ MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v0_9);
+ int classForBasic = methodRegistry.createBasicQosOkBody().getClazz();
+ ContentHeaderBody contentHeaderBody = new ContentHeaderBody(classForBasic, 1, props, bodySize);
+
+ // add content entry to database
+ long messageId = store.getNewMessageId();
+ TupleBinding<MessageContentKey> contentKeyTB = new MessageContentKeyTupleBindingFactory(storeVersion).getInstance();
+ MessageContentKey contentKey = null;
+ if (storeVersion == VERSION_4)
+ {
+ contentKey = new MessageContentKey_4(messageId, 0);
+ }
+ else
+ {
+ throw new Exception(storeVersion + " is not supported");
+ }
+ DatabaseEntry key = new DatabaseEntry();
+ contentKeyTB.objectToEntry(contentKey, key);
+ DatabaseEntry data = new DatabaseEntry();
+ ContentTB contentTB = new ContentTB();
+ contentTB.objectToEntry(completeContentBody, data);
+ store.getContentDb().put(null, key, data);
+
+ // add meta data entry to database
+ TupleBinding<Long> longTB = TupleBinding.getPrimitiveBinding(Long.class);
+ TupleBinding<Object> metaDataTB = new MessageMetaDataTupleBindingFactory(storeVersion).getInstance();
+ key = new DatabaseEntry();
+ data = new DatabaseEntry();
+ longTB.objectToEntry(new Long(messageId), key);
+ MessageMetaData metaData = new MessageMetaData(pubInfoBody, contentHeaderBody, 1);
+ metaDataTB.objectToEntry(metaData, data);
+ store.getMetaDataDb().put(null, key, data);
+
+ // add delivery entry to database
+ TransactionLogResource mockQueue = new TransactionLogResource()
+ {
+ public String getResourceName()
+ {
+ return queueName.asString();
+ }
+ };
+ TransactionLog log = (TransactionLog) store;
+ TransactionLog.Transaction txn = log.newTransaction();
+ txn.enqueueMessage(mockQueue, messageId);
+ txn.commitTran();
+ }
+ finally
+ {
+ // close store
+ store.close();
+ }
+ }
+
+ private void consumeDurableSubscriptionMessages(Connection connection) throws Exception
+ {
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Topic topic = session.createTopic(TOPIC_NAME);
+
+ TopicSubscriber durSub = session.createDurableSubscriber(topic, SUB_NAME,"testprop='true'", false);
+
+ // Retrieve the matching message
+ Message m = durSub.receive(2000);
+ assertNotNull("Failed to receive an expected message", m);
+ assertEquals("Selector property did not match", "true", m.getStringProperty("testprop"));
+ assertEquals("ID property did not match", 1, m.getIntProperty("ID"));
+ assertEquals("Message content was not as expected",BDBStoreUpgradeTestPreparer.generateString(1024) , ((TextMessage)m).getText());
+
+ // Verify that neither the non-matching or uncommitted message are received
+ m = durSub.receive(1000);
+ assertNull("No more messages should have been recieved", m);
+
+ durSub.close();
+ session.close();
+ }
+
+ private void consumeQueueMessages(Connection connection, boolean extraMessage) throws Exception
+ {
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Queue queue = session.createQueue(QUEUE_NAME);
+
+ MessageConsumer consumer = session.createConsumer(queue);
+ Message m;
+
+ // Retrieve the initial pre-upgrade messages
+ for (int i=1; i <= 5 ; i++)
+ {
+ m = consumer.receive(2000);
+ assertNotNull("Failed to receive an expected message", m);
+ assertEquals("ID property did not match", i, m.getIntProperty("ID"));
+ assertEquals("Message content was not as expected", STRING_1024_256, ((TextMessage)m).getText());
+ }
+ for (int i=1; i <= 5 ; i++)
+ {
+ m = consumer.receive(2000);
+ assertNotNull("Failed to receive an expected message", m);
+ assertEquals("ID property did not match", i, m.getIntProperty("ID"));
+ assertEquals("Message content was not as expected", STRING_1024, ((TextMessage)m).getText());
+ }
+
+ if(extraMessage)
+ {
+ //verify that the extra message is received
+ m = consumer.receive(2000);
+ assertNotNull("Failed to receive an expected message", m);
+ assertEquals("ID property did not match", 1, m.getIntProperty("ID"));
+ assertEquals("Message content was not as expected", STRING_1024_256, ((TextMessage)m).getText());
+ }
+
+ // Verify that no more messages are received
+ m = consumer.receive(1000);
+ assertNull("No more messages should have been recieved", m);
+
+ consumer.close();
+ session.close();
+ }
+
+ private void upgradeBrokerStore(String fromDir, String toDir) throws Exception
+ {
+ new BDBStoreUpgrade(_fromDir, _toDir, null, false, true).upgradeFromVersion(VERSION_4);
+ }
+}
diff --git a/qpid/java/bdbstore/src/test/resources/upgrade/bdbstore-to-upgrade/test-store/00000000.jdb b/qpid/java/bdbstore/src/test/resources/upgrade/bdbstore-to-upgrade/test-store/00000000.jdb
new file mode 100644
index 0000000000..c4e4e6c306
--- /dev/null
+++ b/qpid/java/bdbstore/src/test/resources/upgrade/bdbstore-to-upgrade/test-store/00000000.jdb
Binary files differ
diff --git a/qpid/java/broker-plugins/experimental/info/build.xml b/qpid/java/broker-plugins/experimental/info/build.xml
index c5881aa839..8f91adc5ff 100644
--- a/qpid/java/broker-plugins/experimental/info/build.xml
+++ b/qpid/java/broker-plugins/experimental/info/build.xml
@@ -20,7 +20,14 @@ nn - or more contributor license agreements. See the NOTICE file
-->
<project name="AMQ Broker Info Plugin" default="build">
- <property name="module.depends" value="common broker broker-plugins"/>
+ <condition property="info-plugin.optional.depends" value="bdbstore" else="">
+ <and>
+ <contains string="${modules.opt}" substring="bdbstore"/>
+ <contains string="${profile}" substring="bdb"/>
+ </and>
+ </condition>
+
+ <property name="module.depends" value="common broker broker-plugins ${info-plugin.optional.depends}"/>
<property name="module.test.depends" value="test broker/test management/common client systests common/test"/>
<property name="module.manifest" value="MANIFEST.MF"/>
<property name="module.plugin" value="true"/>
diff --git a/qpid/java/broker/bin/qpid-server b/qpid/java/broker/bin/qpid-server
index 90b11da202..382004c9f5 100755
--- a/qpid/java/broker/bin/qpid-server
+++ b/qpid/java/broker/bin/qpid-server
@@ -33,8 +33,8 @@ if [ -z "$QPID_PNAME" ]; then
export QPID_PNAME=" -DPNAME=QPBRKR"
fi
-# Set classpath to include Qpid jar with all required jars in manifest
-QPID_LIBS=$QPID_HOME/lib/qpid-all.jar
+# Set classpath to include the qpid-all manifest jar, and any jars supplied in lib/opt
+QPID_LIBS="$QPID_HOME/lib/qpid-all.jar:$QPID_HOME/lib/opt/*"
# Set other variables used by the qpid-run script before calling
export JAVA=java \
@@ -51,6 +51,6 @@ QPID_OPTS="$QPID_OPTS -Damqj.read_write_pool_size=32 -DQPID_LOG_APPEND=$QPID_LOG
if [ -z "$QPID_PID_FILENAME" ]; then
export QPID_PID_FILENAME="qpid-server.pid"
fi
-echo $$ > ${QPID_WORK}/${QPID_PID_FILENAME}
+echo $$ > "${QPID_WORK}/${QPID_PID_FILENAME}"
-. ${QPID_HOME}/bin/qpid-run org.apache.qpid.server.Main "$@"
+. "${QPID_HOME}/bin/qpid-run" org.apache.qpid.server.Main "$@"
diff --git a/qpid/java/broker/build.xml b/qpid/java/broker/build.xml
index edd71effaa..e733474ef0 100644
--- a/qpid/java/broker/build.xml
+++ b/qpid/java/broker/build.xml
@@ -76,6 +76,10 @@
<copy todir="${module.release}/lib/plugins" failonerror="true">
<fileset dir="${build.lib}/plugins"/>
</copy>
+ <!--copy optional bdbstore module if it exists -->
+ <copy todir="${module.release}/lib/" failonerror="false">
+ <fileset file="${build.lib}/${project.name}-bdbstore-${project.version}.jar"/>
+ </copy>
</target>
<target name="release-bin" depends="release-bin-tasks"/>
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java
index a5999711bc..765dee2878 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java
@@ -144,7 +144,7 @@ public class BasicConsumeMethodHandler implements StateAwareMethodListener<Basic
_logger.debug("Closing connection due to invalid selector");
MethodRegistry methodRegistry = protocolConnection.getMethodRegistry();
- AMQMethodBody responseBody = methodRegistry.createChannelCloseBody(AMQConstant.INVALID_ARGUMENT.getCode(),
+ AMQMethodBody responseBody = methodRegistry.createChannelCloseBody(AMQConstant.ARGUMENT_INVALID.getCode(),
new AMQShortString(ise.getMessage()),
body.getClazz(),
body.getMethod());
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java
index 028f6d85be..67ddd6ca77 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java
@@ -645,7 +645,7 @@ public class ServerSession extends Session implements AuthorizationHolder, Sessi
// Log a warning on idle or open transactions
if (idleWarn > 0L && idleTime > idleWarn)
{
- CurrentActor.get().message(getLogSubject(), ChannelMessages.IDLE_TXN(openTime));
+ CurrentActor.get().message(getLogSubject(), ChannelMessages.IDLE_TXN(idleTime));
_logger.warn("IDLE TRANSACTION ALERT " + getLogSubject().toString() + " " + idleTime + " ms");
}
else if (openWarn > 0L && openTime > openWarn)
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java
index 22760318b5..17bd06538f 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java
@@ -606,6 +606,12 @@ public class ServerSessionDelegate extends SessionDelegate
try
{
+ if (nameNullOrEmpty(method.getExchange()))
+ {
+ exception(session, method, ExecutionErrorCode.INVALID_ARGUMENT, "Delete not allowed for default exchange");
+ return;
+ }
+
Exchange exchange = getExchange(session, method.getExchange());
if(exchange == null)
@@ -641,6 +647,16 @@ public class ServerSessionDelegate extends SessionDelegate
}
}
+ private boolean nameNullOrEmpty(String name)
+ {
+ if(name == null || name.length() == 0)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
private boolean isStandardExchange(Exchange exchange, Collection<ExchangeType<? extends Exchange>> registeredTypes)
{
for(ExchangeType type : registeredTypes)
@@ -687,9 +703,9 @@ public class ServerSessionDelegate extends SessionDelegate
{
exception(session, method, ExecutionErrorCode.ILLEGAL_ARGUMENT, "queue not set");
}
- else if (!method.hasExchange())
+ else if (nameNullOrEmpty(method.getExchange()))
{
- exception(session, method, ExecutionErrorCode.ILLEGAL_ARGUMENT, "exchange not set");
+ exception(session, method, ExecutionErrorCode.INVALID_ARGUMENT, "Bind not allowed for default exchange");
}
/*
else if (!method.hasBindingKey())
@@ -758,9 +774,9 @@ public class ServerSessionDelegate extends SessionDelegate
{
exception(session, method, ExecutionErrorCode.ILLEGAL_ARGUMENT, "queue not set");
}
- else if (!method.hasExchange())
+ else if (nameNullOrEmpty(method.getExchange()))
{
- exception(session, method, ExecutionErrorCode.ILLEGAL_ARGUMENT, "exchange not set");
+ exception(session, method, ExecutionErrorCode.INVALID_ARGUMENT, "Unbind not allowed for default exchange");
}
else if (!method.hasBindingKey())
{
@@ -790,9 +806,6 @@ public class ServerSessionDelegate extends SessionDelegate
}
}
}
-
-
- super.exchangeUnbind(session, method);
}
@Override
diff --git a/qpid/java/build.deps b/qpid/java/build.deps
index 2c56a4a911..d0691275ff 100644
--- a/qpid/java/build.deps
+++ b/qpid/java/build.deps
@@ -135,3 +135,8 @@ broker-plugins-experimental-info.test.libs=${test.libs} ${servlet-api} ${jetty}
management-eclipse-plugin.test.libs=${test.libs}
management-common.test.libs=${test.libs}
+
+# optional bdbstore module deps
+bdb-je=lib/bdbstore/je-4.0.103.jar
+bdbstore.libs=${bdb-je}
+bdbstore.test.libs=${test.libs}
diff --git a/qpid/java/test-profiles/clean-dir b/qpid/java/build.overrides
index 4d6141b4ab..dbe05b4ec0 100755..100644
--- a/qpid/java/test-profiles/clean-dir
+++ b/qpid/java/build.overrides
@@ -1,7 +1,4 @@
-
-#!/bin/bash
#
-#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
@@ -9,17 +6,19 @@
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
-#
+#
# http://www.apache.org/licenses/LICENSE-2.0
-#
+#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-#
#
+# File to allow overriding default values of properties within the build system
+# without having to specify them on the command line on every build execution
-rm -rf $@; mkdir $@
+# Override the setting for the optional modules to be built
+#modules.opt=bdbstore
diff --git a/qpid/java/build.xml b/qpid/java/build.xml
index 84baa4237e..1118822b14 100644
--- a/qpid/java/build.xml
+++ b/qpid/java/build.xml
@@ -22,6 +22,8 @@
<import file="common.xml"/>
+ <property file="${project.root}/build.overrides"/>
+
<findSubProjects name="broker-plugins" dir="broker-plugins"/>
<findSubProjects name="client-plugins" dir="client-plugins"/>
<findSubProjects name="management" dir="management" excludes="common,example"/>
@@ -31,8 +33,9 @@
<property name="modules.tests" value="systests perftests integrationtests testkit"/>
<property name="modules.management" value="${management}"/>
<property name="modules.plugin" value="${broker-plugins} ${client-plugins}"/>
+ <property name="modules.opt" value=""/>
<property name="modules" value="${modules.core} ${modules.examples}
- ${modules.management} ${modules.tests} ${modules.plugin}"/>
+ ${modules.management} ${modules.tests} ${modules.plugin} ${modules.opt}"/>
<property name="qpid.jar" location="${build.lib}/qpid-all.jar"/>
<basename property="qpid.jar.name" file="${qpid.jar}"/>
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_8_0.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_8_0.java
index 92f9ebe07c..b1a22155d6 100644
--- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_8_0.java
+++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_8_0.java
@@ -36,11 +36,13 @@ import javax.jms.XASession;
import javax.net.ssl.SSLContext;
import org.apache.qpid.AMQException;
+import org.apache.qpid.AMQTimeoutException;
import org.apache.qpid.client.failover.FailoverException;
import org.apache.qpid.client.failover.FailoverProtectedOperation;
import org.apache.qpid.client.failover.FailoverRetrySupport;
import org.apache.qpid.client.protocol.AMQProtocolSession;
import org.apache.qpid.client.state.AMQState;
+import org.apache.qpid.client.state.AMQStateManager;
import org.apache.qpid.client.state.StateWaiter;
import org.apache.qpid.framing.BasicQosBody;
import org.apache.qpid.framing.BasicQosOkBody;
@@ -69,8 +71,30 @@ public class AMQConnectionDelegate_8_0 implements AMQConnectionDelegate
public void closeConnection(long timeout) throws JMSException, AMQException
{
- _conn.getProtocolHandler().closeConnection(timeout);
+ final AMQStateManager stateManager = _conn.getProtocolHandler().getStateManager();
+ final AMQState currentState = stateManager.getCurrentState();
+ if (currentState.equals(AMQState.CONNECTION_CLOSED))
+ {
+ _logger.debug("Connection already closed.");
+ }
+ else if (currentState.equals(AMQState.CONNECTION_CLOSING))
+ {
+ _logger.debug("Connection already closing, awaiting closed state.");
+ final StateWaiter closeWaiter = new StateWaiter(stateManager, currentState, EnumSet.of(AMQState.CONNECTION_CLOSED));
+ try
+ {
+ closeWaiter.await(timeout);
+ }
+ catch (AMQTimeoutException te)
+ {
+ throw new AMQTimeoutException("Close did not complete in timely fashion", te);
+ }
+ }
+ else
+ {
+ _conn.getProtocolHandler().closeConnection(timeout);
+ }
}
public AMQConnectionDelegate_8_0(AMQConnection conn)
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java
index 3ef32fb008..acd46da11a 100644
--- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java
+++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java
@@ -793,7 +793,7 @@ public abstract class AMQDestination implements Destination, Referenceable
return _browseOnly;
}
- public void setBrowseOnly(boolean b)
+ private void setBrowseOnly(boolean b)
{
_browseOnly = b;
}
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java
index e0da1ef41f..30c7403a90 100644
--- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java
+++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java
@@ -70,7 +70,6 @@ import org.apache.qpid.AMQDisconnectedException;
import org.apache.qpid.AMQException;
import org.apache.qpid.AMQInvalidArgumentException;
import org.apache.qpid.AMQInvalidRoutingKeyException;
-import org.apache.qpid.client.AMQDestination.AddressOption;
import org.apache.qpid.client.AMQDestination.DestSyntax;
import org.apache.qpid.client.failover.FailoverException;
import org.apache.qpid.client.failover.FailoverNoopSupport;
@@ -88,8 +87,6 @@ import org.apache.qpid.client.message.JMSTextMessage;
import org.apache.qpid.client.message.MessageFactoryRegistry;
import org.apache.qpid.client.message.UnprocessedMessage;
import org.apache.qpid.client.protocol.AMQProtocolHandler;
-import org.apache.qpid.client.state.AMQState;
-import org.apache.qpid.client.state.AMQStateManager;
import org.apache.qpid.client.util.FlowControllingBlockingQueue;
import org.apache.qpid.common.AMQPFilterTypes;
import org.apache.qpid.framing.AMQShortString;
@@ -216,8 +213,6 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic
*/
protected final boolean DEFAULT_MANDATORY = Boolean.parseBoolean(System.getProperty("qpid.default_mandatory", "true"));
- protected final boolean DEFAULT_WAIT_ON_SEND = Boolean.parseBoolean(System.getProperty("qpid.default_wait_on_send", "false"));
-
/**
* The period to wait while flow controlled before sending a log message confirming that the session is still
* waiting on flow control being revoked
@@ -367,7 +362,13 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic
* Set when recover is called. This is to handle the case where recover() is called by application code during
* onMessage() processing to ensure that an auto ack is not sent.
*/
- private boolean _inRecovery;
+ private volatile boolean _sessionInRecovery;
+
+ /**
+ * Set when the dispatcher should direct incoming messages straight into the UnackedMessage list instead of
+ * to the syncRecieveQueue or MessageListener. Used during cleanup, e.g. in Session.recover().
+ */
+ private volatile boolean _usingDispatcherForCleanup;
/** Used to indicates that the connection to which this session belongs, has been stopped. */
private boolean _connectionStopped;
@@ -617,30 +618,27 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic
{
throw new IllegalStateException("Session is already closed");
}
- else if (hasFailedOver())
+ else if (hasFailedOverDirty())
{
+ //perform an implicit recover in this scenario
+ recover();
+
+ //notify the consumer
throw new IllegalStateException("has failed over");
}
- while (true)
+ try
{
- Long tag = _unacknowledgedMessageTags.poll();
- if (tag == null)
- {
- break;
- }
-
- try
- {
- acknowledgeMessage(tag, false);
- }
- catch (TransportException e)
- {
- throw toJMSException("Exception while acknowledging message(s):" + e.getMessage(), e);
- }
+ acknowledgeImpl();
+ }
+ catch (TransportException e)
+ {
+ throw toJMSException("Exception while acknowledging message(s):" + e.getMessage(), e);
}
}
+ protected abstract void acknowledgeImpl() throws JMSException;
+
/**
* Acknowledge one or many messages.
*
@@ -849,42 +847,28 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic
* @throws JMSException If the JMS provider fails to commit the transaction due to some internal error. This does
* not mean that the commit is known to have failed, merely that it is not known whether it
* failed or not.
- * @todo Be aware of possible changes to parameter order as versions change.
*/
public void commit() throws JMSException
{
checkTransacted();
- try
+ //Check that we are clean to commit.
+ if (_failedOverDirty)
{
- //Check that we are clean to commit.
- if (_failedOverDirty)
- {
- rollback();
-
- throw new TransactionRolledBackException("Connection failover has occured since last send. " +
- "Forced rollback");
- }
+ rollback();
+ throw new TransactionRolledBackException("Connection failover has occured with uncommitted transaction activity." +
+ "The session transaction was rolled back.");
+ }
- // Acknowledge all delivered messages
- while (true)
- {
- Long tag = _deliveredMessageTags.poll();
- if (tag == null)
- {
- break;
- }
-
- acknowledgeMessage(tag, false);
- }
- // Commits outstanding messages and acknowledgments
- sendCommit();
+ try
+ {
+ commitImpl();
markClean();
}
catch (AMQException e)
{
- throw new JMSAMQException("Failed to commit: " + e.getMessage() + ":" + e.getCause(), e);
+ throw new JMSAMQException("Exception during commit: " + e.getMessage() + ":" + e.getCause(), e);
}
catch (FailoverException e)
{
@@ -896,8 +880,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic
}
}
- public abstract void sendCommit() throws AMQException, FailoverException;
-
+ protected abstract void commitImpl() throws AMQException, FailoverException, TransportException;
public void confirmConsumerCancelled(int consumerTag)
{
@@ -975,7 +958,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic
return new AMQQueueBrowser(this, (AMQQueue) queue, messageSelector);
}
- public MessageConsumer createBrowserConsumer(Destination destination, String messageSelector, boolean noLocal)
+ protected MessageConsumer createBrowserConsumer(Destination destination, String messageSelector, boolean noLocal)
throws JMSException
{
checkValidDestination(destination);
@@ -989,15 +972,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic
checkValidDestination(destination);
return createConsumerImpl(destination, _prefetchHighMark, _prefetchLowMark, false, (destination instanceof Topic), null, null,
- ((destination instanceof AMQDestination) && ((AMQDestination)destination).isBrowseOnly()), false);
- }
-
- public C createExclusiveConsumer(Destination destination) throws JMSException
- {
- checkValidDestination(destination);
-
- return createConsumerImpl(destination, _prefetchHighMark, _prefetchLowMark, false, true, null, null,
- ((destination instanceof AMQDestination) && ((AMQDestination)destination).isBrowseOnly()), false);
+ isBrowseOnlyDestination(destination), false);
}
public MessageConsumer createConsumer(Destination destination, String messageSelector) throws JMSException
@@ -1005,7 +980,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic
checkValidDestination(destination);
return createConsumerImpl(destination, _prefetchHighMark, _prefetchLowMark, false, (destination instanceof Topic),
- messageSelector, null, ((destination instanceof AMQDestination) && ((AMQDestination)destination).isBrowseOnly()), false);
+ messageSelector, null, isBrowseOnlyDestination(destination), false);
}
public MessageConsumer createConsumer(Destination destination, String messageSelector, boolean noLocal)
@@ -1014,16 +989,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic
checkValidDestination(destination);
return createConsumerImpl(destination, _prefetchHighMark, _prefetchLowMark, noLocal, (destination instanceof Topic),
- messageSelector, null, ((destination instanceof AMQDestination) && ((AMQDestination)destination).isBrowseOnly()), false);
- }
-
- public MessageConsumer createExclusiveConsumer(Destination destination, String messageSelector, boolean noLocal)
- throws JMSException
- {
- checkValidDestination(destination);
-
- return createConsumerImpl(destination, _prefetchHighMark, _prefetchLowMark, noLocal, true,
- messageSelector, null, false, false);
+ messageSelector, null, isBrowseOnlyDestination(destination), false);
}
public MessageConsumer createConsumer(Destination destination, int prefetch, boolean noLocal, boolean exclusive,
@@ -1031,23 +997,15 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic
{
checkValidDestination(destination);
- return createConsumerImpl(destination, prefetch, prefetch / 2, noLocal, exclusive, selector, null, ((destination instanceof AMQDestination) && ((AMQDestination)destination).isBrowseOnly()), false);
+ return createConsumerImpl(destination, prefetch, prefetch / 2, noLocal, exclusive, selector, null, isBrowseOnlyDestination(destination), false);
}
public MessageConsumer createConsumer(Destination destination, int prefetchHigh, int prefetchLow, boolean noLocal,
- boolean exclusive, String selector) throws JMSException
+ boolean exclusive, String selector) throws JMSException
{
checkValidDestination(destination);
- return createConsumerImpl(destination, prefetchHigh, prefetchLow, noLocal, exclusive, selector, null, ((destination instanceof AMQDestination) && ((AMQDestination)destination).isBrowseOnly()), false);
- }
-
- public MessageConsumer createConsumer(Destination destination, int prefetch, boolean noLocal, boolean exclusive,
- String selector, FieldTable rawSelector) throws JMSException
- {
- checkValidDestination(destination);
-
- return createConsumerImpl(destination, prefetch, prefetch / 2, noLocal, exclusive, selector, rawSelector, ((destination instanceof AMQDestination) && ((AMQDestination)destination).isBrowseOnly()), false);
+ return createConsumerImpl(destination, prefetchHigh, prefetchLow, noLocal, exclusive, selector, null, isBrowseOnlyDestination(destination), false);
}
public MessageConsumer createConsumer(Destination destination, int prefetchHigh, int prefetchLow, boolean noLocal,
@@ -1055,7 +1013,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic
{
checkValidDestination(destination);
- return createConsumerImpl(destination, prefetchHigh, prefetchLow, noLocal, exclusive, selector, rawSelector, ((destination instanceof AMQDestination) && ((AMQDestination)destination).isBrowseOnly()),
+ return createConsumerImpl(destination, prefetchHigh, prefetchLow, noLocal, exclusive, selector, rawSelector, isBrowseOnlyDestination(destination),
false);
}
@@ -1244,12 +1202,6 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic
return createProducerImpl(destination, mandatory, immediate);
}
- public P createProducer(Destination destination, boolean mandatory, boolean immediate,
- boolean waitUntilSent) throws JMSException
- {
- return createProducerImpl(destination, mandatory, immediate, waitUntilSent);
- }
-
public TopicPublisher createPublisher(Topic topic) throws JMSException
{
checkNotClosed();
@@ -1467,9 +1419,10 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic
public TopicSubscriber createSubscriber(Topic topic) throws JMSException
{
checkNotClosed();
- Topic dest = checkValidTopic(topic);
+ checkValidTopic(topic);
- return new TopicSubscriberAdaptor(dest, (C) createExclusiveConsumer(dest));
+ return new TopicSubscriberAdaptor<C>(topic,
+ createConsumerImpl(topic, _prefetchHighMark, _prefetchLowMark, false, true, null, null, false, false));
}
/**
@@ -1486,10 +1439,11 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic
public TopicSubscriber createSubscriber(Topic topic, String messageSelector, boolean noLocal) throws JMSException
{
checkNotClosed();
- Topic dest = checkValidTopic(topic);
+ checkValidTopic(topic);
- // AMQTopic dest = new AMQTopic(topic.getTopicName());
- return new TopicSubscriberAdaptor(dest, (C) createExclusiveConsumer(dest, messageSelector, noLocal));
+ return new TopicSubscriberAdaptor<C>(topic,
+ createConsumerImpl(topic, _prefetchHighMark, _prefetchLowMark, noLocal,
+ true, messageSelector, null, false, false));
}
public TemporaryQueue createTemporaryQueue() throws JMSException
@@ -1591,10 +1545,8 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic
abstract public void sync() throws AMQException;
- public int getAcknowledgeMode() throws JMSException
+ public int getAcknowledgeMode()
{
- checkNotClosed();
-
return _acknowledgeMode;
}
@@ -1654,10 +1606,8 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic
return _ticket;
}
- public boolean getTransacted() throws JMSException
+ public boolean getTransacted()
{
- checkNotClosed();
-
return _transacted;
}
@@ -1759,8 +1709,8 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic
// flush any acks we are holding in the buffer.
flushAcknowledgments();
- // this is set only here, and the before the consumer's onMessage is called it is set to false
- _inRecovery = true;
+ // this is only set true here, and only set false when the consumers preDeliver method is called
+ _sessionInRecovery = true;
boolean isSuspended = isSuspended();
@@ -1768,9 +1718,18 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic
{
suspendChannel(true);
}
-
+
+ // Set to true to short circuit delivery of anything currently
+ //in the pre-dispatch queue.
+ _usingDispatcherForCleanup = true;
+
syncDispatchQueue();
-
+
+ // Set to false before sending the recover as 0-8/9/9-1 will
+ //send messages back before the recover completes, and we
+ //probably shouldn't clean those! ;-)
+ _usingDispatcherForCleanup = false;
+
if (_dispatcher != null)
{
_dispatcher.recover();
@@ -1779,10 +1738,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic
sendRecover();
markClean();
-
- // Set inRecovery to false before you start message flow again again.
- _inRecovery = false;
-
+
if (!isSuspended)
{
suspendChannel(false);
@@ -2018,6 +1974,12 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic
{
checkTemporaryDestination(destination);
+ if(!noConsume && isBrowseOnlyDestination(destination))
+ {
+ throw new InvalidDestinationException("The consumer being created is not 'noConsume'," +
+ "but a 'browseOnly' Destination has been supplied.");
+ }
+
final String messageSelector;
if (_strictAMQP && !((selector == null) || selector.equals("")))
@@ -2176,7 +2138,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic
boolean isInRecovery()
{
- return _inRecovery;
+ return _sessionInRecovery;
}
boolean isQueueBound(AMQShortString exchangeName, AMQShortString queueName) throws JMSException
@@ -2298,7 +2260,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic
void setInRecovery(boolean inRecovery)
{
- _inRecovery = inRecovery;
+ _sessionInRecovery = inRecovery;
}
boolean isStarted()
@@ -2637,15 +2599,9 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic
public abstract void sendConsume(C consumer, AMQShortString queueName,
AMQProtocolHandler protocolHandler, boolean nowait, String messageSelector, int tag) throws AMQException, FailoverException;
- private P createProducerImpl(Destination destination, boolean mandatory, boolean immediate)
+ private P createProducerImpl(final Destination destination, final boolean mandatory, final boolean immediate)
throws JMSException
{
- return createProducerImpl(destination, mandatory, immediate, DEFAULT_WAIT_ON_SEND);
- }
-
- private P createProducerImpl(final Destination destination, final boolean mandatory,
- final boolean immediate, final boolean waitUntilSent) throws JMSException
- {
return new FailoverRetrySupport<P, JMSException>(
new FailoverProtectedOperation<P, JMSException>()
{
@@ -2658,7 +2614,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic
try
{
producer = createMessageProducer(destination, mandatory,
- immediate, waitUntilSent, producerId);
+ immediate, producerId);
}
catch (TransportException e)
{
@@ -2673,7 +2629,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic
}
public abstract P createMessageProducer(final Destination destination, final boolean mandatory,
- final boolean immediate, final boolean waitUntilSent, long producerId) throws JMSException;
+ final boolean immediate, final long producerId) throws JMSException;
private void declareExchange(AMQDestination amqd, AMQProtocolHandler protocolHandler, boolean nowait) throws AMQException
{
@@ -3113,21 +3069,11 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic
*
* @return boolean true if failover has occured.
*/
- public boolean hasFailedOver()
+ public boolean hasFailedOverDirty()
{
return _failedOverDirty;
}
- /**
- * Check to see if any message have been sent in this transaction and have not been commited.
- *
- * @return boolean true if a message has been sent but not commited
- */
- public boolean isDirty()
- {
- return _dirty;
- }
-
public void setTicket(int ticket)
{
_ticket = ticket;
@@ -3391,7 +3337,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic
{
rejectMessage(message, true);
}
- else if (isInRecovery())
+ else if (_usingDispatcherForCleanup)
{
_unacknowledgedMessageTags.add(deliveryTag);
}
@@ -3575,4 +3521,9 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic
}
return code;
}
+
+ private boolean isBrowseOnlyDestination(Destination destination)
+ {
+ return ((destination instanceof AMQDestination) && ((AMQDestination)destination).isBrowseOnly());
+ }
}
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java
index bfbb9f7148..86e1fc08de 100644
--- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java
+++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java
@@ -270,7 +270,7 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic
long prefetch = getAMQConnection().getMaxPrefetch();
- if (unackedCount >= prefetch/2 || maxAckDelay <= 0)
+ if (unackedCount >= prefetch/2 || maxAckDelay <= 0 || _acknowledgeMode == javax.jms.Session.AUTO_ACKNOWLEDGE)
{
flushAcknowledgments();
}
@@ -412,25 +412,6 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic
}
}
-
- /**
- * Commit the receipt and the delivery of all messages exchanged by this session resources.
- */
- public void sendCommit() throws AMQException, FailoverException
- {
- getQpidSession().setAutoSync(true);
- try
- {
- getQpidSession().txCommit();
- }
- finally
- {
- getQpidSession().setAutoSync(false);
- }
- // We need to sync so that we get notify of an error.
- sync();
- }
-
/**
* Create a queue with a given name.
*
@@ -463,6 +444,14 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic
public void sendRecover() throws AMQException, FailoverException
{
// release all unacked messages
+ RangeSet ranges = gatherUnackedRangeSet();
+ getQpidSession().messageRelease(ranges, Option.SET_REDELIVERED);
+ // We need to sync so that we get notify of an error.
+ sync();
+ }
+
+ private RangeSet gatherUnackedRangeSet()
+ {
RangeSet ranges = new RangeSet();
while (true)
{
@@ -471,11 +460,11 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic
{
break;
}
- ranges.add((int) (long) tag);
+
+ ranges.add(tag.intValue());
}
- getQpidSession().messageRelease(ranges, Option.SET_REDELIVERED);
- // We need to sync so that we get notify of an error.
- sync();
+
+ return ranges;
}
@@ -676,13 +665,12 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic
* Create an 0_10 message producer
*/
public BasicMessageProducer_0_10 createMessageProducer(final Destination destination, final boolean mandatory,
- final boolean immediate, final boolean waitUntilSent,
- long producerId) throws JMSException
+ final boolean immediate, final long producerId) throws JMSException
{
try
{
return new BasicMessageProducer_0_10(_connection, (AMQDestination) destination, _transacted, _channelId, this,
- getProtocolHandler(), producerId, immediate, mandatory, waitUntilSent);
+ getProtocolHandler(), producerId, immediate, mandatory);
}
catch (AMQException e)
{
@@ -998,32 +986,26 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic
}
}
- @Override
- public void commit() throws JMSException
+ public void commitImpl() throws AMQException, FailoverException, TransportException
{
- checkTransacted();
- try
- {
- if( _txSize > 0 )
- {
- messageAcknowledge(_txRangeSet, true);
- _txRangeSet.clear();
- _txSize = 0;
- }
- sendCommit();
- }
- catch(TransportException e)
+ if( _txSize > 0 )
{
- throw toJMSException("Session exception occured while trying to commit: " + e.getMessage(), e);
+ messageAcknowledge(_txRangeSet, true);
+ _txRangeSet.clear();
+ _txSize = 0;
}
- catch (AMQException e)
+
+ getQpidSession().setAutoSync(true);
+ try
{
- throw new JMSAMQException("Failed to commit: " + e.getMessage(), e);
+ getQpidSession().txCommit();
}
- catch (FailoverException e)
+ finally
{
- throw new JMSAMQException("Fail-over interrupted commit. Status of the commit is uncertain.", e);
+ getQpidSession().setAutoSync(false);
}
+ // We need to sync so that we get notify of an error.
+ sync();
}
protected final boolean tagLE(long tag1, long tag2)
@@ -1386,4 +1368,14 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic
return sb.toString();
}
+ protected void acknowledgeImpl()
+ {
+ RangeSet range = gatherUnackedRangeSet();
+
+ if(range.size() > 0 )
+ {
+ messageAcknowledge(range, true);
+ getQpidSession().sync();
+ }
+ }
}
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_8.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_8.java
index c010e4c7ed..ccb2b00947 100644
--- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_8.java
+++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_8.java
@@ -76,6 +76,7 @@ import org.apache.qpid.framing.amqp_0_91.MethodRegistry_0_91;
import org.apache.qpid.jms.Session;
import org.apache.qpid.protocol.AMQConstant;
import org.apache.qpid.protocol.AMQMethodEvent;
+import org.apache.qpid.transport.TransportException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -125,6 +126,20 @@ public final class AMQSession_0_8 extends AMQSession<BasicMessageConsumer_0_8, B
return getProtocolHandler().getProtocolVersion();
}
+ protected void acknowledgeImpl()
+ {
+ while (true)
+ {
+ Long tag = _unacknowledgedMessageTags.poll();
+ if (tag == null)
+ {
+ break;
+ }
+
+ acknowledgeMessage(tag, false);
+ }
+ }
+
public void acknowledgeMessage(long deliveryTag, boolean multiple)
{
BasicAckBody body = getMethodRegistry().createBasicAckBody(deliveryTag, multiple);
@@ -170,8 +185,20 @@ public final class AMQSession_0_8 extends AMQSession<BasicMessageConsumer_0_8, B
}
}
- public void sendCommit() throws AMQException, FailoverException
+ public void commitImpl() throws AMQException, FailoverException, TransportException
{
+ // Acknowledge all delivered messages
+ while (true)
+ {
+ Long tag = _deliveredMessageTags.poll();
+ if (tag == null)
+ {
+ break;
+ }
+
+ acknowledgeMessage(tag, false);
+ }
+
final AMQProtocolHandler handler = getProtocolHandler();
handler.syncWrite(getProtocolHandler().getMethodRegistry().createTxCommitBody().generateFrame(_channelId), TxCommitOkBody.class);
@@ -401,12 +428,12 @@ public final class AMQSession_0_8 extends AMQSession<BasicMessageConsumer_0_8, B
public BasicMessageProducer_0_8 createMessageProducer(final Destination destination, final boolean mandatory,
- final boolean immediate, final boolean waitUntilSent, long producerId) throws JMSException
+ final boolean immediate, long producerId) throws JMSException
{
try
{
return new BasicMessageProducer_0_8(_connection, (AMQDestination) destination, _transacted, _channelId,
- this, getProtocolHandler(), producerId, immediate, mandatory, waitUntilSent);
+ this, getProtocolHandler(), producerId, immediate, mandatory);
}
catch (AMQException e)
{
@@ -615,5 +642,4 @@ public final class AMQSession_0_8 extends AMQSession<BasicMessageConsumer_0_8, B
return null;
}
}
-
}
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java
index 754055ad98..3b807591b0 100644
--- a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java
+++ b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java
@@ -37,10 +37,7 @@ import javax.jms.MessageListener;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
-import java.util.SortedSet;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.TreeSet;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.LinkedBlockingQueue;
@@ -118,29 +115,10 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa
protected final int _acknowledgeMode;
/**
- * Number of messages unacknowledged in DUPS_OK_ACKNOWLEDGE mode
- */
- private int _outstanding;
-
- /**
- * Switch to enable sending of acknowledgements when using DUPS_OK_ACKNOWLEDGE mode. Enabled when _outstannding
- * number of msgs >= _prefetchHigh and disabled at < _prefetchLow
- */
- private boolean _dups_ok_acknowledge_send;
-
- /**
* List of tags delievered, The last of which which should be acknowledged on commit in transaction mode.
*/
private ConcurrentLinkedQueue<Long> _receivedDeliveryTags = new ConcurrentLinkedQueue<Long>();
- /** The last tag that was "multiple" acknowledged on this session (if transacted) */
- private long _lastAcked;
-
- /** set of tags which have previously been acked; but not part of the multiple ack (transacted mode only) */
- private final SortedSet<Long> _previouslyAcked = new TreeSet<Long>();
-
- private final Object _commitLock = new Object();
-
/**
* The thread that was used to call receive(). This is important for being able to interrupt that thread if a
* receive() is in progress.
@@ -290,17 +268,6 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa
}
}
- protected void preApplicationProcessing(AbstractJMSMessage jmsMsg) throws JMSException
- {
- if (_session.getAcknowledgeMode() == Session.CLIENT_ACKNOWLEDGE)
- {
- _session.addUnacknowledgedMessage(jmsMsg.getDeliveryTag());
- }
-
- _session.setInRecovery(false);
- preDeliver(jmsMsg);
- }
-
/**
* @param immediate if true then return immediately if the connection is failing over
*
@@ -323,14 +290,14 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa
}
}
- if (!_receiving.compareAndSet(false, true))
+ if (isMessageListenerSet())
{
- throw new javax.jms.IllegalStateException("Another thread is already receiving.");
+ throw new javax.jms.IllegalStateException("A listener has already been set.");
}
- if (isMessageListenerSet())
+ if (!_receiving.compareAndSet(false, true))
{
- throw new javax.jms.IllegalStateException("A listener has already been set.");
+ throw new javax.jms.IllegalStateException("Another thread is already receiving.");
}
_receivingThread = Thread.currentThread();
@@ -409,7 +376,7 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa
final AbstractJMSMessage m = returnMessageOrThrow(o);
if (m != null)
{
- preApplicationProcessing(m);
+ preDeliver(m);
postDeliver(m);
}
return m;
@@ -482,7 +449,7 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa
final AbstractJMSMessage m = returnMessageOrThrow(o);
if (m != null)
{
- preApplicationProcessing(m);
+ preDeliver(m);
postDeliver(m);
}
@@ -734,7 +701,7 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa
{
if (isMessageListenerSet())
{
- preApplicationProcessing(jmsMessage);
+ preDeliver(jmsMessage);
getMessageListener().onMessage(jmsMessage);
postDeliver(jmsMessage);
}
@@ -758,49 +725,42 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa
}
}
- void preDeliver(AbstractJMSMessage msg)
+ protected void preDeliver(AbstractJMSMessage msg)
{
+ _session.setInRecovery(false);
+
switch (_acknowledgeMode)
{
-
case Session.PRE_ACKNOWLEDGE:
_session.acknowledgeMessage(msg.getDeliveryTag(), false);
break;
-
+ case Session.AUTO_ACKNOWLEDGE:
+ //fall through
+ case Session.DUPS_OK_ACKNOWLEDGE:
+ _session.addUnacknowledgedMessage(msg.getDeliveryTag());
+ break;
case Session.CLIENT_ACKNOWLEDGE:
// we set the session so that when the user calls acknowledge() it can call the method on session
// to send out the appropriate frame
msg.setAMQSession(_session);
+ _session.addUnacknowledgedMessage(msg.getDeliveryTag());
+ _session.markDirty();
break;
case Session.SESSION_TRANSACTED:
- if (isNoConsume())
- {
- _session.acknowledgeMessage(msg.getDeliveryTag(), false);
- }
- else
- {
- _session.addDeliveredMessage(msg.getDeliveryTag());
- _session.markDirty();
- }
-
+ _session.addDeliveredMessage(msg.getDeliveryTag());
+ _session.markDirty();
+ break;
+ case Session.NO_ACKNOWLEDGE:
+ //do nothing.
+ //path used for NO-ACK consumers, and browsers (see constructor).
break;
}
-
}
void postDeliver(AbstractJMSMessage msg)
{
switch (_acknowledgeMode)
{
-
- case Session.CLIENT_ACKNOWLEDGE:
- if (isNoConsume())
- {
- _session.acknowledgeMessage(msg.getDeliveryTag(), false);
- }
- _session.markDirty();
- break;
-
case Session.DUPS_OK_ACKNOWLEDGE:
case Session.AUTO_ACKNOWLEDGE:
// we do not auto ack a message if the application code called recover()
@@ -838,63 +798,6 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa
return null;
}
- /**
- * Acknowledge up to last message delivered (if any). Used when commiting.
- */
- void acknowledgeDelivered()
- {
- synchronized(_commitLock)
- {
- ArrayList<Long> tagsToAck = new ArrayList<Long>();
-
- while (!_receivedDeliveryTags.isEmpty())
- {
- tagsToAck.add(_receivedDeliveryTags.poll());
- }
-
- Collections.sort(tagsToAck);
-
- long prevAcked = _lastAcked;
- long oldAckPoint = -1;
-
- while(oldAckPoint != prevAcked)
- {
- oldAckPoint = prevAcked;
-
- Iterator<Long> tagsToAckIterator = tagsToAck.iterator();
-
- while(tagsToAckIterator.hasNext() && tagsToAckIterator.next() == prevAcked+1)
- {
- tagsToAckIterator.remove();
- prevAcked++;
- }
-
- Iterator<Long> previousAckIterator = _previouslyAcked.iterator();
- while(previousAckIterator.hasNext() && previousAckIterator.next() == prevAcked+1)
- {
- previousAckIterator.remove();
- prevAcked++;
- }
-
- }
- if(prevAcked != _lastAcked)
- {
- _session.acknowledgeMessage(prevAcked, true);
- _lastAcked = prevAcked;
- }
-
- Iterator<Long> tagsToAckIterator = tagsToAck.iterator();
-
- while(tagsToAckIterator.hasNext())
- {
- Long tag = tagsToAckIterator.next();
- _session.acknowledgeMessage(tag, false);
- _previouslyAcked.add(tag);
- }
- }
- }
-
-
void notifyError(Throwable cause)
{
// synchronized (_closed)
@@ -973,7 +876,7 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa
public boolean isNoConsume()
{
- return _noConsume || _destination.isBrowseOnly() ;
+ return _noConsume;
}
public void rollback()
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_10.java b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_10.java
index 47da59724c..3c24c67f9b 100644
--- a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_10.java
+++ b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_10.java
@@ -66,19 +66,13 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM
private boolean _preAcquire = true;
/**
- * Indicate whether this consumer is started.
- */
- private boolean _isStarted = false;
-
- /**
* Specify whether this consumer is performing a sync receive
*/
private final AtomicBoolean _syncReceive = new AtomicBoolean(false);
private String _consumerTagString;
private long capacity = 0;
-
- //--- constructor
+
protected BasicMessageConsumer_0_10(int channelId, AMQConnection connection, AMQDestination destination,
String messageSelector, boolean noLocal, MessageFactoryRegistry messageFactory,
AMQSession session, AMQProtocolHandler protocolHandler,
@@ -104,7 +98,6 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM
_preAcquire = false;
}
}
- _isStarted = connection.started();
// Destination setting overrides connection defaults
if (destination.getDestSyntax() == DestSyntax.ADDR &&
@@ -172,8 +165,6 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM
}
}
- //----- overwritten methods
-
/**
* This method is invoked when this consumer is stopped.
* It tells the broker to stop delivering messages to this consumer.
@@ -203,11 +194,18 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM
super.notifyMessage(messageFrame);
}
- @Override protected void preApplicationProcessing(AbstractJMSMessage jmsMsg) throws JMSException
+ @Override
+ protected void preDeliver(AbstractJMSMessage jmsMsg)
{
- super.preApplicationProcessing(jmsMsg);
- if (!_session.getTransacted() && _session.getAcknowledgeMode() != org.apache.qpid.jms.Session.CLIENT_ACKNOWLEDGE)
+ super.preDeliver(jmsMsg);
+
+ if (_acknowledgeMode == org.apache.qpid.jms.Session.NO_ACKNOWLEDGE)
{
+ //For 0-10 we need to ensure that all messages are indicated processed in some way to
+ //ensure their AMQP command-id is marked completed, and so we must send a completion
+ //even for no-ack messages even though there isnt actually an 'acknowledgement' occurring.
+ //Add message to the unacked message list to ensure we dont lose record of it before
+ //sending a completion of some sort.
_session.addUnacknowledgedMessage(jmsMsg.getDeliveryTag());
}
}
@@ -219,7 +217,6 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM
return _messageFactory.createMessage(msg.getMessageTransfer());
}
- // private methods
/**
* Check whether a message can be delivered to this consumer.
*
@@ -457,10 +454,8 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM
}
if (_acknowledgeMode == org.apache.qpid.jms.Session.AUTO_ACKNOWLEDGE &&
- !_session.isInRecovery() &&
- _session.getAMQConnection().getSyncAck())
+ !_session.isInRecovery() && _session.getAMQConnection().getSyncAck())
{
- ((AMQSession_0_10) getSession()).flushAcknowledgments();
((AMQSession_0_10) getSession()).getQpidSession().sync();
}
}
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java
index 2bfca025b2..bf4de782a5 100644
--- a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java
+++ b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java
@@ -114,8 +114,6 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac
private final boolean _mandatory;
- private final boolean _waitUntilSent;
-
private boolean _disableMessageId;
private UUIDGen _messageIdGenerator = UUIDs.newGenerator();
@@ -127,8 +125,7 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac
protected PublishMode publishMode = PublishMode.ASYNC_PUBLISH_ALL;
protected BasicMessageProducer(AMQConnection connection, AMQDestination destination, boolean transacted, int channelId,
- AMQSession session, AMQProtocolHandler protocolHandler, long producerId, boolean immediate, boolean mandatory,
- boolean waitUntilSent) throws AMQException
+ AMQSession session, AMQProtocolHandler protocolHandler, long producerId, boolean immediate, boolean mandatory) throws AMQException
{
_connection = connection;
_destination = destination;
@@ -144,7 +141,6 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac
_immediate = immediate;
_mandatory = mandatory;
- _waitUntilSent = waitUntilSent;
_userID = connection.getUsername();
setPublishMode();
}
@@ -364,19 +360,6 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac
}
}
- public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive,
- boolean mandatory, boolean immediate, boolean waitUntilSent) throws JMSException
- {
- checkPreConditions();
- checkDestination(destination);
- synchronized (_connection.getFailoverMutex())
- {
- validateDestination(destination);
- sendImpl((AMQDestination) destination, message, deliveryMode, priority, timeToLive, mandatory, immediate,
- waitUntilSent);
- }
- }
-
private AbstractJMSMessage convertToNativeMessage(Message message) throws JMSException
{
if (message instanceof AbstractJMSMessage)
@@ -451,12 +434,6 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac
}
}
- protected void sendImpl(AMQDestination destination, Message message, int deliveryMode, int priority, long timeToLive,
- boolean mandatory, boolean immediate) throws JMSException
- {
- sendImpl(destination, message, deliveryMode, priority, timeToLive, mandatory, immediate, _waitUntilSent);
- }
-
/**
* The caller of this method must hold the failover mutex.
*
@@ -471,23 +448,13 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac
* @throws JMSException
*/
protected void sendImpl(AMQDestination destination, Message origMessage, int deliveryMode, int priority, long timeToLive,
- boolean mandatory, boolean immediate, boolean wait) throws JMSException
+ boolean mandatory, boolean immediate) throws JMSException
{
checkTemporaryDestination(destination);
origMessage.setJMSDestination(destination);
AbstractJMSMessage message = convertToNativeMessage(origMessage);
- if (_transacted)
- {
- if (_session.hasFailedOver() && _session.isDirty())
- {
- throw new JMSAMQException("Failover has occurred and session is dirty so unable to send.",
- new AMQSessionDirtyException("Failover has occurred and session is dirty " +
- "so unable to send."));
- }
- }
-
UUID messageId = null;
if (_disableMessageId)
{
@@ -501,7 +468,7 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac
try
{
- sendMessage(destination, origMessage, message, messageId, deliveryMode, priority, timeToLive, mandatory, immediate, wait);
+ sendMessage(destination, origMessage, message, messageId, deliveryMode, priority, timeToLive, mandatory, immediate);
}
catch (TransportException e)
{
@@ -526,7 +493,7 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac
abstract void sendMessage(AMQDestination destination, Message origMessage, AbstractJMSMessage message,
UUID messageId, int deliveryMode, int priority, long timeToLive, boolean mandatory,
- boolean immediate, boolean wait) throws JMSException;
+ boolean immediate) throws JMSException;
private void checkTemporaryDestination(AMQDestination destination) throws JMSException
{
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_10.java b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_10.java
index 1fa5c1003f..57f64c2f92 100644
--- a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_10.java
+++ b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_10.java
@@ -61,10 +61,9 @@ public class BasicMessageProducer_0_10 extends BasicMessageProducer
BasicMessageProducer_0_10(AMQConnection connection, AMQDestination destination, boolean transacted, int channelId,
AMQSession session, AMQProtocolHandler protocolHandler, long producerId,
- boolean immediate, boolean mandatory, boolean waitUntilSent) throws AMQException
+ boolean immediate, boolean mandatory) throws AMQException
{
- super(connection, destination, transacted, channelId, session, protocolHandler, producerId, immediate,
- mandatory, waitUntilSent);
+ super(connection, destination, transacted, channelId, session, protocolHandler, producerId, immediate, mandatory);
userIDBytes = Strings.toUTF8(_userID);
}
@@ -104,7 +103,7 @@ public class BasicMessageProducer_0_10 extends BasicMessageProducer
*/
void sendMessage(AMQDestination destination, Message origMessage, AbstractJMSMessage message,
UUID messageId, int deliveryMode, int priority, long timeToLive, boolean mandatory,
- boolean immediate, boolean wait) throws JMSException
+ boolean immediate) throws JMSException
{
message.prepareForSending();
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_8.java b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_8.java
index 26e9814e33..34d2ade723 100644
--- a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_8.java
+++ b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_8.java
@@ -45,10 +45,9 @@ public class BasicMessageProducer_0_8 extends BasicMessageProducer
{
BasicMessageProducer_0_8(AMQConnection connection, AMQDestination destination, boolean transacted, int channelId,
- AMQSession session, AMQProtocolHandler protocolHandler, long producerId, boolean immediate, boolean mandatory,
- boolean waitUntilSent) throws AMQException
+ AMQSession session, AMQProtocolHandler protocolHandler, long producerId, boolean immediate, boolean mandatory) throws AMQException
{
- super(connection, destination,transacted,channelId,session, protocolHandler, producerId, immediate, mandatory,waitUntilSent);
+ super(connection, destination,transacted,channelId,session, protocolHandler, producerId, immediate, mandatory);
}
void declareDestination(AMQDestination destination)
@@ -73,7 +72,7 @@ public class BasicMessageProducer_0_8 extends BasicMessageProducer
void sendMessage(AMQDestination destination, Message origMessage, AbstractJMSMessage message,
UUID messageId, int deliveryMode,int priority, long timeToLive, boolean mandatory,
- boolean immediate, boolean wait) throws JMSException
+ boolean immediate) throws JMSException
{
BasicPublishBody body = getSession().getMethodRegistry().createBasicPublishBody(_session.getTicket(),
destination.getExchangeName(),
@@ -168,7 +167,7 @@ public class BasicMessageProducer_0_8 extends BasicMessageProducer
throw jmse;
}
- _protocolHandler.writeFrame(compositeFrame, wait);
+ _protocolHandler.writeFrame(compositeFrame);
}
/**
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java
index 2cf19bf391..b9d4d6fa95 100644
--- a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java
+++ b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java
@@ -78,7 +78,7 @@ public class ChannelCloseMethodHandler implements StateAwareMethodListener<Chann
{
throw new AMQNoRouteException("Error: " + reason, null, null);
}
- else if (errorCode == AMQConstant.INVALID_ARGUMENT)
+ else if (errorCode == AMQConstant.ARGUMENT_INVALID)
{
_logger.debug("Broker responded with Invalid Argument.");
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate.java b/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate.java
index c2821591d8..a9434edf49 100644
--- a/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate.java
+++ b/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate.java
@@ -26,9 +26,7 @@ import org.apache.qpid.client.AMQSession;
import javax.jms.Destination;
import javax.jms.JMSException;
-import java.nio.ByteBuffer;
import java.util.Enumeration;
-import java.util.Map;
import java.util.UUID;
public interface AMQMessageDelegate
@@ -130,9 +128,9 @@ public interface AMQMessageDelegate
void removeProperty(final String propertyName) throws JMSException;
- void setAMQSession(final AMQSession s);
+ void setAMQSession(final AMQSession<?,?> s);
- AMQSession getAMQSession();
+ AMQSession<?,?> getAMQSession();
long getDeliveryTag();
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate_0_10.java b/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate_0_10.java
index 43b3b85641..f360b546b2 100644
--- a/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate_0_10.java
+++ b/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate_0_10.java
@@ -37,12 +37,10 @@ import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageFormatException;
import javax.jms.MessageNotWriteableException;
-import javax.jms.Session;
import org.apache.qpid.AMQException;
import org.apache.qpid.AMQPInvalidClassException;
import org.apache.qpid.client.AMQDestination;
-import org.apache.qpid.client.AMQSession;
import org.apache.qpid.client.AMQSession_0_10;
import org.apache.qpid.client.CustomJMSXProperty;
import org.apache.qpid.framing.AMQShortString;
@@ -76,13 +74,8 @@ public class AMQMessageDelegate_0_10 extends AbstractAMQMessageDelegate
private Destination _destination;
-
private MessageProperties _messageProps;
private DeliveryProperties _deliveryProps;
- /** If the acknowledge mode is CLIENT_ACKNOWLEDGE the session is required */
- private AMQSession _session;
- private final long _deliveryTag;
-
protected AMQMessageDelegate_0_10()
{
@@ -92,9 +85,9 @@ public class AMQMessageDelegate_0_10 extends AbstractAMQMessageDelegate
protected AMQMessageDelegate_0_10(MessageProperties messageProps, DeliveryProperties deliveryProps, long deliveryTag)
{
+ super(deliveryTag);
_messageProps = messageProps;
_deliveryProps = deliveryProps;
- _deliveryTag = deliveryTag;
_readableProperties = (_messageProps != null);
AMQDestination dest;
@@ -205,7 +198,6 @@ public class AMQMessageDelegate_0_10 extends AbstractAMQMessageDelegate
}
}
-
public long getJMSTimestamp() throws JMSException
{
return _deliveryProps.getTimestamp();
@@ -291,7 +283,7 @@ public class AMQMessageDelegate_0_10 extends AbstractAMQMessageDelegate
try
{
- return AMQDestination.createDestination("ADDR:" + addr.toString());
+ return AMQDestination.createDestination("ADDR:" + addr);
}
catch(Exception e)
{
@@ -325,14 +317,14 @@ public class AMQMessageDelegate_0_10 extends AbstractAMQMessageDelegate
{
try
{
- int type = ((AMQSession_0_10)_session).resolveAddressType(amqd);
+ int type = ((AMQSession_0_10)getAMQSession()).resolveAddressType(amqd);
if (type == AMQDestination.QUEUE_TYPE)
{
- ((AMQSession_0_10)_session).setLegacyFiledsForQueueType(amqd);
+ ((AMQSession_0_10)getAMQSession()).setLegacyFiledsForQueueType(amqd);
}
else
{
- ((AMQSession_0_10)_session).setLegacyFiledsForTopicType(amqd);
+ ((AMQSession_0_10)getAMQSession()).setLegacyFiledsForTopicType(amqd);
}
}
catch(AMQException ex)
@@ -905,64 +897,6 @@ public class AMQMessageDelegate_0_10 extends AbstractAMQMessageDelegate
_readableProperties = false;
}
-
- public void acknowledgeThis() throws JMSException
- {
- // the JMS 1.1 spec says in section 3.6 that calls to acknowledge are ignored when client acknowledge
- // is not specified. In our case, we only set the session field where client acknowledge mode is specified.
- if (_session != null && _session.getAcknowledgeMode() == Session.CLIENT_ACKNOWLEDGE)
- {
- if (_session.getAMQConnection().isClosed())
- {
- throw new javax.jms.IllegalStateException("Connection is already closed");
- }
-
- // we set multiple to true here since acknowledgment implies acknowledge of all previous messages
- // received on the session
- _session.acknowledgeMessage(_deliveryTag, true);
- }
- }
-
- public void acknowledge() throws JMSException
- {
- if (_session != null && _session.getAcknowledgeMode() == Session.CLIENT_ACKNOWLEDGE)
- {
- _session.acknowledge();
- }
- }
-
-
- /**
- * The session is set when CLIENT_ACKNOWLEDGE mode is used so that the CHANNEL ACK can be sent when the user calls
- * acknowledge()
- *
- * @param s the AMQ session that delivered this message
- */
- public void setAMQSession(AMQSession s)
- {
- _session = s;
- }
-
- public AMQSession getAMQSession()
- {
- return _session;
- }
-
- /**
- * Get the AMQ message number assigned to this message
- *
- * @return the message number
- */
- public long getDeliveryTag()
- {
- return _deliveryTag;
- }
-
-
-
-
-
-
protected void checkPropertyName(CharSequence propertyName)
{
if (propertyName == null)
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate_0_8.java b/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate_0_8.java
index b9ba946a20..9ab03412fe 100644
--- a/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate_0_8.java
+++ b/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate_0_8.java
@@ -30,7 +30,6 @@ import java.util.UUID;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageNotWriteableException;
-import javax.jms.Session;
import org.apache.qpid.client.AMQDestination;
import org.apache.qpid.client.AMQQueue;
@@ -60,15 +59,12 @@ public class AMQMessageDelegate_0_8 extends AbstractAMQMessageDelegate
Boolean.parseBoolean(System.getProperties().getProperty(AMQSession.STRICT_AMQP, AMQSession.STRICT_AMQP_DEFAULT));
private ContentHeaderProperties _contentHeaderProperties;
- /** If the acknowledge mode is CLIENT_ACKNOWLEDGE the session is required */
- private AMQSession _session;
- private final long _deliveryTag;
// The base set of items that needs to be set.
private AMQMessageDelegate_0_8(BasicContentHeaderProperties properties, long deliveryTag)
{
+ super(deliveryTag);
_contentHeaderProperties = properties;
- _deliveryTag = deliveryTag;
_readableProperties = (_contentHeaderProperties != null);
_headerAdapter = new JMSHeaderAdapter(_readableProperties ? ((BasicContentHeaderProperties) _contentHeaderProperties).getHeaders()
: (new BasicContentHeaderProperties()).getHeaders() );
@@ -518,58 +514,4 @@ public class AMQMessageDelegate_0_8 extends AbstractAMQMessageDelegate
_readableProperties = false;
}
-
-
- public void acknowledgeThis() throws JMSException
- {
- // the JMS 1.1 spec says in section 3.6 that calls to acknowledge are ignored when client acknowledge
- // is not specified. In our case, we only set the session field where client acknowledge mode is specified.
- if (_session != null && _session.getAcknowledgeMode() == Session.CLIENT_ACKNOWLEDGE)
- {
- if (_session.getAMQConnection().isClosed())
- {
- throw new javax.jms.IllegalStateException("Connection is already closed");
- }
-
- // we set multiple to true here since acknowledgement implies acknowledge of all previous messages
- // received on the session
- _session.acknowledgeMessage(_deliveryTag, true);
- }
- }
-
- public void acknowledge() throws JMSException
- {
- if (_session != null && _session.getAcknowledgeMode() == Session.CLIENT_ACKNOWLEDGE)
- {
- _session.acknowledge();
- }
- }
-
-
- /**
- * The session is set when CLIENT_ACKNOWLEDGE mode is used so that the CHANNEL ACK can be sent when the user calls
- * acknowledge()
- *
- * @param s the AMQ session that delivered this message
- */
- public void setAMQSession(AMQSession s)
- {
- _session = s;
- }
-
- public AMQSession getAMQSession()
- {
- return _session;
- }
-
- /**
- * Get the AMQ message number assigned to this message
- *
- * @return the message number
- */
- public long getDeliveryTag()
- {
- return _deliveryTag;
- }
-
}
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractAMQMessageDelegate.java b/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractAMQMessageDelegate.java
index 89fbc9722c..1b6c0c751d 100644
--- a/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractAMQMessageDelegate.java
+++ b/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractAMQMessageDelegate.java
@@ -23,9 +23,13 @@ package org.apache.qpid.client.message;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
+import javax.jms.JMSException;
+import javax.jms.Session;
+
import org.apache.qpid.client.AMQAnyDestination;
import org.apache.qpid.client.AMQDestination;
import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
import org.apache.qpid.client.AMQTopic;
import org.apache.qpid.exchange.ExchangeDefaults;
import org.apache.qpid.framing.AMQShortString;
@@ -78,7 +82,25 @@ public abstract class AbstractAMQMessageDelegate implements AMQMessageDelegate
new ExchangeInfo(ExchangeDefaults.HEADERS_EXCHANGE_NAME.toString(),
ExchangeDefaults.HEADERS_EXCHANGE_CLASS.toString(),
AMQDestination.QUEUE_TYPE));
-
+ }
+
+ /** If the acknowledge mode is CLIENT_ACKNOWLEDGE the session is required */
+ private AMQSession<?,?> _session;
+ private final long _deliveryTag;
+
+ protected AbstractAMQMessageDelegate(long deliveryTag)
+ {
+ _deliveryTag = deliveryTag;
+ }
+
+ /**
+ * Get the AMQ message number assigned to this message
+ *
+ * @return the message number
+ */
+ public long getDeliveryTag()
+ {
+ return _deliveryTag;
}
/**
@@ -157,6 +179,47 @@ public abstract class AbstractAMQMessageDelegate implements AMQMessageDelegate
{
return _exchangeMap.containsKey(exchange);
}
+
+ public void acknowledgeThis() throws JMSException
+ {
+ // the JMS 1.1 spec says in section 3.6 that calls to acknowledge are ignored when client acknowledge
+ // is not specified. In our case, we only set the session field where client acknowledge mode is specified.
+ if (_session != null && _session.getAcknowledgeMode() == Session.CLIENT_ACKNOWLEDGE)
+ {
+ if (_session.getAMQConnection().isClosed())
+ {
+ throw new javax.jms.IllegalStateException("Connection is already closed");
+ }
+
+ // we set multiple to true here since acknowledgement implies acknowledge of all previous messages
+ // received on the session
+ _session.acknowledgeMessage(getDeliveryTag(), true);
+ }
+ }
+
+ public void acknowledge() throws JMSException
+ {
+ if (_session != null && _session.getAcknowledgeMode() == Session.CLIENT_ACKNOWLEDGE)
+ {
+ _session.acknowledge();
+ }
+ }
+
+ /**
+ * The session is set when CLIENT_ACKNOWLEDGE mode is used so that the CHANNEL ACK can be sent when the user calls
+ * acknowledge()
+ *
+ * @param s the AMQ session that delivered this message
+ */
+ public void setAMQSession(AMQSession<?,?> s)
+ {
+ _session = s;
+ }
+
+ public AMQSession<?,?> getAMQSession()
+ {
+ return _session;
+ }
}
class ExchangeInfo
@@ -202,5 +265,5 @@ class ExchangeInfo
public void setDestType(int destType)
{
this.destType = destType;
- }
+ }
}
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java
index 208496ec41..284954edba 100644
--- a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java
+++ b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java
@@ -30,7 +30,6 @@ import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import org.apache.qpid.AMQConnectionClosedException;
@@ -66,7 +65,6 @@ import org.apache.qpid.protocol.ProtocolEngine;
import org.apache.qpid.thread.Threading;
import org.apache.qpid.transport.Sender;
import org.apache.qpid.transport.network.NetworkConnection;
-import org.apache.qpid.transport.network.NetworkTransport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -516,18 +514,7 @@ public class AMQProtocolHandler implements ProtocolEngine
return getStateManager().createWaiter(states);
}
- /**
- * 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)
- {
- writeFrame(frame, false);
- }
-
- public synchronized void writeFrame(AMQDataBlock frame, boolean wait)
+ public synchronized void writeFrame(AMQDataBlock frame)
{
final ByteBuffer buf = asByteBuffer(frame);
_writtenBytes += buf.remaining();
@@ -678,22 +665,21 @@ public class AMQProtocolHandler implements ProtocolEngine
* <p/>If a failover exception occurs whilst closing the connection it is ignored, as the connection is closed
* anyway.
*
- * @param timeout The timeout to wait for an acknowledgement to the close request.
+ * @param timeout The timeout to wait for an acknowledgment to the close request.
*
* @throws AMQException If the close fails for any reason.
*/
public void closeConnection(long timeout) throws AMQException
{
- ConnectionCloseBody body = _protocolSession.getMethodRegistry().createConnectionCloseBody(AMQConstant.REPLY_SUCCESS.getCode(), // replyCode
- new AMQShortString("JMS client is closing the connection."), 0, 0);
-
- final AMQFrame frame = body.generateFrame(0);
-
- //If the connection is already closed then don't do a syncWrite
if (!getStateManager().getCurrentState().equals(AMQState.CONNECTION_CLOSED))
{
+ // Connection is already closed then don't do a syncWrite
try
{
+ final ConnectionCloseBody body = _protocolSession.getMethodRegistry().createConnectionCloseBody(AMQConstant.REPLY_SUCCESS.getCode(), // replyCode
+ new AMQShortString("JMS client is closing the connection."), 0, 0);
+ final AMQFrame frame = body.generateFrame(0);
+
syncWrite(frame, ConnectionCloseOkBody.class, timeout);
_network.close();
closed();
@@ -704,10 +690,9 @@ public class AMQProtocolHandler implements ProtocolEngine
}
catch (FailoverException e)
{
- _logger.debug("FailoverException interrupted connection close, ignoring as connection close anyway.");
+ _logger.debug("FailoverException interrupted connection close, ignoring as connection closed anyway.");
}
}
-
}
/** @return the number of bytes read from this protocol session */
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java
index 5b7d272506..a938bd47f8 100644
--- a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java
+++ b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java
@@ -20,27 +20,35 @@
*/
package org.apache.qpid.client.protocol;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import javax.jms.JMSException;
import javax.security.sasl.SaslClient;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
import org.apache.qpid.AMQException;
import org.apache.qpid.client.AMQConnection;
import org.apache.qpid.client.AMQSession;
import org.apache.qpid.client.ConnectionTuneParameters;
+import org.apache.qpid.client.handler.ClientMethodDispatcherImpl;
import org.apache.qpid.client.message.UnprocessedMessage;
import org.apache.qpid.client.message.UnprocessedMessage_0_8;
import org.apache.qpid.client.state.AMQStateManager;
-import org.apache.qpid.client.state.AMQState;
-import org.apache.qpid.framing.*;
+import org.apache.qpid.framing.AMQDataBlock;
+import org.apache.qpid.framing.AMQMethodBody;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ContentBody;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.HeartbeatBody;
+import org.apache.qpid.framing.MethodDispatcher;
+import org.apache.qpid.framing.MethodRegistry;
+import org.apache.qpid.framing.ProtocolInitiation;
+import org.apache.qpid.framing.ProtocolVersion;
import org.apache.qpid.protocol.AMQConstant;
import org.apache.qpid.protocol.AMQVersionAwareProtocolSession;
import org.apache.qpid.transport.Sender;
-import org.apache.qpid.client.handler.ClientMethodDispatcherImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Wrapper for protocol session that provides type-safe access to session attributes. <p/> The underlying protocol
@@ -289,22 +297,11 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession
return _connection.getSession(channelId);
}
- /**
- * 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)
{
_protocolHandler.writeFrame(frame);
}
- public void writeFrame(AMQDataBlock frame, boolean wait)
- {
- _protocolHandler.writeFrame(frame, wait);
- }
-
/**
* Starts the process of closing a session
*
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java b/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java
index 9c7d62670c..0d6fc727c1 100644
--- a/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java
+++ b/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java
@@ -31,7 +31,6 @@ import org.slf4j.LoggerFactory;
import java.util.Set;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
-import java.io.IOException;
/**
* The state manager is responsible for managing the state of the protocol session. <p/>
@@ -48,7 +47,7 @@ import java.io.IOException;
*
* The two step process is required as there is an inherit race condition between starting a process that will cause
* the state to change and then attempting to wait for that change. The interest in the change must be first set up so
- * that any asynchrous errors that occur can be delivered to the correct waiters.
+ * that any asynchronous errors that occur can be delivered to the correct waiters.
*/
public class AMQStateManager implements AMQMethodListener
{
@@ -84,7 +83,10 @@ public class AMQStateManager implements AMQMethodListener
public AMQState getCurrentState()
{
- return _currentState;
+ synchronized (_stateLock)
+ {
+ return _currentState;
+ }
}
public void changeState(AMQState newState)
@@ -114,7 +116,7 @@ public class AMQStateManager implements AMQMethodListener
}
/**
- * Setting of the ProtocolSession will be required when Failover has been successfuly compeleted.
+ * Setting of the ProtocolSession will be required when Failover has been successfully completed.
*
* The new {@link AMQProtocolSession} that has been re-established needs to be provided as that is now the
* connection to the network.
@@ -131,9 +133,9 @@ public class AMQStateManager implements AMQMethodListener
}
/**
- * Propogate error to waiters
+ * Propagate error to waiters
*
- * @param error The error to propogate.
+ * @param error The error to propagate.
*/
public void error(Exception error)
{
@@ -177,7 +179,7 @@ public class AMQStateManager implements AMQMethodListener
}
/**
- * Create and add a new waiter to the notifcation list.
+ * Create and add a new waiter to the notification list.
*
* @param states The waiter will attempt to wait for one of these desired set states to be achived.
*
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/state/StateWaiter.java b/qpid/java/client/src/main/java/org/apache/qpid/client/state/StateWaiter.java
index 79f438d35d..732480e1c9 100644
--- a/qpid/java/client/src/main/java/org/apache/qpid/client/state/StateWaiter.java
+++ b/qpid/java/client/src/main/java/org/apache/qpid/client/state/StateWaiter.java
@@ -34,7 +34,7 @@ import java.util.Set;
*
* On construction the current state and a set of States to await for is provided.
*
- * When await() is called the state at constuction is compared against the awaitStates. If the state at construction is
+ * When await() is called the state at construction is compared against the awaitStates. If the state at construction is
* a desired state then await() returns immediately.
*
* Otherwise it will block for the set timeout for a desired state to be achieved.
@@ -48,9 +48,9 @@ public class StateWaiter extends BlockingWaiter<AMQState>
{
private static final Logger _logger = LoggerFactory.getLogger(StateWaiter.class);
- Set<AMQState> _awaitStates;
- private AMQState _startState;
- private AMQStateManager _stateManager;
+ private final Set<AMQState> _awaitStates;
+ private final AMQState _startState;
+ private final AMQStateManager _stateManager;
/**
*
@@ -78,9 +78,9 @@ public class StateWaiter extends BlockingWaiter<AMQState>
}
/**
- * Await for the requried State to be achieved within the default timeout.
+ * Await for the required State to be achieved within the default timeout.
* @return The achieved state that was requested.
- * @throws AMQException The exception that prevented the required state from being achived.
+ * @throws AMQException The exception that prevented the required state from being achieved.
*/
public AMQState await() throws AMQException
{
@@ -88,13 +88,13 @@ public class StateWaiter extends BlockingWaiter<AMQState>
}
/**
- * Await for the requried State to be achieved.
+ * Await for the required State to be achieved.
*
* <b>It is the responsibility of this class to remove the waiter from the StateManager
*
- * @param timeout The time in milliseconds to wait for any of the states to be achived.
+ * @param timeout The time in milliseconds to wait for any of the states to be achieved.
* @return The achieved state that was requested.
- * @throws AMQException The exception that prevented the required state from being achived.
+ * @throws AMQException The exception that prevented the required state from being achieved.
*/
public AMQState await(long timeout) throws AMQException
{
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/util/BlockingWaiter.java b/qpid/java/client/src/main/java/org/apache/qpid/client/util/BlockingWaiter.java
index 208658a5ff..bec41644fc 100644
--- a/qpid/java/client/src/main/java/org/apache/qpid/client/util/BlockingWaiter.java
+++ b/qpid/java/client/src/main/java/org/apache/qpid/client/util/BlockingWaiter.java
@@ -28,9 +28,8 @@ import java.util.concurrent.locks.ReentrantLock;
import org.apache.qpid.AMQException;
import org.apache.qpid.AMQTimeoutException;
import org.apache.qpid.client.failover.FailoverException;
-import org.apache.qpid.framing.AMQMethodBody;
-import org.apache.qpid.protocol.AMQMethodEvent;
-import org.apache.qpid.protocol.AMQMethodListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* BlockingWaiter is a 'rendezvous' which delegates handling of
@@ -64,6 +63,8 @@ import org.apache.qpid.protocol.AMQMethodListener;
*/
public abstract class BlockingWaiter<T>
{
+ private static final Logger _logger = LoggerFactory.getLogger(BlockingWaiter.class);
+
/** This flag is used to indicate that the blocked for method has been received. */
private volatile boolean _ready = false;
@@ -180,7 +181,7 @@ public abstract class BlockingWaiter<T>
}
catch (InterruptedException e)
{
- System.err.println(e.getMessage());
+ _logger.error(e.getMessage(), e);
// IGNORE -- //fixme this isn't ideal as being interrupted isn't equivellant to sucess
// if (!_ready && timeout != -1)
// {
@@ -228,12 +229,12 @@ public abstract class BlockingWaiter<T>
}
/**
- * This is a callback, called when an error has occured that should interupt any waiter.
+ * This is a callback, called when an error has occurred that should interrupt any waiter.
* It is also called from within this class to avoid code repetition but it should only be called by the MINA threads.
*
* Once closed any notification of an exception will be ignored.
*
- * @param e The exception being propogated.
+ * @param e The exception being propagated.
*/
public void error(Exception e)
{
@@ -255,7 +256,7 @@ public abstract class BlockingWaiter<T>
}
else
{
- System.err.println("WARNING: new error '" + e == null ? "null" : e.getMessage() + "' arrived while old one not yet processed:" + _error.getMessage());
+ _logger.error("WARNING: new error '" + e == null ? "null" : e.getMessage() + "' arrived while old one not yet processed:" + _error.getMessage());
}
if (_waiting.get())
@@ -272,7 +273,7 @@ public abstract class BlockingWaiter<T>
}
catch (InterruptedException e1)
{
- System.err.println(e.getMessage());
+ _logger.error(e1.getMessage(), e1);
}
}
_errorAck = false;
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/jms/MessageProducer.java b/qpid/java/client/src/main/java/org/apache/qpid/jms/MessageProducer.java
index b830c377b8..4ad917fa83 100644
--- a/qpid/java/client/src/main/java/org/apache/qpid/jms/MessageProducer.java
+++ b/qpid/java/client/src/main/java/org/apache/qpid/jms/MessageProducer.java
@@ -51,7 +51,4 @@ public interface MessageProducer extends javax.jms.MessageProducer
int priority, long timeToLive, boolean mandatory, boolean immediate)
throws JMSException;
- void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive,
- boolean mandatory, boolean immediate, boolean waitUntilSent) throws JMSException;
-
}
diff --git a/qpid/java/client/src/test/java/org/apache/qpid/client/AMQSession_0_10Test.java b/qpid/java/client/src/test/java/org/apache/qpid/client/AMQSession_0_10Test.java
index ea55419144..849827216c 100644
--- a/qpid/java/client/src/test/java/org/apache/qpid/client/AMQSession_0_10Test.java
+++ b/qpid/java/client/src/test/java/org/apache/qpid/client/AMQSession_0_10Test.java
@@ -97,7 +97,7 @@ public class AMQSession_0_10Test extends TestCase
AMQSession_0_10 session = createThrowingExceptionAMQSession_0_10();
try
{
- session.createMessageProducer(createDestination(), true, true, true, 1l);
+ session.createMessageProducer(createDestination(), true, true, 1l);
fail("JMSException should be thrown");
}
catch (Exception e)
@@ -350,7 +350,7 @@ public class AMQSession_0_10Test extends TestCase
AMQSession_0_10 session = createAMQSession_0_10();
try
{
- session.createMessageProducer(createDestination(), true, true, true, 1l);
+ session.createMessageProducer(createDestination(), true, true, 1l);
}
catch (Exception e)
{
diff --git a/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseMethodHandlerNoCloseOk.java b/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseMethodHandlerNoCloseOk.java
index 66f220643c..d560c413e6 100644
--- a/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseMethodHandlerNoCloseOk.java
+++ b/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseMethodHandlerNoCloseOk.java
@@ -73,7 +73,7 @@ public class ChannelCloseMethodHandlerNoCloseOk implements StateAwareMethodListe
{
throw new AMQNoRouteException("Error: " + reason, null, null);
}
- else if (errorCode == AMQConstant.INVALID_ARGUMENT)
+ else if (errorCode == AMQConstant.ARGUMENT_INVALID)
{
_logger.debug("Broker responded with Invalid Argument.");
diff --git a/qpid/java/client/src/test/java/org/apache/qpid/test/unit/message/TestAMQSession.java b/qpid/java/client/src/test/java/org/apache/qpid/test/unit/message/TestAMQSession.java
index 4637c6e505..6759b43387 100644
--- a/qpid/java/client/src/test/java/org/apache/qpid/test/unit/message/TestAMQSession.java
+++ b/qpid/java/client/src/test/java/org/apache/qpid/test/unit/message/TestAMQSession.java
@@ -20,17 +20,24 @@
*/
package org.apache.qpid.test.unit.message;
-import org.apache.qpid.client.*;
+import java.util.Map;
+
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.TemporaryQueue;
+import javax.jms.Topic;
+import javax.jms.TopicSubscriber;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.AMQDestination;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.BasicMessageConsumer_0_8;
+import org.apache.qpid.client.BasicMessageProducer_0_8;
+import org.apache.qpid.client.failover.FailoverException;
import org.apache.qpid.client.message.AMQMessageDelegateFactory;
import org.apache.qpid.client.protocol.AMQProtocolHandler;
-import org.apache.qpid.client.failover.FailoverException;
import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.framing.FieldTable;
-import org.apache.qpid.AMQException;
-
-import javax.jms.*;
-
-import java.util.Map;
public class TestAMQSession extends AMQSession<BasicMessageConsumer_0_8, BasicMessageProducer_0_8>
{
@@ -57,7 +64,12 @@ public class TestAMQSession extends AMQSession<BasicMessageConsumer_0_8, BasicMe
}
- public void sendCommit() throws AMQException, FailoverException
+ public void commitImpl() throws AMQException, FailoverException
+ {
+
+ }
+
+ public void acknowledgeImpl()
{
}
@@ -117,7 +129,7 @@ public class TestAMQSession extends AMQSession<BasicMessageConsumer_0_8, BasicMe
}
- public BasicMessageProducer_0_8 createMessageProducer(Destination destination, boolean mandatory, boolean immediate, boolean waitUntilSent, long producerId)
+ public BasicMessageProducer_0_8 createMessageProducer(Destination destination, boolean mandatory, boolean immediate, long producerId)
{
return null;
}
diff --git a/qpid/java/common.xml b/qpid/java/common.xml
index 6bb71ef24e..c6688ee2de 100644
--- a/qpid/java/common.xml
+++ b/qpid/java/common.xml
@@ -42,7 +42,6 @@
<property name="build.report" location="${build}/report"/>
<property name="build.release" location="${build}/release"/>
<property name="build.release.prepare" location="${build.release}/prepare"/>
- <property name="build.data" location="${build.scratch}/data"/>
<property name="build.plugins" location="${build}/lib/plugins"/>
<property name="build.coveragereport" location="${build}/coverage"/>
<property name="build.findbugs" location="${build}/findbugs"/>
diff --git a/qpid/java/common/bin/qpid-run b/qpid/java/common/bin/qpid-run
index 15d88992df..ef4363e88b 100755
--- a/qpid/java/common/bin/qpid-run
+++ b/qpid/java/common/bin/qpid-run
@@ -77,7 +77,10 @@ fi
#Set the default system properties that we'll use now that they have
#all been initialised
-SYSTEM_PROPS="-Damqj.logging.level=$AMQJ_LOGGING_LEVEL -DQPID_HOME=$QPID_HOME -DQPID_WORK=$QPID_WORK"
+declare -a SYSTEM_PROPS
+SYSTEM_PROPS[${#SYSTEM_PROPS[@]}]="-Damqj.logging.level=$AMQJ_LOGGING_LEVEL"
+SYSTEM_PROPS[${#SYSTEM_PROPS[@]}]="-DQPID_HOME=$QPID_HOME"
+SYSTEM_PROPS[${#SYSTEM_PROPS[@]}]="-DQPID_WORK=$QPID_WORK"
#If logprefix or logsuffix set to use PID make that happen
#Otherwise just pass the value through for these props
@@ -90,7 +93,7 @@ if [ -n "$QPID_LOG_PREFIX" ]; then
log $INFO Using qpid logprefix property
LOG_PREFIX=" -Dlogprefix=$QPID_LOG_PREFIX"
fi
- SYSTEM_PROPS="${SYSTEM_PROPS} ${LOG_PREFIX}"
+ SYSTEM_PROPS[${#SYSTEM_PROPS[@]}]="${LOG_PREFIX}"
fi
if [ -n "$QPID_LOG_SUFFIX" ]; then
@@ -101,10 +104,10 @@ if [ -n "$QPID_LOG_SUFFIX" ]; then
log $INFO Using qpig logsuffix property
LOG_SUFFIX=" -Dlogsuffix=$QPID_LOG_SUFFIX"
fi
- SYSTEM_PROPS="${SYSTEM_PROPS} ${LOG_SUFFIX}"
+ SYSTEM_PROPS[${#SYSTEM_PROPS[@]}]="${LOG_SUFFIX}"
fi
-log $INFO System Properties set to $SYSTEM_PROPS
+log $INFO System Properties set to ${SYSTEM_PROPS[@]}
log $INFO QPID_OPTS set to $QPID_OPTS
program=$(basename $0)
@@ -254,6 +257,6 @@ if $cygwin; then
JAVA=$(cygpath -u $JAVA)
fi
-COMMAND=($JAVA $JAVA_VM $QPID_PNAME $JAVA_GC $JAVA_MEM $SYSTEM_PROPS $JAVA_OPTS $QPID_OPTS "${JAVA_ARGS[@]}")
+COMMAND=($JAVA $JAVA_VM $QPID_PNAME $JAVA_GC $JAVA_MEM "${SYSTEM_PROPS[@]}" $JAVA_OPTS $QPID_OPTS "${JAVA_ARGS[@]}")
DISPATCH
diff --git a/qpid/java/common/src/main/java/org/apache/qpid/AMQInvalidArgumentException.java b/qpid/java/common/src/main/java/org/apache/qpid/AMQInvalidArgumentException.java
index baca2a4773..2bbaaef1fc 100644
--- a/qpid/java/common/src/main/java/org/apache/qpid/AMQInvalidArgumentException.java
+++ b/qpid/java/common/src/main/java/org/apache/qpid/AMQInvalidArgumentException.java
@@ -34,7 +34,7 @@ public class AMQInvalidArgumentException extends AMQException
{
public AMQInvalidArgumentException(String message, Throwable cause)
{
- super(AMQConstant.INVALID_ARGUMENT, message, cause);
+ super(AMQConstant.ARGUMENT_INVALID, message, cause);
}
public boolean isHardError()
diff --git a/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQConstant.java b/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQConstant.java
index f9f6ca8444..14d1befaf1 100644
--- a/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQConstant.java
+++ b/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQConstant.java
@@ -104,7 +104,7 @@ public final class AMQConstant
public static final AMQConstant REQUEST_TIMEOUT = new AMQConstant(408, "Request Timeout", true);
- public static final AMQConstant INVALID_ARGUMENT = new AMQConstant(409, "argument invalid", true);
+ public static final AMQConstant ARGUMENT_INVALID = new AMQConstant(409, "argument invalid", true);
/**
* The client sent a malformed frame that the server could not decode. This strongly implies a programming error
@@ -153,10 +153,7 @@ public final class AMQConstant
public static final AMQConstant FRAME_MIN_SIZE = new AMQConstant(4096, "frame min size", true);
- /**
- * The server does not support the protocol version
- */
- public static final AMQConstant UNSUPPORTED_BROKER_PROTOCOL_ERROR = new AMQConstant(542, "broker unsupported protocol", true);
+ public static final AMQConstant INVALID_ARGUMENT = new AMQConstant(542, "invalid argument", true);
/**
* The client imp does not support the protocol version
*/
diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/Connection.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/Connection.java
index 347bf8e649..1c521244d0 100644
--- a/qpid/java/common/src/main/java/org/apache/qpid/transport/Connection.java
+++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/Connection.java
@@ -485,26 +485,12 @@ public class Connection extends ConnectionInvoker
{
synchronized (lock)
{
- List <Binary> transactedSessions = new ArrayList();
for (Session ssn : sessions.values())
{
- if (ssn.isTransacted())
- {
- transactedSessions.add(ssn.getName());
- ssn.setState(Session.State.CLOSED);
- }
- else
- {
- map(ssn);
- ssn.attach();
- ssn.resume();
- }
+ map(ssn);
+ ssn.resume();
}
- for (Binary ssn_name : transactedSessions)
- {
- sessions.remove(ssn_name);
- }
setState(OPEN);
}
}
diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/Session.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/Session.java
index 556134f984..0de558d152 100644
--- a/qpid/java/common/src/main/java/org/apache/qpid/transport/Session.java
+++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/Session.java
@@ -259,6 +259,8 @@ public class Session extends SessionInvoker
{
synchronized (commands)
{
+ attach();
+
for (int i = maxComplete + 1; lt(i, commandsOut); i++)
{
Method m = commands[mod(i, commands.length)];
@@ -299,11 +301,18 @@ public class Session extends SessionInvoker
sessionCommandPoint(m.getId(), 0);
send(m);
}
-
+
sessionCommandPoint(commandsOut, 0);
+
sessionFlush(COMPLETED);
resumer = Thread.currentThread();
state = RESUMING;
+
+ if(isTransacted())
+ {
+ txSelect();
+ }
+
listener.resumed(this);
resumer = null;
}
@@ -567,17 +576,6 @@ public class Session extends SessionInvoker
{
if (m.getEncodedTrack() == Frame.L4)
{
-
- if (state == DETACHED && transacted)
- {
- state = CLOSED;
- delegate.closed(this);
- connection.removeSession(this);
- throw new SessionException(
- "Session failed over, possibly in the middle of a transaction. " +
- "Closing the session. Any Transaction in progress will be rolledback.");
- }
-
if (m.hasPayload())
{
acquireCredit();
@@ -585,24 +583,30 @@ public class Session extends SessionInvoker
synchronized (commands)
{
- if (state == DETACHED && m.isUnreliable())
+ //allow the txSelect operation to be invoked during resume
+ boolean skipWait = m instanceof TxSelect && state == RESUMING;
+
+ if(!skipWait)
{
- Thread current = Thread.currentThread();
- if (!current.equals(resumer))
+ if (state == DETACHED && m.isUnreliable())
{
- return;
+ Thread current = Thread.currentThread();
+ if (!current.equals(resumer))
+ {
+ return;
+ }
}
- }
- if (state != OPEN && state != CLOSED && state != CLOSING)
- {
- Thread current = Thread.currentThread();
- if (!current.equals(resumer))
+ if (state != OPEN && state != CLOSED && state != CLOSING)
{
- Waiter w = new Waiter(commands, timeout);
- while (w.hasTime() && (state != OPEN && state != CLOSED))
+ Thread current = Thread.currentThread();
+ if (!current.equals(resumer))
{
- w.await();
+ Waiter w = new Waiter(commands, timeout);
+ while (w.hasTime() && (state != OPEN && state != CLOSED))
+ {
+ w.await();
+ }
}
}
}
diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoNetworkConnection.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoNetworkConnection.java
index 52cc6363a9..bfc77539ce 100644
--- a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoNetworkConnection.java
+++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoNetworkConnection.java
@@ -54,8 +54,8 @@ public class IoNetworkConnection implements NetworkConnection
public void start()
{
- _ioReceiver.initiate();
_ioSender.initiate();
+ _ioReceiver.initiate();
}
public Sender<ByteBuffer> getSender()
diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoSender.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoSender.java
index 473d4d95ff..427487c879 100644
--- a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoSender.java
+++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoSender.java
@@ -113,6 +113,10 @@ public final class IoSender implements Runnable, Sender<ByteBuffer>
{
throw new SenderClosedException("sender is closed", exception);
}
+ if(!senderThread.isAlive())
+ {
+ throw new SenderException("sender thread not alive");
+ }
final int size = buffer.length;
int remaining = buf.remaining();
diff --git a/qpid/java/common/src/test/java/org/apache/qpid/test/utils/QpidTestCase.java b/qpid/java/common/src/test/java/org/apache/qpid/test/utils/QpidTestCase.java
index bde8bc68ad..e69f95f916 100644
--- a/qpid/java/common/src/test/java/org/apache/qpid/test/utils/QpidTestCase.java
+++ b/qpid/java/common/src/test/java/org/apache/qpid/test/utils/QpidTestCase.java
@@ -65,7 +65,7 @@ public class QpidTestCase extends TestCase
String exclusionListString = System.getProperties().getProperty("test.excludelist", "");
List<String> exclusionList = new ArrayList<String>();
- for (String uri : exclusionListURIs.split("\\s+"))
+ for (String uri : exclusionListURIs.split(";\\s*"))
{
File file = new File(uri);
if (file.exists())
@@ -87,6 +87,10 @@ public class QpidTestCase extends TestCase
_logger.warn("Exception when reading exclusion list", e);
}
}
+ else
+ {
+ _logger.info("Specified exclude file does not exist: " + uri);
+ }
}
if (!exclusionListString.equals(""))
diff --git a/qpid/java/lib/bdbstore/README.txt b/qpid/java/lib/bdbstore/README.txt
new file mode 100644
index 0000000000..80adb199bf
--- /dev/null
+++ b/qpid/java/lib/bdbstore/README.txt
@@ -0,0 +1,14 @@
+The BDB JE jar must be downloaded into this directory in order to allow the optional bdbstore module to be built against it.
+
+*NOTE* The BDB JE library is licensed under the Sleepycat Licence [1], which is not compatible with the Apache Lience v2.0. As a result, the BDB JE library is not distributed with the project, and the optional bdbstore module is not compiled by default.
+
+The jar file may be downloaded by either:
+
+ Seperately running the following command from the qpid/java/bdbstore dir: ant download-bdb
+
+ OR
+
+ Adding -Ddownload-bdb=true to your regular build command
+
+
+[1] http://www.oracle.com/technetwork/database/berkeleydb/downloads/jeoslicense-086837.html
diff --git a/qpid/java/module.xml b/qpid/java/module.xml
index 050382ee80..349fb91e4a 100644
--- a/qpid/java/module.xml
+++ b/qpid/java/module.xml
@@ -207,7 +207,6 @@
<mkdir dir="${build.etc}"/>
<mkdir dir="${build.lib}"/>
<mkdir dir="${build.results}"/>
- <mkdir dir="${build.data}"/>
<mkdir dir="${build.plugins}"/>
<mkdir dir="${module.classes}"/>
<mkdir dir="${module.precompiled}"/>
@@ -317,7 +316,7 @@
<property file="${build.scratch}/test-${profile}.properties"/>
<map property="test.excludefiles" value="${test.excludes}">
- <globmapper from="*" to="${test.profiles}/*"/>
+ <globmapper from="*" to="${test.profiles}/*;"/>
</map>
<condition property="dontruntest" value="dontruntest" else="runtest">
@@ -335,7 +334,8 @@
<echo message="Using profile:${profile}" level="info"/>
<junit fork="yes" forkmode="once" maxmemory="${test.mem}" reloading="no"
haltonfailure="${haltonfailure}" haltonerror="${haltonerror}"
- failureproperty="test.failures" printsummary="on" timeout="6000000" >
+ failureproperty="test.failures" printsummary="on" timeout="6000000"
+ dir="${project.root}" >
<jvmarg line="${jvm.args}" />
@@ -660,7 +660,8 @@
<mkdir dir="${build.coveragereport}" />
<junit fork="yes" forkmode="once" maxmemory="${test.mem}" reloading="no"
haltonfailure="${haltonfailure}" haltonerror="${haltonerror}"
- failureproperty="test.failures" printsummary="on" timeout="600000" >
+ failureproperty="test.failures" printsummary="on" timeout="600000"
+ dir="${project.root}" >
<sysproperty key="amqj.logging.level" value="${amqj.logging.level}"/>
<sysproperty key="amqj.protocol.logging.level" value="${amqj.protocol.logging.level}"/>
@@ -670,7 +671,6 @@
<sysproperty key="java.naming.factory.initial" value="${java.naming.factory.initial}"/>
<sysproperty key="java.naming.provider.url" value="${java.naming.provider.url}"/>
<sysproperty key="broker" value="${broker}"/>
- <sysproperty key="broker.clean" value="${broker.clean}"/>
<sysproperty key="broker.version" value="${broker.version}"/>
<sysproperty key="broker.ready" value="${broker.ready}" />
<sysproperty key="test.output" value="${module.results}"/>
diff --git a/qpid/java/systests/build.xml b/qpid/java/systests/build.xml
index 33ad2227bb..fb2bae1d47 100644
--- a/qpid/java/systests/build.xml
+++ b/qpid/java/systests/build.xml
@@ -19,7 +19,15 @@ nn - or more contributor license agreements. See the NOTICE file
-
-->
<project name="System Tests" default="build">
- <property name="module.depends" value="client management/tools/qpid-cli management/common broker broker/test common common/test junit-toolkit"/>
+
+ <condition property="systests.optional.depends" value="bdbstore" else="">
+ <and>
+ <contains string="${modules.opt}" substring="bdbstore"/>
+ <contains string="${profile}" substring="bdb"/>
+ </and>
+ </condition>
+
+ <property name="module.depends" value="client management/common broker broker/test common common/test junit-toolkit ${systests.optional.depends}"/>
<property name="module.test.src" location="src/main/java"/>
<property name="module.test.excludes"
value="**/DropInTest.java,**/TestClientControlledTest.java"/>
diff --git a/qpid/java/systests/etc/config-systests-aclv2.xml b/qpid/java/systests/etc/config-systests-aclv2.xml
index 33563e7891..e8b971a2a0 100644
--- a/qpid/java/systests/etc/config-systests-aclv2.xml
+++ b/qpid/java/systests/etc/config-systests-aclv2.xml
@@ -22,7 +22,7 @@
<configuration>
<system/>
<override>
- <xml fileName="${test.config}" optional="true"/>
+ <xml fileName="${QPID_HOME}/${test.config}" optional="true"/>
<xml fileName="${QPID_HOME}/etc/config-systests-aclv2-settings.xml"/>
<xml fileName="${QPID_HOME}/etc/config-systests-settings.xml"/>
<xml fileName="${QPID_HOME}/etc/config.xml"/>
diff --git a/qpid/java/systests/etc/config-systests-bdb-settings.xml b/qpid/java/systests/etc/config-systests-bdb-settings.xml
new file mode 100644
index 0000000000..4fa69d0abc
--- /dev/null
+++ b/qpid/java/systests/etc/config-systests-bdb-settings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ -
+ - Licensed to the Apache Software Foundation (ASF) under one
+ - or more contributor license agreements. See the NOTICE file
+ - distributed with this work for additional information
+ - regarding copyright ownership. The ASF licenses this file
+ - to you under the Apache License, Version 2.0 (the
+ - "License"); you may not use this file except in compliance
+ - with the License. You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing,
+ - software distributed under the License is distributed on an
+ - "AS IS" BASIS, WITHOUT 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>
+ <virtualhosts>${QPID_HOME}/etc/virtualhosts-systests-bdb.xml</virtualhosts>
+</broker>
+
+
diff --git a/qpid/java/systests/etc/config-systests-bdb.xml b/qpid/java/systests/etc/config-systests-bdb.xml
new file mode 100644
index 0000000000..9364006fcc
--- /dev/null
+++ b/qpid/java/systests/etc/config-systests-bdb.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ -
+ - Licensed to the Apache Software Foundation (ASF) under one
+ - or more contributor license agreements. See the NOTICE file
+ - distributed with this work for additional information
+ - regarding copyright ownership. The ASF licenses this file
+ - to you under the Apache License, Version 2.0 (the
+ - "License"); you may not use this file except in compliance
+ - with the License. You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing,
+ - software distributed under the License is distributed on an
+ - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ - KIND, either express or implied. See the License for the
+ - specific language governing permissions and limitations
+ - under the License.
+ -
+ -->
+<configuration>
+ <system/>
+ <override>
+ <xml fileName="${QPID_HOME}/${test.config}" optional="true"/>
+ <xml fileName="${QPID_HOME}/etc/config-systests-bdb-settings.xml"/>
+ <xml fileName="${QPID_HOME}/etc/config-systests-settings.xml"/>
+ <xml fileName="${QPID_HOME}/etc/config.xml"/>
+ </override>
+</configuration>
diff --git a/qpid/java/systests/etc/config-systests-derby.xml b/qpid/java/systests/etc/config-systests-derby.xml
index ba27a0c020..303154d8f0 100644
--- a/qpid/java/systests/etc/config-systests-derby.xml
+++ b/qpid/java/systests/etc/config-systests-derby.xml
@@ -22,7 +22,7 @@
<configuration>
<system/>
<override>
- <xml fileName="${test.config}" optional="true"/>
+ <xml fileName="${QPID_HOME}/${test.config}" optional="true"/>
<xml fileName="${QPID_HOME}/etc/config-systests-derby-settings.xml"/>
<xml fileName="${QPID_HOME}/etc/config-systests-settings.xml"/>
<xml fileName="${QPID_HOME}/etc/config.xml"/>
diff --git a/qpid/java/systests/etc/config-systests-firewall.xml b/qpid/java/systests/etc/config-systests-firewall.xml
index c0ce71210f..c73ac6a687 100644
--- a/qpid/java/systests/etc/config-systests-firewall.xml
+++ b/qpid/java/systests/etc/config-systests-firewall.xml
@@ -22,7 +22,7 @@
<configuration>
<system/>
<override>
- <xml fileName="${test.config}" optional="true"/>
+ <xml fileName="${QPID_HOME}/${test.config}" optional="true"/>
<xml fileName="${QPID_FIREWALL_CONFIG_SETTINGS}" optional="true"/>
<xml fileName="${QPID_HOME}/etc/config-systests-firewall-settings.xml"/>
<xml fileName="${QPID_HOME}/etc/config-systests-settings.xml"/>
diff --git a/qpid/java/systests/etc/config-systests.xml b/qpid/java/systests/etc/config-systests.xml
index 5d7d878e76..0e8f2803e3 100644
--- a/qpid/java/systests/etc/config-systests.xml
+++ b/qpid/java/systests/etc/config-systests.xml
@@ -22,7 +22,7 @@
<configuration>
<system/>
<override>
- <xml fileName="${test.config}" optional="true"/>
+ <xml fileName="${QPID_HOME}/${test.config}" optional="true"/>
<xml fileName="${QPID_HOME}/etc/config-systests-settings.xml"/>
<xml fileName="${QPID_HOME}/etc/config.xml"/>
</override>
diff --git a/qpid/java/systests/etc/virtualhosts-systests-aclv2.xml b/qpid/java/systests/etc/virtualhosts-systests-aclv2.xml
index eb96577487..db396d7ab1 100644
--- a/qpid/java/systests/etc/virtualhosts-systests-aclv2.xml
+++ b/qpid/java/systests/etc/virtualhosts-systests-aclv2.xml
@@ -22,7 +22,7 @@
<configuration>
<system/>
<override>
- <xml fileName="${test.virtualhosts}" optional="true"/>
+ <xml fileName="${QPID_HOME}/${test.virtualhosts}" optional="true"/>
<xml fileName="${QPID_HOME}/etc/virtualhosts-systests-aclv2-settings.xml"/>
<xml fileName="${QPID_HOME}/etc/virtualhosts.xml"/>
</override>
diff --git a/qpid/java/systests/etc/virtualhosts-systests-bdb-settings.xml b/qpid/java/systests/etc/virtualhosts-systests-bdb-settings.xml
new file mode 100644
index 0000000000..ce16523f13
--- /dev/null
+++ b/qpid/java/systests/etc/virtualhosts-systests-bdb-settings.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ -
+ - Licensed to the Apache Software Foundation (ASF) under one
+ - or more contributor license agreements. See the NOTICE file
+ - distributed with this work for additional information
+ - regarding copyright ownership. The ASF licenses this file
+ - to you under the Apache License, Version 2.0 (the
+ - "License"); you may not use this file except in compliance
+ - with the License. You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing,
+ - software distributed under the License is distributed on an
+ - "AS IS" BASIS, WITHOUT 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>
+ <work>${QPID_WORK}</work>
+
+ <virtualhost>
+ <name>localhost</name>
+ <localhost>
+ <store>
+ <class>org.apache.qpid.server.store.berkeleydb.BDBMessageStore</class>
+ <environment-path>${work}/bdbstore/localhost-store</environment-path>
+ </store>
+ </localhost>
+ </virtualhost>
+
+ <virtualhost>
+ <name>development</name>
+ <development>
+ <store>
+ <class>org.apache.qpid.server.store.berkeleydb.BDBMessageStore</class>
+ <environment-path>${work}/bdbstore/development-store</environment-path>
+ </store>
+ </development>
+ </virtualhost>
+
+ <virtualhost>
+ <name>test</name>
+ <test>
+ <store>
+ <class>org.apache.qpid.server.store.berkeleydb.BDBMessageStore</class>
+ <environment-path>${work}/bdbstore/test-store</environment-path>
+ </store>
+ </test>
+ </virtualhost>
+</virtualhosts>
+
+
diff --git a/qpid/java/systests/etc/virtualhosts-systests-bdb.xml b/qpid/java/systests/etc/virtualhosts-systests-bdb.xml
new file mode 100644
index 0000000000..367fee65ac
--- /dev/null
+++ b/qpid/java/systests/etc/virtualhosts-systests-bdb.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ -
+ - Licensed to the Apache Software Foundation (ASF) under one
+ - or more contributor license agreements. See the NOTICE file
+ - distributed with this work for additional information
+ - regarding copyright ownership. The ASF licenses this file
+ - to you under the Apache License, Version 2.0 (the
+ - "License"); you may not use this file except in compliance
+ - with the License. You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing,
+ - software distributed under the License is distributed on an
+ - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ - KIND, either express or implied. See the License for the
+ - specific language governing permissions and limitations
+ - under the License.
+ -
+ -->
+<configuration>
+ <system/>
+ <override>
+ <xml fileName="${QPID_HOME}/${test.virtualhosts}" optional="true"/>
+ <xml fileName="${QPID_HOME}/etc/virtualhosts-systests-bdb-settings.xml"/>
+ <xml fileName="${QPID_HOME}/etc/virtualhosts.xml"/>
+ </override>
+</configuration>
diff --git a/qpid/java/systests/etc/virtualhosts-systests-derby.xml b/qpid/java/systests/etc/virtualhosts-systests-derby.xml
index 171be37416..3745100e1f 100644
--- a/qpid/java/systests/etc/virtualhosts-systests-derby.xml
+++ b/qpid/java/systests/etc/virtualhosts-systests-derby.xml
@@ -22,7 +22,7 @@
<configuration>
<system/>
<override>
- <xml fileName="${test.virtualhosts}" optional="true"/>
+ <xml fileName="${QPID_HOME}/${test.virtualhosts}" optional="true"/>
<xml fileName="${QPID_HOME}/etc/virtualhosts-systests-derby-settings.xml"/>
<xml fileName="${QPID_HOME}/etc/virtualhosts.xml"/>
</override>
diff --git a/qpid/java/systests/etc/virtualhosts-systests-firewall.xml b/qpid/java/systests/etc/virtualhosts-systests-firewall.xml
index 51ab6739b3..c5c6a86d7c 100644
--- a/qpid/java/systests/etc/virtualhosts-systests-firewall.xml
+++ b/qpid/java/systests/etc/virtualhosts-systests-firewall.xml
@@ -22,7 +22,7 @@
<configuration>
<system/>
<override>
- <xml fileName="${test.virtualhosts}" optional="true"/>
+ <xml fileName="${QPID_HOME}/${test.virtualhosts}" optional="true"/>
<xml fileName="${QPID_FIREWALL_VIRTUALHOSTS_SETTINGS}" optional="true"/>
<xml fileName="${QPID_HOME}/etc/virtualhosts.xml"/>
</override>
diff --git a/qpid/java/systests/etc/virtualhosts-systests.xml b/qpid/java/systests/etc/virtualhosts-systests.xml
index 71f1cc9889..d6aeefac72 100644
--- a/qpid/java/systests/etc/virtualhosts-systests.xml
+++ b/qpid/java/systests/etc/virtualhosts-systests.xml
@@ -22,7 +22,7 @@
<configuration>
<system/>
<override>
- <xml fileName="${test.virtualhosts}" optional="true"/>
+ <xml fileName="${QPID_HOME}/${test.virtualhosts}" optional="true"/>
<xml fileName="${QPID_HOME}/etc/virtualhosts.xml"/>
</override>
</configuration>
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/client/failover/FailoverBehaviourTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/client/failover/FailoverBehaviourTest.java
new file mode 100644
index 0000000000..aa62106c46
--- /dev/null
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/client/failover/FailoverBehaviourTest.java
@@ -0,0 +1,924 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.client.failover;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.jms.Connection;
+import javax.jms.Destination;
+import javax.jms.ExceptionListener;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.jms.TransactionRolledBackException;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.jms.ConnectionListener;
+import org.apache.qpid.test.utils.FailoverBaseCase;
+
+/**
+ * Test suite to test all possible failover corner cases
+ */
+public class FailoverBehaviourTest extends FailoverBaseCase implements ConnectionListener, ExceptionListener
+{
+ private static final String TEST_MESSAGE_FORMAT = "test message {0}";
+
+ /** Indicates whether tests are run against clustered broker */
+ private static boolean CLUSTERED = Boolean.getBoolean("profile.clustered");
+
+ /** Default number of messages to send before failover */
+ private static final int DEFAULT_NUMBER_OF_MESSAGES = 10;
+
+ /** Actual number of messages to send before failover */
+ protected int _messageNumber = Integer.getInteger("profile.failoverMsgCount", DEFAULT_NUMBER_OF_MESSAGES);
+
+ /** Test connection */
+ protected Connection _connection;
+
+ /**
+ * Failover completion latch is used to wait till connectivity to broker is
+ * restored
+ */
+ private CountDownLatch _failoverComplete;
+
+ /**
+ * Consumer session
+ */
+ private Session _consumerSession;
+
+ /**
+ * Test destination
+ */
+ private Destination _destination;
+
+ /**
+ * Consumer
+ */
+ private MessageConsumer _consumer;
+
+ /**
+ * Producer session
+ */
+ private Session _producerSession;
+
+ /**
+ * Producer
+ */
+ private MessageProducer _producer;
+
+ /**
+ * Holds exception sent into {@link ExceptionListener} on failover
+ */
+ private JMSException _exceptionListenerException;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ _connection = getConnection();
+ _connection.setExceptionListener(this);
+ ((AMQConnection) _connection).setConnectionListener(this);
+ _failoverComplete = new CountDownLatch(1);
+ }
+
+ /**
+ * Test whether MessageProducer can successfully publish messages after
+ * failover and rollback transaction
+ */
+ public void testMessageProducingAndRollbackAfterFailover() throws Exception
+ {
+ init(Session.SESSION_TRANSACTED, true);
+ produceMessages();
+ causeFailure();
+
+ assertFailoverException();
+ // producer should be able to send messages after failover
+ _producer.send(_producerSession.createTextMessage("test message " + _messageNumber));
+
+ // rollback after failover
+ _producerSession.rollback();
+
+ // tests whether sending and committing is working after failover
+ produceMessages();
+ _producerSession.commit();
+
+ // tests whether receiving and committing is working after failover
+ consumeMessages();
+ _consumerSession.commit();
+ }
+
+ /**
+ * Test whether {@link TransactionRolledBackException} is thrown on commit
+ * of dirty transacted session after failover.
+ * <p>
+ * Verifies whether second after failover commit is successful.
+ */
+ public void testTransactionRolledBackExceptionThrownOnCommitAfterFailoverOnProducingMessages() throws Exception
+ {
+ init(Session.SESSION_TRANSACTED, true);
+ produceMessages();
+ causeFailure();
+
+ assertFailoverException();
+
+ // producer should be able to send messages after failover
+ _producer.send(_producerSession.createTextMessage("test message " + _messageNumber));
+
+ try
+ {
+ _producerSession.commit();
+ fail("TransactionRolledBackException is expected on commit after failover with dirty session!");
+ }
+ catch (JMSException t)
+ {
+ assertTrue("Expected TransactionRolledBackException but thrown " + t,
+ t instanceof TransactionRolledBackException);
+ }
+
+ // simulate process of user replaying the transaction
+ produceMessages("replayed test message {0}", _messageNumber, false);
+
+ // no exception should be thrown
+ _producerSession.commit();
+
+ // only messages sent after rollback should be received
+ consumeMessages("replayed test message {0}", _messageNumber);
+
+ // no exception should be thrown
+ _consumerSession.commit();
+ }
+
+ /**
+ * Tests JMSException is not thrown on commit with a clean session after
+ * failover
+ */
+ public void testNoJMSExceptionThrownOnCommitAfterFailoverWithCleanProducerSession() throws Exception
+ {
+ init(Session.SESSION_TRANSACTED, true);
+
+ causeFailure();
+
+ assertFailoverException();
+
+ // should not throw an exception for a clean session
+ _producerSession.commit();
+
+ // tests whether sending and committing is working after failover
+ produceMessages();
+ _producerSession.commit();
+
+ // tests whether receiving and committing is working after failover
+ consumeMessages();
+ _consumerSession.commit();
+ }
+
+ /**
+ * Tests {@link TransactionRolledBackException} is thrown on commit of dirty
+ * transacted session after failover.
+ * <p>
+ * Verifies whether second after failover commit is successful.
+ */
+ public void testTransactionRolledBackExceptionThrownOnCommitAfterFailoverOnMessageReceiving() throws Exception
+ {
+ init(Session.SESSION_TRANSACTED, true);
+ produceMessages();
+ _producerSession.commit();
+
+ // receive messages but do not commit
+ consumeMessages();
+
+ causeFailure();
+
+ assertFailoverException();
+
+ try
+ {
+ // should throw TransactionRolledBackException
+ _consumerSession.commit();
+ fail("TransactionRolledBackException is expected on commit after failover");
+ }
+ catch (Exception t)
+ {
+ assertTrue("Expected TransactionRolledBackException but thrown " + t,
+ t instanceof TransactionRolledBackException);
+ }
+
+ resendMessagesIfNecessary();
+
+ // consume messages successfully
+ consumeMessages();
+ _consumerSession.commit();
+ }
+
+ /**
+ * Tests JMSException is not thrown on commit with a clean session after failover
+ */
+ public void testNoJMSExceptionThrownOnCommitAfterFailoverWithCleanConsumerSession() throws Exception
+ {
+ init(Session.SESSION_TRANSACTED, true);
+ produceMessages();
+ _producerSession.commit();
+
+ consumeMessages();
+ _consumerSession.commit();
+
+ causeFailure();
+
+ assertFailoverException();
+
+ // should not throw an exception with a clean consumer session
+ _consumerSession.commit();
+ }
+
+ /**
+ * Test that TransactionRolledBackException is thrown on commit of
+ * dirty session in asynchronous consumer after failover.
+ */
+ public void testTransactionRolledBackExceptionThrownOnCommitAfterFailoverOnReceivingMessagesAsynchronously()
+ throws Exception
+ {
+ init(Session.SESSION_TRANSACTED, false);
+ FailoverTestMessageListener ml = new FailoverTestMessageListener();
+ _consumer.setMessageListener(ml);
+
+ _connection.start();
+
+ produceMessages();
+ _producerSession.commit();
+
+ // wait for message receiving
+ ml.awaitForEnd();
+
+ assertEquals("Received unexpected number of messages!", _messageNumber, ml.getMessageCounter());
+
+ // assert messages
+ int counter = 0;
+ for (Message message : ml.getReceivedMessages())
+ {
+ assertReceivedMessage(message, TEST_MESSAGE_FORMAT, counter++);
+ }
+ ml.reset();
+
+ causeFailure();
+ assertFailoverException();
+
+
+ try
+ {
+ _consumerSession.commit();
+ fail("TransactionRolledBackException should be thrown!");
+ }
+ catch (TransactionRolledBackException e)
+ {
+ // that is what is expected
+ }
+
+ resendMessagesIfNecessary();
+
+ // wait for message receiving
+ ml.awaitForEnd();
+
+ assertEquals("Received unexpected number of messages!", _messageNumber, ml.getMessageCounter());
+
+ // assert messages
+ counter = 0;
+ for (Message message : ml.getReceivedMessages())
+ {
+ assertReceivedMessage(message, TEST_MESSAGE_FORMAT, counter++);
+ }
+
+ // commit again. It should be successful
+ _consumerSession.commit();
+ }
+
+ /**
+ * Test that {@link Session#rollback()} does not throw exception after failover
+ * and that we are able to consume messages.
+ */
+ public void testRollbackAfterFailover() throws Exception
+ {
+ init(Session.SESSION_TRANSACTED, true);
+
+ produceMessages();
+ _producerSession.commit();
+
+ consumeMessages();
+
+ causeFailure();
+
+ assertFailoverException();
+
+ _consumerSession.rollback();
+
+ resendMessagesIfNecessary();
+
+ // tests whether receiving and committing is working after failover
+ consumeMessages();
+ _consumerSession.commit();
+ }
+
+ /**
+ * Test that {@link Session#rollback()} does not throw exception after receiving further messages
+ * after failover, and we can receive published messages after rollback.
+ */
+ public void testRollbackAfterReceivingAfterFailover() throws Exception
+ {
+ init(Session.SESSION_TRANSACTED, true);
+
+ produceMessages();
+ _producerSession.commit();
+
+ consumeMessages();
+ causeFailure();
+
+ assertFailoverException();
+
+ resendMessagesIfNecessary();
+
+ consumeMessages();
+
+ _consumerSession.rollback();
+
+ // tests whether receiving and committing is working after failover
+ consumeMessages();
+ _consumerSession.commit();
+ }
+
+ /**
+ * Test that {@link Session#recover()} does not throw an exception after failover
+ * and that we can consume messages after recover.
+ */
+ public void testRecoverAfterFailover() throws Exception
+ {
+ init(Session.CLIENT_ACKNOWLEDGE, true);
+
+ produceMessages();
+
+ // consume messages but do not acknowledge them
+ consumeMessages();
+
+ causeFailure();
+
+ assertFailoverException();
+
+ _consumerSession.recover();
+
+ resendMessagesIfNecessary();
+
+ // tests whether receiving and acknowledgment is working after recover
+ Message lastMessage = consumeMessages();
+ lastMessage.acknowledge();
+ }
+
+ /**
+ * Test that receiving more messages after failover and then calling
+ * {@link Session#recover()} does not throw an exception
+ * and that we can consume messages after recover.
+ */
+ public void testRecoverWithConsumedMessagesAfterFailover() throws Exception
+ {
+ init(Session.CLIENT_ACKNOWLEDGE, true);
+
+ produceMessages();
+
+ // consume messages but do not acknowledge them
+ consumeMessages();
+
+ causeFailure();
+
+ assertFailoverException();
+
+ // publishing should work after failover
+ resendMessagesIfNecessary();
+
+ // consume messages again on a dirty session
+ consumeMessages();
+
+ // recover should successfully restore session
+ _consumerSession.recover();
+
+ // tests whether receiving and acknowledgment is working after recover
+ Message lastMessage = consumeMessages();
+ lastMessage.acknowledge();
+ }
+
+ /**
+ * Test that first call to {@link Message#acknowledge()} after failover
+ * throws a JMSEXception if session is dirty.
+ */
+ public void testAcknowledgeAfterFailover() throws Exception
+ {
+ init(Session.CLIENT_ACKNOWLEDGE, true);
+
+ produceMessages();
+
+ // consume messages but do not acknowledge them
+ Message lastMessage = consumeMessages();
+ causeFailure();
+
+ assertFailoverException();
+
+ try
+ {
+ // an implicit recover performed when acknowledge throws an exception due to failover
+ lastMessage.acknowledge();
+ fail("JMSException should be thrown");
+ }
+ catch (JMSException t)
+ {
+ // TODO: assert error code and/or expected exception type
+ }
+
+ resendMessagesIfNecessary();
+
+ // tests whether receiving and acknowledgment is working after recover
+ lastMessage = consumeMessages();
+ lastMessage.acknowledge();
+ }
+
+ /**
+ * Test that receiving of messages after failover prior to calling
+ * {@link Message#acknowledge()} still results in acknowledge throwing an exception.
+ */
+ public void testAcknowledgeAfterMessageReceivingAfterFailover() throws Exception
+ {
+ init(Session.CLIENT_ACKNOWLEDGE, true);
+
+ produceMessages();
+
+ // consume messages but do not acknowledge them
+ consumeMessages();
+ causeFailure();
+
+ assertFailoverException();
+
+ resendMessagesIfNecessary();
+
+ // consume again on dirty session
+ Message lastMessage = consumeMessages();
+ try
+ {
+ // an implicit recover performed when acknowledge throws an exception due to failover
+ lastMessage.acknowledge();
+ fail("JMSException should be thrown");
+ }
+ catch (JMSException t)
+ {
+ // TODO: assert error code and/or expected exception type
+ }
+
+ // tests whether receiving and acknowledgment is working on a clean session
+ lastMessage = consumeMessages();
+ lastMessage.acknowledge();
+ }
+
+ /**
+ * Tests that call to {@link Message#acknowledge()} after failover throws an exception in asynchronous consumer
+ * and we can consume messages after acknowledge.
+ */
+ public void testAcknowledgeAfterFailoverForAsynchronousConsumer() throws Exception
+ {
+ init(Session.CLIENT_ACKNOWLEDGE, false);
+ FailoverTestMessageListener ml = new FailoverTestMessageListener();
+ _consumer.setMessageListener(ml);
+ _connection.start();
+
+ produceMessages();
+
+ // wait for message receiving
+ ml.awaitForEnd();
+
+ assertEquals("Received unexpected number of messages!", _messageNumber, ml.getMessageCounter());
+
+ // assert messages
+ int counter = 0;
+ Message currentMessage = null;
+ for (Message message : ml.getReceivedMessages())
+ {
+ assertReceivedMessage(message, TEST_MESSAGE_FORMAT, counter++);
+ currentMessage = message;
+ }
+ ml.reset();
+
+ causeFailure();
+ assertFailoverException();
+
+
+ try
+ {
+ currentMessage.acknowledge();
+ fail("JMSException should be thrown!");
+ }
+ catch (JMSException e)
+ {
+ // TODO: assert error code and/or expected exception type
+ }
+
+ resendMessagesIfNecessary();
+
+ // wait for message receiving
+ ml.awaitForEnd();
+
+ assertEquals("Received unexpected number of messages!", _messageNumber, ml.getMessageCounter());
+
+ // assert messages
+ counter = 0;
+ for (Message message : ml.getReceivedMessages())
+ {
+ assertReceivedMessage(message, TEST_MESSAGE_FORMAT, counter++);
+ currentMessage = message;
+ }
+
+ // acknowledge again. It should be successful
+ currentMessage.acknowledge();
+ }
+
+ /**
+ * Test whether {@link Session#recover()} works as expected after failover
+ * in AA mode.
+ */
+ public void testRecoverAfterFailoverInAutoAcknowledgeMode() throws Exception
+ {
+ init(Session.AUTO_ACKNOWLEDGE, true);
+
+ produceMessages();
+
+ // receive first message in order to start a dispatcher thread
+ Message receivedMessage = _consumer.receive(1000l);
+ assertReceivedMessage(receivedMessage, TEST_MESSAGE_FORMAT, 0);
+
+ causeFailure();
+
+ assertFailoverException();
+
+ _consumerSession.recover();
+
+ resendMessagesIfNecessary();
+
+ // tests whether receiving is working after recover
+ consumeMessages();
+ }
+
+ public void testClientAcknowledgedSessionCloseAfterFailover() throws Exception
+ {
+ sessionCloseAfterFailoverImpl(Session.CLIENT_ACKNOWLEDGE);
+ }
+
+ public void testTransactedSessionCloseAfterFailover() throws Exception
+ {
+ sessionCloseAfterFailoverImpl(Session.SESSION_TRANSACTED);
+ }
+
+ public void testAutoAcknowledgedSessionCloseAfterFailover() throws Exception
+ {
+ sessionCloseAfterFailoverImpl(Session.AUTO_ACKNOWLEDGE);
+ }
+
+ /**
+ * Tests {@link Session#close()} for session with given acknowledge mode
+ * to ensure that close works after failover.
+ *
+ * @param acknowledgeMode session acknowledge mode
+ * @throws JMSException
+ */
+ private void sessionCloseAfterFailoverImpl(int acknowledgeMode) throws JMSException
+ {
+ init(acknowledgeMode, true);
+ produceMessages(TEST_MESSAGE_FORMAT, _messageNumber, false);
+ if (acknowledgeMode == Session.SESSION_TRANSACTED)
+ {
+ _producerSession.commit();
+ }
+
+ // intentionally receive message but do not commit or acknowledge it in
+ // case of transacted or CLIENT_ACK session
+ Message receivedMessage = _consumer.receive(1000l);
+ assertReceivedMessage(receivedMessage, TEST_MESSAGE_FORMAT, 0);
+
+ causeFailure();
+
+ assertFailoverException();
+
+ // for transacted/client_ack session
+ // no exception should be thrown but transaction should be automatically
+ // rolled back
+ _consumerSession.close();
+ }
+
+ /**
+ * A helper method to instantiate produce and consumer sessions, producer
+ * and consumer.
+ *
+ * @param acknowledgeMode
+ * acknowledge mode
+ * @param startConnection
+ * indicates whether connection should be started
+ * @throws JMSException
+ */
+ private void init(int acknowledgeMode, boolean startConnection) throws JMSException
+ {
+ boolean isTransacted = acknowledgeMode == Session.SESSION_TRANSACTED ? true : false;
+
+ _consumerSession = _connection.createSession(isTransacted, acknowledgeMode);
+ _destination = _consumerSession.createQueue(getTestQueueName() + "_" + System.currentTimeMillis());
+ _consumer = _consumerSession.createConsumer(_destination);
+
+ if (startConnection)
+ {
+ _connection.start();
+ }
+
+ _producerSession = _connection.createSession(isTransacted, acknowledgeMode);
+ _producer = _producerSession.createProducer(_destination);
+
+ }
+
+ /**
+ * Resends messages if reconnected to a non-clustered broker
+ *
+ * @throws JMSException
+ */
+ private void resendMessagesIfNecessary() throws JMSException
+ {
+ if (!CLUSTERED)
+ {
+ // assert that a new broker does not have messages on a queue
+ if (_consumer.getMessageListener() == null)
+ {
+ Message message = _consumer.receive(100l);
+ assertNull("Received a message after failover with non-clustered broker!", message);
+ }
+ // re-sending messages if reconnected to a non-clustered broker
+ produceMessages(true);
+ }
+ }
+
+ /**
+ * Produces a default number of messages with default text content into test
+ * queue
+ *
+ * @throws JMSException
+ */
+ private void produceMessages() throws JMSException
+ {
+ produceMessages(false);
+ }
+
+ private void produceMessages(boolean seperateProducer) throws JMSException
+ {
+ produceMessages(TEST_MESSAGE_FORMAT, _messageNumber, seperateProducer);
+ }
+
+ /**
+ * Consumes a default number of messages and asserts their content.
+ *
+ * @return last consumed message
+ * @throws JMSException
+ */
+ private Message consumeMessages() throws JMSException
+ {
+ return consumeMessages(TEST_MESSAGE_FORMAT, _messageNumber);
+ }
+
+ /**
+ * Produces given number of text messages with content matching given
+ * content pattern
+ *
+ * @param messagePattern message content pattern
+ * @param messageNumber number of messages to send
+ * @param standaloneProducer whether to use the existing producer or a new one.
+ * @throws JMSException
+ */
+ private void produceMessages(String messagePattern, int messageNumber, boolean standaloneProducer) throws JMSException
+ {
+ Session producerSession;
+ MessageProducer producer;
+
+ if(standaloneProducer)
+ {
+ producerSession = _connection.createSession(true, Session.SESSION_TRANSACTED);
+ producer = producerSession.createProducer(_destination);
+ }
+ else
+ {
+ producerSession = _producerSession;
+ producer = _producer;
+ }
+
+ for (int i = 0; i < messageNumber; i++)
+ {
+ String text = MessageFormat.format(messagePattern, i);
+ Message message = producerSession.createTextMessage(text);
+ producer.send(message);
+ }
+
+ if(standaloneProducer)
+ {
+ producerSession.commit();
+ }
+ }
+
+ /**
+ * Consumes given number of text messages and asserts that their content
+ * matches given pattern
+ *
+ * @param messagePattern
+ * messages content pattern
+ * @param messageNumber
+ * message number to received
+ * @return last consumed message
+ * @throws JMSException
+ */
+ private Message consumeMessages(String messagePattern, int messageNumber) throws JMSException
+ {
+ Message receivedMesssage = null;
+ for (int i = 0; i < messageNumber; i++)
+ {
+ receivedMesssage = _consumer.receive(1000l);
+ assertReceivedMessage(receivedMesssage, messagePattern, i);
+ }
+ return receivedMesssage;
+ }
+
+ /**
+ * Asserts received message
+ *
+ * @param receivedMessage
+ * received message
+ * @param messagePattern
+ * messages content pattern
+ * @param messageIndex
+ * message index
+ */
+ private void assertReceivedMessage(Message receivedMessage, String messagePattern, int messageIndex)
+ {
+ assertNotNull("Expected message [" + messageIndex + "] is not received!", receivedMessage);
+ assertTrue("Failure to receive message [" + messageIndex + "], expected TextMessage but received "
+ + receivedMessage, receivedMessage instanceof TextMessage);
+ String expectedText = MessageFormat.format(messagePattern, messageIndex);
+ String receivedText = null;
+ try
+ {
+ receivedText = ((TextMessage) receivedMessage).getText();
+ }
+ catch (JMSException e)
+ {
+ fail("JMSException occured while getting message text:" + e.getMessage());
+ }
+ assertEquals("Failover is broken! Expected [" + expectedText + "] but got [" + receivedText + "]",
+ expectedText, receivedText);
+ }
+
+ /**
+ * Causes failover and waits till connection is re-established.
+ */
+ private void causeFailure()
+ {
+ causeFailure(getFailingPort(), DEFAULT_FAILOVER_TIME * 2);
+ }
+
+ /**
+ * Causes failover by stopping broker on given port and waits till
+ * connection is re-established during given time interval.
+ *
+ * @param port
+ * broker port
+ * @param delay
+ * time interval to wait for connection re-establishement
+ */
+ private void causeFailure(int port, long delay)
+ {
+ failBroker(port);
+
+ awaitForFailoverCompletion(delay);
+ }
+
+ private void awaitForFailoverCompletion(long delay)
+ {
+ _logger.info("Awaiting Failover completion..");
+ try
+ {
+ if (!_failoverComplete.await(delay, TimeUnit.MILLISECONDS))
+ {
+ fail("Failover did not complete");
+ }
+ }
+ catch (InterruptedException e)
+ {
+ fail("Test was interrupted:" + e.getMessage());
+ }
+ }
+
+ private void assertFailoverException()
+ {
+ // TODO: assert exception is received (once implemented)
+ // along with error code and/or expected exception type
+ }
+
+ @Override
+ public void bytesSent(long count)
+ {
+ }
+
+ @Override
+ public void bytesReceived(long count)
+ {
+ }
+
+ @Override
+ public boolean preFailover(boolean redirect)
+ {
+ return true;
+ }
+
+ @Override
+ public boolean preResubscribe()
+ {
+ return true;
+ }
+
+ @Override
+ public void failoverComplete()
+ {
+ _failoverComplete.countDown();
+ }
+
+ @Override
+ public void onException(JMSException e)
+ {
+ _exceptionListenerException = e;
+ }
+
+ private class FailoverTestMessageListener implements MessageListener
+ {
+ // message counter
+ private AtomicInteger _counter = new AtomicInteger();
+
+ private List<Message> _receivedMessage = new ArrayList<Message>();
+
+ private volatile CountDownLatch _endLatch;
+
+ public FailoverTestMessageListener() throws JMSException
+ {
+ _endLatch = new CountDownLatch(1);
+ }
+
+ @Override
+ public void onMessage(Message message)
+ {
+ _receivedMessage.add(message);
+ if (_counter.incrementAndGet() % _messageNumber == 0)
+ {
+ _endLatch.countDown();
+ }
+ }
+
+ public void reset()
+ {
+ _receivedMessage.clear();
+ _endLatch = new CountDownLatch(1);
+ _counter.set(0);
+ }
+
+ public List<Message> getReceivedMessages()
+ {
+ return _receivedMessage;
+ }
+
+ public Object awaitForEnd() throws InterruptedException
+ {
+ return _endLatch.await((long) _messageNumber, TimeUnit.SECONDS);
+ }
+
+ public int getMessageCounter()
+ {
+ return _counter.get();
+ }
+ }
+}
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/client/ssl/SSLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/client/ssl/SSLTest.java
index 471ebb16fc..d754979ab9 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/client/ssl/SSLTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/client/ssl/SSLTest.java
@@ -32,9 +32,9 @@ import org.apache.qpid.transport.Connection;
public class SSLTest extends QpidBrokerTestCase
{
- private static final String KEYSTORE = TEST_RESOURCES_DIR + "/ssl/java_client_keystore.jks";
+ private static final String KEYSTORE = "test-profiles/test_resources/ssl/java_client_keystore.jks";
private static final String KEYSTORE_PASSWORD = "password";
- private static final String TRUSTSTORE = TEST_RESOURCES_DIR + "/ssl/java_client_truststore.jks";
+ private static final String TRUSTSTORE = "test-profiles/test_resources/ssl/java_client_truststore.jks";
private static final String TRUSTSTORE_PASSWORD = "password";
private static final String CERT_ALIAS_APP1 = "app1";
private static final String CERT_ALIAS_APP2 = "app2";
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagementActorLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagementActorLoggingTest.java
index 2864d8e994..12a1682212 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagementActorLoggingTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagementActorLoggingTest.java
@@ -106,7 +106,7 @@ public class ManagementActorLoggingTest extends AbstractTestLogging
});
//Remove the connection close from any 0-10 connections
- _monitor.reset();
+ _monitor.markDiscardPoint();
// Get a managedConnection
ManagedConnection mangedConnection = _jmxUtils.getManagedObject(ManagedConnection.class, "org.apache.qpid:type=VirtualHost.Connection,*");
@@ -147,7 +147,7 @@ public class ManagementActorLoggingTest extends AbstractTestLogging
*/
public void testCreateExchangeDirectTransientViaManagementConsole() throws IOException, JMException
{
- _monitor.reset();
+ _monitor.markDiscardPoint();
_jmxUtils.createExchange("test", getName(), "direct", false);
@@ -171,7 +171,7 @@ public class ManagementActorLoggingTest extends AbstractTestLogging
public void testCreateExchangeTopicTransientViaManagementConsole() throws IOException, JMException
{
//Remove any previous exchange declares
- _monitor.reset();
+ _monitor.markDiscardPoint();
_jmxUtils.createExchange("test", getName(), "topic", false);
@@ -196,7 +196,7 @@ public class ManagementActorLoggingTest extends AbstractTestLogging
public void testCreateExchangeFanoutTransientViaManagementConsole() throws IOException, JMException
{
//Remove any previous exchange declares
- _monitor.reset();
+ _monitor.markDiscardPoint();
_jmxUtils.createExchange("test", getName(), "fanout", false);
@@ -221,7 +221,7 @@ public class ManagementActorLoggingTest extends AbstractTestLogging
public void testCreateExchangeHeadersTransientViaManagementConsole() throws IOException, JMException
{
//Remove any previous exchange declares
- _monitor.reset();
+ _monitor.markDiscardPoint();
_jmxUtils.createExchange("test", getName(), "headers", false);
@@ -265,7 +265,7 @@ public class ManagementActorLoggingTest extends AbstractTestLogging
public void testCreateQueueTransientViaManagementConsole() throws IOException, JMException
{
//Remove any previous queue declares
- _monitor.reset();
+ _monitor.markDiscardPoint();
_jmxUtils.createQueue("test", getName(), null, false);
@@ -308,7 +308,7 @@ public class ManagementActorLoggingTest extends AbstractTestLogging
public void testQueueDeleteViaManagementConsole() throws IOException, JMException
{
//Remove any previous queue declares
- _monitor.reset();
+ _monitor.markDiscardPoint();
_jmxUtils.createQueue("test", getName(), null, false);
@@ -354,7 +354,7 @@ public class ManagementActorLoggingTest extends AbstractTestLogging
public void testBindingCreateOnDirectViaManagementConsole() throws IOException, JMException
{
//Remove any previous queue declares
- _monitor.reset();
+ _monitor.markDiscardPoint();
_jmxUtils.createQueue("test", getName(), null, false);
@@ -381,7 +381,7 @@ public class ManagementActorLoggingTest extends AbstractTestLogging
public void testBindingCreateOnTopicViaManagementConsole() throws IOException, JMException
{
//Remove any previous queue declares
- _monitor.reset();
+ _monitor.markDiscardPoint();
_jmxUtils.createQueue("test", getName(), null, false);
@@ -408,7 +408,7 @@ public class ManagementActorLoggingTest extends AbstractTestLogging
public void testBindingCreateOnFanoutViaManagementConsole() throws IOException, JMException
{
//Remove any previous queue declares
- _monitor.reset();
+ _monitor.markDiscardPoint();
_jmxUtils.createQueue("test", getName(), null, false);
@@ -455,7 +455,7 @@ public class ManagementActorLoggingTest extends AbstractTestLogging
public void testUnRegisterExchangeViaManagementConsole() throws IOException, JMException
{
//Remove any previous queue declares
- _monitor.reset();
+ _monitor.markDiscardPoint();
_jmxUtils.createExchange("test", getName(), "direct", false);
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AbstractTestLogging.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AbstractTestLogging.java
index f56f428f0b..484c2afeb5 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AbstractTestLogging.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AbstractTestLogging.java
@@ -349,7 +349,7 @@ public class AbstractTestLogging extends QpidBrokerTestCase
public boolean waitForMessage(String message, long wait) throws FileNotFoundException, IOException
{
- return _monitor.waitForMessage(message, wait, true);
+ return _monitor.waitForMessage(message, wait);
}
/**
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AlertingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AlertingTest.java
index a2487b49bf..aef98b8a2a 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AlertingTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AlertingTest.java
@@ -136,7 +136,7 @@ public class AlertingTest extends AbstractTestLogging
stopBroker();
// Rest the monitoring clearing the current output file.
- _monitor.reset();
+ _monitor.markDiscardPoint();
startBroker();
wasAlertFired();
}
@@ -169,7 +169,7 @@ public class AlertingTest extends AbstractTestLogging
stopBroker();
- _monitor.reset();
+ _monitor.markDiscardPoint();
// Change max message count to 5, start broker and make sure that that's triggered at the right time
setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".queues.maximumMessageCount", "5");
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/BindingLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/BindingLoggingTest.java
index 97914f84a5..be2da128bc 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/BindingLoggingTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/BindingLoggingTest.java
@@ -55,7 +55,7 @@ public class BindingLoggingTest extends AbstractTestLogging
{
super.setUp();
//Ignore broker startup messages
- _monitor.reset();
+ _monitor.markDiscardPoint();
_connection = getConnection();
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/BrokerLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/BrokerLoggingTest.java
index e901903eb4..7969ffc059 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/BrokerLoggingTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/BrokerLoggingTest.java
@@ -246,7 +246,7 @@ public class BrokerLoggingTest extends AbstractTestLogging
if (isJavaBroker() && isExternalBroker())
{
// Get custom -l value used during testing for the broker startup
- String customLog4j = _brokerCommand.substring(_brokerCommand.indexOf("-l") + 2);
+ String customLog4j = _brokerCommand.substring(_brokerCommand.indexOf("-l") + 2).trim();
String TESTID = "BRK-1007";
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/DerbyMessageStoreLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/DerbyMessageStoreLoggingTest.java
index 16c529316a..d45bde2d98 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/DerbyMessageStoreLoggingTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/DerbyMessageStoreLoggingTest.java
@@ -496,7 +496,7 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest
stopBroker();
// Clear our monitor
- _monitor.reset();
+ _monitor.markDiscardPoint();
startBroker();
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/DurableQueueLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/DurableQueueLoggingTest.java
index 32adc49521..602bdb66b5 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/DurableQueueLoggingTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/DurableQueueLoggingTest.java
@@ -58,7 +58,7 @@ public class DurableQueueLoggingTest extends AbstractTestLogging
{
super.setUp();
//Ensure we only have logs from our test
- _monitor.reset();
+ _monitor.markDiscardPoint();
_connection = getConnection();
_session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ExchangeLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ExchangeLoggingTest.java
index b3c080b2e3..ec96f778f6 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ExchangeLoggingTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ExchangeLoggingTest.java
@@ -125,7 +125,7 @@ public class ExchangeLoggingTest extends AbstractTestLogging
public void testExchangeCreate() throws JMSException, IOException
{
//Ignore broker startup messages
- _monitor.reset();
+ _monitor.markDiscardPoint();
_session.createConsumer(_queue);
// Ensure we have received the EXH log msg.
@@ -179,7 +179,7 @@ public class ExchangeLoggingTest extends AbstractTestLogging
public void testExchangeDelete() throws Exception, IOException
{
//Ignore broker startup messages
- _monitor.reset();
+ _monitor.markDiscardPoint();
//create the exchange by creating a consumer
_session.createConsumer(_queue);
@@ -220,7 +220,7 @@ public class ExchangeLoggingTest extends AbstractTestLogging
public void testDiscardedMessage() throws Exception
{
//Ignore broker startup messages
- _monitor.reset();
+ _monitor.markDiscardPoint();
if (!isBroker010())
{
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/QueueLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/QueueLoggingTest.java
index b8a42c0ab3..76ebda0ebd 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/QueueLoggingTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/QueueLoggingTest.java
@@ -53,7 +53,7 @@ public class QueueLoggingTest extends AbstractTestLogging
{
super.setUp();
//Remove broker startup logging messages
- _monitor.reset();
+ _monitor.markDiscardPoint();
_connection = getConnection();
_session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/SubscriptionLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/SubscriptionLoggingTest.java
index 6e156f091e..b6efe53580 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/SubscriptionLoggingTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/SubscriptionLoggingTest.java
@@ -58,7 +58,7 @@ public class SubscriptionLoggingTest extends AbstractTestLogging
{
super.setUp();
//Remove broker startup logging messages
- _monitor.reset();
+ _monitor.markDiscardPoint();
_connection = getConnection();
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ProducerFlowControlTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ProducerFlowControlTest.java
index f78b327209..a724e6c66e 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ProducerFlowControlTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ProducerFlowControlTest.java
@@ -64,7 +64,7 @@ public class ProducerFlowControlTest extends AbstractTestLogging
_jmxUtilConnected=false;
super.setUp();
- _monitor.reset();
+ _monitor.markDiscardPoint();
producerConnection = getConnection();
producerSession = producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/AbstractACLTestCase.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/AbstractACLTestCase.java
index 8aa5b6d9de..32b0185f88 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/AbstractACLTestCase.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/AbstractACLTestCase.java
@@ -83,14 +83,8 @@ public abstract class AbstractACLTestCase extends QpidBrokerTestCase implements
@Override
public void setUp() throws Exception
{
- if (QpidHome == null)
- {
- fail("QPID_HOME not set");
- }
-
// Initialise ACLs.
- _configFile = new File(QpidHome, "etc" + File.separator + getConfig());
-
+ _configFile = new File("build" + File.separator + "etc" + File.separator + getConfig());
// Initialise ACL files
for (String virtualHost : getHostList())
{
@@ -156,7 +150,7 @@ public abstract class AbstractACLTestCase extends QpidBrokerTestCase implements
*/
public void setUpACLFile(String virtualHost) throws IOException, ConfigurationException
{
- String path = QpidHome + File.separator + "etc";
+ String path = "build" + File.separator + "etc";
String className = StringUtils.substringBeforeLast(getClass().getSimpleName().toLowerCase(), "test");
String testName = StringUtils.substringAfter(getName(), "test").toLowerCase();
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLTest.java
index 782ca22965..254e1fe6ac 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLTest.java
@@ -91,9 +91,9 @@ public class ExternalACLTest extends AbstractACLTestCase
//send a message to each queue (also causing an exchange declare)
MessageProducer sender = ((AMQSession<?, ?>) sess).createProducer(null);
((org.apache.qpid.jms.MessageProducer) sender).send(namedQueue, sess.createTextMessage("test"),
- DeliveryMode.NON_PERSISTENT, 0, 0L, false, false, true);
+ DeliveryMode.NON_PERSISTENT, 0, 0L, false, false);
((org.apache.qpid.jms.MessageProducer) sender).send(tempQueue, sess.createTextMessage("test"),
- DeliveryMode.NON_PERSISTENT, 0, 0L, false, false, true);
+ DeliveryMode.NON_PERSISTENT, 0, 0L, false, false);
//consume the messages from the queues
consumer.receive(2000);
@@ -309,7 +309,7 @@ public class ExternalACLTest extends AbstractACLTestCase
// before we finish this test. Message is set !immed !mand as the queue is invalid so want to test ACLs not
// queue existence.
((org.apache.qpid.jms.MessageProducer) sender).send(queue, sess.createTextMessage("test"),
- DeliveryMode.NON_PERSISTENT, 0, 0L, false, false, true);
+ DeliveryMode.NON_PERSISTENT, 0, 0L, false, false);
conn.close();
}
@@ -337,7 +337,7 @@ public class ExternalACLTest extends AbstractACLTestCase
// before we finish this test. Message is set !immed !mand as the queue is invalid so want to test ACLs not
// queue existence.
((org.apache.qpid.jms.MessageProducer) sender).send(queue, session.createTextMessage("test"),
- DeliveryMode.NON_PERSISTENT, 0, 0L, false, false, true);
+ DeliveryMode.NON_PERSISTENT, 0, 0L, false, false);
// Test the connection with a valid consumer
// This may fail as the session may be closed before the queue or the consumer created.
@@ -608,7 +608,7 @@ public class ExternalACLTest extends AbstractACLTestCase
// before we finish this test. Message is set !immed !mand as the queue is invalid so want to test ACLs not
// queue existence.
((org.apache.qpid.jms.MessageProducer) sender).send(queue, session.createTextMessage("test"),
- DeliveryMode.NON_PERSISTENT, 0, 0L, false, false, true);
+ DeliveryMode.NON_PERSISTENT, 0, 0L, false, false);
// Test the connection with a valid consumer
// This may not work as the session may be closed before the queue or consumer creation can occur.
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/firewall/FirewallConfigTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/firewall/FirewallConfigTest.java
index 2d99a44532..044a0af335 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/firewall/FirewallConfigTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/firewall/FirewallConfigTest.java
@@ -35,16 +35,8 @@ public class FirewallConfigTest extends QpidBrokerTestCase
@Override
protected void setUp() throws Exception
{
- // do setup
- final String QPID_HOME = System.getProperty("QPID_HOME");
-
- if (QPID_HOME == null)
- {
- fail("QPID_HOME not set");
- }
-
// Setup initial config file.
- _configFile = new File(QPID_HOME, "etc/config-systests-firewall.xml");
+ _configFile = new File("build/etc/config-systests-firewall.xml");
// Setup temporary config file
_tmpConfig = File.createTempFile("config-systests-firewall", ".xml");
@@ -86,7 +78,7 @@ public class FirewallConfigTest extends QpidBrokerTestCase
public void testVhostAllowBrokerDeny() throws Exception
{
- _configFile = new File(System.getProperty("QPID_HOME"), "etc/config-systests-firewall-2.xml");
+ _configFile = new File("build/etc/config-systests-firewall-2.xml");
super.setUp();
@@ -119,7 +111,7 @@ public class FirewallConfigTest extends QpidBrokerTestCase
public void testVhostDenyBrokerAllow() throws Exception
{
- _configFile = new File(System.getProperty("QPID_HOME"), "etc/config-systests-firewall-3.xml");
+ _configFile = new File("build/etc/config-systests-firewall-3.xml");
super.setUp();
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/destination/AddressBasedDestinationTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/destination/AddressBasedDestinationTest.java
index b5bb74327e..8c3c247e2b 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/destination/AddressBasedDestinationTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/destination/AddressBasedDestinationTest.java
@@ -1116,10 +1116,10 @@ public class AddressBasedDestinationTest extends QpidBrokerTestCase
public void testDestinationOnSend() throws Exception
{
Session ssn = _connection.createSession(false,Session.CLIENT_ACKNOWLEDGE);
- MessageConsumer cons = ssn.createConsumer(ssn.createTopic("amq.topic/test"));
+ MessageConsumer cons = ssn.createConsumer(ssn.createTopic("ADDR:amq.topic/test"));
MessageProducer prod = ssn.createProducer(null);
- Queue queue = ssn.createQueue("amq.topic/test");
+ Queue queue = ssn.createQueue("ADDR:amq.topic/test");
prod.send(queue,ssn.createTextMessage("A"));
Message msg = cons.receive(1000);
@@ -1147,7 +1147,7 @@ public class AddressBasedDestinationTest extends QpidBrokerTestCase
Destination replyToDest = AMQDestination.createDestination(replyTo);
MessageConsumer replyToCons = session.createConsumer(replyToDest);
- Destination dest = session.createQueue("amq.direct/test");
+ Destination dest = session.createQueue("ADDR:amq.direct/test");
MessageConsumer cons = session.createConsumer(dest);
MessageProducer prod = session.createProducer(dest);
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/timeouts/SyncWaitTimeoutDelayTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/timeouts/SyncWaitTimeoutDelayTest.java
index 1a23eee8ab..6189c37306 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/timeouts/SyncWaitTimeoutDelayTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/timeouts/SyncWaitTimeoutDelayTest.java
@@ -63,7 +63,7 @@ public class SyncWaitTimeoutDelayTest extends SyncWaitDelayTest
catch (JMSException e)
{
assertTrue("Wrong exception type received.", e.getLinkedException() instanceof AMQTimeoutException);
- assertTrue("Wrong message received on exception.", e.getMessage().startsWith("Failed to commit"));
+ assertTrue("Wrong message received on exception.", e.getMessage().startsWith("Exception during commit"));
// As we are using Nano time ensure to multiply up the millis.
assertTrue("Timeout was more than 30s default", (System.nanoTime() - start) < (1000000L * 1000 * 30));
}
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/ClientAcknowledgeTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/ClientAcknowledgeTest.java
new file mode 100644
index 0000000000..06be5cf456
--- /dev/null
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/ClientAcknowledgeTest.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.qpid.test.unit.ack;
+
+import javax.jms.Connection;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+public class ClientAcknowledgeTest extends QpidBrokerTestCase
+{
+ private static final long ONE_DAY_MS = 1000l * 60 * 60 * 24;
+ private Connection _connection;
+ private Queue _queue;
+ private Session _consumerSession;
+ private MessageConsumer _consumer;
+ private MessageProducer _producer;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ _queue = getTestQueue();
+ _connection = getConnection();
+ }
+
+ /**
+ * Test that message.acknowledge actually acknowledges, regardless of
+ * the flusher thread period, by restarting the broker after calling
+ * acknowledge, and then verifying after restart that the message acked
+ * is no longer present. This test requires a persistent store.
+ */
+ public void testClientAckWithLargeFlusherPeriod() throws Exception
+ {
+ setTestClientSystemProperty("qpid.session.max_ack_delay", Long.toString(ONE_DAY_MS));
+ _consumerSession = _connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ _consumer = _consumerSession.createConsumer(_queue);
+ _connection.start();
+
+ _producer = _consumerSession.createProducer(_queue);
+ _producer.send(createNextMessage(_consumerSession, 1));
+ _producer.send(createNextMessage(_consumerSession, 2));
+
+ Message message = _consumer.receive(1000l);
+ assertNotNull("Message has not been received", message);
+ assertEquals("Unexpected message is received", 1, message.getIntProperty(INDEX));
+ message.acknowledge();
+
+ //restart broker to allow verification of the acks
+ //without explicitly closing connection (which acks)
+ restartBroker();
+
+ // try to receive the message again, which should fail (as it was ackd)
+ _connection = getConnection();
+ _connection.start();
+ _consumerSession = _connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ _consumer = _consumerSession.createConsumer(_queue);
+ message = _consumer.receive(1000l);
+ assertNotNull("Message has not been received", message);
+ assertEquals("Unexpected message is received", 2, message.getIntProperty(INDEX));
+ }
+}
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/RecoverTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/RecoverTest.java
index 5e7ba5482d..66ca1d8345 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/RecoverTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/RecoverTest.java
@@ -46,7 +46,7 @@ public class RecoverTest extends FailoverBaseCase
{
static final Logger _logger = LoggerFactory.getLogger(RecoverTest.class);
- private Exception _error;
+ private volatile Exception _error;
private AtomicInteger count;
protected AMQConnection _connection;
@@ -249,14 +249,13 @@ public class RecoverTest extends FailoverBaseCase
{
if (!message.getJMSRedelivered())
{
- setError(
- new Exception("Message not marked as redelivered on what should be second delivery attempt"));
+ setError(new Exception("Message not marked as redelivered on what should be second delivery attempt"));
}
}
else
{
- System.err.println(message);
- fail("Message delivered too many times!: " + count);
+ _logger.error(message.toString());
+ setError(new Exception("Message delivered too many times!: " + count));
}
}
catch (JMSException e)
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/basic/close/CloseTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/basic/close/CloseTest.java
index c6b8069300..3c7962d873 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/basic/close/CloseTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/basic/close/CloseTest.java
@@ -21,14 +21,13 @@
package org.apache.qpid.test.unit.basic.close;
import org.apache.qpid.test.utils.QpidBrokerTestCase;
import org.apache.qpid.client.AMQConnection;
-import org.apache.qpid.client.AMQQueue;
-import org.apache.qpid.url.AMQBindingURL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
+import javax.jms.Queue;
import javax.jms.Session;
public class CloseTest extends QpidBrokerTestCase
@@ -41,7 +40,7 @@ public class CloseTest extends QpidBrokerTestCase
Session session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
- AMQQueue queue = new AMQQueue(new AMQBindingURL("test-queue"));
+ Queue queue = session.createQueue("test-queue");
MessageConsumer consumer = session.createConsumer(queue);
MessageProducer producer_not_used_but_created_for_testing = session.createProducer(queue);
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/AMQConnectionTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/AMQConnectionTest.java
index e79fe69199..53a7533869 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/AMQConnectionTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/AMQConnectionTest.java
@@ -20,16 +20,6 @@
*/
package org.apache.qpid.test.unit.client;
-import java.io.BufferedWriter;
-import java.io.InputStreamReader;
-import java.io.LineNumberReader;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import javax.jms.Connection;
-import javax.jms.ExceptionListener;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/AMQSSLConnectionTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/AMQSSLConnectionTest.java
index 53a433c543..5f3daa407a 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/AMQSSLConnectionTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/AMQSSLConnectionTest.java
@@ -26,9 +26,9 @@ import org.apache.qpid.client.AMQConnectionURL;
public class AMQSSLConnectionTest extends AMQConnectionTest
{
- private static final String KEYSTORE = TEST_RESOURCES_DIR + "/ssl/java_client_keystore.jks";
+ private static final String KEYSTORE = "test-profiles/test_resources/ssl/java_client_keystore.jks";
private static final String KEYSTORE_PASSWORD = "password";
- private static final String TRUSTSTORE = TEST_RESOURCES_DIR + "/ssl/java_client_truststore.jks";
+ private static final String TRUSTSTORE = "test-profiles/test_resources/ssl/java_client_truststore.jks";
private static final String TRUSTSTORE_PASSWORD = "password";
@Override
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/message/ObjectMessageTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/message/ObjectMessageTest.java
index fd28b86762..8ad8fa77d7 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/message/ObjectMessageTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/message/ObjectMessageTest.java
@@ -20,14 +20,10 @@
*/
package org.apache.qpid.test.unit.client.message;
-import org.apache.qpid.client.AMQConnection;
-import org.apache.qpid.client.AMQDestination;
-import org.apache.qpid.client.AMQQueue;
-import org.apache.qpid.client.AMQSession;
-import org.apache.qpid.test.utils.QpidBrokerTestCase;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
import javax.jms.JMSException;
import javax.jms.Message;
@@ -35,10 +31,13 @@ import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQDestination;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class ObjectMessageTest extends QpidBrokerTestCase implements MessageListener
{
@@ -67,7 +66,7 @@ public class ObjectMessageTest extends QpidBrokerTestCase implements MessageList
connection.start();
// create a publisher
- producer = session.createProducer(destination, false, false, true);
+ producer = session.createProducer(destination, false, false);
A a1 = new A(1, "A");
A a2 = new A(2, "a");
B b = new B(1, "B");
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/publish/DirtyTransactedPublishTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/publish/DirtyTransactedPublishTest.java
deleted file mode 100644
index 3ec7937812..0000000000
--- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/publish/DirtyTransactedPublishTest.java
+++ /dev/null
@@ -1,403 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.test.unit.publish;
-
-import org.apache.qpid.client.AMQConnection;
-import org.apache.qpid.client.AMQDestination;
-import org.apache.qpid.client.AMQSession;
-import org.apache.qpid.jms.ConnectionListener;
-import org.apache.qpid.test.utils.FailoverBaseCase;
-
-import javax.jms.Connection;
-import javax.jms.JMSException;
-import javax.jms.Message;
-import javax.jms.MessageConsumer;
-import javax.jms.MessageListener;
-import javax.jms.MessageProducer;
-import javax.jms.Queue;
-import javax.jms.Session;
-import javax.jms.TransactionRolledBackException;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * QPID-1816 : Whilst testing Acknoledgement after failover this completes testing
- * of the client after failover. When we have a dirty session we should receive
- * an error if we attempt to publish. This test ensures that both in the synchronous
- * and asynchronous message delivery paths we receive the expected exceptions at
- * the expected time.
- */
-public class DirtyTransactedPublishTest extends FailoverBaseCase implements ConnectionListener
-{
- protected CountDownLatch _failoverCompleted = new CountDownLatch(1);
-
- protected int NUM_MESSAGES;
- protected Connection _connection;
- protected Queue _queue;
- protected Session _consumerSession;
- protected MessageConsumer _consumer;
- protected MessageProducer _producer;
-
- private static final String MSG = "MSG";
- private static final String SEND_FROM_ON_MESSAGE_TEXT = "sendFromOnMessage";
- protected CountDownLatch _receviedAll;
- protected AtomicReference<Exception> _causeOfFailure = new AtomicReference<Exception>(null);
-
- @Override
- protected void setUp() throws Exception
- {
- super.setUp();
- NUM_MESSAGES = 10;
-
- _queue = getTestQueue();
-
- //Create Producer put some messages on the queue
- _connection = getConnection();
- }
-
- /**
- * Initialise the test variables
- * @param transacted is this a transacted test
- * @param mode if not trasacted then what ack mode to use
- * @throws Exception if there is a setup issue.
- */
- protected void init(boolean transacted, int mode) throws Exception
- {
- _consumerSession = _connection.createSession(transacted, mode);
- _consumer = _consumerSession.createConsumer(_queue);
- _producer = _consumerSession.createProducer(_queue);
-
- // These should all end up being prefetched by session
- sendMessage(_consumerSession, _queue, 1);
-
- assertEquals("Wrong number of messages on queue", 1,
- ((AMQSession) _consumerSession).getQueueDepth((AMQDestination) _queue));
- }
-
- /**
- * If a transacted session has failed over whilst it has uncommitted sent
- * data then we need to throw a TransactedRolledbackException on commit()
- *
- * The alternative would be to maintain a replay buffer so that the message
- * could be resent. This is not currently implemented
- *
- * @throws Exception if something goes wrong.
- */
- public void testDirtySendingSynchronousTransacted() throws Exception
- {
- Session producerSession = _connection.createSession(true, Session.SESSION_TRANSACTED);
-
- // Ensure we get failover notifications
- ((AMQConnection) _connection).setConnectionListener(this);
-
- MessageProducer producer = producerSession.createProducer(_queue);
-
- // Create and send message 0
- Message msg = producerSession.createMessage();
- msg.setIntProperty(INDEX, 0);
- producer.send(msg);
-
- // DON'T commit message .. fail connection
-
- failBroker(getFailingPort());
-
- // Ensure destination exists for sending
- producerSession.createConsumer(_queue).close();
-
- // Send the next message
- msg.setIntProperty(INDEX, 1);
- try
- {
- producer.send(msg);
- fail("Should fail with Qpid as we provide early warning of the dirty session via a JMSException.");
- }
- catch (JMSException jmse)
- {
- assertEquals("Early warning of dirty session not correct",
- "Failover has occurred and session is dirty so unable to send.", jmse.getMessage());
- }
-
- // Ignore that the session is dirty and attempt to commit to validate the
- // exception is thrown. AND that the above failure notification did NOT
- // clean up the session.
-
- try
- {
- producerSession.commit();
- fail("Session is dirty we should get an TransactionRolledBackException");
- }
- catch (TransactionRolledBackException trbe)
- {
- // Normal path.
- }
-
- // Resending of messages should now work ok as the commit was forcilbly rolledback
- msg.setIntProperty(INDEX, 0);
- producer.send(msg);
- msg.setIntProperty(INDEX, 1);
- producer.send(msg);
-
- producerSession.commit();
-
- assertEquals("Wrong number of messages on queue", 2,
- ((AMQSession) producerSession).getQueueDepth((AMQDestination) _queue));
- }
-
- /**
- * If a transacted session has failed over whilst it has uncommitted sent
- * data then we need to throw a TransactedRolledbackException on commit()
- *
- * The alternative would be to maintain a replay buffer so that the message
- * could be resent. This is not currently implemented
- *
- * @throws Exception if something goes wrong.
- */
- public void testDirtySendingOnMessageTransacted() throws Exception
- {
- NUM_MESSAGES = 1;
- _receviedAll = new CountDownLatch(NUM_MESSAGES);
- ((AMQConnection) _connection).setConnectionListener(this);
-
- init(true, Session.SESSION_TRANSACTED);
-
- _consumer.setMessageListener(new MessageListener()
- {
-
- public void onMessage(Message message)
- {
- try
- {
- // Create and send message 0
- Message msg = _consumerSession.createMessage();
- msg.setIntProperty(INDEX, 0);
- _producer.send(msg);
-
- // DON'T commit message .. fail connection
-
- failBroker(getFailingPort());
-
- // rep
- repopulateBroker();
-
- // Destination will exist as this failBroker will populate
- // the queue with 1 message
-
- // Send the next message
- msg.setIntProperty(INDEX, 1);
- try
- {
- _producer.send(msg);
- fail("Should fail with Qpid as we provide early warning of the dirty session via a JMSException.");
- }
- catch (JMSException jmse)
- {
- assertEquals("Early warning of dirty session not correct",
- "Failover has occurred and session is dirty so unable to send.", jmse.getMessage());
- }
-
- // Ignore that the session is dirty and attempt to commit to validate the
- // exception is thrown. AND that the above failure notification did NOT
- // clean up the session.
-
- try
- {
- _consumerSession.commit();
- fail("Session is dirty we should get an TransactionRolledBackException");
- }
- catch (TransactionRolledBackException trbe)
- {
- // Normal path.
- }
-
- // Resend messages
- msg.setIntProperty(INDEX, 0);
- msg.setStringProperty(MSG, SEND_FROM_ON_MESSAGE_TEXT);
- _producer.send(msg);
- msg.setIntProperty(INDEX, 1);
- msg.setStringProperty(MSG, SEND_FROM_ON_MESSAGE_TEXT);
- _producer.send(msg);
-
- _consumerSession.commit();
-
- // Stop this consumer .. can't do _consumer.stop == DEADLOCK
- // this doesn't seem to stop dispatcher running
- _connection.stop();
-
- // Signal that the onMessage send part of test is complete
- // main thread can validate that messages are correct
- _receviedAll.countDown();
-
- }
- catch (Exception e)
- {
- fail(e);
- }
-
- }
-
- });
-
- _connection.start();
-
- if (!_receviedAll.await(10000L, TimeUnit.MILLISECONDS))
- {
- // Check to see if we ended due to an exception in the onMessage handler
- Exception cause = _causeOfFailure.get();
- if (cause != null)
- {
- cause.printStackTrace();
- fail(cause.getMessage());
- }
- else
- {
- fail("All messages not received:" + _receviedAll.getCount() + "/" + NUM_MESSAGES);
- }
- }
-
- // Check to see if we ended due to an exception in the onMessage handler
- Exception cause = _causeOfFailure.get();
- if (cause != null)
- {
- cause.printStackTrace();
- fail(cause.getMessage());
- }
-
- _consumer.close();
- _consumerSession.close();
-
- _consumerSession = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
-
- _connection.start();
-
- // Validate that we could send the messages as expected.
- assertEquals("Wrong number of messages on queue", 3,
- ((AMQSession) _consumerSession).getQueueDepth((AMQDestination) _queue));
-
- MessageConsumer consumer = _consumerSession.createConsumer(_queue);
-
- //Validate the message sent to setup the failed over broker.
- Message message = consumer.receive(1000);
- assertNotNull("Message " + 0 + " not received.", message);
- assertEquals("Incorrect message received", 0, message.getIntProperty(INDEX));
-
- // Validate the two messages sent from within the onMessage
- for (int index = 0; index <= 1; index++)
- {
- message = consumer.receive(1000);
- assertNotNull("Message " + index + " not received.", message);
- assertEquals("Incorrect message received", index, message.getIntProperty(INDEX));
- assertEquals("Incorrect message text for message:" + index, SEND_FROM_ON_MESSAGE_TEXT, message.getStringProperty(MSG));
- }
-
- assertNull("Extra message received.", consumer.receiveNoWait());
-
- _consumerSession.close();
-
- assertEquals("Wrong number of messages on queue", 0,
- ((AMQSession) getConnection().createSession(false, Session.AUTO_ACKNOWLEDGE)).getQueueDepth((AMQDestination) _queue));
- }
-
- private void repopulateBroker() throws Exception
- {
- // Repopulate this new broker so we can test what happends after failover
-
- //Get the connection to the first (main port) broker.
- Connection connection = getConnection();
- // Use a transaction to send messages so we can be sure they arrive.
- Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
- // ensure destination is created.
- session.createConsumer(_queue).close();
-
- sendMessage(session, _queue, NUM_MESSAGES);
-
- assertEquals("Wrong number of messages on queue", NUM_MESSAGES,
- ((AMQSession) session).getQueueDepth((AMQDestination) _queue));
-
- connection.close();
- }
-
- // AMQConnectionListener Interface.. used so we can validate that we
- // actually failed over.
-
- public void bytesSent(long count)
- {
- }
-
- public void bytesReceived(long count)
- {
- }
-
- public boolean preFailover(boolean redirect)
- {
- //Allow failover
- return true;
- }
-
- public boolean preResubscribe()
- {
- //Allow failover
- return true;
- }
-
- public void failoverComplete()
- {
- _failoverCompleted.countDown();
- }
-
- /**
- * Override so we can block until failover has completd
- *
- * @param port int the port of the broker to fail.
- */
- @Override
- public void failBroker(int port)
- {
- super.failBroker(port);
-
- try
- {
- if (!_failoverCompleted.await(DEFAULT_FAILOVER_TIME, TimeUnit.MILLISECONDS))
- {
- fail("Failover did not occur in specified time:" + DEFAULT_FAILOVER_TIME);
- }
- }
- catch (InterruptedException e)
- {
- fail("Failover was interuppted");
- }
- }
-
- /**
- * Pass the given exception back to the waiting thread to fail the test run.
- *
- * @param e The exception that is causing the test to fail.
- */
- protected void fail(Exception e)
- {
- _causeOfFailure.set(e);
- // End the test.
- while (_receviedAll.getCount() != 0)
- {
- _receviedAll.countDown();
- }
- }
-}
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutConfigurationTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutConfigurationTest.java
deleted file mode 100644
index 46e5d214f5..0000000000
--- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutConfigurationTest.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.test.unit.transacted;
-
-/**
- * This verifies that changing the {@code transactionTimeout} configuration will alter
- * the behaviour of the transaction open and idle logging, and that when the connection
- * will be closed.
- */
-public class TransactionTimeoutConfigurationTest extends TransactionTimeoutTestCase
-{
- @Override
- protected void configure() throws Exception
- {
- // Setup housekeeping every second
- setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".housekeeping.checkPeriod", "100");
-
- // Set transaction timout properties.
- setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".transactionTimeout.openWarn", "200");
- setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".transactionTimeout.openClose", "1000");
- setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".transactionTimeout.idleWarn", "100");
- setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".transactionTimeout.idleClose", "500");
- }
-
- public void testProducerIdleCommit() throws Exception
- {
- try
- {
- send(5, 0);
-
- sleep(2.0f);
-
- _psession.commit();
- fail("should fail");
- }
- catch (Exception e)
- {
- _exception = e;
- }
-
- monitor(5, 0);
-
- check(IDLE);
- }
-
- public void testProducerOpenCommit() throws Exception
- {
- try
- {
- send(5, 0.3f);
-
- _psession.commit();
- fail("should fail");
- }
- catch (Exception e)
- {
- _exception = e;
- }
-
- monitor(6, 3);
-
- check(OPEN);
- }
-}
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutDisabledTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutDisabledTest.java
index db508143f9..fd8beffbe6 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutDisabledTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutDisabledTest.java
@@ -30,6 +30,8 @@ public class TransactionTimeoutDisabledTest extends TransactionTimeoutTestCase
{
// Setup housekeeping every second
setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".housekeeping.checkPeriod", "100");
+
+ // No transaction timeout configuration.
}
public void testProducerIdleCommit() throws Exception
@@ -47,7 +49,7 @@ public class TransactionTimeoutDisabledTest extends TransactionTimeoutTestCase
fail("Should have succeeded");
}
- assertTrue("Listener should not have received exception", _caught.getCount() == 1);
+ assertEquals("Listener should not have received exception", 0, getNumberOfDeliveredExceptions());
monitor(0, 0);
}
@@ -65,7 +67,7 @@ public class TransactionTimeoutDisabledTest extends TransactionTimeoutTestCase
fail("Should have succeeded");
}
- assertTrue("Listener should not have received exception", _caught.getCount() == 1);
+ assertEquals("Listener should not have received exception", 0, getNumberOfDeliveredExceptions());
monitor(0, 0);
}
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutTest.java
index c912d6a323..f554b0089e 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutTest.java
@@ -30,152 +30,125 @@ package org.apache.qpid.test.unit.transacted;
*/
public class TransactionTimeoutTest extends TransactionTimeoutTestCase
{
- public void testProducerIdle() throws Exception
+
+ protected void configure() throws Exception
{
- try
+ // Setup housekeeping every second
+ setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".housekeeping.checkPeriod", "100");
+
+ if (getName().contains("ProducerIdle"))
{
- sleep(2.0f);
-
- _psession.commit();
+ setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".transactionTimeout.openWarn", "0");
+ setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".transactionTimeout.openClose", "0");
+ setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".transactionTimeout.idleWarn", "500");
+ setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".transactionTimeout.idleClose", "1500");
}
- catch (Exception e)
+ else if (getName().contains("ProducerOpen"))
{
- fail("Should have succeeded");
+ setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".transactionTimeout.openWarn", "1000");
+ setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".transactionTimeout.openClose", "2000");
+ setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".transactionTimeout.idleWarn", "0");
+ setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".transactionTimeout.idleClose", "0");
}
-
- assertTrue("Listener should not have received exception", _caught.getCount() == 1);
-
- monitor(0, 0);
- }
-
- public void testProducerIdleCommit() throws Exception
- {
- try
+ else
{
- send(5, 0);
-
- sleep(2.0f);
-
- _psession.commit();
- fail("should fail");
+ setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".transactionTimeout.openWarn", "1000");
+ setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".transactionTimeout.openClose", "2000");
+ setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".transactionTimeout.idleWarn", "500");
+ setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".transactionTimeout.idleClose", "1000");
}
- catch (Exception e)
- {
- _exception = e;
- }
-
- monitor(5, 0);
-
- check(IDLE);
}
-
- public void testProducerOpenCommit() throws Exception
+
+ public void testProducerIdle() throws Exception
{
- try
- {
- send(6, 0.5f);
-
- _psession.commit();
- fail("should fail");
- }
- catch (Exception e)
- {
- _exception = e;
- }
-
- monitor(0, 10);
-
- check(OPEN);
+ sleep(2.0f);
+
+ _psession.commit();
+
+ assertEquals("Listener should not have received exception", 0, getNumberOfDeliveredExceptions());
+
+ monitor(0, 0);
}
-
- public void testProducerIdleCommitTwice() throws Exception
+
+ public void testProducerIdleCommit() throws Exception
{
+ send(5, 0);
+ // Idle for more than idleClose to generate idle-warns and cause a close.
+ sleep(2.0f);
+
try
{
- send(5, 0);
-
- sleep(1.0f);
-
_psession.commit();
-
- send(5, 0);
-
- sleep(2.0f);
-
- _psession.commit();
- fail("should fail");
+ fail("Exception not thrown");
}
catch (Exception e)
{
_exception = e;
}
-
+
monitor(10, 0);
-
+
check(IDLE);
}
-
- public void testProducerOpenCommitTwice() throws Exception
+
+ public void testProducerIdleCommitTwice() throws Exception
{
+ send(5, 0);
+ // Idle for less than idleClose to generate idle-warns
+ sleep(1.0f);
+
+ _psession.commit();
+
+ send(5, 0);
+ // Now idle for more than idleClose to generate more idle-warns and cause a close.
+ sleep(2.0f);
+
try
{
- send(5, 0);
-
- sleep(1.0f);
-
_psession.commit();
-
- send(6, 0.5f);
-
- _psession.commit();
- fail("should fail");
+ fail("Exception not thrown");
}
catch (Exception e)
{
_exception = e;
}
-
- // the presistent store generates more idle messages?
- monitor(isBrokerStorePersistent() ? 10 : 5, 10);
-
- check(OPEN);
+
+ monitor(15, 0);
+
+ check(IDLE);
}
-
+
public void testProducerIdleRollback() throws Exception
{
+ send(5, 0);
+ // Now idle for more than idleClose to generate more idle-warns and cause a close.
+ sleep(2.0f);
try
{
- send(5, 0);
-
- sleep(2.0f);
-
_psession.rollback();
- fail("should fail");
+ fail("Exception not thrown");
}
catch (Exception e)
{
_exception = e;
}
-
- monitor(5, 0);
-
+
+ monitor(10, 0);
+
check(IDLE);
}
-
+
public void testProducerIdleRollbackTwice() throws Exception
{
+ send(5, 0);
+ // Idle for less than idleClose to generate idle-warns
+ sleep(1.0f);
+ _psession.rollback();
+ send(5, 0);
+ // Now idle for more than idleClose to generate more idle-warns and cause a close.
+ sleep(2.0f);
try
{
- send(5, 0);
-
- sleep(1.0f);
-
- _psession.rollback();
-
- send(5, 0);
-
- sleep(2.0f);
-
_psession.rollback();
fail("should fail");
}
@@ -183,153 +156,153 @@ public class TransactionTimeoutTest extends TransactionTimeoutTestCase
{
_exception = e;
}
-
- monitor(10, 0);
+
+ monitor(15, 0);
check(IDLE);
}
-
- public void testConsumerCommitClose() throws Exception
+
+ public void testProducerOpenCommit() throws Exception
{
try
{
- send(1, 0);
-
+ // Sleep between sends to cause open warns and then cause a close.
+ send(6, 0.5f);
_psession.commit();
-
- expect(1, 0);
-
- _csession.commit();
-
- sleep(3.0f);
-
- _csession.close();
+ fail("Exception not thrown");
}
catch (Exception e)
{
- fail("should have succeeded: " + e.getMessage());
+ _exception = e;
}
-
- assertTrue("Listener should not have received exception", _caught.getCount() == 1);
-
- monitor(0, 0);
+
+ monitor(0, 10);
+
+ check(OPEN);
}
- public void testConsumerIdleReceiveCommit() throws Exception
+ public void testProducerOpenCommitTwice() throws Exception
{
+ send(5, 0);
+ sleep(1.0f);
+ _psession.commit();
+
try
{
- send(1, 0);
-
+ // Now sleep between sends to cause open warns and then cause a close.
+ send(6, 0.5f);
_psession.commit();
-
- sleep(2.0f);
-
- expect(1, 0);
-
- sleep(2.0f);
-
- _csession.commit();
+ fail("Exception not thrown");
}
catch (Exception e)
{
- fail("Should have succeeded");
+ _exception = e;
}
+
+ monitor(0, 10);
- assertTrue("Listener should not have received exception", _caught.getCount() == 1);
-
+ check(OPEN);
+ }
+
+ public void testConsumerCommitClose() throws Exception
+ {
+ send(1, 0);
+
+ _psession.commit();
+
+ expect(1, 0);
+
+ _csession.commit();
+
+ sleep(3.0f);
+
+ _csession.close();
+
+ assertEquals("Listener should not have received exception", 0, getNumberOfDeliveredExceptions());
+
monitor(0, 0);
}
+ public void testConsumerIdleReceiveCommit() throws Exception
+ {
+ send(1, 0);
+
+ _psession.commit();
+
+ sleep(2.0f);
+
+ expect(1, 0);
+
+ sleep(2.0f);
+
+ _csession.commit();
+
+ assertEquals("Listener should not have received exception", 0, getNumberOfDeliveredExceptions());
+
+ monitor(0, 0);
+ }
+
public void testConsumerIdleCommit() throws Exception
{
- try
- {
- send(1, 0);
-
- _psession.commit();
-
- expect(1, 0);
-
- sleep(2.0f);
-
- _csession.commit();
- }
- catch (Exception e)
- {
- fail("Should have succeeded");
- }
-
- assertTrue("Listener should not have received exception", _caught.getCount() == 1);
-
+ send(1, 0);
+
+ _psession.commit();
+
+ expect(1, 0);
+
+ sleep(2.0f);
+
+ _csession.commit();
+
+ assertEquals("Listener should not have received exception", 0, getNumberOfDeliveredExceptions());
+
monitor(0, 0);
}
public void testConsumerIdleRollback() throws Exception
{
- try
- {
- send(1, 0);
-
- _psession.commit();
-
- expect(1, 0);
-
- sleep(2.0f);
-
- _csession.rollback();
- }
- catch (Exception e)
- {
- fail("Should have succeeded");
- }
-
- assertTrue("Listener should not have received exception", _caught.getCount() == 1);
-
+ send(1, 0);
+
+ _psession.commit();
+
+ expect(1, 0);
+
+ sleep(2.0f);
+
+ _csession.rollback();
+
+ assertEquals("Listener should not have received exception", 0, getNumberOfDeliveredExceptions());
+
monitor(0, 0);
}
-
+
public void testConsumerOpenCommit() throws Exception
{
- try
- {
- send(1, 0);
-
- _psession.commit();
-
- sleep(3.0f);
-
- _csession.commit();
- }
- catch (Exception e)
- {
- fail("Should have succeeded");
- }
-
- assertTrue("Listener should not have received exception", _caught.getCount() == 1);
-
+ send(1, 0);
+
+ _psession.commit();
+
+ sleep(3.0f);
+
+ _csession.commit();
+
+ assertEquals("Listener should not have received exception", 0, getNumberOfDeliveredExceptions());
+
monitor(0, 0);
}
public void testConsumerOpenRollback() throws Exception
{
- try
- {
- send(1, 0);
-
- _psession.commit();
-
- sleep(3.0f);
-
- _csession.rollback();
- }
- catch (Exception e)
- {
- fail("Should have succeeded");
- }
-
- assertTrue("Listener should not have received exception", _caught.getCount() == 1);
+ send(1, 0);
+ _psession.commit();
+
+ sleep(3.0f);
+
+ _csession.rollback();
+
+ assertEquals("Listener should not have received exception", 0, getNumberOfDeliveredExceptions());
+
monitor(0, 0);
}
}
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutTestCase.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutTestCase.java
index ef2de5c592..2b90d38049 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutTestCase.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutTestCase.java
@@ -23,6 +23,7 @@ package org.apache.qpid.test.unit.transacted;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
import javax.jms.DeliveryMode;
import javax.jms.ExceptionListener;
@@ -49,7 +50,7 @@ import org.apache.qpid.util.LogMonitor;
/**
* The {@link TestCase} for transaction timeout testing.
*/
-public class TransactionTimeoutTestCase extends QpidBrokerTestCase implements ExceptionListener
+public abstract class TransactionTimeoutTestCase extends QpidBrokerTestCase implements ExceptionListener
{
public static final String VIRTUALHOST = "test";
public static final String TEXT = "0123456789abcdefghiforgettherest";
@@ -64,31 +65,16 @@ public class TransactionTimeoutTestCase extends QpidBrokerTestCase implements Ex
protected Queue _queue;
protected MessageConsumer _consumer;
protected MessageProducer _producer;
- protected CountDownLatch _caught = new CountDownLatch(1);
+ private CountDownLatch _exceptionLatch = new CountDownLatch(1);
+ protected AtomicInteger _exceptionCount = new AtomicInteger(0);
protected String _message;
protected Exception _exception;
protected AMQConstant _code;
-
- protected void configure() throws Exception
- {
- // Setup housekeeping every second
- setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".housekeeping.checkPeriod", "100");
-
- /*
- * Set transaction timout properties. The XML in the virtualhosts configuration is as follows:
- *
- * <transactionTimeout>
- * <openWarn>1000</openWarn>
- * <openClose>2000</openClose>
- * <idleWarn>500</idleWarn>
- * <idleClose>1500</idleClose>
- * </transactionTimeout>
- */
- setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".transactionTimeout.openWarn", "1000");
- setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".transactionTimeout.openClose", "2000");
- setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".transactionTimeout.idleWarn", "500");
- setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".transactionTimeout.idleClose", "1000");
- }
+
+ /**
+ * Subclasses must implement this to configure transaction timeout parameters.
+ */
+ protected abstract void configure() throws Exception;
protected void setUp() throws Exception
{
@@ -233,7 +219,7 @@ public class TransactionTimeoutTestCase extends QpidBrokerTestCase implements Ex
*/
protected void check(String reason)throws InterruptedException
{
- assertTrue("Should have caught exception in listener", _caught.await(1, TimeUnit.SECONDS));
+ assertTrue("Should have caught exception in listener", _exceptionLatch.await(1, TimeUnit.SECONDS));
assertNotNull("Should have thrown exception to client", _exception);
assertTrue("Exception message should contain '" + reason + "': " + _message, _message.contains(reason + " transaction timed out"));
assertNotNull("Exception should have an error code", _code);
@@ -243,11 +229,18 @@ public class TransactionTimeoutTestCase extends QpidBrokerTestCase implements Ex
/** @see javax.jms.ExceptionListener#onException(javax.jms.JMSException) */
public void onException(JMSException jmse)
{
- _caught.countDown();
+ _exceptionLatch.countDown();
+ _exceptionCount.incrementAndGet();
+
_message = jmse.getLinkedException().getMessage();
if (jmse.getLinkedException() instanceof AMQException)
{
_code = ((AMQException) jmse.getLinkedException()).getErrorCode();
}
}
+
+ protected int getNumberOfDeliveredExceptions()
+ {
+ return _exceptionCount.get();
+ }
}
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java
index 40df024c67..bb44aea659 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java
@@ -52,7 +52,6 @@ import javax.naming.NamingException;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.XMLConfiguration;
import org.apache.commons.lang.StringUtils;
-import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.qpid.AMQException;
import org.apache.qpid.client.AMQConnectionFactory;
@@ -115,7 +114,6 @@ public class QpidBrokerTestCase extends QpidTestCase
private static final String BROKER_LANGUAGE = "broker.language";
private static final String BROKER_TYPE = "broker.type";
private static final String BROKER_COMMAND = "broker.command";
- private static final String BROKER_CLEAN = "broker.clean";
private static final String BROKER_CLEAN_BETWEEN_TESTS = "broker.clean.between.tests";
private static final String BROKER_EXISTING_QPID_WORK = "broker.existing.qpid.work";
private static final String BROKER_VERSION = "broker.version";
@@ -143,10 +141,9 @@ public class QpidBrokerTestCase extends QpidTestCase
protected String _brokerLanguage = System.getProperty(BROKER_LANGUAGE, JAVA);
protected BrokerType _brokerType = BrokerType.valueOf(System.getProperty(BROKER_TYPE, "").toUpperCase());
protected String _brokerCommand = System.getProperty(BROKER_COMMAND);
- private String _brokerClean = System.getProperty(BROKER_CLEAN, null);
private Boolean _brokerCleanBetweenTests = Boolean.getBoolean(BROKER_CLEAN_BETWEEN_TESTS);
private final AmqpProtocolVersion _brokerVersion = AmqpProtocolVersion.valueOf(System.getProperty(BROKER_VERSION, ""));
- protected String _output = System.getProperty(TEST_OUTPUT);
+ protected String _output = System.getProperty(TEST_OUTPUT, System.getProperty("java.io.tmpdir"));
protected Boolean _brokerPersistent = Boolean.getBoolean(BROKER_PERSITENT);
private String _brokerProtocolExcludes = System.getProperty(BROKER_PROTOCOL_EXCLUDES);
@@ -217,8 +214,13 @@ public class QpidBrokerTestCase extends QpidTestCase
if (redirected)
{
_outputFile = new File(String.format("%s/TEST-%s.out", _output, qname));
- out = new PrintStream(_outputFile);
+ out = new PrintStream(new FileOutputStream(_outputFile), true);
err = new PrintStream(String.format("%s/TEST-%s.err", _output, qname));
+
+ // This is relying on behaviour specific to log4j 1.2.12. If we were to upgrade to 1.2.13 or
+ // beyond we must change either code (or config) to ensure that ConsoleAppender#setFollow
+ // is set to true otherwise log4j logging will not respect the following reassignment.
+
System.setOut(out);
System.setErr(err);
@@ -259,14 +261,9 @@ public class QpidBrokerTestCase extends QpidTestCase
if(_brokerCleanBetweenTests)
{
- try
- {
- cleanBroker();
- }
- catch (Exception e)
- {
- _logger.error("exception cleaning up broker", e);
- }
+ final String qpidWork = System.getProperty("QPID_WORK");
+ cleanBrokerWork(qpidWork);
+ createBrokerWork(qpidWork);
}
_logger.info("========== stop " + getTestName() + " ==========");
@@ -298,11 +295,11 @@ public class QpidBrokerTestCase extends QpidTestCase
String existingQpidWorkPath = System.getProperty(BROKER_EXISTING_QPID_WORK);
if(existingQpidWorkPath != null && !existingQpidWorkPath.equals(""))
{
- cleanBroker();
+ String qpidWork = getQpidWork(_brokerType, getPort());
File existing = new File(existingQpidWorkPath);
- File qpidWork = new File(getQpidWork(_brokerType, getPort()));
- FileUtils.copyRecursive(existing, qpidWork);
+ cleanBrokerWork(qpidWork);
+ FileUtils.copyRecursive(existing, new File(qpidWork));
}
startBroker();
@@ -494,25 +491,22 @@ public class QpidBrokerTestCase extends QpidTestCase
}
else if (!_brokerType.equals(BrokerType.EXTERNAL))
{
+ // Add the port to QPID_WORK to ensure unique working dirs for multi broker tests
+ final String qpidWork = getQpidWork(_brokerType, port);
String cmd = getBrokerCommand(port);
_logger.info("starting external broker: " + cmd);
ProcessBuilder pb = new ProcessBuilder(cmd.split("\\s+"));
pb.redirectErrorStream(true);
-
Map<String, String> env = pb.environment();
-
String qpidHome = System.getProperty(QPID_HOME);
env.put(QPID_HOME, qpidHome);
-
//Augment Path with bin directory in QPID_HOME.
env.put("PATH", env.get("PATH").concat(File.pathSeparator + qpidHome + "/bin"));
//Add the test name to the broker run.
// DON'T change PNAME, qpid.stop needs this value.
env.put("QPID_PNAME", "-DPNAME=QPBRKR -DTNAME=\"" + getTestName() + "\"");
- // Add the port to QPID_WORK to ensure unique working dirs for multi broker tests
- env.put("QPID_WORK", getQpidWork(_brokerType, port));
-
+ env.put("QPID_WORK", qpidWork);
// Use the environment variable to set amqj.logging.level for the broker
// The value used is a 'server' value in the test configuration to
@@ -563,6 +557,10 @@ public class QpidBrokerTestCase extends QpidTestCase
env.put("QPID_OPTS", QPID_OPTS);
}
}
+
+ // cpp broker requires that the work directory is created
+ createBrokerWork(qpidWork);
+
Process process = pb.start();;
Piper p = new Piper(process.getInputStream(),
@@ -577,7 +575,7 @@ public class QpidBrokerTestCase extends QpidTestCase
_logger.info("broker failed to become ready (" + p.ready + "):" + p.getStopLine());
//Ensure broker has stopped
process.destroy();
- cleanBroker();
+ cleanBrokerWork(qpidWork);
throw new RuntimeException("broker failed to become ready:"
+ p.getStopLine());
}
@@ -587,7 +585,7 @@ public class QpidBrokerTestCase extends QpidTestCase
//test that the broker is still running and hasn't exited unexpectedly
int exit = process.exitValue();
_logger.info("broker aborted: " + exit);
- cleanBroker();
+ cleanBrokerWork(qpidWork);
throw new RuntimeException("broker aborted: " + exit);
}
catch (IllegalThreadStateException e)
@@ -655,21 +653,28 @@ public class QpidBrokerTestCase extends QpidTestCase
public String getTestConfigFile()
{
- String path = _output == null ? System.getProperty("java.io.tmpdir") : _output;
- return path + "/" + getTestQueueName() + "-config.xml";
+ return _output + "/" + getTestQueueName() + "-config.xml";
}
public String getTestVirtualhostsFile()
{
- String path = _output == null ? System.getProperty("java.io.tmpdir") : _output;
- return path + "/" + getTestQueueName() + "-virtualhosts.xml";
+ return _output + "/" + getTestQueueName() + "-virtualhosts.xml";
+ }
+
+ private String relativeToQpidHome(String file)
+ {
+ return file.replace(System.getProperty(QPID_HOME,"QPID_HOME") + "/","");
}
protected void saveTestConfiguration() throws ConfigurationException
{
- // Specifiy the test config file
+ // Specify the test config file
String testConfig = getTestConfigFile();
- setSystemProperty("test.config", testConfig);
+ String relative = relativeToQpidHome(testConfig);
+
+ setSystemProperty("test.config", relative);
+ _logger.info("Set test.config property to: " + relative);
+ _logger.info("Saving test virtualhosts file at: " + testConfig);
// Create the file if configuration does not exist
if (_testConfiguration.isEmpty())
@@ -681,9 +686,13 @@ public class QpidBrokerTestCase extends QpidTestCase
protected void saveTestVirtualhosts() throws ConfigurationException
{
- // Specifiy the test virtualhosts file
+ // Specify the test virtualhosts file
String testVirtualhosts = getTestVirtualhostsFile();
- setSystemProperty("test.virtualhosts", testVirtualhosts);
+ String relative = relativeToQpidHome(testVirtualhosts);
+
+ setSystemProperty("test.virtualhosts", relative);
+ _logger.info("Set test.virtualhosts property to: " + relative);
+ _logger.info("Saving test virtualhosts file at: " + testVirtualhosts);
// Create the file if configuration does not exist
if (_testVirtualhosts.isEmpty())
@@ -693,30 +702,33 @@ public class QpidBrokerTestCase extends QpidTestCase
_testVirtualhosts.save(testVirtualhosts);
}
- public void cleanBroker()
+ protected void cleanBrokerWork(final String qpidWork)
{
- if (_brokerClean != null)
+ if (qpidWork != null)
{
- _logger.info("clean: " + _brokerClean);
+ _logger.info("Cleaning broker work dir: " + qpidWork);
- try
+ File file = new File(qpidWork);
+ if (file.exists())
{
- ProcessBuilder pb = new ProcessBuilder(_brokerClean.split("\\s+"));
- pb.redirectErrorStream(true);
- Process clean = pb.start();
- new Piper(clean.getInputStream(),_brokerOutputStream).start();
-
- clean.waitFor();
-
- _logger.info("clean exited: " + clean.exitValue());
- }
- catch (IOException e)
- {
- throw new RuntimeException(e);
+ final boolean success = FileUtils.delete(file, true);
+ if(!success)
+ {
+ throw new RuntimeException("Failed to recursively delete beneath : " + file);
+ }
}
- catch (InterruptedException e)
+ }
+ }
+
+ protected void createBrokerWork(final String qpidWork)
+ {
+ if (qpidWork != null)
+ {
+ final File dir = new File(qpidWork);
+ dir.mkdirs();
+ if (!dir.isDirectory())
{
- throw new RuntimeException(e);
+ throw new RuntimeException("Failed to created Qpid work directory : " + qpidWork);
}
}
}
@@ -730,7 +742,7 @@ public class QpidBrokerTestCase extends QpidTestCase
{
port = getPort(port);
- _logger.info("stopping broker: " + getBrokerCommand(port));
+ _logger.info("stopping broker on port : " + port);
BrokerHolder broker = _brokers.remove(port);
broker.shutdown();
}
@@ -906,7 +918,7 @@ public class QpidBrokerTestCase extends QpidTestCase
}
/**
- * Add an environtmen variable for the external broker environment
+ * Add an environment variable for the external broker environment
*
* @param property the property to set
* @param value the value to set it to
@@ -990,9 +1002,9 @@ public class QpidBrokerTestCase extends QpidTestCase
* Get the default connection factory for the currently used broker
* Default factory is "local"
*
- * @return A conection factory
+ * @return A connection factory
*
- * @throws Exception if there is an error getting the tactory
+ * @throws Exception if there is an error getting the factory
*/
public AMQConnectionFactory getConnectionFactory() throws NamingException
{
@@ -1016,7 +1028,7 @@ public class QpidBrokerTestCase extends QpidTestCase
*
* @param factoryName The factory name
*
- * @return A conection factory
+ * @return A connection factory
*
* @throws Exception if there is an error getting the tactory
*/
@@ -1054,7 +1066,7 @@ public class QpidBrokerTestCase extends QpidTestCase
{
_logger.info("get connection");
Connection con = getConnectionFactory().createConnection(username, password);
- //add the connection in the lis of connections
+ //add the connection in the list of connections
_connections.add(con);
return con;
}
@@ -1063,7 +1075,7 @@ public class QpidBrokerTestCase extends QpidTestCase
{
_logger.info("get Connection");
Connection con = getConnectionFactory().createConnection(username, password, id);
- //add the connection in the lis of connections
+ //add the connection in the list of connections
_connections.add(con);
return con;
}
@@ -1154,7 +1166,7 @@ public class QpidBrokerTestCase extends QpidTestCase
/**
* Send messages to the given destination.
*
- * If session is transacted then messages will be commited before returning
+ * If session is transacted then messages will be committed before returning
*
* @param session the session to use for sending
* @param destination where to send them to
@@ -1162,7 +1174,7 @@ public class QpidBrokerTestCase extends QpidTestCase
*
* @param batchSize the batchSize in which to commit, 0 means no batching,
* but a single commit at the end
- * @return the sent messgse
+ * @return the sent message
*
* @throws Exception
*/
@@ -1175,7 +1187,7 @@ public class QpidBrokerTestCase extends QpidTestCase
/**
* Send messages to the given destination.
*
- * If session is transacted then messages will be commited before returning
+ * If session is transacted then messages will be committed before returning
*
* @param session the session to use for sending
* @param destination where to send them to
@@ -1184,7 +1196,7 @@ public class QpidBrokerTestCase extends QpidTestCase
* @param offset offset allows the INDEX value of the message to be adjusted.
* @param batchSize the batchSize in which to commit, 0 means no batching,
* but a single commit at the end
- * @return the sent messgse
+ * @return the sent message
*
* @throws Exception
*/
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitor.java b/qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitor.java
index a5e2b80f64..c09e63308c 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitor.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitor.java
@@ -27,11 +27,9 @@ import org.apache.log4j.SimpleLayout;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.util.ArrayList;
+import java.io.LineNumberReader;
import java.util.List;
import java.util.LinkedList;
@@ -45,10 +43,12 @@ import java.util.LinkedList;
public class LogMonitor
{
// The file that the log statements will be written to.
- private File _logfile;
+ private final File _logfile;
// The appender we added to the get messages
- private FileAppender _appender;
+ private final FileAppender _appender;
+
+ private int _linesToSkip = 0;
/**
* Create a new LogMonitor that creates a new Log4j Appender and monitors
@@ -78,6 +78,7 @@ public class LogMonitor
if (file != null && file.exists())
{
_logfile = file;
+ _appender = null;
}
else
{
@@ -99,13 +100,13 @@ public class LogMonitor
* @param wait the time in ms to wait for the message to occur
* @return true if the message was found
*
- * @throws java.io.FileNotFoundException if the Log file can nolonger be found
+ * @throws java.io.FileNotFoundException if the Log file can no longer be found
* @throws IOException thrown when reading the log file
*/
public List<String> waitAndFindMatches(String message, long wait)
throws FileNotFoundException, IOException
{
- if (waitForMessage(message, wait, true))
+ if (waitForMessage(message, wait))
{
return findMatches(message);
}
@@ -116,7 +117,9 @@ public class LogMonitor
}
/**
- * Checks the log for instances of the search string.
+ * Checks the log for instances of the search string. If the caller
+ * has previously called {@link #markDiscardPoint()}, lines up until the discard
+ * point are not considered.
*
* The pattern parameter can take any valid argument used in String.contains()
*
@@ -130,66 +133,99 @@ public class LogMonitor
*/
public List<String> findMatches(String pattern) throws IOException
{
- return FileUtils.searchFile(_logfile, pattern);
+
+ List<String> results = new LinkedList<String>();
+
+ LineNumberReader reader = new LineNumberReader(new FileReader(_logfile));
+ try
+ {
+ while (reader.ready())
+ {
+ String line = reader.readLine();
+ if (reader.getLineNumber() > _linesToSkip && line.contains(pattern))
+ {
+ results.add(line);
+ }
+ }
+ }
+ finally
+ {
+ reader.close();
+ }
+
+ return results;
}
/**
- * Checks the log file for a given message to appear.
+ * Checks the log file for a given message to appear. If the caller
+ * has previously called {@link #markDiscardPoint()}, lines up until the discard
+ * point are not considered.
*
* @param message the message to wait for in the log
* @param wait the time in ms to wait for the message to occur
- *
- * @param printFileOnFailure should we print the contents that have been
- * read if we fail ot find the message.
* @return true if the message was found
*
- * @throws java.io.FileNotFoundException if the Log file can nolonger be found
+ * @throws java.io.FileNotFoundException if the Log file can no longer be found
* @throws IOException thrown when reading the log file
*/
- public boolean waitForMessage(String message, long wait, boolean printFileOnFailure)
+ public boolean waitForMessage(String message, long wait)
throws FileNotFoundException, IOException
{
// Loop through alerts until we're done or wait ms seconds have passed,
// just in case the logfile takes a while to flush.
- BufferedReader reader = new BufferedReader(new FileReader(_logfile));
- boolean found = false;
- long endtime = System.currentTimeMillis() + wait;
- ArrayList<String> contents = new ArrayList<String>();
- while (!found && System.currentTimeMillis() < endtime)
+ LineNumberReader reader = null;
+ try
{
- while (reader.ready())
+ reader = new LineNumberReader(new FileReader(_logfile));
+
+ boolean found = false;
+ long endtime = System.currentTimeMillis() + wait;
+ while (!found && System.currentTimeMillis() < endtime)
{
- String line = reader.readLine();
- contents.add(line);
- if (line.contains(message))
+ boolean ready = true;
+ while (ready = reader.ready())
{
- found = true;
+ String line = reader.readLine();
+
+ if (reader.getLineNumber() > _linesToSkip)
+ {
+ if (line.contains(message))
+ {
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!ready)
+ {
+ try
+ {
+ Thread.sleep(50);
+ }
+ catch (InterruptedException ie)
+ {
+ Thread.currentThread().interrupt();
+ }
}
}
+ return found;
+
}
- if (!found && printFileOnFailure)
+ finally
{
- for (String line : contents)
+ if (reader != null)
{
- System.out.println(line);
+ reader.close();
}
}
- return found;
}
-
- public boolean waitForMessage(String message, long alertLogWaitPeriod) throws FileNotFoundException, IOException
- {
- return waitForMessage(message, alertLogWaitPeriod, true);
- }
-
-
/**
* Read the log file in to memory as a String
*
* @return the current contents of the log file
*
- * @throws java.io.FileNotFoundException if the Log file can nolonger be found
+ * @throws java.io.FileNotFoundException if the Log file can no longer be found
* @throws IOException thrown when reading the log file
*/
public String readFile() throws FileNotFoundException, IOException
@@ -208,14 +244,37 @@ public class LogMonitor
}
/**
- * Clears the log file and writes: 'Log Monitor Reset' at the start of the file
+ * Marks the discard point in the log file.
*
- * @throws java.io.FileNotFoundException if the Log file can nolonger be found
+ * @throws java.io.FileNotFoundException if the Log file can no longer be found
* @throws IOException thrown if there is a problem with the log file
*/
- public void reset() throws FileNotFoundException, IOException
+ public void markDiscardPoint() throws FileNotFoundException, IOException
+ {
+ _linesToSkip = countLinesInFile();
+ }
+
+ private int countLinesInFile() throws IOException
{
- new FileOutputStream(_logfile).getChannel().truncate(0);
+ int lineCount = 0;
+ BufferedReader br = null;
+ try
+ {
+ br = new BufferedReader(new FileReader(_logfile));
+ while(br.readLine() != null)
+ {
+ lineCount++;
+ }
+
+ return lineCount;
+ }
+ finally
+ {
+ if (br != null)
+ {
+ br.close();
+ }
+ }
}
/**
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitorTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitorTest.java
index a99abe4b94..89f707fbef 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitorTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitorTest.java
@@ -155,7 +155,7 @@ public class LogMonitorTest extends TestCase
String notLogged = "This text was not logged";
- validateLogDoesNotContainsMessage(_monitor, notLogged);
+ validateLogDoesNotContainMessage(_monitor, notLogged);
}
public void testWaitForMessage_Timeout() throws IOException
@@ -168,28 +168,27 @@ public class LogMonitorTest extends TestCase
// Verify that we can time out waiting for a message
assertFalse("Message was logged ",
- _monitor.waitForMessage(message, TIME_OUT / 2, false));
+ _monitor.waitForMessage(message, TIME_OUT / 2));
// Verify that the message did eventually get logged.
assertTrue("Message was never logged.",
_monitor.waitForMessage(message, TIME_OUT));
}
- public void testReset() throws IOException
+ public void testDiscardPoint() throws IOException
{
- String message = getName() + ": Test Message";
-
- Logger.getRootLogger().warn(message);
-
- validateLogContainsMessage(_monitor, message);
+ String firstMessage = getName() + ": Test Message1";
+ Logger.getRootLogger().warn(firstMessage);
- String LOG_RESET_TEXT = "Log Monitor Reset";
+ validateLogContainsMessage(_monitor, firstMessage);
- validateLogDoesNotContainsMessage(_monitor, LOG_RESET_TEXT);
+ _monitor.markDiscardPoint();
- _monitor.reset();
+ validateLogDoesNotContainMessage(_monitor, firstMessage);
- assertEquals("", _monitor.readFile());
+ String secondMessage = getName() + ": Test Message2";
+ Logger.getRootLogger().warn(secondMessage);
+ validateLogContainsMessage(_monitor, secondMessage);
}
public void testRead() throws IOException
@@ -214,7 +213,7 @@ public class LogMonitorTest extends TestCase
*
* @throws IOException if a problems occurs
*/
- protected void validateLogDoesNotContainsMessage(LogMonitor log, String message)
+ protected void validateLogDoesNotContainMessage(LogMonitor log, String message)
throws IOException
{
List<String> results = log.findMatches(message);
diff --git a/qpid/java/test-profiles/CPPExcludes b/qpid/java/test-profiles/CPPExcludes
index 2b58a0684d..d05f42e4b7 100755
--- a/qpid/java/test-profiles/CPPExcludes
+++ b/qpid/java/test-profiles/CPPExcludes
@@ -142,7 +142,6 @@ org.apache.qpid.server.failover.MessageDisappearWithIOExceptionTest#*
// These are recent test additions that are failing with the c++ broker
// Temporarily disabling until properly investigated.
-org.apache.qpid.test.unit.publish.DirtyTransactedPublishTest#*
org.apache.qpid.test.unit.ack.FailoverBeforeConsumingRecoverTest#*
org.apache.qpid.test.client.RollbackOrderTest#testOrderingAfterRollbackOnMessage#*
diff --git a/qpid/java/test-profiles/CPPTransientExcludes b/qpid/java/test-profiles/CPPTransientExcludes
index 47f24db19c..a214cf5b5c 100644
--- a/qpid/java/test-profiles/CPPTransientExcludes
+++ b/qpid/java/test-profiles/CPPTransientExcludes
@@ -27,3 +27,6 @@ org.apache.qpid.test.unit.xa.TopicTest#testMultiMessagesDurSubCrash
org.apache.qpid.test.unit.xa.TopicTest#testRecover
org.apache.qpid.test.unit.xa.QueueTest#testRecover
org.apache.qpid.test.unit.xa.QueueTest#testSendAndRecover
+
+// test requires a persistent store
+org.apache.qpid.test.unit.ack.ClientAcknowledgeTest#testClientAckWithLargeFlusherPeriod
diff --git a/qpid/java/test-profiles/Java010Excludes b/qpid/java/test-profiles/Java010Excludes
index fe0a53bdfc..e7718b982d 100755
--- a/qpid/java/test-profiles/Java010Excludes
+++ b/qpid/java/test-profiles/Java010Excludes
@@ -55,9 +55,6 @@ org.apache.qpid.server.queue.ProducerFlowControlTest#*
//QPID-1950 : Commit to test this failure. This is a MINA only failure so it cannot be tested when using 010.
org.apache.qpid.server.failover.MessageDisappearWithIOExceptionTest#*
-//QPID-3421: tests are failing on 0.10 test profile
-org.apache.qpid.test.unit.publish.DirtyTransactedPublishTest#*
-
//QPID-1864: rollback with subscriptions does not work in 0-10 yet
org.apache.qpid.test.client.RollbackOrderTest#testOrderingAfterRollbackOnMessage
diff --git a/qpid/java/test-profiles/JavaBDBExcludes b/qpid/java/test-profiles/JavaBDBExcludes
new file mode 100644
index 0000000000..3fac01cb6d
--- /dev/null
+++ b/qpid/java/test-profiles/JavaBDBExcludes
@@ -0,0 +1,22 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+//This test is subclassed within the bdbstore module to enable it to run and
+//also add some bdb-specific tests. It is excluded to prevent running twice.
+org.apache.qpid.server.store.MessageStoreTest#*
diff --git a/qpid/java/test-profiles/JavaDerbyExcludes b/qpid/java/test-profiles/JavaDerbyExcludes
new file mode 100644
index 0000000000..3caa360d48
--- /dev/null
+++ b/qpid/java/test-profiles/JavaDerbyExcludes
@@ -0,0 +1,21 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+org.apache.qpid.server.store.berkeleydb.BDBMessageStoreTest#*
+org.apache.qpid.server.store.berkeleydb.BDBUpgradeTest#*
diff --git a/qpid/java/test-profiles/JavaTransientExcludes b/qpid/java/test-profiles/JavaTransientExcludes
index 2ea46795d9..67190a6fcc 100644
--- a/qpid/java/test-profiles/JavaTransientExcludes
+++ b/qpid/java/test-profiles/JavaTransientExcludes
@@ -19,6 +19,7 @@
//These tests require a persistent store
org.apache.qpid.server.store.PersistentStoreTest#*
+org.apache.qpid.test.unit.ack.ClientAcknowledgeTest#testClientAckWithLargeFlusherPeriod
org.apache.qpid.test.unit.ct.DurableSubscriberTest#*
@@ -30,3 +31,6 @@ org.apache.qpid.server.store.MessageStoreTest#testQueuePersistence
org.apache.qpid.server.store.MessageStoreTest#testDurableQueueRemoval
org.apache.qpid.server.store.MessageStoreTest#testExchangePersistence
org.apache.qpid.server.store.MessageStoreTest#testDurableExchangeRemoval
+
+org.apache.qpid.server.store.berkeleydb.BDBMessageStoreTest#*
+org.apache.qpid.server.store.berkeleydb.BDBUpgradeTest#*
diff --git a/qpid/java/test-profiles/cpp.testprofile b/qpid/java/test-profiles/cpp.testprofile
index a2114f82ba..7cc7fdc821 100644
--- a/qpid/java/test-profiles/cpp.testprofile
+++ b/qpid/java/test-profiles/cpp.testprofile
@@ -33,7 +33,7 @@ broker.modules=
broker.args=
broker.type=spawned
-broker.command=${broker.executable} -p @PORT --data-dir ${build.data}/@PORT -t --auth no --no-module-dir ${broker.modules} ${broker.args}
+broker.command=${broker.executable} -p @PORT --data-dir ${project.root}/build/work/@PORT -t --auth no --no-module-dir ${broker.modules} ${broker.args}
profile.excludes=CPPPrefetchExcludes CPPTransientExcludes
test.excludes=Excludes CPPExcludes ${profile}.excludes ${profile.excludes} cpp.excludes
diff --git a/qpid/java/test-profiles/java-bdb-spawn.0-10.testprofile b/qpid/java/test-profiles/java-bdb-spawn.0-10.testprofile
new file mode 100644
index 0000000000..cba348b67f
--- /dev/null
+++ b/qpid/java/test-profiles/java-bdb-spawn.0-10.testprofile
@@ -0,0 +1,31 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+broker.language=java
+broker.type=spawned
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.ready=BRK-1004
+broker.stopped=Exception
+broker.config=build/etc/config-systests-bdb.xml
+messagestore.class.name=org.apache.qpid.server.store.berkeleydb.BDBMessageStore
+profile.excludes=JavaExcludes JavaPersistentExcludes Java010Excludes JavaBDBExcludes
+broker.clean.between.tests=true
+broker.persistent=true
+broker.version=v0_10
+
diff --git a/qpid/java/test-profiles/java-bdb-spawn.0-9-1.testprofile b/qpid/java/test-profiles/java-bdb-spawn.0-9-1.testprofile
new file mode 100644
index 0000000000..b04fea21b6
--- /dev/null
+++ b/qpid/java/test-profiles/java-bdb-spawn.0-9-1.testprofile
@@ -0,0 +1,36 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+broker.language=java
+broker.type=spawned
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.ready=BRK-1004
+broker.stopped=Exception
+broker.config=build/etc/config-systests-bdb.xml
+messagestore.class.name=org.apache.qpid.server.store.berkeleydb.BDBMessageStore
+profile.excludes=JavaExcludes JavaPersistentExcludes JavaPre010Excludes JavaBDBExcludes
+broker.clean.between.tests=true
+broker.persistent=true
+broker.protocol.excludes=--exclude-0-10 @PORT --exclude-0-10 @SSL_PORT
+broker.version=v0_9_1
+#
+# Do not enable. Allow client to attempt 0-10 and negotiate downwards
+#
+#qpid.amqp.version=0-91
+
diff --git a/qpid/java/test-profiles/java-bdb.0-10.testprofile b/qpid/java/test-profiles/java-bdb.0-10.testprofile
new file mode 100644
index 0000000000..3ef93a68cb
--- /dev/null
+++ b/qpid/java/test-profiles/java-bdb.0-10.testprofile
@@ -0,0 +1,32 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+broker.language=java
+broker.type=internal
+#broker.command only used for the second broker during failover tests in this profile
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.ready=BRK-1004
+broker.stopped=Exception
+broker.config=build/etc/config-systests-bdb.xml
+messagestore.class.name=org.apache.qpid.server.store.berkeleydb.BDBMessageStore
+profile.excludes=JavaExcludes JavaPersistentExcludes Java010Excludes JavaBDBExcludes
+broker.clean.between.tests=true
+broker.persistent=true
+broker.version=v0_10
+
diff --git a/qpid/java/test-profiles/java-bdb.0-9-1.testprofile b/qpid/java/test-profiles/java-bdb.0-9-1.testprofile
new file mode 100644
index 0000000000..101d38f4b9
--- /dev/null
+++ b/qpid/java/test-profiles/java-bdb.0-9-1.testprofile
@@ -0,0 +1,37 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+broker.language=java
+broker.type=internal
+#broker.command only used for the second broker during failover tests in this profile
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.ready=BRK-1004
+broker.stopped=Exception
+broker.config=build/etc/config-systests-bdb.xml
+messagestore.class.name=org.apache.qpid.server.store.berkeleydb.BDBMessageStore
+profile.excludes=JavaExcludes JavaPersistentExcludes JavaPre010Excludes JavaBDBExcludes
+broker.clean.between.tests=true
+broker.persistent=true
+broker.protocol.excludes=--exclude-0-10 @PORT --exclude-0-10 @SSL_PORT
+broker.version=v0_9_1
+#
+# Do not enable. Allow client to attempt 0-10 and negotiate downwards
+#
+#qpid.amqp.version=0-91
+
diff --git a/qpid/java/test-profiles/java-dby-spawn.0-10.testprofile b/qpid/java/test-profiles/java-dby-spawn.0-10.testprofile
index f51f5a26ac..5bd6f330d5 100644
--- a/qpid/java/test-profiles/java-dby-spawn.0-10.testprofile
+++ b/qpid/java/test-profiles/java-dby-spawn.0-10.testprofile
@@ -19,12 +19,11 @@
broker.language=java
broker.version=v0_10
broker.type=spawned
-broker.command=${project.root}/build/bin/qpid-server -p @PORT -m @MPORT -c @CONFIG_FILE -l ${test.profiles}/log4j-test.xml
-broker.clean=${test.profiles}/clean-dir ${build.data} ${project.root}/build/work
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT -c @CONFIG_FILE -l test-profiles/log4j-test.xml
broker.ready=BRK-1004
broker.stopped=Exception
-broker.config=${project.root}/build/etc/config-systests-derby.xml
+broker.config=build/etc/config-systests-derby.xml
messagestore.class.name=org.apache.qpid.server.store.DerbyMessageStore
-profile.excludes=JavaPersistentExcludes Java010Excludes
+profile.excludes=JavaPersistentExcludes JavaDerbyExcludes Java010Excludes
broker.clean.between.tests=true
broker.persistent=true
diff --git a/qpid/java/test-profiles/java-dby-spawn.0-9-1.testprofile b/qpid/java/test-profiles/java-dby-spawn.0-9-1.testprofile
index 1580cec1c5..e7212d30f8 100644
--- a/qpid/java/test-profiles/java-dby-spawn.0-9-1.testprofile
+++ b/qpid/java/test-profiles/java-dby-spawn.0-9-1.testprofile
@@ -19,14 +19,13 @@
broker.version=v0_9_1
broker.language=java
broker.type=spawned
-broker.command=${project.root}/build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES -c @CONFIG_FILE -l ${test.profiles}/log4j-test.xml
-broker.clean=${test.profiles}/clean-dir ${build.data} ${project.root}/build/work
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
broker.ready=BRK-1004
broker.stopped=Exception
-broker.config=${project.root}/build/etc/config-systests-derby.xml
+broker.config=build/etc/config-systests-derby.xml
broker.protocol.excludes=--exclude-0-10 @PORT --exclude-0-10 @SSL_PORT
messagestore.class.name=org.apache.qpid.server.store.DerbyMessageStore
-profile.excludes=JavaPersistentExcludes JavaPre010Excludes
+profile.excludes=JavaPersistentExcludes JavaDerbyExcludes JavaPre010Excludes
broker.clean.between.tests=true
broker.persistent=true
#
diff --git a/qpid/java/test-profiles/java-dby.0-10.testprofile b/qpid/java/test-profiles/java-dby.0-10.testprofile
index 5a7b9b5cdc..9c23d18f45 100644
--- a/qpid/java/test-profiles/java-dby.0-10.testprofile
+++ b/qpid/java/test-profiles/java-dby.0-10.testprofile
@@ -19,12 +19,12 @@
broker.language=java
broker.version=v0_10
broker.type=internal
-broker.command=${project.root}/build/bin/qpid-server -p @PORT -m @MPORT -c @CONFIG_FILE -l ${test.profiles}/log4j-test.xml
-broker.clean=${test.profiles}/clean-dir ${build.data} ${project.root}/build/work
+#broker.command only used for the second broker during failover tests in this profile
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT -c @CONFIG_FILE -l test-profiles/log4j-test.xml
broker.ready=BRK-1004
broker.stopped=Exception
-broker.config=${project.root}/build/etc/config-systests-derby.xml
+broker.config=build/etc/config-systests-derby.xml
messagestore.class.name=org.apache.qpid.server.store.DerbyMessageStore
-profile.excludes=JavaPersistentExcludes Java010Excludes
+profile.excludes=JavaPersistentExcludes JavaDerbyExcludes Java010Excludes
broker.clean.between.tests=true
broker.persistent=true
diff --git a/qpid/java/test-profiles/java-dby.0-9-1.testprofile b/qpid/java/test-profiles/java-dby.0-9-1.testprofile
index b4d506df05..83f43d8dbf 100644
--- a/qpid/java/test-profiles/java-dby.0-9-1.testprofile
+++ b/qpid/java/test-profiles/java-dby.0-9-1.testprofile
@@ -19,14 +19,14 @@
broker.version=v0_9_1
broker.language=java
broker.type=internal
-broker.command=${project.root}/build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES -c @CONFIG_FILE -l ${test.profiles}/log4j-test.xml
-broker.clean=${test.profiles}/clean-dir ${build.data} ${project.root}/build/work
+#broker.command only used for the second broker during failover tests in this profile
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
broker.ready=BRK-1004
broker.stopped=Exception
-broker.config=${project.root}/build/etc/config-systests-derby.xml
+broker.config=build/etc/config-systests-derby.xml
broker.protocol.excludes=--exclude-0-10 @PORT --exclude-0-10 @SSL_PORT
messagestore.class.name=org.apache.qpid.server.store.DerbyMessageStore
-profile.excludes=JavaPersistentExcludes JavaPre010Excludes
+profile.excludes=JavaPersistentExcludes JavaDerbyExcludes JavaPre010Excludes
broker.clean.between.tests=true
broker.persistent=true
#
diff --git a/qpid/java/test-profiles/java-mms-spawn.0-10.testprofile b/qpid/java/test-profiles/java-mms-spawn.0-10.testprofile
index 4fdac783cc..5e5d2e8a6b 100644
--- a/qpid/java/test-profiles/java-mms-spawn.0-10.testprofile
+++ b/qpid/java/test-profiles/java-mms-spawn.0-10.testprofile
@@ -19,8 +19,7 @@
broker.version=v0_10
broker.language=java
broker.type=spawned
-broker.command=${project.root}/build/bin/qpid-server -p @PORT -m @MPORT -c @CONFIG_FILE -l ${test.profiles}/log4j-test.xml
-broker.clean=${test.profiles}/clean-dir ${build.data} ${project.root}/build/work
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT -c @CONFIG_FILE -l test-profiles/log4j-test.xml
broker.ready=BRK-1004
broker.stopped=Exception
#
diff --git a/qpid/java/test-profiles/java-mms-spawn.0-9-1.testprofile b/qpid/java/test-profiles/java-mms-spawn.0-9-1.testprofile
index 4563600ba1..05b1f89452 100644
--- a/qpid/java/test-profiles/java-mms-spawn.0-9-1.testprofile
+++ b/qpid/java/test-profiles/java-mms-spawn.0-9-1.testprofile
@@ -19,8 +19,7 @@
broker.version=v0_9_1
broker.language=java
broker.type=spawned
-broker.command=${project.root}/build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES -c @CONFIG_FILE -l ${test.profiles}/log4j-test.xml
-broker.clean=${test.profiles}/clean-dir ${build.data} ${project.root}/build/work
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
broker.ready=BRK-1004
broker.stopped=Exception
broker.protocol.excludes=--exclude-0-10 @PORT --exclude-0-10 @SSL_PORT
diff --git a/qpid/java/test-profiles/java-mms.0-10.testprofile b/qpid/java/test-profiles/java-mms.0-10.testprofile
index 3ccc6dfd3b..209d384422 100644
--- a/qpid/java/test-profiles/java-mms.0-10.testprofile
+++ b/qpid/java/test-profiles/java-mms.0-10.testprofile
@@ -19,8 +19,8 @@
broker.language=java
broker.version=v0_10
broker.type=internal
-broker.command=${project.root}/build/bin/qpid-server -p @PORT -m @MPORT -c @CONFIG_FILE -l ${test.profiles}/log4j-test.xml
-broker.clean=${test.profiles}/clean-dir ${build.data} ${project.root}/build/work
+#broker.command only used for the second broker during failover tests in this profile
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT -c @CONFIG_FILE -l test-profiles/log4j-test.xml
broker.ready=BRK-1004
broker.stopped=Exception
diff --git a/qpid/java/test-profiles/java-mms.0-9-1.testprofile b/qpid/java/test-profiles/java-mms.0-9-1.testprofile
index cec02f3aa6..37efa097bb 100644
--- a/qpid/java/test-profiles/java-mms.0-9-1.testprofile
+++ b/qpid/java/test-profiles/java-mms.0-9-1.testprofile
@@ -20,8 +20,7 @@ broker.language=java
broker.version=v0_9_1
broker.type=internal
#broker.command only used for the second broker during failover tests in this profile
-broker.command=${project.root}/build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES -c @CONFIG_FILE -l ${test.profiles}/log4j-test.xml
-broker.clean=${test.profiles}/clean-dir ${build.data} ${project.root}/build/work
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
broker.ready=BRK-1004
broker.stopped=Exception
broker.protocol.excludes=--exclude-0-10 @PORT --exclude-0-10 @SSL_PORT
diff --git a/qpid/java/test-profiles/testprofile.defaults b/qpid/java/test-profiles/testprofile.defaults
index 151e904930..bd2faa7915 100644
--- a/qpid/java/test-profiles/testprofile.defaults
+++ b/qpid/java/test-profiles/testprofile.defaults
@@ -19,9 +19,8 @@
java.naming.factory.initial=org.apache.qpid.jndi.PropertiesFileInitialContextFactory
java.naming.provider.url=${test.profiles}/test-provider.properties
-broker.clean=${test.profiles}/clean-dir ${build.data} ${project.root}/build/work
broker.ready=Listening on TCP
-broker.config=${project.root}/build/etc/config-systests.xml
+broker.config=build/etc/config-systests.xml
messagestore.class.name=org.apache.qpid.server.store.MemoryMessageStore
broker.protocol.excludes=
broker.persistent=false
diff --git a/qpid/tools/src/py/qmf-tool b/qpid/tools/src/py/qmf-tool
index e366d04709..894dc9cc7d 100755
--- a/qpid/tools/src/py/qmf-tool
+++ b/qpid/tools/src/py/qmf-tool
@@ -31,6 +31,100 @@ from qpid.disp import Display
import cqpid
import qmf2
+class OptsAndArgs(object):
+
+ def __init__(self, argv):
+ self.argv = argv
+ self.usage = """qmf-tool [OPTIONS] [<broker-host>[:<port>]]"""
+ self.option_parser = optparse.OptionParser(usage=self.usage)
+ self.conn_group = optparse.OptionGroup(self.option_parser, "Connection Options")
+ self.conn_group.add_option("-u", "--user", action="store", type="string", help="User name for authentication")
+ self.conn_group.add_option("-p", "--password", action="store", type="string", help="Password for authentication")
+ self.conn_group.add_option("-t", "--transport", action="store", type="string", help="Transport type (tcp, ssl, rdma)")
+ self.conn_group.add_option("-m", "--mechanism", action="store", type="string", help="SASL Mechanism for security")
+ self.conn_group.add_option("-s", "--service", action="store", type="string", default="qpidd", help="SASL Service name")
+ self.conn_group.add_option("--min-ssf", action="store", type="int", metavar="<n>", help="Minimum acceptable security strength factor")
+ self.conn_group.add_option("--max-ssf", action="store", type="int", metavar="<n>", help="Maximum acceptable security strength factor")
+ self.conn_group.add_option("--conn-option", action="append", default=[], metavar="<NAME=VALUE>", help="Additional connection option(s)")
+ self.option_parser.add_option_group(self.conn_group)
+
+ self.qmf_group = optparse.OptionGroup(self.option_parser, "QMF Session Options")
+ self.qmf_group.add_option("--domain", action="store", type="string", help="QMF Domain")
+ self.qmf_group.add_option("--agent-age", action="store", type="int", metavar="<n>", help="Time, in minutes, to age out non-communicating agents")
+ self.qmf_group.add_option("--qmf-option", action="append", default=[], metavar="<NAME=VALUE>", help="Additional QMF session option(s)")
+ self.option_parser.add_option_group(self.qmf_group)
+
+ def parse(self):
+ host = "localhost"
+ conn_options = []
+ qmf_options = []
+
+ options, encArgs = self.option_parser.parse_args(args=self.argv)
+ try:
+ encoding = locale.getpreferredencoding()
+ args = [a.decode(encoding) for a in encArgs]
+ except:
+ args = encArgs
+
+ if len(args) > 1:
+ host = args[1]
+
+ if options.user:
+ conn_options.append("username:'%s'" % options.user)
+ if options.password:
+ conn_options.append("password:'%s'" % options.password)
+ if options.transport:
+ conn_options.append("transport:'%s'" % options.transport)
+ if options.mechanism:
+ conn_options.append("sasl_mechanisms:'%s'" % options.mechanism)
+ if options.service:
+ conn_options.append("sasl_service:'%s'" % options.service)
+ if options.min_ssf:
+ conn_options.append("sasl_min_ssf:%d" % options.min_ssf)
+ if options.max_ssf:
+ conn_options.append("sasl_max_ssf:%d" % options.max_ssf)
+ for x in options.conn_option:
+ try:
+ key, val = x.split('=')
+ conn_options.append("%s:%s" % (key, val))
+ except:
+ raise Exception("Improperly formatted text for --conn-option: '%s'" % x)
+
+ if options.domain:
+ qmf_options.append("domain:'%s'" % options.domain)
+ if options.agent_age:
+ qmf_options.append("max-agent-age:%d" % options.agent_age)
+ for x in options.qmf_option:
+ try:
+ key, val = x.split('=')
+ qmf_options.append("%s:%s" % (key, val))
+ except:
+ raise Exception("Improperly formatted text for --qmf-option: '%s'" % x)
+
+ conn_string = '{'
+ first = True
+ for x in conn_options:
+ if first:
+ first = None
+ else:
+ conn_string += ','
+ conn_string += x
+ conn_string += '}'
+
+ qmf_string = '{'
+ first = True
+ for x in qmf_options:
+ if first:
+ first = None
+ else:
+ qmf_string += ','
+ qmf_string += x
+ qmf_string += '}'
+
+ return host, conn_string, qmf_string
+
+
+
class Mcli(Cmd):
""" Management Command Interpreter """
@@ -55,10 +149,11 @@ class Mcli(Cmd):
print
print "Agent Commands:"
print " set filter <filter-string> - Filter the list of agents"
- print " show filter - Show the agent filter currently in effect"
print " list agents - Print a list of the known Agents"
- print " show agent <item-number> - Print detailed information about an Agent"
print " set default <item-number> - Set the default agent for operations"
+ print " show filter - Show the agent filter currently in effect"
+ print " show agent <item-number> - Print detailed information about an Agent"
+ print " show options - Show option strings used in the QMF session"
print
print "Schema Commands:"
print " list packages - Print a list of packages supported by the default agent"
@@ -112,7 +207,7 @@ class Mcli(Cmd):
def complete_show(self, text, line, begidx, endidx):
tokens = split(line[:begidx])
if len(tokens) == 1:
- return [i for i in ('filter', 'agent ', 'class ') if i.startswith(text)]
+ return [i for i in ('options', 'filter', 'agent ', 'class ') if i.startswith(text)]
return []
def do_show(self, data):
@@ -175,13 +270,15 @@ class Mcli(Cmd):
class QmfData:
"""
"""
- def __init__(self, disp, url):
+ def __init__(self, disp, url, conn_options, qmf_options):
self.disp = disp
self.url = url
+ self.conn_options = conn_options
+ self.qmf_options = qmf_options
self.agent_filter = '[]'
- self.connection = cqpid.Connection(self.url)
+ self.connection = cqpid.Connection(self.url, self.conn_options)
self.connection.open()
- self.session = qmf2.ConsoleSession(self.connection)
+ self.session = qmf2.ConsoleSession(self.connection, self.qmf_options)
self.session.setAgentFilter(self.agent_filter)
self.session.open()
self.lock = Lock()
@@ -239,6 +336,12 @@ class QmfData:
print "What do you want to show? Type 'help' for more information."
return
+ if tokens[0] == 'options':
+ print "Options used in this session:"
+ print " Connection Options : %s" % self.scrubConnOptions()
+ print " QMF Session Options: %s" % self.qmf_options
+ return
+
if tokens[0] == 'agent':
self.showAgent(tokens[1:])
return
@@ -636,32 +739,33 @@ class QmfData:
first = None
return result
-def Usage():
- print "Usage: qpid-tool [[<username>/<password>@]<target-host>[:<tcp-port>]]"
- print
+ def scrubConnOptions(self):
+ pw = self.conn_options.find('password:')
+ if pw < 0:
+ return self.conn_options
+ scrubbed = self.conn_options[:pw + 9] + "***"
+ delim = self.conn_options[pw:].find(',')
+ if delim < 0:
+ delim = self.conn_options[pw:].find('}')
+ scrubbed += self.conn_options[pw + delim:]
+ return scrubbed
+
#=========================================================
# Main Program
#=========================================================
-
-# Get host name and port if specified on the command line
-cargs = sys.argv[1:]
-_host = "localhost"
-
-if len(cargs) > 0:
- _host = cargs[0]
-
-if _host[0] == '-':
- Usage()
- if _host != '-h' and _host != "--help":
- print "qpid-tool: error: no such option:", _host
+try:
+ oa = OptsAndArgs(sys.argv)
+ host, conn_options, qmf_options = oa.parse()
+except Exception, e:
+ print "Parse Error: %s" % e
sys.exit(1)
disp = Display()
# Attempt to make a connection to the target broker
try:
- data = QmfData(disp, _host)
+ data = QmfData(disp, host, conn_options, qmf_options)
except Exception, e:
if str(e).find("Exchange not found") != -1:
print "Management not enabled on broker: Use '-m yes' option on broker startup."