summaryrefslogtreecommitdiff
path: root/qpid/java/broker/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'qpid/java/broker/src/test')
-rw-r--r--qpid/java/broker/src/test/java/org/apache/log4j/xml/QpidLog4JConfiguratorTest.java396
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/AMQBrokerManagerMBeanTest.java94
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/ExtractResendAndRequeueTest.java255
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/RunBrokerWithCommand.java132
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/SelectorParserTest.java128
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/ack/AcknowledgeTest.java120
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/QueueConfigurationTest.java145
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java1492
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/TopicConfigurationTest.java130
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/VirtualHostConfigurationTest.java233
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/plugins/ConfigurationPluginTest.java210
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java625
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java195
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersBindingTest.java317
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersExchangeTest.java124
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/TopicExchangeTest.java441
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/Log4jMessageLoggerTest.java271
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/LogMessageTest.java169
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/UnitTestMessageLogger.java72
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/UnitTestMessageLoggerTest.java103
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/AMQPChannelActorTest.java221
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/AMQPConnectionActorTest.java132
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/BaseActorTestCase.java91
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/BaseConnectionActorTestCase.java33
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/CurrentActorTest.java256
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/ManagementActorTest.java103
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/QueueActorTest.java73
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/SubscriptionActorTest.java85
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/TestLogActor.java37
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/management/LoggingManagementMBeanTest.java431
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/AbstractTestMessages.java109
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/BindingMessagesTest.java64
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/BrokerMessagesTest.java116
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/ChannelMessagesTest.java64
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/ConnectionMessagesTest.java90
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/ExchangeMessagesTest.java80
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/ManagementConsoleMessagesTest.java107
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/MessageStoreMessagesTest.java125
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/QueueMessagesTest.java239
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/SubscriptionMessagesTest.java86
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/VirtualHostMessagesTest.java51
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/AbstractTestLogSubject.java288
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/BindingLogSubjectTest.java71
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/ChannelLogSubjectTest.java58
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/ConnectionLogSubjectTest.java46
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/ExchangeLogSubjectTest.java58
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/MessageStoreLogSubjectTest.java62
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/QueueLogSubjectTest.java63
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/SubscriptionLogSubjectTest.java105
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/TestBlankSubject.java33
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/management/AMQUserManagementMBeanTest.java191
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/plugins/MockPluginManager.java56
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/plugins/PluginTest.java56
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java146
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java213
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/MaxChannelsTest.java79
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQPriorityQueueTest.java108
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java350
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueFactoryTest.java75
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java456
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AckTest.java427
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQMessage.java43
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java614
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockMessagePublishInfo.java52
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockQueueEntry.java234
-rwxr-xr-xqpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockStoredMessage.java92
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/queue/QueueEntryTest.java97
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java817
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueThreadPoolTest.java59
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryListTest.java159
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/registry/ApplicationRegistryShutdownTest.java105
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabaseTest.java466
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/HashedUserTest.java95
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabaseTest.java416
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/PlainUserTest.java78
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManagerTest.java209
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java267
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/CRAMMD5HexInitialiserTest.java137
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/CRAMMD5HexServerTest.java230
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/SaslServerTestCase.java66
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/TestPrincipalDatabase.java91
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/amqplain/AMQPlainSaslServerTest.java43
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServerTest.java39
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/stats/StatisticsCounterTest.java144
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreShutdownTest.java81
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreTest.java908
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/store/ReferenceCountingTest.java163
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/store/SkeletonMessageStore.java226
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/store/TestMemoryMessageStore.java98
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/store/TestableMemoryMessageStore.java157
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java262
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/subscription/QueueBrowserUsesNoAckTest.java77
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/txn/AutoCommitTransactionTest.java442
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/txn/LocalTransactionTest.java557
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/txn/MockAction.java56
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/txn/MockServerMessage.java114
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/txn/MockStoreTransaction.java136
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/util/InternalBrokerBaseCase.java365
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/util/LoggingProxyTest.java88
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.java48
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/virtualhost/plugins/SlowConsumerDetectionConfigurationTest.java346
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/virtualhost/plugins/SlowConsumerDetectionPolicyConfigurationTest.java104
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/virtualhost/plugins/SlowConsumerDetectionQueueConfigurationTest.java185
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/virtualhost/plugins/policies/TopicDeletePolicyConfigurationTest.java88
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/virtualhost/plugins/policies/TopicDeletePolicyTest.java293
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/util/MockChannel.java40
106 files changed, 20573 insertions, 0 deletions
diff --git a/qpid/java/broker/src/test/java/org/apache/log4j/xml/QpidLog4JConfiguratorTest.java b/qpid/java/broker/src/test/java/org/apache/log4j/xml/QpidLog4JConfiguratorTest.java
new file mode 100644
index 0000000000..445c7d57f2
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/log4j/xml/QpidLog4JConfiguratorTest.java
@@ -0,0 +1,396 @@
+/*
+ * 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.log4j.xml;
+
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+import org.apache.log4j.xml.QpidLog4JConfigurator.IllegalLoggerLevelException;
+
+import junit.framework.TestCase;
+
+public class QpidLog4JConfiguratorTest extends TestCase
+{
+ private static final String NEWLINE = System.getProperty("line.separator");
+
+ private File _testConfigFile;
+
+ private File createTempTestLog4JConfig(String loggerLevel,String rootLoggerLevel, boolean missingTagClose, boolean incorrectAttribute)
+ {
+ File tmpFile = null;
+ try
+ {
+ tmpFile = File.createTempFile("QpidLog4JConfiguratorTestLog4jConfig", ".tmp");
+ tmpFile.deleteOnExit();
+
+ FileWriter fstream = new FileWriter(tmpFile);
+ BufferedWriter writer = new BufferedWriter(fstream);
+
+ writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+NEWLINE);
+ writer.write("<!DOCTYPE log4j:configuration SYSTEM \"log4j.dtd\">"+NEWLINE);
+
+ writer.write("<log4j:configuration xmlns:log4j=\"http://jakarta.apache.org/log4j/\" debug=\"null\" " +
+ "threshold=\"null\">"+NEWLINE);
+
+ writer.write(" <appender class=\"org.apache.log4j.ConsoleAppender\" name=\"STDOUT\">"+NEWLINE);
+ writer.write(" <layout class=\"org.apache.log4j.PatternLayout\">"+NEWLINE);
+ writer.write(" <param name=\"ConversionPattern\" value=\"%d %-5p [%t] %C{2} (%F:%L) - %m%n\"/>"+NEWLINE);
+ writer.write(" </layout>"+NEWLINE);
+ writer.write(" </appender>"+NEWLINE);
+
+ String closeTag="/";
+ if(missingTagClose)
+ {
+ closeTag="";
+ }
+
+ //Example of a 'category' with a 'priority'
+ writer.write(" <category additivity=\"true\" name=\"logger1\">"+NEWLINE);
+ writer.write(" <priority value=\"" + loggerLevel+ "\"" + closeTag + ">"+NEWLINE);
+ writer.write(" <appender-ref ref=\"STDOUT\"/>"+NEWLINE);
+ writer.write(" </category>"+NEWLINE);
+
+ String attributeName="value";
+ if(incorrectAttribute)
+ {
+ attributeName="values";
+ }
+
+ //Example of a 'category' with a 'level'
+ writer.write(" <category additivity=\"true\" name=\"logger2\">"+NEWLINE);
+ writer.write(" <level " + attributeName + "=\"" + loggerLevel+ "\"/>"+NEWLINE);
+ writer.write(" <appender-ref ref=\"STDOUT\"/>"+NEWLINE);
+ writer.write(" </category>"+NEWLINE);
+
+ //Example of a 'logger' with a 'level'
+ writer.write(" <logger additivity=\"true\" name=\"logger3\">"+NEWLINE);
+ writer.write(" <level value=\"" + loggerLevel+ "\"/>"+NEWLINE);
+ writer.write(" <appender-ref ref=\"STDOUT\"/>"+NEWLINE);
+ writer.write(" </logger>"+NEWLINE);
+
+ //'root' logger
+ writer.write(" <root>"+NEWLINE);
+ writer.write(" <priority value=\"" + rootLoggerLevel+ "\"/>"+NEWLINE);
+ writer.write(" <appender-ref ref=\"STDOUT\"/>"+NEWLINE);
+ writer.write(" </root>"+NEWLINE);
+
+ writer.write("</log4j:configuration>"+NEWLINE);
+
+ writer.flush();
+ writer.close();
+ }
+ catch (IOException e)
+ {
+ fail("Unable to create temporary test log4j configuration");
+ }
+
+ return tmpFile;
+ }
+
+
+
+ //******* Test Methods ******* //
+
+ public void testCheckLevelsAndStrictParser()
+ {
+ //try the valid logger levels
+ _testConfigFile = createTempTestLog4JConfig("all", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("trace", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("debug", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("warn", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("error", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("fatal", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("off", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("null", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("inherited", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ //now try an invalid logger level
+ _testConfigFile = createTempTestLog4JConfig("madeup", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ fail("IllegalLoggerLevelException expected, invalid levels used");
+ }
+ catch (IllegalLoggerLevelException e)
+ {
+ //expected, ignore
+ }
+ catch (IOException e)
+ {
+ fail("Incorrect Exception, expected an IllegalLoggerLevelException");
+ }
+
+
+
+ //now try the valid rootLogger levels
+ _testConfigFile = createTempTestLog4JConfig("info", "all", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "trace", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "debug", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "warn", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "error", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "fatal", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "off", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "null", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "inherited", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "debug", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ //now try an invalid logger level
+ _testConfigFile = createTempTestLog4JConfig("info", "madeup", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ fail("IllegalLoggerLevelException expected, invalid levels used");
+ }
+ catch (IllegalLoggerLevelException e)
+ {
+ //expected, ignore
+ }
+ catch (IOException e)
+ {
+ fail("Incorrect Exception, expected an IllegalLoggerLevelException");
+ }
+
+
+
+ //now try invalid xml
+ _testConfigFile = createTempTestLog4JConfig("info", "info", true, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ fail("IOException expected, malformed XML used");
+ }
+ catch (IllegalLoggerLevelException e)
+ {
+ fail("Incorrect Exception, expected an IOException");
+ }
+ catch (IOException e)
+ {
+ //expected, ignore
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "info", false, true);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ fail("IOException expected, malformed XML used");
+ }
+ catch (IllegalLoggerLevelException e)
+ {
+ //expected, ignore
+ }
+ catch (IOException e)
+ {
+ fail("Incorrect Exception, expected an IllegalLoggerLevelException");
+ }
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/AMQBrokerManagerMBeanTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/AMQBrokerManagerMBeanTest.java
new file mode 100644
index 0000000000..6c135e8ba7
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/AMQBrokerManagerMBeanTest.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;
+
+import junit.framework.TestCase;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.management.common.mbeans.ManagedBroker;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.registry.IApplicationRegistry;
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+import org.apache.qpid.server.virtualhost.VirtualHostImpl;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+public class AMQBrokerManagerMBeanTest extends InternalBrokerBaseCase
+{
+ private QueueRegistry _queueRegistry;
+ private ExchangeRegistry _exchangeRegistry;
+ private VirtualHost _vHost;
+
+ public void testExchangeOperations() throws Exception
+ {
+ String exchange1 = "testExchange1_" + System.currentTimeMillis();
+ String exchange2 = "testExchange2_" + System.currentTimeMillis();
+ String exchange3 = "testExchange3_" + System.currentTimeMillis();
+
+ assertTrue(_exchangeRegistry.getExchange(new AMQShortString(exchange1)) == null);
+ assertTrue(_exchangeRegistry.getExchange(new AMQShortString(exchange2)) == null);
+ assertTrue(_exchangeRegistry.getExchange(new AMQShortString(exchange3)) == null);
+
+
+ ManagedBroker mbean = new AMQBrokerManagerMBean((VirtualHostImpl.VirtualHostMBean) _vHost.getManagedObject());
+ mbean.createNewExchange(exchange1, "direct", false);
+ mbean.createNewExchange(exchange2, "topic", false);
+ mbean.createNewExchange(exchange3, "headers", false);
+
+ assertTrue(_exchangeRegistry.getExchange(new AMQShortString(exchange1)) != null);
+ assertTrue(_exchangeRegistry.getExchange(new AMQShortString(exchange2)) != null);
+ assertTrue(_exchangeRegistry.getExchange(new AMQShortString(exchange3)) != null);
+
+ mbean.unregisterExchange(exchange1);
+ mbean.unregisterExchange(exchange2);
+ mbean.unregisterExchange(exchange3);
+
+ assertTrue(_exchangeRegistry.getExchange(new AMQShortString(exchange1)) == null);
+ assertTrue(_exchangeRegistry.getExchange(new AMQShortString(exchange2)) == null);
+ assertTrue(_exchangeRegistry.getExchange(new AMQShortString(exchange3)) == null);
+ }
+
+ public void testQueueOperations() throws Exception
+ {
+ String queueName = "testQueue_" + System.currentTimeMillis();
+
+ ManagedBroker mbean = new AMQBrokerManagerMBean((VirtualHostImpl.VirtualHostMBean) _vHost.getManagedObject());
+
+ assertTrue(_queueRegistry.getQueue(new AMQShortString(queueName)) == null);
+
+ mbean.createNewQueue(queueName, "test", false);
+ assertTrue(_queueRegistry.getQueue(new AMQShortString(queueName)) != null);
+
+ mbean.deleteQueue(queueName);
+ assertTrue(_queueRegistry.getQueue(new AMQShortString(queueName)) == null);
+ }
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ IApplicationRegistry appRegistry = ApplicationRegistry.getInstance();
+ _vHost = appRegistry.getVirtualHostRegistry().getVirtualHost("test");
+ _queueRegistry = _vHost.getQueueRegistry();
+ _exchangeRegistry = _vHost.getExchangeRegistry();
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/ExtractResendAndRequeueTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/ExtractResendAndRequeueTest.java
new file mode 100644
index 0000000000..d2408ba21f
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/ExtractResendAndRequeueTest.java
@@ -0,0 +1,255 @@
+/*
+ *
+ * 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;
+
+import junit.framework.TestCase;
+import org.apache.qpid.server.ack.UnacknowledgedMessageMapImpl;
+import org.apache.qpid.server.queue.QueueEntry;
+import org.apache.qpid.server.queue.SimpleQueueEntryList;
+import org.apache.qpid.server.queue.MockAMQMessage;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.MockAMQQueue;
+import org.apache.qpid.server.message.AMQMessage;
+import org.apache.qpid.server.queue.QueueEntryIterator;
+import org.apache.qpid.server.subscription.Subscription;
+import org.apache.qpid.server.subscription.MockSubscription;
+import org.apache.qpid.server.store.MemoryMessageStore;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.AMQException;
+
+import java.util.Map;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+
+/**
+ * QPID-1385 : Race condition between added to unacked map and resending due to a rollback.
+ *
+ * In AMQChannel _unackedMap.clear() was done after the visit. This meant that the clear was not in the same
+ * synchronized block as as the preparation to resend.
+ *
+ * This clearing/prep for resend was done as a result of the rollback call. HOWEVER, the delivery thread was still
+ * in the process of sending messages to the client. It is therefore possible that a message could block on the
+ * _unackedMap lock waiting for the visit to compelete so that it can add the new message to the unackedMap....
+ * which is then cleared by the resend/rollback thread.
+ *
+ * This problem was encountered by the testSend2ThenRollback test.
+ *
+ * To try and increase the chance of the race condition occuring this test will send multiple messages so that the
+ * delivery thread will be in progress while the rollback method is called. Hopefully this will cause the
+ * deliveryTag to be lost
+ */
+public class ExtractResendAndRequeueTest extends TestCase
+{
+
+ UnacknowledgedMessageMapImpl _unacknowledgedMessageMap;
+ private static final int INITIAL_MSG_COUNT = 10;
+ private AMQQueue _queue = new MockAMQQueue(getName());
+ private MessageStore _messageStore = new MemoryMessageStore();
+ private LinkedList<QueueEntry> _referenceList = new LinkedList<QueueEntry>();
+
+ @Override
+ public void setUp() throws AMQException
+ {
+ _unacknowledgedMessageMap = new UnacknowledgedMessageMapImpl(100);
+
+ long id = 0;
+ SimpleQueueEntryList list = new SimpleQueueEntryList(_queue);
+
+ // Add initial messages to QueueEntryList
+ for (int count = 0; count < INITIAL_MSG_COUNT; count++)
+ {
+ AMQMessage msg = new MockAMQMessage(id);
+
+ list.add(msg);
+
+ //Increment ID;
+ id++;
+ }
+
+ // Iterate through the QueueEntryList and add entries to unacknowledgeMessageMap and referecenList
+ QueueEntryIterator queueEntries = list.iterator();
+ while(queueEntries.advance())
+ {
+ QueueEntry entry = queueEntries.getNode();
+ _unacknowledgedMessageMap.add(entry.getMessage().getMessageNumber(), entry);
+
+ // Store the entry for future inspection
+ _referenceList.add(entry);
+ }
+
+ assertEquals("Map does not contain correct setup data", INITIAL_MSG_COUNT, _unacknowledgedMessageMap.size());
+ }
+
+ /**
+ * Helper method to create a new subscription and aquire the given messages.
+ *
+ * @param messageList The messages to aquire
+ *
+ * @return Subscription that performed the aquire
+ */
+ private Subscription createSubscriptionAndAquireMessages(LinkedList<QueueEntry> messageList)
+ {
+ Subscription subscription = new MockSubscription();
+
+ // Aquire messages in subscription
+ for (QueueEntry entry : messageList)
+ {
+ entry.acquire(subscription);
+ }
+
+ return subscription;
+ }
+
+ /**
+ * This is the normal consumer rollback method.
+ *
+ * An active consumer that has aquired messages expects those messasges to be reset when rollback is requested.
+ *
+ * This test validates that the msgToResend map includes all the messages and none are left behind.
+ *
+ * @throws AMQException the visit interface throws this
+ */
+ public void testResend() throws AMQException
+ {
+ //We don't need the subscription object here.
+ createSubscriptionAndAquireMessages(_referenceList);
+
+ final Map<Long, QueueEntry> msgToRequeue = new LinkedHashMap<Long, QueueEntry>();
+ final Map<Long, QueueEntry> msgToResend = new LinkedHashMap<Long, QueueEntry>();
+
+ // requeueIfUnabletoResend doesn't matter here.
+ _unacknowledgedMessageMap.visit(new ExtractResendAndRequeue(_unacknowledgedMessageMap, msgToRequeue,
+ msgToResend, true, _messageStore));
+
+ assertEquals("Message count for resend not correct.", INITIAL_MSG_COUNT, msgToResend.size());
+ assertEquals("Message count for requeue not correct.", 0, msgToRequeue.size());
+ assertEquals("Map was not emptied", 0, _unacknowledgedMessageMap.size());
+ }
+
+ /**
+ * This is the normal consumer close method.
+ *
+ * When a consumer that has aquired messages expects closes the messages that it has aquired should be removed from
+ * the unacknowledgeMap and placed in msgToRequeue
+ *
+ * This test validates that the msgToRequeue map includes all the messages and none are left behind.
+ *
+ * @throws AMQException the visit interface throws this
+ */
+ public void testRequeueDueToSubscriptionClosure() throws AMQException
+ {
+ Subscription subscription = createSubscriptionAndAquireMessages(_referenceList);
+
+ // Close subscription
+ subscription.close();
+
+ final Map<Long, QueueEntry> msgToRequeue = new LinkedHashMap<Long, QueueEntry>();
+ final Map<Long, QueueEntry> msgToResend = new LinkedHashMap<Long, QueueEntry>();
+
+ // requeueIfUnabletoResend doesn't matter here.
+ _unacknowledgedMessageMap.visit(new ExtractResendAndRequeue(_unacknowledgedMessageMap, msgToRequeue,
+ msgToResend, true, _messageStore));
+
+ assertEquals("Message count for resend not correct.", 0, msgToResend.size());
+ assertEquals("Message count for requeue not correct.", INITIAL_MSG_COUNT, msgToRequeue.size());
+ assertEquals("Map was not emptied", 0, _unacknowledgedMessageMap.size());
+ }
+
+ /**
+ * If the subscription is null, due to message being retrieved via a GET, And we request that messages are requeued
+ * requeueIfUnabletoResend(set to true) then all messages should be sent to the msgToRequeue map.
+ *
+ * @throws AMQException the visit interface throws this
+ */
+
+ public void testRequeueDueToMessageHavingNoConsumerTag() throws AMQException
+ {
+ final Map<Long, QueueEntry> msgToRequeue = new LinkedHashMap<Long, QueueEntry>();
+ final Map<Long, QueueEntry> msgToResend = new LinkedHashMap<Long, QueueEntry>();
+
+ // requeueIfUnabletoResend = true so all messages should go to msgToRequeue
+ _unacknowledgedMessageMap.visit(new ExtractResendAndRequeue(_unacknowledgedMessageMap, msgToRequeue,
+ msgToResend, true, _messageStore));
+
+ assertEquals("Message count for resend not correct.", 0, msgToResend.size());
+ assertEquals("Message count for requeue not correct.", INITIAL_MSG_COUNT, msgToRequeue.size());
+ assertEquals("Map was not emptied", 0, _unacknowledgedMessageMap.size());
+ }
+
+ /**
+ * If the subscription is null, due to message being retrieved via a GET, And we request that we don't
+ * requeueIfUnabletoResend(set to false) then all messages should be dropped as we do not have a dead letter queue.
+ *
+ * @throws AMQException the visit interface throws this
+ */
+
+ public void testDrop() throws AMQException
+ {
+ final Map<Long, QueueEntry> msgToRequeue = new LinkedHashMap<Long, QueueEntry>();
+ final Map<Long, QueueEntry> msgToResend = new LinkedHashMap<Long, QueueEntry>();
+
+ // requeueIfUnabletoResend = false so all messages should be dropped all maps should be empty
+ _unacknowledgedMessageMap.visit(new ExtractResendAndRequeue(_unacknowledgedMessageMap, msgToRequeue,
+ msgToResend, false, _messageStore));
+
+ assertEquals("Message count for resend not correct.", 0, msgToResend.size());
+ assertEquals("Message count for requeue not correct.", 0, msgToRequeue.size());
+ assertEquals("Map was not emptied", 0, _unacknowledgedMessageMap.size());
+
+
+ for (QueueEntry entry : _referenceList)
+ {
+ assertTrue("Message was not discarded", entry.isDeleted());
+ }
+
+ }
+
+ /**
+ * If the subscription is null, due to message being retrieved via a GET, AND the queue upon which the message was
+ * delivered has been deleted then it is not possible to requeue. Currently we simply discar the message but in the
+ * future we may wish to dead letter the message.
+ *
+ * Validate that at the end of the visit all Maps are empty and all messages are marked as deleted
+ *
+ * @throws AMQException the visit interface throws this
+ */
+ public void testDiscard() throws AMQException
+ {
+ final Map<Long, QueueEntry> msgToRequeue = new LinkedHashMap<Long, QueueEntry>();
+ final Map<Long, QueueEntry> msgToResend = new LinkedHashMap<Long, QueueEntry>();
+
+ _queue.delete();
+
+ // requeueIfUnabletoResend : value doesn't matter here as queue has been deleted
+ _unacknowledgedMessageMap.visit(new ExtractResendAndRequeue(_unacknowledgedMessageMap, msgToRequeue,
+ msgToResend, false, _messageStore));
+
+ assertEquals("Message count for resend not correct.", 0, msgToResend.size());
+ assertEquals("Message count for requeue not correct.", 0, msgToRequeue.size());
+ assertEquals("Map was not emptied", 0, _unacknowledgedMessageMap.size());
+
+ for (QueueEntry entry : _referenceList)
+ {
+ assertTrue("Message was not discarded", entry.isDeleted());
+ }
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/RunBrokerWithCommand.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/RunBrokerWithCommand.java
new file mode 100644
index 0000000000..59543874b4
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/RunBrokerWithCommand.java
@@ -0,0 +1,132 @@
+/*
+ * 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;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.Level;
+
+import java.io.InputStream;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.IOException;
+
+public class RunBrokerWithCommand
+{
+ public static void main(String[] args)
+ {
+ //Start the broker
+ try
+ {
+ String[] fudge = args.clone();
+
+ // Override the first value which is the command we are going to run later.
+ fudge[0] = "-v";
+ new Main(fudge).startup();
+ }
+ catch (Exception e)
+ {
+ System.err.println("Unable to start broker due to: " + e.getMessage());
+
+ e.printStackTrace();
+ exit(1);
+ }
+
+ Logger.getRootLogger().setLevel(Level.ERROR);
+
+ //run command
+ try
+ {
+ Process task = Runtime.getRuntime().exec(args[0]);
+ System.err.println("Started Proccess: " + args[0]);
+
+ InputStream inputStream = task.getInputStream();
+
+ InputStream errorStream = task.getErrorStream();
+
+ Thread out = new Thread(new Outputter("[OUT]", new BufferedReader(new InputStreamReader(inputStream))));
+ Thread err = new Thread(new Outputter("[ERR]", new BufferedReader(new InputStreamReader(errorStream))));
+
+ out.start();
+ err.start();
+
+ out.join();
+ err.join();
+
+ System.err.println("Waiting for process to exit: " + args[0]);
+ task.waitFor();
+ System.err.println("Done Proccess: " + args[0]);
+
+ }
+ catch (IOException e)
+ {
+ System.err.println("Proccess had problems: " + e.getMessage());
+ e.printStackTrace(System.err);
+ exit(1);
+ }
+ catch (InterruptedException e)
+ {
+ System.err.println("Proccess had problems: " + e.getMessage());
+ e.printStackTrace(System.err);
+
+ exit(1);
+ }
+
+
+ exit(0);
+ }
+
+ private static void exit(int i)
+ {
+ Logger.getRootLogger().setLevel(Level.INFO);
+ System.exit(i);
+ }
+
+ static class Outputter implements Runnable
+ {
+
+ BufferedReader reader;
+ String prefix;
+
+ Outputter(String s, BufferedReader r)
+ {
+ prefix = s;
+ reader = r;
+ }
+
+ public void run()
+ {
+ String line;
+ try
+ {
+ while ((line = reader.readLine()) != null)
+ {
+ System.out.println(prefix + line);
+ }
+ }
+ catch (IOException e)
+ {
+ System.out.println("Error occured reading; " + e.getMessage());
+ }
+ }
+
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/SelectorParserTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/SelectorParserTest.java
new file mode 100644
index 0000000000..a0304a7b01
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/SelectorParserTest.java
@@ -0,0 +1,128 @@
+package org.apache.qpid.server;
+
+import junit.framework.TestCase;
+import org.apache.qpid.server.filter.JMSSelectorFilter;
+import org.apache.qpid.AMQException;/*
+ *
+ * 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.
+ *
+ */
+
+public class SelectorParserTest extends TestCase
+{
+ public void testSelectorWithHyphen()
+ {
+ testPass("Cost = 2 AND \"property-with-hyphen\" = 'wibble'");
+ }
+
+ public void testLike()
+ {
+ testFail("Cost LIKE 2");
+ testPass("Cost LIKE 'Hello'");
+ }
+
+ public void testStringQuoted()
+ {
+ testPass("string = 'Test'");
+ }
+
+ public void testProperty()
+ {
+ testPass("prop1 = prop2");
+ }
+
+ public void testPropertyNames()
+ {
+ testPass("$min= TRUE AND _max= FALSE AND Prop_2 = true AND prop$3 = false");
+ }
+
+ public void testProtected()
+ {
+ testFail("NULL = 0 ");
+ testFail("TRUE = 0 ");
+ testFail("FALSE = 0 ");
+ testFail("NOT = 0 ");
+ testFail("AND = 0 ");
+ testFail("OR = 0 ");
+ testFail("BETWEEN = 0 ");
+ testFail("LIKE = 0 ");
+ testFail("IN = 0 ");
+ testFail("IS = 0 ");
+ testFail("ESCAPE = 0 ");
+ }
+
+
+ public void testBoolean()
+ {
+ testPass("min= TRUE AND max= FALSE ");
+ testPass("min= true AND max= false");
+ }
+
+ public void testDouble()
+ {
+ testPass("positive=31E2 AND negative=-31.4E3");
+ testPass("min=" + Double.MIN_VALUE + " AND max=" + Double.MAX_VALUE);
+ }
+
+ public void testLong()
+ {
+ testPass("minLong=" + Long.MIN_VALUE + "L AND maxLong=" + Long.MAX_VALUE + "L");
+ }
+
+ public void testInt()
+ {
+ testPass("minInt=" + Integer.MIN_VALUE + " AND maxInt=" + Integer.MAX_VALUE);
+ }
+
+ public void testSigned()
+ {
+ testPass("negative=-42 AND positive=+42");
+ }
+
+ public void testOctal()
+ {
+ testPass("octal=042");
+ }
+
+
+ private void testPass(String selector)
+ {
+ try
+ {
+ new JMSSelectorFilter(selector);
+ }
+ catch (AMQException e)
+ {
+ fail("Selector '" + selector + "' was not parsed :" + e.getMessage());
+ }
+ }
+
+ private void testFail(String selector)
+ {
+ try
+ {
+ new JMSSelectorFilter(selector);
+ fail("Selector '" + selector + "' was parsed ");
+ }
+ catch (AMQException e)
+ {
+ //normal path
+ }
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/ack/AcknowledgeTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/ack/AcknowledgeTest.java
new file mode 100644
index 0000000000..b3223f16c4
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/ack/AcknowledgeTest.java
@@ -0,0 +1,120 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.ack;
+
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.protocol.InternalTestProtocolSession;
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+
+import java.util.List;
+
+public class AcknowledgeTest extends InternalBrokerBaseCase
+{
+
+ public void testTransactionalSingleAck() throws AMQException
+ {
+ getChannel().setLocalTransactional();
+ runMessageAck(1, 1, 1, false, 0);
+ }
+
+ public void testTransactionalMultiAck() throws AMQException
+ {
+ getChannel().setLocalTransactional();
+ runMessageAck(10, 1, 5, true, 5);
+ }
+
+ public void testTransactionalAckAll() throws AMQException
+ {
+ getChannel().setLocalTransactional();
+ runMessageAck(10, 1, 0, true, 0);
+ }
+
+ public void testNonTransactionalSingleAck() throws AMQException
+ {
+ runMessageAck(1, 1, 1, false, 0);
+ }
+
+ public void testNonTransactionalMultiAck() throws AMQException
+ {
+ runMessageAck(10, 1, 5, true, 5);
+ }
+
+ public void testNonTransactionalAckAll() throws AMQException
+ {
+ runMessageAck(10, 1, 0, true, 0);
+ }
+
+ protected void runMessageAck(int sendMessageCount, long firstDeliveryTag, long acknowledgeDeliveryTag, boolean acknowldegeMultiple, int remainingUnackedMessages) throws AMQException
+ {
+ //Check store is empty
+ checkStoreContents(0);
+
+ //Send required messsages to the queue
+ publishMessages(getSession(), getChannel(), sendMessageCount);
+
+ if (getChannel().isTransactional())
+ {
+ getChannel().commit();
+ }
+
+ //Ensure they are stored
+ checkStoreContents(sendMessageCount);
+
+ //Check that there are no unacked messages
+ assertEquals("Channel should have no unacked msgs ", 0, getChannel().getUnacknowledgedMessageMap().size());
+
+ //Subscribe to the queue
+ AMQShortString subscriber = subscribe(getSession(), getChannel(), getQueue());
+
+ getQueue().deliverAsync();
+
+ //Wait for the messages to be delivered
+ getSession().awaitDelivery(sendMessageCount);
+
+ //Check that they are all waiting to be acknoledged
+ assertEquals("Channel should have unacked msgs", sendMessageCount, getChannel().getUnacknowledgedMessageMap().size());
+
+ List<InternalTestProtocolSession.DeliveryPair> messages = getSession().getDelivers(getChannel().getChannelId(), subscriber, sendMessageCount);
+
+ //Double check we received the right number of messages
+ assertEquals(sendMessageCount, messages.size());
+
+ //Check that the first message has the expected deliveryTag
+ assertEquals("First message does not have expected deliveryTag", firstDeliveryTag, messages.get(0).getDeliveryTag());
+
+ //Send required Acknowledgement
+ getChannel().acknowledgeMessage(acknowledgeDeliveryTag, acknowldegeMultiple);
+
+ if (getChannel().isTransactional())
+ {
+ getChannel().commit();
+ }
+
+ // Check Remaining Acknowledgements
+ assertEquals("Channel unacked msgs count incorrect", remainingUnackedMessages, getChannel().getUnacknowledgedMessageMap().size());
+
+ //Check store contents are also correct.
+ checkStoreContents(remainingUnackedMessages);
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/QueueConfigurationTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/QueueConfigurationTest.java
new file mode 100644
index 0000000000..d2f2ae5eea
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/QueueConfigurationTest.java
@@ -0,0 +1,145 @@
+/*
+ *
+ * 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;
+
+import junit.framework.TestCase;
+import org.apache.commons.configuration.CompositeConfiguration;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.PropertiesConfiguration;
+
+public class QueueConfigurationTest extends TestCase
+{
+
+ private VirtualHostConfiguration _emptyConf;
+ private PropertiesConfiguration _env;
+ private VirtualHostConfiguration _fullHostConf;
+
+ public void setUp() throws Exception
+ {
+ _env = new PropertiesConfiguration();
+ _emptyConf = new VirtualHostConfiguration("test", _env);
+
+ PropertiesConfiguration fullEnv = new PropertiesConfiguration();
+ fullEnv.setProperty("queues.maximumMessageAge", 1);
+ fullEnv.setProperty("queues.maximumQueueDepth", 1);
+ fullEnv.setProperty("queues.maximumMessageSize", 1);
+ fullEnv.setProperty("queues.maximumMessageCount", 1);
+ fullEnv.setProperty("queues.minimumAlertRepeatGap", 1);
+
+ _fullHostConf = new VirtualHostConfiguration("test", fullEnv);
+
+ }
+
+ public void testGetMaximumMessageAge() throws ConfigurationException
+ {
+ // Check default value
+ QueueConfiguration qConf = new QueueConfiguration("test", _emptyConf);
+ assertEquals(0, qConf.getMaximumMessageAge());
+
+ // Check explicit value
+ VirtualHostConfiguration vhostConfig = overrideConfiguration("maximumMessageAge", 2);
+
+ qConf = new QueueConfiguration("test", vhostConfig);
+ assertEquals(2, qConf.getMaximumMessageAge());
+
+ // Check inherited value
+ qConf = new QueueConfiguration("test", _fullHostConf);
+ assertEquals(1, qConf.getMaximumMessageAge());
+ }
+
+ public void testGetMaximumQueueDepth() throws ConfigurationException
+ {
+ // Check default value
+ QueueConfiguration qConf = new QueueConfiguration("test", _emptyConf);
+ assertEquals(0, qConf.getMaximumQueueDepth());
+
+ // Check explicit value
+ VirtualHostConfiguration vhostConfig = overrideConfiguration("maximumQueueDepth", 2);
+ qConf = new QueueConfiguration("test", vhostConfig);
+ assertEquals(2, qConf.getMaximumQueueDepth());
+
+ // Check inherited value
+ qConf = new QueueConfiguration("test", _fullHostConf);
+ assertEquals(1, qConf.getMaximumQueueDepth());
+ }
+
+ public void testGetMaximumMessageSize() throws ConfigurationException
+ {
+ // Check default value
+ QueueConfiguration qConf = new QueueConfiguration("test", _emptyConf);
+ assertEquals(0, qConf.getMaximumMessageSize());
+
+ // Check explicit value
+ VirtualHostConfiguration vhostConfig = overrideConfiguration("maximumMessageSize", 2);
+ qConf = new QueueConfiguration("test", vhostConfig);
+ assertEquals(2, qConf.getMaximumMessageSize());
+
+ // Check inherited value
+ qConf = new QueueConfiguration("test", _fullHostConf);
+ assertEquals(1, qConf.getMaximumMessageSize());
+ }
+
+ public void testGetMaximumMessageCount() throws ConfigurationException
+ {
+ // Check default value
+ QueueConfiguration qConf = new QueueConfiguration("test", _emptyConf);
+ assertEquals(0, qConf.getMaximumMessageCount());
+
+ // Check explicit value
+ VirtualHostConfiguration vhostConfig = overrideConfiguration("maximumMessageCount", 2);
+ qConf = new QueueConfiguration("test", vhostConfig);
+ assertEquals(2, qConf.getMaximumMessageCount());
+
+ // Check inherited value
+ qConf = new QueueConfiguration("test", _fullHostConf);
+ assertEquals(1, qConf.getMaximumMessageCount());
+ }
+
+ public void testGetMinimumAlertRepeatGap() throws ConfigurationException
+ {
+ // Check default value
+ QueueConfiguration qConf = new QueueConfiguration("test", _emptyConf);
+ assertEquals(0, qConf.getMinimumAlertRepeatGap());
+
+ // Check explicit value
+ VirtualHostConfiguration vhostConfig = overrideConfiguration("minimumAlertRepeatGap", 2);
+ qConf = new QueueConfiguration("test", vhostConfig);
+ assertEquals(2, qConf.getMinimumAlertRepeatGap());
+
+ // Check inherited value
+ qConf = new QueueConfiguration("test", _fullHostConf);
+ assertEquals(1, qConf.getMinimumAlertRepeatGap());
+ }
+
+ private VirtualHostConfiguration overrideConfiguration(String property, int value)
+ throws ConfigurationException
+ {
+ PropertiesConfiguration queueConfig = new PropertiesConfiguration();
+ queueConfig.setProperty("queues.queue.test." + property, value);
+
+ CompositeConfiguration config = new CompositeConfiguration();
+ config.addConfiguration(_fullHostConf.getConfig());
+ config.addConfiguration(queueConfig);
+
+ return new VirtualHostConfiguration("test", config);
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java
new file mode 100644
index 0000000000..43540c88a1
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java
@@ -0,0 +1,1492 @@
+/*
+ *
+ * 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;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.List;
+import java.util.Locale;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.XMLConfiguration;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.protocol.AMQProtocolEngine;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.registry.ConfigurationFileApplicationRegistry;
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.server.virtualhost.VirtualHostRegistry;
+import org.apache.qpid.transport.TestNetworkDriver;
+
+public class ServerConfigurationTest extends InternalBrokerBaseCase
+{
+ private XMLConfiguration _config = new XMLConfiguration();
+
+
+ public void testSetJMXManagementPort() throws ConfigurationException
+ {
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ serverConfig.setJMXManagementPort(23);
+ assertEquals(23, serverConfig.getJMXManagementPort());
+ }
+
+ public void testGetJMXManagementPort() throws ConfigurationException
+ {
+ _config.setProperty("management.jmxport", 42);
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(42, serverConfig.getJMXManagementPort());
+ }
+
+ public void testGetPlatformMbeanserver() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(true, serverConfig.getPlatformMbeanserver());
+
+ // Check value we set
+ _config.setProperty("management.platform-mbeanserver", false);
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(false, serverConfig.getPlatformMbeanserver());
+ }
+
+ public void testGetPluginDirectory() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(null, serverConfig.getPluginDirectory());
+
+ // Check value we set
+ _config.setProperty("plugin-directory", "/path/to/plugins");
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals("/path/to/plugins", serverConfig.getPluginDirectory());
+ }
+
+ public void testGetCacheDirectory() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(null, serverConfig.getCacheDirectory());
+
+ // Check value we set
+ _config.setProperty("cache-directory", "/path/to/cache");
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals("/path/to/cache", serverConfig.getCacheDirectory());
+ }
+
+ public void testGetPrincipalDatabaseNames() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(0, serverConfig.getPrincipalDatabaseNames().size());
+
+ // Check value we set
+ _config.setProperty("security.principal-databases.principal-database(0).name", "a");
+ _config.setProperty("security.principal-databases.principal-database(1).name", "b");
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ List<String> dbs = serverConfig.getPrincipalDatabaseNames();
+ assertEquals(2, dbs.size());
+ assertEquals("a", dbs.get(0));
+ assertEquals("b", dbs.get(1));
+ }
+
+ public void testGetPrincipalDatabaseClass() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(0, serverConfig.getPrincipalDatabaseClass().size());
+
+ // Check value we set
+ _config.setProperty("security.principal-databases.principal-database(0).class", "a");
+ _config.setProperty("security.principal-databases.principal-database(1).class", "b");
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ List<String> dbs = serverConfig.getPrincipalDatabaseClass();
+ assertEquals(2, dbs.size());
+ assertEquals("a", dbs.get(0));
+ assertEquals("b", dbs.get(1));
+ }
+
+ public void testGetPrincipalDatabaseAttributeNames() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(0, serverConfig.getPrincipalDatabaseAttributeNames(1).size());
+
+ // Check value we set
+ _config.setProperty("security.principal-databases.principal-database(0).attributes(0).attribute.name", "a");
+ _config.setProperty("security.principal-databases.principal-database(0).attributes(1).attribute.name", "b");
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ List<String> dbs = serverConfig.getPrincipalDatabaseAttributeNames(0);
+ assertEquals(2, dbs.size());
+ assertEquals("a", dbs.get(0));
+ assertEquals("b", dbs.get(1));
+ }
+
+ public void testGetPrincipalDatabaseAttributeValues() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(0, serverConfig.getPrincipalDatabaseAttributeValues(1).size());
+
+ // Check value we set
+ _config.setProperty("security.principal-databases.principal-database(0).attributes(0).attribute.value", "a");
+ _config.setProperty("security.principal-databases.principal-database(0).attributes(1).attribute.value", "b");
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ List<String> dbs = serverConfig.getPrincipalDatabaseAttributeValues(0);
+ assertEquals(2, dbs.size());
+ assertEquals("a", dbs.get(0));
+ assertEquals("b", dbs.get(1));
+ }
+
+
+
+ public void testGetFrameSize() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(65536, serverConfig.getFrameSize());
+
+ // Check value we set
+ _config.setProperty("advanced.framesize", "23");
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(23, serverConfig.getFrameSize());
+ }
+
+ public void testGetProtectIOEnabled() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(false, serverConfig.getProtectIOEnabled());
+
+ // Check value we set
+ _config.setProperty(ServerConfiguration.CONNECTOR_PROTECTIO_ENABLED, true);
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(true, serverConfig.getProtectIOEnabled());
+ }
+
+ public void testGetBufferReadLimit() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(262144, serverConfig.getBufferReadLimit());
+
+ // Check value we set
+ _config.setProperty(ServerConfiguration.CONNECTOR_PROTECTIO_READ_BUFFER_LIMIT_SIZE, 23);
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(23, serverConfig.getBufferReadLimit());
+ }
+
+ public void testGetBufferWriteLimit() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(262144, serverConfig.getBufferWriteLimit());
+
+ // Check value we set
+ _config.setProperty(ServerConfiguration.CONNECTOR_PROTECTIO_WRITE_BUFFER_LIMIT_SIZE, 23);
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(23, serverConfig.getBufferWriteLimit());
+ }
+
+
+ public void testGetStatusEnabled() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(ServerConfiguration.DEFAULT_STATUS_UPDATES.equalsIgnoreCase("on"),
+ serverConfig.getStatusUpdatesEnabled());
+
+ // Check disabling we set
+ _config.setProperty(ServerConfiguration.STATUS_UPDATES, "off");
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(false, serverConfig.getStatusUpdatesEnabled());
+
+ // Check invalid values don't cause error but result in disabled
+ _config.setProperty(ServerConfiguration.STATUS_UPDATES, "Yes Please");
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(false, serverConfig.getStatusUpdatesEnabled());
+
+ }
+ public void testGetSynchedClocks() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(false, serverConfig.getSynchedClocks());
+
+ // Check value we set
+ _config.setProperty("advanced.synced-clocks", true);
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(true, serverConfig.getSynchedClocks());
+ }
+
+ public void testGetLocale() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+
+ // The Default is what ever the VMs default is
+ Locale defaultLocale = Locale.getDefault();
+
+ assertEquals(defaultLocale, serverConfig.getLocale());
+
+
+ //Test Language only
+ Locale update = new Locale("es");
+ _config.setProperty(ServerConfiguration.ADVANCED_LOCALE, "es");
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(update, serverConfig.getLocale());
+
+ //Test Language and Country
+ update = new Locale("es","ES");
+ _config.setProperty(ServerConfiguration.ADVANCED_LOCALE, "es_ES");
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(update, serverConfig.getLocale());
+
+ //Test Language and Country and Variant
+ update = new Locale("es","ES", "Traditional_WIN");
+ _config.setProperty(ServerConfiguration.ADVANCED_LOCALE, "es_ES_Traditional_WIN");
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(update, serverConfig.getLocale());
+ }
+
+
+ public void testGetMsgAuth() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(false, serverConfig.getMsgAuth());
+
+ // Check value we set
+ _config.setProperty("security.msg-auth", true);
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(true, serverConfig.getMsgAuth());
+ }
+
+ public void testGetJMXPrincipalDatabase() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(null, serverConfig.getJMXPrincipalDatabase());
+
+ // Check value we set
+ _config.setProperty("security.jmx.principal-database", "a");
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals("a", serverConfig.getJMXPrincipalDatabase());
+ }
+
+ public void testGetManagementKeyStorePath() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(null, serverConfig.getManagementKeyStorePath());
+
+ // Check value we set
+ _config.setProperty("management.ssl.keyStorePath", "a");
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals("a", serverConfig.getManagementKeyStorePath());
+ }
+
+ public void testGetManagementSSLEnabled() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(true, serverConfig.getManagementSSLEnabled());
+
+ // Check value we set
+ _config.setProperty("management.ssl.enabled", false);
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(false, serverConfig.getManagementSSLEnabled());
+ }
+
+ public void testGetManagementKeyStorePassword() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(null, serverConfig.getManagementKeyStorePassword());
+
+ // Check value we set
+ _config.setProperty("management.ssl.keyStorePassword", "a");
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals("a", serverConfig.getManagementKeyStorePassword());
+ }
+
+ public void testGetQueueAutoRegister() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(true, serverConfig.getQueueAutoRegister());
+
+ // Check value we set
+ _config.setProperty("queue.auto_register", false);
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(false, serverConfig.getQueueAutoRegister());
+ }
+
+ public void testGetManagementEnabled() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(true, serverConfig.getManagementEnabled());
+
+ // Check value we set
+ _config.setProperty("management.enabled", false);
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(false, serverConfig.getManagementEnabled());
+ }
+
+ public void testSetManagementEnabled() throws ConfigurationException
+ {
+ // Check value we set
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ serverConfig.setManagementEnabled(false);
+ assertEquals(false, serverConfig.getManagementEnabled());
+ }
+
+ public void testGetHeartBeatDelay() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(5, serverConfig.getHeartBeatDelay());
+
+ // Check value we set
+ _config.setProperty("heartbeat.delay", 23);
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(23, serverConfig.getHeartBeatDelay());
+ }
+
+ public void testGetHeartBeatTimeout() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(2.0, serverConfig.getHeartBeatTimeout());
+
+ // Check value we set
+ _config.setProperty("heartbeat.timeoutFactor", 2.3);
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(2.3, serverConfig.getHeartBeatTimeout());
+ }
+
+ public void testGetMaximumMessageAge() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(0, serverConfig.getMaximumMessageAge());
+
+ // Check value we set
+ _config.setProperty("maximumMessageAge", 10L);
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(10, serverConfig.getMaximumMessageAge());
+ }
+
+ public void testGetMaximumMessageCount() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(0, serverConfig.getMaximumMessageCount());
+
+ // Check value we set
+ _config.setProperty("maximumMessageCount", 10L);
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(10, serverConfig.getMaximumMessageCount());
+ }
+
+ public void testGetMaximumQueueDepth() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(0, serverConfig.getMaximumQueueDepth());
+
+ // Check value we set
+ _config.setProperty("maximumQueueDepth", 10L);
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(10, serverConfig.getMaximumQueueDepth());
+ }
+
+ public void testGetMaximumMessageSize() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(0, serverConfig.getMaximumMessageSize());
+
+ // Check value we set
+ _config.setProperty("maximumMessageSize", 10L);
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(10, serverConfig.getMaximumMessageSize());
+ }
+
+ public void testGetMinimumAlertRepeatGap() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(0, serverConfig.getMinimumAlertRepeatGap());
+
+ // Check value we set
+ _config.setProperty("minimumAlertRepeatGap", 10L);
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(10, serverConfig.getMinimumAlertRepeatGap());
+ }
+
+ public void testGetProcessors() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(4, serverConfig.getProcessors());
+
+ // Check value we set
+ _config.setProperty("connector.processors", 10);
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(10, serverConfig.getProcessors());
+ }
+
+ public void testGetPort() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertNotNull(serverConfig.getPorts());
+ assertEquals(1, serverConfig.getPorts().size());
+ assertEquals(5672, serverConfig.getPorts().get(0));
+
+
+ // Check value we set
+ _config.setProperty("connector.port", "10");
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertNotNull(serverConfig.getPorts());
+ assertEquals(1, serverConfig.getPorts().size());
+ assertEquals("10", serverConfig.getPorts().get(0));
+ }
+
+ public void testGetBind() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals("wildcard", serverConfig.getBind());
+
+ // Check value we set
+ _config.setProperty("connector.bind", "a");
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals("a", serverConfig.getBind());
+ }
+
+ public void testGetReceiveBufferSize() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(32767, serverConfig.getReceiveBufferSize());
+
+ // Check value we set
+ _config.setProperty("connector.socketReceiveBuffer", "23");
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(23, serverConfig.getReceiveBufferSize());
+ }
+
+ public void testGetWriteBufferSize() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(32767, serverConfig.getWriteBufferSize());
+
+ // Check value we set
+ _config.setProperty("connector.socketWriteBuffer", "23");
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(23, serverConfig.getWriteBufferSize());
+ }
+
+ public void testGetTcpNoDelay() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(true, serverConfig.getTcpNoDelay());
+
+ // Check value we set
+ _config.setProperty("connector.tcpNoDelay", false);
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(false, serverConfig.getTcpNoDelay());
+ }
+
+ public void testGetEnableExecutorPool() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(false, serverConfig.getEnableExecutorPool());
+
+ // Check value we set
+ _config.setProperty("advanced.filterchain[@enableExecutorPool]", true);
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(true, serverConfig.getEnableExecutorPool());
+ }
+
+ public void testGetEnableSSL() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(false, serverConfig.getEnableSSL());
+
+ // Check value we set
+ _config.setProperty("connector.ssl.enabled", true);
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(true, serverConfig.getEnableSSL());
+ }
+
+ public void testGetSSLOnly() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(false, serverConfig.getSSLOnly());
+
+ // Check value we set
+ _config.setProperty("connector.ssl.sslOnly", true);
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(true, serverConfig.getSSLOnly());
+ }
+
+ public void testGetSSLPort() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(8672, serverConfig.getSSLPort());
+
+ // Check value we set
+ _config.setProperty("connector.ssl.port", 23);
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(23, serverConfig.getSSLPort());
+ }
+
+ public void testGetKeystorePath() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals("none", serverConfig.getKeystorePath());
+
+ // Check value we set
+ _config.setProperty("connector.ssl.keystorePath", "a");
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals("a", serverConfig.getKeystorePath());
+ }
+
+ public void testGetKeystorePassword() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals("none", serverConfig.getKeystorePassword());
+
+ // Check value we set
+ _config.setProperty("connector.ssl.keystorePassword", "a");
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals("a", serverConfig.getKeystorePassword());
+ }
+
+ public void testGetCertType() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals("SunX509", serverConfig.getCertType());
+
+ // Check value we set
+ _config.setProperty("connector.ssl.certType", "a");
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals("a", serverConfig.getCertType());
+ }
+
+ public void testGetQpidNIO() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(false, serverConfig.getQpidNIO());
+
+ // Check value we set
+ _config.setProperty("connector.qpidnio", true);
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(true, serverConfig.getQpidNIO());
+ }
+
+ public void testGetUseBiasedWrites() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(false, serverConfig.getUseBiasedWrites());
+
+ // Check value we set
+ _config.setProperty("advanced.useWriteBiasedPool", true);
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(true, serverConfig.getUseBiasedWrites());
+ }
+
+ public void testGetHousekeepingExpiredMessageCheckPeriod() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(30000, serverConfig.getHousekeepingCheckPeriod());
+
+ // Check value we set
+ _config.setProperty("housekeeping.expiredMessageCheckPeriod", 23L);
+ serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+ assertEquals(23, serverConfig.getHousekeepingCheckPeriod());
+ serverConfig.setHousekeepingExpiredMessageCheckPeriod(42L);
+ assertEquals(42, serverConfig.getHousekeepingCheckPeriod());
+ }
+
+ public void testSingleConfiguration() throws IOException, ConfigurationException
+ {
+ File fileA = File.createTempFile(getClass().getName(), null);
+ fileA.deleteOnExit();
+ FileWriter out = new FileWriter(fileA);
+ out.write("<broker><connector><port>2342</port><ssl><port>4235</port></ssl></connector></broker>");
+ out.close();
+ ServerConfiguration conf = new ServerConfiguration(fileA);
+ conf.initialise();
+ assertEquals(4235, conf.getSSLPort());
+ }
+
+ public void testCombinedConfiguration() throws IOException, ConfigurationException
+ {
+ File mainFile = File.createTempFile(getClass().getName(), null);
+ File fileA = File.createTempFile(getClass().getName(), null);
+ File fileB = File.createTempFile(getClass().getName(), null);
+
+ mainFile.deleteOnExit();
+ fileA.deleteOnExit();
+ fileB.deleteOnExit();
+
+ FileWriter out = new FileWriter(mainFile);
+ out.write("<configuration><system/>");
+ out.write("<xml fileName=\"" + fileA.getAbsolutePath() + "\"/>");
+ out.write("<xml fileName=\"" + fileB.getAbsolutePath() + "\"/>");
+ out.write("</configuration>");
+ out.close();
+
+ out = new FileWriter(fileA);
+ out.write("<broker><connector><port>2342</port><ssl><port>4235</port></ssl></connector></broker>");
+ out.close();
+
+ out = new FileWriter(fileB);
+ out.write("<broker><connector><ssl><port>2345</port></ssl><qpidnio>true</qpidnio></connector></broker>");
+ out.close();
+
+ ServerConfiguration config = new ServerConfiguration(mainFile.getAbsoluteFile());
+ config.initialise();
+ assertEquals(4235, config.getSSLPort()); // From first file, not
+ // overriden by second
+ assertNotNull(config.getPorts());
+ assertEquals(1, config.getPorts().size());
+ assertEquals("2342", config.getPorts().get(0)); // From the first file, not
+ // present in the second
+ assertEquals(true, config.getQpidNIO()); // From the second file, not
+ // present in the first
+ }
+
+ public void testVariableInterpolation() throws Exception
+ {
+ File mainFile = File.createTempFile(getClass().getName(), null);
+
+ mainFile.deleteOnExit();
+
+ FileWriter out = new FileWriter(mainFile);
+ out.write("<broker>\n");
+ out.write("\t<work>foo</work>\n");
+ out.write("\t<management><ssl><keyStorePath>${work}</keyStorePath></ssl></management>\n");
+ out.write("</broker>\n");
+ out.close();
+
+ ServerConfiguration config = new ServerConfiguration(mainFile.getAbsoluteFile());
+ config.initialise();
+ assertEquals("Did not get correct interpolated value",
+ "foo", config.getManagementKeyStorePath());
+ }
+
+ private void writeConfigFile(File mainFile, boolean allow) throws IOException {
+ writeConfigFile(mainFile, allow, true, null, "test");
+ }
+
+ private void writeConfigFile(File mainFile, boolean allow, boolean includeVhosts, File vhostsFile, String name) throws IOException {
+ FileWriter out = new FileWriter(mainFile);
+ out.write("<broker>\n");
+ out.write("\t<management><enabled>false</enabled></management>\n");
+ out.write("\t<security>\n");
+ out.write("\t\t<principal-databases>\n");
+ out.write("\t\t\t<principal-database>\n");
+ out.write("\t\t\t\t<name>passwordfile</name>\n");
+ out.write("\t\t\t\t<class>org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase</class>\n");
+ out.write("\t\t\t\t<attributes>\n");
+ out.write("\t\t\t\t\t<attribute>\n");
+ out.write("\t\t\t\t\t\t<name>passwordFile</name>\n");
+ out.write("\t\t\t\t\t\t<value>/dev/null</value>\n");
+ out.write("\t\t\t\t\t</attribute>\n");
+ out.write("\t\t\t\t</attributes>\n");
+ out.write("\t\t\t</principal-database>\n");
+ out.write("\t\t</principal-databases>\n");
+ out.write("\t\t<jmx>\n");
+ out.write("\t\t\t<principal-database>passwordfile</principal-database>\n");
+ out.write("\t\t</jmx>\n");
+ out.write("\t\t<firewall>\n");
+ out.write("\t\t\t<rule access=\""+ ((allow) ? "allow" : "deny") +"\" network=\"127.0.0.1\"/>");
+ out.write("\t\t</firewall>\n");
+ out.write("\t</security>\n");
+ if (includeVhosts)
+ {
+ out.write("\t<virtualhosts>\n");
+ out.write("\t\t<default>test</default>\n");
+ out.write("\t\t<virtualhost>\n");
+ out.write(String.format("\t\t\t<name>%s</name>\n", name));
+ out.write(String.format("\t\t<%s> \n", name));
+ out.write("\t\t\t<exchanges>\n");
+ out.write("\t\t\t\t<exchange>\n");
+ out.write("\t\t\t\t\t<type>topic</type>\n");
+ out.write(String.format("\t\t\t\t\t<name>%s.topic</name>\n", name));
+ out.write("\t\t\t\t\t<durable>true</durable>\n");
+ out.write("\t\t\t\t</exchange>\n");
+ out.write("\t\tt</exchanges>\n");
+ out.write(String.format("\t\t</%s> \n", name));
+ out.write("\t\t</virtualhost>\n");
+ out.write("\t</virtualhosts>\n");
+ }
+ if (vhostsFile != null)
+ {
+ out.write("\t<virtualhosts>"+vhostsFile.getAbsolutePath()+"</virtualhosts>\n");
+ }
+ out.write("</broker>\n");
+ out.close();
+ }
+
+ private void writeTestFishConfigFile(File mainFile) throws IOException {
+ FileWriter out = new FileWriter(mainFile);
+ out.write("<broker>\n");
+ out.write("\t<management><enabled>false</enabled></management>\n");
+ out.write("\t<security>\n");
+ out.write("\t\t<principal-databases>\n");
+ out.write("\t\t\t<principal-database>\n");
+ out.write("\t\t\t\t<name>passwordfile</name>\n");
+ out.write("\t\t\t\t<class>org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase</class>\n");
+ out.write("\t\t\t\t<attributes>\n");
+ out.write("\t\t\t\t\t<attribute>\n");
+ out.write("\t\t\t\t\t\t<name>passwordFile</name>\n");
+ out.write("\t\t\t\t\t\t<value>/dev/null</value>\n");
+ out.write("\t\t\t\t\t</attribute>\n");
+ out.write("\t\t\t\t</attributes>\n");
+ out.write("\t\t\t</principal-database>\n");
+ out.write("\t\t</principal-databases>\n");
+ out.write("\t\t<jmx>\n");
+ out.write("\t\t\t<principal-database>passwordfile</principal-database>\n");
+ out.write("\t\t</jmx>\n");
+ out.write("\t\t<firewall>\n");
+ out.write("\t\t\t<rule access=\"allow\" network=\"127.0.0.1\"/>");
+ out.write("\t\t</firewall>\n");
+ out.write("\t</security>\n");
+ out.write("\t<virtualhosts>\n");
+ out.write("\t\t<virtualhost>\n");
+ out.write("\t\t\t<name>test</name>\n");
+ out.write("\t\t<test> \n");
+ out.write("\t\t\t<exchanges>\n");
+ out.write("\t\t\t\t<exchange>\n");
+ out.write("\t\t\t\t\t<type>topic</type>\n");
+ out.write("\t\t\t\t\t<name>test.topic</name>\n");
+ out.write("\t\t\t\t\t<durable>true</durable>\n");
+ out.write("\t\t\t\t</exchange>\n");
+ out.write("\t\tt</exchanges>\n");
+ out.write("\t\t</test> \n");
+ out.write("\t\t</virtualhost>\n");
+ out.write("\t\t<virtualhost>\n");
+ out.write("\t\t\t<name>fish</name>\n");
+ out.write("\t\t<fish> \n");
+ out.write("\t\t\t<exchanges>\n");
+ out.write("\t\t\t\t<exchange>\n");
+ out.write("\t\t\t\t\t<type>topic</type>\n");
+ out.write("\t\t\t\t\t<name>fish.topic</name>\n");
+ out.write("\t\t\t\t\t<durable>false</durable>\n");
+ out.write("\t\t\t\t</exchange>\n");
+ out.write("\t\tt</exchanges>\n");
+ out.write("\t\t</fish> \n");
+ out.write("\t\t</virtualhost>\n");
+ out.write("\t</virtualhosts>\n");
+ out.write("</broker>\n");
+ out.close();
+ }
+
+ private void writeVirtualHostsFile(File vhostsFile, String name) throws IOException {
+ FileWriter out = new FileWriter(vhostsFile);
+ out.write("<virtualhosts>\n");
+ out.write(String.format("\t\t<default>%s</default>\n", name));
+ out.write("\t<virtualhost>\n");
+ out.write(String.format("\t\t<name>%s</name>\n", name));
+ out.write(String.format("\t\t<%s>\n", name));
+ out.write("\t\t\t<exchanges>\n");
+ out.write("\t\t\t\t<exchange>\n");
+ out.write("\t\t\t\t\t<type>topic</type>\n");
+ out.write("\t\t\t\t\t<name>test.topic</name>\n");
+ out.write("\t\t\t\t\t<durable>true</durable>\n");
+ out.write("\t\t\t\t</exchange>\n");
+ out.write("\t\tt</exchanges>\n");
+ out.write(String.format("\t\t</%s>\n", name));
+ out.write("\t</virtualhost>\n");
+ out.write("</virtualhosts>\n");
+ out.close();
+ }
+
+ private void writeMultiVirtualHostsFile(File vhostsFile) throws IOException {
+ FileWriter out = new FileWriter(vhostsFile);
+ out.write("<virtualhosts>\n");
+ out.write("\t<virtualhost>\n");
+ out.write("\t\t<name>topic</name>\n");
+ out.write("\t\t<topic>\n");
+ out.write("\t\t\t<exchanges>\n");
+ out.write("\t\t\t\t<exchange>\n");
+ out.write("\t\t\t\t\t<type>topic</type>\n");
+ out.write("\t\t\t\t\t<name>test.topic</name>\n");
+ out.write("\t\t\t\t\t<durable>true</durable>\n");
+ out.write("\t\t\t\t</exchange>\n");
+ out.write("\t\tt</exchanges>\n");
+ out.write("\t\t</topic>\n");
+ out.write("\t</virtualhost>\n");
+ out.write("\t<virtualhost>\n");
+ out.write("\t\t<name>fanout</name>\n");
+ out.write("\t\t<fanout>\n");
+ out.write("\t\t\t<exchanges>\n");
+ out.write("\t\t\t\t<exchange>\n");
+ out.write("\t\t\t\t\t<type>fanout</type>\n");
+ out.write("\t\t\t\t\t<name>test.fanout</name>\n");
+ out.write("\t\t\t\t\t<durable>true</durable>\n");
+ out.write("\t\t\t\t</exchange>\n");
+ out.write("\t\tt</exchanges>\n");
+ out.write("\t\t</fanout>\n");
+ out.write("\t</virtualhost>\n");
+ out.write("</virtualhosts>\n");
+ out.close();
+ }
+
+ private void writeMultipleVhostsConfigFile(File mainFile, File[] vhostsFileArray) throws IOException {
+ FileWriter out = new FileWriter(mainFile);
+ out.write("<broker>\n");
+ out.write("\t<management><enabled>false</enabled></management>\n");
+ out.write("\t<security>\n");
+ out.write("\t\t<principal-databases>\n");
+ out.write("\t\t\t<principal-database>\n");
+ out.write("\t\t\t\t<name>passwordfile</name>\n");
+ out.write("\t\t\t\t<class>org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase</class>\n");
+ out.write("\t\t\t\t<attributes>\n");
+ out.write("\t\t\t\t\t<attribute>\n");
+ out.write("\t\t\t\t\t\t<name>passwordFile</name>\n");
+ out.write("\t\t\t\t\t\t<value>/dev/null</value>\n");
+ out.write("\t\t\t\t\t</attribute>\n");
+ out.write("\t\t\t\t</attributes>\n");
+ out.write("\t\t\t</principal-database>\n");
+ out.write("\t\t</principal-databases>\n");
+ out.write("\t\t<jmx>\n");
+ out.write("\t\t\t<principal-database>passwordfile</principal-database>\n");
+ out.write("\t\t</jmx>\n");
+ out.write("\t\t<firewall>\n");
+ out.write("\t\t\t<rule access=\"allow\" network=\"127.0.0.1\"/>");
+ out.write("\t\t</firewall>\n");
+ out.write("\t</security>\n");
+ for (File vhostsFile : vhostsFileArray)
+ {
+ out.write("\t<virtualhosts>"+vhostsFile.getAbsolutePath()+"</virtualhosts>\n");
+ }
+ out.write("</broker>\n");
+ out.close();
+ }
+
+ private void writeCombinedConfigFile(File mainFile, File fileA, File fileB) throws Exception
+ {
+ FileWriter out = new FileWriter(mainFile);
+ out.write("<configuration><system/>");
+ out.write("<xml fileName=\"" + fileA.getAbsolutePath() + "\"/>");
+ out.write("<xml fileName=\"" + fileB.getAbsolutePath() + "\"/>");
+ out.write("</configuration>");
+ out.close();
+ }
+
+ /**
+ * Test that configuration loads correctly when virtual hosts are specified in the main
+ * configuration file only.
+ * <p>
+ * Test for QPID-2361
+ */
+ public void testInternalVirtualhostConfigFile() throws Exception
+ {
+ // Write out config
+ File mainFile = File.createTempFile(getClass().getName(), "config");
+ mainFile.deleteOnExit();
+ writeConfigFile(mainFile, false, true, null, "test");
+
+ // Load config
+ ApplicationRegistry reg = new ConfigurationFileApplicationRegistry(mainFile);
+ ApplicationRegistry.initialise(reg, 1);
+
+ // Test config
+ VirtualHostRegistry virtualHostRegistry = reg.getVirtualHostRegistry();
+ String defaultVirtualHost = reg.getConfiguration().getDefaultVirtualHost();
+ VirtualHost virtualHost = virtualHostRegistry.getVirtualHost("test");
+ Exchange exchange = virtualHost.getExchangeRegistry().getExchange(new AMQShortString("test.topic"));
+
+ assertEquals("Incorrect default host", "test", defaultVirtualHost);
+ assertEquals("Incorrect virtualhost count", 1, virtualHostRegistry.getVirtualHosts().size());
+ assertEquals("Incorrect virtualhost name", "test", virtualHost.getName());
+ assertEquals("Incorrect exchange type", "topic", exchange.getType().getName().toString());
+ }
+
+ /**
+ * Test that configuration loads correctly when virtual hosts are specified in an external
+ * configuration file only.
+ * <p>
+ * Test for QPID-2361
+ */
+ public void testExternalVirtualhostXMLFile() throws Exception
+ {
+ // Write out config
+ File mainFile = File.createTempFile(getClass().getName(), "config");
+ mainFile.deleteOnExit();
+ File vhostsFile = File.createTempFile(getClass().getName(), "vhosts");
+ vhostsFile.deleteOnExit();
+ writeConfigFile(mainFile, false, false, vhostsFile, null);
+ writeVirtualHostsFile(vhostsFile, "test");
+
+ // Load config
+ ApplicationRegistry reg = new ConfigurationFileApplicationRegistry(mainFile);
+ ApplicationRegistry.initialise(reg, 1);
+
+ // Test config
+ VirtualHostRegistry virtualHostRegistry = reg.getVirtualHostRegistry();
+ String defaultVirtualHost = reg.getConfiguration().getDefaultVirtualHost();
+ VirtualHost virtualHost = virtualHostRegistry.getVirtualHost("test");
+ Exchange exchange = virtualHost.getExchangeRegistry().getExchange(new AMQShortString("test.topic"));
+
+ assertEquals("Incorrect default host", "test", defaultVirtualHost);
+ assertEquals("Incorrect virtualhost count", 1, virtualHostRegistry.getVirtualHosts().size());
+ assertEquals("Incorrect virtualhost name", "test", virtualHost.getName());
+ assertEquals("Incorrect exchange type", "topic", exchange.getType().getName().toString());
+ }
+
+ /**
+ * Test that configuration loads correctly when virtual hosts are specified in an external
+ * configuration file only, with two vhosts that have different properties.
+ * <p>
+ * Test for QPID-2361
+ */
+ public void testExternalMultiVirtualhostXMLFile() throws Exception
+ {
+ // Write out vhosts
+ File vhostsFile = File.createTempFile(getClass().getName(), "vhosts-multi");
+ vhostsFile.deleteOnExit();
+ writeMultiVirtualHostsFile(vhostsFile);
+
+ // Write out config
+ File mainFile = File.createTempFile(getClass().getName(), "config");
+ mainFile.deleteOnExit();
+ writeConfigFile(mainFile, false, false, vhostsFile, null);
+
+ // Load config
+ ApplicationRegistry reg = new ConfigurationFileApplicationRegistry(mainFile);
+ ApplicationRegistry.initialise(reg, 1);
+
+ // Test config
+ VirtualHostRegistry virtualHostRegistry = reg.getVirtualHostRegistry();
+
+ assertEquals("Incorrect virtualhost count", 2, virtualHostRegistry.getVirtualHosts().size());
+
+ // test topic host
+ VirtualHost topicVirtualHost = virtualHostRegistry.getVirtualHost("topic");
+ Exchange topicExchange = topicVirtualHost.getExchangeRegistry().getExchange(new AMQShortString("test.topic"));
+
+ assertEquals("Incorrect topic virtualhost name", "topic", topicVirtualHost.getName());
+ assertEquals("Incorrect topic exchange type", "topic", topicExchange.getType().getName().toString());
+
+ // Test fanout host
+ VirtualHost fanoutVirtualHost = virtualHostRegistry.getVirtualHost("fanout");
+ Exchange fanoutExchange = fanoutVirtualHost.getExchangeRegistry().getExchange(new AMQShortString("test.fanout"));
+
+ assertEquals("Incorrect fanout virtualhost name", "fanout", fanoutVirtualHost.getName());
+ assertEquals("Incorrect fanout exchange type", "fanout", fanoutExchange.getType().getName().toString());
+ }
+
+ /**
+ * Test that configuration does not load when virtual hosts are specified in both the main
+ * configuration file and an external file. Should throw a {@link ConfigurationException}.
+ * <p>
+ * Test for QPID-2361
+ */
+ public void testInternalAndExternalVirtualhostXMLFile() throws Exception
+ {
+ // Write out vhosts
+ File vhostsFile = File.createTempFile(getClass().getName(), "vhosts");
+ vhostsFile.deleteOnExit();
+ writeVirtualHostsFile(vhostsFile, "test");
+
+ // Write out config
+ File mainFile = File.createTempFile(getClass().getName(), "config");
+ mainFile.deleteOnExit();
+ writeConfigFile(mainFile, false, true, vhostsFile, "test");
+
+ // Load config
+ try
+ {
+ ApplicationRegistry reg = new ConfigurationFileApplicationRegistry(mainFile);
+ ApplicationRegistry.initialise(reg, 1);
+ fail("Different virtualhost XML configurations not allowed");
+ }
+ catch (ConfigurationException ce)
+ {
+ assertEquals("Incorrect error message", "Only one of external or embedded virtualhosts configuration allowed.", ce.getMessage());
+ }
+ }
+
+ /**
+ * Test that configuration does not load when virtual hosts are specified in multiple external
+ * files. Should throw a {@link ConfigurationException}.
+ * <p>
+ * Test for QPID-2361
+ */
+ public void testMultipleInternalVirtualhostXMLFile() throws Exception
+ {
+ // Write out vhosts
+ File vhostsFileOne = File.createTempFile(getClass().getName(), "vhosts-one");
+ vhostsFileOne.deleteOnExit();
+ writeVirtualHostsFile(vhostsFileOne, "one");
+ File vhostsFileTwo = File.createTempFile(getClass().getName(), "vhosts-two");
+ vhostsFileTwo.deleteOnExit();
+ writeVirtualHostsFile(vhostsFileTwo, "two");
+
+ // Write out config
+ File mainFile = File.createTempFile(getClass().getName(), "config");
+ mainFile.deleteOnExit();
+ writeMultipleVhostsConfigFile(mainFile, new File[] { vhostsFileOne, vhostsFileTwo });
+
+ // Load config
+ try
+ {
+ ApplicationRegistry reg = new ConfigurationFileApplicationRegistry(mainFile);
+ ApplicationRegistry.initialise(reg, 1);
+ fail("Multiple virtualhost XML configurations not allowed");
+ }
+ catch (ConfigurationException ce)
+ {
+ assertEquals("Incorrect error message",
+ "Only one external virtualhosts configuration file allowed, multiple filenames found.",
+ ce.getMessage());
+ }
+ }
+
+ /**
+ * Test that configuration loads correctly when virtual hosts are specified in an external
+ * configuration file in the first of two configurations and embedded in the second. This
+ * will throe a {@link ConfigurationException} since the configurations have different
+ * types.
+ * <p>
+ * Test for QPID-2361
+ */
+ public void testCombinedDifferentVirtualhostConfig() throws Exception
+ {
+ // Write out vhosts config
+ File vhostsFile = File.createTempFile(getClass().getName(), "vhosts");
+ vhostsFile.deleteOnExit();
+ writeVirtualHostsFile(vhostsFile, "external");
+
+ // Write out combined config file
+ File mainFile = File.createTempFile(getClass().getName(), "main");
+ File fileA = File.createTempFile(getClass().getName(), "a");
+ File fileB = File.createTempFile(getClass().getName(), "b");
+ mainFile.deleteOnExit();
+ fileA.deleteOnExit();
+ fileB.deleteOnExit();
+ writeCombinedConfigFile(mainFile, fileA, fileB);
+ writeConfigFile(fileA, false, false, vhostsFile, null);
+ writeConfigFile(fileB, false);
+
+ // Load config
+ try
+ {
+ ServerConfiguration config = new ServerConfiguration(mainFile.getAbsoluteFile());
+ config.initialise();
+ fail("Different virtualhost XML configurations not allowed");
+ }
+ catch (ConfigurationException ce)
+ {
+ assertEquals("Incorrect error message", "Only one of external or embedded virtualhosts configuration allowed.", ce.getMessage());
+ }
+ }
+
+ /**
+ * Test that configuration loads correctly when virtual hosts are specified two overriding configurations
+ * each with an embedded virtualhost section. The first configuration section should be used.
+ * <p>
+ * Test for QPID-2361
+ */
+ public void testCombinedConfigEmbeddedVirtualhost() throws Exception
+ {
+ // Write out combined config file
+ File mainFile = File.createTempFile(getClass().getName(), "main");
+ File fileA = File.createTempFile(getClass().getName(), "a");
+ File fileB = File.createTempFile(getClass().getName(), "b");
+ mainFile.deleteOnExit();
+ fileA.deleteOnExit();
+ fileB.deleteOnExit();
+ writeCombinedConfigFile(mainFile, fileA, fileB);
+ writeConfigFile(fileA, false, true, null, "a");
+ writeConfigFile(fileB, false, true, null, "b");
+
+ // Load config
+ ServerConfiguration config = new ServerConfiguration(mainFile.getAbsoluteFile());
+ config.initialise();
+
+ // Test config
+ VirtualHostConfiguration virtualHost = config.getVirtualHostConfig("a");
+
+ assertEquals("Incorrect virtualhost count", 1, config.getVirtualHosts().length);
+ assertEquals("Incorrect virtualhost name", "a", virtualHost.getName());
+ }
+
+ /**
+ * Test that configuration loads correctly when virtual hosts are specified two overriding configurations
+ * each with an external virtualhost XML file. The first configuration file should be used.
+ * <p>
+ * Test for QPID-2361
+ */
+ public void testCombinedConfigExternalVirtualhost() throws Exception
+ {
+ // Write out vhosts config
+ File vhostsOne = File.createTempFile(getClass().getName(), "vhosts-one");
+ vhostsOne.deleteOnExit();
+ writeVirtualHostsFile(vhostsOne, "one");
+ File vhostsTwo = File.createTempFile(getClass().getName(), "vhosts-two");
+ vhostsTwo.deleteOnExit();
+ writeVirtualHostsFile(vhostsTwo, "two");
+
+ // Write out combined config file
+ File mainFile = File.createTempFile(getClass().getName(), "main");
+ File fileA = File.createTempFile(getClass().getName(), "a");
+ File fileB = File.createTempFile(getClass().getName(), "b");
+ mainFile.deleteOnExit();
+ fileA.deleteOnExit();
+ fileB.deleteOnExit();
+ writeCombinedConfigFile(mainFile, fileA, fileB);
+ writeConfigFile(fileA, false, false, vhostsOne, null);
+ writeConfigFile(fileB, false, false, vhostsTwo, null);
+
+ // Load config
+ ServerConfiguration config = new ServerConfiguration(mainFile.getAbsoluteFile());
+ config.initialise();
+
+ // Test config
+ VirtualHostConfiguration virtualHost = config.getVirtualHostConfig("one");
+
+ assertEquals("Incorrect virtualhost count", 1, config.getVirtualHosts().length);
+ assertEquals("Incorrect virtualhost name", "one", virtualHost.getName());
+ }
+
+ /**
+ * Test that configuration loads correctly when an overriding virtualhost configuration resets
+ * a property of an embedded virtualhost section. The overriding configuration property value
+ * should be used.
+ * <p>
+ * Test for QPID-2361
+ */
+ public void testCombinedConfigEmbeddedVirtualhostOverride() throws Exception
+ {
+ // Write out combined config file
+ File mainFile = File.createTempFile(getClass().getName(), "main");
+ File fileA = File.createTempFile(getClass().getName(), "override");
+ File fileB = File.createTempFile(getClass().getName(), "config");
+ mainFile.deleteOnExit();
+ fileA.deleteOnExit();
+ fileB.deleteOnExit();
+ writeCombinedConfigFile(mainFile, fileA, fileB);
+ writeTestFishConfigFile(fileB);
+
+ // Write out overriding virtualhosts section
+ FileWriter out = new FileWriter(fileA);
+ out.write("<broker>\n");
+ out.write("<virtualhosts>\n");
+ out.write("\t<virtualhost>\n");
+ out.write("\t\t<test>\n");
+ out.write("\t\t\t<exchanges>\n");
+ out.write("\t\t\t\t<exchange>\n");
+ out.write("\t\t\t\t\t<durable>false</durable>\n");
+ out.write("\t\t\t\t</exchange>\n");
+ out.write("\t\tt</exchanges>\n");
+ out.write("\t\t</test>\n");
+ out.write("\t\t<fish>\n");
+ out.write("\t\t\t<exchanges>\n");
+ out.write("\t\t\t\t<exchange>\n");
+ out.write("\t\t\t\t\t<durable>true</durable>\n");
+ out.write("\t\t\t\t</exchange>\n");
+ out.write("\t\tt</exchanges>\n");
+ out.write("\t\t</fish>\n");
+ out.write("\t</virtualhost>\n");
+ out.write("</virtualhosts>\n");
+ out.write("</broker>\n");
+ out.close();
+
+ // Load config
+ ServerConfiguration config = new ServerConfiguration(mainFile.getAbsoluteFile());
+ config.initialise();
+
+ // Test config
+ VirtualHostConfiguration testHost = config.getVirtualHostConfig("test");
+ ExchangeConfiguration testExchange = testHost.getExchangeConfiguration("test.topic");
+ VirtualHostConfiguration fishHost = config.getVirtualHostConfig("fish");
+ ExchangeConfiguration fishExchange = fishHost.getExchangeConfiguration("fish.topic");
+
+ assertEquals("Incorrect virtualhost count", 2, config.getVirtualHosts().length);
+ assertEquals("Incorrect virtualhost name", "test", testHost.getName());
+ assertFalse("Incorrect exchange durable property", testExchange.getDurable());
+ assertEquals("Incorrect virtualhost name", "fish", fishHost.getName());
+ assertTrue("Incorrect exchange durable property", fishExchange.getDurable());
+ }
+
+ /**
+ * Test that configuration loads correctly when the virtualhost configuration is a set of overriding
+ * configuration files that resets a property of a virtualhost. The opmost overriding configuration
+ * property value should be used.
+ * <p>
+ * Test for QPID-2361
+ */
+ public void testCombinedVirtualhostOverride() throws Exception
+ {
+ // Write out combined config file
+ File mainFile = File.createTempFile(getClass().getName(), "main");
+ File vhostsFile = File.createTempFile(getClass().getName(), "vhosts");
+ File fileA = File.createTempFile(getClass().getName(), "vhosts-override");
+ File fileB = File.createTempFile(getClass().getName(), "vhosts-base");
+ mainFile.deleteOnExit();
+ vhostsFile.deleteOnExit();
+ fileA.deleteOnExit();
+ fileB.deleteOnExit();
+ writeConfigFile(mainFile, true, false, vhostsFile, null);
+ writeCombinedConfigFile(vhostsFile, fileA, fileB);
+
+ // Write out overriding virtualhosts sections
+ FileWriter out = new FileWriter(fileA);
+ out.write("<virtualhosts>\n");
+ out.write("\t<virtualhost>\n");
+ out.write("\t\t<test>\n");
+ out.write("\t\t\t<exchanges>\n");
+ out.write("\t\t\t\t<exchange>\n");
+ out.write("\t\t\t\t\t<durable>false</durable>\n");
+ out.write("\t\t\t\t</exchange>\n");
+ out.write("\t\tt</exchanges>\n");
+ out.write("\t\t</test>\n");
+ out.write("\t</virtualhost>\n");
+ out.write("</virtualhosts>\n");
+ out.close();
+ writeVirtualHostsFile(fileB, "test");
+
+ // Load config
+ ServerConfiguration config = new ServerConfiguration(mainFile.getAbsoluteFile());
+ config.initialise();
+
+ // Test config
+ VirtualHostConfiguration testHost = config.getVirtualHostConfig("test");
+ ExchangeConfiguration testExchange = testHost.getExchangeConfiguration("test.topic");
+
+ assertEquals("Incorrect virtualhost count", 1, config.getVirtualHosts().length);
+ assertEquals("Incorrect virtualhost name", "test", testHost.getName());
+ assertFalse("Incorrect exchange durable property", testExchange.getDurable());
+ }
+
+ /**
+ * Test that configuration loads correctly when the virtualhost configuration is a set of overriding
+ * configuration files that define multiple virtualhosts, one per file. Only the virtualhosts defined in
+ * the topmost file should be used.
+ * <p>
+ * Test for QPID-2361
+ */
+ public void testCombinedMultipleVirtualhosts() throws Exception
+ {
+ // Write out combined config file
+ File mainFile = File.createTempFile(getClass().getName(), "main");
+ File vhostsFile = File.createTempFile(getClass().getName(), "vhosts");
+ File fileA = File.createTempFile(getClass().getName(), "vhosts-one");
+ File fileB = File.createTempFile(getClass().getName(), "vhosts-two");
+ mainFile.deleteOnExit();
+ vhostsFile.deleteOnExit();
+ fileA.deleteOnExit();
+ fileB.deleteOnExit();
+ writeConfigFile(mainFile, true, false, vhostsFile, null);
+ writeCombinedConfigFile(vhostsFile, fileA, fileB);
+
+ // Write both virtualhosts definitions
+ writeVirtualHostsFile(fileA, "test-one");
+ writeVirtualHostsFile(fileB, "test-two");
+
+ // Load config
+ ServerConfiguration config = new ServerConfiguration(mainFile.getAbsoluteFile());
+ config.initialise();
+
+ // Test config
+ VirtualHostConfiguration oneHost = config.getVirtualHostConfig("test-one");
+
+ assertEquals("Incorrect virtualhost count", 1, config.getVirtualHosts().length);
+ assertEquals("Incorrect virtualhost name", "test-one", oneHost.getName());
+ }
+
+ /**
+ * Test that a non-existant virtualhost file throws a {@link ConfigurationException}.
+ * <p>
+ * Test for QPID-2624
+ */
+ public void testNonExistantVirtualhosts() throws Exception
+ {
+ // Write out combined config file
+ File mainFile = File.createTempFile(getClass().getName(), "main");
+ File vhostsFile = new File("doesnotexist");
+ mainFile.deleteOnExit();
+ writeConfigFile(mainFile, true, false, vhostsFile, null);
+
+ // Load config
+ try
+ {
+ ServerConfiguration config = new ServerConfiguration(mainFile.getAbsoluteFile());
+ config.initialise();
+ }
+ catch (ConfigurationException ce)
+ {
+ assertEquals("Virtualhosts file does not exist", ce.getMessage());
+ }
+ catch (Exception e)
+ {
+ fail("Should throw a ConfigurationException");
+ }
+ }
+
+ /*
+ * Tests that the old element security.jmx.access (that used to be used
+ * to define JMX access rights) is rejected.
+ */
+ public void testManagementAccessRejected() throws ConfigurationException
+ {
+ // Check default
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ serverConfig.initialise();
+
+ // Check value we set
+ _config.setProperty("security.jmx.access(0)", "jmxremote.access");
+ serverConfig = new ServerConfiguration(_config);
+
+ try
+ {
+ serverConfig.initialise();
+ fail("Exception not thrown");
+ }
+ catch (ConfigurationException ce)
+ {
+ assertEquals("Incorrect error message",
+ "Validation error : security/jmx/access is no longer a supported element within the configuration xml.",
+ ce.getMessage());
+ }
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/TopicConfigurationTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/TopicConfigurationTest.java
new file mode 100644
index 0000000000..7fc3b2d06a
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/TopicConfigurationTest.java
@@ -0,0 +1,130 @@
+/*
+ *
+ * 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;
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.AMQInternalException;
+import org.apache.qpid.AMQSecurityException;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.AMQQueueFactory;
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+
+/**
+ * Test of the new Topic configuration processing
+ */
+public class TopicConfigurationTest extends InternalBrokerBaseCase
+{
+
+ @Override
+ public void configure()
+ {
+ getConfigXml().addProperty("virtualhosts.virtualhost.test.topics.topic.name", "stocks.nyse.appl");
+
+ getConfigXml().addProperty("virtualhosts.virtualhost.test.topics.topic(1).subscriptionName", getName()+":stockSubscription");
+
+ getConfigXml().addProperty("virtualhosts.virtualhost.test.topics.topic(2).name", "stocks.nyse.orcl");
+ getConfigXml().addProperty("virtualhosts.virtualhost.test.topics.topic(2).subscriptionName", getName()+":stockSubscription");
+ }
+
+ /**
+ * Test that a TopicConfig object is created and attached to the queue when it is bound to the topic exchange.
+ *
+
+ * @throws ConfigurationException
+ * @throws AMQSecurityException
+ */
+ public void testTopicCreation() throws ConfigurationException, AMQSecurityException, AMQInternalException
+ {
+ Exchange topicExchange = getVirtualHost().getExchangeRegistry().getExchange(ExchangeDefaults.TOPIC_EXCHANGE_NAME);
+ getVirtualHost().getBindingFactory().addBinding("stocks.nyse.appl", getQueue(), topicExchange, null);
+
+ TopicConfig config = getQueue().getConfiguration().getConfiguration(TopicConfig.class.getName());
+
+ assertNotNull("Queue should have topic configuration bound to it.", config);
+ assertEquals("Configuration name not correct", "stocks.nyse.appl", config.getName());
+ }
+
+ /**
+ * Test that a queue created for a subscription correctly has topic
+ * configuration selected based on the subscription and topic name.
+ *
+ * @throws ConfigurationException
+ * @throws AMQException
+ */
+ public void testSubscriptionWithTopicCreation() throws ConfigurationException, AMQException
+ {
+
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString(getName()+":stockSubscription"), false, new AMQShortString("testowner"),
+ false, false, getVirtualHost(), null);
+
+ getVirtualHost().getQueueRegistry().registerQueue(queue);
+ Exchange defaultExchange = getVirtualHost().getExchangeRegistry().getDefaultExchange();
+ getVirtualHost().getBindingFactory().addBinding(getName(), queue, defaultExchange, null);
+
+
+ Exchange topicExchange = getVirtualHost().getExchangeRegistry().getExchange(ExchangeDefaults.TOPIC_EXCHANGE_NAME);
+ getVirtualHost().getBindingFactory().addBinding("stocks.nyse.orcl", queue, topicExchange, null);
+
+ TopicConfig config = queue.getConfiguration().getConfiguration(TopicConfig.class.getName());
+
+ assertNotNull("Queue should have topic configuration bound to it.", config);
+ assertEquals("Configuration subscription name not correct", getName() + ":stockSubscription", config.getSubscriptionName());
+ assertEquals("Configuration name not correct", "stocks.nyse.orcl", config.getName());
+
+ }
+
+ /**
+ * Test that a queue created for a subscription correctly has topic
+ * configuration attached here this should be the generic topic section
+ * with just the subscriptionName
+ *
+ * @throws ConfigurationException
+ * @throws AMQException
+ */
+ public void testSubscriptionCreation() throws ConfigurationException, AMQException
+ {
+
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString(getName()+":stockSubscription"), false, new AMQShortString("testowner"),
+ false, false, getVirtualHost(), null);
+
+ getVirtualHost().getQueueRegistry().registerQueue(queue);
+ Exchange defaultExchange = getVirtualHost().getExchangeRegistry().getDefaultExchange();
+ getVirtualHost().getBindingFactory().addBinding(getName(), queue, defaultExchange, null);
+
+
+ Exchange topicExchange = getVirtualHost().getExchangeRegistry().getExchange(ExchangeDefaults.TOPIC_EXCHANGE_NAME);
+ getVirtualHost().getBindingFactory().addBinding("stocks.nyse.ibm", queue, topicExchange, null);
+
+ TopicConfig config = queue.getConfiguration().getConfiguration(TopicConfig.class.getName());
+
+ assertNotNull("Queue should have topic configuration bound to it.", config);
+ assertEquals("Configuration subscription name not correct", getName() + ":stockSubscription", config.getSubscriptionName());
+ assertEquals("Configuration name not correct", "#", config.getName());
+
+ }
+
+
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/VirtualHostConfigurationTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/VirtualHostConfigurationTest.java
new file mode 100644
index 0000000000..593119041d
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/VirtualHostConfigurationTest.java
@@ -0,0 +1,233 @@
+/*
+ * 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;
+
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.XMLConfiguration;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.queue.AMQPriorityQueue;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.store.TestableMemoryMessageStore;
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+public class VirtualHostConfigurationTest extends InternalBrokerBaseCase
+{
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ // Set the default configuration items
+ getConfigXml().clear();
+ getConfigXml().addProperty("virtualhosts.virtualhost(-1).name", "test");
+ getConfigXml().addProperty("virtualhosts.virtualhost(-1).test.store.class", TestableMemoryMessageStore.class.getName());
+
+ getConfigXml().addProperty("virtualhosts.virtualhost.name", getName());
+ getConfigXml().addProperty("virtualhosts.virtualhost."+getName()+".store.class", TestableMemoryMessageStore.class.getName());
+ }
+
+ @Override
+ public void createBroker()
+ {
+ // Prevent auto broker startup
+ }
+
+ public void testQueuePriority() throws Exception
+ {
+ // Set up queue with 5 priorities
+ getConfigXml().addProperty("virtualhosts.virtualhost.testQueuePriority.queues(-1).queue(-1).name(-1)",
+ "atest");
+ getConfigXml().addProperty("virtualhosts.virtualhost.testQueuePriority.queues.queue.atest(-1).exchange",
+ "amq.direct");
+ getConfigXml().addProperty("virtualhosts.virtualhost.testQueuePriority.queues.queue.atest.priorities",
+ "5");
+
+ // Set up queue with JMS style priorities
+ getConfigXml().addProperty("virtualhosts.virtualhost.testQueuePriority.queues(-1).queue(-1).name(-1)",
+ "ptest");
+ getConfigXml().addProperty("virtualhosts.virtualhost.testQueuePriority.queues.queue.ptest(-1).exchange",
+ "amq.direct");
+ getConfigXml().addProperty("virtualhosts.virtualhost.testQueuePriority.queues.queue.ptest.priority",
+ "true");
+
+ // Set up queue with no priorities
+ getConfigXml().addProperty("virtualhosts.virtualhost.testQueuePriority.queues(-1).queue(-1).name(-1)",
+ "ntest");
+ getConfigXml().addProperty("virtualhosts.virtualhost.testQueuePriority.queues.queue.ntest(-1).exchange",
+ "amq.direct");
+ getConfigXml().addProperty("virtualhosts.virtualhost.testQueuePriority.queues.queue.ntest.priority",
+ "false");
+
+ // Start the broker now.
+ super.createBroker();
+
+ VirtualHost vhost =
+ ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost(getName());
+
+ // Check that atest was a priority queue with 5 priorities
+ AMQQueue atest = vhost.getQueueRegistry().getQueue(new AMQShortString("atest"));
+ assertTrue(atest instanceof AMQPriorityQueue);
+ assertEquals(5, ((AMQPriorityQueue) atest).getPriorities());
+
+ // Check that ptest was a priority queue with 10 priorities
+ AMQQueue ptest = vhost.getQueueRegistry().getQueue(new AMQShortString("ptest"));
+ assertTrue(ptest instanceof AMQPriorityQueue);
+ assertEquals(10, ((AMQPriorityQueue) ptest).getPriorities());
+
+ // Check that ntest wasn't a priority queue
+ AMQQueue ntest = vhost.getQueueRegistry().getQueue(new AMQShortString("ntest"));
+ assertFalse(ntest instanceof AMQPriorityQueue);
+ }
+
+ public void testQueueAlerts() throws Exception
+ {
+ // Set up queue with 5 priorities
+ getConfigXml().addProperty("virtualhosts.virtualhost.testQueueAlerts.queues.exchange", "amq.topic");
+ getConfigXml().addProperty("virtualhosts.virtualhost.testQueueAlerts.queues.maximumQueueDepth", "1");
+ getConfigXml().addProperty("virtualhosts.virtualhost.testQueueAlerts.queues.maximumMessageSize", "2");
+ getConfigXml().addProperty("virtualhosts.virtualhost.testQueueAlerts.queues.maximumMessageAge", "3");
+
+ getConfigXml().addProperty("virtualhosts.virtualhost.testQueueAlerts.queues(-1).queue(1).name(1)", "atest");
+ getConfigXml().addProperty("virtualhosts.virtualhost.testQueueAlerts.queues.queue.atest(-1).exchange", "amq.direct");
+ getConfigXml().addProperty("virtualhosts.virtualhost.testQueueAlerts.queues.queue.atest(-1).maximumQueueDepth", "4");
+ getConfigXml().addProperty("virtualhosts.virtualhost.testQueueAlerts.queues.queue.atest(-1).maximumMessageSize", "5");
+ getConfigXml().addProperty("virtualhosts.virtualhost.testQueueAlerts.queues.queue.atest(-1).maximumMessageAge", "6");
+
+ getConfigXml().addProperty("virtualhosts.virtualhost.testQueueAlerts.queues(-1).queue(-1).name(-1)", "btest");
+
+ // Start the broker now.
+ super.createBroker();
+
+ VirtualHost vhost =
+ ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost(getName());
+
+ // Check specifically configured values
+ AMQQueue aTest = vhost.getQueueRegistry().getQueue(new AMQShortString("atest"));
+ assertEquals(4, aTest.getMaximumQueueDepth());
+ assertEquals(5, aTest.getMaximumMessageSize());
+ assertEquals(6, aTest.getMaximumMessageAge());
+
+ // Check default values
+ AMQQueue bTest = vhost.getQueueRegistry().getQueue(new AMQShortString("btest"));
+ assertEquals(1, bTest.getMaximumQueueDepth());
+ assertEquals(2, bTest.getMaximumMessageSize());
+ assertEquals(3, bTest.getMaximumMessageAge());
+ }
+
+ /**
+ * Test that the house keeping pool sizes is correctly processed
+ *
+ * @throws Exception
+ */
+ public void testHouseKeepingThreadCount() throws Exception
+ {
+ int initialPoolSize = 10;
+
+ getConfigXml().addProperty("virtualhosts.virtualhost.testHouseKeepingThreadCount.housekeeping.poolSize",
+ initialPoolSize);
+
+ // Start the broker now.
+ super.createBroker();
+
+ VirtualHost vhost =
+ ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost(getName());
+
+ assertEquals("HouseKeeping PoolSize not set correctly.",
+ initialPoolSize, vhost.getHouseKeepingPoolSize());
+ }
+
+ /**
+ * Test default house keeping tasks
+ *
+ * @throws Exception
+ */
+ public void testDefaultHouseKeepingTasks() throws Exception
+ {
+ // Start the broker now.
+ super.createBroker();
+
+ VirtualHost vhost =
+ ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost(getName());
+
+ assertEquals("Default houseKeeping task count incorrect.", 2,
+ vhost.getHouseKeepingTaskCount());
+
+ // Currently the two are tasks:
+ // ExpiredMessageTask from VirtualHost
+ // UpdateTask from the QMF ManagementExchange
+ }
+
+ /**
+ * Test that we can dynamically change the thread pool size
+ *
+ * @throws Exception
+ */
+ public void testDynamicHouseKeepingPoolSizeChange() throws Exception
+ {
+ int initialPoolSize = 10;
+
+ getConfigXml().addProperty("virtualhosts.virtualhost.testDynamicHouseKeepingPoolSizeChange.housekeeping.poolSize",
+ initialPoolSize);
+
+ // Start the broker now.
+ super.createBroker();
+
+ VirtualHost vhost =
+ ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost(getName());
+
+ assertEquals("HouseKeeping PoolSize not set correctly.",
+ initialPoolSize, vhost.getHouseKeepingPoolSize());
+
+ vhost.setHouseKeepingPoolSize(1);
+
+ assertEquals("HouseKeeping PoolSize not correctly change.",
+ 1, vhost.getHouseKeepingPoolSize());
+
+ }
+
+ /**
+ * Tests that the old element security.authentication.name is rejected. This element
+ * was never supported properly as authentication is performed before the virtual host
+ * is considered.
+ */
+ public void testSecurityAuthenticationNameRejected() throws Exception
+ {
+ getConfigXml().addProperty("virtualhosts.virtualhost.testSecurityAuthenticationNameRejected.security.authentication.name",
+ "testdb");
+
+ try
+ {
+ super.createBroker();
+ fail("Exception not thrown");
+ }
+ catch(ConfigurationException ce)
+ {
+ assertEquals("Incorrect error message",
+ "Validation error : security/authentication/name is no longer a supported element within the configuration xml." +
+ " It appears in virtual host definition : " + getName(),
+ ce.getMessage());
+ }
+ }
+
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/plugins/ConfigurationPluginTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/plugins/ConfigurationPluginTest.java
new file mode 100644
index 0000000000..ee2f77f16b
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/plugins/ConfigurationPluginTest.java
@@ -0,0 +1,210 @@
+/*
+ *
+ * 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.plugins;
+
+import junit.framework.TestCase;
+import org.apache.commons.configuration.CompositeConfiguration;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.XMLConfiguration;
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+
+import java.util.List;
+
+/**
+ * Test that verifies that given a Configuration a ConfigurationPlugin can
+ * process and validate that data.
+ */
+public class ConfigurationPluginTest extends InternalBrokerBaseCase
+{
+ private static final double DOUBLE = 3.14;
+ private static final long POSITIVE_LONG = 1000;
+ private static final long NEGATIVE_LONG = -1000;
+ private static final int LIST_SIZE = 3;
+
+ class ConfigPlugin extends ConfigurationPlugin
+ {
+ @Override
+ public String[] getElementsProcessed()
+ {
+ return new String[]{"[@property]", "name",
+ "positiveLong", "negativeLong",
+ "true", "list", "double"};
+ }
+
+ @Override
+ public void validateConfiguration() throws ConfigurationException
+ {
+ // no validation requried
+ }
+
+ public String getName()
+ {
+ return getStringValue("name");
+ }
+
+ public String getProperty()
+ {
+ return getStringValue("[@property]");
+ }
+
+
+ }
+
+ ConfigPlugin _plugin;
+
+ @Override
+ public void setUp() throws Exception
+ {
+ // Test does not directly use the AppRegistry but the configured broker
+ // is required for the correct ConfigurationPlugin processing
+ super.setUp();
+ XMLConfiguration xmlconfig = new XMLConfiguration();
+ xmlconfig.addProperty("base.element[@property]", "property");
+ xmlconfig.addProperty("base.element.name", "name");
+ // We make these strings as that is how they will be read from the file.
+ xmlconfig.addProperty("base.element.positiveLong", String.valueOf(POSITIVE_LONG));
+ xmlconfig.addProperty("base.element.negativeLong", String.valueOf(NEGATIVE_LONG));
+ xmlconfig.addProperty("base.element.boolean", String.valueOf(true));
+ xmlconfig.addProperty("base.element.double", String.valueOf(DOUBLE));
+ for (int i = 0; i < LIST_SIZE; i++)
+ {
+ xmlconfig.addProperty("base.element.list", i);
+ }
+
+ //Use a composite configuration as this is what our broker code uses.
+ CompositeConfiguration composite = new CompositeConfiguration();
+ composite.addConfiguration(xmlconfig);
+
+ _plugin = new ConfigPlugin();
+
+ try
+ {
+ _plugin.setConfiguration("base.element", composite.subset("base.element"));
+ }
+ catch (ConfigurationException e)
+ {
+ e.printStackTrace();
+ fail(e.toString());
+ }
+
+ }
+
+ public void testHasConfiguration()
+ {
+ assertTrue("Plugin has no configuration ", _plugin.hasConfiguration());
+ _plugin = new ConfigPlugin();
+ assertFalse("Plugins has configuration", _plugin.hasConfiguration());
+ }
+
+ public void testValuesRetreived()
+ {
+ assertEquals("Name not correct", "name", _plugin.getName());
+ assertEquals("Property not correct", "property", _plugin.getProperty());
+ }
+
+ public void testContainsPositiveLong()
+ {
+ assertTrue("positiveLong is not positive", _plugin.containsPositiveLong("positiveLong"));
+ assertFalse("NonExistentValue was found", _plugin.containsPositiveLong("NonExistentValue"));
+
+ try
+ {
+ _plugin.validatePositiveLong("positiveLong");
+ }
+ catch (ConfigurationException e)
+ {
+ fail(e.getMessage());
+ }
+
+ try
+ {
+ _plugin.validatePositiveLong("negativeLong");
+ fail("negativeLong should not be positive");
+ }
+ catch (ConfigurationException e)
+ {
+ assertEquals("negativeLong should not be reported as positive",
+ "ConfigPlugin: unable to configure invalid negativeLong:" + NEGATIVE_LONG, e.getMessage());
+ }
+
+ }
+
+ public void testDouble()
+ {
+ assertEquals("Double value not returned", DOUBLE, _plugin.getDoubleValue("double"));
+ assertEquals("default Double value not returned", 0.0, _plugin.getDoubleValue("NonExistent"));
+ assertEquals("set default Double value not returned", DOUBLE, _plugin.getDoubleValue("NonExistent", DOUBLE));
+ }
+
+ public void testLong()
+ {
+ assertTrue("Long value not returned", _plugin.containsLong("positiveLong"));
+ assertFalse("Long value returned", _plugin.containsLong("NonExistent"));
+ assertEquals("Long value not returned", POSITIVE_LONG, _plugin.getLongValue("positiveLong"));
+ assertEquals("default Long value not returned", 0, _plugin.getLongValue("NonExistent"));
+ assertEquals("set default Long value not returned", NEGATIVE_LONG, _plugin.getLongValue("NonExistent", NEGATIVE_LONG));
+ }
+
+ public void testInt()
+ {
+ assertTrue("Int value not returned", _plugin.containsInt("positiveLong"));
+ assertFalse("Int value returned", _plugin.containsInt("NonExistent"));
+ assertEquals("Int value not returned", (int) POSITIVE_LONG, _plugin.getIntValue("positiveLong"));
+ assertEquals("default Int value not returned", 0, _plugin.getIntValue("NonExistent"));
+ assertEquals("set default Int value not returned", (int) NEGATIVE_LONG, _plugin.getIntValue("NonExistent", (int) NEGATIVE_LONG));
+ }
+
+ public void testString()
+ {
+ assertEquals("String value not returned", "name", _plugin.getStringValue("name"));
+ assertNull("Null default String value not returned", _plugin.getStringValue("NonExistent", null));
+ assertNull("default String value not returned", _plugin.getStringValue("NonExistent"));
+ assertEquals("default String value not returned", "Default", _plugin.getStringValue("NonExistent", "Default"));
+ }
+
+ public void testBoolean()
+ {
+ assertTrue("Boolean value not returned", _plugin.containsBoolean("boolean"));
+ assertFalse("Boolean value not returned", _plugin.containsBoolean("NonExistent"));
+ assertTrue("Boolean value not returned", _plugin.getBooleanValue("boolean"));
+ assertFalse("default String value not returned", _plugin.getBooleanValue("NonExistent"));
+ assertTrue("set default String value not returned", _plugin.getBooleanValue("NonExistent", true));
+ }
+
+ public void testList()
+ {
+ assertTrue("list not found in plugin", _plugin.contains("list"));
+ List list = _plugin.getListValue("list");
+ assertNotNull("Returned list should not be null", list);
+ assertEquals("List should not be empty", LIST_SIZE, list.size());
+
+ list = _plugin.getListValue("NonExistent");
+ assertNotNull("Returned list should not be null", list);
+ assertEquals("List is not empty", 0, list.size());
+ }
+
+ public void testContains()
+ {
+ assertTrue("list not found in plugin", _plugin.contains("list"));
+ assertFalse("NonExistent found in plugin", _plugin.contains("NonExistent"));
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java
new file mode 100644
index 0000000000..9e831b2a8e
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java
@@ -0,0 +1,625 @@
+/*
+ *
+ * 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.exchange;
+
+import junit.framework.TestCase;
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.framing.ContentBody;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.framing.FieldTableFactory;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.server.binding.Binding;
+import org.apache.qpid.server.binding.BindingFactory;
+import org.apache.qpid.server.message.AMQMessage;
+import org.apache.qpid.server.message.AMQMessageHeader;
+import org.apache.qpid.server.message.MessageMetaData;
+import org.apache.qpid.server.message.ServerMessage;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.BaseQueue;
+import org.apache.qpid.server.queue.IncomingMessage;
+import org.apache.qpid.server.queue.MockStoredMessage;
+import org.apache.qpid.server.queue.QueueEntry;
+import org.apache.qpid.server.queue.SimpleAMQQueue;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.store.DurableConfigurationStore;
+import org.apache.qpid.server.store.MemoryMessageStore;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.store.StoredMessage;
+import org.apache.qpid.server.subscription.Subscription;
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+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.atomic.AtomicLong;
+
+public class AbstractHeadersExchangeTestBase extends InternalBrokerBaseCase
+{
+ private static final Logger _log = Logger.getLogger(AbstractHeadersExchangeTestBase.class);
+
+ private final HeadersExchange exchange = new HeadersExchange();
+ protected final Set<TestQueue> queues = new HashSet<TestQueue>();
+
+
+
+ /**
+ * Not used in this test, just there to stub out the routing calls
+ */
+ private MessageStore _store = new MemoryMessageStore();
+
+
+ BindingFactory bindingFactory = new BindingFactory(new DurableConfigurationStore.Source()
+ {
+
+ public DurableConfigurationStore getDurableConfigurationStore()
+ {
+ return _store;
+ }
+ },
+ exchange);
+
+ private int count;
+
+ public void testDoNothing()
+ {
+ // this is here only to make junit under Eclipse happy
+ }
+
+ protected TestQueue bindDefault(String... bindings) throws AMQException
+ {
+ String queueName = "Queue" + (++count);
+
+ return bind(queueName, queueName, getHeadersMap(bindings));
+ }
+
+ protected void unbind(TestQueue queue, String... bindings) throws AMQException
+ {
+ String queueName = queue.getName();
+ //TODO - check this
+ exchange.onUnbind(new Binding(null,queueName, queue, exchange, getHeadersMap(bindings)));
+ }
+
+ protected int getCount()
+ {
+ return count;
+ }
+
+ private TestQueue bind(String key, String queueName, Map<String,Object> args) throws AMQException
+ {
+ TestQueue queue = new TestQueue(new AMQShortString(queueName));
+ queues.add(queue);
+ exchange.onBind(new Binding(null,key, queue, exchange, args));
+ return queue;
+ }
+
+
+ protected int route(Message m) throws AMQException
+ {
+ m.getIncomingMessage().headersReceived();
+ m.route(exchange);
+ if(m.getIncomingMessage().allContentReceived())
+ {
+ for(BaseQueue q : m.getIncomingMessage().getDestinationQueues())
+ {
+ q.enqueue(m);
+ }
+ }
+ return m.getIncomingMessage().getDestinationQueues().size();
+ }
+
+ protected void routeAndTest(Message m, TestQueue... expected) throws AMQException
+ {
+ routeAndTest(m, false, Arrays.asList(expected));
+ }
+
+ protected void routeAndTest(Message m, boolean expectReturn, TestQueue... expected) throws AMQException
+ {
+ routeAndTest(m, expectReturn, Arrays.asList(expected));
+ }
+
+ protected void routeAndTest(Message m, List<TestQueue> expected) throws AMQException
+ {
+ routeAndTest(m, false, expected);
+ }
+
+ protected void routeAndTest(Message m, boolean expectReturn, List<TestQueue> expected) throws AMQException
+ {
+ int queueCount = route(m);
+
+ for (TestQueue q : queues)
+ {
+ if (expected.contains(q))
+ {
+ assertTrue("Expected " + m + " to be delivered to " + q, q.isInQueue(m));
+ //assert m.isInQueue(q) : "Expected " + m + " to be delivered to " + q;
+ }
+ else
+ {
+ assertFalse("Did not expect " + m + " to be delivered to " + q, q.isInQueue(m));
+ //assert !m.isInQueue(q) : "Did not expect " + m + " to be delivered to " + q;
+ }
+ }
+
+ if(expectReturn)
+ {
+ assertEquals("Expected "+m+" to be returned due to manadatory flag, and lack of routing",0, queueCount);
+ }
+
+ }
+
+ static Map<String,Object> getHeadersMap(String... entries)
+ {
+ if(entries == null)
+ {
+ return null;
+ }
+
+ Map<String,Object> headers = new HashMap<String,Object>();
+
+ for (String s : entries)
+ {
+ String[] parts = s.split("=", 2);
+ headers.put(parts[0], parts.length > 1 ? parts[1] : "");
+ }
+ return headers;
+ }
+
+ static FieldTable getHeaders(String... entries)
+ {
+ FieldTable headers = FieldTableFactory.newFieldTable();
+ for (String s : entries)
+ {
+ String[] parts = s.split("=", 2);
+ headers.setObject(parts[0], parts.length > 1 ? parts[1] : "");
+ }
+ return headers;
+ }
+
+
+ static final class MessagePublishInfoImpl implements MessagePublishInfo
+ {
+ private AMQShortString _exchange;
+ private boolean _immediate;
+ private boolean _mandatory;
+ private AMQShortString _routingKey;
+
+ public MessagePublishInfoImpl(AMQShortString routingKey)
+ {
+ _routingKey = routingKey;
+ }
+
+ public MessagePublishInfoImpl(AMQShortString exchange, boolean immediate, boolean mandatory, AMQShortString routingKey)
+ {
+ _exchange = exchange;
+ _immediate = immediate;
+ _mandatory = mandatory;
+ _routingKey = routingKey;
+ }
+
+ public AMQShortString getExchange()
+ {
+ return _exchange;
+ }
+
+ public boolean isImmediate()
+ {
+ return _immediate;
+
+ }
+
+ public boolean isMandatory()
+ {
+ return _mandatory;
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return _routingKey;
+ }
+
+
+ public void setExchange(AMQShortString exchange)
+ {
+ _exchange = exchange;
+ }
+
+ public void setImmediate(boolean immediate)
+ {
+ _immediate = immediate;
+ }
+
+ public void setMandatory(boolean mandatory)
+ {
+ _mandatory = mandatory;
+ }
+
+ public void setRoutingKey(AMQShortString routingKey)
+ {
+ _routingKey = routingKey;
+ }
+ }
+
+ static MessagePublishInfo getPublishRequest(final String id)
+ {
+ return new MessagePublishInfoImpl(null, false, false, new AMQShortString(id));
+ }
+
+ static ContentHeaderBody getContentHeader(FieldTable headers)
+ {
+ ContentHeaderBody header = new ContentHeaderBody();
+ header.setProperties(getProperties(headers));
+ return header;
+ }
+
+ static BasicContentHeaderProperties getProperties(FieldTable headers)
+ {
+ BasicContentHeaderProperties properties = new BasicContentHeaderProperties();
+ properties.setHeaders(headers);
+ return properties;
+ }
+
+ static class TestQueue extends SimpleAMQQueue
+ {
+ final List<HeadersExchangeTest.Message> messages = new ArrayList<HeadersExchangeTest.Message>();
+
+ public String toString()
+ {
+ return getNameShortString().toString();
+ }
+
+ public TestQueue(AMQShortString name) throws AMQException
+ {
+ super(name, false, new AMQShortString("test"), true, false,ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test"), Collections.EMPTY_MAP);
+ ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test").getQueueRegistry().registerQueue(this);
+ }
+
+
+
+ /**
+ * We override this method so that the default behaviour, which attempts to use a delivery manager, is
+ * not invoked. It is unnecessary since for this test we only care to know whether the message was
+ * sent to the queue; the queue processing logic is not being tested.
+ * @param msg
+ * @throws AMQException
+ */
+ @Override
+ public void enqueue(ServerMessage msg, PostEnqueueAction action) throws AMQException
+ {
+ messages.add( new HeadersExchangeTest.Message((AMQMessage) msg));
+ final QueueEntry queueEntry = new QueueEntry()
+ {
+
+ public AMQQueue getQueue()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public AMQMessage getMessage()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public long getSize()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean getDeliveredToConsumer()
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean expired() throws AMQException
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isAvailable()
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isAcquired()
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean acquire()
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean acquire(Subscription sub)
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean delete()
+ {
+ return false;
+ }
+
+ public boolean isDeleted()
+ {
+ return false;
+ }
+
+ public boolean acquiredBySubscription()
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isAcquiredBy(Subscription subscription)
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void release()
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean releaseButRetain()
+ {
+ return false;
+ }
+
+ public boolean immediateAndNotDelivered()
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void setRedelivered()
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public AMQMessageHeader getMessageHeader()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isPersistent()
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isRedelivered()
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public Subscription getDeliveredSubscription()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void reject()
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void reject(Subscription subscription)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isRejectedBy(Subscription subscription)
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void requeue(Subscription subscription)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void dequeue()
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void dispose()
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void discard()
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void routeToAlternate()
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isQueueDeleted()
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void addStateChangeListener(StateChangeListener listener)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean removeStateChangeListener(StateChangeListener listener)
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public int compareTo(final QueueEntry o)
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+ };
+
+ if(action != null)
+ {
+ action.onEnqueue(queueEntry);
+ }
+
+ }
+
+ boolean isInQueue(Message msg)
+ {
+ return messages.contains(msg);
+ }
+
+ }
+
+ /**
+ * Just add some extra utility methods to AMQMessage to aid testing.
+ */
+ static class Message extends AMQMessage
+ {
+ private static AtomicLong _messageId = new AtomicLong();
+
+ private class TestIncomingMessage extends IncomingMessage
+ {
+
+ public TestIncomingMessage(final long messageId,
+ final MessagePublishInfo info,
+ final AMQProtocolSession publisher)
+ {
+ super(info);
+ }
+
+
+ public AMQMessage getUnderlyingMessage()
+ {
+ return Message.this;
+ }
+
+
+ public ContentHeaderBody getContentHeader()
+ {
+ try
+ {
+ return Message.this.getContentHeaderBody();
+ }
+ catch (AMQException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private IncomingMessage _incoming;
+
+
+ Message(AMQProtocolSession protocolSession, String id, String... headers) throws AMQException
+ {
+ this(protocolSession, id, getHeaders(headers));
+ }
+
+ Message(AMQProtocolSession protocolSession, String id, FieldTable headers) throws AMQException
+ {
+ this(protocolSession, _messageId.incrementAndGet(),getPublishRequest(id), getContentHeader(headers), Collections.EMPTY_LIST);
+ }
+
+ public IncomingMessage getIncomingMessage()
+ {
+ return _incoming;
+ }
+
+ private Message(AMQProtocolSession protocolsession, long messageId,
+ MessagePublishInfo publish,
+ ContentHeaderBody header,
+ List<ContentBody> bodies) throws AMQException
+ {
+ super(new MockStoredMessage(messageId, publish, header));
+
+ StoredMessage<MessageMetaData> storedMessage = getStoredMessage();
+
+ int pos = 0;
+ for(ContentBody body : bodies)
+ {
+ storedMessage.addContent(pos, body.payload.duplicate().buf());
+ pos += body.payload.limit();
+ }
+
+ _incoming = new TestIncomingMessage(getMessageId(),publish, protocolsession);
+ _incoming.setContentHeaderBody(header);
+
+
+ }
+
+
+ private Message(AMQMessage msg) throws AMQException
+ {
+ super(msg.getStoredMessage());
+ }
+
+
+
+ void route(Exchange exchange) throws AMQException
+ {
+ _incoming.enqueue(exchange.route(_incoming));
+ }
+
+
+ public int hashCode()
+ {
+ return getKey().hashCode();
+ }
+
+ public boolean equals(Object o)
+ {
+ return o instanceof HeadersExchangeTest.Message && equals((HeadersExchangeTest.Message) o);
+ }
+
+ private boolean equals(HeadersExchangeTest.Message m)
+ {
+ return getKey().equals(m.getKey());
+ }
+
+ public String toString()
+ {
+ return getKey().toString();
+ }
+
+ private Object getKey()
+ {
+ try
+ {
+ return getMessagePublishInfo().getRoutingKey();
+ }
+ catch (AMQException e)
+ {
+ _log.error("Error getting routing key: " + e, e);
+ return null;
+ }
+ }
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java
new file mode 100644
index 0000000000..71e92b5294
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java
@@ -0,0 +1,195 @@
+/*
+ *
+ * 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.exchange;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.management.common.mbeans.ManagedExchange;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.AMQQueueFactory;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.registry.IApplicationRegistry;
+import org.apache.qpid.server.management.ManagedObject;
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+
+import javax.management.openmbean.TabularData;
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * Unit test class for testing different Exchange MBean operations
+ */
+public class ExchangeMBeanTest extends InternalBrokerBaseCase
+{
+ private AMQQueue _queue;
+ private QueueRegistry _queueRegistry;
+ private VirtualHost _virtualHost;
+
+ /**
+ * Test for direct exchange mbean
+ * @throws Exception
+ */
+
+ public void testDirectExchangeMBean() throws Exception
+ {
+ DirectExchange exchange = new DirectExchange();
+ exchange.initialise(_virtualHost, ExchangeDefaults.DIRECT_EXCHANGE_NAME, false, 0, true);
+ ManagedObject managedObj = exchange.getManagedObject();
+ ManagedExchange mbean = (ManagedExchange)managedObj;
+
+ mbean.createNewBinding(_queue.getNameShortString().toString(), "binding1");
+ mbean.createNewBinding(_queue.getNameShortString().toString(), "binding2");
+
+ TabularData data = mbean.bindings();
+ ArrayList<Object> list = new ArrayList<Object>(data.values());
+ assertTrue(list.size() == 2);
+
+ // test general exchange properties
+ assertEquals(mbean.getName(), "amq.direct");
+ assertEquals(mbean.getExchangeType(), "direct");
+ assertTrue(mbean.getTicketNo() == 0);
+ assertTrue(!mbean.isDurable());
+ assertTrue(mbean.isAutoDelete());
+ }
+
+ /**
+ * Test for "topic" exchange mbean
+ * @throws Exception
+ */
+
+ public void testTopicExchangeMBean() throws Exception
+ {
+ TopicExchange exchange = new TopicExchange();
+ exchange.initialise(_virtualHost,ExchangeDefaults.TOPIC_EXCHANGE_NAME, false, 0, true);
+ ManagedObject managedObj = exchange.getManagedObject();
+ ManagedExchange mbean = (ManagedExchange)managedObj;
+
+ mbean.createNewBinding(_queue.getNameShortString().toString(), "binding1");
+ mbean.createNewBinding(_queue.getNameShortString().toString(), "binding2");
+
+ TabularData data = mbean.bindings();
+ ArrayList<Object> list = new ArrayList<Object>(data.values());
+ assertTrue(list.size() == 2);
+
+ // test general exchange properties
+ assertEquals(mbean.getName(), "amq.topic");
+ assertEquals(mbean.getExchangeType(), "topic");
+ assertTrue(mbean.getTicketNo() == 0);
+ assertTrue(!mbean.isDurable());
+ assertTrue(mbean.isAutoDelete());
+ }
+
+ /**
+ * Test for "Headers" exchange mbean
+ * @throws Exception
+ */
+
+ public void testHeadersExchangeMBean() throws Exception
+ {
+ HeadersExchange exchange = new HeadersExchange();
+ exchange.initialise(_virtualHost,ExchangeDefaults.HEADERS_EXCHANGE_NAME, false, 0, true);
+ ManagedObject managedObj = exchange.getManagedObject();
+ ManagedExchange mbean = (ManagedExchange)managedObj;
+
+ mbean.createNewBinding(_queue.getNameShortString().toString(), "key1=binding1,key2=binding2");
+ mbean.createNewBinding(_queue.getNameShortString().toString(), "key3=binding3");
+
+ TabularData data = mbean.bindings();
+ ArrayList<Object> list = new ArrayList<Object>(data.values());
+ assertTrue(list.size() == 2);
+
+ // test general exchange properties
+ assertEquals(mbean.getName(), "amq.match");
+ assertEquals(mbean.getExchangeType(), "headers");
+ assertTrue(mbean.getTicketNo() == 0);
+ assertTrue(!mbean.isDurable());
+ assertTrue(mbean.isAutoDelete());
+ }
+
+ /**
+ * Test adding bindings and removing them from the default exchange via JMX.
+ * <p>
+ * QPID-2700
+ */
+ public void testDefaultBindings() throws Exception
+ {
+ int bindings = _queue.getBindingCount();
+
+ Exchange exchange = _queue.getVirtualHost().getExchangeRegistry().getDefaultExchange();
+ ManagedExchange mbean = (ManagedExchange) ((AbstractExchange) exchange).getManagedObject();
+
+ mbean.createNewBinding(_queue.getName(), "robot");
+ mbean.createNewBinding(_queue.getName(), "kitten");
+
+ assertEquals("Should have added two bindings", bindings + 2, _queue.getBindingCount());
+
+ mbean.removeBinding(_queue.getName(), "robot");
+
+ assertEquals("Should have one extra binding", bindings + 1, _queue.getBindingCount());
+
+ mbean.removeBinding(_queue.getName(), "kitten");
+
+ assertEquals("Should have original number of binding", bindings, _queue.getBindingCount());
+ }
+
+ /**
+ * Test adding bindings and removing them from the topic exchange via JMX.
+ * <p>
+ * QPID-2700
+ */
+ public void testTopicBindings() throws Exception
+ {
+ int bindings = _queue.getBindingCount();
+
+ Exchange exchange = _queue.getVirtualHost().getExchangeRegistry().getExchange(new AMQShortString("amq.topic"));
+ ManagedExchange mbean = (ManagedExchange) ((AbstractExchange) exchange).getManagedObject();
+
+ mbean.createNewBinding(_queue.getName(), "robot.#");
+ mbean.createNewBinding(_queue.getName(), "#.kitten");
+
+ assertEquals("Should have added two bindings", bindings + 2, _queue.getBindingCount());
+
+ mbean.removeBinding(_queue.getName(), "robot.#");
+
+ assertEquals("Should have one extra binding", bindings + 1, _queue.getBindingCount());
+
+ mbean.removeBinding(_queue.getName(), "#.kitten");
+
+ assertEquals("Should have original number of binding", bindings, _queue.getBindingCount());
+ }
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ IApplicationRegistry applicationRegistry = ApplicationRegistry.getInstance();
+ _virtualHost = applicationRegistry.getVirtualHostRegistry().getVirtualHost("test");
+ _queueRegistry = _virtualHost.getQueueRegistry();
+ _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue"), false, new AMQShortString("ExchangeMBeanTest"), false, false,
+ _virtualHost, null);
+ _queueRegistry.registerQueue(_queue);
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersBindingTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersBindingTest.java
new file mode 100644
index 0000000000..a7c226cbd8
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersBindingTest.java
@@ -0,0 +1,317 @@
+/*
+ *
+ * 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.exchange;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+
+import junit.framework.TestCase;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.server.binding.Binding;
+import org.apache.qpid.server.message.AMQMessageHeader;
+import org.apache.qpid.server.queue.MockAMQQueue;
+
+/**
+ */
+public class HeadersBindingTest extends TestCase
+{
+
+ private class MockHeader implements AMQMessageHeader
+ {
+
+ private final Map<String, Object> _headers = new HashMap<String, Object>();
+
+ public String getCorrelationId()
+ {
+ return null;
+ }
+
+ public long getExpiration()
+ {
+ return 0;
+ }
+
+ public String getMessageId()
+ {
+ return null;
+ }
+
+ public String getMimeType()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public String getEncoding()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public byte getPriority()
+ {
+ return 0;
+ }
+
+ public long getTimestamp()
+ {
+ return 0;
+ }
+
+ public String getType()
+ {
+ return null;
+ }
+
+ public String getReplyTo()
+ {
+ return null;
+ }
+
+ public String getReplyToExchange()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public String getReplyToRoutingKey()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public Object getHeader(String name)
+ {
+ return _headers.get(name);
+ }
+
+ public boolean containsHeaders(Set<String> names)
+ {
+ return _headers.keySet().containsAll(names);
+ }
+
+ public boolean containsHeader(String name)
+ {
+ return _headers.containsKey(name);
+ }
+
+ public void setString(String key, String value)
+ {
+ setObject(key,value);
+ }
+
+ public void setObject(String key, Object value)
+ {
+ _headers.put(key,value);
+ }
+ }
+
+ private Map<String,Object> bindHeaders = new HashMap<String,Object>();
+ private MockHeader matchHeaders = new MockHeader();
+ private int _count = 0;
+ private MockAMQQueue _queue;
+
+ protected void setUp()
+ {
+ _count++;
+ _queue = new MockAMQQueue(getQueueName());
+ }
+
+ protected String getQueueName()
+ {
+ return "Queue" + _count;
+ }
+
+ public void testDefault_1()
+ {
+ bindHeaders.put("A", "Value of A");
+
+ matchHeaders.setString("A", "Value of A");
+
+ Binding b = new Binding(null, getQueueName(), _queue, null, bindHeaders);
+ assertTrue(new HeadersBinding(b).matches(matchHeaders));
+ }
+
+ public void testDefault_2()
+ {
+ bindHeaders.put("A", "Value of A");
+
+ matchHeaders.setString("A", "Value of A");
+ matchHeaders.setString("B", "Value of B");
+
+ Binding b = new Binding(null, getQueueName(), _queue, null, bindHeaders);
+ assertTrue(new HeadersBinding(b).matches(matchHeaders));
+ }
+
+ public void testDefault_3()
+ {
+ bindHeaders.put("A", "Value of A");
+
+ matchHeaders.setString("A", "Altered value of A");
+
+ Binding b = new Binding(null, getQueueName(), _queue, null, bindHeaders);
+ assertFalse(new HeadersBinding(b).matches(matchHeaders));
+ }
+
+ public void testAll_1()
+ {
+ bindHeaders.put("X-match", "all");
+ bindHeaders.put("A", "Value of A");
+
+ matchHeaders.setString("A", "Value of A");
+
+ Binding b = new Binding(null, getQueueName(), _queue, null, bindHeaders);
+ assertTrue(new HeadersBinding(b).matches(matchHeaders));
+ }
+
+ public void testAll_2()
+ {
+ bindHeaders.put("X-match", "all");
+ bindHeaders.put("A", "Value of A");
+ bindHeaders.put("B", "Value of B");
+
+ matchHeaders.setString("A", "Value of A");
+
+ Binding b = new Binding(null, getQueueName(), _queue, null, bindHeaders);
+ assertFalse(new HeadersBinding(b).matches(matchHeaders));
+ }
+
+ public void testAll_3()
+ {
+ bindHeaders.put("X-match", "all");
+ bindHeaders.put("A", "Value of A");
+ bindHeaders.put("B", "Value of B");
+
+ matchHeaders.setString("A", "Value of A");
+ matchHeaders.setString("B", "Value of B");
+
+ Binding b = new Binding(null, getQueueName(), _queue, null, bindHeaders);
+ assertTrue(new HeadersBinding(b).matches(matchHeaders));
+ }
+
+ public void testAll_4()
+ {
+ bindHeaders.put("X-match", "all");
+ bindHeaders.put("A", "Value of A");
+ bindHeaders.put("B", "Value of B");
+
+ matchHeaders.setString("A", "Value of A");
+ matchHeaders.setString("B", "Value of B");
+ matchHeaders.setString("C", "Value of C");
+
+ Binding b = new Binding(null, getQueueName(), _queue, null, bindHeaders);
+ assertTrue(new HeadersBinding(b).matches(matchHeaders));
+ }
+
+ public void testAll_5()
+ {
+ bindHeaders.put("X-match", "all");
+ bindHeaders.put("A", "Value of A");
+ bindHeaders.put("B", "Value of B");
+
+ matchHeaders.setString("A", "Value of A");
+ matchHeaders.setString("B", "Altered value of B");
+ matchHeaders.setString("C", "Value of C");
+
+ Binding b = new Binding(null, getQueueName(), _queue, null, bindHeaders);
+ assertFalse(new HeadersBinding(b).matches(matchHeaders));
+ }
+
+ public void testAny_1()
+ {
+ bindHeaders.put("X-match", "any");
+ bindHeaders.put("A", "Value of A");
+
+ matchHeaders.setString("A", "Value of A");
+
+ Binding b = new Binding(null, getQueueName(), _queue, null, bindHeaders);
+ assertTrue(new HeadersBinding(b).matches(matchHeaders));
+ }
+
+ public void testAny_2()
+ {
+ bindHeaders.put("X-match", "any");
+ bindHeaders.put("A", "Value of A");
+ bindHeaders.put("B", "Value of B");
+
+ matchHeaders.setString("A", "Value of A");
+
+ Binding b = new Binding(null, getQueueName(), _queue, null, bindHeaders);
+ assertTrue(new HeadersBinding(b).matches(matchHeaders));
+ }
+
+ public void testAny_3()
+ {
+ bindHeaders.put("X-match", "any");
+ bindHeaders.put("A", "Value of A");
+ bindHeaders.put("B", "Value of B");
+
+ matchHeaders.setString("A", "Value of A");
+ matchHeaders.setString("B", "Value of B");
+
+ Binding b = new Binding(null, getQueueName(), _queue, null, bindHeaders);
+ assertTrue(new HeadersBinding(b).matches(matchHeaders));
+ }
+
+ public void testAny_4()
+ {
+ bindHeaders.put("X-match", "any");
+ bindHeaders.put("A", "Value of A");
+ bindHeaders.put("B", "Value of B");
+
+ matchHeaders.setString("A", "Value of A");
+ matchHeaders.setString("B", "Value of B");
+ matchHeaders.setString("C", "Value of C");
+
+ Binding b = new Binding(null, getQueueName(), _queue, null, bindHeaders);
+ assertTrue(new HeadersBinding(b).matches(matchHeaders));
+ }
+
+ public void testAny_5()
+ {
+ bindHeaders.put("X-match", "any");
+ bindHeaders.put("A", "Value of A");
+ bindHeaders.put("B", "Value of B");
+
+ matchHeaders.setString("A", "Value of A");
+ matchHeaders.setString("B", "Altered value of B");
+ matchHeaders.setString("C", "Value of C");
+
+ Binding b = new Binding(null, getQueueName(), _queue, null, bindHeaders);
+ assertTrue(new HeadersBinding(b).matches(matchHeaders));
+ }
+
+ public void testAny_6()
+ {
+ bindHeaders.put("X-match", "any");
+ bindHeaders.put("A", "Value of A");
+ bindHeaders.put("B", "Value of B");
+
+ matchHeaders.setString("A", "Altered value of A");
+ matchHeaders.setString("B", "Altered value of B");
+ matchHeaders.setString("C", "Value of C");
+
+ Binding b = new Binding(null, getQueueName(), _queue, null, bindHeaders);
+ assertFalse(new HeadersBinding(b).matches(matchHeaders));
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(HeadersBindingTest.class);
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersExchangeTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersExchangeTest.java
new file mode 100644
index 0000000000..ac638e4e6a
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersExchangeTest.java
@@ -0,0 +1,124 @@
+/*
+ *
+ * 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.exchange;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.server.protocol.InternalTestProtocolSession;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+
+public class HeadersExchangeTest extends AbstractHeadersExchangeTestBase
+{
+ AMQProtocolSession _protocolSession;
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ // Just use the first vhost.
+ VirtualHost
+ virtualHost = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHosts().iterator().next();
+ _protocolSession = new InternalTestProtocolSession(virtualHost);
+ }
+
+ public void testSimple() throws AMQException
+ {
+ TestQueue q1 = bindDefault("F0000");
+ TestQueue q2 = bindDefault("F0000=Aardvark");
+ TestQueue q3 = bindDefault("F0001");
+ TestQueue q4 = bindDefault("F0001=Bear");
+ TestQueue q5 = bindDefault("F0000", "F0001");
+ TestQueue q6 = bindDefault("F0000=Aardvark", "F0001=Bear");
+ TestQueue q7 = bindDefault("F0000", "F0001=Bear");
+ TestQueue q8 = bindDefault("F0000=Aardvark", "F0001");
+
+ routeAndTest(new Message(_protocolSession, "Message1", "F0000"), q1);
+ routeAndTest(new Message(_protocolSession, "Message2", "F0000=Aardvark"), q1, q2);
+ routeAndTest(new Message(_protocolSession, "Message3", "F0000=Aardvark", "F0001"), q1, q2, q3, q5, q8);
+ routeAndTest(new Message(_protocolSession, "Message4", "F0000", "F0001=Bear"), q1, q3, q4, q5, q7);
+ routeAndTest(new Message(_protocolSession, "Message5", "F0000=Aardvark", "F0001=Bear"),
+ q1, q2, q3, q4, q5, q6, q7, q8);
+ routeAndTest(new Message(_protocolSession, "Message6", "F0002"));
+
+ Message m7 = new Message(_protocolSession, "Message7", "XXXXX");
+
+ MessagePublishInfoImpl pb7 = (MessagePublishInfoImpl) (m7.getMessagePublishInfo());
+ pb7.setMandatory(true);
+ routeAndTest(m7,true);
+
+ Message m8 = new Message(_protocolSession, "Message8", "F0000");
+ MessagePublishInfoImpl pb8 = (MessagePublishInfoImpl)(m8.getMessagePublishInfo());
+ pb8.setMandatory(true);
+ routeAndTest(m8,false,q1);
+
+
+ }
+
+ public void testAny() throws AMQException
+ {
+ TestQueue q1 = bindDefault("F0000", "F0001", "X-match=any");
+ TestQueue q2 = bindDefault("F0000=Aardvark", "F0001=Bear", "X-match=any");
+ TestQueue q3 = bindDefault("F0000", "F0001=Bear", "X-match=any");
+ TestQueue q4 = bindDefault("F0000=Aardvark", "F0001", "X-match=any");
+ TestQueue q6 = bindDefault("F0000=Apple", "F0001", "X-match=any");
+
+ routeAndTest(new Message(_protocolSession, "Message1", "F0000"), q1, q3);
+ routeAndTest(new Message(_protocolSession, "Message2", "F0000=Aardvark"), q1, q2, q3, q4);
+ routeAndTest(new Message(_protocolSession, "Message3", "F0000=Aardvark", "F0001"), q1, q2, q3, q4, q6);
+ routeAndTest(new Message(_protocolSession, "Message4", "F0000", "F0001=Bear"), q1, q2, q3, q4, q6);
+ routeAndTest(new Message(_protocolSession, "Message5", "F0000=Aardvark", "F0001=Bear"), q1, q2, q3, q4, q6);
+ routeAndTest(new Message(_protocolSession, "Message6", "F0002"));
+ }
+
+ public void testMandatory() throws AMQException
+ {
+ bindDefault("F0000");
+ Message m1 = new Message(_protocolSession, "Message1", "XXXXX");
+ Message m2 = new Message(_protocolSession, "Message2", "F0000");
+ MessagePublishInfoImpl pb1 = (MessagePublishInfoImpl) (m1.getMessagePublishInfo());
+ pb1.setMandatory(true);
+ MessagePublishInfoImpl pb2 = (MessagePublishInfoImpl) (m2.getMessagePublishInfo());
+ pb2.setMandatory(true);
+ routeAndTest(m1,true);
+ }
+
+ public void testOnUnbind() throws AMQException
+ {
+ TestQueue q1 = bindDefault("F0000");
+ TestQueue q2 = bindDefault("F0000=Aardvark");
+ TestQueue q3 = bindDefault("F0001");
+
+ routeAndTest(new Message(_protocolSession, "Message1", "F0000"), q1);
+ routeAndTest(new Message(_protocolSession, "Message2", "F0000=Aardvark"), q1, q2);
+ routeAndTest(new Message(_protocolSession, "Message3", "F0001"), q3);
+
+ unbind(q1,"F0000");
+ routeAndTest(new Message(_protocolSession, "Message4", "F0000"));
+ routeAndTest(new Message(_protocolSession, "Message5", "F0000=Aardvark"), q2);
+ }
+
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(HeadersExchangeTest.class);
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/TopicExchangeTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/TopicExchangeTest.java
new file mode 100644
index 0000000000..403a290a0f
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/TopicExchangeTest.java
@@ -0,0 +1,441 @@
+/*
+ * 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.exchange;
+
+import junit.framework.TestCase;
+import junit.framework.Assert;
+import org.apache.qpid.server.queue.*;
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.store.MemoryMessageStore;
+import org.apache.qpid.server.protocol.InternalTestProtocolSession;
+import org.apache.qpid.server.binding.Binding;
+import org.apache.qpid.server.message.AMQMessage;
+import org.apache.qpid.server.message.MessageMetaData;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+
+public class TopicExchangeTest extends InternalBrokerBaseCase
+{
+
+ TopicExchange _exchange;
+
+ VirtualHost _vhost;
+ MessageStore _store;
+
+ InternalTestProtocolSession _protocolSession;
+
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ _exchange = new TopicExchange();
+ _vhost = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHosts().iterator().next();
+ _store = new MemoryMessageStore();
+ _protocolSession = new InternalTestProtocolSession(_vhost);
+ }
+
+ public void testNoRoute() throws AMQException
+ {
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a*#b"), false, null, false, false, _vhost, null);
+ _exchange.registerQueue(new Binding(null,"a.*.#.b", queue,_exchange, null));
+
+
+ IncomingMessage message = createMessage("a.b");
+ routeMessage(message);
+
+ Assert.assertEquals(0, queue.getMessageCount());
+ }
+
+ public void testDirectMatch() throws AMQException
+ {
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("ab"), false, null, false, false, _vhost, null);
+ _exchange.registerQueue(new Binding(null,"a.b", queue,_exchange, null));
+
+
+ IncomingMessage message = createMessage("a.b");
+
+ routeMessage(message);
+
+ Assert.assertEquals(1, queue.getMessageCount());
+
+ Assert.assertEquals("Wrong message recevied", (Object) message.getMessageNumber(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageNumber());
+
+ queue.deleteMessageFromTop();
+ Assert.assertEquals(0, queue.getMessageCount());
+
+
+ message = createMessage("a.c");
+
+ int queueCount = routeMessage(message);
+ Assert.assertEquals("Message should not route to any queues", 0, queueCount);
+
+ Assert.assertEquals(0, queue.getMessageCount());
+ }
+
+
+ public void testStarMatch() throws AMQException
+ {
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a*"), false, null, false, false, _vhost, null);
+ _exchange.registerQueue(new Binding(null,"a.*", queue,_exchange, null));
+
+
+ IncomingMessage message = createMessage("a.b");
+
+ routeMessage(message);
+
+ Assert.assertEquals(1, queue.getMessageCount());
+
+ Assert.assertEquals("Wrong message recevied", (Object) message.getMessageNumber(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageNumber());
+
+ queue.deleteMessageFromTop();
+ Assert.assertEquals(0, queue.getMessageCount());
+
+
+ message = createMessage("a.c");
+
+ int queueCount = routeMessage(message);
+
+ Assert.assertEquals(1, queue.getMessageCount());
+
+ Assert.assertEquals("Wrong message recevied", (Object) message.getMessageNumber(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageNumber());
+
+ queue.deleteMessageFromTop();
+ Assert.assertEquals(0, queue.getMessageCount());
+
+
+ message = createMessage("a");
+
+
+ queueCount = routeMessage(message);
+ Assert.assertEquals("Message should not route to any queues", 0, queueCount);
+
+ Assert.assertEquals(0, queue.getMessageCount());
+ }
+
+ public void testHashMatch() throws AMQException
+ {
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, false, _vhost, null);
+ _exchange.registerQueue(new Binding(null,"a.#", queue,_exchange, null));
+
+
+ IncomingMessage message = createMessage("a.b.c");
+
+ int queueCount = routeMessage(message);
+
+ Assert.assertEquals(1, queue.getMessageCount());
+
+ Assert.assertEquals("Wrong message recevied", (Object) message.getMessageNumber(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageNumber());
+
+ queue.deleteMessageFromTop();
+ Assert.assertEquals(0, queue.getMessageCount());
+
+
+ message = createMessage("a.b");
+
+ queueCount = routeMessage(message);
+
+ Assert.assertEquals(1, queue.getMessageCount());
+
+ Assert.assertEquals("Wrong message recevied", (Object) message.getMessageNumber(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageNumber());
+
+ queue.deleteMessageFromTop();
+ Assert.assertEquals(0, queue.getMessageCount());
+
+
+ message = createMessage("a.c");
+
+ queueCount = routeMessage(message);
+
+ Assert.assertEquals(1, queue.getMessageCount());
+
+ Assert.assertEquals("Wrong message recevied", (Object) message.getMessageNumber(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageNumber());
+
+ queue.deleteMessageFromTop();
+ Assert.assertEquals(0, queue.getMessageCount());
+
+ message = createMessage("a");
+
+ queueCount = routeMessage(message);
+
+ Assert.assertEquals(1, queue.getMessageCount());
+
+ Assert.assertEquals("Wrong message recevied", (Object) message.getMessageNumber(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageNumber());
+
+ queue.deleteMessageFromTop();
+ Assert.assertEquals(0, queue.getMessageCount());
+
+
+ message = createMessage("b");
+
+
+ queueCount = routeMessage(message);
+ Assert.assertEquals("Message should not route to any queues", 0, queueCount);
+
+ Assert.assertEquals(0, queue.getMessageCount());
+ }
+
+
+ public void testMidHash() throws AMQException
+ {
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, false, _vhost, null);
+ _exchange.registerQueue(new Binding(null,"a.*.#.b", queue,_exchange, null));
+
+
+ IncomingMessage message = createMessage("a.c.d.b");
+
+ routeMessage(message);
+
+ Assert.assertEquals(1, queue.getMessageCount());
+
+ Assert.assertEquals("Wrong message recevied", (Object) message.getMessageNumber(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageNumber());
+
+ queue.deleteMessageFromTop();
+ Assert.assertEquals(0, queue.getMessageCount());
+
+ message = createMessage("a.c.b");
+
+ routeMessage(message);
+
+ Assert.assertEquals(1, queue.getMessageCount());
+
+ Assert.assertEquals("Wrong message recevied", (Object) message.getMessageNumber(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageNumber());
+
+ queue.deleteMessageFromTop();
+ Assert.assertEquals(0, queue.getMessageCount());
+
+ }
+
+ public void testMatchafterHash() throws AMQException
+ {
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, false, _vhost, null);
+ _exchange.registerQueue(new Binding(null,"a.*.#.b.c", queue,_exchange, null));
+
+
+ IncomingMessage message = createMessage("a.c.b.b");
+
+ int queueCount = routeMessage(message);
+ Assert.assertEquals("Message should not route to any queues", 0, queueCount);
+
+ Assert.assertEquals(0, queue.getMessageCount());
+
+
+ message = createMessage("a.a.b.c");
+
+ routeMessage(message);
+
+ Assert.assertEquals(1, queue.getMessageCount());
+
+ Assert.assertEquals("Wrong message recevied", (Object) message.getMessageNumber(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageNumber());
+
+ queue.deleteMessageFromTop();
+ Assert.assertEquals(0, queue.getMessageCount());
+
+ message = createMessage("a.b.c.b");
+
+ queueCount = routeMessage(message);
+ Assert.assertEquals("Message should not route to any queues", 0, queueCount);
+
+ Assert.assertEquals(0, queue.getMessageCount());
+
+ message = createMessage("a.b.c.b.c");
+
+ routeMessage(message);
+
+ Assert.assertEquals(1, queue.getMessageCount());
+
+ Assert.assertEquals("Wrong message recevied", (Object) message.getMessageNumber(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageNumber());
+
+ queue.deleteMessageFromTop();
+ Assert.assertEquals(0, queue.getMessageCount());
+
+ }
+
+
+ public void testHashAfterHash() throws AMQException
+ {
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, false, _vhost, null);
+ _exchange.registerQueue(new Binding(null,"a.*.#.b.c.#.d", queue,_exchange, null));
+
+
+ IncomingMessage message = createMessage("a.c.b.b.c");
+
+ int queueCount = routeMessage(message);
+ Assert.assertEquals("Message should not route to any queues", 0, queueCount);
+
+ Assert.assertEquals(0, queue.getMessageCount());
+
+
+ message = createMessage("a.a.b.c.d");
+
+ routeMessage(message);
+
+ Assert.assertEquals(1, queue.getMessageCount());
+
+ Assert.assertEquals("Wrong message recevied", (Object) message.getMessageNumber(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageNumber());
+
+ queue.deleteMessageFromTop();
+ Assert.assertEquals(0, queue.getMessageCount());
+
+ }
+
+ public void testHashHash() throws AMQException
+ {
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, false, _vhost, null);
+ _exchange.registerQueue(new Binding(null,"a.#.*.#.d", queue,_exchange, null));
+
+
+ IncomingMessage message = createMessage("a.c.b.b.c");
+
+ int queueCount = routeMessage(message);
+ Assert.assertEquals("Message should not route to any queues", 0, queueCount);
+
+ Assert.assertEquals(0, queue.getMessageCount());
+
+ message = createMessage("a.a.b.c.d");
+
+ routeMessage(message);
+
+ Assert.assertEquals(1, queue.getMessageCount());
+
+ Assert.assertEquals("Wrong message recevied", (Object) message.getMessageNumber(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageNumber());
+
+ queue.deleteMessageFromTop();
+ Assert.assertEquals(0, queue.getMessageCount());
+
+ }
+
+ public void testSubMatchFails() throws AMQException
+ {
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, false, _vhost, null);
+ _exchange.registerQueue(new Binding(null,"a.b.c.d", queue,_exchange, null));
+
+
+ IncomingMessage message = createMessage("a.b.c");
+
+ int queueCount = routeMessage(message);
+ Assert.assertEquals("Message should not route to any queues", 0, queueCount);
+
+ Assert.assertEquals(0, queue.getMessageCount());
+
+ }
+
+ private int routeMessage(final IncomingMessage message)
+ throws AMQException
+ {
+ MessageMetaData mmd = message.headersReceived();
+ message.setStoredMessage(_store.addMessage(mmd));
+
+ message.enqueue(_exchange.route(message));
+ AMQMessage msg = new AMQMessage(message.getStoredMessage());
+ for(BaseQueue q : message.getDestinationQueues())
+ {
+ q.enqueue(msg);
+ }
+ return message.getDestinationQueues().size();
+ }
+
+ public void testMoreRouting() throws AMQException
+ {
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, false, _vhost, null);
+ _exchange.registerQueue(new Binding(null,"a.b", queue,_exchange, null));
+
+
+ IncomingMessage message = createMessage("a.b.c");
+
+ int queueCount = routeMessage(message);
+ Assert.assertEquals("Message should not route to any queues", 0, queueCount);
+
+ Assert.assertEquals(0, queue.getMessageCount());
+
+ }
+
+ public void testMoreQueue() throws AMQException
+ {
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, false, _vhost, null);
+ _exchange.registerQueue(new Binding(null,"a.b", queue,_exchange, null));
+
+
+ IncomingMessage message = createMessage("a");
+
+ int queueCount = routeMessage(message);
+ Assert.assertEquals("Message should not route to any queues", 0, queueCount);
+
+ Assert.assertEquals(0, queue.getMessageCount());
+
+ }
+
+ private IncomingMessage createMessage(String s) throws AMQException
+ {
+ MessagePublishInfo info = new PublishInfo(new AMQShortString(s));
+
+ IncomingMessage message = new IncomingMessage(info);
+ final ContentHeaderBody chb = new ContentHeaderBody();
+ BasicContentHeaderProperties props = new BasicContentHeaderProperties();
+ chb.setProperties(props);
+ message.setContentHeaderBody(chb);
+
+
+ return message;
+ }
+
+
+ class PublishInfo implements MessagePublishInfo
+ {
+ AMQShortString _routingkey;
+
+ PublishInfo(AMQShortString routingkey)
+ {
+ _routingkey = routingkey;
+ }
+
+ public AMQShortString getExchange()
+ {
+ return null;
+ }
+
+ public void setExchange(AMQShortString exchange)
+ {
+
+ }
+
+ public boolean isImmediate()
+ {
+ return false;
+ }
+
+ public boolean isMandatory()
+ {
+ return true;
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return _routingkey;
+ }
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/Log4jMessageLoggerTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/Log4jMessageLoggerTest.java
new file mode 100644
index 0000000000..a845bff9ce
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/Log4jMessageLoggerTest.java
@@ -0,0 +1,271 @@
+/*
+ * 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.logging;
+
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.qpid.server.logging.actors.BrokerActor;
+
+/** Test that the Log4jMessageLogger defaults behave as expected */
+public class Log4jMessageLoggerTest extends TestCase
+{
+ Level _rootLevel;
+ Log4jTestAppender _appender;
+
+ @Override
+ public void setUp() throws IOException
+ {
+ // Setup a file for logging
+ _appender = new Log4jTestAppender();
+
+ Logger root = Logger.getRootLogger();
+ root.addAppender(_appender);
+
+ _rootLevel = Logger.getRootLogger().getLevel();
+ if (_rootLevel != Level.INFO)
+ {
+ root.setLevel(Level.INFO);
+ root.warn("Root Logger set to:" + _rootLevel + " Resetting to INFO for test.");
+ }
+ root.warn("Adding Test Appender:" + _appender);
+ }
+
+ @Override
+ public void tearDown()
+ {
+ Logger root = Logger.getRootLogger();
+ root.warn("Removing Test Appender:" + _appender);
+ root.warn("Resetting Root Level to : " + _rootLevel);
+
+ Logger.getRootLogger().setLevel(_rootLevel);
+
+ Logger.getRootLogger().removeAppender(_appender);
+
+ //Call close on our appender. This will clear the log messages
+ // from Memory
+ _appender.close();
+ }
+
+ /**
+ * Verify that the Log4jMessageLogger successfully logs a message.
+ */
+ public void testLoggedMessage()
+ {
+ Log4jMessageLogger msgLogger = new Log4jMessageLogger();
+ assertTrue("Expected message logger to be enabled", msgLogger.isEnabled());
+
+ testLoggedMessage(msgLogger, true, getName());
+ }
+
+ /**
+ * Verify that for the given Log4jMessageLogger, after generating a message for the given
+ * log hierarchy that the outcome is as expected.
+ */
+ private String testLoggedMessage(Log4jMessageLogger logger, boolean logExpected, String hierarchy)
+ {
+ //Create Message for test
+ String message = "testDefaults";
+
+ // Log the message
+ logger.rawMessage(message, hierarchy);
+
+ if(logExpected)
+ {
+ verifyLogPresent(message);
+ }
+ else
+ {
+ verifyNoLog(message);
+ }
+
+ return message;
+ }
+
+ /**
+ * Test that specifying different log hierarchies to be used works as expected.
+ * <p/>
+ * Test this by using one hierarchy and verifying it succeeds, then disabling it and
+ * confirming this takes effect, and finally that using another hierarchy still succeeds.
+ */
+ public void testMultipleHierarchyUsage()
+ {
+ String loggerName1 = getName() + ".TestLogger1";
+ String loggerName2 = getName() + ".TestLogger2";
+
+ // Create a message logger to test
+ Log4jMessageLogger msgLogger = new Log4jMessageLogger();
+ assertTrue("Expected message logger to be enabled", msgLogger.isEnabled());
+
+ //verify that using this hierarchy the message gets logged ok
+ String message = testLoggedMessage(msgLogger, true, loggerName1);
+
+ //now disable that hierarchy in log4j
+ Logger.getLogger(loggerName1).setLevel(Level.OFF);
+
+ //clear the previous message from the test appender
+ _appender.close();
+ verifyNoLog(message);
+
+ //verify that the hierarchy disabling took effect
+ testLoggedMessage(msgLogger, false, loggerName1);
+
+ //now ensure that using a new hierarchy results in the message being output
+ testLoggedMessage(msgLogger, true, loggerName2);
+ }
+
+ /**
+ * Test that log4j can be used to manipulate on a per-hierarchy(and thus message) basis
+ * whether a particular status message is enabled.
+ * <p/>
+ * Test this by using two hierarchies, setting one off and one on (info) via log4j directly,
+ * then confirming this gives the expected isMessageEnabled() result. Then reverse the log4j
+ * Levels for the Logger's and ensure the results change as expected.
+ */
+ public void testEnablingAndDisablingMessages()
+ {
+ String loggerName1 = getName() + ".TestLogger1";
+ String loggerName2 = getName() + ".TestLogger2";
+
+ Logger.getLogger(loggerName1).setLevel(Level.INFO);
+ Logger.getLogger(loggerName2).setLevel(Level.OFF);
+
+ Log4jMessageLogger msgLogger = new Log4jMessageLogger();
+ BrokerActor actor = new BrokerActor(msgLogger);
+
+ assertTrue("Expected message logger to be enabled", msgLogger.isEnabled());
+
+ assertTrue("Message should be enabled", msgLogger.isMessageEnabled(actor, loggerName1));
+ assertFalse("Message should be disabled", msgLogger.isMessageEnabled(actor, loggerName2));
+
+ Logger.getLogger(loggerName1).setLevel(Level.WARN);
+ Logger.getLogger(loggerName2).setLevel(Level.INFO);
+
+ assertFalse("Message should be disabled", msgLogger.isMessageEnabled(actor, loggerName1));
+ assertTrue("Message should be enabled", msgLogger.isMessageEnabled(actor, loggerName2));
+ }
+
+ /**
+ * Check that the Log Message reached log4j
+ * @param message the message to search for
+ */
+ private void verifyLogPresent(String message)
+ {
+ List<String> results = findMessageInLog(message);
+
+ //Validate we only got one message
+ assertEquals("The result set was not as expected.", 1, results.size());
+
+ // Validate message
+ String line = results.get(0);
+
+ assertNotNull("No Message retrieved from log file", line);
+ assertTrue("Message not contained in log.:" + line,
+ line.contains(message));
+ }
+
+ /**
+ * Check that the given Message is not present in the log4j records.
+ * @param message the message to search for
+ */
+ private void verifyNoLog(String message)
+ {
+ List<String> results = findMessageInLog(message);
+
+ if (results.size() > 0)
+ {
+ System.err.println("Unexpected Log messages");
+
+ for (String msg : results)
+ {
+ System.err.println(msg);
+ }
+ }
+
+ assertEquals("No message was expected.", 0, results.size());
+ }
+
+ /**
+ * Get the appenders list of events and return a list of all the messages
+ * that contain the given message
+ *
+ * @param message the search string
+ * @return The list of all logged messages that contain the search string.
+ */
+ private List<String> findMessageInLog(String message)
+ {
+ List<LoggingEvent> log = _appender.getLog();
+
+ // Search Results for requested message
+ List<String> result = new LinkedList<String>();
+
+ for (LoggingEvent event : log)
+ {
+ if (String.valueOf(event.getMessage()).contains(message))
+ {
+ result.add(String.valueOf(event.getMessage()));
+ }
+ }
+
+ return result;
+ }
+
+
+ /**
+ * Log4j Appender that simply records all the Logging Events so we can
+ * verify that the above logging will make it to log4j in a unit test.
+ */
+ private class Log4jTestAppender extends AppenderSkeleton
+ {
+ List<LoggingEvent> _log = new LinkedList<LoggingEvent>();
+
+ protected void append(LoggingEvent loggingEvent)
+ {
+ _log.add(loggingEvent);
+ }
+
+ public void close()
+ {
+ _log.clear();
+ }
+
+ /**
+ * @return the list of LoggingEvents that have occured in this Appender
+ */
+ public List<LoggingEvent> getLog()
+ {
+ return _log;
+ }
+
+ public boolean requiresLayout()
+ {
+ return false;
+ }
+ }
+}
+
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/LogMessageTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/LogMessageTest.java
new file mode 100644
index 0000000000..956bb6f8fa
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/LogMessageTest.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.logging;
+
+import junit.framework.TestCase;
+import org.apache.qpid.server.logging.messages.BrokerMessages;
+
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+public class LogMessageTest extends TestCase
+{
+
+ /**
+ * Test that the US local has a loadable bundle.
+ * No longer have a specific en_US bundle so cannot verify that that version
+ * is loaded. Can only verify that we get a ResourceBundle loaded.
+ */
+ public void testBundle()
+ {
+ Locale usLocal = Locale.US;
+ Locale.setDefault(usLocal);
+ ResourceBundle _messages = ResourceBundle.getBundle("org.apache.qpid.server.logging.messages.Broker_logmessages",
+ usLocal);
+
+ assertNotNull("Unable to load ResourceBundle", _messages);
+ }
+
+ /**
+ * Test that loading an undefined locale will result in loading of the
+ * default US locale.
+ */
+ public void testUndefinedLocale()
+ {
+ Locale japanese = Locale.JAPANESE;
+
+ Locale.setDefault(japanese);
+ try
+ {
+ ResourceBundle _messages = ResourceBundle.getBundle("org.apache.qpid.server.logging.messages.Broker_logmessages",
+ japanese);
+
+ assertNotNull("Unable to load ResourceBundle", _messages);
+
+ // If we attempt to load an undefined locale it should default to the Root locale.
+ assertEquals("Loaded bundle has incorrect locale.", Locale.ROOT, _messages.getLocale());
+ }
+ catch (Throwable t)
+ {
+ fail(t.getMessage());
+ }
+ }
+
+ /**
+ * test Simultaneous log message generation.
+ * QPID-2137 highlighted that log message generation was not thread-safe.
+ * Test to ensure that simultaneous logging is possible and does not throw an exception.
+ * @throws InterruptedException if there is a problem joining logging threads.
+ */
+ public void testSimultaneousLogging() throws InterruptedException
+ {
+ int LOGGERS = 10;
+ int LOG_COUNT = 10;
+ LogGenerator[] logGenerators = new LogGenerator[LOGGERS];
+ Thread[] threads = new Thread[LOGGERS];
+
+ //Create Loggers
+ for (int i = 0; i < LOGGERS; i++)
+ {
+ logGenerators[i] = new LogGenerator(LOG_COUNT);
+ threads[i] = new Thread(logGenerators[i]);
+ }
+
+ //Run Loggers
+ for (int i = 0; i < LOGGERS; i++)
+ {
+ threads[i].start();
+ }
+
+ //End Loggers
+ for (int i = 0; i < LOGGERS; i++)
+ {
+ threads[i].join();
+ Exception e = logGenerators[i].getThrowException();
+ // If we have an exception something went wrong.
+ // Check and see if it was QPID-2137
+ if (e != null)
+ {
+ // Just log out if we find the usual exception causing QPID-2137
+ if (e instanceof StringIndexOutOfBoundsException)
+ {
+ System.err.println("Detected QPID-2137");
+ }
+ fail("Exception thrown during log generation:" + e);
+ }
+ }
+ }
+
+ /**
+ * Inner class used by testSimultaneousLogging.
+ *
+ * This class creates a given number of LogMessages using the BrokerMessages package.
+ * CONFIG and LISTENING messages are both created per count.
+ *
+ * This class is run multiple times simultaneously so that we increase the chance of
+ * reproducing QPID-2137. This is reproduced when the pattern string used in the MessageFormat
+ * class is changed whilst formatting is taking place.
+ *
+ */
+ class LogGenerator implements Runnable
+ {
+ private Exception _exception = null;
+ private int _count;
+
+ /**
+ * @param count The number of Log Messages to generate
+ */
+ LogGenerator(int count)
+ {
+ _count = count;
+ }
+
+ public void run()
+ {
+ try
+ {
+ // try and generate _count iterations of Config & Listening messages.
+ for (int i = 0; i < _count; i++)
+ {
+ BrokerMessages.CONFIG("Config");
+ BrokerMessages.LISTENING("TCP", 1234);
+ }
+ }
+ catch (Exception e)
+ {
+ // if something goes wrong recorded it for later analysis.
+ _exception = e;
+ }
+ }
+
+ /**
+ * Return any exception that was thrown during the log generation.
+ * @return Exception
+ */
+ public Exception getThrowException()
+ {
+ return _exception;
+ }
+ }
+
+} \ No newline at end of file
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/UnitTestMessageLogger.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/UnitTestMessageLogger.java
new file mode 100644
index 0000000000..3752dcb37e
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/UnitTestMessageLogger.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.server.logging;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.qpid.server.configuration.ServerConfiguration;
+import org.apache.qpid.server.logging.AbstractRootMessageLogger;
+
+public class UnitTestMessageLogger extends AbstractRootMessageLogger
+{
+ List<Object> _log;
+
+ {
+ _log = new LinkedList<Object>();
+ }
+
+ public UnitTestMessageLogger()
+ {
+
+ }
+
+ public UnitTestMessageLogger(ServerConfiguration config)
+ {
+ super(config);
+ }
+
+ public void rawMessage(String message, String logHierarchy)
+ {
+ _log.add(message);
+ }
+
+ public void rawMessage(String message, Throwable throwable, String logHierarchy)
+ {
+ _log.add(message);
+
+ if(throwable != null)
+ {
+ _log.add(throwable);
+ }
+ }
+
+
+ public List<Object> getLogMessages()
+ {
+ return _log;
+ }
+
+ public void clearLogMessages()
+ {
+ _log.clear();
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/UnitTestMessageLoggerTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/UnitTestMessageLoggerTest.java
new file mode 100644
index 0000000000..e2e112be8f
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/UnitTestMessageLoggerTest.java
@@ -0,0 +1,103 @@
+/*
+ *
+ * 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.logging;
+
+import junit.framework.TestCase;
+
+import java.util.List;
+
+/**
+ * Test: UnitTestMessageLoggerTest
+ *
+ * This test verifies that UnitTestMessageLogger adheres to its interface.
+ *
+ * Messages are logged, and Throwables recorded in an array that can be
+ * retrieved and cleared.
+ *
+ */
+public class UnitTestMessageLoggerTest extends TestCase
+{
+ private static final String TEST_MESSAGE = "Test";
+ private static final String TEST_THROWABLE = "Test Throwable";
+ private static final String TEST_HIERARCHY = "test.hierarchy";
+
+ public void testRawMessage()
+ {
+ UnitTestMessageLogger logger = new UnitTestMessageLogger();
+
+ assertEquals("Messages logged before test start", 0,
+ logger.getLogMessages().size());
+
+ // Log a message
+ logger.rawMessage(TEST_MESSAGE, TEST_HIERARCHY);
+
+ List<Object> messages = logger.getLogMessages();
+
+ assertEquals("Expected to have 1 messages logged", 1, messages.size());
+
+ assertEquals("First message not what was logged",
+ TEST_MESSAGE, messages.get(0));
+ }
+
+ public void testRawMessageWithThrowable()
+ {
+ UnitTestMessageLogger logger = new UnitTestMessageLogger();
+
+ assertEquals("Messages logged before test start", 0,
+ logger.getLogMessages().size());
+
+ // Log a message
+ Throwable throwable = new Throwable(TEST_THROWABLE);
+
+ logger.rawMessage(TEST_MESSAGE, throwable, TEST_HIERARCHY);
+
+ List<Object> messages = logger.getLogMessages();
+
+ assertEquals("Expected to have 2 entries", 2, messages.size());
+
+ assertEquals("Message text not what was logged",
+ TEST_MESSAGE, messages.get(0));
+
+ assertEquals("Message throwable not what was logged",
+ TEST_THROWABLE, ((Throwable) messages.get(1)).getMessage());
+
+ }
+
+ public void testClear()
+ {
+ UnitTestMessageLogger logger = new UnitTestMessageLogger();
+
+ assertEquals("Messages logged before test start", 0,
+ logger.getLogMessages().size());
+
+ // Log a message
+ logger.rawMessage(TEST_MESSAGE, null, TEST_HIERARCHY);
+
+ assertEquals("Expected to have 1 messages logged",
+ 1, logger.getLogMessages().size());
+
+ logger.clearLogMessages();
+
+ assertEquals("Expected to have no messagse after a clear",
+ 0, logger.getLogMessages().size());
+
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/AMQPChannelActorTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/AMQPChannelActorTest.java
new file mode 100644
index 0000000000..6346fff85f
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/AMQPChannelActorTest.java
@@ -0,0 +1,221 @@
+/*
+ *
+ * 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.logging.actors;
+
+import java.util.List;
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.qpid.AMQException;
+
+/**
+ * Test : AMQPChannelActorTest
+ * Validate the AMQPChannelActor class.
+ *
+ * The test creates a new AMQPActor and then logs a message using it.
+ *
+ * The test then verifies that the logged message was the only one created and
+ * that the message contains the required message.
+ */
+public class AMQPChannelActorTest extends BaseConnectionActorTestCase
+{
+
+ @Override
+ public void configure()
+ {
+ // Prevent defaulting Logging to ON
+ }
+
+
+ @Override
+ public void createBroker() throws Exception
+ {
+ //prevent auto-broker startup
+ }
+
+ private void startBrokerNow() throws Exception
+ {
+ super.createBroker();
+
+ _amqpActor = new AMQPChannelActor(getChannel(), _rootLogger);
+ }
+
+
+ /**
+ * Test that when logging on behalf of the channel
+ * The test sends a message then verifies that it entered the logs.
+ *
+ * The log message should be fully repalaced (no '{n}' values) and should
+ * contain the channel id ('/ch:1') identification.
+ */
+ public void testChannel() throws Exception
+ {
+ getConfigXml().setProperty("status-updates", "ON");
+
+ startBrokerNow();
+
+ final String message = sendTestLogMessage(_amqpActor);
+
+ List<Object> logs = _rawLogger.getLogMessages();
+
+ assertEquals("Message log size not as expected.", 1, logs.size());
+
+ // Verify that the logged message is present in the output
+ assertTrue("Message was not found in log message:" + logs.get(0),
+ logs.get(0).toString().contains(message));
+
+ // Verify that the message has the correct type
+ assertTrue("Message contains the [con: prefix",
+ logs.get(0).toString().contains("[con:"));
+
+
+ // Verify that all the values were presented to the MessageFormatter
+ // so we will not end up with '{n}' entries in the log.
+ assertFalse("Verify that the string does not contain any '{'." + logs.get(0),
+ logs.get(0).toString().contains("{"));
+
+ // Verify that the logged message contains the 'ch:1' marker
+ assertTrue("Message was not logged as part of channel 1" + logs.get(0),
+ logs.get(0).toString().contains("/ch:1"));
+
+ }
+
+ /**
+ * Test that if logging is configured to be off in the configuration that
+ * no logging is presented
+ * @throws ConfigurationException
+ * @throws AMQException
+ */
+ public void testChannelLoggingOFF() throws Exception, AMQException
+ {
+ getConfigXml().setProperty("status-updates", "OFF");
+
+ // Start the broker now.
+ startBrokerNow();
+
+ sendTestLogMessage(_amqpActor);
+
+ List<Object> logs = _rawLogger.getLogMessages();
+
+ assertEquals("Message log size not as expected.", 0, logs.size());
+
+ }
+
+ /**
+ * Test that if logging is configured to be off in the configuration that
+ * no logging is presented
+ * @throws ConfigurationException
+ * @throws AMQException
+ */
+ public void testChannelLoggingOfF() throws Exception, AMQException
+ {
+ getConfigXml().setProperty("status-updates", "OfF");
+
+ startBrokerNow();
+
+ sendTestLogMessage(_amqpActor);
+
+ List<Object> logs = _rawLogger.getLogMessages();
+
+ assertEquals("Message log size not as expected.", 0, logs.size());
+
+ }
+
+ /**
+ * Test that if logging is configured to be off in the configuration that
+ * no logging is presented
+ * @throws ConfigurationException
+ * @throws AMQException
+ */
+ public void testChannelLoggingOff() throws Exception, AMQException
+ {
+ getConfigXml().setProperty("status-updates", "Off");
+
+ startBrokerNow();
+
+ sendTestLogMessage(_amqpActor);
+
+ List<Object> logs = _rawLogger.getLogMessages();
+
+ assertEquals("Message log size not as expected.", 0, logs.size());
+
+ }
+
+ /**
+ * Test that if logging is configured to be off in the configuration that
+ * no logging is presented
+ * @throws ConfigurationException
+ * @throws AMQException
+ */
+ public void testChannelLoggingofF() throws Exception, AMQException
+ {
+ getConfigXml().setProperty("status-updates", "ofF");
+
+ startBrokerNow();
+
+ sendTestLogMessage(_amqpActor);
+
+ List<Object> logs = _rawLogger.getLogMessages();
+
+ assertEquals("Message log size not as expected.", 0, logs.size());
+
+ }
+
+ /**
+ * Test that if logging is configured to be off in the configuration that
+ * no logging is presented
+ * @throws ConfigurationException
+ * @throws AMQException
+ */
+ public void testChannelLoggingoff() throws Exception, AMQException
+ {
+ getConfigXml().setProperty("status-updates", "off");
+
+ startBrokerNow();
+
+ sendTestLogMessage(_amqpActor);
+
+ List<Object> logs = _rawLogger.getLogMessages();
+
+ assertEquals("Message log size not as expected.", 0, logs.size());
+
+ }
+
+ /**
+ * Test that if logging is configured to be off in the configuration that
+ * no logging is presented
+ * @throws ConfigurationException
+ * @throws AMQException
+ */
+ public void testChannelLoggingoFf() throws Exception, AMQException
+ {
+ getConfigXml().setProperty("status-updates", "oFf");
+
+ startBrokerNow();
+
+ sendTestLogMessage(_amqpActor);
+
+ List<Object> logs = _rawLogger.getLogMessages();
+
+ assertEquals("Message log size not as expected.", 0, logs.size());
+
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/AMQPConnectionActorTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/AMQPConnectionActorTest.java
new file mode 100644
index 0000000000..4eda9e9da1
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/AMQPConnectionActorTest.java
@@ -0,0 +1,132 @@
+/*
+ *
+ * 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.logging.actors;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.logging.LogMessage;
+import org.apache.qpid.server.logging.LogSubject;
+
+import java.util.List;
+
+/**
+ * Test : AMQPConnectionActorTest
+ * Validate the AMQPConnectionActor class.
+ *
+ * The test creates a new AMQPActor and then logs a message using it.
+ *
+ * The test then verifies that the logged message was the only one created and
+ * that the message contains the required message.
+ */
+public class AMQPConnectionActorTest extends BaseConnectionActorTestCase
+{
+ @Override
+ public void configure()
+ {
+ // Prevent defaulting Logging to ON
+ }
+
+
+ @Override
+ public void createBroker()
+ {
+ //Prevent auto-broker startup
+ }
+
+ /**
+ * Test the AMQPActor logging as a Connection level.
+ *
+ * The test sends a message then verifies that it entered the logs.
+ *
+ * The log message should be fully repalaced (no '{n}' values) and should
+ * not contain any channel identification.
+ */
+ public void testConnection() throws Exception
+ {
+ getConfigXml().setProperty("status-updates", "ON");
+
+ super.createBroker();
+
+ final String message = sendLogMessage();
+
+ List<Object> logs = _rawLogger.getLogMessages();
+
+ assertEquals("Message log size not as expected.", 1, logs.size());
+
+ // Verify that the logged message is present in the output
+ assertTrue("Message was not found in log message",
+ logs.get(0).toString().contains(message));
+
+ // Verify that the message has the correct type
+ assertTrue("Message does not contain the [con: prefix",
+ logs.get(0).toString().contains("[con:"));
+
+ // Verify that all the values were presented to the MessageFormatter
+ // so we will not end up with '{n}' entries in the log.
+ assertFalse("Verify that the string does not contain any '{'.",
+ logs.get(0).toString().contains("{"));
+
+ // Verify that the logged message does not contains the 'ch:' marker
+ assertFalse("Message was logged with a channel identifier." + logs.get(0),
+ logs.get(0).toString().contains("/ch:"));
+ }
+
+ public void testConnectionLoggingOff() throws Exception, AMQException
+ {
+ getConfigXml().setProperty("status-updates", "OFF");
+
+ // Start the broker now.
+ super.createBroker();
+
+ sendLogMessage();
+
+ List<Object> logs = _rawLogger.getLogMessages();
+
+ assertEquals("Message log size not as expected.", 0, logs.size());
+
+ }
+
+ private String sendLogMessage()
+ {
+ final String message = "test logging";
+
+ _amqpActor.message(new LogSubject()
+ {
+ public String toLogString()
+ {
+ return "[AMQPActorTest]";
+ }
+
+ }, new LogMessage()
+ {
+ public String toString()
+ {
+ return message;
+ }
+
+ public String getLogHierarchy()
+ {
+ return "test.hieracrchy";
+ }
+ });
+ return message;
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/BaseActorTestCase.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/BaseActorTestCase.java
new file mode 100644
index 0000000000..60ecbef438
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/BaseActorTestCase.java
@@ -0,0 +1,91 @@
+/*
+ *
+ * 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.logging.actors;
+
+import org.apache.qpid.server.configuration.ServerConfiguration;
+import org.apache.qpid.server.logging.LogMessage;
+import org.apache.qpid.server.logging.LogSubject;
+import org.apache.qpid.server.logging.RootMessageLogger;
+import org.apache.qpid.server.logging.LogActor;
+import org.apache.qpid.server.logging.UnitTestMessageLogger;
+
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+
+public class BaseActorTestCase extends InternalBrokerBaseCase
+{
+ protected LogActor _amqpActor;
+ protected UnitTestMessageLogger _rawLogger;
+ protected RootMessageLogger _rootLogger;
+
+ @Override
+ public void configure()
+ {
+ getConfiguration().getConfig().setProperty(ServerConfiguration.STATUS_UPDATES, "on");
+ }
+
+ @Override
+ public void createBroker() throws Exception
+ {
+ super.createBroker();
+
+ _rawLogger = new UnitTestMessageLogger(getConfiguration());
+ _rootLogger = _rawLogger;
+ }
+
+ public void tearDown() throws Exception
+ {
+ _rawLogger.clearLogMessages();
+
+ super.tearDown();
+ }
+
+ public String sendTestLogMessage(LogActor actor)
+ {
+ String message = "Test logging: " + getName();
+ sendTestLogMessage(actor, message);
+
+ return message;
+ }
+
+ public void sendTestLogMessage(LogActor actor, final String message)
+ {
+ actor.message(new LogSubject()
+ {
+ public String toLogString()
+ {
+ return message;
+ }
+
+ }, new LogMessage()
+ {
+ public String toString()
+ {
+ return message;
+ }
+
+ public String getLogHierarchy()
+ {
+ return "test.hierarchy";
+ }
+ });
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/BaseConnectionActorTestCase.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/BaseConnectionActorTestCase.java
new file mode 100644
index 0000000000..956d296dce
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/BaseConnectionActorTestCase.java
@@ -0,0 +1,33 @@
+/*
+ *
+ * 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.logging.actors;
+
+public class BaseConnectionActorTestCase extends BaseActorTestCase
+{
+
+ @Override
+ public void createBroker() throws Exception
+ {
+ super.createBroker();
+
+ _amqpActor = new AMQPConnectionActor(getSession(), _rootLogger);
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/CurrentActorTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/CurrentActorTest.java
new file mode 100644
index 0000000000..32ad1d110d
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/CurrentActorTest.java
@@ -0,0 +1,256 @@
+/*
+ *
+ * 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.logging.actors;
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.logging.NullRootMessageLogger;
+
+/**
+ * Test : CurrentActorTest
+ * Summary:
+ * Validate ThreadLocal operation.
+ *
+ * Test creates THREADS number of threads which all then execute the same test
+ * together ( as close as looping Thread.start() will allow).
+ *
+ * Test:
+ * Test sets the CurrentActor then proceeds to retrieve the value and use it.
+ *
+ * The test also validates that it is the same LogActor that this thread set.
+ *
+ * Finally the LogActor is removed and tested to make sure that it was
+ * successfully removed.
+ *
+ * By having a higher number of threads than would normally be used in the
+ * Poolling filter we aim to catch the race condition where a ThreadLocal remove
+ * is called before one or more threads call get(). This way we can ensure that
+ * the remove does not affect more than the Thread it was called in.
+ */
+public class CurrentActorTest extends BaseConnectionActorTestCase
+{
+ //Set this to be a reasonably large number
+ int THREADS = 10;
+
+ // Record any exceptions that are thrown by the threads
+ Exception[] _errors = new Exception[THREADS];
+
+ /**
+ * Test that CurrentActor behaves as LIFO queue.
+ *
+ * Test creates two Actors Connection and Channel and then sets the
+ * CurrentActor.
+ *
+ * The test validates that CurrentActor remembers the Connection actor
+ * after the Channel actor has been removed.
+ *
+ * And then finally validates that removing the Connection actor results
+ * in there being no actors set.
+ *
+ * @throws AMQException
+ * @throws org.apache.commons.configuration.ConfigurationException
+ */
+ public void testLIFO() throws AMQException, ConfigurationException
+ {
+ // This test only needs the local objects created, _session etc.
+ // So stopping the broker and making them useless will not affect the
+ // test, but the extra actors the test broker adds will so by stopping
+ // we remove the session actor and so all is good.
+ stopBroker();
+
+ AMQPConnectionActor connectionActor = new AMQPConnectionActor(getSession(),
+ new NullRootMessageLogger());
+
+ /*
+ * Push the actor on to the stack:
+ *
+ * CurrentActor -> Connection
+ * Stack -> null
+ */
+ CurrentActor.set(connectionActor);
+
+ //Use the Actor to send a simple message
+ sendTestLogMessage(CurrentActor.get());
+
+ // Verify it was the same actor as we set earlier
+ assertEquals("Retrieved actor is not as expected ",
+ connectionActor, CurrentActor.get());
+
+ /**
+ * Set the actor to now be the Channel actor so testing the ability
+ * to push the actor on to the stack:
+ *
+ * CurrentActor -> Channel
+ * Stack -> Connection, null
+ *
+ */
+
+ AMQChannel channel = new AMQChannel(getSession(), 1, getSession().getVirtualHost().getMessageStore());
+
+ AMQPChannelActor channelActor = new AMQPChannelActor(channel,
+ new NullRootMessageLogger());
+
+ CurrentActor.set(channelActor);
+
+ //Use the Actor to send a simple message
+ sendTestLogMessage(CurrentActor.get());
+
+ // Verify it was the same actor as we set earlier
+ assertEquals("Retrieved actor is not as expected ",
+ channelActor, CurrentActor.get());
+
+ // Remove the ChannelActor from the stack
+ CurrentActor.remove();
+ /*
+ * Pop the actor on to the stack:
+ *
+ * CurrentActor -> Connection
+ * Stack -> null
+ */
+
+
+ // Verify we now have the same connection actor as we set earlier
+ assertEquals("Retrieved actor is not as expected ",
+ connectionActor, CurrentActor.get());
+
+ // Verify that removing the our last actor it returns us to the test
+ // default that the ApplicationRegistry sets.
+ CurrentActor.remove();
+ /*
+ * Pop the actor on to the stack:
+ *
+ * CurrentActor -> null
+ */
+
+
+ assertEquals("CurrentActor not the Test default", TestLogActor.class ,CurrentActor.get().getClass());
+ }
+
+ /**
+ * Test the setting CurrentActor is done correctly as a ThreadLocal.
+ *
+ * The test starts 'THREADS' threads that all set the CurrentActor log
+ * a message then remove the actor.
+ *
+ * Checks are done to ensure that there is no set actor after the remove.
+ *
+ * If the ThreadLocal was not working then having concurrent actor sets
+ * would result in more than one actor and so the remove will not result
+ * in the clearing of the CurrentActor
+ *
+ */
+ public void testThreadLocal()
+ {
+
+ new Runnable(){
+ public void run()
+ {
+ System.out.println(_errors[0]);
+ }
+ };
+
+ // Setup the threads
+ Thread[] threads = new Thread[THREADS];
+ for (int count = 0; count < THREADS; count++)
+ {
+ Runnable test = new LogMessagesWithAConnectionActor(count);
+ threads[count] = new Thread(test);
+ }
+
+ //Run the threads
+ for (int count = 0; count < THREADS; count++)
+ {
+ threads[count].start();
+ }
+
+ // Wait for them to finish
+ for (int count = 0; count < THREADS; count++)
+ {
+ try
+ {
+ threads[count].join();
+ }
+ catch (InterruptedException e)
+ {
+ //if we are interrupted then we will exit shortly.
+ }
+ }
+
+ // Verify that none of the tests threw an exception
+ for (int count = 0; count < THREADS; count++)
+ {
+ if (_errors[count] != null)
+ {
+ _errors[count].printStackTrace();
+ fail("Error occured in thread:" + count);
+ }
+ }
+ }
+
+ /**
+ * Creates a new ConnectionActor and logs the given number of messages
+ * before removing the actor and validating that there is no set actor.
+ */
+ public class LogMessagesWithAConnectionActor implements Runnable
+ {
+ int count;
+
+ LogMessagesWithAConnectionActor(int count)
+ {
+ this.count = count;
+ }
+
+ public void run()
+ {
+
+ // Create a new actor using retrieving the rootMessageLogger from
+ // the default ApplicationRegistry.
+ //fixme reminder that we need a better approach for broker testing.
+ try
+ {
+
+ AMQPConnectionActor actor = new AMQPConnectionActor(getSession(),
+ new NullRootMessageLogger());
+
+ CurrentActor.set(actor);
+
+ //Use the Actor to send a simple message
+ sendTestLogMessage(CurrentActor.get());
+
+ // Verify it was the same actor as we set earlier
+ assertEquals("Retrieved actor is not as expected ",
+ actor, CurrentActor.get());
+
+ // Verify that removing the actor works for this thread
+ CurrentActor.remove();
+
+ assertNull("CurrentActor should be null", CurrentActor.get());
+ }
+ catch (Exception e)
+ {
+ _errors[count] = e;
+ }
+
+ }
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/ManagementActorTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/ManagementActorTest.java
new file mode 100644
index 0000000000..033ae3b4b3
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/ManagementActorTest.java
@@ -0,0 +1,103 @@
+/*
+ *
+ * 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.logging.actors;
+
+import org.apache.qpid.server.configuration.ServerConfiguration;
+import org.apache.qpid.server.logging.LogMessage;
+import org.apache.qpid.server.logging.LogSubject;
+import org.apache.qpid.AMQException;
+
+import java.util.List;
+
+/**
+ * Test : AMQPManagementActorTest
+ * Validate the AMQPManagementActor class.
+ *
+ * The test creates a new AMQPActor and then logs a message using it.
+ *
+ * The test then verifies that the logged message was the only one created and
+ * that the message contains the required message.
+ */
+public class ManagementActorTest extends BaseActorTestCase
+{
+
+ private static final String IP = "127.0.0.1";
+ private static final String CONNECTION_ID = "1";
+ private String _threadName;
+
+ @Override
+ public void createBroker() throws Exception
+ {
+ super.createBroker();
+ _amqpActor = new ManagementActor(_rootLogger);
+
+ // Set the thread name to be the same as a RMI JMX Connection would use
+ _threadName = Thread.currentThread().getName();
+ Thread.currentThread().setName("RMI TCP Connection(" + CONNECTION_ID + ")-" + IP);
+ }
+
+ @Override
+ public void tearDown() throws Exception
+ {
+ Thread.currentThread().setName(_threadName);
+ super.tearDown();
+ }
+
+ /**
+ * Test the AMQPActor logging as a Connection level.
+ *
+ * The test sends a message then verifies that it entered the logs.
+ *
+ * The log message should be fully repalaced (no '{n}' values) and should
+ * not contain any channel identification.
+ */
+ public void testConnection()
+ {
+ final String message = sendTestLogMessage(_amqpActor);
+
+ List<Object> logs = _rawLogger.getLogMessages();
+
+ assertEquals("Message log size not as expected.", 1, logs.size());
+
+ // Verify that the logged message is present in the output
+ assertTrue("Message was not found in log message",
+ logs.get(0).toString().contains(message));
+
+ // Verify that all the values were presented to the MessageFormatter
+ // so we will not end up with '{n}' entries in the log.
+ assertFalse("Verify that the string does not contain any '{'.",
+ logs.get(0).toString().contains("{"));
+
+ // Verify that the message has the correct type
+ assertTrue("Message does not contain the [mng: prefix",
+ logs.get(0).toString().contains("[mng:"));
+
+ // Verify that the logged message does not contains the 'ch:' marker
+ assertFalse("Message was logged with a channel identifier." + logs.get(0),
+ logs.get(0).toString().contains("/ch:"));
+
+ // Verify that the message has the right values
+ assertTrue("Message contains the [mng: prefix",
+ logs.get(0).toString().contains("[mng:" + CONNECTION_ID + "(" + IP + ")"));
+
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/QueueActorTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/QueueActorTest.java
new file mode 100644
index 0000000000..409f7c84b7
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/QueueActorTest.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.logging.actors;
+
+import java.util.List;
+
+public class QueueActorTest extends BaseConnectionActorTestCase
+{
+
+ @Override
+ public void createBroker() throws Exception
+ {
+ super.createBroker();
+ _amqpActor = new QueueActor(getQueue(), _rootLogger);
+ }
+
+ /**
+ * Test the QueueActor as a logger.
+ *
+ * The test logs a message then verifies that it entered the logs correctly
+ *
+ * The log message should be fully repalaced (no '{n}' values) and should
+ * contain the correct queue identification.
+ */
+ public void testQueueActor()
+ {
+ final String message = sendTestLogMessage(_amqpActor);
+
+ List<Object> logs = _rawLogger.getLogMessages();
+
+ assertEquals("Message log size not as expected.", 1, logs.size());
+
+ String log = logs.get(0).toString();
+
+ // Verify that the logged message is present in the output
+ assertTrue("Message was not found in log message",
+ log.contains(message));
+
+ // Verify that all the values were presented to the MessageFormatter
+ // so we will not end up with '{n}' entries in the log.
+ assertFalse("Verify that the string does not contain any '{':" + log,
+ log.contains("{"));
+
+ // Verify that the message has the correct type
+ assertTrue("Message contains the [vh: prefix:" + log,
+ log.contains("[vh("));
+
+ // Verify that the logged message contains the 'qu(' marker
+ String expected = "qu(" + getName() + ")";
+ assertTrue("Message was not logged with a queue identifer '"+expected+"' actual:" + log,
+ log.contains(expected));
+ }
+
+}
+
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/SubscriptionActorTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/SubscriptionActorTest.java
new file mode 100644
index 0000000000..a2272cc395
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/SubscriptionActorTest.java
@@ -0,0 +1,85 @@
+/*
+ *
+ * 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.logging.actors;
+
+import java.util.List;
+
+import org.apache.qpid.server.subscription.MockSubscription;
+
+/**
+ * Test : AMQPConnectionActorTest
+ * Validate the AMQPConnectionActor class.
+ *
+ * The test creates a new AMQPActor and then logs a message using it.
+ *
+ * The test then verifies that the logged message was the only one created and
+ * that the message contains the required message.
+ */
+public class SubscriptionActorTest extends BaseConnectionActorTestCase
+{
+
+ @Override
+ public void createBroker() throws Exception
+ {
+ super.createBroker();
+
+ MockSubscription mockSubscription = new MockSubscription();
+
+ mockSubscription.setQueue(getQueue(), false);
+
+ _amqpActor = new SubscriptionActor(_rootLogger, mockSubscription);
+ }
+
+ /**
+ * Test the AMQPActor logging as a Subscription logger.
+ *
+ * The test sends a message then verifies that it entered the logs.
+ *
+ * The log message should be fully repalaced (no '{n}' values) and should
+ * contain subscription identification.
+ */
+ public void testSubscription()
+ {
+ final String message = sendTestLogMessage(_amqpActor);
+
+ List<Object> logs = _rawLogger.getLogMessages();
+
+ assertEquals("Message log size not as expected.", 1, logs.size());
+
+ // Verify that the logged message is present in the output
+ assertTrue("Message was not found in log message",
+ logs.get(0).toString().contains(message));
+
+ // Verify that all the values were presented to the MessageFormatter
+ // so we will not end up with '{n}' entries in the log.
+ assertFalse("Verify that the string does not contain any '{'.",
+ logs.get(0).toString().contains("{"));
+
+ // Verify that the message has the correct type
+ assertTrue("Message contains the [sub: prefix",
+ logs.get(0).toString().contains("[sub:"));
+
+ // Verify that the logged message does not contains the 'ch:' marker
+ assertFalse("Message was logged with a channel identifier." + logs.get(0),
+ logs.get(0).toString().contains("/ch:"));
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/TestLogActor.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/TestLogActor.java
new file mode 100644
index 0000000000..30f4e16e42
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/TestLogActor.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.logging.actors;
+
+import org.apache.qpid.server.logging.RootMessageLogger;
+
+public class TestLogActor extends AbstractActor
+{
+ public TestLogActor(RootMessageLogger rootLogger)
+ {
+ super(rootLogger);
+ }
+
+ public String getLogMessage()
+ {
+ return "[Test Actor] ";
+ }
+}
+ \ No newline at end of file
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/management/LoggingManagementMBeanTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/management/LoggingManagementMBeanTest.java
new file mode 100644
index 0000000000..2d25a769aa
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/management/LoggingManagementMBeanTest.java
@@ -0,0 +1,431 @@
+/*
+ * 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.logging.management;
+
+import static org.apache.qpid.management.common.mbeans.LoggingManagement.LOGGER_LEVEL;
+import static org.apache.qpid.management.common.mbeans.LoggingManagement.LOGGER_NAME;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.management.JMException;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.TabularDataSupport;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.qpid.management.common.mbeans.LoggingManagement;
+
+import junit.framework.TestCase;
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+
+public class LoggingManagementMBeanTest extends InternalBrokerBaseCase
+{
+ private static final String TEST_LOGGER = "LoggingManagementMBeanTestLogger";
+ private static final String TEST_LOGGER_CHILD1 = "LoggingManagementMBeanTestLogger.child1";
+ private static final String TEST_LOGGER_CHILD2 = "LoggingManagementMBeanTestLogger.child2";
+
+ private static final String TEST_CATEGORY_PRIORITY = "LogManMBeanTest.category.priority";
+ private static final String TEST_CATEGORY_LEVEL = "LogManMBeanTest.category.level";
+ private static final String TEST_LOGGER_LEVEL = "LogManMBeanTest.logger.level";
+
+ private static final String NEWLINE = System.getProperty("line.separator");
+
+ private File _testConfigFile;
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ _testConfigFile = createTempTestLog4JConfig();
+ }
+
+ @Override
+ public void tearDown() throws Exception
+ {
+ File oldTestConfigFile = new File(_testConfigFile.getAbsolutePath() + ".old");
+ if(oldTestConfigFile.exists())
+ {
+ oldTestConfigFile.delete();
+ }
+
+ _testConfigFile.delete();
+
+ super.tearDown();
+ }
+
+ private File createTempTestLog4JConfig()
+ {
+ File tmpFile = null;
+ try
+ {
+ tmpFile = File.createTempFile("LogManMBeanTestLog4jConfig", ".tmp");
+ tmpFile.deleteOnExit();
+
+ FileWriter fstream = new FileWriter(tmpFile);
+ BufferedWriter writer = new BufferedWriter(fstream);
+
+ writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+NEWLINE);
+ writer.write("<!DOCTYPE log4j:configuration SYSTEM \"log4j.dtd\">"+NEWLINE);
+
+ writer.write("<log4j:configuration xmlns:log4j=\"http://jakarta.apache.org/log4j/\" debug=\"null\" " +
+ "threshold=\"null\">"+NEWLINE);
+
+ writer.write(" <appender class=\"org.apache.log4j.ConsoleAppender\" name=\"STDOUT\">"+NEWLINE);
+ writer.write(" <layout class=\"org.apache.log4j.PatternLayout\">"+NEWLINE);
+ writer.write(" <param name=\"ConversionPattern\" value=\"%d %-5p [%t] %C{2} (%F:%L) - %m%n\"/>"+NEWLINE);
+ writer.write(" </layout>"+NEWLINE);
+ writer.write(" </appender>"+NEWLINE);
+
+ //Example of a 'category' with a 'priority'
+ writer.write(" <category additivity=\"true\" name=\"" + TEST_CATEGORY_PRIORITY +"\">"+NEWLINE);
+ writer.write(" <priority value=\"info\"/>"+NEWLINE);
+ writer.write(" <appender-ref ref=\"STDOUT\"/>"+NEWLINE);
+ writer.write(" </category>"+NEWLINE);
+
+ //Example of a 'category' with a 'level'
+ writer.write(" <category additivity=\"true\" name=\"" + TEST_CATEGORY_LEVEL +"\">"+NEWLINE);
+ writer.write(" <level value=\"warn\"/>"+NEWLINE);
+ writer.write(" <appender-ref ref=\"STDOUT\"/>"+NEWLINE);
+ writer.write(" </category>"+NEWLINE);
+
+ //Example of a 'logger' with a 'level'
+ writer.write(" <logger additivity=\"true\" name=\"" + TEST_LOGGER_LEVEL + "\">"+NEWLINE);
+ writer.write(" <level value=\"error\"/>"+NEWLINE);
+ writer.write(" <appender-ref ref=\"STDOUT\"/>"+NEWLINE);
+ writer.write(" </logger>"+NEWLINE);
+
+ //'root' logger
+ writer.write(" <root>"+NEWLINE);
+ writer.write(" <priority value=\"info\"/>"+NEWLINE);
+ writer.write(" <appender-ref ref=\"STDOUT\"/>"+NEWLINE);
+ writer.write(" </root>"+NEWLINE);
+
+ writer.write("</log4j:configuration>"+NEWLINE);
+
+ writer.flush();
+ writer.close();
+ }
+ catch (IOException e)
+ {
+ fail("Unable to create temporary test log4j configuration");
+ }
+
+ return tmpFile;
+ }
+
+
+
+ //******* Test Methods ******* //
+
+ public void testSetRuntimeLoggerLevel()
+ {
+ LoggingManagementMBean lm = null;
+ try
+ {
+ lm = new LoggingManagementMBean(_testConfigFile.getAbsolutePath(), 0);
+ }
+ catch (JMException e)
+ {
+ fail("Could not create test LoggingManagementMBean");
+ }
+
+ //create a parent test logger, set its level explicitly
+ Logger log = Logger.getLogger(TEST_LOGGER);
+ log.setLevel(Level.toLevel("info"));
+
+ //create child1 test logger, check its *effective* level is the same as the parent, "info"
+ Logger log1 = Logger.getLogger(TEST_LOGGER_CHILD1);
+ assertTrue("Test logger's level was not the expected value",
+ log1.getEffectiveLevel().toString().equalsIgnoreCase("info"));
+
+ //now change its level to "warn"
+ assertTrue("Failed to set logger level", lm.setRuntimeLoggerLevel(TEST_LOGGER_CHILD1, "warn"));
+
+ //check the change, see its actual level is "warn
+ assertTrue("Test logger's level was not the expected value",
+ log1.getLevel().toString().equalsIgnoreCase("warn"));
+
+ //try an invalid level
+ assertFalse("Trying to set an invalid level succeded", lm.setRuntimeLoggerLevel(TEST_LOGGER_CHILD1, "made.up.level"));
+ }
+
+ public void testSetRuntimeRootLoggerLevel()
+ {
+ LoggingManagementMBean lm = null;
+ try
+ {
+ lm = new LoggingManagementMBean(_testConfigFile.getAbsolutePath(), 0);
+ }
+ catch (JMException e)
+ {
+ fail("Could not create test LoggingManagementMBean");
+ }
+
+ Logger log = Logger.getRootLogger();
+
+ //get current root logger level
+ Level origLevel = log.getLevel();
+
+ //change level twice to ensure a new level is actually selected
+
+ //set root loggers level to info
+ assertTrue("Failed to set root logger level", lm.setRuntimeRootLoggerLevel("debug"));
+ //check it is now actually info
+ Level currentLevel = log.getLevel();
+ assertTrue("Logger level was not expected value", currentLevel.equals(Level.toLevel("debug")));
+
+ //try an invalid level
+ assertFalse("Trying to set an invalid level succeded", lm.setRuntimeRootLoggerLevel("made.up.level"));
+
+ //set root loggers level to warn
+ assertTrue("Failed to set logger level", lm.setRuntimeRootLoggerLevel("info"));
+ //check it is now actually warn
+ currentLevel = log.getLevel();
+ assertTrue("Logger level was not expected value", currentLevel.equals(Level.toLevel("info")));
+
+ //restore original level
+ log.setLevel(origLevel);
+ }
+
+ public void testGetRuntimeRootLoggerLevel()
+ {
+ LoggingManagementMBean lm = null;
+ try
+ {
+ lm = new LoggingManagementMBean(_testConfigFile.getAbsolutePath(), 0);
+ }
+ catch (JMException e)
+ {
+ fail("Could not create test LoggingManagementMBean");
+ }
+
+ Logger log = Logger.getRootLogger();
+
+ //get current root logger level
+ Level origLevel = log.getLevel();
+
+ //change level twice to ensure a new level is actually selected
+
+ //set root loggers level to debug
+ log.setLevel(Level.toLevel("debug"));
+ //check it is now actually debug
+ assertTrue("Logger level was not expected value", lm.getRuntimeRootLoggerLevel().equalsIgnoreCase("debug"));
+
+
+ //set root loggers level to warn
+ log.setLevel(Level.toLevel("info"));
+ //check it is now actually warn
+ assertTrue("Logger level was not expected value", lm.getRuntimeRootLoggerLevel().equalsIgnoreCase("info"));
+
+ //restore original level
+ log.setLevel(origLevel);
+ }
+
+ public void testViewEffectiveRuntimeLoggerLevels()
+ {
+ LoggingManagementMBean lm = null;
+ try
+ {
+ lm = new LoggingManagementMBean(_testConfigFile.getAbsolutePath(), 0);
+ }
+ catch (JMException e)
+ {
+ fail("Could not create test LoggingManagementMBean");
+ }
+
+ //(re)create a parent test logger, set its level explicitly
+ Logger log = Logger.getLogger(TEST_LOGGER);
+ log.setLevel(Level.toLevel("info"));
+
+ //retrieve the current effective runtime logger level values
+ TabularDataSupport levels = (TabularDataSupport) lm.viewEffectiveRuntimeLoggerLevels();
+ Collection<Object> records = levels.values();
+ Map<String,String> list = new HashMap<String,String>();
+ for (Object o : records)
+ {
+ CompositeData data = (CompositeData) o;
+ list.put(data.get(LOGGER_NAME).toString(), data.get(LOGGER_LEVEL).toString());
+ }
+
+ //check child2 does not exist already
+ assertFalse("Did not expect this logger to exist already", list.containsKey(TEST_LOGGER_CHILD2));
+
+ //create child2 test logger
+ Logger log2 = Logger.getLogger(TEST_LOGGER_CHILD2);
+
+ //retrieve the current effective runtime logger level values
+ levels = (TabularDataSupport) lm.viewEffectiveRuntimeLoggerLevels();
+ records = levels.values();
+ list = new HashMap<String,String>();
+ for (Object o : records)
+ {
+ CompositeData data = (CompositeData) o;
+ list.put(data.get(LOGGER_NAME).toString(), data.get(LOGGER_LEVEL).toString());
+ }
+
+ //verify the parent and child2 loggers are present in returned values
+ assertTrue(TEST_LOGGER + " logger was not in the returned list", list.containsKey(TEST_LOGGER));
+ assertTrue(TEST_LOGGER_CHILD2 + " logger was not in the returned list", list.containsKey(TEST_LOGGER_CHILD2));
+
+ //check child2's effective level is the same as the parent, "info"
+ assertTrue("Test logger's level was not the expected value",
+ list.get(TEST_LOGGER_CHILD2).equalsIgnoreCase("info"));
+
+ //now change its level explicitly to "warn"
+ log2.setLevel(Level.toLevel("warn"));
+
+ //retrieve the current effective runtime logger level values
+ levels = (TabularDataSupport) lm.viewEffectiveRuntimeLoggerLevels();
+ records = levels.values();
+ list = new HashMap<String,String>();
+ for (Object o : records)
+ {
+ CompositeData data = (CompositeData) o;
+ list.put(data.get(LOGGER_NAME).toString(), data.get(LOGGER_LEVEL).toString());
+ }
+
+ //check child2's effective level is now "warn"
+ assertTrue("Test logger's level was not the expected value",
+ list.get(TEST_LOGGER_CHILD2).equalsIgnoreCase("warn"));
+ }
+
+ public void testViewAndSetConfigFileLoggerLevel() throws Exception
+ {
+ LoggingManagementMBean lm =null;
+ try
+ {
+ lm = new LoggingManagementMBean(_testConfigFile.getAbsolutePath(), 0);
+ }
+ catch (JMException e)
+ {
+ fail("Could not create test LoggingManagementMBean");
+ }
+
+ //retrieve the current values
+ TabularDataSupport levels = (TabularDataSupport) lm.viewConfigFileLoggerLevels();
+ Collection<Object> records = levels.values();
+ Map<String,String> list = new HashMap<String,String>();
+ for (Object o : records)
+ {
+ CompositeData data = (CompositeData) o;
+ list.put(data.get(LOGGER_NAME).toString(), data.get(LOGGER_LEVEL).toString());
+ }
+
+ //check the 3 different types of logger definition are successfully retrieved before update
+ assertTrue("Wrong number of items in returned list", list.size() == 3);
+ assertTrue(TEST_CATEGORY_PRIORITY + " logger was not in the returned list", list.containsKey(TEST_CATEGORY_PRIORITY));
+ assertTrue(TEST_CATEGORY_LEVEL + " logger was not in the returned list", list.containsKey(TEST_CATEGORY_LEVEL));
+ assertTrue(TEST_LOGGER_LEVEL + " logger was not in the returned list", list.containsKey(TEST_LOGGER_LEVEL));
+
+ //check that their level is as expected
+ assertTrue(TEST_CATEGORY_PRIORITY + " logger's level was incorrect", list.get(TEST_CATEGORY_PRIORITY).equalsIgnoreCase("info"));
+ assertTrue(TEST_CATEGORY_LEVEL + " logger's level was incorrect", list.get(TEST_CATEGORY_LEVEL).equalsIgnoreCase("warn"));
+ assertTrue(TEST_LOGGER_LEVEL + " logger's level was incorrect", list.get(TEST_LOGGER_LEVEL).equalsIgnoreCase("error"));
+
+ //increase their levels a notch to test the 3 different types of logger definition are successfully updated
+ //change the category+priority to warn
+ assertTrue("failed to set new level", lm.setConfigFileLoggerLevel(TEST_CATEGORY_PRIORITY, "warn"));
+ //change the category+level to error
+ assertTrue("failed to set new level", lm.setConfigFileLoggerLevel(TEST_CATEGORY_LEVEL, "error"));
+ //change the logger+level to trace
+ assertTrue("failed to set new level", lm.setConfigFileLoggerLevel(TEST_LOGGER_LEVEL, "trace"));
+
+ //try an invalid level
+ assertFalse("Use of an invalid logger level was successfull", lm.setConfigFileLoggerLevel(TEST_LOGGER_LEVEL, "made.up.level"));
+
+ //try an invalid logger name
+ assertFalse("Use of an invalid logger name was successfull", lm.setConfigFileLoggerLevel("made.up.logger.name", "info"));
+
+ //retrieve the new values from the file and check them
+ levels = (TabularDataSupport) lm.viewConfigFileLoggerLevels();
+ records = levels.values();
+ list = new HashMap<String,String>();
+ for (Object o : records)
+ {
+ CompositeData data = (CompositeData) o;
+ list.put(data.get(LOGGER_NAME).toString(), data.get(LOGGER_LEVEL).toString());
+ }
+
+ //check the 3 different types of logger definition are successfully retrieved after update
+ assertTrue("Wrong number of items in returned list", list.size() == 3);
+ assertTrue(TEST_CATEGORY_PRIORITY + " logger was not in the returned list", list.containsKey(TEST_CATEGORY_PRIORITY));
+ assertTrue(TEST_CATEGORY_LEVEL + " logger was not in the returned list", list.containsKey(TEST_CATEGORY_LEVEL));
+ assertTrue(TEST_LOGGER_LEVEL + " logger was not in the returned list", list.containsKey(TEST_LOGGER_LEVEL));
+
+ //check that their level is as expected after the changes
+ assertTrue(TEST_CATEGORY_PRIORITY + " logger's level was incorrect", list.get(TEST_CATEGORY_PRIORITY).equalsIgnoreCase("warn"));
+ assertTrue(TEST_CATEGORY_LEVEL + " logger's level was incorrect", list.get(TEST_CATEGORY_LEVEL).equalsIgnoreCase("error"));
+ assertTrue(TEST_LOGGER_LEVEL + " logger's level was incorrect", list.get(TEST_LOGGER_LEVEL).equalsIgnoreCase("trace"));
+ }
+
+ public void testGetAndSetConfigFileRootLoggerLevel() throws Exception
+ {
+ LoggingManagementMBean lm =null;
+ try
+ {
+ lm = new LoggingManagementMBean(_testConfigFile.getAbsolutePath(), 0);
+ }
+ catch (JMException e)
+ {
+ fail("Could not create test LoggingManagementMBean");
+ }
+
+ //retrieve the current value
+ String level = lm.getConfigFileRootLoggerLevel();
+
+ //check the value was successfully retrieved before update
+ assertTrue("Retrieved RootLogger level was incorrect", level.equalsIgnoreCase("info"));
+
+ //try an invalid level
+ assertFalse("Use of an invalid RootLogger level was successfull", lm.setConfigFileRootLoggerLevel("made.up.level"));
+
+ //change the level to warn
+ assertTrue("Failed to set new RootLogger level", lm.setConfigFileRootLoggerLevel("warn"));
+
+ //retrieve the current value
+ level = lm.getConfigFileRootLoggerLevel();
+
+ //check the value was successfully retrieved after update
+ assertTrue("Retrieved RootLogger level was incorrect", level.equalsIgnoreCase("warn"));
+ }
+
+ public void testGetLog4jLogWatchInterval()
+ {
+ LoggingManagementMBean lm =null;
+ try
+ {
+ lm = new LoggingManagementMBean(_testConfigFile.getAbsolutePath(), 5000);
+ }
+ catch (JMException e)
+ {
+ fail("Could not create test LoggingManagementMBean");
+ }
+
+ assertTrue("Wrong value returned for logWatch period", lm.getLog4jLogWatchInterval() == 5000);
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/AbstractTestMessages.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/AbstractTestMessages.java
new file mode 100644
index 0000000000..e253881d09
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/AbstractTestMessages.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.logging.messages;
+
+import java.util.List;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.PropertiesConfiguration;
+import org.apache.qpid.server.logging.LogActor;
+import org.apache.qpid.server.logging.LogMessage;
+import org.apache.qpid.server.logging.LogSubject;
+import org.apache.qpid.server.logging.UnitTestMessageLogger;
+import org.apache.qpid.server.logging.actors.TestLogActor;
+import org.apache.qpid.server.logging.subjects.TestBlankSubject;
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+
+public abstract class AbstractTestMessages extends InternalBrokerBaseCase
+{
+ protected Configuration _config = new PropertiesConfiguration();
+ protected LogMessage _logMessage = null;
+ protected LogActor _actor;
+ protected UnitTestMessageLogger _logger;
+ protected LogSubject _logSubject = new TestBlankSubject();
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ _logger = new UnitTestMessageLogger();
+
+ _actor = new TestLogActor(_logger);
+ }
+
+ protected List<Object> performLog()
+ {
+ if (_logMessage == null)
+ {
+ throw new NullPointerException("LogMessage has not been set");
+ }
+
+ _actor.message(_logSubject, _logMessage);
+
+ return _logger.getLogMessages();
+ }
+
+ /**
+ * Validate that only a single log messasge occured and that the message
+ * section starts with the specified tag
+ *
+ * @param logs the logs generated during test run
+ * @param tag the tag to check for
+ * @param expected the expected log messages
+ *
+ */
+ protected void validateLogMessage(List<Object> logs, String tag, String[] expected)
+ {
+ assertEquals("Log has incorrect message count", 1, logs.size());
+
+ //We trim() here as we don't care about extra white space at the end of the log message
+ // but we do care about the ability to easily check we don't have unexpected text at
+ // the end.
+ String log = String.valueOf(logs.get(0)).trim();
+
+ // Simple switch to print out all the logged messages
+ //System.err.println(log);
+
+ int msgIndex = log.indexOf(_logSubject.toLogString())+_logSubject.toLogString().length();
+
+ assertTrue("Unable to locate Subject:" + log, msgIndex != -1);
+
+ String message = log.substring(msgIndex);
+
+ assertTrue("Message does not start with tag:" + tag + ":" + message,
+ message.startsWith(tag));
+
+ // Test that the expected items occur in order.
+ int index = 0;
+ for (String text : expected)
+ {
+ index = message.indexOf(text, index);
+ assertTrue("Message does not contain expected (" + text + ") text :" + message, index != -1);
+ index = index + text.length();
+ }
+
+ //Check there is nothing left on the log message
+ assertEquals("Message has more text. '" + log.substring(msgIndex + index) + "'",
+ log.length(), msgIndex + index);
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/BindingMessagesTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/BindingMessagesTest.java
new file mode 100644
index 0000000000..22de8349c6
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/BindingMessagesTest.java
@@ -0,0 +1,64 @@
+/*
+ *
+ * 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.logging.messages;
+
+import java.util.List;
+
+/**
+ * Test BND Log Messages
+ */
+public class BindingMessagesTest extends AbstractTestMessages
+{
+
+ public void testBindCreate_NoArgs()
+ {
+ _logMessage = BindingMessages.CREATED(null, false);
+ List<Object> log = performLog();
+
+ String[] expected = {"Create"};
+
+ validateLogMessage(log, "BND-1001", expected);
+ }
+
+ public void testBindCreate_Args()
+ {
+ String arguments = "arguments";
+
+ _logMessage = BindingMessages.CREATED(arguments, true);
+ List<Object> log = performLog();
+
+ String[] expected = {"Create", ": Arguments :", arguments};
+
+ validateLogMessage(log, "BND-1001", expected);
+ }
+
+ public void testBindDelete()
+ {
+ _logMessage = BindingMessages.DELETED();
+
+ List<Object> log = performLog();
+
+ String[] expected = {"Deleted"};
+
+ validateLogMessage(log, "BND-1002", expected);
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/BrokerMessagesTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/BrokerMessagesTest.java
new file mode 100644
index 0000000000..a3d46f5716
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/BrokerMessagesTest.java
@@ -0,0 +1,116 @@
+/*
+ *
+ * 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.logging.messages;
+
+import java.util.List;
+
+/**
+ * Test BRK log Messages
+ */
+public class BrokerMessagesTest extends AbstractTestMessages
+{
+ public void testBrokerStartup()
+ {
+ String version = "Qpid 0.6";
+ String build = "796936M";
+
+ _logMessage = BrokerMessages.STARTUP(version, build);
+ List<Object> log = performLog();
+
+ String[] expected = {"Startup :", "Version:", version, "Build:", build};
+
+ validateLogMessage(log, "BRK-1001", expected);
+ }
+
+ public void testBrokerListening()
+ {
+ String transport = "TCP";
+ Integer port = 2765;
+
+ _logMessage = BrokerMessages.LISTENING(transport, port);
+
+ List<Object> log = performLog();
+
+ String[] expected = {"Starting", "Listening on ",
+ transport, "port ", String.valueOf(port)};
+
+ validateLogMessage(log, "BRK-1002", expected);
+ }
+
+ public void testBrokerShuttingDown()
+ {
+ String transport = "TCP";
+ Integer port = 2765;
+
+ _logMessage = BrokerMessages.SHUTTING_DOWN(transport, port);
+
+ List<Object> log = performLog();
+
+ String[] expected = {"Shuting down", transport, "port ", String.valueOf(port)};
+
+ validateLogMessage(log, "BRK-1003", expected);
+ }
+
+ public void testBrokerReady()
+ {
+ _logMessage = BrokerMessages.READY();
+ List<Object> log = performLog();
+
+ String[] expected = {"Ready"};
+
+ validateLogMessage(log, "BRK-1004", expected);
+ }
+
+ public void testBrokerStopped()
+ {
+ _logMessage = BrokerMessages.STOPPED();
+ List<Object> log = performLog();
+
+ String[] expected = {"Stopped"};
+
+ validateLogMessage(log, "BRK-1005", expected);
+ }
+
+ public void testBrokerConfig()
+ {
+ String path = "/file/path/to/configuration.xml";
+
+ _logMessage = BrokerMessages.CONFIG(path);
+ List<Object> log = performLog();
+
+ String[] expected = {"Using configuration :", path};
+
+ validateLogMessage(log, "BRK-1006", expected);
+ }
+
+ public void testBrokerLogConfig()
+ {
+ String path = "/file/path/to/configuration.xml";
+
+ _logMessage = BrokerMessages.LOG_CONFIG(path);
+ List<Object> log = performLog();
+
+ String[] expected = {"Using logging configuration :", path};
+
+ validateLogMessage(log, "BRK-1007", expected);
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/ChannelMessagesTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/ChannelMessagesTest.java
new file mode 100644
index 0000000000..e94b79ba95
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/ChannelMessagesTest.java
@@ -0,0 +1,64 @@
+/*
+ *
+ * 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.logging.messages;
+
+import java.util.List;
+
+/**
+ * Test CHN Log Messges
+ */
+public class ChannelMessagesTest extends AbstractTestMessages
+{
+ public void testChannelCreate()
+ {
+ _logMessage = ChannelMessages.CREATE();
+ List<Object> log = performLog();
+
+ // We use the MessageFormat here as that is what the ChannelMessage
+ // will do, this makes the resulting value 12,345
+ String[] expected = {"Create"};
+
+ validateLogMessage(log, "CHN-1001", expected);
+ }
+
+ public void testChannelFlow()
+ {
+ String flow = "ON";
+
+ _logMessage = ChannelMessages.FLOW(flow);
+ List<Object> log = performLog();
+
+ String[] expected = {"Flow", flow};
+
+ validateLogMessage(log, "CHN-1002", expected);
+ }
+
+ public void testChannelClose()
+ {
+ _logMessage = ChannelMessages.CLOSE();
+ List<Object> log = performLog();
+
+ String[] expected = {"Close"};
+
+ validateLogMessage(log, "CHN-1003", expected);
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/ConnectionMessagesTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/ConnectionMessagesTest.java
new file mode 100644
index 0000000000..24fccf8446
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/ConnectionMessagesTest.java
@@ -0,0 +1,90 @@
+/*
+ *
+ * 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.logging.messages;
+
+import java.util.List;
+
+/**
+ * Test CON Log Messages
+ */
+public class ConnectionMessagesTest extends AbstractTestMessages
+{
+ public void testConnectionOpen_WithClientIDProtocolVersion()
+ {
+ String clientID = "client";
+ String protocolVersion = "8-0";
+
+ _logMessage = ConnectionMessages.OPEN(clientID, protocolVersion, true , true);
+ List<Object> log = performLog();
+
+ String[] expected = {"Open :", "Client ID", clientID,
+ ": Protocol Version :", protocolVersion};
+
+ validateLogMessage(log, "CON-1001", expected);
+ }
+
+ public void testConnectionOpen_WithClientIDNoProtocolVersion()
+ {
+ String clientID = "client";
+
+ _logMessage = ConnectionMessages.OPEN(clientID, null,true, false);
+ List<Object> log = performLog();
+
+ String[] expected = {"Open :", "Client ID", clientID};
+
+ validateLogMessage(log, "CON-1001", expected);
+ }
+
+ public void testConnectionOpen_WithNOClientIDProtocolVersion()
+ {
+ String protocolVersion = "8-0";
+
+ _logMessage = ConnectionMessages.OPEN(null, protocolVersion, false , true);
+ List<Object> log = performLog();
+
+ String[] expected = {"Open", ": Protocol Version :", protocolVersion};
+
+ validateLogMessage(log, "CON-1001", expected);
+ }
+
+ public void testConnectionOpen_WithNoClientIDNoProtocolVersion()
+ {
+ _logMessage = ConnectionMessages.OPEN(null, null,false, false);
+ List<Object> log = performLog();
+
+ String[] expected = {"Open"};
+
+ validateLogMessage(log, "CON-1001", expected);
+ }
+
+
+
+ public void testConnectionClose()
+ {
+ _logMessage = ConnectionMessages.CLOSE();
+ List<Object> log = performLog();
+
+ String[] expected = {"Close"};
+
+ validateLogMessage(log, "CON-1002", expected);
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/ExchangeMessagesTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/ExchangeMessagesTest.java
new file mode 100644
index 0000000000..728a98e009
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/ExchangeMessagesTest.java
@@ -0,0 +1,80 @@
+/*
+ *
+ * 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.logging.messages;
+
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+
+import java.util.List;
+
+/**
+ * Test EXH Log Messages
+ */
+public class ExchangeMessagesTest extends AbstractTestMessages
+{
+ public void testExchangeCreated_Transient()
+ {
+ // Get the Default Exchange on the Test Vhost for testing
+ Exchange exchange = ApplicationRegistry.getInstance().
+ getVirtualHostRegistry().getVirtualHost("test").
+ getExchangeRegistry().getDefaultExchange();
+
+ String type = exchange.getTypeShortString().toString();
+ String name = exchange.getNameShortString().toString();
+
+ _logMessage = ExchangeMessages.CREATED(type, name, false);
+ List<Object> log = performLog();
+
+ String[] expected = {"Create :", "Type:", type, "Name:", name};
+
+ validateLogMessage(log, "EXH-1001", expected);
+ }
+
+ public void testExchangeCreated_Persistent()
+ {
+ // Get the Default Exchange on the Test Vhost for testing
+ Exchange exchange = ApplicationRegistry.getInstance().
+ getVirtualHostRegistry().getVirtualHost("test").
+ getExchangeRegistry().getDefaultExchange();
+
+ String type = exchange.getTypeShortString().toString();
+ String name = exchange.getNameShortString().toString();
+
+ _logMessage = ExchangeMessages.CREATED(type, name, true);
+ List<Object> log = performLog();
+
+ String[] expected = {"Create :", "Durable", "Type:", type, "Name:", name};
+
+ validateLogMessage(log, "EXH-1001", expected);
+ }
+
+
+ public void testExchangeDeleted()
+ {
+ _logMessage = ExchangeMessages.DELETED();
+ List<Object> log = performLog();
+
+ String[] expected = {"Deleted"};
+
+ validateLogMessage(log, "EXH-1002", expected);
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/ManagementConsoleMessagesTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/ManagementConsoleMessagesTest.java
new file mode 100644
index 0000000000..4bfbae44ac
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/ManagementConsoleMessagesTest.java
@@ -0,0 +1,107 @@
+/*
+ *
+ * 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.logging.messages;
+
+import java.util.List;
+
+/**
+ * Test MNG Log Messages
+ */
+public class ManagementConsoleMessagesTest extends AbstractTestMessages
+{
+ public void testManagementStartup()
+ {
+ _logMessage = ManagementConsoleMessages.STARTUP();
+ List<Object> log = performLog();
+
+ String[] expected = {"Startup"};
+
+ validateLogMessage(log, "MNG-1001", expected);
+ }
+
+ public void testManagementListening()
+ {
+ String transport = "JMX";
+ Integer port = 8889;
+
+ _logMessage = ManagementConsoleMessages.LISTENING(transport, port);
+ List<Object> log = performLog();
+
+ String[] expected = {"Starting :", transport, ": Listening on port", String.valueOf(port)};
+
+ validateLogMessage(log, "MNG-1002", expected);
+ }
+
+ public void testManagementShuttingDown()
+ {
+ String transport = "JMX";
+ Integer port = 8889;
+
+ _logMessage = ManagementConsoleMessages.SHUTTING_DOWN(transport, port);
+ List<Object> log = performLog();
+
+ String[] expected = {"Shutting down :", transport, ": port", String.valueOf(port)};
+
+ validateLogMessage(log, "MNG-1003", expected);
+ }
+
+ public void testManagementReady()
+ {
+ _logMessage = ManagementConsoleMessages.READY(false);
+ List<Object> log = performLog();
+
+ String[] expected = {"Ready"};
+
+ validateLogMessage(log, "MNG-1004", expected);
+
+ _logger.clearLogMessages();
+
+ _logMessage = ManagementConsoleMessages.READY(true);
+ log = performLog();
+
+ expected = new String[]{"Ready : Using the platform JMX Agent"};
+
+ validateLogMessage(log, "MNG-1004", expected);
+ }
+
+ public void testManagementStopped()
+ {
+ _logMessage = ManagementConsoleMessages.STOPPED();
+ List<Object> log = performLog();
+
+ String[] expected = {"Stopped"};
+
+ validateLogMessage(log, "MNG-1005", expected);
+ }
+
+ public void testManagementSSLKeyStore()
+ {
+ String path = "/path/to/the/keystore/files.jks";
+
+ _logMessage = ManagementConsoleMessages.SSL_KEYSTORE(path);
+ List<Object> log = performLog();
+
+ String[] expected = {"Using SSL Keystore :", path};
+
+ validateLogMessage(log, "MNG-1006", expected);
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/MessageStoreMessagesTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/MessageStoreMessagesTest.java
new file mode 100644
index 0000000000..cc032a0430
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/MessageStoreMessagesTest.java
@@ -0,0 +1,125 @@
+/*
+ *
+ * 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.logging.messages;
+
+import java.util.List;
+
+/**
+ * Test MST Log Messages
+ */
+public class MessageStoreMessagesTest extends AbstractTestMessages
+{
+ public void testMessageStoreCreated()
+ {
+ String name = "DerbyMessageStore";
+
+ _logMessage = MessageStoreMessages.CREATED(name);
+ List<Object> log = performLog();
+
+ String[] expected = {"Created :", name};
+
+ validateLogMessage(log, "MST-1001", expected);
+ }
+
+ public void testMessageStoreStoreLocation()
+ {
+ String location = "/path/to/the/message/store.files";
+
+ _logMessage = MessageStoreMessages.STORE_LOCATION(location);
+ List<Object> log = performLog();
+
+ String[] expected = {"Store location :", location};
+
+ validateLogMessage(log, "MST-1002", expected);
+ }
+
+ public void testMessageStoreClosed()
+ {
+ _logMessage = MessageStoreMessages.CLOSED();
+ List<Object> log = performLog();
+
+ String[] expected = {"Closed"};
+
+ validateLogMessage(log, "MST-1003", expected);
+ }
+
+ public void testMessageStoreRecoveryStart()
+ {
+ _logMessage = MessageStoreMessages.RECOVERY_START();
+ List<Object> log = performLog();
+
+ String[] expected = {"Recovery Start"};
+
+ validateLogMessage(log, "MST-1004", expected);
+ }
+/*
+ public void testMessageStoreRecoveryStart_withQueue()
+ {
+ String queueName = "testQueue";
+
+ _logMessage = MessageStoreMessages.RECOVERY_START(queueName, true);
+ List<Object> log = performLog();
+
+ String[] expected = {"Recovery Start :", queueName};
+
+ validateLogMessage(log, "MST-1004", expected);
+ }
+
+ public void testMessageStoreRecovered()
+ {
+ String queueName = "testQueue";
+ Integer messasgeCount = 2000;
+
+ _logMessage = MessageStoreMessages.MST_RECOVERED(messasgeCount, queueName);
+ List<Object> log = performLog();
+
+ // Here we use MessageFormat to ensure the messasgeCount of 2000 is
+ // reformated for display as '2,000'
+ String[] expected = {"Recovered ",
+ MessageFormat.format("{0,number}", messasgeCount),
+ "messages for queue", queueName};
+
+ validateLogMessage(log, "MST-1005", expected);
+ }
+
+ public void testMessageStoreRecoveryComplete()
+ {
+ _logMessage = MessageStoreMessages.MST_RECOVERY_COMPLETE(null,false);
+ List<Object> log = performLog();
+
+ String[] expected = {"Recovery Complete"};
+
+ validateLogMessage(log, "MST-1006", expected);
+ }
+
+ public void testMessageStoreRecoveryComplete_withQueue()
+ {
+ String queueName = "testQueue";
+
+ _logMessage = MessageStoreMessages.MST_RECOVERY_COMPLETE(queueName, true);
+ List<Object> log = performLog();
+
+ String[] expected = {"Recovery Complete :", queueName};
+
+ validateLogMessage(log, "MST-1006", expected);
+ }
+ */
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/QueueMessagesTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/QueueMessagesTest.java
new file mode 100644
index 0000000000..d51e6a6bb7
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/QueueMessagesTest.java
@@ -0,0 +1,239 @@
+/*
+ *
+ * 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.logging.messages;
+
+import java.util.List;
+
+/**
+ * Test QUE Log Messages
+ */
+public class QueueMessagesTest extends AbstractTestMessages
+{
+ public void testQueueCreatedALL()
+ {
+ String owner = "guest";
+ Integer priority = 3;
+
+ _logMessage = QueueMessages.CREATED(owner, priority, true, true, true, true, true);
+ List<Object> log = performLog();
+
+ String[] expected = {"Create :", "Owner:", owner, "AutoDelete",
+ "Durable", "Transient", "Priority:",
+ String.valueOf(priority)};
+
+ validateLogMessage(log, "QUE-1001", expected);
+ }
+
+ public void testQueueCreatedOwnerAutoDelete()
+ {
+ String owner = "guest";
+
+ _logMessage = QueueMessages.CREATED(owner, null, true, true, false, false, false);
+ List<Object> log = performLog();
+
+ String[] expected = {"Create :", "Owner:", owner, "AutoDelete"};
+
+ validateLogMessage(log, "QUE-1001", expected);
+ }
+
+ public void testQueueCreatedOwnerPriority()
+ {
+ String owner = "guest";
+ Integer priority = 3;
+
+ _logMessage = QueueMessages.CREATED(owner, priority, true, false, false, false, true);
+ List<Object> log = performLog();
+
+ String[] expected = {"Create :", "Owner:", owner, "Priority:",
+ String.valueOf(priority)};
+
+ validateLogMessage(log, "QUE-1001", expected);
+ }
+
+ public void testQueueCreatedOwnerAutoDeletePriority()
+ {
+ String owner = "guest";
+ Integer priority = 3;
+
+ _logMessage = QueueMessages.CREATED(owner, priority, true, true, false, false, true);
+ List<Object> log = performLog();
+
+ String[] expected = {"Create :", "Owner:", owner, "AutoDelete",
+ "Priority:",
+ String.valueOf(priority)};
+
+ validateLogMessage(log, "QUE-1001", expected);
+ }
+
+ public void testQueueCreatedOwnerAutoDeleteTransient()
+ {
+ String owner = "guest";
+
+ _logMessage = QueueMessages.CREATED(owner, null, true, true, false, true, false);
+ List<Object> log = performLog();
+
+ String[] expected = {"Create :", "Owner:", owner, "AutoDelete",
+ "Transient"};
+
+ validateLogMessage(log, "QUE-1001", expected);
+ }
+
+ public void testQueueCreatedOwnerAutoDeleteTransientPriority()
+ {
+ String owner = "guest";
+ Integer priority = 3;
+
+ _logMessage = QueueMessages.CREATED(owner, priority, true, true, false, true, true);
+ List<Object> log = performLog();
+
+ String[] expected = {"Create :", "Owner:", owner, "AutoDelete",
+ "Transient", "Priority:",
+ String.valueOf(priority)};
+
+ validateLogMessage(log, "QUE-1001", expected);
+ }
+
+ public void testQueueCreatedOwnerAutoDeleteDurable()
+ {
+ String owner = "guest";
+
+ _logMessage = QueueMessages.CREATED(owner, null, true, true, true, false, false);
+ List<Object> log = performLog();
+
+ String[] expected = {"Create :", "Owner:", owner, "AutoDelete",
+ "Durable"};
+
+ validateLogMessage(log, "QUE-1001", expected);
+ }
+
+ public void testQueueCreatedOwnerAutoDeleteDurablePriority()
+ {
+ String owner = "guest";
+ Integer priority = 3;
+
+ _logMessage = QueueMessages.CREATED(owner, priority, true, true, true, false, true);
+ List<Object> log = performLog();
+
+ String[] expected = {"Create :", "Owner:", owner, "AutoDelete",
+ "Durable", "Priority:",
+ String.valueOf(priority)};
+
+ validateLogMessage(log, "QUE-1001", expected);
+ }
+
+ public void testQueueCreatedAutoDelete()
+ {
+ _logMessage = QueueMessages.CREATED(null, null, false, true, false, false, false);
+ List<Object> log = performLog();
+
+ String[] expected = {"Create :", "AutoDelete"};
+
+ validateLogMessage(log, "QUE-1001", expected);
+ }
+
+ public void testQueueCreatedPriority()
+ {
+ Integer priority = 3;
+
+ _logMessage = QueueMessages.CREATED(null, priority, false, false, false, false, true);
+ List<Object> log = performLog();
+
+ String[] expected = {"Create :", "Priority:",
+ String.valueOf(priority)};
+
+ validateLogMessage(log, "QUE-1001", expected);
+ }
+
+ public void testQueueCreatedAutoDeletePriority()
+ {
+ Integer priority = 3;
+
+ _logMessage = QueueMessages.CREATED(null, priority, false, true, false, false, true);
+ List<Object> log = performLog();
+
+ String[] expected = {"Create :", "AutoDelete",
+ "Priority:",
+ String.valueOf(priority)};
+
+ validateLogMessage(log, "QUE-1001", expected);
+ }
+
+ public void testQueueCreatedAutoDeleteTransient()
+ {
+ _logMessage = QueueMessages.CREATED(null, null, false, true, false, true, false);
+ List<Object> log = performLog();
+
+ String[] expected = {"Create :", "AutoDelete",
+ "Transient"};
+
+ validateLogMessage(log, "QUE-1001", expected);
+ }
+
+ public void testQueueCreatedAutoDeleteTransientPriority()
+ {
+ Integer priority = 3;
+
+ _logMessage = QueueMessages.CREATED(null, priority, false, true, false, true, true);
+ List<Object> log = performLog();
+
+ String[] expected = {"Create :", "AutoDelete",
+ "Transient", "Priority:",
+ String.valueOf(priority)};
+
+ validateLogMessage(log, "QUE-1001", expected);
+ }
+
+ public void testQueueCreatedAutoDeleteDurable()
+ {
+ _logMessage = QueueMessages.CREATED(null, null, false, true, true, false, false);
+ List<Object> log = performLog();
+
+ String[] expected = {"Create :", "AutoDelete",
+ "Durable"};
+
+ validateLogMessage(log, "QUE-1001", expected);
+ }
+
+ public void testQueueCreatedAutoDeleteDurablePriority()
+ {
+ Integer priority = 3;
+
+ _logMessage = QueueMessages.CREATED(null, priority, false, true, true, false, true);
+ List<Object> log = performLog();
+
+ String[] expected = {"Create :", "AutoDelete",
+ "Durable", "Priority:",
+ String.valueOf(priority)};
+
+ validateLogMessage(log, "QUE-1001", expected);
+ }
+
+ public void testQueueDeleted()
+ {
+ _logMessage = QueueMessages.DELETED();
+ List<Object> log = performLog();
+
+ String[] expected = {"Deleted"};
+
+ validateLogMessage(log, "QUE-1002", expected);
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/SubscriptionMessagesTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/SubscriptionMessagesTest.java
new file mode 100644
index 0000000000..b2bc351f8f
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/SubscriptionMessagesTest.java
@@ -0,0 +1,86 @@
+/*
+ *
+ * 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.logging.messages;
+
+import java.util.List;
+
+/**
+ * Test SUB Log Messages
+ */
+public class SubscriptionMessagesTest extends AbstractTestMessages
+{
+ public void testSubscriptionCreateALL()
+ {
+ String arguments = "arguments";
+
+ _logMessage = SubscriptionMessages.CREATE(arguments, true, true);
+ List<Object> log = performLog();
+
+ String[] expected = {"Create :", "Durable", "Arguments :", arguments};
+
+ validateLogMessage(log, "SUB-1001", expected);
+ }
+
+ public void testSubscriptionCreateDurable()
+ {
+ _logMessage = SubscriptionMessages.CREATE(null, true, false);
+ List<Object> log = performLog();
+
+ String[] expected = {"Create :", "Durable"};
+
+ validateLogMessage(log, "SUB-1001", expected);
+ }
+
+ public void testSubscriptionCreateArguments()
+ {
+ String arguments = "arguments";
+
+ _logMessage = SubscriptionMessages.CREATE(arguments, false, true);
+ List<Object> log = performLog();
+
+ String[] expected = {"Create :","Arguments :", arguments};
+
+ validateLogMessage(log, "SUB-1001", expected);
+ }
+
+
+ public void testSubscriptionClose()
+ {
+ _logMessage = SubscriptionMessages.CLOSE();
+ List<Object> log = performLog();
+
+ String[] expected = {"Close"};
+
+ validateLogMessage(log, "SUB-1002", expected);
+ }
+
+ public void testSubscriptionState()
+ {
+ String state = "ACTIVE";
+
+ _logMessage = SubscriptionMessages.STATE(state);
+ List<Object> log = performLog();
+
+ String[] expected = {"State :", state};
+
+ validateLogMessage(log, "SUB-1003", expected);
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/VirtualHostMessagesTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/VirtualHostMessagesTest.java
new file mode 100644
index 0000000000..17d68ef7c3
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/VirtualHostMessagesTest.java
@@ -0,0 +1,51 @@
+/*
+ *
+ * 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.logging.messages;
+
+import java.util.List;
+
+/**
+ * Test VHT Log Messages
+ */
+public class VirtualHostMessagesTest extends AbstractTestMessages
+{
+ public void testVirtualhostCreated()
+ {
+ String name = "test";
+ _logMessage = VirtualHostMessages.CREATED(name);
+ List<Object> log = performLog();
+
+ String[] expected = {"Created :", name};
+
+ validateLogMessage(log, "VHT-1001", expected);
+ }
+
+ public void testSubscriptionClosed()
+ {
+ _logMessage = VirtualHostMessages.CLOSED();
+ List<Object> log = performLog();
+
+ String[] expected = {"Closed"};
+
+ validateLogMessage(log, "VHT-1002", expected);
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/AbstractTestLogSubject.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/AbstractTestLogSubject.java
new file mode 100644
index 0000000000..1cd8d55b0d
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/AbstractTestLogSubject.java
@@ -0,0 +1,288 @@
+/*
+ *
+ * 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.logging.subjects;
+
+import junit.framework.TestCase;
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.PropertiesConfiguration;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.configuration.ServerConfiguration;
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.logging.LogActor;
+import org.apache.qpid.server.logging.LogMessage;
+import org.apache.qpid.server.logging.LogSubject;
+import org.apache.qpid.server.logging.RootMessageLogger;
+import org.apache.qpid.server.logging.AbstractRootMessageLogger;
+import org.apache.qpid.server.logging.UnitTestMessageLogger;
+import org.apache.qpid.server.logging.actors.TestLogActor;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.protocol.InternalTestProtocolSession;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+
+import java.util.List;
+
+/**
+ * Abstract Test for LogSubject testing
+ * Includes common validation code and two common tests.
+ *
+ * Each test class sets up the LogSubject and contains details of how to
+ * validate this class then performs a log statement with logging enabled and
+ * logging disabled.
+ *
+ * The resulting log file is then validated.
+ *
+ */
+public abstract class AbstractTestLogSubject extends InternalBrokerBaseCase
+{
+ protected Configuration _config = new PropertiesConfiguration();
+ protected LogSubject _subject = null;
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ _config.setProperty(ServerConfiguration.STATUS_UPDATES, "ON");
+ }
+
+
+ protected List<Object> performLog() throws ConfigurationException
+ {
+ if (_subject == null)
+ {
+ throw new NullPointerException("LogSubject has not been set");
+ }
+
+ ServerConfiguration serverConfig = new ServerConfiguration(_config);
+ UnitTestMessageLogger logger = new UnitTestMessageLogger(serverConfig);
+
+ LogActor actor = new TestLogActor(logger);
+
+ actor.message(_subject, new LogMessage()
+ {
+ public String toString()
+ {
+ return "<Log Message>";
+ }
+
+ public String getLogHierarchy()
+ {
+ return "test.hierarchy";
+ }
+ });
+
+ return logger.getLogMessages();
+ }
+
+ /**
+ * Verify that the connection section has the expected items
+ *
+ * @param connectionID - The connection id (int) to check for
+ * @param user - the Connected username
+ * @param ipString - the ipString/hostname
+ * @param vhost - the virtualhost that the user connected to.
+ * @param message - the message these values should appear in.
+ */
+ protected void verifyConnection(long connectionID, String user, String ipString, String vhost, String message)
+ {
+ // This should return us MockProtocolSessionUser@null/test
+ String connectionSlice = getSlice("con:" + connectionID, message);
+
+ assertNotNull("Unable to find connection 'con:" + connectionID + "'",
+ connectionSlice);
+
+ // Exract the userName
+ String[] userNameParts = connectionSlice.split("@");
+
+ assertEquals("Unable to split Username from rest of Connection:"
+ + connectionSlice, 2, userNameParts.length);
+
+ assertEquals("Username not as expected", userNameParts[0], user);
+
+ // Extract IP.
+ // The connection will be of the format - guest@/127.0.0.1:1/test
+ // and so our userNamePart will be '/127.0.0.1:1/test'
+ String[] ipParts = userNameParts[1].split("/");
+
+ // We will have three sections
+ assertEquals("Unable to split IP from rest of Connection:"
+ + userNameParts[1], 3, ipParts.length);
+
+ // We need to skip the first '/' split will be empty so validate 1 as IP
+ assertEquals("IP not as expected", ipString, ipParts[1]);
+
+ //Finally check vhost which is section 2
+ assertEquals("Virtualhost name not as expected.", vhost, ipParts[2]);
+ }
+
+ /**
+ * Verify that the RoutingKey is present in the provided message.
+ *
+ * @param message The message to check
+ * @param routingKey The routing key to check against
+ */
+ protected void verifyRoutingKey(String message, AMQShortString routingKey)
+ {
+ String routingKeySlice = getSlice("rk", message);
+
+ assertNotNull("Routing Key not found:" + message, routingKey);
+
+ assertEquals("Routing key not correct",
+ routingKey.toString(), routingKeySlice);
+ }
+
+ /**
+ * Verify that the given Queue's name exists in the provided message
+ *
+ * @param message The message to check
+ * @param queue The queue to check against
+ */
+ protected void verifyQueue(String message, AMQQueue queue)
+ {
+ String queueSlice = getSlice("qu", message);
+
+ assertNotNull("Queue not found:" + message, queueSlice);
+
+ assertEquals("Queue name not correct",
+ queue.getNameShortString().toString(), queueSlice);
+ }
+
+ /**
+ * Verify that the given exchange (name and type) are present in the
+ * provided message.
+ *
+ * @param message The message to check
+ * @param exchange the exchange to check against
+ */
+ protected void verifyExchange(String message, Exchange exchange)
+ {
+ String exchangeSilce = getSlice("ex", message);
+
+ assertNotNull("Exchange not found:" + message, exchangeSilce);
+
+ String[] exchangeParts = exchangeSilce.split("/");
+
+ assertEquals("Exchange should be in two parts ex(type/name)", 2,
+ exchangeParts.length);
+
+ assertEquals("Exchange type not correct",
+ exchange.getTypeShortString().toString(), exchangeParts[0]);
+
+ assertEquals("Exchange name not correct",
+ exchange.getNameShortString().toString(), exchangeParts[1]);
+
+ }
+
+ /**
+ * Verify that a VirtualHost with the given name appears in the given
+ * message.
+ *
+ * @param message the message to search
+ * @param vhost the vhostName to check against
+ */
+ static public void verifyVirtualHost(String message, VirtualHost vhost)
+ {
+ String vhostSlice = getSlice("vh", message);
+
+ assertNotNull("Virtualhost not found:" + message, vhostSlice);
+
+ assertEquals("Virtualhost not correct", "/" + vhost.getName(), vhostSlice);
+ }
+
+ /**
+ * Parse the log message and return the slice according to the following:
+ * Given Example:
+ * con:1(guest@127.0.0.1/test)/ch:2/ex(amq.direct)/qu(myQueue)/rk(myQueue)
+ *
+ * Each item (except channel) is of the format <key>(<values>)
+ *
+ * So Given an ID to slice on:
+ * con:1 - Connection 1
+ * ex - exchange
+ * qu - queue
+ * rk - routing key
+ *
+ * @param sliceID the slice to locate
+ * @param message the message to search in
+ *
+ * @return the slice if found otherwise null is returned
+ */
+ static public String getSlice(String sliceID, String message)
+ {
+ int indexOfSlice = message.indexOf(sliceID + "(");
+
+ if (indexOfSlice == -1)
+ {
+ return null;
+ }
+
+ int endIndex = message.indexOf(')', indexOfSlice);
+
+ if (endIndex == -1)
+ {
+ return null;
+ }
+
+ return message.substring(indexOfSlice + 1 + sliceID.length(),
+ endIndex);
+ }
+
+ /**
+ * Test that when Logging occurs a single log statement is provided
+ *
+ * @throws ConfigurationException
+ */
+ public void testEnabled() throws ConfigurationException
+ {
+ List<Object> logs = performLog();
+
+ assertEquals("Log has incorrect message count", 1, logs.size());
+
+ validateLogStatement(String.valueOf(logs.get(0)));
+ }
+
+ /**
+ * Call to the individiual tests to validate the message is formatted as
+ * expected
+ *
+ * @param message the message whos format needs validation
+ */
+ protected abstract void validateLogStatement(String message);
+
+ /**
+ * Ensure that when status-updates are off this does not perform logging
+ *
+ * @throws ConfigurationException
+ */
+ public void testDisabled() throws ConfigurationException
+ {
+ _config.setProperty(ServerConfiguration.STATUS_UPDATES, "OFF");
+
+ List<Object> logs = performLog();
+
+ assertEquals("Log has incorrect message count", 0, logs.size());
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/BindingLogSubjectTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/BindingLogSubjectTest.java
new file mode 100644
index 0000000000..e80c4c4679
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/BindingLogSubjectTest.java
@@ -0,0 +1,71 @@
+/*
+ *
+ * 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.logging.subjects;
+
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.MockAMQQueue;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+/**
+ * Validate BindingLogSubjects are logged as expected
+ */
+public class BindingLogSubjectTest extends AbstractTestLogSubject
+{
+
+ private AMQQueue _queue;
+ private AMQShortString _routingKey;
+ private Exchange _exchange;
+ private VirtualHost _testVhost;
+
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ _testVhost = ApplicationRegistry.getInstance().getVirtualHostRegistry().
+ getVirtualHost("test");
+ // Configure items for subjectCreation
+ _routingKey = new AMQShortString("RoutingKey");
+ _exchange = _testVhost.getExchangeRegistry().getDefaultExchange();
+ _queue = new MockAMQQueue("BindingLogSubjectTest");
+ ((MockAMQQueue) _queue).setVirtualHost(_testVhost);
+
+ _subject = new BindingLogSubject(String.valueOf(_routingKey), _exchange, _queue);
+ }
+
+ /**
+ * Validate that the logged Subject message is as expected:
+ * MESSAGE [Blank][vh(/test)/ex(direct/<<default>>)/qu(BindingLogSubjectTest)/rk(RoutingKey)] <Log Message>
+ * @param message the message whos format needs validation
+ */
+ @Override
+ protected void validateLogStatement(String message)
+ {
+ verifyVirtualHost(message, _testVhost);
+ verifyExchange(message, _exchange);
+ verifyQueue(message, _queue);
+ verifyRoutingKey(message, _routingKey);
+ }
+
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/ChannelLogSubjectTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/ChannelLogSubjectTest.java
new file mode 100644
index 0000000000..6bc5effa05
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/ChannelLogSubjectTest.java
@@ -0,0 +1,58 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.logging.subjects;
+
+import org.apache.qpid.server.AMQChannel;
+
+/**
+ * Validate ChannelLogSubjects are logged as expected
+ */
+public class ChannelLogSubjectTest extends ConnectionLogSubjectTest
+{
+ private final int _channelID = 1;
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ AMQChannel channel = new AMQChannel(getSession(), _channelID, getSession().getVirtualHost().getMessageStore());
+
+ _subject = new ChannelLogSubject(channel);
+ }
+
+ /**
+ * MESSAGE [Blank][con:0(MockProtocolSessionUser@null/test)/ch:1] <Log Message>
+ *
+ * @param message the message whos format needs validation
+ */
+ protected void validateLogStatement(String message)
+ {
+ // Use the ConnectionLogSubjectTest to vaildate that the connection
+ // section is ok
+ super.validateLogStatement(message);
+
+ // Finally check that the channel identifier is correctly added
+ assertTrue("Channel 1 identifier not found as part of Subject",
+ message.contains(")/ch:1]"));
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/ConnectionLogSubjectTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/ConnectionLogSubjectTest.java
new file mode 100644
index 0000000000..c246fff2a8
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/ConnectionLogSubjectTest.java
@@ -0,0 +1,46 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.logging.subjects;
+
+/**
+ * Validate ConnectionLogSubjects are logged as expected
+ */
+public class ConnectionLogSubjectTest extends AbstractTestLogSubject
+{
+
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ _subject = new ConnectionLogSubject(getSession());
+ }
+
+ /**
+ * MESSAGE [Blank][con:0(MockProtocolSessionUser@null/test)] <Log Message>
+ *
+ * @param message the message whos format needs validation
+ */
+ protected void validateLogStatement(String message)
+ {
+ verifyConnection(getSession().getSessionID(), "InternalTestProtocolSession", "127.0.0.1:1", "test", message);
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/ExchangeLogSubjectTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/ExchangeLogSubjectTest.java
new file mode 100644
index 0000000000..7e16516fc6
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/ExchangeLogSubjectTest.java
@@ -0,0 +1,58 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.logging.subjects;
+
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+
+/**
+ * Validate ExchangeLogSubjects are logged as expected
+ */
+public class ExchangeLogSubjectTest extends AbstractTestLogSubject
+{
+ Exchange _exchange;
+ VirtualHost _testVhost;
+
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ _testVhost = ApplicationRegistry.getInstance().getVirtualHostRegistry().
+ getVirtualHost("test");
+
+ _exchange = _testVhost.getExchangeRegistry().getDefaultExchange();
+ _subject = new ExchangeLogSubject(_exchange,_testVhost);
+ }
+
+ /**
+ * Validate that the logged Subject message is as expected:
+ * MESSAGE [Blank][vh(/test)/ex(direct/<<default>>)] <Log Message>
+ * @param message the message whos format needs validation
+ */
+ @Override
+ protected void validateLogStatement(String message)
+ {
+ verifyVirtualHost(message, _testVhost);
+ verifyExchange(message, _exchange);
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/MessageStoreLogSubjectTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/MessageStoreLogSubjectTest.java
new file mode 100644
index 0000000000..9c868ea651
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/MessageStoreLogSubjectTest.java
@@ -0,0 +1,62 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.logging.subjects;
+
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+
+/**
+ * Validate MessageStoreLogSubjects are logged as expected
+ */
+public class MessageStoreLogSubjectTest extends AbstractTestLogSubject
+{
+ VirtualHost _testVhost;
+
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ _testVhost = ApplicationRegistry.getInstance().getVirtualHostRegistry().
+ getVirtualHost("test");
+
+ _subject = new MessageStoreLogSubject(_testVhost, _testVhost.getMessageStore());
+ }
+
+ /**
+ * Validate that the logged Subject message is as expected:
+ * MESSAGE [Blank][vh(/test)/ms(MemoryMessageStore)] <Log Message>
+ * @param message the message whos format needs validation
+ */
+ @Override
+ protected void validateLogStatement(String message)
+ {
+ verifyVirtualHost(message, _testVhost);
+
+ String msSlice = getSlice("ms", message);
+
+ assertNotNull("MessageStore not found:" + message, msSlice);
+
+ assertEquals("MessageStore not correct",
+ _testVhost.getMessageStore().getClass().getSimpleName(),
+ msSlice);
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/QueueLogSubjectTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/QueueLogSubjectTest.java
new file mode 100644
index 0000000000..1f432be57a
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/QueueLogSubjectTest.java
@@ -0,0 +1,63 @@
+/*
+ *
+ * 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.logging.subjects;
+
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.MockAMQQueue;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+/**
+ * Validate QueueLogSubjects are logged as expected
+ */
+public class QueueLogSubjectTest extends AbstractTestLogSubject
+{
+
+ private AMQQueue _queue;
+ private VirtualHost _testVhost;
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ _testVhost = ApplicationRegistry.getInstance().getVirtualHostRegistry().
+ getVirtualHost("test");
+
+ _queue = new MockAMQQueue("QueueLogSubjectTest");
+ ((MockAMQQueue) _queue).setVirtualHost(_testVhost);
+
+ _subject = new QueueLogSubject(_queue);
+ }
+
+ /**
+ * Validate that the logged Subject message is as expected:
+ * MESSAGE [Blank][vh(/test)/qu(QueueLogSubjectTest)] <Log Message>
+ *
+ * @param message the message whos format needs validation
+ */
+ @Override
+ protected void validateLogStatement(String message)
+ {
+ verifyVirtualHost(message, _testVhost);
+ verifyQueue(message, _queue);
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/SubscriptionLogSubjectTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/SubscriptionLogSubjectTest.java
new file mode 100644
index 0000000000..0c356e1838
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/SubscriptionLogSubjectTest.java
@@ -0,0 +1,105 @@
+/*
+ *
+ * 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.logging.subjects;
+
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.flow.LimitlessCreditManager;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.MockAMQQueue;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.subscription.Subscription;
+import org.apache.qpid.server.subscription.SubscriptionFactory;
+import org.apache.qpid.server.subscription.SubscriptionFactoryImpl;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+/**
+ * Validate SubscriptionLogSubjects are logged as expected
+ */
+public class SubscriptionLogSubjectTest extends AbstractTestLogSubject
+{
+
+ private AMQQueue _queue;
+ private VirtualHost _testVhost;
+ private int _channelID = 1;
+ private Subscription _subscription;
+
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ _testVhost = ApplicationRegistry.getInstance().getVirtualHostRegistry().
+ getVirtualHost("test");
+
+ _queue = new MockAMQQueue("SubscriptionLogSubjectTest");
+ ((MockAMQQueue) _queue).setVirtualHost(_testVhost);
+
+ AMQChannel channel = new AMQChannel(getSession(), _channelID, getSession().getVirtualHost().getMessageStore());
+
+ getSession().addChannel(channel);
+
+ SubscriptionFactory factory = new SubscriptionFactoryImpl();
+
+ _subscription = factory.createSubscription(_channelID, getSession(), new AMQShortString("cTag"),
+ false, null, false,
+ new LimitlessCreditManager());
+
+ _subscription.setQueue(_queue, false);
+
+ _subject = new SubscriptionLogSubject(_subscription);
+ }
+
+ /**
+ * Validate that the logged Subject message is as expected:
+ * MESSAGE [Blank][sub:0(vh(/test)/qu(SubscriptionLogSubjectTest))] <Log Message>
+ *
+ * @param message the message whos format needs validation
+ */
+ @Override
+ protected void validateLogStatement(String message)
+ {
+ String subscriptionSlice = getSlice("sub:"
+ + _subscription.getSubscriptionID(),
+ message);
+
+ assertNotNull("Unable to locate subscription 'sub:" +
+ _subscription.getSubscriptionID() + "'");
+
+
+
+ // Pull out the qu(..) section from the subscription message
+ // Split it into three parts
+ // MESSAGE [Blank][sub:0(vh(/
+ // test)/
+ // qu(SubscriptionLogSubjectTest))]
+ // Take the last bit and drop off the extra )]
+ String[] parts = message.split("/");
+ assertEquals("Message part count wrong", 3, parts.length);
+ String subscription = parts[2].substring(0, parts[2].indexOf(")") + 1);
+
+ // Adding the ')' is a bit ugly but SubscriptionLogSubject is the only
+ // Subject that nests () and so the simple parser of checking for the
+ // next ')' falls down.
+ verifyVirtualHost(subscriptionSlice+ ")", _queue.getVirtualHost());
+
+ verifyQueue(subscription, _queue);
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/TestBlankSubject.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/TestBlankSubject.java
new file mode 100644
index 0000000000..89688e13b3
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/TestBlankSubject.java
@@ -0,0 +1,33 @@
+/*
+ *
+ * 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.logging.subjects;
+
+/**
+ * Blank Subject for testing
+ */
+public class TestBlankSubject extends AbstractLogSubject
+{
+ public TestBlankSubject()
+ {
+ _logString = "[TestBlankSubject]";
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/management/AMQUserManagementMBeanTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/management/AMQUserManagementMBeanTest.java
new file mode 100644
index 0000000000..21f79e4b69
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/management/AMQUserManagementMBeanTest.java
@@ -0,0 +1,191 @@
+/*
+ *
+ * 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.management;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.TabularData;
+
+
+import org.apache.commons.lang.NotImplementedException;
+import org.apache.qpid.management.common.mbeans.UserManagement;
+import org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase;
+import org.apache.qpid.server.security.auth.management.AMQUserManagementMBean;
+
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+
+/**
+ *
+ * Tests the AMQUserManagementMBean and its interaction with the PrincipalDatabase.
+ *
+ */
+public class AMQUserManagementMBeanTest extends InternalBrokerBaseCase
+{
+ private PlainPasswordFilePrincipalDatabase _database;
+ private AMQUserManagementMBean _amqumMBean;
+
+ private File _passwordFile;
+
+ private static final String TEST_USERNAME = "testuser";
+ private static final String TEST_PASSWORD = "password";
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ _database = new PlainPasswordFilePrincipalDatabase();
+ _amqumMBean = new AMQUserManagementMBean();
+ loadFreshTestPasswordFile();
+ }
+
+ @Override
+ public void tearDown() throws Exception
+ {
+ //clean up test password/access files
+ File _oldPasswordFile = new File(_passwordFile.getAbsolutePath() + ".old");
+ _oldPasswordFile.delete();
+ _passwordFile.delete();
+
+ super.tearDown();
+ }
+
+ public void testDeleteUser()
+ {
+ assertEquals("Unexpected number of users before test", 1,_amqumMBean.viewUsers().size());
+ assertTrue("Delete should return true to flag successful delete", _amqumMBean.deleteUser(TEST_USERNAME));
+ assertEquals("Unexpected number of users after test", 0,_amqumMBean.viewUsers().size());
+ }
+
+ public void testDeleteUserWhereUserDoesNotExist()
+ {
+ assertEquals("Unexpected number of users before test", 1,_amqumMBean.viewUsers().size());
+ assertFalse("Delete should return false to flag unsuccessful delete", _amqumMBean.deleteUser("made.up.username"));
+ assertEquals("Unexpected number of users after test", 1,_amqumMBean.viewUsers().size());
+
+ }
+
+ public void testCreateUser()
+ {
+ assertEquals("Unexpected number of users before test", 1,_amqumMBean.viewUsers().size());
+ assertTrue("Create should return true to flag successful create", _amqumMBean.createUser("newuser", "mypass"));
+ assertEquals("Unexpected number of users before test", 2,_amqumMBean.viewUsers().size());
+ }
+
+ public void testCreateUserWhereUserAlreadyExists()
+ {
+ assertEquals("Unexpected number of users before test", 1,_amqumMBean.viewUsers().size());
+ assertFalse("Create should return false to flag unsuccessful create", _amqumMBean.createUser(TEST_USERNAME, "mypass"));
+ assertEquals("Unexpected number of users before test", 1,_amqumMBean.viewUsers().size());
+ }
+
+ public void testFiveArgCreateUserWithNegativeRightsRemainsSupported()
+ {
+ assertEquals("Unexpected number of users before test", 1,_amqumMBean.viewUsers().size());
+ assertTrue("Create should return true to flag successful create", _amqumMBean.createUser("newuser", "mypass".toCharArray(), false, false, false));
+ assertEquals("Unexpected number of users before test", 2,_amqumMBean.viewUsers().size());
+ }
+
+ public void testSetPassword()
+ {
+ assertTrue("Set password should return true to flag successful change", _amqumMBean.setPassword(TEST_USERNAME, "newpassword"));
+ }
+
+ public void testSetPasswordWhereUserDoesNotExist()
+ {
+ assertFalse("Set password should return false to flag successful change", _amqumMBean.setPassword("made.up.username", "newpassword"));
+ }
+
+ public void testViewUsers()
+ {
+ TabularData userList = _amqumMBean.viewUsers();
+
+ assertNotNull(userList);
+ assertEquals("Unexpected number of users in user list", 1, userList.size());
+ assertTrue(userList.containsKey(new Object[]{TEST_USERNAME}));
+
+ // Check the deprecated read, write and admin items continue to exist but return false.
+ CompositeData userRec = userList.get(new Object[]{TEST_USERNAME});
+ assertTrue(userRec.containsKey(UserManagement.RIGHTS_READ_ONLY));
+ assertEquals(false, userRec.get(UserManagement.RIGHTS_READ_ONLY));
+ assertEquals(false, userRec.get(UserManagement.RIGHTS_READ_WRITE));
+ assertTrue(userRec.containsKey(UserManagement.RIGHTS_READ_WRITE));
+ assertTrue(userRec.containsKey(UserManagement.RIGHTS_ADMIN));
+ assertEquals(false, userRec.get(UserManagement.RIGHTS_ADMIN));
+ }
+
+ // TEST DEPRECATED METHODS
+ public void testFiveArgCreateUserWithPositiveRightsThrowsUnsupportedOperation()
+ {
+ try
+ {
+ _amqumMBean.createUser(TEST_USERNAME, "mypass", true, false, false);
+ fail("Exception not thrown");
+ }
+ catch (UnsupportedOperationException uoe)
+ {
+ // PASS
+ }
+ }
+
+ public void testSetRightsThrowsUnsupportedOperation()
+ {
+ try
+ {
+ _amqumMBean.setRights("", false, false, false);
+ fail("Exception not thrown");
+ }
+ catch(UnsupportedOperationException nie)
+ {
+ // PASS
+ }
+ }
+
+ // ============================ Utility methods =========================
+
+ private void loadFreshTestPasswordFile()
+ {
+ try
+ {
+ if(_passwordFile == null)
+ {
+ _passwordFile = File.createTempFile(this.getClass().getName(),".password");
+ }
+
+ BufferedWriter passwordWriter = new BufferedWriter(new FileWriter(_passwordFile, false));
+ passwordWriter.write(TEST_USERNAME + ":" + TEST_PASSWORD);
+ passwordWriter.newLine();
+ passwordWriter.flush();
+ passwordWriter.close();
+ _database.setPasswordFile(_passwordFile.toString());
+ _amqumMBean.setPrincipalDatabase(_database);
+ }
+ catch (IOException e)
+ {
+ fail("Unable to create test password file: " + e.getMessage());
+ }
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/plugins/MockPluginManager.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/plugins/MockPluginManager.java
new file mode 100644
index 0000000000..a64ec5d3b1
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/plugins/MockPluginManager.java
@@ -0,0 +1,56 @@
+/*
+ * 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.plugins;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.qpid.server.configuration.plugins.ConfigurationPluginFactory;
+import org.apache.qpid.server.exchange.ExchangeType;
+import org.apache.qpid.server.security.SecurityPluginFactory;
+
+public class MockPluginManager extends PluginManager
+{
+ private Map<String, SecurityPluginFactory> _securityPlugins = new HashMap<String, SecurityPluginFactory>();
+ private Map<List<String>, ConfigurationPluginFactory> _configPlugins = new HashMap<List<String>, ConfigurationPluginFactory>();
+
+ public MockPluginManager(String pluginPath, String cachePath) throws Exception
+ {
+ super(pluginPath, cachePath);
+ }
+
+ @Override
+ public Map<String, ExchangeType<?>> getExchanges()
+ {
+ return null;
+ }
+
+ @Override
+ public Map<String, SecurityPluginFactory> getSecurityPlugins()
+ {
+ return _securityPlugins;
+ }
+
+ @Override
+ public Map<List<String>, ConfigurationPluginFactory> getConfigurationPlugins()
+ {
+ return _configPlugins;
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/plugins/PluginTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/plugins/PluginTest.java
new file mode 100644
index 0000000000..8c18ab85b0
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/plugins/PluginTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.plugins;
+
+import org.apache.qpid.server.exchange.ExchangeType;
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+
+
+import java.util.Map;
+
+public class PluginTest extends InternalBrokerBaseCase
+{
+ private static final String TEST_EXCHANGE_CLASS = "org.apache.qpid.extras.exchanges.example.TestExchangeType";
+
+ private static final String PLUGIN_DIRECTORY = System.getProperty("example.plugin.target");
+ private static final String CACHE_DIRECTORY = System.getProperty("example.cache.target");
+
+ @Override
+ public void configure()
+ {
+ getConfiguration().getConfig().addProperty("plugin-directory", PLUGIN_DIRECTORY);
+ getConfiguration().getConfig().addProperty("cache-directory", CACHE_DIRECTORY);
+ }
+
+ public void disabled_testLoadExchanges() throws Exception
+ {
+ PluginManager manager = getRegistry().getPluginManager();
+ Map<String, ExchangeType<?>> exchanges = manager.getExchanges();
+ assertNotNull("No exchanges found in " + PLUGIN_DIRECTORY, exchanges);
+ assertEquals("Wrong number of exchanges found in " + PLUGIN_DIRECTORY, 2, exchanges.size());
+ assertNotNull("Wrong exchange found in " + PLUGIN_DIRECTORY, exchanges.get(TEST_EXCHANGE_CLASS));
+ }
+
+ public void testNoExchanges() throws Exception
+ {
+ PluginManager manager = new PluginManager("/path/to/nowhere", "/tmp");
+ Map<String, ExchangeType<?>> exchanges = manager.getExchanges();
+ assertTrue("Exchanges found", exchanges.isEmpty());
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java
new file mode 100644
index 0000000000..4df051edb5
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java
@@ -0,0 +1,146 @@
+/*
+ *
+ * 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.protocol;
+
+import junit.framework.TestCase;
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.management.common.mbeans.ManagedConnection;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.AMQQueueFactory;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.store.SkeletonMessageStore;
+
+import javax.management.JMException;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.TabularData;
+
+
+/** Test class to test MBean operations for AMQMinaProtocolSession. */
+public class AMQProtocolSessionMBeanTest extends InternalBrokerBaseCase
+{
+ /** Used for debugging. */
+ private static final Logger log = Logger.getLogger(AMQProtocolSessionMBeanTest.class);
+
+ private MessageStore _messageStore = new SkeletonMessageStore();
+ private AMQProtocolEngine _protocolSession;
+ private AMQChannel _channel;
+ private AMQProtocolSessionMBean _mbean;
+
+ public void testChannels() throws Exception
+ {
+ // check the channel count is correct
+ int channelCount = _mbean.channels().size();
+ assertTrue(channelCount == 1);
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue_" + System.currentTimeMillis()),
+ false,
+ new AMQShortString("test"),
+ true,
+ false, _protocolSession.getVirtualHost(), null);
+ AMQChannel channel = new AMQChannel(_protocolSession, 2, _messageStore);
+ channel.setDefaultQueue(queue);
+ _protocolSession.addChannel(channel);
+ channelCount = _mbean.channels().size();
+ assertTrue(channelCount == 2);
+
+ // general properties test
+ _mbean.setMaximumNumberOfChannels(1000L);
+ assertTrue(_mbean.getMaximumNumberOfChannels() == 1000L);
+
+ // check APIs
+ AMQChannel channel3 = new AMQChannel(_protocolSession, 3, _messageStore);
+ channel3.setLocalTransactional();
+ _protocolSession.addChannel(channel3);
+ _mbean.rollbackTransactions(2);
+ _mbean.rollbackTransactions(3);
+ _mbean.commitTransactions(2);
+ _mbean.commitTransactions(3);
+
+ // This should throw exception, because the channel does't exist
+ try
+ {
+ _mbean.commitTransactions(4);
+ fail();
+ }
+ catch (JMException ex)
+ {
+ log.debug("expected exception is thrown :" + ex.getMessage());
+ }
+
+ // check channels() return type conveys flow control blocking status correctly
+ AMQChannel channel4 = new AMQChannel(_protocolSession, 4, _messageStore);
+ _protocolSession.addChannel(channel4);
+ channel4.setDefaultQueue(queue);
+
+ final String blocking = ManagedConnection.FLOW_BLOCKED;
+ TabularData channels = _mbean.channels();
+ CompositeData chan4result = channels.get(new Integer[]{4});
+ assertNotNull(chan4result);
+ assertEquals("Flow should not have been blocked", false, chan4result.get(blocking));
+
+ channel4.block(queue);
+ channels = _mbean.channels();
+ chan4result = channels.get(new Integer[]{4});
+ assertNotNull(chan4result);
+ assertEquals("Flow should have been blocked", true, chan4result.get(blocking));
+
+ channel4.unblock(queue);
+ channels = _mbean.channels();
+ chan4result = channels.get(new Integer[]{4});
+ assertNotNull(chan4result);
+ assertEquals("Flow should have been unblocked", false, chan4result.get(blocking));
+
+ // check if closing of session works
+ _protocolSession.addChannel(new AMQChannel(_protocolSession, 5, _messageStore));
+ _mbean.closeConnection();
+ try
+ {
+ channelCount = _mbean.channels().size();
+ assertTrue(channelCount == 0);
+ // session is now closed so adding another channel should throw an exception
+ _protocolSession.addChannel(new AMQChannel(_protocolSession, 6, _messageStore));
+ fail();
+ }
+ catch (AMQException ex)
+ {
+ log.debug("expected exception is thrown :" + ex.getMessage());
+ }
+ }
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ VirtualHost vhost = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test");
+ _protocolSession = new InternalTestProtocolSession(vhost);
+
+ _channel = new AMQChannel(_protocolSession, 1, _messageStore);
+ _protocolSession.addChannel(_channel);
+ _mbean = (AMQProtocolSessionMBean) _protocolSession.getManagedObject();
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java
new file mode 100644
index 0000000000..3b6cd37ea9
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java
@@ -0,0 +1,213 @@
+/*
+ *
+ * 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.protocol;
+
+import java.security.Principal;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.output.ProtocolOutputConverter;
+import org.apache.qpid.server.message.AMQMessage;
+import org.apache.qpid.server.queue.QueueEntry;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.state.AMQState;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.server.message.MessageContentSource;
+import org.apache.qpid.transport.TestNetworkDriver;
+
+public class InternalTestProtocolSession extends AMQProtocolEngine implements ProtocolOutputConverter
+{
+ // ChannelID(LIST) -> LinkedList<Pair>
+ final Map<Integer, Map<AMQShortString, LinkedList<DeliveryPair>>> _channelDelivers;
+ private AtomicInteger _deliveryCount = new AtomicInteger(0);
+
+ public InternalTestProtocolSession(VirtualHost virtualHost) throws AMQException
+ {
+ super(ApplicationRegistry.getInstance().getVirtualHostRegistry(), new TestNetworkDriver());
+
+ _channelDelivers = new HashMap<Integer, Map<AMQShortString, LinkedList<DeliveryPair>>>();
+
+ // Need to authenticate session for it to be representative testing.
+ setAuthorizedID(new Principal()
+ {
+ public String getName()
+ {
+ return "InternalTestProtocolSession";
+ }
+ });
+
+ setVirtualHost(virtualHost);
+ }
+
+ public ProtocolOutputConverter getProtocolOutputConverter()
+ {
+ return this;
+ }
+
+ public byte getProtocolMajorVersion()
+ {
+ return (byte) 8;
+ }
+
+ public void writeReturn(MessagePublishInfo messagePublishInfo,
+ ContentHeaderBody header,
+ MessageContentSource msgContent,
+ int channelId,
+ int replyCode,
+ AMQShortString replyText) throws AMQException
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public byte getProtocolMinorVersion()
+ {
+ return (byte) 0;
+ }
+
+ // ***
+
+ public List<DeliveryPair> getDelivers(int channelId, AMQShortString consumerTag, int count)
+ {
+ synchronized (_channelDelivers)
+ {
+ List<DeliveryPair> all =_channelDelivers.get(channelId).get(consumerTag);
+
+ if (all == null)
+ {
+ return new ArrayList<DeliveryPair>(0);
+ }
+
+ List<DeliveryPair> msgs = all.subList(0, count);
+
+ List<DeliveryPair> response = new ArrayList<DeliveryPair>(msgs);
+
+ //Remove the msgs from the receivedList.
+ msgs.clear();
+
+ return response;
+ }
+ }
+
+ // *** ProtocolOutputConverter Implementation
+ public void writeReturn(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) throws AMQException
+ {
+ }
+
+ public void confirmConsumerAutoClose(int channelId, AMQShortString consumerTag)
+ {
+ }
+
+ public void writeDeliver(QueueEntry entry, int channelId, long deliveryTag, AMQShortString consumerTag) throws AMQException
+ {
+ _deliveryCount.incrementAndGet();
+
+ synchronized (_channelDelivers)
+ {
+ Map<AMQShortString, LinkedList<DeliveryPair>> consumers = _channelDelivers.get(channelId);
+
+ if (consumers == null)
+ {
+ consumers = new HashMap<AMQShortString, LinkedList<DeliveryPair>>();
+ _channelDelivers.put(channelId, consumers);
+ }
+
+ LinkedList<DeliveryPair> consumerDelivers = consumers.get(consumerTag);
+
+ if (consumerDelivers == null)
+ {
+ consumerDelivers = new LinkedList<DeliveryPair>();
+ consumers.put(consumerTag, consumerDelivers);
+ }
+
+ consumerDelivers.add(new DeliveryPair(deliveryTag, (AMQMessage)entry.getMessage()));
+ }
+ }
+
+ public void writeGetOk(QueueEntry message, int channelId, long deliveryTag, int queueSize) throws AMQException
+ {
+ }
+
+ public void awaitDelivery(int msgs)
+ {
+ while (msgs > _deliveryCount.get())
+ {
+ try
+ {
+ Thread.sleep(100);
+ }
+ catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public class DeliveryPair
+ {
+ private long _deliveryTag;
+ private AMQMessage _message;
+
+ public DeliveryPair(long deliveryTag, AMQMessage message)
+ {
+ _deliveryTag = deliveryTag;
+ _message = message;
+ }
+
+ public AMQMessage getMessage()
+ {
+ return _message;
+ }
+
+ public long getDeliveryTag()
+ {
+ return _deliveryTag;
+ }
+ }
+
+ public boolean isClosed()
+ {
+ return _closed;
+ }
+
+ public void closeProtocolSession(boolean waitLast)
+ {
+ // Override as we don't have a real IOSession to close.
+ // The alternative is to fully implement the TestIOSession to return a CloseFuture from close();
+ // Then the AMQMinaProtocolSession can join on the returning future without a NPE.
+ }
+
+ public void closeSession(AMQSessionModel session, AMQConstant cause, String message) throws AMQException
+ {
+ super.closeSession(session, cause, message);
+
+ //Simulate the Client responding with a CloseOK
+ // should really update the StateManger but we don't have access here
+ // changeState(AMQState.CONNECTION_CLOSED);
+ ((AMQChannel)session).getProtocolSession().closeSession();
+
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/MaxChannelsTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/MaxChannelsTest.java
new file mode 100644
index 0000000000..f6e83e6369
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/MaxChannelsTest.java
@@ -0,0 +1,79 @@
+/*
+ *
+ * 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.protocol;
+
+import junit.framework.TestCase;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+
+/** Test class to test MBean operations for AMQMinaProtocolSession. */
+public class MaxChannelsTest extends InternalBrokerBaseCase
+{
+ private AMQProtocolEngine _session;
+
+ public void testChannels() throws Exception
+ {
+ VirtualHost vhost = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test");
+ _session = new InternalTestProtocolSession(vhost);
+
+ // check the channel count is correct
+ int channelCount = _session.getChannels().size();
+ assertEquals("Initial channel count wrong", 0, channelCount);
+
+ long maxChannels = 10L;
+ _session.setMaximumNumberOfChannels(maxChannels);
+ assertEquals("Number of channels not correctly set.", new Long(maxChannels), _session.getMaximumNumberOfChannels());
+
+
+ try
+ {
+ for (long currentChannel = 0L; currentChannel < maxChannels; currentChannel++)
+ {
+ _session.addChannel(new AMQChannel(_session, (int) currentChannel, null));
+ }
+ }
+ catch (AMQException e)
+ {
+ assertEquals("Wrong exception recevied.", e.getErrorCode(), AMQConstant.NOT_ALLOWED);
+ }
+ assertEquals("Maximum number of channels not set.", new Long(maxChannels), new Long(_session.getChannels().size()));
+ }
+
+ @Override
+ public void tearDown() throws Exception
+ {
+ try {
+ _session.closeSession();
+ } catch (AMQException e) {
+ // Yikes
+ fail(e.getMessage());
+ }
+ finally
+ {
+ super.tearDown();
+ }
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQPriorityQueueTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQPriorityQueueTest.java
new file mode 100644
index 0000000000..3961b3b355
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQPriorityQueueTest.java
@@ -0,0 +1,108 @@
+package org.apache.qpid.server.queue;
+/*
+ *
+ * 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.
+ *
+ */
+
+import java.util.ArrayList;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.message.AMQMessage;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.framing.FieldTable;
+import junit.framework.AssertionFailedError;
+
+public class AMQPriorityQueueTest extends SimpleAMQQueueTest
+{
+
+ @Override
+ public void setUp() throws Exception
+ {
+ _arguments = new FieldTable();
+ _arguments.put(AMQQueueFactory.X_QPID_PRIORITIES, 3);
+ super.setUp();
+ }
+
+ public void testPriorityOrdering() throws AMQException, InterruptedException
+ {
+
+ // Enqueue messages in order
+ _queue.enqueue(createMessage(1L, (byte) 10));
+ _queue.enqueue(createMessage(2L, (byte) 4));
+ _queue.enqueue(createMessage(3L, (byte) 0));
+
+ // Enqueue messages in reverse order
+ _queue.enqueue(createMessage(4L, (byte) 0));
+ _queue.enqueue(createMessage(5L, (byte) 4));
+ _queue.enqueue(createMessage(6L, (byte) 10));
+
+ // Enqueue messages out of order
+ _queue.enqueue(createMessage(7L, (byte) 4));
+ _queue.enqueue(createMessage(8L, (byte) 10));
+ _queue.enqueue(createMessage(9L, (byte) 0));
+
+ // Register subscriber
+ _queue.registerSubscription(_subscription, false);
+ Thread.sleep(150);
+
+ ArrayList<QueueEntry> msgs = _subscription.getMessages();
+ try
+ {
+ assertEquals(new Long(1L), msgs.get(0).getMessage().getMessageNumber());
+ assertEquals(new Long(6L), msgs.get(1).getMessage().getMessageNumber());
+ assertEquals(new Long(8L), msgs.get(2).getMessage().getMessageNumber());
+
+ assertEquals(new Long(2L), msgs.get(3).getMessage().getMessageNumber());
+ assertEquals(new Long(5L), msgs.get(4).getMessage().getMessageNumber());
+ assertEquals(new Long(7L), msgs.get(5).getMessage().getMessageNumber());
+
+ assertEquals(new Long(3L), msgs.get(6).getMessage().getMessageNumber());
+ assertEquals(new Long(4L), msgs.get(7).getMessage().getMessageNumber());
+ assertEquals(new Long(9L), msgs.get(8).getMessage().getMessageNumber());
+ }
+ catch (AssertionFailedError afe)
+ {
+ // Show message order on failure.
+ int index = 1;
+ for (QueueEntry qe : msgs)
+ {
+ System.err.println(index + ":" + qe.getMessage().getMessageNumber());
+ index++;
+ }
+
+ throw afe;
+ }
+
+ }
+
+ protected AMQMessage createMessage(Long id, byte i) throws AMQException
+ {
+ AMQMessage msg = super.createMessage(id);
+ BasicContentHeaderProperties props = new BasicContentHeaderProperties();
+ props.setPriority(i);
+ msg.getContentHeaderBody().setProperties(props);
+ return msg;
+ }
+
+ protected AMQMessage createMessage(Long id) throws AMQException
+ {
+ return createMessage(id, (byte) 0);
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java
new file mode 100644
index 0000000000..a8bddcf6bf
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java
@@ -0,0 +1,350 @@
+/*
+ *
+ * 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.queue;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.framing.abstraction.ContentChunk;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.message.AMQMessage;
+import org.apache.qpid.server.message.MessageMetaData;
+import org.apache.qpid.server.protocol.InternalTestProtocolSession;
+import org.apache.qpid.server.subscription.Subscription;
+import org.apache.qpid.server.subscription.SubscriptionFactoryImpl;
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+
+import javax.management.Notification;
+import java.util.ArrayList;
+
+/** This class tests all the alerts an AMQQueue can throw based on threshold values of different parameters */
+public class AMQQueueAlertTest extends InternalBrokerBaseCase
+{
+ private final static long MAX_MESSAGE_COUNT = 50;
+ private final static long MAX_MESSAGE_AGE = 250; // 0.25 sec
+ private final static long MAX_MESSAGE_SIZE = 2000; // 2 KB
+ private final static long MAX_QUEUE_DEPTH = 10000; // 10 KB
+ private AMQQueueMBean _queueMBean;
+ private static final SubscriptionFactoryImpl SUBSCRIPTION_FACTORY = SubscriptionFactoryImpl.INSTANCE;
+
+ /**
+ * Tests if the alert gets thrown when message count increases the threshold limit
+ *
+ * @throws Exception
+ */
+ public void testMessageCountAlert() throws Exception
+ {
+ setSession(new InternalTestProtocolSession(getVirtualHost()));
+ AMQChannel channel = new AMQChannel(getSession(), 2, getMessageStore());
+ getSession().addChannel(channel);
+
+ setQueue(AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue1"), false, new AMQShortString("AMQueueAlertTest"),
+ false, false,
+ getVirtualHost(), null));
+ _queueMBean = (AMQQueueMBean) getQueue().getManagedObject();
+
+ _queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT);
+
+ sendMessages(channel, MAX_MESSAGE_COUNT, 256l);
+ assertTrue(_queueMBean.getMessageCount() == MAX_MESSAGE_COUNT);
+
+ Notification lastNotification = _queueMBean.getLastNotification();
+ assertNotNull(lastNotification);
+
+ String notificationMsg = lastNotification.getMessage();
+ assertTrue(notificationMsg.startsWith(NotificationCheck.MESSAGE_COUNT_ALERT.name()));
+ }
+
+ /**
+ * Tests if the Message Size alert gets thrown when message of higher than threshold limit is sent
+ *
+ * @throws Exception
+ */
+ public void testMessageSizeAlert() throws Exception
+ {
+ setSession(new InternalTestProtocolSession(getVirtualHost()));
+ AMQChannel channel = new AMQChannel(getSession(), 2, getMessageStore());
+ getSession().addChannel(channel);
+
+ setQueue(AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue2"), false, new AMQShortString("AMQueueAlertTest"),
+ false, false,
+ getVirtualHost(), null));
+ _queueMBean = (AMQQueueMBean) getQueue().getManagedObject();
+ _queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT);
+ _queueMBean.setMaximumMessageSize(MAX_MESSAGE_SIZE);
+
+ sendMessages(channel, 1, MAX_MESSAGE_SIZE * 2);
+ assertTrue(_queueMBean.getMessageCount() == 1);
+
+ Notification lastNotification = _queueMBean.getLastNotification();
+ assertNotNull(lastNotification);
+
+ String notificationMsg = lastNotification.getMessage();
+ assertTrue(notificationMsg.startsWith(NotificationCheck.MESSAGE_SIZE_ALERT.name()));
+ }
+
+ /**
+ * Tests if Queue Depth alert is thrown when queue depth reaches the threshold value
+ *
+ * Based on FT-402 subbmitted by client
+ *
+ * @throws Exception
+ */
+ public void testQueueDepthAlertNoSubscriber() throws Exception
+ {
+ setSession(new InternalTestProtocolSession(getVirtualHost()));
+ AMQChannel channel = new AMQChannel(getSession(), 2, getMessageStore());
+ getSession().addChannel(channel);
+
+ setQueue(AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue3"), false, new AMQShortString("AMQueueAlertTest"),
+ false, false,
+ getVirtualHost(), null));
+ _queueMBean = (AMQQueueMBean) getQueue().getManagedObject();
+ _queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT);
+ _queueMBean.setMaximumQueueDepth(MAX_QUEUE_DEPTH);
+
+ while (getQueue().getQueueDepth() < MAX_QUEUE_DEPTH)
+ {
+ sendMessages(channel, 1, MAX_MESSAGE_SIZE);
+ }
+
+ Notification lastNotification = _queueMBean.getLastNotification();
+ assertNotNull(lastNotification);
+
+ String notificationMsg = lastNotification.getMessage();
+ assertTrue(notificationMsg.startsWith(NotificationCheck.QUEUE_DEPTH_ALERT.name()));
+ }
+
+ /**
+ * Tests if MESSAGE AGE alert is thrown, when a message is in the queue for time higher than threshold value of
+ * message age
+ *
+ * Alternative test to FT-401 provided by client
+ *
+ * @throws Exception
+ */
+ public void testMessageAgeAlert() throws Exception
+ {
+ setSession(new InternalTestProtocolSession(getVirtualHost()));
+ AMQChannel channel = new AMQChannel(getSession(), 2, getMessageStore());
+ getSession().addChannel(channel);
+
+ setQueue(AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue4"), false, new AMQShortString("AMQueueAlertTest"),
+ false, false,
+ getVirtualHost(), null));
+ _queueMBean = (AMQQueueMBean) getQueue().getManagedObject();
+ _queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT);
+ _queueMBean.setMaximumMessageAge(MAX_MESSAGE_AGE);
+
+ sendMessages(channel, 1, MAX_MESSAGE_SIZE);
+
+ // Ensure message sits on queue long enough to age.
+ Thread.sleep(MAX_MESSAGE_AGE * 2);
+
+ Notification lastNotification = _queueMBean.getLastNotification();
+ assertNotNull("Last notification was null", lastNotification);
+
+ String notificationMsg = lastNotification.getMessage();
+ assertTrue(notificationMsg.startsWith(NotificationCheck.MESSAGE_AGE_ALERT.name()));
+ }
+
+ /*
+ This test sends some messages to the queue with subscribers needing message to be acknowledged.
+ The messages will not be acknowledged and will be required twice. Why we are checking this is because
+ the bug reported said that the queueDepth keeps increasing when messages are requeued.
+ // TODO - queue depth now includes unacknowledged messages so does not go down when messages are delivered
+
+ The QueueDepth should decrease when messages are delivered from the queue (QPID-408)
+ */
+ public void testQueueDepthAlertWithSubscribers() throws Exception
+ {
+ AMQChannel channel = new AMQChannel(getSession(), 2, getMessageStore());
+ getSession().addChannel(channel);
+
+ // Create queue
+ setQueue(getNewQueue());
+ Subscription subscription =
+ SUBSCRIPTION_FACTORY.createSubscription(channel.getChannelId(), getSession(), new AMQShortString("consumer_tag"), true, null, false, channel.getCreditManager());
+
+ getQueue().registerSubscription(
+ subscription, false);
+
+ _queueMBean = (AMQQueueMBean) getQueue().getManagedObject();
+ _queueMBean.setMaximumMessageCount(9999l); // Set a high value, because this is not being tested
+ _queueMBean.setMaximumQueueDepth(MAX_QUEUE_DEPTH);
+
+ // Send messages(no of message to be little more than what can cause a Queue_Depth alert)
+ int messageCount = Math.round(MAX_QUEUE_DEPTH / MAX_MESSAGE_SIZE) + 10;
+ long totalSize = (messageCount * MAX_MESSAGE_SIZE);
+ sendMessages(channel, messageCount, MAX_MESSAGE_SIZE);
+
+ // Check queueDepth. There should be no messages on the queue and as the subscriber is listening
+ // so there should be no Queue_Deoth alert raised
+ assertEquals(new Long(totalSize), new Long(_queueMBean.getQueueDepth()));
+ Notification lastNotification = _queueMBean.getLastNotification();
+// assertNull(lastNotification);
+
+ // Kill the subscriber and check for the queue depth values.
+ // Messages are unacknowledged, so those should get requeued. All messages should be on the Queue
+ getQueue().unregisterSubscription(subscription);
+ channel.requeue();
+
+ assertEquals(new Long(totalSize), new Long(_queueMBean.getQueueDepth()));
+
+ lastNotification = _queueMBean.getLastNotification();
+ assertNotNull(lastNotification);
+ String notificationMsg = lastNotification.getMessage();
+ assertTrue(notificationMsg.startsWith(NotificationCheck.QUEUE_DEPTH_ALERT.name()));
+
+ // Connect a consumer again and check QueueDepth values. The queue should get emptied.
+ // Messages will get delivered but still are unacknowledged.
+ Subscription subscription2 =
+ SUBSCRIPTION_FACTORY.createSubscription(channel.getChannelId(), getSession(), new AMQShortString("consumer_tag"), true, null, false, channel.getCreditManager());
+
+ getQueue().registerSubscription(
+ subscription2, false);
+
+ while (getQueue().getUndeliveredMessageCount()!= 0)
+ {
+ Thread.sleep(100);
+ }
+// assertEquals(new Long(0), new Long(_queueMBean.getQueueDepth()));
+
+ // Kill the subscriber again. Now those messages should get requeued again. Check if the queue depth
+ // value is correct.
+ getQueue().unregisterSubscription(subscription2);
+ channel.requeue();
+
+ assertEquals(new Long(totalSize), new Long(_queueMBean.getQueueDepth()));
+ getSession().closeSession();
+
+ // Check the clear queue
+ _queueMBean.clearQueue();
+ assertEquals(new Long(0), new Long(_queueMBean.getQueueDepth()));
+ }
+
+ protected IncomingMessage message(final boolean immediate, long size) throws AMQException
+ {
+ MessagePublishInfo publish = new MessagePublishInfo()
+ {
+
+ public AMQShortString getExchange()
+ {
+ return null;
+ }
+
+ public void setExchange(AMQShortString exchange)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isImmediate()
+ {
+ return immediate;
+ }
+
+ public boolean isMandatory()
+ {
+ return false;
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return null;
+ }
+ };
+
+ ContentHeaderBody contentHeaderBody = new ContentHeaderBody();
+ BasicContentHeaderProperties props = new BasicContentHeaderProperties();
+ contentHeaderBody.setProperties(props);
+ contentHeaderBody.bodySize = size; // in bytes
+ IncomingMessage message = new IncomingMessage(publish);
+ message.setContentHeaderBody(contentHeaderBody);
+
+ return message;
+ }
+
+ @Override
+ protected void configure()
+ {
+ // Increase Alert Check period
+ getConfiguration().setHousekeepingExpiredMessageCheckPeriod(200);
+ }
+
+ private void sendMessages(AMQChannel channel, long messageCount, final long size) throws AMQException
+ {
+ IncomingMessage[] messages = new IncomingMessage[(int) messageCount];
+ MessageMetaData[] metaData = new MessageMetaData[(int) messageCount];
+ for (int i = 0; i < messages.length; i++)
+ {
+ messages[i] = message(false, size);
+ ArrayList<AMQQueue> qs = new ArrayList<AMQQueue>();
+ qs.add(getQueue());
+ metaData[i] = messages[i].headersReceived();
+ messages[i].setStoredMessage(getMessageStore().addMessage(metaData[i]));
+
+ messages[i].enqueue(qs);
+
+ }
+
+ for (int i = 0; i < messageCount; i++)
+ {
+ messages[i].addContentBodyFrame(new ContentChunk(){
+
+ ByteBuffer _data = ByteBuffer.allocate((int)size);
+
+ {
+ _data.limit((int)size);
+ }
+
+ public int getSize()
+ {
+ return (int) size;
+ }
+
+ public ByteBuffer getData()
+ {
+ return _data;
+ }
+
+ public void reduceToFit()
+ {
+
+ }
+ });
+
+ getQueue().enqueue(new AMQMessage(messages[i].getStoredMessage()));
+
+ }
+ }
+
+ private AMQQueue getNewQueue() throws AMQException
+ {
+ return AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue" + Math.random()),
+ false,
+ new AMQShortString("AMQueueAlertTest"),
+ false,
+ false, getVirtualHost(), null);
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueFactoryTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueFactoryTest.java
new file mode 100644
index 0000000000..27891289fb
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueFactoryTest.java
@@ -0,0 +1,75 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
+
+public class AMQQueueFactoryTest extends InternalBrokerBaseCase
+{
+ QueueRegistry _queueRegistry;
+ VirtualHost _virtualHost;
+ int _defaultQueueCount;
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ ApplicationRegistry registry = (ApplicationRegistry) ApplicationRegistry.getInstance();
+
+ _virtualHost = registry.getVirtualHostRegistry().getVirtualHost("test");
+
+ _queueRegistry = _virtualHost.getQueueRegistry();
+
+ _defaultQueueCount = _queueRegistry.getQueues().size();
+ }
+
+ @Override
+ public void tearDown() throws Exception
+ {
+ assertEquals("Queue was not registered in virtualhost", _defaultQueueCount + 1, _queueRegistry.getQueues().size());
+ super.tearDown();
+ }
+
+
+ public void testPriorityQueueRegistration() throws Exception
+ {
+ FieldTable fieldTable = new FieldTable();
+ fieldTable.put(new AMQShortString(AMQQueueFactory.X_QPID_PRIORITIES), 5);
+
+
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testPriorityQueue"), false, new AMQShortString("owner"), false,
+ false, _virtualHost, fieldTable);
+
+ assertEquals("Queue not a priorty queue", AMQPriorityQueue.class, queue.getClass());
+ }
+
+
+ public void testSimpleQueueRegistration() throws Exception
+ {
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue"), false, new AMQShortString("owner"), false,
+ false, _virtualHost, null);
+ assertEquals("Queue not a simple queue", SimpleAMQQueue.class, queue.getClass());
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java
new file mode 100644
index 0000000000..365353e734
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java
@@ -0,0 +1,456 @@
+/*
+ *
+ * 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.queue;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.framing.ContentBody;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.framing.abstraction.ContentChunk;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+import org.apache.qpid.server.message.AMQMessage;
+import org.apache.qpid.server.message.MessageMetaData;
+import org.apache.qpid.server.subscription.Subscription;
+import org.apache.qpid.server.subscription.SubscriptionFactory;
+import org.apache.qpid.server.subscription.SubscriptionFactoryImpl;
+import org.apache.qpid.server.protocol.InternalTestProtocolSession;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.store.TestableMemoryMessageStore;
+import org.apache.mina.common.ByteBuffer;
+
+import javax.management.JMException;
+
+import java.util.ArrayList;
+
+/**
+ * Test class to test AMQQueueMBean attribtues and operations
+ */
+public class AMQQueueMBeanTest extends InternalBrokerBaseCase
+{
+ private static long MESSAGE_SIZE = 1000;
+ private AMQQueueMBean _queueMBean;
+ private static final SubscriptionFactoryImpl SUBSCRIPTION_FACTORY = SubscriptionFactoryImpl.INSTANCE;
+
+ public void testMessageCountTransient() throws Exception
+ {
+ int messageCount = 10;
+ sendMessages(messageCount, false);
+ assertTrue(_queueMBean.getMessageCount() == messageCount);
+ assertTrue(_queueMBean.getReceivedMessageCount() == messageCount);
+ long queueDepth = (messageCount * MESSAGE_SIZE);
+ assertTrue(_queueMBean.getQueueDepth() == queueDepth);
+
+ _queueMBean.deleteMessageFromTop();
+ assertTrue(_queueMBean.getMessageCount() == (messageCount - 1));
+ assertTrue(_queueMBean.getReceivedMessageCount() == messageCount);
+
+ _queueMBean.clearQueue();
+ assertEquals(0,(int)_queueMBean.getMessageCount());
+ assertTrue(_queueMBean.getReceivedMessageCount() == messageCount);
+
+ //Ensure that the data has been removed from the Store
+ verifyBrokerState();
+ }
+
+ public void testMessageCountPersistent() throws Exception
+ {
+ int messageCount = 10;
+ sendMessages(messageCount, true);
+ assertEquals("", messageCount, _queueMBean.getMessageCount().intValue());
+ assertTrue(_queueMBean.getReceivedMessageCount() == messageCount);
+ long queueDepth = (messageCount * MESSAGE_SIZE);
+ assertTrue(_queueMBean.getQueueDepth() == queueDepth);
+
+ _queueMBean.deleteMessageFromTop();
+ assertTrue(_queueMBean.getMessageCount() == (messageCount - 1));
+ assertTrue(_queueMBean.getReceivedMessageCount() == messageCount);
+
+ _queueMBean.clearQueue();
+ assertTrue(_queueMBean.getMessageCount() == 0);
+ assertTrue(_queueMBean.getReceivedMessageCount() == messageCount);
+
+ //Ensure that the data has been removed from the Store
+ verifyBrokerState();
+ }
+
+ public void testDeleteMessages() throws Exception
+ {
+ int messageCount = 10;
+ sendMessages(messageCount, true);
+ assertEquals("", messageCount, _queueMBean.getMessageCount().intValue());
+ assertTrue(_queueMBean.getReceivedMessageCount() == messageCount);
+ long queueDepth = (messageCount * MESSAGE_SIZE);
+ assertTrue(_queueMBean.getQueueDepth() == queueDepth);
+
+ //delete first message
+ _queueMBean.deleteMessages(1L,1L);
+ assertTrue(_queueMBean.getMessageCount() == (messageCount - 1));
+ assertTrue(_queueMBean.getReceivedMessageCount() == messageCount);
+ try
+ {
+ _queueMBean.viewMessageContent(1L);
+ fail("Message should no longer be on the queue");
+ }
+ catch(Exception e)
+ {
+
+ }
+
+ //delete last message, leaving 2nd to 9th
+ _queueMBean.deleteMessages(10L,10L);
+ assertTrue(_queueMBean.getMessageCount() == (messageCount - 2));
+ assertTrue(_queueMBean.getReceivedMessageCount() == messageCount);
+ try
+ {
+ _queueMBean.viewMessageContent(10L);
+ fail("Message should no longer be on the queue");
+ }
+ catch(Exception e)
+ {
+
+ }
+
+ //delete remaining messages, leaving none
+ _queueMBean.deleteMessages(2L,9L);
+ assertTrue(_queueMBean.getMessageCount() == (0));
+ assertTrue(_queueMBean.getReceivedMessageCount() == messageCount);
+
+ //Ensure that the data has been removed from the Store
+ verifyBrokerState();
+ }
+
+ // todo: collect to a general testing class -duplicated from Systest/MessageReturntest
+ private void verifyBrokerState()
+ {
+
+ TestableMemoryMessageStore store = (TestableMemoryMessageStore) getVirtualHost().getMessageStore();
+
+ // Unlike MessageReturnTest there is no need for a delay as there this thread does the clean up.
+
+ assertEquals("Store should have no messages:" + store.getMessageCount(), 0, store.getMessageCount());
+ }
+
+ public void testConsumerCount() throws AMQException
+ {
+
+ assertTrue(getQueue().getActiveConsumerCount() == 0);
+ assertTrue(_queueMBean.getActiveConsumerCount() == 0);
+
+
+ InternalTestProtocolSession protocolSession = new InternalTestProtocolSession(getVirtualHost());
+
+ AMQChannel channel = new AMQChannel(protocolSession, 1, getMessageStore());
+ protocolSession.addChannel(channel);
+
+ Subscription subscription =
+ SUBSCRIPTION_FACTORY.createSubscription(channel.getChannelId(), protocolSession, new AMQShortString("test"), false, null, false, channel.getCreditManager());
+
+ getQueue().registerSubscription(subscription, false);
+ assertEquals(1,(int)_queueMBean.getActiveConsumerCount());
+
+
+ SubscriptionFactory subscriptionFactory = SUBSCRIPTION_FACTORY;
+ Subscription s1 = subscriptionFactory.createSubscription(channel.getChannelId(),
+ protocolSession,
+ new AMQShortString("S1"),
+ false,
+ null,
+ true,
+ channel.getCreditManager());
+
+ Subscription s2 = subscriptionFactory.createSubscription(channel.getChannelId(),
+ protocolSession,
+ new AMQShortString("S2"),
+ false,
+ null,
+ true,
+ channel.getCreditManager());
+ getQueue().registerSubscription(s1,false);
+ getQueue().registerSubscription(s2,false);
+ assertTrue(_queueMBean.getActiveConsumerCount() == 3);
+ assertTrue(_queueMBean.getConsumerCount() == 3);
+
+ s1.close();
+ assertEquals(2, (int) _queueMBean.getActiveConsumerCount());
+ assertTrue(_queueMBean.getConsumerCount() == 3);
+ }
+
+ public void testGeneralProperties() throws Exception
+ {
+ long maxQueueDepth = 1000; // in bytes
+ _queueMBean.setMaximumMessageCount(50000l);
+ _queueMBean.setMaximumMessageSize(2000l);
+ _queueMBean.setMaximumQueueDepth(maxQueueDepth);
+
+ assertEquals("Max MessageCount not set",50000,_queueMBean.getMaximumMessageCount().longValue());
+ assertEquals("Max MessageSize not set",2000, _queueMBean.getMaximumMessageSize().longValue());
+ assertEquals("Max QueueDepth not set",maxQueueDepth, _queueMBean.getMaximumQueueDepth().longValue());
+
+ assertEquals("Queue Name does not match", new AMQShortString(getName()), _queueMBean.getName());
+ assertFalse("AutoDelete should not be set.",_queueMBean.isAutoDelete());
+ assertFalse("Queue should not be durable.",_queueMBean.isDurable());
+
+ //set+get exclusivity using the mbean, and also verify it is actually updated in the queue
+ _queueMBean.setExclusive(true);
+ assertTrue("Exclusive property should be true.",_queueMBean.isExclusive());
+ assertTrue("Exclusive property should be true.", getQueue().isExclusive());
+ _queueMBean.setExclusive(false);
+ assertFalse("Exclusive property should be false.",_queueMBean.isExclusive());
+ assertFalse("Exclusive property should be false.", getQueue().isExclusive());
+ }
+
+ public void testExceptions() throws Exception
+ {
+ try
+ {
+ _queueMBean.viewMessages(0L, 3L);
+ fail();
+ }
+ catch (JMException ex)
+ {
+
+ }
+
+ try
+ {
+ _queueMBean.viewMessages(2L, 1L);
+ fail();
+ }
+ catch (JMException ex)
+ {
+
+ }
+
+ try
+ {
+ _queueMBean.viewMessages(-1L, 1L);
+ fail();
+ }
+ catch (JMException ex)
+ {
+
+ }
+
+ try
+ {
+ long end = Integer.MAX_VALUE;
+ end+=2;
+ _queueMBean.viewMessages(1L, end);
+ fail("Expected Exception due to oversized(> 2^31) message range");
+ }
+ catch (JMException ex)
+ {
+
+ }
+
+ IncomingMessage msg = message(false, false);
+ getQueue().clearQueue();
+ ArrayList<AMQQueue> qs = new ArrayList<AMQQueue>();
+ qs.add(getQueue());
+ msg.enqueue(qs);
+ MessageMetaData mmd = msg.headersReceived();
+ msg.setStoredMessage(getMessageStore().addMessage(mmd));
+ long id = msg.getMessageNumber();
+
+ msg.addContentBodyFrame(new ContentChunk()
+ {
+ ByteBuffer _data = ByteBuffer.allocate((int)MESSAGE_SIZE);
+
+ {
+ _data.limit((int)MESSAGE_SIZE);
+ }
+
+ public int getSize()
+ {
+ return (int) MESSAGE_SIZE;
+ }
+
+ public ByteBuffer getData()
+ {
+ return _data;
+ }
+
+ public void reduceToFit()
+ {
+
+ }
+ });
+
+ AMQMessage m = new AMQMessage(msg.getStoredMessage());
+ for(BaseQueue q : msg.getDestinationQueues())
+ {
+ q.enqueue(m);
+ }
+// _queue.process(_storeContext, new QueueEntry(_queue, msg), false);
+ _queueMBean.viewMessageContent(id);
+ try
+ {
+ _queueMBean.viewMessageContent(id + 1);
+ fail();
+ }
+ catch (JMException ex)
+ {
+
+ }
+ }
+
+ public void testFlowControlProperties() throws Exception
+ {
+ assertTrue(_queueMBean.getCapacity() == 0);
+ assertTrue(_queueMBean.getFlowResumeCapacity() == 0);
+ assertFalse(_queueMBean.isFlowOverfull());
+
+ //capacity currently 0, try setting FlowResumeCapacity above this
+ try
+ {
+ _queueMBean.setFlowResumeCapacity(1L);
+ fail("Should have failed to allow setting FlowResumeCapacity above Capacity");
+ }
+ catch (IllegalArgumentException ex)
+ {
+ //expected exception
+ assertTrue(_queueMBean.getFlowResumeCapacity() == 0);
+ }
+
+ //add a message to the queue
+ sendMessages(1, true);
+
+ //(FlowResume)Capacity currently 0, set both to 2
+ _queueMBean.setCapacity(2L);
+ assertTrue(_queueMBean.getCapacity() == 2L);
+ _queueMBean.setFlowResumeCapacity(2L);
+ assertTrue(_queueMBean.getFlowResumeCapacity() == 2L);
+
+ //Try setting Capacity below FlowResumeCapacity
+ try
+ {
+ _queueMBean.setCapacity(1L);
+ fail("Should have failed to allow setting Capacity below FlowResumeCapacity");
+ }
+ catch (IllegalArgumentException ex)
+ {
+ //expected exception
+ assertTrue(_queueMBean.getCapacity() == 2);
+ }
+
+ //create a channel and use it to exercise the capacity check mechanism
+ AMQChannel channel = new AMQChannel(getSession(), 1, getMessageStore());
+ getQueue().checkCapacity(channel);
+
+ assertTrue(_queueMBean.isFlowOverfull());
+ assertTrue(channel.getBlocking());
+
+ //set FlowResumeCapacity to MESSAGE_SIZE and check queue is now underfull and channel unblocked
+ _queueMBean.setCapacity(MESSAGE_SIZE);//must increase capacity too
+ _queueMBean.setFlowResumeCapacity(MESSAGE_SIZE);
+
+ assertFalse(_queueMBean.isFlowOverfull());
+ assertFalse(channel.getBlocking());
+ }
+
+ private IncomingMessage message(final boolean immediate, boolean persistent) throws AMQException
+ {
+ MessagePublishInfo publish = new MessagePublishInfo()
+ {
+
+ public AMQShortString getExchange()
+ {
+ return null;
+ }
+
+ public void setExchange(AMQShortString exchange)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isImmediate()
+ {
+ return immediate;
+ }
+
+ public boolean isMandatory()
+ {
+ return false;
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return null;
+ }
+ };
+
+ ContentHeaderBody contentHeaderBody = new ContentHeaderBody();
+ contentHeaderBody.bodySize = MESSAGE_SIZE; // in bytes
+ contentHeaderBody.setProperties(new BasicContentHeaderProperties());
+ ((BasicContentHeaderProperties) contentHeaderBody.getProperties()).setDeliveryMode((byte) (persistent ? 2 : 1));
+ IncomingMessage msg = new IncomingMessage(publish);
+ msg.setContentHeaderBody(contentHeaderBody);
+ return msg;
+
+ }
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ _queueMBean = new AMQQueueMBean(getQueue());
+ }
+
+ public void tearDown()
+ {
+ ApplicationRegistry.remove();
+ }
+
+ private void sendMessages(int messageCount, boolean persistent) throws AMQException
+ {
+ for (int i = 0; i < messageCount; i++)
+ {
+ IncomingMessage currentMessage = message(false, persistent);
+ ArrayList<AMQQueue> qs = new ArrayList<AMQQueue>();
+ qs.add(getQueue());
+ currentMessage.enqueue(qs);
+
+ // route header
+ MessageMetaData mmd = currentMessage.headersReceived();
+ currentMessage.setStoredMessage(getMessageStore().addMessage(mmd));
+
+ // Add the body so we have somthing to test later
+ currentMessage.addContentBodyFrame(
+ getSession().getMethodRegistry()
+ .getProtocolVersionMethodConverter()
+ .convertToContentChunk(
+ new ContentBody(ByteBuffer.allocate((int) MESSAGE_SIZE),
+ MESSAGE_SIZE)));
+
+ AMQMessage m = new AMQMessage(currentMessage.getStoredMessage());
+ for(BaseQueue q : currentMessage.getDestinationQueues())
+ {
+ q.enqueue(m);
+ }
+
+
+ }
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AckTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AckTest.java
new file mode 100644
index 0000000000..0f5374b3e5
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AckTest.java
@@ -0,0 +1,427 @@
+/*
+ *
+ * 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.queue;
+
+import junit.framework.TestCase;
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.message.AMQMessage;
+import org.apache.qpid.server.message.MessageMetaData;
+import org.apache.qpid.server.txn.ServerTransaction;
+import org.apache.qpid.server.txn.AutoCommitTransaction;
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.server.protocol.InternalTestProtocolSession;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.subscription.Subscription;
+import org.apache.qpid.server.subscription.SubscriptionFactoryImpl;
+import org.apache.qpid.server.flow.LimitlessCreditManager;
+import org.apache.qpid.server.flow.Pre0_10CreditManager;
+import org.apache.qpid.server.ack.UnacknowledgedMessageMap;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.store.TestMemoryMessageStore;
+
+import java.util.ArrayList;
+import java.util.Set;
+
+/**
+ * Tests that acknowledgements are handled correctly.
+ */
+public class AckTest extends InternalBrokerBaseCase
+{
+ private static final Logger _log = Logger.getLogger(AckTest.class);
+
+ private Subscription _subscription;
+
+ private AMQProtocolSession _protocolSession;
+
+ private TestMemoryMessageStore _messageStore;
+
+ private AMQChannel _channel;
+
+ private AMQQueue _queue;
+
+ private static final AMQShortString DEFAULT_CONSUMER_TAG = new AMQShortString("conTag");
+ private VirtualHost _virtualHost;
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ _virtualHost = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test");
+ _messageStore = new TestMemoryMessageStore();
+ _protocolSession = new InternalTestProtocolSession(_virtualHost);
+ _channel = new AMQChannel(_protocolSession,5, _messageStore /*dont need exchange registry*/);
+
+ _protocolSession.addChannel(_channel);
+
+ _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("myQ"), false, new AMQShortString("guest"), true, false,
+ _virtualHost, null);
+ }
+
+ private void publishMessages(int count) throws AMQException
+ {
+ publishMessages(count, false);
+ }
+
+ private void publishMessages(int count, boolean persistent) throws AMQException
+ {
+ _queue.registerSubscription(_subscription,false);
+ for (int i = 1; i <= count; i++)
+ {
+ // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0)
+ // TODO: Establish some way to determine the version for the test.
+ MessagePublishInfo publishBody = new MessagePublishInfo()
+ {
+
+ public AMQShortString getExchange()
+ {
+ return new AMQShortString("someExchange");
+ }
+
+ public void setExchange(AMQShortString exchange)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isImmediate()
+ {
+ return false;
+ }
+
+ public boolean isMandatory()
+ {
+ return false;
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return new AMQShortString("rk");
+ }
+ };
+ final IncomingMessage msg = new IncomingMessage(publishBody);
+ //IncomingMessage msg2 = null;
+ BasicContentHeaderProperties b = new BasicContentHeaderProperties();
+ ContentHeaderBody cb = new ContentHeaderBody();
+ cb.setProperties(b);
+
+ if (persistent)
+ {
+ //This is DeliveryMode.PERSISTENT
+ b.setDeliveryMode((byte) 2);
+ }
+
+ msg.setContentHeaderBody(cb);
+
+ // we increment the reference here since we are not delivering the messaging to any queues, which is where
+ // the reference is normally incremented. The test is easier to construct if we have direct access to the
+ // subscription
+ ArrayList<AMQQueue> qs = new ArrayList<AMQQueue>();
+ qs.add(_queue);
+ msg.enqueue(qs);
+ MessageMetaData mmd = msg.headersReceived();
+ msg.setStoredMessage(_messageStore.addMessage(mmd));
+ if(msg.allContentReceived())
+ {
+ ServerTransaction txn = new AutoCommitTransaction(_messageStore);
+ txn.enqueue(_queue, msg, new ServerTransaction.Action() {
+ public void postCommit()
+ {
+ try
+ {
+ _queue.enqueue(new AMQMessage(msg.getStoredMessage()));
+ }
+ catch (AMQException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void onRollback()
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+ });
+
+ }
+ // we manually send the message to the subscription
+ //_subscription.send(new QueueEntry(_queue,msg), _queue);
+ }
+ }
+
+ /**
+ * Tests that the acknowledgements are correctly associated with a channel and
+ * order is preserved when acks are enabled
+ */
+ public void testAckChannelAssociationTest() throws AMQException
+ {
+ _subscription = SubscriptionFactoryImpl.INSTANCE.createSubscription(5, _protocolSession, DEFAULT_CONSUMER_TAG, true, null, false, new LimitlessCreditManager());
+ final int msgCount = 10;
+ publishMessages(msgCount, true);
+
+ UnacknowledgedMessageMap map = _channel.getUnacknowledgedMessageMap();
+ assertEquals("",msgCount,map.size());
+
+ Set<Long> deliveryTagSet = map.getDeliveryTags();
+ int i = 1;
+ for (long deliveryTag : deliveryTagSet)
+ {
+ assertTrue(deliveryTag == i);
+ i++;
+ QueueEntry unackedMsg = map.get(deliveryTag);
+ assertTrue(unackedMsg.getQueue() == _queue);
+ }
+
+ }
+
+ /**
+ * Tests that in no-ack mode no messages are retained
+ */
+ public void testNoAckMode() throws AMQException
+ {
+ // false arg means no acks expected
+ _subscription = SubscriptionFactoryImpl.INSTANCE.createSubscription(5, _protocolSession, DEFAULT_CONSUMER_TAG, false, null, false, new LimitlessCreditManager());
+ final int msgCount = 10;
+ publishMessages(msgCount);
+
+ UnacknowledgedMessageMap map = _channel.getUnacknowledgedMessageMap();
+ assertTrue(map.size() == 0);
+ assertTrue(_messageStore.getMessageCount() == 0);
+
+
+ }
+
+ /**
+ * Tests that in no-ack mode no messages are retained
+ */
+ public void testPersistentNoAckMode() throws AMQException
+ {
+ // false arg means no acks expected
+ _subscription = SubscriptionFactoryImpl.INSTANCE.createSubscription(5, _protocolSession, DEFAULT_CONSUMER_TAG, false,null,false, new LimitlessCreditManager());
+ final int msgCount = 10;
+ publishMessages(msgCount, true);
+
+ UnacknowledgedMessageMap map = _channel.getUnacknowledgedMessageMap();
+ assertTrue(map.size() == 0);
+ assertTrue(_messageStore.getMessageCount() == 0);
+
+
+ }
+
+ /**
+ * Tests that a single acknowledgement is handled correctly (i.e multiple flag not
+ * set case)
+ */
+ public void testSingleAckReceivedTest() throws AMQException
+ {
+ _subscription = SubscriptionFactoryImpl.INSTANCE.createSubscription(5, _protocolSession, DEFAULT_CONSUMER_TAG, true,null,false, new LimitlessCreditManager());
+ final int msgCount = 10;
+ publishMessages(msgCount);
+
+ _channel.acknowledgeMessage(5, false);
+ UnacknowledgedMessageMap map = _channel.getUnacknowledgedMessageMap();
+ assertTrue(map.size() == msgCount - 1);
+
+ Set<Long> deliveryTagSet = map.getDeliveryTags();
+ int i = 1;
+ for (long deliveryTag : deliveryTagSet)
+ {
+ assertTrue(deliveryTag == i);
+ QueueEntry unackedMsg = map.get(deliveryTag);
+ assertTrue(unackedMsg.getQueue() == _queue);
+ // 5 is the delivery tag of the message that *should* be removed
+ if (++i == 5)
+ {
+ ++i;
+ }
+ }
+ }
+
+ /**
+ * Tests that a single acknowledgement is handled correctly (i.e multiple flag not
+ * set case)
+ */
+ public void testMultiAckReceivedTest() throws AMQException
+ {
+ _subscription = SubscriptionFactoryImpl.INSTANCE.createSubscription(5, _protocolSession, DEFAULT_CONSUMER_TAG, true,null,false, new LimitlessCreditManager());
+ final int msgCount = 10;
+ publishMessages(msgCount);
+
+ _channel.acknowledgeMessage(5, true);
+ UnacknowledgedMessageMap map = _channel.getUnacknowledgedMessageMap();
+ assertTrue(map.size() == 5);
+
+ Set<Long> deliveryTagSet = map.getDeliveryTags();
+ int i = 1;
+ for (long deliveryTag : deliveryTagSet)
+ {
+ assertTrue(deliveryTag == i + 5);
+ QueueEntry unackedMsg = map.get(deliveryTag);
+ assertTrue(unackedMsg.getQueue() == _queue);
+ ++i;
+ }
+ }
+
+ /**
+ * Tests that a multiple acknowledgement is handled correctly. When ack'ing all pending msgs.
+ */
+ public void testMultiAckAllReceivedTest() throws AMQException
+ {
+ _subscription = SubscriptionFactoryImpl.INSTANCE.createSubscription(5, _protocolSession, DEFAULT_CONSUMER_TAG, true,null,false, new LimitlessCreditManager());
+ final int msgCount = 10;
+ publishMessages(msgCount);
+
+ _channel.acknowledgeMessage(0, true);
+ UnacknowledgedMessageMap map = _channel.getUnacknowledgedMessageMap();
+ assertTrue(map.size() == 0);
+
+ Set<Long> deliveryTagSet = map.getDeliveryTags();
+ int i = 1;
+ for (long deliveryTag : deliveryTagSet)
+ {
+ assertTrue(deliveryTag == i + 5);
+ QueueEntry unackedMsg = map.get(deliveryTag);
+ assertTrue(unackedMsg.getQueue() == _queue);
+ ++i;
+ }
+ }
+
+ /**
+ * A regression fixing QPID-1136 showed this up
+ *
+ * @throws Exception
+ */
+ public void testMessageDequeueRestoresCreditTest() throws Exception
+ {
+ // Send 10 messages
+ Pre0_10CreditManager creditManager = new Pre0_10CreditManager(0l, 1);
+
+ _subscription = SubscriptionFactoryImpl.INSTANCE.createSubscription(5, _protocolSession,
+ DEFAULT_CONSUMER_TAG, true, null, false, creditManager);
+ final int msgCount = 1;
+ publishMessages(msgCount);
+
+ _queue.deliverAsync(_subscription);
+
+ _channel.acknowledgeMessage(1, false);
+
+ // Check credit available
+ assertTrue("No credit available", creditManager.hasCredit());
+
+ }
+
+
+/*
+ public void testPrefetchHighLow() throws AMQException
+ {
+ int lowMark = 5;
+ int highMark = 10;
+
+ _subscription = SubscriptionFactoryImpl.INSTANCE.createSubscription(5, _protocolSession, DEFAULT_CONSUMER_TAG, true,null,false, new LimitlessCreditManager());
+ _channel.setPrefetchLowMarkCount(lowMark);
+ _channel.setPrefetchHighMarkCount(highMark);
+
+ assertTrue(_channel.getPrefetchLowMarkCount() == lowMark);
+ assertTrue(_channel.getPrefetchHighMarkCount() == highMark);
+
+ publishMessages(highMark);
+
+ // at this point we should have sent out only highMark messages
+ // which have not bee received so will be queued up in the channel
+ // which should be suspended
+ assertTrue(_subscription.isSuspended());
+ UnacknowledgedMessageMap map = _channel.getUnacknowledgedMessageMap();
+ assertTrue(map.size() == highMark);
+
+ //acknowledge messages so we are just above lowMark
+ _channel.acknowledgeMessage(lowMark - 1, true);
+
+ //we should still be suspended
+ assertTrue(_subscription.isSuspended());
+ assertTrue(map.size() == lowMark + 1);
+
+ //acknowledge one more message
+ _channel.acknowledgeMessage(lowMark, true);
+
+ //and suspension should be lifted
+ assertTrue(!_subscription.isSuspended());
+
+ //pubilsh more msgs so we are just below the limit
+ publishMessages(lowMark - 1);
+
+ //we should not be suspended
+ assertTrue(!_subscription.isSuspended());
+
+ //acknowledge all messages
+ _channel.acknowledgeMessage(0, true);
+ try
+ {
+ Thread.sleep(3000);
+ }
+ catch (InterruptedException e)
+ {
+ _log.error("Error: " + e, e);
+ }
+ //map will be empty
+ assertTrue(map.size() == 0);
+ }
+
+*/
+/*
+ public void testPrefetch() throws AMQException
+ {
+ _subscription = SubscriptionFactoryImpl.INSTANCE.createSubscription(5, _protocolSession, DEFAULT_CONSUMER_TAG, true,null,false, new LimitlessCreditManager());
+ _channel.setMessageCredit(5);
+
+ assertTrue(_channel.getPrefetchCount() == 5);
+
+ final int msgCount = 5;
+ publishMessages(msgCount);
+
+ // at this point we should have sent out only 5 messages with a further 5 queued
+ // up in the channel which should now be suspended
+ assertTrue(_subscription.isSuspended());
+ UnacknowledgedMessageMap map = _channel.getUnacknowledgedMessageMap();
+ assertTrue(map.size() == 5);
+ _channel.acknowledgeMessage(5, true);
+ assertTrue(!_subscription.isSuspended());
+ try
+ {
+ Thread.sleep(3000);
+ }
+ catch (InterruptedException e)
+ {
+ _log.error("Error: " + e, e);
+ }
+ assertTrue(map.size() == 0);
+ }
+
+*/
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(AckTest.class);
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQMessage.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQMessage.java
new file mode 100644
index 0000000000..7000df157e
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQMessage.java
@@ -0,0 +1,43 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.message.AMQMessage;
+
+public class MockAMQMessage extends AMQMessage
+{
+ public MockAMQMessage(long messageId)
+ throws AMQException
+ {
+ super(new MockStoredMessage(messageId));
+ }
+
+
+
+
+ @Override
+ public long getSize()
+ {
+ return 0l;
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java
new file mode 100644
index 0000000000..888a16053c
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java
@@ -0,0 +1,614 @@
+/*
+ *
+ * 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.queue;
+
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.configuration.*;
+import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin;
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.logging.LogSubject;
+import org.apache.qpid.server.subscription.Subscription;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.server.management.ManagedObject;
+import org.apache.qpid.server.message.ServerMessage;
+import org.apache.qpid.server.security.PrincipalHolder;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.protocol.AMQSessionModel;
+import org.apache.qpid.server.binding.Binding;
+import org.apache.qpid.server.txn.ServerTransaction;
+import org.apache.qpid.AMQException;
+
+import java.util.List;
+import java.util.Set;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+public class MockAMQQueue implements AMQQueue
+{
+ private boolean _deleted = false;
+ private AMQShortString _name;
+ private VirtualHost _virtualhost;
+
+ private PrincipalHolder _principalHolder;
+
+ private AMQSessionModel _exclusiveOwner;
+ private AMQShortString _owner;
+ private List<Binding> _bindings = new CopyOnWriteArrayList<Binding>();
+ private boolean _autoDelete;
+
+ public MockAMQQueue(String name)
+ {
+ _name = new AMQShortString(name);
+ }
+
+ public boolean getDeleteOnNoConsumers()
+ {
+ return false;
+ }
+
+ public void setDeleteOnNoConsumers(boolean b)
+ {
+ }
+
+ public void addBinding(final Binding binding)
+ {
+ _bindings.add(binding);
+ }
+
+ public void removeBinding(final Binding binding)
+ {
+ _bindings.remove(binding);
+ }
+
+ public List<Binding> getBindings()
+ {
+ return _bindings;
+ }
+
+ public int getBindingCount()
+ {
+ return 0;
+ }
+
+ public LogSubject getLogSubject()
+ {
+ return new LogSubject()
+ {
+ public String toLogString()
+ {
+ return "[MockAMQQueue]";
+ }
+
+ };
+ }
+
+ public ConfigStore getConfigStore()
+ {
+ return getVirtualHost().getConfigStore();
+ }
+
+ public long getMessageDequeueCount()
+ {
+ return 0;
+ }
+
+ public long getTotalEnqueueSize()
+ {
+ return 0;
+ }
+
+ public long getTotalDequeueSize()
+ {
+ return 0;
+ }
+
+ public int getBindingCountHigh()
+ {
+ return 0;
+ }
+
+ public long getPersistentByteEnqueues()
+ {
+ return 0;
+ }
+
+ public long getPersistentByteDequeues()
+ {
+ return 0;
+ }
+
+ public long getPersistentMsgEnqueues()
+ {
+ return 0;
+ }
+
+ public long getPersistentMsgDequeues()
+ {
+ return 0;
+ }
+
+ public void purge(final long request)
+ {
+
+ }
+
+ public long getCreateTime()
+ {
+ return 0;
+ }
+
+ public AMQShortString getNameShortString()
+ {
+ return _name;
+ }
+
+ public void setNoLocal(boolean b)
+ {
+
+ }
+
+ public UUID getId()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public QueueConfigType getConfigType()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public ConfiguredObject getParent()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isDurable()
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isAutoDelete()
+ {
+ return _autoDelete;
+ }
+
+ public void setAutoDelete(boolean autodelete)
+ {
+ _autoDelete = autodelete;
+ }
+
+
+ public AMQShortString getOwner()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void setVirtualHost(VirtualHost virtualhost)
+ {
+ _virtualhost = virtualhost;
+ }
+
+ public VirtualHost getVirtualHost()
+ {
+ return _virtualhost;
+ }
+
+ public String getName()
+ {
+ return _name.asString();
+ }
+
+ public void registerSubscription(Subscription subscription, boolean exclusive) throws AMQException
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void unregisterSubscription(Subscription subscription) throws AMQException
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public int getConsumerCount()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public int getActiveConsumerCount()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean hasExclusiveSubscriber()
+ {
+ return false;
+ }
+
+ public boolean isUnused()
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isEmpty()
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public int getMessageCount()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public int getUndeliveredMessageCount()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public long getQueueDepth()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public long getReceivedMessageCount()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public long getOldestMessageArrivalTime()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isDeleted()
+ {
+ return _deleted;
+ }
+
+ public int delete() throws AMQException
+ {
+ _deleted = true;
+ return getMessageCount();
+ }
+
+ public void enqueue(ServerMessage message) throws AMQException
+ {
+ }
+
+ public void enqueue(ServerMessage message, PostEnqueueAction action) throws AMQException
+ {
+ }
+
+
+ public void requeue(QueueEntry entry)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void requeue(QueueEntryImpl storeContext, Subscription subscription)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void dequeue(QueueEntry entry, Subscription sub)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean resend(QueueEntry entry, Subscription subscription) throws AMQException
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void addQueueDeleteTask(Task task)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void removeQueueDeleteTask(final Task task)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public List<QueueEntry> getMessagesOnTheQueue()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public List<QueueEntry> getMessagesOnTheQueue(long fromMessageId, long toMessageId)
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public List<Long> getMessagesOnTheQueue(int num)
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public List<Long> getMessagesOnTheQueue(int num, int offest)
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public QueueEntry getMessageOnTheQueue(long messageId)
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public List<QueueEntry> getMessagesRangeOnTheQueue(long fromPosition, long toPosition)
+ {
+ return null;
+ }
+
+ public void moveMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName, ServerTransaction storeContext)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void copyMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName, ServerTransaction storeContext)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void removeMessagesFromQueue(long fromMessageId, long toMessageId)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public long getMaximumMessageSize()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void setMaximumMessageSize(long value)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public long getMaximumMessageCount()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void setMaximumMessageCount(long value)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public long getMaximumQueueDepth()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void setMaximumQueueDepth(long value)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public long getMaximumMessageAge()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void setMaximumMessageAge(long maximumMessageAge)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean getBlockOnQueueFull()
+ {
+ return false;
+ }
+
+ public void setBlockOnQueueFull(boolean block)
+ {
+ }
+
+ public long getMinimumAlertRepeatGap()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void deleteMessageFromTop()
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public long clearQueue()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+
+ public void checkMessageStatus() throws AMQException
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public Set<NotificationCheck> getNotificationChecks()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void flushSubscription(Subscription sub) throws AMQException
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void deliverAsync(Subscription sub)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void deliverAsync()
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void stop()
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isExclusive()
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public Exchange getAlternateExchange()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void setAlternateExchange(Exchange exchange)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public Map<String, Object> getArguments()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void checkCapacity(AMQChannel channel)
+ {
+ }
+
+ public ManagedObject getManagedObject()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public int compareTo(AMQQueue o)
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void setMinimumAlertRepeatGap(long value)
+ {
+
+ }
+
+ public long getCapacity()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void setCapacity(long capacity)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public long getFlowResumeCapacity()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void setFlowResumeCapacity(long flowResumeCapacity)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void configure(ConfigurationPlugin config)
+ {
+
+ }
+
+ public ConfigurationPlugin getConfiguration()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public PrincipalHolder getPrincipalHolder()
+ {
+ return _principalHolder;
+ }
+
+ public void setPrincipalHolder(PrincipalHolder principalHolder)
+ {
+ _principalHolder = principalHolder;
+ }
+
+ public AMQSessionModel getExclusiveOwningSession()
+ {
+ return _exclusiveOwner;
+ }
+
+ public void setExclusiveOwningSession(AMQSessionModel exclusiveOwner)
+ {
+ _exclusiveOwner = exclusiveOwner;
+ }
+
+
+ public String getResourceName()
+ {
+ return _name.toString();
+ }
+
+ public boolean isOverfull()
+ {
+ return false;
+ }
+
+ public int getConsumerCountHigh()
+ {
+ return 0;
+ }
+
+ public long getByteTxnEnqueues()
+ {
+ return 0;
+ }
+
+ public long getMsgTxnEnqueues()
+ {
+ return 0;
+ }
+
+ public long getByteTxnDequeues()
+ {
+ return 0;
+ }
+
+ public long getMsgTxnDequeues()
+ {
+ return 0;
+ }
+
+ public void decrementUnackedMsgCount()
+ {
+
+ }
+
+ public long getUnackedMessageCount()
+ {
+ return 0;
+ }
+
+ public long getUnackedMessageCountHigh()
+ {
+ return 0;
+ }
+
+ public void setExclusive(boolean exclusive)
+ {
+
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockMessagePublishInfo.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockMessagePublishInfo.java
new file mode 100644
index 0000000000..5a5ffaa14d
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockMessagePublishInfo.java
@@ -0,0 +1,52 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.framing.AMQShortString;
+
+public class MockMessagePublishInfo implements MessagePublishInfo
+{
+ public AMQShortString getExchange()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void setExchange(AMQShortString exchange)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isImmediate()
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isMandatory()
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockQueueEntry.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockQueueEntry.java
new file mode 100644
index 0000000000..5bdbe2c68e
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockQueueEntry.java
@@ -0,0 +1,234 @@
+/*
+*
+ * 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.queue;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.subscription.Subscription;
+import org.apache.qpid.server.message.AMQMessageHeader;
+import org.apache.qpid.server.message.AMQMessage;
+import org.apache.qpid.server.message.ServerMessage;
+
+public class MockQueueEntry implements QueueEntry
+{
+
+ private AMQMessage _message;
+
+ public boolean acquire()
+ {
+ return false;
+ }
+
+ public boolean acquire(Subscription sub)
+ {
+ return false;
+ }
+
+ public boolean acquiredBySubscription()
+ {
+ return false;
+ }
+
+ public boolean isAcquiredBy(Subscription subscription)
+ {
+ return false;
+ }
+
+ public void addStateChangeListener(StateChangeListener listener)
+ {
+
+ }
+
+ public boolean delete()
+ {
+ return false;
+ }
+
+ public void dequeue()
+ {
+
+ }
+
+ public void discard()
+ {
+
+ }
+
+ public void routeToAlternate()
+ {
+
+ }
+
+ public void dispose()
+ {
+
+ }
+
+ public boolean expired() throws AMQException
+ {
+ return false;
+ }
+
+ public boolean isAvailable()
+ {
+ return false;
+ }
+
+ public Subscription getDeliveredSubscription()
+ {
+ return null;
+ }
+
+ public boolean getDeliveredToConsumer()
+ {
+ return false;
+ }
+
+ public ServerMessage getMessage()
+ {
+ return _message;
+ }
+
+ public AMQQueue getQueue()
+ {
+ return null;
+ }
+
+ public long getSize()
+ {
+ return 0;
+ }
+
+ public boolean immediateAndNotDelivered()
+ {
+ return false;
+ }
+
+ public boolean isAcquired()
+ {
+ return false;
+ }
+
+ public boolean isDeleted()
+ {
+ return false;
+ }
+
+
+ public boolean isQueueDeleted()
+ {
+
+ return false;
+ }
+
+
+ public boolean isRejectedBy(Subscription subscription)
+ {
+
+ return false;
+ }
+
+
+ public void reject()
+ {
+
+
+ }
+
+
+ public void reject(Subscription subscription)
+ {
+
+
+ }
+
+
+ public void release()
+ {
+
+
+ }
+
+ public boolean releaseButRetain()
+ {
+ return false;
+ }
+
+
+ public boolean removeStateChangeListener(StateChangeListener listener)
+ {
+
+ return false;
+ }
+
+
+ public void requeue()
+ {
+
+
+ }
+
+ public void requeue(Subscription subscription)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+
+ public void setDeliveredToSubscription()
+ {
+
+
+ }
+
+
+ public void setRedelivered()
+ {
+
+
+ }
+
+ public AMQMessageHeader getMessageHeader()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isPersistent()
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isRedelivered()
+ {
+ return false;
+ }
+
+
+ public int compareTo(QueueEntry o)
+ {
+
+ return 0;
+ }
+
+ public void setMessage(AMQMessage msg)
+ {
+ _message = msg;
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockStoredMessage.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockStoredMessage.java
new file mode 100755
index 0000000000..7dc491de4d
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockStoredMessage.java
@@ -0,0 +1,92 @@
+/*
+*
+* 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.queue;
+
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.store.TransactionLog;
+import org.apache.qpid.server.store.StoredMessage;
+import org.apache.qpid.server.message.MessageMetaData;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+
+import java.nio.ByteBuffer;
+
+public class MockStoredMessage implements StoredMessage<MessageMetaData>
+{
+ private long _messageId;
+ private MessageMetaData _metaData;
+ private final ByteBuffer _content;
+
+
+ public MockStoredMessage(long messageId)
+ {
+ this(messageId, new MockMessagePublishInfo(), new ContentHeaderBody(new BasicContentHeaderProperties(), 60));
+ }
+
+ public MockStoredMessage(long messageId, MessagePublishInfo info, ContentHeaderBody chb)
+ {
+ _messageId = messageId;
+ _metaData = new MessageMetaData(info, chb, 0);
+ _content = ByteBuffer.allocate(_metaData.getContentSize());
+
+ }
+
+ public MessageMetaData getMetaData()
+ {
+ return _metaData;
+ }
+
+ public long getMessageNumber()
+ {
+ return _messageId;
+ }
+
+ public void addContent(int offsetInMessage, ByteBuffer src)
+ {
+ src = src.duplicate();
+ ByteBuffer dst = _content.duplicate();
+ dst.position(offsetInMessage);
+ dst.put(src);
+ }
+
+ public int getContent(int offset, ByteBuffer dst)
+ {
+ ByteBuffer src = _content.duplicate();
+ src.position(offset);
+ src = src.slice();
+ if(dst.remaining() < src.limit())
+ {
+ src.limit(dst.remaining());
+ }
+ dst.put(src);
+ return src.limit();
+ }
+
+ public TransactionLog.StoreFuture flushToStore()
+ {
+ return MessageStore.IMMEDIATE_FUTURE;
+ }
+
+ public void remove()
+ {
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/QueueEntryTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/QueueEntryTest.java
new file mode 100644
index 0000000000..b67723dd25
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/QueueEntryTest.java
@@ -0,0 +1,97 @@
+/*
+ *
+ * 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.queue;
+
+import org.apache.qpid.test.utils.QpidTestCase;
+
+/**
+ *
+ * Tests QueueEntry
+ *
+ */
+public class QueueEntryTest extends QpidTestCase
+{
+ private QueueEntryImpl _queueEntry1 = null;
+ private QueueEntryImpl _queueEntry2 = null;
+ private QueueEntryImpl _queueEntry3 = null;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ int i = 0;
+
+ SimpleQueueEntryList queueEntryList = new SimpleQueueEntryList(null);
+ _queueEntry1 = (QueueEntryImpl) queueEntryList.add(new MockAMQMessage(i++));
+ _queueEntry2 = (QueueEntryImpl) queueEntryList.add(new MockAMQMessage(i++));
+ _queueEntry3 = (QueueEntryImpl) queueEntryList.add(new MockAMQMessage(i++));
+ }
+
+ public void testCompareTo()
+ {
+ assertTrue(_queueEntry1.compareTo(_queueEntry2) < 0);
+ assertTrue(_queueEntry2.compareTo(_queueEntry1) > 0);
+ assertTrue(_queueEntry1.compareTo(_queueEntry1) == 0);
+ }
+
+ /**
+ * Tests that the getNext() can be used to traverse the list.
+ */
+ public void testTraverseWithNoDeletedEntries()
+ {
+ QueueEntryImpl current = _queueEntry1;
+
+ current = current.getNext();
+ assertSame("Unexpected current entry",_queueEntry2, current);
+
+ current = current.getNext();
+ assertSame("Unexpected current entry",_queueEntry3, current);
+
+ current = current.getNext();
+ assertNull(current);
+
+ }
+
+ /**
+ * Tests that the getNext() can be used to traverse the list but deleted
+ * entries are skipped and de-linked from the chain of entries.
+ */
+ public void testTraverseWithDeletedEntries()
+ {
+ // Delete 2nd queue entry
+ _queueEntry2.delete();
+ assertTrue(_queueEntry2.isDeleted());
+
+
+ QueueEntryImpl current = _queueEntry1;
+
+ current = current.getNext();
+ assertSame("Unexpected current entry",_queueEntry3, current);
+
+ current = current.getNext();
+ assertNull(current);
+
+ // Assert the side effects of getNext()
+ assertSame("Next node of entry 1 should now be entry 3",
+ _queueEntry3, _queueEntry1.nextNode());
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java
new file mode 100644
index 0000000000..abe2d1728f
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java
@@ -0,0 +1,817 @@
+/*
+ *
+ * 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.queue;
+
+import org.apache.commons.configuration.PropertiesConfiguration;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.AMQInternalException;
+import org.apache.qpid.AMQSecurityException;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.server.configuration.VirtualHostConfiguration;
+import org.apache.qpid.server.exchange.DirectExchange;
+import org.apache.qpid.server.message.AMQMessage;
+import org.apache.qpid.server.message.MessageMetaData;
+import org.apache.qpid.server.queue.BaseQueue.PostEnqueueAction;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.store.StoredMessage;
+import org.apache.qpid.server.store.TestableMemoryMessageStore;
+import org.apache.qpid.server.subscription.MockSubscription;
+import org.apache.qpid.server.subscription.Subscription;
+import org.apache.qpid.server.txn.AutoCommitTransaction;
+import org.apache.qpid.server.txn.ServerTransaction;
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.server.virtualhost.VirtualHostImpl;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class SimpleAMQQueueTest extends InternalBrokerBaseCase
+{
+
+ protected SimpleAMQQueue _queue;
+ protected VirtualHost _virtualHost;
+ protected TestableMemoryMessageStore _store = new TestableMemoryMessageStore();
+ protected AMQShortString _qname = new AMQShortString("qname");
+ protected AMQShortString _owner = new AMQShortString("owner");
+ protected AMQShortString _routingKey = new AMQShortString("routing key");
+ protected DirectExchange _exchange;
+ protected MockSubscription _subscription = new MockSubscription();
+ protected FieldTable _arguments = null;
+
+ MessagePublishInfo info = new MessagePublishInfo()
+ {
+
+ public AMQShortString getExchange()
+ {
+ return null;
+ }
+
+ public void setExchange(AMQShortString exchange)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isImmediate()
+ {
+ return false;
+ }
+
+ public boolean isMandatory()
+ {
+ return false;
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return null;
+ }
+ };
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ //Create Application Registry for test
+ ApplicationRegistry applicationRegistry = (ApplicationRegistry)ApplicationRegistry.getInstance();
+
+ PropertiesConfiguration env = new PropertiesConfiguration();
+ _virtualHost = new VirtualHostImpl(new VirtualHostConfiguration(getClass().getName(), env), _store);
+ applicationRegistry.getVirtualHostRegistry().registerVirtualHost(_virtualHost);
+
+ _queue = (SimpleAMQQueue) AMQQueueFactory.createAMQQueueImpl(_qname, false, _owner, false, false, _virtualHost, _arguments);
+
+ _exchange = (DirectExchange)_virtualHost.getExchangeRegistry().getExchange(ExchangeDefaults.DIRECT_EXCHANGE_NAME);
+ }
+
+ @Override
+ public void tearDown() throws Exception
+ {
+ _queue.stop();
+ super.tearDown();
+ }
+
+ public void testCreateQueue() throws AMQException
+ {
+ _queue.stop();
+ try {
+ _queue = (SimpleAMQQueue) AMQQueueFactory.createAMQQueueImpl(null, false, _owner, false, false, _virtualHost, _arguments );
+ assertNull("Queue was created", _queue);
+ }
+ catch (IllegalArgumentException e)
+ {
+ assertTrue("Exception was not about missing name",
+ e.getMessage().contains("name"));
+ }
+
+ try {
+ _queue = new SimpleAMQQueue(_qname, false, _owner, false, false,null, Collections.EMPTY_MAP);
+ assertNull("Queue was created", _queue);
+ }
+ catch (IllegalArgumentException e)
+ {
+ assertTrue("Exception was not about missing vhost",
+ e.getMessage().contains("Host"));
+ }
+
+ _queue = (SimpleAMQQueue) AMQQueueFactory.createAMQQueueImpl(_qname, false, _owner, false,
+ false, _virtualHost, _arguments);
+ assertNotNull("Queue was not created", _queue);
+ }
+
+ public void testGetVirtualHost()
+ {
+ assertEquals("Virtual host was wrong", _virtualHost, _queue.getVirtualHost());
+ }
+
+ public void testBinding() throws AMQSecurityException, AMQInternalException
+ {
+ _virtualHost.getBindingFactory().addBinding(String.valueOf(_routingKey), _queue, _exchange, Collections.EMPTY_MAP);
+
+ assertTrue("Routing key was not bound",
+ _exchange.isBound(_routingKey));
+ assertTrue("Queue was not bound to key",
+ _exchange.isBound(_routingKey,_queue));
+ assertEquals("Exchange binding count", 1,
+ _queue.getBindings().size());
+ assertEquals("Wrong exchange bound", String.valueOf(_routingKey),
+ _queue.getBindings().get(0).getBindingKey());
+ assertEquals("Wrong exchange bound", _exchange,
+ _queue.getBindings().get(0).getExchange());
+
+ _virtualHost.getBindingFactory().removeBinding(String.valueOf(_routingKey), _queue, _exchange, Collections.EMPTY_MAP);
+ assertFalse("Routing key was still bound",
+ _exchange.isBound(_routingKey));
+
+ }
+
+ public void testRegisterSubscriptionThenEnqueueMessage() throws AMQException
+ {
+ // Check adding a subscription adds it to the queue
+ _queue.registerSubscription(_subscription, false);
+ assertEquals("Subscription did not get queue", _queue,
+ _subscription.getQueue());
+ assertEquals("Queue does not have consumer", 1,
+ _queue.getConsumerCount());
+ assertEquals("Queue does not have active consumer", 1,
+ _queue.getActiveConsumerCount());
+
+ // Check sending a message ends up with the subscriber
+ AMQMessage messageA = createMessage(new Long(24));
+ _queue.enqueue(messageA);
+ assertEquals(messageA, _subscription.getQueueContext().getLastSeenEntry().getMessage());
+ assertNull(((QueueContext)_subscription.getQueueContext())._releasedEntry);
+
+ // Check removing the subscription removes it's information from the queue
+ _queue.unregisterSubscription(_subscription);
+ assertTrue("Subscription still had queue", _subscription.isClosed());
+ assertFalse("Queue still has consumer", 1 == _queue.getConsumerCount());
+ assertFalse("Queue still has active consumer",
+ 1 == _queue.getActiveConsumerCount());
+
+ AMQMessage messageB = createMessage(new Long (25));
+ _queue.enqueue(messageB);
+ assertNull(_subscription.getQueueContext());
+
+ }
+
+ public void testEnqueueMessageThenRegisterSubscription() throws AMQException, InterruptedException
+ {
+ AMQMessage messageA = createMessage(new Long(24));
+ _queue.enqueue(messageA);
+ _queue.registerSubscription(_subscription, false);
+ Thread.sleep(150);
+ assertEquals(messageA, _subscription.getQueueContext().getLastSeenEntry().getMessage());
+ assertNull("There should be no releasedEntry after an enqueue", ((QueueContext)_subscription.getQueueContext())._releasedEntry);
+ }
+
+ /**
+ * Tests enqueuing two messages.
+ */
+ public void testEnqueueTwoMessagesThenRegisterSubscription() throws Exception
+ {
+ AMQMessage messageA = createMessage(new Long(24));
+ AMQMessage messageB = createMessage(new Long(25));
+ _queue.enqueue(messageA);
+ _queue.enqueue(messageB);
+ _queue.registerSubscription(_subscription, false);
+ Thread.sleep(150);
+ assertEquals(messageB, _subscription.getQueueContext().getLastSeenEntry().getMessage());
+ assertNull("There should be no releasedEntry after enqueues", ((QueueContext)_subscription.getQueueContext())._releasedEntry);
+ }
+
+ /**
+ * Tests that a released queue entry is resent to the subscriber. Verifies also that the
+ * QueueContext._releasedEntry is reset to null after the entry has been reset.
+ */
+ public void testReleasedMessageIsResentToSubscriber() throws Exception
+ {
+ _queue.registerSubscription(_subscription, false);
+
+ final ArrayList<QueueEntry> queueEntries = new ArrayList<QueueEntry>();
+ PostEnqueueAction postEnqueueAction = new PostEnqueueAction()
+ {
+ public void onEnqueue(QueueEntry entry)
+ {
+ queueEntries.add(entry);
+ }
+ };
+
+ AMQMessage messageA = createMessage(new Long(24));
+ AMQMessage messageB = createMessage(new Long(25));
+ AMQMessage messageC = createMessage(new Long(26));
+
+ /* Enqueue three messages */
+
+ _queue.enqueue(messageA, postEnqueueAction);
+ _queue.enqueue(messageB, postEnqueueAction);
+ _queue.enqueue(messageC, postEnqueueAction);
+
+ Thread.sleep(150); // Work done by SubFlushRunner/QueueRunner Threads
+
+ assertEquals("Unexpected total number of messages sent to subscription", 3, _subscription.getMessages().size());
+ assertFalse("Redelivery flag should not be set", queueEntries.get(0).isRedelivered());
+ assertFalse("Redelivery flag should not be set", queueEntries.get(1).isRedelivered());
+ assertFalse("Redelivery flag should not be set", queueEntries.get(2).isRedelivered());
+
+ /* Now release the first message only, causing it to be requeued */
+
+ queueEntries.get(0).release();
+
+ Thread.sleep(150); // Work done by SubFlushRunner/QueueRunner Threads
+
+ assertEquals("Unexpected total number of messages sent to subscription", 4, _subscription.getMessages().size());
+ assertTrue("Redelivery flag should now be set", queueEntries.get(0).isRedelivered());
+ assertFalse("Redelivery flag should remain be unset", queueEntries.get(1).isRedelivered());
+ assertFalse("Redelivery flag should remain be unset",queueEntries.get(2).isRedelivered());
+ assertNull("releasedEntry should be cleared after requeue processed", ((QueueContext)_subscription.getQueueContext())._releasedEntry);
+ }
+
+ /**
+ * Tests that a released message that becomes expired is not resent to the subscriber.
+ * This tests ensures that SimpleAMQQueueEntry.getNextAvailableEntry avoids expired entries.
+ * Verifies also that the QueueContext._releasedEntry is reset to null after the entry has been reset.
+ */
+ public void testReleaseMessageThatBecomesExpiredIsNotRedelivered() throws Exception
+ {
+ _queue.registerSubscription(_subscription, false);
+
+ final ArrayList<QueueEntry> queueEntries = new ArrayList<QueueEntry>();
+ PostEnqueueAction postEnqueueAction = new PostEnqueueAction()
+ {
+ public void onEnqueue(QueueEntry entry)
+ {
+ queueEntries.add(entry);
+ }
+ };
+
+ /* Enqueue one message with expiration set for a short time in the future */
+
+ AMQMessage messageA = createMessage(new Long(24));
+ int messageExpirationOffset = 200;
+ messageA.setExpiration(System.currentTimeMillis() + messageExpirationOffset);
+
+ _queue.enqueue(messageA, postEnqueueAction);
+
+ int subFlushWaitTime = 150;
+ Thread.sleep(subFlushWaitTime); // Work done by SubFlushRunner/QueueRunner Threads
+
+ assertEquals("Unexpected total number of messages sent to subscription", 1, _subscription.getMessages().size());
+ assertFalse("Redelivery flag should not be set", queueEntries.get(0).isRedelivered());
+
+ /* Wait a little more to be sure that message will have expired, then release the first message only, causing it to be requeued */
+ Thread.sleep(messageExpirationOffset - subFlushWaitTime + 10);
+ queueEntries.get(0).release();
+
+ Thread.sleep(subFlushWaitTime); // Work done by SubFlushRunner/QueueRunner Threads
+
+ assertTrue("Expecting the queue entry to be now expired", queueEntries.get(0).expired());
+ assertEquals("Total number of messages sent should not have changed", 1, _subscription.getMessages().size());
+ assertFalse("Redelivery flag should not be set", queueEntries.get(0).isRedelivered());
+ assertNull("releasedEntry should be cleared after requeue processed", ((QueueContext)_subscription.getQueueContext())._releasedEntry);
+
+ }
+
+ /**
+ * Tests that if a client releases entries 'out of order' (the order
+ * used by QueueEntryImpl.compareTo) that messages are still resent
+ * successfully. Specifically this test ensures the {@see SimpleAMQQueue#requeue()}
+ * can correctly move the _releasedEntry to an earlier position in the QueueEntry list.
+ */
+ public void testReleasedOutOfComparableOrderAreRedelivered() throws Exception
+ {
+ _queue.registerSubscription(_subscription, false);
+
+ final ArrayList<QueueEntry> queueEntries = new ArrayList<QueueEntry>();
+ PostEnqueueAction postEnqueueAction = new PostEnqueueAction()
+ {
+ public void onEnqueue(QueueEntry entry)
+ {
+ queueEntries.add(entry);
+ }
+ };
+
+ AMQMessage messageA = createMessage(new Long(24));
+ AMQMessage messageB = createMessage(new Long(25));
+ AMQMessage messageC = createMessage(new Long(26));
+
+ /* Enqueue three messages */
+
+ _queue.enqueue(messageA, postEnqueueAction);
+ _queue.enqueue(messageB, postEnqueueAction);
+ _queue.enqueue(messageC, postEnqueueAction);
+
+ Thread.sleep(150); // Work done by SubFlushRunner/QueueRunner Threads
+
+ assertEquals("Unexpected total number of messages sent to subscription", 3, _subscription.getMessages().size());
+ assertFalse("Redelivery flag should not be set", queueEntries.get(0).isRedelivered());
+ assertFalse("Redelivery flag should not be set", queueEntries.get(1).isRedelivered());
+ assertFalse("Redelivery flag should not be set", queueEntries.get(2).isRedelivered());
+
+ /* Now release the third and first message only, causing it to be requeued */
+
+ queueEntries.get(2).release();
+ queueEntries.get(0).release();
+
+ Thread.sleep(150); // Work done by SubFlushRunner/QueueRunner Threads
+
+ assertEquals("Unexpected total number of messages sent to subscription", 5, _subscription.getMessages().size());
+ assertTrue("Redelivery flag should now be set", queueEntries.get(0).isRedelivered());
+ assertFalse("Redelivery flag should remain be unset", queueEntries.get(1).isRedelivered());
+ assertTrue("Redelivery flag should now be set",queueEntries.get(2).isRedelivered());
+ assertNull("releasedEntry should be cleared after requeue processed", ((QueueContext)_subscription.getQueueContext())._releasedEntry);
+ }
+
+
+ /**
+ * Tests that a release requeues an entry for a queue with multiple subscriptions. Verifies that a
+ * requeue resends a message to a <i>single</i> subscriber.
+ */
+ public void testReleaseForQueueWithMultipleSubscriptions() throws Exception
+ {
+ MockSubscription subscription1 = new MockSubscription();
+ MockSubscription subscription2 = new MockSubscription();
+
+ _queue.registerSubscription(subscription1, false);
+ _queue.registerSubscription(subscription2, false);
+
+ final ArrayList<QueueEntry> queueEntries = new ArrayList<QueueEntry>();
+ PostEnqueueAction postEnqueueAction = new PostEnqueueAction()
+ {
+ public void onEnqueue(QueueEntry entry)
+ {
+ queueEntries.add(entry);
+ }
+ };
+
+ AMQMessage messageA = createMessage(new Long(24));
+ AMQMessage messageB = createMessage(new Long(25));
+
+ /* Enqueue two messages */
+
+ _queue.enqueue(messageA, postEnqueueAction);
+ _queue.enqueue(messageB, postEnqueueAction);
+
+ Thread.sleep(150); // Work done by SubFlushRunner/QueueRunner Threads
+
+ assertEquals("Unexpected total number of messages sent to both after enqueue", 2, subscription1.getMessages().size() + subscription2.getMessages().size());
+
+ /* Now release the first message only, causing it to be requeued */
+ queueEntries.get(0).release();
+
+ Thread.sleep(150); // Work done by SubFlushRunner/QueueRunner Threads
+
+ assertEquals("Unexpected total number of messages sent to both subscriptions after release", 3, subscription1.getMessages().size() + subscription2.getMessages().size());
+ assertNull("releasedEntry should be cleared after requeue processed", ((QueueContext)subscription1.getQueueContext())._releasedEntry);
+ assertNull("releasedEntry should be cleared after requeue processed", ((QueueContext)subscription2.getQueueContext())._releasedEntry);
+ }
+
+ public void testExclusiveConsumer() throws AMQException
+ {
+ // Check adding an exclusive subscription adds it to the queue
+ _queue.registerSubscription(_subscription, true);
+ assertEquals("Subscription did not get queue", _queue,
+ _subscription.getQueue());
+ assertEquals("Queue does not have consumer", 1,
+ _queue.getConsumerCount());
+ assertEquals("Queue does not have active consumer", 1,
+ _queue.getActiveConsumerCount());
+
+ // Check sending a message ends up with the subscriber
+ AMQMessage messageA = createMessage(new Long(24));
+ _queue.enqueue(messageA);
+ assertEquals(messageA, _subscription.getQueueContext().getLastSeenEntry().getMessage());
+
+ // Check we cannot add a second subscriber to the queue
+ Subscription subB = new MockSubscription();
+ Exception ex = null;
+ try
+ {
+ _queue.registerSubscription(subB, false);
+ }
+ catch (AMQException e)
+ {
+ ex = e;
+ }
+ assertNotNull(ex);
+
+ // Check we cannot add an exclusive subscriber to a queue with an
+ // existing subscription
+ _queue.unregisterSubscription(_subscription);
+ _queue.registerSubscription(_subscription, false);
+ try
+ {
+ _queue.registerSubscription(subB, true);
+ }
+ catch (AMQException e)
+ {
+ ex = e;
+ }
+ assertNotNull(ex);
+ }
+
+ public void testAutoDeleteQueue() throws Exception
+ {
+ _queue.stop();
+ _queue = new SimpleAMQQueue(_qname, false, null, true, false, _virtualHost, Collections.EMPTY_MAP);
+ _queue.setDeleteOnNoConsumers(true);
+ _queue.registerSubscription(_subscription, false);
+ AMQMessage message = createMessage(new Long(25));
+ _queue.enqueue(message);
+ _queue.unregisterSubscription(_subscription);
+ assertTrue("Queue was not deleted when subscription was removed",
+ _queue.isDeleted());
+ }
+
+ public void testResend() throws Exception
+ {
+ _queue.registerSubscription(_subscription, false);
+ Long id = new Long(26);
+ AMQMessage message = createMessage(id);
+ _queue.enqueue(message);
+ QueueEntry entry = _subscription.getQueueContext().getLastSeenEntry();
+ entry.setRedelivered();
+ _queue.resend(entry, _subscription);
+
+ }
+
+ public void testGetFirstMessageId() throws Exception
+ {
+ // Create message
+ Long messageId = new Long(23);
+ AMQMessage message = createMessage(messageId);
+
+ // Put message on queue
+ _queue.enqueue(message);
+ // Get message id
+ Long testmsgid = _queue.getMessagesOnTheQueue(1).get(0);
+
+ // Check message id
+ assertEquals("Message ID was wrong", messageId, testmsgid);
+ }
+
+ public void testGetFirstFiveMessageIds() throws Exception
+ {
+ for (int i = 0 ; i < 5; i++)
+ {
+ // Create message
+ Long messageId = new Long(i);
+ AMQMessage message = createMessage(messageId);
+ // Put message on queue
+ _queue.enqueue(message);
+ }
+ // Get message ids
+ List<Long> msgids = _queue.getMessagesOnTheQueue(5);
+
+ // Check message id
+ for (int i = 0; i < 5; i++)
+ {
+ Long messageId = new Long(i);
+ assertEquals("Message ID was wrong", messageId, msgids.get(i));
+ }
+ }
+
+ public void testGetLastFiveMessageIds() throws Exception
+ {
+ for (int i = 0 ; i < 10; i++)
+ {
+ // Create message
+ Long messageId = new Long(i);
+ AMQMessage message = createMessage(messageId);
+ // Put message on queue
+ _queue.enqueue(message);
+ }
+ // Get message ids
+ List<Long> msgids = _queue.getMessagesOnTheQueue(5, 5);
+
+ // Check message id
+ for (int i = 0; i < 5; i++)
+ {
+ Long messageId = new Long(i+5);
+ assertEquals("Message ID was wrong", messageId, msgids.get(i));
+ }
+ }
+
+ public void testGetMessagesRangeOnTheQueue() throws Exception
+ {
+ for (int i = 1 ; i <= 10; i++)
+ {
+ // Create message
+ Long messageId = new Long(i);
+ AMQMessage message = createMessage(messageId);
+ // Put message on queue
+ _queue.enqueue(message);
+ }
+
+ // Get non-existent 0th QueueEntry & check returned list was empty
+ // (the position parameters in this method are indexed from 1)
+ List<QueueEntry> entries = _queue.getMessagesRangeOnTheQueue(0, 0);
+ assertTrue(entries.size() == 0);
+
+ // Check that when 'from' is 0 it is ignored and the range continues from 1
+ entries = _queue.getMessagesRangeOnTheQueue(0, 2);
+ assertTrue(entries.size() == 2);
+ long msgID = entries.get(0).getMessage().getMessageNumber();
+ assertEquals("Message ID was wrong", msgID, 1L);
+ msgID = entries.get(1).getMessage().getMessageNumber();
+ assertEquals("Message ID was wrong", msgID, 2L);
+
+ // Check that when 'from' is greater than 'to' the returned list is empty
+ entries = _queue.getMessagesRangeOnTheQueue(5, 4);
+ assertTrue(entries.size() == 0);
+
+ // Get first QueueEntry & check id
+ entries = _queue.getMessagesRangeOnTheQueue(1, 1);
+ assertTrue(entries.size() == 1);
+ msgID = entries.get(0).getMessage().getMessageNumber();
+ assertEquals("Message ID was wrong", msgID, 1L);
+
+ // Get 5th,6th,7th entries and check id's
+ entries = _queue.getMessagesRangeOnTheQueue(5, 7);
+ assertTrue(entries.size() == 3);
+ msgID = entries.get(0).getMessage().getMessageNumber();
+ assertEquals("Message ID was wrong", msgID, 5L);
+ msgID = entries.get(1).getMessage().getMessageNumber();
+ assertEquals("Message ID was wrong", msgID, 6L);
+ msgID = entries.get(2).getMessage().getMessageNumber();
+ assertEquals("Message ID was wrong", msgID, 7L);
+
+ // Get 10th QueueEntry & check id
+ entries = _queue.getMessagesRangeOnTheQueue(10, 10);
+ assertTrue(entries.size() == 1);
+ msgID = entries.get(0).getMessage().getMessageNumber();
+ assertEquals("Message ID was wrong", msgID, 10L);
+
+ // Get non-existent 11th QueueEntry & check returned set was empty
+ entries = _queue.getMessagesRangeOnTheQueue(11, 11);
+ assertTrue(entries.size() == 0);
+
+ // Get 9th,10th, and non-existent 11th entries & check result is of size 2 with correct IDs
+ entries = _queue.getMessagesRangeOnTheQueue(9, 11);
+ assertTrue(entries.size() == 2);
+ msgID = entries.get(0).getMessage().getMessageNumber();
+ assertEquals("Message ID was wrong", msgID, 9L);
+ msgID = entries.get(1).getMessage().getMessageNumber();
+ assertEquals("Message ID was wrong", msgID, 10L);
+ }
+
+ public void testEnqueueDequeueOfPersistentMessageToNonDurableQueue() throws AMQException
+ {
+ // Create IncomingMessage and nondurable queue
+ final IncomingMessage msg = new IncomingMessage(info);
+ ContentHeaderBody contentHeaderBody = new ContentHeaderBody();
+ contentHeaderBody.setProperties(new BasicContentHeaderProperties());
+ ((BasicContentHeaderProperties) contentHeaderBody.getProperties()).setDeliveryMode((byte) 2);
+ msg.setContentHeaderBody(contentHeaderBody);
+
+ final ArrayList<BaseQueue> qs = new ArrayList<BaseQueue>();
+
+ // Send persistent message
+
+ qs.add(_queue);
+ MessageMetaData metaData = msg.headersReceived();
+ StoredMessage handle = _store.addMessage(metaData);
+ msg.setStoredMessage(handle);
+
+
+ ServerTransaction txn = new AutoCommitTransaction(_store);
+
+ txn.enqueue(qs, msg, new ServerTransaction.Action()
+ {
+ public void postCommit()
+ {
+ msg.enqueue(qs);
+ }
+
+ public void onRollback()
+ {
+ }
+ });
+
+
+
+ // Check that it is enqueued
+ AMQQueue data = _store.getMessages().get(1L);
+ assertNull(data);
+
+ // Dequeue message
+ MockQueueEntry entry = new MockQueueEntry();
+ AMQMessage amqmsg = new AMQMessage(handle);
+
+ entry.setMessage(amqmsg);
+ _queue.dequeue(entry,null);
+
+ // Check that it is dequeued
+ data = _store.getMessages().get(1L);
+ assertNull(data);
+ }
+
+
+ /**
+ * processQueue() is used when asynchronously delivering messages to
+ * subscriptions which could not be delivered immediately during the
+ * enqueue() operation.
+ *
+ * A defect within the method would mean that delivery of these messages may
+ * not occur should the Runner stop before all messages have been processed.
+ * Such a defect was discovered when Selectors were used such that one and
+ * only one subscription can/will accept any given messages, but multiple
+ * subscriptions are present, and one of the earlier subscriptions receives
+ * more messages than the others.
+ *
+ * This test is to validate that the processQueue() method is able to
+ * correctly deliver all of the messages present for asynchronous delivery
+ * to subscriptions in such a scenario.
+ */
+ public void testProcessQueueWithUniqueSelectors() throws Exception
+ {
+ TestSimpleQueueEntryListFactory factory = new TestSimpleQueueEntryListFactory();
+ SimpleAMQQueue testQueue = new SimpleAMQQueue("testQueue", false, "testOwner",false,
+ false, _virtualHost, factory, null)
+ {
+ @Override
+ public void deliverAsync(Subscription sub)
+ {
+ // do nothing, i.e prevent deliveries by the SubFlushRunner
+ // when registering the new subscriptions
+ }
+ };
+
+ // retrieve the QueueEntryList the queue creates and insert the test
+ // messages, thus avoiding straight-through delivery attempts during
+ //enqueue() process.
+ QueueEntryList list = factory.getQueueEntryList();
+ assertNotNull("QueueEntryList should have been created", list);
+
+ QueueEntry msg1 = list.add(createMessage(1L));
+ QueueEntry msg2 = list.add(createMessage(2L));
+ QueueEntry msg3 = list.add(createMessage(3L));
+ QueueEntry msg4 = list.add(createMessage(4L));
+ QueueEntry msg5 = list.add(createMessage(5L));
+
+ // Create lists of the entries each subscription should be interested
+ // in.Bias over 50% of the messages to the first subscription so that
+ // the later subscriptions reject them and report being done before
+ // the first subscription as the processQueue method proceeds.
+ List<QueueEntry> msgListSub1 = createEntriesList(msg1, msg2, msg3);
+ List<QueueEntry> msgListSub2 = createEntriesList(msg4);
+ List<QueueEntry> msgListSub3 = createEntriesList(msg5);
+
+ MockSubscription sub1 = new MockSubscription(msgListSub1);
+ MockSubscription sub2 = new MockSubscription(msgListSub2);
+ MockSubscription sub3 = new MockSubscription(msgListSub3);
+
+ // register the subscriptions
+ testQueue.registerSubscription(sub1, false);
+ testQueue.registerSubscription(sub2, false);
+ testQueue.registerSubscription(sub3, false);
+
+ //check that no messages have been delivered to the
+ //subscriptions during registration
+ assertEquals("No messages should have been delivered yet", 0, sub1.getMessages().size());
+ assertEquals("No messages should have been delivered yet", 0, sub2.getMessages().size());
+ assertEquals("No messages should have been delivered yet", 0, sub3.getMessages().size());
+
+ // call processQueue to deliver the messages
+ testQueue.processQueue(new QueueRunner(testQueue, 1)
+ {
+ @Override
+ public void run()
+ {
+ // we dont actually want/need this runner to do any work
+ // because we we are already doing it!
+ }
+ });
+
+ // check expected messages delivered to correct consumers
+ verifyRecievedMessages(msgListSub1, sub1.getMessages());
+ verifyRecievedMessages(msgListSub2, sub2.getMessages());
+ verifyRecievedMessages(msgListSub3, sub3.getMessages());
+ }
+
+ private List<QueueEntry> createEntriesList(QueueEntry... entries)
+ {
+ ArrayList<QueueEntry> entriesList = new ArrayList<QueueEntry>();
+ for (QueueEntry entry : entries)
+ {
+ entriesList.add(entry);
+ }
+ return entriesList;
+ }
+
+ private void verifyRecievedMessages(List<QueueEntry> expected,
+ List<QueueEntry> delivered)
+ {
+ assertEquals("Consumer did not receive the expected number of messages",
+ expected.size(), delivered.size());
+
+ for (QueueEntry msg : expected)
+ {
+ assertTrue("Consumer did not recieve msg: "
+ + msg.getMessage().getMessageNumber(), delivered.contains(msg));
+ }
+ }
+
+ public class TestMessage extends AMQMessage
+ {
+ private final long _tag;
+ private int _count;
+
+ TestMessage(long tag, long messageId, MessagePublishInfo publishBody)
+ throws AMQException
+ {
+ this(tag, messageId, publishBody, new ContentHeaderBody(1, 1, new BasicContentHeaderProperties(), 0));
+
+ }
+ TestMessage(long tag, long messageId, MessagePublishInfo publishBody, ContentHeaderBody chb)
+ throws AMQException
+ {
+ super(new MockStoredMessage(messageId, publishBody, chb));
+ _tag = tag;
+ }
+
+ public boolean incrementReference()
+ {
+ _count++;
+ return true;
+ }
+
+ public void decrementReference()
+ {
+ _count--;
+ }
+
+ void assertCountEquals(int expected)
+ {
+ assertEquals("Wrong count for message with tag " + _tag, expected, _count);
+ }
+ }
+
+ protected AMQMessage createMessage(Long id) throws AMQException
+ {
+ AMQMessage messageA = new TestMessage(id, id, info);
+ return messageA;
+ }
+
+ class TestSimpleQueueEntryListFactory implements QueueEntryListFactory
+ {
+ QueueEntryList _list;
+
+ public QueueEntryList createQueueEntryList(AMQQueue queue)
+ {
+ _list = new SimpleQueueEntryList(queue);
+ return _list;
+ }
+
+ public QueueEntryList getQueueEntryList()
+ {
+ return _list;
+ }
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueThreadPoolTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueThreadPoolTest.java
new file mode 100644
index 0000000000..a40dc5670f
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueThreadPoolTest.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.queue;
+
+import junit.framework.TestCase;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.pool.ReferenceCountingExecutorService;
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.AMQException;
+
+public class SimpleAMQQueueThreadPoolTest extends InternalBrokerBaseCase
+{
+
+ public void test() throws AMQException
+ {
+ int initialCount = ReferenceCountingExecutorService.getInstance().getReferenceCount();
+ VirtualHost test = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test");
+
+ try
+ {
+ SimpleAMQQueue queue = (SimpleAMQQueue) AMQQueueFactory.createAMQQueueImpl(new AMQShortString("test"), false,
+ new AMQShortString("owner"),
+ false, false, test, null);
+
+ assertFalse("Creation did not start Pool.", ReferenceCountingExecutorService.getInstance().getPool().isShutdown());
+
+ assertEquals("References not increased", initialCount + 1, ReferenceCountingExecutorService.getInstance().getReferenceCount());
+
+ queue.stop();
+
+ assertEquals("References not decreased", initialCount , ReferenceCountingExecutorService.getInstance().getReferenceCount());
+ }
+ finally
+ {
+ ApplicationRegistry.remove();
+ }
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryListTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryListTest.java
new file mode 100644
index 0000000000..320a75045a
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryListTest.java
@@ -0,0 +1,159 @@
+/*
+*
+* 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.queue;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.qpid.server.message.AMQMessage;
+
+import junit.framework.TestCase;
+
+public class SimpleQueueEntryListTest extends TestCase
+{
+ private static final String SCAVENGE_PROP = "qpid.queue.scavenge_count";
+ String oldScavengeValue = null;
+
+ @Override
+ protected void setUp()
+ {
+ oldScavengeValue = System.setProperty(SCAVENGE_PROP, "9");
+ }
+
+ @Override
+ protected void tearDown()
+ {
+ if(oldScavengeValue != null)
+ {
+ System.setProperty(SCAVENGE_PROP, oldScavengeValue);
+ }
+ else
+ {
+ System.clearProperty(SCAVENGE_PROP);
+ }
+ }
+
+ /**
+ * Tests the behavior of the next(QueuyEntry) method.
+ */
+ public void testNext() throws Exception
+ {
+ SimpleQueueEntryList sqel = new SimpleQueueEntryList(null);
+ int i = 0;
+
+ QueueEntry queueEntry1 = sqel.add(new MockAMQMessage(i++));
+ QueueEntry queueEntry2 = sqel.add(new MockAMQMessage(i++));
+
+ assertSame(queueEntry2, sqel.next(queueEntry1));
+ assertNull(sqel.next(queueEntry2));
+ }
+
+ public void testScavenge() throws Exception
+ {
+ SimpleQueueEntryList sqel = new SimpleQueueEntryList(null);
+ ConcurrentHashMap<Integer,QueueEntry> entriesMap = new ConcurrentHashMap<Integer,QueueEntry>();
+
+
+ //Add messages to generate QueueEntry's
+ for(int i = 1; i <= 100 ; i++)
+ {
+ AMQMessage msg = new MockAMQMessage(i);
+ QueueEntry bleh = sqel.add(msg);
+ assertNotNull("QE should not have been null", bleh);
+ entriesMap.put(i,bleh);
+ }
+
+ QueueEntryImpl head = ((QueueEntryImpl) sqel.getHead());
+
+ //We shall now delete some specific messages mid-queue that will lead to
+ //requiring a scavenge once the requested threshold of 9 deletes is passed
+
+ //Delete the 2nd message only
+ assertTrue("Failed to delete QueueEntry", entriesMap.remove(2).delete());
+ verifyDeletedButPresentBeforeScavenge(head, 2);
+
+ //Delete messages 12 to 14
+ assertTrue("Failed to delete QueueEntry", entriesMap.remove(12).delete());
+ verifyDeletedButPresentBeforeScavenge(head, 12);
+ assertTrue("Failed to delete QueueEntry", entriesMap.remove(13).delete());
+ verifyDeletedButPresentBeforeScavenge(head, 13);
+ assertTrue("Failed to delete QueueEntry", entriesMap.remove(14).delete());
+ verifyDeletedButPresentBeforeScavenge(head, 14);
+
+
+ //Delete message 20 only
+ assertTrue("Failed to delete QueueEntry", entriesMap.remove(20).delete());
+ verifyDeletedButPresentBeforeScavenge(head, 20);
+
+ //Delete messages 81 to 84
+ assertTrue("Failed to delete QueueEntry", entriesMap.remove(81).delete());
+ verifyDeletedButPresentBeforeScavenge(head, 81);
+ assertTrue("Failed to delete QueueEntry", entriesMap.remove(82).delete());
+ verifyDeletedButPresentBeforeScavenge(head, 82);
+ assertTrue("Failed to delete QueueEntry", entriesMap.remove(83).delete());
+ verifyDeletedButPresentBeforeScavenge(head, 83);
+ assertTrue("Failed to delete QueueEntry", entriesMap.remove(84).delete());
+ verifyDeletedButPresentBeforeScavenge(head, 84);
+
+ //Delete message 99 - this is the 10th message deleted that is after the queue head
+ //and so will invoke the scavenge() which is set to go after 9 previous deletions
+ assertTrue("Failed to delete QueueEntry", entriesMap.remove(99).delete());
+
+ verifyAllDeletedMessagedNotPresent(head, entriesMap);
+ }
+
+ private void verifyDeletedButPresentBeforeScavenge(QueueEntryImpl head, long messageId)
+ {
+ //Use the head to get the initial entry in the queue
+ QueueEntryImpl entry = head._next;
+
+ for(long i = 1; i < messageId ; i++)
+ {
+ assertEquals("Expected QueueEntry was not found in the list", i, (long) entry.getMessage().getMessageNumber());
+ entry = entry._next;
+ }
+
+ assertTrue("Entry should have been deleted", entry.isDeleted());
+ }
+
+ private void verifyAllDeletedMessagedNotPresent(QueueEntryImpl head, Map<Integer,QueueEntry> remainingMessages)
+ {
+ //Use the head to get the initial entry in the queue
+ QueueEntryImpl entry = head._next;
+
+ assertNotNull("Initial entry should not have been null", entry);
+
+ int count = 0;
+
+ while (entry != null)
+ {
+ assertFalse("Entry " + entry.getMessage().getMessageNumber() + " should not have been deleted", entry.isDeleted());
+ assertNotNull("QueueEntry was not found in the list of remaining entries",
+ remainingMessages.get(entry.getMessage().getMessageNumber().intValue()));
+
+ count++;
+ entry = entry._next;
+ }
+
+ assertEquals("Count should have been equal",count,remainingMessages.size());
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/registry/ApplicationRegistryShutdownTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/registry/ApplicationRegistryShutdownTest.java
new file mode 100644
index 0000000000..e45c8d7b96
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/registry/ApplicationRegistryShutdownTest.java
@@ -0,0 +1,105 @@
+/*
+ *
+ * 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.registry;
+
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+
+import java.security.Security;
+import java.security.Provider;
+import java.util.List;
+import java.util.LinkedList;
+
+/**
+ * QPID-1390 : Test to validate that the AuthenticationManger can successfully unregister any new SASL providers when
+ * The ApplicationRegistry is closed.
+ *
+ * This should be expanded as QPID-1399 is implemented.
+ */
+public class ApplicationRegistryShutdownTest extends InternalBrokerBaseCase
+{
+
+ Provider[] _defaultProviders;
+ @Override
+ public void setUp() throws Exception
+ {
+ // Get default providers
+ _defaultProviders = Security.getProviders();
+
+ //Startup the new broker and register the new providers
+ super.setUp();
+ }
+
+
+ /**
+ * QPID-1399 : Ensure that the Authentiction manager unregisters any SASL providers created during
+ * ApplicationRegistry initialisation.
+ *
+ */
+ public void testAuthenticationMangerCleansUp() throws Exception
+ {
+
+ // Get the providers after initialisation
+ Provider[] providersAfterInitialisation = Security.getProviders();
+
+ // Find the additions
+ List additions = new LinkedList();
+ for (Provider afterInit : providersAfterInitialisation)
+ {
+ boolean found = false;
+ for (Provider defaultProvider : _defaultProviders)
+ {
+ if (defaultProvider == afterInit)
+ {
+ found=true;
+ break;
+ }
+ }
+
+ // Record added registies
+ if (!found)
+ {
+ additions.add(afterInit);
+ }
+ }
+
+ // Not using isEmpty as that is not in Java 5
+ assertTrue("No new SASL mechanisms added by initialisation.", additions.size() != 0 );
+
+ //Close the registry which will perform the close the AuthenticationManager
+ getRegistry().close();
+
+ //Validate that the SASL plugFins have been removed.
+ Provider[] providersAfterClose = Security.getProviders();
+
+ assertTrue("No providers unregistered", providersAfterInitialisation.length > providersAfterClose.length);
+
+ //Ensure that the additions are not still present after close().
+ for (Provider afterClose : providersAfterClose)
+ {
+ assertFalse("Added provider not unregistered", additions.contains(afterClose));
+ }
+ }
+
+
+
+
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabaseTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabaseTest.java
new file mode 100644
index 0000000000..2ab15d4872
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabaseTest.java
@@ -0,0 +1,466 @@
+/*
+ *
+ * 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.security.auth.database;
+
+import junit.framework.TestCase;
+
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.login.AccountNotFoundException;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Pattern;
+
+public class Base64MD5PasswordFilePrincipalDatabaseTest extends TestCase
+{
+
+ private static final String TEST_COMMENT = "# Test Comment";
+
+ private static final String USERNAME = "testUser";
+ private static final String PASSWORD = "guest";
+ private static final String PASSWORD_B64MD5HASHED = "CE4DQ6BIb/BVMN9scFyLtA==";
+ private static char[] PASSWORD_MD5_CHARS;
+ private static final String PRINCIPAL_USERNAME = "testUserPrincipal";
+ private static final Principal PRINCIPAL = new UsernamePrincipal(PRINCIPAL_USERNAME);
+ private Base64MD5PasswordFilePrincipalDatabase _database;
+ private File _pwdFile;
+ private List<File> _testPwdFiles = new ArrayList<File>();
+
+ static
+ {
+ try
+ {
+ Base64 b64 = new Base64();
+ byte[] md5passBytes = PASSWORD_B64MD5HASHED.getBytes(Base64MD5PasswordFilePrincipalDatabase.DEFAULT_ENCODING);
+ byte[] decoded = b64.decode(md5passBytes);
+
+ PASSWORD_MD5_CHARS = new char[decoded.length];
+
+ int index = 0;
+ for (byte c : decoded)
+ {
+ PASSWORD_MD5_CHARS[index++] = (char) c;
+ }
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ fail("Unable to perform B64 decode to get the md5 char[] password");
+ }
+ }
+
+
+ public void setUp() throws Exception
+ {
+ _database = new Base64MD5PasswordFilePrincipalDatabase();
+ _pwdFile = File.createTempFile(this.getClass().getName(), "pwd");
+ _pwdFile.deleteOnExit();
+ _database.setPasswordFile(_pwdFile.getAbsolutePath());
+ _testPwdFiles.clear();
+ }
+
+ public void tearDown() throws Exception
+ {
+ //clean up the created default password file and any backup
+ File oldPwdFile = new File(_pwdFile.getAbsolutePath() + ".old");
+ if(oldPwdFile.exists())
+ {
+ oldPwdFile.delete();
+ }
+
+ _pwdFile.delete();
+
+ //clean up any additional files and their backups
+ for(File f : _testPwdFiles)
+ {
+ oldPwdFile = new File(f.getAbsolutePath() + ".old");
+ if(oldPwdFile.exists())
+ {
+ oldPwdFile.delete();
+ }
+
+ f.delete();
+ }
+ }
+
+ private File createPasswordFile(int commentLines, int users)
+ {
+ try
+ {
+ File testFile = File.createTempFile("Base64MD5PDPDTest","tmp");
+ testFile.deleteOnExit();
+
+ BufferedWriter writer = new BufferedWriter(new FileWriter(testFile));
+
+ for (int i = 0; i < commentLines; i++)
+ {
+ writer.write(TEST_COMMENT);
+ writer.newLine();
+ }
+
+ for (int i = 0; i < users; i++)
+ {
+ writer.write(USERNAME + i + ":Password");
+ writer.newLine();
+ }
+
+ writer.flush();
+ writer.close();
+
+ _testPwdFiles.add(testFile);
+
+ return testFile;
+
+ }
+ catch (IOException e)
+ {
+ fail("Unable to create test password file." + e.getMessage());
+ }
+
+ return null;
+ }
+
+ private void loadPasswordFile(File file)
+ {
+ try
+ {
+ _database.setPasswordFile(file.toString());
+ }
+ catch (IOException e)
+ {
+ fail("Password File was not created." + e.getMessage());
+ }
+ }
+
+ /** **** Test Methods ************** */
+
+ public void testCreatePrincipal()
+ {
+ File testFile = createPasswordFile(1, 0);
+
+ loadPasswordFile(testFile);
+
+
+ Principal principal = new Principal()
+ {
+ public String getName()
+ {
+ return USERNAME;
+ }
+ };
+
+ assertTrue("New user not created.", _database.createPrincipal(principal, PASSWORD.toCharArray()));
+
+ PasswordCallback callback = new PasswordCallback("prompt",false);
+ try
+ {
+ _database.setPassword(principal, callback);
+ }
+ catch (AccountNotFoundException e)
+ {
+ fail("user account did not exist");
+ }
+ assertTrue("Password returned was incorrect.", Arrays.equals(PASSWORD_MD5_CHARS, callback.getPassword()));
+
+ loadPasswordFile(testFile);
+
+ try
+ {
+ _database.setPassword(principal, callback);
+ }
+ catch (AccountNotFoundException e)
+ {
+ fail("user account did not exist");
+ }
+ assertTrue("Password returned was incorrect.", Arrays.equals(PASSWORD_MD5_CHARS, callback.getPassword()));
+
+ assertNotNull("Created User was not saved", _database.getUser(USERNAME));
+
+ assertFalse("Duplicate user created.", _database.createPrincipal(principal, PASSWORD.toCharArray()));
+ }
+
+ public void testCreatePrincipalIsSavedToFile()
+ {
+
+ File testFile = createPasswordFile(1, 0);
+
+ loadPasswordFile(testFile);
+
+ final String CREATED_PASSWORD = "guest";
+ final String CREATED_B64MD5HASHED_PASSWORD = "CE4DQ6BIb/BVMN9scFyLtA==";
+ final String CREATED_USERNAME = "createdUser";
+
+ Principal principal = new Principal()
+ {
+ public String getName()
+ {
+ return CREATED_USERNAME;
+ }
+ };
+
+ _database.createPrincipal(principal, CREATED_PASSWORD.toCharArray());
+
+ try
+ {
+ BufferedReader reader = new BufferedReader(new FileReader(testFile));
+
+ assertTrue("File has no content", reader.ready());
+
+ assertEquals("Comment line has been corrupted.", TEST_COMMENT, reader.readLine());
+
+ assertTrue("File is missing user data.", reader.ready());
+
+ String userLine = reader.readLine();
+
+ String[] result = Pattern.compile(":").split(userLine);
+
+ assertEquals("User line not complete '" + userLine + "'", 2, result.length);
+
+ assertEquals("Username not correct,", CREATED_USERNAME, result[0]);
+ assertEquals("Password not correct,", CREATED_B64MD5HASHED_PASSWORD, result[1]);
+
+ assertFalse("File has more content", reader.ready());
+
+ }
+ catch (IOException e)
+ {
+ fail("Unable to valdate file contents due to:" + e.getMessage());
+ }
+ }
+
+
+ public void testDeletePrincipal()
+ {
+ File testFile = createPasswordFile(1, 1);
+
+ loadPasswordFile(testFile);
+
+ Principal user = _database.getUser(USERNAME + "0");
+ assertNotNull("Generated user not present.", user);
+
+ try
+ {
+ _database.deletePrincipal(user);
+ }
+ catch (AccountNotFoundException e)
+ {
+ fail("User should be present" + e.getMessage());
+ }
+
+ try
+ {
+ _database.deletePrincipal(user);
+ fail("User should not be present");
+ }
+ catch (AccountNotFoundException e)
+ {
+ //pass
+ }
+
+ loadPasswordFile(testFile);
+
+ try
+ {
+ _database.deletePrincipal(user);
+ fail("User should not be present");
+ }
+ catch (AccountNotFoundException e)
+ {
+ //pass
+ }
+
+ assertNull("Deleted user still present.", _database.getUser(USERNAME + "0"));
+ }
+
+ public void testGetUsers()
+ {
+ int USER_COUNT = 10;
+ File testFile = createPasswordFile(1, USER_COUNT);
+
+ loadPasswordFile(testFile);
+
+ Principal user = _database.getUser("MISSING_USERNAME");
+ assertNull("Missing user present.", user);
+
+ List<Principal> users = _database.getUsers();
+
+ assertNotNull("Users list is null.", users);
+
+ assertEquals(USER_COUNT, users.size());
+
+ boolean[] verify = new boolean[USER_COUNT];
+ for (int i = 0; i < USER_COUNT; i++)
+ {
+ Principal principal = users.get(i);
+
+ assertNotNull("Generated user not present.", principal);
+
+ String name = principal.getName();
+
+ int id = Integer.parseInt(name.substring(USERNAME.length()));
+
+ assertFalse("Duplicated username retrieve", verify[id]);
+ verify[id] = true;
+ }
+
+ for (int i = 0; i < USER_COUNT; i++)
+ {
+ assertTrue("User " + i + " missing", verify[i]);
+ }
+ }
+
+ public void testUpdatePasswordIsSavedToFile()
+ {
+
+ File testFile = createPasswordFile(1, 1);
+
+ loadPasswordFile(testFile);
+
+ Principal testUser = _database.getUser(USERNAME + "0");
+
+ assertNotNull(testUser);
+
+ String NEW_PASSWORD = "guest";
+ String NEW_PASSWORD_HASH = "CE4DQ6BIb/BVMN9scFyLtA==";
+ try
+ {
+ _database.updatePassword(testUser, NEW_PASSWORD.toCharArray());
+ }
+ catch (AccountNotFoundException e)
+ {
+ fail(e.toString());
+ }
+
+ try
+ {
+ BufferedReader reader = new BufferedReader(new FileReader(testFile));
+
+ assertTrue("File has no content", reader.ready());
+
+ assertEquals("Comment line has been corrupted.", TEST_COMMENT, reader.readLine());
+
+ assertTrue("File is missing user data.", reader.ready());
+
+ String userLine = reader.readLine();
+
+ String[] result = Pattern.compile(":").split(userLine);
+
+ assertEquals("User line not complete '" + userLine + "'", 2, result.length);
+
+ assertEquals("Username not correct,", USERNAME + "0", result[0]);
+ assertEquals("New Password not correct,", NEW_PASSWORD_HASH, result[1]);
+
+ assertFalse("File has more content", reader.ready());
+
+ }
+ catch (IOException e)
+ {
+ fail("Unable to valdate file contents due to:" + e.getMessage());
+ }
+ }
+
+ public void testSetPasswordFileWithMissingFile()
+ {
+ try
+ {
+ _database.setPasswordFile("DoesntExist");
+ }
+ catch (FileNotFoundException fnfe)
+ {
+ assertTrue(fnfe.getMessage(), fnfe.getMessage().startsWith("Cannot find password file"));
+ }
+ catch (IOException e)
+ {
+ fail("Password File was not created." + e.getMessage());
+ }
+
+ }
+
+ public void testSetPasswordFileWithReadOnlyFile()
+ {
+
+ File testFile = createPasswordFile(0, 0);
+
+ testFile.setReadOnly();
+
+ try
+ {
+ _database.setPasswordFile(testFile.toString());
+ }
+ catch (FileNotFoundException fnfe)
+ {
+ assertTrue(fnfe.getMessage().startsWith("Cannot read password file "));
+ }
+ catch (IOException e)
+ {
+ fail("Password File was not created." + e.getMessage());
+ }
+ }
+
+ public void testCreateUserPrincipal() throws IOException
+ {
+ _database.createPrincipal(PRINCIPAL, PASSWORD.toCharArray());
+ Principal newPrincipal = _database.getUser(PRINCIPAL_USERNAME);
+ assertNotNull(newPrincipal);
+ assertEquals(PRINCIPAL.getName(), newPrincipal.getName());
+ }
+
+ public void testVerifyPassword() throws IOException, AccountNotFoundException
+ {
+ testCreateUserPrincipal();
+ //assertFalse(_pwdDB.verifyPassword(_username, null));
+ assertFalse(_database.verifyPassword(PRINCIPAL_USERNAME, new char[]{}));
+ assertFalse(_database.verifyPassword(PRINCIPAL_USERNAME, (PASSWORD+"z").toCharArray()));
+ assertTrue(_database.verifyPassword(PRINCIPAL_USERNAME, PASSWORD.toCharArray()));
+
+ try
+ {
+ _database.verifyPassword("made.up.username", PASSWORD.toCharArray());
+ fail("Should not have been able to verify this non-existant users password.");
+ }
+ catch (AccountNotFoundException e)
+ {
+ // pass
+ }
+ }
+
+ public void testUpdatePassword() throws IOException, AccountNotFoundException
+ {
+ testCreateUserPrincipal();
+ char[] newPwd = "newpassword".toCharArray();
+ _database.updatePassword(PRINCIPAL, newPwd);
+ assertFalse(_database.verifyPassword(PRINCIPAL_USERNAME, PASSWORD.toCharArray()));
+ assertTrue(_database.verifyPassword(PRINCIPAL_USERNAME, newPwd));
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/HashedUserTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/HashedUserTest.java
new file mode 100644
index 0000000000..aa85cac758
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/HashedUserTest.java
@@ -0,0 +1,95 @@
+/*
+ *
+ * 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.security.auth.database;
+
+import junit.framework.TestCase;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+
+import java.io.UnsupportedEncodingException;
+
+/*
+ Note User is mainly tested by Base64MD5PFPDTest this is just to catch the extra methods
+ */
+public class HashedUserTest extends TestCase
+{
+
+ String USERNAME = "username";
+ String PASSWORD = "password";
+ String B64_ENCODED_PASSWORD = "cGFzc3dvcmQ=";
+
+ public void testToLongArrayConstructor()
+ {
+ try
+ {
+ HashedUser user = new HashedUser(new String[]{USERNAME, PASSWORD, USERNAME});
+ fail("Error expected");
+ }
+ catch (IllegalArgumentException e)
+ {
+ assertEquals("User Data should be length 2, username, password", e.getMessage());
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ fail(e.getMessage());
+ }
+ }
+
+ public void testArrayConstructor()
+ {
+ try
+ {
+ HashedUser user = new HashedUser(new String[]{USERNAME, B64_ENCODED_PASSWORD});
+ assertEquals("Username incorrect", USERNAME, user.getName());
+ int index = 0;
+
+ char[] hash = B64_ENCODED_PASSWORD.toCharArray();
+
+ try
+ {
+ for (byte c : user.getEncodedPassword())
+ {
+ assertEquals("Password incorrect", hash[index], (char) c);
+ index++;
+ }
+ }
+ catch (Exception e)
+ {
+ fail(e.getMessage());
+ }
+
+ hash = PASSWORD.toCharArray();
+
+ index=0;
+ for (char c : user.getPassword())
+ {
+ assertEquals("Password incorrect", hash[index], c);
+ index++;
+ }
+
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ fail(e.getMessage());
+ }
+ }
+}
+
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabaseTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabaseTest.java
new file mode 100644
index 0000000000..a3dad19bb4
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabaseTest.java
@@ -0,0 +1,416 @@
+/*
+ *
+ * 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.security.auth.database;
+
+import junit.framework.TestCase;
+
+import javax.security.auth.login.AccountNotFoundException;
+
+import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+public class PlainPasswordFilePrincipalDatabaseTest extends TestCase
+{
+
+ private static final String TEST_COMMENT = "# Test Comment";
+ private static final String TEST_PASSWORD = "testPassword";
+ private static final char[] TEST_PASSWORD_CHARS = TEST_PASSWORD.toCharArray();
+ private static final String TEST_USERNAME = "testUser";
+
+ private Principal _principal = new UsernamePrincipal(TEST_USERNAME);
+ private PlainPasswordFilePrincipalDatabase _database;
+ private List<File> _testPwdFiles = new ArrayList<File>();
+
+ public void setUp() throws Exception
+ {
+ _database = new PlainPasswordFilePrincipalDatabase();
+ _testPwdFiles.clear();
+ }
+
+ public void tearDown() throws Exception
+ {
+ //clean up any additional files and their backups
+ for(File f : _testPwdFiles)
+ {
+ File oldPwdFile = new File(f.getAbsolutePath() + ".old");
+ if(oldPwdFile.exists())
+ {
+ oldPwdFile.delete();
+ }
+
+ f.delete();
+ }
+ }
+
+ // ******* Test Methods ********** //
+
+ public void testCreatePrincipal()
+ {
+ File testFile = createPasswordFile(1, 0);
+
+ loadPasswordFile(testFile);
+
+ final String CREATED_PASSWORD = "guest";
+ final String CREATED_USERNAME = "createdUser";
+
+ Principal principal = new Principal()
+ {
+ public String getName()
+ {
+ return CREATED_USERNAME;
+ }
+ };
+
+ assertTrue("New user not created.", _database.createPrincipal(principal, CREATED_PASSWORD.toCharArray()));
+
+ loadPasswordFile(testFile);
+
+ assertNotNull("Created User was not saved", _database.getUser(CREATED_USERNAME));
+
+ assertFalse("Duplicate user created.", _database.createPrincipal(principal, CREATED_PASSWORD.toCharArray()));
+
+ testFile.delete();
+ }
+
+ public void testCreatePrincipalIsSavedToFile()
+ {
+
+ File testFile = createPasswordFile(1, 0);
+
+ loadPasswordFile(testFile);
+
+ Principal principal = new Principal()
+ {
+ public String getName()
+ {
+ return TEST_USERNAME;
+ }
+ };
+
+ _database.createPrincipal(principal, TEST_PASSWORD_CHARS);
+
+ try
+ {
+ BufferedReader reader = new BufferedReader(new FileReader(testFile));
+
+ assertTrue("File has no content", reader.ready());
+
+ assertEquals("Comment line has been corrupted.", TEST_COMMENT, reader.readLine());
+
+ assertTrue("File is missing user data.", reader.ready());
+
+ String userLine = reader.readLine();
+
+ String[] result = Pattern.compile(":").split(userLine);
+
+ assertEquals("User line not complete '" + userLine + "'", 2, result.length);
+
+ assertEquals("Username not correct,", TEST_USERNAME, result[0]);
+ assertEquals("Password not correct,", TEST_PASSWORD, result[1]);
+
+ assertFalse("File has more content", reader.ready());
+
+ }
+ catch (IOException e)
+ {
+ fail("Unable to valdate file contents due to:" + e.getMessage());
+ }
+ testFile.delete();
+ }
+
+ public void testDeletePrincipal()
+ {
+ File testFile = createPasswordFile(1, 1);
+
+ loadPasswordFile(testFile);
+
+ Principal user = _database.getUser(TEST_USERNAME + "0");
+ assertNotNull("Generated user not present.", user);
+
+ try
+ {
+ _database.deletePrincipal(user);
+ }
+ catch (AccountNotFoundException e)
+ {
+ fail("User should be present" + e.getMessage());
+ }
+
+ try
+ {
+ _database.deletePrincipal(user);
+ fail("User should not be present");
+ }
+ catch (AccountNotFoundException e)
+ {
+ //pass
+ }
+
+ loadPasswordFile(testFile);
+
+ try
+ {
+ _database.deletePrincipal(user);
+ fail("User should not be present");
+ }
+ catch (AccountNotFoundException e)
+ {
+ //pass
+ }
+
+ assertNull("Deleted user still present.", _database.getUser(TEST_USERNAME + "0"));
+
+ testFile.delete();
+ }
+
+ public void testGetUsers()
+ {
+ int USER_COUNT = 10;
+ File testFile = createPasswordFile(1, USER_COUNT);
+
+ loadPasswordFile(testFile);
+
+ Principal user = _database.getUser("MISSING_USERNAME");
+ assertNull("Missing user present.", user);
+
+ List<Principal> users = _database.getUsers();
+
+ assertNotNull("Users list is null.", users);
+
+ assertEquals(USER_COUNT, users.size());
+
+ boolean[] verify = new boolean[USER_COUNT];
+ for (int i = 0; i < USER_COUNT; i++)
+ {
+ Principal principal = users.get(i);
+
+ assertNotNull("Generated user not present.", principal);
+
+ String name = principal.getName();
+
+ int id = Integer.parseInt(name.substring(TEST_USERNAME.length()));
+
+ assertFalse("Duplicated username retrieve", verify[id]);
+ verify[id] = true;
+ }
+
+ for (int i = 0; i < USER_COUNT; i++)
+ {
+ assertTrue("User " + i + " missing", verify[i]);
+ }
+
+ testFile.delete();
+ }
+
+ public void testUpdatePasswordIsSavedToFile()
+ {
+
+ File testFile = createPasswordFile(1, 1);
+
+ loadPasswordFile(testFile);
+
+ Principal testUser = _database.getUser(TEST_USERNAME + "0");
+
+ assertNotNull(testUser);
+
+ String NEW_PASSWORD = "NewPassword";
+ try
+ {
+ _database.updatePassword(testUser, NEW_PASSWORD.toCharArray());
+ }
+ catch (AccountNotFoundException e)
+ {
+ fail(e.toString());
+ }
+
+ try
+ {
+ BufferedReader reader = new BufferedReader(new FileReader(testFile));
+
+ assertTrue("File has no content", reader.ready());
+
+ assertEquals("Comment line has been corrupted.", TEST_COMMENT, reader.readLine());
+
+ assertTrue("File is missing user data.", reader.ready());
+
+ String userLine = reader.readLine();
+
+ String[] result = Pattern.compile(":").split(userLine);
+
+ assertEquals("User line not complete '" + userLine + "'", 2, result.length);
+
+ assertEquals("Username not correct,", TEST_USERNAME + "0", result[0]);
+ assertEquals("New Password not correct,", NEW_PASSWORD, result[1]);
+
+ assertFalse("File has more content", reader.ready());
+
+ }
+ catch (IOException e)
+ {
+ fail("Unable to valdate file contents due to:" + e.getMessage());
+ }
+ testFile.delete();
+ }
+
+ public void testSetPasswordFileWithMissingFile()
+ {
+ try
+ {
+ _database.setPasswordFile("DoesntExist");
+ }
+ catch (FileNotFoundException fnfe)
+ {
+ assertTrue(fnfe.getMessage(), fnfe.getMessage().startsWith("Cannot find password file"));
+ }
+ catch (IOException e)
+ {
+ fail("Password File was not created." + e.getMessage());
+ }
+
+ }
+
+ public void testSetPasswordFileWithReadOnlyFile()
+ {
+
+ File testFile = createPasswordFile(0, 0);
+
+ testFile.setReadOnly();
+
+ try
+ {
+ _database.setPasswordFile(testFile.toString());
+ }
+ catch (FileNotFoundException fnfe)
+ {
+ assertTrue(fnfe.getMessage().startsWith("Cannot read password file "));
+ }
+ catch (IOException e)
+ {
+ fail("Password File was not created." + e.getMessage());
+ }
+
+ testFile.delete();
+ }
+
+ private void createUserPrincipal() throws IOException
+ {
+ File testFile = createPasswordFile(0, 0);
+ loadPasswordFile(testFile);
+
+ _database.createPrincipal(_principal, TEST_PASSWORD_CHARS);
+ Principal newPrincipal = _database.getUser(TEST_USERNAME);
+ assertNotNull(newPrincipal);
+ assertEquals(_principal.getName(), newPrincipal.getName());
+ }
+
+ public void testVerifyPassword() throws IOException, AccountNotFoundException
+ {
+ createUserPrincipal();
+ assertFalse(_database.verifyPassword(TEST_USERNAME, new char[]{}));
+ assertFalse(_database.verifyPassword(TEST_USERNAME, "massword".toCharArray()));
+ assertTrue(_database.verifyPassword(TEST_USERNAME, TEST_PASSWORD_CHARS));
+
+ try
+ {
+ _database.verifyPassword("made.up.username", TEST_PASSWORD_CHARS);
+ fail("Should not have been able to verify this non-existant users password.");
+ }
+ catch (AccountNotFoundException e)
+ {
+ // pass
+ }
+ }
+
+ public void testUpdatePassword() throws IOException, AccountNotFoundException
+ {
+ createUserPrincipal();
+ char[] newPwd = "newpassword".toCharArray();
+ _database.updatePassword(_principal, newPwd);
+ assertFalse(_database.verifyPassword(TEST_USERNAME, TEST_PASSWORD_CHARS));
+ assertTrue(_database.verifyPassword(TEST_USERNAME, newPwd));
+ }
+
+
+
+ // *********** Utility Methods ******** //
+
+ private File createPasswordFile(int commentLines, int users)
+ {
+ try
+ {
+ File testFile = File.createTempFile(this.getClass().getName(),"tmp");
+ testFile.deleteOnExit();
+
+ BufferedWriter writer = new BufferedWriter(new FileWriter(testFile));
+
+ for (int i = 0; i < commentLines; i++)
+ {
+ writer.write(TEST_COMMENT);
+ writer.newLine();
+ }
+
+ for (int i = 0; i < users; i++)
+ {
+ writer.write(TEST_USERNAME + i + ":" + TEST_PASSWORD);
+ writer.newLine();
+ }
+
+ writer.flush();
+ writer.close();
+
+ _testPwdFiles.add(testFile);
+
+ return testFile;
+
+ }
+ catch (IOException e)
+ {
+ fail("Unable to create test password file." + e.getMessage());
+ }
+
+ return null;
+ }
+
+ private void loadPasswordFile(File file)
+ {
+ try
+ {
+ _database.setPasswordFile(file.toString());
+ }
+ catch (IOException e)
+ {
+ fail("Password File was not created." + e.getMessage());
+ }
+ }
+
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/PlainUserTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/PlainUserTest.java
new file mode 100644
index 0000000000..7f0843d46e
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/PlainUserTest.java
@@ -0,0 +1,78 @@
+/*
+ *
+ * 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.security.auth.database;
+
+import junit.framework.TestCase;
+
+/*
+ Note PlainUser is mainly tested by PlainPFPDTest, this is just to catch the extra methods
+ */
+public class PlainUserTest extends TestCase
+{
+
+ String USERNAME = "username";
+ String PASSWORD = "password";
+
+ public void testTooLongArrayConstructor()
+ {
+ try
+ {
+ PlainUser user = new PlainUser(new String[]{USERNAME, PASSWORD, USERNAME});
+ fail("Error expected");
+ }
+ catch (IllegalArgumentException e)
+ {
+ assertEquals("User Data should be length 2, username, password", e.getMessage());
+ }
+ }
+
+ public void testStringArrayConstructor()
+ {
+ PlainUser user = new PlainUser(new String[]{USERNAME, PASSWORD});
+ assertEquals("Username incorrect", USERNAME, user.getName());
+ int index = 0;
+
+ char[] password = PASSWORD.toCharArray();
+
+ try
+ {
+ for (byte c : user.getPasswordBytes())
+ {
+ assertEquals("Password incorrect", password[index], (char) c);
+ index++;
+ }
+ }
+ catch (Exception e)
+ {
+ fail(e.getMessage());
+ }
+
+ password = PASSWORD.toCharArray();
+
+ index=0;
+ for (char c : user.getPassword())
+ {
+ assertEquals("Password incorrect", password[index], c);
+ index++;
+ }
+ }
+}
+
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManagerTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManagerTest.java
new file mode 100644
index 0000000000..f51ce0b6c6
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManagerTest.java
@@ -0,0 +1,209 @@
+/*
+ *
+ * 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.security.auth.manager;
+
+import java.security.Provider;
+import java.security.Security;
+
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+
+import org.apache.qpid.server.security.auth.AuthenticationResult;
+import org.apache.qpid.server.security.auth.AuthenticationResult.AuthenticationStatus;
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+
+/**
+ *
+ * Tests the public methods of PrincipalDatabaseAuthenticationManager.
+ *
+ */
+public class PrincipalDatabaseAuthenticationManagerTest extends InternalBrokerBaseCase
+{
+ private PrincipalDatabaseAuthenticationManager _manager = null;
+
+ /**
+ * @see org.apache.qpid.server.util.InternalBrokerBaseCase#tearDown()
+ */
+ @Override
+ public void tearDown() throws Exception
+ {
+ super.tearDown();
+ if (_manager != null)
+ {
+ _manager.close();
+ }
+ }
+
+ /**
+ * @see org.apache.qpid.server.util.InternalBrokerBaseCase#setUp()
+ */
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ _manager = new PrincipalDatabaseAuthenticationManager();
+ }
+
+ /**
+ * Tests that the PDAM registers SASL mechanisms correctly with the runtime.
+ */
+ public void testRegisteredMechanisms() throws Exception
+ {
+ assertNotNull(_manager.getMechanisms());
+ // relies on those mechanisms attached to PropertiesPrincipalDatabaseManager
+ assertEquals("PLAIN CRAM-MD5", _manager.getMechanisms());
+
+ Provider qpidProvider = Security.getProvider(PrincipalDatabaseAuthenticationManager.PROVIDER_NAME);
+ assertNotNull(qpidProvider);
+ }
+
+ /**
+ * Tests that the SASL factory method createSaslServer correctly
+ * returns a non-null implementation.
+ */
+ public void testSaslMechanismCreation() throws Exception
+ {
+ SaslServer server = _manager.createSaslServer("CRAM-MD5", "localhost");
+ assertNotNull(server);
+ // Merely tests the creation of the mechanism. Mechanisms themselves are tested
+ // by their own tests.
+ }
+
+ /**
+ *
+ * Tests that the authenticate method correctly interprets an
+ * authentication success.
+ *
+ */
+ public void testAuthenticationSuccess() throws Exception
+ {
+ SaslServer testServer = createTestSaslServer(true, false);
+
+ AuthenticationResult result = _manager.authenticate(testServer, "12345".getBytes());
+ assertEquals(AuthenticationStatus.SUCCESS, result.status);
+ }
+
+ /**
+ *
+ * Tests that the authenticate method correctly interprets an
+ * authentication not complete.
+ *
+ */
+ public void testAuthenticationNotCompleted() throws Exception
+ {
+ SaslServer testServer = createTestSaslServer(false, false);
+
+ AuthenticationResult result = _manager.authenticate(testServer, "12345".getBytes());
+ assertEquals(AuthenticationStatus.CONTINUE, result.status);
+ }
+
+ /**
+ *
+ * Tests that the authenticate method correctly interprets an
+ * authentication error.
+ *
+ */
+ public void testAuthenticationError() throws Exception
+ {
+ SaslServer testServer = createTestSaslServer(false, true);
+
+ AuthenticationResult result = _manager.authenticate(testServer, "12345".getBytes());
+ assertEquals(AuthenticationStatus.ERROR, result.status);
+ }
+
+ /**
+ * Tests the ability to de-register the provider.
+ */
+ public void testClose() throws Exception
+ {
+ assertEquals("PLAIN CRAM-MD5", _manager.getMechanisms());
+ assertNotNull(Security.getProvider(PrincipalDatabaseAuthenticationManager.PROVIDER_NAME));
+
+ _manager.close();
+
+ // Check provider has been removed.
+ assertNull(_manager.getMechanisms());
+ assertNull(Security.getProvider(PrincipalDatabaseAuthenticationManager.PROVIDER_NAME));
+ _manager = null;
+ }
+
+ /**
+ * Test SASL implementation used to test the authenticate() method.
+ */
+ private SaslServer createTestSaslServer(final boolean complete, final boolean throwSaslException)
+ {
+ return new SaslServer()
+ {
+
+ @Override
+ public String getMechanismName()
+ {
+ return null;
+ }
+
+ @Override
+ public byte[] evaluateResponse(byte[] response) throws SaslException
+ {
+ if (throwSaslException)
+ {
+ throw new SaslException("Mocked exception");
+ }
+ return null;
+ }
+
+ @Override
+ public boolean isComplete()
+ {
+ return complete;
+ }
+
+ @Override
+ public String getAuthorizationID()
+ {
+ return null;
+ }
+
+ @Override
+ public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException
+ {
+ return null;
+ }
+
+ @Override
+ public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException
+ {
+ return null;
+ }
+
+ @Override
+ public Object getNegotiatedProperty(String propName)
+ {
+ return null;
+ }
+
+ @Override
+ public void dispose() throws SaslException
+ {
+ }
+ };
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java
new file mode 100644
index 0000000000..e8c24da68d
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java
@@ -0,0 +1,267 @@
+/*
+ *
+ * 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.security.auth.rmi;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Collections;
+
+import javax.management.remote.JMXPrincipal;
+import javax.security.auth.Subject;
+
+import org.apache.qpid.server.security.auth.database.Base64MD5PasswordFilePrincipalDatabase;
+import org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase;
+
+import junit.framework.TestCase;
+
+public class RMIPasswordAuthenticatorTest extends TestCase
+{
+ private final String USERNAME = "guest";
+ private final String PASSWORD = "guest";
+ private final String B64_MD5HASHED_PASSWORD = "CE4DQ6BIb/BVMN9scFyLtA==";
+ private RMIPasswordAuthenticator _rmipa;
+
+ private Base64MD5PasswordFilePrincipalDatabase _md5Pd;
+ private File _md5PwdFile;
+
+ private PlainPasswordFilePrincipalDatabase _plainPd;
+ private File _plainPwdFile;
+
+ private Subject testSubject;
+
+ protected void setUp() throws Exception
+ {
+ _rmipa = new RMIPasswordAuthenticator();
+
+ _md5Pd = new Base64MD5PasswordFilePrincipalDatabase();
+ _md5PwdFile = createTempPasswordFile(this.getClass().getName()+"md5pwd", USERNAME, B64_MD5HASHED_PASSWORD);
+ _md5Pd.setPasswordFile(_md5PwdFile.getAbsolutePath());
+
+ _plainPd = new PlainPasswordFilePrincipalDatabase();
+ _plainPwdFile = createTempPasswordFile(this.getClass().getName()+"plainpwd", USERNAME, PASSWORD);
+ _plainPd.setPasswordFile(_plainPwdFile.getAbsolutePath());
+
+ testSubject = new Subject(true,
+ Collections.singleton(new JMXPrincipal(USERNAME)),
+ Collections.EMPTY_SET,
+ Collections.EMPTY_SET);
+ }
+
+ private File createTempPasswordFile(String filenamePrefix, String user, String password)
+ {
+ try
+ {
+ File testFile = File.createTempFile(filenamePrefix,"tmp");
+ testFile.deleteOnExit();
+
+ BufferedWriter writer = new BufferedWriter(new FileWriter(testFile));
+
+ writer.write(user + ":" + password);
+ writer.newLine();
+
+ writer.flush();
+ writer.close();
+
+ return testFile;
+ }
+ catch (IOException e)
+ {
+ fail("Unable to create temporary test password file." + e.getMessage());
+ }
+
+ return null;
+ }
+
+
+ //********** Test Methods *********//
+
+
+ public void testAuthenticate()
+ {
+ String[] credentials;
+ Subject newSubject;
+
+ // Test when no PD has been set
+ try
+ {
+ credentials = new String[]{USERNAME, PASSWORD};
+ newSubject = _rmipa.authenticate(credentials);
+ fail("SecurityException expected due to lack of principal database");
+ }
+ catch (SecurityException se)
+ {
+ assertEquals("Unexpected exception message",
+ RMIPasswordAuthenticator.UNABLE_TO_LOOKUP, se.getMessage());
+ }
+
+ //The PrincipalDatabase's are tested primarily by their own tests, but
+ //minimal tests are done here to exercise their usage in this area.
+
+ // Test correct passwords are verified with an MD5 PD
+ try
+ {
+ _rmipa.setPrincipalDatabase(_md5Pd);
+ credentials = new String[]{USERNAME, PASSWORD};
+ newSubject = _rmipa.authenticate(credentials);
+ assertTrue("Returned subject does not equal expected value",
+ newSubject.equals(testSubject));
+ }
+ catch (Exception e)
+ {
+ fail("Unexpected Exception:" + e.getMessage());
+ }
+
+ // Test incorrect passwords are not verified with an MD5 PD
+ try
+ {
+ credentials = new String[]{USERNAME, PASSWORD+"incorrect"};
+ newSubject = _rmipa.authenticate(credentials);
+ fail("SecurityException expected due to incorrect password");
+ }
+ catch (SecurityException se)
+ {
+ assertEquals("Unexpected exception message",
+ RMIPasswordAuthenticator.INVALID_CREDENTIALS, se.getMessage());
+ }
+
+ // Test non-existent accounts are not verified with an MD5 PD
+ try
+ {
+ credentials = new String[]{USERNAME+"invalid", PASSWORD};
+ newSubject = _rmipa.authenticate(credentials);
+ fail("SecurityException expected due to non-existant account");
+ }
+ catch (SecurityException se)
+ {
+ assertEquals("Unexpected exception message",
+ RMIPasswordAuthenticator.INVALID_CREDENTIALS, se.getMessage());
+ }
+
+ // Test correct passwords are verified with a Plain PD
+ try
+ {
+ _rmipa.setPrincipalDatabase(_plainPd);
+ credentials = new String[]{USERNAME, PASSWORD};
+ newSubject = _rmipa.authenticate(credentials);
+ assertTrue("Returned subject does not equal expected value",
+ newSubject.equals(testSubject));
+ }
+ catch (Exception e)
+ {
+ fail("Unexpected Exception");
+ }
+
+ // Test incorrect passwords are not verified with a Plain PD
+ try
+ {
+ credentials = new String[]{USERNAME, PASSWORD+"incorrect"};
+ newSubject = _rmipa.authenticate(credentials);
+ fail("SecurityException expected due to incorrect password");
+ }
+ catch (SecurityException se)
+ {
+ assertEquals("Unexpected exception message",
+ RMIPasswordAuthenticator.INVALID_CREDENTIALS, se.getMessage());
+ }
+
+ // Test non-existent accounts are not verified with an Plain PD
+ try
+ {
+ credentials = new String[]{USERNAME+"invalid", PASSWORD};
+ newSubject = _rmipa.authenticate(credentials);
+ fail("SecurityException expected due to non existant account");
+ }
+ catch (SecurityException se)
+ {
+ assertEquals("Unexpected exception message",
+ RMIPasswordAuthenticator.INVALID_CREDENTIALS, se.getMessage());
+ }
+
+ // Test handling of non-string credential's
+ try
+ {
+ Object[] objCredentials = new Object[]{USERNAME, PASSWORD};
+ newSubject = _rmipa.authenticate(objCredentials);
+ fail("SecurityException expected due to non string[] credentials");
+ }
+ catch (SecurityException se)
+ {
+ assertEquals("Unexpected exception message",
+ RMIPasswordAuthenticator.SHOULD_BE_STRING_ARRAY, se.getMessage());
+ }
+
+ // Test handling of incorrect number of credential's
+ try
+ {
+ credentials = new String[]{USERNAME, PASSWORD, PASSWORD};
+ newSubject = _rmipa.authenticate(credentials);
+ fail("SecurityException expected due to supplying wrong number of credentials");
+ }
+ catch (SecurityException se)
+ {
+ assertEquals("Unexpected exception message",
+ RMIPasswordAuthenticator.SHOULD_HAVE_2_ELEMENTS, se.getMessage());
+ }
+
+ // Test handling of null credential's
+ try
+ {
+ //send a null array
+ credentials = null;
+ newSubject = _rmipa.authenticate(credentials);
+ fail("SecurityException expected due to not supplying an array of credentials");
+ }
+ catch (SecurityException se)
+ {
+ assertEquals("Unexpected exception message",
+ RMIPasswordAuthenticator.CREDENTIALS_REQUIRED, se.getMessage());
+ }
+
+ try
+ {
+ //send a null password
+ credentials = new String[]{USERNAME, null};
+ newSubject = _rmipa.authenticate(credentials);
+ fail("SecurityException expected due to sending a null password");
+ }
+ catch (SecurityException se)
+ {
+ assertEquals("Unexpected exception message",
+ RMIPasswordAuthenticator.SHOULD_BE_NON_NULL, se.getMessage());
+ }
+
+ try
+ {
+ //send a null username
+ credentials = new String[]{null, PASSWORD};
+ newSubject = _rmipa.authenticate(credentials);
+ fail("SecurityException expected due to sending a null username");
+ }
+ catch (SecurityException se)
+ {
+ assertEquals("Unexpected exception message",
+ RMIPasswordAuthenticator.SHOULD_BE_NON_NULL, se.getMessage());
+ }
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/CRAMMD5HexInitialiserTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/CRAMMD5HexInitialiserTest.java
new file mode 100644
index 0000000000..3c5ed1d6c2
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/CRAMMD5HexInitialiserTest.java
@@ -0,0 +1,137 @@
+/*
+ *
+ * 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.security.auth.sasl;
+
+import junit.framework.TestCase;
+import org.apache.qpid.server.security.auth.database.PropertiesPrincipalDatabase;
+import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5HexInitialiser;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Properties;
+
+/**
+ * These tests ensure that the Hex wrapping that the initialiser performs does actually operate when the handle method is called.
+ */
+public class CRAMMD5HexInitialiserTest extends TestCase
+{
+ public void testHex()
+ {
+ //Create User details for testing
+ String user = "testUser";
+ String password = "testPassword";
+
+ perform(user, password);
+ }
+
+ public void testHashedHex()
+ {
+ //Create User details for testing
+ String user = "testUser";
+ String password = "testPassword";
+
+ //Create a hashed password that we then attempt to put through the call back mechanism.
+ try
+ {
+ password = new String(MessageDigest.getInstance("MD5").digest(password.getBytes()));
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ fail(e.getMessage());
+ }
+
+ perform(user, password);
+ }
+
+ public void perform(String user, String password)
+ {
+ CRAMMD5HexInitialiser initialiser = new CRAMMD5HexInitialiser();
+
+ //Use properties to create a PrincipalDatabase
+ Properties users = new Properties();
+ users.put(user, password);
+
+ PropertiesPrincipalDatabase db = new PropertiesPrincipalDatabase(users);
+
+ initialiser.initialise(db);
+
+ //setup the callbacks
+ PasswordCallback passwordCallback = new PasswordCallback("password:", false);
+ NameCallback usernameCallback = new NameCallback("user:", user);
+
+ Callback[] callbacks = new Callback[]{usernameCallback, passwordCallback};
+
+ //Check the
+ try
+ {
+ assertNull("The password was not null before the handle call.", passwordCallback.getPassword());
+ initialiser.getCallbackHandler().handle(callbacks);
+ }
+ catch (IOException e)
+ {
+ fail(e.getMessage());
+ }
+ catch (UnsupportedCallbackException e)
+ {
+ fail(e.getMessage());
+ }
+
+ //Hex the password we initialised with and compare it with the passwordCallback
+ assertArrayEquals(toHex(password.toCharArray()), passwordCallback.getPassword());
+ }
+
+ private void assertArrayEquals(char[] expected, char[] actual)
+ {
+ assertEquals("Arrays are not the same length", expected.length, actual.length);
+
+ for (int index = 0; index < expected.length; index++)
+ {
+ assertEquals("Characters are not equal", expected[index], actual[index]);
+ }
+ }
+
+ private char[] toHex(char[] password)
+ {
+ StringBuilder sb = new StringBuilder();
+ for (char c : password)
+ {
+ //toHexString does not prepend 0 so we have to
+ if (((byte) c > -1) && (byte) c < 10)
+ {
+ sb.append(0);
+ }
+
+ sb.append(Integer.toHexString(c & 0xFF));
+ }
+
+ //Extract the hex string as char[]
+ char[] hex = new char[sb.length()];
+
+ sb.getChars(0, sb.length(), hex, 0);
+
+ return hex;
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/CRAMMD5HexServerTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/CRAMMD5HexServerTest.java
new file mode 100644
index 0000000000..8b3f9c0622
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/CRAMMD5HexServerTest.java
@@ -0,0 +1,230 @@
+/*
+ *
+ * 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.security.auth.sasl;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.Principal;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import javax.security.auth.login.AccountNotFoundException;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.codec.binary.Hex;
+import org.apache.qpid.server.security.auth.database.Base64MD5PasswordFilePrincipalDatabase;
+import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5HexInitialiser;
+import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5HexSaslServer;
+import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5HexServerFactory;
+
+/**
+ * Test for the CRAM-MD5-HEX SASL mechanism.
+ *
+ * This test case focuses on testing {@link CRAMMD5HexSaslServer} but also exercises
+ * collaborators {@link CRAMMD5HexInitialiser} and {@link Base64MD5PasswordFilePrincipalDatabase}
+ */
+public class CRAMMD5HexServerTest extends TestCase
+{
+
+ private SaslServer _saslServer; // Class under test
+ private CRAMMD5HexServerFactory _saslFactory;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ CRAMMD5HexInitialiser _initializer = new CRAMMD5HexInitialiser();
+
+ //Use properties to create a PrincipalDatabase
+ Base64MD5PasswordFilePrincipalDatabase db = createTestPrincipalDatabase();
+ assertEquals("Unexpected number of test users in the db", 2, db.getUsers().size());
+
+ _initializer.initialise(db);
+
+ _saslFactory = new CRAMMD5HexServerFactory();
+
+ _saslServer = _saslFactory.createSaslServer(CRAMMD5HexSaslServer.MECHANISM,
+ "AMQP",
+ "localhost",
+ _initializer.getProperties(),
+ _initializer.getCallbackHandler());
+ assertNotNull("Unable to create saslServer with mechanism type " + CRAMMD5HexSaslServer.MECHANISM, _saslServer);
+
+ }
+
+ public void testSuccessfulAuth() throws Exception
+ {
+
+ final byte[] serverChallenge = _saslServer.evaluateResponse(new byte[0]);
+
+ // Generate client response
+ final byte[] clientResponse = generateClientResponse("knownuser", "guest", serverChallenge);
+
+
+ byte[] nextServerChallenge = _saslServer.evaluateResponse(clientResponse);
+ assertTrue("Exchange must be flagged as complete after successful authentication", _saslServer.isComplete());
+ assertNull("Next server challenge must be null after successful authentication", nextServerChallenge);
+
+ }
+
+ public void testKnownUserPresentsWrongPassword() throws Exception
+ {
+ byte[] serverChallenge = _saslServer.evaluateResponse(new byte[0]);
+
+
+ final byte[] clientResponse = generateClientResponse("knownuser", "wrong!", serverChallenge);
+ try
+ {
+ _saslServer.evaluateResponse(clientResponse);
+ fail("Exception not thrown");
+ }
+ catch (SaslException se)
+ {
+ // PASS
+ }
+ assertFalse("Exchange must not be flagged as complete after unsuccessful authentication", _saslServer.isComplete());
+ }
+
+ public void testUnknownUser() throws Exception
+ {
+ final byte[] serverChallenge = _saslServer.evaluateResponse(new byte[0]);
+
+
+ final byte[] clientResponse = generateClientResponse("unknownuser", "guest", serverChallenge);
+
+ try
+ {
+ _saslServer.evaluateResponse(clientResponse);
+ fail("Exception not thrown");
+ }
+ catch (SaslException se)
+ {
+ assertExceptionHasUnderlyingAsCause(AccountNotFoundException.class, se);
+ // PASS
+ }
+ assertFalse("Exchange must not be flagged as complete after unsuccessful authentication", _saslServer.isComplete());
+ }
+
+ /**
+ *
+ * Demonstrates QPID-3158. A defect meant that users with some valid password were failing to
+ * authenticate when using the .NET 0-8 client (uses this SASL mechanism).
+ * It so happens that password "guest2" was one of the affected passwords.
+ *
+ * @throws Exception
+ */
+ public void testSuccessfulAuthReproducingQpid3158() throws Exception
+ {
+ byte[] serverChallenge = _saslServer.evaluateResponse(new byte[0]);
+
+ // Generate client response
+ byte[] resp = generateClientResponse("qpid3158user", "guest2", serverChallenge);
+
+ byte[] nextServerChallenge = _saslServer.evaluateResponse(resp);
+ assertTrue("Exchange must be flagged as complete after successful authentication", _saslServer.isComplete());
+ assertNull("Next server challenge must be null after successful authentication", nextServerChallenge);
+ }
+
+ /**
+ * Since we don't have a CRAM-MD5-HEX implementation client implementation in Java, this method
+ * provides the implementation for first principals.
+ *
+ * @param userId user id
+ * @param clearTextPassword clear text password
+ * @param serverChallenge challenge from server
+ *
+ * @return challenge response
+ */
+ private byte[] generateClientResponse(final String userId, final String clearTextPassword, final byte[] serverChallenge) throws Exception
+ {
+ byte[] digestedPasswordBytes = MessageDigest.getInstance("MD5").digest(clearTextPassword.getBytes());
+ char[] hexEncodedDigestedPassword = Hex.encodeHex(digestedPasswordBytes);
+ byte[] hexEncodedDigestedPasswordBytes = new String(hexEncodedDigestedPassword).getBytes();
+
+
+ Mac hmacMd5 = Mac.getInstance("HmacMD5");
+ hmacMd5.init(new SecretKeySpec(hexEncodedDigestedPasswordBytes, "HmacMD5"));
+ final byte[] messageAuthenticationCode = hmacMd5.doFinal(serverChallenge);
+
+ // Build client response
+ String responseAsString = userId + " " + new String(Hex.encodeHex(messageAuthenticationCode));
+ byte[] resp = responseAsString.getBytes();
+ return resp;
+ }
+
+ /**
+ * Creates a test principal database.
+ *
+ * @return
+ * @throws IOException
+ */
+ private Base64MD5PasswordFilePrincipalDatabase createTestPrincipalDatabase() throws IOException
+ {
+ Base64MD5PasswordFilePrincipalDatabase db = new Base64MD5PasswordFilePrincipalDatabase();
+ File file = File.createTempFile("passwd", "db");
+ file.deleteOnExit();
+ db.setPasswordFile(file.getCanonicalPath());
+ db.createPrincipal( createTestPrincipal("knownuser"), "guest".toCharArray());
+ db.createPrincipal( createTestPrincipal("qpid3158user"), "guest2".toCharArray());
+ return db;
+ }
+
+ private Principal createTestPrincipal(final String name)
+ {
+ return new Principal()
+ {
+
+ @Override
+ public String getName()
+ {
+ return name;
+ }
+ };
+ }
+
+ private void assertExceptionHasUnderlyingAsCause(final Class<? extends Throwable> expectedUnderlying, Throwable e)
+ {
+ assertNotNull(e);
+ int infiniteLoopGuard = 0; // Guard against loops in the cause chain
+ boolean foundExpectedUnderlying = false;
+ while (e.getCause() != null && infiniteLoopGuard++ < 10)
+ {
+ if (expectedUnderlying.equals(e.getCause().getClass()))
+ {
+ foundExpectedUnderlying = true;
+ break;
+ }
+ e = e.getCause();
+ }
+
+ if (!foundExpectedUnderlying)
+ {
+ fail("Not found expected underlying exception " + expectedUnderlying + " as underlying cause of " + e.getClass());
+ }
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/SaslServerTestCase.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/SaslServerTestCase.java
new file mode 100644
index 0000000000..f80413d4f8
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/SaslServerTestCase.java
@@ -0,0 +1,66 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.server.security.auth.sasl;
+
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+
+import org.apache.qpid.server.security.auth.database.PrincipalDatabase;
+
+import junit.framework.TestCase;
+
+public abstract class SaslServerTestCase extends TestCase
+{
+ protected SaslServer server;
+ protected String username = "u";
+ protected String password = "p";
+ protected String notpassword = "a";
+ protected PrincipalDatabase db = new TestPrincipalDatabase();
+
+ protected byte[] correctresponse;
+ protected byte[] wrongresponse;
+
+ public void testSucessfulAuth() throws SaslException
+ {
+ byte[] resp = this.server.evaluateResponse(correctresponse);
+ assertNull(resp);
+ }
+
+ public void testFailAuth()
+ {
+ boolean exceptionCaught = false;
+ try
+ {
+ byte[] resp = this.server.evaluateResponse(wrongresponse);
+ }
+ catch (SaslException e)
+ {
+ assertEquals("Authentication failed", e.getCause().getMessage());
+ exceptionCaught = true;
+ }
+ if (!exceptionCaught)
+ {
+ fail("Should have thrown SaslException");
+ }
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/TestPrincipalDatabase.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/TestPrincipalDatabase.java
new file mode 100644
index 0000000000..8507e49e17
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/TestPrincipalDatabase.java
@@ -0,0 +1,91 @@
+/*
+ *
+ * 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.security.auth.sasl;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.List;
+import java.util.Map;
+
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.login.AccountNotFoundException;
+
+import org.apache.qpid.server.security.auth.database.PrincipalDatabase;
+import org.apache.qpid.server.security.auth.sasl.AuthenticationProviderInitialiser;
+
+public class TestPrincipalDatabase implements PrincipalDatabase
+{
+
+ public boolean createPrincipal(Principal principal, char[] password)
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public boolean deletePrincipal(Principal principal) throws AccountNotFoundException
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public Map<String, AuthenticationProviderInitialiser> getMechanisms()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public Principal getUser(String username)
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public List<Principal> getUsers()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void setPassword(Principal principal, PasswordCallback callback) throws IOException,
+ AccountNotFoundException
+ {
+ callback.setPassword("p".toCharArray());
+ }
+
+ public boolean updatePassword(Principal principal, char[] password) throws AccountNotFoundException
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public boolean verifyPassword(String principal, char[] password) throws AccountNotFoundException
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public void reload() throws IOException
+ {
+ // TODO Auto-generated method stub
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/amqplain/AMQPlainSaslServerTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/amqplain/AMQPlainSaslServerTest.java
new file mode 100644
index 0000000000..6245064bf7
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/amqplain/AMQPlainSaslServerTest.java
@@ -0,0 +1,43 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.server.security.auth.sasl.amqplain;
+
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.framing.FieldTableFactory;
+import org.apache.qpid.server.security.auth.sasl.SaslServerTestCase;
+import org.apache.qpid.server.security.auth.sasl.UsernamePasswordInitialiser;
+
+public class AMQPlainSaslServerTest extends SaslServerTestCase
+{
+ protected void setUp() throws Exception
+ {
+ UsernamePasswordInitialiser handler = new AmqPlainInitialiser();
+ handler.initialise(db);
+ this.server = new AmqPlainSaslServer(handler.getCallbackHandler());
+ FieldTable table = FieldTableFactory.newFieldTable();
+ table.setString("LOGIN", username);
+ table.setString("PASSWORD", password);
+ correctresponse = table.getDataAsBytes();
+ table.setString("PASSWORD", notpassword);
+ wrongresponse = table.getDataAsBytes();
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServerTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServerTest.java
new file mode 100644
index 0000000000..5dd51250dc
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServerTest.java
@@ -0,0 +1,39 @@
+/*
+ *
+ * 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.security.auth.sasl.plain;
+
+import org.apache.qpid.server.security.auth.sasl.SaslServerTestCase;
+import org.apache.qpid.server.security.auth.sasl.UsernamePasswordInitialiser;
+
+public class PlainSaslServerTest extends SaslServerTestCase
+{
+
+ protected void setUp() throws Exception
+ {
+ UsernamePasswordInitialiser handler = new PlainInitialiser();
+ handler.initialise(db);
+ this.server = new PlainSaslServer(handler.getCallbackHandler());
+ correctresponse = new byte[]{0x0, (byte) username.charAt(0), 0x0, (byte) password.charAt(0)};
+ wrongresponse = new byte[]{0x0,(byte) username.charAt(0), 0x0, (byte) notpassword.charAt(0)};
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/stats/StatisticsCounterTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/stats/StatisticsCounterTest.java
new file mode 100644
index 0000000000..fbaa1342c9
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/stats/StatisticsCounterTest.java
@@ -0,0 +1,144 @@
+/*
+ *
+ * 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.stats;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for the {@link StatisticsCounter} class.
+ */
+public class StatisticsCounterTest extends TestCase
+{
+ /**
+ * Check that statistics counters are created correctly.
+ */
+ public void testCreate()
+ {
+ long before = System.currentTimeMillis();
+ StatisticsCounter counter = new StatisticsCounter("name", 1234L);
+ long after = System.currentTimeMillis();
+
+ assertTrue(before <= counter.getStart());
+ assertTrue(after >= counter.getStart());
+ assertTrue(counter.getName().startsWith("name-"));
+ assertEquals(1234L, counter.getPeriod());
+ }
+
+ /**
+ * Check that totals add up correctly.
+ */
+ public void testTotal()
+ {
+ StatisticsCounter counter = new StatisticsCounter("test", 1000L);
+ long start = counter.getStart();
+ for (int i = 0; i < 100; i++)
+ {
+ counter.registerEvent(i, start + i);
+ }
+ assertEquals(99 * 50, counter.getTotal()); // cf. Gauss
+ }
+
+ /**
+ * Test totals add up correctly even when messages are delivered
+ * out-of-order.
+ */
+ public void testTotalOutOfOrder()
+ {
+ StatisticsCounter counter = new StatisticsCounter("test", 1000L);
+ long start = counter.getStart();
+ assertEquals(0, counter.getTotal());
+ counter.registerEvent(10, start + 2500);
+ assertEquals(10, counter.getTotal());
+ counter.registerEvent(20, start + 1500);
+ assertEquals(30, counter.getTotal());
+ counter.registerEvent(10, start + 500);
+ assertEquals(40, counter.getTotal());
+ }
+
+ /**
+ * Test that the peak rate is reported correctly.
+ */
+ public void testPeak() throws Exception
+ {
+ StatisticsCounter counter = new StatisticsCounter("test", 1000L);
+ long start = counter.getStart();
+ assertEquals(0.0, counter.getPeak());
+ Thread.sleep(500);
+ counter.registerEvent(1000, start + 500);
+ Thread.sleep(1000);
+ assertEquals(1000.0, counter.getPeak());
+ counter.registerEvent(2000, start + 1500);
+ Thread.sleep(1000);
+ assertEquals(2000.0, counter.getPeak());
+ counter.registerEvent(1000, start + 2500);
+ Thread.sleep(1000);
+ assertEquals(2000.0, counter.getPeak());
+ }
+
+ /**
+ * Test that peak rate is reported correctly for out-of-order messages,
+ * and the total is also unaffected.
+ */
+ public void testPeakOutOfOrder() throws Exception
+ {
+ StatisticsCounter counter = new StatisticsCounter("test", 1000L);
+ long start = counter.getStart();
+ assertEquals(0.0, counter.getPeak());
+ counter.registerEvent(1000, start + 2500);
+ Thread.sleep(1500);
+ assertEquals(0.0, counter.getPeak());
+ counter.registerEvent(2000, start + 1500);
+ Thread.sleep(1000L);
+ assertEquals(0.0, counter.getPeak());
+ counter.registerEvent(1000, start + 500);
+ Thread.sleep(1500);
+ assertEquals(4000.0, counter.getPeak());
+ Thread.sleep(2000);
+ assertEquals(4000.0, counter.getPeak());
+ counter.registerEvent(1000, start + 500);
+ assertEquals(4000.0, counter.getPeak());
+ Thread.sleep(2000);
+ counter.registerEvent(1000);
+ assertEquals(4000.0, counter.getPeak());
+ assertEquals(6000, counter.getTotal());
+ }
+
+ /**
+ * Test the current rate is generated correctly.
+ */
+ public void testRate() throws Exception
+ {
+ StatisticsCounter counter = new StatisticsCounter("test", 1000L);
+ assertEquals(0.0, counter.getRate());
+ Thread.sleep(500);
+ counter.registerEvent(1000);
+ Thread.sleep(1000);
+ assertEquals(1000.0, counter.getRate());
+ counter.registerEvent(2000);
+ Thread.sleep(1000);
+ assertEquals(2000.0, counter.getRate());
+ counter.registerEvent(1000);
+ Thread.sleep(1000);
+ assertEquals(1000.0, counter.getRate());
+ Thread.sleep(1000);
+ assertEquals(0.0, counter.getRate());
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreShutdownTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreShutdownTest.java
new file mode 100644
index 0000000000..6ca88d1796
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreShutdownTest.java
@@ -0,0 +1,81 @@
+/*
+ *
+ * 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;
+
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+import org.apache.qpid.server.protocol.InternalTestProtocolSession;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+
+import java.util.List;
+
+public class MessageStoreShutdownTest extends InternalBrokerBaseCase
+{
+
+ public void test()
+ {
+ subscribe(getSession(), getChannel(), getQueue());
+
+ try
+ {
+ publishMessages(getSession(), getChannel(), 1);
+ }
+ catch (AMQException e)
+ {
+ e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+ fail(e.getMessage());
+ }
+
+ try
+ {
+ getRegistry().close();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+ fail(e.getMessage());
+ }
+
+ assertTrue("Session should now be closed", getSession().isClosed());
+
+
+ //Test attempting to modify the broker state after session has been closed.
+
+ //The Message should have been removed from the unacked list.
+
+ //Ack Messages
+ List<InternalTestProtocolSession.DeliveryPair> list = getSession().getDelivers(getChannel().getChannelId(), new AMQShortString("sgen_1"), 1);
+
+ InternalTestProtocolSession.DeliveryPair pair = list.get(0);
+
+ try
+ {
+ // The message should now be requeued and so unable to ack it.
+ getChannel().acknowledgeMessage(pair.getDeliveryTag(), false);
+ }
+ catch (AMQException e)
+ {
+ assertEquals("Incorrect exception thrown", "Single ack on delivery tag 1 not known for channel:1", e.getMessage());
+ }
+
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreTest.java
new file mode 100644
index 0000000000..62ceb68208
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreTest.java
@@ -0,0 +1,908 @@
+/*
+ *
+ * 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;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.configuration.PropertiesConfiguration;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.common.AMQPFilterTypes;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.framing.amqp_8_0.BasicConsumeBodyImpl;
+import org.apache.qpid.server.binding.Binding;
+import org.apache.qpid.server.configuration.VirtualHostConfiguration;
+import org.apache.qpid.server.exchange.DirectExchange;
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.exchange.ExchangeType;
+import org.apache.qpid.server.exchange.TopicExchange;
+import org.apache.qpid.server.message.AMQMessage;
+import org.apache.qpid.server.message.MessageMetaData;
+import org.apache.qpid.server.queue.AMQPriorityQueue;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.AMQQueueFactory;
+import org.apache.qpid.server.queue.BaseQueue;
+import org.apache.qpid.server.queue.ConflationQueue;
+import org.apache.qpid.server.queue.IncomingMessage;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.queue.SimpleAMQQueue;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.txn.AutoCommitTransaction;
+import org.apache.qpid.server.txn.ServerTransaction;
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.util.FileUtils;
+
+/**
+ * This tests the MessageStores by using the available interfaces.
+ *
+ * For persistent stores, it validates that Exchanges, Queues, Bindings and
+ * Messages are persisted and recovered correctly.
+ */
+public class MessageStoreTest extends InternalBrokerBaseCase
+{
+ public static final int DEFAULT_PRIORTY_LEVEL = 5;
+ public static final String SELECTOR_VALUE = "Test = 'MST'";
+ public static final String LVQ_KEY = "MST-LVQ-KEY";
+
+ AMQShortString nonDurableExchangeName = new AMQShortString("MST-NonDurableDirectExchange");
+ AMQShortString directExchangeName = new AMQShortString("MST-DirectExchange");
+ AMQShortString topicExchangeName = new AMQShortString("MST-TopicExchange");
+
+ AMQShortString durablePriorityTopicQueueName = new AMQShortString("MST-PriorityTopicQueue-Durable");
+ AMQShortString durableTopicQueueName = new AMQShortString("MST-TopicQueue-Durable");
+ AMQShortString priorityTopicQueueName = new AMQShortString("MST-PriorityTopicQueue");
+ AMQShortString topicQueueName = new AMQShortString("MST-TopicQueue");
+
+ AMQShortString durableExclusiveQueueName = new AMQShortString("MST-Queue-Durable-Exclusive");
+ AMQShortString durablePriorityQueueName = new AMQShortString("MST-PriorityQueue-Durable");
+ AMQShortString durableLastValueQueueName = new AMQShortString("MST-LastValueQueue-Durable");
+ AMQShortString durableQueueName = new AMQShortString("MST-Queue-Durable");
+ AMQShortString priorityQueueName = new AMQShortString("MST-PriorityQueue");
+ AMQShortString queueName = new AMQShortString("MST-Queue");
+
+ AMQShortString directRouting = new AMQShortString("MST-direct");
+ AMQShortString topicRouting = new AMQShortString("MST-topic");
+
+ AMQShortString queueOwner = new AMQShortString("MST");
+
+ protected PropertiesConfiguration _config;
+
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ String storePath = System.getProperty("QPID_WORK") + "/" + getName();
+
+ _config = new PropertiesConfiguration();
+ _config.addProperty("store.class", getTestProfileMessageStoreClassName());
+ _config.addProperty("store.environment-path", storePath);
+
+ cleanup(new File(storePath));
+
+ reloadVirtualHost();
+ }
+
+ protected void reloadVirtualHost()
+ {
+ VirtualHost original = getVirtualHost();
+
+ if (getVirtualHost() != null)
+ {
+ try
+ {
+ getVirtualHost().close();
+ getVirtualHost().getApplicationRegistry().
+ getVirtualHostRegistry().unregisterVirtualHost(getVirtualHost());
+ }
+ catch (Exception e)
+ {
+ fail(e.getMessage());
+ }
+ }
+
+ try
+ {
+ setVirtualHost(ApplicationRegistry.getInstance().createVirtualHost(new VirtualHostConfiguration(getClass().getName(), _config)));
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+
+ assertTrue("Virtualhost has not changed, reload was not successful", original != getVirtualHost());
+ }
+
+ /**
+ * Old MessageStoreTest segment which runs against both persistent and non-persistent stores
+ * creating queues, exchanges and bindings and then verifying message delivery to them.
+ */
+ public void testQueueExchangeAndBindingCreation() throws Exception
+ {
+ assertEquals("Should not be any existing queues", 0, getVirtualHost().getQueueRegistry().getQueues().size());
+
+ createAllQueues();
+ createAllTopicQueues();
+
+ //Register Non-Durable DirectExchange
+ Exchange nonDurableExchange = createExchange(DirectExchange.TYPE, nonDurableExchangeName, false);
+ bindAllQueuesToExchange(nonDurableExchange, directRouting);
+
+ //Register DirectExchange
+ Exchange directExchange = createExchange(DirectExchange.TYPE, directExchangeName, true);
+ bindAllQueuesToExchange(directExchange, directRouting);
+
+ //Register TopicExchange
+ Exchange topicExchange = createExchange(TopicExchange.TYPE, topicExchangeName, true);
+ bindAllTopicQueuesToExchange(topicExchange, topicRouting);
+
+ //Send Message To NonDurable direct Exchange = persistent
+ sendMessageOnExchange(nonDurableExchange, directRouting, true);
+ // and non-persistent
+ sendMessageOnExchange(nonDurableExchange, directRouting, false);
+
+ //Send Message To direct Exchange = persistent
+ sendMessageOnExchange(directExchange, directRouting, true);
+ // and non-persistent
+ sendMessageOnExchange(directExchange, directRouting, false);
+
+ //Send Message To topic Exchange = persistent
+ sendMessageOnExchange(topicExchange, topicRouting, true);
+ // and non-persistent
+ sendMessageOnExchange(topicExchange, topicRouting, false);
+
+ //Ensure all the Queues have four messages (one transient, one persistent) x 2 exchange routings
+ validateMessageOnQueues(4, true);
+ //Ensure all the topics have two messages (one transient, one persistent)
+ validateMessageOnTopics(2, true);
+
+ assertEquals("Not all queues correctly registered",
+ 10, getVirtualHost().getQueueRegistry().getQueues().size());
+ }
+
+ /**
+ * Tests message persistence by running the testQueueExchangeAndBindingCreation() method above
+ * before reloading the virtual host and ensuring that the persistent messages were restored.
+ *
+ * More specific testing of message persistence is left to store-specific unit testing.
+ */
+ public void testMessagePersistence() throws Exception
+ {
+ testQueueExchangeAndBindingCreation();
+
+ reloadVirtualHost();
+
+ //Validate durable queues and subscriptions still have the persistent messages
+ validateMessageOnQueues(2, false);
+ validateMessageOnTopics(1, false);
+ }
+
+ /**
+ * Tests message removal by running the testMessagePersistence() method above before
+ * clearing the queues, reloading the virtual host, and ensuring that the persistent
+ * messages were removed from the queues.
+ */
+ public void testMessageRemoval() throws Exception
+ {
+ testMessagePersistence();
+
+ QueueRegistry queueRegistry = getVirtualHost().getQueueRegistry();
+
+ assertEquals("Incorrect number of queues registered after recovery",
+ 6, queueRegistry.getQueues().size());
+
+ //clear the queue
+ queueRegistry.getQueue(durableQueueName).clearQueue();
+
+ //check the messages are gone
+ validateMessageOnQueue(durableQueueName, 0);
+
+ //reload and verify messages arent restored
+ reloadVirtualHost();
+
+ validateMessageOnQueue(durableQueueName, 0);
+ }
+
+ /**
+ * Tests queue persistence by creating a selection of queues with differing properties, both
+ * durable and non durable, and ensuring that following the recovery process the correct queues
+ * are present and any property manipulations (eg queue exclusivity) are correctly recovered.
+ */
+ public void testQueuePersistence() throws Exception
+ {
+ assertEquals("Should not be any existing queues",
+ 0, getVirtualHost().getQueueRegistry().getQueues().size());
+
+ //create durable and non durable queues/topics
+ createAllQueues();
+ createAllTopicQueues();
+
+ //reload the virtual host, prompting recovery of the queues/topics
+ reloadVirtualHost();
+
+ QueueRegistry queueRegistry = getVirtualHost().getQueueRegistry();
+
+ assertEquals("Incorrect number of queues registered after recovery",
+ 6, queueRegistry.getQueues().size());
+
+ //Validate the non-Durable Queues were not recovered.
+ assertNull("Non-Durable queue still registered:" + priorityQueueName,
+ queueRegistry.getQueue(priorityQueueName));
+ assertNull("Non-Durable queue still registered:" + queueName,
+ queueRegistry.getQueue(queueName));
+ assertNull("Non-Durable queue still registered:" + priorityTopicQueueName,
+ queueRegistry.getQueue(priorityTopicQueueName));
+ assertNull("Non-Durable queue still registered:" + topicQueueName,
+ queueRegistry.getQueue(topicQueueName));
+
+ //Validate normally expected properties of Queues/Topics
+ validateDurableQueueProperties();
+
+ //Update the durable exclusive queue's exclusivity and verify it is persisted and recovered correctly
+ setQueueExclusivity(false);
+ validateQueueExclusivityProperty(false);
+
+ //Reload the Virtualhost to recover the queues again
+ reloadVirtualHost();
+
+ //verify the change was persisted and recovered correctly
+ validateQueueExclusivityProperty(false);
+ }
+
+ /**
+ * Tests queue removal by creating a durable queue, verifying it recovers, and
+ * then removing it from the store, and ensuring that following the second reload
+ * process it is not recovered.
+ */
+ public void testDurableQueueRemoval() throws Exception
+ {
+ //Register Durable Queue
+ createQueue(durableQueueName, false, true, false, false);
+
+ QueueRegistry queueRegistry = getVirtualHost().getQueueRegistry();
+ assertEquals("Incorrect number of queues registered before recovery",
+ 1, queueRegistry.getQueues().size());
+
+ reloadVirtualHost();
+
+ queueRegistry = getVirtualHost().getQueueRegistry();
+ assertEquals("Incorrect number of queues registered after first recovery",
+ 1, queueRegistry.getQueues().size());
+
+ //test that removing the queue means it is not recovered next time
+ getVirtualHost().getDurableConfigurationStore().removeQueue(queueRegistry.getQueue(durableQueueName));
+
+ reloadVirtualHost();
+
+ queueRegistry = getVirtualHost().getQueueRegistry();
+ assertEquals("Incorrect number of queues registered after second recovery",
+ 0, queueRegistry.getQueues().size());
+ assertNull("Durable queue was not removed:" + durableQueueName,
+ queueRegistry.getQueue(durableQueueName));
+ }
+
+ /**
+ * Tests exchange persistence by creating a selection of exchanges, both durable
+ * and non durable, and ensuring that following the recovery process the correct
+ * durable exchanges are still present.
+ */
+ public void testExchangePersistence() throws Exception
+ {
+ int origExchangeCount = getVirtualHost().getExchangeRegistry().getExchangeNames().size();
+
+ Map<AMQShortString, Exchange> oldExchanges = createExchanges();
+
+ assertEquals("Incorrect number of exchanges registered before recovery",
+ origExchangeCount + 3, getVirtualHost().getExchangeRegistry().getExchangeNames().size());
+
+ reloadVirtualHost();
+
+ //verify the exchanges present after recovery
+ validateExchanges(origExchangeCount, oldExchanges);
+ }
+
+ /**
+ * Tests exchange removal by creating a durable exchange, verifying it recovers, and
+ * then removing it from the store, and ensuring that following the second reload
+ * process it is not recovered.
+ */
+ public void testDurableExchangeRemoval() throws Exception
+ {
+ int origExchangeCount = getVirtualHost().getExchangeRegistry().getExchangeNames().size();
+
+ createExchange(DirectExchange.TYPE, directExchangeName, true);
+
+ ExchangeRegistry exchangeRegistry = getVirtualHost().getExchangeRegistry();
+ assertEquals("Incorrect number of exchanges registered before recovery",
+ origExchangeCount + 1, exchangeRegistry.getExchangeNames().size());
+
+ reloadVirtualHost();
+
+ exchangeRegistry = getVirtualHost().getExchangeRegistry();
+ assertEquals("Incorrect number of exchanges registered after first recovery",
+ origExchangeCount + 1, exchangeRegistry.getExchangeNames().size());
+
+ //test that removing the exchange means it is not recovered next time
+ getVirtualHost().getDurableConfigurationStore().removeExchange(exchangeRegistry.getExchange(directExchangeName));
+
+ reloadVirtualHost();
+
+ exchangeRegistry = getVirtualHost().getExchangeRegistry();
+ assertEquals("Incorrect number of exchanges registered after second recovery",
+ origExchangeCount, exchangeRegistry.getExchangeNames().size());
+ assertNull("Durable exchange was not removed:" + directExchangeName,
+ exchangeRegistry.getExchange(directExchangeName));
+ }
+
+ /**
+ * Tests binding persistence by creating a selection of queues and exchanges, both durable
+ * and non durable, then adding bindings with and without selectors before reloading the
+ * virtual host and verifying that following the recovery process the correct durable
+ * bindings (those for durable queues to durable exchanges) are still present.
+ */
+ public void testBindingPersistence() throws Exception
+ {
+ int origExchangeCount = getVirtualHost().getExchangeRegistry().getExchangeNames().size();
+
+ createAllQueues();
+ createAllTopicQueues();
+
+ Map<AMQShortString, Exchange> exchanges = createExchanges();
+
+ Exchange nonDurableExchange = exchanges.get(nonDurableExchangeName);
+ Exchange directExchange = exchanges.get(directExchangeName);
+ Exchange topicExchange = exchanges.get(topicExchangeName);
+
+ bindAllQueuesToExchange(nonDurableExchange, directRouting);
+ bindAllQueuesToExchange(directExchange, directRouting);
+ bindAllTopicQueuesToExchange(topicExchange, topicRouting);
+
+ assertEquals("Incorrect number of exchanges registered before recovery",
+ origExchangeCount + 3, getVirtualHost().getExchangeRegistry().getExchangeNames().size());
+
+ reloadVirtualHost();
+
+ validateExchanges(origExchangeCount, exchanges);
+
+ validateBindingProperties();
+ }
+
+ /**
+ * Tests binding removal by creating a durable exchange, and queue, binding them together,
+ * recovering to verify the persistence, then removing it from the store, and ensuring
+ * that following the second reload process it is not recovered.
+ */
+ public void testDurableBindingRemoval() throws Exception
+ {
+ QueueRegistry queueRegistry = getVirtualHost().getQueueRegistry();
+
+ //create durable queue and exchange, bind them
+ Exchange exch = createExchange(DirectExchange.TYPE, directExchangeName, true);
+ createQueue(durableQueueName, false, true, false, false);
+ bindQueueToExchange(exch, directRouting, queueRegistry.getQueue(durableQueueName), false, null);
+
+ assertEquals("Incorrect number of bindings registered before recovery",
+ 1, queueRegistry.getQueue(durableQueueName).getBindings().size());
+
+ //verify binding is actually normally recovered
+ reloadVirtualHost();
+
+ queueRegistry = getVirtualHost().getQueueRegistry();
+ assertEquals("Incorrect number of bindings registered after first recovery",
+ 1, queueRegistry.getQueue(durableQueueName).getBindings().size());
+
+ ExchangeRegistry exchangeRegistry = getVirtualHost().getExchangeRegistry();
+ exch = exchangeRegistry.getExchange(directExchangeName);
+ assertNotNull("Exchange was not recovered", exch);
+
+ //remove the binding and verify result after recovery
+ unbindQueueFromExchange(exch, directRouting, queueRegistry.getQueue(durableQueueName), false, null);
+
+ reloadVirtualHost();
+
+ queueRegistry = getVirtualHost().getQueueRegistry();
+ assertEquals("Incorrect number of bindings registered after second recovery",
+ 0, queueRegistry.getQueue(durableQueueName).getBindings().size());
+ }
+
+ /**
+ * Validates that the durable exchanges are still present, the non durable exchange is not,
+ * and that the new exchanges are not the same objects as the provided list (i.e. that the
+ * reload actually generated new exchange objects)
+ */
+ private void validateExchanges(int originalNumExchanges, Map<AMQShortString, Exchange> oldExchanges)
+ {
+ ExchangeRegistry registry = getVirtualHost().getExchangeRegistry();
+
+ assertTrue(directExchangeName + " exchange NOT reloaded",
+ registry.getExchangeNames().contains(directExchangeName));
+ assertTrue(topicExchangeName + " exchange NOT reloaded",
+ registry.getExchangeNames().contains(topicExchangeName));
+ assertTrue(nonDurableExchangeName + " exchange reloaded",
+ !registry.getExchangeNames().contains(nonDurableExchangeName));
+
+ //check the old exchange objects are not the same as the new exchanges
+ assertTrue(directExchangeName + " exchange NOT reloaded",
+ registry.getExchange(directExchangeName) != oldExchanges.get(directExchangeName));
+ assertTrue(topicExchangeName + " exchange NOT reloaded",
+ registry.getExchange(topicExchangeName) != oldExchanges.get(topicExchangeName));
+
+ // There should only be the original exchanges + our 2 recovered durable exchanges
+ assertEquals("Incorrect number of exchanges available",
+ originalNumExchanges + 2, registry.getExchangeNames().size());
+ }
+
+ /** Validates the Durable queues and their properties are as expected following recovery */
+ private void validateBindingProperties()
+ {
+ QueueRegistry queueRegistry = getVirtualHost().getQueueRegistry();
+
+ assertEquals("Incorrect number of (durable) queues following recovery", 6, queueRegistry.getQueues().size());
+
+ validateBindingProperties(queueRegistry.getQueue(durablePriorityQueueName).getBindings(), false);
+ validateBindingProperties(queueRegistry.getQueue(durablePriorityTopicQueueName).getBindings(), true);
+ validateBindingProperties(queueRegistry.getQueue(durableQueueName).getBindings(), false);
+ validateBindingProperties(queueRegistry.getQueue(durableTopicQueueName).getBindings(), true);
+ validateBindingProperties(queueRegistry.getQueue(durableExclusiveQueueName).getBindings(), false);
+ }
+
+ /**
+ * Validate that each queue is bound only once following recovery (i.e. that bindings for non durable
+ * queues or to non durable exchanges are not recovered), and if a selector should be present
+ * that it is and contains the correct value
+ *
+ * @param bindings the set of bindings to validate
+ * @param useSelectors if set, check the binding has a JMS_SELECTOR argument and the correct value for it
+ */
+ private void validateBindingProperties(List<Binding> bindings, boolean useSelectors)
+ {
+ assertEquals("Each queue should only be bound once.", 1, bindings.size());
+
+ Binding binding = bindings.get(0);
+
+ if (useSelectors)
+ {
+ assertTrue("Binding does not contain a Selector argument.",
+ binding.getArguments().containsKey(AMQPFilterTypes.JMS_SELECTOR.getValue()));
+ assertEquals("The binding selector argument is incorrect", SELECTOR_VALUE,
+ binding.getArguments().get(AMQPFilterTypes.JMS_SELECTOR.getValue()).toString());
+ }
+ }
+
+ private void setQueueExclusivity(boolean exclusive) throws AMQException
+ {
+ QueueRegistry queueRegistry = getVirtualHost().getQueueRegistry();
+
+ AMQQueue queue = queueRegistry.getQueue(durableExclusiveQueueName);
+
+ queue.setExclusive(exclusive);
+ }
+
+ private void validateQueueExclusivityProperty(boolean expected)
+ {
+ QueueRegistry queueRegistry = getVirtualHost().getQueueRegistry();
+
+ AMQQueue queue = queueRegistry.getQueue(durableExclusiveQueueName);
+
+ assertEquals("Queue exclusivity was incorrect", queue.isExclusive(), expected);
+ }
+
+
+ private void validateDurableQueueProperties()
+ {
+ QueueRegistry queueRegistry = getVirtualHost().getQueueRegistry();
+
+ validateQueueProperties(queueRegistry.getQueue(durablePriorityQueueName), true, true, false, false);
+ validateQueueProperties(queueRegistry.getQueue(durablePriorityTopicQueueName), true, true, false, false);
+ validateQueueProperties(queueRegistry.getQueue(durableQueueName), false, true, false, false);
+ validateQueueProperties(queueRegistry.getQueue(durableTopicQueueName), false, true, false, false);
+ validateQueueProperties(queueRegistry.getQueue(durableExclusiveQueueName), false, true, true, false);
+ validateQueueProperties(queueRegistry.getQueue(durableLastValueQueueName), false, true, true, true);
+ }
+
+ private void validateQueueProperties(AMQQueue queue, boolean usePriority, boolean durable, boolean exclusive, boolean lastValueQueue)
+ {
+ if(usePriority || lastValueQueue)
+ {
+ assertNotSame("Queues cant be both Priority and LastValue based", usePriority, lastValueQueue);
+ }
+
+ if (usePriority)
+ {
+ assertEquals("Queue is no longer a Priority Queue", AMQPriorityQueue.class, queue.getClass());
+ assertEquals("Priority Queue does not have set priorities",
+ DEFAULT_PRIORTY_LEVEL, ((AMQPriorityQueue) queue).getPriorities());
+ }
+ else if (lastValueQueue)
+ {
+ assertEquals("Queue is no longer a LastValue Queue", ConflationQueue.class, queue.getClass());
+ assertEquals("LastValue Queue Key has changed", LVQ_KEY, ((ConflationQueue) queue).getConflationKey());
+ }
+ else
+ {
+ assertEquals("Queue is not 'simple'", SimpleAMQQueue.class, queue.getClass());
+ }
+
+ assertEquals("Queue owner is not as expected", queueOwner, queue.getOwner());
+ assertEquals("Queue durability is not as expected", durable, queue.isDurable());
+ assertEquals("Queue exclusivity is not as expected", exclusive, queue.isExclusive());
+ }
+
+ /**
+ * Delete the Store Environment path
+ *
+ * @param configuration The configuration that contains the store environment path.
+ */
+ private void cleanup(File environmentPath)
+ {
+ if (environmentPath.exists())
+ {
+ FileUtils.delete(environmentPath, true);
+ }
+ }
+
+ private void sendMessageOnExchange(Exchange exchange, AMQShortString routingKey, boolean deliveryMode)
+ {
+ //Set MessagePersistence
+ BasicContentHeaderProperties properties = new BasicContentHeaderProperties();
+ properties.setDeliveryMode(deliveryMode ? Integer.valueOf(2).byteValue() : Integer.valueOf(1).byteValue());
+ FieldTable headers = properties.getHeaders();
+ headers.setString("Test", "MST");
+ properties.setHeaders(headers);
+
+ MessagePublishInfo messageInfo = new TestMessagePublishInfo(exchange, false, false, routingKey);
+
+ final IncomingMessage currentMessage;
+
+
+ currentMessage = new IncomingMessage(messageInfo);
+
+ currentMessage.setExchange(exchange);
+
+ ContentHeaderBody headerBody = new ContentHeaderBody();
+ headerBody.classId = BasicConsumeBodyImpl.CLASS_ID;
+ headerBody.bodySize = 0;
+
+ headerBody.setProperties(properties);
+
+ try
+ {
+ currentMessage.setContentHeaderBody(headerBody);
+ }
+ catch (AMQException e)
+ {
+ fail(e.getMessage());
+ }
+
+ currentMessage.setExpiration();
+
+ MessageMetaData mmd = currentMessage.headersReceived();
+ currentMessage.setStoredMessage(getVirtualHost().getMessageStore().addMessage(mmd));
+ currentMessage.getStoredMessage().flushToStore();
+ currentMessage.route();
+
+
+ // check and deliver if header says body length is zero
+ if (currentMessage.allContentReceived())
+ {
+ ServerTransaction trans = new AutoCommitTransaction(getVirtualHost().getMessageStore());
+ final List<? extends BaseQueue> destinationQueues = currentMessage.getDestinationQueues();
+ trans.enqueue(currentMessage.getDestinationQueues(), currentMessage, new ServerTransaction.Action() {
+ public void postCommit()
+ {
+ try
+ {
+ AMQMessage message = new AMQMessage(currentMessage.getStoredMessage());
+
+ for(BaseQueue queue : destinationQueues)
+ {
+ queue.enqueue(message);
+ }
+ }
+ catch (AMQException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ public void onRollback()
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+ });
+ }
+ }
+
+ private void createAllQueues()
+ {
+ //Register Durable Priority Queue
+ createQueue(durablePriorityQueueName, true, true, false, false);
+
+ //Register Durable Simple Queue
+ createQueue(durableQueueName, false, true, false, false);
+
+ //Register Durable Exclusive Simple Queue
+ createQueue(durableExclusiveQueueName, false, true, true, false);
+
+ //Register Durable LastValue Queue
+ createQueue(durableLastValueQueueName, false, true, true, true);
+
+ //Register NON-Durable Priority Queue
+ createQueue(priorityQueueName, true, false, false, false);
+
+ //Register NON-Durable Simple Queue
+ createQueue(queueName, false, false, false, false);
+ }
+
+ private void createAllTopicQueues()
+ {
+ //Register Durable Priority Queue
+ createQueue(durablePriorityTopicQueueName, true, true, false, false);
+
+ //Register Durable Simple Queue
+ createQueue(durableTopicQueueName, false, true, false, false);
+
+ //Register NON-Durable Priority Queue
+ createQueue(priorityTopicQueueName, true, false, false, false);
+
+ //Register NON-Durable Simple Queue
+ createQueue(topicQueueName, false, false, false, false);
+ }
+
+ private void createQueue(AMQShortString queueName, boolean usePriority, boolean durable, boolean exclusive, boolean lastValueQueue)
+ {
+
+ FieldTable queueArguments = null;
+
+ if(usePriority || lastValueQueue)
+ {
+ assertNotSame("Queues cant be both Priority and LastValue based", usePriority, lastValueQueue);
+ }
+
+ if (usePriority)
+ {
+ queueArguments = new FieldTable();
+ queueArguments.put(AMQQueueFactory.X_QPID_PRIORITIES, DEFAULT_PRIORTY_LEVEL);
+ }
+
+ if (lastValueQueue)
+ {
+ queueArguments = new FieldTable();
+ queueArguments.put(new AMQShortString(AMQQueueFactory.QPID_LAST_VALUE_QUEUE_KEY), LVQ_KEY);
+ }
+
+ AMQQueue queue = null;
+
+ //Ideally we would be able to use the QueueDeclareHandler here.
+ try
+ {
+ queue = AMQQueueFactory.createAMQQueueImpl(queueName, durable, queueOwner, false, exclusive,
+ getVirtualHost(), queueArguments);
+
+ validateQueueProperties(queue, usePriority, durable, exclusive, lastValueQueue);
+
+ if (queue.isDurable() && !queue.isAutoDelete())
+ {
+ getVirtualHost().getMessageStore().createQueue(queue, queueArguments);
+ }
+ }
+ catch (AMQException e)
+ {
+ fail(e.getMessage());
+ }
+
+ getVirtualHost().getQueueRegistry().registerQueue(queue);
+
+ }
+
+ private Map<AMQShortString, Exchange> createExchanges()
+ {
+ Map<AMQShortString, Exchange> exchanges = new HashMap<AMQShortString, Exchange>();
+
+ //Register non-durable DirectExchange
+ exchanges.put(nonDurableExchangeName, createExchange(DirectExchange.TYPE, nonDurableExchangeName, false));
+
+ //Register durable DirectExchange and TopicExchange
+ exchanges.put(directExchangeName ,createExchange(DirectExchange.TYPE, directExchangeName, true));
+ exchanges.put(topicExchangeName,createExchange(TopicExchange.TYPE, topicExchangeName, true));
+
+ return exchanges;
+ }
+
+ private Exchange createExchange(ExchangeType<?> type, AMQShortString name, boolean durable)
+ {
+ Exchange exchange = null;
+
+ try
+ {
+ exchange = type.newInstance(getVirtualHost(), name, durable, 0, false);
+ }
+ catch (AMQException e)
+ {
+ fail(e.getMessage());
+ }
+
+ try
+ {
+ getVirtualHost().getExchangeRegistry().registerExchange(exchange);
+ if (durable)
+ {
+ getVirtualHost().getMessageStore().createExchange(exchange);
+ }
+ }
+ catch (AMQException e)
+ {
+ fail(e.getMessage());
+ }
+ return exchange;
+ }
+
+ private void bindAllQueuesToExchange(Exchange exchange, AMQShortString routingKey)
+ {
+ FieldTable queueArguments = new FieldTable();
+ queueArguments.put(AMQQueueFactory.X_QPID_PRIORITIES, DEFAULT_PRIORTY_LEVEL);
+
+ QueueRegistry queueRegistry = getVirtualHost().getQueueRegistry();
+
+ bindQueueToExchange(exchange, routingKey, queueRegistry.getQueue(durablePriorityQueueName), false, queueArguments);
+ bindQueueToExchange(exchange, routingKey, queueRegistry.getQueue(durableQueueName), false, null);
+ bindQueueToExchange(exchange, routingKey, queueRegistry.getQueue(priorityQueueName), false, queueArguments);
+ bindQueueToExchange(exchange, routingKey, queueRegistry.getQueue(queueName), false, null);
+ bindQueueToExchange(exchange, routingKey, queueRegistry.getQueue(durableExclusiveQueueName), false, null);
+ }
+
+ private void bindAllTopicQueuesToExchange(Exchange exchange, AMQShortString routingKey)
+ {
+ FieldTable queueArguments = new FieldTable();
+ queueArguments.put(AMQQueueFactory.X_QPID_PRIORITIES, DEFAULT_PRIORTY_LEVEL);
+
+ QueueRegistry queueRegistry = getVirtualHost().getQueueRegistry();
+
+ bindQueueToExchange(exchange, routingKey, queueRegistry.getQueue(durablePriorityTopicQueueName), true, queueArguments);
+ bindQueueToExchange(exchange, routingKey, queueRegistry.getQueue(durableTopicQueueName), true, null);
+ bindQueueToExchange(exchange, routingKey, queueRegistry.getQueue(priorityTopicQueueName), true, queueArguments);
+ bindQueueToExchange(exchange, routingKey, queueRegistry.getQueue(topicQueueName), true, null);
+ }
+
+
+ protected void bindQueueToExchange(Exchange exchange, AMQShortString routingKey, AMQQueue queue, boolean useSelector, FieldTable queueArguments)
+ {
+ FieldTable bindArguments = null;
+
+ if (useSelector)
+ {
+ bindArguments = new FieldTable();
+ bindArguments.put(AMQPFilterTypes.JMS_SELECTOR.getValue(), SELECTOR_VALUE );
+ }
+
+ try
+ {
+ getVirtualHost().getBindingFactory().addBinding(String.valueOf(routingKey), queue, exchange, FieldTable.convertToMap(bindArguments));
+ }
+ catch (Exception e)
+ {
+ fail(e.getMessage());
+ }
+ }
+
+ protected void unbindQueueFromExchange(Exchange exchange, AMQShortString routingKey, AMQQueue queue, boolean useSelector, FieldTable queueArguments)
+ {
+ FieldTable bindArguments = null;
+
+ if (useSelector)
+ {
+ bindArguments = new FieldTable();
+ bindArguments.put(AMQPFilterTypes.JMS_SELECTOR.getValue(), SELECTOR_VALUE );
+ }
+
+ try
+ {
+ getVirtualHost().getBindingFactory().removeBinding(String.valueOf(routingKey), queue, exchange, FieldTable.convertToMap(bindArguments));
+ }
+ catch (Exception e)
+ {
+ fail(e.getMessage());
+ }
+ }
+
+ private void validateMessageOnTopics(long messageCount, boolean allQueues)
+ {
+ validateMessageOnQueue(durablePriorityTopicQueueName, messageCount);
+ validateMessageOnQueue(durableTopicQueueName, messageCount);
+
+ if (allQueues)
+ {
+ validateMessageOnQueue(priorityTopicQueueName, messageCount);
+ validateMessageOnQueue(topicQueueName, messageCount);
+ }
+ }
+
+ private void validateMessageOnQueues(long messageCount, boolean allQueues)
+ {
+ validateMessageOnQueue(durablePriorityQueueName, messageCount);
+ validateMessageOnQueue(durableQueueName, messageCount);
+
+ if (allQueues)
+ {
+ validateMessageOnQueue(priorityQueueName, messageCount);
+ validateMessageOnQueue(queueName, messageCount);
+ }
+ }
+
+ private void validateMessageOnQueue(AMQShortString queueName, long messageCount)
+ {
+ AMQQueue queue = getVirtualHost().getQueueRegistry().getQueue(queueName);
+
+ assertNotNull("Queue(" + queueName + ") not correctly registered:", queue);
+
+ assertEquals("Incorrect Message count on queue:" + queueName, messageCount, queue.getMessageCount());
+ }
+
+ private class TestMessagePublishInfo implements MessagePublishInfo
+ {
+
+ Exchange _exchange;
+ boolean _immediate;
+ boolean _mandatory;
+ AMQShortString _routingKey;
+
+ TestMessagePublishInfo(Exchange exchange, boolean immediate, boolean mandatory, AMQShortString routingKey)
+ {
+ _exchange = exchange;
+ _immediate = immediate;
+ _mandatory = mandatory;
+ _routingKey = routingKey;
+ }
+
+ public AMQShortString getExchange()
+ {
+ return _exchange.getNameShortString();
+ }
+
+ public void setExchange(AMQShortString exchange)
+ {
+ //no-op
+ }
+
+ public boolean isImmediate()
+ {
+ return _immediate;
+ }
+
+ public boolean isMandatory()
+ {
+ return _mandatory;
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return _routingKey;
+ }
+ }
+} \ No newline at end of file
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/store/ReferenceCountingTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/store/ReferenceCountingTest.java
new file mode 100644
index 0000000000..2d41eb9899
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/store/ReferenceCountingTest.java
@@ -0,0 +1,163 @@
+/*
+ *
+ * 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;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.server.message.AMQMessage;
+import org.apache.qpid.server.message.MessageMetaData;
+import org.apache.qpid.test.utils.QpidTestCase;
+
+/**
+ * Tests that reference counting works correctly with AMQMessage and the message store
+ */
+public class ReferenceCountingTest extends QpidTestCase
+{
+ private TestMemoryMessageStore _store;
+
+
+ protected void setUp() throws Exception
+ {
+ _store = new TestMemoryMessageStore();
+ }
+
+ /**
+ * Check that when the reference count is decremented the message removes itself from the store
+ */
+ public void testMessageGetsRemoved() throws AMQException
+ {
+ ContentHeaderBody chb = createPersistentContentHeader();
+
+ MessagePublishInfo info = new MessagePublishInfo()
+ {
+
+ public AMQShortString getExchange()
+ {
+ return null;
+ }
+
+ public void setExchange(AMQShortString exchange)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isImmediate()
+ {
+ return false;
+ }
+
+ public boolean isMandatory()
+ {
+ return false;
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return null;
+ }
+ };
+
+
+
+ MessageMetaData mmd = new MessageMetaData(info, chb, 0);
+ StoredMessage storedMessage = _store.addMessage(mmd);
+
+
+ AMQMessage message = new AMQMessage(storedMessage);
+
+ message = message.takeReference();
+
+ // we call routing complete to set up the handle
+ // message.routingComplete(_store, _storeContext, new MessageHandleFactory());
+
+
+ assertEquals(1, _store.getMessageCount());
+ message.decrementReference();
+ assertEquals(0, _store.getMessageCount());
+ }
+
+ private ContentHeaderBody createPersistentContentHeader()
+ {
+ ContentHeaderBody chb = new ContentHeaderBody();
+ BasicContentHeaderProperties bchp = new BasicContentHeaderProperties();
+ bchp.setDeliveryMode((byte)2);
+ chb.setProperties(bchp);
+ return chb;
+ }
+
+ public void testMessageRemains() throws AMQException
+ {
+
+ MessagePublishInfo info = new MessagePublishInfo()
+ {
+
+ public AMQShortString getExchange()
+ {
+ return null;
+ }
+
+ public void setExchange(AMQShortString exchange)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isImmediate()
+ {
+ return false;
+ }
+
+ public boolean isMandatory()
+ {
+ return false;
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return null;
+ }
+ };
+
+ final ContentHeaderBody chb = createPersistentContentHeader();
+
+ MessageMetaData mmd = new MessageMetaData(info, chb, 0);
+ StoredMessage storedMessage = _store.addMessage(mmd);
+
+ AMQMessage message = new AMQMessage(storedMessage);
+
+
+ message = message.takeReference();
+ // we call routing complete to set up the handle
+ // message.routingComplete(_store, _storeContext, new MessageHandleFactory());
+
+ assertEquals(1, _store.getMessageCount());
+ message = message.takeReference();
+ message.decrementReference();
+ assertEquals(1, _store.getMessageCount());
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(ReferenceCountingTest.class);
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/store/SkeletonMessageStore.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/store/SkeletonMessageStore.java
new file mode 100644
index 0000000000..5ff84557d8
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/store/SkeletonMessageStore.java
@@ -0,0 +1,226 @@
+/*
+ *
+ * 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;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.AMQStoreException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.framing.abstraction.ContentChunk;
+import org.apache.qpid.server.message.MessageMetaData;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.message.ServerMessage;
+import org.apache.qpid.server.logging.LogSubject;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
+import java.nio.ByteBuffer;
+
+/**
+ * A message store that does nothing. Designed to be used in tests that do not want to use any message store
+ * functionality.
+ */
+public class SkeletonMessageStore implements MessageStore
+{
+ private final AtomicLong _messageId = new AtomicLong(1);
+
+ public void configure(String base, Configuration config) throws Exception
+ {
+ }
+
+ public void configureConfigStore(String name,
+ ConfigurationRecoveryHandler recoveryHandler,
+ Configuration config,
+ LogSubject logSubject) throws Exception
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void configureMessageStore(String name,
+ MessageStoreRecoveryHandler recoveryHandler,
+ Configuration config,
+ LogSubject logSubject) throws Exception
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void close() throws Exception
+ {
+ }
+
+ public <M extends StorableMessageMetaData> StoredMessage<M> addMessage(M metaData)
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void removeMessage(Long messageId)
+ {
+ }
+
+ public void createExchange(Exchange exchange) throws AMQStoreException
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void removeExchange(Exchange exchange) throws AMQStoreException
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void bindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQStoreException
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void unbindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQStoreException
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void createQueue(AMQQueue queue) throws AMQStoreException
+ {
+ }
+
+ public void createQueue(AMQQueue queue, FieldTable arguments) throws AMQStoreException
+ {
+ }
+
+
+
+
+ public List<AMQQueue> createQueues() throws AMQException
+ {
+ return null;
+ }
+
+ public Long getNewMessageId()
+ {
+ return _messageId.getAndIncrement();
+ }
+
+ public void storeContentBodyChunk(
+ Long messageId,
+ int index,
+ ContentChunk contentBody,
+ boolean lastContentBody) throws AMQException
+ {
+
+ }
+
+ public void storeMessageMetaData(Long messageId, MessageMetaData messageMetaData) throws AMQException
+ {
+
+ }
+
+ public MessageMetaData getMessageMetaData(Long messageId) throws AMQException
+ {
+ return null;
+ }
+
+ public ContentChunk getContentBodyChunk(Long messageId, int index) throws AMQException
+ {
+ return null;
+ }
+
+ public boolean isPersistent()
+ {
+ return false;
+ }
+
+ public void storeMessageHeader(Long messageNumber, ServerMessage message)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void storeContent(Long messageNumber, long offset, ByteBuffer body)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public ServerMessage getMessage(Long messageNumber)
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void removeQueue(final AMQQueue queue) throws AMQStoreException
+ {
+
+ }
+
+ public void configureTransactionLog(String name,
+ TransactionLogRecoveryHandler recoveryHandler,
+ Configuration storeConfiguration,
+ LogSubject logSubject) throws Exception
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public Transaction newTransaction()
+ {
+ return new Transaction()
+ {
+
+ public void enqueueMessage(TransactionLogResource queue, Long messageId) throws AMQStoreException
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void dequeueMessage(TransactionLogResource queue, Long messageId) throws AMQStoreException
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void commitTran() throws AMQStoreException
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public StoreFuture commitTranAsync() throws AMQStoreException
+ {
+ return new StoreFuture()
+ {
+ public boolean isComplete()
+ {
+ return true;
+ }
+
+ public void waitForCompletion()
+ {
+
+ }
+ };
+ }
+
+ public void abortTran() throws AMQStoreException
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+ };
+ }
+
+ public void updateQueue(AMQQueue queue) throws AMQStoreException
+ {
+
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/store/TestMemoryMessageStore.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/store/TestMemoryMessageStore.java
new file mode 100644
index 0000000000..4dea13d391
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/store/TestMemoryMessageStore.java
@@ -0,0 +1,98 @@
+/*
+ *
+ * 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;
+
+import org.apache.qpid.server.message.MessageMetaData;
+import org.apache.qpid.framing.abstraction.ContentChunk;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.List;
+import java.nio.ByteBuffer;
+
+/**
+ * Adds some extra methods to the memory message store for testing purposes.
+ */
+public class TestMemoryMessageStore extends MemoryMessageStore
+{
+ private AtomicInteger _messageCount = new AtomicInteger(0);
+
+
+ public TestMemoryMessageStore()
+ {
+ }
+
+ @Override
+ public StoredMessage addMessage(StorableMessageMetaData metaData)
+ {
+ return new TestableStoredMessage(super.addMessage(metaData));
+ }
+
+ public int getMessageCount()
+ {
+ return _messageCount.get();
+ }
+
+ private class TestableStoredMessage implements StoredMessage
+ {
+ private final StoredMessage _storedMessage;
+
+ public TestableStoredMessage(StoredMessage storedMessage)
+ {
+ _messageCount.incrementAndGet();
+ _storedMessage = storedMessage;
+ }
+
+ public StorableMessageMetaData getMetaData()
+ {
+ return _storedMessage.getMetaData();
+ }
+
+ public long getMessageNumber()
+ {
+ return _storedMessage.getMessageNumber();
+ }
+
+ public void addContent(int offsetInMessage, ByteBuffer src)
+ {
+ _storedMessage.addContent(offsetInMessage, src);
+ }
+
+ public int getContent(int offsetInMessage, ByteBuffer dst)
+ {
+ return _storedMessage.getContent(offsetInMessage, dst);
+ }
+
+ public StoreFuture flushToStore()
+ {
+ return _storedMessage.flushToStore();
+ }
+
+ public void remove()
+ {
+ _storedMessage.remove();
+ _messageCount.decrementAndGet();
+ }
+
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/store/TestableMemoryMessageStore.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/store/TestableMemoryMessageStore.java
new file mode 100644
index 0000000000..3593297a05
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/store/TestableMemoryMessageStore.java
@@ -0,0 +1,157 @@
+/*
+ *
+ * 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;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.qpid.AMQStoreException;
+import org.apache.qpid.server.queue.AMQQueue;
+
+/**
+ * Adds some extra methods to the memory message store for testing purposes.
+ */
+public class TestableMemoryMessageStore extends MemoryMessageStore
+{
+
+ MemoryMessageStore _mms = null;
+ private HashMap<Long, AMQQueue> _messages = new HashMap<Long, AMQQueue>();
+ private AtomicInteger _messageCount = new AtomicInteger(0);
+
+ public TestableMemoryMessageStore(MemoryMessageStore mms)
+ {
+ _mms = mms;
+ }
+
+ public TestableMemoryMessageStore()
+ {
+
+ }
+
+ @Override
+ public void close() throws Exception
+ {
+ // Not required to do anything
+ }
+
+ @Override
+ public StoredMessage addMessage(StorableMessageMetaData metaData)
+ {
+ return new TestableStoredMessage(super.addMessage(metaData));
+ }
+
+ public int getMessageCount()
+ {
+ return _messageCount.get();
+ }
+
+ private class TestableTransaction implements Transaction
+ {
+ public void enqueueMessage(TransactionLogResource queue, Long messageId) throws AMQStoreException
+ {
+ getMessages().put(messageId, (AMQQueue)queue);
+ }
+
+ public void dequeueMessage(TransactionLogResource queue, Long messageId) throws AMQStoreException
+ {
+ getMessages().remove(messageId);
+ }
+
+ public void commitTran() throws AMQStoreException
+ {
+ }
+
+ public StoreFuture commitTranAsync() throws AMQStoreException
+ {
+ return new StoreFuture()
+ {
+ public boolean isComplete()
+ {
+ return true;
+ }
+
+ public void waitForCompletion()
+ {
+
+ }
+ };
+ }
+
+ public void abortTran() throws AMQStoreException
+ {
+ }
+ }
+
+
+ @Override
+ public Transaction newTransaction()
+ {
+ return new TestableTransaction();
+ }
+
+ public HashMap<Long, AMQQueue> getMessages()
+ {
+ return _messages;
+ }
+
+ private class TestableStoredMessage implements StoredMessage
+ {
+ private final StoredMessage _storedMessage;
+
+ public TestableStoredMessage(StoredMessage storedMessage)
+ {
+ _messageCount.incrementAndGet();
+ _storedMessage = storedMessage;
+ }
+
+ public StorableMessageMetaData getMetaData()
+ {
+ return _storedMessage.getMetaData();
+ }
+
+ public long getMessageNumber()
+ {
+ return _storedMessage.getMessageNumber();
+ }
+
+ public void addContent(int offsetInMessage, ByteBuffer src)
+ {
+ _storedMessage.addContent(offsetInMessage, src);
+ }
+
+ public int getContent(int offsetInMessage, ByteBuffer dst)
+ {
+ return _storedMessage.getContent(offsetInMessage, dst);
+ }
+
+ public StoreFuture flushToStore()
+ {
+ return _storedMessage.flushToStore();
+ }
+
+ public void remove()
+ {
+ _storedMessage.remove();
+ _messageCount.decrementAndGet();
+ }
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java
new file mode 100644
index 0000000000..6fbc627d8c
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java
@@ -0,0 +1,262 @@
+package org.apache.qpid.server.subscription;
+
+/*
+*
+* 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.
+*
+*/
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.logging.LogActor;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.QueueEntry;
+import org.apache.qpid.server.queue.QueueEntry.SubscriptionAcquiredState;
+
+public class MockSubscription implements Subscription
+{
+
+ private boolean _closed = false;
+ private AMQShortString tag = new AMQShortString("mocktag");
+ private AMQQueue queue = null;
+ private StateListener _listener = null;
+ private AMQQueue.Context _queueContext = null;
+ private State _state = State.ACTIVE;
+ private ArrayList<QueueEntry> messages = new ArrayList<QueueEntry>();
+ private final Lock _stateChangeLock = new ReentrantLock();
+ private List<QueueEntry> _acceptEntries = null;
+
+ private final QueueEntry.SubscriptionAcquiredState _owningState = new QueueEntry.SubscriptionAcquiredState(this);
+ private final QueueEntry.SubscriptionAssignedState _assignedState = new QueueEntry.SubscriptionAssignedState(this);
+
+
+ private static final AtomicLong idGenerator = new AtomicLong(0);
+ // Create a simple ID that increments for ever new Subscription
+ private final long _subscriptionID = idGenerator.getAndIncrement();
+
+ public MockSubscription()
+ {
+ }
+
+ public MockSubscription(List<QueueEntry> acceptEntries)
+ {
+ _acceptEntries = acceptEntries;
+ }
+
+ public void close()
+ {
+ _closed = true;
+ if (_listener != null)
+ {
+ _listener.stateChange(this, _state, State.CLOSED);
+ }
+ _state = State.CLOSED;
+ }
+
+ public boolean filtersMessages()
+ {
+ return false;
+ }
+
+ public AMQChannel getChannel()
+ {
+ return null;
+ }
+
+ public AMQShortString getConsumerTag()
+ {
+ return tag;
+ }
+
+ public long getSubscriptionID()
+ {
+ return _subscriptionID;
+ }
+
+ public AMQQueue.Context getQueueContext()
+ {
+ return _queueContext;
+ }
+
+ public SubscriptionAcquiredState getOwningState()
+ {
+ return _owningState;
+ }
+
+ public QueueEntry.SubscriptionAssignedState getAssignedState()
+ {
+ return _assignedState;
+ }
+
+ public LogActor getLogActor()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isTransient()
+ {
+ return false;
+ }
+
+ public AMQQueue getQueue()
+ {
+ return queue;
+ }
+
+ public void getSendLock()
+ {
+ _stateChangeLock.lock();
+ }
+
+ public boolean hasInterest(QueueEntry entry)
+ {
+ if(_acceptEntries != null)
+ {
+ //simulate selector behaviour, only signal
+ //interest in the dictated queue entries
+ return _acceptEntries.contains(entry);
+ }
+
+ return true;
+ }
+
+ public boolean isActive()
+ {
+ return true;
+ }
+
+ public void confirmAutoClose()
+ {
+
+ }
+
+ public void set(String key, Object value)
+ {
+ }
+
+ public Object get(String key)
+ {
+ return null;
+ }
+
+ public boolean isAutoClose()
+ {
+ return false;
+ }
+
+ public boolean isBrowser()
+ {
+ return false;
+ }
+
+ public boolean isClosed()
+ {
+ return _closed;
+ }
+
+ public boolean acquires()
+ {
+ return true;
+ }
+
+ public boolean seesRequeues()
+ {
+ return true;
+ }
+
+ public boolean isSuspended()
+ {
+ return false;
+ }
+
+ public void queueDeleted(AMQQueue queue)
+ {
+ }
+
+ public void releaseSendLock()
+ {
+ _stateChangeLock.unlock();
+ }
+
+ public void resend(QueueEntry entry) throws AMQException
+ {
+ }
+
+ public void onDequeue(QueueEntry queueEntry)
+ {
+ }
+
+ public void restoreCredit(QueueEntry queueEntry)
+ {
+ }
+
+ public void send(QueueEntry entry) throws AMQException
+ {
+ if (messages.contains(entry))
+ {
+ entry.setRedelivered();
+ }
+ messages.add(entry);
+ }
+
+ public void setQueueContext(AMQQueue.Context queueContext)
+ {
+ _queueContext = queueContext;
+ }
+
+ public void setQueue(AMQQueue queue, boolean exclusive)
+ {
+ this.queue = queue;
+ }
+
+ public void setNoLocal(boolean noLocal)
+ {
+ }
+
+ public void setStateListener(StateListener listener)
+ {
+ this._listener = listener;
+ }
+
+ public State getState()
+ {
+ return _state;
+ }
+
+ public boolean wouldSuspend(QueueEntry msg)
+ {
+ return false;
+ }
+
+ public ArrayList<QueueEntry> getMessages()
+ {
+ return messages;
+ }
+
+ public boolean isSessionTransactional()
+ {
+ return false;
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/subscription/QueueBrowserUsesNoAckTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/subscription/QueueBrowserUsesNoAckTest.java
new file mode 100644
index 0000000000..b315a79b33
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/subscription/QueueBrowserUsesNoAckTest.java
@@ -0,0 +1,77 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.subscription;
+
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+import org.apache.qpid.server.protocol.InternalTestProtocolSession;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.AMQException;
+
+import java.util.List;
+
+public class QueueBrowserUsesNoAckTest extends InternalBrokerBaseCase
+{
+
+ public void testQueueBrowserUsesNoAck() throws AMQException
+ {
+ int sendMessageCount = 2;
+ int prefetch = 1;
+
+ //Check store is empty
+ checkStoreContents(0);
+
+ //Send required messsages to the queue
+ publishMessages(getSession(), getChannel(), sendMessageCount);
+
+ //Ensure they are stored
+ checkStoreContents(sendMessageCount);
+
+ //Check that there are no unacked messages
+ assertEquals("Channel should have no unacked msgs ", 0,
+ getChannel().getUnacknowledgedMessageMap().size());
+
+ //Set the prefetch on the session to be less than the sent messages
+ getChannel().setCredit(0, prefetch);
+
+ //browse the queue
+ AMQShortString browser = browse(getChannel(), getQueue());
+
+ getQueue().deliverAsync();
+
+ //Wait for messages to fill the prefetch
+ getSession().awaitDelivery(prefetch);
+
+ //Get those messages
+ List<InternalTestProtocolSession.DeliveryPair> messages =
+ getSession().getDelivers(getChannel().getChannelId(), browser,
+ prefetch);
+
+ //Ensure we recevied the prefetched messages
+ assertEquals(prefetch, messages.size());
+
+ //Check the process didn't suspend the subscription as this would
+ // indicate we are using the prefetch credit. i.e. using acks not No-Ack
+ assertTrue("The subscription has been suspended",
+ !getChannel().getSubscription(browser).getState()
+ .equals(Subscription.State.SUSPENDED));
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/txn/AutoCommitTransactionTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/txn/AutoCommitTransactionTest.java
new file mode 100644
index 0000000000..9afed49922
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/txn/AutoCommitTransactionTest.java
@@ -0,0 +1,442 @@
+/*
+ *
+ * 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.txn;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.qpid.server.message.ServerMessage;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.MockAMQQueue;
+import org.apache.qpid.server.queue.MockQueueEntry;
+import org.apache.qpid.server.queue.QueueEntry;
+import org.apache.qpid.server.store.TransactionLog;
+import org.apache.qpid.server.txn.MockStoreTransaction.TransactionState;
+import org.apache.qpid.test.utils.QpidTestCase;
+
+/**
+ * A unit test ensuring that AutoCommitTransaction creates a separate transaction for
+ * each dequeue/enqueue operation that involves enlistable messages. Verifies
+ * that the transaction is properly committed (or rolled-back in the case of exception),
+ * and that post transaction actions are correctly fired.
+ *
+ */
+public class AutoCommitTransactionTest extends QpidTestCase
+{
+ private ServerTransaction _transaction = null; // Class under test
+
+ private TransactionLog _transactionLog;
+ private AMQQueue _queue;
+ private List<AMQQueue> _queues;
+ private Collection<QueueEntry> _queueEntries;
+ private ServerMessage _message;
+ private MockAction _action;
+ private MockStoreTransaction _storeTransaction;
+
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ _storeTransaction = createTestStoreTransaction(false);
+ _transactionLog = MockStoreTransaction.createTestTransactionLog(_storeTransaction);
+ _action = new MockAction();
+
+ _transaction = new AutoCommitTransaction(_transactionLog);
+ }
+
+ /**
+ * Tests the enqueue of a non persistent message to a single non durable queue.
+ * Asserts that a store transaction has not been started and commit action fired.
+ */
+ public void testEnqueueToNonDurableQueueOfNonPersistentMessage() throws Exception
+ {
+ _message = createTestMessage(false);
+ _queue = createTestAMQQueue(false);
+
+ _transaction.enqueue(_queue, _message, _action);
+
+ assertEquals("Enqueue of non-persistent message must not cause message to be enqueued", 0, _storeTransaction.getNumberOfEnqueuedMessages());
+ assertEquals("Unexpected transaction state", TransactionState.NOT_STARTED, _storeTransaction.getState());
+ assertFalse("Rollback action must not be fired", _action.isRollbackActionFired());
+ assertTrue("Post commit action must be fired", _action.isPostCommitActionFired());
+
+ }
+
+ /**
+ * Tests the enqueue of a persistent message to a durable queue.
+ * Asserts that a store transaction has been committed and commit action fired.
+ */
+ public void testEnqueueToDurableQueueOfPersistentMessage() throws Exception
+ {
+ _message = createTestMessage(true);
+ _queue = createTestAMQQueue(true);
+
+ _transaction.enqueue(_queue, _message, _action);
+
+ assertEquals("Enqueue of persistent message to durable queue must cause message to be enqueued", 1, _storeTransaction.getNumberOfEnqueuedMessages());
+ assertEquals("Unexpected transaction state", TransactionState.COMMITTED, _storeTransaction.getState());
+ assertFalse("Rollback action must not be fired", _action.isRollbackActionFired());
+ assertTrue("Post commit action must be fired", _action.isPostCommitActionFired());
+ }
+
+ /**
+ * Tests the case where the store operation throws an exception.
+ * Asserts that the transaction is aborted and rollback action is fired.
+ */
+ public void testStoreEnqueueCausesException() throws Exception
+ {
+ _message = createTestMessage(true);
+ _queue = createTestAMQQueue(true);
+
+ _storeTransaction = createTestStoreTransaction(true);
+ _transactionLog = MockStoreTransaction.createTestTransactionLog(_storeTransaction);
+ _transaction = new AutoCommitTransaction(_transactionLog);
+
+ try
+ {
+ _transaction.enqueue(_queue, _message, _action);
+ fail("Exception not thrown");
+ }
+ catch (RuntimeException re)
+ {
+ // PASS
+ }
+
+ assertEquals("Unexpected transaction state", TransactionState.ABORTED, _storeTransaction.getState());
+ assertTrue("Rollback action must be fired", _action.isRollbackActionFired());
+ assertFalse("Post commit action must be fired", _action.isPostCommitActionFired());
+ }
+
+ /**
+ * Tests the enqueue of a non persistent message to a many non durable queues.
+ * Asserts that a store transaction has not been started and post commit action fired.
+ */
+ public void testEnqueueToManyNonDurableQueuesOfNonPersistentMessage() throws Exception
+ {
+ _message = createTestMessage(false);
+ _queues = createTestBaseQueues(new boolean[] {false, false, false});
+
+ _transaction.enqueue(_queues, _message, _action);
+
+ assertEquals("Enqueue of non-persistent message must not cause message to be enqueued", 0, _storeTransaction.getNumberOfEnqueuedMessages());
+ assertEquals("Unexpected transaction state", TransactionState.NOT_STARTED, _storeTransaction.getState());
+ assertFalse("Rollback action must not be fired", _action.isRollbackActionFired());
+ assertTrue("Post commit action must be fired", _action.isPostCommitActionFired());
+
+ }
+
+
+ /**
+ * Tests the enqueue of a persistent message to a many non durable queues.
+ * Asserts that a store transaction has not been started and post commit action
+ * fired.
+ */
+ public void testEnqueueToManyNonDurableQueuesOfPersistentMessage() throws Exception
+ {
+ _message = createTestMessage(true);
+ _queues = createTestBaseQueues(new boolean[] {false, false, false});
+
+ _transaction.enqueue(_queues, _message, _action);
+
+ assertEquals("Enqueue of persistent message to non-durable queues must not cause message to be enqueued", 0, _storeTransaction.getNumberOfEnqueuedMessages());
+ assertEquals("Unexpected transaction state", TransactionState.NOT_STARTED, _storeTransaction.getState());
+ assertFalse("Rollback action must not be fired", _action.isRollbackActionFired());
+ assertTrue("Post commit action must be fired", _action.isPostCommitActionFired());
+
+ }
+
+ /**
+ * Tests the enqueue of a persistent message to many queues, some durable others not.
+ * Asserts that a store transaction has been committed and post commit action fired.
+ */
+ public void testEnqueueToDurableAndNonDurableQueuesOfPersistentMessage() throws Exception
+ {
+ _message = createTestMessage(true);
+ _queues = createTestBaseQueues(new boolean[] {false, true, false, true});
+
+ _transaction.enqueue(_queues, _message, _action);
+
+ assertEquals("Enqueue of persistent message to durable/non-durable queues must cause messages to be enqueued", 2, _storeTransaction.getNumberOfEnqueuedMessages());
+ assertEquals("Unexpected transaction state", TransactionState.COMMITTED, _storeTransaction.getState());
+ assertFalse("Rollback action must not be fired", _action.isRollbackActionFired());
+ assertTrue("Post commit action must be fired", _action.isPostCommitActionFired());
+ }
+
+ /**
+ * Tests the case where the store operation throws an exception.
+ * Asserts that the transaction is aborted and rollback action fired.
+ */
+ public void testStoreEnqueuesCausesExceptions() throws Exception
+ {
+ _message = createTestMessage(true);
+ _queues = createTestBaseQueues(new boolean[] {true, true});
+
+ _storeTransaction = createTestStoreTransaction(true);
+ _transactionLog = MockStoreTransaction.createTestTransactionLog(_storeTransaction);
+ _transaction = new AutoCommitTransaction(_transactionLog);
+
+ try
+ {
+ _transaction.enqueue(_queues, _message, _action);
+ fail("Exception not thrown");
+ }
+ catch (RuntimeException re)
+ {
+ // PASS
+ }
+
+ assertEquals("Unexpected transaction state", TransactionState.ABORTED, _storeTransaction.getState());
+ assertTrue("Rollback action must be fired", _action.isRollbackActionFired());
+ assertFalse("Post commit action must not be fired", _action.isPostCommitActionFired());
+ }
+
+ /**
+ * Tests the dequeue of a non persistent message from a single non durable queue.
+ * Asserts that a store transaction has not been started and post commit action
+ * fired.
+ */
+ public void testDequeueFromNonDurableQueueOfNonPersistentMessage() throws Exception
+ {
+ _message = createTestMessage(false);
+ _queue = createTestAMQQueue(false);
+
+ _transaction.dequeue(_queue, _message, _action);
+
+ assertEquals("Dequeue of non-persistent message must not cause message to be dequeued", 0, _storeTransaction.getNumberOfDequeuedMessages());
+ assertEquals("Unexpected transaction state", TransactionState.NOT_STARTED, _storeTransaction.getState());
+ assertFalse("Rollback action must not be fired", _action.isRollbackActionFired());
+ assertTrue("Post commit action must be fired", _action.isPostCommitActionFired());
+
+ }
+
+ /**
+ * Tests the dequeue of a persistent message from a single non durable queue.
+ * Asserts that a store transaction has not been started and post commit
+ * action fired.
+ */
+ public void testDequeueFromDurableQueueOfPersistentMessage() throws Exception
+ {
+ _message = createTestMessage(true);
+ _queue = createTestAMQQueue(true);
+
+ _transaction.dequeue(_queue, _message, _action);
+
+ assertEquals("Dequeue of persistent message to durable queue must cause message to be dequeued",1, _storeTransaction.getNumberOfDequeuedMessages());
+ assertEquals("Unexpected transaction state", TransactionState.COMMITTED, _storeTransaction.getState());
+ assertFalse("Rollback action must not be fired", _action.isRollbackActionFired());
+ assertTrue("Post commit action must be fired", _action.isPostCommitActionFired());
+ }
+
+ /**
+ * Tests the case where the store operation throws an exception.
+ * Asserts that the transaction is aborted and post rollback action
+ * fired.
+ */
+ public void testStoreDequeueCausesException() throws Exception
+ {
+ _message = createTestMessage(true);
+ _queue = createTestAMQQueue(true);
+
+ _storeTransaction = createTestStoreTransaction(true);
+ _transactionLog = MockStoreTransaction.createTestTransactionLog(_storeTransaction);
+ _transaction = new AutoCommitTransaction(_transactionLog);
+
+ try
+ {
+ _transaction.dequeue(_queue, _message, _action);
+ fail("Exception not thrown");
+ }
+ catch (RuntimeException re)
+ {
+ // PASS
+ }
+
+ assertEquals("Unexpected transaction state", TransactionState.ABORTED, _storeTransaction.getState());
+
+ assertTrue("Rollback action must be fired", _action.isRollbackActionFired());
+ assertFalse("Post commit action must not be fired", _action.isPostCommitActionFired());
+ }
+
+ /**
+ * Tests the dequeue of a non persistent message from many non durable queues.
+ * Asserts that a store transaction has not been started and post commit action
+ * fired.
+ */
+ public void testDequeueFromManyNonDurableQueuesOfNonPersistentMessage() throws Exception
+ {
+ _queueEntries = createTestQueueEntries(new boolean[] {false, false, false}, new boolean[] {false, false, false});
+
+ _transaction.dequeue(_queueEntries, _action);
+
+ assertEquals("Dequeue of non-persistent messages must not cause message to be dequeued", 0, _storeTransaction.getNumberOfDequeuedMessages());
+ assertEquals("Unexpected transaction state", TransactionState.NOT_STARTED, _storeTransaction.getState());
+ assertEquals("Rollback action must not be fired", false, _action.isRollbackActionFired());
+ assertEquals("Post commit action must be fired", true, _action.isPostCommitActionFired());
+
+ }
+
+
+ /**
+ * Tests the dequeue of a persistent message from a many non durable queues.
+ * Asserts that a store transaction has not been started and post commit action
+ * fired.
+ */
+ public void testDequeueFromManyNonDurableQueuesOfPersistentMessage() throws Exception
+ {
+ _queueEntries = createTestQueueEntries(new boolean[] {false, false, false}, new boolean[] {true, true, true});
+
+ _transaction.dequeue(_queueEntries, _action);
+
+ assertEquals("Dequeue of persistent message from non-durable queues must not cause message to be enqueued", 0, _storeTransaction.getNumberOfDequeuedMessages());
+ assertEquals("Unexpected transaction state", TransactionState.NOT_STARTED, _storeTransaction.getState());
+ assertFalse("Rollback action must not be fired", _action.isRollbackActionFired());
+ assertTrue("Post commit action must be fired", _action.isPostCommitActionFired());
+ }
+
+ /**
+ * Tests the dequeue of a persistent message from many queues, some durable others not.
+ * Asserts that a store transaction has not been started and post commit action fired.
+ */
+ public void testDequeueFromDurableAndNonDurableQueuesOfPersistentMessage() throws Exception
+ {
+ // A transaction will exist owing to the 1st and 3rd.
+ _queueEntries = createTestQueueEntries(new boolean[] {true, false, true, true}, new boolean[] {true, true, true, false});
+
+ _transaction.dequeue(_queueEntries, _action);
+
+ assertEquals("Dequeue of persistent messages from durable/non-durable queues must cause messages to be dequeued", 2, _storeTransaction.getNumberOfDequeuedMessages());
+ assertEquals("Unexpected transaction state", TransactionState.COMMITTED, _storeTransaction.getState());
+ assertFalse("Rollback action must not be fired", _action.isRollbackActionFired());
+ assertTrue("Post commit action must be fired", _action.isPostCommitActionFired());
+ }
+
+ /**
+ * Tests the case where the store operation throws an exception.
+ * Asserts that the transaction is aborted and post rollback action fired.
+ */
+ public void testStoreDequeuesCauseExceptions() throws Exception
+ {
+ // Transactions will exist owing to the 1st and 3rd queue entries in the collection
+ _queueEntries = createTestQueueEntries(new boolean[] {true}, new boolean[] {true});
+
+ _storeTransaction = createTestStoreTransaction(true);
+ _transactionLog = MockStoreTransaction.createTestTransactionLog(_storeTransaction);
+ _transaction = new AutoCommitTransaction(_transactionLog);
+
+ try
+ {
+ _transaction.dequeue(_queueEntries, _action);
+ fail("Exception not thrown");
+ }
+ catch (RuntimeException re)
+ {
+ // PASS
+ }
+
+ assertEquals("Unexpected transaction state", TransactionState.ABORTED, _storeTransaction.getState());
+
+ assertTrue("Rollback action must be fired", _action.isRollbackActionFired());
+ assertFalse("Post commit action must not be fired", _action.isPostCommitActionFired());
+ }
+
+ /**
+ * Tests the add of a post-commit action. Since AutoCommitTranctions
+ * have no long lived transactions, the post commit action is fired immediately.
+ */
+ public void testPostCommitActionFiredImmediately() throws Exception
+ {
+
+ _transaction.addPostTransactionAction(_action);
+
+ assertTrue("Post commit action must be fired", _action.isPostCommitActionFired());
+ assertFalse("Rollback action must be fired", _action.isRollbackActionFired());
+ }
+
+ private Collection<QueueEntry> createTestQueueEntries(boolean[] queueDurableFlags, boolean[] messagePersistentFlags)
+ {
+ Collection<QueueEntry> queueEntries = new ArrayList<QueueEntry>();
+
+ assertTrue("Boolean arrays must be the same length", queueDurableFlags.length == messagePersistentFlags.length);
+
+ for(int i = 0; i < queueDurableFlags.length; i++)
+ {
+ final AMQQueue queue = createTestAMQQueue(queueDurableFlags[i]);
+ final ServerMessage message = createTestMessage(messagePersistentFlags[i]);
+
+ queueEntries.add(new MockQueueEntry()
+ {
+
+ @Override
+ public ServerMessage getMessage()
+ {
+ return message;
+ }
+
+ @Override
+ public AMQQueue getQueue()
+ {
+ return queue;
+ }
+
+ });
+ }
+
+ return queueEntries;
+ }
+
+ private MockStoreTransaction createTestStoreTransaction(boolean throwException)
+ {
+ return new MockStoreTransaction(throwException);
+ }
+
+ private List<AMQQueue> createTestBaseQueues(boolean[] durableFlags)
+ {
+ List<AMQQueue> queues = new ArrayList<AMQQueue>();
+ for (boolean b: durableFlags)
+ {
+ queues.add(createTestAMQQueue(b));
+ }
+
+ return queues;
+ }
+
+ private AMQQueue createTestAMQQueue(final boolean durable)
+ {
+ return new MockAMQQueue("mockQueue")
+ {
+ @Override
+ public boolean isDurable()
+ {
+ return durable;
+ }
+
+ };
+ }
+
+ private ServerMessage createTestMessage(final boolean persistent)
+ {
+ return new MockServerMessage(persistent);
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/txn/LocalTransactionTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/txn/LocalTransactionTest.java
new file mode 100644
index 0000000000..e81fd8e3f1
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/txn/LocalTransactionTest.java
@@ -0,0 +1,557 @@
+/*
+ *
+ * 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.txn;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.qpid.server.message.ServerMessage;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.MockAMQQueue;
+import org.apache.qpid.server.queue.MockQueueEntry;
+import org.apache.qpid.server.queue.QueueEntry;
+import org.apache.qpid.server.store.TransactionLog;
+import org.apache.qpid.server.txn.MockStoreTransaction.TransactionState;
+import org.apache.qpid.test.utils.QpidTestCase;
+
+/**
+ * A unit test ensuring that LocalTransactionTest creates a long-lived store transaction
+ * that spans many dequeue/enqueue operations of enlistable messages. Verifies
+ * that the long-lived transaction is properly committed and rolled back, and that
+ * post transaction actions are correctly fired.
+ *
+ */
+public class LocalTransactionTest extends QpidTestCase
+{
+ private ServerTransaction _transaction = null; // Class under test
+
+ private AMQQueue _queue;
+ private List<AMQQueue> _queues;
+ private Collection<QueueEntry> _queueEntries;
+ private ServerMessage _message;
+ private MockAction _action1;
+ private MockAction _action2;
+ private MockStoreTransaction _storeTransaction;
+ private TransactionLog _transactionLog;
+
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ _storeTransaction = createTestStoreTransaction(false);
+ _transactionLog = MockStoreTransaction.createTestTransactionLog(_storeTransaction);
+ _action1 = new MockAction();
+ _action2 = new MockAction();
+
+ _transaction = new LocalTransaction(_transactionLog);
+
+ }
+
+
+ /**
+ * Tests the enqueue of a non persistent message to a single non durable queue.
+ * Asserts that a store transaction has not been started.
+ */
+ public void testEnqueueToNonDurableQueueOfNonPersistentMessage() throws Exception
+ {
+ _message = createTestMessage(false);
+ _queue = createTestAMQQueue(false);
+
+ _transaction.enqueue(_queue, _message, _action1);
+
+ assertEquals("Enqueue of non-persistent message must not cause message to be enqueued", 0, _storeTransaction.getNumberOfEnqueuedMessages());
+ assertEquals("Unexpected transaction state", TransactionState.NOT_STARTED, _storeTransaction.getState());
+ assertNotFired(_action1);
+ }
+
+ /**
+ * Tests the enqueue of a persistent message to a durable queue.
+ * Asserts that a store transaction has been started.
+ */
+ public void testEnqueueToDurableQueueOfPersistentMessage() throws Exception
+ {
+ _message = createTestMessage(true);
+ _queue = createTestAMQQueue(true);
+
+ _transaction.enqueue(_queue, _message, _action1);
+
+ assertEquals("Enqueue of persistent message to durable queue must cause message to be enqueued", 1, _storeTransaction.getNumberOfEnqueuedMessages());
+ assertEquals("Unexpected transaction state", TransactionState.STARTED, _storeTransaction.getState());
+ assertNotFired(_action1);
+ }
+
+ /**
+ * Tests the case where the store operation throws an exception.
+ * Asserts that the transaction is aborted.
+ */
+ public void testStoreEnqueueCausesException() throws Exception
+ {
+ _message = createTestMessage(true);
+ _queue = createTestAMQQueue(true);
+
+ _storeTransaction = createTestStoreTransaction(true);
+ _transactionLog = MockStoreTransaction.createTestTransactionLog(_storeTransaction);
+ _transaction = new LocalTransaction(_transactionLog);
+
+ try
+ {
+ _transaction.enqueue(_queue, _message, _action1);
+ fail("Exception not thrown");
+ }
+ catch (RuntimeException re)
+ {
+ // PASS
+ }
+
+ assertTrue("Rollback action must be fired", _action1.isRollbackActionFired());
+ assertEquals("Unexpected transaction state", TransactionState.ABORTED, _storeTransaction.getState());
+
+ assertFalse("Post commit action must not be fired", _action1.isPostCommitActionFired());
+
+ }
+
+ /**
+ * Tests the enqueue of a non persistent message to a many non durable queues.
+ * Asserts that a store transaction has not been started.
+ */
+ public void testEnqueueToManyNonDurableQueuesOfNonPersistentMessage() throws Exception
+ {
+ _message = createTestMessage(false);
+ _queues = createTestBaseQueues(new boolean[] {false, false, false});
+
+ _transaction.enqueue(_queues, _message, _action1);
+
+ assertEquals("Enqueue of non-persistent message must not cause message to be enqueued", 0, _storeTransaction.getNumberOfEnqueuedMessages());
+ assertEquals("Unexpected transaction state", TransactionState.NOT_STARTED, _storeTransaction.getState());
+ assertNotFired(_action1);
+ }
+
+ /**
+ * Tests the enqueue of a persistent message to a many non durable queues.
+ * Asserts that a store transaction has not been started.
+ */
+ public void testEnqueueToManyNonDurableQueuesOfPersistentMessage() throws Exception
+ {
+ _message = createTestMessage(true);
+ _queues = createTestBaseQueues(new boolean[] {false, false, false});
+
+ _transaction.enqueue(_queues, _message, _action1);
+
+ assertEquals("Enqueue of persistent message to non-durable queues must not cause message to be enqueued", 0, _storeTransaction.getNumberOfEnqueuedMessages());
+ assertEquals("Unexpected transaction state", TransactionState.NOT_STARTED, _storeTransaction.getState());
+ assertNotFired(_action1);
+
+ }
+
+ /**
+ * Tests the enqueue of a persistent message to many queues, some durable others not.
+ * Asserts that a store transaction has been started.
+ */
+ public void testEnqueueToDurableAndNonDurableQueuesOfPersistentMessage() throws Exception
+ {
+ _message = createTestMessage(true);
+ _queues = createTestBaseQueues(new boolean[] {false, true, false, true});
+
+ _transaction.enqueue(_queues, _message, _action1);
+
+ assertEquals("Enqueue of persistent message to durable/non-durable queues must cause messages to be enqueued", 2, _storeTransaction.getNumberOfEnqueuedMessages());
+ assertEquals("Unexpected transaction state", TransactionState.STARTED, _storeTransaction.getState());
+ assertNotFired(_action1);
+
+ }
+
+ /**
+ * Tests the case where the store operation throws an exception.
+ * Asserts that the transaction is aborted.
+ */
+ public void testStoreEnqueuesCausesExceptions() throws Exception
+ {
+ _message = createTestMessage(true);
+ _queues = createTestBaseQueues(new boolean[] {true, true});
+
+ _storeTransaction = createTestStoreTransaction(true);
+ _transactionLog = MockStoreTransaction.createTestTransactionLog(_storeTransaction);
+ _transaction = new LocalTransaction(_transactionLog);
+
+ try
+ {
+ _transaction.enqueue(_queues, _message, _action1);
+ fail("Exception not thrown");
+ }
+ catch (RuntimeException re)
+ {
+ // PASS
+ }
+
+ assertTrue("Rollback action must be fired", _action1.isRollbackActionFired());
+ assertEquals("Unexpected transaction state", TransactionState.ABORTED, _storeTransaction.getState());
+ assertFalse("Post commit action must not be fired", _action1.isPostCommitActionFired());
+ }
+
+ /**
+ * Tests the dequeue of a non persistent message from a single non durable queue.
+ * Asserts that a store transaction has not been started.
+ */
+ public void testDequeueFromNonDurableQueueOfNonPersistentMessage() throws Exception
+ {
+ _message = createTestMessage(false);
+ _queue = createTestAMQQueue(false);
+
+ _transaction.dequeue(_queue, _message, _action1);
+
+ assertEquals("Dequeue of non-persistent message must not cause message to be enqueued", 0, _storeTransaction.getNumberOfEnqueuedMessages());
+ assertEquals("Unexpected transaction state", TransactionState.NOT_STARTED, _storeTransaction.getState());
+ assertNotFired(_action1);
+
+ }
+
+ /**
+ * Tests the dequeue of a persistent message from a single non durable queue.
+ * Asserts that a store transaction has not been started.
+ */
+ public void testDequeueFromDurableQueueOfPersistentMessage() throws Exception
+ {
+ _message = createTestMessage(true);
+ _queue = createTestAMQQueue(true);
+
+ _transaction.dequeue(_queue, _message, _action1);
+
+ assertEquals("Dequeue of non-persistent message must cause message to be dequeued", 1, _storeTransaction.getNumberOfDequeuedMessages());
+ assertEquals("Unexpected transaction state", TransactionState.STARTED, _storeTransaction.getState());
+ assertNotFired(_action1);
+ }
+
+ /**
+ * Tests the case where the store operation throws an exception.
+ * Asserts that the transaction is aborted.
+ */
+ public void testStoreDequeueCausesException() throws Exception
+ {
+ _message = createTestMessage(true);
+ _queue = createTestAMQQueue(true);
+
+ _storeTransaction = createTestStoreTransaction(true);
+ _transactionLog = MockStoreTransaction.createTestTransactionLog(_storeTransaction);
+ _transaction = new LocalTransaction(_transactionLog);
+
+ try
+ {
+ _transaction.dequeue(_queue, _message, _action1);
+ fail("Exception not thrown");
+ }
+ catch (RuntimeException re)
+ {
+ // PASS
+ }
+
+ assertTrue("Rollback action must be fired", _action1.isRollbackActionFired());
+ assertEquals("Unexpected transaction state", TransactionState.ABORTED, _storeTransaction.getState());
+ assertFalse("Post commit action must not be fired", _action1.isPostCommitActionFired());
+
+ }
+
+ /**
+ * Tests the dequeue of a non persistent message from many non durable queues.
+ * Asserts that a store transaction has not been started.
+ */
+ public void testDequeueFromManyNonDurableQueuesOfNonPersistentMessage() throws Exception
+ {
+ _queueEntries = createTestQueueEntries(new boolean[] {false, false, false}, new boolean[] {false, false, false});
+
+ _transaction.dequeue(_queueEntries, _action1);
+
+ assertEquals("Dequeue of non-persistent messages must not cause message to be dequeued", 0, _storeTransaction.getNumberOfDequeuedMessages());
+ assertEquals("Unexpected transaction state", TransactionState.NOT_STARTED, _storeTransaction.getState());
+ assertNotFired(_action1);
+
+ }
+
+ /**
+ * Tests the dequeue of a persistent message from a many non durable queues.
+ * Asserts that a store transaction has not been started.
+ */
+ public void testDequeueFromManyNonDurableQueuesOfPersistentMessage() throws Exception
+ {
+ _queueEntries = createTestQueueEntries(new boolean[] {false, false, false}, new boolean[] {true, true, true});
+
+ _transaction.dequeue(_queueEntries, _action1);
+
+ assertEquals("Dequeue of persistent message from non-durable queues must not cause message to be enqueued", 0, _storeTransaction.getNumberOfDequeuedMessages());
+ assertEquals("Unexpected transaction state", TransactionState.NOT_STARTED, _storeTransaction.getState());
+ assertNotFired(_action1);
+ }
+
+ /**
+ * Tests the dequeue of a persistent message from many queues, some durable others not.
+ * Asserts that a store transaction has not been started.
+ */
+ public void testDequeueFromDurableAndNonDurableQueuesOfPersistentMessage() throws Exception
+ {
+ // A transaction will exist owing to the 1st and 3rd.
+ _queueEntries = createTestQueueEntries(new boolean[] {true, false, true, true}, new boolean[] {true, true, true, false});
+
+ _transaction.dequeue(_queueEntries, _action1);
+
+ assertEquals("Dequeue of persistent messages from durable/non-durable queues must cause messages to be dequeued", 2, _storeTransaction.getNumberOfDequeuedMessages());
+ assertEquals("Unexpected transaction state", TransactionState.STARTED, _storeTransaction.getState());
+ assertNotFired(_action1);
+ }
+
+ /**
+ * Tests the case where the store operation throws an exception.
+ * Asserts that the transaction is aborted.
+ */
+ public void testStoreDequeuesCauseExceptions() throws Exception
+ {
+ // Transactions will exist owing to the 1st and 3rd queue entries in the collection
+ _queueEntries = createTestQueueEntries(new boolean[] {true}, new boolean[] {true});
+
+ _storeTransaction = createTestStoreTransaction(true);
+ _transactionLog = MockStoreTransaction.createTestTransactionLog(_storeTransaction);
+ _transaction = new LocalTransaction(_transactionLog);
+
+ try
+ {
+ _transaction.dequeue(_queueEntries, _action1);
+ fail("Exception not thrown");
+ }
+ catch (RuntimeException re)
+ {
+ // PASS
+ }
+
+ assertEquals("Unexpected transaction state", TransactionState.ABORTED, _storeTransaction.getState());
+ assertTrue("Rollback action must be fired", _action1.isRollbackActionFired());
+ assertFalse("Post commit action must not be fired", _action1.isPostCommitActionFired());
+ }
+
+ /**
+ * Tests the add of a post-commit action. Unlike AutoCommitTranctions, the post transaction actions
+ * is added to a list to be fired on commit or rollback.
+ */
+ public void testAddingPostCommitActionNotFiredImmediately() throws Exception
+ {
+
+ _transaction.addPostTransactionAction(_action1);
+
+ assertNotFired(_action1);
+ }
+
+
+ /**
+ * Tests committing a transaction without work accepted without error and without causing store
+ * enqueues or dequeues.
+ */
+ public void testCommitNoWork() throws Exception
+ {
+
+ _transaction.commit();
+
+ assertEquals("Unexpected number of store dequeues", 0, _storeTransaction.getNumberOfDequeuedMessages());
+ assertEquals("Unexpected number of store enqueues", 0, _storeTransaction.getNumberOfEnqueuedMessages());
+ assertEquals("Unexpected transaction state", TransactionState.NOT_STARTED, _storeTransaction.getState());
+ }
+
+ /**
+ * Tests rolling back a transaction without work accepted without error and without causing store
+ * enqueues or dequeues.
+ */
+ public void testRollbackNoWork() throws Exception
+ {
+
+ _transaction.rollback();
+
+ assertEquals("Unexpected number of store dequeues", 0, _storeTransaction.getNumberOfDequeuedMessages());
+ assertEquals("Unexpected number of store enqueues", 0, _storeTransaction.getNumberOfEnqueuedMessages());
+ assertEquals("Unexpected transaction state", TransactionState.NOT_STARTED, _storeTransaction.getState());
+ }
+
+ /**
+ * Tests the dequeuing of a message with a commit. Test ensures that the underlying store transaction is
+ * correctly controlled and the post commit action is fired.
+ */
+ public void testCommitWork() throws Exception
+ {
+
+ _message = createTestMessage(true);
+ _queue = createTestAMQQueue(true);
+
+ assertEquals("Unexpected transaction state", TransactionState.NOT_STARTED, _storeTransaction.getState());
+ assertFalse("Post commit action must not be fired yet", _action1.isPostCommitActionFired());
+
+ _transaction.dequeue(_queue, _message, _action1);
+ assertEquals("Unexpected transaction state", TransactionState.STARTED, _storeTransaction.getState());
+ assertFalse("Post commit action must not be fired yet", _action1.isPostCommitActionFired());
+
+ _transaction.commit();
+
+ assertEquals("Unexpected transaction state", TransactionState.COMMITTED, _storeTransaction.getState());
+ assertTrue("Post commit action must be fired", _action1.isPostCommitActionFired());
+ }
+
+ /**
+ * Tests the dequeuing of a message with a rollback. Test ensures that the underlying store transaction is
+ * correctly controlled and the post rollback action is fired.
+ */
+ public void testRollbackWork() throws Exception
+ {
+
+ _message = createTestMessage(true);
+ _queue = createTestAMQQueue(true);
+
+
+ assertEquals("Unexpected transaction state", TransactionState.NOT_STARTED, _storeTransaction.getState());
+ assertFalse("Rollback action must not be fired yet", _action1.isRollbackActionFired());
+
+ _transaction.dequeue(_queue, _message, _action1);
+
+ assertEquals("Unexpected transaction state", TransactionState.STARTED, _storeTransaction.getState());
+ assertFalse("Rollback action must not be fired yet", _action1.isRollbackActionFired());
+
+ _transaction.rollback();
+
+ assertEquals("Unexpected transaction state", TransactionState.ABORTED, _storeTransaction.getState());
+ assertTrue("Rollback action must be fired", _action1.isRollbackActionFired());
+
+ }
+
+ /**
+ * Variation of testCommitWork with an additional post transaction action.
+ *
+ */
+ public void testCommitWorkWithAdditionalPostAction() throws Exception
+ {
+
+ _message = createTestMessage(true);
+ _queue = createTestAMQQueue(true);
+
+ _transaction.addPostTransactionAction(_action1);
+ _transaction.dequeue(_queue, _message, _action2);
+ _transaction.commit();
+
+ assertEquals("Unexpected transaction state", TransactionState.COMMITTED, _storeTransaction.getState());
+
+ assertTrue("Post commit action1 must be fired", _action1.isPostCommitActionFired());
+ assertTrue("Post commit action2 must be fired", _action2.isPostCommitActionFired());
+
+ assertFalse("Rollback action1 must not be fired", _action1.isRollbackActionFired());
+ assertFalse("Rollback action2 must not be fired", _action1.isRollbackActionFired());
+ }
+
+ /**
+ * Variation of testRollbackWork with an additional post transaction action.
+ *
+ */
+ public void testRollbackWorkWithAdditionalPostAction() throws Exception
+ {
+
+ _message = createTestMessage(true);
+ _queue = createTestAMQQueue(true);
+
+ _transaction.addPostTransactionAction(_action1);
+ _transaction.dequeue(_queue, _message, _action2);
+ _transaction.rollback();
+
+ assertEquals("Unexpected transaction state", TransactionState.ABORTED, _storeTransaction.getState());
+
+ assertFalse("Post commit action1 must not be fired", _action1.isPostCommitActionFired());
+ assertFalse("Post commit action2 must not be fired", _action2.isPostCommitActionFired());
+
+ assertTrue("Rollback action1 must be fired", _action1.isRollbackActionFired());
+ assertTrue("Rollback action2 must be fired", _action1.isRollbackActionFired());
+ }
+
+ private Collection<QueueEntry> createTestQueueEntries(boolean[] queueDurableFlags, boolean[] messagePersistentFlags)
+ {
+ Collection<QueueEntry> queueEntries = new ArrayList<QueueEntry>();
+
+ assertTrue("Boolean arrays must be the same length", queueDurableFlags.length == messagePersistentFlags.length);
+
+ for(int i = 0; i < queueDurableFlags.length; i++)
+ {
+ final AMQQueue queue = createTestAMQQueue(queueDurableFlags[i]);
+ final ServerMessage message = createTestMessage(messagePersistentFlags[i]);
+
+ queueEntries.add(new MockQueueEntry()
+ {
+
+ @Override
+ public ServerMessage getMessage()
+ {
+ return message;
+ }
+
+ @Override
+ public AMQQueue getQueue()
+ {
+ return queue;
+ }
+
+ });
+ }
+
+ return queueEntries;
+ }
+
+ private MockStoreTransaction createTestStoreTransaction(boolean throwException)
+ {
+ return new MockStoreTransaction(throwException);
+ }
+
+ private List<AMQQueue> createTestBaseQueues(boolean[] durableFlags)
+ {
+ List<AMQQueue> queues = new ArrayList<AMQQueue>();
+ for (boolean b: durableFlags)
+ {
+ queues.add(createTestAMQQueue(b));
+ }
+
+ return queues;
+ }
+
+ private AMQQueue createTestAMQQueue(final boolean durable)
+ {
+ return new MockAMQQueue("mockQueue")
+ {
+ @Override
+ public boolean isDurable()
+ {
+ return durable;
+ }
+
+ };
+ }
+
+ private ServerMessage createTestMessage(final boolean persistent)
+ {
+ return new MockServerMessage(persistent);
+ }
+
+ private void assertNotFired(MockAction action)
+ {
+ assertFalse("Rollback action must not be fired", action.isRollbackActionFired());
+ assertFalse("Post commit action must not be fired", action.isPostCommitActionFired());
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/txn/MockAction.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/txn/MockAction.java
new file mode 100644
index 0000000000..975e3e91b9
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/txn/MockAction.java
@@ -0,0 +1,56 @@
+/*
+ *
+ * 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.txn;
+
+import org.apache.qpid.server.txn.ServerTransaction.Action;
+
+/**
+ * Mock implementation of a ServerTranaction Action
+ * allowing its state to be observed.
+ *
+ */
+class MockAction implements Action
+{
+ private boolean _rollbackFired = false;
+ private boolean _postCommitFired = false;
+
+ @Override
+ public void postCommit()
+ {
+ _postCommitFired = true;
+ }
+
+ @Override
+ public void onRollback()
+ {
+ _rollbackFired = true;
+ }
+
+ public boolean isRollbackActionFired()
+ {
+ return _rollbackFired;
+ }
+
+ public boolean isPostCommitActionFired()
+ {
+ return _postCommitFired;
+ }
+} \ No newline at end of file
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/txn/MockServerMessage.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/txn/MockServerMessage.java
new file mode 100644
index 0000000000..64c62fd029
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/txn/MockServerMessage.java
@@ -0,0 +1,114 @@
+/*
+ *
+ * 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.txn;
+
+import java.nio.ByteBuffer;
+
+import org.apache.commons.lang.NotImplementedException;
+import org.apache.qpid.server.configuration.SessionConfig;
+import org.apache.qpid.server.message.AMQMessageHeader;
+import org.apache.qpid.server.message.MessageReference;
+import org.apache.qpid.server.message.ServerMessage;
+
+/**
+ * Mock Server Message allowing its persistent flag to be controlled from test.
+ */
+class MockServerMessage implements ServerMessage
+{
+ /**
+ *
+ */
+ private final boolean persistent;
+
+ /**
+ * @param persistent
+ */
+ MockServerMessage(boolean persistent)
+ {
+ this.persistent = persistent;
+ }
+
+ @Override
+ public boolean isPersistent()
+ {
+ return persistent;
+ }
+
+ @Override
+ public MessageReference newReference()
+ {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public boolean isImmediate()
+ {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public long getSize()
+ {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public SessionConfig getSessionConfig()
+ {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public String getRoutingKey()
+ {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public AMQMessageHeader getMessageHeader()
+ {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public long getExpiration()
+ {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public int getContent(ByteBuffer buf, int offset)
+ {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public long getArrivalTime()
+ {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public Long getMessageNumber()
+ {
+ return 0L;
+ }
+} \ No newline at end of file
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/txn/MockStoreTransaction.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/txn/MockStoreTransaction.java
new file mode 100644
index 0000000000..5700bba9f8
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/txn/MockStoreTransaction.java
@@ -0,0 +1,136 @@
+/*
+ *
+ * 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.txn;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.lang.NotImplementedException;
+import org.apache.qpid.AMQStoreException;
+import org.apache.qpid.server.logging.LogSubject;
+import org.apache.qpid.server.store.TransactionLog;
+import org.apache.qpid.server.store.TransactionLogRecoveryHandler;
+import org.apache.qpid.server.store.TransactionLogResource;
+import org.apache.qpid.server.store.TransactionLog.StoreFuture;
+import org.apache.qpid.server.store.TransactionLog.Transaction;
+
+/**
+ * Mock implementation of a (Store) Transaction allow its state to be observed.
+ * Also provide a factory method to produce TestTransactionLog objects suitable
+ * for unit test use.
+ *
+ */
+class MockStoreTransaction implements Transaction
+{
+ enum TransactionState {NOT_STARTED, STARTED, COMMITTED, ABORTED};
+
+ private TransactionState _state = TransactionState.NOT_STARTED;
+
+ private int _numberOfEnqueuedMessages = 0;
+ private int _numberOfDequeuedMessages = 0;
+ private boolean _throwExceptionOnQueueOp;
+
+ public MockStoreTransaction(boolean throwExceptionOnQueueOp)
+ {
+ _throwExceptionOnQueueOp = throwExceptionOnQueueOp;
+ }
+
+ public void setState(TransactionState state)
+ {
+ _state = state;
+ }
+
+ public TransactionState getState()
+ {
+ return _state;
+ }
+
+ @Override
+ public void enqueueMessage(TransactionLogResource queue, Long messageId) throws AMQStoreException
+ {
+ if (_throwExceptionOnQueueOp)
+ {
+
+ throw new AMQStoreException("Mocked exception");
+ }
+
+ _numberOfEnqueuedMessages++;
+ }
+
+ public int getNumberOfDequeuedMessages()
+ {
+ return _numberOfDequeuedMessages;
+ }
+
+ public int getNumberOfEnqueuedMessages()
+ {
+ return _numberOfEnqueuedMessages;
+ }
+
+
+ @Override
+ public void dequeueMessage(TransactionLogResource queue, Long messageId) throws AMQStoreException
+ {
+ if (_throwExceptionOnQueueOp)
+ {
+ throw new AMQStoreException("Mocked exception");
+ }
+
+ _numberOfDequeuedMessages++;
+ }
+
+ @Override
+ public void commitTran() throws AMQStoreException
+ {
+ _state = TransactionState.COMMITTED;
+ }
+
+ @Override
+ public StoreFuture commitTranAsync() throws AMQStoreException
+ {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public void abortTran() throws AMQStoreException
+ {
+ _state = TransactionState.ABORTED;
+ }
+
+ public static TransactionLog createTestTransactionLog(final MockStoreTransaction storeTransaction)
+ {
+ return new TransactionLog()
+ {
+
+ @Override
+ public void configureTransactionLog(String name, TransactionLogRecoveryHandler recoveryHandler,
+ Configuration storeConfiguration, LogSubject logSubject) throws Exception
+ {
+ }
+
+ @Override
+ public Transaction newTransaction()
+ {
+ storeTransaction.setState(TransactionState.STARTED);
+ return storeTransaction;
+ }
+
+ };
+ }
+} \ No newline at end of file
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/util/InternalBrokerBaseCase.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/util/InternalBrokerBaseCase.java
new file mode 100644
index 0000000000..ff94942457
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/util/InternalBrokerBaseCase.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.util;
+
+import org.apache.commons.configuration.XMLConfiguration;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.common.AMQPFilterTypes;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.logging.SystemOutMessageLogger;
+import org.apache.qpid.server.logging.actors.CurrentActor;
+import org.apache.qpid.server.configuration.ServerConfiguration;
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.logging.actors.TestLogActor;
+import org.apache.qpid.server.protocol.InternalTestProtocolSession;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.AMQQueueFactory;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.registry.IApplicationRegistry;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.store.TestableMemoryMessageStore;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.test.utils.QpidTestCase;
+import org.apache.qpid.util.MockChannel;
+
+
+public class InternalBrokerBaseCase extends QpidTestCase
+{
+ private IApplicationRegistry _registry;
+ private MessageStore _messageStore;
+ private MockChannel _channel;
+ private InternalTestProtocolSession _session;
+ private VirtualHost _virtualHost;
+ private AMQQueue _queue;
+ private AMQShortString QUEUE_NAME;
+ private ServerConfiguration _configuration;
+ private XMLConfiguration _configXml = new XMLConfiguration();
+ private boolean _started = false;
+
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ _configXml.addProperty("virtualhosts.virtualhost.name", "test");
+ _configXml.addProperty("virtualhosts.virtualhost.test.store.class", TestableMemoryMessageStore.class.getName());
+
+ _configXml.addProperty("virtualhosts.virtualhost(-1).name", getName());
+ _configXml.addProperty("virtualhosts.virtualhost(-1)."+getName()+".store.class", TestableMemoryMessageStore.class.getName());
+
+ createBroker();
+ }
+
+ protected void createBroker() throws Exception
+ {
+ _started = true;
+ CurrentActor.set(new TestLogActor(new SystemOutMessageLogger()));
+
+ _configuration = new ServerConfiguration(_configXml);
+
+ configure();
+
+ _registry = new TestApplicationRegistry(_configuration);
+ ApplicationRegistry.initialise(_registry);
+ _registry.getVirtualHostRegistry().setDefaultVirtualHostName(getName());
+ _virtualHost = _registry.getVirtualHostRegistry().getVirtualHost(getName());
+
+ QUEUE_NAME = new AMQShortString("test");
+ // Create a queue on the test Vhost.. this will aid in diagnosing duff tests
+ // as the ExpiredMessage Task will log with the test Name.
+ _queue = AMQQueueFactory.createAMQQueueImpl(QUEUE_NAME, false, new AMQShortString("testowner"),
+ false, false, _virtualHost, null);
+
+ Exchange defaultExchange = _virtualHost.getExchangeRegistry().getDefaultExchange();
+ _virtualHost.getBindingFactory().addBinding(QUEUE_NAME.toString(), _queue, defaultExchange, null);
+
+ _virtualHost = _registry.getVirtualHostRegistry().getVirtualHost("test");
+ _messageStore = _virtualHost.getMessageStore();
+
+ _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString(getName()), false, new AMQShortString("testowner"),
+ false, false, _virtualHost, null);
+
+ _virtualHost.getQueueRegistry().registerQueue(_queue);
+
+ defaultExchange = _virtualHost.getExchangeRegistry().getDefaultExchange();
+
+ _virtualHost.getBindingFactory().addBinding(getName(), _queue, defaultExchange, null);
+
+ _session = new InternalTestProtocolSession(_virtualHost);
+ CurrentActor.set(_session.getLogActor());
+
+ _channel = new MockChannel(_session, 1, _messageStore);
+
+ _session.addChannel(_channel);
+ }
+
+ protected void configure()
+ {
+ // Allow other tests to override configuration
+ }
+
+ protected void stopBroker()
+ {
+ try
+ {
+ //Remove the ProtocolSession Actor added during createBroker
+ CurrentActor.remove();
+ }
+ finally
+ {
+ ApplicationRegistry.remove();
+ _started = false;
+ }
+ }
+
+
+ public void tearDown() throws Exception
+ {
+ try
+ {
+ if (_started)
+ {
+ stopBroker();
+ }
+ }
+ finally
+ {
+ super.tearDown();
+ // Purge Any erroneously added actors
+ CurrentActor.removeAll();
+ }
+ }
+
+ protected void checkStoreContents(int messageCount)
+ {
+ assertEquals("Message header count incorrect in the MetaDataMap", messageCount, ((TestableMemoryMessageStore) _messageStore).getMessageCount());
+
+ //The above publish message is sufficiently small not to fit in the header so no Body is required.
+ //assertEquals("Message body count incorrect in the ContentBodyMap", messageCount, ((TestableMemoryMessageStore) _messageStore).getContentBodyMap().size());
+ }
+
+ protected AMQShortString subscribe(InternalTestProtocolSession session, AMQChannel channel, AMQQueue queue)
+ {
+ try
+ {
+ return channel.subscribeToQueue(null, queue, true, null, false, true);
+ }
+ catch (AMQException e)
+ {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+
+ //Keep the compiler happy
+ return null;
+ }
+
+ protected AMQShortString browse(AMQChannel channel, AMQQueue queue)
+ {
+ try
+ {
+ FieldTable filters = new FieldTable();
+ filters.put(AMQPFilterTypes.NO_CONSUME.getValue(), true);
+
+ return channel.subscribeToQueue(null, queue, true, filters, false, true);
+ }
+ catch (AMQException e)
+ {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+
+ //Keep the compiler happy
+ return null;
+ }
+
+ public void publishMessages(InternalTestProtocolSession session, AMQChannel channel, int messages) throws AMQException
+ {
+ MessagePublishInfo info = new MessagePublishInfo()
+ {
+ public AMQShortString getExchange()
+ {
+ return ExchangeDefaults.DEFAULT_EXCHANGE_NAME;
+ }
+
+ public void setExchange(AMQShortString exchange)
+ {
+
+ }
+
+ public boolean isImmediate()
+ {
+ return false;
+ }
+
+ public boolean isMandatory()
+ {
+ return false;
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return new AMQShortString(getName());
+ }
+ };
+
+ for (int count = 0; count < messages; count++)
+ {
+ channel.setPublishFrame(info, _virtualHost.getExchangeRegistry().getExchange(info.getExchange()));
+
+ //Set the body size
+ ContentHeaderBody _headerBody = new ContentHeaderBody();
+ _headerBody.bodySize = 0;
+
+ //Set Minimum properties
+ BasicContentHeaderProperties properties = new BasicContentHeaderProperties();
+
+ properties.setExpiration(0L);
+ properties.setTimestamp(System.currentTimeMillis());
+
+ //Make Message Persistent
+ properties.setDeliveryMode((byte) 2);
+
+ _headerBody.setProperties(properties);
+
+ channel.publishContentHeader(_headerBody);
+ }
+
+ }
+
+ public void acknowledge(AMQChannel channel, long deliveryTag)
+ {
+ try
+ {
+ channel.acknowledgeMessage(deliveryTag, false);
+ }
+ catch (AMQException e)
+ {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+ }
+
+ public IApplicationRegistry getRegistry()
+ {
+ return _registry;
+ }
+
+ public void setRegistry(IApplicationRegistry registry)
+ {
+ _registry = registry;
+ }
+
+ public MessageStore getMessageStore()
+ {
+ return _messageStore;
+ }
+
+ public void setMessageStore(MessageStore messageStore)
+ {
+ _messageStore = messageStore;
+ }
+
+ public MockChannel getChannel()
+ {
+ return _channel;
+ }
+
+ public void setChannel(MockChannel channel)
+ {
+ _channel = channel;
+ }
+
+ public InternalTestProtocolSession getSession()
+ {
+ return _session;
+ }
+
+ public void setSession(InternalTestProtocolSession session)
+ {
+ _session = session;
+ }
+
+ public VirtualHost getVirtualHost()
+ {
+ return _virtualHost;
+ }
+
+ public void setVirtualHost(VirtualHost virtualHost)
+ {
+ _virtualHost = virtualHost;
+ }
+
+ public AMQQueue getQueue()
+ {
+ return _queue;
+ }
+
+ public void setQueue(AMQQueue queue)
+ {
+ _queue = queue;
+ }
+
+ public AMQShortString getQUEUE_NAME()
+ {
+ return QUEUE_NAME;
+ }
+
+ public void setQUEUE_NAME(AMQShortString QUEUE_NAME)
+ {
+ this.QUEUE_NAME = QUEUE_NAME;
+ }
+
+ public ServerConfiguration getConfiguration()
+ {
+ return _configuration;
+ }
+
+ public void setConfiguration(ServerConfiguration configuration)
+ {
+ _configuration = configuration;
+ }
+
+ public XMLConfiguration getConfigXml()
+ {
+ return _configXml;
+ }
+
+ public void setConfigXml(XMLConfiguration configXml)
+ {
+ _configXml = configXml;
+ }
+
+ public boolean isStarted()
+ {
+ return _started;
+ }
+
+ public void setStarted(boolean started)
+ {
+ _started = started;
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/util/LoggingProxyTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/util/LoggingProxyTest.java
new file mode 100644
index 0000000000..c7db51016e
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/util/LoggingProxyTest.java
@@ -0,0 +1,88 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.util;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+public class LoggingProxyTest extends TestCase
+{
+ static interface IFoo {
+ void foo();
+ void foo(int i, Collection c);
+ String bar();
+ String bar(String s, List l);
+ }
+
+ static class Foo implements IFoo {
+ public void foo()
+ {
+ }
+
+ public void foo(int i, Collection c)
+ {
+ }
+
+ public String bar()
+ {
+ return null;
+ }
+
+ public String bar(String s, List l)
+ {
+ return "ha";
+ }
+ }
+
+ public void testSimple() {
+ LoggingProxy proxy = new LoggingProxy(new Foo(), 20);
+ IFoo foo = (IFoo)proxy.getProxy(IFoo.class);
+ foo.foo();
+ assertEquals(2, proxy.getBufferSize());
+ assertTrue(proxy.getBuffer().get(0).toString().matches(".*: foo\\(\\) entered$"));
+ assertTrue(proxy.getBuffer().get(1).toString().matches(".*: foo\\(\\) returned$"));
+
+ foo.foo(3, Arrays.asList(0, 1, 2));
+ assertEquals(4, proxy.getBufferSize());
+ assertTrue(proxy.getBuffer().get(2).toString().matches(".*: foo\\(\\[3, \\[0, 1, 2\\]\\]\\) entered$"));
+ assertTrue(proxy.getBuffer().get(3).toString().matches(".*: foo\\(\\) returned$"));
+
+ foo.bar();
+ assertEquals(6, proxy.getBufferSize());
+ assertTrue(proxy.getBuffer().get(4).toString().matches(".*: bar\\(\\) entered$"));
+ assertTrue(proxy.getBuffer().get(5).toString().matches(".*: bar\\(\\) returned null$"));
+
+ foo.bar("hello", Arrays.asList(1, 2, 3));
+ assertEquals(8, proxy.getBufferSize());
+ assertTrue(proxy.getBuffer().get(6).toString().matches(".*: bar\\(\\[hello, \\[1, 2, 3\\]\\]\\) entered$"));
+ assertTrue(proxy.getBuffer().get(7).toString().matches(".*: bar\\(\\) returned ha$"));
+
+ proxy.dump();
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(LoggingProxyTest.class);
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.java
new file mode 100644
index 0000000000..af8997cf40
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.java
@@ -0,0 +1,48 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.util;
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.qpid.server.configuration.ServerConfiguration;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.security.auth.database.PropertiesPrincipalDatabaseManager;
+
+import java.util.Properties;
+
+
+public class TestApplicationRegistry extends ApplicationRegistry
+{
+ public TestApplicationRegistry(ServerConfiguration config) throws ConfigurationException
+ {
+ super(config);
+ }
+
+ protected void createDatabaseManager(ServerConfiguration configuration) throws Exception
+ {
+ Properties users = new Properties();
+ users.put("guest","guest");
+ users.put("admin","admin");
+ _databaseManager = new PropertiesPrincipalDatabaseManager("testPasswordFile", users);
+ }
+
+}
+
+
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/virtualhost/plugins/SlowConsumerDetectionConfigurationTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/virtualhost/plugins/SlowConsumerDetectionConfigurationTest.java
new file mode 100644
index 0000000000..cc11d68e07
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/virtualhost/plugins/SlowConsumerDetectionConfigurationTest.java
@@ -0,0 +1,346 @@
+/*
+ *
+ * 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.plugins;
+
+import org.apache.commons.configuration.CompositeConfiguration;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.XMLConfiguration;
+import org.apache.qpid.server.configuration.plugins.SlowConsumerDetectionConfiguration;
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Provide Unit Test coverage of the virtualhost SlowConsumer Configuration
+ * This is what controls how often the plugin will execute
+ */
+public class SlowConsumerDetectionConfigurationTest extends InternalBrokerBaseCase
+{
+
+ /**
+ * Default Testing:
+ *
+ * Provide a fully complete and valid configuration specifying 'delay' and
+ * 'timeunit' and ensure that it is correctly processed.
+ *
+ * Ensure no exceptions are thrown and that we get the same values back that
+ * were put into the configuration.
+ */
+ public void testConfigLoadingValidConfig()
+ {
+ SlowConsumerDetectionConfiguration config = new SlowConsumerDetectionConfiguration();
+
+ XMLConfiguration xmlconfig = new XMLConfiguration();
+
+ long DELAY=10;
+ String TIMEUNIT=TimeUnit.MICROSECONDS.toString();
+ xmlconfig.addProperty("delay", String.valueOf(DELAY));
+ xmlconfig.addProperty("timeunit", TIMEUNIT);
+
+ // Create a CompositeConfiguration as this is what the broker uses
+ CompositeConfiguration composite = new CompositeConfiguration();
+ composite.addConfiguration(xmlconfig);
+
+ try
+ {
+ config.setConfiguration("", composite);
+ }
+ catch (ConfigurationException e)
+ {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+
+ assertEquals("Delay not correctly returned.", DELAY, config.getDelay());
+ assertEquals("TimeUnit not correctly returned.",
+ TIMEUNIT, String.valueOf(config.getTimeUnit()));
+ }
+
+ /**
+ * Default Testing:
+ *
+ * Test Missing TimeUnit value gets default.
+ *
+ * The TimeUnit value is optional and default to SECONDS.
+ *
+ * Test that if we do not specify a TimeUnit then we correctly get seconds.
+ *
+ * Also verify that relying on the default does not impact the setting of
+ * the 'delay' value.
+ *
+ */
+ public void testConfigLoadingMissingTimeUnitDefaults()
+ {
+ SlowConsumerDetectionConfiguration config = new SlowConsumerDetectionConfiguration();
+
+ XMLConfiguration xmlconfig = new XMLConfiguration();
+
+ long DELAY=10;
+ xmlconfig.addProperty("delay", String.valueOf(DELAY));
+
+ // Create a CompositeConfiguration as this is what the broker uses
+ CompositeConfiguration composite = new CompositeConfiguration();
+ composite.addConfiguration(xmlconfig);
+ try
+ {
+ config.setConfiguration("", composite);
+ }
+ catch (ConfigurationException e)
+ {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+
+ assertEquals("Delay not correctly returned.", DELAY, config.getDelay());
+ assertEquals("Default TimeUnit incorrect", TimeUnit.SECONDS, config.getTimeUnit());
+ }
+
+ /**
+ * Input Testing:
+ *
+ * TimeUnit parsing requires the String value be in UpperCase.
+ * Ensure we can handle when the user doesn't know this.
+ *
+ * Same test as 'testConfigLoadingValidConfig' but checking that
+ * the timeunit field is not case sensitive.
+ * i.e. the toUpper is being correctly applied.
+ */
+ public void testConfigLoadingValidConfigStrangeTimeUnit()
+ {
+ SlowConsumerDetectionConfiguration config = new SlowConsumerDetectionConfiguration();
+
+ XMLConfiguration xmlconfig = new XMLConfiguration();
+
+ long DELAY=10;
+
+ xmlconfig.addProperty("delay", DELAY);
+ xmlconfig.addProperty("timeunit", "MiCrOsEcOnDs");
+
+ // Create a CompositeConfiguration as this is what the broker uses
+ CompositeConfiguration composite = new CompositeConfiguration();
+ composite.addConfiguration(xmlconfig);
+
+ try
+ {
+ config.setConfiguration("", composite);
+ }
+ catch (ConfigurationException e)
+ {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+
+ assertEquals("Delay not correctly returned.", DELAY, config.getDelay());
+ assertEquals("TimeUnit not correctly returned.",
+ TimeUnit.MICROSECONDS.toString(), String.valueOf(config.getTimeUnit()));
+
+ }
+
+ /**
+ * Failure Testing:
+ *
+ * Test that delay must be long not a string value.
+ * Provide a delay as a written value not a long. 'ten'.
+ *
+ * This should throw a configuration exception which is being trapped and
+ * verified to be the right exception, a NumberFormatException.
+ *
+ */
+ public void testConfigLoadingInValidDelayString()
+ {
+ SlowConsumerDetectionConfiguration config = new SlowConsumerDetectionConfiguration();
+
+ XMLConfiguration xmlconfig = new XMLConfiguration();
+
+ xmlconfig.addProperty("delay", "ten");
+ xmlconfig.addProperty("timeunit", TimeUnit.MICROSECONDS.toString());
+
+ // Create a CompositeConfiguration as this is what the broker uses
+ CompositeConfiguration composite = new CompositeConfiguration();
+ composite.addConfiguration(xmlconfig);
+
+ try
+ {
+ config.setConfiguration("", composite);
+ fail("Configuration should fail to validate");
+ }
+ catch (ConfigurationException e)
+ {
+ Throwable cause = e.getCause();
+
+ assertEquals("Cause not correct", NumberFormatException.class, cause.getClass());
+ }
+ }
+
+ /**
+ * Failure Testing:
+ *
+ * Test that negative delays are invalid.
+ *
+ * Delay must be a positive value as negative delay means doesn't make sense.
+ *
+ * Configuration exception with a useful message should be thrown here.
+ *
+ */
+ public void testConfigLoadingInValidDelayNegative()
+ {
+ SlowConsumerDetectionConfiguration config = new SlowConsumerDetectionConfiguration();
+
+ XMLConfiguration xmlconfig = new XMLConfiguration();
+
+ xmlconfig.addProperty("delay", "-10");
+ xmlconfig.addProperty("timeunit", TimeUnit.MICROSECONDS.toString());
+
+ // Create a CompositeConfiguration as this is what the broker uses
+ CompositeConfiguration composite = new CompositeConfiguration();
+ composite.addConfiguration(xmlconfig);
+
+ try
+ {
+ config.setConfiguration("", composite);
+ fail("Configuration should fail to validate");
+ }
+ catch (ConfigurationException e)
+ {
+ Throwable cause = e.getCause();
+
+ assertNotNull("Configuration Exception must not be null.", cause);
+ assertEquals("Cause not correct",
+ ConfigurationException.class, cause.getClass());
+ assertEquals("Incorrect message.",
+ "SlowConsumerDetectionConfiguration: 'delay' must be a Positive Long value.",
+ cause.getMessage());
+ }
+ }
+
+ /**
+ * Failure Testing:
+ *
+ * Test that delay cannot be 0.
+ *
+ * A zero delay means run constantly. This is not how VirtualHostTasks
+ * are designed to be run so we dis-allow the use of 0 delay.
+ *
+ * Same test as 'testConfigLoadingInValidDelayNegative' but with a 0 value.
+ *
+ */
+ public void testConfigLoadingInValidDelayZero()
+ {
+ SlowConsumerDetectionConfiguration config = new SlowConsumerDetectionConfiguration();
+
+ XMLConfiguration xmlconfig = new XMLConfiguration();
+
+ xmlconfig.addProperty("delay", "0");
+ xmlconfig.addProperty("timeunit", TimeUnit.MICROSECONDS.toString());
+
+ // Create a CompositeConfiguration as this is what the broker uses
+ CompositeConfiguration composite = new CompositeConfiguration();
+ composite.addConfiguration(xmlconfig);
+
+ try
+ {
+ config.setConfiguration("", composite);
+ fail("Configuration should fail to validate");
+ }
+ catch (ConfigurationException e)
+ {
+ Throwable cause = e.getCause();
+
+ assertNotNull("Configuration Exception must not be null.", cause);
+ assertEquals("Cause not correct",
+ ConfigurationException.class, cause.getClass());
+ assertEquals("Incorrect message.",
+ "SlowConsumerDetectionConfiguration: 'delay' must be a Positive Long value.",
+ cause.getMessage());
+ }
+ }
+
+ /**
+ * Failure Testing:
+ *
+ * Test that missing delay fails.
+ * If we have no delay then we do not pick a default. So a Configuration
+ * Exception is thrown.
+ *
+ * */
+ public void testConfigLoadingInValidMissingDelay()
+ {
+ SlowConsumerDetectionConfiguration config = new SlowConsumerDetectionConfiguration();
+
+ XMLConfiguration xmlconfig = new XMLConfiguration();
+
+ xmlconfig.addProperty("timeunit", TimeUnit.SECONDS.toString());
+
+ // Create a CompositeConfiguration as this is what the broker uses
+ CompositeConfiguration composite = new CompositeConfiguration();
+ composite.addConfiguration(xmlconfig);
+ try
+ {
+ config.setConfiguration("", composite);
+ fail("Configuration should fail to validate");
+ }
+ catch (ConfigurationException e)
+ {
+ assertEquals("Incorrect message.", "SlowConsumerDetectionConfiguration: unable to configure invalid delay:null", e.getMessage());
+ }
+ }
+
+ /**
+ * Failure Testing:
+ *
+ * Test that erroneous TimeUnit fails.
+ *
+ * Valid TimeUnit values vary based on the JVM version i.e. 1.6 added HOURS/DAYS etc.
+ *
+ * We don't test the values for TimeUnit are accepted other than MILLISECONDS in the
+ * positive testing at the start.
+ *
+ * Here we ensure that an erroneous for TimeUnit correctly throws an exception.
+ *
+ * We test with 'foo', which will never be a TimeUnit
+ *
+ */
+ public void testConfigLoadingInValidTimeUnit()
+ {
+ SlowConsumerDetectionConfiguration config = new SlowConsumerDetectionConfiguration();
+
+ String TIMEUNIT = "foo";
+ XMLConfiguration xmlconfig = new XMLConfiguration();
+
+ xmlconfig.addProperty("delay", "10");
+ xmlconfig.addProperty("timeunit", TIMEUNIT);
+
+ // Create a CompositeConfiguration as this is what the broker uses
+ CompositeConfiguration composite = new CompositeConfiguration();
+ composite.addConfiguration(xmlconfig);
+ try
+ {
+ config.setConfiguration("", composite);
+ fail("Configuration should fail to validate");
+ }
+ catch (ConfigurationException e)
+ {
+ assertEquals("Incorrect message.", "Unable to configure Slow Consumer Detection invalid TimeUnit:" + TIMEUNIT, e.getMessage());
+ }
+ }
+
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/virtualhost/plugins/SlowConsumerDetectionPolicyConfigurationTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/virtualhost/plugins/SlowConsumerDetectionPolicyConfigurationTest.java
new file mode 100644
index 0000000000..efb898e365
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/virtualhost/plugins/SlowConsumerDetectionPolicyConfigurationTest.java
@@ -0,0 +1,104 @@
+/*
+ *
+ * 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.plugins;
+
+import org.apache.commons.configuration.CompositeConfiguration;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.XMLConfiguration;
+import org.apache.qpid.server.configuration.plugins.SlowConsumerDetectionPolicyConfiguration;
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+
+/**
+ * Test class to ensure that the policy configuration can be processed.
+ */
+public class SlowConsumerDetectionPolicyConfigurationTest extends InternalBrokerBaseCase
+{
+
+ /**
+ * Input Testing:
+ *
+ * Test that a given String can be set and retrieved through the configuration
+ *
+ * No validation is being performed to ensure that the policy exists. Only
+ * that a value can be set for the policy.
+ *
+ */
+ public void testConfigLoadingValidConfig()
+ {
+ SlowConsumerDetectionPolicyConfiguration config = new SlowConsumerDetectionPolicyConfiguration();
+
+ XMLConfiguration xmlconfig = new XMLConfiguration();
+
+ String policyName = "TestPolicy";
+ xmlconfig.addProperty("name", policyName);
+
+ // Create a CompositeConfiguration as this is what the broker uses
+ CompositeConfiguration composite = new CompositeConfiguration();
+ composite.addConfiguration(xmlconfig);
+
+ try
+ {
+ config.setConfiguration("", composite);
+ }
+ catch (ConfigurationException e)
+ {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+
+ assertEquals("Policy name not retrieved as expected.",
+ policyName, config.getPolicyName());
+ }
+
+ /**
+ * Failure Testing:
+ *
+ * Test that providing a configuration section without the 'name' field
+ * causes an exception to be thrown.
+ *
+ * An empty configuration is provided and the thrown exception message
+ * is checked to confirm the right reason.
+ *
+ */
+ public void testConfigLoadingInValidConfig()
+ {
+ SlowConsumerDetectionPolicyConfiguration config = new SlowConsumerDetectionPolicyConfiguration();
+
+ XMLConfiguration xmlconfig = new XMLConfiguration();
+
+
+ // Create a CompositeConfiguration as this is what the broker uses
+ CompositeConfiguration composite = new CompositeConfiguration();
+ composite.addConfiguration(xmlconfig);
+
+ try
+ {
+ config.setConfiguration("", composite);
+ fail("Config is invalid so won't validate.");
+ }
+ catch (ConfigurationException e)
+ {
+ e.printStackTrace();
+ assertEquals("Exception message not as expected.", "No Slow consumer policy defined.", e.getMessage());
+ }
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/virtualhost/plugins/SlowConsumerDetectionQueueConfigurationTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/virtualhost/plugins/SlowConsumerDetectionQueueConfigurationTest.java
new file mode 100644
index 0000000000..be86037dd8
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/virtualhost/plugins/SlowConsumerDetectionQueueConfigurationTest.java
@@ -0,0 +1,185 @@
+/*
+ * 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.plugins;
+
+import org.apache.commons.configuration.CompositeConfiguration;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.XMLConfiguration;
+import org.apache.qpid.server.configuration.plugins.SlowConsumerDetectionQueueConfiguration;
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+
+/**
+ * Unit test the QueueConfiguration processing.
+ *
+ * This is slightly awkward as the {@link SlowConsumerDetectionQueueConfiguration}
+ * requries that a policy be available.
+ * <p>
+ * So all the Valid test much catch the ensuing {@link ConfigurationException} and
+ * validate that the error is due to a lack of a valid policy.
+ */
+public class SlowConsumerDetectionQueueConfigurationTest extends InternalBrokerBaseCase
+{
+ /**
+ * Test a fully loaded configuration file.
+ *
+ * It is not an error to have all control values specified.
+ * <p>
+ * Here we need to catch the {@link ConfigurationException} that ensues due to lack
+ * of a policy plugin.
+ */
+ public void testConfigLoadingValidConfig()
+ {
+ SlowConsumerDetectionQueueConfiguration config = new SlowConsumerDetectionQueueConfiguration();
+
+ XMLConfiguration xmlconfig = new XMLConfiguration();
+
+ xmlconfig.addProperty("messageAge", "60000");
+ xmlconfig.addProperty("depth", "1024");
+ xmlconfig.addProperty("messageCount", "10");
+
+ // Create a CompositeConfiguration as this is what the broker uses
+ CompositeConfiguration composite = new CompositeConfiguration();
+ composite.addConfiguration(xmlconfig);
+
+ try
+ {
+ config.setConfiguration("", composite);
+ fail("No Policies are avaialbe to load in a unit test");
+ }
+ catch (ConfigurationException e)
+ {
+ assertTrue("Exception message incorrect, was: " + e.getMessage(),
+ e.getMessage().startsWith("No Slow Consumer Policy specified. Known Policies:["));
+ }
+ }
+
+ /**
+ * When we do not specify any control value then a {@link ConfigurationException}
+ * must be thrown to remind us.
+ */
+ public void testConfigLoadingMissingConfig()
+ {
+ SlowConsumerDetectionQueueConfiguration config = new SlowConsumerDetectionQueueConfiguration();
+
+ XMLConfiguration xmlconfig = new XMLConfiguration();
+
+ // Create a CompositeConfiguration as this is what the broker uses
+ CompositeConfiguration composite = new CompositeConfiguration();
+ composite.addConfiguration(xmlconfig);
+
+ try
+ {
+ config.setConfiguration("", composite);
+ fail("No Policies are avaialbe to load in a unit test");
+ }
+ catch (ConfigurationException e)
+ {
+
+ assertEquals("At least one configuration property('messageAge','depth'" +
+ " or 'messageCount') must be specified.", e.getMessage());
+ }
+ }
+
+ /**
+ * Setting messageAge on its own is enough to have a valid configuration
+ *
+ * Here we need to catch the {@link ConfigurationException} that ensues due to lack
+ * of a policy plugin.
+ */
+ public void testConfigLoadingMessageAgeOk()
+ {
+ SlowConsumerDetectionQueueConfiguration config = new SlowConsumerDetectionQueueConfiguration();
+
+ XMLConfiguration xmlconfig = new XMLConfiguration();
+ xmlconfig.addProperty("messageAge", "60000");
+
+ // Create a CompositeConfiguration as this is what the broker uses
+ CompositeConfiguration composite = new CompositeConfiguration();
+ composite.addConfiguration(xmlconfig);
+
+ try
+ {
+ config.setConfiguration("", composite);
+ fail("No Policies are avaialbe to load in a unit test");
+ }
+ catch (ConfigurationException e)
+ {
+ assertTrue("Exception message incorrect, was: " + e.getMessage(),
+ e.getMessage().startsWith("No Slow Consumer Policy specified. Known Policies:["));
+ }
+ }
+
+ /**
+ * Setting depth on its own is enough to have a valid configuration.
+ *
+ * Here we need to catch the {@link ConfigurationException} that ensues due to lack
+ * of a policy plugin.
+ */
+ public void testConfigLoadingDepthOk()
+ {
+ SlowConsumerDetectionQueueConfiguration config = new SlowConsumerDetectionQueueConfiguration();
+
+ XMLConfiguration xmlconfig = new XMLConfiguration();
+ xmlconfig.addProperty("depth", "1024");
+
+ // Create a CompositeConfiguration as this is what the broker uses
+ CompositeConfiguration composite = new CompositeConfiguration();
+ composite.addConfiguration(xmlconfig);
+
+ try
+ {
+ config.setConfiguration("", composite);
+ fail("No Policies are avaialbe to load in a unit test");
+ }
+ catch (ConfigurationException e)
+ {
+ assertTrue("Exception message incorrect, was: " + e.getMessage(),
+ e.getMessage().startsWith("No Slow Consumer Policy specified. Known Policies:["));
+ }
+ }
+
+ /**
+ * Setting messageCount on its own is enough to have a valid configuration.
+ *
+ * Here we need to catch the {@link ConfigurationException} that ensues due to lack
+ * of a policy plugin.
+ */
+ public void testConfigLoadingMessageCountOk()
+ {
+ SlowConsumerDetectionQueueConfiguration config = new SlowConsumerDetectionQueueConfiguration();
+
+ XMLConfiguration xmlconfig = new XMLConfiguration();
+ xmlconfig.addProperty("messageCount", "10");
+
+ // Create a CompositeConfiguration as this is what the broker uses
+ CompositeConfiguration composite = new CompositeConfiguration();
+ composite.addConfiguration(xmlconfig);
+
+ try
+ {
+ config.setConfiguration("", composite);
+ fail("No Policies are avaialbe to load in a unit test");
+ }
+ catch (ConfigurationException e)
+ {
+ assertTrue("Exception message incorrect, was: " + e.getMessage(),
+ e.getMessage().startsWith("No Slow Consumer Policy specified. Known Policies:["));
+ }
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/virtualhost/plugins/policies/TopicDeletePolicyConfigurationTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/virtualhost/plugins/policies/TopicDeletePolicyConfigurationTest.java
new file mode 100644
index 0000000000..3d3cc810df
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/virtualhost/plugins/policies/TopicDeletePolicyConfigurationTest.java
@@ -0,0 +1,88 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.virtualhost.plugins.policies;
+
+import org.apache.commons.configuration.CompositeConfiguration;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.XMLConfiguration;
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+
+/**
+ * Test to ensure TopicDelete Policy configuration can be loaded.
+ */
+public class TopicDeletePolicyConfigurationTest extends InternalBrokerBaseCase
+{
+ /**
+ * Test without any configuration being provided that the
+ * deletePersistent option is disabled.
+ */
+ public void testNoConfigNoDeletePersistent()
+ {
+ TopicDeletePolicyConfiguration config = new TopicDeletePolicyConfiguration();
+
+ assertFalse("TopicDelete Configuration with no config should not delete persistent queues.",
+ config.deletePersistent());
+ }
+
+ /**
+ * Test that with the correct configuration the deletePersistent option can
+ * be enabled.
+ *
+ * Test creates a new Configuration object and passes in the xml snippet
+ * that the ConfigurationPlugin would receive during normal execution.
+ * This is the XML that would be matched for this plugin:
+ * <topicdelete>
+ * <delete-persistent>
+ * <topicdelete>
+ *
+ * So it would be subset and passed in as just:
+ * <delete-persistent>
+ *
+ *
+ * The property should therefore be enabled.
+ *
+ */
+ public void testConfigDeletePersistent()
+ {
+ TopicDeletePolicyConfiguration config = new TopicDeletePolicyConfiguration();
+
+ XMLConfiguration xmlconfig = new XMLConfiguration();
+
+ xmlconfig.addProperty("delete-persistent","");
+
+ // Create a CompositeConfiguration as this is what the broker uses
+ CompositeConfiguration composite = new CompositeConfiguration();
+ composite.addConfiguration(xmlconfig);
+
+ try
+ {
+ config.setConfiguration("",composite);
+ }
+ catch (ConfigurationException e)
+ {
+ fail(e.getMessage());
+ }
+
+ assertTrue("A configured TopicDelete should delete persistent queues.",
+ config.deletePersistent());
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/virtualhost/plugins/policies/TopicDeletePolicyTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/virtualhost/plugins/policies/TopicDeletePolicyTest.java
new file mode 100644
index 0000000000..a2e83add05
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/virtualhost/plugins/policies/TopicDeletePolicyTest.java
@@ -0,0 +1,293 @@
+/*
+ *
+ * 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.plugins.policies;
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.XMLConfiguration;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.binding.Binding;
+import org.apache.qpid.server.exchange.DirectExchange;
+import org.apache.qpid.server.exchange.TopicExchange;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.protocol.InternalTestProtocolSession;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.MockAMQQueue;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.util.InternalBrokerBaseCase;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+public class TopicDeletePolicyTest extends InternalBrokerBaseCase
+{
+
+ TopicDeletePolicyConfiguration _config;
+
+ VirtualHost _defaultVhost;
+ InternalTestProtocolSession _connection;
+
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ _defaultVhost = ApplicationRegistry.getInstance().getVirtualHostRegistry().getDefaultVirtualHost();
+
+ _connection = new InternalTestProtocolSession(_defaultVhost);
+
+ _config = new TopicDeletePolicyConfiguration();
+
+ XMLConfiguration config = new XMLConfiguration();
+
+ _config.setConfiguration("", config);
+ }
+
+ private MockAMQQueue createOwnedQueue()
+ {
+ MockAMQQueue queue = new MockAMQQueue("testQueue");
+
+ _defaultVhost.getQueueRegistry().registerQueue(queue);
+
+ try
+ {
+ AMQChannel channel = new AMQChannel(_connection, 0, null);
+ _connection.addChannel(channel);
+
+ queue.setExclusiveOwningSession(channel);
+ }
+ catch (AMQException e)
+ {
+ fail("Unable to create Channel:" + e.getMessage());
+ }
+
+ return queue;
+ }
+
+ private void setQueueToAutoDelete(final AMQQueue queue)
+ {
+ ((MockAMQQueue) queue).setAutoDelete(true);
+
+ queue.setDeleteOnNoConsumers(true);
+ final AMQProtocolSession.Task deleteQueueTask =
+ new AMQProtocolSession.Task()
+ {
+ public void doTask(AMQProtocolSession session) throws AMQException
+ {
+ queue.delete();
+ }
+ };
+
+ ((AMQChannel) queue.getExclusiveOwningSession()).getProtocolSession().addSessionCloseTask(deleteQueueTask);
+ }
+
+ /** Check that a null queue passed in does not upset the policy. */
+ public void testNullQueueParameter() throws ConfigurationException
+ {
+ TopicDeletePolicy policy = new TopicDeletePolicy();
+ policy.configure(_config);
+
+ try
+ {
+ policy.performPolicy(null);
+ }
+ catch (Exception e)
+ {
+ fail("Exception should not be thrown:" + e.getMessage());
+ }
+
+ }
+
+ /**
+ * Set a owning Session to null which means this is not an exclusive queue
+ * so the queue should not be deleted
+ */
+ public void testNonExclusiveQueue()
+ {
+ TopicDeletePolicy policy = new TopicDeletePolicy();
+ policy.configure(_config);
+
+ MockAMQQueue queue = createOwnedQueue();
+
+ queue.setExclusiveOwningSession(null);
+
+ policy.performPolicy(queue);
+
+ assertFalse("Queue should not be deleted", queue.isDeleted());
+ assertFalse("Connection should not be closed", _connection.isClosed());
+ }
+
+ /**
+ * Test that exclusive JMS Queues are not deleted.
+ * Bind the queue to the direct exchange (so it is a JMS Queue).
+ *
+ * JMS Queues are not to be processed so this should not delete the queue.
+ */
+ public void testQueuesAreNotProcessed()
+ {
+ TopicDeletePolicy policy = new TopicDeletePolicy();
+ policy.configure(_config);
+
+ MockAMQQueue queue = createOwnedQueue();
+
+ queue.addBinding(new Binding(null, "bindingKey", queue, new DirectExchange(), null));
+
+ policy.performPolicy(queue);
+
+ assertFalse("Queue should not be deleted", queue.isDeleted());
+ assertFalse("Connection should not be closed", _connection.isClosed());
+ }
+
+ /**
+ * Give a non auto-delete queue is bound to the topic exchange the
+ * TopicDeletePolicy will close the connection and delete the queue,
+ */
+ public void testNonAutoDeleteTopicIsNotClosed()
+ {
+ TopicDeletePolicy policy = new TopicDeletePolicy();
+ policy.configure(_config);
+
+ MockAMQQueue queue = createOwnedQueue();
+
+ queue.addBinding(new Binding(null, "bindingKey", queue, new TopicExchange(), null));
+
+ queue.setAutoDelete(false);
+
+ policy.performPolicy(queue);
+
+ assertFalse("Queue should not be deleted", queue.isDeleted());
+ assertTrue("Connection should be closed", _connection.isClosed());
+ }
+
+ /**
+ * Give a auto-delete queue bound to the topic exchange the TopicDeletePolicy will
+ * close the connection and delete the queue
+ */
+ public void testTopicIsClosed()
+ {
+ TopicDeletePolicy policy = new TopicDeletePolicy();
+ policy.configure(_config);
+
+ final MockAMQQueue queue = createOwnedQueue();
+
+ queue.addBinding(new Binding(null, "bindingKey", queue, new TopicExchange(), null));
+
+ setQueueToAutoDelete(queue);
+
+ policy.performPolicy(queue);
+
+ assertTrue("Queue should be deleted", queue.isDeleted());
+ assertTrue("Connection should be closed", _connection.isClosed());
+ }
+
+ /**
+ * Give a queue bound to the topic exchange the TopicDeletePolicy will
+ * close the connection and NOT delete the queue
+ */
+ public void testNonAutoDeleteTopicIsClosedNotDeleted()
+ {
+ TopicDeletePolicy policy = new TopicDeletePolicy();
+ policy.configure(_config);
+
+ MockAMQQueue queue = createOwnedQueue();
+
+ queue.addBinding(new Binding(null, "bindingKey", queue, new TopicExchange(), null));
+
+ policy.performPolicy(queue);
+
+ assertFalse("Queue should not be deleted", queue.isDeleted());
+ assertTrue("Connection should be closed", _connection.isClosed());
+ }
+
+ /**
+ * Give a queue bound to the topic exchange the TopicDeletePolicy suitably
+ * configured with the delete-persistent tag will close the connection
+ * and delete the queue
+ */
+ public void testPersistentTopicIsClosedAndDeleted()
+ {
+ //Set the config to delete persistent queues
+ _config.getConfig().addProperty("delete-persistent", "");
+
+ TopicDeletePolicy policy = new TopicDeletePolicy();
+ policy.configure(_config);
+
+ assertTrue("Config was not updated to delete Persistent topics",
+ _config.deletePersistent());
+
+ MockAMQQueue queue = createOwnedQueue();
+
+ queue.addBinding(new Binding(null, "bindingKey", queue, new TopicExchange(), null));
+
+ policy.performPolicy(queue);
+
+ assertTrue("Queue should be deleted", queue.isDeleted());
+ assertTrue("Connection should be closed", _connection.isClosed());
+ }
+
+ /**
+ * Give a queue bound to the topic exchange the TopicDeletePolicy not
+ * configured to close a persistent queue
+ */
+ public void testPersistentTopicIsClosedAndDeletedNullConfig()
+ {
+ TopicDeletePolicy policy = new TopicDeletePolicy();
+ // Explicity say we are not configuring the policy.
+ policy.configure(null);
+
+ MockAMQQueue queue = createOwnedQueue();
+
+ queue.addBinding(new Binding(null, "bindingKey", queue, new TopicExchange(), null));
+
+ policy.performPolicy(queue);
+
+ assertFalse("Queue should not be deleted", queue.isDeleted());
+ assertTrue("Connection should be closed", _connection.isClosed());
+ }
+
+ public void testNonExclusiveQueueNullConfig()
+ {
+ _config = null;
+ testNonExclusiveQueue();
+ }
+
+ public void testQueuesAreNotProcessedNullConfig()
+ {
+ _config = null;
+ testQueuesAreNotProcessed();
+ }
+
+ public void testNonAutoDeleteTopicIsNotClosedNullConfig()
+ {
+ _config = null;
+ testNonAutoDeleteTopicIsNotClosed();
+ }
+
+ public void testTopicIsClosedNullConfig()
+ {
+ _config = null;
+ testTopicIsClosed();
+ }
+
+ public void testNonAutoDeleteTopicIsClosedNotDeletedNullConfig() throws AMQException
+ {
+ _config = null;
+ testNonAutoDeleteTopicIsClosedNotDeleted();
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/util/MockChannel.java b/qpid/java/broker/src/test/java/org/apache/qpid/util/MockChannel.java
new file mode 100644
index 0000000000..9bd1e7c5e1
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/util/MockChannel.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.util;
+
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.subscription.Subscription;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+
+public class MockChannel extends AMQChannel
+{
+ public MockChannel(AMQProtocolSession session, int channelId, MessageStore messageStore)
+ throws AMQException
+ {
+ super(session, channelId, messageStore);
+ }
+
+
+
+}