diff options
author | Martin Ritchie <ritchiem@apache.org> | 2009-04-14 15:54:16 +0000 |
---|---|---|
committer | Martin Ritchie <ritchiem@apache.org> | 2009-04-14 15:54:16 +0000 |
commit | 1e568c49bae7bfc03fa89e34dac8ac97ff2a54bb (patch) | |
tree | 882434e81e1fd24301dd32cf3d62b4468404cf32 /java/broker/src/test | |
parent | 1b2b4b309e9392e0523cd62accb8704fd089eef8 (diff) | |
download | qpid-python-1e568c49bae7bfc03fa89e34dac8ac97ff2a54bb.tar.gz |
QPID-1807 : Add 0.5-fix broker and update SlowMessageStore to use MessageStores rather than TransactionLogs
git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk/qpid@764850 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'java/broker/src/test')
64 files changed, 12603 insertions, 0 deletions
diff --git a/java/broker/src/test/java/org/apache/qpid/server/AMQBrokerManagerMBeanTest.java b/java/broker/src/test/java/org/apache/qpid/server/AMQBrokerManagerMBeanTest.java new file mode 100644 index 0000000000..b94f2ef76f --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/AMQBrokerManagerMBeanTest.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; + +import junit.framework.TestCase; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.management.ManagedBroker; +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.virtualhost.VirtualHost; + +public class AMQBrokerManagerMBeanTest extends TestCase +{ + private QueueRegistry _queueRegistry; + private ExchangeRegistry _exchangeRegistry; + + 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); + + VirtualHost vHost = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test"); + + ManagedBroker mbean = new AMQBrokerManagerMBean((VirtualHost.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(); + VirtualHost vHost = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test"); + + ManagedBroker mbean = new AMQBrokerManagerMBean((VirtualHost.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 + protected void setUp() throws Exception + { + super.setUp(); + IApplicationRegistry appRegistry = ApplicationRegistry.getInstance(); + _queueRegistry = appRegistry.getVirtualHostRegistry().getVirtualHost("test").getQueueRegistry(); + _exchangeRegistry = appRegistry.getVirtualHostRegistry().getVirtualHost("test").getExchangeRegistry(); + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/ExtractResendAndRequeueTest.java b/java/broker/src/test/java/org/apache/qpid/server/ExtractResendAndRequeueTest.java new file mode 100644 index 0000000000..5fbf9484f7 --- /dev/null +++ b/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.MockQueueEntry; +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.queue.AMQMessage; +import org.apache.qpid.server.queue.QueueEntryIterator; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.subscription.MockSubscription; +import org.apache.qpid.AMQException; + +import java.util.Map; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.Iterator; + +/** + * 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(); + 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().getMessageId(), 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, new StoreContext())); + + 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, new StoreContext())); + + 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, new StoreContext())); + + 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, new StoreContext())); + + 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, new StoreContext())); + + 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/java/broker/src/test/java/org/apache/qpid/server/RunBrokerWithCommand.java b/java/broker/src/test/java/org/apache/qpid/server/RunBrokerWithCommand.java new file mode 100644 index 0000000000..59543874b4 --- /dev/null +++ b/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/java/broker/src/test/java/org/apache/qpid/server/SelectorParserTest.java b/java/broker/src/test/java/org/apache/qpid/server/SelectorParserTest.java new file mode 100644 index 0000000000..a0304a7b01 --- /dev/null +++ b/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/java/broker/src/test/java/org/apache/qpid/server/ack/AcknowledgeTest.java b/java/broker/src/test/java/org/apache/qpid/server/ack/AcknowledgeTest.java new file mode 100644 index 0000000000..9ef4af2932 --- /dev/null +++ b/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 + { + _channel.setLocalTransactional(); + runMessageAck(1, 1, 1, false, 0); + } + + public void testTransactionalMultiAck() throws AMQException + { + _channel.setLocalTransactional(); + runMessageAck(10, 1, 5, true, 5); + } + + public void testTransactionalAckAll() throws AMQException + { + _channel.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(_session, _channel, sendMessageCount); + + if (_channel.isTransactional()) + { + _channel.commit(); + } + + //Ensure they are stored + checkStoreContents(sendMessageCount); + + //Check that there are no unacked messages + assertEquals("Channel should have no unacked msgs ", 0, _channel.getUnacknowledgedMessageMap().size()); + + //Subscribe to the queue + AMQShortString subscriber = subscribe(_session, _channel, _queue); + + _queue.deliverAsync(); + + //Wait for the messages to be delivered + _session.awaitDelivery(sendMessageCount); + + //Check that they are all waiting to be acknoledged + assertEquals("Channel should have unacked msgs", sendMessageCount, _channel.getUnacknowledgedMessageMap().size()); + + List<InternalTestProtocolSession.DeliveryPair> messages = _session.getDelivers(_channel.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 + _channel.acknowledgeMessage(acknowledgeDeliveryTag, acknowldegeMultiple); + + if (_channel.isTransactional()) + { + _channel.commit(); + } + + // Check Remaining Acknowledgements + assertEquals("Channel unacked msgs count incorrect", remainingUnackedMessages, _channel.getUnacknowledgedMessageMap().size()); + + //Check store contents are also correct. + checkStoreContents(remainingUnackedMessages); + } + +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/ack/TxAckTest.java b/java/broker/src/test/java/org/apache/qpid/server/ack/TxAckTest.java new file mode 100644 index 0000000000..dfbbd56d6f --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/ack/TxAckTest.java @@ -0,0 +1,283 @@ +/* + * + * 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 junit.framework.TestCase; + +import org.apache.commons.configuration.PropertiesConfiguration; +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.server.RequiredDeliveryException; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.configuration.VirtualHostConfiguration; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.MessageHandleFactory; +import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.queue.AMQMessageHandle; +import org.apache.qpid.server.queue.AMQQueueFactory; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.store.TestMemoryMessageStore; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.store.MemoryMessageStore; +import org.apache.qpid.server.txn.NonTransactionalContext; +import org.apache.qpid.server.txn.TransactionalContext; + +import java.util.*; + +public class TxAckTest extends TestCase +{ + private Scenario individual; + private Scenario multiple; + private Scenario combined; + + protected void setUp() throws Exception + { + super.setUp(); + + //ack only 5th msg + individual = new Scenario(10, Arrays.asList(5l), Arrays.asList(1l, 2l, 3l, 4l, 6l, 7l, 8l, 9l, 10l)); + individual.update(5, false); + + //ack all up to and including 5th msg + multiple = new Scenario(10, Arrays.asList(1l, 2l, 3l, 4l, 5l), Arrays.asList(6l, 7l, 8l, 9l, 10l)); + multiple.update(5, true); + + //leave only 8th and 9th unacked + combined = new Scenario(10, Arrays.asList(1l, 2l, 3l, 4l, 5l, 6l, 7l, 10l), Arrays.asList(8l, 9l)); + combined.update(3, false); + combined.update(5, true); + combined.update(7, true); + combined.update(2, true);//should be ignored + combined.update(1, false);//should be ignored + combined.update(10, false); + } + + @Override + protected void tearDown() throws Exception + { + individual.stop(); + multiple.stop(); + combined.stop(); + } + + public void testPrepare() throws AMQException + { + individual.prepare(); + multiple.prepare(); + combined.prepare(); + } + + public void testUndoPrepare() throws AMQException + { + individual.undoPrepare(); + multiple.undoPrepare(); + combined.undoPrepare(); + } + + public void testCommit() throws AMQException + { + individual.commit(); + multiple.commit(); + combined.commit(); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(TxAckTest.class); + } + + private class Scenario + { + private final UnacknowledgedMessageMap _map = new UnacknowledgedMessageMapImpl(5000); + private final TxAck _op = new TxAck(_map); + private final List<Long> _acked; + private final List<Long> _unacked; + private StoreContext _storeContext = new StoreContext(); + private AMQQueue _queue; + + Scenario(int messageCount, List<Long> acked, List<Long> unacked) throws Exception + { + TransactionalContext txnContext = new NonTransactionalContext(new TestMemoryMessageStore(), + _storeContext, null, + new LinkedList<RequiredDeliveryException>() + ); + + PropertiesConfiguration env = new PropertiesConfiguration(); + env.setProperty("name", "test"); + VirtualHost virtualHost = new VirtualHost(new VirtualHostConfiguration("test", env), null); + + _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("test"), false, null, false, + virtualHost, null); + + for (int i = 0; i < messageCount; i++) + { + long deliveryTag = i + 1; + + 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; + } + }; + + TestMessage message = new TestMessage(deliveryTag, i, info, txnContext.getStoreContext()); + _map.add(deliveryTag, _queue.enqueue(new StoreContext(), message)); + } + _acked = acked; + _unacked = unacked; + } + + void update(long deliverytag, boolean multiple) + { + _op.update(deliverytag, multiple); + } + + private void assertCount(List<Long> tags, int expected) + { + for (long tag : tags) + { + QueueEntry u = _map.get(tag); + assertTrue("Message not found for tag " + tag, u != null); + ((TestMessage) u.getMessage()).assertCountEquals(expected); + } + } + + void prepare() throws AMQException + { + _op.consolidate(); + _op.prepare(_storeContext); + + assertCount(_acked, -1); + assertCount(_unacked, 0); + + } + + void undoPrepare() + { + _op.consolidate(); + _op.undoPrepare(); + + assertCount(_acked, 1); + assertCount(_unacked, 0); + } + + void commit() + { + _op.consolidate(); + _op.commit(_storeContext); + + //check acked messages are removed from map + Set<Long> keys = new HashSet<Long>(_map.getDeliveryTags()); + keys.retainAll(_acked); + assertTrue("Expected messages with following tags to have been removed from map: " + keys, keys.isEmpty()); + //check unacked messages are still in map + keys = new HashSet<Long>(_unacked); + keys.removeAll(_map.getDeliveryTags()); + assertTrue("Expected messages with following tags to still be in map: " + keys, keys.isEmpty()); + } + + public void stop() + { + _queue.stop(); + } + } + + private static AMQMessageHandle createMessageHandle(final long messageId, final MessagePublishInfo publishBody) + { + final AMQMessageHandle amqMessageHandle = (new MessageHandleFactory()).createMessageHandle(messageId, + null, + false); + try + { + amqMessageHandle.setPublishAndContentHeaderBody(new StoreContext(), + publishBody, + new ContentHeaderBody() + { + public int getSize() + { + return 1; + } + }); + } + catch (AMQException e) + { + // won't happen + } + + + return amqMessageHandle; + } + + + private class TestMessage extends AMQMessage + { + private final long _tag; + private int _count; + + TestMessage(long tag, long messageId, MessagePublishInfo publishBody, StoreContext storeContext) + throws AMQException + { + super(createMessageHandle(messageId, publishBody), storeContext, publishBody); + _tag = tag; + } + + + public boolean incrementReference() + { + _count++; + return true; + } + + public void decrementReference(StoreContext context) + { + _count--; + } + + void assertCountEquals(int expected) + { + assertEquals("Wrong count for message with tag " + _tag, expected, _count); + } + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/configuration/QueueConfigurationTest.java b/java/broker/src/test/java/org/apache/qpid/server/configuration/QueueConfigurationTest.java new file mode 100644 index 0000000000..9692cf2727 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/configuration/QueueConfigurationTest.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.configuration; + +import junit.framework.TestCase; + +import org.apache.commons.configuration.PropertiesConfiguration; + +public class QueueConfigurationTest extends TestCase +{ + + private VirtualHostConfiguration _emptyConf; + private PropertiesConfiguration _env; + private ServerConfiguration _fullServerConf; + 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() + { + // Check default value + QueueConfiguration qConf = new QueueConfiguration("test", _env, _emptyConf); + assertEquals(0, qConf.getMaximumMessageAge()); + + // Check explicit value + PropertiesConfiguration fullEnv = new PropertiesConfiguration(); + fullEnv.setProperty("maximumMessageAge", 2); + qConf = new QueueConfiguration("test", fullEnv, _fullHostConf); + assertEquals(2, qConf.getMaximumMessageAge()); + + // Check inherited value + qConf = new QueueConfiguration("test", _env, _fullHostConf); + assertEquals(1, qConf.getMaximumMessageAge()); + } + + public void testGetMaximumQueueDepth() + { + // Check default value + QueueConfiguration qConf = new QueueConfiguration("test", _env, _emptyConf); + assertEquals(0, qConf.getMaximumQueueDepth()); + + // Check explicit value + PropertiesConfiguration fullEnv = new PropertiesConfiguration(); + fullEnv.setProperty("maximumQueueDepth", 2); + qConf = new QueueConfiguration("test", fullEnv, _fullHostConf); + assertEquals(2, qConf.getMaximumQueueDepth()); + + // Check inherited value + qConf = new QueueConfiguration("test", _env, _fullHostConf); + assertEquals(1, qConf.getMaximumQueueDepth()); + } + + public void testGetMaximumMessageSize() + { + // Check default value + QueueConfiguration qConf = new QueueConfiguration("test", _env, _emptyConf); + assertEquals(0, qConf.getMaximumMessageSize()); + + // Check explicit value + PropertiesConfiguration fullEnv = new PropertiesConfiguration(); + fullEnv.setProperty("maximumMessageSize", 2); + qConf = new QueueConfiguration("test", fullEnv, _fullHostConf); + assertEquals(2, qConf.getMaximumMessageSize()); + + // Check inherited value + qConf = new QueueConfiguration("test", _env, _fullHostConf); + assertEquals(1, qConf.getMaximumMessageSize()); + } + + public void testGetMaximumMessageCount() + { + // Check default value + QueueConfiguration qConf = new QueueConfiguration("test", _env, _emptyConf); + assertEquals(0, qConf.getMaximumMessageCount()); + + // Check explicit value + PropertiesConfiguration fullEnv = new PropertiesConfiguration(); + fullEnv.setProperty("maximumMessageCount", 2); + qConf = new QueueConfiguration("test", fullEnv, _fullHostConf); + assertEquals(2, qConf.getMaximumMessageCount()); + + // Check inherited value + qConf = new QueueConfiguration("test", _env, _fullHostConf); + assertEquals(1, qConf.getMaximumMessageCount()); + } + + public void testGetMinimumAlertRepeatGap() + { + // Check default value + QueueConfiguration qConf = new QueueConfiguration("test", _env, _emptyConf); + assertEquals(0, qConf.getMinimumAlertRepeatGap()); + + // Check explicit value + PropertiesConfiguration fullEnv = new PropertiesConfiguration(); + fullEnv.setProperty("minimumAlertRepeatGap", 2); + qConf = new QueueConfiguration("test", fullEnv, _fullHostConf); + assertEquals(2, qConf.getMinimumAlertRepeatGap()); + + // Check inherited value + qConf = new QueueConfiguration("test", _env, _fullHostConf); + assertEquals(1, qConf.getMinimumAlertRepeatGap()); + } + +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java b/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java new file mode 100644 index 0000000000..2c39d006b9 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java @@ -0,0 +1,867 @@ +/* + * + * 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 org.apache.commons.configuration.ConfigurationException; +import org.apache.commons.configuration.PropertiesConfiguration; +import org.apache.commons.configuration.SystemConfiguration; +import org.apache.commons.configuration.XMLConfiguration; +import org.apache.qpid.AMQException; +import org.apache.qpid.codec.AMQCodecFactory; +import org.apache.qpid.server.protocol.AMQMinaProtocolSession; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.protocol.TestIoSession; +import org.apache.qpid.server.queue.MockProtocolSession; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.registry.ConfigurationFileApplicationRegistry; +import org.apache.qpid.server.security.access.ACLManager; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.virtualhost.VirtualHostRegistry; + +import junit.framework.TestCase; + +public class ServerConfigurationTest extends TestCase +{ + + private XMLConfiguration _config; + + @Override + public void setUp() + { + _config = new XMLConfiguration(); + } + + @Override + public void tearDown() + { + ApplicationRegistry.removeAll(); + } + + public void testSetJMXManagementPort() throws ConfigurationException + { + ServerConfiguration serverConfig = new ServerConfiguration(_config); + serverConfig.setJMXManagementPort(23); + assertEquals(23, serverConfig.getJMXManagementPort()); + } + + public void testGetJMXManagementPort() throws ConfigurationException + { + _config.setProperty("management.jmxport", 42); + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(42, serverConfig.getJMXManagementPort()); + } + + public void testGetPlatformMbeanserver() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(true, serverConfig.getPlatformMbeanserver()); + + // Check value we set + _config.setProperty("management.platform-mbeanserver", false); + serverConfig = new ServerConfiguration(_config); + assertEquals(false, serverConfig.getPlatformMbeanserver()); + } + + public void testGetPluginDirectory() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(null, serverConfig.getPluginDirectory()); + + // Check value we set + _config.setProperty("plugin-directory", "/path/to/plugins"); + serverConfig = new ServerConfiguration(_config); + assertEquals("/path/to/plugins", serverConfig.getPluginDirectory()); + } + + public void testGetPrincipalDatabaseNames() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + List<String> dbs = serverConfig.getPrincipalDatabaseAttributeValues(0); + assertEquals(2, dbs.size()); + assertEquals("a", dbs.get(0)); + assertEquals("b", dbs.get(1)); + } + + public void testGetManagementAccessList() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(0, serverConfig.getManagementAccessList().size()); + + // Check value we set + _config.setProperty("security.jmx.access(0)", "a"); + _config.setProperty("security.jmx.access(1)", "b"); + serverConfig = new ServerConfiguration(_config); + List<String> dbs = serverConfig.getManagementAccessList(); + 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); + assertEquals(65536, serverConfig.getFrameSize()); + + // Check value we set + _config.setProperty("advanced.framesize", "23"); + serverConfig = new ServerConfiguration(_config); + assertEquals(23, serverConfig.getFrameSize()); + } + + public void testGetProtectIOEnabled() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(false, serverConfig.getProtectIOEnabled()); + + // Check value we set + _config.setProperty("broker.connector.protectio.enabled", true); + serverConfig = new ServerConfiguration(_config); + assertEquals(true, serverConfig.getProtectIOEnabled()); + } + + public void testGetBufferReadLimit() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(262144, serverConfig.getBufferReadLimit()); + + // Check value we set + _config.setProperty("broker.connector.protectio.readBufferLimitSize", 23); + serverConfig = new ServerConfiguration(_config); + assertEquals(23, serverConfig.getBufferReadLimit()); + } + + public void testGetBufferWriteLimit() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(262144, serverConfig.getBufferWriteLimit()); + + // Check value we set + _config.setProperty("broker.connector.protectio.writeBufferLimitSize", 23); + serverConfig = new ServerConfiguration(_config); + assertEquals(23, serverConfig.getBufferWriteLimit()); + } + + public void testGetSynchedClocks() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(false, serverConfig.getSynchedClocks()); + + // Check value we set + _config.setProperty("advanced.synced-clocks", true); + serverConfig = new ServerConfiguration(_config); + assertEquals(true, serverConfig.getSynchedClocks()); + } + + public void testGetMsgAuth() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(false, serverConfig.getMsgAuth()); + + // Check value we set + _config.setProperty("security.msg-auth", true); + serverConfig = new ServerConfiguration(_config); + assertEquals(true, serverConfig.getMsgAuth()); + } + + public void testGetJMXPrincipalDatabase() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(null, serverConfig.getJMXPrincipalDatabase()); + + // Check value we set + _config.setProperty("security.jmx.principal-database", "a"); + serverConfig = new ServerConfiguration(_config); + assertEquals("a", serverConfig.getJMXPrincipalDatabase()); + } + + public void testGetManagementKeyStorePath() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(null, serverConfig.getManagementKeyStorePath()); + + // Check value we set + _config.setProperty("management.ssl.keyStorePath", "a"); + serverConfig = new ServerConfiguration(_config); + assertEquals("a", serverConfig.getManagementKeyStorePath()); + } + + public void testGetManagementSSLEnabled() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(true, serverConfig.getManagementSSLEnabled()); + + // Check value we set + _config.setProperty("management.ssl.enabled", false); + serverConfig = new ServerConfiguration(_config); + assertEquals(false, serverConfig.getManagementSSLEnabled()); + } + + public void testGetManagementKeyStorePassword() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(null, serverConfig.getManagementKeyStorePassword()); + + // Check value we set + _config.setProperty("management.ssl.keyStorePassword", "a"); + serverConfig = new ServerConfiguration(_config); + assertEquals("a", serverConfig.getManagementKeyStorePassword()); + } + + public void testGetQueueAutoRegister() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(true, serverConfig.getQueueAutoRegister()); + + // Check value we set + _config.setProperty("queue.auto_register", false); + serverConfig = new ServerConfiguration(_config); + assertEquals(false, serverConfig.getQueueAutoRegister()); + } + + public void testGetManagementEnabled() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(true, serverConfig.getManagementEnabled()); + + // Check value we set + _config.setProperty("management.enabled", false); + serverConfig = new ServerConfiguration(_config); + assertEquals(false, serverConfig.getManagementEnabled()); + } + + public void testSetManagementEnabled() throws ConfigurationException + { + // Check value we set + ServerConfiguration serverConfig = new ServerConfiguration(_config); + serverConfig.setManagementEnabled(false); + assertEquals(false, serverConfig.getManagementEnabled()); + } + + public void testGetHeartBeatDelay() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(5, serverConfig.getHeartBeatDelay()); + + // Check value we set + _config.setProperty("heartbeat.delay", 23); + serverConfig = new ServerConfiguration(_config); + assertEquals(23, serverConfig.getHeartBeatDelay()); + } + + public void testGetHeartBeatTimeout() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(2.0, serverConfig.getHeartBeatTimeout()); + + // Check value we set + _config.setProperty("heartbeat.timeoutFactor", 2.3); + serverConfig = new ServerConfiguration(_config); + assertEquals(2.3, serverConfig.getHeartBeatTimeout()); + } + + public void testGetMaximumMessageAge() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(0, serverConfig.getMaximumMessageAge()); + + // Check value we set + _config.setProperty("maximumMessageAge", 10L); + serverConfig = new ServerConfiguration(_config); + assertEquals(10, serverConfig.getMaximumMessageAge()); + } + + public void testGetMaximumMessageCount() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(0, serverConfig.getMaximumMessageCount()); + + // Check value we set + _config.setProperty("maximumMessageCount", 10L); + serverConfig = new ServerConfiguration(_config); + assertEquals(10, serverConfig.getMaximumMessageCount()); + } + + public void testGetMaximumQueueDepth() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(0, serverConfig.getMaximumQueueDepth()); + + // Check value we set + _config.setProperty("maximumQueueDepth", 10L); + serverConfig = new ServerConfiguration(_config); + assertEquals(10, serverConfig.getMaximumQueueDepth()); + } + + public void testGetMaximumMessageSize() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(0, serverConfig.getMaximumMessageSize()); + + // Check value we set + _config.setProperty("maximumMessageSize", 10L); + serverConfig = new ServerConfiguration(_config); + assertEquals(10, serverConfig.getMaximumMessageSize()); + } + + public void testGetMinimumAlertRepeatGap() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(0, serverConfig.getMinimumAlertRepeatGap()); + + // Check value we set + _config.setProperty("minimumAlertRepeatGap", 10L); + serverConfig = new ServerConfiguration(_config); + assertEquals(10, serverConfig.getMinimumAlertRepeatGap()); + } + + public void testGetProcessors() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(4, serverConfig.getProcessors()); + + // Check value we set + _config.setProperty("connector.processors", 10); + serverConfig = new ServerConfiguration(_config); + assertEquals(10, serverConfig.getProcessors()); + } + + public void testGetPort() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(5672, serverConfig.getPort()); + + // Check value we set + _config.setProperty("connector.port", 10); + serverConfig = new ServerConfiguration(_config); + assertEquals(10, serverConfig.getPort()); + } + + public void testGetBind() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals("wildcard", serverConfig.getBind()); + + // Check value we set + _config.setProperty("connector.bind", "a"); + serverConfig = new ServerConfiguration(_config); + assertEquals("a", serverConfig.getBind()); + } + + public void testGetReceiveBufferSize() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(32767, serverConfig.getReceiveBufferSize()); + + // Check value we set + _config.setProperty("connector.socketReceiveBuffer", "23"); + serverConfig = new ServerConfiguration(_config); + assertEquals(23, serverConfig.getReceiveBufferSize()); + } + + public void testGetWriteBufferSize() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(32767, serverConfig.getWriteBufferSize()); + + // Check value we set + _config.setProperty("connector.socketWriteBuffer", "23"); + serverConfig = new ServerConfiguration(_config); + assertEquals(23, serverConfig.getWriteBufferSize()); + } + + public void testGetTcpNoDelay() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(true, serverConfig.getTcpNoDelay()); + + // Check value we set + _config.setProperty("connector.tcpNoDelay", false); + serverConfig = new ServerConfiguration(_config); + assertEquals(false, serverConfig.getTcpNoDelay()); + } + + public void testGetEnableExecutorPool() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(false, serverConfig.getEnableExecutorPool()); + + // Check value we set + _config.setProperty("advanced.filterchain[@enableExecutorPool]", true); + serverConfig = new ServerConfiguration(_config); + assertEquals(true, serverConfig.getEnableExecutorPool()); + } + + public void testGetEnablePooledAllocator() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(false, serverConfig.getEnablePooledAllocator()); + + // Check value we set + _config.setProperty("advanced.enablePooledAllocator", true); + serverConfig = new ServerConfiguration(_config); + assertEquals(true, serverConfig.getEnablePooledAllocator()); + } + + public void testGetEnableDirectBuffers() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(false, serverConfig.getEnableDirectBuffers()); + + // Check value we set + _config.setProperty("advanced.enableDirectBuffers", true); + serverConfig = new ServerConfiguration(_config); + assertEquals(true, serverConfig.getEnableDirectBuffers()); + } + + public void testGetEnableSSL() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(false, serverConfig.getEnableSSL()); + + // Check value we set + _config.setProperty("connector.ssl.enabled", true); + serverConfig = new ServerConfiguration(_config); + assertEquals(true, serverConfig.getEnableSSL()); + } + + public void testGetSSLOnly() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(true, serverConfig.getSSLOnly()); + + // Check value we set + _config.setProperty("connector.ssl.sslOnly", false); + serverConfig = new ServerConfiguration(_config); + assertEquals(false, serverConfig.getSSLOnly()); + } + + public void testGetSSLPort() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(8672, serverConfig.getSSLPort()); + + // Check value we set + _config.setProperty("connector.ssl.port", 23); + serverConfig = new ServerConfiguration(_config); + assertEquals(23, serverConfig.getSSLPort()); + } + + public void testGetKeystorePath() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals("none", serverConfig.getKeystorePath()); + + // Check value we set + _config.setProperty("connector.ssl.keystorePath", "a"); + serverConfig = new ServerConfiguration(_config); + assertEquals("a", serverConfig.getKeystorePath()); + } + + public void testGetKeystorePassword() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals("none", serverConfig.getKeystorePassword()); + + // Check value we set + _config.setProperty("connector.ssl.keystorePassword", "a"); + serverConfig = new ServerConfiguration(_config); + assertEquals("a", serverConfig.getKeystorePassword()); + } + + public void testGetCertType() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals("SunX509", serverConfig.getCertType()); + + // Check value we set + _config.setProperty("connector.ssl.certType", "a"); + serverConfig = new ServerConfiguration(_config); + assertEquals("a", serverConfig.getCertType()); + } + + public void testGetQpidNIO() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(false, serverConfig.getQpidNIO()); + + // Check value we set + _config.setProperty("connector.qpidnio", true); + serverConfig = new ServerConfiguration(_config); + assertEquals(true, serverConfig.getQpidNIO()); + } + + public void testGetUseBiasedWrites() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(false, serverConfig.getUseBiasedWrites()); + + // Check value we set + _config.setProperty("advanced.useWriteBiasedPool", true); + serverConfig = new ServerConfiguration(_config); + assertEquals(true, serverConfig.getUseBiasedWrites()); + } + + public void testGetHousekeepingExpiredMessageCheckPeriod() throws ConfigurationException + { + // Check default + ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(30000, serverConfig.getHousekeepingCheckPeriod()); + + // Check value we set + _config.setProperty("housekeeping.expiredMessageCheckPeriod", 23L); + serverConfig = new ServerConfiguration(_config); + 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); + 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()); + assertEquals(4235, config.getSSLPort()); // From first file, not + // overriden by second + assertEquals(2342, config.getPort()); // 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()); + assertEquals("Did not get correct interpolated value", + "foo", config.getManagementKeyStorePath()); + } + + public void testCombinedConfigurationFirewall() throws Exception + { + // Write out config + 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("</configuration>"); + out.close(); + + out = new FileWriter(fileA); + 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<access>/dev/null</access>\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<xml fileName=\"" + fileB.getAbsolutePath() + "\"/>"); + 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</virtualhost>\n"); + out.write("\t</virtualhosts>\n"); + out.write("</broker>\n"); + out.close(); + + out = new FileWriter(fileB); + out.write("<firewall>\n"); + out.write("\t<rule access=\"deny\" network=\"127.0.0.1\"/>"); + out.write("</firewall>\n"); + out.close(); + + // Load config + ApplicationRegistry reg = new ConfigurationFileApplicationRegistry(mainFile); + ApplicationRegistry.initialise(reg, 1); + + // Test config + TestIoSession iosession = new TestIoSession(); + iosession.setAddress("127.0.0.1"); + VirtualHostRegistry virtualHostRegistry = reg.getVirtualHostRegistry(); + VirtualHost virtualHost = virtualHostRegistry.getVirtualHost("test"); + AMQCodecFactory codecFactory = new AMQCodecFactory(true); + AMQProtocolSession session = new AMQMinaProtocolSession(iosession, virtualHostRegistry, codecFactory); + assertFalse(reg.getAccessManager().authoriseConnect(session, virtualHost)); + } + + public void testCombinedConfigurationFirewallReload() throws Exception + { + // Write out config + 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("</configuration>"); + out.close(); + + out = new FileWriter(fileA); + 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<access>/dev/null</access>\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<xml fileName=\"" + fileB.getAbsolutePath() + "\"/>"); + 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</virtualhost>\n"); + out.write("\t</virtualhosts>\n"); + out.write("</broker>\n"); + out.close(); + + out = new FileWriter(fileB); + out.write("<firewall>\n"); + out.write("\t<rule access=\"deny\" network=\"127.0.0.1\"/>"); + out.write("</firewall>\n"); + out.close(); + + // Load config + ApplicationRegistry reg = new ConfigurationFileApplicationRegistry(mainFile); + ApplicationRegistry.initialise(reg, 1); + + // Test config + TestIoSession iosession = new TestIoSession(); + iosession.setAddress("127.0.0.1"); + VirtualHostRegistry virtualHostRegistry = reg.getVirtualHostRegistry(); + VirtualHost virtualHost = virtualHostRegistry.getVirtualHost("test"); + AMQCodecFactory codecFactory = new AMQCodecFactory(true); + AMQProtocolSession session = new AMQMinaProtocolSession(iosession, virtualHostRegistry, codecFactory); + assertFalse(reg.getAccessManager().authoriseConnect(session, virtualHost)); + + RandomAccessFile fileBRandom = new RandomAccessFile(fileB, "rw"); + fileBRandom.setLength(0); + fileBRandom.seek(0); + fileBRandom.close(); + + out = new FileWriter(fileB); + out.write("<firewall>\n"); + out.write("\t<rule access=\"allow\" network=\"127.0.0.1\"/>"); + out.write("</firewall>\n"); + out.close(); + + reg.getConfiguration().reparseConfigFile(); + + assertTrue(reg.getAccessManager().authoriseConnect(session, virtualHost)); + + fileBRandom = new RandomAccessFile(fileB, "rw"); + fileBRandom.setLength(0); + fileBRandom.seek(0); + fileBRandom.close(); + + out = new FileWriter(fileB); + out.write("<firewall>\n"); + out.write("\t<rule access=\"deny\" network=\"127.0.0.1\"/>"); + out.write("</firewall>\n"); + out.close(); + + reg.getConfiguration().reparseConfigFile(); + + assertFalse(reg.getAccessManager().authoriseConnect(session, virtualHost)); + } + +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/configuration/TestPropertyUtils.java b/java/broker/src/test/java/org/apache/qpid/server/configuration/TestPropertyUtils.java new file mode 100644 index 0000000000..3b83190e42 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/configuration/TestPropertyUtils.java @@ -0,0 +1,50 @@ +/* + * + * 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.qpid.configuration.PropertyException; +import org.apache.qpid.configuration.PropertyUtils; + +import junit.framework.TestCase; + +// TODO: This belongs in the "common" module. +public class TestPropertyUtils extends TestCase +{ + public void testSimpleExpansion() throws PropertyException + { + System.setProperty("banana", "fruity"); + String expandedProperty = PropertyUtils.replaceProperties("${banana}"); + assertEquals(expandedProperty, "fruity"); + } + + public void testDualExpansion() throws PropertyException + { + System.setProperty("banana", "fruity"); + System.setProperty("concrete", "horrible"); + String expandedProperty = PropertyUtils.replaceProperties("${banana}xyz${concrete}"); + assertEquals(expandedProperty, "fruityxyzhorrible"); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(TestPropertyUtils.class); + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/configuration/VirtualHostConfigurationTest.java b/java/broker/src/test/java/org/apache/qpid/server/configuration/VirtualHostConfigurationTest.java new file mode 100644 index 0000000000..ba504d3064 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/configuration/VirtualHostConfigurationTest.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.configuration; + + +import junit.framework.TestCase; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.commons.configuration.XMLConfiguration; +import org.apache.qpid.AMQException; +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.virtualhost.VirtualHost; + +public class VirtualHostConfigurationTest extends TestCase +{ + + private VirtualHostConfiguration vhostConfig; + private XMLConfiguration configXml; + + @Override + protected void setUp() throws Exception + { + // Fill config file with stuff + configXml = new XMLConfiguration(); + configXml.setRootElementName("virtualhosts"); + configXml.addProperty("virtualhost(-1).name", "test"); + } + + public void testQueuePriority() throws Exception + { + // Set up queue with 5 priorities + configXml.addProperty("virtualhost.test.queues(-1).queue(-1).name(-1)", + "atest"); + configXml.addProperty("virtualhost.test.queues.queue.atest(-1).exchange", + "amq.direct"); + configXml.addProperty("virtualhost.test.queues.queue.atest.priorities", + "5"); + + // Set up queue with JMS style priorities + configXml.addProperty("virtualhost.test.queues(-1).queue(-1).name(-1)", + "ptest"); + configXml.addProperty("virtualhost.test.queues.queue.ptest(-1).exchange", + "amq.direct"); + configXml.addProperty("virtualhost.test.queues.queue.ptest.priority", + "true"); + + // Set up queue with no priorities + configXml.addProperty("virtualhost.test.queues(-1).queue(-1).name(-1)", + "ntest"); + configXml.addProperty("virtualhost.test.queues.queue.ntest(-1).exchange", + "amq.direct"); + configXml.addProperty("virtualhost.test.queues.queue.ntest.priority", + "false"); + + VirtualHost vhost = new VirtualHost(new VirtualHostConfiguration("test", configXml.subset("virtualhost.test"))); + + // 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 + configXml.addProperty("virtualhost.test.queues.exchange", "amq.topic"); + configXml.addProperty("virtualhost.test.queues.maximumQueueDepth", "1"); + configXml.addProperty("virtualhost.test.queues.maximumMessageSize", "2"); + configXml.addProperty("virtualhost.test.queues.maximumMessageAge", "3"); + + configXml.addProperty("virtualhost.test.queues(-1).queue(1).name(1)", "atest"); + configXml.addProperty("virtualhost.test.queues.queue.atest(-1).exchange", "amq.direct"); + configXml.addProperty("virtualhost.test.queues.queue.atest(-1).maximumQueueDepth", "4"); + configXml.addProperty("virtualhost.test.queues.queue.atest(-1).maximumMessageSize", "5"); + configXml.addProperty("virtualhost.test.queues.queue.atest(-1).maximumMessageAge", "6"); + + configXml.addProperty("virtualhost.test.queues(-1).queue(-1).name(-1)", "btest"); + + VirtualHost vhost = new VirtualHost(new VirtualHostConfiguration("test", configXml.subset("virtualhost.test"))); + + // 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()); + + } + +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java b/java/broker/src/test/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java new file mode 100644 index 0000000000..6dcb187a37 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java @@ -0,0 +1,562 @@ +/* + * + * 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.AMQException; +import org.apache.qpid.framing.*; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.server.queue.*; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.SkeletonMessageStore; +import org.apache.qpid.server.store.MemoryMessageStore; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.txn.NonTransactionalContext; +import org.apache.qpid.server.txn.TransactionalContext; +import org.apache.qpid.server.RequiredDeliveryException; +import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.log4j.Logger; + +import java.util.*; + +public class AbstractHeadersExchangeTestBase extends TestCase +{ + 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(); + + private StoreContext _storeContext = new StoreContext(); + + private MessageHandleFactory _handleFactory = new MessageHandleFactory(); + + private int count; + + public void testDoNothing() + { + // this is here only to make junit under Eclipse happy + } + + protected TestQueue bindDefault(String... bindings) throws AMQException + { + return bind("Queue" + (++count), bindings); + } + + protected TestQueue bind(String queueName, String... bindings) throws AMQException + { + return bind(queueName, getHeaders(bindings)); + } + + protected TestQueue bind(String queue, FieldTable bindings) throws AMQException + { + return bind(new TestQueue(new AMQShortString(queue)), bindings); + } + + protected TestQueue bind(TestQueue queue, String... bindings) throws AMQException + { + return bind(queue, getHeaders(bindings)); + } + + protected TestQueue bind(TestQueue queue, FieldTable bindings) throws AMQException + { + queues.add(queue); + exchange.registerQueue(null, queue, bindings); + return queue; + } + + + protected void route(Message m) throws AMQException + { + m.route(exchange); + m.getIncomingMessage().routingComplete(_store, _handleFactory); + if(m.getIncomingMessage().allContentReceived()) + { + m.getIncomingMessage().deliverToQueues(); + } + } + + 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 + { + try + { + route(m); + assertFalse("Expected "+m+" to be returned due to manadatory flag, and lack of routing",expectReturn); + 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; + } + } + } + + catch (NoRouteException ex) + { + assertTrue("Expected "+m+" not to be returned",expectReturn); + } + + } + + 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.properties = 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 TestQueue(AMQShortString name) throws AMQException + { + super(name, false, new AMQShortString("test"), true, ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test")); + 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 QueueEntry enqueue(StoreContext context, AMQMessage msg) throws AMQException + { + messages.add( new HeadersExchangeTest.Message(msg)); + return 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 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 void setDeliveredToSubscription() + { + //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 String debugIdentity() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean immediateAndNotDelivered() + { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + public void setRedelivered(boolean b) + { + //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(StoreContext storeContext) throws AMQException + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void dequeue(final StoreContext storeContext) throws FailedDequeueException + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void dispose(final StoreContext storeContext) throws MessageCleanupException + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void restoreCredit() + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void discard(StoreContext storeContext) throws FailedDequeueException, MessageCleanupException + { + //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. + } + }; + } + + 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 class TestIncomingMessage extends IncomingMessage + { + + public TestIncomingMessage(final long messageId, + final MessagePublishInfo info, + final TransactionalContext txnContext, + final AMQProtocolSession publisher) + { + super(messageId, info, txnContext, publisher); + } + + + public AMQMessage getUnderlyingMessage() + { + return Message.this; + } + + + public ContentHeaderBody getContentHeaderBody() + { + try + { + return Message.this.getContentHeaderBody(); + } + catch (AMQException e) + { + throw new RuntimeException(e); + } + } + } + + private IncomingMessage _incoming; + + private static MessageStore _messageStore = new SkeletonMessageStore(); + + private static StoreContext _storeContext = new StoreContext(); + + + private static TransactionalContext _txnContext = new NonTransactionalContext(_messageStore, _storeContext, + null, + new LinkedList<RequiredDeliveryException>() + ); + + Message(String id, String... headers) throws AMQException + { + this(id, getHeaders(headers)); + } + + Message(String id, FieldTable headers) throws AMQException + { + this(_messageStore.getNewMessageId(),getPublishRequest(id), getContentHeader(headers), null); + } + + public IncomingMessage getIncomingMessage() + { + return _incoming; + } + + private Message(long messageId, + MessagePublishInfo publish, + ContentHeaderBody header, + List<ContentBody> bodies) throws AMQException + { + super(createMessageHandle(messageId, publish, header), _txnContext.getStoreContext(), publish); + + + + _incoming = new TestIncomingMessage(getMessageId(),publish,_txnContext,new MockProtocolSession(_messageStore)); + _incoming.setContentHeaderBody(header); + + + } + + private static AMQMessageHandle createMessageHandle(final long messageId, + final MessagePublishInfo publish, + final ContentHeaderBody header) + { + + final AMQMessageHandle amqMessageHandle = (new MessageHandleFactory()).createMessageHandle(messageId, + _messageStore, + true); + + try + { + amqMessageHandle.setPublishAndContentHeaderBody(new StoreContext(),publish,header); + } + catch (AMQException e) + { + + } + return amqMessageHandle; + } + + private Message(AMQMessage msg) throws AMQException + { + super(msg); + } + + + + void route(Exchange exchange) throws AMQException + { + 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/java/broker/src/test/java/org/apache/qpid/server/exchange/DestWildExchangeTest.java b/java/broker/src/test/java/org/apache/qpid/server/exchange/DestWildExchangeTest.java new file mode 100644 index 0000000000..aa25e207a9 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/exchange/DestWildExchangeTest.java @@ -0,0 +1,595 @@ +/* + * 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.virtualhost.VirtualHost; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.txn.NonTransactionalContext; +import org.apache.qpid.server.txn.TransactionalContext; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.MemoryMessageStore; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.RequiredDeliveryException; +import org.apache.qpid.server.protocol.InternalTestProtocolSession; +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 java.util.LinkedList; + +public class DestWildExchangeTest extends TestCase +{ + + TopicExchange _exchange; + + VirtualHost _vhost; + MessageStore _store; + StoreContext _context; + + InternalTestProtocolSession _protocolSession; + + + public void setUp() throws AMQException + { + _exchange = new TopicExchange(); + _vhost = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHosts().iterator().next(); + _store = new MemoryMessageStore(); + _context = new StoreContext(); + _protocolSession = new InternalTestProtocolSession(); + } + + public void tearDown() + { + ApplicationRegistry.remove(1); + } + + + public void testNoRoute() throws AMQException + { + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a*#b"), false, null, false, _vhost, null); + _exchange.registerQueue(new AMQShortString("a.*.#.b"), queue, null); + + + MessagePublishInfo info = new PublishInfo(new AMQShortString("a.b")); + + IncomingMessage message = new IncomingMessage(0L, info, null, _protocolSession); + + _exchange.route(message); + + Assert.assertEquals(0, queue.getMessageCount()); + } + + public void testDirectMatch() throws AMQException + { + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("ab"), false, null, false, _vhost, null); + _exchange.registerQueue(new AMQShortString("a.b"), queue, null); + + + IncomingMessage message = createMessage("a.b"); + + try + { + routeMessage(message); + } + catch (AMQException nre) + { + fail("Message has route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId()); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + + message = createMessage("a.c"); + + try + { + routeMessage(message); + fail("Message has no route and should fail to be routed"); + } + catch (AMQException nre) + { + } + + Assert.assertEquals(0, queue.getMessageCount()); + } + + + public void testStarMatch() throws AMQException + { + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a*"), false, null, false, _vhost, null); + _exchange.registerQueue(new AMQShortString("a.*"), queue, null); + + + IncomingMessage message = createMessage("a.b"); + + try + { + routeMessage(message); + } + catch (AMQException nre) + { + fail("Message has route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId()); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + + message = createMessage("a.c"); + + try + { + routeMessage(message); + } + catch (AMQException nre) + { + fail("Message has route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId()); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + + message = createMessage("a"); + + try + { + routeMessage(message); + fail("Message has no route and should fail to be routed"); + } + catch (AMQException nre) + { + } + + Assert.assertEquals(0, queue.getMessageCount()); + } + + public void testHashMatch() throws AMQException + { + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost, null); + _exchange.registerQueue(new AMQShortString("a.#"), queue, null); + + + IncomingMessage message = createMessage("a.b.c"); + + try + { + routeMessage(message); + } + catch (AMQException nre) + { + fail("Message has route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId()); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + + message = createMessage("a.b"); + + try + { + routeMessage(message); + } + catch (AMQException nre) + { + fail("Message has route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId()); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + + message = createMessage("a.c"); + + try + { + routeMessage(message); + } + catch (AMQException nre) + { + fail("Message has route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId()); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + message = createMessage("a"); + + try + { + routeMessage(message); + } + catch (AMQException nre) + { + fail("Message has route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId()); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + + message = createMessage("b"); + + try + { + routeMessage(message); + fail("Message has no route and should fail to be routed"); + } + catch (AMQException nre) + { + } + + Assert.assertEquals(0, queue.getMessageCount()); + } + + + public void testMidHash() throws AMQException + { + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost, null); + _exchange.registerQueue(new AMQShortString("a.*.#.b"), queue, null); + + + IncomingMessage message = createMessage("a.c.d.b"); + + try + { + routeMessage(message); + } + catch (AMQException nre) + { + fail("Message has no route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId()); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + message = createMessage("a.c.b"); + + try + { + routeMessage(message); + } + catch (AMQException nre) + { + fail("Message has no route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId()); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + } + + public void testMatchafterHash() throws AMQException + { + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost, null); + _exchange.registerQueue(new AMQShortString("a.*.#.b.c"), queue, null); + + + IncomingMessage message = createMessage("a.c.b.b"); + + try + { + routeMessage(message); + fail("Message has route and should not be routed"); + } + catch (AMQException nre) + { + } + + Assert.assertEquals(0, queue.getMessageCount()); + + + message = createMessage("a.a.b.c"); + + try + { + routeMessage(message); + } + catch (AMQException nre) + { + fail("Message has no route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId()); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + message = createMessage("a.b.c.b"); + + try + { + routeMessage(message); + fail("Message has route and should not be routed"); + } + catch (AMQException nre) + { + } + + Assert.assertEquals(0, queue.getMessageCount()); + + message = createMessage("a.b.c.b.c"); + + try + { + routeMessage(message); + } + catch (AMQException nre) + { + fail("Message has no route and should be routed"); + + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId()); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + } + + + public void testHashAfterHash() throws AMQException + { + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost, null); + _exchange.registerQueue(new AMQShortString("a.*.#.b.c.#.d"), queue, null); + + + IncomingMessage message = createMessage("a.c.b.b.c"); + + try + { + routeMessage(message); + fail("Message has route and should not be routed"); + } + catch (AMQException nre) + { + } + + Assert.assertEquals(0, queue.getMessageCount()); + + + message = createMessage("a.a.b.c.d"); + + try + { + routeMessage(message); + } + catch (AMQException nre) + { + fail("Message has no route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId()); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + } + + public void testHashHash() throws AMQException + { + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost, null); + _exchange.registerQueue(new AMQShortString("a.#.*.#.d"), queue, null); + + + IncomingMessage message = createMessage("a.c.b.b.c"); + + try + { + routeMessage(message); + fail("Message has route and should not be routed"); + } + catch (AMQException nre) + { + } + + Assert.assertEquals(0, queue.getMessageCount()); + + message = createMessage("a.a.b.c.d"); + + try + { + routeMessage(message); + } + catch (AMQException nre) + { + fail("Message has no route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId()); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + } + + public void testSubMatchFails() throws AMQException + { + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost, null); + _exchange.registerQueue(new AMQShortString("a.b.c.d"), queue, null); + + + IncomingMessage message = createMessage("a.b.c"); + + try + { + routeMessage(message); + fail("Message has route and should not be routed"); + } + catch (AMQException nre) + { + } + + Assert.assertEquals(0, queue.getMessageCount()); + + } + + private void routeMessage(final IncomingMessage message) + throws AMQException + { + _exchange.route(message); + message.routingComplete(_store, new MessageHandleFactory()); + message.deliverToQueues(); + } + + public void testMoreRouting() throws AMQException + { + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost, null); + _exchange.registerQueue(new AMQShortString("a.b"), queue, null); + + + IncomingMessage message = createMessage("a.b.c"); + + try + { + routeMessage(message); + fail("Message has route and should not be routed"); + } + catch (AMQException nre) + { + } + + Assert.assertEquals(0, queue.getMessageCount()); + + } + + public void testMoreQueue() throws AMQException + { + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost, null); + _exchange.registerQueue(new AMQShortString("a.b"), queue, null); + + + IncomingMessage message = createMessage("a"); + + try + { + routeMessage(message); + fail("Message has route and should not be routed"); + } + catch (AMQException nre) + { + } + + Assert.assertEquals(0, queue.getMessageCount()); + + } + + private IncomingMessage createMessage(String s) throws AMQException + { + MessagePublishInfo info = new PublishInfo(new AMQShortString(s)); + + TransactionalContext trancontext = new NonTransactionalContext(_store, _context, null, + new LinkedList<RequiredDeliveryException>() + ); + + IncomingMessage message = new IncomingMessage(0L, info, trancontext,_protocolSession); + message.setContentHeaderBody( new ContentHeaderBody()); + + + 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/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java b/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java new file mode 100644 index 0000000000..8ce7b4c0e1 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.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.exchange; + +import junit.framework.TestCase; +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.virtualhost.VirtualHost; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; + +import javax.management.openmbean.TabularData; +import java.util.ArrayList; + +/** + * Unit test class for testing different Exchange MBean operations + */ +public class ExchangeMBeanTest extends TestCase +{ + 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.getName().toString(), "binding1"); + mbean.createNewBinding(_queue.getName().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.getName().toString(), "binding1"); + mbean.createNewBinding(_queue.getName().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.getName().toString(), "key1=binding1,key2=binding2"); + mbean.createNewBinding(_queue.getName().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()); + } + + @Override + protected void setUp() throws Exception + { + super.setUp(); + + IApplicationRegistry applicationRegistry = ApplicationRegistry.getInstance(1); + _virtualHost = applicationRegistry.getVirtualHostRegistry().getVirtualHost("test"); + _queueRegistry = _virtualHost.getQueueRegistry(); + _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue"), false, new AMQShortString("ExchangeMBeanTest"), false, _virtualHost, + null); + _queueRegistry.registerQueue(_queue); + } + + protected void tearDown() + { + ApplicationRegistry.remove(1); + } + +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersBindingTest.java b/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersBindingTest.java new file mode 100644 index 0000000000..86ba96bf5d --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersBindingTest.java @@ -0,0 +1,199 @@ +/* + * + * 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 junit.framework.TestCase; +import org.apache.qpid.framing.FieldTable; + +/** + */ +public class HeadersBindingTest extends TestCase +{ + private FieldTable bindHeaders = new FieldTable(); + private FieldTable matchHeaders = new FieldTable(); + + public void testDefault_1() + { + bindHeaders.setString("A", "Value of A"); + + matchHeaders.setString("A", "Value of A"); + + assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders)); + } + + public void testDefault_2() + { + bindHeaders.setString("A", "Value of A"); + + matchHeaders.setString("A", "Value of A"); + matchHeaders.setString("B", "Value of B"); + + assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders)); + } + + public void testDefault_3() + { + bindHeaders.setString("A", "Value of A"); + + matchHeaders.setString("A", "Altered value of A"); + + assertFalse(new HeadersBinding(bindHeaders).matches(matchHeaders)); + } + + public void testAll_1() + { + bindHeaders.setString("X-match", "all"); + bindHeaders.setString("A", "Value of A"); + + matchHeaders.setString("A", "Value of A"); + + assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders)); + } + + public void testAll_2() + { + bindHeaders.setString("X-match", "all"); + bindHeaders.setString("A", "Value of A"); + bindHeaders.setString("B", "Value of B"); + + matchHeaders.setString("A", "Value of A"); + + assertFalse(new HeadersBinding(bindHeaders).matches(matchHeaders)); + } + + public void testAll_3() + { + bindHeaders.setString("X-match", "all"); + bindHeaders.setString("A", "Value of A"); + bindHeaders.setString("B", "Value of B"); + + matchHeaders.setString("A", "Value of A"); + matchHeaders.setString("B", "Value of B"); + + assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders)); + } + + public void testAll_4() + { + bindHeaders.setString("X-match", "all"); + bindHeaders.setString("A", "Value of A"); + bindHeaders.setString("B", "Value of B"); + + matchHeaders.setString("A", "Value of A"); + matchHeaders.setString("B", "Value of B"); + matchHeaders.setString("C", "Value of C"); + + assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders)); + } + + public void testAll_5() + { + bindHeaders.setString("X-match", "all"); + bindHeaders.setString("A", "Value of A"); + bindHeaders.setString("B", "Value of B"); + + matchHeaders.setString("A", "Value of A"); + matchHeaders.setString("B", "Altered value of B"); + matchHeaders.setString("C", "Value of C"); + + assertFalse(new HeadersBinding(bindHeaders).matches(matchHeaders)); + } + + public void testAny_1() + { + bindHeaders.setString("X-match", "any"); + bindHeaders.setString("A", "Value of A"); + + matchHeaders.setString("A", "Value of A"); + + assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders)); + } + + public void testAny_2() + { + bindHeaders.setString("X-match", "any"); + bindHeaders.setString("A", "Value of A"); + bindHeaders.setString("B", "Value of B"); + + matchHeaders.setString("A", "Value of A"); + + assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders)); + } + + public void testAny_3() + { + bindHeaders.setString("X-match", "any"); + bindHeaders.setString("A", "Value of A"); + bindHeaders.setString("B", "Value of B"); + + matchHeaders.setString("A", "Value of A"); + matchHeaders.setString("B", "Value of B"); + + assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders)); + } + + public void testAny_4() + { + bindHeaders.setString("X-match", "any"); + bindHeaders.setString("A", "Value of A"); + bindHeaders.setString("B", "Value of B"); + + matchHeaders.setString("A", "Value of A"); + matchHeaders.setString("B", "Value of B"); + matchHeaders.setString("C", "Value of C"); + + assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders)); + } + + public void testAny_5() + { + bindHeaders.setString("X-match", "any"); + bindHeaders.setString("A", "Value of A"); + bindHeaders.setString("B", "Value of B"); + + matchHeaders.setString("A", "Value of A"); + matchHeaders.setString("B", "Altered value of B"); + matchHeaders.setString("C", "Value of C"); + + assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders)); + } + + public void testAny_6() + { + bindHeaders.setString("X-match", "any"); + bindHeaders.setString("A", "Value of A"); + bindHeaders.setString("B", "Value of B"); + + matchHeaders.setString("A", "Altered value of A"); + matchHeaders.setString("B", "Altered value of B"); + matchHeaders.setString("C", "Value of C"); + + assertFalse(new HeadersBinding(bindHeaders).matches(matchHeaders)); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(HeadersBindingTest.class); + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersExchangeTest.java b/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersExchangeTest.java new file mode 100644 index 0000000000..fd11ddeae2 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersExchangeTest.java @@ -0,0 +1,106 @@ +/* + * + * 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.util.NullApplicationRegistry; +import org.apache.qpid.framing.BasicPublishBody; + +public class HeadersExchangeTest extends AbstractHeadersExchangeTestBase +{ + protected void setUp() throws Exception + { + super.setUp(); + ApplicationRegistry.initialise(new NullApplicationRegistry(), 1); + } + + protected void tearDown() + { + ApplicationRegistry.remove(1); + } + + 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("Message1", "F0000"), q1); + routeAndTest(new Message("Message2", "F0000=Aardvark"), q1, q2); + routeAndTest(new Message("Message3", "F0000=Aardvark", "F0001"), q1, q2, q3, q5, q8); + routeAndTest(new Message("Message4", "F0000", "F0001=Bear"), q1, q3, q4, q5, q7); + routeAndTest(new Message("Message5", "F0000=Aardvark", "F0001=Bear"), + q1, q2, q3, q4, q5, q6, q7, q8); + routeAndTest(new Message("Message6", "F0002")); + + Message m7 = new Message("Message7", "XXXXX"); + + MessagePublishInfoImpl pb7 = (MessagePublishInfoImpl) (m7.getMessagePublishInfo()); + pb7.setMandatory(true); + routeAndTest(m7,true); + + Message m8 = new Message("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("Message1", "F0000"), q1, q3); + routeAndTest(new Message("Message2", "F0000=Aardvark"), q1, q2, q3, q4); + routeAndTest(new Message("Message3", "F0000=Aardvark", "F0001"), q1, q2, q3, q4, q6); + routeAndTest(new Message("Message4", "F0000", "F0001=Bear"), q1, q2, q3, q4, q6); + routeAndTest(new Message("Message5", "F0000=Aardvark", "F0001=Bear"), q1, q2, q3, q4, q6); + routeAndTest(new Message("Message6", "F0002")); + } + + public void testMandatory() throws AMQException + { + bindDefault("F0000"); + Message m1 = new Message("Message1", "XXXXX"); + Message m2 = new Message("Message2", "F0000"); + MessagePublishInfoImpl pb1 = (MessagePublishInfoImpl) (m1.getMessagePublishInfo()); + pb1.setMandatory(true); + MessagePublishInfoImpl pb2 = (MessagePublishInfoImpl) (m2.getMessagePublishInfo()); + pb2.setMandatory(true); + routeAndTest(m1,true); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(HeadersExchangeTest.class); + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/logging/management/LoggingManagementMBeanTest.java b/java/broker/src/test/java/org/apache/qpid/server/logging/management/LoggingManagementMBeanTest.java new file mode 100644 index 0000000000..40153be331 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/logging/management/LoggingManagementMBeanTest.java @@ -0,0 +1,413 @@ +/* + * 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 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 junit.framework.TestCase; + +public class LoggingManagementMBeanTest extends TestCase +{ + 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 CATEGORY_PRIORITY = "LogManMBeanTest.category.priority"; + private static final String CATEGORY_LEVEL = "LogManMBeanTest.category.level"; + private static final String LOGGER_LEVEL = "LogManMBeanTest.logger.level"; + + private static final String NAME_INDEX = LoggingManagement.COMPOSITE_ITEM_NAMES[0]; + private static final String LEVEL_INDEX = LoggingManagement.COMPOSITE_ITEM_NAMES[1]; + + private static final String NEWLINE = System.getProperty("line.separator"); + + private File _testConfigFile; + + protected void setUp() throws Exception + { + _testConfigFile = createTempTestLog4JConfig(); + } + + 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=\"" + 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=\"" + 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=\"" + 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(NAME_INDEX).toString(), data.get(LEVEL_INDEX).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(NAME_INDEX).toString(), data.get(LEVEL_INDEX).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(NAME_INDEX).toString(), data.get(LEVEL_INDEX).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(NAME_INDEX).toString(), data.get(LEVEL_INDEX).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(CATEGORY_PRIORITY + " logger was not in the returned list", list.containsKey(CATEGORY_PRIORITY)); + assertTrue(CATEGORY_LEVEL + " logger was not in the returned list", list.containsKey(CATEGORY_LEVEL)); + assertTrue(LOGGER_LEVEL + " logger was not in the returned list", list.containsKey(LOGGER_LEVEL)); + + //check that their level is as expected + assertTrue(CATEGORY_PRIORITY + " logger's level was incorrect", list.get(CATEGORY_PRIORITY).equalsIgnoreCase("info")); + assertTrue(CATEGORY_LEVEL + " logger's level was incorrect", list.get(CATEGORY_LEVEL).equalsIgnoreCase("warn")); + assertTrue(LOGGER_LEVEL + " logger's level was incorrect", list.get(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(CATEGORY_PRIORITY, "warn")); + //change the category+level to error + assertTrue("failed to set new level", lm.setConfigFileLoggerLevel(CATEGORY_LEVEL, "error")); + //change the logger+level to trace + assertTrue("failed to set new level", lm.setConfigFileLoggerLevel(LOGGER_LEVEL, "trace")); + + //try an invalid level + assertFalse("Use of an invalid logger level was successfull", lm.setConfigFileLoggerLevel(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(NAME_INDEX).toString(), data.get(LEVEL_INDEX).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(CATEGORY_PRIORITY + " logger was not in the returned list", list.containsKey(CATEGORY_PRIORITY)); + assertTrue(CATEGORY_LEVEL + " logger was not in the returned list", list.containsKey(CATEGORY_LEVEL)); + assertTrue(LOGGER_LEVEL + " logger was not in the returned list", list.containsKey(LOGGER_LEVEL)); + + //check that their level is as expected after the changes + assertTrue(CATEGORY_PRIORITY + " logger's level was incorrect", list.get(CATEGORY_PRIORITY).equalsIgnoreCase("warn")); + assertTrue(CATEGORY_LEVEL + " logger's level was incorrect", list.get(CATEGORY_LEVEL).equalsIgnoreCase("error")); + assertTrue(LOGGER_LEVEL + " logger's level was incorrect", list.get(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/java/broker/src/test/java/org/apache/qpid/server/plugins/MockPluginManager.java b/java/broker/src/test/java/org/apache/qpid/server/plugins/MockPluginManager.java new file mode 100644 index 0000000000..9599848dde --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/plugins/MockPluginManager.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.plugins; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.qpid.server.exchange.ExchangeType; +import org.apache.qpid.server.security.access.ACLPlugin; +import org.apache.qpid.server.security.access.ACLPluginFactory; +import org.apache.qpid.server.security.access.QueueDenier; + +public class MockPluginManager extends PluginManager +{ + + private Map<String, ACLPluginFactory> _securityPlugins = new HashMap<String, ACLPluginFactory>(); + + public MockPluginManager(String plugindir) throws Exception + { + super(plugindir); + _securityPlugins.put("org.apache.qpid.server.security.access.QueueDenier", QueueDenier.FACTORY); + } + + @Override + public Map<String, ExchangeType<?>> getExchanges() + { + return null; + } + + @Override + public Map<String, ACLPluginFactory> getSecurityPlugins() + { + return _securityPlugins; + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/plugins/PluginTest.java b/java/broker/src/test/java/org/apache/qpid/server/plugins/PluginTest.java new file mode 100644 index 0000000000..11d6105704 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/plugins/PluginTest.java @@ -0,0 +1,53 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.server.plugins; + +import java.util.Map; + +import org.apache.qpid.server.exchange.ExchangeType; + +import junit.framework.TestCase; + +public class PluginTest extends TestCase +{ + + 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"); + + public void testLoadExchanges() throws Exception + { + PluginManager manager = new PluginManager(PLUGIN_DIRECTORY); + 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"); + Map<String, ExchangeType<?>> exchanges = manager.getExchanges(); + assertEquals("Exchanges found", 0, exchanges.size()); + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java b/java/broker/src/test/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java new file mode 100644 index 0000000000..d5db87350b --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.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.protocol; + +import junit.framework.TestCase; + +import org.apache.log4j.Logger; + +import org.apache.qpid.AMQException; +import org.apache.qpid.codec.AMQCodecFactory; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.queue.AMQQueueFactory; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.SkeletonMessageStore; + +import javax.management.JMException; + +/** + * Test class to test MBean operations for AMQMinaProtocolSession. + */ +public class AMQProtocolSessionMBeanTest extends TestCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(AMQProtocolSessionMBeanTest.class); + + private MessageStore _messageStore = new SkeletonMessageStore(); + private AMQMinaProtocolSession _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, + _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 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 + protected void setUp() throws Exception + { + super.setUp(); + + IApplicationRegistry appRegistry = ApplicationRegistry.getInstance(); + _protocolSession = + new AMQMinaProtocolSession(new TestIoSession(), appRegistry.getVirtualHostRegistry(), new AMQCodecFactory(true), + null); + _protocolSession.setVirtualHost(appRegistry.getVirtualHostRegistry().getVirtualHost("test")); + _channel = new AMQChannel(_protocolSession, 1, _messageStore); + _protocolSession.addChannel(_channel); + _mbean = (AMQProtocolSessionMBean) _protocolSession.getManagedObject(); + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java b/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java new file mode 100644 index 0000000000..da35ddc594 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java @@ -0,0 +1,180 @@ +/* + * + * 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 org.apache.qpid.AMQException; +import org.apache.qpid.codec.AMQCodecFactory; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.output.ProtocolOutputConverter; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.AMQChannel; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +public class InternalTestProtocolSession extends AMQMinaProtocolSession implements ProtocolOutputConverter +{ + // ChannelID(LIST) -> LinkedList<Pair> + final Map<Integer, Map<AMQShortString, LinkedList<DeliveryPair>>> _channelDelivers; + private AtomicInteger _deliveryCount = new AtomicInteger(0); + + public InternalTestProtocolSession() throws AMQException + { + super(new TestIoSession(), + ApplicationRegistry.getInstance().getVirtualHostRegistry(), + new AMQCodecFactory(true)); + + _channelDelivers = new HashMap<Integer, Map<AMQShortString, LinkedList<DeliveryPair>>>(); + + } + + public ProtocolOutputConverter getProtocolOutputConverter() + { + return this; + } + + public byte getProtocolMajorVersion() + { + return (byte) 8; + } + + 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(AMQMessage message, 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, message)); + } + } + + public void writeGetOk(AMQMessage 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. + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/protocol/MaxChannelsTest.java b/java/broker/src/test/java/org/apache/qpid/server/protocol/MaxChannelsTest.java new file mode 100644 index 0000000000..1bdabf345b --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/protocol/MaxChannelsTest.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.protocol; + +import junit.framework.TestCase; +import org.apache.qpid.AMQException; +import org.apache.qpid.codec.AMQCodecFactory; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; + +/** Test class to test MBean operations for AMQMinaProtocolSession. */ +public class MaxChannelsTest extends TestCase +{ + private IApplicationRegistry _appRegistry; + private AMQMinaProtocolSession _session; + + public void testChannels() throws Exception + { + _session = new AMQMinaProtocolSession(new TestIoSession(), _appRegistry + .getVirtualHostRegistry(), new AMQCodecFactory(true), null); + _session.setVirtualHost(_appRegistry.getVirtualHostRegistry().getVirtualHost("test")); + + // 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 setUp() + { + _appRegistry = ApplicationRegistry.getInstance(1); + } + + @Override + public void tearDown() + { + try { + _session.closeSession(); + } catch (AMQException e) { + // Yikes + fail(e.getMessage()); + } + ApplicationRegistry.remove(1); + } + +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/protocol/TestIoSession.java b/java/broker/src/test/java/org/apache/qpid/server/protocol/TestIoSession.java new file mode 100644 index 0000000000..211f491867 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/protocol/TestIoSession.java @@ -0,0 +1,328 @@ +/* + * + * 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.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import org.apache.mina.common.CloseFuture; +import org.apache.mina.common.IdleStatus; +import org.apache.mina.common.IoFilterChain; +import org.apache.mina.common.IoHandler; +import org.apache.mina.common.IoService; +import org.apache.mina.common.IoServiceConfig; +import org.apache.mina.common.IoSession; +import org.apache.mina.common.IoSessionConfig; +import org.apache.mina.common.ThreadModel; +import org.apache.mina.common.TrafficMask; +import org.apache.mina.common.TransportType; +import org.apache.mina.common.WriteFuture; +import org.apache.mina.transport.socket.nio.SocketAcceptorConfig; +import org.apache.qpid.pool.ReadWriteThreadModel; + +/** + * Test implementation of IoSession, which is required for some tests. Methods not being used are not implemented, + * so if this class is being used and some methods are to be used, then please update those. + */ +public class TestIoSession implements IoSession +{ + private final ConcurrentMap attributes = new ConcurrentHashMap(); + private String _address = "127.0.0.1"; + private int _port = 1; + + public TestIoSession() + { + } + + public IoService getService() + { + return null; + } + + public IoServiceConfig getServiceConfig() + { + return new TestIoConfig(); + } + + public IoHandler getHandler() + { + return null; + } + + public IoSessionConfig getConfig() + { + return null; + } + + public IoFilterChain getFilterChain() + { + return null; + } + + public WriteFuture write(Object message) + { + return null; + } + + public CloseFuture close() + { + return null; + } + + public Object getAttachment() + { + return getAttribute(""); + } + + public Object setAttachment(Object attachment) + { + return setAttribute("",attachment); + } + + public Object getAttribute(String key) + { + return attributes.get(key); + } + + public Object setAttribute(String key, Object value) + { + return attributes.put(key,value); + } + + public Object setAttribute(String key) + { + return attributes.put(key, Boolean.TRUE); + } + + public Object removeAttribute(String key) + { + return attributes.remove(key); + } + + public boolean containsAttribute(String key) + { + return attributes.containsKey(key); + } + + public Set getAttributeKeys() + { + return attributes.keySet(); + } + + public TransportType getTransportType() + { + return null; + } + + public boolean isConnected() + { + return false; + } + + public boolean isClosing() + { + return false; + } + + public CloseFuture getCloseFuture() + { + return null; + } + + public SocketAddress getRemoteAddress() + { + return new InetSocketAddress(getAddress(), getPort()); + } + + public SocketAddress getLocalAddress() + { + return null; + } + + public SocketAddress getServiceAddress() + { + return null; + } + + public int getIdleTime(IdleStatus status) + { + return 0; + } + + public long getIdleTimeInMillis(IdleStatus status) + { + return 0; + } + + public void setIdleTime(IdleStatus status, int idleTime) + { + + } + + public int getWriteTimeout() + { + return 0; + } + + public long getWriteTimeoutInMillis() + { + return 0; + } + + public void setWriteTimeout(int writeTimeout) + { + + } + + public TrafficMask getTrafficMask() + { + return null; + } + + public void setTrafficMask(TrafficMask trafficMask) + { + + } + + public void suspendRead() + { + + } + + public void suspendWrite() + { + + } + + public void resumeRead() + { + + } + + public void resumeWrite() + { + + } + + public long getReadBytes() + { + return 0; + } + + public long getWrittenBytes() + { + return 0; + } + + public long getReadMessages() + { + return 0; + } + + public long getWrittenMessages() + { + return 0; + } + + public long getWrittenWriteRequests() + { + return 0; + } + + public int getScheduledWriteRequests() + { + return 0; + } + + public int getScheduledWriteBytes() + { + return 0; + } + + public long getCreationTime() + { + return 0; + } + + public long getLastIoTime() + { + return 0; + } + + public long getLastReadTime() + { + return 0; + } + + public long getLastWriteTime() + { + return 0; + } + + public boolean isIdle(IdleStatus status) + { + return false; + } + + public int getIdleCount(IdleStatus status) + { + return 0; + } + + public long getLastIdleTime(IdleStatus status) + { + return 0; + } + + public void setAddress(String string) + { + this._address = string; + } + + public String getAddress() + { + return _address; + } + + public void setPort(int _port) + { + this._port = _port; + } + + public int getPort() + { + return _port; + } + + /** + * Test implementation of IoServiceConfig + */ + private class TestIoConfig extends SocketAcceptorConfig + { + public ThreadModel getThreadModel() + { + return ReadWriteThreadModel.getInstance(); + } + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/AMQPriorityQueueTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQPriorityQueueTest.java new file mode 100644 index 0000000000..aff7af6952 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQPriorityQueueTest.java @@ -0,0 +1,107 @@ +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.framing.BasicContentHeaderProperties; +import org.apache.qpid.framing.FieldTable; +import junit.framework.AssertionFailedError; + +public class AMQPriorityQueueTest extends SimpleAMQQueueTest +{ + + @Override + protected 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(null, createMessage(1L, (byte) 10)); + _queue.enqueue(null, createMessage(2L, (byte) 4)); + _queue.enqueue(null, createMessage(3L, (byte) 0)); + + // Enqueue messages in reverse order + _queue.enqueue(null, createMessage(4L, (byte) 0)); + _queue.enqueue(null, createMessage(5L, (byte) 4)); + _queue.enqueue(null, createMessage(6L, (byte) 10)); + + // Enqueue messages out of order + _queue.enqueue(null, createMessage(7L, (byte) 4)); + _queue.enqueue(null, createMessage(8L, (byte) 10)); + _queue.enqueue(null, 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().getMessageId()); + assertEquals(new Long(6L), msgs.get(1).getMessage().getMessageId()); + assertEquals(new Long(8L), msgs.get(2).getMessage().getMessageId()); + + assertEquals(new Long(2L), msgs.get(3).getMessage().getMessageId()); + assertEquals(new Long(5L), msgs.get(4).getMessage().getMessageId()); + assertEquals(new Long(7L), msgs.get(5).getMessage().getMessageId()); + + assertEquals(new Long(3L), msgs.get(6).getMessage().getMessageId()); + assertEquals(new Long(4L), msgs.get(7).getMessage().getMessageId()); + assertEquals(new Long(9L), msgs.get(8).getMessage().getMessageId()); + } + catch (AssertionFailedError afe) + { + // Show message order on failure. + int index = 1; + for (QueueEntry qe : msgs) + { + System.err.println(index + ":" + qe.getMessage().getMessageId()); + 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().properties = props; + return msg; + } + + protected AMQMessage createMessage(Long id) throws AMQException + { + return createMessage(id, (byte) 0); + } + +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java new file mode 100644 index 0000000000..6589f46c7b --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java @@ -0,0 +1,354 @@ +/* + * + * 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.AMQException; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.MemoryMessageStore; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.txn.TransactionalContext; +import org.apache.qpid.server.txn.NonTransactionalContext; +import org.apache.qpid.server.RequiredDeliveryException; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.subscription.SubscriptionFactoryImpl; +import org.apache.qpid.server.protocol.AMQMinaProtocolSession; +import org.apache.qpid.server.protocol.InternalTestProtocolSession; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.commons.configuration.CompositeConfiguration; +import org.apache.mina.common.ByteBuffer; + +import javax.management.Notification; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.Collections; +import java.util.Set; + +/** This class tests all the alerts an AMQQueue can throw based on threshold values of different parameters */ +public class AMQQueueAlertTest extends TestCase +{ + 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 AMQQueue _queue; + private AMQQueueMBean _queueMBean; + private VirtualHost _virtualHost; + private AMQMinaProtocolSession _protocolSession; + private MessageStore _messageStore = new MemoryMessageStore(); + private StoreContext _storeContext = new StoreContext(); + private TransactionalContext _transactionalContext = new NonTransactionalContext(_messageStore, _storeContext, + null, + new LinkedList<RequiredDeliveryException>() + ); + 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 + { + _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue1"), false, new AMQShortString("AMQueueAlertTest"), + false, _virtualHost, + null); + _queueMBean = (AMQQueueMBean) _queue.getManagedObject(); + + _queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT); + + sendMessages(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 + { + _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue2"), false, new AMQShortString("AMQueueAlertTest"), + false, _virtualHost, + null); + _queueMBean = (AMQQueueMBean) _queue.getManagedObject(); + _queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT); + _queueMBean.setMaximumMessageSize(MAX_MESSAGE_SIZE); + + sendMessages(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 + { + _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue3"), false, new AMQShortString("AMQueueAlertTest"), + false, _virtualHost, + null); + _queueMBean = (AMQQueueMBean) _queue.getManagedObject(); + _queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT); + _queueMBean.setMaximumQueueDepth(MAX_QUEUE_DEPTH); + + while (_queue.getQueueDepth() < MAX_QUEUE_DEPTH) + { + sendMessages(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 + { + _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue4"), false, new AMQShortString("AMQueueAlertTest"), + false, _virtualHost, + null); + _queueMBean = (AMQQueueMBean) _queue.getManagedObject(); + _queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT); + _queueMBean.setMaximumMessageAge(MAX_MESSAGE_AGE); + + sendMessages(1, MAX_MESSAGE_SIZE); + + // Ensure message sits on queue long enough to age. + Thread.sleep(MAX_MESSAGE_AGE * 2); + + Notification lastNotification = _queueMBean.getLastNotification(); + assertNotNull(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 + { + _protocolSession = new InternalTestProtocolSession(); + AMQChannel channel = new AMQChannel(_protocolSession, 2, _messageStore); + _protocolSession.addChannel(channel); + + // Create queue + _queue = getNewQueue(); + Subscription subscription = + SUBSCRIPTION_FACTORY.createSubscription(channel.getChannelId(), _protocolSession, new AMQShortString("consumer_tag"), true, null, false, channel.getCreditManager()); + + _queue.registerSubscription( + subscription, false); + + _queueMBean = (AMQQueueMBean) _queue.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(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 + _queue.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(), _protocolSession, new AMQShortString("consumer_tag"), true, null, false, channel.getCreditManager()); + + _queue.registerSubscription( + subscription2, false); + + while (_queue.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. + _queue.unregisterSubscription(subscription2); + channel.requeue(); + + assertEquals(new Long(totalSize), new Long(_queueMBean.getQueueDepth())); + _protocolSession.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(); + contentHeaderBody.bodySize = size; // in bytes + IncomingMessage message = new IncomingMessage(_messageStore.getNewMessageId(), publish, _transactionalContext, _protocolSession); + message.setContentHeaderBody(contentHeaderBody); + + return message; + } + + @Override + protected void setUp() throws Exception + { + super.setUp(); + IApplicationRegistry applicationRegistry = ApplicationRegistry.getInstance(1); + _virtualHost = applicationRegistry.getVirtualHostRegistry().getVirtualHost("test"); + _protocolSession = new InternalTestProtocolSession(); + + } + + protected void tearDown() + { + ApplicationRegistry.remove(1); + } + + + private void sendMessages(long messageCount, final long size) throws AMQException + { + IncomingMessage[] messages = new IncomingMessage[(int) messageCount]; + for (int i = 0; i < messages.length; i++) + { + messages[i] = message(false, size); + ArrayList<AMQQueue> qs = new ArrayList<AMQQueue>(); + qs.add(_queue); + messages[i].enqueue(qs); + messages[i].routingComplete(_messageStore, new MessageHandleFactory()); + + } + + for (int i = 0; i < messageCount; i++) + { + messages[i].addContentBodyFrame(new ContentChunk(){ + + ByteBuffer _data = ByteBuffer.allocate((int)size); + + public int getSize() + { + return (int) size; + } + + public ByteBuffer getData() + { + return _data; + } + + public void reduceToFit() + { + + } + }); + messages[i].deliverToQueues(); + } + } + + private AMQQueue getNewQueue() throws AMQException + { + return AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue" + Math.random()), + false, + new AMQShortString("AMQueueAlertTest"), + false, + _virtualHost, null); + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueFactoryTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueFactoryTest.java new file mode 100644 index 0000000000..520e49c56a --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueFactoryTest.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.queue; + +import junit.framework.TestCase; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.AMQException; + +public class AMQQueueFactoryTest extends TestCase +{ + QueueRegistry _queueRegistry; + VirtualHost _virtualHost; + + public void setUp() + { + ApplicationRegistry registry = (ApplicationRegistry) ApplicationRegistry.getInstance(); + + _virtualHost = registry.getVirtualHostRegistry().getVirtualHost("test"); + + _queueRegistry = _virtualHost.getQueueRegistry(); + + assertEquals("Queues registered on an empty virtualhost", 0, _queueRegistry.getQueues().size()); + } + + public void tearDown() + { + assertEquals("Queue was not registered in virtualhost", 1, _queueRegistry.getQueues().size()); + ApplicationRegistry.remove(1); + } + + + public void testPriorityQueueRegistration() + { + FieldTable fieldTable = new FieldTable(); + fieldTable.put(new AMQShortString(AMQQueueFactory.X_QPID_PRIORITIES), 5); + + try + { + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testPriorityQueue"), false, new AMQShortString("owner"), false, + _virtualHost, fieldTable); + + assertEquals("Queue not a priorty queue", AMQPriorityQueue.class, queue.getClass()); + } + catch (AMQException e) + { + fail(e.getMessage()); + } + } + + + public void testSimpleQueueRegistration() + { + try + { + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue"), false, new AMQShortString("owner"), false, + _virtualHost, null); + assertEquals("Queue not a simple queue", SimpleAMQQueue.class, queue.getClass()); + } + catch (AMQException e) + { + fail(e.getMessage()); + } + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java new file mode 100644 index 0000000000..ce986cf55b --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java @@ -0,0 +1,349 @@ +/* + * + * 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.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.RequiredDeliveryException; +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.AMQProtocolSession; +import org.apache.qpid.server.protocol.InternalTestProtocolSession; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.txn.TransactionalContext; +import org.apache.qpid.server.txn.NonTransactionalContext; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.store.MemoryMessageStore; +import org.apache.qpid.server.store.TestableMemoryMessageStore; +import org.apache.mina.common.ByteBuffer; + +import javax.management.JMException; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.Collections; + +/** + * Test class to test AMQQueueMBean attribtues and operations + */ +public class AMQQueueMBeanTest extends TestCase +{ + private static long MESSAGE_SIZE = 1000; + private AMQQueue _queue; + private AMQQueueMBean _queueMBean; + private MessageStore _messageStore; + private StoreContext _storeContext = new StoreContext(); + private TransactionalContext _transactionalContext; + private VirtualHost _virtualHost; + private AMQProtocolSession _protocolSession; + 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(); + } + + // todo: collect to a general testing class -duplicated from Systest/MessageReturntest + private void verifyBrokerState() + { + + TestableMemoryMessageStore store = new TestableMemoryMessageStore((MemoryMessageStore) _virtualHost.getMessageStore()); + + // Unlike MessageReturnTest there is no need for a delay as there this thread does the clean up. + assertNotNull("ContentBodyMap should not be null", store.getContentBodyMap()); + assertEquals("Expected the store to have no content:" + store.getContentBodyMap(), 0, store.getContentBodyMap().size()); + assertNotNull("MessageMetaDataMap should not be null", store.getMessageMetaDataMap()); + assertEquals("Expected the store to have no metadata:" + store.getMessageMetaDataMap(), 0, store.getMessageMetaDataMap().size()); + } + + public void testConsumerCount() throws AMQException + { + + assertTrue(_queue.getActiveConsumerCount() == 0); + assertTrue(_queueMBean.getActiveConsumerCount() == 0); + + + InternalTestProtocolSession protocolSession = new InternalTestProtocolSession(); + AMQChannel channel = new AMQChannel(protocolSession, 1, _messageStore); + protocolSession.addChannel(channel); + + Subscription subscription = + SUBSCRIPTION_FACTORY.createSubscription(channel.getChannelId(), protocolSession, new AMQShortString("test"), false, null, false, channel.getCreditManager()); + + _queue.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()); + _queue.registerSubscription(s1,false); + _queue.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() + { + long maxQueueDepth = 1000; // in bytes + _queueMBean.setMaximumMessageCount(50000l); + _queueMBean.setMaximumMessageSize(2000l); + _queueMBean.setMaximumQueueDepth(maxQueueDepth); + + assertTrue(_queueMBean.getMaximumMessageCount() == 50000); + assertTrue(_queueMBean.getMaximumMessageSize() == 2000); + assertTrue(_queueMBean.getMaximumQueueDepth() == (maxQueueDepth)); + + assertTrue(_queueMBean.getName().equals("testQueue")); + assertTrue(_queueMBean.getOwner().equals("AMQueueMBeanTest")); + assertFalse(_queueMBean.isAutoDelete()); + assertFalse(_queueMBean.isDurable()); + } + + public void testExceptions() throws Exception + { + try + { + _queueMBean.viewMessages(0, 3); + fail(); + } + catch (JMException ex) + { + + } + + try + { + _queueMBean.viewMessages(2, 1); + fail(); + } + catch (JMException ex) + { + + } + + try + { + _queueMBean.viewMessages(-1, 1); + fail(); + } + catch (JMException ex) + { + + } + + IncomingMessage msg = message(false, false); + long id = msg.getMessageId(); + _queue.clearQueue(_storeContext); + ArrayList<AMQQueue> qs = new ArrayList<AMQQueue>(); + qs.add(_queue); + msg.enqueue(qs); + msg.routingComplete(_messageStore, new MessageHandleFactory()); + + msg.addContentBodyFrame(new ContentChunk() + { + ByteBuffer _data = ByteBuffer.allocate((int)MESSAGE_SIZE); + + public int getSize() + { + return (int) MESSAGE_SIZE; + } + + public ByteBuffer getData() + { + return _data; + } + + public void reduceToFit() + { + + } + }); + msg.deliverToQueues(); +// _queue.process(_storeContext, new QueueEntry(_queue, msg), false); + _queueMBean.viewMessageContent(id); + try + { + _queueMBean.viewMessageContent(id + 1); + fail(); + } + catch (JMException ex) + { + + } + } + + 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.properties = new BasicContentHeaderProperties(); + ((BasicContentHeaderProperties) contentHeaderBody.properties).setDeliveryMode((byte) (persistent ? 2 : 1)); + IncomingMessage msg = new IncomingMessage(_messageStore.getNewMessageId(), publish, _transactionalContext, _protocolSession); + msg.setContentHeaderBody(contentHeaderBody); + return msg; + + } + + @Override + protected void setUp() throws Exception + { + super.setUp(); + IApplicationRegistry applicationRegistry = ApplicationRegistry.getInstance(1); + _virtualHost = applicationRegistry.getVirtualHostRegistry().getVirtualHost("test"); + _messageStore = _virtualHost.getMessageStore(); + + _transactionalContext = new NonTransactionalContext(_messageStore, _storeContext, + null, + new LinkedList<RequiredDeliveryException>() + ); + + _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue"), false, new AMQShortString("AMQueueMBeanTest"), false, _virtualHost, + null); + _queueMBean = new AMQQueueMBean(_queue); + + _protocolSession = new InternalTestProtocolSession(); + } + + public void tearDown() + { + ApplicationRegistry.remove(1); + } + + 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(_queue); + currentMessage.enqueue(qs); + + // route header + currentMessage.routingComplete(_messageStore, new MessageHandleFactory()); + + // Add the body so we have somthing to test later + currentMessage.addContentBodyFrame( + _protocolSession.getMethodRegistry() + .getProtocolVersionMethodConverter() + .convertToContentChunk( + new ContentBody(ByteBuffer.allocate((int) MESSAGE_SIZE), + MESSAGE_SIZE))); + currentMessage.deliverToQueues(); + + + } + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/AckTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/AckTest.java new file mode 100644 index 0000000000..9c2932c5e2 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/AckTest.java @@ -0,0 +1,420 @@ +/* + * + * 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.RequiredDeliveryException; +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 org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.txn.NonTransactionalContext; +import org.apache.qpid.server.txn.TransactionalContext; +import org.apache.qpid.server.util.NullApplicationRegistry; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.Set; +import java.util.Collections; + +/** + * Tests that acknowledgements are handled correctly. + */ +public class AckTest extends TestCase +{ + private static final Logger _log = Logger.getLogger(AckTest.class); + + private Subscription _subscription; + + private MockProtocolSession _protocolSession; + + private TestMemoryMessageStore _messageStore; + + private StoreContext _storeContext = new StoreContext(); + + private AMQChannel _channel; + + private AMQQueue _queue; + + private static final AMQShortString DEFAULT_CONSUMER_TAG = new AMQShortString("conTag"); + + protected void setUp() throws Exception + { + super.setUp(); + ApplicationRegistry.initialise(new NullApplicationRegistry(), 1); + + _messageStore = new TestMemoryMessageStore(); + _protocolSession = new MockProtocolSession(_messageStore); + _channel = new AMQChannel(_protocolSession,5, _messageStore /*dont need exchange registry*/); + + _protocolSession.addChannel(_channel); + + _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("myQ"), false, new AMQShortString("guest"), true, ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test"), + null); + } + + protected void tearDown() + { + ApplicationRegistry.remove(1); + } + + private void publishMessages(int count) throws AMQException + { + publishMessages(count, false); + } + + private void publishMessages(int count, boolean persistent) throws AMQException + { + TransactionalContext txnContext = new NonTransactionalContext(_messageStore, _storeContext, null, + new LinkedList<RequiredDeliveryException>() + ); + _queue.registerSubscription(_subscription,false); + MessageHandleFactory factory = new MessageHandleFactory(); + 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"); + } + }; + IncomingMessage msg = new IncomingMessage(_messageStore.getNewMessageId(), publishBody, txnContext,_protocolSession); + //IncomingMessage msg2 = null; + if (persistent) + { + BasicContentHeaderProperties b = new BasicContentHeaderProperties(); + //This is DeliveryMode.PERSISTENT + b.setDeliveryMode((byte) 2); + ContentHeaderBody cb = new ContentHeaderBody(); + cb.properties = b; + msg.setContentHeaderBody(cb); + } + else + { + msg.setContentHeaderBody(new ContentHeaderBody()); + } + // 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); + msg.routingComplete(_messageStore, factory); + if(msg.allContentReceived()) + { + msg.deliverToQueues(); + } + // 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(); + assertTrue(map.size() == msgCount); + assertTrue(_messageStore.getMessageMetaDataMap().size() == msgCount); + + 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); + } + + assertTrue(map.size() == msgCount); + assertTrue(_messageStore.getMessageMetaDataMap().size() == msgCount); + } + + /** + * 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.getMessageMetaDataMap().size() == 0); + assertTrue(_messageStore.getContentBodyMap().size() == 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.getMessageMetaDataMap().size() == 0); + assertTrue(_messageStore.getContentBodyMap().size() == 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/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQMessage.java b/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQMessage.java new file mode 100644 index 0000000000..355ba6a362 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQMessage.java @@ -0,0 +1,49 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.queue; + +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; + +public class MockAMQMessage extends AMQMessage +{ + public MockAMQMessage(long messageId) + throws AMQException + { + super(new MockAMQMessageHandle(messageId) , + (StoreContext)null, + (MessagePublishInfo)new MockMessagePublishInfo()); + } + + protected MockAMQMessage(AMQMessage msg) + throws AMQException + { + super(msg); + } + + + @Override + public long getSize() + { + return 0l; + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQMessageHandle.java b/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQMessageHandle.java new file mode 100644 index 0000000000..bdb0707c27 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQMessageHandle.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.queue; + +import org.apache.qpid.server.store.StoreContext; + +public class MockAMQMessageHandle extends InMemoryMessageHandle +{ + public MockAMQMessageHandle(final Long messageId) + { + super(messageId); + } + + @Override + public long getBodySize(StoreContext store) + { + return 0l; + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java b/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java new file mode 100644 index 0000000000..20503bf15c --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java @@ -0,0 +1,335 @@ +/* + * + * 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.framing.FieldTable; +import org.apache.qpid.server.configuration.QueueConfiguration; +import org.apache.qpid.server.configuration.ServerConfiguration; +import org.apache.qpid.server.configuration.VirtualHostConfiguration; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.management.ManagedObject; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.AMQException; +import org.apache.commons.configuration.Configuration; + +import java.util.List; +import java.util.Set; +import java.util.Map; +import java.util.HashMap; +import java.util.LinkedList; + +public class MockAMQQueue implements AMQQueue +{ + private boolean _deleted = false; + private AMQShortString _name; + + public MockAMQQueue(String name) + { + _name = new AMQShortString(name); + } + + public MockAMQQueue() + { + + } + + public AMQShortString getName() + { + return _name; + } + + public boolean isDurable() + { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean isAutoDelete() + { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + public AMQShortString getOwner() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public VirtualHost getVirtualHost() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public void bind(Exchange exchange, AMQShortString routingKey, FieldTable arguments) throws AMQException + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void unBind(Exchange exchange, AMQShortString routingKey, FieldTable arguments) throws AMQException + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public List<ExchangeBinding> getExchangeBindings() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + 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 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 + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public QueueEntry enqueue(StoreContext storeContext, AMQMessage message) throws AMQException + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public void requeue(StoreContext storeContext, QueueEntry entry) throws AMQException + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void dequeue(StoreContext storeContext, QueueEntry entry) throws FailedDequeueException + { + //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 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 void moveMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName, StoreContext storeContext) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void copyMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName, StoreContext storeContext) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void removeMessagesFromQueue(long fromMessageId, long toMessageId, StoreContext storeContext) + { + //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 long getMinimumAlertRepeatGap() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public void deleteMessageFromTop(StoreContext storeContext) throws AMQException + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public long clearQueue(StoreContext storeContext) throws AMQException + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + 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 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. + } + + @Override + public void setMinimumAlertRepeatGap(long value) + { + + } + + public void configure(QueueConfiguration config) + { + + } + +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/MockMessagePublishInfo.java b/java/broker/src/test/java/org/apache/qpid/server/queue/MockMessagePublishInfo.java new file mode 100644 index 0000000000..5a5ffaa14d --- /dev/null +++ b/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/java/broker/src/test/java/org/apache/qpid/server/queue/MockProtocolSession.java b/java/broker/src/test/java/org/apache/qpid/server/queue/MockProtocolSession.java new file mode 100644 index 0000000000..99c88fac3e --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/MockProtocolSession.java @@ -0,0 +1,262 @@ +/* + * + * 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.AMQConnectionException; +import org.apache.qpid.framing.*; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.output.ProtocolOutputConverter; +import org.apache.qpid.server.output.ProtocolOutputConverterRegistry; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.transport.Sender; + +import javax.security.sasl.SaslServer; +import java.util.HashMap; +import java.util.Map; +import java.security.Principal; + +/** + * A protocol session that can be used for testing purposes. + */ +public class MockProtocolSession implements AMQProtocolSession +{ + private MessageStore _messageStore; + + private Map<Integer, AMQChannel> _channelMap = new HashMap<Integer, AMQChannel>(); + + public MockProtocolSession(MessageStore messageStore) + { + _messageStore = messageStore; + } + + public void dataBlockReceived(AMQDataBlock message) throws Exception + { + } + + public void writeFrame(AMQDataBlock frame) + { + } + + public AMQShortString getContextKey() + { + return null; + } + + public void setContextKey(AMQShortString contextKey) + { + } + + public AMQChannel getChannel(int channelId) + { + AMQChannel channel = _channelMap.get(channelId); + if (channel == null) + { + throw new IllegalArgumentException("Invalid channel id: " + channelId); + } + else + { + return channel; + } + } + + public void addChannel(AMQChannel channel) + { + if (channel == null) + { + throw new IllegalArgumentException("Channel must not be null"); + } + else + { + _channelMap.put(channel.getChannelId(), channel); + } + } + + public void closeChannel(int channelId) throws AMQException + { + } + + public void closeChannelOk(int channelId) + { + + } + + public boolean channelAwaitingClosure(int channelId) + { + return false; + } + + public void removeChannel(int channelId) + { + _channelMap.remove(channelId); + } + + public void initHeartbeats(int delay) + { + } + + public void closeSession() throws AMQException + { + } + + public void closeConnection(int channelId, AMQConnectionException e, boolean closeIoSession) throws AMQException + { + } + + public Object getKey() + { + return null; + } + + public String getLocalFQDN() + { + return null; + } + + public SaslServer getSaslServer() + { + return null; + } + + public void setSaslServer(SaslServer saslServer) + { + } + + public FieldTable getClientProperties() + { + return null; + } + + public void setClientProperties(FieldTable clientProperties) + { + } + + public Object getClientIdentifier() + { + return null; + } + + public VirtualHost getVirtualHost() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public void setVirtualHost(VirtualHost virtualHost) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void addSessionCloseTask(Task task) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void removeSessionCloseTask(Task task) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public ProtocolOutputConverter getProtocolOutputConverter() + { + return ProtocolOutputConverterRegistry.getConverter(this); + } + + public void setAuthorizedID(Principal authorizedID) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public Principal getAuthorizedID() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public MethodRegistry getMethodRegistry() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public void methodFrameReceived(int channelId, AMQMethodBody body) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void contentHeaderReceived(int channelId, ContentHeaderBody body) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void contentBodyReceived(int channelId, ContentBody body) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void heartbeatBodyReceived(int channelId, HeartbeatBody body) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public MethodDispatcher getMethodDispatcher() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public ProtocolSessionIdentifier getSessionIdentifier() + { + return null; + } + + public byte getProtocolMajorVersion() + { + return getProtocolVersion().getMajorVersion(); + } + + public byte getProtocolMinorVersion() + { + return getProtocolVersion().getMinorVersion(); + } + + + public ProtocolVersion getProtocolVersion() + { + return ProtocolVersion.getLatestSupportedVersion(); //To change body of implemented methods use File | Settings | File Templates. + } + + + public VersionSpecificRegistry getRegistry() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public void setSender(Sender<java.nio.ByteBuffer> sender) + { + // FIXME AS TODO + + } + + public void init() + { + // TODO Auto-generated method stub + + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/MockQueueEntry.java b/java/broker/src/test/java/org/apache/qpid/server/queue/MockQueueEntry.java new file mode 100644 index 0000000000..37f91e7464 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/MockQueueEntry.java @@ -0,0 +1,197 @@ +/* +* + * 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.store.StoreContext; +import org.apache.qpid.server.subscription.Subscription; + +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 void addStateChangeListener(StateChangeListener listener) + { + + } + + public String debugIdentity() + { + return null; + } + + public boolean delete() + { + return false; + } + + public void dequeue(StoreContext storeContext) throws FailedDequeueException + { + + } + + public void discard(StoreContext storeContext) throws FailedDequeueException, MessageCleanupException + { + + } + + public void dispose(StoreContext storeContext) throws MessageCleanupException + { + + } + + public boolean expired() throws AMQException + { + return false; + } + + public Subscription getDeliveredSubscription() + { + return null; + } + + public boolean getDeliveredToConsumer() + { + return false; + } + + public AMQMessage 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 removeStateChangeListener(StateChangeListener listener) + { + + return false; + } + + + public void requeue(StoreContext storeContext) throws AMQException + { + + + } + + + public void setDeliveredToSubscription() + { + + + } + + + public void setRedelivered(boolean b) + { + + + } + + + public int compareTo(QueueEntry o) + { + + return 0; + } + + public void setMessage(AMQMessage msg) + { + _message = msg; + } + +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java new file mode 100644 index 0000000000..3084dc7fa1 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java @@ -0,0 +1,435 @@ +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 java.util.List; + +import junit.framework.TestCase; + +import org.apache.commons.configuration.PropertiesConfiguration; +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.ContentHeaderProperties; +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.exchange.Exchange; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.StoreContext; +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.subscription.SubscriptionImpl; +import org.apache.qpid.server.txn.NonTransactionalContext; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public class SimpleAMQQueueTest extends TestCase +{ + + 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 = new DirectExchange(); + 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 + protected void setUp() throws Exception + { + super.setUp(); + //Create Application Registry for test + ApplicationRegistry applicationRegistry = (ApplicationRegistry)ApplicationRegistry.getInstance(1); + + PropertiesConfiguration env = new PropertiesConfiguration(); + _virtualHost = new VirtualHost(new VirtualHostConfiguration(getClass().getName(), env), _store); + applicationRegistry.getVirtualHostRegistry().registerVirtualHost(_virtualHost); + + _queue = (SimpleAMQQueue) AMQQueueFactory.createAMQQueueImpl(_qname, false, _owner, false, _virtualHost, _arguments); + } + + @Override + protected void tearDown() + { + _queue.stop(); + ApplicationRegistry.remove(1); + } + + public void testCreateQueue() throws AMQException + { + _queue.stop(); + try { + _queue = (SimpleAMQQueue) AMQQueueFactory.createAMQQueueImpl(null, false, _owner, 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, null); + 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, + _virtualHost, _arguments); + assertNotNull("Queue was not created", _queue); + } + + public void testGetVirtualHost() + { + assertEquals("Virtual host was wrong", _virtualHost, _queue.getVirtualHost()); + } + + public void testBinding() + { + try + { + _queue.bind(_exchange, _routingKey, null); + assertTrue("Routing key was not bound", + _exchange.getBindings().containsKey(_routingKey)); + assertEquals("Queue was not bound to key", + _exchange.getBindings().get(_routingKey).get(0), + _queue); + assertEquals("Exchange binding count", 1, + _queue.getExchangeBindings().size()); + assertEquals("Wrong exchange bound", _routingKey, + _queue.getExchangeBindings().get(0).getRoutingKey()); + assertEquals("Wrong exchange bound", _exchange, + _queue.getExchangeBindings().get(0).getExchange()); + + _queue.unBind(_exchange, _routingKey, null); + assertFalse("Routing key was still bound", + _exchange.getBindings().containsKey(_routingKey)); + assertNull("Routing key was not empty", + _exchange.getBindings().get(_routingKey)); + } + catch (AMQException e) + { + assertNull("Unexpected exception", e); + } + } + + public void testSubscription() 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(null, messageA); + assertEquals(messageA, _subscription.getLastSeenEntry().getMessage()); + + // 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(null, messageB); + QueueEntry entry = _subscription.getLastSeenEntry(); + assertNull(entry); + } + + public void testQueueNoSubscriber() throws AMQException, InterruptedException + { + AMQMessage messageA = createMessage(new Long(24)); + _queue.enqueue(null, messageA); + _queue.registerSubscription(_subscription, false); + Thread.sleep(150); + assertEquals(messageA, _subscription.getLastSeenEntry().getMessage()); + } + + 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(null, messageA); + assertEquals(messageA, _subscription.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); + assertTrue(ex instanceof AMQException); + + // 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, _owner, true, _virtualHost); + _queue.registerSubscription(_subscription, false); + AMQMessage message = createMessage(new Long(25)); + _queue.enqueue(null, 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(null, message); + QueueEntry entry = _subscription.getLastSeenEntry(); + entry.setRedelivered(true); + _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(null, 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(null, 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(null, 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 testEnqueueDequeueOfPersistentMessageToNonDurableQueue() throws AMQException + { + // Create IncomingMessage and nondurable queue + NonTransactionalContext txnContext = new NonTransactionalContext(_store, null, null, null); + IncomingMessage msg = new IncomingMessage(1L, info, txnContext, null); + ContentHeaderBody contentHeaderBody = new ContentHeaderBody(); + contentHeaderBody.properties = new BasicContentHeaderProperties(); + ((BasicContentHeaderProperties) contentHeaderBody.properties).setDeliveryMode((byte) 2); + msg.setContentHeaderBody(contentHeaderBody); + ArrayList<AMQQueue> qs = new ArrayList<AMQQueue>(); + + // Send persistent message + qs.add(_queue); + msg.enqueue(qs); + msg.routingComplete(_store, new MessageHandleFactory()); + _store.storeMessageMetaData(null, new Long(1L), new MessageMetaData(info, contentHeaderBody, 1)); + + // Check that it is enqueued + AMQQueue data = _store.getMessages().get(1L); + assertNotNull(data); + + // Dequeue message + MockQueueEntry entry = new MockQueueEntry(); + AMQMessage amqmsg = new AMQMessage(1L, _store, new MessageHandleFactory(), txnContext); + + entry.setMessage(amqmsg); + _queue.dequeue(null, entry); + + // Check that it is dequeued + data = _store.getMessages().get(1L); + assertNull(data); + } + + + // FIXME: move this to somewhere useful + private static AMQMessageHandle createMessageHandle(final long messageId, final MessagePublishInfo publishBody) + { + final AMQMessageHandle amqMessageHandle = (new MessageHandleFactory()).createMessageHandle(messageId, + null, + false); + try + { + amqMessageHandle.setPublishAndContentHeaderBody(new StoreContext(), + publishBody, + new ContentHeaderBody() + { + public int getSize() + { + return 1; + } + }); + } + catch (AMQException e) + { + // won't happen + } + + + return amqMessageHandle; + } + + public class TestMessage extends AMQMessage + { + private final long _tag; + private int _count; + + TestMessage(long tag, long messageId, MessagePublishInfo publishBody, StoreContext storeContext) + throws AMQException + { + super(createMessageHandle(messageId, publishBody), storeContext, publishBody); + _tag = tag; + } + + + public boolean incrementReference() + { + _count++; + return true; + } + + public void decrementReference(StoreContext context) + { + _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, new StoreContext()); + return messageA; + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueThreadPoolTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueThreadPoolTest.java new file mode 100644 index 0000000000..832df80004 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueThreadPoolTest.java @@ -0,0 +1,60 @@ +/* + * + * 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.virtualhost.VirtualHost; + +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.AMQException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SimpleAMQQueueThreadPoolTest extends TestCase +{ + + public void test() throws AMQException + { + int initialCount = ReferenceCountingExecutorService.getInstance().getReferenceCount(); + VirtualHost test = ApplicationRegistry.getInstance(1).getVirtualHostRegistry().getVirtualHost("test"); + + try + { + SimpleAMQQueue queue = (SimpleAMQQueue) AMQQueueFactory.createAMQQueueImpl(new AMQShortString("test"), false, + new AMQShortString("owner"), + 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(1); + } + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/registry/ApplicationRegistryShutdownTest.java b/java/broker/src/test/java/org/apache/qpid/server/registry/ApplicationRegistryShutdownTest.java new file mode 100644 index 0000000000..939e3436a5 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/registry/ApplicationRegistryShutdownTest.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.registry; + +import junit.framework.TestCase; +import org.apache.qpid.server.util.TestApplicationRegistry; + +import java.security.Security; +import java.security.Provider; +import java.util.List; +import java.util.LinkedList; + +/** + * QPID-1390 : Test to validate that the AuthenticationManger succesfully unregisters any new SASL providers when + * The ApplicationRegistry is closed. + * + * This should be expanded as QPID-1399 is implemented. + */ +public class ApplicationRegistryShutdownTest extends TestCase +{ + + ApplicationRegistry _registry; + + public void setUp() throws Exception + { + _registry = new TestApplicationRegistry(); + } + + /** + * QPID-1399 : Ensure that the Authentiction manager unregisters any SASL providers created during + * ApplicationRegistry initialisation. + * + */ + public void testAuthenticationMangerCleansUp() + { + // Get default providers + Provider[] defaultProviders = Security.getProviders(); + + // Register new providers + try + { + _registry.initialise(); + } + catch (Exception e) + { + fail(e.getMessage()); + } + + // 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 + try + { + _registry.close(); + } + catch (Exception e) + { + fail(e.getMessage()); + } + + //Validate that the SASL plugins 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/java/broker/src/test/java/org/apache/qpid/server/security/access/ACLManagerTest.java b/java/broker/src/test/java/org/apache/qpid/server/security/access/ACLManagerTest.java new file mode 100644 index 0000000000..6c6835ccca --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/security/access/ACLManagerTest.java @@ -0,0 +1,99 @@ +/* + * 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.access; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; + +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.commons.configuration.XMLConfiguration; +import org.apache.qpid.server.configuration.SecurityConfiguration; +import org.apache.qpid.server.configuration.ServerConfiguration; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.plugins.MockPluginManager; +import org.apache.qpid.server.plugins.PluginManager; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.MockAMQQueue; +import org.apache.qpid.server.queue.MockProtocolSession; +import org.apache.qpid.server.store.TestableMemoryMessageStore; + +public class ACLManagerTest extends TestCase +{ + + private ACLManager _authzManager; + private AMQProtocolSession _session; + private SecurityConfiguration _conf; + private PluginManager _pluginManager; + + @Override + public void setUp() throws Exception + { + File tmpFile = File.createTempFile(getClass().getName(), "testconfig"); + tmpFile.deleteOnExit(); + BufferedWriter out = new BufferedWriter(new FileWriter(tmpFile)); + out.write("<security><queueDenier>notyet</queueDenier><exchangeDenier>yes</exchangeDenier></security>"); + out.close(); + + _conf = new SecurityConfiguration(new XMLConfiguration(tmpFile)); + + // Create ACLManager + + _pluginManager = new MockPluginManager(""); + _authzManager = new ACLManager(_conf, _pluginManager); + + _session = new MockProtocolSession(new TestableMemoryMessageStore()); + } + + public void testACLManagerConfigurationPluginManager() throws Exception + { + AMQQueue queue = new MockAMQQueue("notyet"); + AMQQueue otherQueue = new MockAMQQueue("other"); + + assertFalse(_authzManager.authoriseDelete(_session, queue)); + + // This should only be denied if the config hasn't been correctly passed in + assertTrue(_authzManager.authoriseDelete(_session, otherQueue)); + assertTrue(_authzManager.authorisePurge(_session, queue)); + } + + public void testACLManagerConfigurationPluginManagerACLPlugin() throws ConfigurationException + { + _authzManager = new ACLManager(_conf, _pluginManager, ExchangeDenier.FACTORY); + + Exchange exchange = null; + assertFalse(_authzManager.authoriseDelete(_session, exchange)); + } + + public void testConfigurePlugins() throws ConfigurationException + { + Configuration hostConfig = new PropertiesConfiguration(); + hostConfig.setProperty("queueDenier", "thisoneneither"); + _authzManager.configureHostPlugins(new SecurityConfiguration(hostConfig)); + AMQQueue queue = new MockAMQQueue("thisoneneither"); + assertFalse(_authzManager.authoriseDelete(_session, queue)); + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/security/access/ExchangeDenier.java b/java/broker/src/test/java/org/apache/qpid/server/security/access/ExchangeDenier.java new file mode 100644 index 0000000000..f62b0c6241 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/security/access/ExchangeDenier.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.security.access; + +import org.apache.commons.configuration.Configuration; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.security.access.plugins.AllowAll; + +public class ExchangeDenier extends AllowAll +{ + + public static final ACLPluginFactory FACTORY = new ACLPluginFactory() + { + public boolean supportsTag(String name) + { + return name.startsWith("exchangeDenier"); + } + + public ACLPlugin newInstance(Configuration config) + { + return new ExchangeDenier(); + } + }; + + @Override + public AuthzResult authoriseDelete(AMQProtocolSession session, Exchange exchange) + { + return AuthzResult.DENIED; + } + + @Override + public String getPluginName() + { + return getClass().getSimpleName(); + } + + @Override + public boolean supportsTag(String name) + { + return name.equals("exchangeDenier"); + } + +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/security/access/PrincipalPermissionsTest.java b/java/broker/src/test/java/org/apache/qpid/server/security/access/PrincipalPermissionsTest.java new file mode 100644 index 0000000000..e1e93ae9cb --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/security/access/PrincipalPermissionsTest.java @@ -0,0 +1,156 @@ +/* + * + * 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.access; + +import junit.framework.TestCase; + +import org.apache.commons.configuration.PropertiesConfiguration; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.amqp_0_9.ExchangeDeclareBodyImpl; +import org.apache.qpid.framing.amqp_0_9.QueueDeclareBodyImpl; +import org.apache.qpid.framing.amqp_8_0.QueueBindBodyImpl; +import org.apache.qpid.server.configuration.VirtualHostConfiguration; +import org.apache.qpid.server.exchange.DirectExchange; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.AMQQueueFactory; +import org.apache.qpid.server.security.access.ACLPlugin.AuthzResult; +import org.apache.qpid.server.store.SkeletonMessageStore; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public class PrincipalPermissionsTest extends TestCase +{ + + private String _user = "user"; + private PrincipalPermissions _perms; + + // Common things that are passed to frame constructors + private AMQShortString _queueName = new AMQShortString(this.getClass().getName()+"queue"); + private AMQShortString _exchangeName = new AMQShortString("amq.direct"); + private AMQShortString _routingKey = new AMQShortString(this.getClass().getName()+"route"); + private int _ticket = 1; + private FieldTable _arguments = null; + private boolean _nowait = false; + private boolean _passive = false; + private boolean _durable = false; + private boolean _autoDelete = false; + private AMQShortString _exchangeType = new AMQShortString("direct"); + private boolean _internal = false; + + private DirectExchange _exchange; + private VirtualHost _virtualHost; + private AMQShortString _owner = new AMQShortString(this.getClass().getName()+"owner"); + private AMQQueue _queue; + private Boolean _temporary = false; + + @Override + public void setUp() + { + _perms = new PrincipalPermissions(_user); + try + { + PropertiesConfiguration env = new PropertiesConfiguration(); + _virtualHost = new VirtualHost(new VirtualHostConfiguration("test", env)); + _exchange = DirectExchange.TYPE.newInstance(_virtualHost, _exchangeName, _durable, _ticket, _autoDelete); + _queue = AMQQueueFactory.createAMQQueueImpl(_queueName, false, _owner , false, _virtualHost, _arguments); + } + catch (Exception e) + { + fail(e.getMessage()); + } + } + + public void testPrincipalPermissions() + { + assertNotNull(_perms); + assertEquals(AuthzResult.ALLOWED, _perms.authorise(Permission.ACCESS, (Object[]) null)); + } + + // FIXME: test has been disabled since the permissions assume that the user has tried to create + // the queue first. QPID-1597 + public void disableTestBind() throws Exception + { + QueueBindBodyImpl bind = new QueueBindBodyImpl(_ticket, _queueName, _exchangeName, _routingKey, _nowait, _arguments); + Object[] args = new Object[]{bind, _exchange, _queue, _routingKey}; + + assertEquals(AuthzResult.DENIED, _perms.authorise(Permission.BIND, args)); + _perms.grant(Permission.BIND, (Object[]) null); + assertEquals(AuthzResult.ALLOWED, _perms.authorise(Permission.BIND, args)); + } + + public void testQueueCreate() + { + Object[] grantArgs = new Object[]{_temporary , _queueName, _exchangeName, _routingKey}; + Object[] authArgs = new Object[]{_autoDelete, _queueName}; + + assertEquals(AuthzResult.DENIED, _perms.authorise(Permission.CREATEQUEUE, authArgs)); + _perms.grant(Permission.CREATEQUEUE, grantArgs); + assertEquals(AuthzResult.ALLOWED, _perms.authorise(Permission.CREATEQUEUE, authArgs)); + } + + public void testQueueCreateWithNullRoutingKey() + { + Object[] grantArgs = new Object[]{_temporary , _queueName, _exchangeName, null}; + Object[] authArgs = new Object[]{_autoDelete, _queueName}; + + assertEquals(AuthzResult.DENIED, _perms.authorise(Permission.CREATEQUEUE, authArgs)); + _perms.grant(Permission.CREATEQUEUE, grantArgs); + assertEquals(AuthzResult.ALLOWED, _perms.authorise(Permission.CREATEQUEUE, authArgs)); + } + + // FIXME disabled, this fails due to grant putting the grant into the wrong map QPID-1598 + public void disableTestExchangeCreate() + { + ExchangeDeclareBodyImpl exchangeDeclare = + new ExchangeDeclareBodyImpl(_ticket, _exchangeName, _exchangeType, _passive, _durable, + _autoDelete, _internal, _nowait, _arguments); + Object[] authArgs = new Object[]{exchangeDeclare}; + Object[] grantArgs = new Object[]{_exchangeName, _exchangeType}; + + assertEquals(AuthzResult.DENIED, _perms.authorise(Permission.CREATEEXCHANGE, authArgs)); + _perms.grant(Permission.CREATEEXCHANGE, grantArgs); + assertEquals(AuthzResult.ALLOWED, _perms.authorise(Permission.CREATEEXCHANGE, authArgs)); + } + + public void testConsume() + { + Object[] authArgs = new Object[]{_queue}; + Object[] grantArgs = new Object[]{_queueName, _temporary, _temporary}; + + /* FIXME: This throws a null pointer exception QPID-1599 + * assertFalse(_perms.authorise(Permission.CONSUME, authArgs)); + */ + _perms.grant(Permission.CONSUME, grantArgs); + assertEquals(AuthzResult.ALLOWED, _perms.authorise(Permission.CONSUME, authArgs)); + } + + public void testPublish() + { + Object[] authArgs = new Object[]{_exchange, _routingKey}; + Object[] grantArgs = new Object[]{_exchange.getName(), _routingKey}; + + assertEquals(AuthzResult.DENIED, _perms.authorise(Permission.PUBLISH, authArgs)); + _perms.grant(Permission.PUBLISH, grantArgs); + assertEquals(AuthzResult.ALLOWED, _perms.authorise(Permission.PUBLISH, authArgs)); + } + +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/security/access/QueueDenier.java b/java/broker/src/test/java/org/apache/qpid/server/security/access/QueueDenier.java new file mode 100644 index 0000000000..5497f0ae44 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/security/access/QueueDenier.java @@ -0,0 +1,68 @@ +/* + * 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.access; + +import org.apache.commons.configuration.Configuration; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.security.access.ACLPlugin.AuthzResult; +import org.apache.qpid.server.security.access.plugins.AllowAll; + +public class QueueDenier extends AllowAll +{ + + public static final ACLPluginFactory FACTORY = new ACLPluginFactory() + { + public boolean supportsTag(String name) + { + return name.equals("queueDenier"); + } + + public ACLPlugin newInstance(Configuration config) + { + QueueDenier plugin = new QueueDenier(); + plugin.setConfiguration(config); + return plugin; + } + }; + + private String _queueName = ""; + + + @Override + public AuthzResult authoriseDelete(AMQProtocolSession session, AMQQueue queue) + { + if (!(queue.getName().toString().equals(_queueName))) + { + return AuthzResult.ALLOWED; + } + else + { + return AuthzResult.DENIED; + } + } + + @Override + public void setConfiguration(Configuration config) + { + _queueName = config.getString("queueDenier"); + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/security/access/management/AMQUserManagementMBeanTest.java b/java/broker/src/test/java/org/apache/qpid/server/security/access/management/AMQUserManagementMBeanTest.java new file mode 100644 index 0000000000..958ee35476 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/security/access/management/AMQUserManagementMBeanTest.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.security.access.management; + +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 javax.management.MalformedObjectNameException; +import javax.management.ObjectName; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.qpid.server.management.MBeanInvocationHandlerImpl; +import org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase; + +import junit.framework.TestCase; + +/* Note: The main purpose is to test the jmx access rights file manipulation + * within AMQUserManagementMBean. The Principal Databases are tested by their own tests, + * this test just exercises their usage in AMQUserManagementMBean. + */ +public class AMQUserManagementMBeanTest extends TestCase +{ + private PlainPasswordFilePrincipalDatabase _database; + private AMQUserManagementMBean _amqumMBean; + + private File _passwordFile; + private File _accessFile; + + private static final String TEST_USERNAME = "testuser"; + private static final String TEST_PASSWORD = "password"; + + @Override + protected void setUp() throws Exception + { + _database = new PlainPasswordFilePrincipalDatabase(); + _amqumMBean = new AMQUserManagementMBean(); + loadFreshTestPasswordFile(); + loadFreshTestAccessFile(); + } + + @Override + protected void tearDown() throws Exception + { + _passwordFile.delete(); + _accessFile.delete(); + } + + public void testDeleteUser() + { + loadFreshTestPasswordFile(); + loadFreshTestAccessFile(); + + //try deleting a non existant user + assertFalse(_amqumMBean.deleteUser("made.up.username")); + + assertTrue(_amqumMBean.deleteUser(TEST_USERNAME)); + } + + public void testDeleteUserIsSavedToAccessFile() + { + loadFreshTestPasswordFile(); + loadFreshTestAccessFile(); + + assertTrue(_amqumMBean.deleteUser(TEST_USERNAME)); + + //check the access rights were actually deleted from the file + try{ + BufferedReader reader = new BufferedReader(new FileReader(_accessFile)); + + //check the 'generated by' comment line is present + assertTrue("File has no content", reader.ready()); + assertTrue("'Generated by' comment line was missing",reader.readLine().contains("Generated by " + + "AMQUserManagementMBean Console : Last edited by user:")); + + //there should also be a modified date/time comment line + assertTrue("File has no modified date/time comment line", reader.ready()); + assertTrue("Modification date/time comment line was missing",reader.readLine().startsWith("#")); + + //the access file should not contain any further data now as we just deleted the only user + assertFalse("User access data was present when it should have been deleted", reader.ready()); + } + catch (IOException e) + { + fail("Unable to valdate file contents due to:" + e.getMessage()); + } + + } + + public void testSetRights() + { + loadFreshTestPasswordFile(); + loadFreshTestAccessFile(); + + assertFalse(_amqumMBean.setRights("made.up.username", true, false, false)); + + assertTrue(_amqumMBean.setRights(TEST_USERNAME, true, false, false)); + assertTrue(_amqumMBean.setRights(TEST_USERNAME, false, true, false)); + assertTrue(_amqumMBean.setRights(TEST_USERNAME, false, false, true)); + } + + public void testSetRightsIsSavedToAccessFile() + { + loadFreshTestPasswordFile(); + loadFreshTestAccessFile(); + + assertTrue(_amqumMBean.setRights(TEST_USERNAME, false, false, true)); + + //check the access rights were actually updated in the file + try{ + BufferedReader reader = new BufferedReader(new FileReader(_accessFile)); + + //check the 'generated by' comment line is present + assertTrue("File has no content", reader.ready()); + assertTrue("'Generated by' comment line was missing",reader.readLine().contains("Generated by " + + "AMQUserManagementMBean Console : Last edited by user:")); + + //there should also be a modified date/time comment line + assertTrue("File has no modified date/time comment line", reader.ready()); + assertTrue("Modification date/time comment line was missing",reader.readLine().startsWith("#")); + + //the access file should not contain any further data now as we just deleted the only user + assertTrue("User access data was not updated in the access file", + reader.readLine().equals(TEST_USERNAME + "=" + MBeanInvocationHandlerImpl.ADMIN)); + + //the access file should not contain any further data now as we just deleted the only user + assertFalse("Additional user access data was present when there should be no more", reader.ready()); + } + catch (IOException e) + { + fail("Unable to valdate file contents due to:" + e.getMessage()); + } + } + + public void testMBeanVersion() + { + try + { + ObjectName name = _amqumMBean.getObjectName(); + assertEquals(AMQUserManagementMBean.VERSION, Integer.parseInt(name.getKeyProperty("version"))); + } + catch (MalformedObjectNameException e) + { + fail(e.getMessage()); + } + } + + public void testSetAccessFileWithMissingFile() + { + try + { + _amqumMBean.setAccessFile("made.up.filename"); + } + catch (IOException e) + { + fail("Should not have been an IOE." + e.getMessage()); + } + catch (ConfigurationException e) + { + assertTrue(e.getMessage(), e.getMessage().endsWith("does not exist")); + } + } + + public void testSetAccessFileWithReadOnlyFile() + { + File testFile = null; + try + { + testFile = File.createTempFile(this.getClass().getName(),".access.readonly"); + BufferedWriter passwordWriter = new BufferedWriter(new FileWriter(testFile, false)); + passwordWriter.write(TEST_USERNAME + ":" + TEST_PASSWORD); + passwordWriter.newLine(); + passwordWriter.flush(); + passwordWriter.close(); + + testFile.setReadOnly(); + _amqumMBean.setAccessFile(testFile.getPath()); + } + catch (IOException e) + { + fail("Access file was not created." + e.getMessage()); + } + catch (ConfigurationException e) + { + fail("There should not have been a configuration exception." + e.getMessage()); + } + + testFile.delete(); + } + + // ============================ 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()); + } + } + + private void loadFreshTestAccessFile() + { + try + { + if(_accessFile == null) + { + _accessFile = File.createTempFile(this.getClass().getName(),".access"); + } + + BufferedWriter accessWriter = new BufferedWriter(new FileWriter(_accessFile,false)); + accessWriter.write("#Last Updated By comment"); + accessWriter.newLine(); + accessWriter.write("#Date/time comment"); + accessWriter.newLine(); + accessWriter.write(TEST_USERNAME + "=" + MBeanInvocationHandlerImpl.READONLY); + accessWriter.newLine(); + accessWriter.flush(); + accessWriter.close(); + } + catch (IOException e) + { + fail("Unable to create test access file: " + e.getMessage()); + } + + try{ + _amqumMBean.setAccessFile(_accessFile.toString()); + } + catch (Exception e) + { + fail("Unable to set access file: " + e.getMessage()); + } + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/security/access/plugins/network/FirewallPluginTest.java b/java/broker/src/test/java/org/apache/qpid/server/security/access/plugins/network/FirewallPluginTest.java new file mode 100644 index 0000000000..ff1fb8c97d --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/security/access/plugins/network/FirewallPluginTest.java @@ -0,0 +1,295 @@ +/* + * + * 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.access.plugins.network; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.net.InetSocketAddress; + +import junit.framework.TestCase; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.commons.configuration.PropertiesConfiguration; +import org.apache.commons.configuration.XMLConfiguration; +import org.apache.qpid.codec.AMQCodecFactory; +import org.apache.qpid.server.configuration.VirtualHostConfiguration; +import org.apache.qpid.server.protocol.AMQMinaProtocolSession; +import org.apache.qpid.server.protocol.TestIoSession; +import org.apache.qpid.server.security.access.ACLPlugin.AuthzResult; +import org.apache.qpid.server.store.TestableMemoryMessageStore; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.virtualhost.VirtualHostRegistry; + +public class FirewallPluginTest extends TestCase +{ + + public class RuleInfo + { + private String _access; + private String _network; + private String _hostname; + + public void setAccess(String _access) + { + this._access = _access; + } + + public String getAccess() + { + return _access; + } + + public void setNetwork(String _network) + { + this._network = _network; + } + + public String getNetwork() + { + return _network; + } + + public void setHostname(String _hostname) + { + this._hostname = _hostname; + } + + public String getHostname() + { + return _hostname; + } + } + + private TestableMemoryMessageStore _store; + private VirtualHost _virtualHost; + private AMQMinaProtocolSession _session; + + @Override + public void setUp() throws Exception + { + _store = new TestableMemoryMessageStore(); + PropertiesConfiguration env = new PropertiesConfiguration(); + _virtualHost = new VirtualHost(new VirtualHostConfiguration("test", env)); + TestIoSession iosession = new TestIoSession(); + iosession.setAddress("127.0.0.1"); + VirtualHostRegistry virtualHostRegistry = null; + AMQCodecFactory codecFactory = new AMQCodecFactory(true); + _session = new AMQMinaProtocolSession(iosession, virtualHostRegistry, codecFactory); + } + + private FirewallPlugin initialisePlugin(String defaultAction, RuleInfo[] rules) throws IOException, ConfigurationException + { + // Create sample config file + File confFile = File.createTempFile(getClass().getSimpleName()+"conffile", null); + confFile.deleteOnExit(); + BufferedWriter buf = new BufferedWriter(new FileWriter(confFile)); + buf.write("<firewall default-action=\""+defaultAction+"\">\n"); + if (rules != null) + { + for (RuleInfo rule : rules) + { + buf.write("<rule"); + buf.write(" access=\""+rule.getAccess()+"\""); + if (rule.getHostname() != null) + { + buf.write(" hostname=\""+rule.getHostname()+"\""); + } + if (rule.getNetwork() != null) + { + buf.write(" network=\""+rule.getNetwork()+"\""); + } + buf.write("/>\n"); + } + } + buf.write("</firewall>"); + buf.close(); + + // Configure plugin + FirewallPlugin plugin = new FirewallPlugin(); + plugin.setConfiguration(new XMLConfiguration(confFile)); + return plugin; + } + + private FirewallPlugin initialisePlugin(String string) throws ConfigurationException, IOException + { + return initialisePlugin(string, null); + } + + public void testDefaultAction() throws Exception + { + // Test simple deny + FirewallPlugin plugin = initialisePlugin("deny"); + assertEquals(AuthzResult.DENIED, plugin.authoriseConnect(_session, _virtualHost)); + + // Test simple allow + plugin = initialisePlugin("allow"); + assertEquals(AuthzResult.ALLOWED, plugin.authoriseConnect(_session, _virtualHost)); + } + + + public void testSingleIPRule() throws Exception + { + RuleInfo rule = new RuleInfo(); + rule.setAccess("allow"); + rule.setNetwork("192.168.23.23"); + + FirewallPlugin plugin = initialisePlugin("deny", new RuleInfo[]{rule}); + + assertEquals(AuthzResult.DENIED, plugin.authoriseConnect(_session, _virtualHost)); + + // Set session IP so that we're connected from the right address + ((TestIoSession) _session.getIOSession()).setAddress("192.168.23.23"); + assertEquals(AuthzResult.ALLOWED, plugin.authoriseConnect(_session, _virtualHost)); + } + + public void testSingleNetworkRule() throws Exception + { + RuleInfo rule = new RuleInfo(); + rule.setAccess("allow"); + rule.setNetwork("192.168.23.0/24"); + + FirewallPlugin plugin = initialisePlugin("deny", new RuleInfo[]{rule}); + + assertEquals(AuthzResult.DENIED, plugin.authoriseConnect(_session, _virtualHost)); + + // Set session IP so that we're connected from the right address + ((TestIoSession) _session.getIOSession()).setAddress("192.168.23.23"); + assertEquals(AuthzResult.ALLOWED, plugin.authoriseConnect(_session, _virtualHost)); + } + + public void testSingleHostRule() throws Exception + { + RuleInfo rule = new RuleInfo(); + rule.setAccess("allow"); + rule.setHostname(new InetSocketAddress("127.0.0.1", 5672).getHostName()); + + FirewallPlugin plugin = initialisePlugin("deny", new RuleInfo[]{rule}); + + // Set session IP so that we're connected from the right address + ((TestIoSession) _session.getIOSession()).setAddress("127.0.0.1"); + assertEquals(AuthzResult.ALLOWED, plugin.authoriseConnect(_session, _virtualHost)); + } + + public void testSingleHostWilcardRule() throws Exception + { + RuleInfo rule = new RuleInfo(); + rule.setAccess("allow"); + rule.setHostname(".*ocal.*"); + + FirewallPlugin plugin = initialisePlugin("deny", new RuleInfo[]{rule}); + + // Set session IP so that we're connected from the right address + ((TestIoSession) _session.getIOSession()).setAddress("127.0.0.1"); + assertEquals(AuthzResult.ALLOWED, plugin.authoriseConnect(_session, _virtualHost)); + } + + public void testSeveralFirstAllowsAccess() throws Exception + { + RuleInfo firstRule = new RuleInfo(); + firstRule.setAccess("allow"); + firstRule.setNetwork("192.168.23.23"); + + RuleInfo secondRule = new RuleInfo(); + secondRule.setAccess("deny"); + secondRule.setNetwork("192.168.42.42"); + + RuleInfo thirdRule = new RuleInfo(); + thirdRule.setAccess("deny"); + thirdRule.setHostname("localhost"); + + FirewallPlugin plugin = initialisePlugin("deny", new RuleInfo[]{firstRule, secondRule, thirdRule}); + + assertEquals(AuthzResult.DENIED, plugin.authoriseConnect(_session, _virtualHost)); + + // Set session IP so that we're connected from the right address + ((TestIoSession) _session.getIOSession()).setAddress("192.168.23.23"); + assertEquals(AuthzResult.ALLOWED, plugin.authoriseConnect(_session, _virtualHost)); + } + + public void testSeveralLastAllowsAccess() throws Exception + { + RuleInfo firstRule = new RuleInfo(); + firstRule.setAccess("deny"); + firstRule.setHostname("localhost"); + + RuleInfo secondRule = new RuleInfo(); + secondRule.setAccess("deny"); + secondRule.setNetwork("192.168.42.42"); + + RuleInfo thirdRule = new RuleInfo(); + thirdRule.setAccess("allow"); + thirdRule.setNetwork("192.168.23.23"); + + FirewallPlugin plugin = initialisePlugin("deny", new RuleInfo[]{firstRule, secondRule, thirdRule}); + + assertEquals(AuthzResult.DENIED, plugin.authoriseConnect(_session, _virtualHost)); + + // Set session IP so that we're connected from the right address + ((TestIoSession) _session.getIOSession()).setAddress("192.168.23.23"); + assertEquals(AuthzResult.ALLOWED, plugin.authoriseConnect(_session, _virtualHost)); + } + + public void testNetmask() throws Exception + { + RuleInfo firstRule = new RuleInfo(); + firstRule.setAccess("allow"); + firstRule.setNetwork("192.168.23.0/24"); + FirewallPlugin plugin = initialisePlugin("deny", new RuleInfo[]{firstRule}); + + assertEquals(AuthzResult.DENIED, plugin.authoriseConnect(_session, _virtualHost)); + + // Set session IP so that we're connected from the right address + ((TestIoSession) _session.getIOSession()).setAddress("192.168.23.23"); + assertEquals(AuthzResult.ALLOWED, plugin.authoriseConnect(_session, _virtualHost)); + } + + public void testCommaSeperatedNetmask() throws Exception + { + RuleInfo firstRule = new RuleInfo(); + firstRule.setAccess("allow"); + firstRule.setNetwork("10.1.1.1/8, 192.168.23.0/24"); + FirewallPlugin plugin = initialisePlugin("deny", new RuleInfo[]{firstRule}); + + assertEquals(AuthzResult.DENIED, plugin.authoriseConnect(_session, _virtualHost)); + + // Set session IP so that we're connected from the right address + ((TestIoSession) _session.getIOSession()).setAddress("192.168.23.23"); + assertEquals(AuthzResult.ALLOWED, plugin.authoriseConnect(_session, _virtualHost)); + } + + public void testCommaSeperatedHostnames() throws Exception + { + RuleInfo firstRule = new RuleInfo(); + firstRule.setAccess("allow"); + firstRule.setHostname("foo, bar, "+new InetSocketAddress("127.0.0.1", 5672).getHostName()); + FirewallPlugin plugin = initialisePlugin("deny", new RuleInfo[]{firstRule}); + ((TestIoSession) _session.getIOSession()).setAddress("10.0.0.1"); + assertEquals(AuthzResult.DENIED, plugin.authoriseConnect(_session, _virtualHost)); + + // Set session IP so that we're connected from the right address + ((TestIoSession) _session.getIOSession()).setAddress("127.0.0.1"); + assertEquals(AuthzResult.ALLOWED, plugin.authoriseConnect(_session, _virtualHost)); + } + +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabaseTest.java b/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabaseTest.java new file mode 100644 index 0000000000..413b974986 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabaseTest.java @@ -0,0 +1,447 @@ +/* + * + * 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.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; + + 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()); + } + + 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(); + + 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())); + + testFile.delete(); + } + + 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()); + } + testFile.delete(); + } + + + 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")); + + 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(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(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()); + } + 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(); + } + + 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/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/HashedUserTest.java b/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/HashedUserTest.java new file mode 100644 index 0000000000..aa85cac758 --- /dev/null +++ b/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/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabaseTest.java b/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabaseTest.java new file mode 100644 index 0000000000..20b8d0a7b4 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabaseTest.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.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.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; + + public void setUp() throws Exception + { + _database = new PlainPasswordFilePrincipalDatabase(); + } + + // ******* 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(); + + 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/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/PlainUserTest.java b/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/PlainUserTest.java new file mode 100644 index 0000000000..7f0843d46e --- /dev/null +++ b/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/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java b/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java new file mode 100644 index 0000000000..e8c24da68d --- /dev/null +++ b/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/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/SaslServerTestCase.java b/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/SaslServerTestCase.java new file mode 100644 index 0000000000..f80413d4f8 --- /dev/null +++ b/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/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/TestPrincipalDatabase.java b/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/TestPrincipalDatabase.java new file mode 100644 index 0000000000..8507e49e17 --- /dev/null +++ b/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/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/amqplain/AMQPlainSaslServerTest.java b/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/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/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServerTest.java b/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/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/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreShutdownTest.java b/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreShutdownTest.java new file mode 100644 index 0000000000..a695a67eea --- /dev/null +++ b/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(_session, _channel, _queue); + + try + { + publishMessages(_session, _channel, 1); + } + catch (AMQException e) + { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + fail(e.getMessage()); + } + + try + { + _registry.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", _session.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 = _session.getDelivers(_channel.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. + _channel.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/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreTest.java b/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreTest.java new file mode 100644 index 0000000000..4317a501ed --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreTest.java @@ -0,0 +1,646 @@ +/* + * + * 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 junit.framework.TestCase; + +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.ExchangeType; +import org.apache.qpid.server.exchange.TopicExchange; +import org.apache.qpid.server.exchange.ExchangeRegistry; +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.queue.IncomingMessage; +import org.apache.qpid.server.queue.MessageHandleFactory; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.queue.AMQPriorityQueue; +import org.apache.qpid.server.queue.SimpleAMQQueue; +import org.apache.qpid.server.queue.ExchangeBinding; +import org.apache.qpid.server.txn.NonTransactionalContext; +import org.apache.qpid.server.protocol.InternalTestProtocolSession; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.BasicContentHeaderProperties; +import org.apache.qpid.framing.amqp_8_0.BasicConsumeBodyImpl; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.AMQException; +import org.apache.qpid.common.AMQPFilterTypes; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.PropertiesConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.util.List; + +/** + * This tests the MessageStores by using the available interfaces. + * + * This test validates that Exchanges, Queues, Bindings and Messages are persisted correctly. + */ +public class MessageStoreTest extends TestCase +{ + + private static final int DEFAULT_PRIORTY_LEVEL = 5; + private static final Logger _logger = LoggerFactory.getLogger(MessageStoreTest.class); + + public void testMemoryMessageStore() + { + + PropertiesConfiguration config = new PropertiesConfiguration(); + + config.addProperty("store.class", "org.apache.qpid.server.store.MemoryMessageStore"); + + runTestWithStore(config); + } + + public void DISABLE_testDerbyMessageStore() + { + PropertiesConfiguration config = new PropertiesConfiguration(); + + config.addProperty("store.environment-path", "derbyDB_MST"); + config.addProperty("store.class", "org.apache.qpid.server.store.DerbyMessageStore"); + + runTestWithStore(config); + } + + private void reload(Configuration configuration) + { + if (_virtualHost != null) + { + try + { + _virtualHost.close(); + } + catch (Exception e) + { + fail(e.getMessage()); + } + } + + try + { + _virtualHost = new VirtualHost(new VirtualHostConfiguration(getClass().getName(), configuration)); + ApplicationRegistry.getInstance().getVirtualHostRegistry().registerVirtualHost(_virtualHost); + } + catch (Exception e) + { + e.printStackTrace(); + fail(e.getMessage()); + } + } + + VirtualHost _virtualHost = null; + String virtualHostName = "MessageStoreTest"; + + AMQShortString nonDurableExchangeName = new AMQShortString("MST-NonDurableDirectExchange"); + AMQShortString directExchangeName = new AMQShortString("MST-DirectExchange"); + AMQShortString topicExchangeName = new AMQShortString("MST-TopicExchange"); + AMQShortString queueOwner = new AMQShortString("MST"); + + 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 durablePriorityQueueName = new AMQShortString("MST-PriorityQueue-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"); + + protected void setUp() + { + ApplicationRegistry.getInstance(1); + } + + protected void tearDown() + { + ApplicationRegistry.remove(1); + } + + protected void runTestWithStore(Configuration configuration) + { + //Ensure Environment Path is empty + cleanup(configuration); + + //Load the Virtualhost with the required MessageStore + reload(configuration); + + MessageStore messageStore = _virtualHost.getMessageStore(); + + 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", 8, _virtualHost.getQueueRegistry().getQueues().size()); + + if (!messageStore.isPersistent()) + { + _logger.warn("Unable to test Persistent capabilities of messages store(" + messageStore.getClass() + ") as it is not capable of peristence."); + return; + } + + //Reload the Virtualhost to test persistence + _logger.info("Reloading Virtualhost"); + + VirtualHost original = _virtualHost; + + reload(configuration); + + assertTrue("Virtualhost has not been reloaded", original != _virtualHost); + + validateExchanges(); + + //Validate Durable Queues still have the persistentn message + validateMessageOnQueues(2, false); + //Validate Durable Queues still have the persistentn message + validateMessageOnTopics(1, false); + + //Validate Properties of Binding + validateBindingProperties(); + + //Validate Properties of Queues + validateQueueProperties(); + + //Validate Non-Durable Queues are gone. + assertNull("Non-Durable queue still registered:" + priorityQueueName, _virtualHost.getQueueRegistry().getQueue(priorityQueueName)); + assertNull("Non-Durable queue still registered:" + queueName, _virtualHost.getQueueRegistry().getQueue(queueName)); + assertNull("Non-Durable queue still registered:" + priorityTopicQueueName, _virtualHost.getQueueRegistry().getQueue(priorityTopicQueueName)); + assertNull("Non-Durable queue still registered:" + topicQueueName, _virtualHost.getQueueRegistry().getQueue(topicQueueName)); + + assertEquals("Not all queues correctly registered", 4, _virtualHost.getQueueRegistry().getQueues().size()); + } + + private void validateExchanges() + { + ExchangeRegistry registry = _virtualHost.getExchangeRegistry(); + + assertTrue(directExchangeName + " exchange NOT reloaded after failover", + registry.getExchangeNames().contains(directExchangeName)); + assertTrue(topicExchangeName + " exchange NOT reloaded after failover", + registry.getExchangeNames().contains(topicExchangeName)); + assertTrue(nonDurableExchangeName + " exchange reloaded after failover", + !registry.getExchangeNames().contains(nonDurableExchangeName)); + + // There are 5 required exchanges + our 2 durable queues + assertEquals("Incorrect number of exchanges available", 5 + 2, registry.getExchangeNames().size()); + } + + /** Validates that the Durable queues */ + private void validateBindingProperties() + { + QueueRegistry queueRegistry = _virtualHost.getQueueRegistry(); + + validateBindingProperties(queueRegistry.getQueue(durablePriorityQueueName).getExchangeBindings(), false); + validateBindingProperties(queueRegistry.getQueue(durablePriorityTopicQueueName).getExchangeBindings(), true); + validateBindingProperties(queueRegistry.getQueue(durableQueueName).getExchangeBindings(), false); + validateBindingProperties(queueRegistry.getQueue(durableTopicQueueName).getExchangeBindings(), true); + } + + /** + * Validate that each queue is bound once. + * + * @param bindings the set of bindings to validate + * @param useSelectors if set validate that the binding has a JMS_SELECTOR argument + */ + private void validateBindingProperties(List<ExchangeBinding> bindings, boolean useSelectors) + { + assertEquals("Each queue should only be bound once.", 1, bindings.size()); + + ExchangeBinding binding = bindings.get(0); + + if (useSelectors) + { + assertTrue("Binding does not contain a Selector argument.", + binding.getArguments().containsKey(AMQPFilterTypes.JMS_SELECTOR.getValue())); + } + } + + private void validateQueueProperties() + { + QueueRegistry queueRegistry = _virtualHost.getQueueRegistry(); + + validateQueueProperties(queueRegistry.getQueue(durablePriorityQueueName), true); + validateQueueProperties(queueRegistry.getQueue(durablePriorityTopicQueueName), true); + validateQueueProperties(queueRegistry.getQueue(durableQueueName), false); + validateQueueProperties(queueRegistry.getQueue(durableTopicQueueName), false); + + } + + private void validateQueueProperties(AMQQueue queue, boolean usePriority) + { + 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 + { + assertEquals("Queue is no longer a Priority Queue", SimpleAMQQueue.class, queue.getClass()); + } + } + + /** + * Delete the Store Environment path + * + * @param configuration The configuration that contains the store environment path. + */ + private void cleanup(Configuration configuration) + { + + String environment = configuration.getString("store.environment-path"); + + if (environment != null) + { + File environmentPath = new File(environment); + + if (environmentPath.exists()) + { + deleteDirectory(environmentPath); + } + } + } + + private void deleteDirectory(File path) + { + if (path.isDirectory()) + { + for (File file : path.listFiles()) + { + deleteDirectory(file); + } + } + else + { + path.delete(); + } + } + + private void sendMessageOnExchange(Exchange directExchange, AMQShortString routingKey, boolean deliveryMode) + { + //Set MessagePersustebce + 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(directExchange, false, false, routingKey); + + IncomingMessage currentMessage = null; + + try + { + currentMessage = new IncomingMessage(_virtualHost.getMessageStore().getNewMessageId(), + messageInfo, + new NonTransactionalContext(_virtualHost.getMessageStore(), + new StoreContext(), null, null), + new InternalTestProtocolSession()); + } + catch (AMQException e) + { + fail(e.getMessage()); + } + + currentMessage.setMessageStore(_virtualHost.getMessageStore()); + currentMessage.setExchange(directExchange); + + ContentHeaderBody headerBody = new ContentHeaderBody(); + headerBody.classId = BasicConsumeBodyImpl.CLASS_ID; + headerBody.bodySize = 0; + + headerBody.properties = properties; + + try + { + currentMessage.setContentHeaderBody(headerBody); + } + catch (AMQException e) + { + fail(e.getMessage()); + } + + currentMessage.setExpiration(); + + try + { + currentMessage.route(); + } + catch (AMQException e) + { + fail(e.getMessage()); + } + + try + { + currentMessage.routingComplete(_virtualHost.getMessageStore(), new MessageHandleFactory()); + } + catch (AMQException e) + { + fail(e.getMessage()); + } + + // check and deliver if header says body length is zero + if (currentMessage.allContentReceived()) + { + try + { + currentMessage.deliverToQueues(); + } + catch (AMQException e) + { + fail(e.getMessage()); + } + } + } + + private void createAllQueues() + { + //Register Durable Priority Queue + createQueue(durablePriorityQueueName, true, true); + + //Register Durable Simple Queue + createQueue(durableQueueName, false, true); + + //Register NON-Durable Priority Queue + createQueue(priorityQueueName, true, false); + + //Register NON-Durable Simple Queue + createQueue(queueName, false, false); + } + + private void createAllTopicQueues() + { + //Register Durable Priority Queue + createQueue(durablePriorityTopicQueueName, true, true); + + //Register Durable Simple Queue + createQueue(durableTopicQueueName, false, true); + + //Register NON-Durable Priority Queue + createQueue(priorityTopicQueueName, true, false); + + //Register NON-Durable Simple Queue + createQueue(topicQueueName, false, false); + } + + private Exchange createExchange(ExchangeType type, AMQShortString name, boolean durable) + { + Exchange exchange = null; + + try + { + exchange = type.newInstance(_virtualHost, name, durable, 0, false); + } + catch (AMQException e) + { + fail(e.getMessage()); + } + + try + { + _virtualHost.getExchangeRegistry().registerExchange(exchange); + } + catch (AMQException e) + { + fail(e.getMessage()); + } + return exchange; + } + + private void createQueue(AMQShortString queueName, boolean usePriority, boolean durable) + { + + FieldTable queueArguments = null; + + if (usePriority) + { + queueArguments = new FieldTable(); + queueArguments.put(AMQQueueFactory.X_QPID_PRIORITIES, DEFAULT_PRIORTY_LEVEL); + } + + AMQQueue queue = null; + + //Ideally we would be able to use the QueueDeclareHandler here. + try + { + queue = AMQQueueFactory.createAMQQueueImpl(queueName, durable, queueOwner, false, _virtualHost, + queueArguments); + + validateQueueProperties(queue, usePriority); + + if (queue.isDurable() && !queue.isAutoDelete()) + { + _virtualHost.getMessageStore().createQueue(queue, queueArguments); + } + } + catch (AMQException e) + { + fail(e.getMessage()); + } + + try + { + _virtualHost.getQueueRegistry().registerQueue(queue); + } + catch (AMQException e) + { + fail(e.getMessage()); + } + + } + + private void bindAllQueuesToExchange(Exchange exchange, AMQShortString routingKey) + { + FieldTable queueArguments = new FieldTable(); + queueArguments.put(AMQQueueFactory.X_QPID_PRIORITIES, DEFAULT_PRIORTY_LEVEL); + + QueueRegistry queueRegistry = _virtualHost.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); + } + + private void bindAllTopicQueuesToExchange(Exchange exchange, AMQShortString routingKey) + { + FieldTable queueArguments = new FieldTable(); + queueArguments.put(AMQQueueFactory.X_QPID_PRIORITIES, DEFAULT_PRIORTY_LEVEL); + + QueueRegistry queueRegistry = _virtualHost.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) + { + try + { + exchange.registerQueue(queueName, queue, queueArguments); + } + catch (AMQException e) + { + fail(e.getMessage()); + } + + FieldTable bindArguments = null; + + if (useSelector) + { + bindArguments = new FieldTable(); + bindArguments.put(AMQPFilterTypes.JMS_SELECTOR.getValue(), "Test = 'MST'"); + } + + try + { + queue.bind(exchange, routingKey, bindArguments); + } + catch (AMQException e) + { + fail(e.getMessage()); + } + } + + private void validateMessage(long messageCount, boolean allQueues) + { + validateMessageOnTopics(messageCount, allQueues); + validateMessageOnQueues(messageCount, allQueues); + } + + 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 = _virtualHost.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.getName(); + } + + 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/java/broker/src/test/java/org/apache/qpid/server/store/SkeletonMessageStore.java b/java/broker/src/test/java/org/apache/qpid/server/store/SkeletonMessageStore.java new file mode 100644 index 0000000000..fd6789f5ce --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/store/SkeletonMessageStore.java @@ -0,0 +1,156 @@ +/* + * + * 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.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.qpid.server.queue.MessageMetaData; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.configuration.VirtualHostConfiguration; +import org.apache.qpid.server.exchange.Exchange; + +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; + +/** + * A message store that does nothing. Designed to be used in tests that do not want to use any message store + * functionality. + */ +public class SkeletonMessageStore implements MessageStore +{ + private final AtomicLong _messageId = new AtomicLong(1); + + public void configure(String base, Configuration config) throws Exception + { + } + + public void configure(VirtualHost virtualHost, String base, VirtualHostConfiguration config) throws Exception + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void close() throws Exception + { + } + + public void removeMessage(StoreContext s, Long messageId) + { + } + + public void createExchange(Exchange exchange) throws AMQException + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void removeExchange(Exchange exchange) throws AMQException + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void bindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void unbindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void createQueue(AMQQueue queue) throws AMQException + { + } + + public void createQueue(AMQQueue queue, FieldTable arguments) throws AMQException + { + } + + public void beginTran(StoreContext s) throws AMQException + { + } + + public boolean inTran(StoreContext sc) + { + return false; + } + + public void commitTran(StoreContext storeContext) throws AMQException + { + } + + public void abortTran(StoreContext storeContext) throws AMQException + { + } + + public List<AMQQueue> createQueues() throws AMQException + { + return null; + } + + public Long getNewMessageId() + { + return _messageId.getAndIncrement(); + } + + public void storeContentBodyChunk(StoreContext sc, Long messageId, int index, ContentChunk contentBody, boolean lastContentBody) throws AMQException + { + + } + + public void storeMessageMetaData(StoreContext sc, Long messageId, MessageMetaData messageMetaData) throws AMQException + { + + } + + public MessageMetaData getMessageMetaData(StoreContext s,Long messageId) throws AMQException + { + return null; + } + + public ContentChunk getContentBodyChunk(StoreContext s,Long messageId, int index) throws AMQException + { + return null; + } + + public boolean isPersistent() + { + return false; + } + + public void removeQueue(final AMQQueue queue) throws AMQException + { + + } + + public void enqueueMessage(StoreContext context, final AMQQueue queue, Long messageId) throws AMQException + { + + } + + public void dequeueMessage(StoreContext context, final AMQQueue queue, Long messageId) throws AMQException + { + + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/store/TestMemoryMessageStore.java b/java/broker/src/test/java/org/apache/qpid/server/store/TestMemoryMessageStore.java new file mode 100644 index 0000000000..4e48435962 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/store/TestMemoryMessageStore.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.store; + +import org.apache.qpid.server.queue.MessageMetaData; +import org.apache.qpid.framing.ContentBody; +import org.apache.qpid.framing.abstraction.ContentChunk; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.List; + +/** + * Adds some extra methods to the memory message store for testing purposes. + */ +public class TestMemoryMessageStore extends MemoryMessageStore +{ + public TestMemoryMessageStore() + { + _metaDataMap = new ConcurrentHashMap<Long, MessageMetaData>(); + _contentBodyMap = new ConcurrentHashMap<Long, List<ContentChunk>>(); + } + + public ConcurrentMap<Long, MessageMetaData> getMessageMetaDataMap() + { + return _metaDataMap; + } + + public ConcurrentMap<Long, List<ContentChunk>> getContentBodyMap() + { + return _contentBodyMap; + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/store/TestReferenceCounting.java b/java/broker/src/test/java/org/apache/qpid/server/store/TestReferenceCounting.java new file mode 100644 index 0000000000..2346660d25 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/store/TestReferenceCounting.java @@ -0,0 +1,169 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.store; + +import junit.framework.TestCase; +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.queue.AMQMessage; +import org.apache.qpid.server.queue.MessageHandleFactory; +import org.apache.qpid.server.queue.AMQMessageHandle; + +/** + * Tests that reference counting works correctly with AMQMessage and the message store + */ +public class TestReferenceCounting extends TestCase +{ + private TestMemoryMessageStore _store; + + private StoreContext _storeContext = new StoreContext(); + + + protected void setUp() throws Exception + { + super.setUp(); + _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; + } + }; + + + final long messageId = _store.getNewMessageId(); + AMQMessageHandle messageHandle = (new MessageHandleFactory()).createMessageHandle(messageId, _store, true); + messageHandle.setPublishAndContentHeaderBody(_storeContext,info, chb); + AMQMessage message = new AMQMessage(messageHandle, + _storeContext,info); + + message = message.takeReference(); + + // we call routing complete to set up the handle + // message.routingComplete(_store, _storeContext, new MessageHandleFactory()); + + + assertEquals(1, _store.getMessageMetaDataMap().size()); + message.decrementReference(_storeContext); + assertEquals(1, _store.getMessageMetaDataMap().size()); + } + + private ContentHeaderBody createPersistentContentHeader() + { + ContentHeaderBody chb = new ContentHeaderBody(); + BasicContentHeaderProperties bchp = new BasicContentHeaderProperties(); + bchp.setDeliveryMode((byte)2); + chb.properties = 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 Long messageId = _store.getNewMessageId(); + final ContentHeaderBody chb = createPersistentContentHeader(); + AMQMessageHandle messageHandle = (new MessageHandleFactory()).createMessageHandle(messageId, _store, true); + messageHandle.setPublishAndContentHeaderBody(_storeContext,info,chb); + AMQMessage message = new AMQMessage(messageHandle, + _storeContext, + info); + + + message = message.takeReference(); + // we call routing complete to set up the handle + // message.routingComplete(_store, _storeContext, new MessageHandleFactory()); + + + + assertEquals(1, _store.getMessageMetaDataMap().size()); + message = message.takeReference(); + message.decrementReference(_storeContext); + assertEquals(1, _store.getMessageMetaDataMap().size()); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(TestReferenceCounting.class); + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/store/TestableMemoryMessageStore.java b/java/broker/src/test/java/org/apache/qpid/server/store/TestableMemoryMessageStore.java new file mode 100644 index 0000000000..9146fe88ae --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/store/TestableMemoryMessageStore.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.store; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.MessageMetaData; +import org.apache.qpid.framing.ContentBody; +import org.apache.qpid.framing.abstraction.ContentChunk; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.HashMap; +import java.util.List; + +/** + * 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>(); + + public TestableMemoryMessageStore(MemoryMessageStore mms) + { + _mms = mms; + } + + public TestableMemoryMessageStore() + { + _metaDataMap = new ConcurrentHashMap<Long, MessageMetaData>(); + _contentBodyMap = new ConcurrentHashMap<Long, List<ContentChunk>>(); + } + + public ConcurrentMap<Long, MessageMetaData> getMessageMetaDataMap() + { + if (_mms != null) + { + return _mms._metaDataMap; + } + else + { + return _metaDataMap; + } + } + + public ConcurrentMap<Long, List<ContentChunk>> getContentBodyMap() + { + if (_mms != null) + { + return _mms._contentBodyMap; + } + else + { + return _contentBodyMap; + } + } + + public void enqueueMessage(StoreContext context, final AMQQueue queue, Long messageId) throws AMQException + { + getMessages().put(messageId, queue); + } + + public void dequeueMessage(StoreContext context, final AMQQueue queue, Long messageId) throws AMQException + { + getMessages().remove(messageId); + } + + public HashMap<Long, AMQQueue> getMessages() + { + return _messages; + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java b/java/broker/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java new file mode 100644 index 0000000000..33fd669d5c --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java @@ -0,0 +1,180 @@ +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.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.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 QueueEntry lastSeen = null; + private State _state = State.ACTIVE; + private ArrayList<QueueEntry> messages = new ArrayList<QueueEntry>(); + private final Lock _stateChangeLock = new ReentrantLock(); + + 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 QueueEntry getLastSeenEntry() + { + return lastSeen; + } + + public SubscriptionAcquiredState getOwningState() + { + return new QueueEntry.SubscriptionAcquiredState(this); + } + + public AMQQueue getQueue() + { + return queue; + } + + public void getSendLock() + { + _stateChangeLock.lock(); + } + + public boolean hasInterest(QueueEntry msg) + { + return true; + } + + public boolean isActive() + { + return true; + } + + public boolean isAutoClose() + { + return false; + } + + public boolean isBrowser() + { + return false; + } + + public boolean isClosed() + { + return _closed; + } + + public boolean isSuspended() + { + return false; + } + + public void queueDeleted(AMQQueue queue) + { + } + + public void releaseSendLock() + { + _stateChangeLock.unlock(); + } + + public void resend(QueueEntry entry) throws AMQException + { + } + + public void restoreCredit(QueueEntry queueEntry) + { + } + + public void send(QueueEntry msg) throws AMQException + { + lastSeen = msg; + messages.add(msg); + } + + public boolean setLastSeenEntry(QueueEntry expected, QueueEntry newValue) + { + boolean result = false; + if (expected != null) + { + result = (expected.equals(lastSeen)); + } + lastSeen = newValue; + return result; + } + + public void setQueue(AMQQueue queue) + { + this.queue = queue; + } + + 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; + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/subscription/QueueBrowserUsesNoAckTest.java b/java/broker/src/test/java/org/apache/qpid/server/subscription/QueueBrowserUsesNoAckTest.java new file mode 100644 index 0000000000..d0db4ebd38 --- /dev/null +++ b/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(_session, _channel, sendMessageCount); + + //Ensure they are stored + checkStoreContents(sendMessageCount); + + //Check that there are no unacked messages + assertEquals("Channel should have no unacked msgs ", 0, + _channel.getUnacknowledgedMessageMap().size()); + + //Set the prefetch on the session to be less than the sent messages + _channel.setCredit(0, prefetch); + + //browse the queue + AMQShortString browser = browse(_channel, _queue); + + _queue.deliverAsync(); + + //Wait for messages to fill the prefetch + _session.awaitDelivery(prefetch); + + //Get those messages + List<InternalTestProtocolSession.DeliveryPair> messages = + _session.getDelivers(_channel.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", + !_channel.getSubscription(browser).getState() + .equals(Subscription.State.SUSPENDED)); + } + +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/txn/TxnBufferTest.java b/java/broker/src/test/java/org/apache/qpid/server/txn/TxnBufferTest.java new file mode 100644 index 0000000000..84d3d313d1 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/txn/TxnBufferTest.java @@ -0,0 +1,306 @@ +/* + * + * 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 junit.framework.TestCase; +import org.apache.qpid.AMQException; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.TestMemoryMessageStore; +import org.apache.qpid.server.store.StoreContext; + +import java.util.LinkedList; +import java.util.NoSuchElementException; + +public class TxnBufferTest extends TestCase +{ + private final LinkedList<MockOp> ops = new LinkedList<MockOp>(); + + public void testCommit() throws AMQException + { + MockStore store = new MockStore(); + + TxnBuffer buffer = new TxnBuffer(); + buffer.enlist(new MockOp().expectPrepare().expectCommit()); + //check relative ordering + MockOp op = new MockOp().expectPrepare().expectPrepare().expectCommit().expectCommit(); + buffer.enlist(op); + buffer.enlist(op); + buffer.enlist(new MockOp().expectPrepare().expectCommit()); + + buffer.commit(null); + + validateOps(); + store.validate(); + } + + public void testRollback() throws AMQException + { + MockStore store = new MockStore(); + + TxnBuffer buffer = new TxnBuffer(); + buffer.enlist(new MockOp().expectRollback()); + buffer.enlist(new MockOp().expectRollback()); + buffer.enlist(new MockOp().expectRollback()); + + buffer.rollback(null); + + validateOps(); + store.validate(); + } + + public void testCommitWithFailureDuringPrepare() throws AMQException + { + MockStore store = new MockStore(); + store.beginTran(null); + + TxnBuffer buffer = new TxnBuffer(); + buffer.enlist(new StoreMessageOperation(store)); + buffer.enlist(new MockOp().expectPrepare().expectUndoPrepare()); + buffer.enlist(new TxnTester(store)); + buffer.enlist(new MockOp().expectPrepare().expectUndoPrepare()); + buffer.enlist(new FailedPrepare()); + buffer.enlist(new MockOp()); + + try + { + buffer.commit(null); + } + catch (NoSuchElementException e) + { + + } + + validateOps(); + store.validate(); + } + + public void testCommitWithPersistance() throws AMQException + { + MockStore store = new MockStore(); + store.beginTran(null); + store.expectCommit(); + + TxnBuffer buffer = new TxnBuffer(); + buffer.enlist(new MockOp().expectPrepare().expectCommit()); + buffer.enlist(new MockOp().expectPrepare().expectCommit()); + buffer.enlist(new MockOp().expectPrepare().expectCommit()); + buffer.enlist(new StoreMessageOperation(store)); + buffer.enlist(new TxnTester(store)); + + buffer.commit(null); + validateOps(); + store.validate(); + } + + private void validateOps() + { + for (MockOp op : ops) + { + op.validate(); + } + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(TxnBufferTest.class); + } + + class MockOp implements TxnOp + { + final Object PREPARE = "PREPARE"; + final Object COMMIT = "COMMIT"; + final Object UNDO_PREPARE = "UNDO_PREPARE"; + final Object ROLLBACK = "ROLLBACK"; + + private final LinkedList expected = new LinkedList(); + + MockOp() + { + ops.add(this); + } + + public void prepare(StoreContext context) + { + assertEquals(expected.removeLast(), PREPARE); + } + + public void commit(StoreContext context) + { + assertEquals(expected.removeLast(), COMMIT); + } + + public void undoPrepare() + { + assertEquals(expected.removeLast(), UNDO_PREPARE); + } + + public void rollback(StoreContext context) + { + assertEquals(expected.removeLast(), ROLLBACK); + } + + private MockOp expect(Object optype) + { + expected.addFirst(optype); + return this; + } + + MockOp expectPrepare() + { + return expect(PREPARE); + } + + MockOp expectCommit() + { + return expect(COMMIT); + } + + MockOp expectUndoPrepare() + { + return expect(UNDO_PREPARE); + } + + MockOp expectRollback() + { + return expect(ROLLBACK); + } + + void validate() + { + assertEquals("Expected ops were not all invoked", new LinkedList(), expected); + } + + void clear() + { + expected.clear(); + } + } + + class MockStore extends TestMemoryMessageStore + { + final Object BEGIN = "BEGIN"; + final Object ABORT = "ABORT"; + final Object COMMIT = "COMMIT"; + + private final LinkedList expected = new LinkedList(); + private boolean inTran; + + public void beginTran(StoreContext context) throws AMQException + { + inTran = true; + } + + public void commitTran(StoreContext context) throws AMQException + { + assertEquals(expected.removeLast(), COMMIT); + inTran = false; + } + + public void abortTran(StoreContext context) throws AMQException + { + assertEquals(expected.removeLast(), ABORT); + inTran = false; + } + + public boolean inTran(StoreContext context) + { + return inTran; + } + + private MockStore expect(Object optype) + { + expected.addFirst(optype); + return this; + } + + MockStore expectBegin() + { + return expect(BEGIN); + } + + MockStore expectCommit() + { + return expect(COMMIT); + } + + MockStore expectAbort() + { + return expect(ABORT); + } + + void clear() + { + expected.clear(); + } + + void validate() + { + assertEquals("Expected ops were not all invoked", new LinkedList(), expected); + } + } + + class NullOp implements TxnOp + { + public void prepare(StoreContext context) throws AMQException + { + } + public void commit(StoreContext context) + { + } + public void undoPrepare() + { + } + public void rollback(StoreContext context) + { + } + } + + class FailedPrepare extends NullOp + { + public void prepare() throws AMQException + { + throw new AMQException(null, "Fail!", null); + } + } + + class TxnTester extends NullOp + { + private final MessageStore store; + + private final StoreContext context = new StoreContext(); + + TxnTester(MessageStore store) + { + this.store = store; + } + + public void prepare() throws AMQException + { + assertTrue("Expected prepare to be performed under txn", store.inTran(context)); + } + + public void commit() + { + assertTrue("Expected commit not to be performed under txn", !store.inTran(context)); + } + } + +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/util/InternalBrokerBaseCase.java b/java/broker/src/test/java/org/apache/qpid/server/util/InternalBrokerBaseCase.java new file mode 100644 index 0000000000..101c33043d --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/util/InternalBrokerBaseCase.java @@ -0,0 +1,214 @@ +/* + * + * 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 junit.framework.TestCase; + +import org.apache.commons.configuration.PropertiesConfiguration; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.AMQQueueFactory; +import org.apache.qpid.server.configuration.ServerConfiguration; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.protocol.InternalTestProtocolSession; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.ConsumerTagNotUniqueException; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.store.TestableMemoryMessageStore; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.BasicContentHeaderProperties; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.AMQException; +import org.apache.qpid.util.MockChannel; +import org.apache.qpid.common.AMQPFilterTypes; +import org.apache.qpid.exchange.ExchangeDefaults; + +public class InternalBrokerBaseCase extends TestCase +{ + protected IApplicationRegistry _registry; + protected MessageStore _messageStore; + protected MockChannel _channel; + protected InternalTestProtocolSession _session; + protected VirtualHost _virtualHost; + protected StoreContext _storeContext = new StoreContext(); + protected AMQQueue _queue; + protected AMQShortString QUEUE_NAME; + + public void setUp() throws Exception + { + super.setUp(); + PropertiesConfiguration configuration = new PropertiesConfiguration(); + configuration.setProperty("virtualhosts.virtualhost.test.store.class", TestableMemoryMessageStore.class.getName()); + _registry = new TestApplicationRegistry(new ServerConfiguration(configuration)); + ApplicationRegistry.initialise(_registry); + _virtualHost = _registry.getVirtualHostRegistry().getVirtualHost("test"); + + _messageStore = _virtualHost.getMessageStore(); + + QUEUE_NAME = new AMQShortString("test"); + _queue = AMQQueueFactory.createAMQQueueImpl(QUEUE_NAME, false, new AMQShortString("testowner"), + false, _virtualHost, null); + + _virtualHost.getQueueRegistry().registerQueue(_queue); + + Exchange defaultExchange = _virtualHost.getExchangeRegistry().getDefaultExchange(); + + _queue.bind(defaultExchange, QUEUE_NAME, null); + + _session = new InternalTestProtocolSession(); + + _session.setVirtualHost(_virtualHost); + + _channel = new MockChannel(_session, 1, _messageStore); + + _session.addChannel(_channel); + } + + public void tearDown() throws Exception + { + ApplicationRegistry.remove(1); + super.tearDown(); + } + + protected void checkStoreContents(int messageCount) + { + assertEquals("Message header count incorrect in the MetaDataMap", messageCount, ((TestableMemoryMessageStore) _messageStore).getMessageMetaDataMap().size()); + + //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()); + } + catch (ConsumerTagNotUniqueException 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()); + } + catch (ConsumerTagNotUniqueException 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 QUEUE_NAME; + } + }; + + 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.properties = properties; + + channel.publishContentHeader(_headerBody); + } + + } + + public void acknowledge(AMQChannel channel, long deliveryTag) + { + try + { + channel.acknowledgeMessage(deliveryTag, false); + } + catch (AMQException e) + { + e.printStackTrace(); + fail(e.getMessage()); + } + } + +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/util/LoggingProxyTest.java b/java/broker/src/test/java/org/apache/qpid/server/util/LoggingProxyTest.java new file mode 100644 index 0000000000..c7db51016e --- /dev/null +++ b/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/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.java b/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.java new file mode 100644 index 0000000000..c6ecac6a01 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.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.util; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.commons.configuration.MapConfiguration; +import org.apache.commons.configuration.PropertiesConfiguration; +import org.apache.qpid.server.configuration.ServerConfiguration; +import org.apache.qpid.server.configuration.VirtualHostConfiguration; +import org.apache.qpid.server.exchange.ExchangeFactory; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.management.NoopManagedObjectRegistry; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.security.access.ACLManager; +import org.apache.qpid.server.security.access.ACLPlugin; +import org.apache.qpid.server.security.access.plugins.AllowAll; +import org.apache.qpid.server.security.auth.database.PropertiesPrincipalDatabaseManager; +import org.apache.qpid.server.security.auth.manager.PrincipalDatabaseAuthenticationManager; +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.server.virtualhost.VirtualHostRegistry; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Properties; +import java.util.Arrays; + +public class TestApplicationRegistry extends ApplicationRegistry +{ + private QueueRegistry _queueRegistry; + + private ExchangeRegistry _exchangeRegistry; + + private ExchangeFactory _exchangeFactory; + + private MessageStore _messageStore; + + private VirtualHost _vHost; + + + private ServerConfiguration _config; + + public TestApplicationRegistry() throws ConfigurationException + { + super(new ServerConfiguration(new PropertiesConfiguration())); + } + + public TestApplicationRegistry(ServerConfiguration config) throws ConfigurationException + { + super(config); + _config = config; + } + + public void initialise() throws Exception + { + Properties users = new Properties(); + + users.put("guest", "guest"); + + _databaseManager = new PropertiesPrincipalDatabaseManager("default", users); + + _accessManager = new ACLManager(_configuration.getSecurityConfiguration(), _pluginManager, AllowAll.FACTORY); + + _authenticationManager = new PrincipalDatabaseAuthenticationManager(null, null); + + _managedObjectRegistry = new NoopManagedObjectRegistry(); + + _messageStore = new TestableMemoryMessageStore(); + + _virtualHostRegistry = new VirtualHostRegistry(); + + PropertiesConfiguration vhostProps = new PropertiesConfiguration(); + VirtualHostConfiguration hostConfig = new VirtualHostConfiguration("test", vhostProps); + _vHost = new VirtualHost(hostConfig, _messageStore); + + _virtualHostRegistry.registerVirtualHost(_vHost); + + _queueRegistry = _vHost.getQueueRegistry(); + _exchangeFactory = _vHost.getExchangeFactory(); + _exchangeRegistry = _vHost.getExchangeRegistry(); + + } + + public QueueRegistry getQueueRegistry() + { + return _queueRegistry; + } + + public ExchangeRegistry getExchangeRegistry() + { + return _exchangeRegistry; + } + + public ExchangeFactory getExchangeFactory() + { + return _exchangeFactory; + } + + public Collection<String> getVirtualHostNames() + { + String[] hosts = {"test"}; + return Arrays.asList(hosts); + } + + public void setAccessManager(ACLManager newManager) + { + _accessManager = newManager; + } + + public MessageStore getMessageStore() + { + return _messageStore; + } + +} + + diff --git a/java/broker/src/test/java/org/apache/qpid/util/MockChannel.java b/java/broker/src/test/java/org/apache/qpid/util/MockChannel.java new file mode 100644 index 0000000000..447d09429d --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/util/MockChannel.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.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); + } + + public Subscription getSubscription(AMQShortString subscription) + { + return _tag2SubscriptionMap.get(subscription); + } + +} |