summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--qpid/java/bdbstore/jmx/src/main/java/org/apache/qpid/server/store/berkeleydb/jmx/BDBHAMessageStoreManagerMBean.java140
-rw-r--r--qpid/java/bdbstore/jmx/src/main/java/org/apache/qpid/server/store/berkeleydb/jmx/BDBHAMessageStoreManagerMBeanProvider.java18
-rw-r--r--qpid/java/bdbstore/jmx/src/main/java/org/apache/qpid/server/store/berkeleydb/jmx/ManagedBDBHAMessageStore.java3
-rw-r--r--qpid/java/bdbstore/jmx/src/test/java/org/apache/qpid/server/store/berkeleydb/jmx/BDBHAMessageStoreManagerMBeanTest.java145
-rw-r--r--qpid/java/bdbstore/jmx/src/test/java/org/apache/qpid/server/store/berkeleydb/jmx/VirtualHostMBeanTest.java76
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/AbstractBDBMessageStore.java1896
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBBackup.java14
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBHAMessageStore.java658
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBHAVirtualHost.java104
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBHAVirtualHostFactory.java117
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStore.java1684
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreFactory.java5
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/CoalescingCommiter.java (renamed from qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/CommitThreadWrapper.java)58
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/Committer.java55
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/EnvironmentFacade.java61
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/EnvironmentFacadeFactory.java (renamed from qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/HAMessageStoreSmokeTest.java)24
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/LoggingAsyncExceptionListener.java37
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/StandardEnvironmentFacade.java229
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/StandardEnvironmentFacadeFactory.java76
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/DatabasePinger.java76
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/LocalReplicationNode.java437
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/LocalReplicationNodeFactory.java54
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/RemoteReplicationNode.java365
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/RemoteReplicationNodeFactory.java28
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/ReplicatedEnvironmentFacade.java1143
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/ReplicatedEnvironmentFacadeFactory.java94
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/StoreUpgrade.java1
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6.java1
-rw-r--r--qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/Upgrader.java24
-rw-r--r--qpid/java/bdbstore/src/main/java/resources/js/qpid/management/virtualhost/bdb_ha/addVirtualHost.js196
-rw-r--r--qpid/java/bdbstore/src/main/java/resources/js/qpid/management/virtualhost/bdb_ha/show.js187
-rw-r--r--qpid/java/bdbstore/src/main/java/resources/virtualhost/bdb_ha/add.html36
-rw-r--r--qpid/java/bdbstore/src/main/java/resources/virtualhost/bdb_ha/show.html117
-rw-r--r--qpid/java/bdbstore/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.ReplicationNodeFactory19
-rw-r--r--qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBHAMessageStoreTest.java168
-rw-r--r--qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreQuotaEventsTest.java2
-rw-r--r--qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/MessageStoreCreatorTest.java11
-rw-r--r--qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/StandardEnvironmentFacadeTest.java128
-rw-r--r--qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/VirtualHostTest.java285
-rw-r--r--qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/replication/LocalReplicationNodeTest.java309
-rw-r--r--qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/replication/NoopReplicationGroupListener.java42
-rw-r--r--qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/replication/RemoteReplicationNodeTest.java176
-rw-r--r--qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/replication/ReplicatedEnvironmentFacadeTest.java673
-rw-r--r--qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/replication/TestStateChangeListener.java54
-rw-r--r--qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/DatabaseTemplateTest.java1
-rw-r--r--qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6Test.java1
-rw-r--r--qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgraderFailOnNewerVersionTest.java15
-rw-r--r--qpid/java/bdbstore/systests/pom.xml7
-rw-r--r--qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreTest.java39
-rw-r--r--qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/HAClusterBlackboxTest.java127
-rw-r--r--qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/HAClusterManagementTest.java91
-rw-r--r--qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/HAClusterTwoNodeTest.java121
-rw-r--r--qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/HAClusterWhiteboxTest.java19
-rw-r--r--qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/HATestClusterCreator.java196
-rw-r--r--qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/ReplicationNodeRestTest.java169
-rw-r--r--qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/VirtualHostRestTest.java150
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/Broker.java6
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/BrokerOptions.java21
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java5
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/startup/DefaultRecovererProvider.java6
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/startup/ReplicationNodeRecoverer.java47
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/startup/VirtualHostRecoverer.java19
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/store/ManagementModeStoreHandler.java353
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/store/MemoryConfigurationEntryStore.java2
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListener.java36
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/message/internal/InternalMessage.java5
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/Broker.java5
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java17
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/Model.java3
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ReplicationNode.java109
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/State.java2
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/UUIDGenerator.java5
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/VirtualHost.java14
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AbstractAdapter.java16
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AccessControlProviderAdapter.java16
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AmqpPortAdapter.java20
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java35
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java209
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/FileSystemPreferencesProvider.java9
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/GroupProviderAdapter.java14
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java67
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java385
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/plugin/PluggableFactoryLoader.java8
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/plugin/ReplicationNodeFactory.java36
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/plugin/VirtualHostFactory.java3
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java16
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/replication/ReplicationGroupListener.java59
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractDurableConfiguredObjectRecoverer.java6
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractJDBCMessageStore.java27
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractMemoryMessageStore.java10
-rwxr-xr-xqpid/java/broker-core/src/main/java/org/apache/qpid/server/store/ConfigurationRecoveryHandler.java2
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationRecoverer.java9
-rwxr-xr-xqpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStore.java5
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfiguredObjectRecoverer.java2
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/JsonFileConfigStore.java65
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/MessageStore.java11
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/NullMessageStore.java10
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/RecoveryAbortException.java37
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/StateManager.java10
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/UnresolvedObject.java2
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/util/DaemonThreadFactory.java40
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/util/MapValueConverter.java74
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java167
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/DefaultUpgraderProvider.java1
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/StandardVirtualHost.java1
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/StandardVirtualHostFactory.java10
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/State.java3
-rwxr-xr-xqpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java11
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostAttributeRecoveryListener.java (renamed from qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/HAMessageStore.java)13
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostListener.java3
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRecoverer.java73
-rw-r--r--qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/startup/VirtualHostRecovererTest.java36
-rw-r--r--qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/ConfigurationEntryStoreTestCase.java53
-rw-r--r--qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/JsonConfigurationEntryStoreTest.java7
-rw-r--r--qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/ManagementModeStoreHandlerTest.java335
-rw-r--r--qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/MemoryConfigurationEntryStoreTest.java7
-rw-r--r--qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListenerTest.java23
-rw-r--r--qpid/java/broker-core/src/test/java/org/apache/qpid/server/model/ConfiguredObjectStateTransitionTest.java4
-rw-r--r--qpid/java/broker-core/src/test/java/org/apache/qpid/server/model/VirtualHostTest.java94
-rw-r--r--qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/DurableConfigurationRecovererTest.java (renamed from qpid/java/broker-core/src/test/java/org/apache/qpid/server/virtualhost/DurableConfigurationRecovererTest.java)34
-rw-r--r--qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/MessageStoreQuotaEventsTestBase.java10
-rw-r--r--qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/StateManagerTest.java2
-rw-r--r--qpid/java/broker-core/src/test/java/org/apache/qpid/server/util/BrokerTestHelper.java1
-rw-r--r--qpid/java/broker-core/src/test/java/org/apache/qpid/server/virtualhost/HouseKeepingTaskTest.java21
-rw-r--r--qpid/java/broker-core/src/test/java/org/apache/qpid/server/virtualhost/MockVirtualHost.java318
-rw-r--r--qpid/java/broker-core/src/test/java/org/apache/qpid/server/virtualhost/StandardVirtualHostTest.java23
-rw-r--r--qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ServerSessionDelegate.java10
-rw-r--r--qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/ReceivingLink_1_0.java12
-rw-r--r--qpid/java/broker-plugins/derby-store/src/main/java/org/apache/qpid/server/store/derby/DerbyMessageStore.java5
-rw-r--r--qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java21
-rw-r--r--qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/UpdatableStore.js27
-rw-r--r--qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/formatter.js8
-rw-r--r--qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js88
-rw-r--r--qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/VirtualHost.js274
-rw-r--r--qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addVirtualHost.js30
-rw-r--r--qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/virtualhost/standard/show.js100
-rw-r--r--qpid/java/broker-plugins/management-http/src/main/java/resources/showVirtualHost.html132
-rw-r--r--qpid/java/broker-plugins/management-http/src/main/java/resources/virtualhost/standard/show.html35
-rw-r--r--qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagement.java54
-rw-r--r--qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanProvider.java3
-rw-r--r--qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/MBeanUtils.java33
-rw-r--r--qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/VirtualHostMBean.java105
-rw-r--r--qpid/java/systests/etc/config-systests.json2
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/server/store/MessageStoreTest.java3
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/server/store/QuotaMessageStore.java7
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/server/store/SlowMessageStore.java26
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/ManagementLoggingTest.java2
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/AccessControlProviderRestTest.java6
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/Asserts.java3
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/RestTestHelper.java7
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/VirtualHostRestTest.java151
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java2
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/test/utils/JMXTestUtils.java2
-rwxr-xr-xqpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java32
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/test/utils/TestBrokerConfiguration.java63
155 files changed, 10316 insertions, 4997 deletions
diff --git a/qpid/java/bdbstore/jmx/src/main/java/org/apache/qpid/server/store/berkeleydb/jmx/BDBHAMessageStoreManagerMBean.java b/qpid/java/bdbstore/jmx/src/main/java/org/apache/qpid/server/store/berkeleydb/jmx/BDBHAMessageStoreManagerMBean.java
index 28528ec83c..1bc9c6ea6e 100644
--- a/qpid/java/bdbstore/jmx/src/main/java/org/apache/qpid/server/store/berkeleydb/jmx/BDBHAMessageStoreManagerMBean.java
+++ b/qpid/java/bdbstore/jmx/src/main/java/org/apache/qpid/server/store/berkeleydb/jmx/BDBHAMessageStoreManagerMBean.java
@@ -19,8 +19,17 @@
*/
package org.apache.qpid.server.store.berkeleydb.jmx;
+import static org.apache.qpid.server.model.ReplicationNode.COALESCING_SYNC;
+import static org.apache.qpid.server.model.ReplicationNode.DESIGNATED_PRIMARY;
+import static org.apache.qpid.server.model.ReplicationNode.DURABILITY;
+import static org.apache.qpid.server.model.ReplicationNode.GROUP_NAME;
+import static org.apache.qpid.server.model.ReplicationNode.HELPER_HOST_PORT;
+import static org.apache.qpid.server.model.ReplicationNode.HOST_PORT;
+import static org.apache.qpid.server.model.ReplicationNode.ROLE;
+
import java.io.IOException;
-import java.util.List;
+import java.util.Collection;
+import java.util.HashMap;
import java.util.Map;
import javax.management.JMException;
@@ -36,19 +45,16 @@ import javax.management.openmbean.TabularDataSupport;
import javax.management.openmbean.TabularType;
import org.apache.log4j.Logger;
-import org.apache.qpid.AMQStoreException;
import org.apache.qpid.server.jmx.AMQManagedObject;
import org.apache.qpid.server.jmx.ManagedObject;
-import org.apache.qpid.server.store.berkeleydb.BDBHAMessageStore;
+import org.apache.qpid.server.model.ConfiguredObjectFinder;
+import org.apache.qpid.server.model.IllegalStateTransitionException;
+import org.apache.qpid.server.model.ReplicationNode;
+import org.apache.qpid.server.model.State;
+import org.apache.qpid.server.model.VirtualHost;
/**
* Management mbean for BDB HA.
- * <p>
- * At runtime, the classloader loading this clas must have visibility of the other Qpid JMX classes. This is
- * currently arranged through OSGI using the <b>fragment</b> feature so that this bundle shares the
- * same classloader as broker-plugins-management-jmx. See the <b>Fragment-Host:</b> header within the MANIFEST.MF
- * of this bundle.
- * </p>
*/
public class BDBHAMessageStoreManagerMBean extends AMQManagedObject implements ManagedBDBHAMessageStore
{
@@ -58,12 +64,13 @@ public class BDBHAMessageStoreManagerMBean extends AMQManagedObject implements M
private static final CompositeType GROUP_MEMBER_ROW;
private static final OpenType<?>[] GROUP_MEMBER_ATTRIBUTE_TYPES;
+
static
{
try
{
GROUP_MEMBER_ATTRIBUTE_TYPES = new OpenType<?>[] {SimpleType.STRING, SimpleType.STRING};
- final String[] itemNames = new String[] {BDBHAMessageStore.GRP_MEM_COL_NODE_NAME, BDBHAMessageStore.GRP_MEM_COL_NODE_HOST_PORT};
+ final String[] itemNames = new String[] {GRP_MEM_COL_NODE_NAME, GRP_MEM_COL_NODE_HOST_PORT};
final String[] itemDescriptions = new String[] {"Unique node name", "Node host / port "};
GROUP_MEMBER_ROW = new CompositeType("GroupMember", "Replication group member",
itemNames,
@@ -71,7 +78,7 @@ public class BDBHAMessageStoreManagerMBean extends AMQManagedObject implements M
GROUP_MEMBER_ATTRIBUTE_TYPES );
GROUP_MEMBERS_TABLE = new TabularType("GroupMembers", "Replication group memebers",
GROUP_MEMBER_ROW,
- new String[] {BDBHAMessageStore.GRP_MEM_COL_NODE_NAME});
+ new String[] {GRP_MEM_COL_NODE_NAME});
}
catch (final OpenDataException ode)
{
@@ -79,104 +86,95 @@ public class BDBHAMessageStoreManagerMBean extends AMQManagedObject implements M
}
}
- private final BDBHAMessageStore _store;
+ private final ReplicationNode _localReplicationNode;
+ private final String _objectName;
+ private final VirtualHost _parent;
+ private final String _virtualHostName;
- protected BDBHAMessageStoreManagerMBean(BDBHAMessageStore store, ManagedObject parent) throws JMException
+ public BDBHAMessageStoreManagerMBean(ReplicationNode localReplicationNode, ManagedObject parent) throws JMException
{
super(ManagedBDBHAMessageStore.class, ManagedBDBHAMessageStore.TYPE, ((AMQManagedObject)parent).getRegistry());
- LOGGER.debug("Creating BDBHAMessageStoreManagerMBean");
- _store = store;
+
+ _localReplicationNode = localReplicationNode;
+ _virtualHostName = localReplicationNode.getParent(VirtualHost.class).getName();
+ _objectName = ObjectName.quote(_virtualHostName);
+ _parent = _localReplicationNode.getParent(VirtualHost.class);
+
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Creating BDBHAMessageStoreManagerMBean for " + _localReplicationNode.getName());
+ }
register();
}
@Override
public String getObjectInstanceName()
{
- return ObjectName.quote(_store.getName());
+ return _objectName;
}
@Override
public String getGroupName()
{
- return _store.getGroupName();
+ return (String) _localReplicationNode.getAttribute(GROUP_NAME);
}
@Override
public String getNodeName()
{
- return _store.getNodeName();
+ return (String) _localReplicationNode.getName();
}
@Override
public String getNodeHostPort()
{
- return _store.getNodeHostPort();
+ return (String) _localReplicationNode.getAttribute(HOST_PORT);
}
@Override
public String getHelperHostPort()
{
- return _store.getHelperHostPort();
+ return (String) _localReplicationNode.getAttribute(HELPER_HOST_PORT);
}
@Override
public String getDurability() throws IOException, JMException
{
- try
- {
- return _store.getDurability();
- }
- catch (RuntimeException e)
- {
- LOGGER.debug("Failed query replication policy", e);
- throw new JMException(e.getMessage());
- }
+ return (String) _localReplicationNode.getAttribute(DURABILITY);
}
@Override
public boolean getCoalescingSync() throws IOException, JMException
{
- return _store.isCoalescingSync();
+ return (Boolean)_localReplicationNode.getAttribute(COALESCING_SYNC);
}
@Override
public String getNodeState() throws IOException, JMException
{
- try
- {
- return _store.getNodeState();
- }
- catch (RuntimeException e)
- {
- LOGGER.debug("Failed query node state", e);
- throw new JMException(e.getMessage());
- }
+ return (String)_localReplicationNode.getAttribute(ROLE);
}
@Override
public boolean getDesignatedPrimary() throws IOException, JMException
{
- try
- {
- return _store.isDesignatedPrimary();
- }
- catch (RuntimeException e)
- {
- LOGGER.debug("Failed query designated primary", e);
- throw new JMException(e.getMessage());
- }
+ return (Boolean)_localReplicationNode.getAttribute(DESIGNATED_PRIMARY);
}
@Override
public TabularData getAllNodesInGroup() throws IOException, JMException
{
- final TabularDataSupport data = new TabularDataSupport(GROUP_MEMBERS_TABLE);
- final List<Map<String, String>> members = _store.getGroupMembers();
+ Collection<ReplicationNode> allNodes = _parent.getChildren(ReplicationNode.class);
- for (Map<String, String> map : members)
+ final TabularDataSupport data = new TabularDataSupport(GROUP_MEMBERS_TABLE);
+ for (ReplicationNode replicationNode : allNodes)
{
- CompositeData memberData = new CompositeDataSupport(GROUP_MEMBER_ROW, map);
+ Map<String, String> nodeMap = new HashMap<String, String>();
+ nodeMap.put(GRP_MEM_COL_NODE_NAME, replicationNode.getName());
+ nodeMap.put(GRP_MEM_COL_NODE_HOST_PORT, (String)replicationNode.getAttribute(HOST_PORT));
+
+ CompositeData memberData = new CompositeDataSupport(GROUP_MEMBER_ROW, nodeMap);
data.put(memberData);
}
return data;
@@ -185,14 +183,26 @@ public class BDBHAMessageStoreManagerMBean extends AMQManagedObject implements M
@Override
public void removeNodeFromGroup(String nodeName) throws JMException
{
+ // find the replication node object, set the desired state
+ Collection<ReplicationNode> allNodes = _parent.getChildren(ReplicationNode.class);
+ ReplicationNode targetNode = ConfiguredObjectFinder.findConfiguredObjectByName(allNodes, nodeName);
+
+ if (targetNode == null)
+ {
+ throw new JMException("Failed to find replication node with name '" + nodeName + "'.");
+ }
try
{
- _store.removeNodeFromGroup(nodeName);
+ State newState = targetNode.setDesiredState(targetNode.getActualState(), State.DELETED);
+ if (newState != State.DELETED)
+ {
+ throw new JMException("Failed to delete replication node with name '" + nodeName + "'. New unexpectedly state is " + newState);
+ }
}
- catch (AMQStoreException e)
+ catch(IllegalStateTransitionException e)
{
- LOGGER.error("Failed to remove node " + nodeName + " from group", e);
- throw new JMException(e.getMessage());
+ LOGGER.error("Cannot remove node '" + nodeName + "' from the group", e);
+ throw new JMException("Cannot remove node '" + nodeName + "' from the group:" + e.getMessage());
}
}
@@ -201,27 +211,19 @@ public class BDBHAMessageStoreManagerMBean extends AMQManagedObject implements M
{
try
{
- _store.setDesignatedPrimary(primary);
+ _localReplicationNode.setAttribute(DESIGNATED_PRIMARY, _localReplicationNode.getAttribute(DESIGNATED_PRIMARY), primary);
}
- catch (AMQStoreException e)
+ catch (Exception e)
{
- LOGGER.error("Failed to set node " + _store.getNodeName() + " as designated primary", e);
- throw new JMException(e.getMessage());
+ LOGGER.error("Failed to set node " + _localReplicationNode.getName() + " to designated primary : " + primary, e);
+ throw new JMException("Failed to set node " + _localReplicationNode.getName() + " to designated primary : " + primary + ":" + e.getMessage());
}
}
@Override
public void updateAddress(String nodeName, String newHostName, int newPort) throws JMException
{
- try
- {
- _store.updateAddress(nodeName, newHostName, newPort);
- }
- catch(AMQStoreException e)
- {
- LOGGER.error("Failed to update address for node " + nodeName + " to " + newHostName + ":" + newPort, e);
- throw new JMException(e.getMessage());
- }
+ throw new UnsupportedOperationException("Unsupported operation. Delete the node then add a new node in its place.");
}
@Override
diff --git a/qpid/java/bdbstore/jmx/src/main/java/org/apache/qpid/server/store/berkeleydb/jmx/BDBHAMessageStoreManagerMBeanProvider.java b/qpid/java/bdbstore/jmx/src/main/java/org/apache/qpid/server/store/berkeleydb/jmx/BDBHAMessageStoreManagerMBeanProvider.java
index 0492350a25..1c362c3023 100644
--- a/qpid/java/bdbstore/jmx/src/main/java/org/apache/qpid/server/store/berkeleydb/jmx/BDBHAMessageStoreManagerMBeanProvider.java
+++ b/qpid/java/bdbstore/jmx/src/main/java/org/apache/qpid/server/store/berkeleydb/jmx/BDBHAMessageStoreManagerMBeanProvider.java
@@ -21,18 +21,17 @@
package org.apache.qpid.server.store.berkeleydb.jmx;
import javax.management.JMException;
-import javax.management.StandardMBean;
import org.apache.log4j.Logger;
import org.apache.qpid.server.jmx.MBeanProvider;
import org.apache.qpid.server.jmx.ManagedObject;
import org.apache.qpid.server.model.ConfiguredObject;
-import org.apache.qpid.server.model.VirtualHost;
-import org.apache.qpid.server.store.berkeleydb.BDBHAMessageStore;
+import org.apache.qpid.server.store.berkeleydb.replication.LocalReplicationNode;
+import org.apache.qpid.server.store.berkeleydb.replication.ReplicatedEnvironmentFacade;
/**
* This provide will create a {@link BDBHAMessageStoreManagerMBean} if the child is a virtual
- * host and of type {@link BDBHAMessageStore#TYPE}.
+ * host and of type {@link ReplicatedEnvironmentFacade#TYPE}.
*
*/
public class BDBHAMessageStoreManagerMBeanProvider implements MBeanProvider
@@ -47,23 +46,20 @@ public class BDBHAMessageStoreManagerMBeanProvider implements MBeanProvider
@Override
public boolean isChildManageableByMBean(ConfiguredObject child)
{
- return (child instanceof VirtualHost
- && BDBHAMessageStore.TYPE.equals(child.getAttribute(VirtualHost.STORE_TYPE)));
+ return (child instanceof LocalReplicationNode);
}
@Override
- public StandardMBean createMBean(ConfiguredObject child, StandardMBean parent) throws JMException
+ public ManagedObject createMBean(ConfiguredObject child, ManagedObject parent) throws JMException
{
- VirtualHost virtualHostChild = (VirtualHost) child;
-
- BDBHAMessageStore messageStore = (BDBHAMessageStore) virtualHostChild.getMessageStore();
+ LocalReplicationNode localReplicationNode = (LocalReplicationNode) child;
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Creating mBean for child " + child);
}
- return new BDBHAMessageStoreManagerMBean(messageStore, (ManagedObject) parent);
+ return new BDBHAMessageStoreManagerMBean(localReplicationNode, parent);
}
@Override
diff --git a/qpid/java/bdbstore/jmx/src/main/java/org/apache/qpid/server/store/berkeleydb/jmx/ManagedBDBHAMessageStore.java b/qpid/java/bdbstore/jmx/src/main/java/org/apache/qpid/server/store/berkeleydb/jmx/ManagedBDBHAMessageStore.java
index b85e44526b..fc1cd0801a 100644
--- a/qpid/java/bdbstore/jmx/src/main/java/org/apache/qpid/server/store/berkeleydb/jmx/ManagedBDBHAMessageStore.java
+++ b/qpid/java/bdbstore/jmx/src/main/java/org/apache/qpid/server/store/berkeleydb/jmx/ManagedBDBHAMessageStore.java
@@ -41,6 +41,9 @@ public interface ManagedBDBHAMessageStore
public static final String ATTR_DESIGNATED_PRIMARY = "DesignatedPrimary";
public static final String ATTR_COALESCING_SYNC = "CoalescingSync";
+ public static final String GRP_MEM_COL_NODE_HOST_PORT = "NodeHostPort";
+ public static final String GRP_MEM_COL_NODE_NAME = "NodeName";
+
@MBeanAttribute(name=ATTR_GROUP_NAME, description="Name identifying the group")
String getGroupName() throws IOException, JMException;
diff --git a/qpid/java/bdbstore/jmx/src/test/java/org/apache/qpid/server/store/berkeleydb/jmx/BDBHAMessageStoreManagerMBeanTest.java b/qpid/java/bdbstore/jmx/src/test/java/org/apache/qpid/server/store/berkeleydb/jmx/BDBHAMessageStoreManagerMBeanTest.java
index 298d5bc045..b5e01732dd 100644
--- a/qpid/java/bdbstore/jmx/src/test/java/org/apache/qpid/server/store/berkeleydb/jmx/BDBHAMessageStoreManagerMBeanTest.java
+++ b/qpid/java/bdbstore/jmx/src/test/java/org/apache/qpid/server/store/berkeleydb/jmx/BDBHAMessageStoreManagerMBeanTest.java
@@ -19,15 +19,21 @@
*/
package org.apache.qpid.server.store.berkeleydb.jmx;
-import static org.mockito.Mockito.doThrow;
+import static org.apache.qpid.server.model.ReplicationNode.COALESCING_SYNC;
+import static org.apache.qpid.server.model.ReplicationNode.DESIGNATED_PRIMARY;
+import static org.apache.qpid.server.model.ReplicationNode.DURABILITY;
+import static org.apache.qpid.server.model.ReplicationNode.GROUP_NAME;
+import static org.apache.qpid.server.model.ReplicationNode.HELPER_HOST_PORT;
+import static org.apache.qpid.server.model.ReplicationNode.HOST_PORT;
+import static org.apache.qpid.server.model.ReplicationNode.ROLE;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.jar.JarException;
import javax.management.JMException;
import javax.management.ObjectName;
@@ -37,15 +43,15 @@ import javax.management.openmbean.TabularData;
import junit.framework.TestCase;
-import org.apache.qpid.AMQStoreException;
import org.apache.qpid.server.jmx.AMQManagedObject;
import org.apache.qpid.server.jmx.ManagedObjectRegistry;
import org.apache.qpid.server.logging.SystemOutMessageLogger;
import org.apache.qpid.server.logging.actors.CurrentActor;
import org.apache.qpid.server.logging.actors.TestLogActor;
-import org.apache.qpid.server.store.berkeleydb.BDBHAMessageStore;
-import org.apache.qpid.server.store.berkeleydb.jmx.BDBHAMessageStoreManagerMBean;
-import org.apache.qpid.server.store.berkeleydb.jmx.ManagedBDBHAMessageStore;
+import org.apache.qpid.server.model.IllegalStateTransitionException;
+import org.apache.qpid.server.model.ReplicationNode;
+import org.apache.qpid.server.model.State;
+import org.apache.qpid.server.model.VirtualHost;
public class BDBHAMessageStoreManagerMBeanTest extends TestCase
{
@@ -55,11 +61,12 @@ public class BDBHAMessageStoreManagerMBeanTest extends TestCase
private static final String TEST_HELPER_HOST_PORT = "host:5678";
private static final String TEST_DURABILITY = "sync,sync,all";
private static final String TEST_NODE_STATE = "MASTER";
- private static final String TEST_STORE_NAME = "testStoreName";
private static final boolean TEST_DESIGNATED_PRIMARY_FLAG = false;
+ private static final String TEST_VHOST_NAME = "test";
- private BDBHAMessageStore _store;
private BDBHAMessageStoreManagerMBean _mBean;
+ private VirtualHost _virtualHost;
+ private ReplicationNode _localReplicationNode;
private AMQManagedObject _mBeanParent;
@Override
@@ -68,10 +75,16 @@ public class BDBHAMessageStoreManagerMBeanTest extends TestCase
super.setUp();
CurrentActor.set(new TestLogActor(new SystemOutMessageLogger()));
- _store = mock(BDBHAMessageStore.class);
+ _localReplicationNode = mock(ReplicationNode.class);
+ _virtualHost = mock(VirtualHost.class);
_mBeanParent = mock(AMQManagedObject.class);
when(_mBeanParent.getRegistry()).thenReturn(mock(ManagedObjectRegistry.class));
- _mBean = new BDBHAMessageStoreManagerMBean(_store, _mBeanParent);
+ when(_localReplicationNode.getParent(VirtualHost.class)).thenReturn(_virtualHost);
+
+ when(_localReplicationNode.getName()).thenReturn(TEST_NODE_NAME);
+ when(_virtualHost.getName()).thenReturn(TEST_VHOST_NAME);
+
+ _mBean = new BDBHAMessageStoreManagerMBean(_localReplicationNode, _mBeanParent);
}
@Override
@@ -83,103 +96,124 @@ public class BDBHAMessageStoreManagerMBeanTest extends TestCase
public void testObjectName() throws Exception
{
- when(_store.getName()).thenReturn(TEST_STORE_NAME);
-
- String expectedObjectName = "org.apache.qpid:type=BDBHAMessageStore,name=" + ObjectName.quote(TEST_STORE_NAME);
+ String expectedObjectName = "org.apache.qpid:type=BDBHAMessageStore,name=" + ObjectName.quote(TEST_VHOST_NAME);
assertEquals(expectedObjectName, _mBean.getObjectName().toString());
}
public void testGroupName() throws Exception
{
- when(_store.getGroupName()).thenReturn(TEST_GROUP_NAME);
+ when(_localReplicationNode.getAttribute(GROUP_NAME)).thenReturn(TEST_GROUP_NAME);
assertEquals(TEST_GROUP_NAME, _mBean.getAttribute(ManagedBDBHAMessageStore.ATTR_GROUP_NAME));
}
public void testNodeName() throws Exception
{
- when(_store.getNodeName()).thenReturn(TEST_NODE_NAME);
-
assertEquals(TEST_NODE_NAME, _mBean.getAttribute(ManagedBDBHAMessageStore.ATTR_NODE_NAME));
}
public void testNodeHostPort() throws Exception
{
- when(_store.getNodeHostPort()).thenReturn(TEST_NODE_HOST_PORT);
+ when(_localReplicationNode.getAttribute(HOST_PORT)).thenReturn(TEST_NODE_HOST_PORT);
assertEquals(TEST_NODE_HOST_PORT, _mBean.getAttribute(ManagedBDBHAMessageStore.ATTR_NODE_HOST_PORT));
}
public void testHelperHostPort() throws Exception
{
- when(_store.getHelperHostPort()).thenReturn(TEST_HELPER_HOST_PORT);
+ when(_localReplicationNode.getAttribute(HELPER_HOST_PORT)).thenReturn(TEST_HELPER_HOST_PORT);
assertEquals(TEST_HELPER_HOST_PORT, _mBean.getAttribute(ManagedBDBHAMessageStore.ATTR_HELPER_HOST_PORT));
}
public void testDurability() throws Exception
{
- when(_store.getDurability()).thenReturn(TEST_DURABILITY);
+ when(_localReplicationNode.getAttribute(DURABILITY)).thenReturn(TEST_DURABILITY);
assertEquals(TEST_DURABILITY, _mBean.getAttribute(ManagedBDBHAMessageStore.ATTR_DURABILITY));
}
public void testCoalescingSync() throws Exception
{
- when(_store.isCoalescingSync()).thenReturn(true);
+ when(_localReplicationNode.getAttribute(COALESCING_SYNC)).thenReturn(true);
assertEquals(true, _mBean.getAttribute(ManagedBDBHAMessageStore.ATTR_COALESCING_SYNC));
}
public void testNodeState() throws Exception
{
- when(_store.getNodeState()).thenReturn(TEST_NODE_STATE);
+ when(_localReplicationNode.getAttribute(ROLE)).thenReturn(TEST_NODE_STATE);
assertEquals(TEST_NODE_STATE, _mBean.getAttribute(ManagedBDBHAMessageStore.ATTR_NODE_STATE));
}
public void testDesignatedPrimaryFlag() throws Exception
{
- when(_store.isDesignatedPrimary()).thenReturn(TEST_DESIGNATED_PRIMARY_FLAG);
+ when(_localReplicationNode.getAttribute(DESIGNATED_PRIMARY)).thenReturn(TEST_DESIGNATED_PRIMARY_FLAG);
assertEquals(TEST_DESIGNATED_PRIMARY_FLAG, _mBean.getAttribute(ManagedBDBHAMessageStore.ATTR_DESIGNATED_PRIMARY));
}
public void testGroupMembersForGroupWithOneNode() throws Exception
{
- List<Map<String, String>> members = Collections.singletonList(createTestNodeResult());
- when(_store.getGroupMembers()).thenReturn(members);
+ ReplicationNode remoteNode = mock(ReplicationNode.class);
+ when(remoteNode.getName()).thenReturn("remotenode");
+ when(remoteNode.getAttribute(HOST_PORT)).thenReturn("remotehost:port");
+
+ when(_localReplicationNode.getAttribute(HOST_PORT)).thenReturn(TEST_NODE_HOST_PORT);
+
+ Collection<ReplicationNode> nodes = new ArrayList<ReplicationNode>();
+ nodes.add(_localReplicationNode);
+ nodes.add(remoteNode);
+ when(_virtualHost.getChildren(ReplicationNode.class)).thenReturn(nodes);
final TabularData resultsTable = _mBean.getAllNodesInGroup();
- assertTableHasHeadingsNamed(resultsTable, BDBHAMessageStore.GRP_MEM_COL_NODE_NAME, BDBHAMessageStore.GRP_MEM_COL_NODE_HOST_PORT);
+ assertTableHasHeadingsNamed(resultsTable, BDBHAMessageStoreManagerMBean.GRP_MEM_COL_NODE_NAME, BDBHAMessageStoreManagerMBean.GRP_MEM_COL_NODE_HOST_PORT);
final int numberOfDataRows = resultsTable.size();
- assertEquals("Unexpected number of data rows", 1 ,numberOfDataRows);
- final CompositeData row = (CompositeData) resultsTable.values().iterator().next();
- assertEquals(TEST_NODE_NAME, row.get(BDBHAMessageStore.GRP_MEM_COL_NODE_NAME));
- assertEquals(TEST_NODE_HOST_PORT, row.get(BDBHAMessageStore.GRP_MEM_COL_NODE_HOST_PORT));
+ assertEquals("Unexpected number of data rows", 2 ,numberOfDataRows);
+ Iterator<?> iterator = resultsTable.values().iterator();
+
+ final CompositeData firstRow = (CompositeData) iterator.next();
+ assertEquals(TEST_NODE_NAME, firstRow.get(BDBHAMessageStoreManagerMBean.GRP_MEM_COL_NODE_NAME));
+ assertEquals(TEST_NODE_HOST_PORT, firstRow.get(BDBHAMessageStoreManagerMBean.GRP_MEM_COL_NODE_HOST_PORT));
+
+ final CompositeData secondRow = (CompositeData) iterator.next();
+ assertEquals("remotenode", secondRow.get(BDBHAMessageStoreManagerMBean.GRP_MEM_COL_NODE_NAME));
+ assertEquals("remotehost:port", secondRow.get(BDBHAMessageStoreManagerMBean.GRP_MEM_COL_NODE_HOST_PORT));
+
}
public void testRemoveNodeFromReplicationGroup() throws Exception
{
+ Collection<ReplicationNode> nodes = new ArrayList<ReplicationNode>();
+ nodes.add(_localReplicationNode);
+ when(_virtualHost.getChildren(ReplicationNode.class)).thenReturn(nodes);
+ when(_localReplicationNode.getActualState()).thenReturn(State.ACTIVE);
+ when(_localReplicationNode.setDesiredState(State.ACTIVE, State.DELETED)).thenReturn(State.DELETED);
+
_mBean.removeNodeFromGroup(TEST_NODE_NAME);
- verify(_store).removeNodeFromGroup(TEST_NODE_NAME);
+ verify(_localReplicationNode).setDesiredState(State.ACTIVE, State.DELETED);
}
- public void testRemoveNodeFromReplicationGroupWithError() throws Exception
+ public void testRemoveNodeFromReplicationGroupOnIllegalStateTransitionException() throws Exception
{
- doThrow(new AMQStoreException("mocked exception")).when(_store).removeNodeFromGroup(TEST_NODE_NAME);
+ Collection<ReplicationNode> nodes = new ArrayList<ReplicationNode>();
+ nodes.add(_localReplicationNode);
+ when(_virtualHost.getChildren(ReplicationNode.class)).thenReturn(nodes);
+ when(_localReplicationNode.getActualState()).thenReturn(State.ACTIVE);
+ when(_localReplicationNode.setDesiredState(State.ACTIVE, State.DELETED)).thenThrow(new IllegalStateTransitionException());
try
{
_mBean.removeNodeFromGroup(TEST_NODE_NAME);
- fail("Exception not thrown");
+ fail("Should throw JM Exception on IllegalStateTransitionException");
}
- catch (JMException je)
+ catch(JMException e)
{
- // PASS
+ //pass
}
}
@@ -187,32 +221,7 @@ public class BDBHAMessageStoreManagerMBeanTest extends TestCase
{
_mBean.setDesignatedPrimary(true);
- verify(_store).setDesignatedPrimary(true);
- }
-
- public void testSetAsDesignatedPrimaryWithError() throws Exception
- {
- doThrow(new AMQStoreException("mocked exception")).when(_store).setDesignatedPrimary(true);
-
- try
- {
- _mBean.setDesignatedPrimary(true);
- fail("Exception not thrown");
- }
- catch (JMException je)
- {
- // PASS
- }
- }
-
- public void testUpdateAddress() throws Exception
- {
- String newHostName = "newHostName";
- int newPort = 1967;
-
- _mBean.updateAddress(TEST_NODE_NAME, newHostName, newPort);
-
- verify(_store).updateAddress(TEST_NODE_NAME, newHostName, newPort);
+ verify(_localReplicationNode).setAttribute(DESIGNATED_PRIMARY, null, true);
}
private void assertTableHasHeadingsNamed(final TabularData resultsTable, String... headingNames)
@@ -223,12 +232,4 @@ public class BDBHAMessageStoreManagerMBeanTest extends TestCase
assertTrue("Table should have column with heading " + headingName, headingsRow.containsKey(headingName));
}
}
-
- private Map<String, String> createTestNodeResult()
- {
- Map<String, String> items = new HashMap<String, String>();
- items.put(BDBHAMessageStore.GRP_MEM_COL_NODE_NAME, TEST_NODE_NAME);
- items.put(BDBHAMessageStore.GRP_MEM_COL_NODE_HOST_PORT, TEST_NODE_HOST_PORT);
- return items;
- }
}
diff --git a/qpid/java/bdbstore/jmx/src/test/java/org/apache/qpid/server/store/berkeleydb/jmx/VirtualHostMBeanTest.java b/qpid/java/bdbstore/jmx/src/test/java/org/apache/qpid/server/store/berkeleydb/jmx/VirtualHostMBeanTest.java
new file mode 100644
index 0000000000..9995e9a02a
--- /dev/null
+++ b/qpid/java/bdbstore/jmx/src/test/java/org/apache/qpid/server/store/berkeleydb/jmx/VirtualHostMBeanTest.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.jmx;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import junit.framework.TestCase;
+
+import org.apache.qpid.server.jmx.DefaultManagedObject;
+import org.apache.qpid.server.jmx.ManagedObjectRegistry;
+import org.apache.qpid.server.jmx.mbeans.VirtualHostMBean;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.store.berkeleydb.replication.LocalReplicationNode;
+
+public class VirtualHostMBeanTest extends TestCase
+{
+ private VirtualHost _mockVirtualHost;
+ private ManagedObjectRegistry _mockManagedObjectRegistry;
+ private VirtualHostMBean _virtualHostMBean;
+ private LocalReplicationNode _mockLocalReplicationNode;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ _mockVirtualHost = mock(VirtualHost.class);
+ _mockManagedObjectRegistry = mock(ManagedObjectRegistry.class);
+ _mockLocalReplicationNode = mock(LocalReplicationNode.class);
+ when(_mockLocalReplicationNode.getParent(VirtualHost.class)).thenReturn(_mockVirtualHost);
+ when(_mockVirtualHost.getName()).thenReturn("vhost");
+
+ _virtualHostMBean = new VirtualHostMBean(_mockVirtualHost, _mockManagedObjectRegistry);
+ }
+
+ public void testAdditionalMbeanRegistered_LocalReplicationNodeAdded() throws Exception
+ {
+ _virtualHostMBean.childAdded(_mockVirtualHost, _mockLocalReplicationNode);
+
+ verify(_mockManagedObjectRegistry, times(2)).registerObject(any(DefaultManagedObject.class));
+ }
+
+ public void testAdditionalMbeanUnregisteredOnUnregisterOfThisMbean() throws Exception
+ {
+ _virtualHostMBean.childAdded(_mockVirtualHost, _mockLocalReplicationNode);
+ _virtualHostMBean.unregister();
+
+ verify(_mockManagedObjectRegistry, times(2)).unregisterObject(any(DefaultManagedObject.class));
+ }
+
+ public void testAdditionalMbeanUnregistered_LocalReplicationNodeRemoved() throws Exception
+ {
+ _virtualHostMBean.childAdded(_mockVirtualHost, _mockLocalReplicationNode);
+
+ _virtualHostMBean.childRemoved(_mockVirtualHost, _mockLocalReplicationNode);
+ verify(_mockManagedObjectRegistry).unregisterObject(any(BDBHAMessageStoreManagerMBean.class));
+ }
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/AbstractBDBMessageStore.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/AbstractBDBMessageStore.java
deleted file mode 100644
index 35efa55569..0000000000
--- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/AbstractBDBMessageStore.java
+++ /dev/null
@@ -1,1896 +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.server.store.berkeleydb;
-
-import com.sleepycat.bind.tuple.ByteBinding;
-import com.sleepycat.bind.tuple.IntegerBinding;
-import com.sleepycat.bind.tuple.LongBinding;
-import com.sleepycat.je.*;
-import com.sleepycat.je.Transaction;
-
-import java.io.File;
-import java.lang.ref.SoftReference;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
-import java.util.UUID;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicLong;
-
-import org.apache.commons.configuration.Configuration;
-import org.apache.commons.configuration.ConfigurationException;
-import org.apache.log4j.Logger;
-import org.apache.qpid.AMQStoreException;
-import org.apache.qpid.server.message.EnqueueableMessage;
-import org.apache.qpid.server.model.VirtualHost;
-import org.apache.qpid.server.queue.AMQQueue;
-import org.apache.qpid.server.store.*;
-import org.apache.qpid.server.store.MessageStoreRecoveryHandler.StoredMessageRecoveryHandler;
-import org.apache.qpid.server.store.TransactionLogRecoveryHandler.QueueEntryRecoveryHandler;
-import org.apache.qpid.server.store.berkeleydb.entry.PreparedTransaction;
-import org.apache.qpid.server.store.berkeleydb.entry.QueueEntryKey;
-import org.apache.qpid.server.store.berkeleydb.entry.Xid;
-import org.apache.qpid.server.store.berkeleydb.tuple.ConfiguredObjectBinding;
-import org.apache.qpid.server.store.berkeleydb.tuple.ContentBinding;
-import org.apache.qpid.server.store.berkeleydb.tuple.MessageMetaDataBinding;
-import org.apache.qpid.server.store.berkeleydb.tuple.PreparedTransactionBinding;
-import org.apache.qpid.server.store.berkeleydb.tuple.QueueEntryBinding;
-import org.apache.qpid.server.store.berkeleydb.tuple.UUIDTupleBinding;
-import org.apache.qpid.server.store.berkeleydb.tuple.XidBinding;
-import org.apache.qpid.server.store.berkeleydb.upgrade.Upgrader;
-import org.apache.qpid.util.FileUtils;
-
-public abstract class AbstractBDBMessageStore implements MessageStore, DurableConfigurationStore
-{
- private static final Logger LOGGER = Logger.getLogger(AbstractBDBMessageStore.class);
-
- private static final int LOCK_RETRY_ATTEMPTS = 5;
-
- public static final int VERSION = 7;
-
- private static final Map<String, String> ENVCONFIG_DEFAULTS = Collections.unmodifiableMap(new HashMap<String, String>()
- {{
- put(EnvironmentConfig.LOCK_N_LOCK_TABLES, "7");
- put(EnvironmentConfig.STATS_COLLECT, "false"); // Turn off stats generation - feature introduced (and on by default) from BDB JE 5.0.84
- }});
-
- private final AtomicBoolean _closed = new AtomicBoolean(false);
-
- private Environment _environment;
-
- private static String CONFIGURED_OBJECTS = "CONFIGURED_OBJECTS";
- private static String MESSAGEMETADATADB_NAME = "MESSAGE_METADATA";
- private static String MESSAGECONTENTDB_NAME = "MESSAGE_CONTENT";
- private static String DELIVERYDB_NAME = "QUEUE_ENTRIES";
- private static String BRIDGEDB_NAME = "BRIDGES";
- private static String LINKDB_NAME = "LINKS";
- private static String XIDDB_NAME = "XIDS";
- private static String CONFIG_VERSION_DB = "CONFIG_VERSION";
-
- private Database _configuredObjectsDb;
- private Database _configVersionDb;
- private Database _messageMetaDataDb;
- private Database _messageContentDb;
- private Database _deliveryDb;
- private Database _bridgeDb;
- private Database _linkDb;
- private Database _xidDb;
-
- /* =======
- * 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 final AtomicLong _messageId = new AtomicLong(0);
-
- protected final StateManager _stateManager;
-
- private MessageStoreRecoveryHandler _messageRecoveryHandler;
-
- private TransactionLogRecoveryHandler _tlogRecoveryHandler;
-
- private ConfigurationRecoveryHandler _configRecoveryHandler;
-
- private long _totalStoreSize;
- private boolean _limitBusted;
- private long _persistentSizeLowThreshold;
- private long _persistentSizeHighThreshold;
-
- private final EventManager _eventManager = new EventManager();
- private String _storeLocation;
-
- private Map<String, String> _envConfigMap;
- private VirtualHost _virtualHost;
-
- public AbstractBDBMessageStore()
- {
- _stateManager = new StateManager(_eventManager);
- }
-
- @Override
- public void addEventListener(EventListener eventListener, Event... events)
- {
- _eventManager.addEventListener(eventListener, events);
- }
-
- public void configureConfigStore(VirtualHost virtualHost, ConfigurationRecoveryHandler recoveryHandler) throws Exception
- {
- _stateManager.attainState(State.INITIALISING);
-
- _configRecoveryHandler = recoveryHandler;
- _virtualHost = virtualHost;
- }
-
- public void configureMessageStore(VirtualHost virtualHost, MessageStoreRecoveryHandler messageRecoveryHandler,
- TransactionLogRecoveryHandler tlogRecoveryHandler) throws Exception
- {
- if(_stateManager.isInState(State.INITIAL))
- {
- // Is acting as a message store, but not a durable config store
- _stateManager.attainState(State.INITIALISING);
- }
-
- _messageRecoveryHandler = messageRecoveryHandler;
- _tlogRecoveryHandler = tlogRecoveryHandler;
- _virtualHost = virtualHost;
-
- completeInitialisation();
- }
-
- private void completeInitialisation() throws Exception
- {
- configure(_virtualHost);
-
- _stateManager.attainState(State.INITIALISED);
- }
-
- public synchronized void activate() throws Exception
- {
- // check if acting as a durable config store, but not a message store
- if(_stateManager.isInState(State.INITIALISING))
- {
- completeInitialisation();
- }
- _stateManager.attainState(State.ACTIVATING);
-
- if(_configRecoveryHandler != null)
- {
- recoverConfig(_configRecoveryHandler);
- }
- if(_messageRecoveryHandler != null)
- {
- recoverMessages(_messageRecoveryHandler);
- }
- if(_tlogRecoveryHandler != null)
- {
- recoverQueueEntries(_tlogRecoveryHandler);
- }
-
- _stateManager.attainState(State.ACTIVE);
- }
-
- public org.apache.qpid.server.store.Transaction newTransaction()
- {
- return new BDBTransaction();
- }
-
- /**
- * Called after instantiation in order to configure the message store.
- *
- *
- *
- * @param virtualHost 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 void configure(VirtualHost virtualHost) throws Exception
- {
- configure(virtualHost, _messageRecoveryHandler != null);
- }
-
- public void configure(VirtualHost virtualHost, boolean isMessageStore) throws Exception
- {
- String name = virtualHost.getName();
- final String defaultPath = System.getProperty("QPID_WORK") + File.separator + "bdbstore" + File.separator + name;
-
- String storeLocation;
- if(isMessageStore)
- {
- storeLocation = (String) virtualHost.getAttribute(VirtualHost.STORE_PATH);
- if(storeLocation == null)
- {
- storeLocation = defaultPath;
- }
- }
- else // we are acting only as the durable config store
- {
- storeLocation = (String) virtualHost.getAttribute(VirtualHost.CONFIG_STORE_PATH);
- if(storeLocation == null)
- {
- storeLocation = defaultPath;
- }
- }
-
- Object overfullAttr = virtualHost.getAttribute(MessageStoreConstants.OVERFULL_SIZE_ATTRIBUTE);
- Object underfullAttr = virtualHost.getAttribute(MessageStoreConstants.UNDERFULL_SIZE_ATTRIBUTE);
-
- _persistentSizeHighThreshold = overfullAttr == null ? -1l :
- overfullAttr instanceof Number ? ((Number) overfullAttr).longValue() : Long.parseLong(overfullAttr.toString());
- _persistentSizeLowThreshold = underfullAttr == null ? _persistentSizeHighThreshold :
- underfullAttr instanceof Number ? ((Number) underfullAttr).longValue() : Long.parseLong(underfullAttr.toString());
-
-
- if(_persistentSizeLowThreshold > _persistentSizeHighThreshold || _persistentSizeLowThreshold < 0l)
- {
- _persistentSizeLowThreshold = _persistentSizeHighThreshold;
- }
-
- File environmentPath = new File(storeLocation);
- 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.");
- }
- }
-
- _storeLocation = storeLocation;
-
- _envConfigMap = new HashMap<String, String>();
- _envConfigMap.putAll(ENVCONFIG_DEFAULTS);
-
- Object bdbEnvConfigAttr = virtualHost.getAttribute("bdbEnvironmentConfig");
- if(bdbEnvConfigAttr instanceof Map)
- {
- _envConfigMap.putAll((Map)bdbEnvConfigAttr);
- }
-
- LOGGER.info("Configuring BDB message store");
-
- setupStore(environmentPath, name);
- }
-
- protected Map<String,String> getConfigMap(Map<String, String> defaultConfig, Configuration config, String prefix) throws ConfigurationException
- {
- final List<Object> argumentNames = config.getList(prefix + ".name");
- final List<Object> argumentValues = config.getList(prefix + ".value");
- final int initialSize = argumentNames.size() + defaultConfig.size();
-
- final Map<String,String> attributes = new HashMap<String,String>(initialSize);
- attributes.putAll(defaultConfig);
-
- for (int i = 0; i < argumentNames.size(); i++)
- {
- final String argName = argumentNames.get(i).toString();
- final String argValue = argumentValues.get(i).toString();
-
- attributes.put(argName, argValue);
- }
-
- return Collections.unmodifiableMap(attributes);
- }
-
- @Override
- public String getStoreLocation()
- {
- return _storeLocation;
- }
-
- /**
- * Move the store state from INITIAL to ACTIVE without actually recovering.
- *
- * 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
- */
- void startWithNoRecover() throws AMQStoreException
- {
- _stateManager.attainState(State.INITIALISING);
- _stateManager.attainState(State.INITIALISED);
- _stateManager.attainState(State.ACTIVATING);
- _stateManager.attainState(State.ACTIVE);
- }
-
- protected void setupStore(File storePath, String name) throws DatabaseException, AMQStoreException
- {
- _environment = createEnvironment(storePath);
-
- new Upgrader(_environment, name).upgradeIfNecessary();
-
- openDatabases();
-
- _totalStoreSize = getSizeOnDisk();
- }
-
- protected abstract Environment createEnvironment(File environmentPath) throws DatabaseException;
-
- public Environment getEnvironment()
- {
- return _environment;
- }
-
- private void openDatabases() throws DatabaseException
- {
- DatabaseConfig dbConfig = new DatabaseConfig();
- dbConfig.setTransactional(true);
- dbConfig.setAllowCreate(true);
-
- //This is required if we are wanting read only access.
- dbConfig.setReadOnly(false);
-
- _configuredObjectsDb = openDatabase(CONFIGURED_OBJECTS, dbConfig);
- _configVersionDb = openDatabase(CONFIG_VERSION_DB, dbConfig);
- _messageMetaDataDb = openDatabase(MESSAGEMETADATADB_NAME, dbConfig);
- _messageContentDb = openDatabase(MESSAGECONTENTDB_NAME, dbConfig);
- _deliveryDb = openDatabase(DELIVERYDB_NAME, dbConfig);
- _linkDb = openDatabase(LINKDB_NAME, dbConfig);
- _bridgeDb = openDatabase(BRIDGEDB_NAME, dbConfig);
- _xidDb = openDatabase(XIDDB_NAME, dbConfig);
- }
-
- private Database openDatabase(final String dbName, final DatabaseConfig dbConfig)
- {
- // if opening read-only and the database doesn't exist, then you can't create it
- return dbConfig.getReadOnly() && !_environment.getDatabaseNames().contains(dbName)
- ? null
- : _environment.openDatabase(null, dbName, 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 (_closed.compareAndSet(false, true))
- {
- _stateManager.attainState(State.CLOSING);
- closeInternal();
- _stateManager.attainState(State.CLOSED);
- }
- }
-
- protected void closeInternal() throws Exception
- {
- if (_messageMetaDataDb != null)
- {
- LOGGER.info("Closing message metadata database");
- _messageMetaDataDb.close();
- }
-
- if (_messageContentDb != null)
- {
- LOGGER.info("Closing message content database");
- _messageContentDb.close();
- }
-
- if (_configuredObjectsDb != null)
- {
- LOGGER.info("Closing configurable objects database");
- _configuredObjectsDb.close();
- }
-
- if (_deliveryDb != null)
- {
- LOGGER.info("Close delivery database");
- _deliveryDb.close();
- }
-
- if (_bridgeDb != null)
- {
- LOGGER.info("Close bridge database");
- _bridgeDb.close();
- }
-
- if (_linkDb != null)
- {
- LOGGER.info("Close link database");
- _linkDb.close();
- }
-
-
- if (_xidDb != null)
- {
- LOGGER.info("Close xid database");
- _xidDb.close();
- }
-
-
- if (_configVersionDb != null)
- {
- LOGGER.info("Close config version database");
- _configVersionDb.close();
- }
-
- closeEnvironment();
-
- }
-
- private void closeEnvironment() throws DatabaseException
- {
- if (_environment != null)
- {
- // 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.
- try
- {
- _environment.cleanLog();
- }
- finally
- {
- _environment.close();
- }
- }
- }
-
-
- private void recoverConfig(ConfigurationRecoveryHandler recoveryHandler) throws AMQStoreException
- {
- try
- {
- final int configVersion = getConfigVersion();
- recoveryHandler.beginConfigurationRecovery(this, configVersion);
- loadConfiguredObjects(recoveryHandler);
-
- final int newConfigVersion = recoveryHandler.completeConfigurationRecovery();
- if(newConfigVersion != configVersion)
- {
- updateConfigVersion(newConfigVersion);
- }
- }
- catch (DatabaseException e)
- {
- throw new AMQStoreException("Error recovering persistent state: " + e.getMessage(), e);
- }
-
- }
-
- private void updateConfigVersion(int newConfigVersion) throws AMQStoreException
- {
- Cursor cursor = null;
- try
- {
- Transaction txn = _environment.beginTransaction(null, null);
- cursor = _configVersionDb.openCursor(txn, null);
- DatabaseEntry key = new DatabaseEntry();
- ByteBinding.byteToEntry((byte) 0,key);
- DatabaseEntry value = new DatabaseEntry();
-
- while (cursor.getNext(key, value, LockMode.RMW) == OperationStatus.SUCCESS)
- {
- IntegerBinding.intToEntry(newConfigVersion, value);
- OperationStatus status = cursor.put(key, value);
- if (status != OperationStatus.SUCCESS)
- {
- throw new AMQStoreException("Error setting config version: " + status);
- }
- }
- cursor.close();
- cursor = null;
- txn.commit();
- }
- finally
- {
- closeCursorSafely(cursor);
- }
-
- }
-
- private int getConfigVersion() throws AMQStoreException
- {
- Cursor cursor = null;
- try
- {
- cursor = _configVersionDb.openCursor(null, null);
- DatabaseEntry key = new DatabaseEntry();
- DatabaseEntry value = new DatabaseEntry();
- while (cursor.getNext(key, value, LockMode.RMW) == OperationStatus.SUCCESS)
- {
- return IntegerBinding.entryToInt(value);
- }
-
- // Insert 0 as the default config version
- IntegerBinding.intToEntry(0,value);
- ByteBinding.byteToEntry((byte) 0,key);
- OperationStatus status = _configVersionDb.put(null, key, value);
- if (status != OperationStatus.SUCCESS)
- {
- throw new AMQStoreException("Error initialising config version: " + status);
- }
- return 0;
- }
- finally
- {
- closeCursorSafely(cursor);
- }
- }
-
- private void loadConfiguredObjects(ConfigurationRecoveryHandler crh) throws DatabaseException
- {
- Cursor cursor = null;
- try
- {
- cursor = _configuredObjectsDb.openCursor(null, null);
- DatabaseEntry key = new DatabaseEntry();
- DatabaseEntry value = new DatabaseEntry();
- while (cursor.getNext(key, value, LockMode.RMW) == OperationStatus.SUCCESS)
- {
- UUID id = UUIDTupleBinding.getInstance().entryToObject(key);
-
- ConfiguredObjectRecord configuredObject = new ConfiguredObjectBinding(id).entryToObject(value);
- crh.configuredObject(configuredObject.getId(),configuredObject.getType(),configuredObject.getAttributes());
- }
-
- }
- finally
- {
- closeCursorSafely(cursor);
- }
- }
-
- private void closeCursorSafely(Cursor cursor)
- {
- 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();
- DatabaseEntry value = new DatabaseEntry();
- MessageMetaDataBinding valueBinding = MessageMetaDataBinding.getInstance();
-
- long maxId = 0;
-
- while (cursor.getNext(key, value, LockMode.RMW) == OperationStatus.SUCCESS)
- {
- long messageId = LongBinding.entryToLong(key);
- StorableMessageMetaData metaData = valueBinding.entryToObject(value);
-
- StoredBDBMessage message = new StoredBDBMessage(messageId, metaData, true);
-
- mrh.message(message);
-
- maxId = Math.max(maxId, messageId);
- }
-
- _messageId.set(maxId);
- }
- catch (DatabaseException e)
- {
- LOGGER.error("Database Error: " + e.getMessage(), e);
- throw e;
- }
- finally
- {
- closeCursorSafely(cursor);
- }
- }
-
- 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();
- QueueEntryBinding keyBinding = QueueEntryBinding.getInstance();
-
- DatabaseEntry value = new DatabaseEntry();
- while (cursor.getNext(key, value, LockMode.RMW) == OperationStatus.SUCCESS)
- {
- QueueEntryKey qek = keyBinding.entryToObject(key);
-
- entries.add(qek);
- }
-
- try
- {
- cursor.close();
- }
- finally
- {
- cursor = null;
- }
-
- for(QueueEntryKey entry : entries)
- {
- UUID queueId = entry.getQueueId();
- long messageId = entry.getMessageId();
- qerh.queueEntry(queueId, messageId);
- }
- }
- catch (DatabaseException e)
- {
- LOGGER.error("Database Error: " + e.getMessage(), e);
- throw e;
- }
- finally
- {
- closeCursorSafely(cursor);
- }
-
- TransactionLogRecoveryHandler.DtxRecordRecoveryHandler dtxrh = qerh.completeQueueEntryRecovery();
-
- cursor = null;
- try
- {
- cursor = _xidDb.openCursor(null, null);
- DatabaseEntry key = new DatabaseEntry();
- XidBinding keyBinding = XidBinding.getInstance();
- PreparedTransactionBinding valueBinding = new PreparedTransactionBinding();
- DatabaseEntry value = new DatabaseEntry();
-
- while (cursor.getNext(key, value, LockMode.RMW) == OperationStatus.SUCCESS)
- {
- Xid xid = keyBinding.entryToObject(key);
- PreparedTransaction preparedTransaction = valueBinding.entryToObject(value);
- dtxrh.dtxRecord(xid.getFormat(),xid.getGlobalId(),xid.getBranchId(),
- preparedTransaction.getEnqueues(),preparedTransaction.getDequeues());
- }
-
- }
- catch (DatabaseException e)
- {
- LOGGER.error("Database Error: " + e.getMessage(), e);
- throw e;
- }
- finally
- {
- closeCursorSafely(cursor);
- }
-
-
- dtxrh.completeDtxRecordRecovery();
- }
-
- public void removeMessage(long messageId, boolean sync) throws AMQStoreException
- {
-
- boolean complete = false;
- com.sleepycat.je.Transaction tx = null;
-
- Random rand = null;
- int attempts = 0;
- try
- {
- do
- {
- tx = null;
- try
- {
- tx = _environment.beginTransaction(null, null);
-
- //remove the message meta data from the store
- DatabaseEntry key = new DatabaseEntry();
- LongBinding.longToEntry(messageId, key);
-
- if (LOGGER.isDebugEnabled())
- {
- LOGGER.debug("Removing message id " + messageId);
- }
-
-
- OperationStatus status = _messageMetaDataDb.delete(tx, key);
- if (status == OperationStatus.NOTFOUND)
- {
- LOGGER.info("Message not found (attempt to remove failed - probably application initiated rollback) " +
- messageId);
- }
-
- if (LOGGER.isDebugEnabled())
- {
- LOGGER.debug("Deleted metadata for message " + messageId);
- }
-
- //now remove the content data from the store if there is any.
- DatabaseEntry contentKeyEntry = new DatabaseEntry();
- LongBinding.longToEntry(messageId, contentKeyEntry);
- _messageContentDb.delete(tx, contentKeyEntry);
-
- if (LOGGER.isDebugEnabled())
- {
- LOGGER.debug("Deleted content for message " + messageId);
- }
-
- commit(tx, sync);
- complete = true;
- tx = null;
- }
- catch (LockConflictException e)
- {
- try
- {
- if(tx != null)
- {
- tx.abort();
- }
- }
- catch(DatabaseException e2)
- {
- LOGGER.warn("Unable to abort transaction after LockConflictExcption", e2);
- // rethrow the original log conflict exception, the secondary exception should already have
- // been logged.
- throw e;
- }
-
-
- LOGGER.warn("Lock timeout exception. Retrying (attempt "
- + (attempts+1) + " of "+ LOCK_RETRY_ATTEMPTS +") " + e);
-
- if(++attempts < LOCK_RETRY_ATTEMPTS)
- {
- if(rand == null)
- {
- rand = new Random();
- }
-
- try
- {
- Thread.sleep(500l + (long)(500l * rand.nextDouble()));
- }
- catch (InterruptedException e1)
- {
-
- }
- }
- else
- {
- // rethrow the lock conflict exception since we could not solve by retrying
- throw e;
- }
- }
- }
- while(!complete);
- }
- catch (DatabaseException e)
- {
- LOGGER.error("Unexpected BDB exception", e);
-
- if (tx != null)
- {
- try
- {
- tx.abort();
- tx = null;
- }
- 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 (tx != null)
- {
- try
- {
- tx.abort();
- tx = null;
- }
- catch (DatabaseException e1)
- {
- throw new AMQStoreException("Error aborting transaction " + e1, e1);
- }
- }
- }
- }
-
- @Override
- public void create(UUID id, String type, Map<String, Object> attributes) throws AMQStoreException
- {
- if (_stateManager.isInState(State.ACTIVE))
- {
- ConfiguredObjectRecord configuredObject = new ConfiguredObjectRecord(id, type, attributes);
- storeConfiguredObjectEntry(configuredObject);
- }
- }
-
- @Override
- public void remove(UUID id, String type) throws AMQStoreException
- {
- if (LOGGER.isDebugEnabled())
- {
- LOGGER.debug("public void remove(id = " + id + ", type="+type+"): called");
- }
- OperationStatus status = removeConfiguredObject(null, id);
- if (status == OperationStatus.NOTFOUND)
- {
- throw new AMQStoreException("Configured object of type " + type + " with id " + id + " not found");
- }
- }
-
- @Override
- public UUID[] removeConfiguredObjects(final UUID... objects) throws AMQStoreException
- {
- com.sleepycat.je.Transaction txn = _environment.beginTransaction(null, null);
- Collection<UUID> removed = new ArrayList<UUID>(objects.length);
- for(UUID id : objects)
- {
- if(removeConfiguredObject(txn, id) == OperationStatus.SUCCESS)
- {
- removed.add(id);
- }
- }
-
- txn.commit();
- return removed.toArray(new UUID[removed.size()]);
- }
-
- @Override
- public void update(UUID id, String type, Map<String, Object> attributes) throws AMQStoreException
- {
- update(false, id, type, attributes, null);
- }
-
- public void update(ConfiguredObjectRecord... records) throws AMQStoreException
- {
- update(false, records);
- }
-
- public void update(boolean createIfNecessary, ConfiguredObjectRecord... records) throws AMQStoreException
- {
- com.sleepycat.je.Transaction txn = _environment.beginTransaction(null, null);
- for(ConfiguredObjectRecord record : records)
- {
- update(createIfNecessary, record.getId(), record.getType(), record.getAttributes(), txn);
- }
- txn.commit();
- }
-
- private void update(boolean createIfNecessary, UUID id, String type, Map<String, Object> attributes, com.sleepycat.je.Transaction txn) throws AMQStoreException
- {
- if (LOGGER.isDebugEnabled())
- {
- LOGGER.debug("Updating " +type + ", id: " + id);
- }
-
- try
- {
- DatabaseEntry key = new DatabaseEntry();
- UUIDTupleBinding keyBinding = UUIDTupleBinding.getInstance();
- keyBinding.objectToEntry(id, key);
-
- DatabaseEntry value = new DatabaseEntry();
- DatabaseEntry newValue = new DatabaseEntry();
- ConfiguredObjectBinding configuredObjectBinding = ConfiguredObjectBinding.getInstance();
-
- OperationStatus status = _configuredObjectsDb.get(txn, key, value, LockMode.DEFAULT);
- if (status == OperationStatus.SUCCESS || (createIfNecessary && status == OperationStatus.NOTFOUND))
- {
- ConfiguredObjectRecord newQueueRecord = new ConfiguredObjectRecord(id, type, attributes);
-
- // write the updated entry to the store
- configuredObjectBinding.objectToEntry(newQueueRecord, newValue);
- status = _configuredObjectsDb.put(txn, key, newValue);
- if (status != OperationStatus.SUCCESS)
- {
- throw new AMQStoreException("Error updating queue details within the store: " + status);
- }
- }
- else if (status != OperationStatus.NOTFOUND)
- {
- throw new AMQStoreException("Error finding queue details within the store: " + status);
- }
- }
- catch (DatabaseException e)
- {
- throw new AMQStoreException("Error updating queue details within the store: " + e,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
- {
-
- DatabaseEntry key = new DatabaseEntry();
- QueueEntryBinding keyBinding = QueueEntryBinding.getInstance();
- QueueEntryKey dd = new QueueEntryKey(queue.getId(), messageId);
- keyBinding.objectToEntry(dd, key);
- DatabaseEntry value = new DatabaseEntry();
- ByteBinding.byteToEntry((byte) 0, value);
-
- try
- {
- if (LOGGER.isDebugEnabled())
- {
- LOGGER.debug("Enqueuing message " + messageId + " on queue "
- + (queue instanceof AMQQueue ? ((AMQQueue) queue).getName() + " with id " : "") + queue.getId()
- + " in transaction " + tx);
- }
- _deliveryDb.put(tx, key, value);
- }
- catch (DatabaseException e)
- {
- LOGGER.error("Failed to enqueue: " + e.getMessage(), e);
- throw new AMQStoreException("Error writing enqueued message with id " + messageId + " for queue "
- + (queue instanceof AMQQueue ? ((AMQQueue) queue).getName() + " with id " : "") + queue.getId()
- + " to database", e);
- }
- }
-
- /**
- * Extracts a message from a specified queue, in a given transaction.
- *
- * @param tx The transaction for the operation.
- * @param queue The 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
- {
-
- DatabaseEntry key = new DatabaseEntry();
- QueueEntryBinding keyBinding = QueueEntryBinding.getInstance();
- QueueEntryKey queueEntryKey = new QueueEntryKey(queue.getId(), messageId);
- UUID id = queue.getId();
- keyBinding.objectToEntry(queueEntryKey, key);
- if (LOGGER.isDebugEnabled())
- {
- LOGGER.debug("Dequeue message id " + messageId + " from queue "
- + (queue instanceof AMQQueue ? ((AMQQueue) queue).getName() + " with id " : "") + id);
- }
-
- try
- {
-
- OperationStatus status = _deliveryDb.delete(tx, key);
- if (status == OperationStatus.NOTFOUND)
- {
- throw new AMQStoreException("Unable to find message with id " + messageId + " on queue "
- + (queue instanceof AMQQueue ? ((AMQQueue) queue).getName() + " with id " : "") + id);
- }
- else if (status != OperationStatus.SUCCESS)
- {
- throw new AMQStoreException("Unable to remove message with id " + messageId + " on queue"
- + (queue instanceof AMQQueue ? ((AMQQueue) queue).getName() + " with id " : "") + id);
- }
-
- if (LOGGER.isDebugEnabled())
- {
- LOGGER.debug("Removed message " + messageId + " on queue "
- + (queue instanceof AMQQueue ? ((AMQQueue) queue).getName() + " with id " : "") + id
- + " from delivery db");
-
- }
- }
- catch (DatabaseException e)
- {
-
- LOGGER.error("Failed to dequeue message " + messageId + ": " + e.getMessage(), e);
- LOGGER.error(tx);
-
- throw new AMQStoreException("Error accessing database while dequeuing message: " + e.getMessage(), e);
- }
- }
-
-
- private void recordXid(com.sleepycat.je.Transaction txn,
- long format,
- byte[] globalId,
- byte[] branchId,
- org.apache.qpid.server.store.Transaction.Record[] enqueues,
- org.apache.qpid.server.store.Transaction.Record[] dequeues) throws AMQStoreException
- {
- DatabaseEntry key = new DatabaseEntry();
- Xid xid = new Xid(format, globalId, branchId);
- XidBinding keyBinding = XidBinding.getInstance();
- keyBinding.objectToEntry(xid,key);
-
- DatabaseEntry value = new DatabaseEntry();
- PreparedTransaction preparedTransaction = new PreparedTransaction(enqueues, dequeues);
- PreparedTransactionBinding valueBinding = new PreparedTransactionBinding();
- valueBinding.objectToEntry(preparedTransaction, value);
-
- try
- {
- _xidDb.put(txn, key, value);
- }
- catch (DatabaseException e)
- {
- LOGGER.error("Failed to write xid: " + e.getMessage(), e);
- throw new AMQStoreException("Error writing xid to database", e);
- }
- }
-
- private void removeXid(com.sleepycat.je.Transaction txn, long format, byte[] globalId, byte[] branchId)
- throws AMQStoreException
- {
- DatabaseEntry key = new DatabaseEntry();
- Xid xid = new Xid(format, globalId, branchId);
- XidBinding keyBinding = XidBinding.getInstance();
-
- keyBinding.objectToEntry(xid, key);
-
-
- try
- {
-
- OperationStatus status = _xidDb.delete(txn, key);
- if (status == OperationStatus.NOTFOUND)
- {
- throw new AMQStoreException("Unable to find xid");
- }
- else if (status != OperationStatus.SUCCESS)
- {
- throw new AMQStoreException("Unable to remove xid");
- }
-
- }
- catch (DatabaseException e)
- {
-
- LOGGER.error("Failed to remove xid ", e);
- LOGGER.error(txn);
-
- throw new AMQStoreException("Error accessing database while removing xid: " + 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 (tx == null)
- {
- throw new AMQStoreException("Fatal internal error: transactional is null at commitTran");
- }
-
- StoreFuture result;
- try
- {
- result = commit(tx, syncCommit);
-
- if (LOGGER.isDebugEnabled())
- {
- String transactionType = syncCommit ? "synchronous" : "asynchronous";
- LOGGER.debug("commitTranImpl completed " + transactionType + " 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 (LOGGER.isDebugEnabled())
- {
- LOGGER.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 queueId
- *
- * @return a list of message ids for messages enqueued for a particular queue
- */
- List<Long> getEnqueuedMessages(UUID queueId) throws AMQStoreException
- {
- Cursor cursor = null;
- try
- {
- cursor = _deliveryDb.openCursor(null, null);
-
- DatabaseEntry key = new DatabaseEntry();
-
- QueueEntryKey dd = new QueueEntryKey(queueId, 0);
-
- QueueEntryBinding keyBinding = QueueEntryBinding.getInstance();
- keyBinding.objectToEntry(dd, key);
-
- DatabaseEntry value = new DatabaseEntry();
-
- LinkedList<Long> messageIds = new LinkedList<Long>();
-
- OperationStatus status = cursor.getSearchKeyRange(key, value, LockMode.DEFAULT);
- dd = keyBinding.entryToObject(key);
-
- while ((status == OperationStatus.SUCCESS) && dd.getQueueId().equals(queueId))
- {
-
- messageIds.add(dd.getMessageId());
- status = cursor.getNext(key, value, LockMode.DEFAULT);
- if (status == OperationStatus.SUCCESS)
- {
- dd = 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();
- LongBinding.longToEntry(messageId, key);
- DatabaseEntry value = new DatabaseEntry();
- ContentBinding messageBinding = ContentBinding.getInstance();
- messageBinding.objectToEntry(contentBody.array(), value);
- try
- {
- OperationStatus status = _messageContentDb.put(tx, key, value);
- if (status != OperationStatus.SUCCESS)
- {
- throw new AMQStoreException("Error adding content for message id " + messageId + ": " + status);
- }
-
- if (LOGGER.isDebugEnabled())
- {
- LOGGER.debug("Storing content for message " + messageId + " in 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 (LOGGER.isDebugEnabled())
- {
- LOGGER.debug("storeMetaData called for transaction " + tx
- + ", messageId " + messageId
- + ", messageMetaData " + messageMetaData);
- }
-
- DatabaseEntry key = new DatabaseEntry();
- LongBinding.longToEntry(messageId, key);
- DatabaseEntry value = new DatabaseEntry();
-
- MessageMetaDataBinding messageBinding = MessageMetaDataBinding.getInstance();
- messageBinding.objectToEntry(messageMetaData, value);
- try
- {
- _messageMetaDataDb.put(tx, key, value);
- if (LOGGER.isDebugEnabled())
- {
- LOGGER.debug("Storing message metadata for message id " + messageId + " in 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 (LOGGER.isDebugEnabled())
- {
- LOGGER.debug("public MessageMetaData getMessageMetaData(Long messageId = "
- + messageId + "): called");
- }
-
- DatabaseEntry key = new DatabaseEntry();
- LongBinding.longToEntry(messageId, key);
- DatabaseEntry value = new DatabaseEntry();
- MessageMetaDataBinding messageBinding = MessageMetaDataBinding.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 = 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();
- LongBinding.longToEntry(messageId, contentKeyEntry);
- DatabaseEntry value = new DatabaseEntry();
- ContentBinding contentTupleBinding = ContentBinding.getInstance();
-
-
- if (LOGGER.isDebugEnabled())
- {
- LOGGER.debug("Message Id: " + messageId + " Getting content body from offset: " + offset);
- }
-
- try
- {
-
- int written = 0;
- OperationStatus status = _messageContentDb.get(null, contentKeyEntry, value, LockMode.READ_UNCOMMITTED);
- if (status == OperationStatus.SUCCESS)
- {
- byte[] dataAsBytes = contentTupleBinding.entryToObject(value);
- int size = dataAsBytes.length;
- if (offset > size)
- {
- throw new RuntimeException("Offset " + offset + " is greater than message size " + size
- + " for message id " + messageId + "!");
-
- }
-
- written = size - offset;
- if(written > dst.remaining())
- {
- written = dst.remaining();
- }
-
- dst.put(dataAsBytes, offset, written);
- }
- return written;
- }
- catch (DatabaseException e)
- {
- throw new AMQStoreException("Error getting AMQMessage with id " + messageId + " to database: " + e.getMessage(), e);
- }
- }
-
- public boolean isPersistent()
- {
- return true;
- }
-
- @SuppressWarnings("unchecked")
- public <T extends StorableMessageMetaData> StoredMessage<T> addMessage(T metaData)
- {
- if(metaData.isPersistent())
- {
- return (StoredMessage<T>) new StoredBDBMessage(getNewMessageId(), metaData);
- }
- else
- {
- return new StoredMemoryMessage(getNewMessageId(), metaData);
- }
- }
-
- //Package getters for the various databases used by the Store
-
- Database getMetaDataDb()
- {
- return _messageMetaDataDb;
- }
-
- Database getContentDb()
- {
- return _messageContentDb;
- }
-
- Database getDeliveryDb()
- {
- return _deliveryDb;
- }
-
- /**
- * Makes the specified configured object persistent.
- *
- * @param configuredObject Details of the configured object to store.
- * @throws AMQStoreException If the operation fails for any reason.
- */
- private void storeConfiguredObjectEntry(ConfiguredObjectRecord configuredObject) throws AMQStoreException
- {
- if (_stateManager.isInState(State.ACTIVE))
- {
- LOGGER.debug("Storing configured object: " + configuredObject);
- DatabaseEntry key = new DatabaseEntry();
- UUIDTupleBinding keyBinding = UUIDTupleBinding.getInstance();
- keyBinding.objectToEntry(configuredObject.getId(), key);
-
- DatabaseEntry value = new DatabaseEntry();
- ConfiguredObjectBinding queueBinding = ConfiguredObjectBinding.getInstance();
-
- queueBinding.objectToEntry(configuredObject, value);
- try
- {
- OperationStatus status = _configuredObjectsDb.put(null, key, value);
- if (status != OperationStatus.SUCCESS)
- {
- throw new AMQStoreException("Error writing configured object " + configuredObject + " to database: "
- + status);
- }
- }
- catch (DatabaseException e)
- {
- throw new AMQStoreException("Error writing configured object " + configuredObject
- + " to database: " + e.getMessage(), e);
- }
- }
- }
-
- private OperationStatus removeConfiguredObject(Transaction tx, UUID id) throws AMQStoreException
- {
-
- LOGGER.debug("Removing configured object: " + id);
- DatabaseEntry key = new DatabaseEntry();
- UUIDTupleBinding uuidBinding = UUIDTupleBinding.getInstance();
- uuidBinding.objectToEntry(id, key);
- try
- {
- return _configuredObjectsDb.delete(tx, key);
- }
- catch (DatabaseException e)
- {
- throw new AMQStoreException("Error deleting of configured object with id " + id + " from database", e);
- }
- }
-
- protected abstract StoreFuture commit(com.sleepycat.je.Transaction tx, boolean syncCommit) throws DatabaseException;
-
-
- private class StoredBDBMessage implements StoredMessage<StorableMessageMetaData>
- {
-
- private final long _messageId;
- private final boolean _isRecovered;
-
- private StorableMessageMetaData _metaData;
- private volatile SoftReference<StorableMessageMetaData> _metaDataRef;
-
- private byte[] _data;
- private volatile SoftReference<byte[]> _dataRef;
-
- StoredBDBMessage(long messageId, StorableMessageMetaData metaData)
- {
- this(messageId, metaData, false);
- }
-
- StoredBDBMessage(long messageId, StorableMessageMetaData metaData, boolean isRecovered)
- {
- _messageId = messageId;
- _isRecovered = isRecovered;
-
- if(!_isRecovered)
- {
- _metaData = metaData;
- }
- _metaDataRef = new SoftReference<StorableMessageMetaData>(metaData);
- }
-
- public StorableMessageMetaData getMetaData()
- {
- StorableMessageMetaData metaData = _metaDataRef.get();
- if(metaData == null)
- {
- try
- {
- metaData = AbstractBDBMessageStore.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)
- {
- src = src.slice();
-
- if(_data == null)
- {
- _data = new byte[src.remaining()];
- _dataRef = new SoftReference<byte[]>(_data);
- src.duplicate().get(_data);
- }
- else
- {
- byte[] oldData = _data;
- _data = new byte[oldData.length + src.remaining()];
- _dataRef = new SoftReference<byte[]>(_data);
-
- System.arraycopy(oldData,0,_data,0,oldData.length);
- src.duplicate().get(_data, oldData.length, src.remaining());
- }
-
- }
-
- public int getContent(int offsetInMessage, java.nio.ByteBuffer dst)
- {
- byte[] data = _dataRef == null ? null : _dataRef.get();
- if(data != null)
- {
- int length = Math.min(dst.remaining(), data.length - offsetInMessage);
- dst.put(data, offsetInMessage, length);
- return length;
- }
- else
- {
- try
- {
- return AbstractBDBMessageStore.this.getContent(_messageId, offsetInMessage, dst);
- }
- catch (AMQStoreException e)
- {
- // TODO maybe should throw a checked exception, or at least log before throwing
- throw new RuntimeException(e);
- }
- }
- }
-
- public ByteBuffer getContent(int offsetInMessage, int size)
- {
- byte[] data = _dataRef == null ? null : _dataRef.get();
- if(data != null)
- {
- return ByteBuffer.wrap(data,offsetInMessage,size);
- }
- else
- {
- ByteBuffer buf = ByteBuffer.allocate(size);
- int length = getContent(offsetInMessage, buf);
- buf.limit(length);
- buf.position(0);
- return buf;
- }
- }
-
- synchronized void store(com.sleepycat.je.Transaction txn)
- {
- if (!stored())
- {
- try
- {
- _dataRef = new SoftReference<byte[]>(_data);
- AbstractBDBMessageStore.this.storeMetaData(txn, _messageId, _metaData);
- AbstractBDBMessageStore.this.addContent(txn, _messageId, 0,
- _data == null ? ByteBuffer.allocate(0) : ByteBuffer.wrap(_data));
- }
- catch(DatabaseException e)
- {
- throw new RuntimeException(e);
- }
- catch (AMQStoreException e)
- {
- throw new RuntimeException(e);
- }
- catch (RuntimeException e)
- {
- LOGGER.error("RuntimeException during store", e);
- throw e;
- }
- finally
- {
- _metaData = null;
- _data = null;
- }
- }
- }
-
- public synchronized StoreFuture flushToStore()
- {
- if(!stored())
- {
- com.sleepycat.je.Transaction txn = _environment.beginTransaction(null, null);
- store(txn);
- AbstractBDBMessageStore.this.commit(txn,true);
- storedSizeChange(getMetaData().getContentSize());
- }
- return StoreFuture.IMMEDIATE_FUTURE;
- }
-
- public void remove()
- {
- try
- {
- int delta = getMetaData().getContentSize();
- AbstractBDBMessageStore.this.removeMessage(_messageId, false);
- storedSizeChange(-delta);
-
- }
- catch (AMQStoreException e)
- {
- throw new RuntimeException(e);
- }
- }
-
- private boolean stored()
- {
- return _metaData == null || _isRecovered;
- }
- }
-
- private class BDBTransaction implements org.apache.qpid.server.store.Transaction
- {
- private com.sleepycat.je.Transaction _txn;
- private int _storeSizeIncrease;
-
- private BDBTransaction()
- {
- try
- {
- _txn = _environment.beginTransaction(null, null);
- }
- catch (DatabaseException e)
- {
- LOGGER.error("Exception during transaction begin, closing store environment.", e);
- closeEnvironmentSafely();
-
- throw new RuntimeException("Exception during transaction begin, store environment closed.", e);
- }
- }
-
- public void enqueueMessage(TransactionLogResource queue, EnqueueableMessage message) throws AMQStoreException
- {
- if(message.getStoredMessage() instanceof StoredBDBMessage)
- {
- final StoredBDBMessage storedMessage = (StoredBDBMessage) message.getStoredMessage();
- storedMessage.store(_txn);
- _storeSizeIncrease += storedMessage.getMetaData().getContentSize();
- }
-
- AbstractBDBMessageStore.this.enqueueMessage(_txn, queue, message.getMessageNumber());
- }
-
- public void dequeueMessage(TransactionLogResource queue, EnqueueableMessage message) throws AMQStoreException
- {
- AbstractBDBMessageStore.this.dequeueMessage(_txn, queue, message.getMessageNumber());
- }
-
- public void commitTran() throws AMQStoreException
- {
- AbstractBDBMessageStore.this.commitTranImpl(_txn, true);
- AbstractBDBMessageStore.this.storedSizeChange(_storeSizeIncrease);
- }
-
- public StoreFuture commitTranAsync() throws AMQStoreException
- {
- AbstractBDBMessageStore.this.storedSizeChange(_storeSizeIncrease);
- return AbstractBDBMessageStore.this.commitTranImpl(_txn, false);
- }
-
- public void abortTran() throws AMQStoreException
- {
- AbstractBDBMessageStore.this.abortTran(_txn);
- }
-
- public void removeXid(long format, byte[] globalId, byte[] branchId) throws AMQStoreException
- {
- AbstractBDBMessageStore.this.removeXid(_txn, format, globalId, branchId);
- }
-
- public void recordXid(long format, byte[] globalId, byte[] branchId, Record[] enqueues,
- Record[] dequeues) throws AMQStoreException
- {
- AbstractBDBMessageStore.this.recordXid(_txn, format, globalId, branchId, enqueues, dequeues);
- }
- }
-
- private void storedSizeChange(final int delta)
- {
- if(getPersistentSizeHighThreshold() > 0)
- {
- synchronized (this)
- {
- // the delta supplied is an approximation of a store size change. we don;t want to check the statistic every
- // time, so we do so only when there's been enough change that it is worth looking again. We do this by
- // assuming the total size will change by less than twice the amount of the message data change.
- long newSize = _totalStoreSize += 2*delta;
-
- if(!_limitBusted && newSize > getPersistentSizeHighThreshold())
- {
- _totalStoreSize = getSizeOnDisk();
-
- if(_totalStoreSize > getPersistentSizeHighThreshold())
- {
- _limitBusted = true;
- _eventManager.notifyEvent(Event.PERSISTENT_MESSAGE_SIZE_OVERFULL);
- }
- }
- else if(_limitBusted && newSize < getPersistentSizeLowThreshold())
- {
- long oldSize = _totalStoreSize;
- _totalStoreSize = getSizeOnDisk();
-
- if(oldSize <= _totalStoreSize)
- {
-
- reduceSizeOnDisk();
-
- _totalStoreSize = getSizeOnDisk();
-
- }
-
- if(_totalStoreSize < getPersistentSizeLowThreshold())
- {
- _limitBusted = false;
- _eventManager.notifyEvent(Event.PERSISTENT_MESSAGE_SIZE_UNDERFULL);
- }
-
-
- }
- }
- }
- }
-
- private void reduceSizeOnDisk()
- {
- _environment.getConfig().setConfigParam(EnvironmentConfig.ENV_RUN_CLEANER, "false");
- boolean cleaned = false;
- while (_environment.cleanLog() > 0)
- {
- cleaned = true;
- }
- if (cleaned)
- {
- CheckpointConfig force = new CheckpointConfig();
- force.setForce(true);
- _environment.checkpoint(force);
- }
-
-
- _environment.getConfig().setConfigParam(EnvironmentConfig.ENV_RUN_CLEANER, "true");
- }
-
- private long getSizeOnDisk()
- {
- return _environment.getStats(null).getTotalLogSize();
- }
-
- private long getPersistentSizeLowThreshold()
- {
- return _persistentSizeLowThreshold;
- }
-
- private long getPersistentSizeHighThreshold()
- {
- return _persistentSizeHighThreshold;
- }
-
- private void setEnvironmentConfigProperties(EnvironmentConfig envConfig)
- {
- for (Map.Entry<String, String> configItem : _envConfigMap.entrySet())
- {
- LOGGER.debug("Setting EnvironmentConfig key " + configItem.getKey() + " to '" + configItem.getValue() + "'");
- envConfig.setConfigParam(configItem.getKey(), configItem.getValue());
- }
- }
-
- protected EnvironmentConfig createEnvironmentConfig()
- {
- EnvironmentConfig envConfig = new EnvironmentConfig();
- envConfig.setAllowCreate(true);
- envConfig.setTransactional(true);
-
- setEnvironmentConfigProperties(envConfig);
-
- envConfig.setExceptionListener(new LoggingAsyncExceptionListener());
-
- return envConfig;
- }
-
- protected void closeEnvironmentSafely()
- {
- try
- {
- _environment.close();
- }
- catch (DatabaseException ex)
- {
- LOGGER.error("Exception closing store environment", ex);
- }
- catch (IllegalStateException ex)
- {
- LOGGER.error("Exception closing store environment", ex);
- }
- }
-
-
- private class LoggingAsyncExceptionListener implements ExceptionListener
- {
- @Override
- public void exceptionThrown(ExceptionEvent event)
- {
- LOGGER.error("Asynchronous exception thrown by BDB thread '"
- + event.getThreadName() + "'", event.getException());
- }
- }
-
- @Override
- public void onDelete()
- {
- if (LOGGER.isDebugEnabled())
- {
- LOGGER.debug("Deleting store " + _storeLocation);
- }
-
- if (_storeLocation != null)
- {
- File location = new File(_storeLocation);
- if (location.exists())
- {
- if (!FileUtils.delete(location, true))
- {
- LOGGER.error("Cannot delete " + _storeLocation);
- }
- }
- }
- }
-}
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
index 9b97fec479..5a498470fb 100644
--- 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
@@ -22,7 +22,6 @@ 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;
@@ -336,17 +335,4 @@ public class BDBBackup
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/BDBHAMessageStore.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBHAMessageStore.java
deleted file mode 100644
index fb1dc1f1d3..0000000000
--- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBHAMessageStore.java
+++ /dev/null
@@ -1,658 +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.server.store.berkeleydb;
-
-import java.io.File;
-import java.net.InetSocketAddress;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-
-import org.apache.commons.configuration.Configuration;
-import org.apache.commons.configuration.ConfigurationException;
-import org.apache.log4j.Logger;
-import org.apache.qpid.AMQStoreException;
-import org.apache.qpid.server.logging.RootMessageLogger;
-import org.apache.qpid.server.logging.actors.AbstractActor;
-import org.apache.qpid.server.logging.actors.CurrentActor;
-import org.apache.qpid.server.model.VirtualHost;
-import org.apache.qpid.server.store.HAMessageStore;
-import org.apache.qpid.server.store.MessageStore;
-import org.apache.qpid.server.store.MessageStoreRecoveryHandler;
-import org.apache.qpid.server.store.State;
-import org.apache.qpid.server.store.StoreFuture;
-import org.apache.qpid.server.store.TransactionLogRecoveryHandler;
-
-import com.sleepycat.je.DatabaseException;
-import com.sleepycat.je.Durability;
-import com.sleepycat.je.Durability.ReplicaAckPolicy;
-import com.sleepycat.je.Durability.SyncPolicy;
-import com.sleepycat.je.Environment;
-import com.sleepycat.je.EnvironmentConfig;
-import com.sleepycat.je.OperationFailureException;
-import com.sleepycat.je.Transaction;
-import com.sleepycat.je.rep.InsufficientLogException;
-import com.sleepycat.je.rep.NetworkRestore;
-import com.sleepycat.je.rep.NetworkRestoreConfig;
-import com.sleepycat.je.rep.ReplicatedEnvironment;
-import com.sleepycat.je.rep.ReplicationConfig;
-import com.sleepycat.je.rep.ReplicationMutableConfig;
-import com.sleepycat.je.rep.ReplicationNode;
-import com.sleepycat.je.rep.StateChangeEvent;
-import com.sleepycat.je.rep.StateChangeListener;
-import com.sleepycat.je.rep.util.ReplicationGroupAdmin;
-
-public class BDBHAMessageStore extends AbstractBDBMessageStore implements HAMessageStore
-{
- private static final Logger LOGGER = Logger.getLogger(BDBHAMessageStore.class);
-
- private static final Durability DEFAULT_DURABILITY = new Durability(SyncPolicy.NO_SYNC, SyncPolicy.NO_SYNC, ReplicaAckPolicy.SIMPLE_MAJORITY);
-
- public static final String GRP_MEM_COL_NODE_HOST_PORT = "NodeHostPort";
- public static final String GRP_MEM_COL_NODE_NAME = "NodeName";
-
- @SuppressWarnings("serial")
- private static final Map<String, String> REPCONFIG_DEFAULTS = Collections.unmodifiableMap(new HashMap<String, String>()
- {{
- /**
- * Parameter decreased as the 24h default may lead very large log files for most users.
- */
- put(ReplicationConfig.REP_STREAM_TIMEOUT, "1 h");
- /**
- * Parameter increased as the 5 s default may lead to spurious timeouts.
- */
- put(ReplicationConfig.REPLICA_ACK_TIMEOUT, "15 s");
- /**
- * Parameter increased as the 10 s default may lead to spurious timeouts.
- */
- put(ReplicationConfig.INSUFFICIENT_REPLICAS_TIMEOUT, "20 s");
- /**
- * Parameter increased as the 10 h default may cause user confusion.
- */
- put(ReplicationConfig.ENV_SETUP_TIMEOUT, "15 min");
- /**
- * Parameter changed from default true so we adopt immediately adopt the new behaviour early. False
- * is scheduled to become default after JE 5.0.48.
- */
- put(ReplicationConfig.PROTOCOL_OLD_STRING_ENCODING, Boolean.FALSE.toString());
- /**
- * Parameter decreased as a default 5min interval may lead to bigger data losses on Node
- * with NO_SYN durability in case if such Node crushes.
- */
- put(ReplicationConfig.LOG_FLUSH_TASK_INTERVAL, "1 min");
- }});
-
- public static final String TYPE = "BDB-HA";
-
- private String _groupName;
- private String _nodeName;
- private String _nodeHostPort;
- private String _helperHostPort;
- private Durability _durability;
-
- private String _name;
-
- private CommitThreadWrapper _commitThreadWrapper;
- private boolean _coalescingSync;
- private boolean _designatedPrimary;
- private Map<String, String> _repConfig;
-
- @Override
- public void configure(VirtualHost virtualHost) throws Exception
- {
- //Mandatory configuration
- _groupName = getValidatedStringAttribute(virtualHost, "haGroupName");
- _nodeName = getValidatedStringAttribute(virtualHost, "haNodeName");
- _nodeHostPort = getValidatedStringAttribute(virtualHost, "haNodeAddress");
- _helperHostPort = getValidatedStringAttribute(virtualHost, "haHelperAddress");
- _name = virtualHost.getName();
-
- //Optional configuration
- String durabilitySetting = getStringAttribute(virtualHost,"haDurability",null);
- if (durabilitySetting == null)
- {
- _durability = DEFAULT_DURABILITY;
- }
- else
- {
- _durability = Durability.parse(durabilitySetting);
- }
- _designatedPrimary = getBooleanAttribute(virtualHost, "haDesignatedPrimary", Boolean.FALSE);
- _coalescingSync = getBooleanAttribute(virtualHost, "haCoalescingSync", Boolean.TRUE);
-
- _repConfig = new HashMap<String, String>(REPCONFIG_DEFAULTS);
- Object repConfigAttr = virtualHost.getAttribute("haReplicationConfig");
- if(repConfigAttr instanceof Map)
- {
- _repConfig.putAll((Map)repConfigAttr);
- }
-
- if (_coalescingSync && _durability.getLocalSync() == SyncPolicy.SYNC)
- {
- throw new ConfigurationException("Coalescing sync cannot be used with master sync policy " + SyncPolicy.SYNC
- + "! Please set highAvailability.coalescingSync to false in store configuration.");
- }
-
- super.configure(virtualHost);
- }
-
-
- private String getValidatedStringAttribute(org.apache.qpid.server.model.VirtualHost virtualHost, String attributeName)
- throws ConfigurationException
- {
- Object attrValue = virtualHost.getAttribute(attributeName);
- if(attrValue != null)
- {
- return attrValue.toString();
- }
- else
- {
- throw new ConfigurationException("BDB HA configuration key not found. Please specify configuration attribute: "
- + attributeName);
- }
- }
-
- private String getStringAttribute(org.apache.qpid.server.model.VirtualHost virtualHost, String attributeName, String defaultVal)
- {
- Object attrValue = virtualHost.getAttribute(attributeName);
- if(attrValue != null)
- {
- return attrValue.toString();
- }
- return defaultVal;
- }
-
- private boolean getBooleanAttribute(org.apache.qpid.server.model.VirtualHost virtualHost, String attributeName, boolean defaultVal)
- {
- Object attrValue = virtualHost.getAttribute(attributeName);
- if(attrValue != null)
- {
- if(attrValue instanceof Boolean)
- {
- return ((Boolean) attrValue).booleanValue();
- }
- else if(attrValue instanceof String)
- {
- return Boolean.parseBoolean((String)attrValue);
- }
-
- }
- return defaultVal;
- }
-
-
- @Override
- protected void setupStore(File storePath, String name) throws DatabaseException, AMQStoreException
- {
- super.setupStore(storePath, name);
-
- if(_coalescingSync)
- {
- _commitThreadWrapper = new CommitThreadWrapper("Commit-Thread-" + name, getEnvironment());
- _commitThreadWrapper.startCommitThread();
- }
- }
-
- @Override
- protected Environment createEnvironment(File environmentPath) throws DatabaseException
- {
- if (LOGGER.isInfoEnabled())
- {
- LOGGER.info("Environment path " + environmentPath.getAbsolutePath());
- LOGGER.info("Group name " + _groupName);
- LOGGER.info("Node name " + _nodeName);
- LOGGER.info("Node host port " + _nodeHostPort);
- LOGGER.info("Helper host port " + _helperHostPort);
- LOGGER.info("Durability " + _durability);
- LOGGER.info("Coalescing sync " + _coalescingSync);
- LOGGER.info("Designated primary (applicable to 2 node case only) " + _designatedPrimary);
- }
-
- final ReplicationConfig replicationConfig = new ReplicationConfig(_groupName, _nodeName, _nodeHostPort);
-
- replicationConfig.setHelperHosts(_helperHostPort);
- replicationConfig.setDesignatedPrimary(_designatedPrimary);
- setReplicationConfigProperties(replicationConfig);
-
- final EnvironmentConfig envConfig = createEnvironmentConfig();
- envConfig.setDurability(_durability);
-
- ReplicatedEnvironment replicatedEnvironment = null;
- try
- {
- replicatedEnvironment = new ReplicatedEnvironment(environmentPath, replicationConfig, envConfig);
- }
- catch (final InsufficientLogException ile)
- {
- LOGGER.info("InsufficientLogException thrown and so full network restore required", ile);
- NetworkRestore restore = new NetworkRestore();
- NetworkRestoreConfig config = new NetworkRestoreConfig();
- config.setRetainLogFiles(false);
- restore.execute(ile, config);
- replicatedEnvironment = new ReplicatedEnvironment(environmentPath, replicationConfig, envConfig);
- }
-
- return replicatedEnvironment;
- }
-
- @Override
- public void configureMessageStore(VirtualHost virtualHost, MessageStoreRecoveryHandler messageRecoveryHandler,
- TransactionLogRecoveryHandler tlogRecoveryHandler) throws Exception
- {
- super.configureMessageStore(virtualHost, messageRecoveryHandler, tlogRecoveryHandler);
-
- final ReplicatedEnvironment replicatedEnvironment = getReplicatedEnvironment();
-
- replicatedEnvironment.setStateChangeListener(new BDBHAMessageStoreStateChangeListener());
- }
-
- @Override
- public synchronized void activate() throws Exception
- {
- // Before proceeding, perform a log flush with an fsync
- getEnvironment().flushLog(true);
-
- super.activate();
- }
-
- @Override
- public synchronized void passivate()
- {
- if (_stateManager.isNotInState(State.INITIALISED))
- {
- LOGGER.debug("Store becoming passive");
- _stateManager.attainState(State.INITIALISED);
- }
- }
-
- public String getName()
- {
- return _name;
- }
-
- public String getGroupName()
- {
- return _groupName;
- }
-
- public String getNodeName()
- {
- return _nodeName;
- }
-
- public String getNodeHostPort()
- {
- return _nodeHostPort;
- }
-
- public String getHelperHostPort()
- {
- return _helperHostPort;
- }
-
- public String getDurability()
- {
- return _durability.toString();
- }
-
- public boolean isCoalescingSync()
- {
- return _coalescingSync;
- }
-
- public String getNodeState()
- {
- ReplicatedEnvironment.State state = getReplicatedEnvironment().getState();
- return state.toString();
- }
-
- public Boolean isDesignatedPrimary()
- {
- return getReplicatedEnvironment().getRepMutableConfig().getDesignatedPrimary();
- }
-
- public List<Map<String, String>> getGroupMembers()
- {
- List<Map<String, String>> members = new ArrayList<Map<String,String>>();
-
- for (ReplicationNode node : getReplicatedEnvironment().getGroup().getNodes())
- {
- Map<String, String> nodeMap = new HashMap<String, String>();
- nodeMap.put(BDBHAMessageStore.GRP_MEM_COL_NODE_NAME, node.getName());
- nodeMap.put(BDBHAMessageStore.GRP_MEM_COL_NODE_HOST_PORT, node.getHostName() + ":" + node.getPort());
- members.add(nodeMap);
- }
-
- return members;
- }
-
- public void removeNodeFromGroup(String nodeName) throws AMQStoreException
- {
- try
- {
- createReplicationGroupAdmin().removeMember(nodeName);
- }
- catch (OperationFailureException ofe)
- {
- throw new AMQStoreException("Failed to remove '" + nodeName + "' from group. " + ofe.getMessage(), ofe);
- }
- catch (DatabaseException e)
- {
- throw new AMQStoreException("Failed to remove '" + nodeName + "' from group. " + e.getMessage(), e);
- }
- }
-
- public void setDesignatedPrimary(boolean isPrimary) throws AMQStoreException
- {
- try
- {
- final ReplicatedEnvironment replicatedEnvironment = getReplicatedEnvironment();
- synchronized(replicatedEnvironment)
- {
- final ReplicationMutableConfig oldConfig = replicatedEnvironment.getRepMutableConfig();
- final ReplicationMutableConfig newConfig = oldConfig.setDesignatedPrimary(isPrimary);
- replicatedEnvironment.setRepMutableConfig(newConfig);
- }
-
- if (LOGGER.isInfoEnabled())
- {
- LOGGER.info("Node " + _nodeName + " successfully set as designated primary for group");
- }
- }
- catch (DatabaseException e)
- {
- throw new AMQStoreException("Failed to set '" + _nodeName + "' as designated primary for group. " + e.getMessage(), e);
- }
- }
-
- ReplicatedEnvironment getReplicatedEnvironment()
- {
- return (ReplicatedEnvironment)getEnvironment();
- }
-
- public void updateAddress(String nodeName, String newHostName, int newPort) throws AMQStoreException
- {
- try
- {
- createReplicationGroupAdmin().updateAddress(nodeName, newHostName, newPort);
- }
- catch (OperationFailureException ofe)
- {
- throw new AMQStoreException("Failed to update address for '" + nodeName +
- "' with new host " + newHostName + " and new port " + newPort + ". " + ofe.getMessage(), ofe);
- }
- catch (DatabaseException e)
- {
- throw new AMQStoreException("Failed to update address for '" + nodeName +
- "' with new host " + newHostName + " and new port " + newPort + ". " + e.getMessage(), e);
- }
- }
-
- @Override
- protected StoreFuture commit(Transaction tx, boolean syncCommit) throws DatabaseException
- {
- // Using commit() instead of commitNoSync() for the HA store to allow
- // the HA durability configuration to influence resulting behaviour.
- try
- {
- tx.commit();
- }
- catch (DatabaseException de)
- {
- LOGGER.error("Got DatabaseException on commit, closing environment", de);
-
- closeEnvironmentSafely();
-
- throw de;
- }
-
- if(_coalescingSync)
- {
- return _commitThreadWrapper.commit(tx, syncCommit);
- }
- else
- {
- return StoreFuture.IMMEDIATE_FUTURE;
- }
- }
-
- @Override
- protected void closeInternal() throws Exception
- {
- substituteNoOpStateChangeListenerOn(getReplicatedEnvironment());
-
- try
- {
- if(_coalescingSync)
- {
- _commitThreadWrapper.stopCommitThread();
- }
- }
- finally
- {
- super.closeInternal();
- }
- }
-
- /**
- * Replicas emit a state change event {@link com.sleepycat.je.rep.ReplicatedEnvironment.State#DETACHED} during
- * {@link Environment#close()}. We replace the StateChangeListener so we silently ignore this state change.
- */
- private void substituteNoOpStateChangeListenerOn(ReplicatedEnvironment replicatedEnvironment)
- {
- LOGGER.debug("Substituting no-op state change listener for environment close");
- replicatedEnvironment.setStateChangeListener(new NoOpStateChangeListener());
- }
-
- private ReplicationGroupAdmin createReplicationGroupAdmin()
- {
- final Set<InetSocketAddress> helpers = new HashSet<InetSocketAddress>();
- helpers.addAll(getReplicatedEnvironment().getRepConfig().getHelperSockets());
-
- final ReplicationConfig repConfig = getReplicatedEnvironment().getRepConfig();
- helpers.add(InetSocketAddress.createUnresolved(repConfig.getNodeHostname(), repConfig.getNodePort()));
-
- return new ReplicationGroupAdmin(_groupName, helpers);
- }
-
-
- private void setReplicationConfigProperties(ReplicationConfig replicationConfig)
- {
- for (Map.Entry<String, String> configItem : _repConfig.entrySet())
- {
- if (LOGGER.isDebugEnabled())
- {
- LOGGER.debug("Setting ReplicationConfig key " + configItem.getKey() + " to '" + configItem.getValue() + "'");
- }
- replicationConfig.setConfigParam(configItem.getKey(), configItem.getValue());
- }
- }
-
- private String getValidatedPropertyFromConfig(String key, Configuration config) throws ConfigurationException
- {
- if (!config.containsKey(key))
- {
- throw new ConfigurationException("BDB HA configuration key not found. Please specify configuration key with XPath: "
- + key.replace('.', '/'));
- }
- return config.getString(key);
- }
-
- private class BDBHAMessageStoreStateChangeListener implements StateChangeListener
- {
- private final Executor _executor = Executors.newSingleThreadExecutor();
-
- @Override
- public void stateChange(StateChangeEvent stateChangeEvent) throws RuntimeException
- {
- com.sleepycat.je.rep.ReplicatedEnvironment.State state = stateChangeEvent.getState();
-
- if (LOGGER.isInfoEnabled())
- {
- LOGGER.info("Received BDB event indicating transition to state " + state);
- }
-
- switch (state)
- {
- case MASTER:
- activateStoreAsync();
- break;
- case REPLICA:
- passivateStoreAsync();
- break;
- case DETACHED:
- LOGGER.error("BDB replicated node in detached state, therefore passivating.");
- passivateStoreAsync();
- break;
- case UNKNOWN:
- LOGGER.warn("BDB replicated node in unknown state (hopefully temporarily)");
- break;
- default:
- LOGGER.error("Unexpected state change: " + state);
- throw new IllegalStateException("Unexpected state change: " + state);
- }
- }
-
- /**
- * Calls {@link MessageStore#activate()}.
- *
- * <p/>
- *
- * This is done a background thread, in line with
- * {@link StateChangeListener#stateChange(StateChangeEvent)}'s JavaDoc, because
- * activate may execute transactions, which can't complete until
- * {@link StateChangeListener#stateChange(StateChangeEvent)} has returned.
- */
- private void activateStoreAsync()
- {
- String threadName = "BDBHANodeActivationThread-" + _name;
- executeStateChangeAsync(new Callable<Void>()
- {
- @Override
- public Void call() throws Exception
- {
- try
- {
- activate();
- }
- catch (Exception e)
- {
- LOGGER.error("Failed to activate on hearing MASTER change event",e);
- throw e;
- }
- return null;
- }
- }, threadName);
- }
-
- /**
- * Calls {@link #passivate()}.
- *
- * <p/>
- * This is done a background thread, in line with
- * {@link StateChangeListener#stateChange(StateChangeEvent)}'s JavaDoc, because
- * passivation due to the effect of state change listeners.
- */
- private void passivateStoreAsync()
- {
- String threadName = "BDBHANodePassivationThread-" + _name;
- executeStateChangeAsync(new Callable<Void>()
- {
-
- @Override
- public Void call() throws Exception
- {
- try
- {
- passivate();
- }
- catch (Exception e)
- {
- LOGGER.error("Failed to passivate on hearing REPLICA or DETACHED change event",e);
- throw e;
- }
- return null;
- }
- }, threadName);
- }
-
- private void executeStateChangeAsync(final Callable<Void> callable, final String threadName)
- {
- final RootMessageLogger _rootLogger = CurrentActor.get().getRootMessageLogger();
-
- _executor.execute(new Runnable()
- {
-
- @Override
- public void run()
- {
- final String originalThreadName = Thread.currentThread().getName();
- Thread.currentThread().setName(threadName);
- try
- {
- CurrentActor.set(new AbstractActor(_rootLogger)
- {
- @Override
- public String getLogMessage()
- {
- return threadName;
- }
- });
-
- try
- {
- callable.call();
- }
- catch (Exception e)
- {
- LOGGER.error("Exception during state change", e);
- }
- }
- finally
- {
- Thread.currentThread().setName(originalThreadName);
- }
- }
- });
- }
- }
-
- private class NoOpStateChangeListener implements StateChangeListener
- {
- @Override
- public void stateChange(StateChangeEvent stateChangeEvent)
- throws RuntimeException
- {
- }
- }
-
- @Override
- public String getStoreType()
- {
- return TYPE;
- }
-}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBHAVirtualHost.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBHAVirtualHost.java
index bb3c7b108d..52bb5e574b 100644
--- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBHAVirtualHost.java
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBHAVirtualHost.java
@@ -20,9 +20,13 @@ package org.apache.qpid.server.store.berkeleydb;
*
*/
+import java.util.UUID;
+
+import org.apache.log4j.Logger;
import org.apache.qpid.server.configuration.VirtualHostConfiguration;
import org.apache.qpid.server.connection.IConnectionRegistry;
import org.apache.qpid.server.logging.subjects.MessageStoreLogSubject;
+import org.apache.qpid.server.model.UUIDGenerator;
import org.apache.qpid.server.model.VirtualHost;
import org.apache.qpid.server.stats.StatisticsGatherer;
import org.apache.qpid.server.store.DurableConfigurationRecoverer;
@@ -31,15 +35,22 @@ import org.apache.qpid.server.store.Event;
import org.apache.qpid.server.store.EventListener;
import org.apache.qpid.server.store.MessageStore;
import org.apache.qpid.server.store.OperationalLoggingListener;
+import org.apache.qpid.server.store.berkeleydb.replication.ReplicatedEnvironmentFacade;
+import org.apache.qpid.server.store.berkeleydb.replication.ReplicatedEnvironmentFacadeFactory;
import org.apache.qpid.server.virtualhost.AbstractVirtualHost;
import org.apache.qpid.server.virtualhost.DefaultUpgraderProvider;
import org.apache.qpid.server.virtualhost.State;
import org.apache.qpid.server.virtualhost.VirtualHostConfigRecoveryHandler;
import org.apache.qpid.server.virtualhost.VirtualHostRegistry;
+import com.sleepycat.je.rep.StateChangeEvent;
+import com.sleepycat.je.rep.StateChangeListener;
+
public class BDBHAVirtualHost extends AbstractVirtualHost
{
- private BDBHAMessageStore _messageStore;
+ private static final Logger LOGGER = Logger.getLogger(BDBHAVirtualHost.class);
+
+ private BDBMessageStore _messageStore;
private boolean _inVhostInitiatedClose;
@@ -57,18 +68,15 @@ public class BDBHAVirtualHost extends AbstractVirtualHost
protected void initialiseStorage(VirtualHostConfiguration hostConfig, VirtualHost virtualHost) throws Exception
{
- _messageStore = new BDBHAMessageStore();
+ _messageStore = new BDBMessageStore(new ReplicatedEnvironmentFacadeFactory());
- final MessageStoreLogSubject storeLogSubject =
- new MessageStoreLogSubject(getName(), _messageStore.getClass().getSimpleName());
+ MessageStoreLogSubject storeLogSubject = new MessageStoreLogSubject(getName(), _messageStore.getClass().getSimpleName());
OperationalLoggingListener.listen(_messageStore, storeLogSubject);
_messageStore.addEventListener(new BeforeActivationListener(), Event.BEFORE_ACTIVATE);
_messageStore.addEventListener(new AfterActivationListener(), Event.AFTER_ACTIVATE);
_messageStore.addEventListener(new BeforeCloseListener(), Event.BEFORE_CLOSE);
-
-
_messageStore.addEventListener(new AfterInitialisationListener(), Event.AFTER_INIT);
_messageStore.addEventListener(new BeforePassivationListener(), Event.BEFORE_PASSIVATE);
@@ -85,6 +93,11 @@ public class BDBHAVirtualHost extends AbstractVirtualHost
virtualHost, recoveryHandler,
recoveryHandler
);
+
+ // Make the virtualhost model object a replication group listener
+ ReplicatedEnvironmentFacade environmentFacade = (ReplicatedEnvironmentFacade) _messageStore.getEnvironmentFacade();
+ environmentFacade.setReplicationGroupListener(getReplicationGroupListener());
+ environmentFacade.setStateChangeListener(new BDBHAMessageStoreStateChangeListener());
}
@@ -122,6 +135,13 @@ public class BDBHAVirtualHost extends AbstractVirtualHost
return _messageStore;
}
+ @Override
+ public UUID getId()
+ {
+ //TODO: a temporary approach untill we change the broker model to have Nodes as Broker children
+ return UUIDGenerator.generateVhostUUID(((ReplicatedEnvironmentFacade) _messageStore.getEnvironmentFacade()).getGroupName());
+ }
+
private final class AfterInitialisationListener implements EventListener
{
public void event(Event event)
@@ -145,12 +165,7 @@ public class BDBHAVirtualHost extends AbstractVirtualHost
* is documented as exceptionally rare..
*/
- getConnectionRegistry().close(IConnectionRegistry.VHOST_PASSIVATE_REPLY_TEXT);
- removeHouseKeepingTasks();
-
- getQueueRegistry().stopAllAndUnregisterMBeans();
- getExchangeRegistry().clearAndUnregisterMbeans();
- getDtxRegistry().close();
+ passivate(IConnectionRegistry.VHOST_PASSIVATE_REPLY_TEXT);
finalState = State.PASSIVE;
}
@@ -202,4 +217,69 @@ public class BDBHAVirtualHost extends AbstractVirtualHost
}
}
+ private class BDBHAMessageStoreStateChangeListener implements StateChangeListener
+ {
+
+ @Override
+ public void stateChange(StateChangeEvent stateChangeEvent) throws RuntimeException
+ {
+ com.sleepycat.je.rep.ReplicatedEnvironment.State state = stateChangeEvent.getState();
+
+ if (LOGGER.isInfoEnabled())
+ {
+ LOGGER.info("Received BDB event indicating transition to state " + state
+ + " when current message store state is " + _messageStore._stateManager.getState());
+ }
+
+ switch (state)
+ {
+ case MASTER:
+ activate();
+ break;
+ case REPLICA:
+ passivate();
+ break;
+ case DETACHED:
+ LOGGER.error("BDB replicated node in detached state, therefore passivating.");
+ passivate();
+ break;
+ case UNKNOWN:
+ LOGGER.warn("BDB replicated node in unknown state (hopefully temporarily)");
+ break;
+ default:
+ LOGGER.error("Unexpected state change: " + state);
+ throw new IllegalStateException("Unexpected state change: " + state);
+ }
+ }
+
+ private void activate()
+ {
+ try
+ {
+ _messageStore.getEnvironmentFacade().getEnvironment().flushLog(true);
+ _messageStore.activate();
+ }
+ catch (Exception e)
+ {
+ LOGGER.error("Failed to activate on hearing MASTER change event", e);
+ }
+ }
+
+ private void passivate()
+ {
+ try
+ {
+ //TODO: move this this into the store method passivate()
+ if (_messageStore._stateManager.isNotInState(org.apache.qpid.server.store.State.INITIALISED))
+ {
+ _messageStore._stateManager.attainState(org.apache.qpid.server.store.State.INITIALISED);
+ }
+ }
+ catch (Exception e)
+ {
+ LOGGER.error("Failed to passivate on hearing REPLICA or DETACHED change event", e);
+ }
+ }
+
+ }
}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBHAVirtualHostFactory.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBHAVirtualHostFactory.java
index 3b564f33fd..48d863530a 100644
--- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBHAVirtualHostFactory.java
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBHAVirtualHostFactory.java
@@ -23,12 +23,18 @@ import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.UUID;
+
import org.apache.commons.configuration.Configuration;
import org.apache.qpid.server.configuration.VirtualHostConfiguration;
+import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.model.ReplicationNode;
+import org.apache.qpid.server.model.UUIDGenerator;
import org.apache.qpid.server.model.adapter.VirtualHostAdapter;
import org.apache.qpid.server.plugin.VirtualHostFactory;
import org.apache.qpid.server.stats.StatisticsGatherer;
import org.apache.qpid.server.store.MessageStoreConstants;
+import org.apache.qpid.server.store.berkeleydb.replication.LocalReplicationNode;
import org.apache.qpid.server.virtualhost.VirtualHost;
import org.apache.qpid.server.virtualhost.VirtualHostRegistry;
@@ -60,82 +66,26 @@ public class BDBHAVirtualHostFactory implements VirtualHostFactory
@Override
public void validateAttributes(Map<String, Object> attributes)
{
- validateAttribute(org.apache.qpid.server.model.VirtualHost.STORE_PATH, String.class, attributes);
- validateAttribute("haGroupName", String.class, attributes);
- validateAttribute("haNodeName", String.class, attributes);
- validateAttribute("haNodeAddress", String.class, attributes);
- validateAttribute("haHelperAddress", String.class, attributes);
}
- private void validateAttribute(String attrName, Class<?> clazz, Map<String, Object> attributes)
- {
- Object attr = attributes.get(attrName);
- if(!clazz.isInstance(attr))
- {
- throw new IllegalArgumentException("Attribute '"+ attrName
- +"' is required and must be of type "+clazz.getSimpleName()+".");
- }
- }
@Override
public Map<String, Object> createVirtualHostConfiguration(VirtualHostAdapter virtualHostAdapter)
{
+ //TODO: Dead code?
LinkedHashMap<String,Object> convertedMap = new LinkedHashMap<String, Object>();
convertedMap.put("store.environment-path", virtualHostAdapter.getAttribute(org.apache.qpid.server.model.VirtualHost.STORE_PATH));
-
return convertedMap;
}
+ @Override
public Map<String, Object> convertVirtualHostConfiguration(Configuration configuration)
{
-
LinkedHashMap<String,Object> convertedMap = new LinkedHashMap<String, Object>();
-
Configuration storeConfiguration = configuration.subset("store");
-
- convertedMap.put(org.apache.qpid.server.model.VirtualHost.STORE_PATH, storeConfiguration.getString(MessageStoreConstants.ENVIRONMENT_PATH_PROPERTY));
convertedMap.put(MessageStoreConstants.OVERFULL_SIZE_ATTRIBUTE, storeConfiguration.getString(MessageStoreConstants.OVERFULL_SIZE_PROPERTY));
convertedMap.put(MessageStoreConstants.UNDERFULL_SIZE_ATTRIBUTE, storeConfiguration.getString(MessageStoreConstants.UNDERFULL_SIZE_PROPERTY));
- convertedMap.put("haGroupName", configuration.getString("store.highAvailability.groupName"));
- convertedMap.put("haNodeName", configuration.getString("store.highAvailability.nodeName"));
- convertedMap.put("haNodeAddress", configuration.getString("store.highAvailability.nodeHostPort"));
- convertedMap.put("haHelperAddress", configuration.getString("store.highAvailability.helperHostPort"));
-
- final Object haDurability = configuration.getString("store.highAvailability.durability");
- if(haDurability !=null)
- {
- convertedMap.put("haDurability", haDurability);
- }
-
- final Object designatedPrimary = configuration.getString("store.highAvailability.designatedPrimary");
- if(designatedPrimary!=null)
- {
- convertedMap.put("haDesignatedPrimary", designatedPrimary);
- }
-
- final Object coalescingSync = configuration.getString("store.highAvailability.coalescingSync");
- if(coalescingSync!=null)
- {
- convertedMap.put("haCoalescingSync", coalescingSync);
- }
-
-
- Map<String, String> attributes = getEnvironmentMap(storeConfiguration, "envConfig");
-
- if(!attributes.isEmpty())
- {
- convertedMap.put("bdbEnvironmentConfig",attributes);
- }
-
- attributes = getEnvironmentMap(storeConfiguration, "repConfig");
-
- if(!attributes.isEmpty())
- {
- convertedMap.put("haReplicationConfig",attributes);
- }
-
return convertedMap;
-
}
private Map<String, String> getEnvironmentMap(Configuration storeConfiguration, String configName)
@@ -155,4 +105,55 @@ public class BDBHAVirtualHostFactory implements VirtualHostFactory
}
return attributes;
}
+
+ @Override
+ public ReplicationNode createReplicationNode(Configuration configuration, org.apache.qpid.server.model.VirtualHost virtualHost)
+ {
+ Configuration storeConfiguration = configuration.subset("store");
+
+ String nodeName = storeConfiguration.getString("highAvailability.nodeName");
+ String groupName = storeConfiguration.getString("highAvailability.groupName");
+ Map<String, Object> attributes = new HashMap<String, Object>();
+ attributes.put(ReplicationNode.NAME, nodeName);
+ attributes.put(ReplicationNode.GROUP_NAME, groupName);
+ attributes.put(ReplicationNode.HOST_PORT, storeConfiguration.getString("highAvailability.nodeHostPort"));
+ attributes.put(ReplicationNode.HELPER_HOST_PORT, storeConfiguration.getString("highAvailability.helperHostPort"));
+ attributes.put(org.apache.qpid.server.model.VirtualHost.STORE_PATH,
+ storeConfiguration.getString(MessageStoreConstants.ENVIRONMENT_PATH_PROPERTY));
+
+ String durability = storeConfiguration.getString("highAvailability.durability");
+ if (durability != null)
+ {
+ attributes.put(ReplicationNode.DURABILITY, durability);
+ }
+
+ String designatedPrimary = storeConfiguration.getString("highAvailability.designatedPrimary");
+ if (designatedPrimary != null)
+ {
+ attributes.put(ReplicationNode.DESIGNATED_PRIMARY, designatedPrimary);
+ }
+
+ String coalescingSync = storeConfiguration.getString("highAvailability.coalescingSync");
+ if (coalescingSync != null)
+ {
+ attributes.put(ReplicationNode.COALESCING_SYNC, coalescingSync);
+ }
+
+ Map<String, String> envAttributes = getEnvironmentMap(storeConfiguration, "envConfig");
+ if (envAttributes != null && envAttributes.size() > 0)
+ {
+ attributes.put(ReplicationNode.PARAMETERS, envAttributes);
+ }
+
+ Map<String, String> repAttributes = getEnvironmentMap(storeConfiguration, "repConfig");
+ if (repAttributes != null && repAttributes.size() > 0)
+ {
+ attributes.put(ReplicationNode.REPLICATION_PARAMETERS, repAttributes);
+ }
+
+ Broker broker = virtualHost.getParent(Broker.class);
+ UUID uuid = UUIDGenerator.generateReplicationNodeId(groupName, nodeName);
+ return new LocalReplicationNode(uuid, attributes, virtualHost, broker.getTaskExecutor());
+ }
+
}
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
index 4028de4b80..ab481fe816 100644
--- 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
@@ -20,16 +20,45 @@
*/
package org.apache.qpid.server.store.berkeleydb;
+import com.sleepycat.bind.tuple.ByteBinding;
+import com.sleepycat.bind.tuple.IntegerBinding;
+import com.sleepycat.bind.tuple.LongBinding;
+import com.sleepycat.je.*;
+import com.sleepycat.je.Transaction;
+
import java.io.File;
+import java.lang.ref.SoftReference;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.log4j.Logger;
import org.apache.qpid.AMQStoreException;
-import org.apache.qpid.server.store.MessageStore;
-import org.apache.qpid.server.store.StoreFuture;
-
-import com.sleepycat.je.DatabaseException;
-import com.sleepycat.je.Environment;
-import com.sleepycat.je.EnvironmentConfig;
+import org.apache.qpid.server.message.EnqueueableMessage;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.store.*;
+import org.apache.qpid.server.store.MessageStoreRecoveryHandler.StoredMessageRecoveryHandler;
+import org.apache.qpid.server.store.TransactionLogRecoveryHandler.QueueEntryRecoveryHandler;
+import org.apache.qpid.server.store.berkeleydb.entry.PreparedTransaction;
+import org.apache.qpid.server.store.berkeleydb.entry.QueueEntryKey;
+import org.apache.qpid.server.store.berkeleydb.entry.Xid;
+import org.apache.qpid.server.store.berkeleydb.tuple.ConfiguredObjectBinding;
+import org.apache.qpid.server.store.berkeleydb.tuple.ContentBinding;
+import org.apache.qpid.server.store.berkeleydb.tuple.MessageMetaDataBinding;
+import org.apache.qpid.server.store.berkeleydb.tuple.PreparedTransactionBinding;
+import org.apache.qpid.server.store.berkeleydb.tuple.QueueEntryBinding;
+import org.apache.qpid.server.store.berkeleydb.tuple.UUIDTupleBinding;
+import org.apache.qpid.server.store.berkeleydb.tuple.XidBinding;
+import org.apache.qpid.server.store.berkeleydb.upgrade.Upgrader;
+import org.apache.qpid.util.FileUtils;
/**
* BDBMessageStore implements a persistent {@link MessageStore} using the BDB high performance log.
@@ -39,76 +68,1661 @@ import com.sleepycat.je.EnvironmentConfig;
* 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>
*/
-public class BDBMessageStore extends AbstractBDBMessageStore
+public class BDBMessageStore implements MessageStore, DurableConfigurationStore
{
private static final Logger LOGGER = Logger.getLogger(BDBMessageStore.class);
- public static final String TYPE = "BDB";
- private CommitThreadWrapper _commitThreadWrapper;
+
+ public static final int VERSION = 7;
+ public static final String ENVIRONMENT_CONFIGURATION = "bdbEnvironmentConfig";
+
+ private static final int LOCK_RETRY_ATTEMPTS = 5;
+ private static String CONFIGURED_OBJECTS_DB_NAME = "CONFIGURED_OBJECTS";
+ private static String MESSAGE_META_DATA_DB_NAME = "MESSAGE_METADATA";
+ private static String MESSAGE_CONTENT_DB_NAME = "MESSAGE_CONTENT";
+ private static String DELIVERY_DB_NAME = "QUEUE_ENTRIES";
+ private static String BRIDGEDB_NAME = "BRIDGES";
+ private static String LINKDB_NAME = "LINKS";
+ private static String XID_DB_NAME = "XIDS";
+ private static String CONFIG_VERSION_DB_NAME = "CONFIG_VERSION";
+ private static final String[] DATABASE_NAMES = new String[] { CONFIGURED_OBJECTS_DB_NAME, MESSAGE_META_DATA_DB_NAME,
+ MESSAGE_CONTENT_DB_NAME, DELIVERY_DB_NAME, BRIDGEDB_NAME, LINKDB_NAME, XID_DB_NAME, CONFIG_VERSION_DB_NAME };
+
+ private final AtomicBoolean _closed = new AtomicBoolean(false);
+
+ private EnvironmentFacade _environmentFacade;
+ private final AtomicLong _messageId = new AtomicLong(0);
+
+ protected final StateManager _stateManager;
+
+ private MessageStoreRecoveryHandler _messageRecoveryHandler;
+
+ private TransactionLogRecoveryHandler _tlogRecoveryHandler;
+
+ private ConfigurationRecoveryHandler _configRecoveryHandler;
+
+ private long _totalStoreSize;
+ private boolean _limitBusted;
+ private long _persistentSizeLowThreshold;
+ private long _persistentSizeHighThreshold;
+
+ private final EventManager _eventManager = new EventManager();
+ private final String _type;
+ private VirtualHost _virtualHost;
+
+ private final EnvironmentFacadeFactory _environmentFacadeFactory;
+
+ private volatile Committer _committer;
+
+ public BDBMessageStore()
+ {
+ this(new StandardEnvironmentFacadeFactory());
+ }
+
+ public BDBMessageStore(EnvironmentFacadeFactory environmentFacadeFactory)
+ {
+ _type = environmentFacadeFactory.getType();;
+ _environmentFacadeFactory = environmentFacadeFactory;
+ _stateManager = new StateManager(_eventManager);
+ }
@Override
- protected void setupStore(File storePath, String name) throws DatabaseException, AMQStoreException
+ public void addEventListener(EventListener eventListener, Event... events)
{
- super.setupStore(storePath, name);
+ _eventManager.addEventListener(eventListener, events);
+ }
- _commitThreadWrapper = new CommitThreadWrapper("Commit-Thread-" + name, getEnvironment());
- _commitThreadWrapper.startCommitThread();
+ @Override
+ public void configureConfigStore(VirtualHost virtualHost, ConfigurationRecoveryHandler recoveryHandler)
+ {
+ _stateManager.attainState(State.INITIALISING);
+
+ _configRecoveryHandler = recoveryHandler;
+ _virtualHost = virtualHost;
+ }
+
+ @Override
+ public void configureMessageStore(VirtualHost virtualHost, MessageStoreRecoveryHandler messageRecoveryHandler,
+ TransactionLogRecoveryHandler tlogRecoveryHandler) throws AMQStoreException
+ {
+ if(_stateManager.isInState(State.INITIAL))
+ {
+ // Is acting as a message store, but not a durable config store
+ _stateManager.attainState(State.INITIALISING);
+ }
+
+ _messageRecoveryHandler = messageRecoveryHandler;
+ _tlogRecoveryHandler = tlogRecoveryHandler;
+ _virtualHost = virtualHost;
+
+
+ completeInitialisation();
}
- protected Environment createEnvironment(File environmentPath) throws DatabaseException
+ private void completeInitialisation() throws AMQStoreException
{
- LOGGER.info("BDB message store using environment path " + environmentPath.getAbsolutePath());
- EnvironmentConfig envConfig = createEnvironmentConfig();
+ configure(_virtualHost, _messageRecoveryHandler != null);
+ _stateManager.attainState(State.INITIALISED);
+ }
+
+ private void startActivation() throws AMQStoreException
+ {
+ DatabaseConfig dbConfig = new DatabaseConfig();
+ dbConfig.setTransactional(true);
+ dbConfig.setAllowCreate(true);
try
{
- return new Environment(environmentPath, envConfig);
+ new Upgrader(_environmentFacade.getEnvironment(), _virtualHost.getName()).upgradeIfNecessary();
+ _environmentFacade.openDatabases(dbConfig, DATABASE_NAMES);
+ _totalStoreSize = getSizeOnDisk();
+ }
+ catch(DatabaseException e)
+ {
+ throw _environmentFacade.handleDatabaseException("Cannot configure store", e);
+ }
+
+ }
+
+ @Override
+ public synchronized void activate() throws AMQStoreException
+ {
+ // check if acting as a durable config store, but not a message store
+ if(_stateManager.isInState(State.INITIALISING))
+ {
+ completeInitialisation();
+ }
+
+ _stateManager.attainState(State.ACTIVATING);
+ startActivation();
+
+ if(_configRecoveryHandler != null)
+ {
+ recoverConfig(_configRecoveryHandler);
}
- catch (DatabaseException de)
+ if(_messageRecoveryHandler != null)
+ {
+ recoverMessages(_messageRecoveryHandler);
+ }
+ if(_tlogRecoveryHandler != null)
+ {
+ recoverQueueEntries(_tlogRecoveryHandler);
+ }
+
+ _stateManager.attainState(State.ACTIVE);
+ }
+
+ @Override
+ public org.apache.qpid.server.store.Transaction newTransaction() throws AMQStoreException
+ {
+ return new BDBTransaction();
+ }
+
+ private void configure(VirtualHost virtualHost, boolean isMessageStore) throws AMQStoreException
+ {
+ Object overfullAttr = virtualHost.getAttribute(MessageStoreConstants.OVERFULL_SIZE_ATTRIBUTE);
+ Object underfullAttr = virtualHost.getAttribute(MessageStoreConstants.UNDERFULL_SIZE_ATTRIBUTE);
+
+ _persistentSizeHighThreshold = overfullAttr == null ? -1l :
+ overfullAttr instanceof Number ? ((Number) overfullAttr).longValue() : Long.parseLong(overfullAttr.toString());
+ _persistentSizeLowThreshold = underfullAttr == null ? _persistentSizeHighThreshold :
+ underfullAttr instanceof Number ? ((Number) underfullAttr).longValue() : Long.parseLong(underfullAttr.toString());
+
+
+ if(_persistentSizeLowThreshold > _persistentSizeHighThreshold || _persistentSizeLowThreshold < 0l)
{
- if (de.getMessage().contains("Environment.setAllowCreate is false"))
+ _persistentSizeLowThreshold = _persistentSizeHighThreshold;
+ }
+
+ _environmentFacade = _environmentFacadeFactory.createEnvironmentFacade(virtualHost, isMessageStore);
+
+ _committer = _environmentFacade.createCommitter(virtualHost.getName());
+ _committer.start();
+ }
+
+ @Override
+ public String getStoreLocation()
+ {
+ if (_environmentFacade == null)
+ {
+ return null;
+ }
+ return _environmentFacade.getStoreLocation();
+ }
+
+ public EnvironmentFacade getEnvironmentFacade()
+ {
+ return _environmentFacade;
+ }
+
+ /**
+ * Called to close and cleanup any resources used by the message store.
+ *
+ * @throws Exception If the close fails.
+ */
+ @Override
+ public void close() throws AMQStoreException
+ {
+ if (_closed.compareAndSet(false, true))
+ {
+ _stateManager.attainState(State.CLOSING);
+ try
{
- //Allow the creation this time
- envConfig.setAllowCreate(true);
- return new Environment(environmentPath, envConfig);
+ try
+ {
+ _committer.stop();
+ }
+ finally
+ {
+ closeEnvironment();
+ }
}
- else
+ catch(DatabaseException e)
+ {
+ throw new AMQStoreException("Exception occured on message store close", e);
+ }
+ _stateManager.attainState(State.CLOSED);
+ }
+ }
+
+ private void closeEnvironment()
+ {
+ if (_environmentFacade != null)
+ {
+ _environmentFacade.close();
+ }
+ }
+
+ private void recoverConfig(ConfigurationRecoveryHandler recoveryHandler) throws AMQStoreException
+ {
+ try
+ {
+ final int configVersion = getConfigVersion();
+ recoveryHandler.beginConfigurationRecovery(this, configVersion);
+ loadConfiguredObjects(recoveryHandler);
+
+ final int newConfigVersion = recoveryHandler.completeConfigurationRecovery();
+ if(newConfigVersion != configVersion)
+ {
+ updateConfigVersion(newConfigVersion);
+ }
+ }
+ catch (DatabaseException e)
+ {
+ throw _environmentFacade.handleDatabaseException("Error recovering persistent state: " + e.getMessage(), e);
+ }
+
+ }
+
+ @SuppressWarnings("resource")
+ private void updateConfigVersion(int newConfigVersion) throws AMQStoreException
+ {
+ Cursor cursor = null;
+ try
+ {
+ Transaction txn = _environmentFacade.getEnvironment().beginTransaction(null, null);
+ cursor = getConfigVersionDb().openCursor(txn, null);
+ DatabaseEntry key = new DatabaseEntry();
+ ByteBinding.byteToEntry((byte) 0,key);
+ DatabaseEntry value = new DatabaseEntry();
+
+ while (cursor.getNext(key, value, LockMode.RMW) == OperationStatus.SUCCESS)
+ {
+ IntegerBinding.intToEntry(newConfigVersion, value);
+ OperationStatus status = cursor.put(key, value);
+ if (status != OperationStatus.SUCCESS)
+ {
+ throw new AMQStoreException("Error setting config version: " + status);
+ }
+ }
+ cursor.close();
+ cursor = null;
+ txn.commit();
+ }
+ finally
+ {
+ closeCursorSafely(cursor);
+ }
+
+ }
+
+ private int getConfigVersion() throws AMQStoreException
+ {
+ Cursor cursor = null;
+ try
+ {
+ cursor = getConfigVersionDb().openCursor(null, null);
+ DatabaseEntry key = new DatabaseEntry();
+ DatabaseEntry value = new DatabaseEntry();
+ while (cursor.getNext(key, value, LockMode.RMW) == OperationStatus.SUCCESS)
+ {
+ return IntegerBinding.entryToInt(value);
+ }
+
+ // Insert 0 as the default config version
+ IntegerBinding.intToEntry(0,value);
+ ByteBinding.byteToEntry((byte) 0,key);
+ OperationStatus status = getConfigVersionDb().put(null, key, value);
+ if (status != OperationStatus.SUCCESS)
+ {
+ throw new AMQStoreException("Error initialising config version: " + status);
+ }
+ return 0;
+ }
+ finally
+ {
+ closeCursorSafely(cursor);
+ }
+ }
+
+ private void loadConfiguredObjects(ConfigurationRecoveryHandler crh) throws DatabaseException, AMQStoreException
+ {
+ Cursor cursor = null;
+ try
+ {
+ cursor = getConfiguredObjectsDb().openCursor(null, null);
+ DatabaseEntry key = new DatabaseEntry();
+ DatabaseEntry value = new DatabaseEntry();
+ while (cursor.getNext(key, value, LockMode.RMW) == OperationStatus.SUCCESS)
+ {
+ UUID id = UUIDTupleBinding.getInstance().entryToObject(key);
+
+ ConfiguredObjectRecord configuredObject = new ConfiguredObjectBinding(id).entryToObject(value);
+ crh.configuredObject(configuredObject.getId(),configuredObject.getType(),configuredObject.getAttributes());
+ }
+
+ }
+ finally
+ {
+ closeCursorSafely(cursor);
+ }
+ }
+
+ private void closeCursorSafely(Cursor cursor) throws AMQStoreException
+ {
+ if (cursor != null)
+ {
+ try
{
- throw de;
+ cursor.close();
+ }
+ catch(DatabaseException e)
+ {
+ throw _environmentFacade.handleDatabaseException("Cannot close cursor", e);
}
}
}
+
+ private void recoverMessages(MessageStoreRecoveryHandler msrh) throws AMQStoreException
+ {
+ StoredMessageRecoveryHandler mrh = msrh.begin();
+
+ Cursor cursor = null;
+ try
+ {
+ cursor = getMessageMetaDataDb().openCursor(null, null);
+ DatabaseEntry key = new DatabaseEntry();
+ DatabaseEntry value = new DatabaseEntry();
+ MessageMetaDataBinding valueBinding = MessageMetaDataBinding.getInstance();
+
+ long maxId = 0;
+
+ while (cursor.getNext(key, value, LockMode.RMW) == OperationStatus.SUCCESS)
+ {
+ long messageId = LongBinding.entryToLong(key);
+ StorableMessageMetaData metaData = valueBinding.entryToObject(value);
+
+ StoredBDBMessage message = new StoredBDBMessage(messageId, metaData, true);
+
+ mrh.message(message);
+
+ maxId = Math.max(maxId, messageId);
+ }
+
+ _messageId.set(maxId);
+ mrh.completeMessageRecovery();
+ }
+ catch (DatabaseException e)
+ {
+ throw _environmentFacade.handleDatabaseException("Cannot recover messages", e);
+ }
+ finally
+ {
+ closeCursorSafely(cursor);
+ }
+ }
+
+ private void recoverQueueEntries(TransactionLogRecoveryHandler recoveryHandler)
+ throws AMQStoreException
+ {
+ QueueEntryRecoveryHandler qerh = recoveryHandler.begin(this);
+
+ ArrayList<QueueEntryKey> entries = new ArrayList<QueueEntryKey>();
+
+ Cursor cursor = null;
+ try
+ {
+ cursor = getDeliveryDb().openCursor(null, null);
+ DatabaseEntry key = new DatabaseEntry();
+ QueueEntryBinding keyBinding = QueueEntryBinding.getInstance();
+
+ DatabaseEntry value = new DatabaseEntry();
+ while (cursor.getNext(key, value, LockMode.RMW) == OperationStatus.SUCCESS)
+ {
+ QueueEntryKey qek = keyBinding.entryToObject(key);
+
+ entries.add(qek);
+ }
+
+ try
+ {
+ cursor.close();
+ }
+ finally
+ {
+ cursor = null;
+ }
+
+ for(QueueEntryKey entry : entries)
+ {
+ UUID queueId = entry.getQueueId();
+ long messageId = entry.getMessageId();
+ qerh.queueEntry(queueId, messageId);
+ }
+ }
+ catch (DatabaseException e)
+ {
+ throw _environmentFacade.handleDatabaseException("Cannot recover queue entries", e);
+ }
+ finally
+ {
+ closeCursorSafely(cursor);
+ }
+
+ TransactionLogRecoveryHandler.DtxRecordRecoveryHandler dtxrh = qerh.completeQueueEntryRecovery();
+
+ cursor = null;
+ try
+ {
+ cursor = getXidDb().openCursor(null, null);
+ DatabaseEntry key = new DatabaseEntry();
+ XidBinding keyBinding = XidBinding.getInstance();
+ PreparedTransactionBinding valueBinding = new PreparedTransactionBinding();
+ DatabaseEntry value = new DatabaseEntry();
+
+ while (cursor.getNext(key, value, LockMode.RMW) == OperationStatus.SUCCESS)
+ {
+ Xid xid = keyBinding.entryToObject(key);
+ PreparedTransaction preparedTransaction = valueBinding.entryToObject(value);
+ dtxrh.dtxRecord(xid.getFormat(),xid.getGlobalId(),xid.getBranchId(),
+ preparedTransaction.getEnqueues(),preparedTransaction.getDequeues());
+ }
+
+ }
+ catch (DatabaseException e)
+ {
+ throw _environmentFacade.handleDatabaseException("Cannot recover transactions", e);
+ }
+ finally
+ {
+ closeCursorSafely(cursor);
+ }
+
+
+ dtxrh.completeDtxRecordRecovery();
+ }
+
+ public void removeMessage(long messageId, boolean sync) throws AMQStoreException
+ {
+
+ boolean complete = false;
+ com.sleepycat.je.Transaction tx = null;
+
+ Random rand = null;
+ int attempts = 0;
+ try
+ {
+ do
+ {
+ tx = null;
+ try
+ {
+ tx = _environmentFacade.getEnvironment().beginTransaction(null, null);
+
+ //remove the message meta data from the store
+ DatabaseEntry key = new DatabaseEntry();
+ LongBinding.longToEntry(messageId, key);
+
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Removing message id " + messageId);
+ }
+
+
+ OperationStatus status = getMessageMetaDataDb().delete(tx, key);
+ if (status == OperationStatus.NOTFOUND)
+ {
+ LOGGER.info("Message not found (attempt to remove failed - probably application initiated rollback) " +
+ messageId);
+ }
+
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Deleted metadata for message " + messageId);
+ }
+
+ //now remove the content data from the store if there is any.
+ DatabaseEntry contentKeyEntry = new DatabaseEntry();
+ LongBinding.longToEntry(messageId, contentKeyEntry);
+ getMessageContentDb().delete(tx, contentKeyEntry);
+
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Deleted content for message " + messageId);
+ }
+
+ _environmentFacade.commit(tx);
+ _committer.commit(tx, sync);
+
+ complete = true;
+ tx = null;
+ }
+ catch (LockConflictException e)
+ {
+ try
+ {
+ if(tx != null)
+ {
+ tx.abort();
+ }
+ }
+ catch(DatabaseException e2)
+ {
+ LOGGER.warn("Unable to abort transaction after LockConflictExcption on removal of message with id " + messageId, e2);
+ // rethrow the original log conflict exception, the secondary exception should already have
+ // been logged.
+ throw _environmentFacade.handleDatabaseException("Cannot remove message with id " + messageId, e);
+ }
+
+
+ LOGGER.warn("Lock timeout exception. Retrying (attempt "
+ + (attempts+1) + " of "+ LOCK_RETRY_ATTEMPTS +") " + e);
+
+ if(++attempts < LOCK_RETRY_ATTEMPTS)
+ {
+ if(rand == null)
+ {
+ rand = new Random();
+ }
+
+ try
+ {
+ Thread.sleep(500l + (long)(500l * rand.nextDouble()));
+ }
+ catch (InterruptedException e1)
+ {
+
+ }
+ }
+ else
+ {
+ // rethrow the lock conflict exception since we could not solve by retrying
+ throw _environmentFacade.handleDatabaseException("Cannot remove messages", e);
+ }
+ }
+ }
+ while(!complete);
+ }
+ catch (DatabaseException e)
+ {
+ LOGGER.error("Unexpected BDB exception", e);
+
+ try
+ {
+ abortTransactionIgnoringException("Error aborting transaction on removal of message with id " + messageId, tx);
+ }
+ finally
+ {
+ tx = null;
+ }
+
+ throw _environmentFacade.handleDatabaseException("Error removing message with id " + messageId + " from database: " + e.getMessage(), e);
+ }
+ finally
+ {
+ try
+ {
+ abortTransactionIgnoringException("Error aborting transaction on removal of message with id " + messageId, tx);
+ }
+ finally
+ {
+ tx = null;
+ }
+ }
+ }
+
+ private void abortTransactionIgnoringException(String errorMessage, com.sleepycat.je.Transaction tx)
+ {
+ try
+ {
+ if (tx != null)
+ {
+ tx.abort();
+ }
+ }
+ catch (DatabaseException e1)
+ {
+ // We need the possible side effect of the handler restarting the environment but don't care about the exception
+ _environmentFacade.handleDatabaseException(null, e1);
+ LOGGER.warn(errorMessage, e1);
+ }
+ }
+
+ @Override
+ public void create(UUID id, String type, Map<String, Object> attributes) throws AMQStoreException
+ {
+ if (_stateManager.isInState(State.ACTIVE))
+ {
+ ConfiguredObjectRecord configuredObject = new ConfiguredObjectRecord(id, type, attributes);
+ storeConfiguredObjectEntry(configuredObject);
+ }
+ }
+
@Override
- protected void closeInternal() throws Exception
+ public void remove(UUID id, String type) throws AMQStoreException
+ {
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("public void remove(id = " + id + ", type="+type+"): called");
+ }
+ OperationStatus status = removeConfiguredObject(null, id);
+ if (status == OperationStatus.NOTFOUND)
+ {
+ throw new AMQStoreException("Configured object of type " + type + " with id " + id + " not found");
+ }
+ }
+
+ @Override
+ public UUID[] removeConfiguredObjects(final UUID... objects) throws AMQStoreException
+ {
+ com.sleepycat.je.Transaction txn = _environmentFacade.getEnvironment().beginTransaction(null, null);
+ Collection<UUID> removed = new ArrayList<UUID>(objects.length);
+ for(UUID id : objects)
+ {
+ if(removeConfiguredObject(txn, id) == OperationStatus.SUCCESS)
+ {
+ removed.add(id);
+ }
+ }
+ commitTransaction(txn);
+ return removed.toArray(new UUID[removed.size()]);
+ }
+
+ private void commitTransaction(com.sleepycat.je.Transaction txn) throws AMQStoreException
+ {
+ try
+ {
+ txn.commit();
+ }
+ catch(DatabaseException e)
+ {
+ throw _environmentFacade.handleDatabaseException("Cannot commit transaction on configured objects removal", e);
+ }
+ }
+
+ @Override
+ public void update(UUID id, String type, Map<String, Object> attributes) throws AMQStoreException
+ {
+ update(false, id, type, attributes, null);
+ }
+
+ @Override
+ public void update(ConfiguredObjectRecord... records) throws AMQStoreException
+ {
+ update(false, records);
+ }
+
+ @Override
+ public void update(boolean createIfNecessary, ConfiguredObjectRecord... records) throws AMQStoreException
+ {
+ com.sleepycat.je.Transaction txn = _environmentFacade.getEnvironment().beginTransaction(null, null);
+ for(ConfiguredObjectRecord record : records)
+ {
+ update(createIfNecessary, record.getId(), record.getType(), record.getAttributes(), txn);
+ }
+ commitTransaction(txn);
+ }
+
+ private void update(boolean createIfNecessary, UUID id, String type, Map<String, Object> attributes, com.sleepycat.je.Transaction txn) throws AMQStoreException
+ {
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Updating " +type + ", id: " + id);
+ }
+
+ try
+ {
+ DatabaseEntry key = new DatabaseEntry();
+ UUIDTupleBinding keyBinding = UUIDTupleBinding.getInstance();
+ keyBinding.objectToEntry(id, key);
+
+ DatabaseEntry value = new DatabaseEntry();
+ DatabaseEntry newValue = new DatabaseEntry();
+ ConfiguredObjectBinding configuredObjectBinding = ConfiguredObjectBinding.getInstance();
+
+ OperationStatus status = getConfiguredObjectsDb().get(txn, key, value, LockMode.DEFAULT);
+ if (status == OperationStatus.SUCCESS || (createIfNecessary && status == OperationStatus.NOTFOUND))
+ {
+ ConfiguredObjectRecord newQueueRecord = new ConfiguredObjectRecord(id, type, attributes);
+
+ // write the updated entry to the store
+ configuredObjectBinding.objectToEntry(newQueueRecord, newValue);
+ status = getConfiguredObjectsDb().put(txn, key, newValue);
+ if (status != OperationStatus.SUCCESS)
+ {
+ throw new AMQStoreException("Error updating configuration details within the store: " + status);
+ }
+ }
+ else if (status != OperationStatus.NOTFOUND)
+ {
+ throw new AMQStoreException("Error finding configuration details within the store: " + status);
+ }
+ }
+ catch (DatabaseException e)
+ {
+ if (txn != null)
+ {
+ abortTransactionIgnoringException("Error updating configuration details within the store: " + e.getMessage(), txn);
+ }
+ throw _environmentFacade.handleDatabaseException("Error updating configuration details within the store: " + e,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
+ {
+
+ DatabaseEntry key = new DatabaseEntry();
+ QueueEntryBinding keyBinding = QueueEntryBinding.getInstance();
+ QueueEntryKey dd = new QueueEntryKey(queue.getId(), messageId);
+ keyBinding.objectToEntry(dd, key);
+ DatabaseEntry value = new DatabaseEntry();
+ ByteBinding.byteToEntry((byte) 0, value);
+
+ try
+ {
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Enqueuing message " + messageId + " on queue "
+ + (queue instanceof AMQQueue ? ((AMQQueue) queue).getName() + " with id " : "") + queue.getId()
+ + " in transaction " + tx);
+ }
+ getDeliveryDb().put(tx, key, value);
+ }
+ catch (DatabaseException e)
+ {
+ LOGGER.error("Failed to enqueue: " + e.getMessage(), e);
+ throw _environmentFacade.handleDatabaseException("Error writing enqueued message with id " + messageId + " for queue "
+ + (queue instanceof AMQQueue ? ((AMQQueue) queue).getName() + " with id " : "") + queue.getId()
+ + " to database", e);
+ }
+ }
+
+ /**
+ * Extracts a message from a specified queue, in a given transaction.
+ *
+ * @param tx The transaction for the operation.
+ * @param queue The 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
+ {
+
+ DatabaseEntry key = new DatabaseEntry();
+ QueueEntryBinding keyBinding = QueueEntryBinding.getInstance();
+ QueueEntryKey queueEntryKey = new QueueEntryKey(queue.getId(), messageId);
+ UUID id = queue.getId();
+ keyBinding.objectToEntry(queueEntryKey, key);
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Dequeue message id " + messageId + " from queue "
+ + (queue instanceof AMQQueue ? ((AMQQueue) queue).getName() + " with id " : "") + id);
+ }
+
+ try
+ {
+
+ OperationStatus status = getDeliveryDb().delete(tx, key);
+ if (status == OperationStatus.NOTFOUND)
+ {
+ throw new AMQStoreException("Unable to find message with id " + messageId + " on queue "
+ + (queue instanceof AMQQueue ? ((AMQQueue) queue).getName() + " with id " : "") + id);
+ }
+ else if (status != OperationStatus.SUCCESS)
+ {
+ throw new AMQStoreException("Unable to remove message with id " + messageId + " on queue"
+ + (queue instanceof AMQQueue ? ((AMQQueue) queue).getName() + " with id " : "") + id);
+ }
+
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Removed message " + messageId + " on queue "
+ + (queue instanceof AMQQueue ? ((AMQQueue) queue).getName() + " with id " : "") + id
+ + " from delivery db");
+
+ }
+ }
+ catch (DatabaseException e)
+ {
+
+ LOGGER.error("Failed to dequeue message " + messageId + " in transaction " + tx , e);
+
+ throw _environmentFacade.handleDatabaseException("Error accessing database while dequeuing message: " + e.getMessage(), e);
+ }
+ }
+
+
+ private void recordXid(com.sleepycat.je.Transaction txn,
+ long format,
+ byte[] globalId,
+ byte[] branchId,
+ org.apache.qpid.server.store.Transaction.Record[] enqueues,
+ org.apache.qpid.server.store.Transaction.Record[] dequeues) throws AMQStoreException
+ {
+ DatabaseEntry key = new DatabaseEntry();
+ Xid xid = new Xid(format, globalId, branchId);
+ XidBinding keyBinding = XidBinding.getInstance();
+ keyBinding.objectToEntry(xid,key);
+
+ DatabaseEntry value = new DatabaseEntry();
+ PreparedTransaction preparedTransaction = new PreparedTransaction(enqueues, dequeues);
+ PreparedTransactionBinding valueBinding = new PreparedTransactionBinding();
+ valueBinding.objectToEntry(preparedTransaction, value);
+
+ try
+ {
+ getXidDb().put(txn, key, value);
+ }
+ catch (DatabaseException e)
+ {
+ LOGGER.error("Failed to write xid: " + e.getMessage(), e);
+ throw _environmentFacade.handleDatabaseException("Error writing xid to database", e);
+ }
+ }
+
+ private void removeXid(com.sleepycat.je.Transaction txn, long format, byte[] globalId, byte[] branchId)
+ throws AMQStoreException
+ {
+ DatabaseEntry key = new DatabaseEntry();
+ Xid xid = new Xid(format, globalId, branchId);
+ XidBinding keyBinding = XidBinding.getInstance();
+
+ keyBinding.objectToEntry(xid, key);
+
+
+ try
+ {
+
+ OperationStatus status = getXidDb().delete(txn, key);
+ if (status == OperationStatus.NOTFOUND)
+ {
+ throw new AMQStoreException("Unable to find xid");
+ }
+ else if (status != OperationStatus.SUCCESS)
+ {
+ throw new AMQStoreException("Unable to remove xid");
+ }
+
+ }
+ catch (DatabaseException e)
+ {
+
+ LOGGER.error("Failed to remove xid in transaction " + txn, e);
+
+ throw _environmentFacade.handleDatabaseException("Error accessing database while removing xid: " + 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 (tx == null)
+ {
+ throw new AMQStoreException("Fatal internal error: transactional is null at commitTran");
+ }
+
+ _environmentFacade.commit(tx);
+ StoreFuture result = _committer.commit(tx, syncCommit);
+
+ if (LOGGER.isDebugEnabled())
+ {
+ String transactionType = syncCommit ? "synchronous" : "asynchronous";
+ LOGGER.debug("commitTranImpl completed " + transactionType + " transaction " + tx);
+ }
+
+ 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 (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("abortTran called for transaction " + tx);
+ }
+
+ try
+ {
+ tx.abort();
+ }
+ catch (DatabaseException e)
+ {
+ throw _environmentFacade.handleDatabaseException("Error aborting transaction: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Primarily for testing purposes.
+ *
+ * @param queueId
+ *
+ * @return a list of message ids for messages enqueued for a particular queue
+ */
+ List<Long> getEnqueuedMessages(UUID queueId) throws AMQStoreException
+ {
+ Cursor cursor = null;
+ try
+ {
+ cursor = getDeliveryDb().openCursor(null, null);
+
+ DatabaseEntry key = new DatabaseEntry();
+
+ QueueEntryKey dd = new QueueEntryKey(queueId, 0);
+
+ QueueEntryBinding keyBinding = QueueEntryBinding.getInstance();
+ keyBinding.objectToEntry(dd, key);
+
+ DatabaseEntry value = new DatabaseEntry();
+
+ LinkedList<Long> messageIds = new LinkedList<Long>();
+
+ OperationStatus status = cursor.getSearchKeyRange(key, value, LockMode.DEFAULT);
+ dd = keyBinding.entryToObject(key);
+
+ while ((status == OperationStatus.SUCCESS) && dd.getQueueId().equals(queueId))
+ {
+
+ messageIds.add(dd.getMessageId());
+ status = cursor.getNext(key, value, LockMode.DEFAULT);
+ if (status == OperationStatus.SUCCESS)
+ {
+ dd = keyBinding.entryToObject(key);
+ }
+ }
+
+ return messageIds;
+ }
+ catch (DatabaseException e)
+ {
+ throw new AMQStoreException("Database error: " + e.getMessage(), e);
+ }
+ finally
+ {
+ closeCursorSafely(cursor);
+ }
+ }
+
+ /**
+ * 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();
+ LongBinding.longToEntry(messageId, key);
+ DatabaseEntry value = new DatabaseEntry();
+ ContentBinding messageBinding = ContentBinding.getInstance();
+ messageBinding.objectToEntry(contentBody.array(), value);
+ try
+ {
+ OperationStatus status = getMessageContentDb().put(tx, key, value);
+ if (status != OperationStatus.SUCCESS)
+ {
+ throw new AMQStoreException("Error adding content for message id " + messageId + ": " + status);
+ }
+
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Storing content for message " + messageId + " in transaction " + tx);
+
+ }
+ }
+ catch (DatabaseException e)
+ {
+ throw _environmentFacade.handleDatabaseException("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
{
- _commitThreadWrapper.stopCommitThread();
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("storeMetaData called for transaction " + tx
+ + ", messageId " + messageId
+ + ", messageMetaData " + messageMetaData);
+ }
+
+ DatabaseEntry key = new DatabaseEntry();
+ LongBinding.longToEntry(messageId, key);
+ DatabaseEntry value = new DatabaseEntry();
+
+ MessageMetaDataBinding messageBinding = MessageMetaDataBinding.getInstance();
+ messageBinding.objectToEntry(messageMetaData, value);
+ try
+ {
+ getMessageMetaDataDb().put(tx, key, value);
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Storing message metadata for message id " + messageId + " in transaction " + tx);
+ }
+ }
+ catch (DatabaseException e)
+ {
+ throw _environmentFacade.handleDatabaseException("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 (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("public MessageMetaData getMessageMetaData(Long messageId = "
+ + messageId + "): called");
+ }
+
+ DatabaseEntry key = new DatabaseEntry();
+ LongBinding.longToEntry(messageId, key);
+ DatabaseEntry value = new DatabaseEntry();
+ MessageMetaDataBinding messageBinding = MessageMetaDataBinding.getInstance();
+
+ try
+ {
+ OperationStatus status = getMessageMetaDataDb().get(null, key, value, LockMode.READ_UNCOMMITTED);
+ if (status != OperationStatus.SUCCESS)
+ {
+ throw new AMQStoreException("Metadata not found for message with id " + messageId);
+ }
+
+ StorableMessageMetaData mdd = messageBinding.entryToObject(value);
+
+ return mdd;
+ }
+ catch (DatabaseException e)
+ {
+ throw _environmentFacade.handleDatabaseException("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();
+ LongBinding.longToEntry(messageId, contentKeyEntry);
+ DatabaseEntry value = new DatabaseEntry();
+ ContentBinding contentTupleBinding = ContentBinding.getInstance();
+
+
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Message Id: " + messageId + " Getting content body from offset: " + offset);
+ }
+
+ try
+ {
+
+ int written = 0;
+ OperationStatus status = getMessageContentDb().get(null, contentKeyEntry, value, LockMode.READ_UNCOMMITTED);
+ if (status == OperationStatus.SUCCESS)
+ {
+ byte[] dataAsBytes = contentTupleBinding.entryToObject(value);
+ int size = dataAsBytes.length;
+ if (offset > size)
+ {
+ throw new RuntimeException("Offset " + offset + " is greater than message size " + size
+ + " for message id " + messageId + "!");
+
+ }
+
+ written = size - offset;
+ if(written > dst.remaining())
+ {
+ written = dst.remaining();
+ }
+
+ dst.put(dataAsBytes, offset, written);
+ }
+ return written;
+ }
+ catch (DatabaseException e)
+ {
+ throw _environmentFacade.handleDatabaseException("Error getting AMQMessage with id " + messageId + " to database: " + e.getMessage(), e);
+ }
+ }
- super.closeInternal();
+ @Override
+ public boolean isPersistent()
+ {
+ return true;
}
@Override
- protected StoreFuture commit(com.sleepycat.je.Transaction tx, boolean syncCommit) throws DatabaseException
+ @SuppressWarnings("unchecked")
+ public <T extends StorableMessageMetaData> StoredMessage<T> addMessage(T metaData)
{
+ if(metaData.isPersistent())
+ {
+ return (StoredMessage<T>) new StoredBDBMessage(getNewMessageId(), metaData);
+ }
+ else
+ {
+ return new StoredMemoryMessage(getNewMessageId(), metaData);
+ }
+ }
+
+ /**
+ * Makes the specified configured object persistent.
+ *
+ * @param configuredObject Details of the configured object to store.
+ * @throws AMQStoreException If the operation fails for any reason.
+ */
+ private void storeConfiguredObjectEntry(ConfiguredObjectRecord configuredObject) throws AMQStoreException
+ {
+ if (_stateManager.isInState(State.ACTIVE))
+ {
+ LOGGER.debug("Storing configured object: " + configuredObject);
+ DatabaseEntry key = new DatabaseEntry();
+ UUIDTupleBinding keyBinding = UUIDTupleBinding.getInstance();
+ keyBinding.objectToEntry(configuredObject.getId(), key);
+
+ DatabaseEntry value = new DatabaseEntry();
+ ConfiguredObjectBinding queueBinding = ConfiguredObjectBinding.getInstance();
+
+ queueBinding.objectToEntry(configuredObject, value);
+ try
+ {
+ OperationStatus status = getConfiguredObjectsDb().put(null, key, value);
+ if (status != OperationStatus.SUCCESS)
+ {
+ throw new AMQStoreException("Error writing configured object " + configuredObject + " to database: "
+ + status);
+ }
+ }
+ catch (DatabaseException e)
+ {
+ throw _environmentFacade.handleDatabaseException("Error writing configured object " + configuredObject
+ + " to database: " + e.getMessage(), e);
+ }
+ }
+ }
+
+ private OperationStatus removeConfiguredObject(Transaction tx, UUID id) throws AMQStoreException
+ {
+
+ LOGGER.debug("Removing configured object: " + id);
+ DatabaseEntry key = new DatabaseEntry();
+ UUIDTupleBinding uuidBinding = UUIDTupleBinding.getInstance();
+ uuidBinding.objectToEntry(id, key);
try
{
- tx.commitNoSync();
+ return getConfiguredObjectsDb().delete(tx, key);
}
- catch(DatabaseException de)
+ catch (DatabaseException e)
{
- LOGGER.error("Got DatabaseException on commit, closing environment", de);
+ throw _environmentFacade.handleDatabaseException("Error deleting of configured object with id " + id + " from database", e);
+ }
+ }
+
+
+
+ private class StoredBDBMessage implements StoredMessage<StorableMessageMetaData>
+ {
+
+ private final long _messageId;
+ private final boolean _isRecovered;
+
+ private StorableMessageMetaData _metaData;
+ private volatile SoftReference<StorableMessageMetaData> _metaDataRef;
- closeEnvironmentSafely();
+ private byte[] _data;
+ private volatile SoftReference<byte[]> _dataRef;
- throw de;
+ StoredBDBMessage(long messageId, StorableMessageMetaData metaData)
+ {
+ this(messageId, metaData, false);
+ }
+
+ StoredBDBMessage(long messageId, StorableMessageMetaData metaData, boolean isRecovered)
+ {
+ _messageId = messageId;
+ _isRecovered = isRecovered;
+
+ if(!_isRecovered)
+ {
+ _metaData = metaData;
+ }
+ _metaDataRef = new SoftReference<StorableMessageMetaData>(metaData);
+ }
+
+ 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)
+ {
+ src = src.slice();
+
+ if(_data == null)
+ {
+ _data = new byte[src.remaining()];
+ _dataRef = new SoftReference<byte[]>(_data);
+ src.duplicate().get(_data);
+ }
+ else
+ {
+ byte[] oldData = _data;
+ _data = new byte[oldData.length + src.remaining()];
+ _dataRef = new SoftReference<byte[]>(_data);
+
+ System.arraycopy(oldData,0,_data,0,oldData.length);
+ src.duplicate().get(_data, oldData.length, src.remaining());
+ }
+
+ }
+
+ public int getContent(int offsetInMessage, java.nio.ByteBuffer dst)
+ {
+ byte[] data = _dataRef == null ? null : _dataRef.get();
+ if(data != null)
+ {
+ int length = Math.min(dst.remaining(), data.length - offsetInMessage);
+ dst.put(data, offsetInMessage, length);
+ return length;
+ }
+ else
+ {
+ try
+ {
+ return BDBMessageStore.this.getContent(_messageId, offsetInMessage, dst);
+ }
+ catch (AMQStoreException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ public ByteBuffer getContent(int offsetInMessage, int size)
+ {
+ byte[] data = _dataRef == null ? null : _dataRef.get();
+ if(data != null)
+ {
+ return ByteBuffer.wrap(data,offsetInMessage,size);
+ }
+ else
+ {
+ ByteBuffer buf = ByteBuffer.allocate(size);
+ int length = getContent(offsetInMessage, buf);
+ buf.limit(length);
+ buf.position(0);
+ return buf;
+ }
}
- return _commitThreadWrapper.commit(tx, syncCommit);
+ synchronized void store(com.sleepycat.je.Transaction txn)
+ {
+ if (!stored())
+ {
+ try
+ {
+ _dataRef = new SoftReference<byte[]>(_data);
+ BDBMessageStore.this.storeMetaData(txn, _messageId, _metaData);
+ BDBMessageStore.this.addContent(txn, _messageId, 0,
+ _data == null ? ByteBuffer.allocate(0) : ByteBuffer.wrap(_data));
+ }
+ catch (AMQStoreException e)
+ {
+ throw new RuntimeException(e);
+ }
+ catch (RuntimeException e)
+ {
+ LOGGER.error("RuntimeException during store", e);
+ throw e;
+ }
+ finally
+ {
+ _metaData = null;
+ _data = null;
+ }
+ }
+ }
+
+ public synchronized StoreFuture flushToStore()
+ {
+ if(!stored())
+ {
+ try
+ {
+ com.sleepycat.je.Transaction txn;
+ try
+ {
+ txn = _environmentFacade.getEnvironment().beginTransaction(
+ null, null);
+ }
+ catch (DatabaseException e)
+ {
+ throw _environmentFacade.handleDatabaseException("failed to begin transaction", e);
+ }
+ store(txn);
+ _environmentFacade.commit(txn);
+ _committer.commit(txn, true);
+
+ storedSizeChangeOccured(getMetaData().getContentSize());
+ }
+ catch (AMQStoreException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ return StoreFuture.IMMEDIATE_FUTURE;
+ }
+
+ public void remove()
+ {
+ try
+ {
+ int delta = getMetaData().getContentSize();
+ BDBMessageStore.this.removeMessage(_messageId, false);
+ storedSizeChangeOccured(-delta);
+
+ }
+ catch (AMQStoreException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private boolean stored()
+ {
+ return _metaData == null || _isRecovered;
+ }
+ }
+
+ private class BDBTransaction implements org.apache.qpid.server.store.Transaction
+ {
+ private com.sleepycat.je.Transaction _txn;
+ private int _storeSizeIncrease;
+
+ private BDBTransaction() throws AMQStoreException
+ {
+ try
+ {
+ _txn = _environmentFacade.getEnvironment().beginTransaction(null, null);
+ }
+ catch(DatabaseException e)
+ {
+ throw _environmentFacade.handleDatabaseException("Cannot create store transaction", e);
+ }
+ }
+
+ public void enqueueMessage(TransactionLogResource queue, EnqueueableMessage message) throws AMQStoreException
+ {
+ if(message.getStoredMessage() instanceof StoredBDBMessage)
+ {
+ final StoredBDBMessage storedMessage = (StoredBDBMessage) message.getStoredMessage();
+ storedMessage.store(_txn);
+ _storeSizeIncrease += storedMessage.getMetaData().getContentSize();
+ }
+
+ BDBMessageStore.this.enqueueMessage(_txn, queue, message.getMessageNumber());
+ }
+
+ public void dequeueMessage(TransactionLogResource queue, EnqueueableMessage message) throws AMQStoreException
+ {
+ BDBMessageStore.this.dequeueMessage(_txn, queue, message.getMessageNumber());
+ }
+
+ public void commitTran() throws AMQStoreException
+ {
+ BDBMessageStore.this.commitTranImpl(_txn, true);
+ BDBMessageStore.this.storedSizeChangeOccured(_storeSizeIncrease);
+ }
+
+ public StoreFuture commitTranAsync() throws AMQStoreException
+ {
+ BDBMessageStore.this.storedSizeChangeOccured(_storeSizeIncrease);
+ return BDBMessageStore.this.commitTranImpl(_txn, false);
+ }
+
+ public void abortTran() throws AMQStoreException
+ {
+ BDBMessageStore.this.abortTran(_txn);
+ }
+
+ public void removeXid(long format, byte[] globalId, byte[] branchId) throws AMQStoreException
+ {
+ BDBMessageStore.this.removeXid(_txn, format, globalId, branchId);
+ }
+
+ public void recordXid(long format, byte[] globalId, byte[] branchId, Record[] enqueues,
+ Record[] dequeues) throws AMQStoreException
+ {
+ BDBMessageStore.this.recordXid(_txn, format, globalId, branchId, enqueues, dequeues);
+ }
+ }
+
+ private void storedSizeChangeOccured(final int delta) throws AMQStoreException
+ {
+ try
+ {
+ storedSizeChange(delta);
+ }
+ catch(DatabaseException e)
+ {
+ throw _environmentFacade.handleDatabaseException("Stored size change exception", e);
+ }
+ }
+
+ private void storedSizeChange(final int delta)
+ {
+ if(getPersistentSizeHighThreshold() > 0)
+ {
+ synchronized (this)
+ {
+ // the delta supplied is an approximation of a store size change. we don;t want to check the statistic every
+ // time, so we do so only when there's been enough change that it is worth looking again. We do this by
+ // assuming the total size will change by less than twice the amount of the message data change.
+ long newSize = _totalStoreSize += 2*delta;
+
+ if(!_limitBusted && newSize > getPersistentSizeHighThreshold())
+ {
+ _totalStoreSize = getSizeOnDisk();
+
+ if(_totalStoreSize > getPersistentSizeHighThreshold())
+ {
+ _limitBusted = true;
+ _eventManager.notifyEvent(Event.PERSISTENT_MESSAGE_SIZE_OVERFULL);
+ }
+ }
+ else if(_limitBusted && newSize < getPersistentSizeLowThreshold())
+ {
+ long oldSize = _totalStoreSize;
+ _totalStoreSize = getSizeOnDisk();
+
+ if(oldSize <= _totalStoreSize)
+ {
+
+ reduceSizeOnDisk();
+
+ _totalStoreSize = getSizeOnDisk();
+
+ }
+
+ if(_totalStoreSize < getPersistentSizeLowThreshold())
+ {
+ _limitBusted = false;
+ _eventManager.notifyEvent(Event.PERSISTENT_MESSAGE_SIZE_UNDERFULL);
+ }
+
+
+ }
+ }
+ }
+ }
+
+ private void reduceSizeOnDisk()
+ {
+ _environmentFacade.getEnvironment().getConfig().setConfigParam(EnvironmentConfig.ENV_RUN_CLEANER, "false");
+ boolean cleaned = false;
+ while (_environmentFacade.getEnvironment().cleanLog() > 0)
+ {
+ cleaned = true;
+ }
+ if (cleaned)
+ {
+ CheckpointConfig force = new CheckpointConfig();
+ force.setForce(true);
+ _environmentFacade.getEnvironment().checkpoint(force);
+ }
+
+
+ _environmentFacade.getEnvironment().getConfig().setConfigParam(EnvironmentConfig.ENV_RUN_CLEANER, "true");
+ }
+
+ private long getSizeOnDisk()
+ {
+ return _environmentFacade.getEnvironment().getStats(null).getTotalLogSize();
+ }
+
+ private long getPersistentSizeLowThreshold()
+ {
+ return _persistentSizeLowThreshold;
+ }
+
+ private long getPersistentSizeHighThreshold()
+ {
+ return _persistentSizeHighThreshold;
+ }
+
+
+ @Override
+ public void onDelete()
+ {
+ String storeLocation = getStoreLocation();
+
+ if (storeLocation != null)
+ {
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Deleting store " + storeLocation);
+ }
+
+ File location = new File(storeLocation);
+ if (location.exists())
+ {
+ if (!FileUtils.delete(location, true))
+ {
+ LOGGER.error("Cannot delete " + storeLocation);
+ }
+ }
+ }
}
@Override
public String getStoreType()
{
- return TYPE;
+ return _type;
+ }
+
+ private Database getMessageContentDb()
+ {
+ return _environmentFacade.getOpenDatabase(MESSAGE_CONTENT_DB_NAME);
+ }
+
+ private Database getConfiguredObjectsDb()
+ {
+ return _environmentFacade.getOpenDatabase(CONFIGURED_OBJECTS_DB_NAME);
+ }
+
+ private Database getConfigVersionDb()
+ {
+ return _environmentFacade.getOpenDatabase(CONFIG_VERSION_DB_NAME);
+ }
+
+ private Database getMessageMetaDataDb()
+ {
+ return _environmentFacade.getOpenDatabase(MESSAGE_META_DATA_DB_NAME);
+ }
+
+ private Database getDeliveryDb()
+ {
+ return _environmentFacade.getOpenDatabase(DELIVERY_DB_NAME);
+ }
+
+ private Database getXidDb()
+ {
+ return _environmentFacade.getOpenDatabase(XID_DB_NAME);
}
}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreFactory.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreFactory.java
index d7c8b23d39..4abe81c56c 100644
--- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreFactory.java
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreFactory.java
@@ -24,6 +24,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+
import org.apache.commons.configuration.Configuration;
import org.apache.qpid.server.model.VirtualHost;
import org.apache.qpid.server.plugin.DurableConfigurationStoreFactory;
@@ -37,7 +38,7 @@ public class BDBMessageStoreFactory implements MessageStoreFactory, DurableConfi
@Override
public String getType()
{
- return BDBMessageStore.TYPE;
+ return StandardEnvironmentFacade.TYPE;
}
@Override
@@ -71,7 +72,7 @@ public class BDBMessageStoreFactory implements MessageStoreFactory, DurableConfi
if(initialSize != 0)
{
- return Collections.singletonMap("bdbEnvironmentConfig", (Object)attributes);
+ return Collections.singletonMap(BDBMessageStore.ENVIRONMENT_CONFIGURATION, (Object)attributes);
}
else
{
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/CommitThreadWrapper.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/CoalescingCommiter.java
index 598d20146c..d36f9539dd 100644
--- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/CommitThreadWrapper.java
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/CoalescingCommiter.java
@@ -27,31 +27,40 @@ import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.log4j.Logger;
import org.apache.qpid.server.store.StoreFuture;
-import com.sleepycat.je.CheckpointConfig;
import com.sleepycat.je.DatabaseException;
-import com.sleepycat.je.Environment;
import com.sleepycat.je.Transaction;
-public class CommitThreadWrapper
+public class CoalescingCommiter implements Committer
{
private final CommitThread _commitThread;
-
- public CommitThreadWrapper(String name, Environment env)
+
+ public CoalescingCommiter(String name, EnvironmentFacade environmentFacade)
{
- _commitThread = new CommitThread(name, env);
+ _commitThread = new CommitThread("Commit-Thread-" + name, environmentFacade);
}
- public void startCommitThread()
+ @Override
+ public void start()
{
_commitThread.start();
}
- public void stopCommitThread() throws InterruptedException
+ @Override
+ public void stop()
{
_commitThread.close();
- _commitThread.join();
+ try
+ {
+ _commitThread.join();
+ }
+ catch (InterruptedException ie)
+ {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException("Commit thread has not shutdown", ie);
+ }
}
+ @Override
public StoreFuture commit(Transaction tx, boolean syncCommit)
{
BDBCommitFuture commitFuture = new BDBCommitFuture(_commitThread, tx, syncCommit);
@@ -65,9 +74,9 @@ public class CommitThreadWrapper
private final CommitThread _commitThread;
private final Transaction _tx;
- private DatabaseException _databaseException;
+ private final boolean _syncCommit;
+ private RuntimeException _databaseException;
private boolean _complete;
- private boolean _syncCommit;
public BDBCommitFuture(CommitThread commitThread, Transaction tx, boolean syncCommit)
{
@@ -87,7 +96,7 @@ public class CommitThreadWrapper
notifyAll();
}
- public synchronized void abort(DatabaseException databaseException)
+ public synchronized void abort(RuntimeException databaseException)
{
_complete = true;
_databaseException = databaseException;
@@ -165,15 +174,13 @@ public class CommitThreadWrapper
private final AtomicBoolean _stopped = new AtomicBoolean(false);
private final Queue<BDBCommitFuture> _jobQueue = new ConcurrentLinkedQueue<BDBCommitFuture>();
- private final CheckpointConfig _config = new CheckpointConfig();
private final Object _lock = new Object();
- private Environment _environment;
+ private final EnvironmentFacade _environmentFacade;
- public CommitThread(String name, Environment env)
+ public CommitThread(String name, EnvironmentFacade environmentFacade)
{
super(name);
- _config.setForce(true);
- _environment = env;
+ _environmentFacade = environmentFacade;
}
public void explicitNotify()
@@ -194,7 +201,7 @@ public class CommitThreadWrapper
{
try
{
- // RHM-7 Periodically wake up and check, just in case we
+ // Periodically wake up and check, just in case we
// missed a notification. Don't want to lock the broker hard.
_lock.wait(1000);
}
@@ -219,7 +226,7 @@ public class CommitThreadWrapper
startTime = System.currentTimeMillis();
}
- _environment.flushLog(true);
+ _environmentFacade.getEnvironment().flushLog(true);
if(LOGGER.isDebugEnabled())
{
@@ -252,7 +259,7 @@ public class CommitThreadWrapper
try
{
- _environment.close();
+ _environmentFacade.close();
}
catch (DatabaseException ex)
{
@@ -269,7 +276,10 @@ public class CommitThreadWrapper
public void addJob(BDBCommitFuture commit, final boolean sync)
{
-
+ if (_stopped.get())
+ {
+ throw new IllegalStateException("Commit thread is stopped");
+ }
_jobQueue.add(commit);
if(sync)
{
@@ -282,9 +292,15 @@ public class CommitThreadWrapper
public void close()
{
+ RuntimeException e = new RuntimeException("Commit thread has been closed, transaction aborted");
synchronized (_lock)
{
_stopped.set(true);
+ BDBCommitFuture commit = null;
+ while ((commit = _jobQueue.poll()) != null)
+ {
+ commit.abort(e);
+ }
_lock.notifyAll();
}
}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/Committer.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/Committer.java
new file mode 100644
index 0000000000..36ee2ad306
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/Committer.java
@@ -0,0 +1,55 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb;
+
+import org.apache.qpid.server.store.StoreFuture;
+
+import com.sleepycat.je.Transaction;
+
+public interface Committer
+{
+ void start();
+
+ StoreFuture commit(Transaction tx, boolean syncCommit);
+
+ void stop();
+
+ Committer IMMEDIATE_FUTURE_COMMITTER = new Committer()
+ {
+
+ @Override
+ public void start()
+ {
+ }
+
+ @Override
+ public StoreFuture commit(Transaction tx, boolean syncCommit)
+ {
+ return StoreFuture.IMMEDIATE_FUTURE;
+ }
+
+ @Override
+ public void stop()
+ {
+ }
+ };
+
+} \ No newline at end of file
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/EnvironmentFacade.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/EnvironmentFacade.java
new file mode 100644
index 0000000000..60ff529203
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/EnvironmentFacade.java
@@ -0,0 +1,61 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.qpid.AMQStoreException;
+
+import com.sleepycat.je.Database;
+import com.sleepycat.je.DatabaseConfig;
+import com.sleepycat.je.DatabaseException;
+import com.sleepycat.je.Environment;
+import com.sleepycat.je.EnvironmentConfig;
+
+public interface EnvironmentFacade
+{
+ @SuppressWarnings("serial")
+ final Map<String, String> ENVCONFIG_DEFAULTS = Collections.unmodifiableMap(new HashMap<String, String>()
+ {{
+ put(EnvironmentConfig.LOCK_N_LOCK_TABLES, "7");
+ // Turn off stats generation - feature introduced (and on by default) from BDB JE 5.0.84
+ put(EnvironmentConfig.STATS_COLLECT, "false");
+ }});
+
+ Environment getEnvironment();
+
+ Committer createCommitter(String name);
+
+ void openDatabases(DatabaseConfig dbConfig, String... databaseNames);
+
+ Database getOpenDatabase(String name);
+
+ void commit(com.sleepycat.je.Transaction tx) throws AMQStoreException;
+
+ AMQStoreException handleDatabaseException(String contextMessage, DatabaseException e);
+
+ void close();
+
+ String getStoreLocation();
+
+}
diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/HAMessageStoreSmokeTest.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/EnvironmentFacadeFactory.java
index 3f32df4b0c..b784e436b9 100644
--- a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/HAMessageStoreSmokeTest.java
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/EnvironmentFacadeFactory.java
@@ -1,4 +1,5 @@
/*
+ *
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
@@ -19,26 +20,13 @@
*/
package org.apache.qpid.server.store.berkeleydb;
-import org.apache.commons.configuration.ConfigurationException;
import org.apache.qpid.server.model.VirtualHost;
-import org.apache.qpid.test.utils.QpidTestCase;
-
-import static org.mockito.Mockito.mock;
-public class HAMessageStoreSmokeTest extends QpidTestCase
+public interface EnvironmentFacadeFactory
{
- private final BDBHAMessageStore _store = new BDBHAMessageStore();
- public void testMissingHAConfigThrowsException() throws Exception
- {
- try
- {
- _store.configure(mock(VirtualHost.class));
- fail("Expected an exception to be thrown");
- }
- catch (ConfigurationException ce)
- {
- assertTrue(ce.getMessage().contains("BDB HA configuration key not found"));
- }
- }
+ EnvironmentFacade createEnvironmentFacade(VirtualHost virtualHost, boolean isMessageStore);
+
+ String getType();
+
}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/LoggingAsyncExceptionListener.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/LoggingAsyncExceptionListener.java
new file mode 100644
index 0000000000..b13766a136
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/LoggingAsyncExceptionListener.java
@@ -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.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb;
+
+import org.apache.log4j.Logger;
+
+import com.sleepycat.je.ExceptionEvent;
+import com.sleepycat.je.ExceptionListener;
+
+public class LoggingAsyncExceptionListener implements ExceptionListener
+{
+ private static final Logger LOGGER = Logger.getLogger(LoggingAsyncExceptionListener.class);
+
+ @Override
+ public void exceptionThrown(ExceptionEvent event)
+ {
+ LOGGER.error("Asynchronous exception thrown by BDB thread '" + event.getThreadName() + "'", event.getException());
+ }
+} \ No newline at end of file
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/StandardEnvironmentFacade.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/StandardEnvironmentFacade.java
new file mode 100644
index 0000000000..3b6eef832b
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/StandardEnvironmentFacade.java
@@ -0,0 +1,229 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQStoreException;
+
+import com.sleepycat.je.Database;
+import com.sleepycat.je.DatabaseConfig;
+import com.sleepycat.je.DatabaseException;
+import com.sleepycat.je.Environment;
+import com.sleepycat.je.EnvironmentConfig;
+
+public class StandardEnvironmentFacade implements EnvironmentFacade
+{
+ private static final Logger LOGGER = Logger.getLogger(StandardEnvironmentFacade.class);
+ public static final String TYPE = "BDB";
+
+ private final String _storePath;
+ private final Map<String, Database> _databases = new HashMap<String, Database>();
+
+ private Environment _environment;
+
+ public StandardEnvironmentFacade(String storePath, Map<String, String> attributes)
+ {
+ _storePath = storePath;
+
+ if (LOGGER.isInfoEnabled())
+ {
+ LOGGER.info("Creating environment at environment path " + _storePath);
+ }
+
+ File environmentPath = new File(storePath);
+ 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.");
+ }
+ }
+
+ EnvironmentConfig envConfig = new EnvironmentConfig();
+ envConfig.setAllowCreate(true);
+ envConfig.setTransactional(true);
+
+ for (Map.Entry<String, String> configItem : attributes.entrySet())
+ {
+ LOGGER.debug("Setting EnvironmentConfig key " + configItem.getKey() + " to '" + configItem.getValue() + "'");
+ envConfig.setConfigParam(configItem.getKey(), configItem.getValue());
+ }
+
+ envConfig.setExceptionListener(new LoggingAsyncExceptionListener());
+
+ _environment = new Environment(environmentPath, envConfig);
+ }
+
+ @Override
+ public void commit(com.sleepycat.je.Transaction tx) throws AMQStoreException
+ {
+ try
+ {
+ tx.commitNoSync();
+ }
+ catch (DatabaseException de)
+ {
+ LOGGER.error("Got DatabaseException on commit, closing environment", de);
+
+ closeEnvironmentSafely();
+
+ throw handleDatabaseException("Got DatabaseException on commit", de);
+ }
+ }
+
+ @Override
+ public void close()
+ {
+ closeDatabases();
+ closeEnvironment();
+ }
+
+ private void closeDatabases()
+ {
+ RuntimeException firstThrownException = null;
+ for (Database database : _databases.values())
+ {
+ try
+ {
+ database.close();
+ }
+ catch(RuntimeException e)
+ {
+ if (firstThrownException == null)
+ {
+ firstThrownException = e;
+ }
+ }
+ }
+ if (firstThrownException != null)
+ {
+ throw firstThrownException;
+ }
+ }
+
+ private void closeEnvironmentSafely()
+ {
+ if (_environment != null)
+ {
+ if (_environment.isValid())
+ {
+ try
+ {
+ closeDatabases();
+ }
+ catch(Exception e)
+ {
+ LOGGER.error("Exception closing environment databases", e);
+ }
+ }
+ try
+ {
+ _environment.close();
+ }
+ catch (DatabaseException ex)
+ {
+ LOGGER.error("Exception closing store environment", ex);
+ }
+ catch (IllegalStateException ex)
+ {
+ LOGGER.error("Exception closing store environment", ex);
+ }
+ finally
+ {
+ _environment = null;
+ }
+ }
+ }
+
+ @Override
+ public Environment getEnvironment()
+ {
+ return _environment;
+ }
+
+ private void closeEnvironment()
+ {
+ if (_environment != null)
+ {
+ // 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.
+ try
+ {
+ _environment.cleanLog();
+ }
+ finally
+ {
+ _environment.close();
+ _environment = null;
+ }
+ }
+ }
+
+ @Override
+ public AMQStoreException handleDatabaseException(String contextMessage, DatabaseException e)
+ {
+ if (_environment != null && !_environment.isValid())
+ {
+ closeEnvironmentSafely();
+ }
+ return new AMQStoreException(contextMessage, e);
+ }
+
+ @Override
+ public void openDatabases(DatabaseConfig dbConfig, String... databaseNames)
+ {
+ for (String databaseName : databaseNames)
+ {
+ Database database = _environment.openDatabase(null, databaseName, dbConfig);
+ _databases .put(databaseName, database);
+ }
+ }
+
+ @Override
+ public Database getOpenDatabase(String name)
+ {
+ Database database = _databases.get(name);
+ if (database == null)
+ {
+ throw new IllegalArgumentException("Database with name '" + name + "' has not been opened");
+ }
+ return database;
+ }
+
+ @Override
+ public Committer createCommitter(String name)
+ {
+ return new CoalescingCommiter(name, this);
+ }
+
+ @Override
+ public String getStoreLocation()
+ {
+ return _storePath;
+ }
+
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/StandardEnvironmentFacadeFactory.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/StandardEnvironmentFacadeFactory.java
new file mode 100644
index 0000000000..384ceba98a
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/StandardEnvironmentFacadeFactory.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;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.qpid.server.configuration.BrokerProperties;
+import org.apache.qpid.server.model.VirtualHost;
+
+public class StandardEnvironmentFacadeFactory implements EnvironmentFacadeFactory
+{
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public EnvironmentFacade createEnvironmentFacade(VirtualHost virtualHost, boolean isMessageStore)
+ {
+ Map<String, String> envConfigMap = new HashMap<String, String>();
+ envConfigMap.putAll(EnvironmentFacade.ENVCONFIG_DEFAULTS);
+
+ Object environmentConfigurationAttributes = virtualHost.getAttribute(BDBMessageStore.ENVIRONMENT_CONFIGURATION);
+ if (environmentConfigurationAttributes instanceof Map)
+ {
+ envConfigMap.putAll((Map<String, String>) environmentConfigurationAttributes);
+ }
+
+ String name = virtualHost.getName();
+ final String defaultPath = System.getProperty(BrokerProperties.PROPERTY_QPID_WORK) + File.separator + "bdbstore" + File.separator + name;
+
+ String storeLocation;
+ if(isMessageStore)
+ {
+ storeLocation = (String) virtualHost.getAttribute(VirtualHost.STORE_PATH);
+ if(storeLocation == null)
+ {
+ storeLocation = defaultPath;
+ }
+ }
+ else // we are acting only as the durable config store
+ {
+ storeLocation = (String) virtualHost.getAttribute(VirtualHost.CONFIG_STORE_PATH);
+ if(storeLocation == null)
+ {
+ storeLocation = defaultPath;
+ }
+ }
+
+ return new StandardEnvironmentFacade(storeLocation, envConfigMap);
+ }
+
+ @Override
+ public String getType()
+ {
+ return StandardEnvironmentFacade.TYPE;
+ }
+
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/DatabasePinger.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/DatabasePinger.java
new file mode 100644
index 0000000000..38fdf34196
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/DatabasePinger.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.replication;
+
+import org.apache.qpid.server.store.berkeleydb.EnvironmentFacade;
+
+import com.sleepycat.bind.tuple.IntegerBinding;
+import com.sleepycat.bind.tuple.LongBinding;
+import com.sleepycat.je.Database;
+import com.sleepycat.je.DatabaseEntry;
+import com.sleepycat.je.DatabaseException;
+import com.sleepycat.je.Transaction;
+
+public class DatabasePinger
+{
+ public static final String PING_DATABASE_NAME = "PINGDB";
+ private static final int ID = 0;
+
+ public void pingDb(EnvironmentFacade facade)
+ {
+ try
+ {
+ final Database db = facade.getOpenDatabase(PING_DATABASE_NAME);
+
+ DatabaseEntry key = new DatabaseEntry();
+ IntegerBinding.intToEntry(ID, key);
+
+ DatabaseEntry value = new DatabaseEntry();
+ LongBinding.longToEntry(System.currentTimeMillis(), value);
+ Transaction txn = null;
+ try
+ {
+ txn = facade.getEnvironment().beginTransaction(null, null);
+ db.put(txn, key, value);
+ txn.commit();
+ txn = null;
+ }
+ finally
+ {
+ try
+ {
+ if (txn != null)
+ {
+ txn.abort();
+ }
+ }
+ finally
+ {
+ db.close();
+ }
+ }
+ }
+ catch (DatabaseException de)
+ {
+ facade.handleDatabaseException("DatabaseException from DatabasePinger ", de);
+ }
+ }
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/LocalReplicationNode.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/LocalReplicationNode.java
new file mode 100644
index 0000000000..ef2c48463b
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/LocalReplicationNode.java
@@ -0,0 +1,437 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.replication;
+
+import java.lang.reflect.Type;
+import java.security.AccessControlException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.qpid.server.configuration.IllegalConfigurationException;
+import org.apache.qpid.server.configuration.updater.TaskExecutor;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.LifetimePolicy;
+import org.apache.qpid.server.model.ReplicationNode;
+import org.apache.qpid.server.model.State;
+import org.apache.qpid.server.model.Statistics;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.model.adapter.AbstractAdapter;
+import org.apache.qpid.server.model.adapter.NoStatistics;
+import org.apache.qpid.server.util.MapValueConverter;
+import org.apache.qpid.server.util.ParameterizedTypeImpl;
+
+import com.sleepycat.je.DatabaseException;
+import com.sleepycat.je.Durability;
+import com.sleepycat.je.Durability.ReplicaAckPolicy;
+import com.sleepycat.je.Durability.SyncPolicy;
+import com.sleepycat.je.rep.ReplicatedEnvironment;
+
+public class LocalReplicationNode extends AbstractAdapter implements ReplicationNode
+{
+
+ private static final Durability DEFAULT_DURABILITY = new Durability(SyncPolicy.NO_SYNC, SyncPolicy.NO_SYNC,
+ ReplicaAckPolicy.SIMPLE_MAJORITY);
+ static final boolean DEFAULT_DESIGNATED_PRIMARY = false;
+ static final int DEFAULT_PRIORITY = 1;
+ static final int DEFAULT_QUORUM_OVERRIDE = 0;
+
+ @SuppressWarnings("serial")
+ static final Map<String, Object> DEFAULTS = new HashMap<String, Object>()
+ {{
+ put(DURABILITY, DEFAULT_DURABILITY.toString());
+ put(COALESCING_SYNC, true);
+ put(DESIGNATED_PRIMARY, DEFAULT_DESIGNATED_PRIMARY);
+ put(PRIORITY, DEFAULT_PRIORITY);
+ put(QUORUM_OVERRIDE, DEFAULT_QUORUM_OVERRIDE);
+ //TODO: add defaults for parameters and replicatedParameters
+ }};
+
+ @SuppressWarnings("serial")
+ private static final Map<String, Type> ATTRIBUTE_TYPES = new HashMap<String, Type>()
+ {{
+ put(ID, UUID.class);
+ put(NAME, String.class);
+ put(GROUP_NAME, String.class);
+ put(HOST_PORT, String.class);
+ put(HELPER_HOST_PORT, String.class);
+ put(DURABILITY, String.class);
+ put(COALESCING_SYNC, Boolean.class);
+ put(DESIGNATED_PRIMARY, Boolean.class);
+ put(PRIORITY, Integer.class);
+ put(QUORUM_OVERRIDE, Integer.class);
+ put(ROLE, String.class);
+ put(JOIN_TIME, Long.class);
+ put(PARAMETERS, new ParameterizedTypeImpl(Map.class, String.class, String.class));
+ put(REPLICATION_PARAMETERS, new ParameterizedTypeImpl(Map.class, String.class, String.class));
+ put(STORE_PATH, String.class);
+ put(LAST_KNOWN_REPLICATION_TRANSACTION_ID, Long.class);
+ }};
+
+ static final String[] IMMUTABLE_ATTRIBUTES = {ReplicationNode.GROUP_NAME, ReplicationNode.HELPER_HOST_PORT,
+ ReplicationNode.HOST_PORT, ReplicationNode.COALESCING_SYNC, ReplicationNode.DURABILITY,
+ ReplicationNode.JOIN_TIME, ReplicationNode.LAST_KNOWN_REPLICATION_TRANSACTION_ID, ReplicationNode.NAME,
+ ReplicationNode.STORE_PATH, ReplicationNode.PARAMETERS, ReplicationNode.REPLICATION_PARAMETERS};
+
+ private final VirtualHost _virtualHost;
+ private volatile ReplicatedEnvironmentFacade _replicatedEnvironmentFacade;
+
+ //TODO: add state management
+ public LocalReplicationNode(UUID id, Map<String, Object> attributes, VirtualHost virtualHost, TaskExecutor taskExecutor)
+ {
+ super(id, DEFAULTS, validateAttributes(MapValueConverter.convert(attributes, ATTRIBUTE_TYPES)), taskExecutor);
+ _virtualHost = virtualHost;
+ addParent(VirtualHost.class, virtualHost);
+ validateAttributes(attributes);
+ }
+
+ private static Map<String, Object> validateAttributes(Map<String, Object> attributes)
+ {
+ if (attributes.get(NAME) == null)
+ {
+ throw new IllegalConfigurationException("Name is not specified");
+ }
+ if (attributes.get(GROUP_NAME) == null)
+ {
+ throw new IllegalConfigurationException("Group name is not specified");
+ }
+ if (attributes.get(HOST_PORT) == null)
+ {
+ throw new IllegalConfigurationException("Host and port attribute is not specified");
+ }
+ if (attributes.get(HELPER_HOST_PORT) == null)
+ {
+ throw new IllegalConfigurationException("Helper host and port attribute is not specified");
+ }
+ Object storePath = attributes.get(STORE_PATH);
+ if (storePath == null || storePath.equals(""))
+ {
+ throw new IllegalConfigurationException("Store path is not specified for the replication node");
+ }
+ return attributes;
+ }
+
+ @Override
+ public String getName()
+ {
+ return (String)getAttribute(NAME);
+ }
+
+ @Override
+ public String setName(String currentName, String desiredName)
+ throws IllegalStateException, AccessControlException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public State getActualState()
+ {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public boolean isDurable()
+ {
+ return true;
+ }
+
+ @Override
+ public void setDurable(boolean durable) throws IllegalStateException,
+ AccessControlException, IllegalArgumentException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public LifetimePolicy getLifetimePolicy()
+ {
+ return LifetimePolicy.PERMANENT;
+ }
+
+ @Override
+ public LifetimePolicy setLifetimePolicy(LifetimePolicy expected,
+ LifetimePolicy desired) throws IllegalStateException,
+ AccessControlException, IllegalArgumentException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long getTimeToLive()
+ {
+ return 0;
+ }
+
+ @Override
+ public long setTimeToLive(long expected, long desired)
+ throws IllegalStateException, AccessControlException,
+ IllegalArgumentException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Collection<String> getAttributeNames()
+ {
+ return ReplicationNode.AVAILABLE_ATTRIBUTES;
+ }
+
+ @Override
+ public Object getAttribute(String attributeName)
+ {
+ if (ReplicationNode.ID.equals(attributeName))
+ {
+ return getId();
+ }
+ else if (ReplicationNode.LIFETIME_POLICY.equals(attributeName))
+ {
+ return getLifetimePolicy();
+ }
+ else if (ReplicationNode.DURABLE.equals(attributeName))
+ {
+ return isDurable();
+ }
+ else if(STATE.equals(attributeName))
+ {
+ return getActualState();
+ }
+ else if(TIME_TO_LIVE.equals(attributeName))
+ {
+ return getLifetimePolicy();
+ }
+ if (_replicatedEnvironmentFacade != null)
+ {
+ try
+ {
+ if(ROLE.equals(attributeName))
+ {
+ return _replicatedEnvironmentFacade.getNodeState();
+ }
+ else if(JOIN_TIME.equals(attributeName))
+ {
+ return _replicatedEnvironmentFacade.getJoinTime();
+ }
+ else if(LAST_KNOWN_REPLICATION_TRANSACTION_ID.equals(attributeName))
+ {
+ return _replicatedEnvironmentFacade.getLastKnownReplicationTransactionId();
+ }
+ else if(QUORUM_OVERRIDE.equals(attributeName))
+ {
+ return _replicatedEnvironmentFacade.getElectableGroupSizeOverride();
+ }
+ else if(DESIGNATED_PRIMARY.equals(attributeName))
+ {
+ return _replicatedEnvironmentFacade.isDesignatedPrimary();
+ }
+ else if(PRIORITY.equals(attributeName))
+ {
+ return _replicatedEnvironmentFacade.getPriority();
+ }
+ }
+ catch(IllegalStateException e)
+ {
+ // ignore, as attribute value will be returned from actual/default attribute maps if present
+ }
+ catch(DatabaseException e)
+ {
+ // ignore, as attribute value will be returned from actual/default attribute maps if present
+ }
+ }
+ return super.getAttribute(attributeName);
+ }
+
+ @Override
+ public Statistics getStatistics()
+ {
+ return NoStatistics.getInstance();
+ }
+
+ @Override
+ public <C extends ConfiguredObject> Collection<C> getChildren(Class<C> clazz)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <C extends ConfiguredObject> C createChild(Class<C> childClass,
+ Map<String, Object> attributes, ConfiguredObject... otherParents)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean changeAttribute(final String name, final Object expected, final Object desired)
+ {
+ updateReplicatedEnvironmentFacade(name, desired);
+ if (!ROLE.equals(name))
+ {
+ return super.changeAttribute(name, expected, desired);
+ }
+ return false;
+ }
+
+ @Override
+ public void changeAttributes(Map<String, Object> attributes)
+ throws IllegalStateException, AccessControlException,
+ IllegalArgumentException
+ {
+ Map<String, Object> convertedAttributes = MapValueConverter.convert(attributes, ATTRIBUTE_TYPES);
+
+ checkWhetherImmutableAttributeChanged(convertedAttributes);
+
+ super.changeAttributes(convertedAttributes);
+ }
+
+ private void updateReplicatedEnvironmentFacade(String attributeName, Object attributeValue)
+ {
+ if (_replicatedEnvironmentFacade != null)
+ {
+ if (PRIORITY.equals(attributeName))
+ {
+ int priority = (Integer)attributeValue;
+ try
+ {
+ _replicatedEnvironmentFacade.setPriority(priority);
+ }
+ catch(Exception e)
+ {
+ throw new IllegalConfigurationException("Cannot set attribute " + PRIORITY + " to " + priority, e);
+ }
+ }
+
+ if (DESIGNATED_PRIMARY.equals(attributeName))
+ {
+ boolean designatedPrimary = (Boolean)attributeValue;
+ try
+ {
+ _replicatedEnvironmentFacade.setDesignatedPrimary(designatedPrimary);
+ }
+ catch(Exception e)
+ {
+ throw new IllegalConfigurationException("Cannot set attribute '" + DESIGNATED_PRIMARY + "' to " + designatedPrimary, e);
+ }
+ }
+
+ if (QUORUM_OVERRIDE.equals(attributeName))
+ {
+ int quorumOverride = (Integer)attributeValue;
+ try
+ {
+ _replicatedEnvironmentFacade.setElectableGroupSizeOverride(quorumOverride);
+ }
+ catch(Exception e)
+ {
+ throw new IllegalConfigurationException("Cannot set attribute '" + QUORUM_OVERRIDE + "' to " + quorumOverride, e);
+ }
+ }
+ }
+
+ if (ROLE.equals(attributeName))
+ {
+ String currentRole = (String)getAttribute(ROLE);
+ if (!ReplicatedEnvironment.State.REPLICA.name().equals(currentRole))
+ {
+ throw new IllegalConfigurationException("Cannot transfer mastership when not a replica");
+ }
+
+ // we do not want to write role into the store
+ String role = (String)attributeValue;
+
+ if (ReplicatedEnvironment.State.MASTER.name().equals(role) )
+ {
+ try
+ {
+ _replicatedEnvironmentFacade.transferMasterToSelfAsynchronously();
+ }
+ catch(Exception e)
+ {
+ throw new IllegalConfigurationException("Cannot transfer mastership", e);
+ }
+ }
+ else
+ {
+ throw new IllegalConfigurationException("Changing role to other value then " + ReplicatedEnvironment.State.MASTER.name() + " is unsupported");
+ }
+ }
+ }
+
+ private void checkWhetherImmutableAttributeChanged(Map<String, Object> convertedAttributes)
+ {
+ for (int i = 0; i < IMMUTABLE_ATTRIBUTES.length; i++)
+ {
+ String attributeName = IMMUTABLE_ATTRIBUTES[i];
+ if (convertedAttributes.containsKey(attributeName))
+ {
+ Object newValue = convertedAttributes.get(attributeName);
+ Object currentValue = getAttribute(attributeName);
+ if (currentValue == null)
+ {
+ if (newValue != null)
+ {
+ throw new IllegalConfigurationException("Cannot change value of immutable attribute " + attributeName);
+ }
+ }
+ else
+ {
+ if (!currentValue.equals(newValue))
+ {
+ throw new IllegalConfigurationException("Cannot change value of immutable attribute " + attributeName);
+ }
+ }
+ }
+ }
+ }
+
+ protected VirtualHost getVirtualHost()
+ {
+ return _virtualHost;
+ }
+
+ @Override
+ protected boolean setState(State currentState, State desiredState)
+ {
+ if (desiredState == State.ACTIVE || desiredState == State.STOPPED)
+ {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isLocal()
+ {
+ return true;
+ }
+
+ public void setReplicatedEnvironmentFacade(ReplicatedEnvironmentFacade replicatedEnvironmentFacade)
+ {
+ _replicatedEnvironmentFacade = replicatedEnvironmentFacade;
+ }
+
+ public Object getActualAttribute(String attributeName)
+ {
+ return super.getAttribute(attributeName);
+ }
+
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/LocalReplicationNodeFactory.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/LocalReplicationNodeFactory.java
new file mode 100644
index 0000000000..6c87b58299
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/LocalReplicationNodeFactory.java
@@ -0,0 +1,54 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb.replication;
+
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.model.ReplicationNode;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.plugin.ReplicationNodeFactory;
+import org.apache.qpid.server.store.berkeleydb.BDBHAVirtualHostFactory;
+
+public class LocalReplicationNodeFactory implements ReplicationNodeFactory
+{
+
+ @Override
+ public String getType()
+ {
+ return BDBHAVirtualHostFactory.TYPE;
+ }
+
+ @Override
+ public ReplicationNode createInstance(UUID id,
+ Map<String, Object> attributes, VirtualHost virtualHost)
+ {
+ // TODO KW Temporary code
+ Broker broker = virtualHost.getParent(Broker.class);
+ if (broker == null)
+ {
+ throw new IllegalStateException("Cannot find the broker among virtual host parents");
+ }
+ return new LocalReplicationNode(id, attributes, virtualHost, broker.getTaskExecutor());
+ }
+
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/RemoteReplicationNode.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/RemoteReplicationNode.java
new file mode 100644
index 0000000000..de301b91ba
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/RemoteReplicationNode.java
@@ -0,0 +1,365 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.replication;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.security.AccessControlException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.server.configuration.IllegalConfigurationException;
+import org.apache.qpid.server.configuration.updater.TaskExecutor;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.IllegalStateTransitionException;
+import org.apache.qpid.server.model.LifetimePolicy;
+import org.apache.qpid.server.model.ReplicationNode;
+import org.apache.qpid.server.model.State;
+import org.apache.qpid.server.model.Statistics;
+import org.apache.qpid.server.model.UUIDGenerator;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.model.adapter.AbstractAdapter;
+import org.apache.qpid.server.model.adapter.NoStatistics;
+import org.apache.qpid.server.util.MapValueConverter;
+
+import com.sleepycat.je.rep.MasterStateException;
+import com.sleepycat.je.rep.NodeState;
+import com.sleepycat.je.rep.ReplicatedEnvironment;
+import com.sleepycat.je.rep.utilint.ServiceDispatcher.ServiceConnectFailedException;
+
+/**
+ * Represents a remote replication node in a BDB group.
+ */
+public class RemoteReplicationNode extends AbstractAdapter implements ReplicationNode
+{
+ private static final Logger LOGGER = Logger.getLogger(RemoteReplicationNode.class);
+
+ @SuppressWarnings("serial")
+ private static final Map<String, Type> ATTRIBUTE_TYPES = new HashMap<String, Type>()
+ {{
+ put(ROLE, String.class);
+ }};
+
+ private final com.sleepycat.je.rep.ReplicationNode _replicationNode;
+ private final String _hostPort;
+ private final String _groupName;
+ private final ReplicatedEnvironmentFacade _replicatedEnvironmentFacade;
+
+ private volatile String _role;
+ private volatile long _joinTime;
+ private volatile long _lastTransactionId;
+
+ private final AtomicReference<State> _state;
+
+ public RemoteReplicationNode(com.sleepycat.je.rep.ReplicationNode replicationNode, VirtualHost virtualHost,
+ TaskExecutor taskExecutor, ReplicatedEnvironmentFacade replicatedEnvironmentFacade)
+ {
+ super(UUIDGenerator.generateReplicationNodeId(replicatedEnvironmentFacade.getGroupName(), replicationNode.getName()), null, null, taskExecutor);
+ addParent(VirtualHost.class, virtualHost);
+ _groupName = replicatedEnvironmentFacade.getGroupName();
+ _hostPort = replicationNode.getHostName() + ":" + replicationNode.getPort();
+ _replicationNode = replicationNode;
+ _replicatedEnvironmentFacade = replicatedEnvironmentFacade;
+ _state = new AtomicReference<State>(State.ACTIVE);
+ }
+
+ @Override
+ public boolean isLocal()
+ {
+ return false;
+ }
+
+ @Override
+ public String getName()
+ {
+ return (String)getAttribute(NAME);
+ }
+
+ @Override
+ public String setName(String currentName, String desiredName) throws IllegalStateException, AccessControlException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public State getActualState()
+ {
+ return _state.get();
+ }
+
+ @Override
+ public boolean isDurable()
+ {
+ return true;
+ }
+
+ @Override
+ public void setDurable(boolean durable) throws IllegalStateException, AccessControlException, IllegalArgumentException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public LifetimePolicy getLifetimePolicy()
+ {
+ return LifetimePolicy.PERMANENT;
+ }
+
+ @Override
+ public LifetimePolicy setLifetimePolicy(LifetimePolicy expected, LifetimePolicy desired) throws IllegalStateException,
+ AccessControlException, IllegalArgumentException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long getTimeToLive()
+ {
+ return 0;
+ }
+
+ @Override
+ public long setTimeToLive(long expected, long desired) throws IllegalStateException, AccessControlException,
+ IllegalArgumentException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Statistics getStatistics()
+ {
+ return NoStatistics.getInstance();
+ }
+
+ @Override
+ public <C extends ConfiguredObject> Collection<C> getChildren(Class<C> clazz)
+ {
+ return Collections.emptySet();
+ }
+
+ @Override
+ protected boolean setState(State currentState, State desiredState)
+ {
+ //TODO: Need to decide how to display STOPPED state on a remote node when a corresponding local node is in STOPPED state
+ if (desiredState == State.DELETED)
+ {
+ String nodeName = getName();
+
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Deleting node '" + nodeName + "' from group '" + _groupName + "'");
+ }
+
+ try
+ {
+ _replicatedEnvironmentFacade.removeNodeFromGroup(nodeName);
+ _state.set(State.DELETED);
+ return true;
+ }
+ catch(MasterStateException e)
+ {
+ throw new IllegalStateTransitionException("Node '" + nodeName + "' cannot be deleted when role is a master");
+ }
+ catch (Exception e)
+ {
+ throw new IllegalStateTransitionException("Unexpected exception on node '" + nodeName + "' deletion", e);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Object getAttribute(String name)
+ {
+ if (ReplicationNode.ID.equals(name))
+ {
+ return getId();
+ }
+ else if (LIFETIME_POLICY.equals(name))
+ {
+ return getLifetimePolicy();
+ }
+ else if (DURABLE.equals(name))
+ {
+ return isDurable();
+ }
+ else if(STATE.equals(name))
+ {
+ return getActualState();
+ }
+ else if(TIME_TO_LIVE.equals(name))
+ {
+ return getLifetimePolicy();
+ }
+ else if (ROLE.equals(name))
+ {
+ return _role;
+ }
+ else if (JOIN_TIME.equals(name))
+ {
+ return _joinTime;
+ }
+ else if (LAST_KNOWN_REPLICATION_TRANSACTION_ID.equals(name))
+ {
+ return _lastTransactionId;
+ }
+ else if (NAME.equals(name))
+ {
+ return _replicationNode.getName();
+ }
+ else if (GROUP_NAME.equals(name))
+ {
+ return _groupName;
+ }
+ else if (HOST_PORT.equals(name))
+ {
+ return _hostPort;
+ }
+ return super.getAttribute(name);
+ }
+
+ public void updateNodeState()
+ {
+ String oldRole = _role;
+ long oldJoinTime = _joinTime;
+ long oldTransactionId = _lastTransactionId;
+
+ try
+ {
+ //TODO: updateNodeState is called from ReplicatedEnvironmentFacade to call getRemoteNodeState. Odd!!!
+ NodeState state = _replicatedEnvironmentFacade.getRemoteNodeState(_replicationNode);
+ _role = state.getNodeState().name();
+ _joinTime = state.getJoinTime();
+ _lastTransactionId = state.getCurrentTxnEndVLSN();
+ }
+ catch (IOException e)
+ {
+ _role = com.sleepycat.je.rep.ReplicatedEnvironment.State.UNKNOWN.name();
+ LOGGER.warn("Cannot connect to node " + _replicationNode.getName() + " from " + _groupName);
+ }
+ catch (ServiceConnectFailedException e)
+ {
+ _role = com.sleepycat.je.rep.ReplicatedEnvironment.State.UNKNOWN.name();
+ LOGGER.warn("Cannot retrieve the node details for node " + _replicationNode.getName() + " from " + _groupName);
+ }
+
+ if (!_role.equals(oldRole))
+ {
+ attributeSet(ROLE, oldRole, _role);
+ }
+
+ if (_joinTime != oldJoinTime)
+ {
+ attributeSet(JOIN_TIME, oldJoinTime, _joinTime);
+ }
+
+ if (_lastTransactionId != oldTransactionId)
+ {
+ attributeSet(LAST_KNOWN_REPLICATION_TRANSACTION_ID, oldTransactionId, _lastTransactionId);
+ }
+ }
+
+ @Override
+ public Collection<String> getAttributeNames()
+ {
+ return ReplicationNode.AVAILABLE_ATTRIBUTES;
+ }
+
+ @Override
+ public void changeAttributes(Map<String, Object> attributes)
+ throws IllegalStateException, AccessControlException,
+ IllegalArgumentException
+ {
+ checkWhetherImmutableAttributeChanged(attributes);
+ Map<String, Object> convertedAttributes = MapValueConverter.convert(attributes, ATTRIBUTE_TYPES);
+
+ if (convertedAttributes.containsKey(ROLE))
+ {
+ String currentRole = (String)getAttribute(ROLE);
+ if (!ReplicatedEnvironment.State.REPLICA.name().equals(currentRole))
+ {
+ throw new IllegalConfigurationException("Cannot transfer mastership when not a replica");
+ }
+
+ String role = (String)convertedAttributes.get(ROLE);
+
+ if (ReplicatedEnvironment.State.MASTER.name().equals(role) )
+ {
+ try
+ {
+ String nodeName = getName();
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Trying to transfer master to " + nodeName);
+ }
+
+ _replicatedEnvironmentFacade.transferMasterAsynchronously(nodeName);
+
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("The mastership has been transfered to " + nodeName);
+ }
+ }
+ catch(Exception e)
+ {
+ throw new IllegalConfigurationException("Cannot transfer mastership to " + getName(), e);
+ }
+ }
+ else
+ {
+ throw new IllegalConfigurationException("Changing role to other value then "
+ + ReplicatedEnvironment.State.MASTER.name() + " is unsupported");
+ }
+ }
+
+ super.changeAttributes(convertedAttributes);
+ }
+
+ private void checkWhetherImmutableAttributeChanged(Map<String, Object> attributes)
+ {
+ Set<String> immutableAttributeNames = new HashSet<String>(getAttributeNames());
+ immutableAttributeNames.remove(ROLE);
+ for (String attributeName : immutableAttributeNames)
+ {
+ if (attributes.containsKey(attributeName))
+ {
+ // the name is appended into attributes map in REST layer
+ if (attributeName.equals(NAME) && getName().equals(attributes.get(NAME)))
+ {
+ continue;
+ }
+ throw new IllegalConfigurationException("Cannot change value of immutable attribute " + attributeName);
+ }
+ }
+ }
+
+ com.sleepycat.je.rep.ReplicationNode getReplicationNode()
+ {
+ return _replicationNode;
+ }
+
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/RemoteReplicationNodeFactory.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/RemoteReplicationNodeFactory.java
new file mode 100644
index 0000000000..da235f5616
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/RemoteReplicationNodeFactory.java
@@ -0,0 +1,28 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb.replication;
+
+public interface RemoteReplicationNodeFactory
+{
+ RemoteReplicationNode create(com.sleepycat.je.rep.ReplicationNode jeNode, ReplicatedEnvironmentFacade environmentFacade);
+
+ long getRemoteNodeMonitorInterval();
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/ReplicatedEnvironmentFacade.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/ReplicatedEnvironmentFacade.java
new file mode 100644
index 0000000000..dc0b1b2ff1
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/ReplicatedEnvironmentFacade.java
@@ -0,0 +1,1143 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.replication;
+
+import static org.apache.qpid.server.model.ReplicationNode.COALESCING_SYNC;
+import static org.apache.qpid.server.model.ReplicationNode.DESIGNATED_PRIMARY;
+import static org.apache.qpid.server.model.ReplicationNode.DURABILITY;
+import static org.apache.qpid.server.model.ReplicationNode.GROUP_NAME;
+import static org.apache.qpid.server.model.ReplicationNode.HELPER_HOST_PORT;
+import static org.apache.qpid.server.model.ReplicationNode.HOST_PORT;
+import static org.apache.qpid.server.model.ReplicationNode.PARAMETERS;
+import static org.apache.qpid.server.model.ReplicationNode.PRIORITY;
+import static org.apache.qpid.server.model.ReplicationNode.QUORUM_OVERRIDE;
+import static org.apache.qpid.server.model.ReplicationNode.REPLICATION_PARAMETERS;
+import static org.apache.qpid.server.model.ReplicationNode.STORE_PATH;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQStoreException;
+import org.apache.qpid.server.replication.ReplicationGroupListener;
+import org.apache.qpid.server.store.berkeleydb.CoalescingCommiter;
+import org.apache.qpid.server.store.berkeleydb.Committer;
+import org.apache.qpid.server.store.berkeleydb.EnvironmentFacade;
+import org.apache.qpid.server.store.berkeleydb.LoggingAsyncExceptionListener;
+import org.apache.qpid.server.util.DaemonThreadFactory;
+
+import com.sleepycat.je.Database;
+import com.sleepycat.je.DatabaseConfig;
+import com.sleepycat.je.DatabaseException;
+import com.sleepycat.je.Durability;
+import com.sleepycat.je.Environment;
+import com.sleepycat.je.EnvironmentConfig;
+import com.sleepycat.je.EnvironmentFailureException;
+import com.sleepycat.je.Transaction;
+import com.sleepycat.je.rep.InsufficientLogException;
+import com.sleepycat.je.rep.InsufficientReplicasException;
+import com.sleepycat.je.rep.NetworkRestore;
+import com.sleepycat.je.rep.NetworkRestoreConfig;
+import com.sleepycat.je.rep.NodeState;
+import com.sleepycat.je.rep.RepInternal;
+import com.sleepycat.je.rep.ReplicatedEnvironment;
+import com.sleepycat.je.rep.ReplicationConfig;
+import com.sleepycat.je.rep.ReplicationGroup;
+import com.sleepycat.je.rep.ReplicationMutableConfig;
+import com.sleepycat.je.rep.ReplicationNode;
+import com.sleepycat.je.rep.RestartRequiredException;
+import com.sleepycat.je.rep.StateChangeEvent;
+import com.sleepycat.je.rep.StateChangeListener;
+import com.sleepycat.je.rep.util.DbPing;
+import com.sleepycat.je.rep.util.ReplicationGroupAdmin;
+import com.sleepycat.je.rep.utilint.ServiceDispatcher.ServiceConnectFailedException;
+import com.sleepycat.je.rep.vlsn.VLSNRange;
+import com.sleepycat.je.utilint.PropUtil;
+import com.sleepycat.je.utilint.VLSN;
+
+public class ReplicatedEnvironmentFacade implements EnvironmentFacade, StateChangeListener
+{
+ public static final String GROUP_CHECK_INTERVAL_PROPERTY_NAME = "qpid.bdb.ha.group_check_interval";
+ public static final String MASTER_TRANSFER_TIMEOUT_PROPERTY_NAME = "qpid.bdb.ha.master_transfer_interval";
+
+ private static final Logger LOGGER = Logger.getLogger(ReplicatedEnvironmentFacade.class);
+ private static final long DEFAULT_GROUP_CHECK_INTERVAL = 1000l;
+ private static final long GROUP_CHECK_INTERVAL = Long.getLong(GROUP_CHECK_INTERVAL_PROPERTY_NAME, DEFAULT_GROUP_CHECK_INTERVAL);
+
+ private static final int DEFAULT_MASTER_TRANSFER_TIMEOUT = 1000 * 60;
+
+ public static final int MASTER_TRANSFER_TIMEOUT = Integer.getInteger(MASTER_TRANSFER_TIMEOUT_PROPERTY_NAME, DEFAULT_MASTER_TRANSFER_TIMEOUT);
+
+ public static final String DB_PING_SOCKET_TIMEOUT_PROPERTY_NAME = "qpid.bdb.ha.db_ping_socket_timeout";
+ private static final int DEFAULT_DB_PING_SOCKET_TIMEOUT = 1000;
+
+ private static final int DB_PING_SOCKET_TIMEOUT = Integer.getInteger(DB_PING_SOCKET_TIMEOUT_PROPERTY_NAME, DEFAULT_DB_PING_SOCKET_TIMEOUT);
+
+ @SuppressWarnings("serial")
+ private static final Map<String, String> REPCONFIG_DEFAULTS = Collections.unmodifiableMap(new HashMap<String, String>()
+ {{
+ /**
+ * Parameter decreased as the 24h default may lead very large log files for most users.
+ */
+ put(ReplicationConfig.REP_STREAM_TIMEOUT, "1 h");
+ /**
+ * Parameter increased as the 5 s default may lead to spurious timeouts.
+ */
+ put(ReplicationConfig.REPLICA_ACK_TIMEOUT, "15 s");
+ /**
+ * Parameter increased as the 10 s default may lead to spurious timeouts.
+ */
+ put(ReplicationConfig.INSUFFICIENT_REPLICAS_TIMEOUT, "20 s");
+ /**
+ * Parameter increased as the 10 h default may cause user confusion.
+ */
+ put(ReplicationConfig.ENV_SETUP_TIMEOUT, "15 min");
+ /**
+ * Parameter changed from default true so we adopt immediately adopt the new behaviour early. False
+ * is scheduled to become default after JE 5.0.48.
+ */
+ put(ReplicationConfig.PROTOCOL_OLD_STRING_ENCODING, Boolean.FALSE.toString());
+ /**
+ * Parameter decreased as a default 5min interval may lead to bigger data losses on Node
+ * with NO_SYN durability in case if such Node crushes.
+ */
+ put(ReplicationConfig.LOG_FLUSH_TASK_INTERVAL, "1 min");
+
+ /**
+ * Timeout to transit into UNKNOWN state if the majority is not available.
+ * By default it is switched off.
+ */
+ put(ReplicationConfig.ENV_UNKNOWN_STATE_TIMEOUT, "5 s");
+ }});
+
+ public static final String TYPE = "BDB-HA";
+
+
+ private final LocalReplicationNode _replicationNode;
+ private final Durability _durability;
+ private final Boolean _coalescingSync;
+ private final String _prettyGroupNodeName;
+ private final File _environmentDirectory;
+
+ private final ExecutorService _environmentJobExecutor;
+ private final ScheduledExecutorService _groupChangeExecutor;
+ private final AtomicReference<State> _state = new AtomicReference<State>(State.OPENING);
+ private final ConcurrentMap<String, DatabaseHolder> _databases = new ConcurrentHashMap<String, DatabaseHolder>();
+ private final ConcurrentMap<String, RemoteReplicationNode> _remoteReplicationNodes = new ConcurrentHashMap<String, RemoteReplicationNode>();
+ private final RemoteReplicationNodeFactory _remoteReplicationNodeFactory;
+ private final AtomicReference<ReplicationGroupListener> _replicationGroupListener = new AtomicReference<ReplicationGroupListener>();
+ private final AtomicReference<StateChangeListener> _stateChangeListener = new AtomicReference<StateChangeListener>();
+
+ private volatile ReplicatedEnvironment _environment;
+ private long _joinTime;
+
+ public ReplicatedEnvironmentFacade(LocalReplicationNode replicationNode, RemoteReplicationNodeFactory remoteReplicationNodeFactory)
+ {
+ _environmentDirectory = new File((String)replicationNode.getAttribute(STORE_PATH));
+ if (!_environmentDirectory.exists())
+ {
+ if (!_environmentDirectory.mkdirs())
+ {
+ throw new IllegalArgumentException("Environment path " + _environmentDirectory + " could not be read or created. "
+ + "Ensure the path is correct and that the permissions are correct.");
+ }
+ }
+
+ _replicationNode = replicationNode;
+
+ _durability = Durability.parse((String)_replicationNode.getAttribute(DURABILITY));
+ _coalescingSync = (Boolean)_replicationNode.getAttribute(COALESCING_SYNC);
+ _prettyGroupNodeName = (String)_replicationNode.getAttribute(GROUP_NAME) + ":" + _replicationNode.getName();
+
+ // we relay on this executor being single-threaded as we need to restart and mutate the environment in one thread
+ _environmentJobExecutor = Executors.newSingleThreadExecutor(new DaemonThreadFactory("Environment-" + _prettyGroupNodeName));
+ _groupChangeExecutor = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors() + 1, new DaemonThreadFactory("Group-Change-Learner:" + _prettyGroupNodeName));
+
+ _remoteReplicationNodeFactory = remoteReplicationNodeFactory;
+ _groupChangeExecutor.scheduleWithFixedDelay(new GroupChangeLearner(), 0, GROUP_CHECK_INTERVAL, TimeUnit.MILLISECONDS);
+ _groupChangeExecutor.schedule(new RemoteNodeStateLearner(), _remoteReplicationNodeFactory.getRemoteNodeMonitorInterval(), TimeUnit.MILLISECONDS);
+
+ // create environment in a separate thread to avoid renaming of the current thread by JE
+ _environment = createEnvironment(true);
+ populateExistingRemoteReplicationNodes();
+ }
+
+ @Override
+ public void commit(final Transaction tx) throws AMQStoreException
+ {
+ try
+ {
+ // Using commit() instead of commitNoSync() for the HA store to allow
+ // the HA durability configuration to influence resulting behaviour.
+ tx.commit();
+ }
+ catch (DatabaseException de)
+ {
+ throw handleDatabaseException("Got DatabaseException on commit, closing environment", de);
+ }
+ }
+
+ @Override
+ public void close()
+ {
+ if (_state.compareAndSet(State.OPENING, State.CLOSING) ||
+ _state.compareAndSet(State.OPEN, State.CLOSING) ||
+ _state.compareAndSet(State.RESTARTING, State.CLOSING) )
+ {
+ try
+ {
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Closing replicated environment facade for " + _prettyGroupNodeName);
+ }
+
+ _environmentJobExecutor.shutdown();
+ _groupChangeExecutor.shutdown();
+ closeDatabases();
+ closeEnvironment();
+ }
+ finally
+ {
+ _state.compareAndSet(State.CLOSING, State.CLOSED);
+ }
+ }
+ }
+
+ @Override
+ public AMQStoreException handleDatabaseException(String contextMessage, final DatabaseException dbe)
+ {
+ boolean restart = (dbe instanceof InsufficientReplicasException || dbe instanceof InsufficientReplicasException || dbe instanceof RestartRequiredException);
+ if (restart)
+ {
+ if (_state.compareAndSet(State.OPEN, State.RESTARTING))
+ {
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Environment restarting due to exception " + dbe.getMessage(), dbe);
+ }
+
+ _environmentJobExecutor.execute(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ try
+ {
+ restartEnvironment(dbe);
+ }
+ catch (Exception e)
+ {
+ LOGGER.error("Exception on environment restart", e);
+ }
+ }
+ });
+
+ }
+ else
+ {
+ LOGGER.info("Cannot restart environment because of facade state: " + _state.get());
+ }
+ }
+ return new AMQStoreException(contextMessage, dbe);
+ }
+
+ @Override
+ public void openDatabases(DatabaseConfig dbConfig, String... databaseNames)
+ {
+ if (_state.get() != State.OPEN)
+ {
+ throw new IllegalStateException("Environment facade is not in opened state");
+ }
+
+ if (!_environment.isValid())
+ {
+ throw new IllegalStateException("Environment is not valid");
+ }
+
+ if (_environment.getState() != ReplicatedEnvironment.State.MASTER)
+ {
+ throw new IllegalStateException("Databases can only be opened on Master node");
+ }
+
+ for (String databaseName : databaseNames)
+ {
+ _databases.put(databaseName, new DatabaseHolder(dbConfig));
+ }
+ for (String databaseName : databaseNames)
+ {
+ DatabaseHolder holder = _databases.get(databaseName);
+ openDatabaseInternally(databaseName, holder);
+ }
+ }
+
+ private void openDatabaseInternally(String databaseName, DatabaseHolder holder)
+ {
+ Database database = _environment.openDatabase(null, databaseName, holder.getConfig());
+ holder.setDatabase(database);
+ }
+
+ @Override
+ public Database getOpenDatabase(String name)
+ {
+ if (_state.get() != State.OPEN)
+ {
+ throw new IllegalStateException("Environment facade is not in opened state");
+ }
+
+ if (!_environment.isValid())
+ {
+ throw new IllegalStateException("Environment is not valid");
+ }
+ DatabaseHolder databaseHolder = _databases.get(name);
+ if (databaseHolder == null)
+ {
+ throw new IllegalArgumentException("Database with name '" + name + "' has never been requested to be opened");
+ }
+ Database database = databaseHolder.getDatabase();
+ if (database == null)
+ {
+ throw new IllegalArgumentException("Database with name '" + name + "' has not been opened");
+ }
+ return database;
+ }
+
+ @Override
+ public String getStoreLocation()
+ {
+ return _environmentDirectory.getAbsolutePath();
+ }
+
+ @Override
+ public void stateChange(final StateChangeEvent stateChangeEvent)
+ {
+ if (LOGGER.isInfoEnabled())
+ {
+ LOGGER.info("The node '" + _prettyGroupNodeName + "' state is " + stateChangeEvent.getState());
+ }
+
+ if (_state.get() != State.CLOSING && _state.get() != State.CLOSED)
+ {
+ _groupChangeExecutor.submit(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ stateChanged(stateChangeEvent);
+ }
+ });
+ }
+ }
+
+ private void stateChanged(StateChangeEvent stateChangeEvent)
+ {
+ ReplicatedEnvironment.State state = stateChangeEvent.getState();
+
+ if (state == ReplicatedEnvironment.State.REPLICA || state == ReplicatedEnvironment.State.MASTER)
+ {
+ if (_state.compareAndSet(State.OPENING, State.OPEN) || _state.compareAndSet(State.RESTARTING, State.OPEN))
+ {
+ LOGGER.info("The environment facade is in open state for node " + _prettyGroupNodeName);
+ _joinTime = System.currentTimeMillis();
+ }
+ }
+
+ if (state == ReplicatedEnvironment.State.MASTER)
+ {
+ reopenDatabases();
+ }
+
+ StateChangeListener listener = _stateChangeListener.get();
+ if (listener != null)
+ {
+ listener.stateChange(stateChangeEvent);
+ }
+ }
+
+ private void reopenDatabases()
+ {
+ DatabaseConfig pingDbConfig = new DatabaseConfig();
+ pingDbConfig.setTransactional(true);
+ pingDbConfig.setAllowCreate(true);
+
+ _databases.putIfAbsent(DatabasePinger.PING_DATABASE_NAME, new DatabaseHolder(pingDbConfig));
+
+ for (Map.Entry<String, DatabaseHolder> entry : _databases.entrySet())
+ {
+ openDatabaseInternally(entry.getKey(), entry.getValue());
+ }
+ }
+
+ public String getGroupName()
+ {
+ return (String)_replicationNode.getAttribute(GROUP_NAME);
+ }
+
+ public String getNodeName()
+ {
+ return _replicationNode.getName();
+ }
+
+ public String getHostPort()
+ {
+ return (String)_replicationNode.getAttribute(HOST_PORT);
+ }
+
+ public String getHelperHostPort()
+ {
+ return (String)_replicationNode.getAttribute(HELPER_HOST_PORT);
+ }
+
+ public String getDurability()
+ {
+ return _durability.toString();
+ }
+
+ public boolean isCoalescingSync()
+ {
+ return _coalescingSync;
+ }
+
+ public String getNodeState()
+ {
+ ReplicatedEnvironment.State state = _environment.getState();
+ return state.toString();
+ }
+
+ public void removeNodeFromGroup(final String nodeName)
+ {
+ createReplicationGroupAdmin().removeMember(nodeName);
+ }
+
+ public boolean isDesignatedPrimary()
+ {
+ return _environment.getRepMutableConfig().getDesignatedPrimary();
+ }
+
+ public Future<Void> setDesignatedPrimary(final boolean isPrimary)
+ {
+ if (LOGGER.isInfoEnabled())
+ {
+ LOGGER.info("Submitting a job to set designated primary on " + _prettyGroupNodeName + " to " + isPrimary);
+ }
+
+ return _environmentJobExecutor.submit(new Callable<Void>()
+ {
+ @Override
+ public Void call()
+ {
+ setDesignatedPrimaryInternal(isPrimary);
+ return null;
+ }
+ });
+ }
+
+ private void setDesignatedPrimaryInternal(final boolean isPrimary)
+ {
+ try
+ {
+ final ReplicationMutableConfig oldConfig = _environment.getRepMutableConfig();
+ final ReplicationMutableConfig newConfig = oldConfig.setDesignatedPrimary(isPrimary);
+ _environment.setRepMutableConfig(newConfig);
+
+ if (LOGGER.isInfoEnabled())
+ {
+ LOGGER.info("Node " + _prettyGroupNodeName + " successfully set designated primary : " + isPrimary);
+ }
+ }
+ catch (Exception e)
+ {
+ LOGGER.error("Cannot set designated primary to " + isPrimary + " on node " + _prettyGroupNodeName, e);
+ }
+ }
+
+ int getPriority()
+ {
+ ReplicationMutableConfig repConfig = _environment.getRepMutableConfig();
+ return repConfig.getNodePriority();
+ }
+
+ public Future<Void> setPriority(final int priority)
+ {
+ if (LOGGER.isInfoEnabled())
+ {
+ LOGGER.info("Submitting a job to set priority on " + _prettyGroupNodeName + " to " + priority);
+ }
+
+ return _environmentJobExecutor.submit(new Callable<Void>()
+ {
+ @Override
+ public Void call()
+ {
+ setPriorityInternal(priority);
+ return null;
+ }
+ });
+ }
+
+ private void setPriorityInternal(int priority)
+ {
+ try
+ {
+ final ReplicationMutableConfig oldConfig = _environment.getRepMutableConfig();
+ final ReplicationMutableConfig newConfig = oldConfig.setNodePriority(priority);
+ _environment.setRepMutableConfig(newConfig);
+
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Node " + _prettyGroupNodeName + " priority has been changed to " + priority);
+ }
+ }
+ catch (Exception e)
+ {
+ LOGGER.error("Cannot set priority to " + priority + " on node " + _prettyGroupNodeName, e);
+ }
+ }
+
+ int getElectableGroupSizeOverride()
+ {
+ ReplicationMutableConfig repConfig = _environment.getRepMutableConfig();
+ return repConfig.getElectableGroupSizeOverride();
+ }
+
+ public Future<Void> setElectableGroupSizeOverride(final int electableGroupOverride)
+ {
+ if (LOGGER.isInfoEnabled())
+ {
+ LOGGER.info("Submitting a job to set electable group override on " + _prettyGroupNodeName + " to " + electableGroupOverride);
+ }
+
+ return _environmentJobExecutor.submit(new Callable<Void>()
+ {
+ @Override
+ public Void call()
+ {
+ setElectableGroupSizeOverrideInternal(electableGroupOverride);
+ return null;
+ }
+ });
+ }
+
+ private void setElectableGroupSizeOverrideInternal(int electableGroupOverride)
+ {
+ try
+ {
+ final ReplicationMutableConfig oldConfig = _environment.getRepMutableConfig();
+ final ReplicationMutableConfig newConfig = oldConfig.setElectableGroupSizeOverride(electableGroupOverride);
+ _environment.setRepMutableConfig(newConfig);
+
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Node " + _prettyGroupNodeName + " electable group size override has been changed to " + electableGroupOverride);
+ }
+ }
+ catch (Exception e)
+ {
+ LOGGER.error("Cannot set electable group size to " + electableGroupOverride + " on node " + _prettyGroupNodeName, e);
+ }
+ }
+
+
+ public long getJoinTime()
+ {
+ return _joinTime ;
+ }
+
+ public long getLastKnownReplicationTransactionId()
+ {
+ if (_state.get() == State.OPEN)
+ {
+ VLSNRange range = RepInternal.getRepImpl(_environment).getVLSNIndex().getRange();
+ VLSN lastTxnEnd = range.getLastTxnEnd();
+ return lastTxnEnd.getSequence();
+ }
+ else
+ {
+ return -1L;
+ }
+ }
+
+ public void transferMasterToSelfAsynchronously()
+ {
+ final String nodeName = getNodeName();
+ transferMasterAsynchronously(nodeName);
+ }
+
+ public void transferMasterAsynchronously(final String nodeName)
+ {
+ _groupChangeExecutor.submit(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ try
+ {
+ ReplicationGroupAdmin admin = createReplicationGroupAdmin();
+ String newMaster = admin.transferMaster(Collections.singleton(nodeName), MASTER_TRANSFER_TIMEOUT, TimeUnit.MILLISECONDS, true);
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("The mastership has been transfered to " + newMaster);
+ }
+ }
+ catch (DatabaseException e)
+ {
+ LOGGER.warn("Exception on transfering the mastership to " + _prettyGroupNodeName
+ + " Master transfer timeout : " + MASTER_TRANSFER_TIMEOUT, e);
+ }
+ }
+ });
+ }
+
+ public ReplicatedEnvironment getEnvironment()
+ {
+ return _environment;
+ }
+
+ public State getFacadeState()
+ {
+ return _state.get();
+ }
+
+ public void setReplicationGroupListener(ReplicationGroupListener replicationGroupListener)
+ {
+ if (_replicationGroupListener.compareAndSet(null, replicationGroupListener))
+ {
+ notifyExistingRemoteReplicationNodes(replicationGroupListener);
+ }
+ else
+ {
+ throw new IllegalStateException("ReplicationGroupListener is already set on " + _prettyGroupNodeName);
+ }
+ }
+
+ public void setStateChangeListener(StateChangeListener stateChangeListener)
+ {
+ if (_stateChangeListener.compareAndSet(null, stateChangeListener))
+ {
+ _environment.setStateChangeListener(this);
+ }
+ else
+ {
+ throw new IllegalStateException("StateChangeListener is already set on " + _prettyGroupNodeName);
+ }
+ }
+
+ private void populateExistingRemoteReplicationNodes()
+ {
+ ReplicationGroup group = _environment.getGroup();
+ Set<ReplicationNode> nodes = new HashSet<ReplicationNode>(group.getElectableNodes());
+ String localNodeName = getNodeName();
+
+ for (ReplicationNode replicationNode : nodes)
+ {
+ String discoveredNodeName = replicationNode.getName();
+ if (!discoveredNodeName.equals(localNodeName))
+ {
+ RemoteReplicationNode remoteNode = _remoteReplicationNodeFactory.create(replicationNode, this);
+
+ _remoteReplicationNodes.put(replicationNode.getName(), remoteNode);
+ }
+ }
+ }
+
+ private void notifyExistingRemoteReplicationNodes(ReplicationGroupListener listener)
+ {
+ for (org.apache.qpid.server.model.ReplicationNode value : _remoteReplicationNodes.values())
+ {
+ listener.onReplicationNodeRecovered(value);
+ }
+ }
+
+ private ReplicationGroupAdmin createReplicationGroupAdmin()
+ {
+ final Set<InetSocketAddress> helpers = new HashSet<InetSocketAddress>();
+ for (RemoteReplicationNode node : _remoteReplicationNodes.values())
+ {
+ helpers.add(node.getReplicationNode().getSocketAddress());
+ }
+
+ //TODO: refactor this into a method on LocalReplicationNode
+ String hostPort = (String)_replicationNode.getAttribute(org.apache.qpid.server.model.ReplicationNode.HOST_PORT);
+ String[] tokens = hostPort.split(":");
+ helpers.add(new InetSocketAddress(tokens[0], Integer.parseInt(tokens[1])));
+
+ return new ReplicationGroupAdmin((String)_replicationNode.getAttribute(GROUP_NAME), helpers);
+ }
+
+ private void closeEnvironment()
+ {
+ // 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.
+ try
+ {
+ if (_environment.isValid())
+ {
+ _environment.cleanLog();
+ }
+ }
+ finally
+ {
+ _environment.close();
+ _environment = null;
+ }
+ }
+
+ private void restartEnvironment(DatabaseException dbe)
+ {
+ LOGGER.info("Restarting environment");
+
+ closeEnvironmentSafely();
+
+ _environment = createEnvironment(false);
+
+ if (_stateChangeListener.get() != null)
+ {
+ _environment.setStateChangeListener(this);
+ }
+
+ LOGGER.info("Environment is restarted");
+ }
+
+ private void closeEnvironmentSafely()
+ {
+ Environment environment = _environment;
+ if (environment != null)
+ {
+ try
+ {
+ if (environment.isValid())
+ {
+ try
+ {
+ closeDatabases();
+ }
+ catch(Exception e)
+ {
+ LOGGER.warn("Ignoring an exception whilst closing databases", e);
+ }
+ }
+ environment.close();
+ }
+ catch (EnvironmentFailureException efe)
+ {
+ LOGGER.warn("Ignoring an exception whilst closing environment", efe);
+ }
+ }
+ }
+
+ private void closeDatabases()
+ {
+ RuntimeException firstThrownException = null;
+ for (Map.Entry<String, DatabaseHolder> entry : _databases.entrySet())
+ {
+ DatabaseHolder databaseHolder = entry.getValue();
+ Database database = databaseHolder.getDatabase();
+ if (database != null)
+ {
+ try
+ {
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Closing database " + entry.getKey() + " on " + _prettyGroupNodeName);
+ }
+
+ database.close();
+ }
+ catch(RuntimeException e)
+ {
+ LOGGER.error("Failed to close database on " + _prettyGroupNodeName, e);
+ if (firstThrownException == null)
+ {
+ firstThrownException = e;
+ }
+ }
+ finally
+ {
+ databaseHolder.setDatabase(null);
+ }
+ }
+ }
+ if (firstThrownException != null)
+ {
+ throw firstThrownException;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private ReplicatedEnvironment createEnvironment(boolean createEnvironmentInSeparateThread)
+ {
+ String groupName = (String)_replicationNode.getActualAttribute(GROUP_NAME);
+ String helperHostPort = (String)_replicationNode.getActualAttribute(HELPER_HOST_PORT);
+ String hostPort = (String)_replicationNode.getActualAttribute(HOST_PORT);
+ Map<String, String> environmentParameters = (Map<String, String>)_replicationNode.getActualAttribute(PARAMETERS);
+ Map<String, String> replicationEnvironmentParameters = (Map<String, String>)_replicationNode.getActualAttribute(REPLICATION_PARAMETERS);
+ Boolean designatedPrimary = (Boolean)_replicationNode.getActualAttribute(DESIGNATED_PRIMARY);
+ Integer priority = (Integer)_replicationNode.getActualAttribute(PRIORITY);
+ Integer quorumOverride = (Integer)_replicationNode.getActualAttribute(QUORUM_OVERRIDE);
+
+ if (LOGGER.isInfoEnabled())
+ {
+ LOGGER.info("Creating environment");
+ LOGGER.info("Environment path " + _environmentDirectory.getAbsolutePath());
+ LOGGER.info("Group name " + groupName);
+ LOGGER.info("Node name " + _replicationNode.getName());
+ LOGGER.info("Node host port " + hostPort);
+ LOGGER.info("Helper host port " + helperHostPort);
+ LOGGER.info("Durability " + _durability);
+ LOGGER.info("Coalescing sync " + _coalescingSync);
+ LOGGER.info("Designated primary (applicable to 2 node case only) " + designatedPrimary);
+ LOGGER.info("Node priority " + priority);
+ LOGGER.info("Quorum override " + quorumOverride);
+ }
+
+ Map<String, String> replicationEnvironmentSettings = new HashMap<String, String>(REPCONFIG_DEFAULTS);
+ if (replicationEnvironmentParameters != null && !replicationEnvironmentParameters.isEmpty())
+ {
+ replicationEnvironmentSettings.putAll(replicationEnvironmentParameters);
+ }
+ Map<String, String> environmentSettings = new HashMap<String, String>(EnvironmentFacade.ENVCONFIG_DEFAULTS);
+ if (environmentParameters != null && !environmentParameters.isEmpty())
+ {
+ environmentSettings.putAll(environmentParameters);
+ }
+
+ ReplicationConfig replicationConfig = new ReplicationConfig(groupName, _replicationNode.getName(), hostPort);
+ replicationConfig.setHelperHosts(helperHostPort);
+ replicationConfig.setDesignatedPrimary(designatedPrimary);
+ replicationConfig.setNodePriority(priority);
+ replicationConfig.setElectableGroupSizeOverride(quorumOverride);
+
+ for (Map.Entry<String, String> configItem : replicationEnvironmentSettings.entrySet())
+ {
+ if (LOGGER.isInfoEnabled())
+ {
+ LOGGER.info("Setting ReplicationConfig key " + configItem.getKey() + " to '" + configItem.getValue() + "'");
+ }
+ replicationConfig.setConfigParam(configItem.getKey(), configItem.getValue());
+ }
+
+ EnvironmentConfig envConfig = new EnvironmentConfig();
+ envConfig.setAllowCreate(true);
+ envConfig.setTransactional(true);
+ envConfig.setExceptionListener(new LoggingAsyncExceptionListener());
+ envConfig.setDurability(_durability);
+
+ for (Map.Entry<String, String> configItem : environmentSettings.entrySet())
+ {
+ if (LOGGER.isInfoEnabled())
+ {
+ LOGGER.info("Setting EnvironmentConfig key " + configItem.getKey() + " to '" + configItem.getValue() + "'");
+ }
+ envConfig.setConfigParam(configItem.getKey(), configItem.getValue());
+ }
+
+ if (createEnvironmentInSeparateThread)
+ {
+ return createEnvironmentInSeparateThread(_environmentDirectory, envConfig, replicationConfig);
+ }
+ else
+ {
+ return createEnvironment(_environmentDirectory, envConfig, replicationConfig);
+ }
+ }
+
+ private ReplicatedEnvironment createEnvironmentInSeparateThread(final File environmentPathFile, final EnvironmentConfig envConfig,
+ final ReplicationConfig replicationConfig)
+ {
+ Future<ReplicatedEnvironment> environmentFuture = _environmentJobExecutor.submit(new Callable<ReplicatedEnvironment>(){
+ @Override
+ public ReplicatedEnvironment call() throws Exception
+ {
+ String originalThreadName = Thread.currentThread().getName();
+ try
+ {
+ return createEnvironment(environmentPathFile, envConfig, replicationConfig);
+ }
+ finally
+ {
+ Thread.currentThread().setName(originalThreadName);
+ }
+ }});
+
+ long setUpTimeOutMillis = PropUtil.parseDuration(replicationConfig.getConfigParam(ReplicationConfig.ENV_SETUP_TIMEOUT));
+ try
+ {
+ return environmentFuture.get(setUpTimeOutMillis, TimeUnit.MILLISECONDS);
+ }
+ catch (InterruptedException e)
+ {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException("Environment creation was interrupted", e);
+ }
+ catch (ExecutionException e)
+ {
+ throw new RuntimeException("Unexpected exception on environment creation", e.getCause());
+ }
+ catch (TimeoutException e)
+ {
+ throw new RuntimeException("JE environment has not been created in due time");
+ }
+ }
+
+ private ReplicatedEnvironment createEnvironment(File environmentPathFile, EnvironmentConfig envConfig,
+ final ReplicationConfig replicationConfig)
+ {
+ ReplicatedEnvironment environment = null;
+ try
+ {
+ environment = new ReplicatedEnvironment(environmentPathFile, replicationConfig, envConfig);
+ }
+ catch (final InsufficientLogException ile)
+ {
+ LOGGER.info("InsufficientLogException thrown and so full network restore required", ile);
+ NetworkRestore restore = new NetworkRestore();
+ NetworkRestoreConfig config = new NetworkRestoreConfig();
+ config.setRetainLogFiles(false);
+ restore.execute(ile, config);
+ environment = new ReplicatedEnvironment(environmentPathFile, replicationConfig, envConfig);
+ }
+ return environment;
+ }
+
+ @Override
+ public Committer createCommitter(String name)
+ {
+ if (_coalescingSync)
+ {
+ return new CoalescingCommiter(name, this);
+ }
+ else
+ {
+ return Committer.IMMEDIATE_FUTURE_COMMITTER;
+ }
+ }
+
+ public NodeState getRemoteNodeState(ReplicationNode repNode) throws IOException, ServiceConnectFailedException
+ {
+ if (repNode == null)
+ {
+ throw new IllegalArgumentException("Node cannot be null");
+ }
+ return new DbPing(repNode, (String)_replicationNode.getAttribute(GROUP_NAME), DB_PING_SOCKET_TIMEOUT).getNodeState();
+ }
+
+ // For testing only
+ int getNumberOfElectableGroupMembers()
+ {
+ return _environment.getGroup().getElectableNodes().size();
+ }
+
+ private final class GroupChangeLearner implements Runnable
+ {
+ @Override
+ public void run()
+ {
+ String groupName = (String)_replicationNode.getAttribute(GROUP_NAME);
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Checking for changes in the group " + groupName);
+ }
+
+ ReplicatedEnvironment env = _environment;
+ ReplicationGroupListener replicationGroupListener = _replicationGroupListener.get();
+ if (env != null && env.isValid())
+ {
+ ReplicationGroup group = env.getGroup();
+ Set<ReplicationNode> nodes = new HashSet<ReplicationNode>(group.getElectableNodes());
+ String localNodeName = getNodeName();
+
+ Map<String, org.apache.qpid.server.model.ReplicationNode> removalMap = new HashMap<String, org.apache.qpid.server.model.ReplicationNode>(_remoteReplicationNodes);
+ for (ReplicationNode replicationNode : nodes)
+ {
+ String discoveredNodeName = replicationNode.getName();
+ if (!discoveredNodeName.equals(localNodeName))
+ {
+ if (!_remoteReplicationNodes.containsKey(discoveredNodeName))
+ {
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Remote replication node added '" + replicationNode + "' to '" + groupName + "'");
+ }
+
+ RemoteReplicationNode remoteNode = _remoteReplicationNodeFactory.create(replicationNode, ReplicatedEnvironmentFacade.this);
+
+ _remoteReplicationNodes.put(discoveredNodeName, remoteNode);
+
+ if (replicationGroupListener != null)
+ {
+ replicationGroupListener.onReplicationNodeAddedToGroup(remoteNode);
+ }
+ }
+ else
+ {
+ removalMap.remove(discoveredNodeName);
+ }
+ }
+ }
+
+ if (!removalMap.isEmpty())
+ {
+ for (Map.Entry<String, org.apache.qpid.server.model.ReplicationNode> replicationNodeEntry : removalMap.entrySet())
+ {
+ String replicationNodeName = replicationNodeEntry.getKey();
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Remote replication node removed '" + replicationNodeName + "' from '" + groupName + "'");
+ }
+ _remoteReplicationNodes.remove(replicationNodeName);
+ if (replicationGroupListener != null)
+ {
+ replicationGroupListener.onReplicationNodeRemovedFromGroup(replicationNodeEntry.getValue());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private class RemoteNodeStateLearner implements Callable<Void>
+ {
+ private Map<String, String> _previousGroupState = Collections.emptyMap();
+ @Override
+ public Void call()
+ {
+ long remoteNodeMonitorInterval = _remoteReplicationNodeFactory.getRemoteNodeMonitorInterval();
+ try
+ {
+ Set<Future<Void>> futures = new HashSet<Future<Void>>();
+ for (final RemoteReplicationNode node : _remoteReplicationNodes.values())
+ {
+ Future<Void> future = _groupChangeExecutor.submit(new Callable<Void>()
+ {
+ @Override
+ public Void call()
+ {
+ node.updateNodeState();
+ return null;
+ }
+ });
+ futures.add(future);
+ }
+
+ for (Future<Void> future : futures)
+ {
+ try
+ {
+ future.get(remoteNodeMonitorInterval, TimeUnit.MILLISECONDS);
+ }
+ catch (InterruptedException e)
+ {
+ Thread.currentThread().interrupt();
+ }
+ catch (ExecutionException e)
+ {
+ LOGGER.warn("Cannot update node state for group " + (String)_replicationNode.getAttribute(GROUP_NAME), e.getCause());
+ }
+ catch (TimeoutException e)
+ {
+ LOGGER.warn("Timeout whilst updating node state for group " + (String)_replicationNode.getAttribute(GROUP_NAME));
+ future.cancel(true);
+ }
+ }
+
+ if (ReplicatedEnvironment.State.MASTER == _environment.getState())
+ {
+ Map<String, String> currentGroupState = new HashMap<String, String>();
+ for (final RemoteReplicationNode node : _remoteReplicationNodes.values())
+ {
+ currentGroupState.put(node.getName(), (String)node.getAttribute(org.apache.qpid.server.model.ReplicationNode.ROLE));
+ }
+ boolean stateChanged = !_previousGroupState.equals(currentGroupState);
+ _previousGroupState = currentGroupState;
+ if (stateChanged && State.OPEN == _state.get())
+ {
+ new DatabasePinger().pingDb(ReplicatedEnvironmentFacade.this);
+ }
+ }
+ }
+ finally
+ {
+ _groupChangeExecutor.schedule(this, remoteNodeMonitorInterval, TimeUnit.MILLISECONDS);
+ }
+ return null;
+ }
+ }
+
+ public static enum State
+ {
+ OPENING,
+ OPEN,
+ RESTARTING,
+ CLOSING,
+ CLOSED
+ }
+
+ private static class DatabaseHolder
+ {
+ private final DatabaseConfig _config;
+ private Database _database;
+
+ public DatabaseHolder(DatabaseConfig config)
+ {
+ _config = config;
+ }
+
+ public Database getDatabase()
+ {
+ return _database;
+ }
+
+ public void setDatabase(Database database)
+ {
+ _database = database;
+ }
+
+ public DatabaseConfig getConfig()
+ {
+ return _config;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "DatabaseHolder [_config=" + _config + ", _database=" + _database + "]";
+ }
+
+ }
+
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/ReplicatedEnvironmentFacadeFactory.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/ReplicatedEnvironmentFacadeFactory.java
new file mode 100644
index 0000000000..1066cca21d
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/replication/ReplicatedEnvironmentFacadeFactory.java
@@ -0,0 +1,94 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.replication;
+
+import java.util.Collection;
+
+import org.apache.qpid.server.configuration.IllegalConfigurationException;
+import org.apache.qpid.server.model.ReplicationNode;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.store.berkeleydb.EnvironmentFacade;
+import org.apache.qpid.server.store.berkeleydb.EnvironmentFacadeFactory;
+
+import com.sleepycat.je.Durability;
+import com.sleepycat.je.Durability.SyncPolicy;
+
+//TODO: Should LocalReplicationNode implement EnvironmentFacadeFactory instead of having this class?
+public class ReplicatedEnvironmentFacadeFactory implements EnvironmentFacadeFactory
+{
+
+ @Override
+ public EnvironmentFacade createEnvironmentFacade(VirtualHost virtualHost, boolean isMessageStore)
+ {
+ Collection<ReplicationNode> replicationNodes = virtualHost.getChildren(ReplicationNode.class);
+ if (replicationNodes == null || replicationNodes.size() != 1)
+ {
+ throw new IllegalStateException("Expected exactly one replication node but got " + (replicationNodes==null ? 0 :replicationNodes.size()) + " nodes");
+ }
+ ReplicationNode localNode = replicationNodes.iterator().next();
+ if (!(localNode instanceof LocalReplicationNode))
+ {
+ throw new IllegalStateException("Cannot find local replication node among virtual host nodes");
+ }
+ LocalReplicationNode localReplicationNode = (LocalReplicationNode)localNode;
+
+ String durability = (String)localNode.getAttribute(ReplicationNode.DURABILITY);
+ Boolean coalescingSync = (Boolean)localNode.getAttribute(ReplicationNode.COALESCING_SYNC);
+
+ if (coalescingSync && Durability.parse(durability).getLocalSync() == SyncPolicy.SYNC)
+ {
+ throw new IllegalConfigurationException("Coalescing sync cannot be used with master sync policy " + SyncPolicy.SYNC
+ + "! Please set highAvailability.coalescingSync to false in store configuration.");
+ }
+
+ ReplicatedEnvironmentFacade facade = new ReplicatedEnvironmentFacade(localReplicationNode, new RemoteReplicationNodeFactoryImpl(virtualHost));
+ localReplicationNode.setReplicatedEnvironmentFacade(facade);
+ return facade;
+ }
+
+ static class RemoteReplicationNodeFactoryImpl implements RemoteReplicationNodeFactory
+ {
+ private VirtualHost _virtualHost;
+
+ public RemoteReplicationNodeFactoryImpl(VirtualHost virtualHost)
+ {
+ _virtualHost = virtualHost;
+ }
+
+ @Override
+ public RemoteReplicationNode create(com.sleepycat.je.rep.ReplicationNode replicationNode, ReplicatedEnvironmentFacade environmentFacade)
+ {
+ return new RemoteReplicationNode(replicationNode, _virtualHost, _virtualHost.getTaskExecutor(), environmentFacade);
+ }
+
+ @Override
+ public long getRemoteNodeMonitorInterval()
+ {
+ return (Long)_virtualHost.getAttribute(VirtualHost.REMOTE_REPLICATION_NODE_MONITOR_INTERVAL);
+ }
+ }
+
+ @Override
+ public String getType()
+ {
+ return ReplicatedEnvironmentFacade.TYPE;
+ }
+}
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/StoreUpgrade.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/StoreUpgrade.java
index f73e2e5d78..f43a229603 100644
--- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/StoreUpgrade.java
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/StoreUpgrade.java
@@ -22,6 +22,7 @@ package org.apache.qpid.server.store.berkeleydb.upgrade;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
+
import org.apache.qpid.AMQStoreException;
public interface StoreUpgrade
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6.java
index a478872ad0..f62ed7d578 100644
--- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6.java
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6.java
@@ -45,7 +45,6 @@ import org.apache.qpid.server.model.Exchange;
import org.apache.qpid.server.model.LifetimePolicy;
import org.apache.qpid.server.model.Queue;
import org.apache.qpid.server.model.UUIDGenerator;
-import org.apache.qpid.server.queue.AMQQueueFactory;
import org.apache.qpid.server.queue.QueueArgumentsConverter;
import org.apache.qpid.server.store.berkeleydb.AMQShortStringEncoding;
import org.apache.qpid.server.store.berkeleydb.FieldTableEncoding;
diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/Upgrader.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/Upgrader.java
index e769bfae81..7e09ab6cb0 100644
--- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/Upgrader.java
+++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/Upgrader.java
@@ -21,11 +21,13 @@
package org.apache.qpid.server.store.berkeleydb.upgrade;
import com.sleepycat.je.Cursor;
+
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
+import org.apache.log4j.Logger;
import org.apache.qpid.AMQStoreException;
-import org.apache.qpid.server.store.berkeleydb.AbstractBDBMessageStore;
+import org.apache.qpid.server.store.berkeleydb.BDBMessageStore;
import com.sleepycat.bind.tuple.IntegerBinding;
import com.sleepycat.bind.tuple.LongBinding;
@@ -34,11 +36,12 @@ import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
-import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
public class Upgrader
{
+ private static final Logger LOGGER = Logger.getLogger(Upgrader.class);
+
static final String VERSION_DB_NAME = "DB_VERSION";
private Environment _environment;
@@ -64,7 +67,8 @@ public class Upgrader
if(versionDb.count() == 0L)
{
- int sourceVersion = isEmpty ? AbstractBDBMessageStore.VERSION: identifyOldStoreVersion();
+
+ int sourceVersion = isEmpty ? BDBMessageStore.VERSION: identifyOldStoreVersion();
DatabaseEntry key = new DatabaseEntry();
IntegerBinding.intToEntry(sourceVersion, key);
DatabaseEntry value = new DatabaseEntry();
@@ -74,11 +78,17 @@ public class Upgrader
}
int version = getSourceVersion(versionDb);
- if(version > AbstractBDBMessageStore.VERSION)
+
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Source message store version is " + version);
+ }
+
+ if(version > BDBMessageStore.VERSION)
{
throw new AMQStoreException("Database version " + version
+ " is higher than the most recent known version: "
- + AbstractBDBMessageStore.VERSION);
+ + BDBMessageStore.VERSION);
}
performUpgradeFromVersion(version, versionDb);
}
@@ -127,7 +137,7 @@ public class Upgrader
void performUpgradeFromVersion(int sourceVersion, Database versionDb)
throws AMQStoreException
{
- while(sourceVersion != AbstractBDBMessageStore.VERSION)
+ while(sourceVersion != BDBMessageStore.VERSION)
{
upgrade(sourceVersion, ++sourceVersion);
DatabaseEntry key = new DatabaseEntry();
@@ -179,7 +189,7 @@ public class Upgrader
private int identifyOldStoreVersion() throws DatabaseException
{
- int version = 0;
+ int version = BDBMessageStore.VERSION;
for (String databaseName : _environment.getDatabaseNames())
{
if (databaseName.contains("_v"))
diff --git a/qpid/java/bdbstore/src/main/java/resources/js/qpid/management/virtualhost/bdb_ha/addVirtualHost.js b/qpid/java/bdbstore/src/main/java/resources/js/qpid/management/virtualhost/bdb_ha/addVirtualHost.js
index 44ad5fa57a..4e65201308 100644
--- a/qpid/java/bdbstore/src/main/java/resources/js/qpid/management/virtualhost/bdb_ha/addVirtualHost.js
+++ b/qpid/java/bdbstore/src/main/java/resources/js/qpid/management/virtualhost/bdb_ha/addVirtualHost.js
@@ -25,8 +25,19 @@ define(["dojo/_base/xhr",
"dijit/registry",
"dojo/parser",
"dojo/_base/array",
+ "dojo/json",
+ "dojo/query",
+ "dojo/_base/connect",
+ "dojo/_base/event",
+ "dojo/store/Memory",
+ "dojox/grid/DataGrid",
+ "dojo/data/ObjectStore",
"dojo/domReady!"],
- function (xhr, dom, construct, win, registry, parser, array) {
+ function (xhr, dom, construct, win, registry, parser, array, json, query, connect, event, Memory, DataGrid, ObjectStore) {
+ var nodeFields = ["storePath", "groupName", "nodeName", "state", "role", "hostPort", "helperHostPort",
+ "coalescingSync", "designatedPrimary", "durability", "priority",
+ "quorumOverride"];
+
return {
show: function() {
@@ -45,7 +56,190 @@ define(["dojo/_base/xhr",
load: function(data) {
node.innerHTML = data;
parser.parse(node);
+ for(var i = 0; i < nodeFields.length; i++)
+ {
+ that[nodeFields[i]] = registry.byId("formAddVirtualHost.specific." + nodeFields[i]);
+ }
+
+ that._initSettingsUI();
}});
+ },
+ save: function()
+ {
+ this.success = false;
+ var that = this;
+ var virtualHostName = registry.byId("formAddVirtualHost.name").get("value");
+ var virtualHostNameEncoded = encodeURIComponent(virtualHostName);
+
+
+ // create virtual host in QUIESCED state
+ var hostData =
+ {
+ desiredState: "QUIESCED",
+ name: virtualHostName,
+ type: registry.byId("addVirtualHost.type").get("value"),
+ };
+
+ xhr.put({url: "rest/virtualhost/" + virtualHostNameEncoded,
+ sync: true, handleAs: "json",
+ headers: { "Content-Type": "application/json"},
+ putData: json.stringify(hostData),
+ load: function(x) { that.success = true; },
+ error: function(error) {that.success = false; that.failureReason = error;}});
+
+ // if success, create node
+ if (this.success)
+ {
+ var node = {};
+ for(var i = 0; i < nodeFields.length; i++)
+ {
+ var fieldName = nodeFields[i];
+ var widget = this[fieldName];
+ if (widget)
+ {
+ node[fieldName] = widget.type=="checkbox"? widget.get("checked"): widget.get("value");
+ }
+ }
+
+ node.name = this.nodeName.value;
+
+ var data = this.settingsStore.objectStore.data;
+ if (data.length > 0 )
+ {
+ var parameters = null;
+ var replicationParameters = null;
+
+ for(var i=0; i<data.length; i++)
+ {
+ if (data[i].name && data[i].value)
+ {
+ if (data[i].name.indexOf("je.rep.") == 0)
+ {
+ if (replicationParameters == null)
+ {
+ replicationParameters = {};
+ }
+ replicationParameters[data[i].name] = data[i].value;
+ }
+ else
+ {
+ if (parameters == null)
+ {
+ parameters = {};
+ }
+ parameters[data[i].name] = data[i].value;
+ }
+ }
+ }
+
+ if (parameters)
+ {
+ node["parameters"] = parameters;
+ }
+
+ if (replicationParameters)
+ {
+ node["replicationParameters"] = replicationParameters;
+ }
+ }
+
+ xhr.put({url: "rest/replicationnode/" + virtualHostNameEncoded + "/" + encodeURIComponent(this.nodeName.value),
+ sync: true, handleAs: "json",
+ headers: { "Content-Type": "application/json"},
+ putData: json.stringify(node),
+ load: function(x) { that.success = true; },
+ error: function(error) {that.success = false; that.failureReason = error;}});
+
+ // if success, change virtual host state to ACTIVE
+ if (this.success)
+ {
+ xhr.put({url: "rest/virtualhost/" + virtualHostNameEncoded,
+ sync: true, handleAs: "json",
+ headers: { "Content-Type": "application/json"},
+ putData: json.stringify({desiredState: "ACTIVE"}),
+ load: function(x) { that.success = true; },
+ error: function(error) {that.success = false; that.failureReason = error;}});
+ }
+ }
+
+ return this.success;
+ },
+
+ _initSettingsUI: function()
+ {
+ var that = this;
+ var layout = [[
+ {'name': 'Name', 'field': 'name', 'width': '200px', 'editable': true},
+ {'name': 'Value', 'field': 'value', 'width': 'auto', 'editable': true}
+ ]];
+ this.idGenerator = 0;
+ this.settingsStore= new ObjectStore({objectStore: new Memory({data: [], idProperty: "id"})});
+ this.settings = new DataGrid({
+ id: 'formAddVirtualHost.specific.jeSettingsGrid',
+ store: this.settingsStore,
+ structure: layout,
+ rowSelector: '20px',
+ query:{ name: '*' },
+ rowsPerPage:20,
+ selectionMode: 'multiple',
+ disable: true},
+ "formAddVirtualHost.specific.jeSettings");
+
+ this.removeSettingButton = registry.byId("formAddVirtualHost.specific.removeSetting");
+ this.removeSettingButton.set("disabled", true);
+
+ connect.connect(this.settings.selection, 'onSelected', function(rowIndex){
+ var data = that.settings.selection.getSelected();
+ that.removeSettingButton.set("disabled",!data.length );
+ });
+
+ this.settings.startup();
+
+ registry.byId("formAddVirtualHost.specific.addSetting").on("click", function(){
+ var rowIndex = that.settingsStore.objectStore.data.length;
+ var myNewItem = {id: (that.idGenerator++), name: "", value: ""};
+ that.settingsStore.objectStore.data.push(myNewItem);
+ that.settingsStore.objectStore.setData(that.settingsStore.objectStore.data);
+ that.settings.set("disabled", false);
+ that.removeSettingButton.set("disabled", false);
+ that.settings._refresh();
+ that.settings.focus.setFocusIndex( rowIndex, 0 );
+ that.settings.edit.setEditCell( that.settings.focus.cell, rowIndex );
+ });
+
+ this.removeSettingButton.on("click", function(){
+ var items = that.settings.selection.getSelected();
+ if(items.length){
+ var data = that.settingsStore.objectStore.data;
+
+ array.forEach(items, function(selectedItem){
+ if(selectedItem !== null){
+ for(var i=0; i<data.length;)
+ {
+ if (data[i].id==selectedItem.id)
+ {
+ data.splice(i, 1);
+ break;
+ }
+ else
+ {
+ i++;
+ }
+ }
+ }
+ });
+ that.settingsStore.objectStore.setData(data);
+ }
+
+ if (that.settingsStore.objectStore.data.length == 0)
+ {
+ that.settings.set("disabled", true);
+ that.removeSettingButton.set("disabled", true);
+ }
+
+ that.settings._refresh();
+ event.stop(e);
+ });
}
};
});
diff --git a/qpid/java/bdbstore/src/main/java/resources/js/qpid/management/virtualhost/bdb_ha/show.js b/qpid/java/bdbstore/src/main/java/resources/js/qpid/management/virtualhost/bdb_ha/show.js
new file mode 100644
index 0000000000..72d36d57d2
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/resources/js/qpid/management/virtualhost/bdb_ha/show.js
@@ -0,0 +1,187 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+define(["dojo/_base/xhr",
+ "dojo/parser",
+ "dojo/string",
+ "dojox/html/entities",
+ "dojo/query",
+ "dijit/registry",
+ "dojox/grid/EnhancedGrid",
+ "qpid/common/UpdatableStore",
+ "qpid/management/UserPreferences",
+ "dojo/domReady!"],
+ function (xhr, parser, json, entities, query, registry, EnhancedGrid, UpdatableStore, UserPreferences) {
+
+ var hostFields = ["desiredState", "quiesceOnMasterChange"];
+
+ var nodeFields = ["storePath", "name", "state", "role", "hostPort", "helperHostPort",
+ "coalescingSync", "designatedPrimary", "durability", "priority", "quorumOverride"];
+
+ var buttons = ["activateVirtualHostButton", "quiesceVirtualHostButton", "stopVirtualHostButton",
+ "editVirtualHostButton", "activateNodeButton", "stopNodeButton","editNodeButton",
+ "removeNodeButton", "transferMasterButton"];
+
+ function findNode(nodeClass, containerNode)
+ {
+ return query("." + nodeClass, containerNode)[0];
+ }
+
+ function convertMap(map)
+ {
+ var data =[];
+ for(var propName in map)
+ {
+ if(map.hasOwnProperty(propName))
+ {
+ data.push({id: propName, name: propName, value: map[propName]});
+ }
+ }
+ return data;
+ }
+
+ function BDBHA(containerNode) {
+ var that = this;
+ xhr.get({url: "virtualhost/bdb_ha/show.html",
+ sync: true,
+ load: function(template) {
+ containerNode.innerHTML = template;
+ parser.parse(containerNode);
+ }});
+ this._init(containerNode);
+ this.designatedPrimaryContainer = findNode("designatedPrimaryContainer", containerNode);
+ this.priorityContainer = findNode("priorityContainer", containerNode);
+ this.quorumOverrideContainer = findNode("quorumOverrideContainer", containerNode);
+ }
+
+ BDBHA.prototype.update=function(data)
+ {
+ for(var i = 0; i < hostFields.length; i++)
+ {
+ var name = hostFields[i];
+ this[name].innerHTML = entities.encode(String(data[name]));
+ }
+ var nodes = data.replicationnodes;
+ if (nodes && nodes.length>0)
+ {
+ var localNode = nodes[0];
+ for(var i = 0; i < nodeFields.length; i++)
+ {
+ var name = nodeFields[i];
+ this[name].innerHTML = entities.encode(String(localNode[name]));
+ }
+ this.parametersGrid.update(convertMap(localNode.parameters));
+ this.parametersGrid.grid._refresh();
+ this.replicationParametersGrid.update(convertMap(localNode.replicationParameters));
+ this.replicationParametersGrid.grid._refresh();
+ if (nodes.length < 3)
+ {
+ this.designatedPrimaryContainer.style.display="block";
+ this.priorityContainer.style.display="none";
+ this.quorumOverrideContainer.style.display="none";
+ }
+ else
+ {
+ this.designatedPrimaryContainer.style.display="none";
+ this.priorityContainer.style.display="block";
+ this.quorumOverrideContainer.style.display="block";
+ }
+ if (this.membersGrid.update(nodes))
+ {
+ this.membersGrid.grid._refresh();
+ }
+ }
+ };
+
+ BDBHA.prototype._init = function(containerNode)
+ {
+ this._initFields(hostFields, containerNode);
+ this._initFields(nodeFields, containerNode);
+
+ for(var i = 0; i < buttons.length; i++)
+ {
+ var buttonName = buttons[i];
+ var buttonNode = findNode(buttonName, containerNode);
+ if (buttonNode)
+ {
+ var buttonWidget = registry.byNode(buttonNode);
+ if (buttonWidget)
+ {
+ this[buttonName] = buttonWidget;
+ var handler = this["_onClick_" + buttonName];
+ if (handler)
+ {
+ buttonWidget.on("click", function(evt){
+ handler(evt);
+ });
+ }
+ else
+ {
+ buttonWidget.set("disabled", true);
+ }
+ }
+ }
+ }
+
+ this.membersGrid = new UpdatableStore([],
+ findNode("cluserNodes", containerNode),
+ [
+ { name: 'Name', field: 'name', width: '10%' },
+ { name: 'Role', field: 'role', width: '10%' },
+ { name: 'Host and Port', field: 'hostPort', width: '35%' },
+ { name: 'Join Time', field: 'joinTime', width: '25%', formatter: function(value){ return value ? UserPreferences.formatDateTime(value) : "";} },
+ { name: 'Replication Transaction ID', field: 'lastKnownReplicationTransactionId', width: '20%' }
+ ],
+ null,
+ {
+ keepSelection: true,
+ plugins: {
+ indirectSelection: true
+ }
+ },
+ EnhancedGrid, true );
+
+ this.parametersGrid = new UpdatableStore([],
+ findNode("parameters", containerNode),
+ [
+ { name: 'Name', field: 'name', width: '50%' },
+ { name: 'Value', field: 'value', width: '50%' }
+ ],
+ null, null, null, true );
+
+ this.replicationParametersGrid = new UpdatableStore([],
+ findNode("replicationParameters", containerNode),
+ [
+ { name: 'Name', field: 'name', width: '50%' },
+ { name: 'Value', field: 'value', width: '50%' }
+ ],
+ null, null, null, true );
+ }
+
+ BDBHA.prototype._initFields = function(nodeFields, containerNode)
+ {
+ for(var i = 0; i < nodeFields.length; i++)
+ {
+ this[nodeFields[i]] = findNode(nodeFields[i], containerNode);
+ }
+ }
+
+ return BDBHA;
+});
diff --git a/qpid/java/bdbstore/src/main/java/resources/virtualhost/bdb_ha/add.html b/qpid/java/bdbstore/src/main/java/resources/virtualhost/bdb_ha/add.html
index 31734c818d..d51a5406da 100644
--- a/qpid/java/bdbstore/src/main/java/resources/virtualhost/bdb_ha/add.html
+++ b/qpid/java/bdbstore/src/main/java/resources/virtualhost/bdb_ha/add.html
@@ -26,50 +26,50 @@
<td class="tableContainer-labelCell" style="width: 300px;"><strong>Node Name*: </strong></td>
<td class="tableContainer-valueCell">
<input dojoType="dijit/form/ValidationTextBox" id="formAddVirtualHost.specific.nodeName"
- required="true" name="haNodeName" placeholder="node name"/>
+ required="true" name="nodeName" placeholder="node name"/>
</td>
</tr>
<tr>
<td class="tableContainer-labelCell" style="width: 300px;"><strong>Replication Group*: </strong></td>
<td class="tableContainer-valueCell">
<input dojoType="dijit/form/ValidationTextBox" id="formAddVirtualHost.specific.groupName"
- required="true" name="haGroupName" placeholder="group name"/>
+ required="true" name="groupName" placeholder="group name"/>
</td>
</tr>
<tr>
<td class="tableContainer-labelCell" style="width: 300px;"><strong>Node Address*: </strong></td>
<td class="tableContainer-valueCell">
- <input dojoType="dijit/form/ValidationTextBox" id="formAddVirtualHost.specific.nodeAddress"
- required="true" name="haNodeAddress" data-dojo-props="regExp:'([0-9a-zA-Z.-_]|::)+:[0-9]{1,5}', invalidMessage:'Must be of the form host:port'" placeholder="host:port"/>
+ <input dojoType="dijit/form/ValidationTextBox" id="formAddVirtualHost.specific.hostPort"
+ required="true" name="hostPort" data-dojo-props="regExp:'([0-9a-zA-Z.-_]|::)+:[0-9]{1,5}', invalidMessage:'Must be of the form host:port'" placeholder="host:port"/>
</td>
</tr>
<tr>
<td class="tableContainer-labelCell" style="width: 300px;"><strong>Helper Address*: </strong></td>
<td class="tableContainer-valueCell">
- <input dojoType="dijit/form/ValidationTextBox" id="formAddVirtualHost.specific.helperAddress"
- required="true" name="haHelperAddress" data-dojo-props="regExp:'([0-9a-zA-Z.-_]|::)+:[0-9]{1,5}', invalidMessage:'Must be of the form host:port'" placeholder="host:port"/>
+ <input dojoType="dijit/form/ValidationTextBox" id="formAddVirtualHost.specific.helperHostPort"
+ required="true" name="helperHostPort" data-dojo-props="regExp:'([0-9a-zA-Z.-_]|::)+:[0-9]{1,5}', invalidMessage:'Must be of the form host:port'" placeholder="host:port"/>
</td>
</tr>
<tr>
<td class="tableContainer-labelCell" style="width: 300px;"><strong>Durability: </strong></td>
<td class="tableContainer-valueCell">
<input dojoType="dijit/form/ValidationTextBox" id="formAddVirtualHost.specific.haDurability"
- name="haDurability" placeholder="NO_SYNC,NO_SYNC,SIMPLE_MAJORITY"/>
+ name="durability" placeholder="NO_SYNC,NO_SYNC,SIMPLE_MAJORITY"/>
</td>
</tr>
<tr>
<td class="tableContainer-labelCell" style="width: 300px;"><strong>Coalesce local sync: </strong></td>
<td class="tableContainer-valueCell">
- <input dojoType="dijit/form/CheckBox" id="formAddVirtualHost.specific.haCoalescingSync"
+ <input dojoType="dijit/form/CheckBox" id="formAddVirtualHost.specific.coalescingSync"
checked="true" onchange="require(['dijit/registry', 'dojo/domReady!'],
function(registry){
- var checkbox = registry.byId('formAddVirtualHost.specific.haCoalescingSync');
- var hidden = registry.byId('formAddVirtualHost.specific.haCoalescingSyncHidden');
+ var checkbox = registry.byId('formAddVirtualHost.specific.coalescingSync');
+ var hidden = registry.byId('formAddVirtualHost.specific.coalescingSyncHidden');
hidden.set('value', checkbox.get('checked'));
})"/>
- <input dojoType="dijit/form/TextBox" id="formAddVirtualHost.specific.haCoalescingSyncHidden" type="hidden" name="haCoalescingSync" value="true"/>
+ <input dojoType="dijit/form/TextBox" id="formAddVirtualHost.specific.coalescingSyncHidden" type="hidden" name="coalescingSync" value="true"/>
</td>
</tr>
@@ -77,14 +77,20 @@
<tr>
<td class="tableContainer-labelCell" style="width: 300px;"><strong>Designated Primary: </strong></td>
<td class="tableContainer-valueCell">
- <input dojoType="dijit/form/CheckBox" id="formAddVirtualHost.specific.haDesignatedPrimary"
+ <input dojoType="dijit/form/CheckBox" id="formAddVirtualHost.specific.designatedPrimary"
onchange="require(['dijit/registry', 'dojo/domReady!'],
function(registry){
- var checkbox = registry.byId('formAddVirtualHost.specific.haDesignatedPrimary');
- var hidden = registry.byId('formAddVirtualHost.specific.haDesignatedPrimaryHidden');
+ var checkbox = registry.byId('formAddVirtualHost.specific.designatedPrimary');
+ var hidden = registry.byId('formAddVirtualHost.specific.designatedPrimaryHidden');
hidden.set('value', checkbox.get('checked'));
})"/>
- <input dojoType="dijit/form/TextBox" id="formAddVirtualHost.specific.haDesignatedPrimaryHidden" type="hidden" name="haDesignatedPrimary" value="false"/>
+ <input dojoType="dijit/form/TextBox" id="formAddVirtualHost.specific.designatedPrimaryHidden" type="hidden" name="designatedPrimary" value="false"/>
</td>
</tr>
</table>
+
+<div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Override JE settings', open: false">
+ <div id="formAddVirtualHost.specific.jeSettings" style="width: 400px; height: 100px"></div>
+ <div id='formAddVirtualHost.specific.addSetting' data-dojo-type="dijit.form.Button" data-dojo-props="title:'Add new setting'">Add</div>
+ <div id='formAddVirtualHost.specific.removeSetting' data-dojo-type="dijit.form.Button" data-dojo-props="title:'Remove the selected settings', disable:true">Remove</div>
+</div> \ No newline at end of file
diff --git a/qpid/java/bdbstore/src/main/java/resources/virtualhost/bdb_ha/show.html b/qpid/java/bdbstore/src/main/java/resources/virtualhost/bdb_ha/show.html
new file mode 100644
index 0000000000..71862bfade
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/java/resources/virtualhost/bdb_ha/show.html
@@ -0,0 +1,117 @@
+<!--
+ -
+ - Licensed to the Apache Software Foundation (ASF) under one
+ - or more contributor license agreements. See the NOTICE file
+ - distributed with this work for additional information
+ - regarding copyright ownership. The ASF licenses this file
+ - to you under the Apache License, Version 2.0 (the
+ - "License"); you may not use this file except in compliance
+ - with the License. You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing,
+ - software distributed under the License is distributed on an
+ - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ - KIND, either express or implied. See the License for the
+ - specific language governing permissions and limitations
+ - under the License.
+ -
+ -->
+<br/>
+<div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Replication Group'">
+ <div style="clear:both">
+ <div class="formLabel-labelCell" style="float:left; width: 200px">Desired State:</div>
+ <div class="desiredState" style="float:left;">ACTIVE</div>
+ </div>
+ <div style="clear:both">
+ <div class="formLabel-labelCell" style="float:left; width: 200px;">Quiesce On Master Change:</div>
+ <div style="float:left;">
+ <input class="quiesceOnMasterChange" type="checkbox" disabled='disabled'/>
+ </div>
+ </div>
+ <div style="clear:both"></div>
+
+ <div class="dijitDialogPaneActionBar">
+ <button data-dojo-type="dijit.form.Button" class="activateVirtualHostButton" data-dojo-props="label: 'Activate', disabled: true" type="button">Activate</button>
+ <button data-dojo-type="dijit.form.Button" class="quiesceVirtualHostButton" type="button">Quiesce</button>
+ <button data-dojo-type="dijit.form.Button" class="stopVirtualHostButton" type="button">Stop</button>
+ <button data-dojo-type="dijit.form.Button" class="editVirtualHostButton" type="button">Edit</button>
+ </div>
+</div>
+<br/>
+
+<div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Node'">
+ <div style="clear:both">
+ <div class="formLabel-labelCell" style="float:left; width: 200px;">Node Name:</div>
+ <div class="name" style="float:left;">N/A</div>
+ </div>
+ <div style="clear:both">
+ <div class="formLabel-labelCell" style="float:left; width: 200px">Store Path:</div>
+ <div class="storePath" style="float:left;"></div>
+ </div>
+ <div style="clear:both">
+ <div class="formLabel-labelCell" style="float:left; width: 200px;">State:</div>
+ <div class="state" style="float:left;">ACTIVE</div>
+ </div>
+ <div style="clear:both">
+ <div class="formLabel-labelCell" style="float:left; width: 200px;">Role:</div>
+ <div class="role" style="float:left;">N/A</div>
+ </div>
+ <div style="clear:both">
+ <div class="formLabel-labelCell" style="float:left; width: 200px;">Host and Port:</div>
+ <div class="hostPort" style="float:left;">N/A</div>
+ </div>
+ <div style="clear:both">
+ <div class="formLabel-labelCell" style="float:left; width: 200px;">Helper Node Host and Port:</div>
+ <div class="helperHostPort" style="float:left;">N/A</div>
+ </div>
+ <div style="clear:both">
+ <div class="formLabel-labelCell" style="float:left; width: 200px;">Coalescing Sync:</div>
+ <div class="coalescingSync" style="float:left;">N/A</div>
+ </div>
+ <div style="clear:both">
+ <div class="formLabel-labelCell" style="float:left; width: 200px;">Durability:</div>
+ <div class="durability" style="float:left;">N/A</div>
+ </div>
+ <div style="clear:both" class="designatedPrimaryContainer">
+ <div class="formLabel-labelCell" style="float:left; width: 200px;">Designated Primary:</div>
+ <div class="designatedPrimary" style="float:left;">N/A</div>
+ </div>
+ <div style="clear:both" class="priorityContainer">
+ <div class="formLabel-labelCell" style="float:left; width: 200px;">Priority:</div>
+ <div class="priority" style="float:left;">Never</div>
+ </div>
+ <div style="clear:both" class="quorumOverrideContainer">
+ <div class="formLabel-labelCell" style="float:left; width: 200px;">Quorum override:</div>
+ <div style="float:left;">
+ <span class="quorumOverride" >N/A</span>
+ <span style="margin-left: 20px;">[ 0 signifies simple majority ]</span>
+ </div>
+ </div>
+ <div style="clear:both"></div>
+ <br/>
+ <div style="clear:both" data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Parameters'">
+ <div class="parameters"></div>
+ </div>
+ <div style="clear:both"></div>
+ <br/>
+ <div style="clear:both" data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Replication Parameters'">
+ <div class="replicationParameters"></div>
+ </div>
+ <div style="clear:both"></div>
+ <div class="dijitDialogPaneActionBar">
+ <button data-dojo-type="dijit.form.Button" class="activateNodeButton" type="button" data-dojo-props="iconClass: 'dijitEditorIconCreateLink', disabled: true">Activate</button>
+ <button data-dojo-type="dijit.form.Button" class="stopNodeButton" type="button" data-dojo-props="iconClass: 'dijitEditorIconUnlink'">Stop</button>
+ <button data-dojo-type="dijit.form.Button" class="editNodeButton" type="button">Edit</button>
+ </div>
+</div>
+
+<br/>
+<div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Group nodes'">
+ <div class="cluserNodes"></div>
+ <div class="cluserNodesToolbar dijitDialogPaneActionBar">
+ <button data-dojo-type="dijit.form.Button" class="removeNodeButton" data-dojo-props="iconClass: 'dijitIconDelete'">Remove Node</button>
+ <button data-dojo-type="dijit.form.Button" class="transferMasterButton" data-dojo-props="iconClass: 'dijitIconConfigure'">Make Node Master</button>
+ </div>
+</div> \ No newline at end of file
diff --git a/qpid/java/bdbstore/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.ReplicationNodeFactory b/qpid/java/bdbstore/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.ReplicationNodeFactory
new file mode 100644
index 0000000000..3050cbbd81
--- /dev/null
+++ b/qpid/java/bdbstore/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.ReplicationNodeFactory
@@ -0,0 +1,19 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT 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.replication.LocalReplicationNodeFactory \ No newline at end of file
diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBHAMessageStoreTest.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBHAMessageStoreTest.java
deleted file mode 100644
index d7acf27f75..0000000000
--- a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBHAMessageStoreTest.java
+++ /dev/null
@@ -1,168 +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.server.store.berkeleydb;
-
-import java.io.File;
-import java.net.InetAddress;
-
-import java.util.HashMap;
-import java.util.Map;
-import org.apache.commons.configuration.XMLConfiguration;
-import org.apache.qpid.server.configuration.VirtualHostConfiguration;
-import org.apache.qpid.server.util.BrokerTestHelper;
-import org.apache.qpid.server.virtualhost.VirtualHost;
-import org.apache.qpid.test.utils.QpidTestCase;
-import org.apache.qpid.util.FileUtils;
-
-import com.sleepycat.je.Environment;
-import com.sleepycat.je.EnvironmentConfig;
-import com.sleepycat.je.rep.ReplicatedEnvironment;
-import com.sleepycat.je.rep.ReplicationConfig;
-
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-
-public class BDBHAMessageStoreTest extends QpidTestCase
-{
- private static final String TEST_LOG_FILE_MAX = "1000000";
- private static final String TEST_ELECTION_RETRIES = "1000";
- private static final String TEST_NUMBER_OF_THREADS = "10";
- private static final String TEST_ENV_CONSISTENCY_TIMEOUT = "9999999";
- private String _groupName;
- private String _workDir;
- private int _masterPort;
- private String _host;
- private XMLConfiguration _configXml;
- private VirtualHost _virtualHost;
- private org.apache.qpid.server.model.VirtualHost _modelVhost;
-
- public void setUp() throws Exception
- {
- super.setUp();
-
- _workDir = TMP_FOLDER + File.separator + getName();
- _host = InetAddress.getByName("localhost").getHostAddress();
- _groupName = "group" + getName();
- _masterPort = -1;
-
- FileUtils.delete(new File(_workDir), true);
- _configXml = new XMLConfiguration();
- _modelVhost = mock(org.apache.qpid.server.model.VirtualHost.class);
-
-
- BrokerTestHelper.setUp();
- }
-
- public void tearDown() throws Exception
- {
- try
- {
- if (_virtualHost != null)
- {
- _virtualHost.close();
- }
- FileUtils.delete(new File(_workDir), true);
- }
- finally
- {
- BrokerTestHelper.tearDown();
- super.tearDown();
- }
- }
-
- public void testSetSystemConfiguration() throws Exception
- {
- // create virtual host configuration, registry and host instance
- addVirtualHostConfiguration();
- String vhostName = "test" + _masterPort;
- VirtualHostConfiguration configuration = new VirtualHostConfiguration(vhostName, _configXml.subset("virtualhosts.virtualhost." + vhostName), BrokerTestHelper.createBrokerMock());
-
- _virtualHost = BrokerTestHelper.createVirtualHost(configuration,null,_modelVhost);
- BDBHAMessageStore store = (BDBHAMessageStore) _virtualHost.getMessageStore();
-
- // test whether JVM system settings were applied
- Environment env = store.getEnvironment();
- assertEquals("Unexpected number of cleaner threads", TEST_NUMBER_OF_THREADS, env.getConfig().getConfigParam(EnvironmentConfig.CLEANER_THREADS));
- assertEquals("Unexpected log file max", TEST_LOG_FILE_MAX, env.getConfig().getConfigParam(EnvironmentConfig.LOG_FILE_MAX));
-
- ReplicatedEnvironment repEnv = store.getReplicatedEnvironment();
- assertEquals("Unexpected number of elections primary retries", TEST_ELECTION_RETRIES,
- repEnv.getConfig().getConfigParam(ReplicationConfig.ELECTIONS_PRIMARY_RETRIES));
- assertEquals("Unexpected number of elections primary retries", TEST_ENV_CONSISTENCY_TIMEOUT,
- repEnv.getConfig().getConfigParam(ReplicationConfig.ENV_CONSISTENCY_TIMEOUT));
- }
-
- private void addVirtualHostConfiguration() throws Exception
- {
- int port = findFreePort();
- if (_masterPort == -1)
- {
- _masterPort = port;
- }
- String nodeName = getNodeNameForNodeAt(port);
-
- String vhostName = "test" + port;
- String vhostPrefix = "virtualhosts.virtualhost." + vhostName;
-
- _configXml.addProperty("virtualhosts.virtualhost.name", vhostName);
- _configXml.addProperty(vhostPrefix + ".type", BDBHAVirtualHostFactory.TYPE);
-
- when(_modelVhost.getAttribute(eq(_modelVhost.STORE_PATH))).thenReturn(_workDir + File.separator
- + port);
- when(_modelVhost.getAttribute(eq("haGroupName"))).thenReturn(_groupName);
- when(_modelVhost.getAttribute(eq("haNodeName"))).thenReturn(nodeName);
- when(_modelVhost.getAttribute(eq("haNodeAddress"))).thenReturn(getNodeHostPortForNodeAt(port));
- when(_modelVhost.getAttribute(eq("haHelperAddress"))).thenReturn(getHelperHostPort());
-
- Map<String,String> bdbEnvConfig = new HashMap<String,String>();
- bdbEnvConfig.put(EnvironmentConfig.CLEANER_THREADS, TEST_NUMBER_OF_THREADS);
- bdbEnvConfig.put(EnvironmentConfig.LOG_FILE_MAX, TEST_LOG_FILE_MAX);
-
- when(_modelVhost.getAttribute(eq("bdbEnvironmentConfig"))).thenReturn(bdbEnvConfig);
-
- Map<String,String> repConfig = new HashMap<String,String>();
- repConfig.put(ReplicationConfig.ELECTIONS_PRIMARY_RETRIES, TEST_ELECTION_RETRIES);
- repConfig.put(ReplicationConfig.ENV_CONSISTENCY_TIMEOUT, TEST_ENV_CONSISTENCY_TIMEOUT);
- when(_modelVhost.getAttribute(eq("haReplicationConfig"))).thenReturn(repConfig);
-
- }
-
- private String getNodeNameForNodeAt(final int bdbPort)
- {
- return "node" + getName() + bdbPort;
- }
-
- private String getNodeHostPortForNodeAt(final int bdbPort)
- {
- return _host + ":" + bdbPort;
- }
-
- private String getHelperHostPort()
- {
- if (_masterPort == -1)
- {
- throw new IllegalStateException("Helper port not yet assigned.");
- }
- return _host + ":" + _masterPort;
- }
-}
diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreQuotaEventsTest.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreQuotaEventsTest.java
index 4684358190..7a645a6932 100644
--- a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreQuotaEventsTest.java
+++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreQuotaEventsTest.java
@@ -65,7 +65,7 @@ public class BDBMessageStoreQuotaEventsTest extends MessageStoreQuotaEventsTestB
_logger.debug("Applying store specific config. overfull-sze=" + OVERFULL_SIZE + ", underfull-size=" + UNDERFULL_SIZE);
Map<String,String> envMap = Collections.singletonMap("je.log.fileMax", MAX_BDB_LOG_SIZE);
- when(virtualHost.getAttribute(eq("bdbEnvironmentConfig"))).thenReturn(envMap);
+ when(virtualHost.getAttribute(eq(BDBMessageStore.ENVIRONMENT_CONFIGURATION))).thenReturn(envMap);
when(virtualHost.getAttribute(eq(MessageStoreConstants.OVERFULL_SIZE_ATTRIBUTE))).thenReturn(OVERFULL_SIZE);
when(virtualHost.getAttribute(eq(MessageStoreConstants.UNDERFULL_SIZE_ATTRIBUTE))).thenReturn(UNDERFULL_SIZE);
diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/MessageStoreCreatorTest.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/MessageStoreCreatorTest.java
index 730001d849..48fb180984 100644
--- a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/MessageStoreCreatorTest.java
+++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/MessageStoreCreatorTest.java
@@ -22,20 +22,15 @@ package org.apache.qpid.server.store.berkeleydb;
import org.apache.qpid.server.store.MessageStore;
import org.apache.qpid.server.store.MessageStoreCreator;
-import org.apache.qpid.server.store.berkeleydb.BDBMessageStore;
import org.apache.qpid.test.utils.QpidTestCase;
public class MessageStoreCreatorTest extends QpidTestCase
{
- private static final String[] STORE_TYPES = {BDBMessageStore.TYPE};
-
public void testMessageStoreCreator()
{
MessageStoreCreator messageStoreCreator = new MessageStoreCreator();
- for (String type : STORE_TYPES)
- {
- MessageStore store = messageStoreCreator.createMessageStore(type);
- assertNotNull("Store of type " + type + " is not created", store);
- }
+ String type = new BDBMessageStoreFactory().getType();
+ MessageStore store = messageStoreCreator.createMessageStore(type);
+ assertNotNull("Store of type " + type + " is not created", store);
}
}
diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/StandardEnvironmentFacadeTest.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/StandardEnvironmentFacadeTest.java
new file mode 100644
index 0000000000..b19e18b204
--- /dev/null
+++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/StandardEnvironmentFacadeTest.java
@@ -0,0 +1,128 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb;
+
+import java.io.File;
+import java.util.Collections;
+
+import org.apache.qpid.test.utils.QpidTestCase;
+import org.apache.qpid.util.FileUtils;
+
+import com.sleepycat.je.Database;
+import com.sleepycat.je.DatabaseConfig;
+import com.sleepycat.je.Environment;
+
+public class StandardEnvironmentFacadeTest extends QpidTestCase
+{
+ protected File _storePath;
+ protected EnvironmentFacade _environmentFacade;
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ _storePath = new File(TMP_FOLDER + File.separator + "bdb" + File.separator + getTestName());
+ }
+
+ protected void tearDown() throws Exception
+ {
+ try
+ {
+ super.tearDown();
+ if (_environmentFacade != null)
+ {
+ _environmentFacade.close();
+ }
+ }
+ finally
+ {
+ if (_storePath != null)
+ {
+ FileUtils.delete(_storePath, true);
+ }
+ }
+ }
+
+ public void testEnvironmentFacade() throws Exception
+ {
+ EnvironmentFacade ef = getEnvironmentFacade();
+ assertNotNull("Environment should not be null", ef);
+ Environment e = ef.getEnvironment();
+ assertTrue("Environment is not valid", e.isValid());
+ }
+
+ public void testClose() throws Exception
+ {
+ EnvironmentFacade ef = getEnvironmentFacade();
+ ef.close();
+ Environment e = ef.getEnvironment();
+
+ assertNull("Environment should be null after facade close", e);
+ }
+
+ public void testOpenDatabases() throws Exception
+ {
+ EnvironmentFacade ef = getEnvironmentFacade();
+ DatabaseConfig dbConfig = new DatabaseConfig();
+ dbConfig.setTransactional(true);
+ dbConfig.setAllowCreate(true);
+ ef.openDatabases(dbConfig, "test1", "test2");
+ Database test1 = ef.getOpenDatabase("test1");
+ Database test2 = ef.getOpenDatabase("test2");
+
+ assertEquals("Unexpected name for open database test1", "test1" , test1.getDatabaseName());
+ assertEquals("Unexpected name for open database test2", "test2" , test2.getDatabaseName());
+ }
+
+ public void testGetOpenDatabaseForNonExistingDatabase() throws Exception
+ {
+ EnvironmentFacade ef = getEnvironmentFacade();
+ DatabaseConfig dbConfig = new DatabaseConfig();
+ dbConfig.setTransactional(true);
+ dbConfig.setAllowCreate(true);
+ ef.openDatabases(dbConfig, "test1");
+ Database test1 = ef.getOpenDatabase("test1");
+ assertEquals("Unexpected name for open database test1", "test1" , test1.getDatabaseName());
+ try
+ {
+ ef.getOpenDatabase("test2");
+ fail("An exception should be thrown for the non existing database");
+ }
+ catch(IllegalArgumentException e)
+ {
+ assertEquals("Unexpected exception message", "Database with name 'test2' has not been opened", e.getMessage());
+ }
+ }
+
+ EnvironmentFacade getEnvironmentFacade() throws Exception
+ {
+ if (_environmentFacade == null)
+ {
+ _environmentFacade = createEnvironmentFacade();
+ }
+ return _environmentFacade;
+ }
+
+ EnvironmentFacade createEnvironmentFacade()
+ {
+ return new StandardEnvironmentFacade(_storePath.getAbsolutePath(), Collections.<String, String>emptyMap());
+ }
+
+}
diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/VirtualHostTest.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/VirtualHostTest.java
new file mode 100644
index 0000000000..7269988042
--- /dev/null
+++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/VirtualHostTest.java
@@ -0,0 +1,285 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+import org.apache.qpid.server.configuration.ConfigurationEntry;
+import org.apache.qpid.server.configuration.ConfigurationEntryStore;
+import org.apache.qpid.server.configuration.ConfiguredObjectRecoverer;
+import org.apache.qpid.server.configuration.RecovererProvider;
+import org.apache.qpid.server.configuration.startup.ReplicationNodeRecoverer;
+import org.apache.qpid.server.configuration.startup.VirtualHostRecoverer;
+import org.apache.qpid.server.configuration.updater.TaskExecutor;
+import org.apache.qpid.server.logging.SystemOutMessageLogger;
+import org.apache.qpid.server.logging.actors.CurrentActor;
+import org.apache.qpid.server.logging.actors.TestLogActor;
+import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.ReplicationNode;
+import org.apache.qpid.server.model.State;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.stats.StatisticsGatherer;
+import org.apache.qpid.server.store.berkeleydb.replication.ReplicatedEnvironmentFacade;
+import org.apache.qpid.server.util.BrokerTestHelper;
+import org.apache.qpid.server.virtualhost.StandardVirtualHostFactory;
+import org.apache.qpid.test.utils.QpidTestCase;
+import org.apache.qpid.test.utils.TestFileUtils;
+import org.apache.qpid.util.FileUtils;
+
+import com.sleepycat.je.EnvironmentConfig;
+import com.sleepycat.je.rep.ReplicatedEnvironment;
+import com.sleepycat.je.rep.ReplicationConfig;
+
+public class VirtualHostTest extends QpidTestCase
+{
+
+ private Broker _broker;
+ private StatisticsGatherer _statisticsGatherer;
+ private RecovererProvider _recovererProvider;
+ private File _configFile;
+ private File _bdbStorePath;
+ private VirtualHost _host;
+ private ConfigurationEntryStore _store;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ CurrentActor.set(new TestLogActor(new SystemOutMessageLogger()));
+
+ _store = mock(ConfigurationEntryStore.class);
+ _broker = BrokerTestHelper.createBrokerMock();
+ TaskExecutor taslExecutor = mock(TaskExecutor.class);
+ when(taslExecutor.isTaskExecutorThread()).thenReturn(true);
+ when(_broker.getTaskExecutor()).thenReturn(taslExecutor);
+
+
+ _recovererProvider = new RecovererProvider()
+ {
+ @Override
+ public ConfiguredObjectRecoverer<? extends ConfiguredObject> getRecoverer(String type)
+ {
+ if (type.equals(ReplicationNode.class.getSimpleName()))
+ {
+ return new ReplicationNodeRecoverer();
+ }
+ throw new IllegalArgumentException("Not supported type: " + type);
+ }
+ };
+
+ _statisticsGatherer = mock(StatisticsGatherer.class);
+
+ _bdbStorePath = new File(TMP_FOLDER, getTestName() + "." + System.currentTimeMillis());
+ _bdbStorePath.deleteOnExit();
+ }
+
+ @Override
+ protected void tearDown() throws Exception
+ {
+ try
+ {
+ if (_host != null)
+ {
+ _host.setDesiredState(_host.getActualState(), State.STOPPED);
+ }
+ }
+ finally
+ {
+ if (_configFile != null)
+ {
+ _configFile.delete();
+ }
+ if (_bdbStorePath != null)
+ {
+ FileUtils.delete(_bdbStorePath, true);
+ }
+ super.tearDown();
+ CurrentActor.remove();
+ }
+ }
+
+ public void testCreateBdbHaVirtualHostFromConfigurationEntry()
+ {
+ String repStreamTimeout = "2 h";
+ String nodeName = "node";
+ String groupName = "group";
+ String nodeHostPort = "localhost:" + findFreePort();
+ String helperHostPort = nodeHostPort;
+ String durability = "NO_SYNC,SYNC,NONE";
+
+ UUID nodeId = UUID.randomUUID();
+ Map<String, Object> nodeAttributes = new HashMap<String, Object>();
+ nodeAttributes.put(ReplicationNode.NAME, nodeName);
+ nodeAttributes.put(ReplicationNode.GROUP_NAME, groupName);
+ nodeAttributes.put(ReplicationNode.HOST_PORT, nodeHostPort);
+ nodeAttributes.put(ReplicationNode.HELPER_HOST_PORT, helperHostPort);
+ nodeAttributes.put(ReplicationNode.DURABILITY, durability);
+ nodeAttributes.put(ReplicationNode.STORE_PATH, _bdbStorePath.getAbsolutePath());
+ nodeAttributes.put(ReplicationNode.REPLICATION_PARAMETERS,
+ Collections.singletonMap(ReplicationConfig.REP_STREAM_TIMEOUT, repStreamTimeout));
+
+ ConfigurationEntry nodeEntry = new ConfigurationEntry(nodeId, ReplicationNode.class.getSimpleName(),
+ nodeAttributes, Collections.<UUID> emptySet(), _store);
+ when(_store.getEntry(nodeId)).thenReturn(nodeEntry);
+
+ String hostName = getName();
+
+ Map<String, Object> virtualHostAttributes = new HashMap<String, Object>();
+ virtualHostAttributes.put(VirtualHost.NAME, hostName);
+ virtualHostAttributes.put(VirtualHost.TYPE, BDBHAVirtualHostFactory.TYPE);
+
+ _host = createHost(virtualHostAttributes, Collections.singleton(nodeId));
+ _host.setDesiredState(State.INITIALISING, State.ACTIVE);
+
+ assertEquals("Unexpected host name", hostName, _host.getName());
+ assertEquals("Unexpected host type", BDBHAVirtualHostFactory.TYPE, _host.getType());
+ assertEquals("Unexpected store type", ReplicatedEnvironmentFacade.TYPE, _host.getAttribute(VirtualHost.STORE_TYPE));
+
+ ReplicationNode localNode = _host.getChildren(ReplicationNode.class).iterator().next();
+
+ assertEquals(nodeName, localNode.getName());
+ assertEquals(groupName, localNode.getAttribute(ReplicationNode.GROUP_NAME));
+ assertEquals(nodeHostPort, localNode.getAttribute(ReplicationNode.HOST_PORT));
+ assertEquals(helperHostPort, localNode.getAttribute(ReplicationNode.HELPER_HOST_PORT));
+ assertEquals(durability, localNode.getAttribute(ReplicationNode.DURABILITY));
+ assertEquals("Unexpected store path", _bdbStorePath.getAbsolutePath(), localNode.getAttribute(ReplicationNode.STORE_PATH));
+
+ BDBMessageStore messageStore = (BDBMessageStore) _host.getMessageStore();
+ ReplicatedEnvironment environment = (ReplicatedEnvironment) messageStore.getEnvironmentFacade().getEnvironment();
+ ReplicationConfig envConfig = environment.getRepConfig();
+ assertEquals("Unexpected JE replication stream timeout", repStreamTimeout, envConfig.getConfigParam(ReplicationConfig.REP_STREAM_TIMEOUT));
+
+ }
+
+ public void testCreateBdbVirtualHostFromConfigurationFile()
+ {
+ String hostName = getName();
+ long logFileMax = 2000000;
+ _host = createHostFromConfiguration(hostName, logFileMax);
+ _host.setDesiredState(State.INITIALISING, State.ACTIVE);
+ assertEquals("Unexpected host name", hostName, _host.getName());
+ assertEquals("Unexpected host type", StandardVirtualHostFactory.TYPE, _host.getType());
+ assertEquals("Unexpected store type", new BDBMessageStoreFactory().getType(), _host.getAttribute(VirtualHost.STORE_TYPE));
+ assertEquals("Unexpected store path", _bdbStorePath.getAbsolutePath(), _host.getAttribute(VirtualHost.STORE_PATH));
+
+ BDBMessageStore messageStore = (BDBMessageStore) _host.getMessageStore();
+ EnvironmentConfig envConfig = messageStore.getEnvironmentFacade().getEnvironment().getConfig();
+ assertEquals("Unexpected JE log file max", String.valueOf(logFileMax), envConfig.getConfigParam(EnvironmentConfig.LOG_FILE_MAX));
+
+ }
+
+ public void testCreateBdbHaVirtualHostFromConfigurationFile()
+ {
+ String hostName = getName();
+
+ String repStreamTimeout = "2 h";
+ String nodeName = "node";
+ String groupName = "group";
+ String nodeHostPort = "localhost:" + findFreePort();
+ String helperHostPort = nodeHostPort;
+ String durability = "NO_SYNC,SYNC,NONE";
+ _host = createHaHostFromConfiguration(hostName, groupName, nodeName, nodeHostPort, helperHostPort, durability, repStreamTimeout);
+ _host.setDesiredState(State.INITIALISING, State.ACTIVE);
+ assertEquals("Unexpected host name", hostName, _host.getName());
+ assertEquals("Unexpected host type", BDBHAVirtualHostFactory.TYPE, _host.getType());
+ assertEquals("Unexpected store type", ReplicatedEnvironmentFacade.TYPE, _host.getAttribute(VirtualHost.STORE_TYPE));
+ assertEquals("Unexpected store path", _bdbStorePath.getAbsolutePath(), _host.getAttribute(VirtualHost.STORE_PATH));
+
+ ReplicationNode localNode = _host.getChildren(ReplicationNode.class).iterator().next();
+
+ assertEquals(nodeName, localNode.getName());
+ assertEquals(groupName, localNode.getAttribute(ReplicationNode.GROUP_NAME));
+ assertEquals(nodeHostPort, localNode.getAttribute(ReplicationNode.HOST_PORT));
+ assertEquals(helperHostPort, localNode.getAttribute(ReplicationNode.HELPER_HOST_PORT));
+ assertEquals(durability, localNode.getAttribute(ReplicationNode.DURABILITY));
+
+ BDBMessageStore messageStore = (BDBMessageStore) _host.getMessageStore();
+ ReplicatedEnvironment environment = (ReplicatedEnvironment) messageStore.getEnvironmentFacade().getEnvironment();
+ ReplicationConfig envConfig = environment.getRepConfig();
+ assertEquals("Unexpected JE replication stream timeout", repStreamTimeout, envConfig.getConfigParam(ReplicationConfig.REP_STREAM_TIMEOUT));
+ }
+
+ private VirtualHost createHost(Map<String, Object> attributes, Set<UUID> children)
+ {
+ ConfigurationEntry entry = new ConfigurationEntry(UUID.randomUUID(), VirtualHost.class.getSimpleName(), attributes,
+ children, _store);
+
+ return new VirtualHostRecoverer(_statisticsGatherer).create(_recovererProvider, entry, _broker);
+ }
+
+ private VirtualHost createHost(Map<String, Object> attributes)
+ {
+ return createHost(attributes, Collections.<UUID> emptySet());
+ }
+
+ private VirtualHost createHostFromConfiguration(String hostName, long logFileMax)
+ {
+ String content = "<virtualhosts><virtualhost><name>" + hostName + "</name><" + hostName + ">"
+ + "<store><class>" + BDBMessageStore.class.getName() + "</class>"
+ + "<environment-path>" + _bdbStorePath.getAbsolutePath() + "</environment-path>"
+ + "<envConfig><name>" + EnvironmentConfig.LOG_FILE_MAX + "</name><value>" + logFileMax + "</value></envConfig>"
+ + "</store>"
+ + "</" + hostName + "></virtualhost></virtualhosts>";
+ Map<String, Object> attributes = writeConfigAndGenerateAttributes(content);
+ return createHost(attributes);
+ }
+
+
+ private VirtualHost createHaHostFromConfiguration(String hostName, String groupName, String nodeName, String nodeHostPort, String helperHostPort, String durability, String repStreamTimeout)
+ {
+ String content = "<virtualhosts><virtualhost><name>" + hostName + "</name><" + hostName + ">"
+ + "<type>" + BDBHAVirtualHostFactory.TYPE + "</type>"
+ + "<store><class>" + BDBMessageStore.class.getName() + "</class>"
+ + "<environment-path>" + _bdbStorePath.getAbsolutePath() + "</environment-path>"
+ + "<highAvailability>"
+ + "<groupName>" + groupName + "</groupName>"
+ + "<nodeName>" + nodeName + "</nodeName>"
+ + "<nodeHostPort>" + nodeHostPort + "</nodeHostPort>"
+ + "<helperHostPort>" + helperHostPort + "</helperHostPort>"
+ + "<durability>" + durability.replaceAll(",", "\\\\,") + "</durability>"
+ + "</highAvailability>"
+ + "<repConfig><name>" + ReplicationConfig.REP_STREAM_TIMEOUT + "</name><value>" + repStreamTimeout + "</value></repConfig>"
+ + "</store>"
+ + "</" + hostName + "></virtualhost></virtualhosts>";
+ Map<String, Object> attributes = writeConfigAndGenerateAttributes(content);
+ return createHost(attributes);
+ }
+
+ private Map<String, Object> writeConfigAndGenerateAttributes(String content)
+ {
+ _configFile = TestFileUtils.createTempFile(this, ".virtualhost.xml", content);
+ Map<String, Object> attributes = new HashMap<String, Object>();
+ attributes.put(VirtualHost.NAME, getName());
+ attributes.put(VirtualHost.CONFIG_PATH, _configFile.getAbsolutePath());
+ return attributes;
+ }
+}
+
+ \ No newline at end of file
diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/replication/LocalReplicationNodeTest.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/replication/LocalReplicationNodeTest.java
new file mode 100644
index 0000000000..fc0926d38e
--- /dev/null
+++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/replication/LocalReplicationNodeTest.java
@@ -0,0 +1,309 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.replication;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.UUID;
+
+import org.apache.qpid.server.configuration.IllegalConfigurationException;
+import org.apache.qpid.server.configuration.updater.TaskExecutor;
+import org.apache.qpid.server.model.ReplicationNode;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.test.utils.QpidTestCase;
+
+import com.sleepycat.je.rep.ReplicatedEnvironment;
+
+public class LocalReplicationNodeTest extends QpidTestCase
+{
+
+ private static final Object INVALID_VALUE = new Object();
+ private UUID _id;
+ private VirtualHost _virtualHost;
+ private TaskExecutor _taskExecutor;
+ private ReplicatedEnvironmentFacade _facade;
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ _taskExecutor = mock(TaskExecutor.class);
+ when(_taskExecutor.isTaskExecutorThread()).thenReturn(true);
+ _virtualHost = mock(VirtualHost.class);
+ _facade = mock(ReplicatedEnvironmentFacade.class);
+ }
+
+ @Override
+ public void tearDown() throws Exception
+ {
+ super.tearDown();
+ }
+
+ public void testCreateLocalReplicationNodeWithoutDefaultParametersAndValidParameters()
+ {
+ Map<String, Object> attributes = createValidAttributes();
+
+ LocalReplicationNode node = new LocalReplicationNode(_id, attributes, _virtualHost, _taskExecutor);
+
+ assertNodeAttributes(attributes, node);
+
+ for (Map.Entry<String, Object> attributeEntry : LocalReplicationNode.DEFAULTS.entrySet())
+ {
+ assertEquals("Unexpected attribute value for attribute name " + attributeEntry.getKey(), attributeEntry.getValue(), node.getAttribute(attributeEntry.getKey()));
+ }
+ }
+
+ public void testCreateLocalReplicationNodeWithoutDefaultParametersAndMissedParameters()
+ {
+ Map<String, Object> attributes = createValidAttributes();
+
+ for (Map.Entry<String, Object> attributeEntry : attributes.entrySet())
+ {
+ String name = attributeEntry.getKey();
+ Map<String, Object> incompleteAttributes = new HashMap<String, Object>(attributes);
+ incompleteAttributes.remove(name);
+ try
+ {
+ new LocalReplicationNode(_id, incompleteAttributes, _virtualHost, _taskExecutor);
+ fail("Node creation should fails when attribute " + name + " is missed");
+ }
+ catch(IllegalConfigurationException e)
+ {
+ // pass
+ }
+ }
+ }
+
+ public void testCreateLocalReplicationNodeWithoutDefaultParametersAndInvalidParameters()
+ {
+
+ Map<String, Object> attributes = createValidAttributes();
+
+ for (Map.Entry<String, Object> attributeEntry : attributes.entrySet())
+ {
+ String name = attributeEntry.getKey();
+ Object value = attributeEntry.getValue();
+ if (!(value instanceof String))
+ {
+ Map<String, Object> invalidAttributes = new HashMap<String, Object>(attributes);
+ invalidAttributes.put(name, INVALID_VALUE);
+ try
+ {
+ new LocalReplicationNode(_id, attributes, _virtualHost, _taskExecutor);
+ fail("Node creation should fails when attribute " + name + " is invalid");
+ }
+ catch(IllegalConfigurationException e)
+ {
+ // pass
+ }
+ }
+ }
+ }
+
+ public void testCreateLocalReplicationNodeWithOverriddenDefaultParameters()
+ {
+ Map<String, Object> attributes = createValidAttributes();
+ attributes.put(ReplicationNode.DURABILITY, "SYNC,SYNC,NONE");
+ attributes.put(ReplicationNode.COALESCING_SYNC, false);
+ attributes.put(ReplicationNode.DESIGNATED_PRIMARY, true);
+
+ LocalReplicationNode node = new LocalReplicationNode(_id, attributes, _virtualHost, _taskExecutor);
+
+ assertNodeAttributes(attributes, node);
+ }
+
+ public void testGetValuesFromReplicatedEnvironmentFacade()
+ {
+ LocalReplicationNode node = new LocalReplicationNode(_id, createValidAttributes(), _virtualHost, _taskExecutor);
+
+ assertNull("Unexpected role attribute", node.getAttribute(ReplicationNode.ROLE));
+ assertNull("Unexpected join time attribute", node.getAttribute(ReplicationNode.JOIN_TIME));
+ assertNull("Unexpected last transaction id", node.getAttribute(ReplicationNode.LAST_KNOWN_REPLICATION_TRANSACTION_ID));
+ assertEquals("Unexpected priority attribute", LocalReplicationNode.DEFAULT_PRIORITY, node.getAttribute(ReplicationNode.PRIORITY));
+ assertEquals("Unexpected quorum override attribute", LocalReplicationNode.DEFAULT_QUORUM_OVERRIDE, node.getAttribute(ReplicationNode.QUORUM_OVERRIDE));
+ assertEquals("Unexpected designated primary attribute", LocalReplicationNode.DEFAULT_DESIGNATED_PRIMARY, node.getAttribute(ReplicationNode.DESIGNATED_PRIMARY));
+
+ String masterState = "MASTER";
+ long joinTime = System.currentTimeMillis();
+ long lastKnowTransactionId = 1000l;
+ boolean designatedPrimary = true;
+ int priority = 2;
+ int quorumOverride = 3;
+
+ when(_facade.getNodeState()).thenReturn(masterState);
+ when(_facade.getJoinTime()).thenReturn(joinTime);
+ when(_facade.getLastKnownReplicationTransactionId()).thenReturn(lastKnowTransactionId);
+ when(_facade.isDesignatedPrimary()).thenReturn(designatedPrimary);
+ when(_facade.getPriority()).thenReturn(priority);
+ when(_facade.getElectableGroupSizeOverride()).thenReturn(quorumOverride);
+
+ node.setReplicatedEnvironmentFacade(_facade);
+ assertEquals("Unexpected role attribute", masterState, node.getAttribute(ReplicationNode.ROLE));
+ assertEquals("Unexpected join time attribute", joinTime, node.getAttribute(ReplicationNode.JOIN_TIME));
+ assertEquals("Unexpected last transaction id attribute", lastKnowTransactionId, node.getAttribute(ReplicationNode.LAST_KNOWN_REPLICATION_TRANSACTION_ID));
+ assertEquals("Unexpected priority attribute", priority, node.getAttribute(ReplicationNode.PRIORITY));
+ assertEquals("Unexpected quorum override attribute", quorumOverride, node.getAttribute(ReplicationNode.QUORUM_OVERRIDE));
+ }
+
+ public void testSetDesignatedPrimary() throws Exception
+ {
+ LocalReplicationNode node = new LocalReplicationNode(_id, createValidAttributes(), _virtualHost, _taskExecutor);
+ node.setReplicatedEnvironmentFacade(_facade);
+
+ node.setAttributes(Collections.<String, Object>singletonMap(ReplicationNode.DESIGNATED_PRIMARY, true));
+
+ verify(_facade).setDesignatedPrimary(true);
+
+ node.setAttributes(Collections.<String, Object>singletonMap(ReplicationNode.DESIGNATED_PRIMARY, false));
+ verify(_facade).setDesignatedPrimary(false);
+ }
+
+ public void testSetPriority() throws Exception
+ {
+ LocalReplicationNode node = new LocalReplicationNode(_id, createValidAttributes(), _virtualHost, _taskExecutor);
+ node.setReplicatedEnvironmentFacade(_facade);
+ node.setAttributes(Collections.<String, Object>singletonMap(ReplicationNode.PRIORITY, 100));
+
+ verify(_facade).setPriority(100);
+ }
+
+ public void testSetQuorumOverride() throws Exception
+ {
+ LocalReplicationNode node = new LocalReplicationNode(_id, createValidAttributes(), _virtualHost, _taskExecutor);
+ node.setReplicatedEnvironmentFacade(_facade);
+
+ node.setAttributes(Collections.<String, Object>singletonMap(ReplicationNode.QUORUM_OVERRIDE, 10));
+
+ verify(_facade).setElectableGroupSizeOverride(10);
+ }
+
+ public void testSetRole() throws Exception
+ {
+ when(_facade.getNodeState()).thenReturn(ReplicatedEnvironment.State.REPLICA.name());
+
+ LocalReplicationNode node = new LocalReplicationNode(_id, createValidAttributes(), _virtualHost, _taskExecutor);
+ node.setReplicatedEnvironmentFacade(_facade);
+
+ node.setAttributes(Collections.<String, Object>singletonMap(ReplicationNode.ROLE, ReplicatedEnvironment.State.MASTER.name()));
+
+ verify(_facade).transferMasterToSelfAsynchronously();
+ }
+
+ public void testSetRoleToReplicaUnsupported() throws Exception
+ {
+ when(_facade.getNodeState()).thenReturn(ReplicatedEnvironment.State.REPLICA.name());
+
+ LocalReplicationNode node = new LocalReplicationNode(_id, createValidAttributes(), _virtualHost, _taskExecutor);
+ node.setReplicatedEnvironmentFacade(_facade);
+
+ try
+ {
+ node.setAttributes(Collections.<String, Object>singletonMap(ReplicationNode.ROLE, ReplicatedEnvironment.State.REPLICA.name()));
+ fail("Exception not thrown");
+ }
+ catch(IllegalConfigurationException e)
+ {
+ // PASS
+ }
+ }
+
+ public void testSetRoleWhenCurrentRoleNotRepliaIsUnsupported() throws Exception
+ {
+ when(_facade.getNodeState()).thenReturn(ReplicatedEnvironment.State.MASTER.name());
+
+ LocalReplicationNode node = new LocalReplicationNode(_id, createValidAttributes(), _virtualHost, _taskExecutor);
+ node.setReplicatedEnvironmentFacade(_facade);
+
+ try
+ {
+ node.setAttributes(Collections.<String, Object>singletonMap(ReplicationNode.ROLE, ReplicatedEnvironment.State.MASTER.name()));
+ fail("Exception not thrown");
+ }
+ catch(IllegalConfigurationException e)
+ {
+ // PASS
+ }
+ }
+
+ public void testSetImmutableAttributesThrowException() throws Exception
+ {
+ Map<String, Object> changeAttributeMap = new HashMap<String, Object>();
+ changeAttributeMap.put(ReplicationNode.GROUP_NAME, "newGroupName");
+ changeAttributeMap.put(ReplicationNode.HELPER_HOST_PORT, "newhost:1234");
+ changeAttributeMap.put(ReplicationNode.HOST_PORT, "newhost:1234");
+ changeAttributeMap.put(ReplicationNode.COALESCING_SYNC, Boolean.FALSE);
+ changeAttributeMap.put(ReplicationNode.DURABILITY, "durability");
+ changeAttributeMap.put(ReplicationNode.JOIN_TIME, 1000l);
+ changeAttributeMap.put(ReplicationNode.LAST_KNOWN_REPLICATION_TRANSACTION_ID, 10001l);
+ changeAttributeMap.put(ReplicationNode.NAME, "newName");
+ changeAttributeMap.put(ReplicationNode.STORE_PATH, "/not/used");
+ changeAttributeMap.put(ReplicationNode.PARAMETERS, Collections.emptyMap());
+ changeAttributeMap.put(ReplicationNode.REPLICATION_PARAMETERS, Collections.emptyMap());
+
+ for (Entry<String, Object> entry : changeAttributeMap.entrySet())
+ {
+ assertSetAttributesThrowsException(entry.getKey(), entry.getValue());
+ }
+ }
+
+ private void assertSetAttributesThrowsException(String attributeName, Object attributeValue)
+ {
+ LocalReplicationNode node = new LocalReplicationNode(_id, createValidAttributes(), _virtualHost, _taskExecutor);
+
+ try
+ {
+ node.setAttributes(Collections.<String, Object>singletonMap(attributeName, attributeValue));
+ fail("Operation to change attribute '" + attributeName + "' should fail");
+ }
+ catch(IllegalConfigurationException e)
+ {
+ // pass
+ }
+ }
+
+ private Map<String, Object> createValidAttributes()
+ {
+ Map<String, Object> attributes = new HashMap<String, Object>();
+ attributes.put(ReplicationNode.NAME, "testNode");
+ attributes.put(ReplicationNode.GROUP_NAME, "testGroup");
+ attributes.put(ReplicationNode.HOST_PORT, "localhost:5000");
+ attributes.put(ReplicationNode.HELPER_HOST_PORT, "localhost:5001");
+ attributes.put(ReplicationNode.STORE_PATH, TMP_FOLDER + File.separator + getTestName());
+ return attributes;
+ }
+
+ private void assertNodeAttributes(Map<String, Object> expectedAttributes,
+ LocalReplicationNode node)
+ {
+ for (Map.Entry<String, Object> attributeEntry : expectedAttributes.entrySet())
+ {
+ assertEquals("Unexpected attribute value for attribute name " + attributeEntry.getKey(), attributeEntry.getValue(), node.getAttribute(attributeEntry.getKey()));
+ }
+ }
+
+}
diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/replication/NoopReplicationGroupListener.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/replication/NoopReplicationGroupListener.java
new file mode 100644
index 0000000000..21c902ae8f
--- /dev/null
+++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/replication/NoopReplicationGroupListener.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.replication;
+
+import org.apache.qpid.server.model.ReplicationNode;
+import org.apache.qpid.server.replication.ReplicationGroupListener;
+
+class NoopReplicationGroupListener implements ReplicationGroupListener
+{
+ @Override
+ public void onReplicationNodeRecovered(ReplicationNode node)
+ {
+ }
+
+ @Override
+ public void onReplicationNodeAddedToGroup(ReplicationNode node)
+ {
+ }
+
+ @Override
+ public void onReplicationNodeRemovedFromGroup(ReplicationNode node)
+ {
+ }
+} \ No newline at end of file
diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/replication/RemoteReplicationNodeTest.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/replication/RemoteReplicationNodeTest.java
new file mode 100644
index 0000000000..25c58e47a5
--- /dev/null
+++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/replication/RemoteReplicationNodeTest.java
@@ -0,0 +1,176 @@
+package org.apache.qpid.server.store.berkeleydb.replication;
+
+import static org.apache.qpid.server.model.ReplicationNode.COALESCING_SYNC;
+import static org.apache.qpid.server.model.ReplicationNode.DURABILITY;
+import static org.apache.qpid.server.model.ReplicationNode.GROUP_NAME;
+import static org.apache.qpid.server.model.ReplicationNode.HELPER_HOST_PORT;
+import static org.apache.qpid.server.model.ReplicationNode.HOST_PORT;
+import static org.apache.qpid.server.model.ReplicationNode.JOIN_TIME;
+import static org.apache.qpid.server.model.ReplicationNode.LAST_KNOWN_REPLICATION_TRANSACTION_ID;
+import static org.apache.qpid.server.model.ReplicationNode.NAME;
+import static org.apache.qpid.server.model.ReplicationNode.PARAMETERS;
+import static org.apache.qpid.server.model.ReplicationNode.REPLICATION_PARAMETERS;
+import static org.apache.qpid.server.model.ReplicationNode.ROLE;
+import static org.apache.qpid.server.model.ReplicationNode.STORE_PATH;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.qpid.server.configuration.IllegalConfigurationException;
+import org.apache.qpid.server.configuration.updater.TaskExecutor;
+import org.apache.qpid.server.model.IllegalStateTransitionException;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.test.utils.QpidTestCase;
+
+import com.sleepycat.je.rep.MasterStateException;
+import com.sleepycat.je.rep.NodeState;
+import com.sleepycat.je.rep.ReplicatedEnvironment.State;
+import com.sleepycat.je.rep.ReplicationNode;
+
+public class RemoteReplicationNodeTest extends QpidTestCase
+{
+
+ private RemoteReplicationNode _node;
+ private String _groupName;
+ private VirtualHost _virtualHost;
+ private TaskExecutor _taskExecutor;
+ private ReplicationNode _replicationNode;
+ private String _nodeName;
+ private int _port;
+ private ReplicatedEnvironmentFacade _replicatedEnvironmentFacade;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ _groupName = getTestName();
+ _nodeName = getTestName() + "Name";
+ _port = 5000;
+ _replicationNode = mock(ReplicationNode.class);
+ _virtualHost = mock(VirtualHost.class);
+ _taskExecutor = mock(TaskExecutor.class);
+ _replicatedEnvironmentFacade = mock(ReplicatedEnvironmentFacade.class);
+ when(_replicatedEnvironmentFacade.getGroupName()).thenReturn(_groupName);
+
+ when(_taskExecutor.isTaskExecutorThread()).thenReturn(true);
+ when(_replicationNode.getName()).thenReturn(_nodeName);
+ when(_replicationNode.getHostName()).thenReturn("localhost");
+ when(_replicationNode.getPort()).thenReturn(_port);
+
+ _node = new RemoteReplicationNode(_replicationNode, _virtualHost, _taskExecutor, _replicatedEnvironmentFacade);
+ }
+
+ public void testGetAttribute() throws Exception
+ {
+ State state = State.MASTER;
+ long joinTime = System.currentTimeMillis();
+ long currentTxnEndVLSN = 3;
+
+ updateNodeState(state, joinTime, currentTxnEndVLSN);
+
+ assertEquals("Unexpected name", _nodeName, _node.getAttribute(NAME));
+ assertEquals("Unexpected group name", _groupName, _node.getAttribute(GROUP_NAME));
+ assertEquals("Unexpected state", state.name(), _node.getAttribute(ROLE));
+ assertEquals("Unexpected transaction id", currentTxnEndVLSN, _node.getAttribute(LAST_KNOWN_REPLICATION_TRANSACTION_ID));
+ assertEquals("Unexpected join time", joinTime, _node.getAttribute(JOIN_TIME));
+ }
+
+ public void testSetRoleAttribute() throws Exception
+ {
+ updateNodeState();
+ _node.setAttributes(Collections.<String, Object>singletonMap(ROLE, State.MASTER.name()));
+
+ verify(_replicatedEnvironmentFacade).transferMasterAsynchronously(_nodeName);
+ }
+
+ public void testSetRoleAttributeDisallowedIfAlreadyMaster() throws Exception
+ {
+ updateNodeState(State.MASTER, System.currentTimeMillis(), 0L);
+ try
+ {
+ _node.setAttributes(Collections.<String, Object>singletonMap(ROLE, State.MASTER.name()));
+ fail("Exception not thrown");
+ }
+ catch (IllegalConfigurationException ice)
+ {
+ // pass
+ }
+
+ verify(_replicatedEnvironmentFacade, never()).transferMasterAsynchronously(_nodeName);
+ }
+
+ public void testSetDesiredStateToDeleted() throws Exception
+ {
+ _node.setDesiredState(_node.getActualState(), org.apache.qpid.server.model.State.DELETED);
+ verify(_replicatedEnvironmentFacade).removeNodeFromGroup(_nodeName);
+ }
+
+ public void testSetDesiredStateToDeletedOnMasterStateException() throws Exception
+ {
+ doThrow(new MasterStateException("mocked exception")).when(_replicatedEnvironmentFacade).removeNodeFromGroup(_nodeName);
+ try
+ {
+ _node.setDesiredState(_node.getActualState(), org.apache.qpid.server.model.State.DELETED);
+ fail("Exception not thrown");
+ }
+ catch(IllegalStateTransitionException e)
+ {
+ // pass
+ }
+ }
+
+ public void testSetImmutableAttributesThrowException() throws Exception
+ {
+ Map<String, Object> changeAttributeMap = new HashMap<String, Object>();
+ changeAttributeMap.put(GROUP_NAME, "newGroupName");
+ changeAttributeMap.put(HELPER_HOST_PORT, "newhost:1234");
+ changeAttributeMap.put(HOST_PORT, "newhost:1234");
+ changeAttributeMap.put(COALESCING_SYNC, Boolean.FALSE);
+ changeAttributeMap.put(DURABILITY, "durability");
+ changeAttributeMap.put(JOIN_TIME, 1000l);
+ changeAttributeMap.put(LAST_KNOWN_REPLICATION_TRANSACTION_ID, 10001l);
+ changeAttributeMap.put(NAME, "newName");
+ changeAttributeMap.put(STORE_PATH, "/not/used");
+ changeAttributeMap.put(PARAMETERS, Collections.emptyMap());
+ changeAttributeMap.put(REPLICATION_PARAMETERS, Collections.emptyMap());
+
+ for (Entry<String, Object> entry : changeAttributeMap.entrySet())
+ {
+ assertSetAttributesThrowsException(entry.getKey(), entry.getValue());
+ }
+ }
+
+ private void assertSetAttributesThrowsException(String attributeName, Object attributeValue) throws Exception
+ {
+ updateNodeState();
+
+ try
+ {
+ _node.setAttributes(Collections.<String, Object>singletonMap(attributeName, attributeValue));
+ fail("Operation to change attribute '" + attributeName + "' should fail");
+ }
+ catch(IllegalConfigurationException e)
+ {
+ // pass
+ }
+ }
+
+ private void updateNodeState() throws Exception
+ {
+ updateNodeState( State.REPLICA, System.currentTimeMillis(), 3);
+ }
+
+ private void updateNodeState(State state, long joinTime, long currentTxnEndVLSN) throws Exception
+ {
+ NodeState nodeState = new NodeState(_nodeName, _groupName, state, null, null, joinTime, currentTxnEndVLSN, 2, 1, 0, null, 0.0);
+ when(_replicatedEnvironmentFacade.getRemoteNodeState(_replicationNode)).thenReturn(nodeState);
+ _node.updateNodeState();
+ }
+}
diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/replication/ReplicatedEnvironmentFacadeTest.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/replication/ReplicatedEnvironmentFacadeTest.java
new file mode 100644
index 0000000000..052341c810
--- /dev/null
+++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/replication/ReplicatedEnvironmentFacadeTest.java
@@ -0,0 +1,673 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.replication;
+
+import static org.apache.qpid.server.model.ReplicationNode.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQStoreException;
+import org.apache.qpid.server.configuration.updater.TaskExecutor;
+import org.apache.qpid.server.model.ReplicationNode;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.replication.ReplicationGroupListener;
+import org.apache.qpid.server.store.berkeleydb.EnvironmentFacade;
+import org.apache.qpid.server.store.berkeleydb.replication.RemoteReplicationNode;
+import org.apache.qpid.server.store.berkeleydb.replication.RemoteReplicationNodeFactory;
+import org.apache.qpid.server.store.berkeleydb.replication.ReplicatedEnvironmentFacade;
+import org.apache.qpid.server.store.berkeleydb.replication.ReplicatedEnvironmentFacadeFactory;
+import org.apache.qpid.test.utils.QpidTestCase;
+import org.apache.qpid.test.utils.TestFileUtils;
+import org.apache.qpid.util.FileUtils;
+
+import com.sleepycat.je.Database;
+import com.sleepycat.je.DatabaseConfig;
+import com.sleepycat.je.Durability;
+import com.sleepycat.je.Environment;
+import com.sleepycat.je.rep.InsufficientReplicasException;
+import com.sleepycat.je.rep.ReplicatedEnvironment.State;
+import com.sleepycat.je.rep.ReplicationConfig;
+import com.sleepycat.je.rep.StateChangeEvent;
+import com.sleepycat.je.rep.StateChangeListener;
+
+public class ReplicatedEnvironmentFacadeTest extends QpidTestCase
+{
+ private static final Logger LOGGER = Logger.getLogger(ReplicatedEnvironmentFacadeTest.class);
+
+ private static final int TEST_NODE_PORT = new QpidTestCase().findFreePort();
+ private static final int LISTENER_TIMEOUT = 5;
+ private static final int WAIT_STATE_CHANGE_TIMEOUT = 30;
+ private static final String TEST_GROUP_NAME = "testGroupName";
+ private static final String TEST_NODE_NAME = "testNodeName";
+ private static final String TEST_NODE_HOST_PORT = "localhost:" + TEST_NODE_PORT;
+ private static final String TEST_NODE_HELPER_HOST_PORT = TEST_NODE_HOST_PORT;
+ private static final String TEST_DURABILITY = Durability.parse("NO_SYNC,NO_SYNC,SIMPLE_MAJORITY").toString();
+ private static final boolean TEST_DESIGNATED_PRIMARY = false;
+ private static final boolean TEST_COALESCING_SYNC = true;
+ private static final int TEST_PRIORITY = 10;
+ private static final int TEST_ELECTABLE_GROUP_OVERRIDE = 0;
+
+ private File _storePath;
+ private final Map<String, ReplicatedEnvironmentFacade> _nodes = new HashMap<String, ReplicatedEnvironmentFacade>();
+ private VirtualHost _virtualHost = mock(VirtualHost.class);
+
+ private RemoteReplicationNodeFactory _remoteReplicationNodeFactory = new ReplicatedEnvironmentFacadeFactory.RemoteReplicationNodeFactoryImpl(_virtualHost);
+
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ TaskExecutor taskExecutor = mock(TaskExecutor.class);
+ when(taskExecutor.isTaskExecutorThread()).thenReturn(true);
+ when(_virtualHost.getTaskExecutor()).thenReturn(taskExecutor);
+
+ _storePath = TestFileUtils.createTestDirectory("bdb", true);
+
+ when(_virtualHost.getAttribute(VirtualHost.REMOTE_REPLICATION_NODE_MONITOR_INTERVAL)).thenReturn(100L);
+ setTestSystemProperty(ReplicatedEnvironmentFacade.DB_PING_SOCKET_TIMEOUT_PROPERTY_NAME, "100");
+ }
+
+ @Override
+ public void tearDown() throws Exception
+ {
+ try
+ {
+ for (EnvironmentFacade ef : _nodes.values())
+ {
+ ef.close();
+ }
+ }
+ finally
+ {
+ try
+ {
+ if (_storePath != null)
+ {
+ FileUtils.delete(_storePath, true);
+ }
+ }
+ finally
+ {
+ super.tearDown();
+ }
+ }
+ }
+ public void testEnvironmentFacade() throws Exception
+ {
+ EnvironmentFacade ef = createMaster();
+ assertNotNull("Environment should not be null", ef);
+ Environment e = ef.getEnvironment();
+ assertTrue("Environment is not valid", e.isValid());
+ }
+
+ public void testClose() throws Exception
+ {
+ EnvironmentFacade ef = createMaster();
+ ef.close();
+ Environment e = ef.getEnvironment();
+
+ assertNull("Environment should be null after facade close", e);
+ }
+
+ public void testOpenDatabases() throws Exception
+ {
+ EnvironmentFacade ef = createMaster();
+ DatabaseConfig dbConfig = new DatabaseConfig();
+ dbConfig.setTransactional(true);
+ dbConfig.setAllowCreate(true);
+ ef.openDatabases(dbConfig, "test1", "test2");
+ Database test1 = ef.getOpenDatabase("test1");
+ Database test2 = ef.getOpenDatabase("test2");
+
+ assertEquals("Unexpected name for open database test1", "test1" , test1.getDatabaseName());
+ assertEquals("Unexpected name for open database test2", "test2" , test2.getDatabaseName());
+ }
+
+ public void testGetOpenDatabaseForNonExistingDatabase() throws Exception
+ {
+ EnvironmentFacade ef = createMaster();
+ DatabaseConfig dbConfig = new DatabaseConfig();
+ dbConfig.setTransactional(true);
+ dbConfig.setAllowCreate(true);
+ ef.openDatabases(dbConfig, "test1");
+ Database test1 = ef.getOpenDatabase("test1");
+ assertEquals("Unexpected name for open database test1", "test1" , test1.getDatabaseName());
+ try
+ {
+ ef.getOpenDatabase("test2");
+ fail("An exception should be thrown for the non existing database");
+ }
+ catch(IllegalArgumentException e)
+ {
+ assertEquals("Unexpected exception message", "Database with name 'test2' has never been requested to be opened", e.getMessage());
+ }
+ }
+
+ public void testGetGroupName() throws Exception
+ {
+ assertEquals("Unexpected group name", TEST_GROUP_NAME, createMaster().getGroupName());
+ }
+
+ public void testGetNodeName() throws Exception
+ {
+ assertEquals("Unexpected group name", TEST_NODE_NAME, createMaster().getNodeName());
+ }
+
+ public void testLastKnownReplicationTransactionId() throws Exception
+ {
+ ReplicatedEnvironmentFacade master = createMaster();
+ long lastKnownReplicationTransactionId = master.getLastKnownReplicationTransactionId();
+ assertTrue("Unexpected LastKnownReplicationTransactionId " + lastKnownReplicationTransactionId, lastKnownReplicationTransactionId > 0);
+ }
+
+ public void testGetNodeHostPort() throws Exception
+ {
+ assertEquals("Unexpected node host port", TEST_NODE_HOST_PORT, createMaster().getHostPort());
+ }
+
+ public void testGetHelperHostPort() throws Exception
+ {
+ assertEquals("Unexpected node helper host port", TEST_NODE_HELPER_HOST_PORT, createMaster().getHelperHostPort());
+ }
+
+ public void testGetDurability() throws Exception
+ {
+ assertEquals("Unexpected durability", TEST_DURABILITY.toString(), createMaster().getDurability());
+ }
+
+ public void testIsCoalescingSync() throws Exception
+ {
+ assertEquals("Unexpected coalescing sync", TEST_COALESCING_SYNC, createMaster().isCoalescingSync());
+ }
+
+ public void testGetNodeState() throws Exception
+ {
+ assertEquals("Unexpected state", State.MASTER.name(), createMaster().getNodeState());
+ }
+
+
+ public void testPriority() throws Exception
+ {
+ ReplicatedEnvironmentFacade facade = createMaster();
+ assertEquals("Unexpected priority", TEST_PRIORITY, facade.getPriority());
+ Future<Void> future = facade.setPriority(TEST_PRIORITY + 1);
+ future.get(5, TimeUnit.SECONDS);
+ assertEquals("Unexpected priority after change", TEST_PRIORITY + 1, facade.getPriority());
+ }
+
+ public void testDesignatedPrimary() throws Exception
+ {
+ ReplicatedEnvironmentFacade master = createMaster();
+ assertEquals("Unexpected designated primary", TEST_DESIGNATED_PRIMARY, master.isDesignatedPrimary());
+ Future<Void> future = master.setDesignatedPrimary(!TEST_DESIGNATED_PRIMARY);
+ future.get(5, TimeUnit.SECONDS);
+ assertEquals("Unexpected designated primary after change", !TEST_DESIGNATED_PRIMARY, master.isDesignatedPrimary());
+ }
+
+ public void testElectableGroupSizeOverride() throws Exception
+ {
+ ReplicatedEnvironmentFacade facade = createMaster();
+ assertEquals("Unexpected Electable Group Size Override", TEST_ELECTABLE_GROUP_OVERRIDE, facade.getElectableGroupSizeOverride());
+ Future<Void> future = facade.setElectableGroupSizeOverride(TEST_ELECTABLE_GROUP_OVERRIDE + 1);
+ future.get(5, TimeUnit.SECONDS);
+ assertEquals("Unexpected Electable Group Size Override after change", TEST_ELECTABLE_GROUP_OVERRIDE + 1, facade.getElectableGroupSizeOverride());
+ }
+
+ public void testReplicationGroupListenerHearsAboutExistingRemoteReplicationNodes() throws Exception
+ {
+ ReplicatedEnvironmentFacade master = createMaster();
+ String nodeName2 = TEST_NODE_NAME + "_2";
+ String host = "localhost";
+ int port = getNextAvailable(TEST_NODE_PORT + 1);
+ String node2NodeHostPort = host + ":" + port;
+
+ final AtomicInteger invocationCount = new AtomicInteger();
+ final CountDownLatch nodeRecoveryLatch = new CountDownLatch(1);
+ ReplicationGroupListener listener = new NoopReplicationGroupListener()
+ {
+ @Override
+ public void onReplicationNodeRecovered(ReplicationNode node)
+ {
+ nodeRecoveryLatch.countDown();
+ invocationCount.incrementAndGet();
+ }
+ };
+
+ addReplica(nodeName2, node2NodeHostPort, listener);
+
+ assertEquals("Unexpected number of nodes", 2, master.getNumberOfElectableGroupMembers());
+
+ assertTrue("Listener not fired within timeout", nodeRecoveryLatch.await(LISTENER_TIMEOUT, TimeUnit.SECONDS));
+ assertEquals("Unexpected number of listener invocations", 1, invocationCount.get());
+ }
+
+ public void testReplicationGroupListenerHearsNodeAdded() throws Exception
+ {
+ final CountDownLatch nodeAddedLatch = new CountDownLatch(1);
+ final AtomicInteger invocationCount = new AtomicInteger();
+ ReplicationGroupListener listener = new NoopReplicationGroupListener()
+ {
+ @Override
+ public void onReplicationNodeAddedToGroup(ReplicationNode node)
+ {
+ invocationCount.getAndIncrement();
+ nodeAddedLatch.countDown();
+ }
+ };
+
+ TestStateChangeListener stateChangeListener = new TestStateChangeListener(State.MASTER);
+ ReplicatedEnvironmentFacade replicatedEnvironmentFacade = addNode(State.MASTER, stateChangeListener, listener);
+ assertTrue("Master was not started", stateChangeListener.awaitForStateChange(LISTENER_TIMEOUT, TimeUnit.SECONDS));
+
+ assertEquals("Unexpected number of nodes at start of test", 1, replicatedEnvironmentFacade.getNumberOfElectableGroupMembers());
+
+ String node2Name = TEST_NODE_NAME + "_2";
+ String node2NodeHostPort = "localhost" + ":" + getNextAvailable(TEST_NODE_PORT + 1);
+ addReplica(node2Name, node2NodeHostPort);
+
+ assertTrue("Listener not fired within timeout", nodeAddedLatch.await(LISTENER_TIMEOUT, TimeUnit.SECONDS));
+
+ assertEquals("Unexpected number of nodes", 2, replicatedEnvironmentFacade.getNumberOfElectableGroupMembers());
+
+ assertEquals("Unexpected number of listener invocations", 1, invocationCount.get());
+ }
+
+ public void testReplicationGroupListenerHearsNodeRemoved() throws Exception
+ {
+ final CountDownLatch nodeDeletedLatch = new CountDownLatch(1);
+ final CountDownLatch nodeAddedLatch = new CountDownLatch(1);
+ final AtomicInteger invocationCount = new AtomicInteger();
+ ReplicationGroupListener listener = new NoopReplicationGroupListener()
+ {
+ @Override
+ public void onReplicationNodeRecovered(ReplicationNode node)
+ {
+ nodeAddedLatch.countDown();
+ }
+
+ @Override
+ public void onReplicationNodeAddedToGroup(ReplicationNode node)
+ {
+ nodeAddedLatch.countDown();
+ }
+
+ @Override
+ public void onReplicationNodeRemovedFromGroup(ReplicationNode node)
+ {
+ invocationCount.getAndIncrement();
+ nodeDeletedLatch.countDown();
+ }
+ };
+
+ TestStateChangeListener stateChangeListener = new TestStateChangeListener(State.MASTER);
+ ReplicatedEnvironmentFacade replicatedEnvironmentFacade = addNode(State.MASTER, stateChangeListener, listener);
+ assertTrue("Master was not started", stateChangeListener.awaitForStateChange(LISTENER_TIMEOUT, TimeUnit.SECONDS));
+
+ String node2Name = TEST_NODE_NAME + "_2";
+ String node2NodeHostPort = "localhost" + ":" + getNextAvailable(TEST_NODE_PORT + 1);
+ addReplica(node2Name, node2NodeHostPort);
+
+ assertEquals("Unexpected number of nodes at start of test", 2, replicatedEnvironmentFacade.getNumberOfElectableGroupMembers());
+
+ // Need to await the listener hearing the addition of the node to the model.
+ assertTrue("Node add not fired within timeout", nodeAddedLatch.await(LISTENER_TIMEOUT, TimeUnit.SECONDS));
+
+ // Now remove the node and ensure we hear the event
+ replicatedEnvironmentFacade.removeNodeFromGroup(node2Name);
+
+ assertTrue("Node delete not fired within timeout", nodeDeletedLatch.await(LISTENER_TIMEOUT, TimeUnit.SECONDS));
+
+ assertEquals("Unexpected number of nodes after node removal", 1, replicatedEnvironmentFacade.getNumberOfElectableGroupMembers());
+
+ assertEquals("Unexpected number of listener invocations", 1, invocationCount.get());
+ }
+
+ public void testMasterHearsRemoteNodeRoles() throws Exception
+ {
+
+ final CountDownLatch nodeAddedLatch = new CountDownLatch(1);
+ final AtomicReference<ReplicationNode> nodeRef = new AtomicReference<ReplicationNode>();
+ ReplicationGroupListener listener = new NoopReplicationGroupListener()
+ {
+ @Override
+ public void onReplicationNodeAddedToGroup(ReplicationNode node)
+ {
+ nodeRef.set(node);
+ nodeAddedLatch.countDown();
+ }
+ };
+
+ TestStateChangeListener stateChangeListener = new TestStateChangeListener(State.MASTER);
+ ReplicatedEnvironmentFacade replicatedEnvironmentFacade = addNode(State.MASTER, stateChangeListener, listener);
+ assertTrue("Master was not started", stateChangeListener.awaitForStateChange(LISTENER_TIMEOUT, TimeUnit.SECONDS));
+
+ String node2Name = TEST_NODE_NAME + "_2";
+ String node2NodeHostPort = "localhost" + ":" + getNextAvailable(TEST_NODE_PORT + 1);
+ addReplica(node2Name, node2NodeHostPort);
+
+ assertEquals("Unexpected number of nodes at start of test", 2, replicatedEnvironmentFacade.getNumberOfElectableGroupMembers());
+
+ assertTrue("Node add not fired within timeout", nodeAddedLatch.await(LISTENER_TIMEOUT, TimeUnit.SECONDS));
+
+ RemoteReplicationNode remoteNode = (RemoteReplicationNode)nodeRef.get();
+ assertEquals("Unexpcted node name", node2Name, remoteNode.getName());
+
+ // Need to poll to await the remote node updating itself
+ long timeout = System.currentTimeMillis() + 5000;
+ while(!State.REPLICA.name().equals(remoteNode.getAttribute(ReplicationNode.ROLE)) && System.currentTimeMillis() < timeout)
+ {
+ Thread.sleep(200);
+ }
+
+ assertEquals("Unexpcted node role (after waiting)", State.REPLICA.name(), remoteNode.getAttribute(ReplicationNode.ROLE));
+ assertNotNull("Replica node " + ReplicationNode.JOIN_TIME + " attribute is not set", remoteNode.getAttribute(ReplicationNode.JOIN_TIME));
+ assertNotNull("Replica node " + ReplicationNode.LAST_KNOWN_REPLICATION_TRANSACTION_ID + " attribute is not set", remoteNode.getAttribute(ReplicationNode.LAST_KNOWN_REPLICATION_TRANSACTION_ID));
+ }
+
+ public void testRemoveNodeFromGroup() throws Exception
+ {
+ ReplicatedEnvironmentFacade environmentFacade = createMaster();
+
+ String node2Name = TEST_NODE_NAME + "_2";
+ String node2NodeHostPort = "localhost:" + getNextAvailable(TEST_NODE_PORT + 1);
+ ReplicatedEnvironmentFacade ref2 = addReplica(node2Name, node2NodeHostPort);
+
+ assertEquals("Unexpected group members count", 2, environmentFacade.getNumberOfElectableGroupMembers());
+ ref2.close();
+
+ environmentFacade.removeNodeFromGroup(node2Name);
+ assertEquals("Unexpected group members count", 1, environmentFacade.getNumberOfElectableGroupMembers());
+ }
+
+ public void testEnvironmentRestartOnInsufficientReplicas() throws Exception
+ {
+ long startTime = System.currentTimeMillis();
+
+ ReplicatedEnvironmentFacade master = createMaster();
+
+ int replica1Port = getNextAvailable(TEST_NODE_PORT + 1);
+ String replica1NodeName = TEST_NODE_NAME + "_1";
+ String replica1NodeHostPort = "localhost:" + replica1Port;
+ ReplicatedEnvironmentFacade replica1 = addReplica(replica1NodeName, replica1NodeHostPort);
+
+ int replica2Port = getNextAvailable(replica1Port + 1);
+ String replica2NodeName = TEST_NODE_NAME + "_2";
+ String replica2NodeHostPort = "localhost:" + replica2Port;
+ ReplicatedEnvironmentFacade replica2 = addReplica(replica2NodeName, replica2NodeHostPort);
+
+ long setUpTime = System.currentTimeMillis();
+ LOGGER.debug("XXX Start Up Time " + (setUpTime - startTime));
+ String databaseName = "test";
+
+ DatabaseConfig dbConfig = createDatabase(master, databaseName);
+
+ // close replicas
+ replica1.close();
+ replica2.close();
+
+ long closeTime = System.currentTimeMillis();
+ LOGGER.debug("XXX Env close Time " + (closeTime - setUpTime));
+ Environment e = master.getEnvironment();
+ Database db = master.getOpenDatabase(databaseName);
+ try
+ {
+ master.openDatabases(dbConfig, "test2");
+ fail("Opening of new database without quorum should fail");
+ }
+ catch(InsufficientReplicasException ex)
+ {
+ master.handleDatabaseException(null, ex);
+ }
+ long openDatabaseTime = System.currentTimeMillis();
+ LOGGER.debug("XXX Open db Time " + (openDatabaseTime - closeTime ));
+
+ replica1 = addReplica(replica1NodeName, replica1NodeHostPort);
+ replica2 = addReplica(replica2NodeName, replica2NodeHostPort);
+
+ long reopenTime = System.currentTimeMillis();
+ LOGGER.debug("XXX Restart Time " + (reopenTime - openDatabaseTime ));
+ // Need to poll to await the remote node updating itself
+ long timeout = System.currentTimeMillis() + 5000;
+ while(!(State.REPLICA.name().equals(master.getNodeState()) || State.MASTER.name().equals(master.getNodeState()) ) && System.currentTimeMillis() < timeout)
+ {
+ Thread.sleep(200);
+ }
+ long recoverTime = System.currentTimeMillis();
+ LOGGER.debug("XXX Recover Time " + (recoverTime - reopenTime));
+ assertTrue("The node could not rejoin the cluster. State is " + master.getNodeState(),
+ State.REPLICA.name().equals(master.getNodeState()) || State.MASTER.name().equals(master.getNodeState()) );
+
+ Environment e2 = master.getEnvironment();
+ assertNotSame("Environment has not been restarted", e2, e);
+
+ Database db1 = master.getOpenDatabase(databaseName);
+ assertNotSame("Database should be the re-created", db1, db);
+ }
+
+ public void testEnvironmentAutomaticallyRestartsAndBecomesUnknownOnInsufficientReplicas() throws Exception
+ {
+ final CountDownLatch masterLatch = new CountDownLatch(1);
+ final AtomicInteger masterStateChangeCount = new AtomicInteger();
+ final CountDownLatch unknownLatch = new CountDownLatch(1);
+ final AtomicInteger unknownStateChangeCount = new AtomicInteger();
+ StateChangeListener stateChangeListener = new StateChangeListener()
+ {
+ @Override
+ public void stateChange(StateChangeEvent stateChangeEvent) throws RuntimeException
+ {
+ if (stateChangeEvent.getState() == State.MASTER)
+ {
+ masterStateChangeCount.incrementAndGet();
+ masterLatch.countDown();
+ }
+ else if (stateChangeEvent.getState() == State.UNKNOWN)
+ {
+ unknownStateChangeCount.incrementAndGet();
+ unknownLatch.countDown();
+ }
+ }
+ };
+
+ addNode(State.MASTER, stateChangeListener, new NoopReplicationGroupListener());
+ assertTrue("Master was not started", masterLatch.await(LISTENER_TIMEOUT, TimeUnit.SECONDS));
+
+ int replica1Port = getNextAvailable(TEST_NODE_PORT + 1);
+ String node1NodeHostPort = "localhost:" + replica1Port;
+ int replica2Port = getNextAvailable(replica1Port + 1);
+ String node2NodeHostPort = "localhost:" + replica2Port;
+
+ ReplicatedEnvironmentFacade replica1 = addReplica(TEST_NODE_NAME + "_1", node1NodeHostPort);
+ ReplicatedEnvironmentFacade replica2 = addReplica(TEST_NODE_NAME + "_2", node2NodeHostPort);
+
+ // close replicas
+ replica1.close();
+ replica2.close();
+
+ assertTrue("Environment should be recreated and go into unknown state",
+ unknownLatch.await(WAIT_STATE_CHANGE_TIMEOUT, TimeUnit.SECONDS));
+
+ assertEquals("Node made master an unexpected number of times", 1, masterStateChangeCount.get());
+ assertEquals("Node made unknown an unexpected number of times", 1, unknownStateChangeCount.get());
+ }
+
+ public void testEnvironmentFacadeDetectsRemovalOfRemoteNode() throws Exception
+ {
+ final CountDownLatch nodeRemovedLatch = new CountDownLatch(1);
+ final CountDownLatch nodeAddedLatch = new CountDownLatch(1);
+ final AtomicReference<ReplicationNode> addedNodeRef = new AtomicReference<ReplicationNode>();
+ final AtomicReference<ReplicationNode> removedNodeRef = new AtomicReference<ReplicationNode>();
+ ReplicationGroupListener listener = new NoopReplicationGroupListener()
+ {
+ @Override
+ public void onReplicationNodeAddedToGroup(ReplicationNode node)
+ {
+ if (addedNodeRef.compareAndSet(null, node))
+ {
+ nodeAddedLatch.countDown();
+ }
+ }
+
+ @Override
+ public void onReplicationNodeRemovedFromGroup(ReplicationNode node)
+ {
+ removedNodeRef.set(node);
+ nodeRemovedLatch.countDown();
+ }
+ };
+
+ TestStateChangeListener stateChangeListener = new TestStateChangeListener(State.MASTER);
+ final ReplicatedEnvironmentFacade masterEnvironment = addNode(State.MASTER, stateChangeListener, listener);
+ assertTrue("Master was not started", stateChangeListener.awaitForStateChange(LISTENER_TIMEOUT, TimeUnit.SECONDS));
+
+ masterEnvironment.setDesignatedPrimary(true);
+
+ int replica1Port = getNextAvailable(TEST_NODE_PORT + 1);
+ String node1NodeHostPort = "localhost:" + replica1Port;
+
+ String replicaName = TEST_NODE_NAME + "_1";
+ addReplica(replicaName, node1NodeHostPort);
+
+ assertTrue("Node should be added", nodeAddedLatch.await(WAIT_STATE_CHANGE_TIMEOUT, TimeUnit.SECONDS));
+
+ ReplicationNode node = addedNodeRef.get();
+ assertEquals("Unexpected node name", replicaName, node.getName());
+
+ // Need to poll to await the remote node updating itself
+ long timeout = System.currentTimeMillis() + 5000;
+ while(!State.REPLICA.name().equals(node.getAttribute(ReplicationNode.ROLE)) && System.currentTimeMillis() < timeout)
+ {
+ Thread.sleep(200);
+ }
+ assertEquals("Unexpected node role", State.REPLICA.name(), node.getAttribute(ReplicationNode.ROLE));
+
+ // removing remote node
+ node.setDesiredState(node.getActualState(), org.apache.qpid.server.model.State.DELETED);
+
+ assertTrue("Node deleting is undetected by the environment facade", nodeRemovedLatch.await(WAIT_STATE_CHANGE_TIMEOUT, TimeUnit.SECONDS));
+ assertEquals("Unexpected node is deleted", node, removedNodeRef.get());
+
+ //TODO: need a way to shut down the remote environment when the corresponding remote node is deleted.
+ // It is unclear whether it is possible
+ }
+
+ public void testCloseStateTransitions() throws Exception
+ {
+ ReplicatedEnvironmentFacade replicatedEnvironmentFacade = createMaster();
+
+ assertEquals("Unexpected state " + replicatedEnvironmentFacade.getFacadeState(), ReplicatedEnvironmentFacade.State.OPEN, replicatedEnvironmentFacade.getFacadeState());
+ replicatedEnvironmentFacade.close();
+ assertEquals("Unexpected state " + replicatedEnvironmentFacade.getFacadeState(), ReplicatedEnvironmentFacade.State.CLOSED, replicatedEnvironmentFacade.getFacadeState());
+ }
+
+ private ReplicatedEnvironmentFacade createMaster() throws Exception
+ {
+ TestStateChangeListener stateChangeListener = new TestStateChangeListener(State.MASTER);
+ ReplicatedEnvironmentFacade env = addNode(State.MASTER, stateChangeListener, new NoopReplicationGroupListener());
+ assertTrue("Environment was not created", stateChangeListener.awaitForStateChange(LISTENER_TIMEOUT, TimeUnit.SECONDS));
+ return env;
+ }
+
+ private ReplicatedEnvironmentFacade addReplica(String nodeName, String nodeHostPort) throws Exception
+ {
+ return addReplica(nodeName, nodeHostPort, new NoopReplicationGroupListener());
+ }
+
+ private ReplicatedEnvironmentFacade addReplica(String nodeName, String nodeHostPort, ReplicationGroupListener replicationGroupListener)
+ throws Exception
+ {
+ TestStateChangeListener testStateChangeListener = new TestStateChangeListener(State.REPLICA);
+ ReplicatedEnvironmentFacade replicaEnvironmentFacade = addNode(nodeName, nodeHostPort, TEST_DESIGNATED_PRIMARY, State.REPLICA, testStateChangeListener, replicationGroupListener);
+ assertTrue("Replica " + nodeName + " was not started", testStateChangeListener.awaitForStateChange(LISTENER_TIMEOUT, TimeUnit.SECONDS));
+ return replicaEnvironmentFacade;
+ }
+
+ private ReplicatedEnvironmentFacade addNode(String nodeName, String nodeHostPort, boolean designatedPrimary,
+ State desiredState, StateChangeListener stateChangeListener, ReplicationGroupListener replicationGroupListener)
+ {
+ LocalReplicationNode node = createReplicationNodeMock(nodeName, nodeHostPort, designatedPrimary);
+ ReplicatedEnvironmentFacade ref = new ReplicatedEnvironmentFacade(node, _remoteReplicationNodeFactory);
+ ref.setReplicationGroupListener(replicationGroupListener);
+ ref.setStateChangeListener(stateChangeListener);
+ _nodes.put(nodeName, ref);
+ return ref;
+ }
+
+ private ReplicatedEnvironmentFacade addNode(State desiredState, StateChangeListener stateChangeListener, ReplicationGroupListener groupChangeListener)
+ {
+ return addNode(TEST_NODE_NAME, TEST_NODE_HOST_PORT, TEST_DESIGNATED_PRIMARY, desiredState, stateChangeListener, groupChangeListener);
+ }
+
+ private DatabaseConfig createDatabase(ReplicatedEnvironmentFacade environmentFacade, String databaseName) throws AMQStoreException
+ {
+ DatabaseConfig dbConfig = new DatabaseConfig();
+ dbConfig.setTransactional(true);
+ dbConfig.setAllowCreate(true);
+ environmentFacade.openDatabases(dbConfig, databaseName);
+ return dbConfig;
+ }
+
+ private LocalReplicationNode createReplicationNodeMock(String nodeName, String nodeHostPort, boolean designatedPrimary)
+ {
+ LocalReplicationNode node = mock(LocalReplicationNode.class);
+ when(node.getAttribute(NAME)).thenReturn(nodeName);
+ when(node.getName()).thenReturn(nodeName);
+ when(node.getAttribute(HOST_PORT)).thenReturn(nodeHostPort);
+ when(node.getAttribute(DESIGNATED_PRIMARY)).thenReturn(designatedPrimary);
+ when(node.getAttribute(QUORUM_OVERRIDE)).thenReturn(TEST_ELECTABLE_GROUP_OVERRIDE);
+ when(node.getAttribute(PRIORITY)).thenReturn(TEST_PRIORITY);
+ when(node.getAttribute(GROUP_NAME)).thenReturn(TEST_GROUP_NAME);
+ when(node.getAttribute(HELPER_HOST_PORT)).thenReturn(TEST_NODE_HELPER_HOST_PORT);
+ when(node.getAttribute(DURABILITY)).thenReturn(TEST_DURABILITY);
+ when(node.getAttribute(COALESCING_SYNC)).thenReturn(TEST_COALESCING_SYNC);
+
+ // TODO REF contract with LRN is too complicated.
+ when(node.getActualAttribute(HOST_PORT)).thenReturn(nodeHostPort);
+ when(node.getActualAttribute(DESIGNATED_PRIMARY)).thenReturn(designatedPrimary);
+ when(node.getActualAttribute(QUORUM_OVERRIDE)).thenReturn(TEST_ELECTABLE_GROUP_OVERRIDE);
+ when(node.getActualAttribute(PRIORITY)).thenReturn(TEST_PRIORITY);
+ when(node.getActualAttribute(GROUP_NAME)).thenReturn(TEST_GROUP_NAME);
+ when(node.getActualAttribute(HELPER_HOST_PORT)).thenReturn(TEST_NODE_HELPER_HOST_PORT);
+ when(node.getActualAttribute(DURABILITY)).thenReturn(TEST_DURABILITY);
+ when(node.getActualAttribute(COALESCING_SYNC)).thenReturn(TEST_COALESCING_SYNC);
+
+ Map<String, String> repConfig = new HashMap<String, String>();
+ repConfig.put(ReplicationConfig.REPLICA_ACK_TIMEOUT, "2 s");
+ repConfig.put(ReplicationConfig.INSUFFICIENT_REPLICAS_TIMEOUT, "2 s");
+ when(node.getActualAttribute(REPLICATION_PARAMETERS)).thenReturn(repConfig);
+
+ when(node.getAttribute(STORE_PATH)).thenReturn(new File(_storePath, nodeName).getAbsolutePath());
+ return node;
+ }
+}
diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/replication/TestStateChangeListener.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/replication/TestStateChangeListener.java
new file mode 100644
index 0000000000..1e244e1f89
--- /dev/null
+++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/replication/TestStateChangeListener.java
@@ -0,0 +1,54 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store.berkeleydb.replication;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import com.sleepycat.je.rep.StateChangeEvent;
+import com.sleepycat.je.rep.StateChangeListener;
+import com.sleepycat.je.rep.ReplicatedEnvironment.State;
+
+class TestStateChangeListener implements StateChangeListener
+{
+ private final State _expectedState;
+ private final CountDownLatch _latch;
+
+ public TestStateChangeListener(State expectedState)
+ {
+ _expectedState = expectedState;
+ _latch = new CountDownLatch(1);
+ }
+
+ @Override
+ public void stateChange(StateChangeEvent stateChangeEvent) throws RuntimeException
+ {
+ if (stateChangeEvent.getState() == _expectedState)
+ {
+ _latch.countDown();
+ }
+ }
+
+ public boolean awaitForStateChange(long timeout, TimeUnit timeUnit) throws InterruptedException
+ {
+ return _latch.await(timeout, timeUnit);
+ }
+} \ No newline at end of file
diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/DatabaseTemplateTest.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/DatabaseTemplateTest.java
index 7ec442b73d..e3b940e6a0 100644
--- a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/DatabaseTemplateTest.java
+++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/DatabaseTemplateTest.java
@@ -26,6 +26,7 @@ import static org.mockito.Matchers.same;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+
import junit.framework.TestCase;
import com.sleepycat.je.Database;
diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6Test.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6Test.java
index 44f0861275..701fd94115 100644
--- a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6Test.java
+++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6Test.java
@@ -47,7 +47,6 @@ import org.apache.qpid.server.model.Exchange;
import org.apache.qpid.server.model.LifetimePolicy;
import org.apache.qpid.server.model.Queue;
import org.apache.qpid.server.model.UUIDGenerator;
-import org.apache.qpid.server.queue.AMQQueueFactory;
import org.apache.qpid.server.queue.QueueArgumentsConverter;
import org.apache.qpid.server.store.berkeleydb.entry.Xid;
import org.apache.qpid.server.store.berkeleydb.tuple.XidBinding;
diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgraderFailOnNewerVersionTest.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgraderFailOnNewerVersionTest.java
index 75717120b3..55538c4881 100644
--- a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgraderFailOnNewerVersionTest.java
+++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgraderFailOnNewerVersionTest.java
@@ -20,22 +20,15 @@
*/
package org.apache.qpid.server.store.berkeleydb.upgrade;
+import org.apache.qpid.AMQStoreException;
+import org.apache.qpid.server.store.berkeleydb.BDBMessageStore;
+
import com.sleepycat.bind.tuple.IntegerBinding;
-import com.sleepycat.bind.tuple.LongBinding;
import com.sleepycat.je.Cursor;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
-import com.sleepycat.je.Environment;
-import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.OperationStatus;
-import com.sleepycat.je.Transaction;
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-import org.apache.qpid.AMQStoreException;
-import org.apache.qpid.server.store.berkeleydb.AbstractBDBMessageStore;
-import org.apache.qpid.server.store.berkeleydb.tuple.ContentBinding;
public class UpgraderFailOnNewerVersionTest extends AbstractUpgradeTestCase
{
@@ -102,7 +95,7 @@ public class UpgraderFailOnNewerVersionTest extends AbstractUpgradeTestCase
catch(AMQStoreException ex)
{
assertEquals("Incorrect exception thrown", "Database version 999 is higher than the most recent known version: "
- + AbstractBDBMessageStore.VERSION, ex.getMessage());
+ + BDBMessageStore.VERSION, ex.getMessage());
}
}
diff --git a/qpid/java/bdbstore/systests/pom.xml b/qpid/java/bdbstore/systests/pom.xml
index 01b87448e2..c25470bf6d 100644
--- a/qpid/java/bdbstore/systests/pom.xml
+++ b/qpid/java/bdbstore/systests/pom.xml
@@ -45,6 +45,13 @@
</dependency>
<dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>${mockito-version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
<groupId>org.apache.qpid</groupId>
<artifactId>qpid-systests</artifactId>
<version>${project.version}</version>
diff --git a/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreTest.java b/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreTest.java
index cecd39381e..bd57edfaa8 100644
--- a/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreTest.java
+++ b/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreTest.java
@@ -20,11 +20,15 @@
*/
package org.apache.qpid.server.store.berkeleydb;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import java.io.File;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
+
import org.apache.qpid.AMQStoreException;
import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.framing.BasicContentHeaderProperties;
@@ -41,12 +45,14 @@ import org.apache.qpid.server.message.MessageReference;
import org.apache.qpid.server.message.ServerMessage;
import org.apache.qpid.server.model.UUIDGenerator;
import org.apache.qpid.server.protocol.v0_8.MessageMetaDataType_0_8;
+import org.apache.qpid.server.store.MessageStoreRecoveryHandler;
import org.apache.qpid.server.store.MessageStoreTest;
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.Transaction;
import org.apache.qpid.server.store.TransactionLogResource;
+import org.apache.qpid.server.store.MessageStoreRecoveryHandler.StoredMessageRecoveryHandler;
import org.apache.qpid.transport.DeliveryProperties;
import org.apache.qpid.transport.Header;
import org.apache.qpid.transport.MessageAcceptMode;
@@ -73,7 +79,7 @@ public class BDBMessageStoreTest extends MessageStoreTest
{
MessageStore store = getVirtualHost().getMessageStore();
- AbstractBDBMessageStore bdbStore = assertBDBStore(store);
+ BDBMessageStore bdbStore = assertBDBStore(store);
// Create content ByteBuffers.
// Split the content into 2 chunks for the 0-8 message, as per broker behaviour.
@@ -126,7 +132,7 @@ public class BDBMessageStoreTest extends MessageStoreTest
/*
* reload the store only (read-only)
*/
- AbstractBDBMessageStore readOnlyStore = reloadStore(bdbStore);
+ BDBMessageStore readOnlyStore = reloadStore(bdbStore);
/*
* Read back and validate the 0-8 message metadata and content
@@ -225,14 +231,17 @@ public class BDBMessageStoreTest extends MessageStoreTest
* 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 AbstractBDBMessageStore reloadStore(AbstractBDBMessageStore messageStore) throws Exception
+ private BDBMessageStore reloadStore(BDBMessageStore messageStore) throws Exception
{
messageStore.close();
- AbstractBDBMessageStore newStore = new BDBMessageStore();
- newStore.configure(getVirtualHostModel(),true);
+ BDBMessageStore newStore = new BDBMessageStore();
+
+ MessageStoreRecoveryHandler recoveryHandler = mock(MessageStoreRecoveryHandler.class);
+ when(recoveryHandler.begin()).thenReturn(mock(StoredMessageRecoveryHandler.class));
+ newStore.configureMessageStore(getVirtualHostModel(), recoveryHandler, null);
- newStore.startWithNoRecover();
+ newStore.activate();
return newStore;
}
@@ -287,7 +296,7 @@ public class BDBMessageStoreTest extends MessageStoreTest
public void testGetContentWithOffset() throws Exception
{
MessageStore store = getVirtualHost().getMessageStore();
- AbstractBDBMessageStore bdbStore = assertBDBStore(store);
+ BDBMessageStore bdbStore = assertBDBStore(store);
StoredMessage<MessageMetaData> storedMessage_0_8 = createAndStoreSingleChunkMessage_0_8(store);
long messageid_0_8 = storedMessage_0_8.getMessageNumber();
@@ -347,7 +356,7 @@ public class BDBMessageStoreTest extends MessageStoreTest
public void testMessageCreationAndRemoval() throws Exception
{
MessageStore store = getVirtualHost().getMessageStore();
- AbstractBDBMessageStore bdbStore = assertBDBStore(store);
+ BDBMessageStore bdbStore = assertBDBStore(store);
StoredMessage<MessageMetaData> storedMessage_0_8 = createAndStoreSingleChunkMessage_0_8(store);
long messageid_0_8 = storedMessage_0_8.getMessageNumber();
@@ -372,15 +381,15 @@ public class BDBMessageStoreTest extends MessageStoreTest
assertEquals("Retrieved content when none was expected",
0, bdbStore.getContent(messageid_0_8, 0, dst));
}
- private AbstractBDBMessageStore assertBDBStore(MessageStore store)
+ private BDBMessageStore assertBDBStore(MessageStore store)
{
assertEquals("Test requires an instance of BDBMessageStore to proceed", BDBMessageStore.class, store.getClass());
- return (AbstractBDBMessageStore) store;
+ return (BDBMessageStore) store;
}
- private StoredMessage<MessageMetaData> createAndStoreSingleChunkMessage_0_8(MessageStore store)
+ private StoredMessage<MessageMetaData> createAndStoreSingleChunkMessage_0_8(MessageStore store) throws AMQStoreException
{
ByteBuffer chunk1 = ByteBuffer.wrap(CONTENT_BYTES);
@@ -410,7 +419,7 @@ public class BDBMessageStoreTest extends MessageStoreTest
{
MessageStore log = getVirtualHost().getMessageStore();
- AbstractBDBMessageStore bdbStore = assertBDBStore(log);
+ BDBMessageStore bdbStore = assertBDBStore(log);
final UUID mockQueueId = UUIDGenerator.generateRandomUUID();
TransactionLogResource mockQueue = new TransactionLogResource()
@@ -460,7 +469,7 @@ public class BDBMessageStoreTest extends MessageStoreTest
{
MessageStore log = getVirtualHost().getMessageStore();
- AbstractBDBMessageStore bdbStore = assertBDBStore(log);
+ BDBMessageStore bdbStore = assertBDBStore(log);
final UUID mockQueueId = UUIDGenerator.generateRandomUUID();
TransactionLogResource mockQueue = new TransactionLogResource()
@@ -506,7 +515,7 @@ public class BDBMessageStoreTest extends MessageStoreTest
public void testOnDelete() throws Exception
{
MessageStore log = getVirtualHost().getMessageStore();
- AbstractBDBMessageStore bdbStore = assertBDBStore(log);
+ BDBMessageStore bdbStore = assertBDBStore(log);
String storeLocation = bdbStore.getStoreLocation();
File location = new File(storeLocation);
@@ -529,7 +538,7 @@ public class BDBMessageStoreTest extends MessageStoreTest
{
MessageStore log = getVirtualHost().getMessageStore();
- AbstractBDBMessageStore bdbStore = assertBDBStore(log);
+ BDBMessageStore bdbStore = assertBDBStore(log);
final UUID mockQueueId = UUIDGenerator.generateRandomUUID();
TransactionLogResource mockQueue = new TransactionLogResource()
diff --git a/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/HAClusterBlackboxTest.java b/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/HAClusterBlackboxTest.java
index 0464269efc..84b8de7be9 100644
--- a/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/HAClusterBlackboxTest.java
+++ b/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/HAClusterBlackboxTest.java
@@ -20,6 +20,10 @@
package org.apache.qpid.server.store.berkeleydb;
import java.io.File;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -30,6 +34,7 @@ import org.apache.log4j.Logger;
import org.apache.qpid.client.AMQConnection;
import org.apache.qpid.jms.ConnectionListener;
import org.apache.qpid.jms.ConnectionURL;
+import org.apache.qpid.server.model.ReplicationNode;
import org.apache.qpid.test.utils.QpidBrokerTestCase;
import org.apache.qpid.test.utils.TestUtils;
@@ -61,7 +66,7 @@ public class HAClusterBlackboxTest extends QpidBrokerTestCase
setSystemProperty("java.util.logging.config.file", "etc" + File.separator + "log.properties");
- _clusterCreator.configureClusterNodes();
+ _clusterCreator.configureClusterNodes(null);
_brokerFailoverUrl = _clusterCreator.getConnectionUrlForAllClusterNodes();
@@ -115,6 +120,126 @@ public class HAClusterBlackboxTest extends QpidBrokerTestCase
connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
}
+ public void testTransferMasterFromLocalNode() throws Exception
+ {
+ final Connection connection = getConnection(_brokerFailoverUrl);
+
+ ((AMQConnection)connection).setConnectionListener(_failoverAwaitingListener);
+
+ final int activeBrokerPort = _clusterCreator.getBrokerPortNumberFromConnection(connection);
+ LOGGER.info("Active connection port " + activeBrokerPort);
+
+ final int inactiveBrokerPort = _clusterCreator.getPortNumberOfAnInactiveBroker(connection);
+ LOGGER.info("Update role attribute on inactive broker on port " + inactiveBrokerPort);
+
+ Map<String, Object> attributes = _clusterCreator.getReplicationNodeAttributes(inactiveBrokerPort);
+ assertEquals("Inactive broker has unexpeced role", "REPLICA", attributes.get(ReplicationNode.ROLE));
+ _clusterCreator.setReplicationNodeAttributes(inactiveBrokerPort, Collections.<String, Object>singletonMap(ReplicationNode.ROLE, "MASTER"));
+
+ _failoverAwaitingListener.assertFailoverOccurs(20000);
+ LOGGER.info("Listener has finished");
+
+ attributes = _clusterCreator.getReplicationNodeAttributes(inactiveBrokerPort);
+ assertEquals("Inactive broker has unexpeced role", "MASTER", attributes.get(ReplicationNode.ROLE));
+
+ assertProducingConsuming(connection);
+ }
+
+ public void testTransferMasterFromRemoteNode() throws Exception
+ {
+ final Connection connection = getConnection(_brokerFailoverUrl);
+
+ ((AMQConnection)connection).setConnectionListener(_failoverAwaitingListener);
+
+ final int activeBrokerPort = _clusterCreator.getBrokerPortNumberFromConnection(connection);
+ LOGGER.info("Active connection port " + activeBrokerPort);
+
+ final int inactiveBrokerPort = _clusterCreator.getPortNumberOfAnInactiveBroker(connection);
+ LOGGER.info("Update role attribute on inactive broker on port " + inactiveBrokerPort);
+
+ String nodeName = _clusterCreator.getNodeNameForBrokerPort(inactiveBrokerPort);
+ _clusterCreator.awaitNodeToAttainRole(activeBrokerPort, nodeName, "REPLICA");
+ Map<String, Object> attributes = _clusterCreator.getReplicationNodeAttributes(activeBrokerPort, nodeName);
+ assertEquals("Inactive broker has unexpeced role", "REPLICA", attributes.get(ReplicationNode.ROLE));
+
+ _clusterCreator.setReplicationNodeAttributes(activeBrokerPort, nodeName, Collections.<String, Object>singletonMap(ReplicationNode.ROLE, "MASTER"));
+
+ _failoverAwaitingListener.assertFailoverOccurs(20000);
+ LOGGER.info("Listener has finished");
+
+ attributes = _clusterCreator.getReplicationNodeAttributes(inactiveBrokerPort);
+ assertEquals("Inactive broker has unexpeced role", "MASTER", attributes.get(ReplicationNode.ROLE));
+
+ assertProducingConsuming(connection);
+ }
+ public void testQuorumOverride() throws Exception
+ {
+ final Connection connection = getConnection(_brokerFailoverUrl);
+
+ ((AMQConnection)connection).setConnectionListener(_failoverAwaitingListener);
+
+ Set<Integer> ports = _clusterCreator.getBrokerPortNumbersForNodes();
+ Iterator<Integer> iterator = ports.iterator();
+ Integer quorumOverridePort = iterator.next();
+ iterator.remove();
+
+ for (Integer p : ports)
+ {
+ _clusterCreator.stopNode(p);
+ }
+
+ Map<String, Object> attributes = _clusterCreator.getReplicationNodeAttributes(quorumOverridePort);
+ assertEquals("Broker has unexpeced quorum override", new Integer(0), attributes.get(ReplicationNode.QUORUM_OVERRIDE));
+ _clusterCreator.setReplicationNodeAttributes(quorumOverridePort, Collections.<String, Object>singletonMap(ReplicationNode.QUORUM_OVERRIDE, 1));
+
+ _failoverAwaitingListener.assertFailoverOccurs(20000);
+ LOGGER.info("Listener has finished");
+
+ attributes = _clusterCreator.getReplicationNodeAttributes(quorumOverridePort);
+ assertEquals("Broker has unexpeced quorum override", new Integer(1), attributes.get(ReplicationNode.QUORUM_OVERRIDE));
+
+ assertProducingConsuming(connection);
+ }
+
+ public void testPriority() throws Exception
+ {
+ final Connection connection = getConnection(_brokerFailoverUrl);
+
+ ((AMQConnection)connection).setConnectionListener(_failoverAwaitingListener);
+
+ final int activeBrokerPort = _clusterCreator.getBrokerPortNumberFromConnection(connection);
+ LOGGER.info("Active connection port " + activeBrokerPort);
+
+ int priority = 1;
+ Integer highestPriorityBrokerPort = null;
+ Set<Integer> ports = _clusterCreator.getBrokerPortNumbersForNodes();
+ for (Integer port : ports)
+ {
+ if (activeBrokerPort != port.intValue())
+ {
+ priority = priority + 1;
+ highestPriorityBrokerPort = port;
+ _clusterCreator.setReplicationNodeAttributes(port, Collections.<String, Object>singletonMap(ReplicationNode.PRIORITY, priority));
+ }
+ }
+
+ LOGGER.info("Broker on port " + highestPriorityBrokerPort + " has the highest priority of " + priority);
+
+ Map<String, Object> attributes = _clusterCreator.getReplicationNodeAttributes(highestPriorityBrokerPort);
+ assertEquals("Broker has unexpeced priority", priority, attributes.get(ReplicationNode.PRIORITY));
+
+ LOGGER.info("Shutting down the MASTER");
+ _clusterCreator.stopNode(activeBrokerPort);
+
+ _failoverAwaitingListener.assertFailoverOccurs(20000);
+ LOGGER.info("Listener has finished");
+
+ attributes = _clusterCreator.getReplicationNodeAttributes(highestPriorityBrokerPort);
+ assertEquals("Inactive broker has unexpeced role", "MASTER", attributes.get(ReplicationNode.ROLE));
+
+ assertProducingConsuming(connection);
+ }
+
private final class FailoverAwaitingListener implements ConnectionListener
{
private final CountDownLatch _failoverLatch = new CountDownLatch(1);
diff --git a/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/HAClusterManagementTest.java b/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/HAClusterManagementTest.java
index 4b50121a7a..9adc834f95 100644
--- a/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/HAClusterManagementTest.java
+++ b/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/HAClusterManagementTest.java
@@ -41,8 +41,6 @@ import org.apache.qpid.server.store.berkeleydb.jmx.ManagedBDBHAMessageStore;
import org.apache.qpid.test.utils.JMXTestUtils;
import org.apache.qpid.test.utils.QpidBrokerTestCase;
-import com.sleepycat.je.EnvironmentFailureException;
-
/**
* System test verifying the ability to control a cluster via the Management API.
*
@@ -68,7 +66,7 @@ public class HAClusterManagementTest extends QpidBrokerTestCase
{
_brokerType = BrokerType.SPAWNED;
- _clusterCreator.configureClusterNodes();
+ _clusterCreator.configureClusterNodes(null);
_brokerFailoverUrl = _clusterCreator.getConnectionUrlForAllClusterNodes();
_clusterCreator.startCluster();
@@ -143,11 +141,11 @@ public class HAClusterManagementTest extends QpidBrokerTestCase
CompositeData row = groupMembers.get(new Object[] {nodeName});
assertNotNull("Table does not contain row for node name " + nodeName, row);
- assertEquals(nodeHostPort, row.get(BDBHAMessageStore.GRP_MEM_COL_NODE_HOST_PORT));
+ assertEquals(nodeHostPort, row.get(ManagedBDBHAMessageStore.GRP_MEM_COL_NODE_HOST_PORT));
}
}
- public void testRemoveNodeFromGroup() throws Exception
+ public void testRemoveRemoteNodeFromGroup() throws Exception
{
final Iterator<Integer> brokerPortNumberIterator = getBrokerPortNumbers().iterator();
final int brokerPortNumberToMakeObservation = brokerPortNumberIterator.next();
@@ -155,72 +153,19 @@ public class HAClusterManagementTest extends QpidBrokerTestCase
final ManagedBDBHAMessageStore storeBean = getStoreBeanForNodeAtBrokerPort(brokerPortNumberToMakeObservation);
awaitAllNodesJoiningGroup(storeBean, NUMBER_OF_NODES);
- final String removedNodeName = _clusterCreator.getNodeNameForNodeAt(_clusterCreator.getBdbPortForBrokerPort(brokerPortNumberToBeRemoved));
+ final String removedNodeName = _clusterCreator.getNodeNameForBrokerPort(brokerPortNumberToBeRemoved);
_clusterCreator.stopNode(brokerPortNumberToBeRemoved);
- storeBean.removeNodeFromGroup(removedNodeName);
-
- final int numberOfDataRowsAfterRemoval = storeBean.getAllNodesInGroup().size();
- assertEquals("Unexpected number of data rows before test", NUMBER_OF_NODES - 1,numberOfDataRowsAfterRemoval);
- }
-
- /**
- * Updates the address of a node.
- *
- * If the broker (node) can subsequently start without error then the update was a success, hence no need for an explicit
- * assert.
- *
- * @see #testRestartNodeWithNewPortNumberWithoutFirstCallingUpdateAddressThrowsAnException() for converse case
- */
- public void testUpdateAddress() throws Exception
- {
- final Iterator<Integer> brokerPortNumberIterator = getBrokerPortNumbers().iterator();
- final int brokerPortNumberToPerformUpdate = brokerPortNumberIterator.next();
- final int brokerPortNumberToBeMoved = brokerPortNumberIterator.next();
- final ManagedBDBHAMessageStore storeBean = getStoreBeanForNodeAtBrokerPort(brokerPortNumberToPerformUpdate);
-
- _clusterCreator.stopNode(brokerPortNumberToBeMoved);
-
- final int oldBdbPort = _clusterCreator.getBdbPortForBrokerPort(brokerPortNumberToBeMoved);
- final int newBdbPort = getNextAvailable(oldBdbPort + 1);
-
- storeBean.updateAddress(_clusterCreator.getNodeNameForNodeAt(oldBdbPort), _clusterCreator.getIpAddressOfBrokerHost(), newBdbPort);
-
- _clusterCreator.modifyClusterNodeBdbAddress(brokerPortNumberToBeMoved, newBdbPort);
-
- _clusterCreator.startNode(brokerPortNumberToBeMoved);
- }
-
- /**
- * @see #testUpdateAddress()
- */
- public void testRestartNodeWithNewPortNumberWithoutFirstCallingUpdateAddressThrowsAnException() throws Exception
- {
- final Iterator<Integer> brokerPortNumberIterator = getBrokerPortNumbers().iterator();
- final int brokerPortNumberToBeMoved = brokerPortNumberIterator.next();
-
- _clusterCreator.stopNode(brokerPortNumberToBeMoved);
-
- final int oldBdbPort = _clusterCreator.getBdbPortForBrokerPort(brokerPortNumberToBeMoved);
- final int newBdbPort = getNextAvailable(oldBdbPort + 1);
-
- // now deliberately don't call updateAddress
- _clusterCreator.modifyClusterNodeBdbAddress(brokerPortNumberToBeMoved, newBdbPort);
+ storeBean.removeNodeFromGroup(removedNodeName);
- try
+ long limitTime = System.currentTimeMillis() + 5000;
+ while((NUMBER_OF_NODES == storeBean.getAllNodesInGroup().size()) && System.currentTimeMillis() < limitTime)
{
- _clusterCreator.startNode(brokerPortNumberToBeMoved);
- fail("Exception not thrown");
- }
- catch(RuntimeException rte)
- {
- //check cause was BDBs EnvironmentFailureException
- assertTrue("Message '"+rte.getMessage()+"' does not contain '"
- + EnvironmentFailureException.class.getName()
- + "'.",
- rte.getMessage().contains(EnvironmentFailureException.class.getName()));
- // PASS
+ Thread.sleep(100l);
}
+
+ int numberOfDataRowsAfterRemoval = storeBean.getAllNodesInGroup().size();
+ assertEquals("Unexpected number of data rows after test", NUMBER_OF_NODES - 1, numberOfDataRowsAfterRemoval);
}
public void testVirtualHostOperationsDeniedForNonMasterNode() throws Exception
@@ -253,6 +198,20 @@ public class HAClusterManagementTest extends QpidBrokerTestCase
}
}
+ public void testSetDesignatedPrimary() throws Exception
+ {
+ int brokerPort = _clusterCreator.getBrokerPortNumbersForNodes().iterator().next();
+ final ManagedBDBHAMessageStore storeBean = getStoreBeanForNodeAtBrokerPort(brokerPort);
+ assertFalse("Unexpected designated primary before change", storeBean.getDesignatedPrimary());
+ storeBean.setDesignatedPrimary(true);
+ long limit = System.currentTimeMillis() + 5000;
+ while(!storeBean.getDesignatedPrimary() && System.currentTimeMillis() < limit)
+ {
+ Thread.sleep(100l);
+ }
+ assertTrue("Unexpected designated primary after change", storeBean.getDesignatedPrimary());
+ }
+
private ManagedBDBHAMessageStore getStoreBeanForNodeAtBrokerPort(final int brokerPortNumber) throws Exception
{
_jmxUtils.open(brokerPortNumber);
diff --git a/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/HAClusterTwoNodeTest.java b/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/HAClusterTwoNodeTest.java
index 95626f7fa5..0031b024ba 100644
--- a/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/HAClusterTwoNodeTest.java
+++ b/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/HAClusterTwoNodeTest.java
@@ -20,33 +20,30 @@
package org.apache.qpid.server.store.berkeleydb;
import java.io.File;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.JMSException;
-import javax.jms.Message;
-import javax.jms.MessageConsumer;
import javax.jms.Session;
-import javax.management.ObjectName;
import org.apache.qpid.jms.ConnectionURL;
-import org.apache.qpid.server.store.berkeleydb.jmx.ManagedBDBHAMessageStore;
-import org.apache.qpid.test.utils.JMXTestUtils;
+import org.apache.qpid.server.model.ReplicationNode;
import org.apache.qpid.test.utils.QpidBrokerTestCase;
import com.sleepycat.je.rep.ReplicationConfig;
public class HAClusterTwoNodeTest extends QpidBrokerTestCase
{
- private static final long RECEIVE_TIMEOUT = 5000l;
-
private static final String VIRTUAL_HOST = "test";
- private static final String MANAGED_OBJECT_QUERY = "org.apache.qpid:type=BDBHAMessageStore,name=" + ObjectName.quote(VIRTUAL_HOST);
+ public static final long RECEIVE_TIMEOUT = 5000l;
+
private static final int NUMBER_OF_NODES = 2;
private final HATestClusterCreator _clusterCreator = new HATestClusterCreator(this, VIRTUAL_HOST, NUMBER_OF_NODES);
- private final JMXTestUtils _jmxUtils = new JMXTestUtils(this);
private ConnectionURL _brokerFailoverUrl;
@@ -62,19 +59,6 @@ public class HAClusterTwoNodeTest extends QpidBrokerTestCase
}
@Override
- protected void tearDown() throws Exception
- {
- try
- {
- _jmxUtils.close();
- }
- finally
- {
- super.tearDown();
- }
- }
-
- @Override
public void startBroker() throws Exception
{
// Don't start default broker provided by QBTC.
@@ -84,16 +68,12 @@ public class HAClusterTwoNodeTest extends QpidBrokerTestCase
{
setSystemProperty("java.util.logging.config.file", "etc" + File.separator + "log.properties");
- String storeConfigKeyPrefix = _clusterCreator.getStoreConfigKeyPrefix();
-
- setVirtualHostConfigurationProperty(storeConfigKeyPrefix + ".repConfig(0).name", ReplicationConfig.INSUFFICIENT_REPLICAS_TIMEOUT);
- setVirtualHostConfigurationProperty(storeConfigKeyPrefix + ".repConfig(0).value", "2 s");
-
- setVirtualHostConfigurationProperty(storeConfigKeyPrefix + ".repConfig(1).name", ReplicationConfig.ELECTIONS_PRIMARY_RETRIES);
- setVirtualHostConfigurationProperty(storeConfigKeyPrefix + ".repConfig(1).value", "0");
+ Map<String,String> replicationParameters = new HashMap<String, String>();
+ replicationParameters.put(ReplicationConfig.INSUFFICIENT_REPLICAS_TIMEOUT, "2 s");
+ replicationParameters.put(ReplicationConfig.ELECTIONS_PRIMARY_RETRIES, "0");
- _clusterCreator.configureClusterNodes();
- _clusterCreator.setDesignatedPrimaryOnFirstBroker(designedPrimary);
+ _clusterCreator.configureClusterNodes(replicationParameters);
+ _clusterCreator.configureDesignatedPrimaryOnFirstBroker(designedPrimary);
_brokerFailoverUrl = _clusterCreator.getConnectionUrlForAllClusterNodes();
_clusterCreator.startCluster();
}
@@ -143,7 +123,7 @@ public class HAClusterTwoNodeTest extends QpidBrokerTestCase
try
{
assertProducingConsuming(connection);
- fail("JMS peristent operations succeded on Master 'not designated primary' buy they should fail as replica is not available");
+ fail("JMS peristent operations succeded on Master 'not designated primary' but they should fail as replica is not available");
}
catch(JMSException e)
{
@@ -170,48 +150,79 @@ public class HAClusterTwoNodeTest extends QpidBrokerTestCase
public void testInitialDesignatedPrimaryStateOfNodes() throws Exception
{
startCluster(true);
- final ManagedBDBHAMessageStore primaryStoreBean = getStoreBeanForNodeAtBrokerPort(_clusterCreator.getBrokerPortNumberOfPrimary());
- assertTrue("Expected primary node to be set as designated primary", primaryStoreBean.getDesignatedPrimary());
+ Map<String, Object> primaryAttributes = _clusterCreator.getReplicationNodeAttributes(_clusterCreator.getBrokerPortNumberOfPrimary());
+ assertTrue("Expected primary node to be set as designated primary", (Boolean)primaryAttributes.get(ReplicationNode.DESIGNATED_PRIMARY));
- final ManagedBDBHAMessageStore secondaryStoreBean = getStoreBeanForNodeAtBrokerPort(_clusterCreator.getBrokerPortNumberOfSecondaryNode());
- assertFalse("Expected secondary node to NOT be set as designated primary", secondaryStoreBean.getDesignatedPrimary());
+ Map<String, Object> secondaryAttributes = _clusterCreator.getReplicationNodeAttributes(_clusterCreator.getBrokerPortNumberOfSecondaryNode());
+ assertFalse("Expected secondary node to NOT be set as designated primary", (Boolean)secondaryAttributes.get(ReplicationNode.DESIGNATED_PRIMARY));
}
public void testSecondaryDesignatedAsPrimaryAfterOrginalPrimaryStopped() throws Exception
{
startCluster(true);
_clusterCreator.stopNode(_clusterCreator.getBrokerPortNumberOfPrimary());
- final ManagedBDBHAMessageStore storeBean = getStoreBeanForNodeAtBrokerPort(_clusterCreator.getBrokerPortNumberOfSecondaryNode());
- assertFalse("Expected node to NOT be set as designated primary", storeBean.getDesignatedPrimary());
- storeBean.setDesignatedPrimary(true);
- assertTrue("Expected node to now be set as designated primary", storeBean.getDesignatedPrimary());
+ int brokerPortNumberOfSecondaryNode = _clusterCreator.getBrokerPortNumberOfSecondaryNode();
+
+ Map<String, Object> secondaryAttributes = _clusterCreator.getReplicationNodeAttributes(brokerPortNumberOfSecondaryNode);
+ assertFalse("Expected node to NOT be set as designated primary", (Boolean)secondaryAttributes.get(ReplicationNode.DESIGNATED_PRIMARY));
+
+ setDesignatedPrimary(brokerPortNumberOfSecondaryNode, true);
+
+ secondaryAttributes = _clusterCreator.getReplicationNodeAttributes(brokerPortNumberOfSecondaryNode);
+ assertTrue("Expected node to now be set as designated primary", (Boolean)secondaryAttributes.get(ReplicationNode.DESIGNATED_PRIMARY));
final Connection connection = getConnection(_brokerFailoverUrl);
assertNotNull("Expected to get a valid connection to new primary", connection);
assertProducingConsuming(connection);
}
- private ManagedBDBHAMessageStore getStoreBeanForNodeAtBrokerPort(
- final int activeBrokerPortNumber) throws Exception
+ public void testMasterNotDesignatedPrimaryAssignedDesignatedPrimaryLaterOn() throws Exception
{
- _jmxUtils.open(activeBrokerPortNumber);
+ startCluster(false);
- ManagedBDBHAMessageStore storeBean = _jmxUtils.getManagedObject(ManagedBDBHAMessageStore.class, MANAGED_OBJECT_QUERY);
- return storeBean;
- }
+ // Shutdown replica
+ _clusterCreator.stopNode(_clusterCreator.getBrokerPortNumberOfSecondaryNode());
- private void assertProducingConsuming(final Connection connection) throws JMSException, Exception
- {
+ // Do transaction
+ final Connection connection = getConnection(_brokerFailoverUrl);
Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
Destination destination = session.createQueue(getTestQueueName());
- MessageConsumer consumer = session.createConsumer(destination);
- sendMessage(session, destination, 1);
- connection.start();
- Message m1 = consumer.receive(RECEIVE_TIMEOUT);
- assertNotNull("Message 1 is not received", m1);
- assertEquals("Unexpected first message received", 0, m1.getIntProperty(INDEX));
- session.commit();
+ try
+ {
+ session.createConsumer(destination);
+ fail("Creation of durable queue should fail as there is no majority");
+ }
+ catch (JMSException je)
+ {
+ // pass
+ je.printStackTrace();
+ }
+
+ int brokerPortNumberOfPrimary = _clusterCreator.getBrokerPortNumberOfPrimary();
+
+ // Check master is in UNKNOWN
+ awaitNodeToAttainRole(brokerPortNumberOfPrimary, "UNKNOWN");
+
+ // Designate primary
+ setDesignatedPrimary(brokerPortNumberOfPrimary, true);
+
+ // Check master is MASTER
+ awaitNodeToAttainRole(brokerPortNumberOfPrimary, "MASTER");
+
+ final Connection connection2 = getConnection(_brokerFailoverUrl);
+ assertProducingConsuming(connection2);
+ }
+
+ private void setDesignatedPrimary(int brokerPort, boolean designatedPrimary) throws Exception
+ {
+ _clusterCreator.setReplicationNodeAttributes(brokerPort, Collections.<String, Object>singletonMap(ReplicationNode.DESIGNATED_PRIMARY, designatedPrimary));
+ }
+
+ private void awaitNodeToAttainRole(int brokerPort, String desiredRole) throws Exception
+ {
+ String nodeName = _clusterCreator.getNodeNameForBrokerPort(brokerPort);
+ _clusterCreator.awaitNodeToAttainRole(brokerPort, nodeName, desiredRole);
}
}
diff --git a/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/HAClusterWhiteboxTest.java b/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/HAClusterWhiteboxTest.java
index 408643b98a..616294c4cc 100644
--- a/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/HAClusterWhiteboxTest.java
+++ b/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/HAClusterWhiteboxTest.java
@@ -23,7 +23,6 @@ import java.io.File;
import java.util.Set;
import javax.jms.Connection;
-import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
@@ -60,7 +59,7 @@ public class HAClusterWhiteboxTest extends QpidBrokerTestCase
setSystemProperty("java.util.logging.config.file", "etc" + File.separator + "log.properties");
- _clusterCreator.configureClusterNodes();
+ _clusterCreator.configureClusterNodes(null);
_clusterCreator.startCluster();
super.setUp();
@@ -235,22 +234,6 @@ public class HAClusterWhiteboxTest extends QpidBrokerTestCase
killBroker(initialPortNumber); // kill awaits the death of the child
}
- private void assertProducingConsuming(final Connection connection) throws JMSException, Exception
- {
- Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
- Destination destination = session.createQueue(getTestQueueName());
- MessageConsumer consumer = session.createConsumer(destination);
- sendMessage(session, destination, 2);
- connection.start();
- Message m1 = consumer.receive(RECEIVE_TIMEOUT);
- assertNotNull("Message 1 is not received", m1);
- assertEquals("Unexpected first message received", 0, m1.getIntProperty(INDEX));
- Message m2 = consumer.receive(RECEIVE_TIMEOUT);
- assertNotNull("Message 2 is not received", m2);
- assertEquals("Unexpected second message received", 1, m2.getIntProperty(INDEX));
- session.commit();
- }
-
private void closeConnection(final Connection initialConnection)
{
try
diff --git a/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/HATestClusterCreator.java b/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/HATestClusterCreator.java
index 353c3a0ec5..3674f59bba 100644
--- a/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/HATestClusterCreator.java
+++ b/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/HATestClusterCreator.java
@@ -19,16 +19,19 @@
*/
package org.apache.qpid.server.store.berkeleydb;
+import java.io.File;
+import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
+import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
@@ -38,14 +41,21 @@ import java.util.concurrent.TimeUnit;
import javax.jms.Connection;
-import org.apache.commons.configuration.XMLConfiguration;
-import org.apache.commons.lang.StringUtils;
+import junit.framework.Assert;
+
import org.apache.log4j.Logger;
import org.apache.qpid.client.AMQConnection;
import org.apache.qpid.client.AMQConnectionURL;
-import org.apache.qpid.test.utils.TestBrokerConfiguration;
+import org.apache.qpid.server.configuration.BrokerProperties;
+import org.apache.qpid.server.management.plugin.HttpManagement;
+import org.apache.qpid.server.model.ReplicationNode;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.systest.rest.RestTestHelper;
import org.apache.qpid.test.utils.QpidBrokerTestCase;
+import org.apache.qpid.test.utils.TestBrokerConfiguration;
import org.apache.qpid.url.URLSyntaxException;
+import org.codehaus.jackson.JsonGenerationException;
+import org.codehaus.jackson.map.JsonMappingException;
public class HATestClusterCreator
{
@@ -65,17 +75,14 @@ public class HATestClusterCreator
private static final int CONNECTDELAY = 75;
private final QpidBrokerTestCase _testcase;
- private final Map<Integer, Integer> _brokerPortToBdbPortMap = new HashMap<Integer, Integer>();
- private final Map<Integer, BrokerConfigHolder> _brokerConfigurations = new TreeMap<Integer, BrokerConfigHolder>();
+ private final Map<Integer, Integer> _brokerPortToBdbPortMap = new TreeMap<Integer, Integer>();
private final String _virtualHostName;
- private final String _vhostStoreConfigKeyPrefix;
private final String _ipAddressOfBroker;
- private final String _groupName ;
+ private final String _groupName;
private final int _numberOfNodes;
private int _bdbHelperPort;
private int _primaryBrokerPort;
- private String _vhostConfigKeyPrefix;
public HATestClusterCreator(QpidBrokerTestCase testcase, String virtualHostName, int numberOfNodes)
{
@@ -84,12 +91,10 @@ public class HATestClusterCreator
_groupName = "group" + _testcase.getName();
_ipAddressOfBroker = getIpAddressOfBrokerHost();
_numberOfNodes = numberOfNodes;
- _vhostConfigKeyPrefix = "virtualhosts.virtualhost." + _virtualHostName + ".";
- _vhostStoreConfigKeyPrefix = _vhostConfigKeyPrefix + "store.";
_bdbHelperPort = 0;
}
- public void configureClusterNodes() throws Exception
+ public void configureClusterNodes(Map<String,String> replicationParameters) throws Exception
{
int brokerPort = _testcase.findFreePort();
@@ -104,50 +109,36 @@ public class HATestClusterCreator
_bdbHelperPort = bdbPort;
}
- configureClusterNode(brokerPort, bdbPort);
- TestBrokerConfiguration brokerConfiguration = _testcase.getBrokerConfiguration(brokerPort);
- brokerConfiguration.addJmxManagementConfiguration();
- collectConfig(brokerPort, brokerConfiguration, _testcase.getTestVirtualhosts());
+ configureClusterNodeInBrokerConfiguration(brokerPort, bdbPort, replicationParameters);
brokerPort = _testcase.getNextAvailable(bdbPort + 1);
}
}
- public void setDesignatedPrimaryOnFirstBroker(boolean designatedPrimary) throws Exception
+ public void configureDesignatedPrimaryOnFirstBroker(boolean designatedPrimary) throws Exception
{
if (_numberOfNodes != 2)
{
throw new IllegalArgumentException("Only two nodes groups have the concept of primary");
}
-
- final Entry<Integer, BrokerConfigHolder> brokerConfigEntry = _brokerConfigurations.entrySet().iterator().next();
- final String configKey = getConfigKey("highAvailability.designatedPrimary");
- brokerConfigEntry.getValue().getTestVirtualhosts().setProperty(configKey, Boolean.toString(designatedPrimary));
- _primaryBrokerPort = brokerConfigEntry.getKey();
- }
-
- /**
- * @param configKeySuffix "highAvailability.designatedPrimary", for example
- * @return "virtualhost.test.store.highAvailability.designatedPrimary", for example
- */
- private String getConfigKey(String configKeySuffix)
- {
- final String configKey = StringUtils.substringAfter(_vhostStoreConfigKeyPrefix + configKeySuffix, "virtualhosts.");
- return configKey;
+ Map.Entry<Integer, Integer> portsEntry = _brokerPortToBdbPortMap.entrySet().iterator().next();
+ TestBrokerConfiguration brokerConfiguration = _testcase.getBrokerConfiguration(portsEntry.getKey());
+ String nodeName = getNodeNameForNodeAt(portsEntry.getValue());
+ brokerConfiguration.setObjectAttribute(nodeName, ReplicationNode.DESIGNATED_PRIMARY, designatedPrimary);
+
+ // store broker configuration on next restart
+ brokerConfiguration.setSaved(false);
+ _primaryBrokerPort = portsEntry.getKey();
}
public void startNode(final int brokerPortNumber) throws Exception
{
- final BrokerConfigHolder brokerConfigHolder = _brokerConfigurations.get(brokerPortNumber);
-
- _testcase.setTestVirtualhosts(brokerConfigHolder.getTestVirtualhosts());
-
_testcase.startBroker(brokerPortNumber);
}
public void startCluster() throws Exception
{
- for (final Integer brokerPortNumber : _brokerConfigurations.keySet())
+ for (final Integer brokerPortNumber : _brokerPortToBdbPortMap.keySet())
{
startNode(brokerPortNumber);
}
@@ -155,21 +146,19 @@ public class HATestClusterCreator
public void startClusterParallel() throws Exception
{
- final ExecutorService executor = Executors.newFixedThreadPool(_brokerConfigurations.size());
+ final ExecutorService executor = Executors.newFixedThreadPool(_brokerPortToBdbPortMap.size());
try
{
List<Future<Object>> brokers = new CopyOnWriteArrayList<Future<Object>>();
- for (final Integer brokerPortNumber : _brokerConfigurations.keySet())
+ for (final Integer brokerPortNumber : _brokerPortToBdbPortMap.keySet())
{
- final BrokerConfigHolder brokerConfigHolder = _brokerConfigurations.get(brokerPortNumber);
Future<Object> future = executor.submit(new Callable<Object>()
{
public Object call()
{
try
{
- _testcase.startBroker(brokerPortNumber, brokerConfigHolder.getTestConfiguration(),
- brokerConfigHolder.getTestVirtualhosts());
+ _testcase.startBroker(brokerPortNumber);
return "OK";
}
catch (Exception e)
@@ -213,7 +202,7 @@ public class HATestClusterCreator
public void stopCluster() throws Exception
{
- for (final Integer brokerPortNumber : _brokerConfigurations.keySet())
+ for (final Integer brokerPortNumber : _brokerPortToBdbPortMap.keySet())
{
try
{
@@ -301,6 +290,11 @@ public class HATestClusterCreator
return _groupName;
}
+ public String getNodeNameForBrokerPort(final int brokerPort)
+ {
+ return getNodeNameForNodeAt(_brokerPortToBdbPortMap.get(brokerPort));
+ }
+
public String getNodeNameForNodeAt(final int bdbPort)
{
return "node" + _testcase.getName() + bdbPort;
@@ -345,21 +339,40 @@ public class HATestClusterCreator
public Set<Integer> getBrokerPortNumbersForNodes()
{
- return new HashSet<Integer>(_brokerConfigurations.keySet());
+ return new HashSet<Integer>(_brokerPortToBdbPortMap.keySet());
}
- private void configureClusterNode(final int brokerPort, final int bdbPort) throws Exception
+ private void configureClusterNodeInBrokerConfiguration(final int brokerPort, final int bdbPort, Map<String,String> replicationParameters) throws Exception
{
- final String nodeName = getNodeNameForNodeAt(bdbPort);
+ String nodeName = getNodeNameForNodeAt(bdbPort);
+ TestBrokerConfiguration config = _testcase.getBrokerConfiguration(brokerPort);
+
+ //remove default non-ha test virtual host
+ config.removeObjectConfiguration("test");
+
+ // replication node
+ Map<String, Object> replicationNodeAttributes = new HashMap<String, Object>();
+ replicationNodeAttributes.put(ReplicationNode.NAME, nodeName);
+ replicationNodeAttributes.put(ReplicationNode.GROUP_NAME, _groupName);
+ replicationNodeAttributes.put(ReplicationNode.HOST_PORT, getNodeHostPortForNodeAt(bdbPort));
+ replicationNodeAttributes.put(ReplicationNode.HELPER_HOST_PORT, getHelperHostPort());
+ if (replicationParameters != null)
+ {
+ replicationNodeAttributes.put(ReplicationNode.REPLICATION_PARAMETERS, replicationParameters);
+ }
+ replicationNodeAttributes.put(ReplicationNode.STORE_PATH, System.getProperty(BrokerProperties.PROPERTY_QPID_WORK) + File.separator + nodeName);
+ // ha virtual host
+ Map<String, Object> virtualHostAttributes = new HashMap<String, Object>();
+ virtualHostAttributes.put(VirtualHost.NAME, _virtualHostName);
+ virtualHostAttributes.put(VirtualHost.TYPE, BDBHAVirtualHostFactory.TYPE);
- _testcase.setVirtualHostConfigurationProperty(_vhostConfigKeyPrefix + "type", BDBHAVirtualHostFactory.TYPE);
- _testcase.setVirtualHostConfigurationProperty(_vhostStoreConfigKeyPrefix + "class", "org.apache.qpid.server.store.berkeleydb.BDBHAMessageStore");
+ UUID hostId = config.addVirtualHostConfiguration(virtualHostAttributes);
+ config.addReplicationNodeConfiguration(hostId, replicationNodeAttributes);
- _testcase.setVirtualHostConfigurationProperty(_vhostStoreConfigKeyPrefix + "highAvailability.groupName", _groupName);
- _testcase.setVirtualHostConfigurationProperty(_vhostStoreConfigKeyPrefix + "highAvailability.nodeName", nodeName);
- _testcase.setVirtualHostConfigurationProperty(_vhostStoreConfigKeyPrefix + "highAvailability.nodeHostPort", getNodeHostPortForNodeAt(bdbPort));
- _testcase.setVirtualHostConfigurationProperty(_vhostStoreConfigKeyPrefix + "highAvailability.helperHostPort", getHelperHostPort());
+ config.addJmxManagementConfiguration();
+ config.addHttpManagementConfiguration();
+ config.setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, HttpManagement.HTTP_BASIC_AUTHENTICATION_ENABLED, true);
}
public String getIpAddressOfBrokerHost()
@@ -375,55 +388,68 @@ public class HATestClusterCreator
}
}
- private void collectConfig(final int brokerPortNumber, TestBrokerConfiguration testConfiguration, XMLConfiguration testVirtualhosts)
+ public void setReplicationNodeAttributes(int brokerPort, Map<String, Object> attributeMap) throws Exception
{
- _brokerConfigurations.put(brokerPortNumber, new BrokerConfigHolder(testConfiguration,
- (XMLConfiguration) testVirtualhosts.clone()));
+ String replicationNodeName = getNodeNameForBrokerPort(brokerPort);
+ setReplicationNodeAttributes(brokerPort, replicationNodeName, attributeMap);
}
- public class BrokerConfigHolder
+ public void setReplicationNodeAttributes(int brokerPort, String replicationNodeName, Map<String, Object> attributeMap)
+ throws IOException, JsonGenerationException, JsonMappingException, Exception
{
- private final TestBrokerConfiguration _testConfiguration;
- private final XMLConfiguration _testVirtualhosts;
-
- public BrokerConfigHolder(TestBrokerConfiguration testConfiguration, XMLConfiguration testVirtualhosts)
+ RestTestHelper restHelper = createRestTestHelper(brokerPort);
+ int status = restHelper.submitRequest("/rest/replicationnode/" + _virtualHostName + "/" + replicationNodeName , "PUT", attributeMap);
+ if (status != 200)
{
- _testConfiguration = testConfiguration;
- _testVirtualhosts = testVirtualhosts;
+ throw new Exception("Unexpected http status when updating " + replicationNodeName + " attribute's : " + status);
}
+ }
+
+ public Map<String, Object> getReplicationNodeAttributes(int brokerPort) throws Exception
+ {
+ String replicationNodeName = getNodeNameForBrokerPort(brokerPort);
+ return getReplicationNodeAttributes(brokerPort, replicationNodeName);
+ }
- public TestBrokerConfiguration getTestConfiguration()
+ public Map<String, Object> getReplicationNodeAttributes(int brokerPort, String replicationNodeName) throws IOException
+ {
+ RestTestHelper restHelper = createRestTestHelper(brokerPort);
+ List<Map<String, Object>> results= restHelper.getJsonAsList("/rest/replicationnode/" + _virtualHostName + "/" + replicationNodeName );
+ int size = results.size();
+ if (size == 0)
{
- return _testConfiguration;
+ return Collections.emptyMap();
}
-
- public XMLConfiguration getTestVirtualhosts()
+ else if (size == 1)
{
- return _testVirtualhosts;
+ return results.get(0);
+ }
+ else
+ {
+ throw new RuntimeException("Unexpected number of nodes " + size);
}
}
- public void modifyClusterNodeBdbAddress(int brokerPortNumberToBeMoved, int newBdbPort)
+ private RestTestHelper createRestTestHelper(int brokerPort)
{
- final BrokerConfigHolder brokerConfigHolder = _brokerConfigurations.get(brokerPortNumberToBeMoved);
- final XMLConfiguration virtualHostConfig = brokerConfigHolder.getTestVirtualhosts();
-
- final String configKey = getConfigKey("highAvailability.nodeHostPort");
- final String oldBdbHostPort = virtualHostConfig.getString(configKey);
-
- final String[] oldHostAndPort = StringUtils.split(oldBdbHostPort, ":");
- final String oldHost = oldHostAndPort[0];
-
- final String newBdbHostPort = oldHost + ":" + newBdbPort;
-
- virtualHostConfig.setProperty(configKey, newBdbHostPort);
- collectConfig(brokerPortNumberToBeMoved, brokerConfigHolder.getTestConfiguration(), virtualHostConfig);
+ int httpPort = _testcase.getHttpManagementPort(brokerPort);
+ return RestTestHelper.createRestTestHelperWithDefaultCredentials(httpPort);
}
- public String getStoreConfigKeyPrefix()
+ public void awaitNodeToAttainRole(int brokerPort, String nodeName, String desiredRole) throws Exception
{
- return _vhostStoreConfigKeyPrefix;
- }
-
+ final long startTime = System.currentTimeMillis();
+ Map<String, Object> data = Collections.emptyMap();
+ while(!desiredRole.equals(data.get(ReplicationNode.ROLE)) && (System.currentTimeMillis() - startTime) < 30000)
+ {
+ LOGGER.debug("Awaiting node '" + nodeName + "' to transit into " + desiredRole + " role");
+ data = getReplicationNodeAttributes(brokerPort, nodeName);
+ if (!desiredRole.equals(data.get(ReplicationNode.ROLE)))
+ {
+ Thread.sleep(1000);
+ }
+ }
+ Assert.assertEquals("Node is in unexpected role", desiredRole, data.get(ReplicationNode.ROLE));
+ }
}
diff --git a/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/ReplicationNodeRestTest.java b/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/ReplicationNodeRestTest.java
new file mode 100644
index 0000000000..631e329160
--- /dev/null
+++ b/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/ReplicationNodeRestTest.java
@@ -0,0 +1,169 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.qpid.server.model.ReplicationNode;
+import org.apache.qpid.server.model.State;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.systest.rest.QpidRestTestCase;
+import org.apache.qpid.util.FileUtils;
+
+public class ReplicationNodeRestTest extends QpidRestTestCase
+{
+ private static final String NODE_NAME = "node1";
+ private static final String GROUP_NAME = "replication-group";
+
+ private String _hostName;
+ private File _storeFile;
+ private int _haPort;
+ private String _nodeRestUrl;
+ private String _hostRestUrl;
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ _hostName = getTestName();
+ _nodeRestUrl = "/rest/replicationnode/" + _hostName + "/" + NODE_NAME;
+ _hostRestUrl = "/rest/virtualhost/" + _hostName;
+
+ _storeFile = new File(TMP_FOLDER, "store-" + _hostName + "-" + System.currentTimeMillis());
+ _haPort = findFreePort();
+
+ Map<String, Object> hostData = new HashMap<String, Object>();
+ hostData.put(VirtualHost.NAME, _hostName);
+ hostData.put(VirtualHost.TYPE, BDBHAVirtualHostFactory.TYPE);
+ hostData.put(VirtualHost.DESIRED_STATE, State.QUIESCED);
+
+ int responseCode = getRestTestHelper().submitRequest(_hostRestUrl, "PUT", hostData);
+ assertEquals("Unexpected response code for virtual host creation request", 201, responseCode);
+
+ String hostPort = "localhost:" + _haPort;
+ Map<String, Object> nodeData = new HashMap<String, Object>();
+ nodeData.put(ReplicationNode.NAME, NODE_NAME);
+ nodeData.put(ReplicationNode.GROUP_NAME, GROUP_NAME);
+ nodeData.put(ReplicationNode.HOST_PORT, hostPort);
+ nodeData.put(ReplicationNode.HELPER_HOST_PORT, hostPort);
+ nodeData.put(ReplicationNode.STORE_PATH, _storeFile.getAbsolutePath());
+
+ responseCode = getRestTestHelper().submitRequest(_nodeRestUrl, "PUT", nodeData);
+ assertEquals("Unexpected response code for node creation request", 201, responseCode);
+
+ }
+
+ @Override
+ public void tearDown() throws Exception
+ {
+ try
+ {
+ super.tearDown();
+ }
+ finally
+ {
+ if (_storeFile != null)
+ {
+ FileUtils.delete(_storeFile, true);
+ }
+ }
+ }
+
+ public void testChangePriority() throws Exception
+ {
+ assertReplicationNodeSetAttribute(ReplicationNode.PRIORITY, 1, 2, 3);
+ }
+
+ public void testChangeQuorumOverride() throws Exception
+ {
+ assertReplicationNodeSetAttribute(ReplicationNode.QUORUM_OVERRIDE, 0, 1, 2);
+ }
+
+ public void testChangeDesignatedPrimary() throws Exception
+ {
+ assertReplicationNodeSetAttribute(ReplicationNode.DESIGNATED_PRIMARY, false, true, false);
+ }
+
+ public void testCreationOfSecondLocalReplicationNodeFails() throws Exception
+ {
+ String hostPort = "localhost:" + _haPort;
+ Map<String, Object> nodeData = new HashMap<String, Object>();
+ nodeData.put(ReplicationNode.NAME, NODE_NAME + 1);
+ nodeData.put(ReplicationNode.GROUP_NAME, GROUP_NAME);
+ nodeData.put(ReplicationNode.HOST_PORT, hostPort);
+ nodeData.put(ReplicationNode.HELPER_HOST_PORT, hostPort);
+ nodeData.put(ReplicationNode.STORE_PATH, _storeFile.getAbsolutePath());
+
+ int responseCode = getRestTestHelper().submitRequest(_nodeRestUrl + 1, "PUT", nodeData);
+ assertEquals("Adding of a second replication node should fail", 409, responseCode);
+ }
+
+ public void testUpdateImmutableAttributeWithTheSameValueSucceeds() throws Exception
+ {
+ String hostPort = "localhost:" + _haPort;
+ Map<String, Object> nodeData = new HashMap<String, Object>();
+ nodeData.put(ReplicationNode.NAME, NODE_NAME);
+ nodeData.put(ReplicationNode.GROUP_NAME, GROUP_NAME);
+ nodeData.put(ReplicationNode.HOST_PORT, hostPort);
+ nodeData.put(ReplicationNode.HELPER_HOST_PORT, hostPort);
+ nodeData.put(ReplicationNode.STORE_PATH, _storeFile.getAbsolutePath());
+
+ int responseCode = getRestTestHelper().submitRequest(_nodeRestUrl, "PUT", nodeData);
+ assertEquals("Update with unchanged attribute should succeed", 200, responseCode);
+ }
+
+ private void assertReplicationNodeSetAttribute(String attributeName, Object initialValue,
+ Object newValueBeforeHostActivation, Object newValueAfterHostActivation) throws Exception
+ {
+ Map<String, Object> nodeAttributes = getRestTestHelper().getJsonAsSingletonList(_nodeRestUrl);
+ assertEquals("Unexpected " + attributeName + " after creation", initialValue, nodeAttributes.get(attributeName));
+
+ int responseCode = getRestTestHelper().submitRequest(_nodeRestUrl, "PUT", Collections.<String, Object>singletonMap(attributeName, newValueBeforeHostActivation));
+ assertEquals("Unexpected response code for node " + attributeName + " update", 200, responseCode);
+
+ waitForAttributeChanged(attributeName, newValueBeforeHostActivation);
+
+ responseCode = getRestTestHelper().submitRequest(_hostRestUrl, "PUT", Collections.<String, Object>singletonMap(VirtualHost.DESIRED_STATE, State.ACTIVE));
+ assertEquals("Unexpected response code for virtual host update status", 200, responseCode);
+
+ waitForAttributeChanged(attributeName, newValueBeforeHostActivation);
+
+ responseCode = getRestTestHelper().submitRequest(_nodeRestUrl, "PUT", Collections.<String, Object>singletonMap(attributeName, newValueAfterHostActivation));
+ assertEquals("Unexpected response code for node " + attributeName + " update", 200, responseCode);
+
+ waitForAttributeChanged(attributeName, newValueAfterHostActivation);
+ }
+
+ private void waitForAttributeChanged(String attributeName, Object newValue) throws Exception
+ {
+ Map<String, Object> nodeAttributes = getRestTestHelper().getJsonAsSingletonList(_nodeRestUrl);
+ long limit = System.currentTimeMillis() + 5000;
+ while(!newValue.equals(nodeAttributes.get(attributeName)) && System.currentTimeMillis() < limit)
+ {
+ Thread.sleep(100l);
+ nodeAttributes = getRestTestHelper().getJsonAsSingletonList(_nodeRestUrl);
+ }
+ assertEquals("Unexpected attribute " + attributeName, newValue, nodeAttributes.get(attributeName));
+ }
+}
diff --git a/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/VirtualHostRestTest.java b/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/VirtualHostRestTest.java
new file mode 100644
index 0000000000..b4a3661039
--- /dev/null
+++ b/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/VirtualHostRestTest.java
@@ -0,0 +1,150 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.qpid.server.model.ReplicationNode;
+import org.apache.qpid.server.model.State;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.systest.rest.Asserts;
+import org.apache.qpid.systest.rest.QpidRestTestCase;
+import org.apache.qpid.util.FileUtils;
+
+public class VirtualHostRestTest extends QpidRestTestCase
+{
+
+ private static final String VIRTUALHOST_NODES_ATTRIBUTE = "replicationnodes";
+ private File _storeFile;
+ private String _hostName;
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ _hostName = getTestName();
+
+ _storeFile = new File(TMP_FOLDER, "store-" + _hostName + "-" + System.currentTimeMillis());
+ }
+
+ @Override
+ public void tearDown() throws Exception
+ {
+ try
+ {
+ super.tearDown();
+ }
+ finally
+ {
+ if (_storeFile != null)
+ {
+ FileUtils.delete(_storeFile, true);
+ }
+ }
+ }
+
+ public void testPutCreateHAVirtualHost() throws Exception
+ {
+ Map<String, Object> hostData = new HashMap<String, Object>();
+ hostData.put(VirtualHost.NAME, _hostName);
+ hostData.put(VirtualHost.TYPE, BDBHAVirtualHostFactory.TYPE);
+ hostData.put(VirtualHost.DESIRED_STATE, State.QUIESCED);
+
+ int responseCode = getRestTestHelper().submitRequest("/rest/virtualhost/" + _hostName, "PUT", hostData);
+ assertEquals("Unexpected response code for virtual host creation request", 201, responseCode);
+
+ Map<String, Object> hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/" + _hostName);
+ assertEquals("Virtual host in unexpected desired state ", State.QUIESCED.name(), hostDetails.get(VirtualHost.DESIRED_STATE));
+ assertEquals("Virtual host in unexpected actual state ", State.QUIESCED.name(), hostDetails.get(VirtualHost.STATE));
+
+ String storeLocation = _storeFile.getAbsolutePath();
+ String nodeName = "node1";
+ String groupName = "replication-group";
+ int port = findFreePort();
+ String hostPort = "localhost:" + port;
+
+ Map<String, Object> nodeData = new HashMap<String, Object>();
+ nodeData.put(ReplicationNode.NAME, nodeName);
+ nodeData.put(ReplicationNode.GROUP_NAME, groupName);
+ nodeData.put(ReplicationNode.HOST_PORT, hostPort);
+ nodeData.put(ReplicationNode.HELPER_HOST_PORT, hostPort);
+ nodeData.put(ReplicationNode.STORE_PATH, storeLocation);
+
+ String createNodeUrl = "/rest/replicationnode/" + _hostName + "/" + nodeName;
+ responseCode = getRestTestHelper().submitRequest(createNodeUrl, "PUT", nodeData);
+ assertEquals("Unexpected response code for node creation request", 201, responseCode);
+
+ hostData.clear();
+ hostData.put(VirtualHost.DESIRED_STATE, State.ACTIVE);
+ responseCode = getRestTestHelper().submitRequest("/rest/virtualhost/" + _hostName, "PUT", hostData);
+ assertEquals("Unexpected response code for virtual host update status", 200, responseCode);
+
+ waitForVirtualHostActivation(_hostName, 10000l);
+
+ Map<String, Object> replicationNodeDetails = getRestTestHelper().getJsonAsSingletonList("/rest/replicationnode/" + _hostName + "/" + nodeName);
+ assertLocalNode(nodeData, replicationNodeDetails);
+
+ // make sure that the host is saved in the broker store
+ restartBroker();
+
+ hostDetails = waitForVirtualHostActivation(_hostName, 10000l);
+ Asserts.assertVirtualHost(_hostName, hostDetails);
+ assertEquals("Unexpected virtual host type", BDBHAVirtualHostFactory.TYPE.toString(), hostDetails.get(VirtualHost.TYPE));
+
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> nodes = (List<Map<String, Object>>) hostDetails.get(VIRTUALHOST_NODES_ATTRIBUTE);
+ assertEquals("Unexpected number of nodes", 1, nodes.size());
+ assertLocalNode(nodeData, nodes.get(0));
+
+ // verify that that node rest interface returns the same node attributes
+ replicationNodeDetails = getRestTestHelper().getJsonAsSingletonList("/rest/replicationnode/" + _hostName + "/" + nodeName);
+ assertLocalNode(nodeData, replicationNodeDetails);
+ }
+
+ private void assertLocalNode(Map<String, Object> expectedNodeAttributes, Map<String, Object> actualNodesAttributes)
+ {
+ for (Map.Entry<String, Object> entry : actualNodesAttributes.entrySet())
+ {
+ String name = entry.getKey();
+ Object value = entry.getValue();
+ assertEquals("Unexpected node attribute " + name + " value ", value, actualNodesAttributes.get(name));
+ }
+ }
+
+ private Map<String, Object> waitForVirtualHostActivation(String hostName, long timeout) throws Exception
+ {
+ Map<String, Object> hostDetails = null;
+ long startTime = System.currentTimeMillis();
+ boolean isActive = false;
+ do
+ {
+ hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/" + hostName);
+ isActive = hostDetails.get(VirtualHost.STATE).equals(State.ACTIVE.name());
+ Thread.sleep(100l);
+ }
+ while(!isActive && System.currentTimeMillis() - startTime < timeout );
+ assertTrue("Unexpected virtual host state:" + hostDetails.get(VirtualHost.STATE), isActive);
+ return hostDetails;
+ }
+}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/Broker.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/Broker.java
index 66171c6fc2..bc784947fc 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/Broker.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/Broker.java
@@ -31,7 +31,6 @@ import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.apache.qpid.server.configuration.ConfigurationEntryStore;
import org.apache.qpid.server.configuration.BrokerConfigurationStoreCreator;
-import org.apache.qpid.server.configuration.store.ManagementModeStoreHandler;
import org.apache.qpid.server.logging.SystemOutMessageLogger;
import org.apache.qpid.server.logging.actors.BrokerActor;
import org.apache.qpid.server.logging.actors.CurrentActor;
@@ -111,11 +110,6 @@ public class Broker
ConfigurationEntryStore store = storeCreator.createStore(storeLocation, storeType, options.getInitialConfigurationLocation(),
options.isOverwriteConfigurationStore(), options.getConfigProperties());
- if (options.isManagementMode())
- {
- store = new ManagementModeStoreHandler(store, options);
- }
-
_applicationRegistry = new ApplicationRegistry(store);
try
{
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/BrokerOptions.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/BrokerOptions.java
index 316d9bd88e..14a7c77f62 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/BrokerOptions.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/BrokerOptions.java
@@ -28,6 +28,7 @@ import java.util.concurrent.ConcurrentHashMap;
import org.apache.qpid.server.configuration.BrokerProperties;
import org.apache.qpid.server.configuration.ConfigurationEntryStore;
+import org.apache.qpid.server.configuration.IllegalConfigurationException;
import org.apache.qpid.server.configuration.store.MemoryConfigurationEntryStore;
import org.apache.qpid.server.util.StringUtil;
@@ -68,6 +69,7 @@ public class BrokerOptions
private static final int MANAGEMENT_MODE_PASSWORD_LENGTH = 10;
private static final File FALLBACK_WORK_DIR = new File(System.getProperty("user.dir"), "work");
+ private static final int MAX_PORT_NUMBER = 65535;
private String _logConfigFile;
private Integer _logWatchFrequency = 0;
@@ -143,6 +145,10 @@ public class BrokerOptions
public void setManagementModeRmiPortOverride(int managementModeRmiPortOverride)
{
+ if (checkLegalPortNumber(managementModeRmiPortOverride))
+ {
+ throw new IllegalConfigurationException("Invalid rmi port is specified: " + managementModeRmiPortOverride);
+ }
_managementModeRmiPortOverride = managementModeRmiPortOverride;
}
@@ -153,6 +159,10 @@ public class BrokerOptions
public void setManagementModeJmxPortOverride(int managementModeJmxPortOverride)
{
+ if (checkLegalPortNumber(managementModeJmxPortOverride))
+ {
+ throw new IllegalConfigurationException("Invalid jmx port is specified: " + managementModeJmxPortOverride);
+ }
_managementModeJmxPortOverride = managementModeJmxPortOverride;
}
@@ -163,6 +173,10 @@ public class BrokerOptions
public void setManagementModeHttpPortOverride(int managementModeHttpPortOverride)
{
+ if (checkLegalPortNumber(managementModeHttpPortOverride))
+ {
+ throw new IllegalConfigurationException("Invalid http port is specified: " + managementModeHttpPortOverride);
+ }
_managementModeHttpPortOverride = managementModeHttpPortOverride;
}
@@ -373,4 +387,11 @@ public class BrokerOptions
return _configProperties.get(QPID_HOME_DIR);
}
+
+ private boolean checkLegalPortNumber(int portNumber)
+ {
+ return portNumber < 1 || portNumber > MAX_PORT_NUMBER;
+ }
+
+
}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java
index 189f5916e0..897c750667 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java
@@ -121,11 +121,6 @@ public class VirtualHostConfiguration extends AbstractConfiguration
return getLongValue("housekeeping.checkPeriod", _defaultHouseKeepingCheckPeriod);
}
- public Configuration getStoreConfiguration()
- {
- return getConfig().subset("store");
- }
-
public String getMessageStoreClass()
{
return getStringValue("store.class", null);
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/startup/DefaultRecovererProvider.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/startup/DefaultRecovererProvider.java
index 14b6d9f118..19a6190c15 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/startup/DefaultRecovererProvider.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/startup/DefaultRecovererProvider.java
@@ -33,6 +33,7 @@ import org.apache.qpid.server.model.KeyStore;
import org.apache.qpid.server.model.Plugin;
import org.apache.qpid.server.model.Port;
import org.apache.qpid.server.model.PreferencesProvider;
+import org.apache.qpid.server.model.ReplicationNode;
import org.apache.qpid.server.model.TrustStore;
import org.apache.qpid.server.model.VirtualHost;
import org.apache.qpid.server.model.adapter.AccessControlProviderFactory;
@@ -126,7 +127,10 @@ public class DefaultRecovererProvider implements RecovererProvider
{
return new PluginRecoverer(_pluginFactoryServiceLoader);
}
-
+ else if(ReplicationNode.class.getSimpleName().equals(type))
+ {
+ return new ReplicationNodeRecoverer();
+ }
return null;
}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/startup/ReplicationNodeRecoverer.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/startup/ReplicationNodeRecoverer.java
new file mode 100644
index 0000000000..97ac0ced34
--- /dev/null
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/startup/ReplicationNodeRecoverer.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.configuration.startup;
+
+import org.apache.qpid.server.configuration.ConfigurationEntry;
+import org.apache.qpid.server.configuration.ConfiguredObjectRecoverer;
+import org.apache.qpid.server.configuration.RecovererProvider;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.ReplicationNode;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.plugin.ReplicationNodeFactory;
+
+public class ReplicationNodeRecoverer implements ConfiguredObjectRecoverer<ReplicationNode>
+{
+
+ @Override
+ public ReplicationNode create(RecovererProvider recovererProvider, ConfigurationEntry entry, ConfiguredObject... parents)
+ {
+ VirtualHost virtualHost = RecovererHelper.verifyOnlyParentIsOfType(VirtualHost.class, parents);
+ String type = virtualHost.getType();
+ ReplicationNodeFactory replicationNodeFactory = ReplicationNodeFactory.FACTORIES.get(type);
+ if (replicationNodeFactory == null)
+ {
+ throw new IllegalStateException("Cannot find ReplicationNodeFactory for type '" + type + "'");
+ }
+ return replicationNodeFactory.createInstance(entry.getId() , entry.getAttributes(), virtualHost);
+ }
+
+}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/startup/VirtualHostRecoverer.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/startup/VirtualHostRecoverer.java
index 4f863adfb5..c2f75246fd 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/startup/VirtualHostRecoverer.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/startup/VirtualHostRecoverer.java
@@ -21,6 +21,9 @@
package org.apache.qpid.server.configuration.startup;
+import java.util.Collection;
+import java.util.Map;
+
import org.apache.qpid.server.configuration.ConfigurationEntry;
import org.apache.qpid.server.configuration.ConfiguredObjectRecoverer;
import org.apache.qpid.server.configuration.RecovererProvider;
@@ -45,7 +48,21 @@ public class VirtualHostRecoverer implements ConfiguredObjectRecoverer<VirtualHo
{
Broker broker = RecovererHelper.verifyOnlyBrokerIsParent(parents);
- return new VirtualHostAdapter(entry.getId(), entry.getAttributes(), broker, _brokerStatisticsGatherer, broker.getTaskExecutor());
+ Map<String, Object> attributes = entry.getAttributes();
+ VirtualHostAdapter virtualHostAdapter = new VirtualHostAdapter(entry.getId(), attributes, broker, _brokerStatisticsGatherer, broker.getTaskExecutor());
+
+ Map<String, Collection<ConfigurationEntry>> childEntries = entry.getChildren();
+ for (Map.Entry<String, Collection<ConfigurationEntry>> childrenEntry : childEntries.entrySet())
+ {
+ String childType = childrenEntry.getKey();
+ ConfiguredObjectRecoverer<? extends ConfiguredObject> recoverer = recovererProvider.getRecoverer(childType);
+ for (ConfigurationEntry childEntry : childrenEntry.getValue())
+ {
+ ConfiguredObject configuredObject = recoverer.create(recovererProvider, childEntry, virtualHostAdapter);
+ virtualHostAdapter.recoverChild(configuredObject);
+ }
+ }
+ return virtualHostAdapter;
}
}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/store/ManagementModeStoreHandler.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/store/ManagementModeStoreHandler.java
deleted file mode 100644
index ab06f1b94b..0000000000
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/store/ManagementModeStoreHandler.java
+++ /dev/null
@@ -1,353 +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.server.configuration.store;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-
-import org.apache.log4j.Logger;
-import org.apache.qpid.server.BrokerOptions;
-import org.apache.qpid.server.configuration.ConfigurationEntry;
-import org.apache.qpid.server.configuration.ConfigurationEntryStore;
-import org.apache.qpid.server.configuration.IllegalConfigurationException;
-import org.apache.qpid.server.model.Port;
-import org.apache.qpid.server.model.Protocol;
-import org.apache.qpid.server.model.State;
-import org.apache.qpid.server.model.VirtualHost;
-import org.apache.qpid.server.util.MapValueConverter;
-
-public class ManagementModeStoreHandler implements ConfigurationEntryStore
-{
- private static final Logger LOGGER = Logger.getLogger(ManagementModeStoreHandler.class);
-
- private static final String MANAGEMENT_MODE_PORT_PREFIX = "MANAGEMENT-MODE-PORT-";
- private static final String PORT_TYPE = Port.class.getSimpleName();
- private static final String VIRTUAL_HOST_TYPE = VirtualHost.class.getSimpleName();
- private static final String ATTRIBUTE_STATE = VirtualHost.STATE;
- private static final Object MANAGEMENT_MODE_AUTH_PROVIDER = "mm-auth";
-
-
- private final ConfigurationEntryStore _store;
- private final Map<UUID, ConfigurationEntry> _cliEntries;
- private final Map<UUID, Object> _quiescedEntries;
- private final UUID _rootId;
-
- public ManagementModeStoreHandler(ConfigurationEntryStore store, BrokerOptions options)
- {
- ConfigurationEntry storeRoot = store.getRootEntry();
- _store = store;
- _rootId = storeRoot.getId();
- _cliEntries = createPortsFromCommandLineOptions(options);
- _quiescedEntries = quiesceEntries(storeRoot, options);
- }
-
- @Override
- public ConfigurationEntry getRootEntry()
- {
- return getEntry(_rootId);
- }
-
- @Override
- public ConfigurationEntry getEntry(UUID id)
- {
- synchronized (_store)
- {
- if (_cliEntries.containsKey(id))
- {
- return _cliEntries.get(id);
- }
-
- ConfigurationEntry entry = _store.getEntry(id);
- if (_quiescedEntries.containsKey(id))
- {
- entry = createEntryWithState(entry, State.QUIESCED);
- }
- else if (id == _rootId)
- {
- entry = createRootWithCLIEntries(entry);
- }
- return entry;
- }
- }
-
- @Override
- public void save(ConfigurationEntry... entries)
- {
- synchronized (_store)
- {
- ConfigurationEntry[] entriesToSave = new ConfigurationEntry[entries.length];
-
- for (int i = 0; i < entries.length; i++)
- {
- ConfigurationEntry entry = entries[i];
- UUID id = entry.getId();
- if (_cliEntries.containsKey(id))
- {
- throw new IllegalConfigurationException("Cannot save configuration provided as command line argument:"
- + entry);
- }
- else if (_quiescedEntries.containsKey(id))
- {
- // save entry with the original state
- entry = createEntryWithState(entry, _quiescedEntries.get(ATTRIBUTE_STATE));
- }
- else if (_rootId.equals(id))
- {
- // save root without command line entries
- Set<UUID> childrenIds = new HashSet<UUID>(entry.getChildrenIds());
- if (!_cliEntries.isEmpty())
- {
- childrenIds.removeAll(_cliEntries.entrySet());
- }
- HashMap<String, Object> attributes = new HashMap<String, Object>(entry.getAttributes());
- entry = new ConfigurationEntry(entry.getId(), entry.getType(), attributes, childrenIds, this);
- }
- entriesToSave[i] = entry;
- }
-
- _store.save(entriesToSave);
- }
- }
-
- @Override
- public UUID[] remove(UUID... entryIds)
- {
- synchronized (_store)
- {
- for (UUID id : entryIds)
- {
- if (_cliEntries.containsKey(id))
- {
- throw new IllegalConfigurationException("Cannot change configuration for command line entry:"
- + _cliEntries.get(id));
- }
- }
- UUID[] result = _store.remove(entryIds);
- for (UUID id : entryIds)
- {
- if (_quiescedEntries.containsKey(id))
- {
- _quiescedEntries.remove(id);
- }
- }
- return result;
- }
- }
-
- @Override
- public void copyTo(String copyLocation)
- {
- synchronized (_store)
- {
- _store.copyTo(copyLocation);
- }
- }
-
- @Override
- public String getStoreLocation()
- {
- return _store.getStoreLocation();
- }
-
- @Override
- public int getVersion()
- {
- return _store.getVersion();
- }
-
- @Override
- public String getType()
- {
- return _store.getType();
- }
-
- private Map<UUID, ConfigurationEntry> createPortsFromCommandLineOptions(BrokerOptions options)
- {
- int managementModeRmiPortOverride = options.getManagementModeRmiPortOverride();
- if (managementModeRmiPortOverride < 0)
- {
- throw new IllegalConfigurationException("Invalid rmi port is specified: " + managementModeRmiPortOverride);
- }
- int managementModeJmxPortOverride = options.getManagementModeJmxPortOverride();
- if (managementModeJmxPortOverride < 0)
- {
- throw new IllegalConfigurationException("Invalid jmx port is specified: " + managementModeJmxPortOverride);
- }
- int managementModeHttpPortOverride = options.getManagementModeHttpPortOverride();
- if (managementModeHttpPortOverride < 0)
- {
- throw new IllegalConfigurationException("Invalid http port is specified: " + managementModeHttpPortOverride);
- }
- Map<UUID, ConfigurationEntry> cliEntries = new HashMap<UUID, ConfigurationEntry>();
- if (managementModeRmiPortOverride != 0)
- {
- ConfigurationEntry entry = createCLIPortEntry(managementModeRmiPortOverride, Protocol.RMI);
- cliEntries.put(entry.getId(), entry);
- if (managementModeJmxPortOverride == 0)
- {
- ConfigurationEntry connectorEntry = createCLIPortEntry(managementModeRmiPortOverride + 100, Protocol.JMX_RMI);
- cliEntries.put(connectorEntry.getId(), connectorEntry);
- }
- }
- if (managementModeJmxPortOverride != 0)
- {
- ConfigurationEntry entry = createCLIPortEntry(managementModeJmxPortOverride, Protocol.JMX_RMI);
- cliEntries.put(entry.getId(), entry);
- }
- if (managementModeHttpPortOverride != 0)
- {
- ConfigurationEntry entry = createCLIPortEntry(managementModeHttpPortOverride, Protocol.HTTP);
- cliEntries.put(entry.getId(), entry);
- }
- return cliEntries;
- }
-
- private ConfigurationEntry createCLIPortEntry(int port, Protocol protocol)
- {
- Map<String, Object> attributes = new HashMap<String, Object>();
- attributes.put(Port.PORT, port);
- attributes.put(Port.PROTOCOLS, Collections.singleton(protocol));
- attributes.put(Port.NAME, MANAGEMENT_MODE_PORT_PREFIX + protocol.name());
- if (protocol != Protocol.RMI)
- {
- attributes.put(Port.AUTHENTICATION_PROVIDER, MANAGEMENT_MODE_AUTH_PROVIDER);
- }
- ConfigurationEntry portEntry = new ConfigurationEntry(UUID.randomUUID(), PORT_TYPE, attributes,
- Collections.<UUID> emptySet(), this);
- if (LOGGER.isDebugEnabled())
- {
- LOGGER.debug("Add management mode port configuration " + portEntry + " for port " + port + " and protocol "
- + protocol);
- }
- return portEntry;
- }
-
- private ConfigurationEntry createRootWithCLIEntries(ConfigurationEntry storeRoot)
- {
- Set<UUID> childrenIds = new HashSet<UUID>(storeRoot.getChildrenIds());
- if (!_cliEntries.isEmpty())
- {
- childrenIds.addAll(_cliEntries.keySet());
- }
- ConfigurationEntry root = new ConfigurationEntry(storeRoot.getId(), storeRoot.getType(), new HashMap<String, Object>(
- storeRoot.getAttributes()), childrenIds, this);
- return root;
- }
-
- private Map<UUID, Object> quiesceEntries(ConfigurationEntry storeRoot, BrokerOptions options)
- {
- Map<UUID, Object> quiescedEntries = new HashMap<UUID, Object>();
- Set<UUID> childrenIds;
- int managementModeRmiPortOverride = options.getManagementModeRmiPortOverride();
- int managementModeJmxPortOverride = options.getManagementModeJmxPortOverride();
- int managementModeHttpPortOverride = options.getManagementModeHttpPortOverride();
- childrenIds = storeRoot.getChildrenIds();
- for (UUID id : childrenIds)
- {
- ConfigurationEntry entry = _store.getEntry(id);
- String entryType = entry.getType();
- Map<String, Object> attributes = entry.getAttributes();
- boolean quiesce = false;
- if (VIRTUAL_HOST_TYPE.equals(entryType) && options.isManagementModeQuiesceVirtualHosts())
- {
- quiesce = true;
- }
- else if (PORT_TYPE.equals(entryType))
- {
- if (attributes == null)
- {
- throw new IllegalConfigurationException("Port attributes are not set in " + entry);
- }
- Set<Protocol> protocols = getPortProtocolsAttribute(attributes);
- if (protocols == null)
- {
- quiesce = true;
- }
- else
- {
- for (Protocol protocol : protocols)
- {
- switch (protocol)
- {
- case JMX_RMI:
- quiesce = managementModeJmxPortOverride > 0 || managementModeRmiPortOverride > 0;
- break;
- case RMI:
- quiesce = managementModeRmiPortOverride > 0;
- break;
- case HTTP:
- quiesce = managementModeHttpPortOverride > 0;
- break;
- default:
- quiesce = true;
- }
- }
- }
- }
- if (quiesce)
- {
- if (LOGGER.isDebugEnabled())
- {
- LOGGER.debug("Management mode quiescing entry " + entry);
- }
-
- // save original state
- quiescedEntries.put(entry.getId(), attributes.get(ATTRIBUTE_STATE));
- }
- }
- return quiescedEntries;
- }
-
- private Set<Protocol> getPortProtocolsAttribute(Map<String, Object> attributes)
- {
- Object object = attributes.get(Port.PROTOCOLS);
- if (object == null)
- {
- return null;
- }
- return MapValueConverter.getEnumSetAttribute(Port.PROTOCOLS, attributes, Protocol.class);
- }
-
- private ConfigurationEntry createEntryWithState(ConfigurationEntry entry, Object state)
- {
- Map<String, Object> attributes = new HashMap<String, Object>(entry.getAttributes());
- if (state == null)
- {
- attributes.remove(ATTRIBUTE_STATE);
- }
- else
- {
- attributes.put(ATTRIBUTE_STATE, state);
- }
- Set<UUID> originalChildren = entry.getChildrenIds();
- Set<UUID> children = null;
- if (originalChildren != null)
- {
- children = new HashSet<UUID>(originalChildren);
- }
- return new ConfigurationEntry(entry.getId(), entry.getType(), attributes, children, entry.getStore());
- }
-
-}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/store/MemoryConfigurationEntryStore.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/store/MemoryConfigurationEntryStore.java
index a04df9efe4..cee1232fb6 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/store/MemoryConfigurationEntryStore.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/store/MemoryConfigurationEntryStore.java
@@ -547,7 +547,7 @@ public class MemoryConfigurationEntryStore implements ConfigurationEntryStore
}
else if (fieldNode.isObject())
{
- // ignore, in-line objects are not supported yet
+ attributes.put(fieldName, toObject(fieldNode) );
}
else
{
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListener.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListener.java
index 6cd3447ebd..bef4b6dec9 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListener.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListener.java
@@ -35,6 +35,7 @@ import org.apache.qpid.server.model.ConfigurationChangeListener;
import org.apache.qpid.server.model.ConfiguredObject;
import org.apache.qpid.server.model.Model;
import org.apache.qpid.server.model.Port;
+import org.apache.qpid.server.model.ReplicationNode;
import org.apache.qpid.server.model.State;
import org.apache.qpid.server.model.VirtualHost;
@@ -61,8 +62,9 @@ public class StoreConfigurationChangeListener implements ConfigurationChangeList
@Override
public void childAdded(ConfiguredObject object, ConfiguredObject child)
{
- // exclude VirtualHost children from storing in broker store
- if (!(object instanceof VirtualHost))
+ // exclude VirtualHost children (except for local ReplicationNode) from storing in broker stores
+ if (!(object instanceof VirtualHost) || (object instanceof VirtualHost && child instanceof ReplicationNode
+ && ((ReplicationNode) child).isLocal()))
{
child.addChangeListener(this);
ConfigurationEntry parentEntry = toConfigurationEntry(object);
@@ -95,10 +97,10 @@ public class StoreConfigurationChangeListener implements ConfigurationChangeList
private Set<UUID> getChildrenIds(ConfiguredObject object, Class<? extends ConfiguredObject> objectType)
{
- // Virtual Host children's IDs should not be stored in broker store
+ // Virtual Host children's IDs (except local replication node) should not be stored in broker store
if (object instanceof VirtualHost)
{
- return Collections.emptySet();
+ return getVirtualHostStorableChildrenIds((VirtualHost)object);
}
Set<UUID> childrenIds = new TreeSet<UUID>();
Collection<Class<? extends ConfiguredObject>> childClasses = Model.getInstance().getChildTypes(objectType);
@@ -119,6 +121,32 @@ public class StoreConfigurationChangeListener implements ConfigurationChangeList
return childrenIds;
}
+ private Set<UUID> getVirtualHostStorableChildrenIds(VirtualHost host)
+ {
+ Collection<ReplicationNode> nodes = host.getChildren(ReplicationNode.class);
+ if (nodes.isEmpty())
+ {
+ return Collections.emptySet();
+ }
+ else
+ {
+ ReplicationNode localNode = null;
+ for (ReplicationNode node : nodes)
+ {
+ if (node.isLocal())
+ {
+ localNode = node;
+ break;
+ }
+ }
+ if (localNode == null)
+ {
+ throw new IllegalStateException("Cannot find local replication node among virtual host nodes");
+ }
+ return Collections.singleton(localNode.getId());
+ }
+ }
+
private Class<? extends ConfiguredObject> getConfiguredObjectType(ConfiguredObject object)
{
if (object instanceof Broker)
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/message/internal/InternalMessage.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/message/internal/InternalMessage.java
index f972bd78f6..1584cf3029 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/message/internal/InternalMessage.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/message/internal/InternalMessage.java
@@ -20,6 +20,7 @@
*/
package org.apache.qpid.server.message.internal;
+import org.apache.qpid.AMQStoreException;
import org.apache.qpid.server.message.AMQMessageHeader;
import org.apache.qpid.server.message.AbstractServerMessageImpl;
import org.apache.qpid.server.store.MessageStore;
@@ -140,6 +141,10 @@ public class InternalMessage extends AbstractServerMessageImpl<InternalMessage,
return new InternalMessage(handle, internalHeader, bodyObject);
}
+ catch(AMQStoreException e)
+ {
+ throw new RuntimeException(e);
+ }
catch (IOException e)
{
throw new RuntimeException(e);
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/Broker.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/Broker.java
index 5df6d9475e..28881ff9a6 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/Broker.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/Broker.java
@@ -185,4 +185,9 @@ public interface Broker extends ConfiguredObject
boolean isManagementMode();
AuthenticationProvider getAuthenticationProvider(SocketAddress localAddress);
+
+ /**
+ * TODO: Remove this
+ */
+ boolean isPreviouslyUsedPortNumber(int port);
}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java
index ab9c60573a..3bd9441f87 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java
@@ -38,6 +38,8 @@ import java.util.UUID;
*/
public interface ConfiguredObject
{
+ final String DESIRED_STATE = "desiredState";
+
public static final String ID = "id";
public static final String NAME = "name";
// public static final String TYPE = "type";
@@ -269,4 +271,19 @@ public interface ConfiguredObject
ConfiguredObject... otherParents);
void setAttributes(Map<String, Object> attributes) throws IllegalStateException, AccessControlException, IllegalArgumentException;
+
+ /**
+ * Tells the object (and all its children) to attain its desired state. The desired state will come from the store (if available)
+ * or default to {@link State#ACTIVE} otherwise.
+ * <p>
+ * This method is called exactly once immediately after recovery on broker startup or object creation to initialize the object and its children.
+ */
+ void attainDesiredState();
+
+ /**
+ * Close the object.
+ * <p>
+ * The method is used to deinitialize the configured objects on broker shutdown or object is deleted.
+ */
+ void close();
}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/Model.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/Model.java
index 50538a5580..255193c56e 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/Model.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/Model.java
@@ -37,7 +37,7 @@ public class Model
*
*/
public static final int MODEL_MAJOR_VERSION = 1;
- public static final int MODEL_MINOR_VERSION = 2;
+ public static final int MODEL_MINOR_VERSION = 3;
public static final String MODEL_VERSION = MODEL_MAJOR_VERSION + "." + MODEL_MINOR_VERSION;
private static final Model MODEL_INSTANCE = new Model();
@@ -68,6 +68,7 @@ public class Model
addRelationship(VirtualHost.class, Queue.class);
addRelationship(VirtualHost.class, Connection.class);
addRelationship(VirtualHost.class, VirtualHostAlias.class);
+ addRelationship(VirtualHost.class, ReplicationNode.class);
addRelationship(AuthenticationProvider.class, User.class);
addRelationship(AuthenticationProvider.class, PreferencesProvider.class);
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ReplicationNode.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ReplicationNode.java
new file mode 100644
index 0000000000..0771908a5a
--- /dev/null
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ReplicationNode.java
@@ -0,0 +1,109 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.model;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+
+public interface ReplicationNode extends ConfiguredObject
+{
+ String STATE = "state";
+ String CREATED = "created";
+ String DURABLE = "durable";
+ String LIFETIME_POLICY = "lifetimePolicy";
+ String TIME_TO_LIVE = "timeToLive";
+ String TYPE = "type";
+ String UPDATED = "updated";
+
+ /** Name of the group to which this replication node belongs */
+ String GROUP_NAME = "groupName";
+
+ /** Node host name/IP and port separated by semicolon*/
+ String HOST_PORT = "hostPort";
+
+ /** Node helper host name/IP and port separated by semicolon*/
+ String HELPER_HOST_PORT = "helperHostPort";
+
+ /** Durability settings*/
+ String DURABILITY = "durability";
+
+ /** Sync multiple transactions on disc at the same time*/
+ String COALESCING_SYNC = "coalescingSync";
+
+ /** A designated primary setting for 2-nodes group*/
+ String DESIGNATED_PRIMARY = "designatedPrimary";
+
+ /** Node priority. 1 signifies normal priority; 0 signifies node will never be elected. */
+ String PRIORITY = "priority";
+
+ /** The overridden minimum number of group nodes required to commit transaction on this node instead of simple majority*/
+ String QUORUM_OVERRIDE = "quorumOverride";
+
+ /** Node role: MASTER,REPLICA,UNKNOWN,DETACHED*/
+ String ROLE = "role";
+
+ /** Time when node joined the group */
+ String JOIN_TIME = "joinTime";
+
+ /** Last known replication transaction id */
+ String LAST_KNOWN_REPLICATION_TRANSACTION_ID= "lastKnownReplicationTransactionId";
+
+ /** Map with additional implementation specific node settings */
+ String PARAMETERS = "parameters";
+
+ /** Map with additional implementation specific replication parameters*/
+ String REPLICATION_PARAMETERS = "replicationParameters";
+
+ /** Store path */
+ String STORE_PATH = "storePath";
+
+ // Attributes
+ public static final Collection<String> AVAILABLE_ATTRIBUTES =
+ Collections.unmodifiableList(
+ Arrays.asList(
+ ID,
+ NAME,
+ TYPE,
+ STATE,
+ DURABLE,
+ LIFETIME_POLICY,
+ TIME_TO_LIVE,
+ CREATED,
+ UPDATED,
+ GROUP_NAME,
+ HOST_PORT,
+ HELPER_HOST_PORT,
+ DURABILITY,
+ COALESCING_SYNC,
+ DESIGNATED_PRIMARY,
+ PRIORITY,
+ QUORUM_OVERRIDE,
+ ROLE,
+ JOIN_TIME,
+ LAST_KNOWN_REPLICATION_TRANSACTION_ID,
+ PARAMETERS,
+ REPLICATION_PARAMETERS,
+ STORE_PATH
+ ));
+
+ public boolean isLocal();
+}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/State.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/State.java
index a4287e09b1..a30d488054 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/State.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/State.java
@@ -27,6 +27,6 @@ public enum State
STOPPED,
ACTIVE,
DELETED,
- REPLICA,
+ UNAVAILABLE,
ERRORED
}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/UUIDGenerator.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/UUIDGenerator.java
index 7def89025d..2af644518a 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/UUIDGenerator.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/UUIDGenerator.java
@@ -97,4 +97,9 @@ public class UUIDGenerator
{
return createUUID(PreferencesProvider.class.getName(), authenticationProviderName, preferencesProviderName);
}
+
+ public static UUID generateReplicationNodeId(String groupName, String nodeName)
+ {
+ return createUUID(ReplicationNode.class.getName(), groupName, nodeName);
+ }
}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/VirtualHost.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/VirtualHost.java
index f0241f8b30..81ceb88c79 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/VirtualHost.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/VirtualHost.java
@@ -20,6 +20,7 @@
*/
package org.apache.qpid.server.model;
+import org.apache.qpid.server.configuration.updater.TaskExecutor;
import org.apache.qpid.server.message.MessageInstance;
import org.apache.qpid.server.queue.QueueEntry;
import org.apache.qpid.server.security.SecurityManager;
@@ -125,6 +126,9 @@ public interface VirtualHost extends ConfiguredObject
String UPDATED = "updated";
String CONFIG_PATH = "configPath";
+ String QUIESCE_ON_MASTER_CHANGE = "quiesceOnMasterChange";
+ String REMOTE_REPLICATION_NODE_MONITOR_INTERVAL = "remoteReplicationNodeMonitorInterval";
+
// Attributes
public static final Collection<String> AVAILABLE_ATTRIBUTES =
Collections.unmodifiableList(
@@ -133,6 +137,7 @@ public interface VirtualHost extends ConfiguredObject
NAME,
TYPE,
STATE,
+ DESIRED_STATE,
DURABLE,
LIFETIME_POLICY,
TIME_TO_LIVE,
@@ -158,7 +163,9 @@ public interface VirtualHost extends ConfiguredObject
QUEUE_ALERT_THRESHOLD_MESSAGE_SIZE,
QUEUE_ALERT_THRESHOLD_QUEUE_DEPTH_BYTES,
QUEUE_ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES,
- CONFIG_PATH));
+ CONFIG_PATH,
+ QUIESCE_ON_MASTER_CHANGE,
+ REMOTE_REPLICATION_NODE_MONITOR_INTERVAL));
int CURRENT_CONFIG_VERSION = 3;
@@ -167,6 +174,7 @@ public interface VirtualHost extends ConfiguredObject
Collection<Connection> getConnections();
Collection<Queue> getQueues();
Collection<Exchange> getExchanges();
+ Collection<ReplicationNode> getReplicationNodes();
Exchange createExchange(String name, State initialState, boolean durable,
LifetimePolicy lifetime, long ttl, String type, Map<String, Object> attributes)
@@ -201,7 +209,11 @@ public interface VirtualHost extends ConfiguredObject
*/
SecurityManager getSecurityManager();
+ //TODO: remove this unused method
MessageStore getMessageStore();
String getType();
+
+ TaskExecutor getTaskExecutor();
+
}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AbstractAdapter.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AbstractAdapter.java
index 7cc88f8743..5236411250 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AbstractAdapter.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AbstractAdapter.java
@@ -32,6 +32,7 @@ import org.apache.qpid.server.model.ConfigurationChangeListener;
import org.apache.qpid.server.model.ConfiguredObject;
import org.apache.qpid.server.model.IllegalStateTransitionException;
import org.apache.qpid.server.model.State;
+import org.apache.qpid.server.util.MapValueConverter;
import org.apache.qpid.server.configuration.IllegalConfigurationException;
import org.apache.qpid.server.configuration.updater.ChangeAttributesTask;
import org.apache.qpid.server.configuration.updater.ChangeStateTask;
@@ -91,6 +92,8 @@ public abstract class AbstractAdapter implements ConfiguredObject
}
}
}
+
+ _defaultAttributes.put(DESIRED_STATE, State.ACTIVE);
if (defaults != null)
{
_defaultAttributes.putAll(defaults);
@@ -109,7 +112,7 @@ public abstract class AbstractAdapter implements ConfiguredObject
public State getDesiredState()
{
- return null; //TODO
+ return MapValueConverter.toEnum(DESIRED_STATE, getAttribute(DESIRED_STATE), State.class);
}
@Override
@@ -474,4 +477,15 @@ public abstract class AbstractAdapter implements ConfiguredObject
return merged;
}
+
+ @Override
+ public void attainDesiredState()
+ {
+ setDesiredState(getActualState(), getDesiredState());
+ }
+
+ @Override
+ public void close()
+ {
+ }
}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AccessControlProviderAdapter.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AccessControlProviderAdapter.java
index a6fe191523..0264529fc1 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AccessControlProviderAdapter.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AccessControlProviderAdapter.java
@@ -44,9 +44,10 @@ import org.apache.qpid.server.util.MapValueConverter;
public class AccessControlProviderAdapter extends AbstractAdapter implements AccessControlProvider
{
+
private static final Logger LOGGER = Logger.getLogger(AccessControlProviderAdapter.class);
- protected AccessControl _accessControl;
+ protected final AccessControl _accessControl;
protected final Broker _broker;
protected Collection<String> _supportedAttributes;
@@ -210,6 +211,7 @@ public class AccessControlProviderAdapter extends AbstractAdapter implements Acc
if(desiredState == State.DELETED)
{
+ _accessControl.close();
return _state.compareAndSet(state, State.DELETED);
}
else if (desiredState == State.QUIESCED)
@@ -297,4 +299,16 @@ public class AccessControlProviderAdapter extends AbstractAdapter implements Acc
{
return _accessControl;
}
+
+ @Override
+ public void close()
+ {
+ _accessControl.close();
+ }
+
+ @Override
+ public void attainDesiredState()
+ {
+ setDesiredState(State.INITIALISING, _broker.isManagementMode() ? State.QUIESCED : getDesiredState());
+ }
}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AmqpPortAdapter.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AmqpPortAdapter.java
index c255ce2f4a..eb3432c852 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AmqpPortAdapter.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AmqpPortAdapter.java
@@ -29,9 +29,9 @@ import java.util.UUID;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
-
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
+
import org.apache.qpid.server.configuration.BrokerProperties;
import org.apache.qpid.server.configuration.IllegalConfigurationException;
import org.apache.qpid.server.logging.actors.CurrentActor;
@@ -200,4 +200,22 @@ public class AmqpPortAdapter extends PortAdapter
}
return null;
}
+
+ @Override
+ protected boolean shouldQuiesce()
+ {
+ //TODO: it seems unnecessary to quiesce previously used AMQP ports as they should be closed properly
+ if ( _broker.isPreviouslyUsedPortNumber(getPort()))
+ {
+ // always quiesce port with port number which was previously used
+ return true;
+ }
+
+ if (_broker.isManagementMode())
+ {
+ return true;
+ }
+
+ return false;
+ }
}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java
index 033429980f..83d51cee8c 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java
@@ -266,10 +266,6 @@ public abstract class AuthenticationProviderAdapter<T extends AuthenticationMana
try
{
_authManager.initialise();
- if (_preferencesProvider != null)
- {
- _preferencesProvider.setDesiredState(_preferencesProvider.getActualState(), State.ACTIVE);
- }
return true;
}
catch(RuntimeException e)
@@ -302,10 +298,6 @@ public abstract class AuthenticationProviderAdapter<T extends AuthenticationMana
if (_state.compareAndSet(state, State.STOPPED))
{
_authManager.close();
- if (_preferencesProvider != null)
- {
- _preferencesProvider.setDesiredState(_preferencesProvider.getActualState(), State.STOPPED);
- }
return true;
}
else
@@ -442,13 +434,38 @@ public abstract class AuthenticationProviderAdapter<T extends AuthenticationMana
PreferencesProviderFactory factory = PreferencesProviderFactory.FACTORIES.get(type);
UUID id = UUIDGenerator.generatePreferencesProviderUUID(name, getName());
PreferencesProvider pp = factory.createInstance(id, attributes, this);
- pp.setDesiredState(State.INITIALISING, State.ACTIVE);
+ pp.attainDesiredState();
+ // TODO if preferencesProvider already exists, it should be closed.
_preferencesProvider = pp;
return (C)pp;
}
throw new IllegalArgumentException("Cannot create child of class " + childClass.getSimpleName());
}
+ @Override
+ public void attainDesiredState()
+ {
+ if (_preferencesProvider != null)
+ {
+ _preferencesProvider.attainDesiredState();
+ }
+
+ super.attainDesiredState();
+ }
+
+ @Override
+ public void close()
+ {
+ if (_preferencesProvider != null)
+ {
+ _preferencesProvider.close();
+ }
+ if (_authManager != null)
+ {
+ _authManager.close();
+ }
+ }
+
public static class SimpleAuthenticationProviderAdapter extends AuthenticationProviderAdapter<AuthenticationManager>
{
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java
index b0ef11bc1b..8bc6ae63cf 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java
@@ -30,6 +30,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
+import java.util.concurrent.atomic.AtomicReference;
import org.apache.log4j.Logger;
import org.apache.qpid.common.QpidProperties;
@@ -54,6 +55,7 @@ import org.apache.qpid.server.model.LifetimePolicy;
import org.apache.qpid.server.model.Model;
import org.apache.qpid.server.model.Plugin;
import org.apache.qpid.server.model.Port;
+import org.apache.qpid.server.model.Protocol;
import org.apache.qpid.server.model.State;
import org.apache.qpid.server.model.Statistics;
import org.apache.qpid.server.model.TrustStore;
@@ -182,6 +184,7 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat
private final AccessControlProviderFactory _accessControlProviderFactory;
private final PortFactory _portFactory;
private final SecurityManager _securityManager;
+ private final AtomicReference<State> _state;
private final Collection<String> _supportedVirtualHostStoreTypes;
private Collection<String> _supportedBrokerStoreTypes;
@@ -210,15 +213,57 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat
_supportedVirtualHostStoreTypes = new MessageStoreCreator().getStoreTypes();
_supportedBrokerStoreTypes = new BrokerConfigurationStoreCreator().getStoreTypes();
_brokerStore = brokerStore;
+
+ _state = new AtomicReference<State>(State.INITIALISING);
if (_brokerOptions.isManagementMode())
{
- AuthenticationManager authManager = new SimpleAuthenticationManager(BrokerOptions.MANAGEMENT_MODE_USER_NAME, _brokerOptions.getManagementModePassword());
- AuthenticationProvider authenticationProvider = new SimpleAuthenticationProviderAdapter(UUID.randomUUID(), this,
- authManager, Collections.<String, Object> emptyMap(), Collections.<String> emptySet());
- _managementAuthenticationProvider = authenticationProvider;
+ createManagementModeConfiguredObjects();
+ }
+ }
+
+ private void createManagementModeConfiguredObjects()
+ {
+ AuthenticationManager authManager = new SimpleAuthenticationManager(BrokerOptions.MANAGEMENT_MODE_USER_NAME, _brokerOptions.getManagementModePassword());
+ AuthenticationProvider authenticationProvider = new SimpleAuthenticationProviderAdapter(UUID.randomUUID(), this,
+ authManager, Collections.<String, Object> emptyMap(), Collections.<String> emptySet());
+ _managementAuthenticationProvider = authenticationProvider;
+
+ int httpPortOverride = _brokerOptions.getManagementModeHttpPortOverride();
+ if (httpPortOverride != 0)
+ {
+ createPort(createCLIPortAttributes(httpPortOverride, Protocol.HTTP));
+ }
+
+ int managementModeRmiPortOverride = _brokerOptions.getManagementModeRmiPortOverride();
+ int managementModeJmxPortOverride = _brokerOptions.getManagementModeJmxPortOverride();
+ if (managementModeRmiPortOverride != 0)
+ {
+ createPort(createCLIPortAttributes(managementModeRmiPortOverride, Protocol.RMI));
+ if (managementModeJmxPortOverride == 0)
+ {
+ createPort(createCLIPortAttributes(managementModeRmiPortOverride + 100, Protocol.JMX_RMI));
+ }
+ }
+ if (managementModeJmxPortOverride != 0)
+ {
+ createPort(createCLIPortAttributes(managementModeJmxPortOverride, Protocol.JMX_RMI));
}
}
+ private Map<String, Object> createCLIPortAttributes(int port, Protocol protocol)
+ {
+ Map<String, Object> attributes = new HashMap<String, Object>();
+ attributes.put(Port.PORT, port);
+ attributes.put(Port.PROTOCOLS, Collections.singleton(protocol));
+ attributes.put(Port.NAME, PortAdapter.MANAGEMENT_MODE_PORT_PREFIX + protocol.name());
+ if (protocol != Protocol.RMI)
+ {
+ //TODO: analyze whether setting of auth provider is still needed
+ //attributes.put(Port.AUTHENTICATION_PROVIDER, MANAGEMENT_MODE_AUTH_PROVIDER);
+ }
+ return attributes;
+ }
+
public Collection<VirtualHost> getVirtualHosts()
{
synchronized(_vhostAdapters)
@@ -291,6 +336,7 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat
private VirtualHost createVirtualHost(final Map<String, Object> attributes)
throws AccessControlException, IllegalArgumentException
{
+
final VirtualHostAdapter virtualHostAdapter = new VirtualHostAdapter(UUID.randomUUID(), attributes, this,
_statisticsGatherer, getTaskExecutor());
addVirtualHost(virtualHostAdapter);
@@ -300,7 +346,7 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat
SecurityManager.setAccessChecksDisabled(true);
try
{
- virtualHostAdapter.setDesiredState(State.INITIALISING, State.ACTIVE);
+ virtualHostAdapter.attainDesiredState();
}
finally
{
@@ -333,7 +379,7 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat
public State getActualState()
{
- return null; //TODO
+ return _state.get();
}
@@ -460,16 +506,7 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat
{
Port port = _portFactory.createPort(UUID.randomUUID(), this, attributes);
addPort(port);
-
- //1. AMQP ports are disabled during ManagementMode.
- //2. The management plugins can currently only start ports at broker startup and
- // not when they are newly created via the management interfaces.
- //3. When active ports are deleted, or their port numbers updated, the broker must be
- // restarted for it to take effect so we can't reuse port numbers until it is.
- boolean quiesce = isManagementMode() || !(port instanceof AmqpPortAdapter) || isPreviouslyUsedPortNumber(port);
-
- port.setDesiredState(State.INITIALISING, quiesce ? State.QUIESCED : State.ACTIVE);
-
+ port.attainDesiredState();
return port;
}
@@ -513,9 +550,7 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat
addAccessControlProvider(accessControlProvider);
}
- boolean quiesce = isManagementMode() ;
- accessControlProvider.setDesiredState(State.INITIALISING, quiesce ? State.QUIESCED : State.ACTIVE);
-
+ accessControlProvider.attainDesiredState();
return accessControlProvider;
}
@@ -565,7 +600,7 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat
private AuthenticationProvider createAuthenticationProvider(Map<String, Object> attributes)
{
AuthenticationProvider authenticationProvider = _authenticationProviderFactory.create(UUID.randomUUID(), this, attributes);
- authenticationProvider.setDesiredState(State.INITIALISING, State.ACTIVE);
+ authenticationProvider.attainDesiredState();
addAuthenticationProvider(authenticationProvider);
return authenticationProvider;
}
@@ -597,7 +632,7 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat
private GroupProvider createGroupProvider(Map<String, Object> attributes)
{
GroupProvider groupProvider = _groupProviderFactory.create(UUID.randomUUID(), this, attributes);
- groupProvider.setDesiredState(State.INITIALISING, State.ACTIVE);
+ groupProvider.attainDesiredState();
addGroupProvider(groupProvider);
return groupProvider;
}
@@ -861,75 +896,17 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat
@Override
public boolean setState(State currentState, State desiredState)
{
- if (desiredState == State.ACTIVE)
+ if ((desiredState == State.ACTIVE || desiredState == State.STOPPED) && _state.compareAndSet(getActualState(), desiredState))
{
- changeState(_groupProviders, currentState, State.ACTIVE, false);
- changeState(_authenticationProviders, currentState, State.ACTIVE, false);
- changeState(_accessControlProviders, currentState, State.ACTIVE, false);
-
- CurrentActor.set(new BrokerActor(getRootMessageLogger()));
- try
- {
- changeState(_vhostAdapters, currentState, State.ACTIVE, false);
- }
- finally
- {
- CurrentActor.remove();
- }
-
- changeState(_portAdapters, currentState,State.ACTIVE, false);
- changeState(_plugins, currentState,State.ACTIVE, false);
-
- if (isManagementMode())
- {
- CurrentActor.get().message(BrokerMessages.MANAGEMENT_MODE(BrokerOptions.MANAGEMENT_MODE_USER_NAME, _brokerOptions.getManagementModePassword()));
- }
- return true;
- }
- else if (desiredState == State.STOPPED)
- {
- changeState(_plugins, currentState,State.STOPPED, true);
- changeState(_portAdapters, currentState, State.STOPPED, true);
- changeState(_vhostAdapters,currentState, State.STOPPED, true);
- changeState(_authenticationProviders, currentState, State.STOPPED, true);
- changeState(_groupProviders, currentState, State.STOPPED, true);
return true;
}
return false;
}
- private void changeState(Map<?, ? extends ConfiguredObject> configuredObjectMap, State currentState, State desiredState, boolean swallowException)
+ @Override
+ public State getDesiredState()
{
- synchronized(configuredObjectMap)
- {
- Collection<? extends ConfiguredObject> adapters = configuredObjectMap.values();
- for (ConfiguredObject configuredObject : adapters)
- {
- if (State.ACTIVE.equals(desiredState) && State.QUIESCED.equals(configuredObject.getActualState()))
- {
- if (LOGGER.isDebugEnabled())
- {
- LOGGER.debug(configuredObject + " cannot be activated as it is " +State.QUIESCED);
- }
- continue;
- }
- try
- {
- configuredObject.setDesiredState(currentState, desiredState);
- }
- catch(RuntimeException e)
- {
- if (swallowException)
- {
- LOGGER.error("Failed to stop " + configuredObject, e);
- }
- else
- {
- throw e;
- }
- }
- }
- }
+ return State.ACTIVE;
}
@Override
@@ -1258,8 +1235,70 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat
}
}
- private boolean isPreviouslyUsedPortNumber(Port port)
+ @Override
+ public boolean isPreviouslyUsedPortNumber(int port)
+ {
+ return _stillInUsePortNumbers.containsValue(port);
+ }
+
+ @Override
+ public void attainDesiredState()
+ {
+ attainDesiredState(_groupProviders.values());
+ attainDesiredState(_authenticationProviders.values());
+ attainDesiredState(_accessControlProviders.values());
+ CurrentActor.set(new BrokerActor(getRootMessageLogger()));
+ try
+ {
+ attainDesiredState(_vhostAdapters.values());
+ }
+ finally
+ {
+ CurrentActor.remove();
+ }
+ attainDesiredState(_portAdapters.values());
+ attainDesiredState(_plugins.values());
+
+ if (isManagementMode())
+ {
+ CurrentActor.get().message(BrokerMessages.MANAGEMENT_MODE(BrokerOptions.MANAGEMENT_MODE_USER_NAME, _brokerOptions.getManagementModePassword()));
+ }
+
+ super.attainDesiredState();
+ }
+
+ private <T extends ConfiguredObject> void attainDesiredState(Collection<T> children)
{
- return _stillInUsePortNumbers.containsValue(port.getPort());
+ for (T child : children)
+ {
+ child.attainDesiredState();
+ }
}
+
+ @Override
+ public void close()
+ {
+ closeAll(_plugins.values());
+ closeAll(_portAdapters.values());
+ closeAll(_vhostAdapters.values());
+ closeAll(_authenticationProviders.values());
+ closeAll(_groupProviders.values());
+ closeAll(_accessControlProviders.values());
+ }
+
+ private <T extends ConfiguredObject> void closeAll(Collection<T> children)
+ {
+ for (T child : children)
+ {
+ try
+ {
+ child.close();
+ }
+ catch(Exception e)
+ {
+ LOGGER.warn("Failed to close " + child, e);
+ }
+ }
+ }
+
}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/FileSystemPreferencesProvider.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/FileSystemPreferencesProvider.java
index 39f0017fc3..4e6952fff5 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/FileSystemPreferencesProvider.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/FileSystemPreferencesProvider.java
@@ -227,6 +227,8 @@ public class FileSystemPreferencesProvider extends AbstractAdapter implements Pr
finally
{
_store.delete();
+ // TODO this should change to that the authentication provider listens for the delete and
+ // responds accordingly.
_authenticationProvider.setPreferencesProvider(null);
}
return true;
@@ -389,6 +391,13 @@ public class FileSystemPreferencesProvider extends AbstractAdapter implements Pr
_store.createIfNotExist();
}
+ @Override
+ public void close()
+ {
+ _store.close();
+ }
+
+
public static class FileSystemPreferencesStore
{
private final ObjectMapper _objectMapper;
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/GroupProviderAdapter.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/GroupProviderAdapter.java
index 9323606c83..0855fb289e 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/GroupProviderAdapter.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/GroupProviderAdapter.java
@@ -378,6 +378,19 @@ public class GroupProviderAdapter extends AbstractAdapter implements
throw new UnsupportedOperationException("Changing attributes on group providers is not supported.");
}
+
+ @Override
+ public State getDesiredState()
+ {
+ return State.ACTIVE;
+ }
+
+ @Override
+ public void close()
+ {
+ _groupManager.close();
+ }
+
private class GroupAdapter extends AbstractAdapter implements Group
{
private final String _group;
@@ -706,5 +719,4 @@ public class GroupProviderAdapter extends AbstractAdapter implements
}
}
-
}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java
index 882335626d..d64a50ffca 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java
@@ -26,6 +26,7 @@ import java.security.AccessControlException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -33,9 +34,11 @@ import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
+import org.apache.log4j.Logger;
import org.apache.qpid.server.model.AuthenticationProvider;
import org.apache.qpid.server.model.Broker;
import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.ConfiguredObjectFinder;
import org.apache.qpid.server.model.Connection;
import org.apache.qpid.server.model.KeyStore;
import org.apache.qpid.server.model.LifetimePolicy;
@@ -55,6 +58,8 @@ import org.apache.qpid.server.configuration.updater.TaskExecutor;
public class PortAdapter extends AbstractAdapter implements Port
{
+ private static final Logger LOGGER = Logger.getLogger(PortAdapter.class);
+
@SuppressWarnings("serial")
public static final Map<String, Type> ATTRIBUTE_TYPES = Collections.unmodifiableMap(new HashMap<String, Type>(){{
put(NAME, String.class);
@@ -73,6 +78,8 @@ public class PortAdapter extends AbstractAdapter implements Port
put(AUTHENTICATION_PROVIDER, String.class);
}});
+ static final String MANAGEMENT_MODE_PORT_PREFIX = "MANAGEMENT-MODE-PORT-";
+
private final Broker _broker;
private AuthenticationProvider _authenticationProvider;
private AtomicReference<State> _state;
@@ -81,7 +88,6 @@ public class PortAdapter extends AbstractAdapter implements Port
{
super(id, defaults, MapValueConverter.convert(attributes, ATTRIBUTE_TYPES), taskExecutor);
_broker = broker;
- State state = MapValueConverter.getEnumAttribute(State.class, STATE, attributes, State.INITIALISING);
Collection<Protocol> protocols = getProtocols();
boolean rmiRegistry = protocols != null && protocols.contains(Protocol.RMI);
@@ -100,8 +106,9 @@ public class PortAdapter extends AbstractAdapter implements Port
}
}
- _state = new AtomicReference<State>(state);
+ _state = new AtomicReference<State>(State.INITIALISING);
addParent(Broker.class, broker);
+
}
@Override
@@ -564,7 +571,61 @@ public class PortAdapter extends AbstractAdapter implements Port
@Override
public String toString()
{
- return getClass().getSimpleName() + " [id=" + getId() + ", name=" + getName() + ", port=" + getPort() + "]";
+ return getClass().getSimpleName() + " [id=" + getId() + ", name=" + getName() + ", port=" + getPort() + ", state=" + getActualState() + "]";
+ }
+
+ @Override
+ public void close()
+ {
+ onStop();
+ }
+
+ @Override
+ public void attainDesiredState()
+ {
+ State desiredState = shouldQuiesce() ? State.QUIESCED : getDesiredState();
+ setDesiredState(getActualState(), desiredState);
}
+ protected boolean shouldQuiesce()
+ {
+ if (getName().startsWith(MANAGEMENT_MODE_PORT_PREFIX))
+ {
+ // port is CLI port therefore it should be active
+ return false;
+ }
+
+ if ( _broker.isPreviouslyUsedPortNumber(getPort()))
+ {
+ // always quiesce port with port number which was previously used
+ return true;
+ }
+
+ if (_broker.isManagementMode())
+ {
+ EnumSet<Protocol> managementProtocols = EnumSet.of(Protocol.HTTP, Protocol.JMX_RMI, Protocol.RMI);
+ Collection<Port> ports = _broker.getPorts();
+ Collection<Protocol> protocols = getProtocols();
+ for (Protocol protocol : protocols)
+ {
+ Port managementModePort = ConfiguredObjectFinder.findConfiguredObjectByName(ports, MANAGEMENT_MODE_PORT_PREFIX + protocol.name());
+ if (managementProtocols.contains(protocol) && managementModePort != null)
+ {
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Port " + this + " is overridden by management port " + managementModePort);
+ }
+
+ // quiesce the port if exists overridden CLI port for the protocol
+ return true;
+ }
+ }
+ }
+ else
+ {
+ // quiesce non-amqp ports created after broker start-up
+ return _broker.getActualState() == State.ACTIVE;
+ }
+ return false;
+ }
}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java
index c43dc34d2f..9016c2ac66 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java
@@ -20,6 +20,8 @@
*/
package org.apache.qpid.server.model.adapter;
+import static org.apache.qpid.server.model.VirtualHost.ID;
+
import java.io.File;
import java.lang.reflect.Type;
import java.security.AccessControlException;
@@ -34,6 +36,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
+import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.configuration.CompositeConfiguration;
import org.apache.commons.configuration.ConfigurationException;
@@ -44,6 +47,9 @@ import org.apache.qpid.AMQException;
import org.apache.qpid.server.configuration.IllegalConfigurationException;
import org.apache.qpid.server.configuration.VirtualHostConfiguration;
import org.apache.qpid.server.configuration.XmlConfigurationUtilities.MyConfiguration;
+import org.apache.qpid.server.configuration.updater.TaskExecutor;
+import org.apache.qpid.server.logging.actors.BrokerActor;
+import org.apache.qpid.server.logging.actors.CurrentActor;
import org.apache.qpid.server.message.MessageInstance;
import org.apache.qpid.server.message.ServerMessage;
import org.apache.qpid.server.model.Broker;
@@ -56,16 +62,19 @@ import org.apache.qpid.server.model.Port;
import org.apache.qpid.server.model.Protocol;
import org.apache.qpid.server.model.Queue;
import org.apache.qpid.server.model.QueueType;
+import org.apache.qpid.server.model.ReplicationNode;
import org.apache.qpid.server.model.State;
import org.apache.qpid.server.model.Statistics;
import org.apache.qpid.server.model.UUIDGenerator;
import org.apache.qpid.server.model.VirtualHost;
import org.apache.qpid.server.model.VirtualHostAlias;
-import org.apache.qpid.server.configuration.updater.TaskExecutor;
import org.apache.qpid.server.plugin.ExchangeType;
+import org.apache.qpid.server.plugin.ReplicationNodeFactory;
+import org.apache.qpid.server.plugin.VirtualHostFactory;
import org.apache.qpid.server.protocol.AMQConnectionModel;
import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.queue.AMQQueueFactory;
+import org.apache.qpid.server.replication.ReplicationGroupListener;
import org.apache.qpid.server.security.SecurityManager;
import org.apache.qpid.server.security.access.Operation;
import org.apache.qpid.server.security.auth.AuthenticatedPrincipal;
@@ -74,15 +83,15 @@ import org.apache.qpid.server.store.MessageStore;
import org.apache.qpid.server.txn.LocalTransaction;
import org.apache.qpid.server.txn.ServerTransaction;
import org.apache.qpid.server.util.MapValueConverter;
-import org.apache.qpid.server.plugin.VirtualHostFactory;
import org.apache.qpid.server.virtualhost.ExchangeExistsException;
import org.apache.qpid.server.virtualhost.ReservedExchangeNameException;
import org.apache.qpid.server.virtualhost.UnknownExchangeException;
+import org.apache.qpid.server.virtualhost.VirtualHostAttributeRecoveryListener;
import org.apache.qpid.server.virtualhost.VirtualHostListener;
import org.apache.qpid.server.virtualhost.VirtualHostRegistry;
import org.apache.qpid.server.virtualhost.plugins.QueueExistsException;
-public final class VirtualHostAdapter extends AbstractAdapter implements VirtualHost, VirtualHostListener
+public final class VirtualHostAdapter extends AbstractAdapter implements VirtualHost, VirtualHostListener, ReplicationGroupListener, VirtualHostAttributeRecoveryListener
{
private static final Logger LOGGER = Logger.getLogger(VirtualHostAdapter.class);
@@ -93,10 +102,23 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual
put(STORE_PATH, String.class);
put(STORE_TYPE, String.class);
put(CONFIG_PATH, String.class);
- put(STATE, State.class);
+ put(DESIRED_STATE, State.class);
+ put(REMOTE_REPLICATION_NODE_MONITOR_INTERVAL, Long.class);
+ put(QUIESCE_ON_MASTER_CHANGE, Boolean.class);
}});
- private org.apache.qpid.server.virtualhost.VirtualHost _virtualHost;
+ public static final List<String> UPDATABLE_ATTRIBUTES = Collections.unmodifiableList(Arrays.asList(DESIRED_STATE));
+
+ private static final long DEFAULT_REMOTE_REPLICATION_NODE_MONITOR_INTERVAL = 10000L;
+
+ @SuppressWarnings("serial")
+ static final Map<String, Object> DEFAULTS = new HashMap<String, Object>()
+ {{
+ put(REMOTE_REPLICATION_NODE_MONITOR_INTERVAL, DEFAULT_REMOTE_REPLICATION_NODE_MONITOR_INTERVAL);
+ put(QUIESCE_ON_MASTER_CHANGE, false);
+ }};
+
+ private volatile org.apache.qpid.server.virtualhost.VirtualHost _virtualHost;
private final Map<AMQConnectionModel, ConnectionAdapter> _connectionAdapters =
new HashMap<AMQConnectionModel, ConnectionAdapter>();
@@ -111,13 +133,22 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual
private final List<VirtualHostAlias> _aliases = new ArrayList<VirtualHostAlias>();
private StatisticsGatherer _brokerStatisticsGatherer;
+ private final List<ReplicationNode> _replicationNodes = new ArrayList<ReplicationNode>();
+
+ private final TaskExecutor _taskExecutor;
+ private final AtomicReference<State> _state;
+
public VirtualHostAdapter(UUID id, Map<String, Object> attributes, Broker broker, StatisticsGatherer brokerStatisticsGatherer, TaskExecutor taskExecutor)
{
- super(id, null, MapValueConverter.convert(attributes, ATTRIBUTE_TYPES, false), taskExecutor, false);
+ super(id, DEFAULTS, MapValueConverter.convert(attributes, ATTRIBUTE_TYPES, false), taskExecutor, false);
+ _taskExecutor = taskExecutor;
_broker = broker;
_brokerStatisticsGatherer = brokerStatisticsGatherer;
- validateAttributes();
addParent(Broker.class, broker);
+ _state = new AtomicReference<State>(State.INITIALISING);
+ validateAttributes();
+ _virtualHost = createVirtualHostImpl();
+ _statistics = new VirtualHostStatisticsAdapter(_virtualHost);
}
private void validateAttributes()
@@ -142,29 +173,12 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual
{
validateAttributes(type);
}
- }/*
- else
- {
- if (type != null)
- {
- invalidAttributes = true;
- }
+ }
- }*/
if (invalidAttributes)
{
throw new IllegalConfigurationException("Please specify either the 'configPath' attribute or 'type' attributes");
}
-
- // pre-load the configuration in order to validate
- try
- {
- createVirtualHostConfiguration(name);
- }
- catch(ConfigurationException e)
- {
- throw new IllegalConfigurationException("Failed to validate configuration", e);
- }
}
private void validateAttributes(String type)
@@ -262,6 +276,15 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual
}
}
+ @Override
+ public Collection<ReplicationNode> getReplicationNodes()
+ {
+ synchronized (_replicationNodes)
+ {
+ return Collections.unmodifiableList(_replicationNodes);
+ }
+ }
+
public Exchange createExchange(Map<String, Object> attributes)
throws AccessControlException, IllegalArgumentException
@@ -501,18 +524,14 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual
@Override
public State getActualState()
{
- if (_virtualHost == null)
+ org.apache.qpid.server.virtualhost.VirtualHost virtualHost = _virtualHost;
+ if (virtualHost == null)
{
- State state = (State)super.getAttribute(STATE);
- if (state == null)
- {
- return State.INITIALISING;
- }
- return state;
+ return _state.get();
}
else
{
- org.apache.qpid.server.virtualhost.State implementationState = _virtualHost.getState();
+ org.apache.qpid.server.virtualhost.State implementationState = virtualHost.getState();
switch(implementationState)
{
case INITIALISING:
@@ -520,11 +539,13 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual
case ACTIVE:
return State.ACTIVE;
case PASSIVE:
- return State.REPLICA;
+ return State.UNAVAILABLE;
case STOPPED:
return State.STOPPED;
case ERRORED:
return State.ERRORED;
+ case QUIESCED:
+ return State.QUIESCED;
default:
throw new IllegalStateException("Unsupported state:" + implementationState);
}
@@ -569,6 +590,7 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual
return _statistics;
}
+ @SuppressWarnings("unchecked")
@Override
public <C extends ConfiguredObject> Collection<C> getChildren(Class<C> clazz)
{
@@ -588,12 +610,17 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual
{
return (Collection<C>) getAliases();
}
+ else if (clazz == ReplicationNode.class)
+ {
+ return (Collection<C>)getReplicationNodes();
+ }
else
{
return Collections.emptySet();
}
}
+ @SuppressWarnings("unchecked")
@Override
public <C extends ConfiguredObject> C addChild(Class<C> childClass, Map<String, Object> attributes, ConfiguredObject... otherParents)
{
@@ -621,6 +648,11 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual
{
throw new UnsupportedOperationException();
}
+ else if(childClass == ReplicationNode.class)
+ {
+ return (C)createReplicationNode(attributes);
+ }
+
throw new IllegalArgumentException("Cannot create a child of class " + childClass.getSimpleName());
}
@@ -901,6 +933,7 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual
private Object getAttributeFromVirtualHostImplementation(String name)
{
+ MessageStore messageStore = _virtualHost.getMessageStore();
if(SUPPORTED_EXCHANGE_TYPES.equals(name))
{
List<String> types = new ArrayList<String>();
@@ -934,13 +967,13 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual
{
return _virtualHost.getConfiguration().getFlowResumeCapacity();
}
- else if(STORE_TYPE.equals(name))
+ else if(STORE_TYPE.equals(name) && messageStore != null)
{
- return _virtualHost.getMessageStore().getStoreType();
+ return messageStore.getStoreType();
}
- else if(STORE_PATH.equals(name))
+ else if(STORE_PATH.equals(name) && messageStore != null && messageStore.getStoreLocation() != null)
{
- return _virtualHost.getMessageStore().getStoreLocation();
+ return messageStore.getStoreLocation();
}
else if(STORE_TRANSACTION_IDLE_TIMEOUT_CLOSE.equals(name))
{
@@ -1046,29 +1079,40 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual
@Override
protected boolean setState(State currentState, State desiredState)
{
+ State actualState = getActualState();
if (desiredState == State.ACTIVE)
{
- try
- {
- activate();
- }
- catch(RuntimeException e)
+ if( _state.compareAndSet(State.INITIALISING, State.ACTIVE) || _state.compareAndSet(State.STOPPED, State.ACTIVE)
+ || _state.compareAndSet(State.QUIESCED, State.ACTIVE) || _state.compareAndSet(State.ERRORED, State.ACTIVE))
+ {
+ try
+ {
+ activate();
+ }
+ catch(Exception e)
+ {
+ _state.set(State.ERRORED);
+ if (_broker.isManagementMode())
+ {
+ LOGGER.warn("Failed to activate virtual host: " + getName(), e);
+ }
+ else
+ {
+ throw new IllegalStateException("Failed to activate virtual host: " + getName(), e);
+ }
+ }
+ return true;
+ }
+ else
{
- changeAttribute(STATE, State.INITIALISING, State.ERRORED);
- if (_broker.isManagementMode())
- {
- LOGGER.warn("Failed to activate virtual host: " + getName(), e);
- }
- else
- {
- throw e;
- }
+ throw new IllegalStateException("Cannot activate host with state " + actualState);
}
- return true;
}
else if (desiredState == State.STOPPED)
{
- if (_virtualHost != null)
+ if (_state.compareAndSet(State.ACTIVE, State.STOPPED) || _state.compareAndSet(State.INITIALISING, State.STOPPED)
+ || _state.compareAndSet(State.QUIESCED, State.STOPPED) || _state.compareAndSet(State.UNAVAILABLE, State.STOPPED)
+ || _state.compareAndSet(State.ERRORED, State.STOPPED))
{
try
{
@@ -1076,12 +1120,18 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual
}
finally
{
- _broker.getVirtualHostRegistry().unregisterVirtualHost(_virtualHost);
+ _queueAdapters.clear();
+ _exchangeAdapters.clear();
+ _aliases.clear();
}
+ return true;
+ }
+ else
+ {
+ throw new IllegalStateException("Cannot stop host with state " + actualState);
}
- return true;
}
- else if (desiredState == State.DELETED)
+ else if (desiredState == State.DELETED && actualState != State.DELETED)
{
String hostName = getName();
@@ -1089,13 +1139,15 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual
{
throw new IntegrityViolationException("Cannot delete default virtual host '" + hostName + "'");
}
- if (_virtualHost != null)
+
+ if (actualState == State.ACTIVE)
{
- if (_virtualHost.getState() == org.apache.qpid.server.virtualhost.State.ACTIVE)
- {
- setDesiredState(currentState, State.STOPPED);
- }
+ setDesiredState(actualState, State.STOPPED);
+ }
+ close();
+ if (_virtualHost != null)
+ {
MessageStore ms = _virtualHost.getMessageStore();
if (ms != null)
{
@@ -1111,19 +1163,35 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual
_virtualHost = null;
}
- setAttribute(VirtualHost.STATE, getActualState(), State.DELETED);
+
+ _state.set(State.DELETED);
+
return true;
}
+ else if (desiredState == State.QUIESCED)
+ {
+ if( _state.compareAndSet(State.INITIALISING, State.QUIESCED) || _state.compareAndSet(State.STOPPED, State.QUIESCED)
+ || _state.compareAndSet(State.ACTIVE, State.QUIESCED) || _state.compareAndSet(State.ERRORED, State.QUIESCED))
+ {
+ _virtualHost.quiesce();
+ return true;
+ }
+ else
+ {
+ throw new IllegalStateException("Cannot quiesce host with state " + actualState);
+ }
+ }
return false;
}
- private void activate()
+ private org.apache.qpid.server.virtualhost.VirtualHost createVirtualHostImpl()
{
+ org.apache.qpid.server.virtualhost.VirtualHost virtualHost = null;
VirtualHostRegistry virtualHostRegistry = _broker.getVirtualHostRegistry();
String virtualHostName = getName();
try
{
- VirtualHostConfiguration configuration = createVirtualHostConfiguration(virtualHostName);
+ VirtualHostConfiguration configuration = createVirtualHostConfiguration(virtualHostName, this);
String type = configuration.getType();
final VirtualHostFactory factory = VirtualHostFactory.FACTORIES.get(type);
if(factory == null)
@@ -1132,11 +1200,19 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual
}
else
{
- _virtualHost = factory.createVirtualHost(_broker.getVirtualHostRegistry(),
+ CurrentActor.set(new BrokerActor(_broker.getRootMessageLogger()));
+ try
+ {
+ virtualHost = factory.createVirtualHost(_broker.getVirtualHostRegistry(),
_brokerStatisticsGatherer,
_broker.getSecurityManager(),
configuration,
this);
+ }
+ finally
+ {
+ CurrentActor.remove();
+ }
}
}
catch (Exception e)
@@ -1144,10 +1220,16 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual
throw new RuntimeException("Failed to create virtual host " + virtualHostName, e);
}
- virtualHostRegistry.registerVirtualHost(_virtualHost);
+ virtualHostRegistry.registerVirtualHost(virtualHost);
+ virtualHost.addVirtualHostListener(this);
+ virtualHost.setReplicationGroupListener(this);
+ virtualHost.setVirtualHostAttributeRecoveryListener(this);
+ return virtualHost;
+ }
- _statistics = new VirtualHostStatisticsAdapter(_virtualHost);
- _virtualHost.addVirtualHostListener(this);
+ private void activate() throws Exception
+ {
+ _virtualHost.activate();
populateQueues();
populateExchanges();
@@ -1163,12 +1245,13 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual
}
}
- private VirtualHostConfiguration createVirtualHostConfiguration(String virtualHostName) throws ConfigurationException
+ private VirtualHostConfiguration createVirtualHostConfiguration(String virtualHostName, ReplicationGroupListener listener) throws ConfigurationException
{
VirtualHostConfiguration configuration;
String configurationFile = (String)getAttribute(CONFIG_PATH);
if (configurationFile == null)
{
+ LOGGER.debug("Creating virtual host configuration from the attributes");
final MyConfiguration basicConfiguration = new MyConfiguration();
PropertiesConfiguration config = new PropertiesConfiguration();
final String type = (String) getAttribute(TYPE);
@@ -1190,6 +1273,10 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual
}
else
{
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Creating virtual host configuration from configuration file " + configurationFile);
+ }
if (!new File(configurationFile).exists())
{
throw new IllegalConfigurationException("Configuration file '" + configurationFile + "' does not exist");
@@ -1205,7 +1292,15 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual
changeAttribute(entry.getKey(), getAttribute(entry.getKey()), entry.getValue());
}
}
-
+ ReplicationNode replicationNode = factory.createReplicationNode(configuration.getConfig(), this);
+ if (listener != null && replicationNode != null)
+ {
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Replication node " + replicationNode);
+ }
+ listener.onReplicationNodeRecovered(replicationNode);
+ }
}
return configuration;
}
@@ -1223,9 +1318,67 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual
}
@Override
+ public TaskExecutor getTaskExecutor()
+ {
+ return _taskExecutor;
+ }
+
+ @Override
protected void changeAttributes(Map<String, Object> attributes)
{
- throw new UnsupportedOperationException("Changing attributes on virtualhosts is not supported.");
+ checkWhetherAttributeChangeIsSupported(attributes);
+ Map<String, Object> convertedAttributes = MapValueConverter.convert(attributes, ATTRIBUTE_TYPES);
+ super.changeAttributes(convertedAttributes);
+ }
+
+ private void checkWhetherAttributeChangeIsSupported(Map<String, Object> attributes)
+ {
+ for (String attributeName : attributes.keySet())
+ {
+ // the name is appended into attributes map in REST layer
+ if (attributeName.equals(NAME) && getName().equals(attributes.get(NAME)))
+ {
+ continue;
+ }
+
+ if (!UPDATABLE_ATTRIBUTES.contains(attributeName))
+ {
+ throw new IllegalConfigurationException("Cannot change value of attribute " + attributeName);
+ }
+ }
+ }
+
+ @Override
+ protected boolean changeAttribute(final String name, final Object expected, final Object desired)
+ {
+ if (DESIRED_STATE.equals(name))
+ {
+ return changeDesiredStateAttribute(expected, desired);
+ }
+ return super.changeAttribute(name, expected, desired);
+ }
+
+ private boolean changeDesiredStateAttribute(final Object expected, final Object desired)
+ {
+ State expectedState = (State)expected;
+ State desiredState = (State)desired;
+
+ if (desiredState == State.UNAVAILABLE || desired == State.ERRORED)
+ {
+ throw new IllegalConfigurationException("Changing state to " + State.UNAVAILABLE + " or " + State.ERRORED + " is not allowed");
+ }
+
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug(String.format("Change state of virtual host '%s' from '%s' to '%s'", getName(), expectedState, desiredState));
+ }
+
+ if (super.changeAttribute(DESIRED_STATE, expected, desired))
+ {
+ setDesiredState(expectedState, desiredState);
+ return true;
+ }
+ return false ;
}
@Override
@@ -1257,4 +1410,92 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual
throw new AccessControlException("Setting of virtual host attributes is denied");
}
}
+
+ @Override
+ public void onReplicationNodeRecovered(ReplicationNode node)
+ {
+ //TODO: should we be adding ConfigurationChangeListener to node?
+ _replicationNodes.add(node);
+ }
+
+ @Override
+ public void onReplicationNodeAddedToGroup(ReplicationNode node)
+ {
+ _replicationNodes.add(node);
+ }
+
+ @Override
+ public void onReplicationNodeRemovedFromGroup(ReplicationNode node)
+ {
+ _replicationNodes.remove(node);
+ }
+
+ public void recoverChild(ConfiguredObject configuredObject)
+ {
+ if (configuredObject instanceof ReplicationNode)
+ {
+ ReplicationNode node = (ReplicationNode)configuredObject;
+ if (!_replicationNodes.isEmpty())
+ {
+ throw new IllegalStateException("Replication node cannot be recovered because virtual host already contains replication node");
+ }
+ onReplicationNodeRecovered(node);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Cannot recover child of type :" + configuredObject.getClass().getName());
+ }
+ }
+
+ private ReplicationNode createReplicationNode(Map<String, Object> attributes)
+ {
+ ReplicationNode node = null;
+
+ String type = getType();
+ ReplicationNodeFactory factory = ReplicationNodeFactory.FACTORIES.get(type);
+ if (factory == null)
+ {
+ throw new IllegalConfigurationException("Cannot find replication node factory for type " + type);
+ }
+
+ String groupName = MapValueConverter.getStringAttribute(ReplicationNode.GROUP_NAME, attributes);
+ String nodeName = MapValueConverter.getStringAttribute(ReplicationNode.NAME, attributes);
+
+ synchronized (_replicationNodes)
+ {
+ if (!_replicationNodes.isEmpty())
+ {
+ throw new IllegalStateException("Replication node cannot be created because virtual host already contains replication node");
+ }
+ node = factory.createInstance(UUIDGenerator.generateReplicationNodeId(groupName, nodeName), attributes, this);
+ node.attainDesiredState();
+
+ _replicationNodes.add(node);
+ }
+ //TODO: make VirtualHost a ConfigurationChangeListener and add it to node to listen for delete events
+ return node;
+ }
+
+ @Override
+ public void close()
+ {
+ if (_virtualHost != null)
+ {
+ try
+ {
+ _virtualHost.close();
+ }
+ finally
+ {
+ _broker.getVirtualHostRegistry().unregisterVirtualHost(_virtualHost);
+ }
+ }
+ }
+
+ @Override
+ public void attributesRecovered(Map<String, Object> attributes)
+ {
+ changeAttributes(attributes);
+ }
+
}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/plugin/PluggableFactoryLoader.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/plugin/PluggableFactoryLoader.java
index 7a8b7c0c65..d1389be713 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/plugin/PluggableFactoryLoader.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/plugin/PluggableFactoryLoader.java
@@ -31,11 +31,17 @@ public class PluggableFactoryLoader<T extends Pluggable>
private final Map<String, T> _factoriesMap;
private final Set<String> _types;
+
public PluggableFactoryLoader(Class<T> factoryClass)
{
+ this(factoryClass, true);
+ }
+
+ public PluggableFactoryLoader(Class<T> factoryClass, boolean atLeastOnce)
+ {
Map<String, T> fm = new HashMap<String, T>();
QpidServiceLoader<T> qpidServiceLoader = new QpidServiceLoader<T>();
- Iterable<T> factories = qpidServiceLoader.atLeastOneInstanceOf(factoryClass);
+ Iterable<T> factories = atLeastOnce? qpidServiceLoader.atLeastOneInstanceOf(factoryClass) : qpidServiceLoader.instancesOf(factoryClass);
for (T factory : factories)
{
String descriptiveType = factory.getType();
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/plugin/ReplicationNodeFactory.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/plugin/ReplicationNodeFactory.java
new file mode 100644
index 0000000000..e2a0a1ff64
--- /dev/null
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/plugin/ReplicationNodeFactory.java
@@ -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.
+ *
+ */
+package org.apache.qpid.server.plugin;
+
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.qpid.server.model.ReplicationNode;
+import org.apache.qpid.server.model.VirtualHost;
+
+public interface ReplicationNodeFactory extends Pluggable
+{
+
+ PluggableFactoryLoader<ReplicationNodeFactory> FACTORIES = new PluggableFactoryLoader<ReplicationNodeFactory>(ReplicationNodeFactory.class, false);
+
+ ReplicationNode createInstance(UUID id, Map<String, Object> attributes, VirtualHost virtualHost);
+
+}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/plugin/VirtualHostFactory.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/plugin/VirtualHostFactory.java
index 9549b70c83..fd1ca7cca1 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/plugin/VirtualHostFactory.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/plugin/VirtualHostFactory.java
@@ -27,6 +27,7 @@ import java.util.List;
import java.util.Map;
import org.apache.commons.configuration.Configuration;
import org.apache.qpid.server.configuration.VirtualHostConfiguration;
+import org.apache.qpid.server.model.ReplicationNode;
import org.apache.qpid.server.model.adapter.VirtualHostAdapter;
import org.apache.qpid.server.security.SecurityManager;
import org.apache.qpid.server.stats.StatisticsGatherer;
@@ -49,6 +50,8 @@ public interface VirtualHostFactory extends Pluggable
Map<String,Object> convertVirtualHostConfiguration(Configuration configuration);
+ ReplicationNode createReplicationNode(Configuration configuration, org.apache.qpid.server.model.VirtualHost virtualHost);
+
static final class TYPES
{
private TYPES()
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java
index cc1b22d8e1..e861937ed4 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java
@@ -23,6 +23,7 @@ package org.apache.qpid.server.registry;
import java.util.Collection;
import java.util.Timer;
import java.util.TimerTask;
+import java.util.concurrent.Callable;
import org.apache.log4j.Logger;
import org.apache.qpid.common.Closeable;
@@ -106,7 +107,7 @@ public class ApplicationRegistry implements IApplicationRegistry
BrokerActor actor = new BrokerActor(startupMessageLogger);
CurrentActor.set(actor);
- CurrentActor.setDefault(actor);
+ CurrentActor.setDefault(new BrokerActor(_rootMessageLogger));
GenericActor.setDefaultMessageLogger(_rootMessageLogger);
try
{
@@ -125,7 +126,7 @@ public class ApplicationRegistry implements IApplicationRegistry
initialiseStatisticsReporting();
// starting the broker
- _broker.setDesiredState(State.INITIALISING, State.ACTIVE);
+ _broker.attainDesiredState();
CurrentActor.get().message(BrokerMessages.READY());
}
@@ -133,8 +134,6 @@ public class ApplicationRegistry implements IApplicationRegistry
{
CurrentActor.remove();
}
-
- CurrentActor.setDefault(new BrokerActor(_rootMessageLogger));
}
private void initialiseStatisticsReporting()
@@ -253,7 +252,14 @@ public class ApplicationRegistry implements IApplicationRegistry
if (_broker != null)
{
- _broker.setDesiredState(_broker.getActualState(), State.STOPPED);
+ _taskExecutor.submitAndWait(new Callable<Void>(){
+
+ @Override
+ public Void call() throws Exception
+ {
+ _broker.close();
+ return null;
+ }});
}
//Shutdown virtualhosts
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/replication/ReplicationGroupListener.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/replication/ReplicationGroupListener.java
new file mode 100644
index 0000000000..d2a6095e5f
--- /dev/null
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/replication/ReplicationGroupListener.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.replication;
+
+import org.apache.qpid.server.model.ReplicationNode;
+
+public interface ReplicationGroupListener
+{
+ /**
+ * Fired when a remote replication node is added to a group. This event happens
+ * exactly once just after a new replication node is created.
+ */
+ void onReplicationNodeAddedToGroup(ReplicationNode node);
+
+ /**
+ * Fired exactly once for each existing remote node. Used to inform the application
+ * on any existing nodes as it starts up for the first time.
+ */
+ void onReplicationNodeRecovered(ReplicationNode node);
+
+ /**
+ * Fired when a remote replication node is (permanently) removed from group. This event
+ * happens exactly once just after the existing replication node is deleted.
+ */
+ void onReplicationNodeRemovedFromGroup(ReplicationNode node);
+
+ /**
+ * Fired when a remote replication node (that is already a member of the group) joins
+ * the group. This will typically occur when another replication node is started perhaps
+ * because the broker has been started.
+ */
+ //void onReplicationNodeUp();
+
+ /**
+ * Fired when a remote replication node (that is already a member of the group) leaves
+ * the group. This will typically occur when another replication node is stopped perhaps
+ * because its broker has been stopped.
+ */
+ //void onReplicationNodeDown();
+
+}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractDurableConfiguredObjectRecoverer.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractDurableConfiguredObjectRecoverer.java
index dbe8bf22a0..3d91420fc5 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractDurableConfiguredObjectRecoverer.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractDurableConfiguredObjectRecoverer.java
@@ -20,11 +20,16 @@
*/
package org.apache.qpid.server.store;
+import java.util.Arrays;
import java.util.Map;
import java.util.UUID;
+import org.apache.log4j.Logger;
+
public abstract class AbstractDurableConfiguredObjectRecoverer<T> implements DurableConfiguredObjectRecoverer
{
+ private static final Logger LOGGER = Logger.getLogger(AbstractDurableConfiguredObjectRecoverer.class);
+
@Override
public void load(final DurableConfigurationRecoverer durableConfigurationRecoverer,
final UUID id,
@@ -67,7 +72,6 @@ public abstract class AbstractDurableConfiguredObjectRecoverer<T> implements Dur
{
durableConfigurationRecoverer.addUnresolvedObject(getType(), id, obj);
}
-
}
public abstract UnresolvedObject<T> createUnresolvedObject(final UUID id,
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractJDBCMessageStore.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractJDBCMessageStore.java
index d80fa656e7..eb3bc9c9e0 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractJDBCMessageStore.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractJDBCMessageStore.java
@@ -177,7 +177,7 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC
}
@Override
- public void configureConfigStore(VirtualHost virtualHost, ConfigurationRecoveryHandler configRecoveryHandler) throws Exception
+ public void configureConfigStore(VirtualHost virtualHost, ConfigurationRecoveryHandler configRecoveryHandler)
{
_stateManager.attainState(State.INITIALISING);
_configRecoveryHandler = configRecoveryHandler;
@@ -187,7 +187,7 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC
@Override
public void configureMessageStore(VirtualHost virtualHost, MessageStoreRecoveryHandler recoveryHandler,
- TransactionLogRecoveryHandler tlogRecoveryHandler) throws Exception
+ TransactionLogRecoveryHandler tlogRecoveryHandler) throws AMQStoreException
{
if(_stateManager.isInState(State.INITIAL))
{
@@ -197,8 +197,14 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC
_virtualHost = virtualHost;
_tlogRecoveryHandler = tlogRecoveryHandler;
_messageRecoveryHandler = recoveryHandler;
-
- completeInitialisation();
+ try
+ {
+ completeInitialisation();
+ }
+ catch(Exception e)
+ {
+ throw new AMQStoreException("Cannot initialize store", e);
+ }
}
private void completeInitialisation() throws ClassNotFoundException, SQLException, AMQStoreException
@@ -209,8 +215,10 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC
}
@Override
- public void activate() throws Exception
+ public void activate() throws AMQStoreException
{
+ try
+ {
if(_stateManager.isInState(State.INITIALISING))
{
completeInitialisation();
@@ -234,6 +242,11 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC
}
_stateManager.attainState(State.ACTIVE);
+ }
+ catch(Exception e)
+ {
+ throw new AMQStoreException("Cannot activate store", e);
+ }
}
private void commonConfiguration()
@@ -668,7 +681,7 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC
}
@Override
- public void close() throws Exception
+ public void close() throws AMQStoreException
{
if (_closed.compareAndSet(false, true))
{
@@ -681,7 +694,7 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC
}
- protected abstract void doClose() throws Exception;
+ protected abstract void doClose() throws AMQStoreException;
@Override
public StoredMessage addMessage(StorableMessageMetaData metaData)
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractMemoryMessageStore.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractMemoryMessageStore.java
index 1a7cd72cb6..4813edd30b 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractMemoryMessageStore.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractMemoryMessageStore.java
@@ -80,14 +80,14 @@ abstract public class AbstractMemoryMessageStore extends NullMessageStore
}
@Override
- public void configureConfigStore(VirtualHost virtualHost, ConfigurationRecoveryHandler recoveryHandler) throws Exception
+ public void configureConfigStore(VirtualHost virtualHost, ConfigurationRecoveryHandler recoveryHandler)
{
_stateManager.attainState(State.INITIALISING);
}
@Override
public void configureMessageStore(VirtualHost virtualHost, MessageStoreRecoveryHandler recoveryHandler,
- TransactionLogRecoveryHandler tlogRecoveryHandler) throws Exception
+ TransactionLogRecoveryHandler tlogRecoveryHandler)
{
if(_stateManager.isInState(State.INITIAL))
{
@@ -97,7 +97,7 @@ abstract public class AbstractMemoryMessageStore extends NullMessageStore
}
@Override
- public void activate() throws Exception
+ public void activate()
{
if(_stateManager.isInState(State.INITIALISING))
@@ -110,7 +110,7 @@ abstract public class AbstractMemoryMessageStore extends NullMessageStore
}
@Override
- public StoredMessage addMessage(StorableMessageMetaData metaData)
+ public StoredMessage<? extends StorableMessageMetaData> addMessage(StorableMessageMetaData metaData)
{
final long id = _messageId.getAndIncrement();
StoredMemoryMessage message = new StoredMemoryMessage(id, metaData);
@@ -131,7 +131,7 @@ abstract public class AbstractMemoryMessageStore extends NullMessageStore
}
@Override
- public void close() throws Exception
+ public void close()
{
if (_closed.compareAndSet(false, true))
{
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/ConfigurationRecoveryHandler.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/ConfigurationRecoveryHandler.java
index c6ebe90802..d99862b104 100755
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/ConfigurationRecoveryHandler.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/ConfigurationRecoveryHandler.java
@@ -27,7 +27,7 @@ public interface ConfigurationRecoveryHandler
{
void beginConfigurationRecovery(DurableConfigurationStore store, int configVersion);
- void configuredObject(UUID id, String type, Map<String, Object> attributes);
+ void configuredObject(UUID id, String type, Map<String, Object> attributes) throws RecoveryAbortException;
/**
*
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationRecoverer.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationRecoverer.java
index 1caaedf6dc..44417bea97 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationRecoverer.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationRecoverer.java
@@ -68,6 +68,7 @@ public class DurableConfigurationRecoverer implements ConfigurationRecoveryHandl
@Override
public void beginConfigurationRecovery(final DurableConfigurationStore store, final int configVersion)
{
+ reset();
_logSubject = new MessageStoreLogSubject(_name, store.getClass().getSimpleName());
_store = store;
@@ -103,10 +104,18 @@ public class DurableConfigurationRecoverer implements ConfigurationRecoveryHandl
checkUnresolvedDependencies();
applyUpgrade();
+ reset();
CurrentActor.get().message(_logSubject, ConfigStoreMessages.RECOVERY_COMPLETE());
return CURRENT_CONFIG_VERSION;
}
+ private void reset()
+ {
+ _resolvedObjects.clear();
+ _unresolvedObjects.clear();
+ _dependencyListeners.clear();
+ }
+
private void applyUpgrade()
{
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStore.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStore.java
index 9c9ce7df59..538a5daa64 100755
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStore.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStore.java
@@ -28,7 +28,6 @@ import org.apache.qpid.server.model.VirtualHost;
public interface DurableConfigurationStore
{
-
public static interface Source
{
DurableConfigurationStore getDurableConfigurationStore();
@@ -46,7 +45,7 @@ public interface DurableConfigurationStore
* @param recoveryHandler Handler to be called as the store recovers on start up
* @throws Exception If any error occurs that means the store is unable to configure itself.
*/
- void configureConfigStore(VirtualHost virtualHost, ConfigurationRecoveryHandler recoveryHandler) throws Exception;
+ void configureConfigStore(VirtualHost virtualHost, ConfigurationRecoveryHandler recoveryHandler) throws AMQStoreException;
/**
@@ -90,5 +89,5 @@ public interface DurableConfigurationStore
public void update(boolean createIfNecessary, ConfiguredObjectRecord... records) throws AMQStoreException;
- void close() throws Exception;
+ void close() throws AMQStoreException;
}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfiguredObjectRecoverer.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfiguredObjectRecoverer.java
index e065728bd3..ea1c3794d9 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfiguredObjectRecoverer.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfiguredObjectRecoverer.java
@@ -27,7 +27,7 @@ public interface DurableConfiguredObjectRecoverer
{
public void load(final DurableConfigurationRecoverer durableConfigurationRecoverer,
final UUID id,
- final Map<String, Object> attributes);
+ final Map<String, Object> attributes) throws RecoveryAbortException;
public String getType();
}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/JsonFileConfigStore.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/JsonFileConfigStore.java
index 9b20c9a780..c1d9838833 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/JsonFileConfigStore.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/JsonFileConfigStore.java
@@ -65,8 +65,7 @@ public class JsonFileConfigStore implements DurableConfigurationStore
}
@Override
- public void configureConfigStore(final VirtualHost virtualHost, final ConfigurationRecoveryHandler recoveryHandler)
- throws Exception
+ public void configureConfigStore(final VirtualHost virtualHost, final ConfigurationRecoveryHandler recoveryHandler) throws AMQStoreException
{
_name = virtualHost.getName();
@@ -75,38 +74,44 @@ public class JsonFileConfigStore implements DurableConfigurationStore
{
throw new AMQStoreException("Cannot determine path for configuration storage");
}
- _directoryName = (String) storePathAttr;
- _configFileName = _name + ".json";
- _backupFileName = _name + ".bak";
- checkDirectoryIsWritable(_directoryName);
- getFileLock();
-
- if(!fileExists(_configFileName))
+ try
{
- if(!fileExists(_backupFileName))
+ _directoryName = (String) storePathAttr;
+ _configFileName = _name + ".json";
+ _backupFileName = _name + ".bak";
+ checkDirectoryIsWritable(_directoryName);
+ getFileLock();
+
+ if(!fileExists(_configFileName))
{
- File newFile = new File(_directoryName, _configFileName);
- _objectMapper.writeValue(newFile, Collections.emptyMap());
+ if(!fileExists(_backupFileName))
+ {
+ File newFile = new File(_directoryName, _configFileName);
+ _objectMapper.writeValue(newFile, Collections.emptyMap());
+ }
+ else
+ {
+ renameFile(_backupFileName, _configFileName);
+ }
}
- else
+
+ load();
+ recoveryHandler.beginConfigurationRecovery(this,_configVersion);
+ List<ConfiguredObjectRecord> records = new ArrayList<ConfiguredObjectRecord>(_objectsById.values());
+ for(ConfiguredObjectRecord record : records)
{
- renameFile(_backupFileName, _configFileName);
+ recoveryHandler.configuredObject(record.getId(), record.getType(), record.getAttributes());
+ }
+ int oldConfigVersion = _configVersion;
+ _configVersion = recoveryHandler.completeConfigurationRecovery();
+ if(oldConfigVersion != _configVersion)
+ {
+ save();
}
}
-
-
- load();
- recoveryHandler.beginConfigurationRecovery(this,_configVersion);
- List<ConfiguredObjectRecord> records = new ArrayList<ConfiguredObjectRecord>(_objectsById.values());
- for(ConfiguredObjectRecord record : records)
- {
- recoveryHandler.configuredObject(record.getId(), record.getType(), record.getAttributes());
- }
- int oldConfigVersion = _configVersion;
- _configVersion = recoveryHandler.completeConfigurationRecovery();
- if(oldConfigVersion != _configVersion)
+ catch(IOException e)
{
- save();
+ throw new AMQStoreException("Cannot configure store", e);
}
}
@@ -478,12 +483,16 @@ public class JsonFileConfigStore implements DurableConfigurationStore
save();
}
- public void close() throws Exception
+ public void close() throws AMQStoreException
{
try
{
releaseFileLock();
}
+ catch(Exception e)
+ {
+ throw new AMQStoreException("Cannot release file lock", e);
+ }
finally
{
_fileLock = null;
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/MessageStore.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/MessageStore.java
index 996d71d51d..55a0ca009a 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/MessageStore.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/MessageStore.java
@@ -20,6 +20,7 @@
*/
package org.apache.qpid.server.store;
+import org.apache.qpid.AMQStoreException;
import org.apache.qpid.server.model.VirtualHost;
/**
@@ -41,11 +42,11 @@ public interface MessageStore
* @throws Exception If any error occurs that means the store is unable to configure itself.
*/
void configureMessageStore(VirtualHost virtualHost, MessageStoreRecoveryHandler messageRecoveryHandler,
- TransactionLogRecoveryHandler tlogRecoveryHandler) throws Exception;
+ TransactionLogRecoveryHandler tlogRecoveryHandler) throws AMQStoreException;
- void activate() throws Exception;
+ void activate() throws AMQStoreException;
- public <T extends StorableMessageMetaData> StoredMessage<T> addMessage(T metaData);
+ public <T extends StorableMessageMetaData> StoredMessage<T> addMessage(T metaData) throws AMQStoreException;
/**
@@ -55,14 +56,14 @@ public interface MessageStore
*/
boolean isPersistent();
- Transaction newTransaction();
+ Transaction newTransaction() throws AMQStoreException;
/**
* Called to close and cleanup any resources used by the message store.
*
* @throws Exception If the close fails.
*/
- void close() throws Exception;
+ void close() throws AMQStoreException;
void addEventListener(EventListener eventListener, Event... events);
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/NullMessageStore.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/NullMessageStore.java
index 57dbfabaa4..99b32d347f 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/NullMessageStore.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/NullMessageStore.java
@@ -27,7 +27,7 @@ import org.apache.qpid.server.model.VirtualHost;
public abstract class NullMessageStore implements MessageStore, DurableConfigurationStore
{
@Override
- public void configureConfigStore(VirtualHost virtualHost, ConfigurationRecoveryHandler recoveryHandler) throws Exception
+ public void configureConfigStore(VirtualHost virtualHost, ConfigurationRecoveryHandler recoveryHandler)
{
}
@@ -42,7 +42,7 @@ public abstract class NullMessageStore implements MessageStore, DurableConfigura
}
@Override
- public void update(boolean createIfNecessary, ConfiguredObjectRecord... records) throws AMQStoreException
+ public void update(boolean createIfNecessary, ConfiguredObjectRecord... records)
{
}
@@ -65,12 +65,12 @@ public abstract class NullMessageStore implements MessageStore, DurableConfigura
@Override
public void configureMessageStore(VirtualHost virtualHost, MessageStoreRecoveryHandler recoveryHandler,
- TransactionLogRecoveryHandler tlogRecoveryHandler) throws Exception
+ TransactionLogRecoveryHandler tlogRecoveryHandler)
{
}
@Override
- public void close() throws Exception
+ public void close()
{
}
@@ -93,7 +93,7 @@ public abstract class NullMessageStore implements MessageStore, DurableConfigura
}
@Override
- public void activate() throws Exception
+ public void activate()
{
}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/RecoveryAbortException.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/RecoveryAbortException.java
new file mode 100644
index 0000000000..fa6f792493
--- /dev/null
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/RecoveryAbortException.java
@@ -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.
+ *
+ */
+package org.apache.qpid.server.store;
+
+@SuppressWarnings("serial")
+public class RecoveryAbortException extends RuntimeException
+{
+
+ public RecoveryAbortException(String message)
+ {
+ super(message);
+ }
+
+ public RecoveryAbortException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+
+}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/StateManager.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/StateManager.java
index e4efc26477..9cf28095d2 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/StateManager.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/StateManager.java
@@ -24,10 +24,12 @@ package org.apache.qpid.server.store;
import java.util.EnumMap;
import java.util.Map;
-import org.apache.qpid.server.store.StateManager.Transition;
+import org.apache.log4j.Logger;
public class StateManager
{
+ private static final Logger LOGGER = Logger.getLogger(StateManager.class);
+
private State _state = State.INITIAL;
private EventListener _eventListener;
@@ -79,6 +81,7 @@ public class StateManager
public static final Transition ACTIVATE_COMPLETE = new Transition(State.ACTIVATING, State.ACTIVE, Event.AFTER_ACTIVATE);
public static final Transition CLOSE_INITIALISED = new Transition(State.INITIALISED, State.CLOSING, Event.BEFORE_CLOSE);;
+ public static final Transition CLOSE_ACTIVATING = new Transition(State.ACTIVATING, State.CLOSING, Event.BEFORE_CLOSE);
public static final Transition CLOSE_ACTIVE = new Transition(State.ACTIVE, State.CLOSING, Event.BEFORE_CLOSE);
public static final Transition CLOSE_QUIESCED = new Transition(State.QUIESCED, State.CLOSING, Event.BEFORE_CLOSE);
public static final Transition CLOSE_COMPLETE = new Transition(State.CLOSING, State.CLOSED, Event.AFTER_CLOSE);
@@ -116,6 +119,11 @@ public class StateManager
public synchronized void attainState(State desired)
{
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Attaining state from " + _state + " to " + desired);
+ }
+
Transition transition = null;
final Map<State, Transition> stateTransitionMap = _validTransitions.get(_state);
if(stateTransitionMap != null)
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/UnresolvedObject.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/UnresolvedObject.java
index 7ebebadae7..ea49f0787c 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/UnresolvedObject.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/UnresolvedObject.java
@@ -24,5 +24,5 @@ public interface UnresolvedObject<T>
{
public UnresolvedDependency[] getUnresolvedDependencies();
- T resolve();
+ T resolve() throws RecoveryAbortException;
}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/util/DaemonThreadFactory.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/util/DaemonThreadFactory.java
new file mode 100644
index 0000000000..4f1f830fd0
--- /dev/null
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/util/DaemonThreadFactory.java
@@ -0,0 +1,40 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.util;
+
+import java.util.concurrent.ThreadFactory;
+
+public final class DaemonThreadFactory implements ThreadFactory
+{
+ private String _threadName;
+ public DaemonThreadFactory(String threadName)
+ {
+ _threadName = threadName;
+ }
+
+ @Override
+ public Thread newThread(Runnable r)
+ {
+ Thread thread = new Thread(r, _threadName);
+ thread.setDaemon(true);
+ return thread;
+ }
+} \ No newline at end of file
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/util/MapValueConverter.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/util/MapValueConverter.java
index 37e0177b00..c3b339ca6e 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/util/MapValueConverter.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/util/MapValueConverter.java
@@ -313,21 +313,7 @@ public class MapValueConverter
else if (typeObject instanceof ParameterizedType)
{
ParameterizedType parameterizedType= (ParameterizedType)typeObject;
- Type type = parameterizedType.getRawType();
- if (type == Set.class)
- {
- Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
- if (actualTypeArguments.length != 1)
- {
- throw new IllegalArgumentException("Set type argument is not specified");
- }
- Class<?> classObject = (Class<?>)actualTypeArguments[0];
- value = toSet(rawValue, classObject, attributeName);
- }
- else
- {
- throw new IllegalArgumentException("Conversion into " + parameterizedType + " is not yet supported");
- }
+ value = convertParameterizedType(rawValue, parameterizedType, attributeName);
}
else
{
@@ -344,6 +330,62 @@ public class MapValueConverter
return attributes;
}
+ private static Object convertParameterizedType(Object rawValue, ParameterizedType parameterizedType, String attributeName)
+ {
+ Type type = parameterizedType.getRawType();
+ Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
+ Object convertedValue;
+ if (type == Set.class)
+ {
+ if (actualTypeArguments.length != 1)
+ {
+ throw new IllegalArgumentException("Unexpected number of Set type arguments " + actualTypeArguments.length);
+ }
+ Class<?> classObject = (Class<?>)actualTypeArguments[0];
+ convertedValue = toSet(rawValue, classObject, attributeName);
+ }
+ else if (type == Map.class)
+ {
+ if (actualTypeArguments.length != 2)
+ {
+ throw new IllegalArgumentException("Unexpected number of Map type arguments " + actualTypeArguments.length);
+ }
+ Class<?> keyClassObject = (Class<?>)actualTypeArguments[0];
+ Class<?> valueClassObject = (Class<?>)actualTypeArguments[1];
+ convertedValue = toMap(rawValue, keyClassObject, valueClassObject, attributeName);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Conversion into " + parameterizedType + " is not yet supported");
+ }
+ return convertedValue;
+ }
+
+ private static <K,V> Map<K, V> toMap(Object rawValue, Class<K> keyClassObject, Class<V> valueClassObject, String attributeName)
+ {
+ if (rawValue == null)
+ {
+ return null;
+ }
+ if (rawValue instanceof Map)
+ {
+ Map<K, V> convertedMap = new HashMap<K, V>();
+ Map<?, ?> rawMap = (Map<?,?>)rawValue;
+
+ for (Map.Entry<?, ?> entry : rawMap.entrySet())
+ {
+ K convertedKey = convert(entry.getKey(), keyClassObject, attributeName + " (map key)");
+ V convertedValue = convert(entry.getValue(), valueClassObject, attributeName + " (map value)");
+ convertedMap.put(convertedKey, convertedValue);
+ }
+ return convertedMap;
+ }
+ else
+ {
+ throw new IllegalArgumentException("rawValue is not of unexpected type Map, was : " + rawValue.getClass());
+ }
+ }
+
public static <T> Set<T> toSet(Object rawValue, Class<T> setItemClass, String attributeName)
{
if (rawValue == null)
@@ -353,7 +395,7 @@ public class MapValueConverter
HashSet<T> set = new HashSet<T>();
if (rawValue instanceof Iterable)
{
- Iterable<?> iterable = (Iterable<?>)rawValue;
+ Iterable<?> iterable = (Iterable<?>)rawValue;
for (Object object : iterable)
{
T converted = convert(object, setItemClass, attributeName);
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java
index 3d42b07117..7d2429f907 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java
@@ -30,11 +30,14 @@ import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.log4j.Logger;
import org.apache.qpid.AMQException;
import org.apache.qpid.AMQSecurityException;
+import org.apache.qpid.AMQStoreException;
import org.apache.qpid.server.configuration.ExchangeConfiguration;
import org.apache.qpid.server.configuration.QueueConfiguration;
import org.apache.qpid.server.configuration.VirtualHostConfiguration;
@@ -47,6 +50,8 @@ import org.apache.qpid.server.exchange.ExchangeFactory;
import org.apache.qpid.server.exchange.ExchangeRegistry;
import org.apache.qpid.server.logging.actors.CurrentActor;
import org.apache.qpid.server.logging.messages.VirtualHostMessages;
+import org.apache.qpid.server.model.ConfigurationChangeListener;
+import org.apache.qpid.server.model.ConfiguredObject;
import org.apache.qpid.server.message.MessageDestination;
import org.apache.qpid.server.message.MessageNode;
import org.apache.qpid.server.message.MessageSource;
@@ -61,18 +66,21 @@ import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.queue.AMQQueueFactory;
import org.apache.qpid.server.queue.DefaultQueueRegistry;
import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.replication.ReplicationGroupListener;
import org.apache.qpid.server.security.SecurityManager;
import org.apache.qpid.server.stats.StatisticsCounter;
import org.apache.qpid.server.stats.StatisticsGatherer;
+import org.apache.qpid.server.store.ConfiguredObjectRecord;
import org.apache.qpid.server.store.DurableConfigurationStore;
import org.apache.qpid.server.store.DurableConfigurationStoreHelper;
import org.apache.qpid.server.store.DurableConfiguredObjectRecoverer;
import org.apache.qpid.server.store.Event;
import org.apache.qpid.server.store.EventListener;
+import org.apache.qpid.server.store.RecoveryAbortException;
import org.apache.qpid.server.txn.DtxRegistry;
import org.apache.qpid.server.virtualhost.plugins.QueueExistsException;
-public abstract class AbstractVirtualHost implements VirtualHost, IConnectionRegistry.RegistryChangeListener, EventListener
+public abstract class AbstractVirtualHost implements VirtualHost, IConnectionRegistry.RegistryChangeListener, EventListener, ConfigurationChangeListener
{
private static final Logger _logger = Logger.getLogger(AbstractVirtualHost.class);
@@ -84,8 +92,6 @@ public abstract class AbstractVirtualHost implements VirtualHost, IConnectionReg
private final long _createTime = System.currentTimeMillis();
- private final ScheduledThreadPoolExecutor _houseKeepingTasks;
-
private final VirtualHostRegistry _virtualHostRegistry;
private final StatisticsGatherer _brokerStatisticsGatherer;
@@ -107,12 +113,22 @@ public abstract class AbstractVirtualHost implements VirtualHost, IConnectionReg
private final SystemNodeRegistry _systemNodeRegistry = new SystemNodeRegistry();
private final org.apache.qpid.server.model.VirtualHost _model;
- private volatile State _state = State.INITIALISING;
+ private final Map<String, LinkRegistry> _linkRegistry = new HashMap<String, LinkRegistry>();
- private StatisticsCounter _messagesDelivered, _dataDelivered, _messagesReceived, _dataReceived;
+ private final AtomicReference<VirtualHostAttributeRecoveryListener> _virtualHostAttributeRecoveryListener = new AtomicReference<VirtualHostAttributeRecoveryListener>();
+ private final AtomicReference<ReplicationGroupListener> _replicationGroupListener = new AtomicReference<ReplicationGroupListener>();
- private final Map<String, LinkRegistry> _linkRegistry = new HashMap<String, LinkRegistry>();
- private boolean _blocked;
+ /**
+ * Flag indicating whether virtual host is still active, for instance,
+ * it can be in QUIESCED state but the connections, queues, etc could be still open.
+ * Thus, it all active objects needs to be shutdown on close
+ */
+ private final AtomicBoolean _active = new AtomicBoolean();
+
+ private volatile StatisticsCounter _messagesDelivered, _dataDelivered, _messagesReceived, _dataReceived;
+ private volatile boolean _blocked;
+ private volatile State _state = State.INITIALISING;
+ private volatile ScheduledThreadPoolExecutor _houseKeepingTasks;
private final Map<String, MessageDestination> _systemNodeDestinations =
Collections.synchronizedMap(new HashMap<String,MessageDestination>());
@@ -146,16 +162,11 @@ public abstract class AbstractVirtualHost implements VirtualHost, IConnectionReg
_id = UUIDGenerator.generateVhostUUID(_name);
- CurrentActor.get().message(VirtualHostMessages.CREATED(_name));
-
_securityManager = new SecurityManager(parentSecurityManager, _vhostConfig.getConfig().getString("security.acl"), _name);
_connectionRegistry = new ConnectionRegistry();
_connectionRegistry.addRegistryChangeListener(this);
- _houseKeepingTasks = new ScheduledThreadPoolExecutor(_vhostConfig.getHouseKeepingThreadCount());
-
-
_queueRegistry = new DefaultQueueRegistry(this);
_queueFactory = new AMQQueueFactory(this, _queueRegistry);
@@ -168,10 +179,43 @@ public abstract class AbstractVirtualHost implements VirtualHost, IConnectionReg
initialiseStatistics();
- initialiseStorage(hostConfig, virtualHost);
+ CurrentActor.get().message(VirtualHostMessages.CREATED(_name));
+ }
+
+ @Override
+ public void activate() throws Exception
+ {
+ State currentState = getState();
+ if (_active.compareAndSet(false, true))
+ {
+ _houseKeepingTasks = new ScheduledThreadPoolExecutor(_vhostConfig.getHouseKeepingThreadCount());
+
+ try
+ {
+ initialiseStorage(_vhostConfig, _model);
+
+ getMessageStore().addEventListener(this, Event.PERSISTENT_MESSAGE_SIZE_OVERFULL);
+ getMessageStore().addEventListener(this, Event.PERSISTENT_MESSAGE_SIZE_UNDERFULL);
+ }
+ catch(RecoveryAbortException e)
+ {
+ _logger.warn("Activation is aborted due to : " + e.getMessage());
+ return;
+ }
+ }
+ else
+ {
+ if ( currentState == State.QUIESCED)
+ {
+ setState(State.ACTIVE);
+ }
+ }
+ }
- getMessageStore().addEventListener(this, Event.PERSISTENT_MESSAGE_SIZE_OVERFULL);
- getMessageStore().addEventListener(this, Event.PERSISTENT_MESSAGE_SIZE_UNDERFULL);
+ @Override
+ public void quiesce()
+ {
+ setState(State.QUIESCED);
}
private void registerSystemNodes()
@@ -223,6 +267,11 @@ public abstract class AbstractVirtualHost implements VirtualHost, IConnectionReg
protected void shutdownHouseKeeping()
{
+ if (_houseKeepingTasks == null)
+ {
+ return;
+ }
+
_houseKeepingTasks.shutdown();
try
@@ -676,23 +725,35 @@ public abstract class AbstractVirtualHost implements VirtualHost, IConnectionReg
return _securityManager;
}
- public void close()
+
+ protected void passivate(String reason)
{
+ _model.removeChangeListener(this);
+ removeHouseKeepingTasks();
+
//Stop Connections
- _connectionRegistry.close();
+ _connectionRegistry.close(reason);
_queueRegistry.stopAllAndUnregisterMBeans();
_dtxRegistry.close();
- closeStorage();
- shutdownHouseKeeping();
// clear exchange objects
_exchangeRegistry.clearAndUnregisterMbeans();
+ }
- _state = State.STOPPED;
+ public void close()
+ {
+ if (_active.compareAndSet(true, false))
+ {
+ passivate(IConnectionRegistry.BROKER_SHUTDOWN_REPLY_TEXT);
+ closeStorage();
+ shutdownHouseKeeping();
+ }
+ _state = State.STOPPED;
CurrentActor.get().message(VirtualHostMessages.CLOSED());
}
+
protected void closeStorage()
{
//Close MessageStore
@@ -884,6 +945,7 @@ public abstract class AbstractVirtualHost implements VirtualHost, IConnectionReg
try
{
initialiseHouseKeeping(_vhostConfig.getHousekeepingCheckPeriod());
+ _model.addChangeListener(this);
finalState = State.ACTIVE;
}
finally
@@ -904,6 +966,7 @@ public abstract class AbstractVirtualHost implements VirtualHost, IConnectionReg
protected Map<String, DurableConfiguredObjectRecoverer> getDurableConfigurationRecoverers()
{
DurableConfiguredObjectRecoverer[] recoverers = {
+ new VirtualHostRecoverer(this, getVirtualHostAttributeRecoveryListener()),
new QueueRecoverer(this, getExchangeRegistry(), _queueFactory),
new ExchangeRecoverer(getExchangeRegistry(), getExchangeFactory()),
new BindingRecoverer(this, getExchangeRegistry())
@@ -917,6 +980,70 @@ public abstract class AbstractVirtualHost implements VirtualHost, IConnectionReg
return recovererMap;
}
+ @Override
+ public void setVirtualHostAttributeRecoveryListener(VirtualHostAttributeRecoveryListener listener)
+ {
+ if (!_virtualHostAttributeRecoveryListener.compareAndSet(null, listener))
+ {
+ throw new IllegalStateException("Attribute recovery listener is already set on virtual host " + getName());
+ }
+ }
+
+ protected VirtualHostAttributeRecoveryListener getVirtualHostAttributeRecoveryListener()
+ {
+ return _virtualHostAttributeRecoveryListener.get();
+ }
+
+ @Override
+ public void setReplicationGroupListener(ReplicationGroupListener listener)
+ {
+ if (!_replicationGroupListener.compareAndSet(null, listener))
+ {
+ throw new IllegalStateException("Replication group listener is already set on virtual host " + getName());
+ }
+ }
+
+ protected ReplicationGroupListener getReplicationGroupListener()
+ {
+ return _replicationGroupListener.get();
+ }
+
+ @Override
+ public void stateChanged(ConfiguredObject object, org.apache.qpid.server.model.State oldState, org.apache.qpid.server.model.State newState)
+ {
+ // no-op
+ }
+
+ @Override
+ public void childAdded(ConfiguredObject object, ConfiguredObject child)
+ {
+ // no-op
+ }
+
+ @Override
+ public void childRemoved(ConfiguredObject object, ConfiguredObject child)
+ {
+ // no-op
+ }
+
+ @Override
+ public void attributeSet(ConfiguredObject object, String attributeName, Object oldAttributeValue, Object newAttributeValue)
+ {
+ DurableConfigurationStore durableConfigurationStore = getDurableConfigurationStore();
+ if (durableConfigurationStore != null)
+ {
+ try
+ {
+ ConfiguredObjectRecord record = new ConfiguredObjectRecord(getId(), org.apache.qpid.server.model.VirtualHost.class.getSimpleName(), Collections.singletonMap(attributeName, newAttributeValue));
+ durableConfigurationStore.update(true, record);
+ }
+ catch (AMQStoreException e)
+ {
+ _logger.error("Can save virtual host attribute " + attributeName + " value " + newAttributeValue, e);
+ }
+ }
+ }
+
private class VirtualHostHouseKeepingTask extends HouseKeepingTask
{
public VirtualHostHouseKeepingTask()
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/DefaultUpgraderProvider.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/DefaultUpgraderProvider.java
index 12f8c7dae8..c99092fff7 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/DefaultUpgraderProvider.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/DefaultUpgraderProvider.java
@@ -63,7 +63,6 @@ public class DefaultUpgraderProvider implements UpgraderProvider
currentUpgrader = addUpgrader(currentUpgrader, new Version1Upgrader());
case 2:
currentUpgrader = addUpgrader(currentUpgrader, new Version2Upgrader());
-
case CURRENT_CONFIG_VERSION:
currentUpgrader = addUpgrader(currentUpgrader, new NullUpgrader(recoverer));
break;
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/StandardVirtualHost.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/StandardVirtualHost.java
index b7e51d88d3..8b74915059 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/StandardVirtualHost.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/StandardVirtualHost.java
@@ -48,6 +48,7 @@ public class StandardVirtualHost extends AbstractVirtualHost
private MessageStore initialiseMessageStore(VirtualHostConfiguration hostConfig, VirtualHost virtualHost) throws Exception
{
+ //TODO: we should not be using hostConfig for store creation
final Object storeTypeAttr = virtualHost.getAttribute(VirtualHost.STORE_TYPE);
String storeType = storeTypeAttr == null ? null : String.valueOf(storeTypeAttr);
MessageStore messageStore = null;
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/StandardVirtualHostFactory.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/StandardVirtualHostFactory.java
index 08f35c08f9..b8641fce4d 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/StandardVirtualHostFactory.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/StandardVirtualHostFactory.java
@@ -23,6 +23,7 @@ import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.commons.configuration.Configuration;
import org.apache.qpid.server.configuration.VirtualHostConfiguration;
+import org.apache.qpid.server.model.ReplicationNode;
import org.apache.qpid.server.model.adapter.VirtualHostAdapter;
import org.apache.qpid.server.plugin.MessageStoreFactory;
import org.apache.qpid.server.plugin.VirtualHostFactory;
@@ -89,6 +90,7 @@ public class StandardVirtualHostFactory implements VirtualHostFactory
@Override
public Map<String,Object> createVirtualHostConfiguration(VirtualHostAdapter virtualHostAdapter)
{
+ //TODO: DO we really need it?
Map<String,Object> convertedMap = new LinkedHashMap<String, Object>();
convertedMap.put("store.type", virtualHostAdapter.getAttribute(org.apache.qpid.server.model.VirtualHost.STORE_TYPE));
convertedMap.put("store.environment-path", virtualHostAdapter.getAttribute(org.apache.qpid.server.model.VirtualHost.STORE_PATH));
@@ -101,6 +103,8 @@ public class StandardVirtualHostFactory implements VirtualHostFactory
{
Map<String,Object> convertedMap = new LinkedHashMap<String, Object>();
Configuration storeConfiguration = configuration.subset("store");
+
+ //TODO: If store class is specified, convert class into type
convertedMap.put(org.apache.qpid.server.model.VirtualHost.STORE_TYPE, storeConfiguration.getString("type"));
convertedMap.put(org.apache.qpid.server.model.VirtualHost.STORE_PATH, storeConfiguration.getString(MessageStoreConstants.ENVIRONMENT_PATH_PROPERTY));
@@ -115,4 +119,10 @@ public class StandardVirtualHostFactory implements VirtualHostFactory
return convertedMap;
}
+
+ @Override
+ public ReplicationNode createReplicationNode(Configuration configuration, org.apache.qpid.server.model.VirtualHost virtualHost)
+ {
+ return null;
+ }
}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/State.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/State.java
index 55e2539dcf..7b5e4ea598 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/State.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/State.java
@@ -27,5 +27,6 @@ public enum State
PASSIVE,
STOPPED,
/** Terminal state that signifies the virtual host has experienced an unexpected condition. */
- ERRORED
+ ERRORED,
+ QUIESCED
}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java
index 7034311d84..9edc227813 100755
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java
@@ -25,7 +25,6 @@ import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ScheduledFuture;
import org.apache.qpid.AMQException;
-import org.apache.qpid.AMQSecurityException;
import org.apache.qpid.common.Closeable;
import org.apache.qpid.server.configuration.VirtualHostConfiguration;
import org.apache.qpid.server.connection.IConnectionRegistry;
@@ -36,7 +35,7 @@ import org.apache.qpid.server.message.MessageSource;
import org.apache.qpid.server.plugin.ExchangeType;
import org.apache.qpid.server.protocol.LinkRegistry;
import org.apache.qpid.server.queue.AMQQueue;
-import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.replication.ReplicationGroupListener;
import org.apache.qpid.server.security.SecurityManager;
import org.apache.qpid.server.stats.StatisticsGatherer;
import org.apache.qpid.server.store.DurableConfigurationStore;
@@ -102,6 +101,14 @@ public interface VirtualHost extends DurableConfigurationStore.Source, Closeable
void close();
+ void activate() throws Exception;
+
+ void quiesce();
+
+ void setVirtualHostAttributeRecoveryListener(VirtualHostAttributeRecoveryListener listener);
+
+ void setReplicationGroupListener(ReplicationGroupListener listener);
+
UUID getId();
void scheduleHouseKeepingTask(long period, HouseKeepingTask task);
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/HAMessageStore.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostAttributeRecoveryListener.java
index 59483751ca..67731c0e89 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/HAMessageStore.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostAttributeRecoveryListener.java
@@ -1,4 +1,5 @@
/*
+ *
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
@@ -17,13 +18,11 @@
* under the License.
*
*/
-package org.apache.qpid.server.store;
+package org.apache.qpid.server.virtualhost;
+
+import java.util.Map;
-public interface HAMessageStore extends MessageStore
+public interface VirtualHostAttributeRecoveryListener
{
- /**
- * Used to indicate that a store requires to make itself unavailable for read and read/write
- * operations.
- */
- void passivate();
+ public void attributesRecovered(Map<String, Object> attributes);
}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostListener.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostListener.java
index 8527435eea..a0258e09a1 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostListener.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostListener.java
@@ -20,6 +20,8 @@
*/
package org.apache.qpid.server.virtualhost;
+import java.util.Map;
+
import org.apache.qpid.server.exchange.Exchange;
import org.apache.qpid.server.protocol.AMQConnectionModel;
import org.apache.qpid.server.queue.AMQQueue;
@@ -38,4 +40,5 @@ public interface VirtualHostListener
public void exchangeRegistered(Exchange exchange);
public void exchangeUnregistered(Exchange exchange);
+
}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRecoverer.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRecoverer.java
new file mode 100644
index 0000000000..4033bac488
--- /dev/null
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRecoverer.java
@@ -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.
+ *
+ */
+package org.apache.qpid.server.virtualhost;
+
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.qpid.server.store.AbstractDurableConfiguredObjectRecoverer;
+import org.apache.qpid.server.store.RecoveryAbortException;
+import org.apache.qpid.server.store.UnresolvedDependency;
+import org.apache.qpid.server.store.UnresolvedObject;
+
+public class VirtualHostRecoverer extends AbstractDurableConfiguredObjectRecoverer<VirtualHost>
+{
+ private final VirtualHost _host;
+ private final VirtualHostAttributeRecoveryListener _listener;
+
+ public VirtualHostRecoverer(VirtualHost host, VirtualHostAttributeRecoveryListener listener)
+ {
+ _host = host;
+ _listener = listener;
+ }
+
+ @Override
+ public String getType()
+ {
+ return org.apache.qpid.server.model.VirtualHost.class.getSimpleName();
+ }
+
+ @Override
+ public UnresolvedObject<VirtualHost> createUnresolvedObject(UUID id, String type, final Map<String, Object> attributes)
+ {
+ return new UnresolvedObject<VirtualHost>()
+ {
+ @Override
+ public UnresolvedDependency<?>[] getUnresolvedDependencies()
+ {
+ return new UnresolvedDependency<?>[0];
+ }
+
+ @Override
+ public VirtualHost resolve()
+ {
+ _listener.attributesRecovered(attributes);
+ Object desiredState = attributes.get(org.apache.qpid.server.model.VirtualHost.DESIRED_STATE);
+ if (desiredState != null && State.STOPPED.name().equals(desiredState))
+ {
+ throw new RecoveryAbortException("Virtual host state is STOPPED. Aborting the recovery");
+ }
+ return _host;
+ }
+ };
+ }
+
+}
diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/startup/VirtualHostRecovererTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/startup/VirtualHostRecovererTest.java
index 422a266efb..4f5bf908c6 100644
--- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/startup/VirtualHostRecovererTest.java
+++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/startup/VirtualHostRecovererTest.java
@@ -35,19 +35,39 @@ import org.apache.qpid.server.model.Broker;
import org.apache.qpid.server.model.VirtualHost;
import org.apache.qpid.server.security.SecurityManager;
import org.apache.qpid.server.stats.StatisticsGatherer;
+import org.apache.qpid.server.util.BrokerTestHelper;
import org.apache.qpid.server.virtualhost.StandardVirtualHostFactory;
+import org.apache.qpid.server.virtualhost.VirtualHostRegistry;
import org.apache.qpid.test.utils.TestFileUtils;
public class VirtualHostRecovererTest extends TestCase
{
+
+ private Broker _broker;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ BrokerTestHelper.setUp();
+ _broker = BrokerTestHelper.createBrokerMock();
+ when(_broker.getVirtualHostRegistry()).thenReturn(mock(VirtualHostRegistry.class));
+ }
+
+ @Override
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ BrokerTestHelper.tearDown();
+ }
+
public void testCreate()
{
StatisticsGatherer statisticsGatherer = mock(StatisticsGatherer.class);
SecurityManager securityManager = mock(SecurityManager.class);
ConfigurationEntry entry = mock(ConfigurationEntry.class);
- Broker parent = mock(Broker.class);
- when(parent.getAttribute(Broker.VIRTUALHOST_HOUSEKEEPING_CHECK_PERIOD)).thenReturn(3000l);
- when(parent.getSecurityManager()).thenReturn(securityManager);
+ when(_broker.getAttribute(Broker.VIRTUALHOST_HOUSEKEEPING_CHECK_PERIOD)).thenReturn(3000l);
+ when(_broker.getSecurityManager()).thenReturn(securityManager);
VirtualHostRecoverer recoverer = new VirtualHostRecoverer(statisticsGatherer);
Map<String, Object> attributes = new HashMap<String, Object>();
@@ -58,7 +78,7 @@ public class VirtualHostRecovererTest extends TestCase
attributes.put(VirtualHost.CONFIG_PATH, file.getAbsolutePath());
when(entry.getAttributes()).thenReturn(attributes);
- VirtualHost host = recoverer.create(null, entry, parent);
+ VirtualHost host = recoverer.create(null, entry, _broker);
assertNotNull("Null is returned", host);
assertEquals("Unexpected name", getName(), host.getName());
@@ -69,9 +89,9 @@ public class VirtualHostRecovererTest extends TestCase
StatisticsGatherer statisticsGatherer = mock(StatisticsGatherer.class);
SecurityManager securityManager = mock(SecurityManager.class);
ConfigurationEntry entry = mock(ConfigurationEntry.class);
- Broker parent = mock(Broker.class);
- when(parent.getAttribute(Broker.VIRTUALHOST_HOUSEKEEPING_CHECK_PERIOD)).thenReturn(3000l);
- when(parent.getSecurityManager()).thenReturn(securityManager);
+
+ when(_broker.getAttribute(Broker.VIRTUALHOST_HOUSEKEEPING_CHECK_PERIOD)).thenReturn(3000l);
+ when(_broker.getSecurityManager()).thenReturn(securityManager);
VirtualHostRecoverer recoverer = new VirtualHostRecoverer(statisticsGatherer);
Map<String, Object> attributes = new HashMap<String, Object>();
@@ -81,7 +101,7 @@ public class VirtualHostRecovererTest extends TestCase
attributes.put(VirtualHost.STORE_TYPE, "TESTMEMORY");
when(entry.getAttributes()).thenReturn(attributes);
- VirtualHost host = recoverer.create(null, entry, parent);
+ VirtualHost host = recoverer.create(null, entry, _broker);
assertNotNull("Null is returned", host);
assertEquals("Unexpected name", getName(), host.getName());
diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/ConfigurationEntryStoreTestCase.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/ConfigurationEntryStoreTestCase.java
index d419030c1d..8946851bad 100644
--- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/ConfigurationEntryStoreTestCase.java
+++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/ConfigurationEntryStoreTestCase.java
@@ -22,6 +22,7 @@ package org.apache.qpid.server.configuration.store;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
@@ -34,6 +35,7 @@ import org.apache.qpid.server.model.GroupProvider;
import org.apache.qpid.server.model.KeyStore;
import org.apache.qpid.server.model.Port;
import org.apache.qpid.server.model.PreferencesProvider;
+import org.apache.qpid.server.model.ReplicationNode;
import org.apache.qpid.server.model.Transport;
import org.apache.qpid.server.model.TrustStore;
import org.apache.qpid.server.model.VirtualHost;
@@ -97,6 +99,8 @@ public abstract class ConfigurationEntryStoreTestCase extends QpidTestCase
protected abstract void addConfiguration(UUID id, String type, Map<String, Object> attributes, UUID parentId);
+ protected abstract ConfigurationEntryStore reOpenStore();
+
protected final void addConfiguration(UUID id, String type, Map<String, Object> attributes)
{
addConfiguration(id, type, attributes, _brokerId);
@@ -434,4 +438,53 @@ public abstract class ConfigurationEntryStoreTestCase extends QpidTestCase
_store.save(newAuthenticationProviderConfigEntry, preferencesProviderEntry);
}
+ public void testAddHaVirtualHostWithReplicationNode()
+ {
+ String nodeName = "nodeName";
+ String groupName = "groupName";
+ String hostPort = "localhost:9999";
+ String helperHostPort = "localhost:8888";
+ Map<String, String> parameters = new HashMap<String, String>();
+ parameters.put("param1", "value1");
+ parameters.put("param2", "value2");
+ UUID nodeId = UUID.randomUUID();
+
+ Map<String, Object> nodeAttributes = new HashMap<String, Object>();
+ nodeAttributes.put(ReplicationNode.NAME, nodeName);
+ nodeAttributes.put(ReplicationNode.GROUP_NAME, groupName);
+ nodeAttributes.put(ReplicationNode.HOST_PORT, hostPort);
+ nodeAttributes.put(ReplicationNode.HELPER_HOST_PORT, helperHostPort);
+ nodeAttributes.put(ReplicationNode.PARAMETERS, parameters);
+
+ ConfigurationEntry nodeEntry = new ConfigurationEntry(nodeId, ReplicationNode.class.getSimpleName(), nodeAttributes,
+ Collections.<UUID>emptySet(), _store);
+ Map<String, Object> virtualHostAttributes = new HashMap<String, Object>();
+ virtualHostAttributes.put(VirtualHost.NAME, "ha");
+ virtualHostAttributes.put(VirtualHost.TYPE, "DUMMY-HA");
+ UUID virtualHostId = UUID.randomUUID();
+ Set<UUID> childrenIds = Collections.singleton(nodeId);
+ ConfigurationEntry hostEntry = new ConfigurationEntry(virtualHostId, VirtualHost.class.getSimpleName(), virtualHostAttributes,
+ childrenIds, _store);
+
+ ConfigurationEntry brokerEntry = _store.getRootEntry();
+ Set<UUID> brokerChildren = new HashSet<UUID>(brokerEntry.getChildrenIds());
+ brokerChildren.add(virtualHostId);
+ ConfigurationEntry newRootEntry = new ConfigurationEntry(brokerEntry.getId(), brokerEntry.getType(), brokerEntry.getAttributes(), brokerChildren, _store);
+ _store.save(hostEntry, nodeEntry, newRootEntry);
+
+ _store = reOpenStore();
+
+ ConfigurationEntry loadedHostEntry = _store.getEntry(virtualHostId);
+
+ assertEquals("Unexpected type", VirtualHost.class.getSimpleName(), loadedHostEntry.getType());
+ assertEquals("Unexpected virtual host id", virtualHostId, loadedHostEntry.getId());
+ assertEquals("Unexpected virtual host attributes", virtualHostAttributes, loadedHostEntry.getAttributes());
+ assertEquals("Unexpected virtual host children", childrenIds, loadedHostEntry.getChildrenIds());
+
+ ConfigurationEntry loadedNodeEntry = _store.getEntry(nodeId);
+ assertEquals("Unexpected type", ReplicationNode.class.getSimpleName(), loadedNodeEntry.getType());
+ assertEquals("Unexpected node id", nodeId, loadedNodeEntry.getId());
+ assertEquals("Unexpected node attributes", nodeAttributes, loadedNodeEntry.getAttributes());
+ assertTrue("Unexpected node children", loadedNodeEntry.getChildrenIds().isEmpty());
+ }
}
diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/JsonConfigurationEntryStoreTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/JsonConfigurationEntryStoreTest.java
index 2c59bfd453..0ca45f5b42 100644
--- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/JsonConfigurationEntryStoreTest.java
+++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/JsonConfigurationEntryStoreTest.java
@@ -71,6 +71,12 @@ public class JsonConfigurationEntryStoreTest extends ConfigurationEntryStoreTest
return store;
}
+ @Override
+ protected ConfigurationEntryStore reOpenStore()
+ {
+ return new JsonConfigurationEntryStore(_storeFile.getAbsolutePath(), null, false, Collections.<String,String>emptyMap());
+ }
+
private File createStoreFile(UUID brokerId, Map<String, Object> brokerAttributes) throws IOException,
JsonGenerationException, JsonMappingException
{
@@ -265,4 +271,5 @@ public class JsonConfigurationEntryStoreTest extends ConfigurationEntryStoreTest
assertEquals("Unexpected preferences provider type", FileSystemPreferencesProvider.PROVIDER_TYPE,
attributes.get(PreferencesProvider.TYPE));
}
+
}
diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/ManagementModeStoreHandlerTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/ManagementModeStoreHandlerTest.java
deleted file mode 100644
index 34b4fbf1ab..0000000000
--- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/ManagementModeStoreHandlerTest.java
+++ /dev/null
@@ -1,335 +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.server.configuration.store;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.any;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.UUID;
-
-import org.apache.qpid.server.BrokerOptions;
-import org.apache.qpid.server.configuration.ConfigurationEntry;
-import org.apache.qpid.server.configuration.ConfigurationEntryStore;
-import org.apache.qpid.server.configuration.IllegalConfigurationException;
-import org.apache.qpid.server.model.Broker;
-import org.apache.qpid.server.model.Port;
-import org.apache.qpid.server.model.Protocol;
-import org.apache.qpid.server.model.State;
-import org.apache.qpid.server.model.VirtualHost;
-import org.apache.qpid.test.utils.QpidTestCase;
-
-public class ManagementModeStoreHandlerTest extends QpidTestCase
-{
- private ManagementModeStoreHandler _handler;
- private BrokerOptions _options;
- private ConfigurationEntryStore _store;
- private ConfigurationEntry _root;
- private ConfigurationEntry _portEntry;
- private UUID _rootId, _portEntryId;
-
- protected void setUp() throws Exception
- {
- super.setUp();
- _rootId = UUID.randomUUID();
- _portEntryId = UUID.randomUUID();
- _store = mock(ConfigurationEntryStore.class);
- _root = mock(ConfigurationEntry.class);
- _portEntry = mock(ConfigurationEntry.class);
- when(_store.getRootEntry()).thenReturn(_root);
- when(_root.getId()).thenReturn(_rootId);
- when(_portEntry.getId()).thenReturn(_portEntryId);
- when(_store.getEntry(_portEntryId)).thenReturn(_portEntry);
- when(_store.getEntry(_rootId)).thenReturn(_root);
- when(_root.getChildrenIds()).thenReturn(Collections.singleton(_portEntryId));
- when(_portEntry.getType()).thenReturn(Port.class.getSimpleName());
- _options = new BrokerOptions();
- _handler = new ManagementModeStoreHandler(_store, _options);
- }
-
- public void testGetRootEntryWithEmptyOptions()
- {
- ConfigurationEntry root = _handler.getRootEntry();
- assertEquals("Unexpected root id", _rootId, root.getId());
- assertEquals("Unexpected children", Collections.singleton(_portEntryId), root.getChildrenIds());
- }
-
- public void testGetRootEntryWithHttpPortOverriden()
- {
- _options.setManagementModeHttpPortOverride(9090);
- _handler = new ManagementModeStoreHandler(_store, _options);
- ConfigurationEntry root = _handler.getRootEntry();
- assertEquals("Unexpected root id", _rootId, root.getId());
- Collection<UUID> childrenIds = root.getChildrenIds();
- assertEquals("Unexpected children size", 2, childrenIds.size());
- assertTrue("Store port entry id is not found", childrenIds.contains(_portEntryId));
- }
-
- public void testGetRootEntryWithRmiPortOverriden()
- {
- _options.setManagementModeRmiPortOverride(9090);
- _handler = new ManagementModeStoreHandler(_store, _options);
- ConfigurationEntry root = _handler.getRootEntry();
- assertEquals("Unexpected root id", _rootId, root.getId());
- Collection<UUID> childrenIds = root.getChildrenIds();
- assertEquals("Unexpected children size", 3, childrenIds.size());
- assertTrue("Store port entry id is not found", childrenIds.contains(_portEntryId));
- }
-
- public void testGetRootEntryWithConnectorPortOverriden()
- {
- _options.setManagementModeJmxPortOverride(9090);
- _handler = new ManagementModeStoreHandler(_store, _options);
- ConfigurationEntry root = _handler.getRootEntry();
- assertEquals("Unexpected root id", _rootId, root.getId());
- Collection<UUID> childrenIds = root.getChildrenIds();
- assertEquals("Unexpected children size", 2, childrenIds.size());
- assertTrue("Store port entry id is not found", childrenIds.contains(_portEntryId));
- }
-
- public void testGetRootEntryWithManagementPortsOverriden()
- {
- _options.setManagementModeHttpPortOverride(1000);
- _options.setManagementModeRmiPortOverride(2000);
- _options.setManagementModeJmxPortOverride(3000);
- _handler = new ManagementModeStoreHandler(_store, _options);
- ConfigurationEntry root = _handler.getRootEntry();
- assertEquals("Unexpected root id", _rootId, root.getId());
- Collection<UUID> childrenIds = root.getChildrenIds();
- assertEquals("Unexpected children size", 4, childrenIds.size());
- assertTrue("Store port entry id is not found", childrenIds.contains(_portEntryId));
- }
-
- public void testGetEntryByRootId()
- {
- ConfigurationEntry root = _handler.getEntry(_rootId);
- assertEquals("Unexpected root id", _rootId, root.getId());
- assertEquals("Unexpected children", Collections.singleton(_portEntryId), root.getChildrenIds());
- }
-
- public void testGetEntryByPortId()
- {
- ConfigurationEntry portEntry = _handler.getEntry(_portEntryId);
- assertEquals("Unexpected entry id", _portEntryId, portEntry.getId());
- assertTrue("Unexpected children", portEntry.getChildrenIds().isEmpty());
- assertEquals("Unexpected state", State.QUIESCED, portEntry.getAttributes().get(Port.STATE));
- }
-
- public void testGetEntryByCLIConnectorPortId()
- {
- _options.setManagementModeJmxPortOverride(9090);
- _handler = new ManagementModeStoreHandler(_store, _options);
-
- UUID optionsPort = getOptionsPortId();
- ConfigurationEntry portEntry = _handler.getEntry(optionsPort);
- assertCLIPortEntry(portEntry, optionsPort, Protocol.JMX_RMI);
- }
-
- public void testGetEntryByCLIHttpPortId()
- {
- _options.setManagementModeHttpPortOverride(9090);
- _handler = new ManagementModeStoreHandler(_store, _options);
-
- UUID optionsPort = getOptionsPortId();
- ConfigurationEntry portEntry = _handler.getEntry(optionsPort);
- assertCLIPortEntry(portEntry, optionsPort, Protocol.HTTP);
- }
-
- public void testHttpPortEntryIsQuiesced()
- {
- Map<String, Object> attributes = new HashMap<String, Object>();
- attributes.put(Port.PROTOCOLS, Collections.singleton(Protocol.HTTP));
- when(_portEntry.getAttributes()).thenReturn(attributes);
- _options.setManagementModeHttpPortOverride(9090);
- _handler = new ManagementModeStoreHandler(_store, _options);
-
- ConfigurationEntry portEntry = _handler.getEntry(_portEntryId);
- assertEquals("Unexpected state", State.QUIESCED, portEntry.getAttributes().get(Port.STATE));
- }
-
- public void testRmiPortEntryIsQuiesced()
- {
- Map<String, Object> attributes = new HashMap<String, Object>();
- attributes.put(Port.PROTOCOLS, Collections.singleton(Protocol.RMI));
- when(_portEntry.getAttributes()).thenReturn(attributes);
- _options.setManagementModeRmiPortOverride(9090);
- _handler = new ManagementModeStoreHandler(_store, _options);
-
- ConfigurationEntry portEntry = _handler.getEntry(_portEntryId);
- assertEquals("Unexpected state", State.QUIESCED, portEntry.getAttributes().get(Port.STATE));
- }
-
- public void testConnectorPortEntryIsQuiesced()
- {
- Map<String, Object> attributes = new HashMap<String, Object>();
- attributes.put(Port.PROTOCOLS, Collections.singleton(Protocol.JMX_RMI));
- when(_portEntry.getAttributes()).thenReturn(attributes);
- _options.setManagementModeRmiPortOverride(9090);
- _handler = new ManagementModeStoreHandler(_store, _options);
-
- ConfigurationEntry portEntry = _handler.getEntry(_portEntryId);
- assertEquals("Unexpected state", State.QUIESCED, portEntry.getAttributes().get(Port.STATE));
- }
-
- public void testVirtualHostEntryIsNotQuiescedByDefault()
- {
- virtualHostEntryQuiescedStatusTestImpl(false);
- }
-
- public void testVirtualHostEntryIsQuiescedWhenRequested()
- {
- virtualHostEntryQuiescedStatusTestImpl(true);
- }
-
- private void virtualHostEntryQuiescedStatusTestImpl(boolean mmQuiesceVhosts)
- {
- UUID virtualHostId = UUID.randomUUID();
- ConfigurationEntry virtualHost = mock(ConfigurationEntry.class);
- when(virtualHost.getId()).thenReturn(virtualHostId);
- when(virtualHost.getType()).thenReturn(VirtualHost.class.getSimpleName());
- Map<String, Object> attributes = new HashMap<String, Object>();
- attributes.put(VirtualHost.CONFIG_PATH, "/path/to/host.xml");
- when(virtualHost.getAttributes()).thenReturn(attributes);
- when(_store.getEntry(virtualHostId)).thenReturn(virtualHost);
- when(_root.getChildrenIds()).thenReturn(new HashSet<UUID>(Arrays.asList(_portEntryId, virtualHostId)));
-
- State expectedState = mmQuiesceVhosts ? State.QUIESCED : null;
- if(mmQuiesceVhosts)
- {
- _options.setManagementModeQuiesceVirtualHosts(mmQuiesceVhosts);
- }
-
- _handler = new ManagementModeStoreHandler(_store, _options);
-
- ConfigurationEntry hostEntry = _handler.getEntry(virtualHostId);
- Map<String, Object> hostAttributes = hostEntry.getAttributes();
- assertEquals("Unexpected state", expectedState, hostAttributes.get(VirtualHost.STATE));
- hostAttributes.remove(VirtualHost.STATE);
- assertEquals("Unexpected attributes", attributes, hostAttributes);
- }
-
- @SuppressWarnings("unchecked")
- private void assertCLIPortEntry(ConfigurationEntry portEntry, UUID optionsPort, Protocol protocol)
- {
- assertEquals("Unexpected entry id", optionsPort, portEntry.getId());
- assertTrue("Unexpected children", portEntry.getChildrenIds().isEmpty());
- Map<String, Object> attributes = portEntry.getAttributes();
- assertEquals("Unexpected name", "MANAGEMENT-MODE-PORT-" + protocol.name(), attributes.get(Port.NAME));
- assertEquals("Unexpected protocol", Collections.singleton(protocol), new HashSet<Protocol>(
- (Collection<Protocol>) attributes.get(Port.PROTOCOLS)));
- }
-
- public void testSavePort()
- {
- _options.setManagementModeHttpPortOverride(1000);
- _options.setManagementModeRmiPortOverride(2000);
- _options.setManagementModeJmxPortOverride(3000);
- _handler = new ManagementModeStoreHandler(_store, _options);
-
- Map<String, Object> attributes = new HashMap<String, Object>();
- attributes.put(Port.NAME, "TEST");
- ConfigurationEntry configurationEntry = new ConfigurationEntry(_portEntryId, Port.class.getSimpleName(), attributes,
- Collections.<UUID> emptySet(), null);
- _handler.save(configurationEntry);
- verify(_store).save(any(ConfigurationEntry.class));
- }
-
- public void testSaveRoot()
- {
- _options.setManagementModeHttpPortOverride(1000);
- _options.setManagementModeRmiPortOverride(2000);
- _options.setManagementModeJmxPortOverride(3000);
- _handler = new ManagementModeStoreHandler(_store, _options);
-
- ConfigurationEntry root = _handler.getRootEntry();
- Map<String, Object> attributes = new HashMap<String, Object>();
- attributes.put(Broker.NAME, "TEST");
- ConfigurationEntry configurationEntry = new ConfigurationEntry(_rootId, Broker.class.getSimpleName(), attributes,
- root.getChildrenIds(), null);
- _handler.save(configurationEntry);
- verify(_store).save(any(ConfigurationEntry.class));
- }
-
- public void testSaveCLIHttpPort()
- {
- _options.setManagementModeHttpPortOverride(1000);
- _handler = new ManagementModeStoreHandler(_store, _options);
-
- UUID portId = getOptionsPortId();
- Map<String, Object> attributes = new HashMap<String, Object>();
- attributes.put(Port.NAME, "TEST");
- ConfigurationEntry configurationEntry = new ConfigurationEntry(portId, Port.class.getSimpleName(), attributes,
- Collections.<UUID> emptySet(), null);
- try
- {
- _handler.save(configurationEntry);
- fail("Exception should be thrown on trying to save CLI port");
- }
- catch (IllegalConfigurationException e)
- {
- // pass
- }
- }
-
- public void testRemove()
- {
- _options.setManagementModeHttpPortOverride(1000);
- _handler = new ManagementModeStoreHandler(_store, _options);
-
- _handler.remove(_portEntryId);
- verify(_store).remove(_portEntryId);
- }
-
- public void testRemoveCLIPort()
- {
- _options.setManagementModeHttpPortOverride(1000);
- _handler = new ManagementModeStoreHandler(_store, _options);
- UUID portId = getOptionsPortId();
- try
- {
- _handler.remove(portId);
- fail("Exception should be thrown on trying to remove CLI port");
- }
- catch (IllegalConfigurationException e)
- {
- // pass
- }
- }
-
- private UUID getOptionsPortId()
- {
- ConfigurationEntry root = _handler.getRootEntry();
- assertEquals("Unexpected root id", _rootId, root.getId());
- Collection<UUID> childrenIds = root.getChildrenIds();
-
- childrenIds.remove(_portEntryId);
- UUID optionsPort = childrenIds.iterator().next();
- return optionsPort;
- }
-
-}
diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/MemoryConfigurationEntryStoreTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/MemoryConfigurationEntryStoreTest.java
index 508cd2b321..48c253d7e1 100644
--- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/MemoryConfigurationEntryStoreTest.java
+++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/MemoryConfigurationEntryStoreTest.java
@@ -60,6 +60,12 @@ public class MemoryConfigurationEntryStoreTest extends ConfigurationEntryStoreTe
store.save(newParentEntry, new ConfigurationEntry(id, type, attributes, Collections.<UUID> emptySet(), store));
}
+ @Override
+ protected ConfigurationEntryStore reOpenStore()
+ {
+ return getStore();
+ }
+
public void testCreateWithNullLocationAndNullInitialStore()
{
try
@@ -130,4 +136,5 @@ public class MemoryConfigurationEntryStoreTest extends ConfigurationEntryStoreTe
{
assertEquals("Unexpected type", "memory", getStore().getType());
}
+
}
diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListenerTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListenerTest.java
index c23c4715e8..292e12f380 100644
--- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListenerTest.java
+++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListenerTest.java
@@ -33,6 +33,7 @@ import org.apache.qpid.server.configuration.ConfigurationEntryStore;
import org.apache.qpid.server.model.Broker;
import org.apache.qpid.server.model.ConfiguredObject;
import org.apache.qpid.server.model.Queue;
+import org.apache.qpid.server.model.ReplicationNode;
import org.apache.qpid.server.model.State;
import org.apache.qpid.server.model.VirtualHost;
import org.apache.qpid.test.utils.QpidTestCase;
@@ -95,6 +96,28 @@ public class StoreConfigurationChangeListenerTest extends QpidTestCase
verifyNoMoreInteractions(_store);
}
+ public void testLocalReplicationNodeAddedForVirtualHost()
+ {
+ notifyBrokerStarted();
+
+ VirtualHost object = mock(VirtualHost.class);
+ ReplicationNode node = mock(ReplicationNode.class);
+ when(node.isLocal()).thenReturn(true);
+ _listener.childAdded(object, node);
+ verify(_store).save(any(ConfigurationEntry.class), any(ConfigurationEntry.class));
+ }
+
+ public void testRemoteReplicationNodeAddedForVirtualHost()
+ {
+ notifyBrokerStarted();
+
+ VirtualHost object = mock(VirtualHost.class);
+ ReplicationNode node = mock(ReplicationNode.class);
+ when(node.isLocal()).thenReturn(false);
+ _listener.childAdded(object, node);
+ verifyNoMoreInteractions(_store);
+ }
+
private void notifyBrokerStarted()
{
Broker broker = mock(Broker.class);
diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/model/ConfiguredObjectStateTransitionTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/model/ConfiguredObjectStateTransitionTest.java
index 1b7ef39b89..82ba20a4bd 100644
--- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/model/ConfiguredObjectStateTransitionTest.java
+++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/model/ConfiguredObjectStateTransitionTest.java
@@ -141,7 +141,7 @@ public class ConfiguredObjectStateTransitionTest extends QpidTestCase
private void assertAllInvalidStateTransitions(ConfigurationEntry providerEntry)
{
ConfiguredObject provider = createConfiguredObject(providerEntry);
- assertInvalidStateTransition(provider, State.INITIALISING, State.REPLICA);
+ assertInvalidStateTransition(provider, State.INITIALISING, State.UNAVAILABLE);
provider.setDesiredState(State.INITIALISING, State.QUIESCED);
assertInvalidStateTransition(provider, State.QUIESCED, State.INITIALISING);
@@ -153,7 +153,7 @@ public class ConfiguredObjectStateTransitionTest extends QpidTestCase
assertInvalidStateTransition(provider, State.DELETED, State.INITIALISING);
assertInvalidStateTransition(provider, State.DELETED, State.QUIESCED);
assertInvalidStateTransition(provider, State.DELETED, State.ACTIVE);
- assertInvalidStateTransition(provider, State.DELETED, State.REPLICA);
+ assertInvalidStateTransition(provider, State.DELETED, State.UNAVAILABLE);
assertInvalidStateTransition(provider, State.DELETED, State.ERRORED);
}
diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/model/VirtualHostTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/model/VirtualHostTest.java
index 05ac4a1ec3..3a9d706754 100644
--- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/model/VirtualHostTest.java
+++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/model/VirtualHostTest.java
@@ -23,13 +23,12 @@ package org.apache.qpid.server.model;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import java.io.File;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
-import junit.framework.TestCase;
-
import org.apache.qpid.server.configuration.ConfigurationEntry;
import org.apache.qpid.server.configuration.RecovererProvider;
import org.apache.qpid.server.configuration.startup.VirtualHostRecoverer;
@@ -42,6 +41,7 @@ import org.apache.qpid.server.store.TestMemoryMessageStore;
import org.apache.qpid.server.util.BrokerTestHelper;
import org.apache.qpid.server.virtualhost.StandardVirtualHostFactory;
import org.apache.qpid.test.utils.QpidTestCase;
+import org.apache.qpid.test.utils.TestFileUtils;
public class VirtualHostTest extends QpidTestCase
{
@@ -49,6 +49,7 @@ public class VirtualHostTest extends QpidTestCase
private Broker _broker;
private StatisticsGatherer _statisticsGatherer;
private RecovererProvider _recovererProvider;
+ private File _configFile;
@Override
protected void setUp() throws Exception
@@ -68,6 +69,10 @@ public class VirtualHostTest extends QpidTestCase
@Override
protected void tearDown() throws Exception
{
+ if (_configFile != null)
+ {
+ _configFile.delete();
+ }
super.tearDown();
CurrentActor.remove();
}
@@ -76,15 +81,17 @@ public class VirtualHostTest extends QpidTestCase
{
VirtualHost host = createHost();
- assertEquals("Unexpected state", State.INITIALISING, host.getAttribute(VirtualHost.STATE));
+ assertEquals("Unexpected state", State.INITIALISING, host.getActualState());
+ assertEquals("Unexpected desired state", State.ACTIVE, host.getDesiredState());
}
public void testActiveState()
{
VirtualHost host = createHost();
- host.setDesiredState(State.INITIALISING, State.ACTIVE);
- assertEquals("Unexpected state", State.ACTIVE, host.getAttribute(VirtualHost.STATE));
+ host.attainDesiredState();
+ assertEquals("Unexpected state", State.ACTIVE, host.getActualState());
+ assertEquals("Unexpected desired state", State.ACTIVE, host.getDesiredState());
}
public void testQuiescedState()
@@ -93,37 +100,64 @@ public class VirtualHostTest extends QpidTestCase
attributes.put(VirtualHost.NAME, getName());
attributes.put(VirtualHost.TYPE, StandardVirtualHostFactory.TYPE);
attributes.put(VirtualHost.STORE_TYPE, TestMemoryMessageStore.TYPE);
- attributes.put(VirtualHost.STATE, State.QUIESCED);
+ attributes.put(VirtualHost.DESIRED_STATE, State.QUIESCED);
VirtualHost host = createHost(attributes);
+ host.attainDesiredState();
- assertEquals("Unexpected state", State.QUIESCED, host.getAttribute(VirtualHost.STATE));
+ assertEquals("Unexpected state", State.QUIESCED, host.getActualState());
+ assertEquals("Unexpected desired state", State.QUIESCED, host.getDesiredState());
+ }
- host.setDesiredState(State.QUIESCED, State.ACTIVE);
- assertEquals("Unexpected state", State.ACTIVE, host.getAttribute(VirtualHost.STATE));
+ public void testChangeStateFromQuiescedToActiveViaAttributes()
+ {
+ Map<String, Object> attributes = new HashMap<String, Object>();
+ attributes.put(VirtualHost.NAME, getName());
+ attributes.put(VirtualHost.TYPE, StandardVirtualHostFactory.TYPE);
+ attributes.put(VirtualHost.STORE_TYPE, TestMemoryMessageStore.TYPE);
+ attributes.put(VirtualHost.DESIRED_STATE, State.QUIESCED);
+
+ VirtualHost host = createHost(attributes);
+ host.attainDesiredState();
+
+ assertEquals("Unexpected state", State.QUIESCED, host.getActualState());
+ assertEquals("Unexpected desired state", State.QUIESCED, host.getDesiredState());
+
+ attributes = new HashMap<String, Object>();
+ attributes.put(VirtualHost.NAME, getName());
+ attributes.put(VirtualHost.DESIRED_STATE, State.ACTIVE);
+ host.setAttributes(attributes);
+ assertEquals("Unexpected state", State.ACTIVE, host.getActualState());
+ assertEquals("Unexpected desired state", State.ACTIVE, host.getDesiredState());
}
- public void testStoppedState()
+ public void testChangeStateFromActiveToStoppedViaAttributes()
{
VirtualHost host = createHost();
+ assertEquals("Unexpected state", State.INITIALISING, host.getActualState());
+ assertEquals("Unexpected desired state", State.ACTIVE, host.getDesiredState());
- assertEquals("Unexpected state", State.INITIALISING, host.getAttribute(VirtualHost.STATE));
+ host.attainDesiredState();
+ assertEquals("Unexpected state", State.ACTIVE, host.getActualState());
+ assertEquals("Unexpected desired state", State.ACTIVE, host.getDesiredState());
- host.setDesiredState(State.INITIALISING, State.ACTIVE);
- assertEquals("Unexpected state", State.ACTIVE, host.getAttribute(VirtualHost.STATE));
+ Map<String, Object> attributes = new HashMap<String, Object>();
+ attributes.put(VirtualHost.NAME, getName());
+ attributes.put(VirtualHost.DESIRED_STATE, State.STOPPED);
+ host.setAttributes(attributes);
- host.setDesiredState(State.ACTIVE, State.STOPPED);
- assertEquals("Unexpected state", State.STOPPED, host.getAttribute(VirtualHost.STATE));
+ assertEquals("Unexpected state", State.STOPPED, host.getActualState());
+ assertEquals("Unexpected desired state", State.STOPPED, host.getDesiredState());
}
public void testDeletedState()
{
VirtualHost host = createHost();
- assertEquals("Unexpected state", State.INITIALISING, host.getAttribute(VirtualHost.STATE));
+ assertEquals("Unexpected state", State.INITIALISING, host.getActualState());
host.setDesiredState(State.INITIALISING, State.DELETED);
- assertEquals("Unexpected state", State.DELETED, host.getAttribute(VirtualHost.STATE));
+ assertEquals("Unexpected state", State.DELETED, host.getActualState());
}
public void testCreateQueueChildHavingMessageGroupingAttributes()
@@ -148,6 +182,18 @@ public class VirtualHostTest extends QpidTestCase
}
+ public void testCreateVirtualHostFromConfigurationFile()
+ {
+ String hostName = getName();
+ int maximuMessageAge = 123;
+ VirtualHost host = createHostFromConfiguration(hostName, maximuMessageAge);
+ host.setDesiredState(State.INITIALISING, State.ACTIVE);
+ assertEquals("Unexpected host name", hostName, host.getName());
+ assertEquals("Unexpected host type", StandardVirtualHostFactory.TYPE, host.getType());
+ assertEquals("Unexpected store type", TestMemoryMessageStore.TYPE, host.getAttribute(VirtualHost.STORE_TYPE));
+ assertEquals("Unexpected maximum message age alert", maximuMessageAge, host.getAttribute(VirtualHost.QUEUE_ALERT_THRESHOLD_MESSAGE_AGE));
+ }
+
private VirtualHost createHost()
{
Map<String, Object> attributes = new HashMap<String, Object>();
@@ -167,4 +213,18 @@ public class VirtualHostTest extends QpidTestCase
return new VirtualHostRecoverer(_statisticsGatherer).create(_recovererProvider, entry, _broker);
}
+ private VirtualHost createHostFromConfiguration(String hostName, long maximuMessageAge)
+ {
+ String content = "<virtualhosts><virtualhost><name>" + hostName + "</name><" + hostName + ">"
+ + "<queues><maximumMessageAge>" + maximuMessageAge + "</maximumMessageAge></queues>"
+ + "<store><class>" + TestMemoryMessageStore.class.getName() + "</class></store>"
+ + "</" + hostName + "></virtualhost></virtualhosts>";
+ _configFile = TestFileUtils.createTempFile(this, ".virtualhost.xml", content);
+ Map<String, Object> attributes = new HashMap<String, Object>();
+ attributes.put(VirtualHost.NAME, getName());
+ attributes.put(VirtualHost.CONFIG_PATH, _configFile.getAbsolutePath());
+ return createHost(attributes);
+ }
}
+
+ \ No newline at end of file
diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/virtualhost/DurableConfigurationRecovererTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/DurableConfigurationRecovererTest.java
index b71c6a92e6..9bce9398c1 100644
--- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/virtualhost/DurableConfigurationRecovererTest.java
+++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/DurableConfigurationRecovererTest.java
@@ -18,7 +18,7 @@
* under the License.
*
*/
-package org.apache.qpid.server.virtualhost;
+package org.apache.qpid.server.store;
import java.util.Arrays;
import java.util.Collections;
@@ -46,6 +46,11 @@ import org.apache.qpid.server.store.ConfiguredObjectRecord;
import org.apache.qpid.server.store.DurableConfigurationRecoverer;
import org.apache.qpid.server.store.DurableConfigurationStore;
import org.apache.qpid.server.store.DurableConfiguredObjectRecoverer;
+import org.apache.qpid.server.virtualhost.BindingRecoverer;
+import org.apache.qpid.server.virtualhost.DefaultUpgraderProvider;
+import org.apache.qpid.server.virtualhost.ExchangeRecoverer;
+import org.apache.qpid.server.virtualhost.QueueRecoverer;
+import org.apache.qpid.server.virtualhost.VirtualHost;
import org.apache.qpid.test.utils.QpidTestCase;
import org.mockito.ArgumentCaptor;
import org.mockito.invocation.InvocationOnMock;
@@ -59,6 +64,7 @@ import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.same;
import static org.apache.qpid.server.model.VirtualHost.CURRENT_CONFIG_VERSION;
@@ -404,6 +410,32 @@ public class DurableConfigurationRecovererTest extends QpidTestCase
assertEquals(customExchange, _vhost.getQueue(queueId).getAlternateExchange());
}
+ @SuppressWarnings("unchecked")
+ public void testRecovererCacheIsClearedAfterComplete()
+ {
+ String queueName = getName();
+ UUID bindingId = new UUID(99, 99);
+ when(_directExchange.getBinding(same(queueName), any(AMQQueue.class), any(Map.class))).thenReturn(mock(org.apache.qpid.server.binding.Binding.class));
+
+ _durableConfigurationRecoverer.beginConfigurationRecovery(_store, 2);
+ _durableConfigurationRecoverer.configuredObject(bindingId, Binding.class.getSimpleName(), createBinding(queueName, DIRECT_EXCHANGE_ID, QUEUE_ID));
+ _durableConfigurationRecoverer.configuredObject(QUEUE_ID, Queue.class.getSimpleName(), createQueue(queueName, DIRECT_EXCHANGE_ID));
+
+ Object cachedBinding = _durableConfigurationRecoverer.getResolvedObject(Binding.class.getSimpleName(), bindingId);
+ assertNotNull("Binding is not cached:" + cachedBinding, cachedBinding);
+
+ Object cachedQueue = _durableConfigurationRecoverer.getResolvedObject(Queue.class.getSimpleName(), QUEUE_ID);
+ assertNotNull("Queue is not cached:" + cachedQueue, cachedQueue);
+
+ _durableConfigurationRecoverer.completeConfigurationRecovery();
+
+ cachedBinding = _durableConfigurationRecoverer.getResolvedObject(Binding.class.getSimpleName(), bindingId);
+ assertNull("Binding is cached:" + cachedBinding, cachedBinding);
+
+ cachedQueue = _durableConfigurationRecoverer.getResolvedObject(Queue.class.getSimpleName(), QUEUE_ID);
+ assertNull("Queue is cached:" + cachedQueue, cachedQueue);
+ }
+
private void verifyCorrectUpdates(final ConfiguredObjectRecord[] expected) throws AMQStoreException
{
doAnswer(new Answer()
diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/MessageStoreQuotaEventsTestBase.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/MessageStoreQuotaEventsTestBase.java
index 7a4f92f0ca..daf2640bba 100644
--- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/MessageStoreQuotaEventsTestBase.java
+++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/MessageStoreQuotaEventsTestBase.java
@@ -27,8 +27,10 @@ import java.util.List;
import java.util.UUID;
import org.apache.log4j.Logger;
+import org.apache.qpid.AMQStoreException;
import org.apache.qpid.server.message.EnqueueableMessage;
import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.store.MessageStoreRecoveryHandler.StoredMessageRecoveryHandler;
import org.apache.qpid.test.utils.QpidTestCase;
import org.apache.qpid.util.FileUtils;
@@ -71,8 +73,10 @@ public abstract class MessageStoreQuotaEventsTestBase extends QpidTestCase imple
_store = createStore();
((DurableConfigurationStore)_store).configureConfigStore(vhost, null);
- _store.configureMessageStore(vhost, mock(MessageStoreRecoveryHandler.class), null);
-
+ MessageStoreRecoveryHandler recoveryHandler = mock(MessageStoreRecoveryHandler.class);
+ when(recoveryHandler.begin()).thenReturn(mock(StoredMessageRecoveryHandler.class));
+ _store.configureMessageStore(vhost, recoveryHandler, null);
+ _store.activate();
_transactionResource = UUID.randomUUID();
_events = new ArrayList<Event>();
_store.addEventListener(this, Event.PERSISTENT_MESSAGE_SIZE_OVERFULL, Event.PERSISTENT_MESSAGE_SIZE_UNDERFULL);
@@ -118,7 +122,7 @@ public abstract class MessageStoreQuotaEventsTestBase extends QpidTestCase imple
assertEvent(2, Event.PERSISTENT_MESSAGE_SIZE_UNDERFULL);
}
- protected EnqueueableMessage addMessage(long id)
+ protected EnqueueableMessage addMessage(long id) throws AMQStoreException
{
StorableMessageMetaData metaData = createMetaData(id, MESSAGE_DATA.length);
StoredMessage<?> handle = _store.addMessage(metaData);
diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/StateManagerTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/StateManagerTest.java
index 3ee98f9a21..16d18de713 100644
--- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/StateManagerTest.java
+++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/StateManagerTest.java
@@ -141,7 +141,7 @@ public class StateManagerTest extends TestCase implements EventListener
performInvalidTransitions(StateManager.INITIALISE, State.INITIALISED);
performInvalidTransitions(StateManager.INITIALISE_COMPLETE, State.ACTIVATING, State.CLOSING);
- performInvalidTransitions(StateManager.ACTIVATE, State.ACTIVE);
+ performInvalidTransitions(StateManager.ACTIVATE, State.ACTIVE, State.CLOSING);
performInvalidTransitions(StateManager.ACTIVATE_COMPLETE, State.QUIESCING, State.CLOSING, State.INITIALISED);
performInvalidTransitions(StateManager.QUIESCE, State.QUIESCED);
performInvalidTransitions(StateManager.QUIESCE_COMPLETE, State.ACTIVATING, State.CLOSING);
diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/util/BrokerTestHelper.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/util/BrokerTestHelper.java
index ed1ea01108..9671cd7a80 100644
--- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/util/BrokerTestHelper.java
+++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/util/BrokerTestHelper.java
@@ -111,6 +111,7 @@ public class BrokerTestHelper
{
virtualHostRegistry.registerVirtualHost(host);
}
+ host.activate();
return host;
}
diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/virtualhost/HouseKeepingTaskTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/virtualhost/HouseKeepingTaskTest.java
index 8b4a52bb79..e27598f035 100644
--- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/virtualhost/HouseKeepingTaskTest.java
+++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/virtualhost/HouseKeepingTaskTest.java
@@ -20,6 +20,9 @@
*/
package org.apache.qpid.server.virtualhost;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import org.apache.qpid.server.logging.LogActor;
import org.apache.qpid.server.logging.NullRootMessageLogger;
import org.apache.qpid.server.logging.actors.CurrentActor;
@@ -30,6 +33,16 @@ import java.util.concurrent.CountDownLatch;
public class HouseKeepingTaskTest extends QpidTestCase
{
+ private static final String HOUSE_KEEPING_TASK_TEST_VHOST = "HouseKeepingTaskTestVhost";
+ private VirtualHost _host;
+
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ _host = mock(VirtualHost.class);
+ when(_host.getName()).thenReturn(HOUSE_KEEPING_TASK_TEST_VHOST);
+ }
+
/**
* Tests that the abstract HouseKeepingTask properly cleans up any LogActor
* it adds to the CurrentActor stack by verifying the CurrentActor set
@@ -45,7 +58,7 @@ public class HouseKeepingTaskTest extends QpidTestCase
assertEquals("Expected LogActor was not returned", testActor, CurrentActor.get());
final CountDownLatch latch = new CountDownLatch(1);
- HouseKeepingTask testTask = new HouseKeepingTask(new MockVirtualHost("HouseKeepingTaskTestVhost"))
+ HouseKeepingTask testTask = new HouseKeepingTask(_host)
{
@Override
public void execute()
@@ -73,11 +86,9 @@ public class HouseKeepingTaskTest extends QpidTestCase
String originalThreadName = Thread.currentThread().getName();
- String vhostName = "HouseKeepingTaskTestVhost";
-
- String expectedThreadNameDuringExecution = vhostName + ":" + "ThreadNameRememberingTask";
+ String expectedThreadNameDuringExecution = HOUSE_KEEPING_TASK_TEST_VHOST + ":" + "ThreadNameRememberingTask";
- ThreadNameRememberingTask testTask = new ThreadNameRememberingTask(new MockVirtualHost(vhostName));
+ ThreadNameRememberingTask testTask = new ThreadNameRememberingTask(_host);
testTask.run();
diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/virtualhost/MockVirtualHost.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/virtualhost/MockVirtualHost.java
deleted file mode 100644
index 832b89c81a..0000000000
--- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/virtualhost/MockVirtualHost.java
+++ /dev/null
@@ -1,318 +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.server.virtualhost;
-
-import java.util.Collection;
-import java.util.Map;
-import java.util.concurrent.ScheduledFuture;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.server.configuration.VirtualHostConfiguration;
-import org.apache.qpid.server.connection.IConnectionRegistry;
-import org.apache.qpid.server.exchange.Exchange;
-import org.apache.qpid.server.message.MessageDestination;
-import org.apache.qpid.server.message.MessageSource;
-import org.apache.qpid.server.plugin.ExchangeType;
-import org.apache.qpid.server.protocol.LinkRegistry;
-import org.apache.qpid.server.queue.AMQQueue;
-import org.apache.qpid.server.queue.QueueRegistry;
-import org.apache.qpid.server.security.SecurityManager;
-import org.apache.qpid.server.security.auth.manager.AuthenticationManager;
-import org.apache.qpid.server.stats.StatisticsCounter;
-import org.apache.qpid.server.store.DurableConfigurationStore;
-import org.apache.qpid.server.store.MessageStore;
-import org.apache.qpid.server.txn.DtxRegistry;
-
-import java.util.UUID;
-
-public class MockVirtualHost implements VirtualHost
-{
- private String _name;
-
- public MockVirtualHost(String name)
- {
- _name = name;
- }
-
- public void close()
- {
-
- }
-
- @Override
- public VirtualHostRegistry getVirtualHostRegistry()
- {
- return null;
- }
-
- public AuthenticationManager getAuthenticationManager()
- {
- return null;
- }
-
- public DtxRegistry getDtxRegistry()
- {
- return null;
- }
-
- public VirtualHostConfiguration getConfiguration()
- {
- return null;
- }
-
- public IConnectionRegistry getConnectionRegistry()
- {
- return null;
- }
-
- public int getHouseKeepingActiveCount()
- {
- return 0;
- }
-
- public long getHouseKeepingCompletedTaskCount()
- {
- return 0;
- }
-
- public int getHouseKeepingPoolSize()
- {
- return 0;
- }
-
- public long getHouseKeepingTaskCount()
- {
- return 0;
- }
-
- public MessageStore getMessageStore()
- {
- return null;
- }
-
- public DurableConfigurationStore getDurableConfigurationStore()
- {
- return null;
- }
-
- public String getName()
- {
- return _name;
- }
-
- public QueueRegistry getQueueRegistry()
- {
- return null;
- }
-
- @Override
- public AMQQueue getQueue(String name)
- {
- return null;
- }
-
- @Override
- public MessageSource getMessageSource(final String name)
- {
- return null;
- }
-
- @Override
- public AMQQueue getQueue(UUID id)
- {
- return null;
- }
-
- @Override
- public Collection<AMQQueue> getQueues()
- {
- return null;
- }
-
- @Override
- public int removeQueue(AMQQueue queue) throws AMQException
- {
- return 0;
- }
-
- @Override
- public AMQQueue createQueue(UUID id,
- String queueName,
- boolean durable,
- String owner,
- boolean autoDelete,
- boolean exclusive,
- boolean deleteOnNoConsumer,
- Map<String, Object> arguments) throws AMQException
- {
- return null;
- }
-
- @Override
- public Exchange createExchange(UUID id,
- String exchange,
- String type,
- boolean durable,
- boolean autoDelete,
- String alternateExchange) throws AMQException
- {
- return null;
- }
-
- @Override
- public void removeExchange(Exchange exchange, boolean force) throws AMQException
- {
- }
-
- @Override
- public MessageDestination getMessageDestination(final String name)
- {
- return null;
- }
-
- @Override
- public Exchange getExchange(String name)
- {
- return null;
- }
-
- @Override
- public Exchange getExchange(UUID id)
- {
- return null;
- }
-
- @Override
- public Exchange getDefaultExchange()
- {
- return null;
- }
-
- @Override
- public Collection<Exchange> getExchanges()
- {
- return null;
- }
-
- @Override
- public Collection<ExchangeType<? extends Exchange>> getExchangeTypes()
- {
- return null;
- }
-
- public SecurityManager getSecurityManager()
- {
- return null;
- }
-
- @Override
- public void addVirtualHostListener(VirtualHostListener listener)
- {
- }
-
- public LinkRegistry getLinkRegistry(String remoteContainerId)
- {
- return null;
- }
-
- public ScheduledFuture<?> scheduleTask(long delay, Runnable timeoutTask)
- {
- return null;
- }
-
- public void scheduleHouseKeepingTask(long period, HouseKeepingTask task)
- {
-
- }
-
- public void setHouseKeepingPoolSize(int newSize)
- {
-
- }
-
-
- public long getCreateTime()
- {
- return 0;
- }
-
- public UUID getId()
- {
- return null;
- }
-
- public boolean isDurable()
- {
- return false;
- }
-
- public StatisticsCounter getDataDeliveryStatistics()
- {
- return null;
- }
-
- public StatisticsCounter getDataReceiptStatistics()
- {
- return null;
- }
-
- public StatisticsCounter getMessageDeliveryStatistics()
- {
- return null;
- }
-
- public StatisticsCounter getMessageReceiptStatistics()
- {
- return null;
- }
-
- public void initialiseStatistics()
- {
-
- }
-
- public void registerMessageDelivered(long messageSize)
- {
-
- }
-
- public void registerMessageReceived(long messageSize, long timestamp)
- {
-
- }
-
- public void resetStatistics()
- {
-
- }
-
- public State getState()
- {
- return State.ACTIVE;
- }
-
- public void block()
- {
- }
-
- public void unblock()
- {
- }
-}
diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/virtualhost/StandardVirtualHostTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/virtualhost/StandardVirtualHostTest.java
index f46349daa4..6699a756d5 100644
--- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/virtualhost/StandardVirtualHostTest.java
+++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/virtualhost/StandardVirtualHostTest.java
@@ -27,10 +27,8 @@ import static org.mockito.Mockito.when;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
-
import org.apache.qpid.server.binding.Binding;
import org.apache.qpid.server.configuration.VirtualHostConfiguration;
-
import org.apache.qpid.server.exchange.Exchange;
import org.apache.qpid.server.model.Broker;
import org.apache.qpid.server.queue.AMQQueue;
@@ -266,10 +264,18 @@ public class StandardVirtualHostTest extends QpidTestCase
_virtualHostRegistry = broker.getVirtualHostRegistry();
VirtualHostConfiguration configuration = new VirtualHostConfiguration(vhostName, config, broker);
- VirtualHost host = new StandardVirtualHostFactory().createVirtualHost(_virtualHostRegistry, mock(StatisticsGatherer.class), new SecurityManager(mock(Broker.class), false), configuration,
- mock(org.apache.qpid.server.model.VirtualHost.class));
- _virtualHostRegistry.registerVirtualHost(host);
+ return createVirtualHost(configuration);
+ }
+ private VirtualHost createVirtualHost(VirtualHostConfiguration configuration) throws Exception
+ {
+ org.apache.qpid.server.model.VirtualHost virtualHost = mock(org.apache.qpid.server.model.VirtualHost.class);
+ when(virtualHost.getAttribute(eq(org.apache.qpid.server.model.VirtualHost.STORE_TYPE))).thenReturn(
+ TestMemoryMessageStore.TYPE);
+ VirtualHost host = new StandardVirtualHostFactory().createVirtualHost(_virtualHostRegistry,
+ mock(StatisticsGatherer.class), new SecurityManager(mock(Broker.class), false), configuration, virtualHost);
+ _virtualHostRegistry.registerVirtualHost(host);
+ host.activate();
return host;
}
@@ -366,11 +372,6 @@ public class StandardVirtualHostTest extends QpidTestCase
Configuration config = new PropertiesConfiguration();
VirtualHostConfiguration configuration = new VirtualHostConfiguration(virtualHostName, config, broker);
- final org.apache.qpid.server.model.VirtualHost virtualHost = mock(org.apache.qpid.server.model.VirtualHost.class);
- when(virtualHost.getAttribute(eq(org.apache.qpid.server.model.VirtualHost.STORE_TYPE))).thenReturn(TestMemoryMessageStore.TYPE);
- VirtualHost host = new StandardVirtualHostFactory().createVirtualHost(_virtualHostRegistry, mock(StatisticsGatherer.class), new SecurityManager(mock(Broker.class), false), configuration,
- virtualHost);
- _virtualHostRegistry.registerVirtualHost(host);
- return host;
+ return createVirtualHost(configuration);
}
}
diff --git a/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ServerSessionDelegate.java b/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ServerSessionDelegate.java
index 9a90b74656..d4fb2761a4 100644
--- a/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ServerSessionDelegate.java
+++ b/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ServerSessionDelegate.java
@@ -392,7 +392,15 @@ public class ServerSessionDelegate extends SessionDelegate
private StoredMessage<MessageMetaData_0_10> createStoreMessage(final MessageTransfer xfr,
final MessageMetaData_0_10 messageMetaData, final MessageStore store)
{
- final StoredMessage<MessageMetaData_0_10> storeMessage = store.addMessage(messageMetaData);
+ StoredMessage<MessageMetaData_0_10> storeMessage;
+ try
+ {
+ storeMessage = store.addMessage(messageMetaData);
+ }
+ catch (AMQStoreException e)
+ {
+ throw new RuntimeException("Cannot store the message", e);
+ }
ByteBuffer body = xfr.getBody();
if(body != null)
{
diff --git a/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/ReceivingLink_1_0.java b/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/ReceivingLink_1_0.java
index 927972c8b2..df39ed667e 100644
--- a/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/ReceivingLink_1_0.java
+++ b/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/ReceivingLink_1_0.java
@@ -26,6 +26,8 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+
+import org.apache.qpid.AMQStoreException;
import org.apache.qpid.amqp_1_0.messaging.SectionDecoderImpl;
import org.apache.qpid.amqp_1_0.transport.DeliveryStateHandler;
import org.apache.qpid.amqp_1_0.transport.LinkEndpoint;
@@ -148,7 +150,15 @@ public class ReceivingLink_1_0 implements ReceivingLinkListener, Link_1_0, Deliv
_sectionDecoder,
immutableSections);
- StoredMessage<MessageMetaData_1_0> storedMessage = _vhost.getMessageStore().addMessage(mmd);
+ StoredMessage<MessageMetaData_1_0> storedMessage;
+ try
+ {
+ storedMessage = _vhost.getMessageStore().addMessage(mmd);
+ }
+ catch (AMQStoreException e)
+ {
+ throw new RuntimeException("Cannot store message", e);
+ }
boolean skipping = true;
int offset = 0;
diff --git a/qpid/java/broker-plugins/derby-store/src/main/java/org/apache/qpid/server/store/derby/DerbyMessageStore.java b/qpid/java/broker-plugins/derby-store/src/main/java/org/apache/qpid/server/store/derby/DerbyMessageStore.java
index bc8d157346..01b3890334 100644
--- a/qpid/java/broker-plugins/derby-store/src/main/java/org/apache/qpid/server/store/derby/DerbyMessageStore.java
+++ b/qpid/java/broker-plugins/derby-store/src/main/java/org/apache/qpid/server/store/derby/DerbyMessageStore.java
@@ -33,6 +33,7 @@ import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
+import org.apache.qpid.AMQStoreException;
import org.apache.qpid.server.model.VirtualHost;
import org.apache.qpid.server.store.AbstractJDBCMessageStore;
import org.apache.qpid.server.store.DurableConfigurationStore;
@@ -99,7 +100,7 @@ public class DerbyMessageStore extends AbstractJDBCMessageStore implements Messa
return "bigint";
}
- protected void doClose() throws SQLException
+ protected void doClose() throws AMQStoreException
{
try
{
@@ -117,7 +118,7 @@ public class DerbyMessageStore extends AbstractJDBCMessageStore implements Messa
else
{
getLogger().error("Exception whilst shutting down the store: " + e);
- throw e;
+ throw new AMQStoreException("Cannot close store", e);
}
}
}
diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java
index 3375a784ea..3992c707f4 100644
--- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java
+++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java
@@ -67,6 +67,7 @@ import org.apache.qpid.server.model.Port;
import org.apache.qpid.server.model.PreferencesProvider;
import org.apache.qpid.server.model.Protocol;
import org.apache.qpid.server.model.Queue;
+import org.apache.qpid.server.model.ReplicationNode;
import org.apache.qpid.server.model.Session;
import org.apache.qpid.server.model.State;
import org.apache.qpid.server.model.Transport;
@@ -223,7 +224,12 @@ public class HttpManagement extends AbstractPluginAdapter implements HttpManagem
int lastPort = -1;
for (Port port : ports)
{
- if (State.QUIESCED.equals(port.getActualState()))
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Http port " + port);
+ }
+
+ if (State.ACTIVE != port.getActualState())
{
continue;
}
@@ -326,6 +332,12 @@ public class HttpManagement extends AbstractPluginAdapter implements HttpManagem
connector.setHost(bindingAddress.trim());
}
connector.setPort(port.getPort());
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Added Jetty connector " + connector + " for port " + port);
+ }
+
server.addConnector(connector);
}
@@ -362,6 +374,7 @@ public class HttpManagement extends AbstractPluginAdapter implements HttpManagem
addRestServlet(root, "truststore", TrustStore.class);
addRestServlet(root, "plugin", Plugin.class);
addRestServlet(root, "preferencesprovider", AuthenticationProvider.class, PreferencesProvider.class);
+ addRestServlet(root, "replicationnode", VirtualHost.class, ReplicationNode.class);
root.addServlet(new ServletHolder(new UserPreferencesServlet()), "/rest/userpreferences/*");
root.addServlet(new ServletHolder(new LoggedOnUserPreferencesServlet()), "/rest/preferences");
@@ -523,4 +536,10 @@ public class HttpManagement extends AbstractPluginAdapter implements HttpManagem
}
}
+ @Override
+ public void close()
+ {
+ stop();
+ }
+
}
diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/UpdatableStore.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/UpdatableStore.js
index ea3ba78372..8156961149 100644
--- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/UpdatableStore.js
+++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/UpdatableStore.js
@@ -18,10 +18,11 @@
* under the License.
*
*/
-define(["dojo/store/Memory",
- "dojox/grid/DataGrid",
- "dojo/data/ObjectStore",
- "dojo/store/Observable"], function (Memory, DataGrid, ObjectStore, Observable) {
+define(["qpid/common/util",
+ "dojo/store/Memory",
+ "dojox/grid/DataGrid",
+ "dojo/data/ObjectStore",
+ "dojo/store/Observable"], function (util, Memory, DataGrid, ObjectStore, Observable) {
function UpdatableStore( data, divName, structure, func, props, Grid, notObservable ) {
@@ -86,19 +87,13 @@ define(["dojo/store/Memory",
if(data) {
for(var i=0; i < data.length; i++) {
if(theItem = store.get(data[i].id)) {
- var modified;
- for(var propName in data[i]) {
- if(data[i].hasOwnProperty(propName)) {
- if(theItem[ propName ] != data[i][ propName ]) {
- theItem[ propName ] = data[i][ propName ];
- modified = true;
- changed = true;
- }
- }
- }
+ var modified = !util.equals(theItem, data[i]);
if(modified) {
- // ... check attributes for updates
- store.notify(theItem, data[i].id);
+ store.put(data[i], {overwrite: true});
+ if (store instanceof Observable)
+ {
+ store.notify(theItem, data[i].id);
+ }
changed = true;
}
} else {
diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/formatter.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/formatter.js
index 2f8683ee1c..38255237db 100644
--- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/formatter.js
+++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/formatter.js
@@ -27,6 +27,10 @@ define(function () {
var returnVal = { units: "B",
value: "0"};
+ if (!amount)
+ {
+ return returnVal;
+ }
if(amount < 1000)
{
@@ -56,6 +60,10 @@ define(function () {
{
var returnVal = { units: "ms",
value: "0"};
+ if (!amount)
+ {
+ return returnVal;
+ }
if(amount < 1000)
{
diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js
index 3d349830ac..12068fa101 100644
--- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js
+++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js
@@ -369,6 +369,94 @@ define(["dojo/_base/xhr",
{
alert(error);
}
+ };
+
+ util.equals = function(object1, object2)
+ {
+ if (object1 && object2)
+ {
+ if (typeof object1 != typeof object2)
+ {
+ return false;
+ }
+ else
+ {
+ if (object1 instanceof Array || typeof object1 == "array")
+ {
+ if (object1.length != object2.length)
+ {
+ return false;
+ }
+
+ for (var i = 0, l=object1.length; i < l; i++)
+ {
+ var item = object1[i];
+ if (item && (item instanceof Array || typeof item == "array" || item instanceof Object))
+ {
+ if (!this.equals(item, object2[i]))
+ {
+ return false;
+ }
+ }
+ else if (item != object2[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ else if (object1 instanceof Object)
+ {
+ for (propName in object1)
+ {
+ if (object1.hasOwnProperty(propName) != object2.hasOwnProperty(propName))
+ {
+ return false;
+ }
+ else if (typeof object1[propName] != typeof object2[propName])
+ {
+ return false;
+ }
+ }
+
+ for(propName in object2)
+ {
+ var object1Prop = object1[propName];
+ var object2Prop = object2[propName];
+
+ if (object2.hasOwnProperty(propName) != object1.hasOwnProperty(propName))
+ {
+ return false;
+ }
+ else if (typeof object1Prop != typeof object2Prop)
+ {
+ return false;
+ }
+
+ if(!object2.hasOwnProperty(propName))
+ {
+ // skip functions
+ continue;
+ }
+
+ if (object1Prop && (object1Prop instanceof Array || typeof object1Prop == "array" || object1Prop instanceof Object))
+ {
+ if (!this.equals(object1Prop, object2Prop))
+ {
+ return false;
+ }
+ }
+ else if(object1Prop != object2Prop)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ }
+ return object1 === object2;
}
return util;
diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/VirtualHost.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/VirtualHost.js
index e92fb4492e..c9723e0dfb 100644
--- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/VirtualHost.js
+++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/VirtualHost.js
@@ -32,6 +32,7 @@ define(["dojo/_base/xhr",
"qpid/management/addQueue",
"qpid/management/addExchange",
"dojox/grid/EnhancedGrid",
+ "dijit/form/ToggleButton",
"dojo/domReady!"],
function (xhr, parser, query, connect, registry, entities, properties, updater, util, formatter, UpdatableStore, addQueue, addExchange, EnhancedGrid) {
@@ -63,8 +64,6 @@ define(["dojo/_base/xhr",
updater.add( that.vhostUpdater );
- that.vhostUpdater.update();
-
var addQueueButton = query(".addQueueButton", contentPane.containerNode)[0];
connect.connect(registry.byNode(addQueueButton), "onClick", function(evt){ addQueue.show(that.name) });
@@ -95,6 +94,23 @@ define(["dojo/_base/xhr",
);
}});
+ var vhostData = this.vhostUpdater.vhostData;
+ var virtualHostDetailsDiv = query(".virtualHostDetails", contentPane.containerNode)[0];
+ require(["qpid/management/virtualhost/" + vhostData.type.toLowerCase() + "/show"],
+ function(VirtualHostDetails) {
+ try
+ {
+ that.vhostUpdater.details = new VirtualHostDetails(virtualHostDetailsDiv);
+ that.vhostUpdater.details.update(vhostData);
+ }
+ catch(e)
+ {
+ if (console && console.error)
+ {
+ console.error(e);
+ }
+ }
+ });
};
VirtualHost.prototype.close = function() {
@@ -118,6 +134,7 @@ define(["dojo/_base/xhr",
}
storeNodes(["name",
+ "type",
"state",
"durable",
"lifetimePolicy",
@@ -136,15 +153,13 @@ define(["dojo/_base/xhr",
"msgOutRate",
"bytesOutRate",
"bytesOutRateUnits",
- "storeType",
- "storePath",
"configPath"]);
this.query = "rest/virtualhost/"+ encodeURIComponent(vhost.name);
var that = this;
- xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}).then(function(data) {
+ xhr.get({url: this.query, sync: true, handleAs: "json"}).then(function(data) {
that.vhostData = data[0];
if (!that.vhostData.hasOwnProperty("configPath"))
@@ -172,7 +187,7 @@ define(["dojo/_base/xhr",
}};
that.updateHeader();
- that.queuesGrid = new UpdatableStore(that.vhostData.queues, findNode("queues"),
+ that.queuesGrid = new UpdatableStore(that.vhostData.queues || [], findNode("queues"),
[ { name: "Name", field: "name", width: "90px"},
{ name: "Messages", field: "queueDepthMessages", width: "90px"},
{ name: "Arguments", field: "arguments", width: "100%"}
@@ -188,7 +203,7 @@ define(["dojo/_base/xhr",
});
} , gridProperties, EnhancedGrid);
- that.exchangesGrid = new UpdatableStore(that.vhostData.exchanges, findNode("exchanges"),
+ that.exchangesGrid = new UpdatableStore(that.vhostData.exchanges || [], findNode("exchanges"),
[
{ name: "Name", field: "name", width: "120px"},
{ name: "Type", field: "type", width: "120px"},
@@ -206,7 +221,7 @@ define(["dojo/_base/xhr",
} , gridProperties, EnhancedGrid);
- that.connectionsGrid = new UpdatableStore(that.vhostData.connections,
+ that.connectionsGrid = new UpdatableStore(that.vhostData.connections || [],
findNode("connections"),
[ { name: "Name", field: "name", width: "150px"},
{ name: "User", field: "principal", width: "120px"},
@@ -232,9 +247,6 @@ define(["dojo/_base/xhr",
controller.show("connection", connectionName, vhost, theItem.id);
});
} );
-
-
-
});
}
@@ -242,150 +254,172 @@ define(["dojo/_base/xhr",
Updater.prototype.updateHeader = function()
{
this.name.innerHTML = entities.encode(String(this.vhostData[ "name" ]));
+ this.type.innerHTML = entities.encode(String(this.vhostData[ "type" ]));
this.state.innerHTML = entities.encode(String(this.vhostData[ "state" ]));
this.durable.innerHTML = entities.encode(String(this.vhostData[ "durable" ]));
this.lifetimePolicy.innerHTML = entities.encode(String(this.vhostData[ "lifetimePolicy" ]));
- this.storeType.innerHTML = entities.encode(String(this.vhostData[ "storeType" ]));
- this.storePath.innerHTML = entities.encode(String(this.vhostData[ "storePath" ]));
this.configPath.innerHTML = entities.encode(String(this.vhostData[ "configPath" ]));
};
Updater.prototype.update = function()
{
-
var thisObj = this;
xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"})
.then(function(data) {
- thisObj.vhostData = data[0];
- util.flattenStatistics( thisObj.vhostData );
- var connections = thisObj.vhostData[ "connections" ];
- var queues = thisObj.vhostData[ "queues" ];
- var exchanges = thisObj.vhostData[ "exchanges" ];
+ try
+ {
+ thisObj.performUpdate(data[0]);
+ }
+ catch(e)
+ {
+ if (console && console.error)
+ {
+ console.error(e);
+ }
+ }
+ });
+ };
- thisObj.updateHeader();
+ Updater.prototype.performUpdate = function(vhostData)
+ {
+ this.vhostData = vhostData;
+ util.flattenStatistics( this.vhostData );
+ var connections = this.vhostData[ "connections" ];
+ var queues = this.vhostData[ "queues" ];
+ var exchanges = this.vhostData[ "exchanges" ];
+ this.updateHeader();
- // update alerting info
- var alertRepeatGap = formatter.formatTime( thisObj.vhostData["queue.alertRepeatGap"] );
+ // update alerting info
+ var alertRepeatGap = formatter.formatTime( this.vhostData["queue.alertRepeatGap"] );
- thisObj.alertRepeatGap.innerHTML = alertRepeatGap.value;
- thisObj.alertRepeatGapUnits.innerHTML = alertRepeatGap.units;
+ this.alertRepeatGap.innerHTML = alertRepeatGap.value;
+ this.alertRepeatGapUnits.innerHTML = alertRepeatGap.units;
- var alertMsgAge = formatter.formatTime( thisObj.vhostData["queue.alertThresholdMessageAge"] );
+ var alertMsgAge = formatter.formatTime( this.vhostData["queue.alertThresholdMessageAge"] );
- thisObj.alertThresholdMessageAge.innerHTML = alertMsgAge.value;
- thisObj.alertThresholdMessageAgeUnits.innerHTML = alertMsgAge.units;
+ this.alertThresholdMessageAge.innerHTML = alertMsgAge.value;
+ this.alertThresholdMessageAgeUnits.innerHTML = alertMsgAge.units;
- var alertMsgSize = formatter.formatBytes( thisObj.vhostData["queue.alertThresholdMessageSize"] );
+ var alertMsgSize = formatter.formatBytes( this.vhostData["queue.alertThresholdMessageSize"] );
- thisObj.alertThresholdMessageSize.innerHTML = alertMsgSize.value;
- thisObj.alertThresholdMessageSizeUnits.innerHTML = alertMsgSize.units;
+ this.alertThresholdMessageSize.innerHTML = alertMsgSize.value;
+ this.alertThresholdMessageSizeUnits.innerHTML = alertMsgSize.units;
- var alertQueueDepth = formatter.formatBytes( thisObj.vhostData["queue.alertThresholdQueueDepthBytes"] );
+ var alertQueueDepth = formatter.formatBytes( this.vhostData["queue.alertThresholdQueueDepthBytes"] );
- thisObj.alertThresholdQueueDepthBytes.innerHTML = alertQueueDepth.value;
- thisObj.alertThresholdQueueDepthBytesUnits.innerHTML = alertQueueDepth.units;
+ this.alertThresholdQueueDepthBytes.innerHTML = alertQueueDepth.value;
+ this.alertThresholdQueueDepthBytesUnits.innerHTML = alertQueueDepth.units;
- thisObj.alertThresholdQueueDepthMessages.innerHTML = entities.encode(String(thisObj.vhostData["queue.alertThresholdQueueDepthMessages"]));
+ this.alertThresholdQueueDepthMessages.innerHTML = entities.encode(String(this.vhostData["queue.alertThresholdQueueDepthMessages"]));
- var stats = thisObj.vhostData[ "statistics" ];
+ var stats = this.vhostData[ "statistics" ] || {};
- var sampleTime = new Date();
- var messageIn = stats["messagesIn"];
- var bytesIn = stats["bytesIn"];
- var messageOut = stats["messagesOut"];
- var bytesOut = stats["bytesOut"];
+ var sampleTime = new Date();
+ var messageIn = stats["messagesIn"];
+ var bytesIn = stats["bytesIn"];
+ var messageOut = stats["messagesOut"];
+ var bytesOut = stats["bytesOut"];
- if(thisObj.sampleTime)
- {
- var samplePeriod = sampleTime.getTime() - thisObj.sampleTime.getTime();
-
- var msgInRate = (1000 * (messageIn - thisObj.messageIn)) / samplePeriod;
- var msgOutRate = (1000 * (messageOut - thisObj.messageOut)) / samplePeriod;
- var bytesInRate = (1000 * (bytesIn - thisObj.bytesIn)) / samplePeriod;
- var bytesOutRate = (1000 * (bytesOut - thisObj.bytesOut)) / samplePeriod;
-
- thisObj.msgInRate.innerHTML = msgInRate.toFixed(0);
- var bytesInFormat = formatter.formatBytes( bytesInRate );
- thisObj.bytesInRate.innerHTML = "(" + bytesInFormat.value;
- thisObj.bytesInRateUnits.innerHTML = bytesInFormat.units + "/s)";
-
- thisObj.msgOutRate.innerHTML = msgOutRate.toFixed(0);
- var bytesOutFormat = formatter.formatBytes( bytesOutRate );
- thisObj.bytesOutRate.innerHTML = "(" + bytesOutFormat.value;
- thisObj.bytesOutRateUnits.innerHTML = bytesOutFormat.units + "/s)";
-
- if(connections && thisObj.connections)
- {
- for(var i=0; i < connections.length; i++)
- {
- var connection = connections[i];
- for(var j = 0; j < thisObj.connections.length; j++)
- {
- var oldConnection = thisObj.connections[j];
- if(oldConnection.id == connection.id)
- {
- msgOutRate = (1000 * (connection.messagesOut - oldConnection.messagesOut)) /
- samplePeriod;
- connection.msgOutRate = msgOutRate.toFixed(0) + "msg/s";
-
- bytesOutRate = (1000 * (connection.bytesOut - oldConnection.bytesOut)) /
- samplePeriod;
- var bytesOutRateFormat = formatter.formatBytes( bytesOutRate );
- connection.bytesOutRate = bytesOutRateFormat.value + bytesOutRateFormat.units + "/s";
-
-
- msgInRate = (1000 * (connection.messagesIn - oldConnection.messagesIn)) /
- samplePeriod;
- connection.msgInRate = msgInRate.toFixed(0) + "msg/s";
-
- bytesInRate = (1000 * (connection.bytesIn - oldConnection.bytesIn)) /
- samplePeriod;
- var bytesInRateFormat = formatter.formatBytes( bytesInRate );
- connection.bytesInRate = bytesInRateFormat.value + bytesInRateFormat.units + "/s";
- }
-
-
- }
-
- }
- }
- }
+ if(this.sampleTime)
+ {
+ var samplePeriod = sampleTime.getTime() - this.sampleTime.getTime();
- thisObj.sampleTime = sampleTime;
- thisObj.messageIn = messageIn;
- thisObj.bytesIn = bytesIn;
- thisObj.messageOut = messageOut;
- thisObj.bytesOut = bytesOut;
- thisObj.connections = connections;
+ var msgInRate = (1000 * (messageIn - this.messageIn)) / samplePeriod;
+ var msgOutRate = (1000 * (messageOut - this.messageOut)) / samplePeriod;
+ var bytesInRate = (1000 * (bytesIn - this.bytesIn)) / samplePeriod;
+ var bytesOutRate = (1000 * (bytesOut - this.bytesOut)) / samplePeriod;
- // update queues
- thisObj.queuesGrid.update(thisObj.vhostData.queues);
+ this.msgInRate.innerHTML = msgInRate.toFixed(0);
+ var bytesInFormat = formatter.formatBytes( bytesInRate );
+ this.bytesInRate.innerHTML = "(" + bytesInFormat.value;
+ this.bytesInRateUnits.innerHTML = bytesInFormat.units + "/s)";
- // update exchanges
- thisObj.exchangesGrid.update(thisObj.vhostData.exchanges);
+ this.msgOutRate.innerHTML = msgOutRate.toFixed(0);
+ var bytesOutFormat = formatter.formatBytes( bytesOutRate );
+ this.bytesOutRate.innerHTML = "(" + bytesOutFormat.value;
+ this.bytesOutRateUnits.innerHTML = bytesOutFormat.units + "/s)";
- var exchangesGrid = thisObj.exchangesGrid.grid;
- for(var i=0; i< thisObj.vhostData.exchanges.length; i++)
- {
- var data = exchangesGrid.getItem(i);
- var isStandard = false;
- if (data && data.name)
- {
- isStandard = util.isReservedExchangeName(data.name);
- }
- exchangesGrid.rowSelectCell.setDisabled(i, isStandard);
- }
+ if(connections && this.connections)
+ {
+ for(var i=0; i < connections.length; i++)
+ {
+ var connection = connections[i];
+ for(var j = 0; j < this.connections.length; j++)
+ {
+ var oldConnection = this.connections[j];
+ if(oldConnection.id == connection.id)
+ {
+ msgOutRate = (1000 * (connection.messagesOut - oldConnection.messagesOut)) /
+ samplePeriod;
+ connection.msgOutRate = msgOutRate.toFixed(0) + "msg/s";
- // update connections
- thisObj.connectionsGrid.update(thisObj.vhostData.connections)
+ bytesOutRate = (1000 * (connection.bytesOut - oldConnection.bytesOut)) /
+ samplePeriod;
+ var bytesOutRateFormat = formatter.formatBytes( bytesOutRate );
+ connection.bytesOutRate = bytesOutRateFormat.value + bytesOutRateFormat.units + "/s";
- });
- };
+ msgInRate = (1000 * (connection.messagesIn - oldConnection.messagesIn)) /
+ samplePeriod;
+ connection.msgInRate = msgInRate.toFixed(0) + "msg/s";
+
+ bytesInRate = (1000 * (connection.bytesIn - oldConnection.bytesIn)) /
+ samplePeriod;
+ var bytesInRateFormat = formatter.formatBytes( bytesInRate );
+ connection.bytesInRate = bytesInRateFormat.value + bytesInRateFormat.units + "/s";
+ }
+
+
+ }
+
+ }
+ }
+ }
+ this.sampleTime = sampleTime;
+ this.messageIn = messageIn;
+ this.bytesIn = bytesIn;
+ this.messageOut = messageOut;
+ this.bytesOut = bytesOut;
+ this.connections = connections;
+
+ // update queues
+ if (this.vhostData.queues)
+ {
+ this.queuesGrid.update(this.vhostData.queues);
+ }
+
+ // update exchanges
+ if (this.vhostData.exchanges)
+ {
+ this.exchangesGrid.update(this.vhostData.exchanges);
+ var exchangesGrid = this.exchangesGrid.grid;
+ for(var i=0; i< this.vhostData.exchanges.length; i++)
+ {
+ var data = exchangesGrid.getItem(i);
+ var isStandard = false;
+ if (data && data.name)
+ {
+ isStandard = util.isReservedExchangeName(data.name);
+ }
+ exchangesGrid.rowSelectCell.setDisabled(i, isStandard);
+ }
+ }
+
+ // update connections
+ if (this.vhostData.connections)
+ {
+ this.connectionsGrid.update(this.vhostData.connections);
+ }
+
+ if (this.details)
+ {
+ this.details.update(this.vhostData);
+ }
+ }
return VirtualHost;
});
diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addVirtualHost.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addVirtualHost.js
index 4873baeef7..b4db56b4e1 100644
--- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addVirtualHost.js
+++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addVirtualHost.js
@@ -123,15 +123,27 @@ define(["dojo/_base/xhr",
alert("Either configuration file or type have to be specified!");
return false;
}
- var newVirtualHost = convertToVirtualHost(formValues);
- var that = this;
- xhr.put({url: "rest/virtualhost/" + encodeURIComponent(newVirtualHost.name),
- sync: true, handleAs: "json",
- headers: { "Content-Type": "application/json"},
- putData: json.toJson(newVirtualHost),
- load: function(x) {that.success = true; },
- error: function(error) {that.success = false; that.failureReason = error;}});
+ if (addVirtualHost.typeSpecificWidget && addVirtualHost.typeSpecificWidget.save
+ && typeof addVirtualHost.typeSpecificWidget.save == "function")
+ {
+ this.success = addVirtualHost.typeSpecificWidget.save();
+ if (!this.success)
+ {
+ this.failureReason = addVirtualHost.failureReason
+ }
+ }
+ else
+ {
+ var that = this;
+ var newVirtualHost = convertToVirtualHost(formValues);
+ xhr.put({url: "rest/virtualhost/" + encodeURIComponent(newVirtualHost.name),
+ sync: true, handleAs: "json",
+ headers: { "Content-Type": "application/json"},
+ putData: json.toJson(newVirtualHost),
+ load: function(){ that.success = true; },
+ error: function(error) {that.success = false; that.failureReason = error;}});
+ }
if(this.success === true)
{
@@ -156,12 +168,14 @@ define(["dojo/_base/xhr",
function(vhostType)
{
vhostType.show();
+ addVirtualHost.typeSpecificWidget = vhostType;
});
}
}
addVirtualHost.show = function() {
var that = this;
+ this.typeSpecificWidget = null;
dom.byId("addVirtualHost.typeSpecificDiv").innerHTML = "";
registry.byId("formAddVirtualHost").reset();
dojo.byId("formAddVirtualHost.id").value="";
diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/virtualhost/standard/show.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/virtualhost/standard/show.js
new file mode 100644
index 0000000000..4970936b63
--- /dev/null
+++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/virtualhost/standard/show.js
@@ -0,0 +1,100 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+define(["dojo/_base/xhr",
+ "dojo/parser",
+ "dojo/string",
+ "dojox/html/entities",
+ "dojo/query",
+ "dijit/registry",
+ "dojox/grid/EnhancedGrid",
+ "qpid/common/UpdatableStore",
+ "qpid/common/formatter",
+ "dojo/domReady!"],
+ function (xhr, parser, json, entities, query, registry, EnhancedGrid, UpdatableStore, formatter) {
+
+ var fields = ["storePath", "storeType", "desiredState"];
+
+ var buttons = [];
+
+ function findNode(nodeClass, containerNode)
+ {
+ return query("." + nodeClass, containerNode)[0];
+ }
+
+ function Standard(containerNode) {
+ var that = this;
+ xhr.get({url: "virtualhost/standard/show.html",
+ sync: true,
+ load: function(template) {
+ that._init(template, containerNode);
+ }});
+ }
+
+ Standard.prototype.update=function(data)
+ {
+ for(var i = 0; i < fields.length; i++)
+ {
+ var name = fields[i];
+ this[name].innerHTML = entities.encode(String(data[name]));
+ }
+ };
+
+ Standard.prototype._init = function(template, containerNode)
+ {
+ containerNode.innerHTML = template;
+ parser.parse(containerNode);
+ this._initFields(fields, containerNode);
+ for(var i = 0; i < buttons.length; i++)
+ {
+ var buttonName = buttons[i];
+ var buttonNode = findNode(buttonName, containerNode);
+ if (buttonNode)
+ {
+ var buttonWidget = registry.byNode(buttonNode);
+ if (buttonWidget)
+ {
+ this[buttonName] = buttonWidget;
+ var handler = this["_onClick_" + buttonName];
+ if (handler)
+ {
+ buttonWidget.on("click", function(evt){
+ handler(evt);
+ });
+ }
+ else
+ {
+ //buttonWidget.set("disabled", true);
+ }
+ }
+ }
+ }
+ }
+
+ Standard.prototype._initFields = function(fields, containerNode)
+ {
+ for(var i = 0; i < fields.length; i++)
+ {
+ this[fields[i]] = findNode(fields[i], containerNode);
+ }
+ }
+
+ return Standard;
+});
diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/showVirtualHost.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/showVirtualHost.html
index 69856365db..a0142d77c0 100644
--- a/qpid/java/broker-plugins/management-http/src/main/java/resources/showVirtualHost.html
+++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/showVirtualHost.html
@@ -21,103 +21,85 @@
-->
<div class="virtualhost">
- <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Virtual Host Attributes', open: true">
- <div style="clear:both">
- <div class="formLabel-labelCell" style="float:left; width: 100px;">Name:</div>
- <div class="name" style="float:left;"></div>
- </div>
- <div style="clear:both">
- <div class="formLabel-labelCell" style="float:left; width: 100px;">State:</div>
- <div class="state" style="float:left;"></div>
- </div>
- <div style="clear:both">
- <div class="formLabel-labelCell" style="float:left; width: 100px;">Durable:</div>
- <div class="durable" style="float:left;"></div>
- </div>
- <div style="clear:both">
- <div class="formLabel-labelCell" style="float:left; width: 100px;">Lifespan:</div>
- <div class="lifetimePolicy" style="float:left;"></div>
- </div>
- <div style="clear:both">
- <div class="formLabel-labelCell" style="float:left; width: 100px;">Inbound:</div>
- <div style="float:left;">
- <span class="msgInRate"></span>
- <span> msg/s</span>
- <span class="bytesInRate"></span>
- <span class="bytesInRateUnits"></span>
- </div>
- </div>
- <div style="clear:both">
- <div class="formLabel-labelCell" style="float:left; width: 100px;">Outbound:</div>
- <div style="float:left;">
- <span class="msgOutRate"></span>
- <span> msg/s</span>
- <span class="bytesOutRate"></span>
- <span class="bytesOutRateUnits"></span>
- </div>
- </div>
- <div style="clear:both">
- <div class="formLabel-labelCell" style="float:left; width: 100px;">Store Type:</div>
- <div class="storeType" style="float:left;"></div>
- </div>
- <div style="clear:both">
- <div class="formLabel-labelCell" style="float:left; width: 100px;">Store Path:</div>
- <div class="storePath" style="float:left;"></div>
- </div>
- <div class="configPathDiv" style="clear:both">
- <div class="formLabel-labelCell" style="float:left; width: 100px;">Config Path:</div>
- <div class="configPath" style="float:left;"></div>
+
+ <div style="clear:both">
+ <div class="formLabel-labelCell" style="float:left; width: 200px">Name:</div>
+ <div class="name" style="float:left;"></div>
+ </div>
+ <div style="clear:both">
+ <div class="formLabel-labelCell" style="float:left; width: 200px">Type:</div>
+ <div class="type" style="float:left;"></div>
+ </div>
+ <div style="clear:both">
+ <div class="formLabel-labelCell" style="float:left; width: 200px">Actual State:</div>
+ <div class="state" style="float:left;"></div>
+ </div>
+ <div style="clear:both">
+ <div class="formLabel-labelCell" style="float:left; width: 200px">Durable:</div>
+ <div class="durable" style="float:left;"></div>
+ </div>
+ <div style="clear:both">
+ <div class="formLabel-labelCell" style="float:left; width: 200px">Lifespan:</div>
+ <div class="lifetimePolicy" style="float:left;"></div>
+ </div>
+ <div style="clear:both">
+ <div class="formLabel-labelCell" style="float:left; width: 200px">Inbound:</div>
+ <div style="float:left;">
+ <span class="msgInRate"></span>
+ <span> msg/s</span>
+ <span class="bytesInRate"></span>
+ <span class="bytesInRateUnits"></span>
</div>
- <div style="clear:both"></div>
- </div>
- <br/>
- <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Exchanges'">
- <div class="exchanges"></div>
- <button data-dojo-type="dijit.form.Button" class="addExchangeButton">Add Exchange</button>
- <button data-dojo-type="dijit.form.Button" class="deleteExchangeButton">Delete Exchange</button>
</div>
- <br/>
- <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Queues'">
- <div class="queues"></div>
- <button data-dojo-type="dijit.form.Button" class="addQueueButton">Add Queue</button>
- <button data-dojo-type="dijit.form.Button" class="deleteQueueButton">Delete Queue</button>
+ <div style="clear:both">
+ <div class="formLabel-labelCell" style="float:left; width: 200px">Outbound:</div>
+ <div style="float:left;">
+ <span class="msgOutRate"></span>
+ <span> msg/s</span>
+ <span class="bytesOutRate"></span>
+ <span class="bytesOutRateUnits"></span>
+ </div>
</div>
- <br/>
- <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Connections'">
- <div class="connections"></div>
+ <div class="configPathDiv" style="clear:both">
+ <div class="formLabel-labelCell" style="float:left; width: 200px">Config Path:</div>
+ <div class="configPath" style="float:left;"></div>
</div>
+ <div style="clear:both"></div>
+
+ <div class="virtualHostDetails"></div>
<br/>
+
<div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Alerting Thresholds', open: false">
<div style="clear:both">
- <div class="formLabel-labelCell" style="float:left; width: 150px;">Queue Depth:</div>
+ <div class="formLabel-labelCell" style="float:left; width: 200px">Queue Depth:</div>
<div style="float:left;">
<span class="alertThresholdQueueDepthMessages"></span>
<span>msgs</span>
</div>
</div>
<div style="clear:both">
- <div class="formLabel-labelCell" style="float:left; width: 150px;">Queue Depth:</div>
+ <div class="formLabel-labelCell" style="float:left; width: 200px">Queue Depth:</div>
<div style="float:left;">
<span class="alertThresholdQueueDepthBytes"></span>
<span class="alertThresholdQueueDepthBytesUnits"></span>
</div>
</div>
<div style="clear:both">
- <div class="formLabel-labelCell" style="float:left; width: 150px;">Message Age:</div>
+ <div class="formLabel-labelCell" style="float:left; width: 200px">Message Age:</div>
<div style="float:left;">
<span class="alertThresholdMessageAge"></span>
<span class="alertThresholdMessageAgeUnits"></span>
</div>
</div>
<div style="clear:both">
- <div class="formLabel-labelCell" style="float:left; width: 150px;">Message Size:</div>
+ <div class="formLabel-labelCell" style="float:left; width: 200px">Message Size:</div>
<div style="float:left;">
<span class="alertThresholdMessageSize"></span>
<span class="alertThresholdMessageSizeUnits"></span>
</div>
</div>
<div style="clear:both">
- <div class="formLabel-labelCell" style="float:left; width: 150px;">Alert frequency:</div>
+ <div class="formLabel-labelCell" style="float:left; width: 200px">Alert frequency:</div>
<div style="float:left;">
<span class="alertRepeatGap"></span>
<span class="alertRepeatGapUnits"></span>
@@ -125,5 +107,23 @@
</div>
<div style="clear:both"></div>
</div>
+ <br/>
+
+ <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Exchanges'">
+ <div class="exchanges"></div>
+ <button data-dojo-type="dijit.form.Button" class="addExchangeButton">Add Exchange</button>
+ <button data-dojo-type="dijit.form.Button" class="deleteExchangeButton">Delete Exchange</button>
+ </div>
+ <br/>
+ <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Queues'">
+ <div class="queues"></div>
+ <button data-dojo-type="dijit.form.Button" class="addQueueButton">Add Queue</button>
+ <button data-dojo-type="dijit.form.Button" class="deleteQueueButton">Delete Queue</button>
+ </div>
+ <br/>
+ <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Connections'">
+ <div class="connections"></div>
+ </div>
+
</div>
diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/virtualhost/standard/show.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/virtualhost/standard/show.html
new file mode 100644
index 0000000000..85040cdefb
--- /dev/null
+++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/virtualhost/standard/show.html
@@ -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.
+ -
+ -->
+<div>
+ <div style="clear:both">
+ <div class="formLabel-labelCell" style="float:left; width: 200px">Desired State:</div>
+ <div class="desiredState" style="float:left;">ACTIVE</div>
+ </div>
+ <div style="clear:both">
+ <div class="formLabel-labelCell" style="float:left; width: 200px">Store Type:</div>
+ <div class="storeType" style="float:left;"></div>
+ </div>
+ <div style="clear:both;">
+ <div class="formLabel-labelCell" style="float:left; width: 200px">Store Path:</div>
+ <div class="storePath" style="float:left;"></div>
+ </div>
+ <div style="clear:both"></div>
+</div>
diff --git a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagement.java b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagement.java
index 18e9f9f809..44421c721a 100644
--- a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagement.java
+++ b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagement.java
@@ -35,6 +35,7 @@ import javax.management.JMException;
import org.apache.log4j.Logger;
import org.apache.qpid.server.configuration.IllegalConfigurationException;
import org.apache.qpid.server.jmx.mbeans.LoggingManagementMBean;
+import org.apache.qpid.server.jmx.mbeans.MBeanUtils;
import org.apache.qpid.server.jmx.mbeans.UserManagementMBean;
import org.apache.qpid.server.jmx.mbeans.ServerInformationMBean;
import org.apache.qpid.server.jmx.mbeans.Shutdown;
@@ -52,7 +53,6 @@ import org.apache.qpid.server.model.State;
import org.apache.qpid.server.model.VirtualHost;
import org.apache.qpid.server.model.adapter.AbstractPluginAdapter;
import org.apache.qpid.server.plugin.PluginFactory;
-import org.apache.qpid.server.plugin.QpidServiceLoader;
import org.apache.qpid.server.util.MapValueConverter;
public class JMXManagement extends AbstractPluginAdapter implements ConfigurationChangeListener
@@ -130,7 +130,12 @@ public class JMXManagement extends AbstractPluginAdapter implements Configuratio
Collection<Port> ports = broker.getPorts();
for (Port port : ports)
{
- if (State.QUIESCED.equals(port.getActualState()))
+ if(LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Port " + port);
+ }
+
+ if (State.ACTIVE != port.getActualState())
{
continue;
}
@@ -153,6 +158,11 @@ public class JMXManagement extends AbstractPluginAdapter implements Configuratio
throw new IllegalStateException("No JMX RMI port found supporting protocol " + Protocol.RMI);
}
+ if(LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Found connector port " + connectorPort + " found registry port " + registryPort);
+ }
+
_objectRegistry = new JMXManagedObjectRegistry(broker, connectorPort, registryPort, this);
broker.addChangeListener(this);
@@ -173,7 +183,9 @@ public class JMXManagement extends AbstractPluginAdapter implements Configuratio
{
LOGGER.debug("Check for additional MBeans for virtual host:" + virtualHost.getName());
}
- createAdditionalMBeansFromProviders(virtualHost, mbean);
+
+ _children.put(virtualHost, mbean);
+ MBeanUtils.createAdditionalMBeansFromProviders(virtualHost, mbean);
}
}
Collection<AuthenticationProvider> authenticationProviders = broker.getAuthenticationProviders();
@@ -263,7 +275,10 @@ public class JMXManagement extends AbstractPluginAdapter implements Configuratio
if (mbean != null)
{
- createAdditionalMBeansFromProviders(child, mbean);
+ _children.put(child, mbean);
+ MBeanUtils.createAdditionalMBeansFromProviders(child, mbean);
+ // TODO track the mbeans that have been created on behalf of a child in a map, then
+ // if the child is ever removed, destroy these beans too.
}
}
catch(Exception e)
@@ -301,31 +316,6 @@ public class JMXManagement extends AbstractPluginAdapter implements Configuratio
// no-op
}
- private void createAdditionalMBeansFromProviders(ConfiguredObject child, AMQManagedObject mbean) throws JMException
- {
- _children.put(child, mbean);
-
- QpidServiceLoader<MBeanProvider> qpidServiceLoader = new QpidServiceLoader<MBeanProvider>();
- for (MBeanProvider provider : qpidServiceLoader.instancesOf(MBeanProvider.class))
- {
- if(LOGGER.isDebugEnabled())
- {
- LOGGER.debug("Consulting mbean provider : " + provider + " for child : " + child);
- }
-
- if (provider.isChildManageableByMBean(child))
- {
- if(LOGGER.isDebugEnabled())
- {
- LOGGER.debug("Provider will create mbean");
- }
- provider.createMBean(child, mbean);
- // TODO track the mbeans that have been created on behalf of a child in a map, then
- // if the child is ever removed, destroy these beans too.
- }
- }
- }
-
@Override
public String getName()
{
@@ -373,4 +363,10 @@ public class JMXManagement extends AbstractPluginAdapter implements Configuratio
}
}
}
+
+ @Override
+ public void close()
+ {
+ stop();
+ }
}
diff --git a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanProvider.java b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanProvider.java
index a0ef052314..941aac1a2a 100644
--- a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanProvider.java
+++ b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanProvider.java
@@ -22,7 +22,6 @@
package org.apache.qpid.server.jmx;
import javax.management.JMException;
-import javax.management.StandardMBean;
import org.apache.qpid.server.model.ConfiguredObject;
import org.apache.qpid.server.plugin.Pluggable;
@@ -47,6 +46,6 @@ public interface MBeanProvider extends Pluggable
*
* @return newly created mbean
*/
- StandardMBean createMBean(ConfiguredObject child, StandardMBean parent) throws JMException;
+ ManagedObject createMBean(ConfiguredObject child, ManagedObject parent) throws JMException;
}
diff --git a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/MBeanUtils.java b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/MBeanUtils.java
index 97e84d4796..1ddcae33a8 100644
--- a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/MBeanUtils.java
+++ b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/MBeanUtils.java
@@ -20,15 +20,28 @@
*/
package org.apache.qpid.server.jmx.mbeans;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.management.JMException;
import javax.management.OperationsException;
+import org.apache.log4j.Logger;
+import org.apache.qpid.server.jmx.AMQManagedObject;
+import org.apache.qpid.server.jmx.MBeanProvider;
+import org.apache.qpid.server.jmx.ManagedObject;
+import org.apache.qpid.server.model.ConfiguredObject;
import org.apache.qpid.server.model.ConfiguredObjectFinder;
import org.apache.qpid.server.model.Exchange;
import org.apache.qpid.server.model.Queue;
import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.plugin.QpidServiceLoader;
public class MBeanUtils
{
+ private static final Logger LOGGER = Logger.getLogger(MBeanUtils.class);
+
public static Queue findQueueFromQueueName(VirtualHost virtualHost, String queueName) throws OperationsException
{
Queue queue = ConfiguredObjectFinder.findConfiguredObjectByName(virtualHost.getQueues(), queueName);
@@ -54,4 +67,24 @@ public class MBeanUtils
return exchange;
}
}
+
+ public static Collection<ManagedObject> createAdditionalMBeansFromProviders(ConfiguredObject child, AMQManagedObject mbean) throws JMException
+ {
+ List<ManagedObject> mbeans = new ArrayList<ManagedObject>();
+ QpidServiceLoader<MBeanProvider> qpidServiceLoader = new QpidServiceLoader<MBeanProvider>();
+ for (MBeanProvider provider : qpidServiceLoader.instancesOf(MBeanProvider.class))
+ {
+ if (provider.isChildManageableByMBean(child))
+ {
+ if(LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Provider of type " + provider.getType() + " will create MBean for child : " + child);
+ }
+ ManagedObject childMBean = provider.createMBean(child, mbean);
+ mbeans.add(childMBean);
+ }
+ }
+ return mbeans;
+ }
+
}
diff --git a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/VirtualHostMBean.java b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/VirtualHostMBean.java
index e9e3e1df49..fa907552fd 100644
--- a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/VirtualHostMBean.java
+++ b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/VirtualHostMBean.java
@@ -21,6 +21,14 @@
package org.apache.qpid.server.jmx.mbeans;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.management.JMException;
+import javax.management.ObjectName;
+
import org.apache.log4j.Logger;
import org.apache.qpid.server.jmx.AMQManagedObject;
import org.apache.qpid.server.jmx.ManagedObject;
@@ -29,26 +37,26 @@ import org.apache.qpid.server.model.ConfigurationChangeListener;
import org.apache.qpid.server.model.ConfiguredObject;
import org.apache.qpid.server.model.Connection;
import org.apache.qpid.server.model.Exchange;
+import org.apache.qpid.server.model.Model;
import org.apache.qpid.server.model.Queue;
import org.apache.qpid.server.model.State;
import org.apache.qpid.server.model.VirtualHost;
import org.apache.qpid.server.virtualhost.ManagedVirtualHost;
-import javax.management.JMException;
-import javax.management.ObjectName;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-
public class VirtualHostMBean extends AMQManagedObject implements ManagedVirtualHost, ConfigurationChangeListener
{
private static final Logger LOGGER = Logger.getLogger(VirtualHostMBean.class);
private final VirtualHost _virtualHost;
+ private final Object _childrenLock = new Object();
+
private final Map<ConfiguredObject, AMQManagedObject> _children =
new HashMap<ConfiguredObject, AMQManagedObject>();
+
+ private final Map<ConfiguredObject, Collection<ManagedObject>> _additionalChildren =
+ new HashMap<ConfiguredObject, Collection<ManagedObject>>();
+
private VirtualHostManagerMBean _managerMBean;
public VirtualHostMBean(VirtualHost virtualHost, ManagedObjectRegistry registry) throws JMException
@@ -61,13 +69,48 @@ public class VirtualHostMBean extends AMQManagedObject implements ManagedVirtual
initExchanges();
initConnections();
+ initAdditionalMbeansForAllChildren();
+
//This is the actual JMX bean for this 'VirtualHostMBean', leave it alone.
_managerMBean = new VirtualHostManagerMBean(this);
}
+ private void initAdditionalMbeansForAllChildren()
+ {
+ synchronized (_childrenLock)
+ {
+ Collection<Class<? extends ConfiguredObject>> childrenTypes = Model.getInstance().getChildTypes(VirtualHost.class);
+ for (Class<? extends ConfiguredObject> childType : childrenTypes)
+ {
+ Collection<? extends ConfiguredObject> children = _virtualHost.getChildren(childType);
+ for (ConfiguredObject child : children)
+ {
+ createAdditionalMBeans(child);
+ }
+ }
+ }
+ }
+
+ private void createAdditionalMBeans(ConfiguredObject child)
+ {
+ try
+ {
+ Collection<ManagedObject> mbeans = MBeanUtils.createAdditionalMBeansFromProviders(child, this);
+ if (!mbeans.isEmpty())
+ {
+ _additionalChildren.put(child, mbeans);
+ }
+ }
+ catch(Exception e)
+ {
+ LOGGER.error("Cannot create mbeans for the child " + child.getName(), e);
+ }
+ }
+
+
private void initQueues()
{
- synchronized (_children)
+ synchronized (_childrenLock)
{
for(Queue queue : _virtualHost.getQueues())
{
@@ -88,7 +131,7 @@ public class VirtualHostMBean extends AMQManagedObject implements ManagedVirtual
private void initExchanges()
{
- synchronized (_children)
+ synchronized (_childrenLock)
{
for(Exchange exchange : _virtualHost.getExchanges())
{
@@ -109,7 +152,7 @@ public class VirtualHostMBean extends AMQManagedObject implements ManagedVirtual
private void initConnections()
{
- synchronized (_children)
+ synchronized (_childrenLock)
{
for(Connection conn : _virtualHost.getConnections())
{
@@ -145,7 +188,7 @@ public class VirtualHostMBean extends AMQManagedObject implements ManagedVirtual
public void childAdded(ConfiguredObject object, ConfiguredObject child)
{
- synchronized (_children)
+ synchronized (_childrenLock)
{
try
{
@@ -153,36 +196,29 @@ public class VirtualHostMBean extends AMQManagedObject implements ManagedVirtual
{
QueueMBean queueMB = new QueueMBean((Queue)child, this);
_children.put(child, queueMB);
-
}
else if(child instanceof Exchange)
{
ExchangeMBean exchangeMBean = new ExchangeMBean((Exchange)child, this);
_children.put(child, exchangeMBean);
-
}
else if(child instanceof Connection)
{
ConnectionMBean connectionMBean = new ConnectionMBean((Connection)child, this);
_children.put(child, connectionMBean);
-
- }
- else
- {
- LOGGER.debug("Unsupported child : " + child.getName() + " type : " + child.getClass());
}
-
}
catch(Exception e)
{
LOGGER.error("Exception while creating mbean for " + child.getClass().getSimpleName() + " " + child.getName(), e);
}
+ createAdditionalMBeans(child);
}
}
public void childRemoved(ConfiguredObject object, ConfiguredObject child)
{
- synchronized (_children)
+ synchronized (_childrenLock)
{
AMQManagedObject mbean = _children.remove(child);
if(mbean != null)
@@ -196,6 +232,26 @@ public class VirtualHostMBean extends AMQManagedObject implements ManagedVirtual
LOGGER.error("Exception while unregistering mbean for " + child.getClass().getSimpleName() + " " + child.getName(), e);
}
}
+ unregisterAdditionalMBeansIfPresent(child);
+ }
+ }
+
+ private void unregisterAdditionalMBeansIfPresent(ConfiguredObject child)
+ {
+ Collection<ManagedObject> additionalMBeans = _additionalChildren.remove(child);
+ if (additionalMBeans != null)
+ {
+ for (ManagedObject mbean : additionalMBeans)
+ {
+ try
+ {
+ mbean.unregister();
+ }
+ catch(Exception e)
+ {
+ LOGGER.error("Exception while unregistering mbean for " + child.getClass().getSimpleName() + " " + child.getName(), e);
+ }
+ }
}
}
@@ -213,7 +269,7 @@ public class VirtualHostMBean extends AMQManagedObject implements ManagedVirtual
public Collection<QueueMBean> getQueues()
{
Collection<AMQManagedObject> children;
- synchronized (_children)
+ synchronized (_childrenLock)
{
children = new ArrayList<AMQManagedObject>(_children.values());
}
@@ -235,7 +291,7 @@ public class VirtualHostMBean extends AMQManagedObject implements ManagedVirtual
{
_virtualHost.removeChangeListener(this);
- synchronized (_children)
+ synchronized (_childrenLock)
{
for (AMQManagedObject mbean : _children.values())
{
@@ -252,6 +308,11 @@ public class VirtualHostMBean extends AMQManagedObject implements ManagedVirtual
}
}
_children.clear();
+
+ for (ConfiguredObject child : new ArrayList<ConfiguredObject>(_additionalChildren.keySet()))
+ {
+ unregisterAdditionalMBeansIfPresent(child);
+ }
}
_managerMBean.unregister();
}
diff --git a/qpid/java/systests/etc/config-systests.json b/qpid/java/systests/etc/config-systests.json
index 12a8a5c5a6..55b73a4263 100644
--- a/qpid/java/systests/etc/config-systests.json
+++ b/qpid/java/systests/etc/config-systests.json
@@ -22,7 +22,7 @@
"name": "Broker",
"defaultVirtualHost" : "test",
"storeVersion": 1,
- "modelVersion": "1.0",
+ "modelVersion": "1.3",
"authenticationproviders" : [ {
"name" : "plain",
"type" : "PlainPasswordFile",
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/store/MessageStoreTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/store/MessageStoreTest.java
index de36c6e413..2b21f4aeaa 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/server/store/MessageStoreTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/store/MessageStoreTest.java
@@ -28,6 +28,7 @@ import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.log4j.Logger;
import org.apache.qpid.AMQException;
+import org.apache.qpid.AMQStoreException;
import org.apache.qpid.common.AMQPFilterTypes;
import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.framing.BasicContentHeaderProperties;
@@ -604,7 +605,7 @@ public class MessageStoreTest extends QpidTestCase
}
}
- private void sendMessageOnExchange(Exchange exchange, String routingKey, boolean deliveryMode)
+ private void sendMessageOnExchange(Exchange exchange, String routingKey, boolean deliveryMode) throws AMQStoreException
{
//Set MessagePersistence
BasicContentHeaderProperties properties = new BasicContentHeaderProperties();
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/store/QuotaMessageStore.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/store/QuotaMessageStore.java
index 75ce0e68d8..7b43cd5022 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/server/store/QuotaMessageStore.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/store/QuotaMessageStore.java
@@ -49,7 +49,6 @@ public class
@Override
public void configureConfigStore(VirtualHost virtualHost, ConfigurationRecoveryHandler recoveryHandler)
- throws Exception
{
Object overfullAttr = virtualHost.getAttribute(MessageStoreConstants.OVERFULL_SIZE_ATTRIBUTE);
_persistentSizeHighThreshold = overfullAttr == null
@@ -76,13 +75,13 @@ public class
@Override
public void configureMessageStore(VirtualHost virtualHost, MessageStoreRecoveryHandler recoveryHandler,
- TransactionLogRecoveryHandler tlogRecoveryHandler) throws Exception
+ TransactionLogRecoveryHandler tlogRecoveryHandler)
{
_stateManager.attainState(State.INITIALISED);
}
@Override
- public void activate() throws Exception
+ public void activate()
{
_stateManager.attainState(State.ACTIVATING);
_stateManager.attainState(State.ACTIVE);
@@ -152,7 +151,7 @@ public class
}
@Override
- public void close() throws Exception
+ public void close()
{
if (_closed.compareAndSet(false, true))
{
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/store/SlowMessageStore.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/store/SlowMessageStore.java
index cc0205085b..9cacffa086 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/server/store/SlowMessageStore.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/store/SlowMessageStore.java
@@ -48,7 +48,7 @@ public class SlowMessageStore implements MessageStore, DurableConfigurationStore
// ***** MessageStore Interface.
- public void configureConfigStore(VirtualHost virtualHost, ConfigurationRecoveryHandler recoveryHandler) throws Exception
+ public void configureConfigStore(VirtualHost virtualHost, ConfigurationRecoveryHandler recoveryHandler) throws AMQStoreException
{
_logger.info("Starting SlowMessageStore on Virtualhost:" + virtualHost.getName());
@@ -67,9 +67,17 @@ public class SlowMessageStore implements MessageStore, DurableConfigurationStore
if (messageStoreClass != null)
{
- Class<?> clazz = Class.forName(messageStoreClass);
-
- Object o = clazz.newInstance();
+ Class<?> clazz = null;
+ Object o = null;
+ try
+ {
+ clazz = Class.forName(messageStoreClass);
+ o = clazz.newInstance();
+ }
+ catch(Exception e)
+ {
+ throw new AMQStoreException("Cannot instantiate message store:" + messageStoreClass, e );
+ }
if (!(o instanceof MessageStore))
{
@@ -152,19 +160,19 @@ public class SlowMessageStore implements MessageStore, DurableConfigurationStore
public void configureMessageStore(VirtualHost virtualHost, MessageStoreRecoveryHandler messageRecoveryHandler,
- TransactionLogRecoveryHandler tlogRecoveryHandler) throws Exception
+ TransactionLogRecoveryHandler tlogRecoveryHandler) throws AMQStoreException
{
_realStore.configureMessageStore(virtualHost, messageRecoveryHandler, tlogRecoveryHandler);
}
- public void close() throws Exception
+ public void close() throws AMQStoreException
{
doPreDelay("close");
_realStore.close();
doPostDelay("close");
}
- public <M extends StorableMessageMetaData> StoredMessage<M> addMessage(M metaData)
+ public <M extends StorableMessageMetaData> StoredMessage<M> addMessage(M metaData) throws AMQStoreException
{
return _realStore.addMessage(metaData);
}
@@ -219,7 +227,7 @@ public class SlowMessageStore implements MessageStore, DurableConfigurationStore
doPostDelay("update");
}
- public Transaction newTransaction()
+ public Transaction newTransaction() throws AMQStoreException
{
doPreDelay("beginTran");
Transaction txn = new SlowTransaction(_realStore.newTransaction());
@@ -311,7 +319,7 @@ public class SlowMessageStore implements MessageStore, DurableConfigurationStore
}
@Override
- public void activate() throws Exception
+ public void activate() throws AMQStoreException
{
_realStore.activate();
}
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/ManagementLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/ManagementLoggingTest.java
index f396c79351..0f80345e57 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/ManagementLoggingTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/ManagementLoggingTest.java
@@ -195,7 +195,7 @@ public class ManagementLoggingTest extends AbstractTestLogging
validateMessageID("MNG-1002", log);
//Check the RMI Registry port is as expected
- int mPort = getManagementPort(getPort());
+ int mPort = getJmxManagementPort(getPort());
assertTrue("RMI Registry port not as expected(" + mPort + ").:" + getMessageString(log),
getMessageString(log).endsWith(String.valueOf(mPort)));
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/AccessControlProviderRestTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/AccessControlProviderRestTest.java
index 312443cfa7..32c7f4daa7 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/AccessControlProviderRestTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/AccessControlProviderRestTest.java
@@ -233,7 +233,7 @@ public class AccessControlProviderRestTest extends QpidRestTestCase
assertCanAccessManagementInterface(accessControlProviderName2, true);
}
- public void testRemovalOfAccessControlProviderInErrorStateUsingManagementMode() throws Exception
+ public void testRemovalOfAccessControlProviderInQuiescedStateUsingManagementMode() throws Exception
{
stopBroker();
@@ -245,14 +245,14 @@ public class AccessControlProviderRestTest extends QpidRestTestCase
assertFalse("ACL file should not exist", file.exists());
UUID id = getBrokerConfiguration().addAclFileConfiguration(file.getAbsolutePath());
getBrokerConfiguration().setSaved(false);
- startBroker(0, true);
+ startBroker(0, true);
getRestTestHelper().setUsernameAndPassword(BrokerOptions.MANAGEMENT_MODE_USER_NAME, MANAGEMENT_MODE_PASSWORD);
Map<String, Object> acl = getRestTestHelper().getJsonAsSingletonList("/rest/accesscontrolprovider/" + TestBrokerConfiguration.ENTRY_NAME_ACL_FILE);
assertEquals("Unexpected id", id.toString(), acl.get(AccessControlProvider.ID));
assertEquals("Unexpected path", file.getAbsolutePath() , acl.get(FileAccessControlProviderConstants.PATH));
- assertEquals("Unexpected state", State.ERRORED.name() , acl.get(AccessControlProvider.STATE));
+ assertEquals("Unexpected state", State.QUIESCED.name() , acl.get(AccessControlProvider.STATE));
int status = getRestTestHelper().submitRequest("/rest/accesscontrolprovider/" + TestBrokerConfiguration.ENTRY_NAME_ACL_FILE, "DELETE", null);
assertEquals("ACL was not deleted", 200, status);
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/Asserts.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/Asserts.java
index 9028130c18..5dc1717751 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/Asserts.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/Asserts.java
@@ -52,7 +52,8 @@ public class Asserts
assertNotNull("Virtualhost " + virtualHostName + " data are not found", virtualHost);
assertAttributesPresent(virtualHost, VirtualHost.AVAILABLE_ATTRIBUTES, VirtualHost.TIME_TO_LIVE,
VirtualHost.CREATED, VirtualHost.UPDATED, VirtualHost.SUPPORTED_QUEUE_TYPES, VirtualHost.STORE_PATH,
- VirtualHost.CONFIG_PATH, VirtualHost.TYPE, VirtualHost.CONFIG_STORE_PATH, VirtualHost.CONFIG_STORE_TYPE);
+ VirtualHost.CONFIG_PATH, VirtualHost.TYPE, VirtualHost.CONFIG_STORE_PATH, VirtualHost.CONFIG_STORE_TYPE,
+ VirtualHost.QUIESCE_ON_MASTER_CHANGE);
assertEquals("Unexpected value of attribute " + VirtualHost.NAME, virtualHostName, virtualHost.get(VirtualHost.NAME));
assertNotNull("Unexpected value of attribute " + VirtualHost.ID, virtualHost.get(VirtualHost.ID));
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/RestTestHelper.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/RestTestHelper.java
index 810b70a2ba..5700c3b8f5 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/RestTestHelper.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/RestTestHelper.java
@@ -510,4 +510,11 @@ public class RestTestHelper
_useSslAuth = useSslAuth;
_useSsl = true;
}
+
+ public static RestTestHelper createRestTestHelperWithDefaultCredentials(int port)
+ {
+ RestTestHelper helper = new RestTestHelper(port);
+ helper.setUsernameAndPassword("webadmin", "webadmin");
+ return helper;
+ }
}
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/VirtualHostRestTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/VirtualHostRestTest.java
index 96ca0c2def..4a8b321997 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/VirtualHostRestTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/VirtualHostRestTest.java
@@ -23,10 +23,17 @@ package org.apache.qpid.systest.rest;
import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import javax.jms.Connection;
+import javax.jms.Destination;
+import javax.jms.IllegalStateException;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.servlet.http.HttpServletResponse;
@@ -35,6 +42,7 @@ import org.apache.commons.configuration.XMLConfiguration;
import org.apache.qpid.client.AMQConnection;
import org.apache.qpid.server.model.Exchange;
import org.apache.qpid.server.model.Queue;
+import org.apache.qpid.server.model.State;
import org.apache.qpid.server.model.VirtualHost;
import org.apache.qpid.server.queue.AMQQueueFactory;
import org.apache.qpid.server.virtualhost.StandardVirtualHostFactory;
@@ -494,6 +502,149 @@ public class VirtualHostRestTest extends QpidRestTestCase
Asserts.assertQueue(queueName, "standard", queue, null);
}
+ public void testConnectionToVirtualHostInQuiescedState() throws Exception
+ {
+ Connection connection = getConnection();
+ assertProducingConsuming(connection);
+
+ Map<String, Object> attributes = Collections.<String, Object>singletonMap(VirtualHost.DESIRED_STATE, State.QUIESCED.name());
+ int status = getRestTestHelper().submitRequest("/rest/virtualhost/test", "PUT", attributes);
+ assertEquals("Unexpected http status", 200, status);
+
+ Map<String, Object> hostAttributes = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/test");
+ assertEquals("Unexpected state", State.QUIESCED.name(), hostAttributes.get(VirtualHost.STATE));
+
+ assertProducingConsuming(connection);
+
+ try
+ {
+ getConnection();
+ fail("A new connection to the QUIESCED virtual host should fail");
+ }
+ catch(JMSException e)
+ {
+ // pass
+ }
+
+ // test that operations to create exchange and queue are working for existing connection
+ Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
+ String queueName = getTestQueueName() + 1;
+ String exchangeName = getTestName();
+ Destination destination = session.createQueue("direct://" +exchangeName + "//" + queueName);
+ MessageConsumer consumer = session.createConsumer(destination);
+ sendMessage(session, destination, 1);
+ connection.start();
+ Message m1 = consumer.receive(RECEIVE_TIMEOUT);
+ assertNotNull("Message 1 is not received", m1);
+ assertEquals("Unexpected first message received", 0, m1.getIntProperty(INDEX));
+ session.commit();
+
+ Map<String, Object> queueAttributes = getRestTestHelper().getJsonAsSingletonList("/rest/queue/test/" + queueName);
+ assertEquals("Unexpected queue name", queueName, queueAttributes.get(Queue.NAME));
+
+ Map<String, Object> exchangeAttributes = getRestTestHelper().getJsonAsSingletonList("/rest/exchange/test/" + exchangeName);
+ assertEquals("Unexpected exchange name", exchangeName, exchangeAttributes.get(VirtualHost.NAME));
+
+ attributes = Collections.<String, Object>singletonMap(VirtualHost.DESIRED_STATE, State.ACTIVE.name());
+ status = getRestTestHelper().submitRequest("/rest/virtualhost/test", "PUT", attributes);
+ assertEquals("Unexpected http status", 200, status);
+
+ Connection connection2 = getConnection();
+ assertProducingConsuming(connection2);
+ }
+
+ public void testConnectionToVirtualHostInStoppedState() throws Exception
+ {
+ Connection connection = getConnection();
+ assertProducingConsuming(connection);
+
+ Map<String, Object> attributes = Collections.<String, Object>singletonMap(VirtualHost.DESIRED_STATE, State.STOPPED.name());
+ int status = getRestTestHelper().submitRequest("/rest/virtualhost/test", "PUT", attributes);
+ assertEquals("Unexpected http status", 200, status);
+
+ Map<String, Object> hostAttributes = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/test");
+ assertEquals("Unexpected state", State.STOPPED.name(), hostAttributes.get(VirtualHost.STATE));
+
+ try
+ {
+ connection.createSession(true, Session.SESSION_TRANSACTED);
+ fail("Connection should be closed");
+ }
+ catch(IllegalStateException e)
+ {
+ // pass
+ }
+
+ try
+ {
+ getConnection();
+ fail("A new connection to the STOPPED virtual host should fail");
+ }
+ catch(JMSException e)
+ {
+ // pass
+ }
+
+ attributes = Collections.<String, Object>singletonMap(VirtualHost.DESIRED_STATE, State.ACTIVE.name());
+ status = getRestTestHelper().submitRequest("/rest/virtualhost/test", "PUT", attributes);
+ assertEquals("Unexpected http status", 200, status);
+
+ Connection connection2 = getConnection();
+ assertProducingConsuming(connection2);
+ }
+
+ public void testRestartStoppedVirtualHost() throws Exception
+ {
+ Map<String, Object> attributes = Collections.<String, Object>singletonMap(VirtualHost.DESIRED_STATE, State.STOPPED.name());
+ int status = getRestTestHelper().submitRequest("/rest/virtualhost/test", "PUT", attributes);
+ assertEquals("Unexpected http status", 200, status);
+
+ Map<String, Object> hostAttributes = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/test");
+ assertEquals("Unexpected state", State.STOPPED.name(), hostAttributes.get(VirtualHost.STATE));
+
+ restartBroker();
+
+ hostAttributes = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/test");
+ assertEquals("Unexpected state after restart", State.STOPPED.name(), hostAttributes.get(VirtualHost.STATE));
+
+ status = getRestTestHelper().submitRequest("/rest/virtualhost/test", "PUT", Collections.<String, Object>singletonMap(VirtualHost.DESIRED_STATE, State.ACTIVE.name()));
+ assertEquals("Unexpected http status on state change to ACTIVE", 200, status);
+
+ Connection connection = getConnection();
+ assertProducingConsuming(connection);
+ }
+
+ public void testRestartQuiescedVirtualHost() throws Exception
+ {
+ Map<String, Object> attributes = Collections.<String, Object>singletonMap(VirtualHost.DESIRED_STATE, State.QUIESCED.name());
+ int status = getRestTestHelper().submitRequest("/rest/virtualhost/test", "PUT", attributes);
+ assertEquals("Unexpected http status", 200, status);
+
+ Map<String, Object> hostAttributes = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/test");
+ assertEquals("Unexpected state", State.QUIESCED.name(), hostAttributes.get(VirtualHost.STATE));
+
+ restartBroker();
+
+ hostAttributes = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/test");
+ assertEquals("Unexpected state after restart", State.QUIESCED.name(), hostAttributes.get(VirtualHost.STATE));
+
+ status = getRestTestHelper().submitRequest("/rest/virtualhost/test", "PUT", Collections.<String, Object>singletonMap(VirtualHost.DESIRED_STATE, State.ACTIVE.name()));
+ assertEquals("Unexpected http status on state change to ACTIVE", 200, status);
+
+ Connection connection = getConnection();
+ assertProducingConsuming(connection);
+ }
+
+ public void testDeleteVirtualHost() throws Exception
+ {
+ Map<String, Object> attributes = Collections.<String, Object>singletonMap(VirtualHost.DESIRED_STATE, State.DELETED.name());
+ int status = getRestTestHelper().submitRequest("/rest/virtualhost/" + TEST2_VIRTUALHOST, "PUT", attributes);
+ assertEquals("Unexpected http status", 200, status);
+
+ List<Map<String, Object>> hostAttributes = getRestTestHelper().getJsonAsList("/rest/virtualhost/" + TEST2_VIRTUALHOST);
+ assertTrue("Virtual host should be deleted", hostAttributes.isEmpty());
+ }
+
private void createExchange(String exchangeName, String exchangeType) throws IOException
{
HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/exchange/test/" + exchangeName, "PUT");
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java
index cc8bfb9433..3b5aef2e51 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java
@@ -164,7 +164,7 @@ public class DurableSubscriptionTest extends QpidBrokerTestCase
{
//Verify that the queue was deleted by querying for its JMX MBean
_jmxc = JMXConnnectionFactory.getJMXConnection(5000, "127.0.0.1",
- getManagementPort(getPort()), USER, PASSWORD);
+ getJmxManagementPort(getPort()), USER, PASSWORD);
_jmxConnected = true;
_mbsc = _jmxc.getMBeanServerConnection();
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/JMXTestUtils.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/JMXTestUtils.java
index 4e5c4ca034..dcb0eb150b 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/JMXTestUtils.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/JMXTestUtils.java
@@ -82,7 +82,7 @@ public class JMXTestUtils
public void open(final int brokerPort) throws Exception
{
int actualBrokerPort = _test.getPort(brokerPort);
- int managementPort = _test.getManagementPort(actualBrokerPort);
+ int managementPort = _test.getJmxManagementPort(actualBrokerPort);
_jmxc = JMXConnnectionFactory.getJMXConnection(5000, "127.0.0.1",
managementPort, _user, _password);
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 91dcf48001..d226c0785a 100755
--- 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
@@ -159,6 +159,7 @@ public class QpidBrokerTestCase extends QpidTestCase
public static final int DEFAULT_PORT = Integer.getInteger("test.port", DEFAULT_PORT_VALUE);
public static final int FAILING_PORT = Integer.parseInt(System.getProperty("test.port.alt"));
public static final int DEFAULT_MANAGEMENT_PORT = Integer.getInteger("test.mport", DEFAULT_JMXPORT_REGISTRYSERVER);
+ public static final int HTTP_MANAGEMENT_PORT = Integer.getInteger("test.hport", DEFAULT_HTTP_MANAGEMENT_PORT);
public static final int DEFAULT_SSL_PORT = Integer.getInteger("test.port.ssl", DEFAULT_SSL_PORT_VALUE);
protected String _brokerLanguage = System.getProperty(BROKER_LANGUAGE, JAVA);
@@ -247,8 +248,9 @@ public class QpidBrokerTestCase extends QpidTestCase
if (actualPort != DEFAULT_PORT)
{
configuration.setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_AMQP_PORT, Port.PORT, actualPort);
- configuration.setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_RMI_PORT, Port.PORT, getManagementPort(actualPort));
- configuration.setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_JMX_PORT, Port.PORT, getManagementPort(actualPort) + JMXPORT_CONNECTORSERVER_OFFSET);
+ configuration.setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_RMI_PORT, Port.PORT, getJmxManagementPort(actualPort));
+ configuration.setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_JMX_PORT, Port.PORT, getJmxManagementPort(actualPort) + JMXPORT_CONNECTORSERVER_OFFSET);
+ configuration.setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_HTTP_PORT, Port.PORT, getHttpManagementPort(actualPort));
}
return configuration;
}
@@ -361,11 +363,16 @@ public class QpidBrokerTestCase extends QpidTestCase
*
* @return the management port that corresponds to the broker on the given port
*/
- protected int getManagementPort(int mainPort)
+ protected int getJmxManagementPort(int mainPort)
{
return mainPort + (DEFAULT_MANAGEMENT_PORT - DEFAULT_PORT);
}
+ public int getHttpManagementPort(int mainPort)
+ {
+ return mainPort + (HTTP_MANAGEMENT_PORT - DEFAULT_PORT);
+ }
+
/**
* The returned set of port numbers is only a guess because it assumes no ports have been overridden
* using system properties.
@@ -373,11 +380,13 @@ public class QpidBrokerTestCase extends QpidTestCase
protected Set<Integer> guessAllPortsUsedByBroker(int mainPort)
{
Set<Integer> ports = new HashSet<Integer>();
- int managementPort = getManagementPort(mainPort);
+ int managementPort = getJmxManagementPort(mainPort);
+ int httpManagementPort = getHttpManagementPort(mainPort);
int connectorServerPort = managementPort + JMXPORT_CONNECTORSERVER_OFFSET;
ports.add(mainPort);
ports.add(managementPort);
+ ports.add(httpManagementPort);
ports.add(connectorServerPort);
ports.add(DEFAULT_SSL_PORT);
@@ -1488,4 +1497,19 @@ public class QpidBrokerTestCase extends QpidTestCase
return supportedStoresClassToTypeMapping.get(storeClass);
}
+ public void assertProducingConsuming(final Connection connection) throws Exception
+ {
+ Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
+ Destination destination = session.createQueue(getTestQueueName());
+ MessageConsumer consumer = session.createConsumer(destination);
+ sendMessage(session, destination, 1);
+ session.commit();
+ connection.start();
+ Message m1 = consumer.receive(RECEIVE_TIMEOUT);
+ assertNotNull("Message 1 is not received", m1);
+ assertEquals("Unexpected first message received", 0, m1.getIntProperty(INDEX));
+ session.commit();
+ session.close();
+ }
+
}
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/TestBrokerConfiguration.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/TestBrokerConfiguration.java
index 9e893bb7bb..9ada95d728 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/TestBrokerConfiguration.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/TestBrokerConfiguration.java
@@ -39,6 +39,7 @@ import org.apache.qpid.server.model.KeyStore;
import org.apache.qpid.server.model.Plugin;
import org.apache.qpid.server.model.Port;
import org.apache.qpid.server.model.PreferencesProvider;
+import org.apache.qpid.server.model.ReplicationNode;
import org.apache.qpid.server.model.TrustStore;
import org.apache.qpid.server.model.UUIDGenerator;
import org.apache.qpid.server.model.VirtualHost;
@@ -115,10 +116,15 @@ public class TestBrokerConfiguration
return null;
}
- public UUID addObjectConfiguration(String name, String type, Map<String, Object> attributes)
+ public UUID addObjectConfiguration(String type, Map<String, Object> attributes)
+ {
+ return addObjectConfiguration(type, attributes, Collections.<UUID>emptySet());
+ }
+
+ public UUID addObjectConfiguration(String type, Map<String, Object> attributes, Set<UUID> childrenIds)
{
UUID id = UUIDGenerator.generateRandomUUID();
- addObjectConfiguration(id, type, attributes);
+ addObjectConfiguration(id, type, attributes, childrenIds);
return id;
}
@@ -127,7 +133,7 @@ public class TestBrokerConfiguration
Map<String, Object> attributes = new HashMap<String, Object>();
attributes.put(PluginFactory.PLUGIN_TYPE, MANAGEMENT_JMX_PLUGIN_TYPE);
attributes.put(Plugin.NAME, ENTRY_NAME_JMX_MANAGEMENT);
- return addObjectConfiguration(ENTRY_NAME_JMX_MANAGEMENT, Plugin.class.getSimpleName(), attributes);
+ return addObjectConfiguration(Plugin.class.getSimpleName(), attributes);
}
public UUID addHttpManagementConfiguration()
@@ -135,7 +141,7 @@ public class TestBrokerConfiguration
Map<String, Object> attributes = new HashMap<String, Object>();
attributes.put(PluginFactory.PLUGIN_TYPE, MANAGEMENT_HTTP_PLUGIN_TYPE);
attributes.put(Plugin.NAME, ENTRY_NAME_HTTP_MANAGEMENT);
- return addObjectConfiguration(ENTRY_NAME_HTTP_MANAGEMENT, Plugin.class.getSimpleName(), attributes);
+ return addObjectConfiguration(Plugin.class.getSimpleName(), attributes);
}
public UUID addGroupFileConfiguration(String groupFilePath)
@@ -160,44 +166,37 @@ public class TestBrokerConfiguration
public UUID addPortConfiguration(Map<String, Object> attributes)
{
- String name = (String) attributes.get(Port.NAME);
- return addObjectConfiguration(name, Port.class.getSimpleName(), attributes);
+ return addObjectConfiguration(Port.class.getSimpleName(), attributes);
}
public UUID addVirtualHostConfiguration(Map<String, Object> attributes)
{
- String name = (String) attributes.get(VirtualHost.NAME);
- return addObjectConfiguration(name, VirtualHost.class.getSimpleName(), attributes);
+ return addObjectConfiguration(VirtualHost.class.getSimpleName(), attributes);
}
public UUID addAuthenticationProviderConfiguration(Map<String, Object> attributes)
{
- String name = (String) attributes.get(AuthenticationProvider.NAME);
- return addObjectConfiguration(name, AuthenticationProvider.class.getSimpleName(), attributes);
+ return addObjectConfiguration(AuthenticationProvider.class.getSimpleName(), attributes);
}
public UUID addGroupProviderConfiguration(Map<String, Object> attributes)
{
- String name = (String) attributes.get(GroupProvider.NAME);
- return addObjectConfiguration(name, GroupProvider.class.getSimpleName(), attributes);
+ return addObjectConfiguration(GroupProvider.class.getSimpleName(), attributes);
}
public UUID addAccessControlConfiguration(Map<String, Object> attributes)
{
- String name = (String) attributes.get(AccessControlProvider.NAME);
- return addObjectConfiguration(name, AccessControlProvider.class.getSimpleName(), attributes);
+ return addObjectConfiguration(AccessControlProvider.class.getSimpleName(), attributes);
}
public UUID addTrustStoreConfiguration(Map<String, Object> attributes)
{
- String name = (String) attributes.get(TrustStore.NAME);
- return addObjectConfiguration(name, TrustStore.class.getSimpleName(), attributes);
+ return addObjectConfiguration(TrustStore.class.getSimpleName(), attributes);
}
public UUID addKeyStoreConfiguration(Map<String, Object> attributes)
{
- String name = (String) attributes.get(KeyStore.NAME);
- return addObjectConfiguration(name, KeyStore.class.getSimpleName(), attributes);
+ return addObjectConfiguration(KeyStore.class.getSimpleName(), attributes);
}
private boolean setObjectAttributes(ConfigurationEntry entry, Map<String, Object> attributes)
@@ -240,20 +239,26 @@ public class TestBrokerConfiguration
return null;
}
- private void addObjectConfiguration(UUID id, String type, Map<String, Object> attributes)
+ private void addObjectConfiguration(UUID id, String type, Map<String, Object> attributes, Set<UUID> childrenId)
{
- ConfigurationEntry entry = new ConfigurationEntry(id, type, attributes, Collections.<UUID> emptySet(), _store);
- ConfigurationEntry root = _store.getRootEntry();
+ ConfigurationEntry parent = _store.getRootEntry();
+ addObjectConfigurationToParent(parent, id, type, attributes, childrenId);
+ }
- Map<String, Collection<ConfigurationEntry>> children = root.getChildren();
+ private void addObjectConfigurationToParent(ConfigurationEntry parent, UUID id, String type, Map<String, Object> attributes,
+ Set<UUID> childrenId)
+ {
+ ConfigurationEntry entry = new ConfigurationEntry(id, type, attributes, childrenId, _store);
+
+ Map<String, Collection<ConfigurationEntry>> children = parent.getChildren();
verifyChildWithNameDoesNotExist(id, type, attributes, children);
- Set<UUID> childrenIds = new HashSet<UUID>(root.getChildrenIds());
+ Set<UUID> childrenIds = new HashSet<UUID>(parent.getChildrenIds());
childrenIds.add(id);
- ConfigurationEntry newRoot = new ConfigurationEntry(root.getId(), root.getType(), root.getAttributes(), childrenIds,
+ ConfigurationEntry newParent = new ConfigurationEntry(parent.getId(), parent.getType(), parent.getAttributes(), childrenIds,
_store);
- _store.save(newRoot, entry);
+ _store.save(newParent, entry);
}
private void verifyChildWithNameDoesNotExist(UUID id, String type,
@@ -308,4 +313,12 @@ public class TestBrokerConfiguration
_store.save(newAp, pp);
}
+ public UUID addReplicationNodeConfiguration(UUID hostId, Map<String, Object> replicationNodeAttributes)
+ {
+ ConfigurationEntry parent = _store.getEntry(hostId);
+ UUID id = UUID.randomUUID();
+ addObjectConfigurationToParent(parent, id, ReplicationNode.class.getSimpleName(), replicationNodeAttributes, Collections.<UUID>emptySet());
+ return id;
+ }
+
}