diff options
| author | Kim van der Riet <kpvdr@apache.org> | 2012-05-04 15:39:19 +0000 |
|---|---|---|
| committer | Kim van der Riet <kpvdr@apache.org> | 2012-05-04 15:39:19 +0000 |
| commit | 633c33f224f3196f3f9bd80bd2e418d8143fea06 (patch) | |
| tree | 1391da89470593209466df68c0b40b89c14963b1 /java/broker | |
| parent | c73f9286ebff93a6c8dbc29cf05e258c4b55c976 (diff) | |
| download | qpid-python-633c33f224f3196f3f9bd80bd2e418d8143fea06.tar.gz | |
QPID-3858: Updated branch - merged from trunk r.1333987
git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/asyncstore@1334037 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'java/broker')
209 files changed, 12425 insertions, 2979 deletions
diff --git a/java/broker/bin/create-example-ssl-stores.bat b/java/broker/bin/create-example-ssl-stores.bat deleted file mode 100644 index 5419c098d5..0000000000 --- a/java/broker/bin/create-example-ssl-stores.bat +++ /dev/null @@ -1,36 +0,0 @@ -@REM
-@REM Licensed to the Apache Software Foundation (ASF) under one
-@REM or more contributor license agreements. See the NOTICE file
-@REM distributed with this work for additional information
-@REM regarding copyright ownership. The ASF licenses this file
-@REM to you under the Apache License, Version 2.0 (the
-@REM "License"); you may not use this file except in compliance
-@REM with the License. You may obtain a copy of the License at
-@REM
-@REM http://www.apache.org/licenses/LICENSE-2.0
-@REM
-@REM Unless required by applicable law or agreed to in writing,
-@REM software distributed under the License is distributed on an
-@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-@REM KIND, either express or implied. See the License for the
-@REM specific language governing permissions and limitations
-@REM under the License.
-@REM
-
-@REM Create example keystore for broker and trust store for client/management console.
-@REM
-@REM Use generated qpid.keystore as the brokers keystore
-@REM Use generated qpid.truststore as client/consoles truststore
-@REM All passwords have value: password
-
-@REM Create Broker Keystore:
-keytool -genkey -alias qpidBroker -keyalg RSA -validity 365 -keystore qpid.keystore -storepass password -keypass password -dname "CN=hostname, OU=OrgUnit, O=Org, L=City, C=US"
-
-@REM Export Self Signed Cert:
-keytool -export -alias qpidBroker -keystore qpid.keystore -file qpidBroker.cer -storepass password
-
-@REM Import Broker Cert Into MC TrustStore:
-keytool -import -alias qpidBrokerCert -file qpidBroker.cer -keystore qpid.truststore -storepass password -noprompt
-
-@REM Delete the cert
-del qpidBroker.cer
\ No newline at end of file diff --git a/java/broker/bin/create-example-ssl-stores.sh b/java/broker/bin/create-example-ssl-stores.sh deleted file mode 100755 index bfcb3dfecf..0000000000 --- a/java/broker/bin/create-example-ssl-stores.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -# Create example keystore for broker and trust store for client/management console. -# -# Use generated qpid.keystore as the brokers keystore -# Use generated qpid.truststore as client/consoles truststore -# All passwords have value: password - -#Create Broker Keystore: -keytool -genkey -alias qpidBroker -keyalg RSA -validity 365 -keystore qpid.keystore \ --storepass password -keypass password -dname "CN=hostname, OU=OrgUnit, O=Org, L=City, C=US" - -#Export Self Signed Cert: -keytool -export -alias qpidBroker -keystore qpid.keystore -file qpidBroker.cer -storepass password - -#Import Broker Cert Into MC TrustStore: -keytool -import -alias qpidBrokerCert -file qpidBroker.cer -keystore qpid.truststore -storepass password -noprompt - -#Delete the cert -rm qpidBroker.cer diff --git a/java/broker/build.xml b/java/broker/build.xml index bb809583ee..9e8bf12f18 100644 --- a/java/broker/build.xml +++ b/java/broker/build.xml @@ -19,7 +19,7 @@ - --> <project name="AMQ Broker" default="build"> - <property name="module.depends" value="management/common common"/> + <property name="module.depends" value="management/common common amqp-1-0-common"/> <property name="module.test.depends" value="common/test" /> <property name="module.main" value="org.apache.qpid.server.Main"/> <property name="module.genpom" value="true"/> diff --git a/java/broker/etc/config.xml b/java/broker/etc/config.xml index 6839f8cf9f..e1aacd43b5 100644 --- a/java/broker/etc/config.xml +++ b/java/broker/etc/config.xml @@ -53,8 +53,7 @@ </jmxport> <ssl> <enabled>false</enabled> - <!-- Update below path to your keystore location, or run the bin/create-example-ssl-stores(.sh|.bat) - script from within the etc/ folder to generate an example store with self-signed cert --> + <!-- Update below path to your keystore location. --> <keyStorePath>${conf}/qpid.keystore</keyStorePath> <keyStorePassword>password</keyStorePassword> </ssl> diff --git a/java/broker/etc/virtualhosts.xml b/java/broker/etc/virtualhosts.xml index 4dcdcda6d2..1f7f91de9a 100644 --- a/java/broker/etc/virtualhosts.xml +++ b/java/broker/etc/virtualhosts.xml @@ -25,8 +25,8 @@ <name>localhost</name> <localhost> <store> - <class>org.apache.qpid.server.store.MemoryMessageStore</class> - <!--<class>org.apache.qpid.server.store.DerbyMessageStore</class> + <factoryclass>org.apache.qpid.server.store.MemoryMessageStoreFactory</factoryclass> + <!--<factoryclass>org.apache.qpid.server.store.derby.DerbyMessageStoreFactory</factoryclass> <environment-path>${QPID_WORK}/derbystore</environment-path>--> </store> @@ -86,8 +86,8 @@ <name>development</name> <development> <store> - <class>org.apache.qpid.server.store.MemoryMessageStore</class> - <!--<class>org.apache.qpid.server.store.DerbyMessageStore</class> + <factoryclass>org.apache.qpid.server.store.MemoryMessageStoreFactory</factoryclass> + <!--<factoryclass>org.apache.qpid.server.store.derby.DerbyMessageStoreFactory</factoryclass> <environment-path>${QPID_WORK}/derbystore</environment-path>--> </store> @@ -125,8 +125,8 @@ <name>test</name> <test> <store> - <class>org.apache.qpid.server.store.MemoryMessageStore</class> - <!--<class>org.apache.qpid.server.store.DerbyMessageStore</class> + <factoryclass>org.apache.qpid.server.store.MemoryMessageStoreFactory</factoryclass> + <!--<factoryclass>org.apache.qpid.server.store.derby.DerbyMessageStoreFactory</factoryclass> <environment-path>${QPID_WORK}/derbystore</environment-path>--> </store> diff --git a/java/broker/src/main/java/broker.bnd b/java/broker/src/main/java/broker.bnd index 25b0495a63..fa433848a6 100755 --- a/java/broker/src/main/java/broker.bnd +++ b/java/broker/src/main/java/broker.bnd @@ -17,7 +17,7 @@ # under the License. # -ver: 0.15.0 +ver: 0.17.0 Bundle-SymbolicName: qpid-broker Bundle-Version: ${ver} diff --git a/java/broker/src/main/java/org/apache/qpid/qmf/ManagementExchange.java b/java/broker/src/main/java/org/apache/qpid/qmf/ManagementExchange.java index 1a098d0446..034a4ae53c 100644 --- a/java/broker/src/main/java/org/apache/qpid/qmf/ManagementExchange.java +++ b/java/broker/src/main/java/org/apache/qpid/qmf/ManagementExchange.java @@ -37,6 +37,7 @@ import org.apache.qpid.server.exchange.topic.TopicNormalizer; import org.apache.qpid.server.exchange.topic.TopicParser; import org.apache.qpid.server.message.InboundMessage; import org.apache.qpid.server.message.ServerMessage; +import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.BaseQueue; import org.apache.qpid.server.virtualhost.HouseKeepingTask; @@ -83,7 +84,8 @@ public class ManagementExchange implements Exchange, QMFService.Listener private class ManagementQueue implements BaseQueue { - private final String NAME_AS_STRING = "##__mgmt_pseudo_queue__##" + UUID.randomUUID().toString(); + private final UUID QUEUE_ID = UUIDGenerator.generateUUID(); + private final String NAME_AS_STRING = "##__mgmt_pseudo_queue__##" + QUEUE_ID.toString(); private final AMQShortString NAME_AS_SHORT_STRING = new AMQShortString(NAME_AS_STRING); public void enqueue(ServerMessage message) throws AMQException @@ -129,9 +131,10 @@ public class ManagementExchange implements Exchange, QMFService.Listener return NAME_AS_SHORT_STRING; } - public String getResourceName() + @Override + public UUID getId() { - return NAME_AS_STRING; + return QUEUE_ID; } } @@ -155,14 +158,14 @@ public class ManagementExchange implements Exchange, QMFService.Listener return ManagementExchange.class; } - public ManagementExchange newInstance(VirtualHost host, + public ManagementExchange newInstance(UUID id, VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) throws AMQException { ManagementExchange exch = new ManagementExchange(); - exch.initialise(host, name, durable, ticket, autoDelete); + exch.initialise(id, host, name, durable, ticket, autoDelete); return exch; } @@ -183,7 +186,7 @@ public class ManagementExchange implements Exchange, QMFService.Listener return QPID_MANAGEMENT_TYPE; } - public void initialise(VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) + public void initialise(UUID id, VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) throws AMQException { if(!QPID_MANAGEMENT.equals(name)) @@ -191,7 +194,7 @@ public class ManagementExchange implements Exchange, QMFService.Listener throw new AMQException("Can't create more than one Management exchange"); } _virtualHost = host; - _id = host.getConfigStore().createId(); + _id = id; _virtualHost.scheduleHouseKeepingTask(_virtualHost.getBroker().getManagementPublishInterval(), new UpdateTask(_virtualHost)); getConfigStore().addConfiguredObject(this); getQMFService().addListener(this); @@ -410,6 +413,11 @@ public class ManagementExchange implements Exchange, QMFService.Listener return queues; } + public boolean isBound(String bindingKey, Map<String, Object> arguments, AMQQueue queue) + { + return false; //TODO + } + public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue) { return false; //To change body of implemented methods use File | Settings | File Templates. diff --git a/java/broker/src/main/java/org/apache/qpid/qmf/QMFService.java b/java/broker/src/main/java/org/apache/qpid/qmf/QMFService.java index 4367a04da9..900b722886 100644 --- a/java/broker/src/main/java/org/apache/qpid/qmf/QMFService.java +++ b/java/broker/src/main/java/org/apache/qpid/qmf/QMFService.java @@ -658,6 +658,11 @@ public class QMFService implements ConfigStore.ConfigEventListener, Closeable return _obj.getStagingThreshold(); } + public Boolean getMgmtPublish() + { + return true; + } + public Integer getMgmtPubInterval() { return _obj.getManagementPublishInterval(); diff --git a/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java b/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java index 265aa7714e..0f32b98aa8 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java +++ b/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java @@ -33,11 +33,11 @@ import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.actors.ManagementActor; import org.apache.qpid.server.management.AMQManagedObject; import org.apache.qpid.server.management.ManagedObject; +import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.AMQQueueFactory; import org.apache.qpid.server.queue.AMQQueueMBean; import org.apache.qpid.server.queue.QueueRegistry; -import org.apache.qpid.server.store.DurableConfigurationStore; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.virtualhost.VirtualHostImpl; @@ -49,6 +49,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.UUID; /** * This MBean implements the broker management interface and exposes the @@ -60,8 +61,6 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr private final QueueRegistry _queueRegistry; private final ExchangeRegistry _exchangeRegistry; private final ExchangeFactory _exchangeFactory; - private final Exchange _defaultExchange; - private final DurableConfigurationStore _durableConfig; private final VirtualHostImpl.VirtualHostMBean _virtualHostMBean; @@ -75,8 +74,6 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr _queueRegistry = virtualHost.getQueueRegistry(); _exchangeRegistry = virtualHost.getExchangeRegistry(); - _defaultExchange = _exchangeRegistry.getDefaultExchange(); - _durableConfig = virtualHost.getDurableConfigurationStore(); _exchangeFactory = virtualHost.getExchangeFactory(); } @@ -176,12 +173,12 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr Exchange exchange = _exchangeRegistry.getExchange(new AMQShortString(exchangeName)); if (exchange == null) { - exchange = _exchangeFactory.createExchange(new AMQShortString(exchangeName), new AMQShortString(type), - durable, false, 0); + exchange = _exchangeFactory.createExchange(new AMQShortString(exchangeName), + new AMQShortString(type), durable, false, 0); _exchangeRegistry.registerExchange(exchange); if (durable) { - _durableConfig.createExchange(exchange); + getVirtualHost().getMessageStore().createExchange(exchange); } } else @@ -249,45 +246,42 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr public void createNewQueue(String queueName, String owner, boolean durable, Map<String,Object> arguments) throws JMException { final AMQShortString queueNameAsAMQShortString = new AMQShortString(queueName); - AMQQueue queue = _queueRegistry.getQueue(queueNameAsAMQShortString); - if (queue != null) + synchronized (_queueRegistry) { - throw new JMException("The queue \"" + queueName + "\" already exists."); - } - - CurrentActor.set(new ManagementActor(getLogActor().getRootMessageLogger())); - try - { - AMQShortString ownerShortString = null; - if (owner != null) + AMQQueue queue = _queueRegistry.getQueue(queueNameAsAMQShortString); + if (queue != null) { - ownerShortString = new AMQShortString(owner); + throw new JMException("The queue \"" + queueName + "\" already exists."); } - FieldTable args = null; - if(arguments != null) + CurrentActor.set(new ManagementActor(getLogActor().getRootMessageLogger())); + try { - args = FieldTable.convertToFieldTable(arguments); - } - final VirtualHost virtualHost = getVirtualHost(); + FieldTable args = null; + if(arguments != null) + { + args = FieldTable.convertToFieldTable(arguments); + } + final VirtualHost virtualHost = getVirtualHost(); + + queue = AMQQueueFactory.createAMQQueueImpl(UUIDGenerator.generateUUID(), queueName, durable, owner, + false, false, getVirtualHost(), arguments); + if (queue.isDurable() && !queue.isAutoDelete()) + { + getVirtualHost().getMessageStore().createQueue(queue, args); + } - queue = AMQQueueFactory.createAMQQueueImpl(queueNameAsAMQShortString, durable, ownerShortString, - false, false, getVirtualHost(), args); - if (queue.isDurable() && !queue.isAutoDelete()) + virtualHost.getBindingFactory().addBinding(queueName, queue, _exchangeRegistry.getDefaultExchange(), null); + } + catch (AMQException ex) { - _durableConfig.createQueue(queue, args); + JMException jme = new JMException(ex.toString()); + throw new MBeanException(jme, "Error in creating queue " + queueName); + } + finally + { + CurrentActor.remove(); } - - virtualHost.getBindingFactory().addBinding(queueName, queue, _defaultExchange, null); - } - catch (AMQException ex) - { - JMException jme = new JMException(ex.toString()); - throw new MBeanException(jme, "Error in creating queue " + queueName); - } - finally - { - CurrentActor.remove(); } } @@ -317,7 +311,7 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr queue.delete(); if (queue.isDurable()) { - _durableConfig.removeQueue(queue); + getVirtualHost().getMessageStore().removeQueue(queue); } } catch (AMQException ex) diff --git a/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java b/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java index e5e755bd23..8198cec821 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java +++ b/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java @@ -20,6 +20,7 @@ */ package org.apache.qpid.server; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -82,11 +83,13 @@ import org.apache.qpid.server.queue.IncomingMessage; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.StoreFuture; import org.apache.qpid.server.store.StoredMessage; import org.apache.qpid.server.subscription.ClientDeliveryMethod; import org.apache.qpid.server.subscription.RecordDeliveryMethod; import org.apache.qpid.server.subscription.Subscription; import org.apache.qpid.server.subscription.SubscriptionFactoryImpl; +import org.apache.qpid.server.subscription.SubscriptionImpl; import org.apache.qpid.server.txn.AsyncAutoCommitTransaction; import org.apache.qpid.server.txn.LocalTransaction; import org.apache.qpid.server.txn.ServerTransaction; @@ -273,7 +276,7 @@ public class AMQChannel implements SessionConfig, AMQSessionModel, AsyncAutoComm { throw new AMQSecurityException("Permission denied: " + e.getName()); } - _currentMessage = new IncomingMessage(info); + _currentMessage = new IncomingMessage(info, getProtocolSession().getReference()); _currentMessage.setExchange(e); } @@ -295,26 +298,9 @@ public class AMQChannel implements SessionConfig, AMQSessionModel, AsyncAutoComm _currentMessage.setExpiration(); + _currentMessage.headersReceived(getProtocolSession().getLastReceivedTime()); - MessageMetaData mmd = _currentMessage.headersReceived(getProtocolSession().getLastReceivedTime()); - final StoredMessage<MessageMetaData> handle = _messageStore.addMessage(mmd); - _currentMessage.setStoredMessage(handle); - - routeCurrentMessage(); - - - _transaction.addPostTransactionAction(new ServerTransaction.Action() - { - - public void postCommit() - { - } - - public void onRollback() - { - handle.remove(); - } - }); + _currentMessage.route(); deliverCurrentMessageIfComplete(); } @@ -346,17 +332,41 @@ public class AMQChannel implements SessionConfig, AMQSessionModel, AsyncAutoComm { _actor.message(ExchangeMessages.DISCARDMSG(_currentMessage.getExchange().asString(), _currentMessage.getRoutingKey())); } - } else { + final StoredMessage<MessageMetaData> handle = _messageStore.addMessage(_currentMessage.getMessageMetaData()); + _currentMessage.setStoredMessage(handle); + int bodyCount = _currentMessage.getBodyCount(); + if(bodyCount > 0) + { + long bodyLengthReceived = 0; + for(int i = 0 ; i < bodyCount ; i++) + { + ContentChunk contentChunk = _currentMessage.getContentChunk(i); + handle.addContent((int)bodyLengthReceived, ByteBuffer.wrap(contentChunk.getData())); + bodyLengthReceived += contentChunk.getSize(); + } + } + + _transaction.addPostTransactionAction(new ServerTransaction.Action() + { + public void postCommit() + { + } + + public void onRollback() + { + handle.remove(); + } + }); + _transaction.enqueue(destinationQueues, _currentMessage, new MessageDeliveryAction(_currentMessage, destinationQueues), getProtocolSession().getLastReceivedTime()); incrementOutstandingTxnsIfNecessary(); - updateTransactionalActivity(); + updateTransactionalActivity(); + _currentMessage.getStoredMessage().flushToStore(); } } - _currentMessage.getStoredMessage().flushToStore(); - } finally { @@ -383,9 +393,6 @@ public class AMQChannel implements SessionConfig, AMQSessionModel, AsyncAutoComm try { - - // returns true iff the message was delivered (i.e. if all data was - // received final ContentChunk contentChunk = _session.getMethodRegistry().getProtocolVersionMethodConverter().convertToContentChunk(contentBody); @@ -409,11 +416,6 @@ public class AMQChannel implements SessionConfig, AMQSessionModel, AsyncAutoComm } } - protected void routeCurrentMessage() throws AMQException - { - _currentMessage.route(); - } - public long getNextDeliveryTag() { return ++_deliveryTag; @@ -1123,11 +1125,6 @@ public class AMQChannel implements SessionConfig, AMQSessionModel, AsyncAutoComm } - public Object getID() - { - return _channelId; - } - public AMQConnectionModel getConnectionModel() { return _session; @@ -1377,7 +1374,7 @@ public class AMQChannel implements SessionConfig, AMQSessionModel, AsyncAutoComm { if(_blockingQueues.remove(queue)) { - if(_blocking.compareAndSet(true,false)) + if(_blocking.compareAndSet(true,false) && !isClosing()) { _actor.message(_logSubject, ChannelMessages.FLOW_REMOVED()); @@ -1386,6 +1383,16 @@ public class AMQChannel implements SessionConfig, AMQSessionModel, AsyncAutoComm } } + public boolean onSameConnection(InboundMessage inbound) + { + if(inbound instanceof IncomingMessage) + { + IncomingMessage incoming = (IncomingMessage) inbound; + return getProtocolSession().getReference() == incoming.getConnectionReference(); + } + return false; + } + private void flow(boolean flow) { MethodRegistry methodRegistry = _session.getMethodRegistry(); @@ -1551,7 +1558,7 @@ public class AMQChannel implements SessionConfig, AMQSessionModel, AsyncAutoComm } } - public void recordFuture(final MessageStore.StoreFuture future, final ServerTransaction.Action action) + public void recordFuture(final StoreFuture future, final ServerTransaction.Action action) { _unfinishedCommandsQueue.add(new AsyncCommand(future, action)); } @@ -1585,10 +1592,10 @@ public class AMQChannel implements SessionConfig, AMQSessionModel, AsyncAutoComm private static class AsyncCommand { - private final MessageStore.StoreFuture _future; + private final StoreFuture _future; private ServerTransaction.Action _action; - public AsyncCommand(final MessageStore.StoreFuture future, final ServerTransaction.Action action) + public AsyncCommand(final StoreFuture future, final ServerTransaction.Action action) { _future = future; _action = action; @@ -1615,9 +1622,8 @@ public class AMQChannel implements SessionConfig, AMQSessionModel, AsyncAutoComm } } - @Override public int compareTo(AMQSessionModel session) { - return getId().toString().compareTo(session.getID().toString()); + return getId().compareTo(session.getId()); } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/Broker.java b/java/broker/src/main/java/org/apache/qpid/server/Broker.java index 072f8dc132..5004d320c2 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/Broker.java +++ b/java/broker/src/main/java/org/apache/qpid/server/Broker.java @@ -20,6 +20,7 @@ */ package org.apache.qpid.server; +import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; import org.apache.log4j.xml.QpidLog4JConfigurator; @@ -52,15 +53,24 @@ import java.io.InputStream; import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.EnumSet; +import java.util.Formatter; import java.util.HashSet; import java.util.List; import java.util.Properties; import java.util.Set; +import java.util.logging.ConsoleHandler; +import java.util.logging.FileHandler; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogRecord; public class Broker { + private static final Logger LOGGER = Logger.getLogger(Broker.class); + private static final int IPV4_ADDRESS_LENGTH = 4; private static final char IPV4_LITERAL_SEPARATOR = '.'; + private volatile Thread _shutdownHookThread; protected static class InitException extends RuntimeException { @@ -74,7 +84,14 @@ public class Broker public void shutdown() { - ApplicationRegistry.remove(); + try + { + removeShutdownHook(); + } + finally + { + ApplicationRegistry.remove(); + } } public void startup() throws Exception @@ -88,6 +105,7 @@ public class Broker { CurrentActor.set(new BrokerActor(new SystemOutMessageLogger())); startupImpl(options); + addShutdownHook(); } finally { @@ -147,6 +165,12 @@ public class Broker parsePortList(sslPorts, serverConfig.getSSLPorts()); } + Set<Integer> exclude_1_0 = new HashSet<Integer>(options.getExcludedPorts(ProtocolExclusion.v1_0)); + if(exclude_1_0.isEmpty()) + { + parsePortList(exclude_1_0, serverConfig.getPortExclude10()); + } + Set<Integer> exclude_0_10 = new HashSet<Integer>(options.getExcludedPorts(ProtocolExclusion.v0_10)); if(exclude_0_10.isEmpty()) { @@ -177,14 +201,14 @@ public class Broker bindAddr = serverConfig.getBind(); } - InetAddress bindAddress = null; + InetAddress bindAddress; if (bindAddr.equals(WILDCARD_ADDRESS)) { - bindAddress = new InetSocketAddress(0).getAddress(); + bindAddress = null; } else { - bindAddress = InetAddress.getByAddress(parseIP(bindAddr)); + bindAddress = InetAddress.getByName(bindAddr); } final AmqpProtocolVersion defaultSupportedProtocolReply = serverConfig.getDefaultSupportedProtocolReply(); @@ -193,18 +217,22 @@ public class Broker { for(int port : ports) { + final InetSocketAddress inetSocketAddress = new InetSocketAddress(bindAddress, port); + final Set<AmqpProtocolVersion> supported = - getSupportedVersions(port, exclude_0_10, exclude_0_9_1, exclude_0_9, exclude_0_8, serverConfig); + getSupportedVersions(port, exclude_1_0, exclude_0_10, exclude_0_9_1, exclude_0_9, + exclude_0_8, serverConfig); final NetworkTransportConfiguration settings = - new ServerNetworkTransportConfiguration(serverConfig, port, bindAddress.getHostName(), Transport.TCP); + new ServerNetworkTransportConfiguration(serverConfig, inetSocketAddress, Transport.TCP); final IncomingNetworkTransport transport = Transport.getIncomingTransportInstance(); final MultiVersionProtocolEngineFactory protocolEngineFactory = new MultiVersionProtocolEngineFactory(supported, defaultSupportedProtocolReply); transport.accept(settings, protocolEngineFactory, null); - ApplicationRegistry.getInstance().addAcceptor(new InetSocketAddress(bindAddress, port), + + ApplicationRegistry.getInstance().addAcceptor(inetSocketAddress, new QpidAcceptor(transport,"TCP")); CurrentActor.get().message(BrokerMessages.LISTENING("TCP", port)); } @@ -219,17 +247,21 @@ public class Broker for(int sslPort : sslPorts) { + final InetSocketAddress inetSocketAddress = new InetSocketAddress(bindAddress, sslPort); + final Set<AmqpProtocolVersion> supported = - getSupportedVersions(sslPort, exclude_0_10, exclude_0_9_1, exclude_0_9, exclude_0_8, serverConfig); + getSupportedVersions(sslPort, exclude_1_0, exclude_0_10, exclude_0_9_1, + exclude_0_9, exclude_0_8, serverConfig); final NetworkTransportConfiguration settings = - new ServerNetworkTransportConfiguration(serverConfig, sslPort, bindAddress.getHostName(), Transport.TCP); + new ServerNetworkTransportConfiguration(serverConfig, inetSocketAddress, Transport.TCP); final IncomingNetworkTransport transport = Transport.getIncomingTransportInstance(); final MultiVersionProtocolEngineFactory protocolEngineFactory = new MultiVersionProtocolEngineFactory(supported, defaultSupportedProtocolReply); transport.accept(settings, protocolEngineFactory, sslContext); - ApplicationRegistry.getInstance().addAcceptor(new InetSocketAddress(bindAddress, sslPort), + + ApplicationRegistry.getInstance().addAcceptor(inetSocketAddress, new QpidAcceptor(transport,"TCP")); CurrentActor.get().message(BrokerMessages.LISTENING("TCP/SSL", sslPort)); } @@ -244,13 +276,20 @@ public class Broker } } - private static Set<AmqpProtocolVersion> getSupportedVersions(final int port, final Set<Integer> exclude_0_10, - final Set<Integer> exclude_0_9_1, final Set<Integer> exclude_0_9, - final Set<Integer> exclude_0_8, - final ServerConfiguration serverConfig) + private static Set<AmqpProtocolVersion> getSupportedVersions(final int port, + final Set<Integer> exclude_1_0, + final Set<Integer> exclude_0_10, + final Set<Integer> exclude_0_9_1, + final Set<Integer> exclude_0_9, + final Set<Integer> exclude_0_8, + final ServerConfiguration serverConfig) { final EnumSet<AmqpProtocolVersion> supported = EnumSet.allOf(AmqpProtocolVersion.class); + if(exclude_1_0.contains(port) || !serverConfig.isAmqp10enabled()) + { + supported.remove(AmqpProtocolVersion.v1_0_0); + } if(exclude_0_10.contains(port) || !serverConfig.isAmqp010enabled()) { supported.remove(AmqpProtocolVersion.v0_10); @@ -349,34 +388,6 @@ public class Broker } } - private byte[] parseIP(String address) throws Exception - { - char[] literalBuffer = address.toCharArray(); - int byteCount = 0; - int currByte = 0; - byte[] ip = new byte[IPV4_ADDRESS_LENGTH]; - for (int i = 0; i < literalBuffer.length; i++) - { - char currChar = literalBuffer[i]; - if ((currChar >= '0') && (currChar <= '9')) - { - currByte = (currByte * 10) + (Character.digit(currChar, 10) & 0xFF); - } - - if (currChar == IPV4_LITERAL_SEPARATOR || (i + 1 == literalBuffer.length)) - { - ip[byteCount++] = (byte) currByte; - currByte = 0; - } - } - - if (byteCount != 4) - { - throw new Exception("Invalid IP address: " + address); - } - return ip; - } - private void configureLogging(File logConfigFile, long logWatchTime) throws InitException, IOException { if (logConfigFile.exists() && logConfigFile.canRead()) @@ -441,4 +452,56 @@ public class Broker blm.register(); } + + private void addShutdownHook() + { + Thread shutdownHookThread = new Thread(new ShutdownService()); + shutdownHookThread.setName("QpidBrokerShutdownHook"); + + Runtime.getRuntime().addShutdownHook(shutdownHookThread); + _shutdownHookThread = shutdownHookThread; + + LOGGER.debug("Added shutdown hook"); + } + + private void removeShutdownHook() + { + Thread shutdownThread = _shutdownHookThread; + + //if there is a shutdown thread and we aren't it, we should remove it + if(shutdownThread != null && !(Thread.currentThread() == shutdownThread)) + { + LOGGER.debug("Removing shutdown hook"); + + _shutdownHookThread = null; + + boolean removed = false; + try + { + removed = Runtime.getRuntime().removeShutdownHook(shutdownThread); + } + catch(IllegalStateException ise) + { + //ignore, means the JVM is already shutting down + } + + if(LOGGER.isDebugEnabled()) + { + LOGGER.debug("Removed shutdown hook: " + removed); + } + } + else + { + LOGGER.debug("Skipping shutdown hook removal as there either isnt one, or we are it."); + } + } + + private class ShutdownService implements Runnable + { + public void run() + { + LOGGER.debug("Shutdown hook running"); + Broker.this.shutdown(); + } + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/BrokerOptions.java b/java/broker/src/main/java/org/apache/qpid/server/BrokerOptions.java index 1f96a24701..d871c724fd 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/BrokerOptions.java +++ b/java/broker/src/main/java/org/apache/qpid/server/BrokerOptions.java @@ -47,7 +47,6 @@ public class BrokerOptions private Integer _logWatchFrequency = 0; - public void addPort(final int port) { _ports.add(port); @@ -107,7 +106,6 @@ public class BrokerOptions { _jmxPortConnectorServer = jmxPortConnectorServer; } - public String getQpidHome() { return System.getProperty(QPID_HOME); @@ -163,5 +161,4 @@ public class BrokerOptions { _bundleContext = bundleContext; } - }
\ No newline at end of file diff --git a/java/broker/src/main/java/org/apache/qpid/server/Main.java b/java/broker/src/main/java/org/apache/qpid/server/Main.java index 5fcd8a7b52..70fa414e3c 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/Main.java +++ b/java/broker/src/main/java/org/apache/qpid/server/Main.java @@ -59,6 +59,12 @@ public class Main .withDescription("SSL port. Overrides any value in the config file") .withLongOpt("sslport").create("s"); + + private static final Option OPTION_EXCLUDE_1_0 = + OptionBuilder.withArgName("port").hasArg() + .withDescription("when listening on the specified port do not accept AMQP1-0 connections. The specified port must be one specified on the command line") + .withLongOpt("exclude-1-0").create(); + private static final Option OPTION_EXCLUDE_0_10 = OptionBuilder.withArgName("port").hasArg() .withDescription("when listening on the specified port do not accept AMQP0-10 connections. The specified port must be one specified on the command line") @@ -116,6 +122,7 @@ public class Main OPTIONS.addOption(OPTION_LOG_WATCH); OPTIONS.addOption(OPTION_PORT); OPTIONS.addOption(OPTION_SSLPORT); + OPTIONS.addOption(OPTION_EXCLUDE_1_0); OPTIONS.addOption(OPTION_EXCLUDE_0_10); OPTIONS.addOption(OPTION_EXCLUDE_0_9_1); OPTIONS.addOption(OPTION_EXCLUDE_0_9); diff --git a/java/broker/src/main/java/org/apache/qpid/server/ProtocolExclusion.java b/java/broker/src/main/java/org/apache/qpid/server/ProtocolExclusion.java index 22d97d36dd..fe6e32173f 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/ProtocolExclusion.java +++ b/java/broker/src/main/java/org/apache/qpid/server/ProtocolExclusion.java @@ -28,7 +28,8 @@ public enum ProtocolExclusion v0_8("exclude-0-8","--exclude-0-8"), v0_9("exclude-0-9", "--exclude-0-9"), v0_9_1("exclude-0-9-1", "--exclude-0-9-1"), - v0_10("exclude-0-10", "--exclude-0-10"); + v0_10("exclude-0-10", "--exclude-0-10"), + v1_0("exclude-1-0", "--exclude-1-0"); private static final Map<String, ProtocolExclusion> MAP = new HashMap<String,ProtocolExclusion>(); diff --git a/java/broker/src/main/java/org/apache/qpid/server/binding/Binding.java b/java/broker/src/main/java/org/apache/qpid/server/binding/Binding.java index 8e44da095a..2efd4cee26 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/binding/Binding.java +++ b/java/broker/src/main/java/org/apache/qpid/server/binding/Binding.java @@ -117,7 +117,7 @@ public class Binding public String toString() { - return "Binding{bindingKey="+_bindingKey+", exchange="+_exchange+", queue="+_queue+"}"; + return "Binding{bindingKey="+_bindingKey+", exchange="+_exchange+", queue="+_queue+", id= " + _id + " }"; } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/binding/BindingFactory.java b/java/broker/src/main/java/org/apache/qpid/server/binding/BindingFactory.java index fe66a6d341..abf252c733 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/binding/BindingFactory.java +++ b/java/broker/src/main/java/org/apache/qpid/server/binding/BindingFactory.java @@ -24,7 +24,6 @@ import org.apache.qpid.AMQException; import org.apache.qpid.AMQInternalException; import org.apache.qpid.AMQSecurityException; import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.FieldTable; import org.apache.qpid.server.configuration.BindingConfig; import org.apache.qpid.server.configuration.BindingConfigType; import org.apache.qpid.server.configuration.ConfigStore; @@ -33,58 +32,35 @@ import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.messages.BindingMessages; import org.apache.qpid.server.logging.subjects.BindingLogSubject; +import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.store.DurableConfigurationStore; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.Collections; import java.util.Map; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; public class BindingFactory { private final VirtualHost _virtualHost; - private final DurableConfigurationStore.Source _configSource; - private final Exchange _defaultExchange; private final ConcurrentHashMap<BindingImpl, BindingImpl> _bindings = new ConcurrentHashMap<BindingImpl, BindingImpl>(); - public BindingFactory(final VirtualHost vhost) { - this(vhost, vhost.getExchangeRegistry().getDefaultExchange()); + _virtualHost = vhost; } - public BindingFactory(final DurableConfigurationStore.Source configSource, final Exchange defaultExchange) - { - _configSource = configSource; - _defaultExchange = defaultExchange; - if (configSource instanceof VirtualHost) - { - _virtualHost = (VirtualHost) configSource; - } - else - { - _virtualHost = null; - } - } - - public VirtualHost getVirtualHost() - { - return _virtualHost; - } - - - private final class BindingImpl extends Binding implements AMQQueue.Task, Exchange.Task, BindingConfig { private final BindingLogSubject _logSubject; //TODO : persist creation time private long _createTime = System.currentTimeMillis(); - private BindingImpl(String bindingKey, final AMQQueue queue, final Exchange exchange, final Map<String, Object> arguments) + private BindingImpl(UUID id, String bindingKey, final AMQQueue queue, final Exchange exchange, final Map<String, Object> arguments) { - super(queue.getVirtualHost().getConfigStore().createId(), bindingKey, queue, exchange, arguments); + super(id, bindingKey, queue, exchange, arguments); _logSubject = new BindingLogSubject(bindingKey,exchange,queue); } @@ -141,48 +117,59 @@ public class BindingFactory public boolean addBinding(String bindingKey, AMQQueue queue, Exchange exchange, Map<String, Object> arguments) throws AMQSecurityException, AMQInternalException { - return makeBinding(bindingKey, queue, exchange, arguments, false, false); + return makeBinding(null, bindingKey, queue, exchange, arguments, false, false); } - public boolean replaceBinding(final String bindingKey, + public boolean replaceBinding(final UUID id, final String bindingKey, final AMQQueue queue, final Exchange exchange, final Map<String, Object> arguments) throws AMQSecurityException, AMQInternalException { - return makeBinding(bindingKey, queue, exchange, arguments, false, true); + return makeBinding(id, bindingKey, queue, exchange, arguments, false, true); } - private boolean makeBinding(String bindingKey, AMQQueue queue, Exchange exchange, Map<String, Object> arguments, boolean restore, boolean force) throws AMQSecurityException, AMQInternalException + private boolean makeBinding(UUID id, String bindingKey, AMQQueue queue, Exchange exchange, Map<String, Object> arguments, boolean restore, boolean force) throws AMQSecurityException, AMQInternalException { assert queue != null; + final Exchange defaultExchange = _virtualHost.getExchangeRegistry().getDefaultExchange(); + if (bindingKey == null) { bindingKey = ""; } if (exchange == null) { - exchange = _defaultExchange; + exchange = defaultExchange; } if (arguments == null) { arguments = Collections.emptyMap(); } + if (exchange == null) + { + throw new IllegalArgumentException("exchange cannot be null"); + } + // The default exchange bindings must reflect the existence of queues, allow // all operations on it to succeed. It is up to the broker to prevent illegal // attempts at binding to this exchange, not the ACLs. - if(exchange != _defaultExchange) + if(exchange != defaultExchange) { //Perform ACLs - if (!getVirtualHost().getSecurityManager().authoriseBind(exchange, queue, new AMQShortString(bindingKey))) + if (!_virtualHost.getSecurityManager().authoriseBind(exchange, queue, new AMQShortString(bindingKey))) { throw new AMQSecurityException("Permission denied: binding " + bindingKey); } } - - BindingImpl b = new BindingImpl(bindingKey,queue,exchange,arguments); - BindingImpl existingMapping = _bindings.putIfAbsent(b,b); + + if (id == null) + { + id = UUIDGenerator.generateUUID(); + } + BindingImpl b = new BindingImpl(id, bindingKey, queue, exchange, arguments); + BindingImpl existingMapping = _bindings.putIfAbsent(b, b); if (existingMapping == null || force) { if (existingMapping != null) @@ -192,7 +179,7 @@ public class BindingFactory if (b.isDurable() && !restore) { - _configSource.getDurableConfigurationStore().bindQueue(exchange,new AMQShortString(bindingKey),queue,FieldTable.convertToFieldTable(arguments)); + _virtualHost.getMessageStore().bindQueue(b); } queue.addQueueDeleteTask(b); @@ -212,12 +199,12 @@ public class BindingFactory private ConfigStore getConfigStore() { - return getVirtualHost().getConfigStore(); + return _virtualHost.getConfigStore(); } - public void restoreBinding(final String bindingKey, final AMQQueue queue, final Exchange exchange, final Map<String, Object> argumentMap) throws AMQSecurityException, AMQInternalException + public void restoreBinding(final UUID id, final String bindingKey, final AMQQueue queue, final Exchange exchange, final Map<String, Object> argumentMap) throws AMQSecurityException, AMQInternalException { - makeBinding(bindingKey,queue,exchange,argumentMap,true, false); + makeBinding(id, bindingKey,queue,exchange,argumentMap,true, false); } public void removeBinding(final Binding b) throws AMQSecurityException, AMQInternalException @@ -229,13 +216,15 @@ public class BindingFactory public Binding removeBinding(String bindingKey, AMQQueue queue, Exchange exchange, Map<String, Object> arguments) throws AMQSecurityException, AMQInternalException { assert queue != null; + final Exchange defaultExchange = _virtualHost.getExchangeRegistry().getDefaultExchange(); + if (bindingKey == null) { bindingKey = ""; } if (exchange == null) { - exchange = _defaultExchange; + exchange = defaultExchange; } if (arguments == null) { @@ -245,16 +234,16 @@ public class BindingFactory // The default exchange bindings must reflect the existence of queues, allow // all operations on it to succeed. It is up to the broker to prevent illegal // attempts at binding to this exchange, not the ACLs. - if(exchange != _defaultExchange) + if(exchange != defaultExchange) { // Check access - if (!getVirtualHost().getSecurityManager().authoriseUnbind(exchange, new AMQShortString(bindingKey), queue)) + if (!_virtualHost.getSecurityManager().authoriseUnbind(exchange, new AMQShortString(bindingKey), queue)) { throw new AMQSecurityException("Permission denied: unbinding " + bindingKey); } } - BindingImpl b = _bindings.remove(new BindingImpl(bindingKey,queue,exchange,arguments)); + BindingImpl b = _bindings.remove(new BindingImpl(null, bindingKey,queue,exchange,arguments)); if (b != null) { @@ -265,10 +254,7 @@ public class BindingFactory if (b.isDurable()) { - _configSource.getDurableConfigurationStore().unbindQueue(exchange, - new AMQShortString(bindingKey), - queue, - FieldTable.convertToFieldTable(arguments)); + _virtualHost.getMessageStore().unbindQueue(b); } b.logDestruction(); getConfigStore().removeConfiguredObject(b); @@ -280,20 +266,22 @@ public class BindingFactory public Binding getBinding(String bindingKey, AMQQueue queue, Exchange exchange, Map<String, Object> arguments) { assert queue != null; + final Exchange defaultExchange = _virtualHost.getExchangeRegistry().getDefaultExchange(); + if(bindingKey == null) { bindingKey = ""; } if(exchange == null) { - exchange = _defaultExchange; + exchange = defaultExchange; } if(arguments == null) { arguments = Collections.emptyMap(); } - BindingImpl b = new BindingImpl(bindingKey,queue,exchange,arguments); + BindingImpl b = new BindingImpl(null, bindingKey,queue,exchange,arguments); return _bindings.get(b); } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java b/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java index 259902a938..46027d02c6 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java +++ b/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java @@ -88,6 +88,7 @@ public class ServerConfiguration extends ConfigurationPlugin public static final String MGMT_JMXPORT_CONNECTORSERVER = "management.jmxport.connectorServer"; public static final String STATUS_UPDATES = "status-updates"; public static final String ADVANCED_LOCALE = "advanced.locale"; + public static final String CONNECTOR_AMQP10ENABLED = "connector.amqp10enabled"; public static final String CONNECTOR_AMQP010ENABLED = "connector.amqp010enabled"; public static final String CONNECTOR_AMQP091ENABLED = "connector.amqp091enabled"; public static final String CONNECTOR_AMQP09ENABLED = "connector.amqp09enabled"; @@ -667,6 +668,11 @@ public class ServerConfiguration extends ConfigurationPlugin return getListValue("connector.port", Collections.<Integer>singletonList(DEFAULT_PORT)); } + public List getPortExclude10() + { + return getListValue("connector.non10port"); + } + public List getPortExclude010() { return getListValue("connector.non010port"); @@ -737,7 +743,7 @@ public class ServerConfiguration extends ConfigurationPlugin public String getConnectorKeyManagerFactoryAlgorithm() { final String systemFallback = KeyManagerFactory.getDefaultAlgorithm(); - // deprecated, pre-0.15 brokers supported this name. + // deprecated, pre-0.17 brokers supported this name. final String fallback = getStringValue("connector.ssl.certType", systemFallback); return getStringValue("connector.ssl.keyManagerFactoryAlgorithm", fallback); } @@ -843,6 +849,11 @@ public class ServerConfiguration extends ConfigurationPlugin return getConfig().getString("deadLetterQueueSuffix", AMQQueueFactory.DEFAULT_DLQ_NAME_SUFFIX); } + public boolean isAmqp10enabled() + { + return getConfig().getBoolean(CONNECTOR_AMQP10ENABLED, true); + } + public boolean isAmqp010enabled() { return getConfig().getBoolean(CONNECTOR_AMQP010ENABLED, true); diff --git a/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerNetworkTransportConfiguration.java b/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerNetworkTransportConfiguration.java index 81dfcb4465..f6fe47b996 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerNetworkTransportConfiguration.java +++ b/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerNetworkTransportConfiguration.java @@ -19,22 +19,21 @@ */ package org.apache.qpid.server.configuration; +import java.net.InetSocketAddress; import org.apache.qpid.transport.NetworkTransportConfiguration; public class ServerNetworkTransportConfiguration implements NetworkTransportConfiguration { private final ServerConfiguration _serverConfig; - private final int _port; - private final String _host; private final String _transport; + private InetSocketAddress _address; public ServerNetworkTransportConfiguration(final ServerConfiguration serverConfig, - final int port, final String host, + final InetSocketAddress address, final String transport) { _serverConfig = serverConfig; - _port = port; - _host = host; + _address = address; _transport = transport; } @@ -55,12 +54,12 @@ public class ServerNetworkTransportConfiguration implements NetworkTransportConf public Integer getPort() { - return _port; + return _address.getPort(); } public String getHost() { - return _host; + return _address.getHostName(); } public String getTransport() @@ -72,4 +71,9 @@ public class ServerNetworkTransportConfiguration implements NetworkTransportConf { return _serverConfig.getConnectorProcessors(); } + + public InetSocketAddress getAddress() + { + return _address; + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfig.java b/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfig.java index 9256724c56..b96ddc56c6 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfig.java +++ b/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfig.java @@ -29,6 +29,4 @@ public interface VirtualHostConfig extends ConfiguredObject<VirtualHostConfigTyp String getFederationTag(); - void setBroker(BrokerConfig brokerConfig); - } diff --git a/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java b/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java index 5fd261a081..5f472b6ddd 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java +++ b/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java @@ -32,6 +32,7 @@ import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.store.MemoryMessageStore; +import org.apache.qpid.server.store.MemoryMessageStoreFactory; import java.util.ArrayList; import java.util.Arrays; @@ -42,9 +43,9 @@ import java.util.Map; public class VirtualHostConfiguration extends ConfigurationPlugin { - private String _name; - private Map<String, QueueConfiguration> _queues = new HashMap<String, QueueConfiguration>(); - private Map<String, ExchangeConfiguration> _exchanges = new HashMap<String, ExchangeConfiguration>(); + private final String _name; + private final Map<String, QueueConfiguration> _queues = new HashMap<String, QueueConfiguration>(); + private final Map<String, ExchangeConfiguration> _exchanges = new HashMap<String, ExchangeConfiguration>(); public VirtualHostConfiguration(String name, Configuration config) throws ConfigurationException { @@ -92,11 +93,6 @@ public class VirtualHostConfiguration extends ConfigurationPlugin return getLongValue("housekeeping.checkPeriod", ApplicationRegistry.getInstance().getConfiguration().getHousekeepingCheckPeriod()); } - public String getAuthenticationDatabase() - { - return getStringValue("security.authentication.name"); - } - public List getCustomExchanges() { return getListValue("custom-exchanges.class-name"); @@ -107,14 +103,14 @@ public class VirtualHostConfiguration extends ConfigurationPlugin return getConfig().subset("store"); } - public String getMessageStoreClass() + public String getMessageStoreFactoryClass() { - return getStringValue("store.class", MemoryMessageStore.class.getName()); + return getStringValue("store.factoryclass", MemoryMessageStoreFactory.class.getName()); } - public void setMessageStoreClass(String storeClass) + public void setMessageStoreFactoryClass(String storeFactoryClass) { - getConfig().setProperty("store.class", storeClass); + getConfig().setProperty("store.factoryclass", storeFactoryClass); } public List getExchanges() @@ -253,16 +249,6 @@ public class VirtualHostConfiguration extends ConfigurationPlugin return queueConfig; } - public long getMemoryUsageMaximum() - { - return getLongValue("queues.maximumMemoryUsage"); - } - - public long getMemoryUsageMinimum() - { - return getLongValue("queues.minimumMemoryUsage"); - } - public int getMaximumMessageAge() { return getIntValue("queues.maximumMessageAge"); diff --git a/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java index 9159489299..4a58314f51 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java @@ -46,11 +46,19 @@ public class ConnectionRegistry implements IConnectionRegistry, Closeable /** Close all of the currently open connections. */ public void close() { - _logger.debug("Closing connection registry :" + _registry.size() + " connections."); + close(IConnectionRegistry.BROKER_SHUTDOWN_REPLY_TEXT); + } + + public void close(final String replyText) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Closing connection registry :" + _registry.size() + " connections."); + } while (!_registry.isEmpty()) { AMQConnectionModel connection = _registry.get(0); - closeConnection(connection, AMQConstant.CONNECTION_FORCED, "Broker is shutting down"); + closeConnection(connection, AMQConstant.CONNECTION_FORCED, replyText); } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/connection/IConnectionRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/connection/IConnectionRegistry.java index 89582e5748..954c448b72 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/connection/IConnectionRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/connection/IConnectionRegistry.java @@ -28,12 +28,17 @@ import java.util.List; public interface IConnectionRegistry { + public static final String BROKER_SHUTDOWN_REPLY_TEXT = "Broker is shutting down"; + public static final String VHOST_PASSIVATE_REPLY_TEXT = "Virtual host is being passivated"; + public void initialise(); public void close() throws AMQException; - + + public void close(String replyText) throws AMQException; + public void closeConnection(AMQConnectionModel connection, AMQConstant cause, String message); - + public List<AMQConnectionModel> getConnections(); public void registerConnection(AMQConnectionModel connnection); diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java index cae07046fa..9493f400f2 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java @@ -20,8 +20,6 @@ */ package org.apache.qpid.server.exchange; -import org.apache.log4j.Logger; - import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.server.binding.Binding; @@ -116,7 +114,7 @@ public abstract class AbstractExchange implements Exchange, Managable */ protected abstract AbstractExchangeMBean createMBean() throws JMException; - public void initialise(VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) + public void initialise(UUID id, VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) throws AMQException { _virtualHost = host; @@ -125,10 +123,18 @@ public abstract class AbstractExchange implements Exchange, Managable _autoDelete = autoDelete; _ticket = ticket; - // TODO - fix - _id = getConfigStore().createId(); + _id = id; getConfigStore().addConfiguredObject(this); + createAndRegisterMBean(); + _logSubject = new ExchangeLogSubject(this, this.getVirtualHost()); + + // Log Exchange creation + CurrentActor.get().message(ExchangeMessages.CREATED(String.valueOf(getTypeShortString()), String.valueOf(name), durable)); + } + + private void createAndRegisterMBean() + { try { _exchangeMbean = createMBean(); @@ -136,12 +142,8 @@ public abstract class AbstractExchange implements Exchange, Managable } catch (JMException e) { - getLogger().error(e); + throw new RuntimeException("Failed to register mbean",e); } - _logSubject = new ExchangeLogSubject(this, this.getVirtualHost()); - - // Log Exchange creation - CurrentActor.get().message(ExchangeMessages.CREATED(String.valueOf(getTypeShortString()), String.valueOf(name), durable)); } public ConfigStore getConfigStore() @@ -149,8 +151,6 @@ public abstract class AbstractExchange implements Exchange, Managable return getVirtualHost().getConfigStore(); } - public abstract Logger getLogger(); - public boolean isDurable() { return _durable; @@ -324,8 +324,7 @@ public abstract class AbstractExchange implements Exchange, Managable public Map<String, Object> getArguments() { - // TODO - Fix - return Collections.EMPTY_MAP; + return Collections.emptyMap(); } public UUID getId() diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java index 153419de1b..5058f91995 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java @@ -25,9 +25,11 @@ import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.AMQSecurityException; import org.apache.qpid.AMQUnknownExchangeType; +import org.apache.qpid.exchange.ExchangeDefaults; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.qmf.ManagementExchange; import org.apache.qpid.server.configuration.VirtualHostConfiguration; +import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -35,6 +37,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.UUID; public class DefaultExchangeFactory implements ExchangeFactory { @@ -76,17 +79,29 @@ public class DefaultExchangeFactory implements ExchangeFactory return publicTypes; } - - public Exchange createExchange(String exchange, String type, boolean durable, boolean autoDelete) - throws AMQException + throws AMQException { return createExchange(new AMQShortString(exchange), new AMQShortString(type), durable, autoDelete, 0); } - public Exchange createExchange(AMQShortString exchange, AMQShortString type, boolean durable, boolean autoDelete, - int ticket) + public Exchange createExchange(UUID id, String exchange, String type, boolean durable, boolean autoDelete) + throws AMQException + { + return createExchange(id, new AMQShortString(exchange), new AMQShortString(type), durable, autoDelete, 0); + } + + public Exchange createExchange(AMQShortString exchange, AMQShortString type, boolean durable, + boolean autoDelete, int ticket) + throws AMQException + { + UUID id = UUIDGenerator.generateExchangeUUID(exchange.asString(), _host.getName()); + return createExchange(id, exchange, type, durable, autoDelete, ticket); + } + + public Exchange createExchange(UUID id, AMQShortString exchange, AMQShortString type, boolean durable, + boolean autoDelete, int ticket) throws AMQException { // Check access @@ -102,7 +117,7 @@ public class DefaultExchangeFactory implements ExchangeFactory throw new AMQUnknownExchangeType("Unknown exchange type: " + type,null); } - Exchange e = exchType.newInstance(_host, exchange, durable, ticket, autoDelete); + Exchange e = exchType.newInstance(id, _host, exchange, durable, ticket, autoDelete); return e; } diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java index a5fa9f014e..bf4184bf0b 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java @@ -21,7 +21,6 @@ package org.apache.qpid.server.exchange; import org.apache.log4j.Logger; - import org.apache.qpid.AMQException; import org.apache.qpid.AMQSecurityException; import org.apache.qpid.exchange.ExchangeDefaults; @@ -31,12 +30,13 @@ import org.apache.qpid.server.store.DurableConfigurationStore; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.Collection; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; public class DefaultExchangeRegistry implements ExchangeRegistry { - private static final Logger _log = Logger.getLogger(DefaultExchangeRegistry.class); + private static final Logger LOGGER = Logger.getLogger(DefaultExchangeRegistry.class); /** * Maps from exchange name to exchange instance @@ -59,11 +59,9 @@ public class DefaultExchangeRegistry implements ExchangeRegistry new ExchangeInitialiser().initialise(_host.getExchangeFactory(), this, getDurableConfigurationStore()); } - - public DurableConfigurationStore getDurableConfigurationStore() { - return _host.getDurableConfigurationStore(); + return _host.getMessageStore(); } public void registerExchange(Exchange exchange) throws AMQException @@ -153,4 +151,49 @@ public class DefaultExchangeRegistry implements ExchangeRegistry } } + @Override + public void clearAndUnregisterMbeans() + { + for (final AMQShortString exchangeName : getExchangeNames()) + { + final Exchange exchange = getExchange(exchangeName); + + if (exchange instanceof AbstractExchange) + { + AbstractExchange abstractExchange = (AbstractExchange) exchange; + try + { + abstractExchange.getManagedObject().unregister(); + } + catch (AMQException e) + { + LOGGER.warn("Failed to unregister mbean", e); + } + } + } + _exchangeMap.clear(); + _exchangeMapStr.clear(); + } + + @Override + public synchronized Exchange getExchange(UUID exchangeId) + { + if (exchangeId == null) + { + return getDefaultExchange(); + } + else + { + Collection<Exchange> exchanges = _exchangeMap.values(); + for (Exchange exchange : exchanges) + { + if (exchange.getId().equals(exchangeId)) + { + return exchange; + } + } + return null; + } + } + } diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/DirectExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/DirectExchange.java index 8c0a5001db..af9322764a 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/DirectExchange.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/DirectExchange.java @@ -36,6 +36,7 @@ import javax.management.JMException; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArraySet; @@ -103,14 +104,14 @@ public class DirectExchange extends AbstractExchange return DirectExchange.class; } - public DirectExchange newInstance(VirtualHost host, + public DirectExchange newInstance(UUID id, VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) throws AMQException { DirectExchange exch = new DirectExchange(); - exch.initialise(host,name,durable,ticket,autoDelete); + exch.initialise(id, host,name,durable,ticket,autoDelete); return exch; } @@ -131,12 +132,6 @@ public class DirectExchange extends AbstractExchange return new DirectExchangeMBean(this); } - public Logger getLogger() - { - return _logger; - } - - public List<? extends BaseQueue> doRoute(InboundMessage payload) { diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java index c3ae15f09c..289cb1a923 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java @@ -35,6 +35,8 @@ import org.apache.qpid.server.virtualhost.VirtualHost; import javax.management.JMException; import java.util.Collection; import java.util.List; +import java.util.Map; +import java.util.UUID; public interface Exchange extends ExchangeReferrer, ExchangeConfig { @@ -49,7 +51,7 @@ public interface Exchange extends ExchangeReferrer, ExchangeConfig AMQShortString getTypeShortString(); - void initialise(VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) + void initialise(UUID id, VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) throws AMQException, JMException; boolean isDurable(); @@ -111,6 +113,8 @@ public interface Exchange extends ExchangeReferrer, ExchangeConfig boolean isBound(String bindingKey, AMQQueue queue); + public boolean isBound(String bindingKey, Map<String,Object> arguments, AMQQueue queue); + boolean isBound(String bindingKey); void addCloseTask(Task task); diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeFactory.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeFactory.java index 577da79028..aae4ae89bb 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeFactory.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeFactory.java @@ -25,6 +25,7 @@ import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.server.configuration.VirtualHostConfiguration; import java.util.Collection; +import java.util.UUID; public interface ExchangeFactory @@ -40,4 +41,10 @@ public interface ExchangeFactory Collection<ExchangeType<? extends Exchange>> getPublicCreatableTypes(); Exchange createExchange(String exchange, String type, boolean durable, boolean autoDelete) throws AMQException; + + Exchange createExchange(UUID id, String exchange, String type, boolean durable, boolean autoDelete) throws AMQException; + + Exchange createExchange(UUID id, AMQShortString exchange, AMQShortString type, boolean durable, + boolean autoDelete, int ticket) + throws AMQException; } diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeInitialiser.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeInitialiser.java index 4dfcce7bbe..ba4f57a8e0 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeInitialiser.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeInitialiser.java @@ -20,6 +20,7 @@ */ package org.apache.qpid.server.exchange; + import org.apache.qpid.AMQException; import org.apache.qpid.exchange.ExchangeDefaults; import org.apache.qpid.framing.AMQShortString; @@ -27,7 +28,8 @@ import org.apache.qpid.server.store.DurableConfigurationStore; public class ExchangeInitialiser { - public void initialise(ExchangeFactory factory, ExchangeRegistry registry, DurableConfigurationStore store) throws AMQException{ + public void initialise(ExchangeFactory factory, ExchangeRegistry registry, DurableConfigurationStore store) throws AMQException + { for (ExchangeType<? extends Exchange> type : factory.getRegisteredTypes()) { define (registry, factory, type.getDefaultExchangeName(), type.getName(), store); @@ -44,7 +46,6 @@ public class ExchangeInitialiser { Exchange exchange = f.createExchange(name, type, true, false, 0); r.registerExchange(exchange); - if(exchange.isDurable()) { store.createExchange(exchange); diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeRegistry.java index 18eb37e037..795ae2e140 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeRegistry.java @@ -24,6 +24,7 @@ import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import java.util.Collection; +import java.util.UUID; public interface ExchangeRegistry @@ -51,5 +52,9 @@ public interface ExchangeRegistry Exchange getExchange(String exchangeName); - void unregisterExchange(String exchange, boolean ifUnused) throws ExchangeInUseException, AMQException;; + void unregisterExchange(String exchange, boolean ifUnused) throws ExchangeInUseException, AMQException; + + void clearAndUnregisterMbeans(); + + Exchange getExchange(UUID exchangeId); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeType.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeType.java index ce339c4e29..a01e41f039 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeType.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeType.java @@ -20,6 +20,8 @@ */ package org.apache.qpid.server.exchange; +import java.util.UUID; + import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -29,7 +31,7 @@ public interface ExchangeType<T extends Exchange> { public AMQShortString getName(); public Class<T> getExchangeClass(); - public T newInstance(VirtualHost host, AMQShortString name, + public T newInstance(UUID id, VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) throws AMQException; public AMQShortString getDefaultExchangeName(); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java index 76f86ea1b4..5ebcfd095f 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java @@ -34,6 +34,7 @@ import org.apache.qpid.server.virtualhost.VirtualHost; import javax.management.JMException; import java.util.ArrayList; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; public class FanoutExchange extends AbstractExchange @@ -52,11 +53,6 @@ public class FanoutExchange extends AbstractExchange return new FanoutExchangeMBean(this); } - public Logger getLogger() - { - return _logger; - } - public static final ExchangeType<FanoutExchange> TYPE = new ExchangeType<FanoutExchange>() { @@ -70,14 +66,14 @@ public class FanoutExchange extends AbstractExchange return FanoutExchange.class; } - public FanoutExchange newInstance(VirtualHost host, + public FanoutExchange newInstance(UUID id, VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) throws AMQException { FanoutExchange exch = new FanoutExchange(); - exch.initialise(host, name, durable, ticket, autoDelete); + exch.initialise(id, host, name, durable, ticket, autoDelete); return exch; } diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersBinding.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersBinding.java index 0886ae2ae0..b6f5f973f4 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersBinding.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersBinding.java @@ -39,7 +39,7 @@ class HeadersBinding { private static final Logger _logger = Logger.getLogger(HeadersBinding.class); - private final FieldTable _mappings; + private final Map<String,Object> _mappings; private final Binding _binding; private final Set<String> required = new HashSet<String>(); private final Map<String,Object> matches = new HashMap<String,Object>(); @@ -58,7 +58,7 @@ class HeadersBinding _binding = binding; if(_binding !=null) { - _mappings = FieldTable.convertToFieldTable(_binding.getArguments()); + _mappings = _binding.getArguments(); initMappings(); } else @@ -69,37 +69,23 @@ class HeadersBinding private void initMappings() { - _mappings.processOverElements(new FieldTable.FieldTableElementProcessor() + for(Map.Entry<String, Object> entry : _mappings.entrySet()) { - - public boolean processElement(String propertyName, AMQTypedValue value) + String propertyName = entry.getKey(); + Object value = entry.getValue(); + if (isSpecial(propertyName)) { - if (isSpecial(propertyName)) - { - processSpecial(propertyName, value.getValue()); - } - else if (value.getValue() == null || value.getValue().equals("")) - { - required.add(propertyName); - } - else - { - matches.put(propertyName,value.getValue()); - } - - return true; + processSpecial(propertyName, value); } - - public Object getResult() + else if (value == null || value.equals("")) { - return null; + required.add(propertyName); } - }); - } - - protected FieldTable getMappings() - { - return _mappings; + else + { + matches.put(propertyName,value); + } + } } public Binding getBinding() diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java index de12da74b4..16ba3c0431 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java @@ -37,6 +37,7 @@ import javax.management.JMException; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.Map; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArraySet; @@ -93,12 +94,12 @@ public class HeadersExchange extends AbstractExchange return HeadersExchange.class; } - public HeadersExchange newInstance(VirtualHost host, AMQShortString name, boolean durable, int ticket, + public HeadersExchange newInstance(UUID id, VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) throws AMQException { HeadersExchange exch = new HeadersExchange(); - exch.initialise(host, name, durable, ticket, autoDelete); + exch.initialise(id, host, name, durable, ticket, autoDelete); return exch; } @@ -146,6 +147,33 @@ public class HeadersExchange extends AbstractExchange return new ArrayList<BaseQueue>(queues); } + + public boolean isBound(String bindingKey, Map<String, Object> arguments, AMQQueue queue) + { + CopyOnWriteArraySet<Binding> bindings; + if(bindingKey == null) + { + bindings = new CopyOnWriteArraySet<Binding>(getBindings()); + } + else + { + bindings = _bindingsByKey.get(bindingKey); + } + + if(bindings != null) + { + for(Binding binding : bindings) + { + if(queue == null || binding.getQueue().equals(queue)) + { + return arguments == null ? binding.getArguments() == null : binding.getArguments().equals(arguments); + } + } + } + + return false; + } + public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue) { //fixme isBound here should take the arguements in to consideration. @@ -204,11 +232,6 @@ public class HeadersExchange extends AbstractExchange return new HeadersExchangeMBean(this); } - public Logger getLogger() - { - return _logger; - } - protected void onBind(final Binding binding) { String bindingKey = binding.getBindingKey(); @@ -251,10 +274,11 @@ public class HeadersExchange extends AbstractExchange { bindings.remove(binding); } - + + boolean removedBinding = _bindingHeaderMatchers.remove(new HeadersBinding(binding)); if(_logger.isDebugEnabled()) { - _logger.debug("Removing Binding: " + _bindingHeaderMatchers.remove(new HeadersBinding(binding))); + _logger.debug("Removing Binding: " + removedBinding); } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java index 6d7242a78d..7ea7a41826 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java @@ -20,13 +20,24 @@ */ package org.apache.qpid.server.exchange; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.UUID; +import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; +import javax.management.JMException; import org.apache.log4j.Logger; - import org.apache.qpid.AMQException; import org.apache.qpid.AMQInvalidArgumentException; import org.apache.qpid.common.AMQPFilterTypes; import org.apache.qpid.exchange.ExchangeDefaults; import org.apache.qpid.filter.SelectorParsingException; +import org.apache.qpid.filter.selector.ParseException; import org.apache.qpid.filter.selector.TokenMgrError; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; @@ -36,23 +47,14 @@ import org.apache.qpid.server.exchange.topic.TopicMatcherResult; import org.apache.qpid.server.exchange.topic.TopicNormalizer; import org.apache.qpid.server.exchange.topic.TopicParser; import org.apache.qpid.server.filter.JMSSelectorFilter; -import org.apache.qpid.filter.selector.ParseException; +import org.apache.qpid.server.filter.MessageFilter; import org.apache.qpid.server.message.InboundMessage; +import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.BaseQueue; +import org.apache.qpid.server.queue.Filterable; import org.apache.qpid.server.virtualhost.VirtualHost; -import javax.management.JMException; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.WeakHashMap; -import java.util.concurrent.ConcurrentHashMap; - public class TopicExchange extends AbstractExchange { @@ -69,14 +71,14 @@ public class TopicExchange extends AbstractExchange return TopicExchange.class; } - public TopicExchange newInstance(VirtualHost host, + public TopicExchange newInstance(UUID id, VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) throws AMQException { TopicExchange exch = new TopicExchange(); - exch.initialise(host, name, durable, ticket, autoDelete); + exch.initialise(id, host, name, durable, ticket, autoDelete); return exch; } @@ -122,24 +124,26 @@ public class TopicExchange extends AbstractExchange FieldTable oldArgs = _bindings.get(binding); TopicExchangeResult result = _topicExchangeResults.get(routingKey); - if(argumentsContainSelector(args)) + if(argumentsContainFilter(args)) { - if(argumentsContainSelector(oldArgs)) + if(argumentsContainFilter(oldArgs)) { - result.replaceQueueFilter(queue,createSelectorFilter(oldArgs), createSelectorFilter(args)); + result.replaceQueueFilter(queue, + createMessageFilter(oldArgs, queue), + createMessageFilter(args, queue)); } else { - result.addFilteredQueue(queue,createSelectorFilter(args)); + result.addFilteredQueue(queue, createMessageFilter(args, queue)); result.removeUnfilteredQueue(queue); } } else { - if(argumentsContainSelector(oldArgs)) + if(argumentsContainFilter(oldArgs)) { result.addUnfilteredQueue(queue); - result.removeFilteredQueue(queue, createSelectorFilter(oldArgs)); + result.removeFilteredQueue(queue, createMessageFilter(oldArgs, queue)); } else { @@ -158,9 +162,9 @@ public class TopicExchange extends AbstractExchange if(result == null) { result = new TopicExchangeResult(); - if(argumentsContainSelector(args)) + if(argumentsContainFilter(args)) { - result.addFilteredQueue(queue, createSelectorFilter(args)); + result.addFilteredQueue(queue, createMessageFilter(args, queue)); } else { @@ -171,9 +175,9 @@ public class TopicExchange extends AbstractExchange } else { - if(argumentsContainSelector(args)) + if(argumentsContainFilter(args)) { - result.addFilteredQueue(queue, createSelectorFilter(args)); + result.addFilteredQueue(queue, createMessageFilter(args, queue)); } else { @@ -187,9 +191,28 @@ public class TopicExchange extends AbstractExchange } - private JMSSelectorFilter createSelectorFilter(final FieldTable args) throws AMQInvalidArgumentException + private MessageFilter createMessageFilter(final FieldTable args, AMQQueue queue) throws AMQInvalidArgumentException { + if(argumentsContainNoLocal(args)) + { + MessageFilter filter = new NoLocalFilter(queue); + + if(argumentsContainJMSSelector(args)) + { + filter = new CompoundFilter(filter, createJMSSelectorFilter(args)); + } + return filter; + } + else + { + return createJMSSelectorFilter(args); + } + + } + + private MessageFilter createJMSSelectorFilter(FieldTable args) throws AMQInvalidArgumentException + { final String selectorString = args.getString(AMQPFilterTypes.JMS_SELECTOR.getValue()); WeakReference<JMSSelectorFilter> selectorRef = _selectorCache.get(selectorString); JMSSelectorFilter selector = null; @@ -217,11 +240,25 @@ public class TopicExchange extends AbstractExchange return selector; } - private static boolean argumentsContainSelector(final FieldTable args) + private static boolean argumentsContainFilter(final FieldTable args) { - return args != null && args.containsKey(AMQPFilterTypes.JMS_SELECTOR.getValue()) && args.getString(AMQPFilterTypes.JMS_SELECTOR.getValue()).trim().length() != 0; + return argumentsContainNoLocal(args) || argumentsContainJMSSelector(args); } + private static boolean argumentsContainNoLocal(final FieldTable args) + { + return args != null + && args.containsKey(AMQPFilterTypes.NO_LOCAL.getValue()) + && Boolean.TRUE.equals(args.get(AMQPFilterTypes.NO_LOCAL.getValue())); + } + + private static boolean argumentsContainJMSSelector(final FieldTable args) + { + return args != null && (args.containsKey(AMQPFilterTypes.JMS_SELECTOR.getValue()) + && args.getString(AMQPFilterTypes.JMS_SELECTOR.getValue()).trim().length() != 0); + } + + public ArrayList<BaseQueue> doRoute(InboundMessage payload) { @@ -275,6 +312,28 @@ public class TopicExchange extends AbstractExchange } } + public boolean isBound(String bindingKey, Map<String, Object> arguments, AMQQueue queue) + { + Binding binding = new Binding(null, bindingKey, queue, this, arguments); + if (arguments == null) + { + return _bindings.containsKey(binding); + } + else + { + FieldTable o = _bindings.get(binding); + if (o != null) + { + return arguments.equals(FieldTable.convertToMap(o)); + } + else + { + return false; + } + } + + } + public boolean isBound(AMQShortString routingKey, AMQQueue queue) { return isBound(routingKey, null, queue); @@ -321,11 +380,11 @@ public class TopicExchange extends AbstractExchange result.removeBinding(binding); - if(argumentsContainSelector(bindingArgs)) + if(argumentsContainFilter(bindingArgs)) { try { - result.removeFilteredQueue(binding.getQueue(), createSelectorFilter(bindingArgs)); + result.removeFilteredQueue(binding.getQueue(), createMessageFilter(bindingArgs, binding.getQueue())); } catch (AMQInvalidArgumentException e) { @@ -349,11 +408,6 @@ public class TopicExchange extends AbstractExchange return new TopicExchangeMBean(this); } - public Logger getLogger() - { - return _logger; - } - private Collection<AMQQueue> getMatchedQueues(InboundMessage message, AMQShortString routingKey) { @@ -402,4 +456,96 @@ public class TopicExchange extends AbstractExchange deregisterQueue(binding); } + private static final class NoLocalFilter implements MessageFilter + { + private final AMQQueue _queue; + + public NoLocalFilter(AMQQueue queue) + { + _queue = queue; + } + + public boolean matches(Filterable message) + { + InboundMessage inbound = (InboundMessage) message; + final AMQSessionModel exclusiveOwningSession = _queue.getExclusiveOwningSession(); + return exclusiveOwningSession == null || !exclusiveOwningSession.onSameConnection(inbound); + + } + + @Override + public boolean equals(Object o) + { + if (this == o) + { + return true; + } + + if (o == null || getClass() != o.getClass()) + { + return false; + } + + NoLocalFilter that = (NoLocalFilter) o; + + return _queue == null ? that._queue == null : _queue.equals(that._queue); + } + + @Override + public int hashCode() + { + return _queue != null ? _queue.hashCode() : 0; + } + } + + private static final class CompoundFilter implements MessageFilter + { + private MessageFilter _noLocalFilter; + private MessageFilter _jmsSelectorFilter; + + public CompoundFilter(MessageFilter filter, MessageFilter jmsSelectorFilter) + { + _noLocalFilter = filter; + _jmsSelectorFilter = jmsSelectorFilter; + } + + public boolean matches(Filterable message) + { + return _noLocalFilter.matches(message) && _jmsSelectorFilter.matches(message); + } + + @Override + public boolean equals(Object o) + { + if (this == o) + { + return true; + } + if (o == null || getClass() != o.getClass()) + { + return false; + } + + CompoundFilter that = (CompoundFilter) o; + + if (_jmsSelectorFilter != null ? !_jmsSelectorFilter.equals(that._jmsSelectorFilter) : that._jmsSelectorFilter != null) + { + return false; + } + if (_noLocalFilter != null ? !_noLocalFilter.equals(that._noLocalFilter) : that._noLocalFilter != null) + { + return false; + } + + return true; + } + + @Override + public int hashCode() + { + int result = _noLocalFilter != null ? _noLocalFilter.hashCode() : 0; + result = 31 * result + (_jmsSelectorFilter != null ? _jmsSelectorFilter.hashCode() : 0); + return result; + } + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/federation/Bridge.java b/java/broker/src/main/java/org/apache/qpid/server/federation/Bridge.java index b58802e1ff..c7046f8e53 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/federation/Bridge.java +++ b/java/broker/src/main/java/org/apache/qpid/server/federation/Bridge.java @@ -132,7 +132,7 @@ public class Bridge implements BridgeConfig { try { - brokerLink.getVirtualHost().getDurableConfigurationStore().createBridge(this); + brokerLink.getVirtualHost().getMessageStore().createBridge(this); } catch (AMQStoreException e) { @@ -220,7 +220,7 @@ public class Bridge implements BridgeConfig { try { - brokerLink.getVirtualHost().getDurableConfigurationStore().createBridge(this); + brokerLink.getVirtualHost().getMessageStore().createBridge(this); } catch (AMQStoreException e) { @@ -767,13 +767,13 @@ public class Bridge implements BridgeConfig try { - _queue = AMQQueueFactory.createAMQQueueImpl(_tmpQueueName, + _queue = AMQQueueFactory.createAMQQueueImpl(null, + _tmpQueueName, isDurable(), _link.getFederationTag(), false, false, - getVirtualHost(), - options); + getVirtualHost(), options); } catch (AMQException e) { diff --git a/java/broker/src/main/java/org/apache/qpid/server/federation/BrokerLink.java b/java/broker/src/main/java/org/apache/qpid/server/federation/BrokerLink.java index 032df8bb0d..4bcc0d6136 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/federation/BrokerLink.java +++ b/java/broker/src/main/java/org/apache/qpid/server/federation/BrokerLink.java @@ -250,7 +250,7 @@ public class BrokerLink implements LinkConfig, ConnectionListener { try { - _virtualHost.getDurableConfigurationStore().createBrokerLink(this); + _virtualHost.getMessageStore().createBrokerLink(this); } catch (AMQStoreException e) { @@ -295,7 +295,7 @@ public class BrokerLink implements LinkConfig, ConnectionListener { try { - _virtualHost.getDurableConfigurationStore().createBrokerLink(this); + _virtualHost.getMessageStore().createBrokerLink(this); } catch (AMQStoreException e) { diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/SimpleFilterManager.java b/java/broker/src/main/java/org/apache/qpid/server/filter/SimpleFilterManager.java index 18a0e4c8bf..6c158de8b5 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/filter/SimpleFilterManager.java +++ b/java/broker/src/main/java/org/apache/qpid/server/filter/SimpleFilterManager.java @@ -39,6 +39,12 @@ public class SimpleFilterManager implements FilterManager _filters = new ConcurrentLinkedQueue<MessageFilter>(); } + public SimpleFilterManager(JMSSelectorFilter messageFilter) + { + this(); + add(messageFilter); + } + public void add(MessageFilter filter) { _filters.add(filter); diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java index 79fcfb6d76..eab28ac9d4 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java @@ -32,6 +32,7 @@ import org.apache.qpid.server.protocol.AMQProtocolSession; import org.apache.qpid.server.state.AMQState; import org.apache.qpid.server.state.AMQStateManager; import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.virtualhost.State; import org.apache.qpid.server.virtualhost.VirtualHost; public class ConnectionOpenMethodHandler implements StateAwareMethodListener<ConnectionOpenBody> @@ -82,6 +83,10 @@ public class ConnectionOpenMethodHandler implements StateAwareMethodListener<Con { throw body.getConnectionException(AMQConstant.ACCESS_REFUSED, "Permission denied: '" + virtualHost.getName() + "'"); } + else if (virtualHost.getState() != State.ACTIVE) + { + throw body.getConnectionException(AMQConstant.CONNECTION_FORCED, "Virtual host '" + virtualHost.getName() + "' is not active"); + } session.setVirtualHost(virtualHost); @@ -89,10 +94,10 @@ public class ConnectionOpenMethodHandler implements StateAwareMethodListener<Con if (session.getContextKey() == null) { session.setContextKey(generateClientID()); - } + } MethodRegistry methodRegistry = session.getMethodRegistry(); - AMQMethodBody responseBody = methodRegistry.createConnectionOpenOkBody(body.getVirtualHost()); + AMQMethodBody responseBody = methodRegistry.createConnectionOpenOkBody(body.getVirtualHost()); stateManager.changeState(AMQState.CONNECTION_OPEN); diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeclareHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeclareHandler.java index 81e2e5557a..8756409f64 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeclareHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeclareHandler.java @@ -26,6 +26,7 @@ import org.apache.qpid.AMQConnectionException; import org.apache.qpid.AMQException; import org.apache.qpid.AMQUnknownExchangeType; import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.ExchangeDeclareBody; import org.apache.qpid.framing.MethodRegistry; import org.apache.qpid.protocol.AMQConstant; @@ -65,26 +66,39 @@ public class ExchangeDeclareHandler implements StateAwareMethodListener<Exchange throw body.getChannelNotFoundException(channelId); } + final AMQShortString exchangeName = body.getExchange(); if (_logger.isDebugEnabled()) { - _logger.debug("Request to declare exchange of type " + body.getType() + " with name " + body.getExchange()); + _logger.debug("Request to declare exchange of type " + body.getType() + " with name " + exchangeName); } synchronized(exchangeRegistry) { - Exchange exchange = exchangeRegistry.getExchange(body.getExchange()); + Exchange exchange = exchangeRegistry.getExchange(exchangeName); if (exchange == null) { if(body.getPassive() && ((body.getType() == null) || body.getType().length() ==0)) { - throw body.getChannelException(AMQConstant.NOT_FOUND, "Unknown exchange: " + body.getExchange()); + throw body.getChannelException(AMQConstant.NOT_FOUND, "Unknown exchange: " + exchangeName); + } + else if(exchangeName.startsWith("amq.")) + { + throw body.getConnectionException(AMQConstant.NOT_ALLOWED, + "Attempt to declare exchange: " + exchangeName + + " which begins with reserved prefix 'amq.'."); + } + else if(exchangeName.startsWith("qpid.")) + { + throw body.getConnectionException(AMQConstant.NOT_ALLOWED, + "Attempt to declare exchange: " + exchangeName + + " which begins with reserved prefix 'qpid.'."); } else { try { - exchange = exchangeFactory.createExchange(body.getExchange() == null ? null : body.getExchange().intern(), + exchange = exchangeFactory.createExchange(exchangeName == null ? null : exchangeName.intern(), body.getType() == null ? null : body.getType().intern(), body.getDurable(), body.getPassive(), body.getTicket()); @@ -92,19 +106,20 @@ public class ExchangeDeclareHandler implements StateAwareMethodListener<Exchange if (exchange.isDurable()) { - virtualHost.getDurableConfigurationStore().createExchange(exchange); + virtualHost.getMessageStore().createExchange(exchange); } } catch(AMQUnknownExchangeType e) { - throw body.getConnectionException(AMQConstant.COMMAND_INVALID, "Unknown exchange: " + body.getExchange(),e); + throw body.getConnectionException(AMQConstant.COMMAND_INVALID, "Unknown exchange: " + exchangeName,e); } } } else if (!exchange.getTypeShortString().equals(body.getType()) && !((body.getType() == null || body.getType().length() ==0) && body.getPassive())) { - throw new AMQConnectionException(AMQConstant.NOT_ALLOWED, "Attempt to redeclare exchange: " + body.getExchange() + " of type " + exchange.getTypeShortString() + " to " + body.getType() +".",body.getClazz(), body.getMethod(),body.getMajor(),body.getMinor(),null); + throw new AMQConnectionException(AMQConstant.NOT_ALLOWED, "Attempt to redeclare exchange: " + + exchangeName + " of type " + exchange.getTypeShortString() + " to " + body.getType() +".",body.getClazz(), body.getMethod(),body.getMajor(),body.getMinor(),null); } } if(!body.getNowait()) diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java index bb979d5441..49ca934966 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java @@ -134,7 +134,7 @@ public class QueueBindHandler implements StateAwareMethodListener<QueueBindBody> Map<String, Object> oldArgs = oldBinding.getArguments(); if((oldArgs == null && !arguments.isEmpty()) || (oldArgs != null && !oldArgs.equals(arguments))) { - virtualHost.getBindingFactory().replaceBinding(bindingKey, queue, exch, arguments); + virtualHost.getBindingFactory().replaceBinding(oldBinding.getId(), bindingKey, queue, exch, arguments); } } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java index f57f7eb9e6..396829df91 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java @@ -24,6 +24,7 @@ import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; import org.apache.qpid.framing.MethodRegistry; import org.apache.qpid.framing.QueueDeclareBody; import org.apache.qpid.framing.QueueDeclareOkBody; @@ -31,6 +32,7 @@ import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.server.AMQChannel; import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.protocol.AMQProtocolSession; import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.queue.AMQQueue; @@ -43,6 +45,7 @@ import org.apache.qpid.server.store.DurableConfigurationStore; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.Collections; +import java.util.Map; import java.util.UUID; public class QueueDeclareHandler implements StateAwareMethodListener<QueueDeclareBody> @@ -65,7 +68,7 @@ public class QueueDeclareHandler implements StateAwareMethodListener<QueueDeclar VirtualHost virtualHost = protocolConnection.getVirtualHost(); ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry(); QueueRegistry queueRegistry = virtualHost.getQueueRegistry(); - DurableConfigurationStore store = virtualHost.getDurableConfigurationStore(); + DurableConfigurationStore store = virtualHost.getMessageStore(); final AMQShortString queueName; @@ -219,10 +222,11 @@ public class QueueDeclareHandler implements StateAwareMethodListener<QueueDeclar throws AMQException { final QueueRegistry registry = virtualHost.getQueueRegistry(); - AMQShortString owner = body.getExclusive() ? session.getContextKey() : null; + String owner = body.getExclusive() ? AMQShortString.toString(session.getContextKey()) : null; - final AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(queueName, body.getDurable(), owner, body.getAutoDelete(), - body.getExclusive(),virtualHost, body.getArguments()); + Map<String, Object> arguments = FieldTable.convertToMap(body.getArguments()); + final AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(UUIDGenerator.generateUUID(), AMQShortString.toString(queueName), body.getDurable(), owner, body.getAutoDelete(), + body.getExclusive(),virtualHost, arguments); if (body.getExclusive() && !body.getDurable()) { diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java index cc37259d54..762f090b83 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java @@ -62,7 +62,7 @@ public class QueueDeleteHandler implements StateAwareMethodListener<QueueDeleteB AMQProtocolSession protocolConnection = stateManager.getProtocolSession(); VirtualHost virtualHost = protocolConnection.getVirtualHost(); QueueRegistry queueRegistry = virtualHost.getQueueRegistry(); - DurableConfigurationStore store = virtualHost.getDurableConfigurationStore(); + DurableConfigurationStore store = virtualHost.getMessageStore(); AMQChannel channel = protocolConnection.getChannel(channelId); diff --git a/java/broker/src/main/java/org/apache/qpid/server/logging/actors/CurrentActor.java b/java/broker/src/main/java/org/apache/qpid/server/logging/actors/CurrentActor.java index feacf35d41..97134515a0 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/logging/actors/CurrentActor.java +++ b/java/broker/src/main/java/org/apache/qpid/server/logging/actors/CurrentActor.java @@ -20,11 +20,13 @@ */ package org.apache.qpid.server.logging.actors; -import org.apache.qpid.server.logging.LogActor; - import java.util.EmptyStackException; import java.util.Stack; +import org.apache.qpid.server.logging.LogActor; +import org.apache.qpid.server.logging.LogMessage; +import org.apache.qpid.server.logging.LogSubject; + /** * The CurrentActor is a ThreadLocal wrapper that allows threads in the broker * to retrieve an actor to perform logging. This approach is used so for two @@ -126,4 +128,14 @@ public class CurrentActor { _defaultActor = defaultActor; } + + public static void message(LogSubject subject, LogMessage message) + { + get().message(subject, message); + } + + public static void message(LogMessage message) + { + get().message(message); + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/logging/messages/ConfigStore_logmessages.properties b/java/broker/src/main/java/org/apache/qpid/server/logging/messages/ConfigStore_logmessages.properties index 3bc5074777..541f8b8c68 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/logging/messages/ConfigStore_logmessages.properties +++ b/java/broker/src/main/java/org/apache/qpid/server/logging/messages/ConfigStore_logmessages.properties @@ -18,8 +18,7 @@ # # Default File used for all non-defined locales. -# 0 - name -CREATED = CFG-1001 : Created : {0} +CREATED = CFG-1001 : Created # 0 - path STORE_LOCATION = CFG-1002 : Store location : {0} CLOSE = CFG-1003 : Closed diff --git a/java/broker/src/main/java/org/apache/qpid/server/logging/messages/MessageStore_logmessages.properties b/java/broker/src/main/java/org/apache/qpid/server/logging/messages/MessageStore_logmessages.properties index a2cedeb22a..081f2bbca3 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/logging/messages/MessageStore_logmessages.properties +++ b/java/broker/src/main/java/org/apache/qpid/server/logging/messages/MessageStore_logmessages.properties @@ -18,11 +18,11 @@ # # Default File used for all non-defined locales. # -# 0 - name -CREATED = MST-1001 : Created : {0} +CREATED = MST-1001 : Created # 0 - path STORE_LOCATION = MST-1002 : Store location : {0} CLOSED = MST-1003 : Closed RECOVERY_START = MST-1004 : Recovery Start RECOVERED = MST-1005 : Recovered {0,number} messages -RECOVERY_COMPLETE = MST-1006 : Recovery Complete
\ No newline at end of file +RECOVERY_COMPLETE = MST-1006 : Recovery Complete +PASSIVATE = MST-1007 : Store Passivated diff --git a/java/broker/src/main/java/org/apache/qpid/server/logging/messages/TransactionLog_logmessages.properties b/java/broker/src/main/java/org/apache/qpid/server/logging/messages/TransactionLog_logmessages.properties index fadc2e2098..b9e87159a6 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/logging/messages/TransactionLog_logmessages.properties +++ b/java/broker/src/main/java/org/apache/qpid/server/logging/messages/TransactionLog_logmessages.properties @@ -19,8 +19,7 @@ # Default File used for all non-defined locales. # # -# 0 - name -CREATED = TXN-1001 : Created : {0} +CREATED = TXN-1001 : Created # 0 - path STORE_LOCATION = TXN-1002 : Store location : {0} CLOSED = TXN-1003 : Closed @@ -31,3 +30,9 @@ RECOVERY_START = TXN-1004 : Recovery Start[ : {0}] RECOVERED = TXN-1005 : Recovered {0,number} messages for queue {1} # 0 - queue name RECOVERY_COMPLETE = TXN-1006 : Recovery Complete[ : {0}] +# 0 - xid +# 1 - queue name +XA_INCOMPLETE_QUEUE = TXN-1007 : XA transaction recover for xid {0} incomplete as it references a queue {1} which was not durably retained +# 0 - xid format +# 1 - message id +XA_INCOMPLETE_MESSAGE = TXN-1008 : XA transaction recover for xid {0} incomplete as it references a message {1} which was not durably retained diff --git a/java/broker/src/main/java/org/apache/qpid/server/logging/subjects/BindingLogSubject.java b/java/broker/src/main/java/org/apache/qpid/server/logging/subjects/BindingLogSubject.java index 8f0b9182a9..6f18cbcc6b 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/logging/subjects/BindingLogSubject.java +++ b/java/broker/src/main/java/org/apache/qpid/server/logging/subjects/BindingLogSubject.java @@ -40,7 +40,8 @@ public class BindingLogSubject extends AbstractLogSubject public BindingLogSubject(String routingKey, Exchange exchange, AMQQueue queue) { - setLogStringWithFormat(BINDING_FORMAT, queue.getVirtualHost().getName(), + setLogStringWithFormat(BINDING_FORMAT, + queue.getVirtualHost().getName(), exchange.getTypeShortString(), exchange.getNameShortString(), queue.getNameShortString(), diff --git a/java/broker/src/main/java/org/apache/qpid/server/logging/subjects/MessageStoreLogSubject.java b/java/broker/src/main/java/org/apache/qpid/server/logging/subjects/MessageStoreLogSubject.java index 969288be00..08963bd8f1 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/logging/subjects/MessageStoreLogSubject.java +++ b/java/broker/src/main/java/org/apache/qpid/server/logging/subjects/MessageStoreLogSubject.java @@ -20,7 +20,6 @@ */ package org.apache.qpid.server.logging.subjects; -import org.apache.qpid.server.store.MessageStore; import org.apache.qpid.server.virtualhost.VirtualHost; import static org.apache.qpid.server.logging.subjects.LogSubjectFormat.STORE_FORMAT; @@ -28,10 +27,9 @@ import static org.apache.qpid.server.logging.subjects.LogSubjectFormat.STORE_FOR public class MessageStoreLogSubject extends AbstractLogSubject { - /** Create an ExchangeLogSubject that Logs in the following format. */ - public MessageStoreLogSubject(VirtualHost vhost, MessageStore store) + /** Create an MessageStoreLogSubject that Logs in the following format. */ + public MessageStoreLogSubject(VirtualHost vhost, String messageStoreName) { - setLogStringWithFormat(STORE_FORMAT, vhost.getName(), - store.getClass().getSimpleName()); + setLogStringWithFormat(STORE_FORMAT, vhost.getName(), messageStoreName); } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/management/AbstractAMQManagedConnectionObject.java b/java/broker/src/main/java/org/apache/qpid/server/management/AbstractAMQManagedConnectionObject.java index e7c07b6dd4..6cfc827046 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/management/AbstractAMQManagedConnectionObject.java +++ b/java/broker/src/main/java/org/apache/qpid/server/management/AbstractAMQManagedConnectionObject.java @@ -1,3 +1,23 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ package org.apache.qpid.server.management; import org.apache.qpid.management.common.mbeans.ManagedConnection; diff --git a/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java index b6623bbd09..04a5b27991 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java @@ -157,9 +157,7 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry if (!ksf.exists()) { - throw new FileNotFoundException("Cannot find JMX management SSL keystore file " + ksf + "\n" - + "Check broker configuration, or see create-example-ssl-stores script" - + "in the bin/ directory if you need to generate an example store."); + throw new FileNotFoundException("Cannot find JMX management SSL keystore file: " + ksf); } if (!ksf.canRead()) { diff --git a/java/broker/src/main/java/org/apache/qpid/server/message/MessageMetaData_0_10.java b/java/broker/src/main/java/org/apache/qpid/server/message/MessageMetaData_0_10.java index 805094a061..88b0f60346 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/message/MessageMetaData_0_10.java +++ b/java/broker/src/main/java/org/apache/qpid/server/message/MessageMetaData_0_10.java @@ -50,6 +50,7 @@ public class MessageMetaData_0_10 implements StorableMessageMetaData, InboundMes public static final MessageMetaDataType.Factory<MessageMetaData_0_10> FACTORY = new MetaDataFactory(); private volatile ByteBuffer _encoded; + private Object _connectionReference; public MessageMetaData_0_10(MessageTransfer xfr) @@ -219,6 +220,16 @@ public class MessageMetaData_0_10 implements StorableMessageMetaData, InboundMes return _header; } + public void setConnectionReference(Object connectionReference) + { + _connectionReference = connectionReference; + } + + public Object getConnectionReference() + { + return _connectionReference; + } + private static class MetaDataFactory implements MessageMetaDataType.Factory<MessageMetaData_0_10> { public MessageMetaData_0_10 createMetaData(ByteBuffer buf) diff --git a/java/broker/src/main/java/org/apache/qpid/server/message/MessageMetaData_1_0.java b/java/broker/src/main/java/org/apache/qpid/server/message/MessageMetaData_1_0.java new file mode 100755 index 0000000000..7d030fe711 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/message/MessageMetaData_1_0.java @@ -0,0 +1,522 @@ +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*/ +package org.apache.qpid.server.message; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.apache.qpid.amqp_1_0.codec.ValueHandler; +import org.apache.qpid.amqp_1_0.messaging.SectionDecoder; +import org.apache.qpid.amqp_1_0.type.AmqpErrorException; +import org.apache.qpid.amqp_1_0.type.Section; +import org.apache.qpid.amqp_1_0.type.Symbol; +import org.apache.qpid.amqp_1_0.type.codec.AMQPDescribedTypeRegistry; +import org.apache.qpid.amqp_1_0.type.messaging.AmqpSequence; +import org.apache.qpid.amqp_1_0.type.messaging.AmqpValue; +import org.apache.qpid.amqp_1_0.type.messaging.ApplicationProperties; +import org.apache.qpid.amqp_1_0.type.messaging.Data; +import org.apache.qpid.amqp_1_0.type.messaging.DeliveryAnnotations; +import org.apache.qpid.amqp_1_0.type.messaging.Footer; +import org.apache.qpid.amqp_1_0.type.messaging.Header; +import org.apache.qpid.amqp_1_0.type.messaging.MessageAnnotations; +import org.apache.qpid.amqp_1_0.type.messaging.Properties; +import org.apache.qpid.server.store.MessageMetaDataType; +import org.apache.qpid.server.store.StorableMessageMetaData; + +public class MessageMetaData_1_0 implements StorableMessageMetaData +{ + // TODO move to somewhere more useful + public static final Symbol JMS_TYPE = Symbol.valueOf("x-opt-jms-type"); + + + private Header _header; + private Properties _properties; + private Map _deliveryAnnotations; + private Map _messageAnnotations; + private Map _appProperties; + private Map _footer; + + private List<ByteBuffer> _encodedSections = new ArrayList<ByteBuffer>(3); + + private volatile ByteBuffer _encoded; + private MessageHeader_1_0 _messageHeader; + + + + public MessageMetaData_1_0(ByteBuffer[] fragments, SectionDecoder decoder) + { + this(fragments, decoder, new ArrayList<ByteBuffer>(3)); + } + + public MessageMetaData_1_0(ByteBuffer[] fragments, SectionDecoder decoder, List<ByteBuffer> immuatableSections) + { + this(constructSections(fragments, decoder,immuatableSections), immuatableSections); + } + + private MessageMetaData_1_0(List<Section> sections, List<ByteBuffer> encodedSections) + { + _encodedSections = encodedSections; + + Iterator<Section> sectIter = sections.iterator(); + + Section section = sectIter.hasNext() ? sectIter.next() : null; + if(section instanceof Header) + { + _header = (Header) section; + section = sectIter.hasNext() ? sectIter.next() : null; + } + + if(section instanceof DeliveryAnnotations) + { + _deliveryAnnotations = ((DeliveryAnnotations) section).getValue(); + section = sectIter.hasNext() ? sectIter.next() : null; + } + + if(section instanceof MessageAnnotations) + { + _messageAnnotations = ((MessageAnnotations) section).getValue(); + section = sectIter.hasNext() ? sectIter.next() : null; + } + + if(section instanceof Properties) + { + _properties = (Properties) section; + section = sectIter.hasNext() ? sectIter.next() : null; + } + + if(section instanceof ApplicationProperties) + { + _appProperties = ((ApplicationProperties) section).getValue(); + section = sectIter.hasNext() ? sectIter.next() : null; + } + + if(section instanceof Footer) + { + _footer = ((Footer) section).getValue(); + section = sectIter.hasNext() ? sectIter.next() : null; + } + + _messageHeader = new MessageHeader_1_0(); + + } + + private static List<Section> constructSections(final ByteBuffer[] fragments, final SectionDecoder decoder, List<ByteBuffer> encodedSections) + { + List<Section> sections = new ArrayList<Section>(3); + + ByteBuffer src; + if(fragments.length == 1) + { + src = fragments[0].duplicate(); + } + else + { + int size = 0; + for(ByteBuffer buf : fragments) + { + size += buf.remaining(); + } + src = ByteBuffer.allocate(size); + for(ByteBuffer buf : fragments) + { + src.put(buf.duplicate()); + } + src.flip(); + + } + + try + { + int startBarePos = -1; + int lastPos = src.position(); + Section s = decoder.readSection(src); + + + + if(s instanceof Header) + { + sections.add(s); + lastPos = src.position(); + s = src.hasRemaining() ? decoder.readSection(src) : null; + } + + if(s instanceof DeliveryAnnotations) + { + sections.add(s); + lastPos = src.position(); + s = src.hasRemaining() ? decoder.readSection(src) : null; + } + + if(s instanceof MessageAnnotations) + { + sections.add(s); + lastPos = src.position(); + s = src.hasRemaining() ? decoder.readSection(src) : null; + } + + if(s instanceof Properties) + { + sections.add(s); + if(startBarePos == -1) + { + startBarePos = lastPos; + } + s = src.hasRemaining() ? decoder.readSection(src) : null; + } + + if(s instanceof ApplicationProperties) + { + sections.add(s); + if(startBarePos == -1) + { + startBarePos = lastPos; + } + s = src.hasRemaining() ? decoder.readSection(src) : null; + } + + if(s instanceof AmqpValue) + { + if(startBarePos == -1) + { + startBarePos = lastPos; + } + s = src.hasRemaining() ? decoder.readSection(src) : null; + } + else if(s instanceof Data) + { + if(startBarePos == -1) + { + startBarePos = lastPos; + } + do + { + s = src.hasRemaining() ? decoder.readSection(src) : null; + } while(s instanceof Data); + } + else if(s instanceof AmqpSequence) + { + if(startBarePos == -1) + { + startBarePos = lastPos; + } + do + { + s = src.hasRemaining() ? decoder.readSection(src) : null; + } + while(s instanceof AmqpSequence); + } + + if(s instanceof Footer) + { + sections.add(s); + } + + + int pos = 0; + for(ByteBuffer buf : fragments) + { +/* + if(pos < startBarePos) + { + if(pos + buf.remaining() > startBarePos) + { + ByteBuffer dup = buf.duplicate(); + dup.position(dup.position()+startBarePos-pos); + dup.slice(); + encodedSections.add(dup); + } + } + else +*/ + { + encodedSections.add(buf.duplicate()); + } + pos += buf.remaining(); + } + + return sections; + } + catch (AmqpErrorException e) + { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + throw new IllegalArgumentException(e); + } + } + + + public MessageMetaDataType getType() + { + return MessageMetaDataType.META_DATA_1_0; + } + + + public int getStorableSize() + { + int size = 0; + + for(ByteBuffer bin : _encodedSections) + { + size += bin.limit(); + } + + return size; + } + + private ByteBuffer encodeAsBuffer() + { + ByteBuffer buf = ByteBuffer.allocate(getStorableSize()); + + for(ByteBuffer bin : _encodedSections) + { + buf.put(bin.duplicate()); + } + + return buf; + } + + public int writeToBuffer(int offsetInMetaData, ByteBuffer dest) + { + ByteBuffer buf = _encoded; + + if(buf == null) + { + buf = encodeAsBuffer(); + _encoded = buf; + } + + buf = buf.duplicate(); + + buf.position(offsetInMetaData); + + if(dest.remaining() < buf.limit()) + { + buf.limit(dest.remaining()); + } + dest.put(buf); + return buf.limit(); + } + + public int getContentSize() + { + ByteBuffer buf = _encoded; + + if(buf == null) + { + buf = encodeAsBuffer(); + _encoded = buf; + } + return buf.remaining(); + } + + public boolean isPersistent() + { + return _header != null && Boolean.TRUE.equals(_header.getDurable()); + } + + public MessageHeader_1_0 getMessageHeader() + { + return _messageHeader; + } + + public static final MessageMetaDataType.Factory<MessageMetaData_1_0> FACTORY = new MetaDataFactory(); + + + private static class MetaDataFactory implements MessageMetaDataType.Factory<MessageMetaData_1_0> + { + private final AMQPDescribedTypeRegistry _typeRegistry = AMQPDescribedTypeRegistry.newInstance(); + + public MessageMetaData_1_0 createMetaData(ByteBuffer buf) + { + ValueHandler valueHandler = new ValueHandler(_typeRegistry); + + ArrayList<Section> sections = new ArrayList<Section>(3); + ArrayList<ByteBuffer> encodedSections = new ArrayList<ByteBuffer>(3); + + while(buf.hasRemaining()) + { + try + { + ByteBuffer encodedBuf = buf.duplicate(); + sections.add((Section) valueHandler.parse(buf)); + encodedBuf.limit(buf.position()); + encodedSections.add(encodedBuf); + + } + catch (AmqpErrorException e) + { + //TODO + throw new RuntimeException(e); + } + + } + + return new MessageMetaData_1_0(sections,encodedSections); + + } + } + + public class MessageHeader_1_0 implements AMQMessageHeader + { + + public String getCorrelationId() + { + if(_properties == null || _properties.getCorrelationId() == null) + { + return null; + } + else + { + return _properties.getMessageId().toString(); + } + } + + public long getExpiration() + { + return 0; //TODO + } + + public String getMessageId() + { + if(_properties == null || _properties.getCorrelationId() == null) + { + return null; + } + else + { + return _properties.getCorrelationId().toString(); + } + } + + public String getMimeType() + { + + if(_properties == null || _properties.getContentType() == null) + { + return null; + } + else + { + return _properties.getContentType().toString(); + } + } + + public String getEncoding() + { + return null; //TODO + } + + public byte getPriority() + { + if(_header == null || _header.getPriority() == null) + { + return 4; //javax.jms.Message.DEFAULT_PRIORITY; + } + else + { + return _header.getPriority().byteValue(); + } + } + + public long getTimestamp() + { + if(_properties == null || _properties.getCreationTime() == null) + { + return 0L; + } + else + { + return _properties.getCreationTime().getTime(); + } + + } + + public String getType() + { + + if(_messageAnnotations == null || _messageAnnotations.get(JMS_TYPE) == null) + { + return null; + } + else + { + return _messageAnnotations.get(JMS_TYPE).toString(); + } + } + + public String getReplyTo() + { + if(_properties == null || _properties.getReplyTo() == null) + { + return null; + } + else + { + return _properties.getReplyTo().toString(); + } + } + + public String getReplyToExchange() + { + return null; //TODO + } + + public String getReplyToRoutingKey() + { + return null; //TODO + } + + public Object getHeader(final String name) + { + return _appProperties == null ? null : _appProperties.get(name); + } + + public boolean containsHeaders(final Set<String> names) + { + if(_appProperties == null) + { + return false; + } + + for(String key : names) + { + if(!_appProperties.containsKey(key)) + { + return false; + } + } + return true; + } + + public boolean containsHeader(final String name) + { + return _appProperties != null && _appProperties.containsKey(name); + } + + public String getSubject() + { + return _properties == null ? null : _properties.getSubject(); + } + } + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/model/Binding.java b/java/broker/src/main/java/org/apache/qpid/server/model/Binding.java new file mode 100644 index 0000000000..fdb009386c --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/model/Binding.java @@ -0,0 +1,75 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.model; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +public interface Binding extends ConfiguredObject +{ + + public String MATCHED_BYTES = "matchedBytes"; + public String MATCHED_MESSAGES = "matchedMessages"; + public String STATE_CHANGED = "stateChanged"; + + public static final Collection<String> AVAILABLE_STATISTICS = + Collections.unmodifiableCollection( + Arrays.asList( + MATCHED_BYTES, + MATCHED_MESSAGES, + STATE_CHANGED)); + + + public String ARGUMENTS = "arguments"; + public String CREATED = "created"; + public String DURABLE = "durable"; + public String ID = "id"; + public String LIFETIME_POLICY = "lifetimePolicy"; + public String NAME = "name"; + public String STATE = "state"; + public String TIME_TO_LIVE = "timeToLive"; + public String UPDATED = "updated"; + public String QUEUE = "queue"; + public String EXCHANGE = "exchange"; + + public static final Collection<String> AVAILABLE_ATTRIBUTES = + Collections.unmodifiableCollection( + Arrays.asList(ID, + NAME, + STATE, + DURABLE, + LIFETIME_POLICY, + TIME_TO_LIVE, + CREATED, + UPDATED, + EXCHANGE, + QUEUE, + ARGUMENTS) + ); + + + + Map<String,Object> getArguments(); + + void delete(); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/model/ConfigurationChangeListener.java b/java/broker/src/main/java/org/apache/qpid/server/model/ConfigurationChangeListener.java new file mode 100644 index 0000000000..6477633a9b --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/model/ConfigurationChangeListener.java @@ -0,0 +1,38 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.model; + +public interface ConfigurationChangeListener +{ + /** + * Inform the listener that the passed object has changed state + * + * @param object the object whose state has changed + * @param oldState the state prior to the change + * @param newState the state after the change + */ + void stateChanged(ConfiguredObject object, State oldState, State newState); + + void childAdded(ConfiguredObject object, ConfiguredObject child); + + void childRemoved(ConfiguredObject object, ConfiguredObject child); + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java b/java/broker/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java new file mode 100644 index 0000000000..fb47a54d0a --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java @@ -0,0 +1,229 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.model; + +import java.security.AccessControlException; +import java.util.Collection; +import java.util.UUID; + +public interface ConfiguredObject +{ + + /** + * Get the universally unique identifier for the object + * + * @return the objects id + */ + UUID getId(); + + /** + * Get the name of the object + * + * @return the name of the object + */ + String getName(); + + + /** + * Attempt to change the name of the object + * + * Request a change to the name of the object. The caller must pass in the name it believes the object currently + * has. If the current name differes from this expected value, then no name change will occur + * + * @param currentName the name the caller believes the object to have + * @param desiredName the name the caller would like the object to have + * @return the new name for the object + * @throws IllegalStateException if the name of the object may not be changed in in the current state + * @throws AccessControlException if the current context does not have permission to change the name + * @throws IllegalArgumentException if the provided name is not legal + * @throws NullPointerException if the desired name is null + */ + String setName(String currentName, String desiredName) throws IllegalStateException, + AccessControlException; + + + /** + * Get the desired state of the object. + * + * This is the state set at the object itself, however the object + * may not be able attain this state if one of its ancestors is in a different state (in particular a descendant + * object may not be ACTIVE if all of its ancestors are not also ACTIVE). + * + * @return the desired state of the object + */ + State getDesiredState(); + + /** + * Change the desired state of the object + * + * Request a change to the current state. The caller must pass in the state it believe the object to be in, if + * this differs from the current desired state when the object evalues the request, then no state change will occur. + * + * @param currentState the state the caller believes the object to be in + * @param desiredState the state the caller wishes the object to attain + * @return the new current state + * @throws IllegalStateTransitionException the requested state tranisition is invalid + * @throws AccessControlException the current context does not have sufficeint permissions to change the state + */ + State setDesiredState(State currentState, State desiredState) throws IllegalStateTransitionException, + AccessControlException; + + /** + * Get the actual state of the object. + * + * This state is derived fromt the desired state of the object itself and + * the actual state of its parents. If an object "desires" to be ACTIVE, but one of its parents is STOPPED, then + * the actual state of the object will be STOPPED + * + * @return the actual state of the object + */ + State getActualState(); + + + /** + * Add a listener which will be informed of all changes to this configuration object + * + * @param listener the listener to add + */ + void addChangeListener(ConfigurationChangeListener listener); + + /** + * Remove a change listener + * + * + * @param listener the listener to remove + * @return true iff a listener was removed + */ + boolean removeChangeListener(ConfigurationChangeListener listener); + + /** + * Get the parent of the given type for this object + * + * @param clazz the class of parent being asked for + * @return the objects parent + */ + <T extends ConfiguredObject> T getParent(Class<T> clazz); + + + /** + * Returns whether the the object configuration is durably stored + * + * @return the durablity + */ + boolean isDurable(); + + /** + * Sets the durability of the object + * + * @param durable true iff the caller wishes the object to store its configuration durably + * + * @throws IllegalStateException if the durability cannot be changed in the current state + * @throws AccessControlException if the current context does not have sufficient permission to change the durability + * @throws IllegalArgumentException if the object does not support the requested durability + */ + void setDurable(boolean durable) throws IllegalStateException, + AccessControlException, + IllegalArgumentException; + + /** + * Return the lifetime policy for the object + * + * @return the lifetime policy + */ + LifetimePolicy getLifetimePolicy(); + + /** + * Set the lifetime policy of the object + * + * @param expected The lifetime policy the caller believes the object currently has + * @param desired The lifetime policy the caller desires the object to have + * @return the new lifetime policy + * @throws IllegalStateException if the lifetime policy cannot be changed in the current state + * @throws AccessControlException if the caller does not have permission to change the lifetime policy + * @throws IllegalArgumentException if the object does not support the requested lifetime policy + */ + LifetimePolicy setLifetimePolicy(LifetimePolicy expected, LifetimePolicy desired) throws IllegalStateException, + AccessControlException, + IllegalArgumentException; + + /** + * Get the time the object will live once the lifetime policy conditions are no longer fulfilled + * + * @return the time to live + */ + long getTimeToLive(); + + /** + * Set the ttl value + * + * @param expected the ttl the caller believes the object currently has + * @param desired the ttl value the caller + * @return the new ttl value + * @throws IllegalStateException if the ttl cannot be set in the current state + * @throws AccessControlException if the caller does not have permission to change the ttl + * @throws IllegalArgumentException if the object does not support the requested ttl value + */ + long setTimeToLive(long expected, long desired) throws IllegalStateException, + AccessControlException, + IllegalArgumentException; + + /** + * Get the names of attributes that are set on this object + * + * Not that the returned collection is correct at the time the method is called, but will not reflect future + * additions or removals when they occur + * + * @return the collection of attribute names + */ + Collection<String> getAttributeNames(); + + + /** + * Return the value for the given attribute + * + * @param name the name of the attribute + * @return the value of the attribute at the object (or null if the attribute is not set + */ + Object getAttribute(String name); + + /** + * Set the value of an attribute + * + * @param name the name of the attribute to be set + * @param expected the value the caller believes the attribute currently has (or null if it is expected to be unset) + * @param desired the desired value for the attribute (or null to unset the attribute) + * @return the new value for the given attribute + * @throws IllegalStateException if the attribute cannot be set while the object is in its current state + * @throws AccessControlException if the caller does not have permission to alter the value of the attribute + * @throws IllegalArgumentException if the provided value is not valid for the given argument + */ + Object setAttribute(String name, Object expected, Object desired) throws IllegalStateException, + AccessControlException, + IllegalArgumentException; + + + /** + * Return the Statistics holder for the ConfiguredObject + * + * @return the Statistics holder for the ConfiguredObject (or null if none exists) + */ + Statistics getStatistics(); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/model/Consumer.java b/java/broker/src/main/java/org/apache/qpid/server/model/Consumer.java new file mode 100644 index 0000000000..958177e713 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/model/Consumer.java @@ -0,0 +1,73 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.model; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +public interface Consumer extends ConfiguredObject +{ + public String DISTRIBUTION_MODE = "distributionMode"; + public String EXCLUSIVE = "exclusive"; + public String NO_LOCAL = "noLocal"; + public String SELECTOR = "selector"; + public String SETTLEMENT_MODE = "settlementMode"; + public String CREATED = "created"; + public String DURABLE = "durable"; + public String ID = "id"; + public String LIFETIME_POLICY = "lifetimePolicy"; + public String NAME = "name"; + public String STATE = "state"; + public String TIME_TO_LIVE = "timeToLive"; + public String UPDATED = "updated"; + + public Collection<String> AVAILABLE_ATTRIBUTES = + Collections.unmodifiableCollection( + Arrays.asList(ID, + NAME, + STATE, + DURABLE, + LIFETIME_POLICY, + TIME_TO_LIVE, + CREATED, + UPDATED, + DISTRIBUTION_MODE, + SETTLEMENT_MODE, + EXCLUSIVE, + NO_LOCAL, + SELECTOR)); + + public String BYTES_OUT = "bytesOut"; + public String MESSAGES_OUT = "messagesOut"; + public String STATE_CHANGED = "stateChanged"; + public String UNACKNOWLEDGED_BYTES = "unacknowledgedBytes"; + public String UNACKNOWLEDGED_MESSAGES = "unacknowledgedMessages"; + + public Collection<String> AVAILABLE_STATISTICS = + Collections.unmodifiableCollection( + Arrays.asList(BYTES_OUT, + MESSAGES_OUT, + STATE_CHANGED, + UNACKNOWLEDGED_BYTES, + UNACKNOWLEDGED_MESSAGES) + ); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/model/Exchange.java b/java/broker/src/main/java/org/apache/qpid/server/model/Exchange.java new file mode 100644 index 0000000000..e872273d05 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/model/Exchange.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.model; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +public interface Exchange extends ConfiguredObject +{ + String BINDING_COUNT = "bindingCount"; + String BYTES_DROPPED = "bytesDropped"; + String BYTES_IN = "bytesIn"; + String MESSAGES_DROPPED = "messagesDropped"; + String MESSAGES_IN = "messagesIn"; + String PRODUCER_COUNT = "producerCount"; + String STATE_CHANGED = "stateChanged"; + + public static final Collection<String> AVAILABLE_STATISTICS = + Collections.unmodifiableList( + Arrays.asList(BINDING_COUNT, + BYTES_DROPPED, + BYTES_IN, + MESSAGES_DROPPED, + MESSAGES_IN, + PRODUCER_COUNT, + STATE_CHANGED)); + + String CREATED = "created"; + String DURABLE = "durable"; + String ID = "id"; + String LIFETIME_POLICY = "lifetimePolicy"; + String NAME = "name"; + String STATE = "state"; + String TIME_TO_LIVE = "timeToLive"; + String UPDATED = "updated"; + String ALTERNATE_EXCHANGE = "alternateExchange"; + String TYPE = "type"; + + // Attributes + public static final Collection<String> AVAILABLE_ATTRIBUTES = + Collections.unmodifiableList( + Arrays.asList( + ID, + NAME, + STATE, + DURABLE, + LIFETIME_POLICY, + TIME_TO_LIVE, + CREATED, + UPDATED, + ALTERNATE_EXCHANGE, + TYPE + )); + + String getExchangeType(); + + //children + Collection<Binding> getBindings(); + Collection<Publisher> getPublishers(); + + //operations + Binding createBinding(String bindingKey, + Queue queue, + Map<String,Object> bindingArguments, + Map<String, Object> attributes); + + + // Statistics + + void delete(); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/model/IllegalStateTransitionException.java b/java/broker/src/main/java/org/apache/qpid/server/model/IllegalStateTransitionException.java new file mode 100644 index 0000000000..9cab5e2103 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/model/IllegalStateTransitionException.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.model; + +public class IllegalStateTransitionException extends RuntimeException +{ + public IllegalStateTransitionException() + { + } + + public IllegalStateTransitionException(final String message) + { + super(message); + } + + public IllegalStateTransitionException(final String message, final Throwable cause) + { + super(message, cause); + } + + public IllegalStateTransitionException(final Throwable cause) + { + super(cause); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/model/LifetimePolicy.java b/java/broker/src/main/java/org/apache/qpid/server/model/LifetimePolicy.java new file mode 100644 index 0000000000..c9006f4e71 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/model/LifetimePolicy.java @@ -0,0 +1,27 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.model; + +public enum LifetimePolicy +{ + PERMANENT, + AUTO_DELETE +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/model/Publisher.java b/java/broker/src/main/java/org/apache/qpid/server/model/Publisher.java new file mode 100644 index 0000000000..cdb85d8023 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/model/Publisher.java @@ -0,0 +1,25 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.model; + +public interface Publisher extends ConfiguredObject +{ +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/model/Queue.java b/java/broker/src/main/java/org/apache/qpid/server/model/Queue.java new file mode 100644 index 0000000000..7c4f0de22b --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/model/Queue.java @@ -0,0 +1,146 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.model; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import org.apache.qpid.server.queue.QueueEntryVisitor; + + +public interface Queue extends ConfiguredObject +{ + public static final String BINDING_COUNT = "bindingCount"; + public static final String CONSUMER_COUNT = "consumerCount"; + public static final String CONSUMER_COUNT_WITH_CREDIT = "consumerCountWithCredit"; + public static final String DISCARDS_TTL_BYTES = "discardsTtlBytes"; + public static final String DISCARDS_TTL_MESSAGES = "discardsTtlMessages"; + public static final String PERSISTENT_DEQUEUED_BYTES = "persistentDequeuedBytes"; + public static final String PERSISTENT_DEQUEUED_MESSAGES = "persistentDequeuedMessages"; + public static final String PERSISTENT_ENQUEUED_BYTES = "persistentEnqueuedBytes"; + public static final String PERSISTENT_ENQUEUED_MESSAGES = "persistentEnqueuedMessages"; + public static final String QUEUE_DEPTH_BYTES = "queueDepthBytes"; + public static final String QUEUE_DEPTH_MESSAGES = "queueDepthMessages"; + public static final String STATE_CHANGED = "stateChanged"; + public static final String TOTAL_DEQUEUED_BYTES = "totalDequeuedBytes"; + public static final String TOTAL_DEQUEUED_MESSAGES = "totalDequeuedMessages"; + public static final String TOTAL_ENQUEUED_BYTES = "totalEnqueuedBytes"; + public static final String TOTAL_ENQUEUED_MESSAGES = "totalEnqueuedMessages"; + public static final String UNACKNOWLEDGED_BYTES = "unacknowledgedBytes"; + public static final String UNACKNOWLEDGED_MESSAGES = "unacknowledgedMessages"; + + public static final Collection<String> AVAILABLE_STATISTICS = + Collections.unmodifiableList( + Arrays.asList(BINDING_COUNT, + CONSUMER_COUNT, + CONSUMER_COUNT_WITH_CREDIT, + DISCARDS_TTL_BYTES, + DISCARDS_TTL_MESSAGES, + PERSISTENT_DEQUEUED_BYTES, + PERSISTENT_DEQUEUED_MESSAGES, + PERSISTENT_ENQUEUED_BYTES, + PERSISTENT_ENQUEUED_MESSAGES, + QUEUE_DEPTH_BYTES, + QUEUE_DEPTH_MESSAGES, + STATE_CHANGED, + TOTAL_DEQUEUED_BYTES, + TOTAL_DEQUEUED_MESSAGES, + TOTAL_ENQUEUED_BYTES, + TOTAL_ENQUEUED_MESSAGES, + UNACKNOWLEDGED_BYTES, + UNACKNOWLEDGED_MESSAGES)); + + + + public static final String ID = "id"; + public static final String NAME = "name"; + public static final String STATE = "state"; + public static final String DURABLE = "durable"; + public static final String LIFETIME_POLICY = "lifetimePolicy"; + public static final String TIME_TO_LIVE = "timeToLive"; + public static final String CREATED = "created"; + public static final String UPDATED = "updated"; + + public static final String ALERT_REPEAT_GAP = "alertRepeatGap"; + public static final String ALERT_THRESHOLD_MESSAGE_AGE = "alertThresholdMessageAge"; + public static final String ALERT_THRESHOLD_MESSAGE_SIZE = "alertThresholdMessageSize"; + public static final String ALERT_THRESHOLD_QUEUE_DEPTH_BYTES = "alertThresholdQueueDepthBytes"; + public static final String ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES = "alertThresholdQueueDepthMessages"; + public static final String ALTERNATE_EXCHANGE = "alternateExchange"; + public static final String EXCLUSIVE = "exclusive"; + public static final String MESSAGE_GROUP_KEY = "messageGroupKey"; + public static final String MESSAGE_GROUP_DEFAULT_GROUP = "messageGroupDefaultGroup"; + public static final String MESSAGE_GROUP_SHARED_GROUPS = "messageGroupSharedGroups"; + public static final String LVQ_KEY = "lvqKey"; + public static final String MAXIMUM_DELIVERY_ATTEMPTS = "maximumDeliveryAttempts"; + public static final String NO_LOCAL = "noLocal"; + public static final String OWNER = "owner"; + public static final String QUEUE_FLOW_CONTROL_SIZE_BYTES = "queueFlowControlSizeBytes"; + public static final String QUEUE_FLOW_RESUME_SIZE_BYTES = "queueFlowResumeSizeBytes"; + public static final String QUEUE_FLOW_STOPPED = "queueFlowStopped"; + public static final String SORT_KEY = "sortKey"; + public static final String TYPE = "type"; + + + + + public static final Collection<String> AVAILABLE_ATTRIBUTES = + Collections.unmodifiableList( + Arrays.asList(ID, + NAME, + STATE, + DURABLE, + LIFETIME_POLICY, + TIME_TO_LIVE, + CREATED, + UPDATED, + TYPE, + ALTERNATE_EXCHANGE, + EXCLUSIVE, + OWNER, + NO_LOCAL, + LVQ_KEY, + SORT_KEY, + MESSAGE_GROUP_KEY, + MESSAGE_GROUP_DEFAULT_GROUP, + MESSAGE_GROUP_SHARED_GROUPS, + MAXIMUM_DELIVERY_ATTEMPTS, + QUEUE_FLOW_CONTROL_SIZE_BYTES, + QUEUE_FLOW_RESUME_SIZE_BYTES, + QUEUE_FLOW_STOPPED, + ALERT_THRESHOLD_MESSAGE_AGE, + ALERT_THRESHOLD_MESSAGE_SIZE, + ALERT_THRESHOLD_QUEUE_DEPTH_BYTES, + ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES, + ALERT_REPEAT_GAP + )); + + //children + Collection<Binding> getBindings(); + Collection<Consumer> getConsumers(); + + + //operations + + void visit(QueueEntryVisitor visitor); + + void delete(); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/model/State.java b/java/broker/src/main/java/org/apache/qpid/server/model/State.java new file mode 100644 index 0000000000..a73b2c9d3e --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/model/State.java @@ -0,0 +1,30 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.model; + +public enum State +{ + INITIALISING, + QUIESCED, + STOPPED, + ACTIVE, + DELETED +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/model/Statistics.java b/java/broker/src/main/java/org/apache/qpid/server/model/Statistics.java new file mode 100644 index 0000000000..2cb81eae82 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/model/Statistics.java @@ -0,0 +1,25 @@ +package org.apache.qpid.server.model; + +import java.util.Collection; + +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * <p/> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p/> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 interface Statistics +{ + Collection<String> getStatisticNames(); + public Object getStatistic(String name); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/model/UUIDGenerator.java b/java/broker/src/main/java/org/apache/qpid/server/model/UUIDGenerator.java new file mode 100644 index 0000000000..d8493c6df4 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/model/UUIDGenerator.java @@ -0,0 +1,54 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.model; + +import java.util.UUID; + +import org.apache.qpid.exchange.ExchangeDefaults; + + +public class UUIDGenerator +{ + + public static UUID generateUUID() + { + return UUID.randomUUID(); + } + + public static UUID generateUUID(String objectName, String virtualHostName) + { + StringBuilder sb = new StringBuilder(); + sb.append(virtualHostName).append(objectName); + return UUID.nameUUIDFromBytes(sb.toString().getBytes()); + } + + public static UUID generateExchangeUUID(String echangeName, String virtualHostName) + { + if(ExchangeDefaults.DEFAULT_EXCHANGE_NAME.asString().equals(echangeName) || echangeName.startsWith("amq.") || echangeName.startsWith("qpid.")) + { + return generateUUID(echangeName, virtualHostName); + } + else + { + return generateUUID(); + } + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java index f6980be525..b750b29952 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java @@ -58,6 +58,7 @@ import org.apache.qpid.server.state.AMQStateManager; import org.apache.qpid.server.stats.StatisticsCounter; import org.apache.qpid.server.subscription.ClientDeliveryMethod; import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.subscription.SubscriptionImpl; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.virtualhost.VirtualHostRegistry; import org.apache.qpid.transport.Sender; @@ -1315,7 +1316,8 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr public void closeSession(AMQSessionModel session, AMQConstant cause, String message) throws AMQException { - closeChannel((Integer)session.getID()); + int channelId = ((AMQChannel)session).getChannelId(); + closeChannel(channelId); MethodRegistry methodRegistry = getMethodRegistry(); ChannelCloseBody responseBody = @@ -1324,7 +1326,7 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr new AMQShortString(message), 0,0); - writeFrame(responseBody.generateFrame((Integer)session.getID())); + writeFrame(responseBody.generateFrame(channelId)); } public void close(AMQConstant cause, String message) throws AMQException @@ -1454,7 +1456,7 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr throws AMQException { registerMessageDelivered(entry.getMessage().getSize()); - _protocolOutputConverter.writeDeliver(entry, _channelId, deliveryTag, sub.getConsumerTag()); + _protocolOutputConverter.writeDeliver(entry, _channelId, deliveryTag, ((SubscriptionImpl)sub).getConsumerTag()); entry.incrementDeliveryCount(); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQSessionModel.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQSessionModel.java index a80eb46cfa..fa171815ca 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQSessionModel.java +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQSessionModel.java @@ -20,10 +20,12 @@ */ package org.apache.qpid.server.protocol; +import java.util.UUID; import java.util.concurrent.ConcurrentSkipListSet; import org.apache.qpid.AMQException; import org.apache.qpid.server.logging.LogSubject; +import org.apache.qpid.server.message.InboundMessage; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.SimpleAMQQueue; @@ -34,7 +36,8 @@ import org.apache.qpid.server.queue.SimpleAMQQueue; */ public interface AMQSessionModel extends Comparable<AMQSessionModel> { - public Object getID(); + /** Unique session ID across entire broker*/ + public UUID getId(); public AMQConnectionModel getConnectionModel(); @@ -64,4 +67,7 @@ public interface AMQSessionModel extends Comparable<AMQSessionModel> void block(AMQQueue queue); void unblock(AMQQueue queue); + + + boolean onSameConnection(InboundMessage inbound); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/AmqpProtocolVersion.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/AmqpProtocolVersion.java index e925d7a1ec..0a71fe257a 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/protocol/AmqpProtocolVersion.java +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/AmqpProtocolVersion.java @@ -20,4 +20,4 @@ */ package org.apache.qpid.server.protocol; -public enum AmqpProtocolVersion { v0_8, v0_9, v0_9_1, v0_10 }
\ No newline at end of file +public enum AmqpProtocolVersion { v0_8, v0_9, v0_9_1, v0_10, v1_0_0 } diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngine.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngine.java index 3b26f05f84..652ffee004 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngine.java +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngine.java @@ -175,6 +175,28 @@ public class MultiVersionProtocolEngine implements ServerProtocolEngine (byte) 10 }; + private static final byte[] AMQP_1_0_0_HEADER = + new byte[] { (byte) 'A', + (byte) 'M', + (byte) 'Q', + (byte) 'P', + (byte) 0, + (byte) 1, + (byte) 0, + (byte) 0 + }; + + private static final byte[] AMQP_SASL_1_0_0_HEADER = + new byte[] { (byte) 'A', + (byte) 'M', + (byte) 'Q', + (byte) 'P', + (byte) 3, + (byte) 1, + (byte) 0, + (byte) 0 + }; + public void setNetworkConnection(NetworkConnection networkConnection) { setNetworkConnection(networkConnection, networkConnection.getSender()); @@ -289,8 +311,48 @@ public class MultiVersionProtocolEngine implements ServerProtocolEngine } }; + private DelegateCreator creator_1_0_0 = new DelegateCreator() + { + + public AmqpProtocolVersion getVersion() + { + return AmqpProtocolVersion.v1_0_0; + } + + + public byte[] getHeaderIdentifier() + { + return AMQP_1_0_0_HEADER; + } + + public ServerProtocolEngine getProtocolEngine() + { + return new ProtocolEngine_1_0_0(_appRegistry,_id); + } + }; + + private DelegateCreator creator_1_0_0_SASL = new DelegateCreator() + { + + public AmqpProtocolVersion getVersion() + { + return AmqpProtocolVersion.v1_0_0; + } + + + public byte[] getHeaderIdentifier() + { + return AMQP_SASL_1_0_0_HEADER; + } + + public ServerProtocolEngine getProtocolEngine() + { + return new ProtocolEngine_1_0_0_SASL(_network, _appRegistry, _id); + } + }; + private final DelegateCreator[] _creators = - new DelegateCreator[] { creator_0_8, creator_0_9, creator_0_9_1, creator_0_10 }; + new DelegateCreator[] { creator_0_8, creator_0_9, creator_0_9_1, creator_0_10, creator_1_0_0_SASL, creator_1_0_0 }; private class ClosedDelegateProtocolEngine implements ServerProtocolEngine diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_1_0_0.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_1_0_0.java new file mode 100755 index 0000000000..f31ad5052b --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_1_0_0.java @@ -0,0 +1,394 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.SocketAddress; +import java.nio.ByteBuffer; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicLong; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.security.auth.callback.CallbackHandler; +import org.apache.qpid.amqp_1_0.codec.FrameWriter; +import org.apache.qpid.amqp_1_0.framing.AMQFrame; +import org.apache.qpid.amqp_1_0.framing.FrameHandler; +import org.apache.qpid.amqp_1_0.framing.OversizeFrameException; +import org.apache.qpid.amqp_1_0.transport.CallbackHandlerSource; +import org.apache.qpid.amqp_1_0.transport.ConnectionEndpoint; +import org.apache.qpid.amqp_1_0.transport.Container; +import org.apache.qpid.amqp_1_0.transport.FrameOutputHandler; +import org.apache.qpid.amqp_1_0.type.Binary; +import org.apache.qpid.amqp_1_0.type.FrameBody; +import org.apache.qpid.protocol.ServerProtocolEngine; +import org.apache.qpid.server.configuration.ConfigStore; +import org.apache.qpid.server.configuration.ConnectionConfigType; +import org.apache.qpid.server.protocol.v1_0.Connection_1_0; +import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.server.security.auth.manager.AuthenticationManager; +import org.apache.qpid.transport.Sender; +import org.apache.qpid.transport.network.NetworkConnection; + +public class ProtocolEngine_1_0_0 implements ServerProtocolEngine, FrameOutputHandler +{ + static final AtomicLong _connectionIdSource = new AtomicLong(0L); + + //private NetworkConnection _networkDriver; + private long _readBytes; + private long _writtenBytes; + private final UUID _id; + private final IApplicationRegistry _appRegistry; + private long _createTime = System.currentTimeMillis(); + private ConnectionEndpoint _conn; + private final long _connectionId; + + private static final ByteBuffer HEADER = + ByteBuffer.wrap(new byte[] + { + (byte)'A', + (byte)'M', + (byte)'Q', + (byte)'P', + (byte) 0, + (byte) 1, + (byte) 0, + (byte) 0 + }); + + private FrameWriter _frameWriter; + private FrameHandler _frameHandler; + private Object _sendLock = new Object(); + private byte _major; + private byte _minor; + private byte _revision; + private NetworkConnection _network; + private Sender<ByteBuffer> _sender; + + + static enum State { + A, + M, + Q, + P, + PROTOCOL, + MAJOR, + MINOR, + REVISION, + FRAME + } + + private State _state = State.A; + + + + public ProtocolEngine_1_0_0(final IApplicationRegistry appRegistry, long id) + { + _id = appRegistry.getConfigStore().createId(); + _appRegistry = appRegistry; + _connectionId = id; + } + + + public SocketAddress getRemoteAddress() + { + return _network.getRemoteAddress(); + } + + public SocketAddress getLocalAddress() + { + return _network.getLocalAddress(); + } + + public long getReadBytes() + { + return _readBytes; + } + + public long getWrittenBytes() + { + return _writtenBytes; + } + + public void writerIdle() + { + //Todo + } + + public void readerIdle() + { + //Todo + } + + public void setNetworkConnection(final NetworkConnection network, final Sender<ByteBuffer> sender) + { + _network = network; + _sender = sender; + + Container container = new Container(_appRegistry.getBrokerId().toString()); + + _conn = new ConnectionEndpoint(container,asCallbackHandlerSource(_appRegistry.getAuthenticationManager())); + _conn.setConnectionEventListener(new Connection_1_0(_appRegistry)); + _conn.setFrameOutputHandler(this); + _conn.setRemoteAddress(_network.getRemoteAddress()); + + _frameWriter = new FrameWriter(_conn.getDescribedTypeRegistry()); + _frameHandler = new FrameHandler(_conn); + + _sender.send(HEADER.duplicate()); + _sender.flush(); + } + + private CallbackHandlerSource asCallbackHandlerSource(final AuthenticationManager authenticationManager) + { + return new CallbackHandlerSource() + { + @Override + public CallbackHandler getHandler(String mechanism) + { + return authenticationManager.getHandler(mechanism); + } + }; + } + + public String getAddress() + { + return getRemoteAddress().toString(); + } + + + public ConfigStore getConfigStore() + { + return _appRegistry.getConfigStore(); + } + + public UUID getId() + { + return _id; + } + + public ConnectionConfigType getConfigType() + { + return ConnectionConfigType.getInstance(); + } + + public boolean isDurable() + { + return false; + } + + public synchronized void received(ByteBuffer msg) + { + if(RAW_LOGGER.isLoggable(Level.FINE)) + { + ByteBuffer dup = msg.duplicate(); + byte[] data = new byte[dup.remaining()]; + dup.get(data); + Binary bin = new Binary(data); + RAW_LOGGER.fine("RECV[" + getRemoteAddress() + "] : " + bin.toString()); + } + _readBytes += msg.remaining(); + switch(_state) + { + case A: + if(msg.hasRemaining()) + { + msg.get(); + } + else + { + break; + } + case M: + if(msg.hasRemaining()) + { + msg.get(); + } + else + { + _state = State.M; + break; + } + + case Q: + if(msg.hasRemaining()) + { + msg.get(); + } + else + { + _state = State.Q; + break; + } + case P: + if(msg.hasRemaining()) + { + msg.get(); + } + else + { + _state = State.P; + break; + } + case PROTOCOL: + if(msg.hasRemaining()) + { + msg.get(); + } + else + { + _state = State.PROTOCOL; + break; + } + case MAJOR: + if(msg.hasRemaining()) + { + _major = msg.get(); + } + else + { + _state = State.MAJOR; + break; + } + case MINOR: + if(msg.hasRemaining()) + { + _minor = msg.get(); + } + else + { + _state = State.MINOR; + break; + } + case REVISION: + if(msg.hasRemaining()) + { + _revision = msg.get(); + + _state = State.FRAME; + } + else + { + _state = State.REVISION; + break; + } + case FRAME: + if(msg.hasRemaining()) + { + _frameHandler.parse(msg); + } + } + + } + + public void exception(Throwable t) + { + t.printStackTrace(); + } + + public void closed() + { + _conn.inputClosed(); + if(_conn != null && _conn.getConnectionEventListener() != null) + { + ((Connection_1_0)_conn.getConnectionEventListener()).closed(); + } + + } + + public long getCreateTime() + { + return _createTime; + } + + + public boolean canSend() + { + return true; + } + + public void send(final AMQFrame amqFrame) + { + send(amqFrame, null); + } + + private final Logger FRAME_LOGGER = Logger.getLogger("FRM"); + private final Logger RAW_LOGGER = Logger.getLogger("RAW"); + + + public void send(final AMQFrame amqFrame, ByteBuffer buf) + { + synchronized(_sendLock) + { + + if(FRAME_LOGGER.isLoggable(Level.FINE)) + { + FRAME_LOGGER.fine("SEND[" + getRemoteAddress() + "|" + amqFrame.getChannel() + "] : " + amqFrame.getFrameBody()); + } + + + _frameWriter.setValue(amqFrame); + + + + ByteBuffer dup = ByteBuffer.allocate(_conn.getMaxFrameSize()); + + int size = _frameWriter.writeToBuffer(dup); + if(size > _conn.getMaxFrameSize()) + { + throw new OversizeFrameException(amqFrame,size); + } + + dup.flip(); + _writtenBytes += dup.limit(); + + if(RAW_LOGGER.isLoggable(Level.FINE)) + { + ByteBuffer dup2 = dup.duplicate(); + byte[] data = new byte[dup2.remaining()]; + dup2.get(data); + Binary bin = new Binary(data); + RAW_LOGGER.fine("SEND[" + getRemoteAddress() + "] : " + bin.toString()); + } + + + _sender.send(dup); + _sender.flush(); + + } + } + + public void send(short channel, FrameBody body) + { + AMQFrame frame = AMQFrame.createAMQFrame(channel, body); + send(frame); + + } + + public void close() + { + //TODO + } + + public long getConnectionId() + { + return _connectionId; + } + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_1_0_0_SASL.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_1_0_0_SASL.java new file mode 100644 index 0000000000..ffd5e750b4 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_1_0_0_SASL.java @@ -0,0 +1,449 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.io.PrintWriter; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.security.auth.callback.CallbackHandler; +import org.apache.qpid.amqp_1_0.codec.FrameWriter; +import org.apache.qpid.amqp_1_0.codec.ProtocolHandler; +import org.apache.qpid.amqp_1_0.framing.AMQFrame; +import org.apache.qpid.amqp_1_0.framing.OversizeFrameException; +import org.apache.qpid.amqp_1_0.framing.SASLFrameHandler; +import org.apache.qpid.amqp_1_0.transport.CallbackHandlerSource; +import org.apache.qpid.amqp_1_0.transport.ConnectionEndpoint; +import org.apache.qpid.amqp_1_0.transport.Container; +import org.apache.qpid.amqp_1_0.transport.FrameOutputHandler; +import org.apache.qpid.amqp_1_0.type.Binary; +import org.apache.qpid.amqp_1_0.type.FrameBody; +import org.apache.qpid.protocol.ServerProtocolEngine; +import org.apache.qpid.server.configuration.ConfigStore; +import org.apache.qpid.server.configuration.ConnectionConfigType; +import org.apache.qpid.server.protocol.v1_0.Connection_1_0; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.server.security.auth.manager.AuthenticationManager; +import org.apache.qpid.transport.Sender; +import org.apache.qpid.transport.network.NetworkConnection; + +public class ProtocolEngine_1_0_0_SASL implements ServerProtocolEngine, FrameOutputHandler +{ + private long _readBytes; + private long _writtenBytes; + private final UUID _id; + private final IApplicationRegistry _appRegistry; + private long _createTime = System.currentTimeMillis(); + private ConnectionEndpoint _conn; + private long _connectionId; + + private static final ByteBuffer HEADER = + ByteBuffer.wrap(new byte[] + { + (byte)'A', + (byte)'M', + (byte)'Q', + (byte)'P', + (byte) 3, + (byte) 1, + (byte) 0, + (byte) 0 + }); + + private static final ByteBuffer PROTOCOL_HEADER = + ByteBuffer.wrap(new byte[] + { + (byte)'A', + (byte)'M', + (byte)'Q', + (byte)'P', + (byte) 0, + (byte) 1, + (byte) 0, + (byte) 0 + }); + + + private FrameWriter _frameWriter; + private ProtocolHandler _frameHandler; + private ByteBuffer _buf = ByteBuffer.allocate(1024 * 1024); + private Object _sendLock = new Object(); + private byte _major; + private byte _minor; + private byte _revision; + private PrintWriter _out; + private NetworkConnection _network; + private Sender<ByteBuffer> _sender; + + + static enum State { + A, + M, + Q, + P, + PROTOCOL, + MAJOR, + MINOR, + REVISION, + FRAME + } + + private State _state = State.A; + + + public ProtocolEngine_1_0_0_SASL(final NetworkConnection networkDriver, final IApplicationRegistry appRegistry, + long id) + { + _id = appRegistry.getConfigStore().createId(); + _connectionId = id; + _appRegistry = appRegistry; + + if(networkDriver != null) + { + setNetworkConnection(networkDriver, networkDriver.getSender()); + } + } + + + public SocketAddress getRemoteAddress() + { + return _network.getRemoteAddress(); + } + + public SocketAddress getLocalAddress() + { + return _network.getLocalAddress(); + } + + public long getReadBytes() + { + return _readBytes; + } + + public long getWrittenBytes() + { + return _writtenBytes; + } + + public void writerIdle() + { + //Todo + } + + public void readerIdle() + { + //Todo + } + + public void setNetworkConnection(final NetworkConnection network, final Sender<ByteBuffer> sender) + { + _network = network; + _sender = sender; + + Container container = new Container(_appRegistry.getBrokerId().toString()); + + _conn = new ConnectionEndpoint(container, asCallbackHandlerSource(ApplicationRegistry.getInstance() + .getAuthenticationManager())); + _conn.setConnectionEventListener(new Connection_1_0(_appRegistry)); + _conn.setRemoteAddress(getRemoteAddress()); + + + _conn.setFrameOutputHandler(this); + _conn.setSaslFrameOutput(this); + + _conn.setOnSaslComplete(new Runnable() + { + + + public void run() + { + if(_conn.isAuthenticated()) + { + _sender.send(PROTOCOL_HEADER.duplicate()); + _sender.flush(); + } + else + { + _network.close(); + } + } + }); + _frameWriter = new FrameWriter(_conn.getDescribedTypeRegistry()); + _frameHandler = new SASLFrameHandler(_conn); + + _sender.send(HEADER.duplicate()); + _sender.flush(); + + _conn.initiateSASL(); + + + } + + private CallbackHandlerSource asCallbackHandlerSource(final AuthenticationManager authenticationManager) + { + return new CallbackHandlerSource() + { + @Override + public CallbackHandler getHandler(String mechanism) + { + return authenticationManager.getHandler(mechanism); + } + }; + } + + public String getAddress() + { + return getRemoteAddress().toString(); + } + + + public ConfigStore getConfigStore() + { + return _appRegistry.getConfigStore(); + } + + public UUID getId() + { + return _id; + } + + public ConnectionConfigType getConfigType() + { + return ConnectionConfigType.getInstance(); + } + + public boolean isDurable() + { + return false; + } + + private final Logger RAW_LOGGER = Logger.getLogger("RAW"); + + + public synchronized void received(ByteBuffer msg) + { + if(RAW_LOGGER.isLoggable(Level.FINE)) + { + ByteBuffer dup = msg.duplicate(); + byte[] data = new byte[dup.remaining()]; + dup.get(data); + Binary bin = new Binary(data); + RAW_LOGGER.fine("RECV[" + getRemoteAddress() + "] : " + bin.toString()); + } + _readBytes += msg.remaining(); + switch(_state) + { + case A: + if(msg.hasRemaining()) + { + msg.get(); + } + else + { + break; + } + case M: + if(msg.hasRemaining()) + { + msg.get(); + } + else + { + _state = State.M; + break; + } + + case Q: + if(msg.hasRemaining()) + { + msg.get(); + } + else + { + _state = State.Q; + break; + } + case P: + if(msg.hasRemaining()) + { + msg.get(); + } + else + { + _state = State.P; + break; + } + case PROTOCOL: + if(msg.hasRemaining()) + { + msg.get(); + } + else + { + _state = State.PROTOCOL; + break; + } + case MAJOR: + if(msg.hasRemaining()) + { + _major = msg.get(); + } + else + { + _state = State.MAJOR; + break; + } + case MINOR: + if(msg.hasRemaining()) + { + _minor = msg.get(); + } + else + { + _state = State.MINOR; + break; + } + case REVISION: + if(msg.hasRemaining()) + { + _revision = msg.get(); + + _state = State.FRAME; + } + else + { + _state = State.REVISION; + break; + } + case FRAME: + if(msg.hasRemaining()) + { + _frameHandler = _frameHandler.parse(msg); + } + } + + } + + public void exception(Throwable t) + { + t.printStackTrace(); + } + + public void closed() + { + // todo + _conn.inputClosed(); + if(_conn != null && _conn.getConnectionEventListener() != null) + { + ((Connection_1_0)_conn.getConnectionEventListener()).closed(); + } + + } + + public long getCreateTime() + { + return _createTime; + } + + + public boolean canSend() + { + return true; + } + + public void send(final AMQFrame amqFrame) + { + send(amqFrame, null); + } + + private static final Logger FRAME_LOGGER = Logger.getLogger("FRM"); + + + public void send(final AMQFrame amqFrame, ByteBuffer buf) + { + + synchronized(_sendLock) + { + + if(FRAME_LOGGER.isLoggable(Level.FINE)) + { + FRAME_LOGGER.fine("SEND[" + getRemoteAddress() + "|" + amqFrame.getChannel() + "] : " + amqFrame.getFrameBody()); + } + + + _frameWriter.setValue(amqFrame); + + + + ByteBuffer dup = ByteBuffer.allocate(_conn.getMaxFrameSize()); + + int size = _frameWriter.writeToBuffer(dup); + if(size > _conn.getMaxFrameSize()) + { + throw new OversizeFrameException(amqFrame,size); + } + + dup.flip(); + _writtenBytes += dup.limit(); + + if(RAW_LOGGER.isLoggable(Level.FINE)) + { + ByteBuffer dup2 = dup.duplicate(); + byte[] data = new byte[dup2.remaining()]; + dup2.get(data); + Binary bin = new Binary(data); + RAW_LOGGER.fine("SEND[" + getRemoteAddress() + "] : " + bin.toString()); + } + + + _sender.send(dup); + _sender.flush(); + + + } + } + + public void send(short channel, FrameBody body) + { + AMQFrame frame = AMQFrame.createAMQFrame(channel, body); + send(frame); + + } + + public void close() + { + _sender.close(); + } + + public void setLogOutput(final PrintWriter out) + { + _out = out; + } + + public long getConnectionId() + { + return _connectionId; + } + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Connection_1_0.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Connection_1_0.java new file mode 100644 index 0000000000..318a240b27 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Connection_1_0.java @@ -0,0 +1,98 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.protocol.v1_0; + +import org.apache.qpid.amqp_1_0.transport.ConnectionEventListener; +import org.apache.qpid.amqp_1_0.transport.SessionEndpoint; + +import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.server.virtualhost.VirtualHost; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class Connection_1_0 implements ConnectionEventListener +{ + + private IApplicationRegistry _appRegistry; + private VirtualHost _vhost; + + + public static interface Task + { + public void doTask(Connection_1_0 connection); + } + + + private List<Task> _closeTasks = + Collections.synchronizedList(new ArrayList<Task>()); + + + + public Connection_1_0(IApplicationRegistry appRegistry) + { + _appRegistry = appRegistry; + _vhost = _appRegistry.getVirtualHostRegistry().getDefaultVirtualHost(); + } + + public void remoteSessionCreation(SessionEndpoint endpoint) + { + Session_1_0 session = new Session_1_0(_vhost, _appRegistry, this); + endpoint.setSessionEventListener(session); + } + + + void removeConnectionCloseTask(final Task task) + { + _closeTasks.remove( task ); + } + + void addConnectionCloseTask(final Task task) + { + _closeTasks.add( task ); + } + + public void closeReceived() + { + List<Task> taskCopy; + synchronized (_closeTasks) + { + taskCopy = new ArrayList<Task>(_closeTasks); + } + for(Task task : taskCopy) + { + task.doTask(this); + } + synchronized (_closeTasks) + { + _closeTasks.clear(); + } + + } + + public void closed() + { + closeReceived(); + } + + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Destination.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Destination.java new file mode 100644 index 0000000000..d45758391c --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Destination.java @@ -0,0 +1,28 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.protocol.v1_0; + + +public interface Destination +{ + + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ExchangeDestination.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ExchangeDestination.java new file mode 100644 index 0000000000..ba1a1ca45c --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ExchangeDestination.java @@ -0,0 +1,108 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.v1_0; + +import java.util.List; +import org.apache.qpid.AMQException; +import org.apache.qpid.amqp_1_0.type.Outcome; +import org.apache.qpid.amqp_1_0.type.messaging.Accepted; +import org.apache.qpid.amqp_1_0.type.messaging.TerminusDurability; +import org.apache.qpid.amqp_1_0.type.messaging.TerminusExpiryPolicy; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.queue.BaseQueue; +import org.apache.qpid.server.txn.ServerTransaction; + +public class ExchangeDestination implements ReceivingDestination, SendingDestination +{ + private static final Accepted ACCEPTED = new Accepted(); + private static final Outcome[] OUTCOMES = { ACCEPTED }; + + private Exchange _exchange; + private TerminusDurability _durability; + private TerminusExpiryPolicy _expiryPolicy; + + public ExchangeDestination(Exchange exchange, TerminusDurability durable, TerminusExpiryPolicy expiryPolicy) + { + _exchange = exchange; + _durability = durable; + _expiryPolicy = expiryPolicy; + } + + public Outcome[] getOutcomes() + { + return OUTCOMES; + } + + public Outcome send(final Message_1_0 message, ServerTransaction txn) + { + final List<? extends BaseQueue> queues = _exchange.route(message); + + txn.enqueue(queues,message, new ServerTransaction.Action() + { + + BaseQueue[] _queues = queues.toArray(new BaseQueue[queues.size()]); + + public void postCommit() + { + for(int i = 0; i < _queues.length; i++) + { + try + { + _queues[i].enqueue(message); + } + catch (AMQException e) + { + // TODO + throw new RuntimeException(e); + } + } + } + + public void onRollback() + { + // NO-OP + } + }, System.currentTimeMillis()); + + return ACCEPTED; + } + + TerminusDurability getDurability() + { + return _durability; + } + + TerminusExpiryPolicy getExpiryPolicy() + { + return _expiryPolicy; + } + + public int getCredit() + { + // TODO - fix + return 20000; + } + + public Exchange getExchange() + { + return _exchange; + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/LinkRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/LinkRegistry.java new file mode 100644 index 0000000000..42eea05d37 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/LinkRegistry.java @@ -0,0 +1,59 @@ +package org.apache.qpid.server.protocol.v1_0; + +import java.util.HashMap; +import java.util.Map; + +public class LinkRegistry +{ + private final Map<String, SendingLink_1_0> _sendingLinks = new HashMap<String, SendingLink_1_0>(); + private final Map<String, ReceivingLink_1_0> _receivingLinks = new HashMap<String, ReceivingLink_1_0>(); + + public synchronized SendingLink_1_0 getDurableSendingLink(String name) + { + return _sendingLinks.get(name); + } + + public synchronized boolean registerSendingLink(String name, SendingLink_1_0 link) + { + if(_sendingLinks.containsKey(name)) + { + return false; + } + else + { + _sendingLinks.put(name, link); + return true; + } + } + + public synchronized boolean unregisterSendingLink(String name) + { + if(!_sendingLinks.containsKey(name)) + { + return false; + } + else + { + _sendingLinks.remove(name); + return true; + } + } + + public synchronized ReceivingLink_1_0 getDurableReceivingLink(String name) + { + return _receivingLinks.get(name); + } + + public synchronized boolean registerReceivingLink(String name, ReceivingLink_1_0 link) + { + if(_receivingLinks.containsKey(name)) + { + return false; + } + else + { + _receivingLinks.put(name, link); + return true; + } + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Link_1_0.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Link_1_0.java new file mode 100644 index 0000000000..db81d3b205 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Link_1_0.java @@ -0,0 +1,26 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.v1_0; + +public interface Link_1_0 +{ + void start(); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Message_1_0.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Message_1_0.java new file mode 100644 index 0000000000..140a815f57 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Message_1_0.java @@ -0,0 +1,172 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.v1_0; + + +import java.lang.ref.WeakReference; +import java.nio.ByteBuffer; +import java.util.List; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.configuration.SessionConfig; +import org.apache.qpid.server.message.InboundMessage; +import org.apache.qpid.server.message.MessageMetaData_1_0; +import org.apache.qpid.server.message.MessageReference; +import org.apache.qpid.server.message.ServerMessage; +import org.apache.qpid.server.store.StoredMessage; + +public class Message_1_0 implements ServerMessage, InboundMessage +{ + private final StoredMessage<MessageMetaData_1_0> _storedMessage; + private List<ByteBuffer> _fragments; + private WeakReference<Session_1_0> _session; + + + public Message_1_0(final StoredMessage<MessageMetaData_1_0> storedMessage, + final List<ByteBuffer> fragments, + final Session_1_0 session) + { + _storedMessage = storedMessage; + _fragments = fragments; + _session = new WeakReference<Session_1_0>(session); + } + + public String getRoutingKey() + { + Object routingKey = getMessageHeader().getHeader("routing-key"); + if(routingKey != null) + { + return routingKey.toString(); + } + else + { + return getMessageHeader().getSubject(); + } + } + + public AMQShortString getRoutingKeyShortString() + { + return AMQShortString.valueOf(getRoutingKey()); + } + + private MessageMetaData_1_0 getMessageMetaData() + { + return _storedMessage.getMetaData(); + } + + public MessageMetaData_1_0.MessageHeader_1_0 getMessageHeader() + { + return getMessageMetaData().getMessageHeader(); + } + + public StoredMessage getStoredMessage() + { + return _storedMessage; + } + + public boolean isPersistent() + { + return getMessageMetaData().isPersistent(); + } + + public boolean isRedelivered() + { + // TODO + return false; + } + + public long getSize() + { + // TODO + return 0l; + } + + public boolean isImmediate() + { + return false; + } + + public long getExpiration() + { + return getMessageHeader().getExpiration(); + } + + public MessageReference<Message_1_0> newReference() + { + return new Reference(this); + } + + public long getMessageNumber() + { + return _storedMessage.getMessageNumber(); + } + + public long getArrivalTime() + { + return 0; //TODO + } + + public int getContent(final ByteBuffer buf, final int offset) + { + return _storedMessage.getContent(offset, buf); + } + + public ByteBuffer getContent(int offset, int size) + { + ByteBuffer buf = ByteBuffer.allocate(size); + buf.limit(getContent(buf, offset)); + + return buf; + } + + public SessionConfig getSessionConfig() + { + return null; //TODO + } + + public List<ByteBuffer> getFragments() + { + return _fragments; + } + + public Session_1_0 getSession() + { + return _session.get(); + } + + public static class Reference extends MessageReference<Message_1_0> + { + public Reference(Message_1_0 message) + { + super(message); + } + + protected void onReference(Message_1_0 message) + { + + } + + protected void onRelease(Message_1_0 message) + { + + } + +} +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/QueueDestination.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/QueueDestination.java new file mode 100644 index 0000000000..af3f0b7872 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/QueueDestination.java @@ -0,0 +1,100 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.protocol.v1_0; + +import org.apache.qpid.amqp_1_0.type.Outcome; +import org.apache.qpid.amqp_1_0.type.messaging.Accepted; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.queue.AMQQueue; + +import org.apache.qpid.server.txn.ServerTransaction; + +import java.util.Arrays; + +public class QueueDestination implements SendingDestination, ReceivingDestination +{ + private static final Accepted ACCEPTED = new Accepted(); + private static final Outcome[] OUTCOMES = new Outcome[] { ACCEPTED }; + + + private AMQQueue _queue; + + public QueueDestination(AMQQueue queue) + { + _queue = queue; + } + + public Outcome[] getOutcomes() + { + return OUTCOMES; + } + + public Outcome send(final Message_1_0 message, ServerTransaction txn) + { + + try + { + txn.enqueue(_queue,message, new ServerTransaction.Action() + { + + + public void postCommit() + { + try + { + + _queue.enqueue(message); + } + catch (Exception e) + { + // TODO + throw new RuntimeException(e); + } + + } + + public void onRollback() + { + // NO-OP + } + }); + } + catch(Exception e) + { + e.printStackTrace(); + throw new RuntimeException(e); + } + return ACCEPTED; + } + + public int getCredit() + { + // TODO - fix + return 100; + } + + public AMQQueue getQueue() + { + return _queue; + } + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ReceivingDestination.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ReceivingDestination.java new file mode 100644 index 0000000000..4ae0596e25 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ReceivingDestination.java @@ -0,0 +1,35 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.protocol.v1_0; + +import org.apache.qpid.amqp_1_0.type.Outcome; + +import org.apache.qpid.server.txn.ServerTransaction; + +public interface ReceivingDestination extends Destination +{ + + Outcome[] getOutcomes(); + + Outcome send(Message_1_0 message, ServerTransaction txn); + + int getCredit(); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ReceivingLinkAttachment.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ReceivingLinkAttachment.java new file mode 100644 index 0000000000..6da5081185 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ReceivingLinkAttachment.java @@ -0,0 +1,51 @@ +package org.apache.qpid.server.protocol.v1_0; + +import org.apache.qpid.amqp_1_0.transport.DeliveryStateHandler; +import org.apache.qpid.amqp_1_0.transport.ReceivingLinkEndpoint; +import org.apache.qpid.amqp_1_0.transport.SendingLinkEndpoint; +import org.apache.qpid.amqp_1_0.type.Binary; +import org.apache.qpid.amqp_1_0.type.DeliveryState; +import org.apache.qpid.amqp_1_0.type.Source; +import org.apache.qpid.amqp_1_0.type.Target; + +public class ReceivingLinkAttachment +{ + private final Session_1_0 _session; + private final ReceivingLinkEndpoint _endpoint; + + public ReceivingLinkAttachment(final Session_1_0 session, final ReceivingLinkEndpoint endpoint) + { + _session = session; + _endpoint = endpoint; + } + + public Session_1_0 getSession() + { + return _session; + } + + public ReceivingLinkEndpoint getEndpoint() + { + return _endpoint; + } + + public Source getSource() + { + return getEndpoint().getSource(); + } + + public void setDeliveryStateHandler(final DeliveryStateHandler handler) + { + getEndpoint().setDeliveryStateHandler(handler); + } + + public void updateDisposition(final Binary deliveryTag, final DeliveryState state, final boolean settled) + { + getEndpoint().updateDisposition(deliveryTag, state, settled); + } + + public Target getTarget() + { + return getEndpoint().getTarget(); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ReceivingLink_1_0.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ReceivingLink_1_0.java new file mode 100644 index 0000000000..e097dd5c83 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ReceivingLink_1_0.java @@ -0,0 +1,305 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.v1_0; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.qpid.amqp_1_0.messaging.SectionDecoderImpl; +import org.apache.qpid.amqp_1_0.transport.DeliveryStateHandler; +import org.apache.qpid.amqp_1_0.transport.LinkEndpoint; +import org.apache.qpid.amqp_1_0.transport.ReceivingLinkEndpoint; +import org.apache.qpid.amqp_1_0.transport.ReceivingLinkListener; +import org.apache.qpid.amqp_1_0.type.Binary; +import org.apache.qpid.amqp_1_0.type.DeliveryState; +import org.apache.qpid.amqp_1_0.type.Outcome; +import org.apache.qpid.amqp_1_0.type.UnsignedInteger; +import org.apache.qpid.amqp_1_0.type.messaging.Target; +import org.apache.qpid.amqp_1_0.type.messaging.TerminusDurability; +import org.apache.qpid.amqp_1_0.type.transaction.TransactionalState; +import org.apache.qpid.amqp_1_0.type.transport.Detach; +import org.apache.qpid.amqp_1_0.type.transport.ReceiverSettleMode; +import org.apache.qpid.amqp_1_0.type.transport.Transfer; +import org.apache.qpid.server.message.MessageMetaData_1_0; +import org.apache.qpid.server.store.StoredMessage; +import org.apache.qpid.server.txn.AutoCommitTransaction; +import org.apache.qpid.server.txn.ServerTransaction; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public class ReceivingLink_1_0 implements ReceivingLinkListener, Link_1_0, DeliveryStateHandler +{ + private VirtualHost _vhost; + + private ReceivingDestination _destination; + private SectionDecoderImpl _sectionDecoder; + private volatile ReceivingLinkAttachment _attachment; + + + private ArrayList<Transfer> _incompleteMessage; + private TerminusDurability _durability; + + private Map<Binary, Outcome> _unsettledMap = Collections.synchronizedMap(new HashMap<Binary, Outcome>()); + private boolean _resumedMessage; + private Binary _messageDeliveryTag; + private ReceiverSettleMode _receivingSettlementMode; + + + public ReceivingLink_1_0(ReceivingLinkAttachment receivingLinkAttachment, VirtualHost vhost, + ReceivingDestination destination) + { + _vhost = vhost; + _destination = destination; + _attachment = receivingLinkAttachment; + receivingLinkAttachment.setDeliveryStateHandler(this); + + _durability = ((Target)receivingLinkAttachment.getTarget()).getDurable(); + + _sectionDecoder = new SectionDecoderImpl(receivingLinkAttachment.getEndpoint().getSession().getConnection().getDescribedTypeRegistry()); + + + } + + public void messageTransfer(Transfer xfr) + { + // TODO - cope with fragmented messages + + List<ByteBuffer> fragments = null; + + + + if(Boolean.TRUE.equals(xfr.getMore()) && _incompleteMessage == null) + { + _incompleteMessage = new ArrayList<Transfer>(); + _incompleteMessage.add(xfr); + _resumedMessage = Boolean.TRUE.equals(xfr.getResume()); + _messageDeliveryTag = xfr.getDeliveryTag(); + return; + } + else if(_incompleteMessage != null) + { + _incompleteMessage.add(xfr); + + if(Boolean.TRUE.equals(xfr.getMore())) + { + return; + } + + fragments = new ArrayList<ByteBuffer>(_incompleteMessage.size()); + for(Transfer t : _incompleteMessage) + { + fragments.add(t.getPayload()); + } + _incompleteMessage=null; + + } + else + { + _resumedMessage = Boolean.TRUE.equals(xfr.getResume()); + _messageDeliveryTag = xfr.getDeliveryTag(); + fragments = Collections.singletonList(xfr.getPayload()); + } + + if(_resumedMessage) + { + if(_unsettledMap.containsKey(_messageDeliveryTag)) + { + Outcome outcome = _unsettledMap.get(_messageDeliveryTag); + boolean settled = ReceiverSettleMode.FIRST.equals(getReceivingSettlementMode()); + getEndpoint().updateDisposition(_messageDeliveryTag, (DeliveryState) outcome, settled); + if(settled) + { + _unsettledMap.remove(_messageDeliveryTag); + } + } + else + { + System.err.println("UNEXPECTED!!"); + System.err.println("Delivery Tag: " + _messageDeliveryTag); + System.err.println("_unsettledMap: " + _unsettledMap); + + } + } + else + { + MessageMetaData_1_0 mmd = null; + List<ByteBuffer> immutableSections = new ArrayList<ByteBuffer>(3); + mmd = new MessageMetaData_1_0(fragments.toArray(new ByteBuffer[fragments.size()]), + _sectionDecoder, + immutableSections); + + StoredMessage<MessageMetaData_1_0> storedMessage = _vhost.getMessageStore().addMessage(mmd); + + boolean skipping = true; + int offset = 0; + + for(ByteBuffer bareMessageBuf : immutableSections) + { + storedMessage.addContent(offset, bareMessageBuf.duplicate()); + offset += bareMessageBuf.remaining(); + } + + storedMessage.flushToStore(); + + Message_1_0 message = new Message_1_0(storedMessage, fragments, getSession()); + + + Binary transactionId = null; + org.apache.qpid.amqp_1_0.type.DeliveryState xfrState = xfr.getState(); + if(xfrState != null) + { + if(xfrState instanceof TransactionalState) + { + transactionId = ((TransactionalState)xfrState).getTxnId(); + } + } + + ServerTransaction transaction = null; + if(transactionId != null) + { + transaction = getSession().getTransaction(transactionId); + } + else + { + Session_1_0 session = getSession(); + transaction = session != null ? session.getTransaction(null) : new AutoCommitTransaction(_vhost.getMessageStore()); + } + + Outcome outcome = _destination.send(message, transaction); + + DeliveryState resultantState; + + if(transactionId == null) + { + resultantState = (DeliveryState) outcome; + } + else + { + TransactionalState transactionalState = new TransactionalState(); + transactionalState.setOutcome(outcome); + transactionalState.setTxnId(transactionId); + resultantState = transactionalState; + + } + + + boolean settled = transaction instanceof AutoCommitTransaction && ReceiverSettleMode.FIRST.equals(getReceivingSettlementMode()); + + final Binary deliveryTag = xfr.getDeliveryTag(); + + if(!settled) + { + _unsettledMap.put(deliveryTag, outcome); + } + + getEndpoint().updateDisposition(deliveryTag, resultantState, settled); + + if(!(transaction instanceof AutoCommitTransaction)) + { + ServerTransaction.Action a; + transaction.addPostTransactionAction(new ServerTransaction.Action() + { + public void postCommit() + { + getEndpoint().updateDisposition(deliveryTag, null, true); + } + + public void onRollback() + { + getEndpoint().updateDisposition(deliveryTag, null, true); + } + }); + } + } + } + + private ReceiverSettleMode getReceivingSettlementMode() + { + return _receivingSettlementMode; + } + + public void remoteDetached(LinkEndpoint endpoint, Detach detach) + { + //TODO + // if not durable or close + if(!TerminusDurability.UNSETTLED_STATE.equals(_durability) || + (detach != null && Boolean.TRUE.equals(detach.getClosed()))) + { + endpoint.close(); + } + else if(detach == null || detach.getError() != null) + { + _attachment = null; + } + } + + public void start() + { + getEndpoint().setLinkCredit(UnsignedInteger.valueOf(_destination.getCredit())); + getEndpoint().setCreditWindow(); + } + + public ReceivingLinkEndpoint getEndpoint() + { + return _attachment.getEndpoint(); + } + + + public Session_1_0 getSession() + { + ReceivingLinkAttachment attachment = _attachment; + return attachment == null ? null : attachment.getSession(); + } + + public void handle(Binary deliveryTag, DeliveryState state, Boolean settled) + { + if(Boolean.TRUE.equals(settled)) + { + _unsettledMap.remove(deliveryTag); + } + } + + public void setLinkAttachment(ReceivingLinkAttachment linkAttachment) + { + _attachment = linkAttachment; + _receivingSettlementMode = linkAttachment.getEndpoint().getReceivingSettlementMode(); + ReceivingLinkEndpoint endpoint = linkAttachment.getEndpoint(); + Map initialUnsettledMap = endpoint.getInitialUnsettledMap(); + + Map<Binary, Outcome> unsettledCopy = new HashMap<Binary, Outcome>(_unsettledMap); + for(Map.Entry<Binary, Outcome> entry : unsettledCopy.entrySet()) + { + Binary deliveryTag = entry.getKey(); + if(!initialUnsettledMap.containsKey(deliveryTag)) + { + _unsettledMap.remove(deliveryTag); + } + } + + } + + public Map getUnsettledOutcomeMap() + { + return _unsettledMap; + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingDestination.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingDestination.java new file mode 100644 index 0000000000..6d601c9dda --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingDestination.java @@ -0,0 +1,27 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.v1_0; + + +public interface SendingDestination extends Destination +{ + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingLinkAttachment.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingLinkAttachment.java new file mode 100644 index 0000000000..9d7af24135 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingLinkAttachment.java @@ -0,0 +1,44 @@ +package org.apache.qpid.server.protocol.v1_0; + +import org.apache.qpid.amqp_1_0.transport.DeliveryStateHandler; +import org.apache.qpid.amqp_1_0.transport.SendingLinkEndpoint; +import org.apache.qpid.amqp_1_0.type.Binary; +import org.apache.qpid.amqp_1_0.type.DeliveryState; +import org.apache.qpid.amqp_1_0.type.Source; + +public class SendingLinkAttachment +{ + private final Session_1_0 _session; + private final SendingLinkEndpoint _endpoint; + + public SendingLinkAttachment(final Session_1_0 session, final SendingLinkEndpoint endpoint) + { + _session = session; + _endpoint = endpoint; + } + + public Session_1_0 getSession() + { + return _session; + } + + public SendingLinkEndpoint getEndpoint() + { + return _endpoint; + } + + public Source getSource() + { + return getEndpoint().getSource(); + } + + public void setDeliveryStateHandler(final DeliveryStateHandler handler) + { + getEndpoint().setDeliveryStateHandler(handler); + } + + public void updateDisposition(final Binary deliveryTag, final DeliveryState state, final boolean settled) + { + getEndpoint().updateDisposition(deliveryTag, state, settled); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingLink_1_0.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingLink_1_0.java new file mode 100644 index 0000000000..b3e9a74d04 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingLink_1_0.java @@ -0,0 +1,648 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.v1_0; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQInternalException; +import org.apache.qpid.AMQSecurityException; +import org.apache.qpid.amqp_1_0.transport.DeliveryStateHandler; +import org.apache.qpid.amqp_1_0.transport.LinkEndpoint; +import org.apache.qpid.amqp_1_0.transport.SendingLinkEndpoint; +import org.apache.qpid.amqp_1_0.transport.SendingLinkListener; +import org.apache.qpid.amqp_1_0.type.AmqpErrorException; +import org.apache.qpid.amqp_1_0.type.Binary; +import org.apache.qpid.amqp_1_0.type.DeliveryState; +import org.apache.qpid.amqp_1_0.type.Outcome; +import org.apache.qpid.amqp_1_0.type.Symbol; +import org.apache.qpid.amqp_1_0.type.UnsignedInteger; +import org.apache.qpid.amqp_1_0.type.messaging.Accepted; +import org.apache.qpid.amqp_1_0.type.messaging.ExactSubjectFilter; +import org.apache.qpid.amqp_1_0.type.messaging.Filter; +import org.apache.qpid.amqp_1_0.type.messaging.MatchingSubjectFilter; +import org.apache.qpid.amqp_1_0.type.messaging.Modified; +import org.apache.qpid.amqp_1_0.type.messaging.NoLocalFilter; +import org.apache.qpid.amqp_1_0.type.messaging.Released; +import org.apache.qpid.amqp_1_0.type.messaging.Source; +import org.apache.qpid.amqp_1_0.type.messaging.StdDistMode; +import org.apache.qpid.amqp_1_0.type.messaging.TerminusDurability; +import org.apache.qpid.amqp_1_0.type.transport.AmqpError; +import org.apache.qpid.amqp_1_0.type.transport.Detach; +import org.apache.qpid.amqp_1_0.type.transport.Error; +import org.apache.qpid.amqp_1_0.type.transport.Transfer; +import org.apache.qpid.filter.SelectorParsingException; +import org.apache.qpid.filter.selector.ParseException; +import org.apache.qpid.server.binding.Binding; +import org.apache.qpid.server.exchange.DirectExchange; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.exchange.TopicExchange; +import org.apache.qpid.server.filter.JMSSelectorFilter; +import org.apache.qpid.server.filter.SimpleFilterManager; +import org.apache.qpid.server.model.UUIDGenerator; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.AMQQueueFactory; +import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.txn.AutoCommitTransaction; +import org.apache.qpid.server.txn.ServerTransaction; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public class SendingLink_1_0 implements SendingLinkListener, Link_1_0, DeliveryStateHandler +{ + private VirtualHost _vhost; + private SendingDestination _destination; + + private Subscription_1_0 _subscription; + private boolean _draining; + private final Map<Binary, QueueEntry> _unsettledMap = + new HashMap<Binary, QueueEntry>(); + + private final ConcurrentHashMap<Binary, UnsettledAction> _unsettledActionMap = + new ConcurrentHashMap<Binary, UnsettledAction>(); + private volatile SendingLinkAttachment _linkAttachment; + private TerminusDurability _durability; + private List<QueueEntry> _resumeFullTransfers = new ArrayList<QueueEntry>(); + private List<Binary> _resumeAcceptedTransfers = new ArrayList<Binary>(); + private Runnable _closeAction; + + public SendingLink_1_0(final SendingLinkAttachment linkAttachment, + final VirtualHost vhost, + final SendingDestination destination) + throws AmqpErrorException + { + _vhost = vhost; + _destination = destination; + _linkAttachment = linkAttachment; + final Source source = (Source) linkAttachment.getSource(); + _durability = source.getDurable(); + linkAttachment.setDeliveryStateHandler(this); + QueueDestination qd = null; + AMQQueue queue = null; + + + + boolean noLocal = false; + JMSSelectorFilter messageFilter = null; + + if(destination instanceof QueueDestination) + { + queue = ((QueueDestination) _destination).getQueue(); + if(queue.getArguments() != null && queue.getArguments().containsKey("topic")) + { + source.setDistributionMode(StdDistMode.COPY); + } + qd = (QueueDestination) destination; + + Map<Symbol,Filter> filters = source.getFilter(); + + Map<Symbol,Filter> actualFilters = new HashMap<Symbol,Filter>(); + + if(filters != null) + { + for(Map.Entry<Symbol,Filter> entry : filters.entrySet()) + { + if(entry.getValue() instanceof NoLocalFilter) + { + actualFilters.put(entry.getKey(), entry.getValue()); + noLocal = true; + } + else if(messageFilter == null && entry.getValue() instanceof org.apache.qpid.amqp_1_0.type.messaging.JMSSelectorFilter) + { + + org.apache.qpid.amqp_1_0.type.messaging.JMSSelectorFilter selectorFilter = (org.apache.qpid.amqp_1_0.type.messaging.JMSSelectorFilter) entry.getValue(); + try + { + messageFilter = new JMSSelectorFilter(selectorFilter.getValue()); + + actualFilters.put(entry.getKey(), entry.getValue()); + } + catch (ParseException e) + { + Error error = new Error(); + error.setCondition(AmqpError.INVALID_FIELD); + error.setDescription("Invalid JMS Selector: " + selectorFilter.getValue()); + error.setInfo(Collections.singletonMap(Symbol.valueOf("field"), Symbol.valueOf("filter"))); + throw new AmqpErrorException(error); + } + catch (SelectorParsingException e) + { + Error error = new Error(); + error.setCondition(AmqpError.INVALID_FIELD); + error.setDescription("Invalid JMS Selector: " + selectorFilter.getValue()); + error.setInfo(Collections.singletonMap(Symbol.valueOf("field"), Symbol.valueOf("filter"))); + throw new AmqpErrorException(error); + } + + + } + } + } + source.setFilter(actualFilters.isEmpty() ? null : actualFilters); + + _subscription = new Subscription_1_0(this, qd); + } + else if(destination instanceof ExchangeDestination) + { + try + { + + ExchangeDestination exchangeDestination = (ExchangeDestination) destination; + + boolean isDurable = exchangeDestination.getDurability() == TerminusDurability.CONFIGURATION + || exchangeDestination.getDurability() == TerminusDurability.UNSETTLED_STATE; + String name; + if(isDurable) + { + String remoteContainerId = getEndpoint().getSession().getConnection().getRemoteContainerId(); + remoteContainerId = remoteContainerId.replace("_","__").replace(".", "_:"); + + String endpointName = linkAttachment.getEndpoint().getName(); + endpointName = endpointName + .replace("_", "__") + .replace(".", "_:") + .replace("(", "_O") + .replace(")", "_C") + .replace("<", "_L") + .replace(">", "_R"); + name = "qpid_/" + remoteContainerId + "_/" + endpointName; + } + else + { + name = UUID.randomUUID().toString(); + } + + queue = _vhost.getQueueRegistry().getQueue(name); + Exchange exchange = exchangeDestination.getExchange(); + + if(queue == null) + { + queue = AMQQueueFactory.createAMQQueueImpl( + UUIDGenerator.generateUUID(), + name, + isDurable, + null, + true, + true, + _vhost, + Collections.EMPTY_MAP); + } + else + { + List<Binding> bindings = queue.getBindings(); + List<Binding> bindingsToRemove = new ArrayList<Binding>(); + for(Binding existingBinding : bindings) + { + if(existingBinding.getExchange() != _vhost.getExchangeRegistry().getDefaultExchange() + && existingBinding.getExchange() != exchange) + { + bindingsToRemove.add(existingBinding); + } + } + for(Binding existingBinding : bindingsToRemove) + { + existingBinding.getExchange().removeBinding(existingBinding); + } + } + + + String binding = ""; + + Map<Symbol,Filter> filters = source.getFilter(); + Map<Symbol,Filter> actualFilters = new HashMap<Symbol,Filter>(); + boolean hasBindingFilter = false; + if(filters != null && !filters.isEmpty()) + { + + for(Map.Entry<Symbol,Filter> entry : filters.entrySet()) + { + if(!hasBindingFilter + && entry.getValue() instanceof ExactSubjectFilter + && exchange.getType() == DirectExchange.TYPE) + { + ExactSubjectFilter filter = (ExactSubjectFilter) filters.values().iterator().next(); + source.setFilter(filters); + binding = filter.getValue(); + actualFilters.put(entry.getKey(), entry.getValue()); + hasBindingFilter = true; + } + else if(!hasBindingFilter + && entry.getValue() instanceof MatchingSubjectFilter + && exchange.getType() == TopicExchange.TYPE) + { + MatchingSubjectFilter filter = (MatchingSubjectFilter) filters.values().iterator().next(); + source.setFilter(filters); + binding = filter.getValue(); + actualFilters.put(entry.getKey(), entry.getValue()); + hasBindingFilter = true; + } + else if(entry.getValue() instanceof NoLocalFilter) + { + actualFilters.put(entry.getKey(), entry.getValue()); + noLocal = true; + } + else if(messageFilter == null && entry.getValue() instanceof org.apache.qpid.amqp_1_0.type.messaging.JMSSelectorFilter) + { + + org.apache.qpid.amqp_1_0.type.messaging.JMSSelectorFilter selectorFilter = (org.apache.qpid.amqp_1_0.type.messaging.JMSSelectorFilter) entry.getValue(); + try + { + messageFilter = new JMSSelectorFilter(selectorFilter.getValue()); + + actualFilters.put(entry.getKey(), entry.getValue()); + } + catch (ParseException e) + { + Error error = new Error(); + error.setCondition(AmqpError.INVALID_FIELD); + error.setDescription("Invalid JMS Selector: " + selectorFilter.getValue()); + error.setInfo(Collections.singletonMap(Symbol.valueOf("field"), Symbol.valueOf("filter"))); + throw new AmqpErrorException(error); + } + catch (SelectorParsingException e) + { + Error error = new Error(); + error.setCondition(AmqpError.INVALID_FIELD); + error.setDescription("Invalid JMS Selector: " + selectorFilter.getValue()); + error.setInfo(Collections.singletonMap(Symbol.valueOf("field"), Symbol.valueOf("filter"))); + throw new AmqpErrorException(error); + } + + + } + } + } + source.setFilter(actualFilters.isEmpty() ? null : actualFilters); + + vhost.getBindingFactory().addBinding(binding,queue,exchange,null); + source.setDistributionMode(StdDistMode.COPY); + + qd = new QueueDestination(queue); + } + catch (AMQSecurityException e) + { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + catch (AMQInternalException e) + { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } catch (AMQException e) + { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + _subscription = new Subscription_1_0(this, qd, true); + + } + + if(_subscription != null) + { + _subscription.setNoLocal(noLocal); + if(messageFilter!=null) + { + _subscription.setFilters(new SimpleFilterManager(messageFilter)); + } + + try + { + + queue.registerSubscription(_subscription, false); + } + catch (AMQException e) + { + e.printStackTrace(); //TODO + } + } + + } + + public void resume(SendingLinkAttachment linkAttachment) + { + _linkAttachment = linkAttachment; + + } + + public void remoteDetached(final LinkEndpoint endpoint, final Detach detach) + { + //TODO + // if not durable or close + if(!TerminusDurability.UNSETTLED_STATE.equals(_durability)) + { + AMQQueue queue = _subscription.getQueue(); + + try + { + + queue.unregisterSubscription(_subscription); + + } + catch (AMQException e) + { + e.printStackTrace(); //TODO + } + + Modified state = new Modified(); + state.setDeliveryFailed(true); + + for(UnsettledAction action : _unsettledActionMap.values()) + { + + action.process(state,Boolean.TRUE); + } + _unsettledActionMap.clear(); + + endpoint.close(); + + if(_destination instanceof ExchangeDestination + && (_durability == TerminusDurability.CONFIGURATION + || _durability == TerminusDurability.UNSETTLED_STATE)) + { + try + { + queue.delete(); + } + catch(AMQException e) + { + e.printStackTrace(); // TODO - Implement + } + } + + if(_closeAction != null) + { + _closeAction.run(); + } + } + else if(detach == null || detach.getError() != null) + { + _linkAttachment = null; + _subscription.flowStateChanged(); + } + else + { + endpoint.detach(); + } + } + + public void start() + { + //TODO + } + + public SendingLinkEndpoint getEndpoint() + { + return _linkAttachment == null ? null : _linkAttachment.getEndpoint() ; + } + + public Session_1_0 getSession() + { + return _linkAttachment == null ? null : _linkAttachment.getSession(); + } + + public void flowStateChanged() + { + if(Boolean.TRUE.equals(getEndpoint().getDrain()) + && hasCredit()) + { + _draining = true; + } + + while(!_resumeAcceptedTransfers.isEmpty() && getEndpoint().hasCreditToSend()) + { + Accepted accepted = new Accepted(); + synchronized(getLock()) + { + + Transfer xfr = new Transfer(); + Binary dt = _resumeAcceptedTransfers.remove(0); + xfr.setDeliveryTag(dt); + xfr.setState(accepted); + xfr.setResume(Boolean.TRUE); + getEndpoint().transfer(xfr); + } + + } + if(_resumeAcceptedTransfers.isEmpty()) + { + _subscription.flowStateChanged(); + } + + } + + boolean hasCredit() + { + return getEndpoint().getLinkCredit().compareTo(UnsignedInteger.ZERO) > 0; + } + + public boolean isDraining() + { + return false; //TODO + } + + public boolean drained() + { + if(getEndpoint() != null) + { + synchronized(getEndpoint().getLock()) + { + if(_draining) + { + //TODO + getEndpoint().drained(); + _draining = false; + return true; + } + else + { + return false; + } + } + } + else + { + return false; + } + } + + public void addUnsettled(Binary tag, UnsettledAction unsettledAction, QueueEntry queueEntry) + { + _unsettledActionMap.put(tag,unsettledAction); + if(getTransactionId() == null) + { + _unsettledMap.put(tag, queueEntry); + } + } + + public void removeUnsettled(Binary tag) + { + _unsettledActionMap.remove(tag); + } + + public void handle(Binary deliveryTag, DeliveryState state, Boolean settled) + { + UnsettledAction action = _unsettledActionMap.get(deliveryTag); + boolean localSettle = false; + if(action != null) + { + localSettle = action.process(state, settled); + if(localSettle && !Boolean.TRUE.equals(settled)) + { + _linkAttachment.updateDisposition(deliveryTag, state, true); + } + } + if(Boolean.TRUE.equals(settled) || localSettle) + { + _unsettledActionMap.remove(deliveryTag); + _unsettledMap.remove(deliveryTag); + } + } + + ServerTransaction getTransaction(Binary transactionId) + { + return _linkAttachment.getSession().getTransaction(transactionId); + } + + public Binary getTransactionId() + { + SendingLinkEndpoint endpoint = getEndpoint(); + return endpoint == null ? null : endpoint.getTransactionId(); + } + + public synchronized Object getLock() + { + return _linkAttachment == null ? this : getEndpoint().getLock(); + } + + public boolean isDetached() + { + return _linkAttachment == null || getEndpoint().isDetached(); + } + + public boolean isAttached() + { + return _linkAttachment != null && getEndpoint().isAttached(); + } + + public synchronized void setLinkAttachment(SendingLinkAttachment linkAttachment) + { + + if(_subscription.isActive()) + { + _subscription.suspend(); + } + + _linkAttachment = linkAttachment; + + SendingLinkEndpoint endpoint = linkAttachment.getEndpoint(); + endpoint.setDeliveryStateHandler(this); + Map initialUnsettledMap = endpoint.getInitialUnsettledMap(); + Map<Binary, QueueEntry> unsettledCopy = new HashMap<Binary, QueueEntry>(_unsettledMap); + _resumeAcceptedTransfers.clear(); + _resumeFullTransfers.clear(); + + for(Map.Entry<Binary, QueueEntry> entry : unsettledCopy.entrySet()) + { + Binary deliveryTag = entry.getKey(); + final QueueEntry queueEntry = entry.getValue(); + if(initialUnsettledMap == null || !initialUnsettledMap.containsKey(deliveryTag)) + { + queueEntry.setRedelivered(); + queueEntry.release(); + _unsettledMap.remove(deliveryTag); + } + else if(initialUnsettledMap != null && (initialUnsettledMap.get(deliveryTag) instanceof Outcome)) + { + Outcome outcome = (Outcome) initialUnsettledMap.get(deliveryTag); + + if(outcome instanceof Accepted) + { + AutoCommitTransaction txn = new AutoCommitTransaction(_vhost.getMessageStore()); + if(_subscription.acquires()) + { + txn.dequeue(Collections.singleton(queueEntry), + new ServerTransaction.Action() + { + public void postCommit() + { + queueEntry.discard(); + } + + public void onRollback() + { + //To change body of implemented methods use File | Settings | File Templates. + } + }); + } + } + else if(outcome instanceof Released) + { + AutoCommitTransaction txn = new AutoCommitTransaction(_vhost.getMessageStore()); + if(_subscription.acquires()) + { + txn.dequeue(Collections.singleton(queueEntry), + new ServerTransaction.Action() + { + public void postCommit() + { + queueEntry.release(); + } + + public void onRollback() + { + //To change body of implemented methods use File | Settings | File Templates. + } + }); + } + } + //_unsettledMap.remove(deliveryTag); + initialUnsettledMap.remove(deliveryTag); + _resumeAcceptedTransfers.add(deliveryTag); + } + else + { + _resumeFullTransfers.add(queueEntry); + // exists in receivers map, but not yet got an outcome ... should resend with resume = true + } + // TODO - else + } + + + } + + public Map getUnsettledOutcomeMap() + { + Map<Binary, QueueEntry> unsettled = new HashMap<Binary, QueueEntry>(_unsettledMap); + + for(Map.Entry<Binary, QueueEntry> entry : unsettled.entrySet()) + { + entry.setValue(null); + } + + return unsettled; + } + + public void setCloseAction(Runnable action) + { + _closeAction = action; + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Session_1_0.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Session_1_0.java new file mode 100644 index 0000000000..48a551e42a --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Session_1_0.java @@ -0,0 +1,446 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.v1_0; + +import org.apache.qpid.amqp_1_0.transport.LinkEndpoint; +import org.apache.qpid.amqp_1_0.transport.ReceivingLinkEndpoint; +import org.apache.qpid.amqp_1_0.transport.SendingLinkEndpoint; +import org.apache.qpid.amqp_1_0.transport.SessionEventListener; +import org.apache.qpid.amqp_1_0.type.*; +import org.apache.qpid.amqp_1_0.type.messaging.*; +import org.apache.qpid.amqp_1_0.type.messaging.Source; +import org.apache.qpid.amqp_1_0.type.messaging.Target; +import org.apache.qpid.amqp_1_0.type.transaction.Coordinator; +import org.apache.qpid.amqp_1_0.type.transaction.TxnCapability; +import org.apache.qpid.amqp_1_0.type.transport.*; + +import org.apache.qpid.amqp_1_0.type.transport.Error; +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQSecurityException; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.model.UUIDGenerator; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.AMQQueueFactory; +import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.server.txn.AutoCommitTransaction; +import org.apache.qpid.server.txn.ServerTransaction; +import org.apache.qpid.server.virtualhost.VirtualHost; + +import java.util.*; + +public class Session_1_0 implements SessionEventListener +{ + private static final Symbol LIFETIME_POLICY = Symbol.valueOf("lifetime-policy"); + private IApplicationRegistry _appRegistry; + private VirtualHost _vhost; + private AutoCommitTransaction _transaction; + + private final LinkedHashMap<Integer, ServerTransaction> _openTransactions = + new LinkedHashMap<Integer, ServerTransaction>(); + private final Connection_1_0 _connection; + + + public Session_1_0(VirtualHost vhost, IApplicationRegistry appRegistry, final Connection_1_0 connection) + { + _appRegistry = appRegistry; + _vhost = vhost; + _transaction = new AutoCommitTransaction(vhost.getMessageStore()); + _connection = connection; + + } + + public void remoteLinkCreation(final LinkEndpoint endpoint) + { + + + Destination destination; + Link_1_0 link = null; + Error error = null; + + final LinkRegistry linkRegistry = _vhost.getLinkRegistry(endpoint.getSession().getConnection().getRemoteContainerId()); + + + if(endpoint.getRole() == Role.SENDER) + { + + SendingLink_1_0 previousLink = linkRegistry.getDurableSendingLink(endpoint.getName()); + + if(previousLink == null) + { + + Target target = (Target) endpoint.getTarget(); + Source source = (Source) endpoint.getSource(); + + + if(source != null) + { + if(Boolean.TRUE.equals(source.getDynamic())) + { + AMQQueue tempQueue = createTemporaryQueue(source.getDynamicNodeProperties()); + source.setAddress(tempQueue.getName()); + } + String addr = source.getAddress(); + AMQQueue queue = _vhost.getQueueRegistry().getQueue(addr); + if(queue != null) + { + + destination = new QueueDestination(queue); + + + + } + else + { + Exchange exchg = _vhost.getExchangeRegistry().getExchange(addr); + if(exchg != null) + { + destination = new ExchangeDestination(exchg, source.getDurable(), source.getExpiryPolicy()); + } + else + { + + endpoint.setSource(null); + destination = null; + } + } + + } + else + { + destination = null; + } + + if(destination != null) + { + final SendingLinkEndpoint sendingLinkEndpoint = (SendingLinkEndpoint) endpoint; + try + { + final SendingLink_1_0 sendingLink = new SendingLink_1_0(new SendingLinkAttachment(this, sendingLinkEndpoint), + _vhost, + (SendingDestination) destination + ); + sendingLinkEndpoint.setLinkEventListener(sendingLink); + link = sendingLink; + if(TerminusDurability.UNSETTLED_STATE.equals(source.getDurable())) + { + linkRegistry.registerSendingLink(endpoint.getName(), sendingLink); + } + } + catch(AmqpErrorException e) + { + e.printStackTrace(); + destination = null; + sendingLinkEndpoint.setSource(null); + error = e.getError(); + } + } + } + else + { + Source newSource = (Source) endpoint.getSource(); + + Source oldSource = (Source) previousLink.getEndpoint().getSource(); + final TerminusDurability newSourceDurable = newSource == null ? null : newSource.getDurable(); + if(newSourceDurable != null) + { + oldSource.setDurable(newSourceDurable); + if(newSourceDurable.equals(TerminusDurability.NONE)) + { + linkRegistry.unregisterSendingLink(endpoint.getName()); + } + } + endpoint.setSource(oldSource); + SendingLinkEndpoint sendingLinkEndpoint = (SendingLinkEndpoint) endpoint; + previousLink.setLinkAttachment(new SendingLinkAttachment(this, sendingLinkEndpoint)); + sendingLinkEndpoint.setLinkEventListener(previousLink); + link = previousLink; + endpoint.setLocalUnsettled(previousLink.getUnsettledOutcomeMap()); + } + } + else + { + if(endpoint.getTarget() instanceof Coordinator) + { + Coordinator coordinator = (Coordinator) endpoint.getTarget(); + TxnCapability[] capabilities = coordinator.getCapabilities(); + boolean localTxn = false; + boolean multiplePerSession = false; + if(capabilities != null) + { + for(TxnCapability capability : capabilities) + { + if(capability.equals(TxnCapability.LOCAL_TXN)) + { + localTxn = true; + } + else if(capability.equals(TxnCapability.MULTI_TXNS_PER_SSN)) + { + multiplePerSession = true; + } + else + { + error = new Error(); + error.setCondition(AmqpError.NOT_IMPLEMENTED); + error.setDescription("Unsupported capability: " + capability); + break; + } + } + } + + /* if(!localTxn) + { + capabilities.add(TxnCapabilities.LOCAL_TXN); + }*/ + + final ReceivingLinkEndpoint receivingLinkEndpoint = (ReceivingLinkEndpoint) endpoint; + final TxnCoordinatorLink_1_0 coordinatorLink = + new TxnCoordinatorLink_1_0(_vhost, this, receivingLinkEndpoint, _openTransactions); + receivingLinkEndpoint.setLinkEventListener(coordinatorLink); + link = coordinatorLink; + + + } + else + { + + ReceivingLink_1_0 previousLink = linkRegistry.getDurableReceivingLink(endpoint.getName()); + + if(previousLink == null) + { + + Target target = (Target) endpoint.getTarget(); + + if(target != null) + { + if(Boolean.TRUE.equals(target.getDynamic())) + { + + AMQQueue tempQueue = createTemporaryQueue(target.getDynamicNodeProperties()); + target.setAddress(tempQueue.getName()); + } + + String addr = target.getAddress(); + Exchange exchg = _vhost.getExchangeRegistry().getExchange(addr); + if(exchg != null) + { + destination = new ExchangeDestination(exchg, target.getDurable(), + target.getExpiryPolicy()); + } + else + { + AMQQueue queue = _vhost.getQueueRegistry().getQueue(addr); + if(queue != null) + { + + destination = new QueueDestination(queue); + } + else + { + endpoint.setTarget(null); + destination = null; + } + + } + + + } + else + { + destination = null; + } + if(destination != null) + { + final ReceivingLinkEndpoint receivingLinkEndpoint = (ReceivingLinkEndpoint) endpoint; + final ReceivingLink_1_0 receivingLink = new ReceivingLink_1_0(new ReceivingLinkAttachment(this, receivingLinkEndpoint), _vhost, + (ReceivingDestination) destination); + receivingLinkEndpoint.setLinkEventListener(receivingLink); + link = receivingLink; + if(TerminusDurability.UNSETTLED_STATE.equals(target.getDurable())) + { + linkRegistry.registerReceivingLink(endpoint.getName(), receivingLink); + } + } + } + else + { + ReceivingLinkEndpoint receivingLinkEndpoint = (ReceivingLinkEndpoint) endpoint; + previousLink.setLinkAttachment(new ReceivingLinkAttachment(this, receivingLinkEndpoint)); + receivingLinkEndpoint.setLinkEventListener(previousLink); + link = previousLink; + endpoint.setLocalUnsettled(previousLink.getUnsettledOutcomeMap()); + + } + } + } + + endpoint.attach(); + + if(link == null) + { + if(error == null) + { + error = new Error(); + error.setCondition(AmqpError.NOT_FOUND); + } + endpoint.detach(error); + } + else + { + link.start(); + } + } + + + private AMQQueue createTemporaryQueue(Map properties) + { + final String queueName = UUID.randomUUID().toString(); + AMQQueue queue = null; + try + { + LifetimePolicy lifetimePolicy = properties == null + ? null + : (LifetimePolicy) properties.get(LIFETIME_POLICY); + + final AMQQueue tempQueue = queue = AMQQueueFactory.createAMQQueueImpl( UUIDGenerator.generateUUID(), + queueName, + false, // durable + null, // owner + false, // autodelete + false, // exclusive + _vhost, + properties); + + + + if (lifetimePolicy == null || lifetimePolicy instanceof DeleteOnClose) + { + final Connection_1_0.Task deleteQueueTask = + new Connection_1_0.Task() + { + public void doTask(Connection_1_0 session) + { + if (_vhost.getQueueRegistry().getQueue(queueName) == tempQueue) + { + try + { + tempQueue.delete(); + } + catch (AMQException e) + { + e.printStackTrace(); //TODO. + } + } + } + }; + + _connection.addConnectionCloseTask(deleteQueueTask); + + queue.addQueueDeleteTask(new AMQQueue.Task() + { + public void doTask(AMQQueue queue) + { + _connection.removeConnectionCloseTask(deleteQueueTask); + } + + + }); + } + else if(lifetimePolicy instanceof DeleteOnNoLinks) + { + + } + else if(lifetimePolicy instanceof DeleteOnNoMessages) + { + + } + else if(lifetimePolicy instanceof DeleteOnNoLinksOrMessages) + { + + } + } + catch (AMQSecurityException e) + { + e.printStackTrace(); //TODO. + } catch (AMQException e) + { + e.printStackTrace(); //TODO + } + + return queue; + } + + public ServerTransaction getTransaction(Binary transactionId) + { + // TODO should treat invalid id differently to null + ServerTransaction transaction = _openTransactions.get(binaryToInteger(transactionId)); + return transaction == null ? _transaction : transaction; + } + + public void remoteEnd(End end) + { + Iterator<Map.Entry<Integer, ServerTransaction>> iter = _openTransactions.entrySet().iterator(); + + while(iter.hasNext()) + { + Map.Entry<Integer, ServerTransaction> entry = iter.next(); + entry.getValue().rollback(); + iter.remove(); + } + + } + + Integer binaryToInteger(final Binary txnId) + { + if(txnId == null) + { + return null; + } + + if(txnId.getLength() > 4) + throw new IllegalArgumentException(); + + int id = 0; + byte[] data = txnId.getArray(); + for(int i = 0; i < txnId.getLength(); i++) + { + id <<= 8; + id += data[i+txnId.getArrayOffset()]; + } + + return id; + + } + + Binary integerToBinary(final int txnId) + { + byte[] data = new byte[4]; + data[3] = (byte) (txnId & 0xff); + data[2] = (byte) ((txnId & 0xff00) >> 8); + data[1] = (byte) ((txnId & 0xff0000) >> 16); + data[0] = (byte) ((txnId & 0xff000000) >> 24); + return new Binary(data); + + } + + public void forceEnd() + { + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Subscription_1_0.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Subscription_1_0.java new file mode 100644 index 0000000000..08926d000d --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Subscription_1_0.java @@ -0,0 +1,634 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.v1_0; + +import java.nio.ByteBuffer; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.ReentrantLock; +import org.apache.qpid.AMQException; +import org.apache.qpid.amqp_1_0.codec.ValueHandler; +import org.apache.qpid.amqp_1_0.messaging.SectionEncoder; +import org.apache.qpid.amqp_1_0.messaging.SectionEncoderImpl; +import org.apache.qpid.amqp_1_0.transport.SendingLinkEndpoint; +import org.apache.qpid.amqp_1_0.type.AmqpErrorException; +import org.apache.qpid.amqp_1_0.type.Binary; +import org.apache.qpid.amqp_1_0.type.DeliveryState; +import org.apache.qpid.amqp_1_0.type.Outcome; +import org.apache.qpid.amqp_1_0.type.UnsignedInteger; +import org.apache.qpid.amqp_1_0.type.codec.AMQPDescribedTypeRegistry; +import org.apache.qpid.amqp_1_0.type.messaging.Accepted; +import org.apache.qpid.amqp_1_0.type.messaging.Header; +import org.apache.qpid.amqp_1_0.type.messaging.Modified; +import org.apache.qpid.amqp_1_0.type.messaging.Released; +import org.apache.qpid.amqp_1_0.type.messaging.Source; +import org.apache.qpid.amqp_1_0.type.messaging.StdDistMode; +import org.apache.qpid.amqp_1_0.type.transaction.TransactionalState; +import org.apache.qpid.amqp_1_0.type.transport.SenderSettleMode; +import org.apache.qpid.amqp_1_0.type.transport.Transfer; +import org.apache.qpid.server.filter.FilterManager; +import org.apache.qpid.server.logging.LogActor; +import org.apache.qpid.server.message.ServerMessage; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.txn.ServerTransaction; + +class Subscription_1_0 implements Subscription +{ + private SendingLink_1_0 _link; + + private AMQQueue _queue; + + private final AtomicReference<State> _state = new AtomicReference<State>(State.SUSPENDED); + + private final QueueEntry.SubscriptionAcquiredState _owningState = new QueueEntry.SubscriptionAcquiredState(this); + private final QueueEntry.SubscriptionAssignedState _assignedState = new QueueEntry.SubscriptionAssignedState(this); + private final long _id; + private final boolean _acquires; + private AMQQueue.Context _queueContext; + private Map<String, Object> _properties = new ConcurrentHashMap<String, Object>(); + private ReentrantLock _stateChangeLock = new ReentrantLock(); + + private boolean _noLocal; + private FilterManager _filters; + + private long _deliveryTag = 0L; + private StateListener _stateListener; + + private Binary _transactionId; + private final AMQPDescribedTypeRegistry _typeRegistry = AMQPDescribedTypeRegistry.newInstance() + .registerTransportLayer() + .registerMessagingLayer() + .registerTransactionLayer() + .registerSecurityLayer(); + private SectionEncoder _sectionEncoder = new SectionEncoderImpl(_typeRegistry); + + public Subscription_1_0(final SendingLink_1_0 link, final QueueDestination destination) + { + this(link, destination, ((Source)link.getEndpoint().getSource()).getDistributionMode() != StdDistMode.COPY); + } + + public Subscription_1_0(final SendingLink_1_0 link, final QueueDestination destination, boolean acquires) + { + _link = link; + _queue = destination.getQueue(); + _id = getEndpoint().getLocalHandle().longValue(); + _acquires = acquires; + } + + private SendingLinkEndpoint getEndpoint() + { + return _link.getEndpoint(); + } + + public LogActor getLogActor() + { + return null; //TODO + } + + public boolean isTransient() + { + return true; //TODO + } + + public AMQQueue getQueue() + { + return _queue; + } + + public QueueEntry.SubscriptionAcquiredState getOwningState() + { + return _owningState; + } + + public QueueEntry.SubscriptionAssignedState getAssignedState() + { + return _assignedState; + } + + public void setQueue(final AMQQueue queue, final boolean exclusive) + { + //TODO + } + + public void setNoLocal(final boolean noLocal) + { + _noLocal = noLocal; + } + + public boolean isNoLocal() + { + return _noLocal; + } + + public long getSubscriptionID() + { + return _id; + } + + public boolean isSuspended() + { + return !isActive();// || !getEndpoint().hasCreditToSend(); + + } + + public boolean hasInterest(final QueueEntry entry) + { + return !(_noLocal && (entry.getMessage() instanceof Message_1_0) + && ((Message_1_0)entry.getMessage()).getSession() == getSession()) + && checkFilters(entry); + + } + + private boolean checkFilters(final QueueEntry entry) + { + return (_filters == null) || _filters.allAllow(entry); + } + + public boolean isClosed() + { + return !getEndpoint().isAttached(); + } + + public boolean acquires() + { + return _acquires; + } + + public boolean seesRequeues() + { + // TODO + return acquires(); + } + + public void close() + { + getEndpoint().detach(); + } + + public void send(QueueEntry entry, boolean batch) throws AMQException + { + // TODO + send(entry); + } + + public void flushBatched() + { + // TODO + } + + public void send(final QueueEntry queueEntry) throws AMQException + { + //TODO + ServerMessage serverMessage = queueEntry.getMessage(); + if(serverMessage instanceof Message_1_0) + { + Message_1_0 message = (Message_1_0) serverMessage; + Transfer transfer = new Transfer(); + //TODO + + + List<ByteBuffer> fragments = message.getFragments(); + ByteBuffer payload; + if(fragments.size() == 1) + { + payload = fragments.get(0); + } + else + { + int size = 0; + for(ByteBuffer fragment : fragments) + { + size += fragment.remaining(); + } + + payload = ByteBuffer.allocate(size); + + for(ByteBuffer fragment : fragments) + { + payload.put(fragment.duplicate()); + } + + payload.flip(); + } + + if(queueEntry.getDeliveryCount() != 0) + { + payload = payload.duplicate(); + ValueHandler valueHandler = new ValueHandler(_typeRegistry); + + Header oldHeader = null; + try + { + ByteBuffer encodedBuf = payload.duplicate(); + Object value = valueHandler.parse(payload); + if(value instanceof Header) + { + oldHeader = (Header) value; + } + else + { + payload.position(0); + } + } + catch (AmqpErrorException e) + { + //TODO + throw new RuntimeException(e); + } + + Header header = new Header(); + if(oldHeader != null) + { + header.setDurable(oldHeader.getDurable()); + header.setPriority(oldHeader.getPriority()); + header.setTtl(oldHeader.getTtl()); + } + header.setDeliveryCount(UnsignedInteger.valueOf(queueEntry.getDeliveryCount())); + _sectionEncoder.reset(); + _sectionEncoder.encodeObject(header); + Binary encodedHeader = _sectionEncoder.getEncoding(); + + ByteBuffer oldPayload = payload; + payload = ByteBuffer.allocate(oldPayload.remaining() + encodedHeader.getLength()); + payload.put(encodedHeader.getArray(),encodedHeader.getArrayOffset(),encodedHeader.getLength()); + payload.put(oldPayload); + payload.flip(); + } + + transfer.setPayload(payload); + byte[] data = new byte[8]; + ByteBuffer.wrap(data).putLong(_deliveryTag++); + final Binary tag = new Binary(data); + + transfer.setDeliveryTag(tag); + + synchronized(_link.getLock()) + { + if(_link.isAttached()) + { + if(SenderSettleMode.SETTLED.equals(getEndpoint().getSendingSettlementMode())) + { + transfer.setSettled(true); + } + else + { + UnsettledAction action = _acquires + ? new DispositionAction(tag, queueEntry) + : new DoNothingAction(tag, queueEntry); + + _link.addUnsettled(tag, action, queueEntry); + } + + if(_transactionId != null) + { + TransactionalState state = new TransactionalState(); + state.setTxnId(_transactionId); + transfer.setState(state); + } + // TODO - need to deal with failure here + if(_acquires && _transactionId != null) + { + ServerTransaction txn = _link.getTransaction(_transactionId); + if(txn != null) + { + txn.addPostTransactionAction(new ServerTransaction.Action(){ + + public void postCommit() + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void onRollback() + { + if(queueEntry.isAcquiredBy(Subscription_1_0.this)) + { + queueEntry.release(); + _link.getEndpoint().updateDisposition(tag, (DeliveryState)null, true); + + + } + } + }); + } + + } + + getEndpoint().transfer(transfer); + } + else + { + queueEntry.release(); + } + } + } + + } + + public void queueDeleted(final AMQQueue queue) + { + //TODO + getEndpoint().setSource(null); + getEndpoint().detach(); + } + + public synchronized boolean wouldSuspend(final QueueEntry msg) + { + final boolean hasCredit = _link.isAttached() && getEndpoint().hasCreditToSend(); + if(!hasCredit && getState() == State.ACTIVE) + { + suspend(); + } + + return !hasCredit; + } + + public boolean trySendLock() + { + return _stateChangeLock.tryLock(); + } + + public synchronized void suspend() + { + if(_state.compareAndSet(State.ACTIVE, State.SUSPENDED)) + { + _stateListener.stateChange(this, State.ACTIVE, State.SUSPENDED); + } + } + + public void getSendLock() + { + _stateChangeLock.lock(); + } + + public void releaseSendLock() + { + _stateChangeLock.unlock(); + } + + public void releaseQueueEntry(QueueEntry queueEntryImpl) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + + public void onDequeue(final QueueEntry queueEntry) + { + //TODO + } + + public void restoreCredit(final QueueEntry queueEntry) + { + //TODO + } + + public void setStateListener(final StateListener listener) + { + _stateListener = listener; + } + + public State getState() + { + return _state.get(); + } + + public AMQQueue.Context getQueueContext() + { + return _queueContext; + } + + public void setQueueContext(AMQQueue.Context queueContext) + { + _queueContext = queueContext; + } + + + public boolean isActive() + { + return getState() == State.ACTIVE; + } + + public void set(String key, Object value) + { + _properties.put(key, value); + } + + public Object get(String key) + { + return _properties.get(key); + } + + public boolean isSessionTransactional() + { + return false; //TODO + } + + public synchronized void queueEmpty() + { + if(_link.drained()) + { + if(_state.compareAndSet(State.ACTIVE, State.SUSPENDED)) + { + _stateListener.stateChange(this, State.ACTIVE, State.SUSPENDED); + } + } + } + + public synchronized void flowStateChanged() + { + if(isSuspended() && getEndpoint() != null) + { + if(_state.compareAndSet(State.SUSPENDED, State.ACTIVE)) + { + _stateListener.stateChange(this, State.SUSPENDED, State.ACTIVE); + } + _transactionId = _link.getTransactionId(); + } + } + + public Session_1_0 getSession() + { + return _link.getSession(); + } + + private class DispositionAction implements UnsettledAction + { + + private final QueueEntry _queueEntry; + private final Binary _deliveryTag; + + public DispositionAction(Binary tag, QueueEntry queueEntry) + { + _deliveryTag = tag; + _queueEntry = queueEntry; + } + + public boolean process(DeliveryState state, final Boolean settled) + { + + Binary transactionId = null; + final Outcome outcome; + // If disposition is settled this overrides the txn? + if(state instanceof TransactionalState) + { + transactionId = ((TransactionalState)state).getTxnId(); + outcome = ((TransactionalState)state).getOutcome(); + } + else if (state instanceof Outcome) + { + outcome = (Outcome) state; + } + else + { + outcome = null; + } + + + ServerTransaction txn = _link.getTransaction(transactionId); + + if(outcome instanceof Accepted) + { + txn.dequeue(_queueEntry.getQueue(), _queueEntry.getMessage(), + new ServerTransaction.Action() + { + + public void postCommit() + { + if(_queueEntry.isAcquiredBy(Subscription_1_0.this)) + { + _queueEntry.discard(); + } + } + + public void onRollback() + { + + } + }); + txn.addPostTransactionAction(new ServerTransaction.Action() + { + public void postCommit() + { + //_link.getEndpoint().settle(_deliveryTag); + _link.getEndpoint().updateDisposition(_deliveryTag, (DeliveryState)outcome, true); + _link.getEndpoint().sendFlowConditional(); + } + + public void onRollback() + { + if(Boolean.TRUE.equals(settled)) + { + final Modified modified = new Modified(); + modified.setDeliveryFailed(true); + _link.getEndpoint().updateDisposition(_deliveryTag, modified, true); + _link.getEndpoint().sendFlowConditional(); + } + } + }); + } + else if(outcome instanceof Released) + { + txn.addPostTransactionAction(new ServerTransaction.Action() + { + public void postCommit() + { + + _queueEntry.release(); + _link.getEndpoint().settle(_deliveryTag); + } + + public void onRollback() + { + _link.getEndpoint().settle(_deliveryTag); + } + }); + } + + else if(outcome instanceof Modified) + { + txn.addPostTransactionAction(new ServerTransaction.Action() + { + public void postCommit() + { + + _queueEntry.release(); + if(Boolean.TRUE.equals(((Modified)outcome).getDeliveryFailed())) + { + _queueEntry.incrementDeliveryCount(); + } + _link.getEndpoint().settle(_deliveryTag); + } + + public void onRollback() + { + if(Boolean.TRUE.equals(settled)) + { + final Modified modified = new Modified(); + modified.setDeliveryFailed(true); + _link.getEndpoint().updateDisposition(_deliveryTag, modified, true); + _link.getEndpoint().sendFlowConditional(); + } + } + }); + } + + return (transactionId == null && outcome != null); + } + } + + private class DoNothingAction implements UnsettledAction + { + public DoNothingAction(final Binary tag, + final QueueEntry queueEntry) + { + } + + public boolean process(final DeliveryState state, final Boolean settled) + { + Binary transactionId = null; + Outcome outcome = null; + // If disposition is settled this overrides the txn? + if(state instanceof TransactionalState) + { + transactionId = ((TransactionalState)state).getTxnId(); + outcome = ((TransactionalState)state).getOutcome(); + } + else if (state instanceof Outcome) + { + outcome = (Outcome) state; + } + return true; + } + } + + public FilterManager getFilters() + { + return _filters; + } + + public void setFilters(final FilterManager filters) + { + _filters = filters; + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/TxnCoordinatorLink_1_0.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/TxnCoordinatorLink_1_0.java new file mode 100644 index 0000000000..a05d14816a --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/TxnCoordinatorLink_1_0.java @@ -0,0 +1,195 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.protocol.v1_0; + +import org.apache.qpid.amqp_1_0.messaging.SectionDecoder; +import org.apache.qpid.amqp_1_0.messaging.SectionDecoderImpl; +import org.apache.qpid.amqp_1_0.transport.LinkEndpoint; +import org.apache.qpid.amqp_1_0.transport.ReceivingLinkEndpoint; +import org.apache.qpid.amqp_1_0.transport.ReceivingLinkListener; +import org.apache.qpid.amqp_1_0.type.*; +import org.apache.qpid.amqp_1_0.type.DeliveryState; +import org.apache.qpid.amqp_1_0.type.messaging.*; +import org.apache.qpid.amqp_1_0.type.transaction.Declare; +import org.apache.qpid.amqp_1_0.type.transaction.Declared; +import org.apache.qpid.amqp_1_0.type.transaction.Discharge; +import org.apache.qpid.amqp_1_0.type.transport.*; +import org.apache.qpid.amqp_1_0.type.transport.Error; +import org.apache.qpid.server.txn.LocalTransaction; +import org.apache.qpid.server.txn.ServerTransaction; +import org.apache.qpid.server.virtualhost.VirtualHost; + +import java.nio.ByteBuffer; +import java.util.*; + +public class TxnCoordinatorLink_1_0 implements ReceivingLinkListener, Link_1_0 +{ + private VirtualHost _vhost; + private ReceivingLinkEndpoint _endpoint; + + private ArrayList<Transfer> _incompleteMessage; + private SectionDecoder _sectionDecoder; + private LinkedHashMap<Integer, ServerTransaction> _openTransactions; + private Session_1_0 _session; + + + public TxnCoordinatorLink_1_0(VirtualHost vhost, + Session_1_0 session_1_0, ReceivingLinkEndpoint endpoint, + LinkedHashMap<Integer, ServerTransaction> openTransactions) + { + _vhost = vhost; + _session = session_1_0; + _endpoint = endpoint; + _sectionDecoder = new SectionDecoderImpl(endpoint.getSession().getConnection().getDescribedTypeRegistry()); + _openTransactions = openTransactions; + } + + public void messageTransfer(Transfer xfr) + { + // TODO - cope with fragmented messages + + ByteBuffer payload = null; + + + if(Boolean.TRUE.equals(xfr.getMore()) && _incompleteMessage == null) + { + _incompleteMessage = new ArrayList<Transfer>(); + _incompleteMessage.add(xfr); + return; + } + else if(_incompleteMessage != null) + { + _incompleteMessage.add(xfr); + if(Boolean.TRUE.equals(xfr.getMore())) + { + return; + } + + int size = 0; + for(Transfer t : _incompleteMessage) + { + size += t.getPayload().limit(); + } + payload = ByteBuffer.allocate(size); + for(Transfer t : _incompleteMessage) + { + payload.put(t.getPayload().duplicate()); + } + payload.flip(); + _incompleteMessage=null; + + } + else + { + payload = xfr.getPayload(); + } + + + // Only interested int he amqp-value section that holds the message to the co-ordinator + try + { + List<Section> sections = _sectionDecoder.parseAll(payload); + + for(Section section : sections) + { + if(section instanceof AmqpValue) + { + Object command = ((AmqpValue) section).getValue(); + + if(command instanceof Declare) + { + Integer txnId = Integer.valueOf(0); + Iterator<Integer> existingTxn = _openTransactions.keySet().iterator(); + while(existingTxn.hasNext()) + { + txnId = existingTxn.next(); + } + txnId = Integer.valueOf(txnId.intValue() + 1); + + _openTransactions.put(txnId, new LocalTransaction(_vhost.getMessageStore())); + + Declared state = new Declared(); + + + + state.setTxnId(_session.integerToBinary(txnId)); + _endpoint.updateDisposition(xfr.getDeliveryTag(), state, true); + + } + else if(command instanceof Discharge) + { + Discharge discharge = (Discharge) command; + + DeliveryState state = xfr.getState(); + discharge(_session.binaryToInteger(discharge.getTxnId()), discharge.getFail()); + _endpoint.updateDisposition(xfr.getDeliveryTag(), new Accepted(), true); + + } + } + } + + } + catch (AmqpErrorException e) + { + e.printStackTrace(); //TODO. + } + + } + + public void remoteDetached(LinkEndpoint endpoint, Detach detach) + { + //TODO + endpoint.detach(); + } + + private Error discharge(Integer transactionId, boolean fail) + { + Error error = null; + ServerTransaction txn = _openTransactions.get(transactionId); + if(txn != null) + { + if(fail) + { + txn.rollback(); + } + else + { + txn.commit(); + } + _openTransactions.remove(transactionId); + } + else + { + error = new Error(); + error.setCondition(AmqpError.NOT_FOUND); + error.setDescription("Unkown transactionId" + transactionId); + } + return error; + } + + + + public void start() + { + _endpoint.setLinkCredit(UnsignedInteger.ONE); + _endpoint.setCreditWindow(); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/UnsettledAction.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/UnsettledAction.java new file mode 100644 index 0000000000..c497cc5146 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/UnsettledAction.java @@ -0,0 +1,8 @@ +package org.apache.qpid.server.protocol.v1_0; + +import org.apache.qpid.amqp_1_0.type.DeliveryState; + +public interface UnsettledAction +{ + boolean process(DeliveryState state, Boolean settled); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java index f6bf6626a0..46c2a635b7 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java @@ -23,19 +23,20 @@ package org.apache.qpid.server.queue; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.Map; +import java.util.UUID; public class AMQPriorityQueue extends OutOfOrderQueue { - protected AMQPriorityQueue(final String name, + protected AMQPriorityQueue(UUID id, + final String name, final boolean durable, final String owner, final boolean autoDelete, boolean exclusive, final VirtualHost virtualHost, - Map<String, Object> arguments, - int priorities) + Map<String, Object> arguments, int priorities) { - super(name, durable, owner, autoDelete, exclusive, virtualHost, new PriorityQueueList.Factory(priorities), arguments); + super(id, name, durable, owner, autoDelete, exclusive, virtualHost, new PriorityQueueList.Factory(priorities), arguments); } public int getPriorities() diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java index a3da598bdf..e643338c3d 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java @@ -34,7 +34,6 @@ import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.security.AuthorizationHolder; import org.apache.qpid.server.store.TransactionLogResource; import org.apache.qpid.server.subscription.Subscription; -import org.apache.qpid.server.txn.ServerTransaction; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.List; @@ -141,10 +140,9 @@ public interface AMQQueue extends Managable, Comparable<AMQQueue>, ExchangeRefer public List<QueueEntry> getMessagesRangeOnTheQueue(final long fromPosition, final long toPosition); - void moveMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName, - ServerTransaction transaction); + void moveMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName); - void copyMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName, ServerTransaction transaction); + void copyMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName); void removeMessagesFromQueue(long fromMessageId, long toMessageId); diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java index aca5891d2e..f2b7d7c56b 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java @@ -20,6 +20,10 @@ */ package org.apache.qpid.server.queue; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + import org.apache.qpid.AMQException; import org.apache.qpid.AMQSecurityException; import org.apache.qpid.exchange.ExchangeDefaults; @@ -30,12 +34,10 @@ import org.apache.qpid.server.configuration.ServerConfiguration; import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.exchange.ExchangeFactory; import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.virtualhost.VirtualHost; -import java.util.HashMap; -import java.util.Map; - public class AMQQueueFactory { public static final String X_QPID_PRIORITIES = "x-qpid-priorities"; @@ -166,8 +168,13 @@ public class AMQQueueFactory } }; - - /** @see #createAMQQueueImpl(String, boolean, String, boolean, boolean, VirtualHost, Map) */ + /** + * Creates a new queue with a random id. + * + * @see #createAMQQueueImpl(UUID, String, boolean, String, boolean, boolean, VirtualHost, Map) + * @deprecated because only called from unit tests + * */ + @Deprecated public static AMQQueue createAMQQueueImpl(AMQShortString name, boolean durable, AMQShortString owner, @@ -175,22 +182,28 @@ public class AMQQueueFactory boolean exclusive, VirtualHost virtualHost, final FieldTable arguments) throws AMQException { - return createAMQQueueImpl(name == null ? null : name.toString(), + return createAMQQueueImpl(UUIDGenerator.generateUUID(), + name == null ? null : name.toString(), durable, owner == null ? null : owner.toString(), autoDelete, - exclusive, - virtualHost, FieldTable.convertToMap(arguments)); + exclusive, virtualHost, FieldTable.convertToMap(arguments)); } - - public static AMQQueue createAMQQueueImpl(String queueName, + /** + * @param id the id to use. If default then one is generated from queueName. TODO check correctness of calls that pass a null value. + */ + public static AMQQueue createAMQQueueImpl(UUID id, + String queueName, boolean durable, String owner, boolean autoDelete, - boolean exclusive, - VirtualHost virtualHost, Map<String, Object> arguments) throws AMQSecurityException, AMQException + boolean exclusive, VirtualHost virtualHost, Map<String, Object> arguments) throws AMQSecurityException, AMQException { + if (id == null) + { + throw new IllegalArgumentException("Queue id must not be null"); + } if (queueName == null) { throw new IllegalArgumentException("Queue name must not be null"); @@ -241,19 +254,19 @@ public class AMQQueueFactory AMQQueue q; if(sortingKey != null) { - q = new SortedQueue(queueName, durable, owner, autoDelete, exclusive, virtualHost, arguments, sortingKey); + q = new SortedQueue(id, queueName, durable, owner, autoDelete, exclusive, virtualHost, arguments, sortingKey); } else if(conflationKey != null) { - q = new ConflationQueue(queueName, durable, owner, autoDelete, exclusive, virtualHost, arguments, conflationKey); + q = new ConflationQueue(id, queueName, durable, owner, autoDelete, exclusive, virtualHost, arguments, conflationKey); } else if(priorities > 1) { - q = new AMQPriorityQueue(queueName, durable, owner, autoDelete, exclusive, virtualHost, arguments, priorities); + q = new AMQPriorityQueue(id, queueName, durable, owner, autoDelete, exclusive, virtualHost, arguments, priorities); } else { - q = new SimpleAMQQueue(queueName, durable, owner, autoDelete, exclusive, virtualHost, arguments); + q = new SimpleAMQQueue(id, queueName, durable, owner, autoDelete, exclusive, virtualHost, arguments); } //Register the new queue @@ -287,12 +300,12 @@ public class AMQQueueFactory if(dlExchange == null) { - dlExchange = exchangeFactory.createExchange(new AMQShortString(dlExchangeName), ExchangeDefaults.FANOUT_EXCHANGE_CLASS, true, false, 0); + dlExchange = exchangeFactory.createExchange(UUIDGenerator.generateUUID(dlExchangeName, virtualHost.getName()), new AMQShortString(dlExchangeName), ExchangeDefaults.FANOUT_EXCHANGE_CLASS, true, false, 0); exchangeRegistry.registerExchange(dlExchange); //enter the dle in the persistent store - virtualHost.getDurableConfigurationStore().createExchange(dlExchange); + virtualHost.getMessageStore().createExchange(dlExchange); } } @@ -309,10 +322,10 @@ public class AMQQueueFactory args.put(X_QPID_DLQ_ENABLED, false); args.put(X_QPID_MAXIMUM_DELIVERY_COUNT, 0); - dlQueue = createAMQQueueImpl(dlQueueName, true, owner, false, exclusive, virtualHost, args); + dlQueue = createAMQQueueImpl(UUIDGenerator.generateUUID(dlQueueName, virtualHost.getName()), dlQueueName, true, owner, false, exclusive, virtualHost, args); //enter the dlq in the persistent store - virtualHost.getDurableConfigurationStore().createQueue(dlQueue, FieldTable.convertToFieldTable(args)); + virtualHost.getMessageStore().createQueue(dlQueue, FieldTable.convertToFieldTable(args)); } } @@ -364,7 +377,10 @@ public class AMQQueueFactory arguments.put(X_QPID_DLQ_ENABLED, true); } - AMQQueue q = createAMQQueueImpl(queueName, durable, owner, autodelete, exclusive, host, arguments); + // we need queues that are defined in config to have deterministic ids. + UUID id = UUIDGenerator.generateUUID(queueName, host.getName()); + + AMQQueue q = createAMQQueueImpl(id, queueName, durable, owner, autodelete, exclusive, host, arguments); q.configure(config); return q; } diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java index 7c59097965..b0d4cb3486 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java @@ -36,8 +36,6 @@ import org.apache.qpid.server.message.AMQMessage; import org.apache.qpid.server.message.AMQMessageHeader; import org.apache.qpid.server.message.MessageTransferMessage; import org.apache.qpid.server.message.ServerMessage; -import org.apache.qpid.server.txn.LocalTransaction; -import org.apache.qpid.server.txn.ServerTransaction; import org.apache.qpid.transport.MessageProperties; import javax.management.JMException; @@ -613,9 +611,7 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que throw new OperationsException("\"From MessageId\" should be greater than 0 and less than \"To MessageId\""); } - ServerTransaction txn = new LocalTransaction(_queue.getVirtualHost().getMessageStore()); - _queue.moveMessagesToAnotherQueue(fromMessageId, toMessageId, toQueueName, txn); - txn.commit(); + _queue.moveMessagesToAnotherQueue(fromMessageId, toMessageId, toQueueName); } /** @@ -648,11 +644,7 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que throw new OperationsException("\"From MessageId\" should be greater than 0 and less than \"To MessageId\""); } - ServerTransaction txn = new LocalTransaction(_queue.getVirtualHost().getMessageStore()); - - _queue.copyMessagesToAnotherQueue(fromMessageId, toMessageId, toQueueName, txn); - - txn.commit(); + _queue.copyMessagesToAnotherQueue(fromMessageId, toMessageId, toQueueName); } /** diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/ConflationQueue.java b/java/broker/src/main/java/org/apache/qpid/server/queue/ConflationQueue.java index 2c645cc555..c2813bb7a5 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/ConflationQueue.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/ConflationQueue.java @@ -21,22 +21,23 @@ package org.apache.qpid.server.queue; -import org.apache.qpid.server.virtualhost.VirtualHost; - import java.util.Map; +import java.util.UUID; + +import org.apache.qpid.server.virtualhost.VirtualHost; public class ConflationQueue extends SimpleAMQQueue { - protected ConflationQueue(String name, + protected ConflationQueue(UUID id, + String name, boolean durable, String owner, boolean autoDelete, boolean exclusive, VirtualHost virtualHost, - Map<String, Object> args, - String conflationKey) + Map<String, Object> args, String conflationKey) { - super(name, durable, owner, autoDelete, exclusive, virtualHost, new ConflationQueueList.Factory(conflationKey), args); + super(id, name, durable, owner, autoDelete, exclusive, virtualHost, new ConflationQueueList.Factory(conflationKey), args); } public String getConflationKey() diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/DefaultQueueRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/queue/DefaultQueueRegistry.java index d76487073d..2493974d45 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/DefaultQueueRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/DefaultQueueRegistry.java @@ -20,15 +20,21 @@ */ package org.apache.qpid.server.queue; +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.exchange.DefaultExchangeRegistry; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.Collection; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; public class DefaultQueueRegistry implements QueueRegistry { + private static final Logger LOGGER = Logger.getLogger(DefaultExchangeRegistry.class); + private ConcurrentMap<AMQShortString, AMQQueue> _queueMap = new ConcurrentHashMap<AMQShortString, AMQQueue>(); private final VirtualHost _virtualHost; @@ -72,4 +78,36 @@ public class DefaultQueueRegistry implements QueueRegistry { return getQueue(new AMQShortString(queue)); } + + @Override + public void stopAllAndUnregisterMBeans() + { + for (final AMQQueue queue : getQueues()) + { + queue.stop(); + try + { + queue.getManagedObject().unregister(); + } + catch (AMQException e) + { + LOGGER.warn("Failed to unregister mbean", e); + } + } + _queueMap.clear(); + } + + @Override + public synchronized AMQQueue getQueue(UUID queueId) + { + Collection<AMQQueue> queues = _queueMap.values(); + for (AMQQueue queue : queues) + { + if (queue.getId().equals(queueId)) + { + return queue; + } + } + return null; + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/IncomingMessage.java b/java/broker/src/main/java/org/apache/qpid/server/queue/IncomingMessage.java index 0162f1b738..c5a610c7b6 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/IncomingMessage.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/IncomingMessage.java @@ -70,8 +70,6 @@ public class IncomingMessage implements Filterable, InboundMessage, EnqueableMes private Exchange _exchange; - - private int _receivedChunkCount = 0; private List<ContentChunk> _contentChunks = new ArrayList<ContentChunk>(); // we keep both the original meta data object and the store reference to it just in case the @@ -80,13 +78,20 @@ public class IncomingMessage implements Filterable, InboundMessage, EnqueableMes private MessageMetaData _messageMetaData; private StoredMessage<MessageMetaData> _storedMessageHandle; + private Object _connectionReference; public IncomingMessage( final MessagePublishInfo info ) { + this(info, null); + } + + public IncomingMessage(MessagePublishInfo info, Object reference) + { _messagePublishInfo = info; + _connectionReference = reference; } public void setContentHeaderBody(final ContentHeaderBody contentHeaderBody) throws AMQException @@ -125,12 +130,6 @@ public class IncomingMessage implements Filterable, InboundMessage, EnqueableMes } - public MessageMetaData headersReceived() - { - - return headersReceived(System.currentTimeMillis()); - } - public MessageMetaData headersReceived(long currentTime) { _messageMetaData = new MessageMetaData(_messagePublishInfo, _contentHeaderBody, 0, currentTime); @@ -143,16 +142,10 @@ public class IncomingMessage implements Filterable, InboundMessage, EnqueableMes return _destinationQueues; } - public int addContentBodyFrame(final ContentChunk contentChunk) - throws AMQException + public void addContentBodyFrame(final ContentChunk contentChunk) throws AMQException { - _storedMessageHandle.addContent((int)_bodyLengthReceived, ByteBuffer.wrap(contentChunk.getData())); _bodyLengthReceived += contentChunk.getSize(); _contentChunks.add(contentChunk); - - - - return _receivedChunkCount++; } public boolean allContentReceived() @@ -252,18 +245,12 @@ public class IncomingMessage implements Filterable, InboundMessage, EnqueableMes return _expiration; } - public int getReceivedChunkCount() - { - return _receivedChunkCount; - } - - public int getBodyCount() throws AMQException { return _contentChunks.size(); } - public ContentChunk getContentChunk(int index) throws IllegalArgumentException, AMQException + public ContentChunk getContentChunk(int index) { return _contentChunks.get(index); } @@ -318,4 +305,14 @@ public class IncomingMessage implements Filterable, InboundMessage, EnqueableMes { return _storedMessageHandle; } + + public Object getConnectionReference() + { + return _connectionReference; + } + + public MessageMetaData getMessageMetaData() + { + return _messageMetaData; + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/OutOfOrderQueue.java b/java/broker/src/main/java/org/apache/qpid/server/queue/OutOfOrderQueue.java index 53121fc031..daa5db393a 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/OutOfOrderQueue.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/OutOfOrderQueue.java @@ -1,3 +1,23 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.subscription.Subscription; @@ -5,15 +25,16 @@ import org.apache.qpid.server.subscription.SubscriptionList; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.Map; +import java.util.UUID; public abstract class OutOfOrderQueue extends SimpleAMQQueue { - protected OutOfOrderQueue(String name, boolean durable, String owner, - boolean autoDelete, boolean exclusive, VirtualHost virtualHost, - QueueEntryListFactory entryListFactory, Map<String, Object> arguments) + protected OutOfOrderQueue(UUID id, String name, boolean durable, + String owner, boolean autoDelete, boolean exclusive, + VirtualHost virtualHost, QueueEntryListFactory entryListFactory, Map<String, Object> arguments) { - super(name, durable, owner, autoDelete, exclusive, virtualHost, entryListFactory, arguments); + super(id, name, durable, owner, autoDelete, exclusive, virtualHost, entryListFactory, arguments); } @Override diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java b/java/broker/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java index 05141a48a1..1d13ee66c0 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java @@ -25,19 +25,19 @@ import org.apache.qpid.server.message.ServerMessage; public class PriorityQueueList implements QueueEntryList<SimpleQueueEntryImpl> { private final AMQQueue _queue; - private final SimpleQueueEntryList[] _priorityLists; + private final PriorityQueueEntrySubList[] _priorityLists; private final int _priorities; private final int _priorityOffset; public PriorityQueueList(AMQQueue queue, int priorities) { _queue = queue; - _priorityLists = new SimpleQueueEntryList[priorities]; + _priorityLists = new PriorityQueueEntrySubList[priorities]; _priorities = priorities; _priorityOffset = 5-((priorities + 1)/2); for(int i = 0; i < priorities; i++) { - _priorityLists[i] = new SimpleQueueEntryList(queue); + _priorityLists[i] = new PriorityQueueEntrySubList(queue); } } @@ -161,4 +161,48 @@ public class PriorityQueueList implements QueueEntryList<SimpleQueueEntryImpl> return new PriorityQueueList(queue, _priorities); } } + + private static class PriorityQueueEntrySubList extends SimpleQueueEntryList + { + public PriorityQueueEntrySubList(AMQQueue queue) + { + super(queue); + } + + @Override + protected PriorityQueueEntryImpl createQueueEntry(ServerMessage<?> message) + { + return new PriorityQueueEntryImpl(this, message); + } + } + + private static class PriorityQueueEntryImpl extends SimpleQueueEntryImpl + { + public PriorityQueueEntryImpl(PriorityQueueEntrySubList queueEntryList, ServerMessage<?> message) + { + super(queueEntryList, message); + } + + @Override + public int compareTo(final QueueEntry o) + { + byte thisPriority = getMessageHeader().getPriority(); + byte otherPriority = o.getMessageHeader().getPriority(); + + if(thisPriority != otherPriority) + { + /* + * Different priorities, so answer can only be greater than or less than + * + * A message with higher priority (e.g. 5) is conceptually 'earlier' in the + * priority queue than one with a lower priority (e.g. 4). + */ + return thisPriority > otherPriority ? -1 : 1; + } + else + { + return super.compareTo(o); + } + } + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueContext.java b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueContext.java index c8f04c7b96..79279b44c7 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueContext.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueContext.java @@ -52,4 +52,13 @@ final class QueueContext implements AMQQueue.Context { return _releasedEntry; } + + @Override + public String toString() + { + return "QueueContext{" + + "_lastSeenEntry=" + _lastSeenEntry + + ", _releasedEntry=" + _releasedEntry + + '}'; + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java index fea5303e23..209553e8fa 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java @@ -227,9 +227,10 @@ public abstract class QueueEntryImpl implements QueueEntry public void release() { EntryState state = _state; - + if((state.getState() == State.ACQUIRED) &&_stateUpdater.compareAndSet(this, state, AVAILABLE_STATE)) { + if(state instanceof SubscriptionAcquiredState) { getQueue().decrementUnackedMsgCount(); @@ -254,6 +255,7 @@ public abstract class QueueEntryImpl implements QueueEntry routeToAlternate(); } } + } public boolean releaseButRetain() @@ -267,7 +269,6 @@ public abstract class QueueEntryImpl implements QueueEntry Subscription sub = ((SubscriptionAcquiredState) state).getSubscription(); if(_stateUpdater.compareAndSet(this, state, sub.getAssignedState())) { - System.err.println("Message released (and retained)" + getMessage().getMessageNumber()); getQueue().requeue(this); if(_stateChangeListeners != null) { @@ -417,11 +418,19 @@ public abstract class QueueEntryImpl implements QueueEntry if (alternateExchange != null) { - final List<? extends BaseQueue> rerouteQueues = alternateExchange.route(new InboundMessageAdapter(this)); + InboundMessageAdapter inboundMessageAdapter = new InboundMessageAdapter(this); + List<? extends BaseQueue> queues = alternateExchange.route(inboundMessageAdapter); final ServerMessage message = getMessage(); - if (rerouteQueues != null && rerouteQueues.size() != 0) + if ((queues == null || queues.size() == 0) && alternateExchange.getAlternateExchange() != null) { + queues = alternateExchange.getAlternateExchange().route(inboundMessageAdapter); + } + + + if (queues != null && queues.size() != 0) + { + final List<? extends BaseQueue> rerouteQueues = queues; ServerTransaction txn = new LocalTransaction(getQueue().getVirtualHost().getMessageStore()); txn.enqueue(rerouteQueues, message, new ServerTransaction.Action() diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryVisitor.java b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryVisitor.java new file mode 100644 index 0000000000..1578d21321 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryVisitor.java @@ -0,0 +1,22 @@ +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 +* <p/> +* http://www.apache.org/licenses/LICENSE-2.0 +* <p/> +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT 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 interface QueueEntryVisitor +{ + boolean visit(QueueEntry entry); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRegistry.java index 80f6bd1493..72a54c9889 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRegistry.java @@ -24,6 +24,7 @@ import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.Collection; +import java.util.UUID; public interface QueueRegistry { @@ -40,4 +41,8 @@ public interface QueueRegistry Collection<AMQQueue> getQueues(); AMQQueue getQueue(String queue); + + void stopAllAndUnregisterMBeans(); + + AMQQueue getQueue(UUID queueId); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java b/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java index 891a492b7f..d7eb304c92 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java @@ -191,29 +191,29 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes private int _maximumDeliveryCount = ApplicationRegistry.getInstance().getConfiguration().getMaxDeliveryCount(); private final MessageGroupManager _messageGroupManager; - protected SimpleAMQQueue(AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, boolean exclusive, VirtualHost virtualHost, Map<String,Object> arguments) + protected SimpleAMQQueue(UUID id, AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, boolean exclusive, VirtualHost virtualHost, Map<String,Object> arguments) { - this(name, durable, owner, autoDelete, exclusive, virtualHost,new SimpleQueueEntryList.Factory(), arguments); + this(id, name, durable, owner, autoDelete, exclusive,virtualHost, new SimpleQueueEntryList.Factory(), arguments); } - public SimpleAMQQueue(String queueName, boolean durable, String owner, boolean autoDelete, boolean exclusive, VirtualHost virtualHost, Map<String, Object> arguments) + public SimpleAMQQueue(UUID id, String queueName, boolean durable, String owner, boolean autoDelete, boolean exclusive, VirtualHost virtualHost, Map<String, Object> arguments) { - this(queueName, durable, owner, autoDelete, exclusive, virtualHost, new SimpleQueueEntryList.Factory(), arguments); + this(id, queueName, durable, owner, autoDelete, exclusive, virtualHost, new SimpleQueueEntryList.Factory(), arguments); } - public SimpleAMQQueue(String queueName, boolean durable, String owner, boolean autoDelete, boolean exclusive, VirtualHost virtualHost, QueueEntryListFactory entryListFactory, Map<String, Object> arguments) + public SimpleAMQQueue(UUID id, String queueName, boolean durable, String owner, boolean autoDelete, boolean exclusive, VirtualHost virtualHost, QueueEntryListFactory entryListFactory, Map<String, Object> arguments) { - this(queueName == null ? null : new AMQShortString(queueName), durable, owner == null ? null : new AMQShortString(owner), autoDelete, exclusive, virtualHost, entryListFactory, arguments); + this(id, queueName == null ? null : new AMQShortString(queueName), durable, owner == null ? null : new AMQShortString(owner), autoDelete, exclusive, virtualHost, entryListFactory, arguments); } - protected SimpleAMQQueue(AMQShortString name, + protected SimpleAMQQueue(UUID id, + AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, boolean exclusive, VirtualHost virtualHost, - QueueEntryListFactory entryListFactory, - Map<String,Object> arguments) + QueueEntryListFactory entryListFactory, Map<String,Object> arguments) { if (name == null) @@ -236,7 +236,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes _entries = entryListFactory.createQueueEntryList(this); _arguments = arguments; - _id = virtualHost.getConfigStore().createId(); + _id = id; _asyncDelivery = ReferenceCountingExecutorService.getInstance().acquireExecutorService(); @@ -346,7 +346,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes if(isDurable()) { - getVirtualHost().getDurableConfigurationStore().updateQueue(this); + getVirtualHost().getMessageStore().updateQueue(this); } } @@ -862,7 +862,6 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes public void requeue(QueueEntry entry) { - SubscriptionList.SubscriptionNodeIterator subscriberIter = _subscriptionList.iterator(); // iterate over all the subscribers, and if they are in advance of this queue entry then move them backwards while (subscriberIter.advance() && entry.isAvailable()) @@ -1198,19 +1197,10 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes public void moveMessagesToAnotherQueue(final long fromMessageId, final long toMessageId, - String queueName, - ServerTransaction txn) throws IllegalArgumentException + String destinationQueueName) throws IllegalArgumentException { - final AMQQueue toQueue = getVirtualHost().getQueueRegistry().getQueue(new AMQShortString(queueName)); - if (toQueue == null) - { - throw new IllegalArgumentException("Queue '" + queueName + "' is not registered with the virtualhost."); - } - else if (toQueue == this) - { - throw new IllegalArgumentException("The destination queue cant be the same as the source queue"); - } + final AMQQueue toQueue = getValidatedDestinationQueue(destinationQueueName); List<QueueEntry> entries = getMessagesOnTheQueue(new QueueEntryFilter() { @@ -1230,65 +1220,68 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes }); - - // Move the messages in on the message store. - for (final QueueEntry entry : entries) + final ServerTransaction txn = new LocalTransaction(getVirtualHost().getMessageStore()); + boolean shouldRollback = true; + try { - final ServerMessage message = entry.getMessage(); - txn.enqueue(toQueue, message, - new ServerTransaction.Action() - { - - public void postCommit() + // Move the messages in on the message store. + for (final QueueEntry entry : entries) + { + final ServerMessage message = entry.getMessage(); + txn.enqueue(toQueue, message, + new ServerTransaction.Action() { - try + + public void postCommit() { - toQueue.enqueue(message); + try + { + toQueue.enqueue(message); + } + catch (AMQException e) + { + throw new RuntimeException(e); + } } - catch (AMQException e) + + public void onRollback() { - throw new RuntimeException(e); + entry.release(); } - } - - public void onRollback() - { - entry.release(); - } - }); - txn.dequeue(this, message, - new ServerTransaction.Action() - { - - public void postCommit() + }); + txn.dequeue(this, message, + new ServerTransaction.Action() { - entry.discard(); - } - public void onRollback() - { + public void postCommit() + { + entry.discard(); + } - } - }); + public void onRollback() + { + } + }); + } + txn.commit(); + shouldRollback = false; + } + finally + { + if (shouldRollback) + { + txn.rollback(); + } } } public void copyMessagesToAnotherQueue(final long fromMessageId, final long toMessageId, - String queueName, - final ServerTransaction txn) throws IllegalArgumentException + String destinationQueueName) throws IllegalArgumentException { - final AMQQueue toQueue = getVirtualHost().getQueueRegistry().getQueue(new AMQShortString(queueName)); - if (toQueue == null) - { - throw new IllegalArgumentException("Queue '" + queueName + "' is not registered with the virtualhost."); - } - else if (toQueue == this) - { - throw new IllegalArgumentException("The destination queue cant be the same as the source queue"); - } + final AMQQueue toQueue = getValidatedDestinationQueue(destinationQueueName); List<QueueEntry> entries = getMessagesOnTheQueue(new QueueEntryFilter() { @@ -1306,36 +1299,63 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } }); - - // Move the messages in on the message store. - for (QueueEntry entry : entries) + final ServerTransaction txn = new LocalTransaction(_virtualHost.getMessageStore()); + boolean shouldRollback = true; + try { - final ServerMessage message = entry.getMessage(); - - txn.enqueue(toQueue, message, new ServerTransaction.Action() + // Copy the messages in on the message store. + for (QueueEntry entry : entries) { - public void postCommit() + final ServerMessage message = entry.getMessage(); + + txn.enqueue(toQueue, message, new ServerTransaction.Action() { - try + public void postCommit() { - toQueue.enqueue(message); + try + { + toQueue.enqueue(message); + } + catch (AMQException e) + { + throw new RuntimeException(e); + } } - catch (AMQException e) + + public void onRollback() { - throw new RuntimeException(e); } - } + }); - public void onRollback() - { - - } - }); + } + txn.commit(); + shouldRollback = false; + } + finally + { + if (shouldRollback) + { + txn.rollback(); + } } } + private AMQQueue getValidatedDestinationQueue(String queueName) + { + final AMQQueue toQueue = getVirtualHost().getQueueRegistry().getQueue(new AMQShortString(queueName)); + if (toQueue == null) + { + throw new IllegalArgumentException("Queue '" + queueName + "' is not registered with the virtualhost."); + } + else if (toQueue == this) + { + throw new IllegalArgumentException("The destination queue can't be the same as the source queue"); + } + return toQueue; + } + public void removeMessagesFromQueue(long fromMessageId, long toMessageId) { @@ -1515,10 +1535,16 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes for(final QueueEntry entry : entries) { adapter.setEntry(entry); - final List<? extends BaseQueue> rerouteQueues = _alternateExchange.route(adapter); + List<? extends BaseQueue> queues = _alternateExchange.route(adapter); + if((queues == null || queues.size() == 0) && _alternateExchange.getAlternateExchange() != null) + { + queues = _alternateExchange.getAlternateExchange().route(adapter); + } + final ServerMessage message = entry.getMessage(); - if(rerouteQueues != null && rerouteQueues.size() != 0) + if(queues != null && queues.size() != 0) { + final List<? extends BaseQueue> rerouteQueues = queues; txn.enqueue(rerouteQueues, entry.getMessage(), new ServerTransaction.Action() { @@ -1716,6 +1742,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes { boolean atTail = false; final boolean keepSendLockHeld = iterations <= SimpleAMQQueue.MAX_ASYNC_DELIVERIES; + boolean queueEmpty = false; try { @@ -1733,12 +1760,9 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } atTail = attemptDelivery(sub, true); - if (atTail && !sub.isSuspended() && sub.isAutoClose()) + if (atTail && getNextAvailableEntry(sub) == null) { - unregisterSubscription(sub); - - sub.confirmAutoClose(); - + queueEmpty = true; } else if (!atTail) { @@ -1760,6 +1784,11 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes { sub.releaseSendLock(); } + if(queueEmpty) + { + sub.queueEmpty(); + } + sub.flushBatched(); } @@ -1895,7 +1924,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes if(context != null) { QueueEntry releasedNode = context.getReleasedEntry(); - return releasedNode == null || releasedNode.compareTo(entry) < 0; + return releasedNode != null && releasedNode.compareTo(entry) < 0; } else { @@ -1982,13 +2011,9 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes if (subscriptionDone) { sub.flushBatched(); - //close autoClose subscriptions if we are not currently intent on continuing - if (lastLoop && !sub.isSuspended() && sub.isAutoClose()) + if (lastLoop && !sub.isSuspended()) { - - unregisterSubscription(sub); - - sub.confirmAutoClose(); + sub.queueEmpty(); } break; } @@ -2064,9 +2089,13 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes // Only process nodes that are not currently deleted and not dequeued if (!node.isDispensed()) { - // If the node has exired then aquire it + // If the node has exired then acquire it if (node.expired() && node.acquire()) { + if (_logger.isDebugEnabled()) + { + _logger.debug("Dequeuing expired node " + node); + } // Then dequeue it. dequeueEntry(node); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java b/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java index e57390163c..b8d8ec19f4 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java @@ -104,7 +104,7 @@ public class SimpleQueueEntryList implements QueueEntryList<SimpleQueueEntryImpl } } - protected SimpleQueueEntryImpl createQueueEntry(ServerMessage message) + protected SimpleQueueEntryImpl createQueueEntry(ServerMessage<?> message) { return new SimpleQueueEntryImpl(this, message); } @@ -116,7 +116,6 @@ public class SimpleQueueEntryList implements QueueEntryList<SimpleQueueEntryImpl public static class QueueEntryIteratorImpl implements QueueEntryIterator<SimpleQueueEntryImpl> { - private SimpleQueueEntryImpl _lastNode; QueueEntryIteratorImpl(SimpleQueueEntryImpl startNode) @@ -124,10 +123,9 @@ public class SimpleQueueEntryList implements QueueEntryList<SimpleQueueEntryImpl _lastNode = startNode; } - public boolean atTail() { - return _lastNode.getNextNode() == null; + return _lastNode.getNextValidEntry() == null; } public SimpleQueueEntryImpl getNode() @@ -137,28 +135,17 @@ public class SimpleQueueEntryList implements QueueEntryList<SimpleQueueEntryImpl public boolean advance() { + SimpleQueueEntryImpl nextValidNode = _lastNode.getNextValidEntry(); - if(!atTail()) + if(nextValidNode != null) { - SimpleQueueEntryImpl nextNode = _lastNode.getNextNode(); - while(nextNode.isDispensed() && nextNode.getNextNode() != null) - { - nextNode = nextNode.getNextNode(); - } - _lastNode = nextNode; - return true; - - } - else - { - return false; + _lastNode = nextValidNode; } + return nextValidNode != null; } - } - public QueueEntryIteratorImpl iterator() { return new QueueEntryIteratorImpl(_head); diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/SortedQueue.java b/java/broker/src/main/java/org/apache/qpid/server/queue/SortedQueue.java index 446f57b142..b3566df0c4 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/SortedQueue.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/SortedQueue.java @@ -24,6 +24,7 @@ import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.Map; +import java.util.UUID; public class SortedQueue extends OutOfOrderQueue { @@ -33,12 +34,12 @@ public class SortedQueue extends OutOfOrderQueue private final Object _sortedQueueLock = new Object(); private final String _sortedPropertyName; - protected SortedQueue(final String name, final boolean durable, - final String owner, final boolean autoDelete, final boolean exclusive, - final VirtualHost virtualHost, Map<String, Object> arguments, String sortedPropertyName) + protected SortedQueue(UUID id, final String name, + final boolean durable, final String owner, final boolean autoDelete, + final boolean exclusive, final VirtualHost virtualHost, Map<String, Object> arguments, String sortedPropertyName) { - super(name, durable, owner, autoDelete, exclusive, virtualHost, - new SortedQueueEntryListFactory(sortedPropertyName), arguments); + super(id, name, durable, owner, autoDelete, exclusive, + virtualHost, new SortedQueueEntryListFactory(sortedPropertyName), arguments); this._sortedPropertyName = sortedPropertyName; } diff --git a/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java index 224d694932..4ed28b965d 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java @@ -186,19 +186,6 @@ public abstract class ApplicationRegistry implements IApplicationRegistry _qmfService = qmfService; } - static - { - Runtime.getRuntime().addShutdownHook(new Thread(new ShutdownService())); - } - - private static class ShutdownService implements Runnable - { - public void run() - { - remove(); - } - } - public static void initialise(IApplicationRegistry instance) throws Exception { if(instance == null) @@ -273,7 +260,6 @@ public abstract class ApplicationRegistry implements IApplicationRegistry _logger.info("Shutting down ApplicationRegistry(" + instance + ")"); } instance.close(); - instance.getBroker().getSystem().removeBroker(instance.getBroker()); } } catch (Exception e) @@ -536,35 +522,49 @@ public abstract class ApplicationRegistry implements IApplicationRegistry } } - public void close() { if (_logger.isInfoEnabled()) { _logger.info("Shutting down ApplicationRegistry:" + this); } - - //Stop Statistics Reporting - if (_reportingTimer != null) + + //Set the Actor for Broker Shutdown + CurrentActor.set(new BrokerActor(getRootMessageLogger())); + try { - _reportingTimer.cancel(); - } + //Stop Statistics Reporting + if (_reportingTimer != null) + { + _reportingTimer.cancel(); + } + + //Stop incoming connections + unbind(); - //Stop incoming connections - unbind(); + //Shutdown virtualhosts + close(_virtualHostRegistry); - //Shutdown virtualhosts - close(_virtualHostRegistry); + close(_authenticationManager); - close(_authenticationManager); + close(_qmfService); - close(_qmfService); + close(_pluginManager); - close(_pluginManager); + close(_managedObjectRegistry); - close(_managedObjectRegistry); + BrokerConfig broker = getBroker(); + if(broker != null) + { + broker.getSystem().removeBroker(broker); + } - CurrentActor.get().message(BrokerMessages.STOPPED()); + CurrentActor.get().message(BrokerMessages.STOPPED()); + } + finally + { + CurrentActor.remove(); + } } private void unbind() @@ -664,7 +664,7 @@ public abstract class ApplicationRegistry implements IApplicationRegistry public VirtualHost createVirtualHost(final VirtualHostConfiguration vhostConfig) throws Exception { - VirtualHostImpl virtualHost = new VirtualHostImpl(this, vhostConfig, null); + VirtualHostImpl virtualHost = new VirtualHostImpl(this, vhostConfig); _virtualHostRegistry.registerVirtualHost(virtualHost); getBroker().addVirtualHost(virtualHost); return virtualHost; diff --git a/java/broker/src/main/java/org/apache/qpid/server/registry/BrokerConfigAdapter.java b/java/broker/src/main/java/org/apache/qpid/server/registry/BrokerConfigAdapter.java index 58fdc99dd3..db436b99e8 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/registry/BrokerConfigAdapter.java +++ b/java/broker/src/main/java/org/apache/qpid/server/registry/BrokerConfigAdapter.java @@ -114,7 +114,6 @@ public class BrokerConfigAdapter implements BrokerConfig public void addVirtualHost(final VirtualHostConfig virtualHost) { - virtualHost.setBroker(this); _vhosts.put(virtualHost.getId(), virtualHost); getConfigStore().addConfiguredObject(virtualHost); diff --git a/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java index e9ba2764e5..b28e3d6c89 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java @@ -25,8 +25,6 @@ import org.osgi.framework.BundleContext; import org.apache.qpid.AMQException; import org.apache.qpid.server.configuration.ServerConfiguration; -import org.apache.qpid.server.logging.actors.BrokerActor; -import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.management.JMXManagedObjectRegistry; import org.apache.qpid.server.management.NoopManagedObjectRegistry; @@ -45,22 +43,6 @@ public class ConfigurationFileApplicationRegistry extends ApplicationRegistry } @Override - public void close() - { - //Set the Actor for Broker Shutdown - CurrentActor.set(new BrokerActor(getRootMessageLogger())); - try - { - super.close(); - } - finally - { - CurrentActor.remove(); - } - } - - - @Override protected void initialiseManagedObjectRegistry() throws AMQException { if (getConfiguration().getManagementEnabled()) diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.java index 4ffa5a4bc2..6c1a917d5b 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.java @@ -24,6 +24,7 @@ import org.apache.qpid.common.Closeable; import org.apache.qpid.server.plugins.Plugin; import org.apache.qpid.server.security.auth.AuthenticationResult; +import javax.security.auth.callback.CallbackHandler; import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; @@ -88,4 +89,6 @@ public interface AuthenticationManager extends Closeable, Plugin * @return authentication result */ AuthenticationResult authenticate(String username, String password); + + CallbackHandler getHandler(String mechanism); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java index 3fa0de2af0..b5d70d9200 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java @@ -300,6 +300,11 @@ public class PrincipalDatabaseAuthenticationManager implements AuthenticationMan } } + public CallbackHandler getHandler(String mechanism) + { + return _callbackHandlerMap.get(mechanism); + } + /** * @see org.apache.qpid.server.security.auth.manager.AuthenticationManager#authenticate(String, String) */ diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/anonymous/AnonymousSaslServerFactory.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/anonymous/AnonymousSaslServerFactory.java index 3664568b75..4650234972 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/anonymous/AnonymousSaslServerFactory.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/anonymous/AnonymousSaslServerFactory.java @@ -58,4 +58,4 @@ public class AnonymousSaslServerFactory implements SaslServerFactory return new String[]{AnonymousSaslServer.MECHANISM}; } } -}
\ No newline at end of file +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServer.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServer.java index c1f306dce9..a811806c00 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServer.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServer.java @@ -51,56 +51,65 @@ public class PlainSaslServer implements SaslServer public byte[] evaluateResponse(byte[] response) throws SaslException { - try + int authzidNullPosition = findNullPosition(response, 0); + if (authzidNullPosition < 0) { - int authzidNullPosition = findNullPosition(response, 0); - if (authzidNullPosition < 0) - { - throw new SaslException("Invalid PLAIN encoding, authzid null terminator not found"); - } - int authcidNullPosition = findNullPosition(response, authzidNullPosition + 1); - if (authcidNullPosition < 0) - { - throw new SaslException("Invalid PLAIN encoding, authcid null terminator not found"); - } + throw new SaslException("Invalid PLAIN encoding, authzid null terminator not found"); + } + int authcidNullPosition = findNullPosition(response, authzidNullPosition + 1); + if (authcidNullPosition < 0) + { + throw new SaslException("Invalid PLAIN encoding, authcid null terminator not found"); + } + + PlainPasswordCallback passwordCb; + AuthorizeCallback authzCb; + try + { // we do not currently support authcid in any meaningful way String authzid = new String(response, authzidNullPosition + 1, authcidNullPosition - authzidNullPosition - 1, "utf8"); // TODO: should not get pwd as a String but as a char array... int passwordLen = response.length - authcidNullPosition - 1; String pwd = new String(response, authcidNullPosition + 1, passwordLen, "utf8"); - + // we do not care about the prompt but it throws if null NameCallback nameCb = new NameCallback("prompt", authzid); - PlainPasswordCallback passwordCb = new PlainPasswordCallback("prompt", false, pwd); - AuthorizeCallback authzCb = new AuthorizeCallback(authzid, authzid); + passwordCb = new PlainPasswordCallback("prompt", false, pwd); + authzCb = new AuthorizeCallback(authzid, authzid); Callback[] callbacks = new Callback[]{nameCb, passwordCb, authzCb}; _cbh.handle(callbacks); - if (passwordCb.isAuthenticated()) - { - _complete = true; - } - if (authzCb.isAuthorized() && _complete) - { - _authorizationId = authzCb.getAuthenticationID(); - return null; - } - else - { - throw new SaslException("Authentication failed"); - } } catch (IOException e) { + if(e instanceof SaslException) + { + throw (SaslException) e; + } throw new SaslException("Error processing data: " + e, e); } catch (UnsupportedCallbackException e) { throw new SaslException("Unable to obtain data from callback handler: " + e, e); } + + if (passwordCb.isAuthenticated()) + { + _complete = true; + } + + if (authzCb.isAuthorized() && _complete) + { + _authorizationId = authzCb.getAuthenticationID(); + return null; + } + else + { + throw new SaslException("Authentication failed"); + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/ConfigurationRecoveryHandler.java b/java/broker/src/main/java/org/apache/qpid/server/store/ConfigurationRecoveryHandler.java index fb67500da9..1307b1dbd4 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/store/ConfigurationRecoveryHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/store/ConfigurationRecoveryHandler.java @@ -32,19 +32,19 @@ public interface ConfigurationRecoveryHandler public static interface QueueRecoveryHandler { - void queue(String queueName, String owner, boolean exclusive, FieldTable arguments); + void queue(UUID id, String queueName, String owner, boolean exclusive, FieldTable arguments); ExchangeRecoveryHandler completeQueueRecovery(); } public static interface ExchangeRecoveryHandler { - void exchange(String exchangeName, String type, boolean autoDelete); + void exchange(UUID id, String exchangeName, String type, boolean autoDelete); BindingRecoveryHandler completeExchangeRecovery(); } public static interface BindingRecoveryHandler { - void binding(String exchangeName, String queueName, String bindingKey, ByteBuffer buf); + void binding(UUID bindingId, UUID exchangeId, UUID queueId, String bindingName, ByteBuffer buf); BrokerLinkRecoveryHandler completeBindingRecovery(); } @@ -60,13 +60,4 @@ public interface ConfigurationRecoveryHandler void completeBridgeRecoveryForLink(); } - public static interface QueueEntryRecoveryHandler - { - void complete(); - - void queueEntry(String queueName, long messageId); - } - - - } diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/ConfiguredObjectHelper.java b/java/broker/src/main/java/org/apache/qpid/server/store/ConfiguredObjectHelper.java new file mode 100644 index 0000000000..1a67fdf540 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/store/ConfiguredObjectHelper.java @@ -0,0 +1,183 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.store; + +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.model.Binding; +import org.apache.qpid.server.model.Exchange; +import org.apache.qpid.server.model.LifetimePolicy; +import org.apache.qpid.server.model.Queue; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.store.ConfigurationRecoveryHandler.BindingRecoveryHandler; +import org.apache.qpid.server.store.ConfigurationRecoveryHandler.ExchangeRecoveryHandler; +import org.apache.qpid.server.store.ConfigurationRecoveryHandler.QueueRecoveryHandler; +import org.apache.qpid.server.util.MapJsonSerializer; + +public class ConfiguredObjectHelper +{ + /** + * Name of queue attribute to store queue creation arguments. + * <p> + * This attribute is not defined yet on Queue configured object interface. + */ + private static final String QUEUE_ARGUMENTS = "ARGUMENTS"; + + private MapJsonSerializer _serializer = new MapJsonSerializer(); + + public void loadQueue(ConfiguredObjectRecord configuredObject, QueueRecoveryHandler qrh) + { + if (Queue.class.getName().equals(configuredObject.getType())) + { + Map<String, Object> attributeMap = _serializer.deserialize(configuredObject.getAttributes()); + String queueName = (String) attributeMap.get(Queue.NAME); + String owner = (String) attributeMap.get(Queue.OWNER); + boolean exclusive = (Boolean) attributeMap.get(Queue.EXCLUSIVE); + @SuppressWarnings("unchecked") + Map<String, Object> queueArgumentsMap = (Map<String, Object>) attributeMap.get(QUEUE_ARGUMENTS); + FieldTable arguments = null; + if (queueArgumentsMap != null) + { + arguments = FieldTable.convertToFieldTable(queueArgumentsMap); + } + qrh.queue(configuredObject.getId(), queueName, owner, exclusive, arguments); + } + } + + public ConfiguredObjectRecord updateQueueConfiguredObject(final AMQQueue queue, ConfiguredObjectRecord queueRecord) + { + Map<String, Object> attributesMap = _serializer.deserialize(queueRecord.getAttributes()); + attributesMap.put(Queue.NAME, queue.getName()); + attributesMap.put(Queue.EXCLUSIVE, queue.isExclusive()); + String newJson = _serializer.serialize(attributesMap); + ConfiguredObjectRecord newQueueRecord = new ConfiguredObjectRecord(queue.getId(), queueRecord.getType(), newJson); + return newQueueRecord; + } + + public ConfiguredObjectRecord createQueueConfiguredObject(AMQQueue queue, FieldTable arguments) + { + Map<String, Object> attributesMap = new HashMap<String, Object>(); + attributesMap.put(Queue.NAME, queue.getName()); + attributesMap.put(Queue.OWNER, AMQShortString.toString(queue.getOwner())); + attributesMap.put(Queue.EXCLUSIVE, queue.isExclusive()); + if (arguments != null) + { + attributesMap.put(QUEUE_ARGUMENTS, FieldTable.convertToMap(arguments)); + } + String json = _serializer.serialize(attributesMap); + ConfiguredObjectRecord configuredObject = new ConfiguredObjectRecord(queue.getId(), Queue.class.getName(), json); + return configuredObject; + } + + public void loadExchange(ConfiguredObjectRecord configuredObject, ExchangeRecoveryHandler erh) + { + if (Exchange.class.getName().equals(configuredObject.getType())) + { + Map<String, Object> attributeMap = _serializer.deserialize(configuredObject.getAttributes()); + String exchangeName = (String) attributeMap.get(Exchange.NAME); + String exchangeType = (String) attributeMap.get(Exchange.TYPE); + String lifeTimePolicy = (String) attributeMap.get(Exchange.LIFETIME_POLICY); + boolean autoDelete = lifeTimePolicy == null + || LifetimePolicy.valueOf(lifeTimePolicy) == LifetimePolicy.AUTO_DELETE; + erh.exchange(configuredObject.getId(), exchangeName, exchangeType, autoDelete); + } + } + + public ConfiguredObjectRecord createExchangeConfiguredObject(org.apache.qpid.server.exchange.Exchange exchange) + { + Map<String, Object> attributesMap = new HashMap<String, Object>(); + attributesMap.put(Exchange.NAME, exchange.getName()); + attributesMap.put(Exchange.TYPE, AMQShortString.toString(exchange.getTypeShortString())); + attributesMap.put(Exchange.LIFETIME_POLICY, exchange.isAutoDelete() ? LifetimePolicy.AUTO_DELETE.name() + : LifetimePolicy.PERMANENT.name()); + String json = _serializer.serialize(attributesMap); + ConfiguredObjectRecord configuredObject = new ConfiguredObjectRecord(exchange.getId(), Exchange.class.getName(), json); + return configuredObject; + } + + public void loadQueueBinding(ConfiguredObjectRecord configuredObject, BindingRecoveryHandler brh) + { + if (Binding.class.getName().equals(configuredObject.getType())) + { + Map<String, Object> attributeMap = _serializer.deserialize(configuredObject.getAttributes()); + UUID exchangeId = UUID.fromString((String)attributeMap.get(Binding.EXCHANGE)); + UUID queueId = UUID.fromString((String) attributeMap.get(Binding.QUEUE)); + String bindingName = (String) attributeMap.get(Binding.NAME); + + @SuppressWarnings("unchecked") + Map<String, Object> bindingArgumentsMap = (Map<String, Object>) attributeMap.get(Binding.ARGUMENTS); + FieldTable arguments = null; + if (bindingArgumentsMap != null) + { + arguments = FieldTable.convertToFieldTable(bindingArgumentsMap); + } + ByteBuffer argumentsBB = (arguments == null ? null : ByteBuffer.wrap(arguments.getDataAsBytes())); + + brh.binding(configuredObject.getId(), exchangeId, queueId, bindingName, argumentsBB); + } + } + + public ConfiguredObjectRecord createBindingConfiguredObject(org.apache.qpid.server.binding.Binding binding) + { + Map<String, Object> attributesMap = new HashMap<String, Object>(); + attributesMap.put(Binding.NAME, binding.getBindingKey()); + attributesMap.put(Binding.EXCHANGE, binding.getExchange().getId()); + attributesMap.put(Binding.QUEUE, binding.getQueue().getId()); + Map<String, Object> arguments = binding.getArguments(); + if (arguments != null) + { + attributesMap.put(Binding.ARGUMENTS, arguments); + } + String json = _serializer.serialize(attributesMap); + ConfiguredObjectRecord configuredObject = new ConfiguredObjectRecord(binding.getId(), Binding.class.getName(), json); + return configuredObject; + } + + public void recoverQueues(QueueRecoveryHandler qrh, List<ConfiguredObjectRecord> configuredObjects) + { + for (ConfiguredObjectRecord configuredObjectRecord : configuredObjects) + { + loadQueue(configuredObjectRecord, qrh); + } + } + + public void recoverExchanges(ExchangeRecoveryHandler erh, List<ConfiguredObjectRecord> configuredObjects) + { + for (ConfiguredObjectRecord configuredObjectRecord : configuredObjects) + { + loadExchange(configuredObjectRecord, erh); + } + } + + public void recoverBindings(BindingRecoveryHandler brh, List<ConfiguredObjectRecord> configuredObjects) + { + for (ConfiguredObjectRecord configuredObjectRecord : configuredObjects) + { + loadQueueBinding(configuredObjectRecord, brh); + } + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/AbstractMessageStore.java b/java/broker/src/main/java/org/apache/qpid/server/store/ConfiguredObjectRecord.java index fc5d2a4e42..95e1713d78 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/store/AbstractMessageStore.java +++ b/java/broker/src/main/java/org/apache/qpid/server/store/ConfiguredObjectRecord.java @@ -20,24 +20,46 @@ */ package org.apache.qpid.server.store; -import org.apache.qpid.server.logging.LogSubject; -import org.apache.qpid.server.logging.actors.CurrentActor; -import org.apache.qpid.server.logging.messages.MessageStoreMessages; -import org.apache.qpid.server.logging.subjects.MessageStoreLogSubject; -import org.apache.qpid.server.virtualhost.VirtualHost; +import java.util.UUID; -public abstract class AbstractMessageStore implements MessageStore +public class ConfiguredObjectRecord { - private LogSubject _logSubject; + private UUID _id; + private String _type; + private String _attributes; - public void configure(VirtualHost virtualHost) throws Exception + public ConfiguredObjectRecord(UUID id, String type, String attributes) { - _logSubject = new MessageStoreLogSubject(virtualHost, this); - CurrentActor.get().message(_logSubject, MessageStoreMessages.CREATED(this.getClass().getName())); + super(); + _id = id; + _type = type; + _attributes = attributes; } - public void close() throws Exception + public UUID getId() { - CurrentActor.get().message(_logSubject,MessageStoreMessages.CLOSED()); + return _id; } + + public void setId(UUID id) + { + _id = id; + } + + public String getType() + { + return _type; + } + + public String getAttributes() + { + return _attributes; + } + + @Override + public String toString() + { + return "ConfiguredObjectRecord [id=" + _id + ", type=" + _type + ", attributes=" + _attributes + "]"; + } + } diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/DurableConfigurationStore.java b/java/broker/src/main/java/org/apache/qpid/server/store/DurableConfigurationStore.java index 123ecd8145..655887e5c2 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/store/DurableConfigurationStore.java +++ b/java/broker/src/main/java/org/apache/qpid/server/store/DurableConfigurationStore.java @@ -23,12 +23,11 @@ package org.apache.qpid.server.store; import org.apache.commons.configuration.Configuration; import org.apache.qpid.AMQStoreException; -import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.binding.Binding; import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.federation.Bridge; import org.apache.qpid.server.federation.BrokerLink; -import org.apache.qpid.server.logging.LogSubject; import org.apache.qpid.server.queue.AMQQueue; public interface DurableConfigurationStore @@ -36,23 +35,21 @@ public interface DurableConfigurationStore public static interface Source { - DurableConfigurationStore getDurableConfigurationStore(); + DurableConfigurationStore getMessageStore(); } /** * Called after instantiation in order to configure the message store. A particular implementation can define * whatever parameters it wants. * - * @param name The name to be used by this storem + * @param name The name to be used by this store * @param recoveryHandler Handler to be called as the store recovers on start up * @param config The apache commons configuration object. - * * @throws Exception If any error occurs that means the store is unable to configure itself. */ void configureConfigStore(String name, ConfigurationRecoveryHandler recoveryHandler, - Configuration config, - LogSubject logSubject) throws Exception; + Configuration config) throws Exception; /** * Makes the specified exchange persistent. * @@ -72,28 +69,22 @@ public interface DurableConfigurationStore void removeExchange(Exchange exchange) throws AMQStoreException; /** - * Binds the specified queue to an exchange with a routing key. + * Store the queue binding. * - * @param exchange The exchange to bind to. - * @param routingKey The routing key to bind by. - * @param queue The queue to bind. - * @param args Additional parameters. + * @param binding queue binding * * @throws AMQStoreException if the operation fails for any reason. */ - void bindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQStoreException; + void bindQueue(Binding binding) throws AMQStoreException; /** - * Unbinds the specified from an exchange under a particular routing key. + * Removes queue binding * - * @param exchange The exchange to unbind from. - * @param routingKey The routing key to unbind. - * @param queue The queue to unbind. - * @param args Additonal parameters. + * @param binding queue binding to remove * * @throws AMQStoreException If the operation fails for any reason. */ - void unbindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQStoreException; + void unbindQueue(Binding binding) throws AMQStoreException; /** * Makes the specified queue persistent. diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/Event.java b/java/broker/src/main/java/org/apache/qpid/server/store/Event.java new file mode 100644 index 0000000000..bbde11ab4c --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/store/Event.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.store; + +public enum Event +{ + BEFORE_INIT, + AFTER_INIT, + BEFORE_ACTIVATE, + AFTER_ACTIVATE, + BEFORE_PASSIVATE, + AFTER_PASSIVATE, + BEFORE_CLOSE, + AFTER_CLOSE +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/EventListener.java b/java/broker/src/main/java/org/apache/qpid/server/store/EventListener.java new file mode 100644 index 0000000000..33ae7b5b24 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/store/EventListener.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.store; + +public interface EventListener +{ + public void event(Event event); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/EventManager.java b/java/broker/src/main/java/org/apache/qpid/server/store/EventManager.java new file mode 100644 index 0000000000..21ae3924b8 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/store/EventManager.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.store; + +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; + +public class EventManager +{ + private Map<Event, List<EventListener>> _listeners = new EnumMap<Event, List<EventListener>> (Event.class); + + public synchronized void addEventListener(EventListener listener, Event... events) + { + for(Event event : events) + { + List<EventListener> list = _listeners.get(event); + if(list == null) + { + list = new ArrayList<EventListener>(); + _listeners.put(event,list); + } + list.add(listener); + } + } + + public synchronized void notifyEvent(Event event) + { + if (_listeners.containsKey(event)) + { + for (EventListener listener : _listeners.get(event)) + { + listener.event(event); + } + } + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java b/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java index 546a81a050..59624b7a75 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java +++ b/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java @@ -21,200 +21,117 @@ package org.apache.qpid.server.store; import org.apache.commons.configuration.Configuration; -import org.apache.log4j.Logger; - -import org.apache.qpid.AMQException; import org.apache.qpid.AMQStoreException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.server.federation.Bridge; -import org.apache.qpid.server.federation.BrokerLink; -import org.apache.qpid.server.logging.LogSubject; -import org.apache.qpid.server.logging.actors.CurrentActor; -import org.apache.qpid.server.logging.messages.ConfigStoreMessages; -import org.apache.qpid.server.logging.messages.MessageStoreMessages; import org.apache.qpid.server.message.EnqueableMessage; -import org.apache.qpid.server.queue.AMQQueue; -import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; -/** A simple message store that stores the messages in a threadsafe structure in memory. */ -public class MemoryMessageStore implements MessageStore, DurableConfigurationStore +/** A simple message store that stores the messages in a thread-safe structure in memory. */ +public class MemoryMessageStore extends NullMessageStore { - private static final Logger _log = Logger.getLogger(MemoryMessageStore.class); - - private static final int DEFAULT_HASHTABLE_CAPACITY = 50000; - - private static final String HASHTABLE_CAPACITY_CONFIG = "hashtable-capacity"; - - private final AtomicLong _messageId = new AtomicLong(1); - private AtomicBoolean _closed = new AtomicBoolean(false); - private LogSubject _logSubject; + private final AtomicBoolean _closed = new AtomicBoolean(false); private static final Transaction IN_MEMORY_TRANSACTION = new Transaction() { + @Override + public StoreFuture commitTranAsync() throws AMQStoreException + { + return StoreFuture.IMMEDIATE_FUTURE; + } + + @Override public void enqueueMessage(TransactionLogResource queue, EnqueableMessage message) throws AMQStoreException { } + @Override public void dequeueMessage(TransactionLogResource queue, EnqueableMessage message) throws AMQStoreException { } + @Override public void commitTran() throws AMQStoreException { } - public StoreFuture commitTranAsync() throws AMQStoreException + @Override + public void abortTran() throws AMQStoreException { - return IMMEDIATE_FUTURE; } - public void abortTran() throws AMQStoreException + @Override + public void removeXid(long format, byte[] globalId, byte[] branchId) { } - }; - - public void configureConfigStore(String name, ConfigurationRecoveryHandler handler, Configuration configuration, LogSubject logSubject) throws Exception - { - _logSubject = logSubject; - CurrentActor.get().message(_logSubject, ConfigStoreMessages.CREATED(this.getClass().getName())); - - - } - - public void configureMessageStore(String name, - MessageStoreRecoveryHandler recoveryHandler, - Configuration config, - LogSubject logSubject) throws Exception - { - if(_logSubject == null) + @Override + public void recordXid(long format, byte[] globalId, byte[] branchId, Record[] enqueues, Record[] dequeues) { - _logSubject = logSubject; } - int hashtableCapacity = config.getInt(name + "." + HASHTABLE_CAPACITY_CONFIG, DEFAULT_HASHTABLE_CAPACITY); - _log.info("Using capacity " + hashtableCapacity + " for hash tables"); - CurrentActor.get().message(_logSubject, MessageStoreMessages.CREATED(this.getClass().getName())); - } - - public void close() throws Exception - { - _closed.getAndSet(true); - CurrentActor.get().message(_logSubject,MessageStoreMessages.CLOSED()); - - } - - public StoredMessage addMessage(StorableMessageMetaData metaData) - { - final long id = _messageId.getAndIncrement(); - StoredMemoryMessage message = new StoredMemoryMessage(id, metaData); - - return message; - } - - - public void createExchange(Exchange exchange) throws AMQStoreException - { - - } - - public void removeExchange(Exchange exchange) throws AMQStoreException - { - - } - - public void bindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQStoreException - { - - } - - public void unbindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQStoreException - { - - } - - - public void createQueue(AMQQueue queue) throws AMQStoreException - { - // Not requred to do anything - } + }; - public void createQueue(AMQQueue queue, FieldTable arguments) throws AMQStoreException - { - // Not required to do anything - } + private final StateManager _stateManager; + private final EventManager _eventManager = new EventManager(); - public void removeQueue(final AMQQueue queue) throws AMQStoreException - { - // Not required to do anything - } - - public void updateQueue(final AMQQueue queue) throws AMQStoreException + public MemoryMessageStore() { - // Not required to do anything + _stateManager = new StateManager(_eventManager); } - public void createBrokerLink(final BrokerLink link) throws AMQStoreException + @Override + public void configureConfigStore(String name, ConfigurationRecoveryHandler recoveryHandler, Configuration config) throws Exception { - + _stateManager.attainState(State.CONFIGURING); } - public void deleteBrokerLink(final BrokerLink link) throws AMQStoreException + @Override + public void configureMessageStore(String name, MessageStoreRecoveryHandler recoveryHandler, TransactionLogRecoveryHandler tlogRecoveryHandler, Configuration config) throws Exception { - + _stateManager.attainState(State.CONFIGURED); } - public void createBridge(final Bridge bridge) throws AMQStoreException + @Override + public void activate() throws Exception { - + _stateManager.attainState(State.RECOVERING); + + _stateManager.attainState(State.ACTIVE); } - public void deleteBridge(final Bridge bridge) throws AMQStoreException + @Override + public StoredMessage addMessage(StorableMessageMetaData metaData) { + final long id = _messageId.getAndIncrement(); + StoredMemoryMessage message = new StoredMemoryMessage(id, metaData); + return message; } - public void configureTransactionLog(String name, - TransactionLogRecoveryHandler recoveryHandler, - Configuration storeConfiguration, - LogSubject logSubject) throws Exception - { - //To change body of implemented methods use File | Settings | File Templates. - } - + @Override public Transaction newTransaction() { return IN_MEMORY_TRANSACTION; } - - public List<AMQQueue> createQueues() throws AMQException + @Override + public boolean isPersistent() { - return null; + return false; } - public Long getNewMessageId() + @Override + public void close() throws Exception { - return _messageId.getAndIncrement(); + _stateManager.attainState(State.CLOSING); + _closed.getAndSet(true); + _stateManager.attainState(State.CLOSED); } - public boolean isPersistent() + @Override + public void addEventListener(EventListener eventListener, Event... events) { - return false; - } - - private void checkNotClosed() throws MessageStoreClosedException - { - if (_closed.get()) - { - throw new MessageStoreClosedException(); - } + _eventManager.addEventListener(eventListener, events); } - - } diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStoreFactory.java b/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStoreFactory.java new file mode 100644 index 0000000000..8724f102c6 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStoreFactory.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.store; + +public class MemoryMessageStoreFactory implements MessageStoreFactory +{ + + @Override + public MessageStore createMessageStore() + { + return new MemoryMessageStore(); + } + + @Override + public String getStoreClassName() + { + return MemoryMessageStore.class.getSimpleName(); + } + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/MessageMetaDataType.java b/java/broker/src/main/java/org/apache/qpid/server/store/MessageMetaDataType.java index 428bb1e41b..0fab60b6f3 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/store/MessageMetaDataType.java +++ b/java/broker/src/main/java/org/apache/qpid/server/store/MessageMetaDataType.java @@ -22,13 +22,16 @@ package org.apache.qpid.server.store; import org.apache.qpid.server.message.MessageMetaData; import org.apache.qpid.server.message.MessageMetaData_0_10; +import org.apache.qpid.server.message.MessageMetaData_1_0; import java.nio.ByteBuffer; public enum MessageMetaDataType { META_DATA_0_8 { public Factory<MessageMetaData> getFactory() { return MessageMetaData.FACTORY; } }, - META_DATA_0_10 { public Factory<MessageMetaData_0_10> getFactory() { return MessageMetaData_0_10.FACTORY; } }; + META_DATA_0_10 { public Factory<MessageMetaData_0_10> getFactory() { return MessageMetaData_0_10.FACTORY; } }, + META_DATA_1_0 { public Factory<MessageMetaData_1_0> getFactory() { return MessageMetaData_1_0.FACTORY; } }; + public static interface Factory<M extends StorableMessageMetaData> diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java b/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java index 2fecd5d4be..cf08ee00ff 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java +++ b/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java @@ -22,52 +22,28 @@ package org.apache.qpid.server.store; import org.apache.commons.configuration.Configuration; -import org.apache.qpid.AMQStoreException; -import org.apache.qpid.server.logging.LogSubject; -import org.apache.qpid.server.message.EnqueableMessage; - /** * MessageStore defines the interface to a storage area, which can be used to preserve the state of messages. * */ -public interface MessageStore +public interface MessageStore extends DurableConfigurationStore { - StoreFuture IMMEDIATE_FUTURE = new StoreFuture() - { - public boolean isComplete() - { - return true; - } - - public void waitForCompletion() - { - - } - }; - - /** * Called after instantiation in order to configure the message store. A particular implementation can define * whatever parameters it wants. * - * @param name The name to be used by this storem - * @param recoveryHandler Handler to be called as the store recovers on start up + * @param name The name to be used by this store + * @param messageRecoveryHandler Handler to be called as the store recovers on start up + * @param tlogRecoveryHandler * @param config The apache commons configuration object. - * * @throws Exception If any error occurs that means the store is unable to configure itself. */ void configureMessageStore(String name, - MessageStoreRecoveryHandler recoveryHandler, - Configuration config, - LogSubject logSubject) throws Exception; - - /** - * Called to close and cleanup any resources used by the message store. - * - * @throws Exception If the close fails. - */ - void close() throws Exception; + MessageStoreRecoveryHandler messageRecoveryHandler, + TransactionLogRecoveryHandler tlogRecoveryHandler, + Configuration config) throws Exception; + void activate() throws Exception; public <T extends StorableMessageMetaData> StoredMessage<T> addMessage(T metaData); @@ -79,70 +55,16 @@ public interface MessageStore */ boolean isPersistent(); - - - public static interface Transaction - { - /** - * Places a message onto a specified queue, in a given transactional context. - * - * - * - * @param queue The queue to place the message on. - * @param message - * @throws org.apache.qpid.AMQStoreException If the operation fails for any reason. - */ - void enqueueMessage(TransactionLogResource queue, EnqueableMessage message) throws AMQStoreException; - - /** - * Extracts a message from a specified queue, in a given transactional context. - * - * @param queue The queue to place the message on. - * @param message The message to dequeue. - * @throws AMQStoreException If the operation fails for any reason, or if the specified message does not exist. - */ - void dequeueMessage(TransactionLogResource queue, EnqueableMessage message) throws AMQStoreException; - - - /** - * Commits all operations performed within a given transactional context. - * - * @throws AMQStoreException If the operation fails for any reason. - */ - void commitTran() throws AMQStoreException; - - /** - * Commits all operations performed within a given transactional context. - * - * @throws AMQStoreException If the operation fails for any reason. - */ - StoreFuture commitTranAsync() throws AMQStoreException; - - /** - * Abandons all operations performed within a given transactional context. - * - * @throws AMQStoreException If the operation fails for any reason. - */ - void abortTran() throws AMQStoreException; - - - - } - - public void configureTransactionLog(String name, - TransactionLogRecoveryHandler recoveryHandler, - Configuration storeConfiguration, - LogSubject logSubject) throws Exception; - Transaction newTransaction(); + /** + * Called to close and cleanup any resources used by the message store. + * + * @throws Exception If the close fails. + */ + void close() throws Exception; + void addEventListener(EventListener eventListener, Event... events); - public static interface StoreFuture - { - boolean isComplete(); - - void waitForCompletion(); - } - + String getStoreLocation(); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/MessageStoreConstants.java b/java/broker/src/main/java/org/apache/qpid/server/store/MessageStoreConstants.java new file mode 100644 index 0000000000..aba7456a44 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/store/MessageStoreConstants.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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; + +public class MessageStoreConstants +{ + + public static final String ENVIRONMENT_PATH_PROPERTY = "environment-path"; + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/MessageStoreFactory.java b/java/broker/src/main/java/org/apache/qpid/server/store/MessageStoreFactory.java new file mode 100644 index 0000000000..a35db62b03 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/store/MessageStoreFactory.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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; + +public interface MessageStoreFactory +{ + MessageStore createMessageStore(); + + String getStoreClassName(); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/NullMessageStore.java b/java/broker/src/main/java/org/apache/qpid/server/store/NullMessageStore.java new file mode 100644 index 0000000000..34c7d2d933 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/store/NullMessageStore.java @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.store; + +import org.apache.commons.configuration.Configuration; +import org.apache.qpid.AMQStoreException; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.binding.Binding; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.federation.Bridge; +import org.apache.qpid.server.federation.BrokerLink; +import org.apache.qpid.server.queue.AMQQueue; + +public class NullMessageStore implements MessageStore +{ + @Override + public void configureConfigStore(String name, + ConfigurationRecoveryHandler recoveryHandler, + Configuration config) throws Exception + { + } + + @Override + public void createExchange(Exchange exchange) throws AMQStoreException + { + } + + @Override + public void removeExchange(Exchange exchange) throws AMQStoreException + { + } + + @Override + public void bindQueue(Binding binding) throws AMQStoreException + { + } + + @Override + public void unbindQueue(Binding binding) throws AMQStoreException + { + } + + @Override + public void createQueue(AMQQueue queue) throws AMQStoreException + { + } + + @Override + public void createQueue(AMQQueue queue, FieldTable arguments) throws AMQStoreException + { + } + + @Override + public void removeQueue(AMQQueue queue) throws AMQStoreException + { + } + + @Override + public void updateQueue(AMQQueue queue) throws AMQStoreException + { + } + + @Override + public void createBrokerLink(final BrokerLink link) throws AMQStoreException + { + } + + @Override + public void deleteBrokerLink(final BrokerLink link) throws AMQStoreException + { + } + + @Override + public void createBridge(final Bridge bridge) throws AMQStoreException + { + } + + @Override + public void deleteBridge(final Bridge bridge) throws AMQStoreException + { + } + + @Override + public void configureMessageStore(String name, + MessageStoreRecoveryHandler recoveryHandler, + TransactionLogRecoveryHandler tlogRecoveryHandler, Configuration config) throws Exception + { + } + + @Override + public void close() throws Exception + { + } + + @Override + public <T extends StorableMessageMetaData> StoredMessage<T> addMessage(T metaData) + { + return null; + } + + @Override + public boolean isPersistent() + { + return false; + } + + @Override + public Transaction newTransaction() + { + return null; + } + + @Override + public void activate() throws Exception + { + } + + @Override + public void addEventListener(EventListener eventListener, Event... events) + { + } + + @Override + public String getStoreLocation() + { + return null; + } + +}
\ No newline at end of file diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/OperationalLoggingListener.java b/java/broker/src/main/java/org/apache/qpid/server/store/OperationalLoggingListener.java new file mode 100644 index 0000000000..caff17daa5 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/store/OperationalLoggingListener.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.store; + +import org.apache.qpid.server.logging.LogSubject; +import org.apache.qpid.server.logging.actors.CurrentActor; +import org.apache.qpid.server.logging.messages.ConfigStoreMessages; +import org.apache.qpid.server.logging.messages.MessageStoreMessages; +import org.apache.qpid.server.logging.messages.TransactionLogMessages; + +public class OperationalLoggingListener implements EventListener +{ + protected final LogSubject _logSubject; + private MessageStore _store; + + private OperationalLoggingListener(final MessageStore store, LogSubject logSubject) + { + _logSubject = logSubject; + store.addEventListener(this, Event.BEFORE_INIT, Event.AFTER_INIT, Event.BEFORE_ACTIVATE, Event.AFTER_ACTIVATE, Event.AFTER_CLOSE); + _store = store; + } + + public void event(Event event) + { + switch(event) + { + case BEFORE_INIT: + CurrentActor.get().message(_logSubject, ConfigStoreMessages.CREATED()); + break; + case AFTER_INIT: + CurrentActor.get().message(_logSubject, MessageStoreMessages.CREATED()); + CurrentActor.get().message(_logSubject, TransactionLogMessages.CREATED()); + String storeLocation = _store.getStoreLocation(); + if (storeLocation != null) + { + CurrentActor.get().message(_logSubject, MessageStoreMessages.STORE_LOCATION(storeLocation)); + } + break; + case BEFORE_ACTIVATE: + CurrentActor.get().message(_logSubject, MessageStoreMessages.RECOVERY_START()); + break; + case AFTER_ACTIVATE: + CurrentActor.get().message(_logSubject, MessageStoreMessages.RECOVERY_COMPLETE()); + break; + case AFTER_CLOSE: + CurrentActor.get().message(_logSubject,MessageStoreMessages.CLOSED()); + break; + + } + } + + public static void listen(final MessageStore store, LogSubject logSubject) + { + new OperationalLoggingListener(store, logSubject); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/State.java b/java/broker/src/main/java/org/apache/qpid/server/store/State.java new file mode 100644 index 0000000000..7cbdede85e --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/store/State.java @@ -0,0 +1,38 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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; + +public enum State +{ + + INITIAL, + CONFIGURING, + CONFIGURED, + RECOVERING, + ACTIVE, + QUIESCING, + QUIESCED, + CLOSING, + CLOSED; + + + +}
\ No newline at end of file diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/StateManager.java b/java/broker/src/main/java/org/apache/qpid/server/store/StateManager.java new file mode 100644 index 0000000000..5998be5bb6 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/store/StateManager.java @@ -0,0 +1,151 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.store; + + +import java.util.EnumMap; +import java.util.Map; + +public class StateManager +{ + private State _state = State.INITIAL; + private EventListener _eventListener; + + private static final Map<State,Map<State, Transition>> _validTransitions = new EnumMap<State, Map<State, Transition>>(State.class); + + + static class Transition + { + private final Event _event; + private final State _endState; + private final State _startState; + + public Transition(State startState, State endState, Event event) + { + _event = event; + _startState = startState; + _endState = endState; + + Map<State, Transition> stateTransitions = _validTransitions.get(startState); + if(stateTransitions == null) + { + stateTransitions = new EnumMap<State, Transition>(State.class); + _validTransitions.put(startState, stateTransitions); + } + stateTransitions.put(endState, this); + } + + public Event getEvent() + { + return _event; + } + + public State getStartState() + { + return _startState; + } + + public State getEndState() + { + return _endState; + } + + } + + public static final Transition CONFIGURE = new Transition(State.INITIAL, State.CONFIGURING, Event.BEFORE_INIT); + public static final Transition CONFIGURE_COMPLETE = new Transition(State.CONFIGURING, State.CONFIGURED, Event.AFTER_INIT); + public static final Transition RECOVER = new Transition(State.CONFIGURED, State.RECOVERING, Event.BEFORE_ACTIVATE); + public static final Transition ACTIVATE = new Transition(State.RECOVERING, State.ACTIVE, Event.AFTER_ACTIVATE); + public static final Transition CLOSE_ACTIVE = new Transition(State.ACTIVE, State.CLOSING, Event.BEFORE_CLOSE); + public static final Transition CLOSE_QUIESCED = new Transition(State.QUIESCED, State.CLOSING, Event.BEFORE_CLOSE); + public static final Transition CLOSE_COMPLETE = new Transition(State.CLOSING, State.CLOSED, Event.AFTER_CLOSE); + public static final Transition QUIESCE = new Transition(State.ACTIVE, State.QUIESCING, Event.BEFORE_PASSIVATE); + public static final Transition QUIESCE_COMPLETE = new Transition(State.QUIESCING, State.QUIESCED, Event.BEFORE_PASSIVATE); + public static final Transition RESTART = new Transition(State.QUIESCED, State.RECOVERING, Event.BEFORE_ACTIVATE); + + + public StateManager(final EventManager eventManager) + { + this(new EventListener() + { + @Override + public void event(Event event) + { + eventManager.notifyEvent(event); + } + }); + } + + + public StateManager(EventListener eventListener) + { + _eventListener = eventListener; + } + + public synchronized State getState() + { + return _state; + } + + public synchronized void stateTransition(final State current, final State desired) + { + if (_state != current) + { + throw new IllegalStateException("Cannot transition to the state: " + desired + "; need to be in state: " + current + + "; currently in state: " + _state); + } + attainState(desired); + } + + public synchronized void attainState(State desired) + { + Transition transition = null; + final Map<State, Transition> stateTransitionMap = _validTransitions.get(_state); + if(stateTransitionMap != null) + { + transition = stateTransitionMap.get(desired); + } + if(transition == null) + { + throw new IllegalStateException("No valid transition from state " + _state + " to state " + desired); + } + _state = desired; + _eventListener.event(transition.getEvent()); + } + + public synchronized boolean isInState(State testedState) + { + return _state.equals(testedState); + } + + public synchronized boolean isNotInState(State testedState) + { + return !isInState(testedState); + } + + public synchronized void checkInState(State checkedState) + { + if (isNotInState(checkedState)) + { + throw new IllegalStateException("Unexpected state. Was : " + _state + " but expected : " + checkedState); + } + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/StoreFuture.java b/java/broker/src/main/java/org/apache/qpid/server/store/StoreFuture.java new file mode 100644 index 0000000000..7d3bf90a75 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/store/StoreFuture.java @@ -0,0 +1,40 @@ +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*/ +package org.apache.qpid.server.store; + +public interface StoreFuture +{ + StoreFuture IMMEDIATE_FUTURE = new StoreFuture() + { + public boolean isComplete() + { + return true; + } + + public void waitForCompletion() + { + } + }; + + boolean isComplete(); + + void waitForCompletion(); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/StoredMemoryMessage.java b/java/broker/src/main/java/org/apache/qpid/server/store/StoredMemoryMessage.java index 144cc629bd..e7302270bb 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/store/StoredMemoryMessage.java +++ b/java/broker/src/main/java/org/apache/qpid/server/store/StoredMemoryMessage.java @@ -122,9 +122,9 @@ public class StoredMemoryMessage implements StoredMessage return buf; } - public MessageStore.StoreFuture flushToStore() + public StoreFuture flushToStore() { - return MessageStore.IMMEDIATE_FUTURE; + return StoreFuture.IMMEDIATE_FUTURE; } diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/StoredMessage.java b/java/broker/src/main/java/org/apache/qpid/server/store/StoredMessage.java index d4a0381929..7909003855 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/store/StoredMessage.java +++ b/java/broker/src/main/java/org/apache/qpid/server/store/StoredMessage.java @@ -34,7 +34,7 @@ public interface StoredMessage<M extends StorableMessageMetaData> ByteBuffer getContent(int offsetInMessage, int size); - MessageStore.StoreFuture flushToStore(); + StoreFuture flushToStore(); void remove(); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/Transaction.java b/java/broker/src/main/java/org/apache/qpid/server/store/Transaction.java new file mode 100644 index 0000000000..ed6b89e373 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/store/Transaction.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.AMQStoreException; +import org.apache.qpid.server.message.EnqueableMessage; + +public interface Transaction +{ + /** + * Places a message onto a specified queue, in a given transactional context. + * + * + * + * @param queue The queue to place the message on. + * @param message + * @throws org.apache.qpid.AMQStoreException If the operation fails for any reason. + */ + void enqueueMessage(TransactionLogResource queue, EnqueableMessage message) throws AMQStoreException; + + /** + * Extracts a message from a specified queue, in a given transactional context. + * + * @param queue The queue to place the message on. + * @param message The message to dequeue. + * @throws AMQStoreException If the operation fails for any reason, or if the specified message does not exist. + */ + void dequeueMessage(TransactionLogResource queue, EnqueableMessage message) throws AMQStoreException; + + + /** + * Commits all operations performed within a given transactional context. + * + * @throws AMQStoreException If the operation fails for any reason. + */ + void commitTran() throws AMQStoreException; + + /** + * Commits all operations performed within a given transactional context. + * + * @throws AMQStoreException If the operation fails for any reason. + */ + StoreFuture commitTranAsync() throws AMQStoreException; + + /** + * Abandons all operations performed within a given transactional context. + * + * @throws AMQStoreException If the operation fails for any reason. + */ + void abortTran() throws AMQStoreException; + + + public static interface Record + { + TransactionLogResource getQueue(); + EnqueableMessage getMessage(); + } + + void removeXid(long format, byte[] globalId, byte[] branchId) throws AMQStoreException; + + void recordXid(long format, byte[] globalId, byte[] branchId, Transaction.Record[] enqueues, Transaction.Record[] dequeues) + throws AMQStoreException; +}
\ No newline at end of file diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/TransactionLogRecoveryHandler.java b/java/broker/src/main/java/org/apache/qpid/server/store/TransactionLogRecoveryHandler.java index 802596ed1e..bd4da648f9 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/store/TransactionLogRecoveryHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/store/TransactionLogRecoveryHandler.java @@ -20,14 +20,23 @@ */ package org.apache.qpid.server.store; +import java.util.UUID; + public interface TransactionLogRecoveryHandler { QueueEntryRecoveryHandler begin(MessageStore log); public static interface QueueEntryRecoveryHandler { - void queueEntry(String queuename, long messageId); + DtxRecordRecoveryHandler completeQueueEntryRecovery(); + + void queueEntry(UUID queueId, long messageId); + } + + public static interface DtxRecordRecoveryHandler + { + void dtxRecord(long format, byte[] globalId, byte[] branchId, Transaction.Record[] enqueues, Transaction.Record[] dequeues); - void completeQueueEntryRecovery(); + void completeDtxRecordRecovery(); } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/TransactionLogResource.java b/java/broker/src/main/java/org/apache/qpid/server/store/TransactionLogResource.java index 0d81dd151d..576dca847d 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/store/TransactionLogResource.java +++ b/java/broker/src/main/java/org/apache/qpid/server/store/TransactionLogResource.java @@ -20,7 +20,9 @@ */ package org.apache.qpid.server.store; +import java.util.UUID; + public interface TransactionLogResource { - public String getResourceName(); + public UUID getId(); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java b/java/broker/src/main/java/org/apache/qpid/server/store/derby/DerbyMessageStore.java index 2877e25645..0371cdcfcb 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java +++ b/java/broker/src/main/java/org/apache/qpid/server/store/derby/DerbyMessageStore.java @@ -18,25 +18,8 @@ * under the License. * */ -package org.apache.qpid.server.store; +package org.apache.qpid.server.store.derby; -import org.apache.commons.configuration.Configuration; -import org.apache.log4j.Logger; - -import org.apache.qpid.AMQException; -import org.apache.qpid.AMQStoreException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.server.federation.Bridge; -import org.apache.qpid.server.federation.BrokerLink; -import org.apache.qpid.server.logging.LogSubject; -import org.apache.qpid.server.logging.actors.CurrentActor; -import org.apache.qpid.server.logging.messages.ConfigStoreMessages; -import org.apache.qpid.server.logging.messages.MessageStoreMessages; -import org.apache.qpid.server.logging.messages.TransactionLogMessages; -import org.apache.qpid.server.message.EnqueableMessage; -import org.apache.qpid.server.queue.AMQQueue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -46,6 +29,7 @@ import java.io.File; import java.io.IOException; import java.lang.ref.SoftReference; import java.nio.ByteBuffer; +import java.nio.charset.Charset; import java.sql.Blob; import java.sql.Connection; import java.sql.Driver; @@ -62,38 +46,67 @@ import java.util.Map; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; +import org.apache.commons.configuration.Configuration; +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQStoreException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.binding.Binding; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.federation.Bridge; +import org.apache.qpid.server.federation.BrokerLink; +import org.apache.qpid.server.message.EnqueableMessage; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.store.ConfigurationRecoveryHandler; +import org.apache.qpid.server.store.ConfiguredObjectHelper; +import org.apache.qpid.server.store.ConfiguredObjectRecord; +import org.apache.qpid.server.store.Event; +import org.apache.qpid.server.store.EventListener; +import org.apache.qpid.server.store.EventManager; +import org.apache.qpid.server.store.MessageMetaDataType; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.MessageStoreConstants; +import org.apache.qpid.server.store.MessageStoreRecoveryHandler; +import org.apache.qpid.server.store.State; +import org.apache.qpid.server.store.StateManager; +import org.apache.qpid.server.store.StorableMessageMetaData; +import org.apache.qpid.server.store.StoreFuture; +import org.apache.qpid.server.store.StoredMemoryMessage; +import org.apache.qpid.server.store.StoredMessage; +import org.apache.qpid.server.store.Transaction; +import org.apache.qpid.server.store.TransactionLogRecoveryHandler; +import org.apache.qpid.server.store.TransactionLogResource; /** - * An implementation of a {@link MessageStore} that uses Apache Derby as the persistance + * An implementation of a {@link MessageStore} that uses Apache Derby as the persistence * mechanism. - * + * * TODO extract the SQL statements into a generic JDBC store */ -public class DerbyMessageStore implements MessageStore, DurableConfigurationStore +public class DerbyMessageStore implements MessageStore { private static final Logger _logger = Logger.getLogger(DerbyMessageStore.class); - public static final String ENVIRONMENT_PATH_PROPERTY = "environment-path"; - - private static final String SQL_DRIVER_NAME = "org.apache.derby.jdbc.EmbeddedDriver"; private static final String DB_VERSION_TABLE_NAME = "QPID_DB_VERSION"; - private static final String EXCHANGE_TABLE_NAME = "QPID_EXCHANGE"; - private static final String QUEUE_TABLE_NAME = "QPID_QUEUE"; - private static final String BINDINGS_TABLE_NAME = "QPID_BINDINGS"; - private static final String QUEUE_ENTRY_TABLE_NAME = "QPID_QUEUE_ENTRY"; + private static final String QUEUE_ENTRY_TABLE_NAME = "QPID_QUEUE_ENTRIES"; - private static final String META_DATA_TABLE_NAME = "QPID_META_DATA"; + private static final String META_DATA_TABLE_NAME = "QPID_MESSAGE_METADATA"; private static final String MESSAGE_CONTENT_TABLE_NAME = "QPID_MESSAGE_CONTENT"; private static final String LINKS_TABLE_NAME = "QPID_LINKS"; private static final String BRIDGES_TABLE_NAME = "QPID_BRIDGES"; + private static final String XID_TABLE_NAME = "QPID_XIDS"; + private static final String XID_ACTIONS_TABLE_NAME = "QPID_XID_ACTIONS"; + + private static final String CONFIGURED_OBJECTS_TABLE_NAME = "QPID_CONFIGURED_OBJECTS"; - private static final int DB_VERSION = 3; + private static final int DB_VERSION = 6; @@ -109,38 +122,23 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor private static final String CREATE_DB_VERSION_TABLE = "CREATE TABLE "+DB_VERSION_TABLE_NAME+" ( version int not null )"; private static final String INSERT_INTO_DB_VERSION = "INSERT INTO "+DB_VERSION_TABLE_NAME+" ( version ) VALUES ( ? )"; - private static final String CREATE_EXCHANGE_TABLE = "CREATE TABLE "+EXCHANGE_TABLE_NAME+" ( name varchar(255) not null, type varchar(255) not null, autodelete SMALLINT not null, PRIMARY KEY ( name ) )"; - private static final String CREATE_QUEUE_TABLE = "CREATE TABLE "+QUEUE_TABLE_NAME+" ( name varchar(255) not null, owner varchar(255), exclusive SMALLINT not null, arguments blob, PRIMARY KEY ( name ))"; - private static final String CREATE_BINDINGS_TABLE = "CREATE TABLE "+BINDINGS_TABLE_NAME+" ( exchange_name varchar(255) not null, queue_name varchar(255) not null, binding_key varchar(255) not null, arguments blob , PRIMARY KEY ( exchange_name, queue_name, binding_key ) )"; - private static final String SELECT_FROM_QUEUE = "SELECT name, owner, exclusive, arguments FROM " + QUEUE_TABLE_NAME; - private static final String FIND_QUEUE = "SELECT name, owner FROM " + QUEUE_TABLE_NAME + " WHERE name = ?"; - private static final String UPDATE_QUEUE_EXCLUSIVITY = "UPDATE " + QUEUE_TABLE_NAME + " SET exclusive = ? WHERE name = ?"; - private static final String SELECT_FROM_EXCHANGE = "SELECT name, type, autodelete FROM " + EXCHANGE_TABLE_NAME; - private static final String SELECT_FROM_BINDINGS = - "SELECT exchange_name, queue_name, binding_key, arguments FROM " + BINDINGS_TABLE_NAME + " ORDER BY exchange_name"; - private static final String FIND_BINDING = - "SELECT * FROM " + BINDINGS_TABLE_NAME + " WHERE exchange_name = ? AND queue_name = ? AND binding_key = ? "; - private static final String INSERT_INTO_EXCHANGE = "INSERT INTO " + EXCHANGE_TABLE_NAME + " ( name, type, autodelete ) VALUES ( ?, ?, ? )"; - private static final String DELETE_FROM_EXCHANGE = "DELETE FROM " + EXCHANGE_TABLE_NAME + " WHERE name = ?"; - private static final String FIND_EXCHANGE = "SELECT name FROM " + EXCHANGE_TABLE_NAME + " WHERE name = ?"; - private static final String INSERT_INTO_BINDINGS = "INSERT INTO " + BINDINGS_TABLE_NAME + " ( exchange_name, queue_name, binding_key, arguments ) values ( ?, ?, ?, ? )"; - private static final String DELETE_FROM_BINDINGS = "DELETE FROM " + BINDINGS_TABLE_NAME + " WHERE exchange_name = ? AND queue_name = ? AND binding_key = ?"; - private static final String INSERT_INTO_QUEUE = "INSERT INTO " + QUEUE_TABLE_NAME + " (name, owner, exclusive, arguments) VALUES (?, ?, ?, ?)"; - private static final String DELETE_FROM_QUEUE = "DELETE FROM " + QUEUE_TABLE_NAME + " WHERE name = ?"; - - private static final String CREATE_QUEUE_ENTRY_TABLE = "CREATE TABLE "+QUEUE_ENTRY_TABLE_NAME+" ( queue_name varchar(255) not null, message_id bigint not null, PRIMARY KEY (queue_name, message_id) )"; - private static final String INSERT_INTO_QUEUE_ENTRY = "INSERT INTO " + QUEUE_ENTRY_TABLE_NAME + " (queue_name, message_id) values (?,?)"; - private static final String DELETE_FROM_QUEUE_ENTRY = "DELETE FROM " + QUEUE_ENTRY_TABLE_NAME + " WHERE queue_name = ? AND message_id =?"; - private static final String SELECT_FROM_QUEUE_ENTRY = "SELECT queue_name, message_id FROM " + QUEUE_ENTRY_TABLE_NAME + " ORDER BY queue_name, message_id"; - - - private static final String CREATE_META_DATA_TABLE = "CREATE TABLE "+META_DATA_TABLE_NAME+" ( message_id bigint not null, meta_data blob, PRIMARY KEY ( message_id ) )"; - private static final String CREATE_MESSAGE_CONTENT_TABLE = "CREATE TABLE "+MESSAGE_CONTENT_TABLE_NAME+" ( message_id bigint not null, offset int not null, last_byte int not null, content blob , PRIMARY KEY (message_id, offset) )"; - - private static final String INSERT_INTO_MESSAGE_CONTENT = "INSERT INTO " + MESSAGE_CONTENT_TABLE_NAME + "( message_id, offset, last_byte, content ) values (?, ?, ?, ?)"; - private static final String SELECT_FROM_MESSAGE_CONTENT = - "SELECT offset, content FROM " + MESSAGE_CONTENT_TABLE_NAME + " WHERE message_id = ? AND last_byte > ? AND offset < ? ORDER BY message_id, offset"; - private static final String DELETE_FROM_MESSAGE_CONTENT = "DELETE FROM " + MESSAGE_CONTENT_TABLE_NAME + " WHERE message_id = ?"; + private static final String CREATE_QUEUE_ENTRY_TABLE = "CREATE TABLE "+QUEUE_ENTRY_TABLE_NAME+" ( queue_id varchar(36) not null, message_id bigint not null, PRIMARY KEY (queue_id, message_id) )"; + private static final String INSERT_INTO_QUEUE_ENTRY = "INSERT INTO " + QUEUE_ENTRY_TABLE_NAME + " (queue_id, message_id) values (?,?)"; + private static final String DELETE_FROM_QUEUE_ENTRY = "DELETE FROM " + QUEUE_ENTRY_TABLE_NAME + " WHERE queue_id = ? AND message_id =?"; + private static final String SELECT_FROM_QUEUE_ENTRY = "SELECT queue_id, message_id FROM " + QUEUE_ENTRY_TABLE_NAME + " ORDER BY queue_id, message_id"; + + + private static final String CREATE_META_DATA_TABLE = "CREATE TABLE " + META_DATA_TABLE_NAME + + " ( message_id bigint not null, meta_data blob, PRIMARY KEY ( message_id ) )"; + private static final String CREATE_MESSAGE_CONTENT_TABLE = "CREATE TABLE " + MESSAGE_CONTENT_TABLE_NAME + + " ( message_id bigint not null, content blob , PRIMARY KEY (message_id) )"; + + private static final String INSERT_INTO_MESSAGE_CONTENT = "INSERT INTO " + MESSAGE_CONTENT_TABLE_NAME + + "( message_id, content ) values (?, ?)"; + private static final String SELECT_FROM_MESSAGE_CONTENT = "SELECT content FROM " + MESSAGE_CONTENT_TABLE_NAME + + " WHERE message_id = ?"; + private static final String DELETE_FROM_MESSAGE_CONTENT = "DELETE FROM " + MESSAGE_CONTENT_TABLE_NAME + + " WHERE message_id = ?"; private static final String INSERT_INTO_META_DATA = "INSERT INTO " + META_DATA_TABLE_NAME + "( message_id , meta_data ) values (?, ?)";; private static final String SELECT_FROM_META_DATA = @@ -155,7 +153,7 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor + " arguments blob, PRIMARY KEY ( id_lsb, id_msb ))"; private static final String SELECT_FROM_LINKS = "SELECT create_time, arguments FROM " + LINKS_TABLE_NAME + " WHERE id_lsb = ? and id_msb"; - private static final String DELETE_FROM_LINKS = "DELETE FROM " + LINKS_TABLE_NAME + private static final String DELETE_FROM_LINKS = "DELETE FROM " + LINKS_TABLE_NAME + " WHERE id_lsb = ? and id_msb = ?"; private static final String SELECT_ALL_FROM_LINKS = "SELECT id_lsb, id_msb, create_time, " + "arguments FROM " + LINKS_TABLE_NAME; @@ -173,12 +171,12 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor + " link_id_msb bigint not null," + " arguments blob, PRIMARY KEY ( id_lsb, id_msb ))"; private static final String SELECT_FROM_BRIDGES = - "SELECT create_time, link_id_lsb, link_id_msb, arguments FROM " + "SELECT create_time, link_id_lsb, link_id_msb, arguments FROM " + BRIDGES_TABLE_NAME + " WHERE id_lsb = ? and id_msb = ?"; - private static final String DELETE_FROM_BRIDGES = "DELETE FROM " + BRIDGES_TABLE_NAME + private static final String DELETE_FROM_BRIDGES = "DELETE FROM " + BRIDGES_TABLE_NAME + " WHERE id_lsb = ? and id_msb = ?"; - private static final String SELECT_ALL_FROM_BRIDGES = "SELECT id_lsb, id_msb, " - + " create_time," + private static final String SELECT_ALL_FROM_BRIDGES = "SELECT id_lsb, id_msb, " + + " create_time," + " link_id_lsb, link_id_msb, " + "arguments FROM " + BRIDGES_TABLE_NAME + " WHERE link_id_lsb = ? and link_id_msb = ?"; @@ -190,125 +188,112 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor + "arguments )" + " values (?, ?, ?, ?, ?, ?)"; + private static final String CREATE_XIDS_TABLE = + "CREATE TABLE "+XID_TABLE_NAME+" ( format bigint not null," + + " global_id varchar(64) for bit data, branch_id varchar(64) for bit data, PRIMARY KEY ( format, " + + "global_id, branch_id ))"; + private static final String INSERT_INTO_XIDS = + "INSERT INTO "+XID_TABLE_NAME+" ( format, global_id, branch_id ) values (?, ?, ?)"; + private static final String DELETE_FROM_XIDS = "DELETE FROM " + XID_TABLE_NAME + + " WHERE format = ? and global_id = ? and branch_id = ?"; + private static final String SELECT_ALL_FROM_XIDS = "SELECT format, global_id, branch_id FROM " + XID_TABLE_NAME; + + + private static final String CREATE_XID_ACTIONS_TABLE = + "CREATE TABLE "+XID_ACTIONS_TABLE_NAME+" ( format bigint not null," + + " global_id varchar(64) for bit data not null, branch_id varchar(64) for bit data not null, " + + "action_type char not null, queue_id varchar(36) not null, message_id bigint not null" + + ", PRIMARY KEY ( " + + "format, global_id, branch_id, action_type, queue_id, message_id))"; + private static final String INSERT_INTO_XID_ACTIONS = + "INSERT INTO "+XID_ACTIONS_TABLE_NAME+" ( format, global_id, branch_id, action_type, " + + "queue_id, message_id ) values (?,?,?,?,?,?) "; + private static final String DELETE_FROM_XID_ACTIONS = "DELETE FROM " + XID_ACTIONS_TABLE_NAME + + " WHERE format = ? and global_id = ? and branch_id = ?"; + private static final String SELECT_ALL_FROM_XID_ACTIONS = + "SELECT action_type, queue_id, message_id FROM " + XID_ACTIONS_TABLE_NAME + + " WHERE format = ? and global_id = ? and branch_id = ?"; + + private static final String CREATE_CONFIGURED_OBJECTS_TABLE = "CREATE TABLE " + CONFIGURED_OBJECTS_TABLE_NAME + + " ( id VARCHAR(36) not null, object_type varchar(255), attributes blob, PRIMARY KEY (id))"; + private static final String INSERT_INTO_CONFIGURED_OBJECTS = "INSERT INTO " + CONFIGURED_OBJECTS_TABLE_NAME + + " ( id, object_type, attributes) VALUES (?,?,?)"; + private static final String UPDATE_CONFIGURED_OBJECTS = "UPDATE " + CONFIGURED_OBJECTS_TABLE_NAME + + " set object_type =?, attributes = ? where id = ?"; + private static final String DELETE_FROM_CONFIGURED_OBJECTS = "DELETE FROM " + CONFIGURED_OBJECTS_TABLE_NAME + + " where id = ?"; + private static final String FIND_CONFIGURED_OBJECT = "SELECT object_type, attributes FROM " + CONFIGURED_OBJECTS_TABLE_NAME + + " where id = ?"; + private static final String SELECT_FROM_CONFIGURED_OBJECTS = "SELECT id, object_type, attributes FROM " + CONFIGURED_OBJECTS_TABLE_NAME; + + private final Charset UTF8_CHARSET = Charset.forName("UTF-8"); private static final String DERBY_SINGLE_DB_SHUTDOWN_CODE = "08006"; + private final StateManager _stateManager; + + private final EventManager _eventManager = new EventManager(); - private LogSubject _logSubject; - private boolean _configured; - + private MessageStoreRecoveryHandler _messageRecoveryHandler; - private static final class CommitStoreFuture implements StoreFuture - { - public boolean isComplete() - { - return true; - } + private TransactionLogRecoveryHandler _tlogRecoveryHandler; - public void waitForCompletion() - { + private ConfigurationRecoveryHandler _configRecoveryHandler; + private String _storeLocation; - } - } - - private enum State + public DerbyMessageStore() { - INITIAL, - CONFIGURING, - RECOVERING, - STARTED, - CLOSING, - CLOSED + _stateManager = new StateManager(_eventManager); } - private State _state = State.INITIAL; - + private ConfiguredObjectHelper _configuredObjectHelper = new ConfiguredObjectHelper(); + @Override public void configureConfigStore(String name, - ConfigurationRecoveryHandler recoveryHandler, - Configuration storeConfiguration, - LogSubject logSubject) throws Exception + ConfigurationRecoveryHandler configRecoveryHandler, + Configuration storeConfiguration) throws Exception { - stateTransition(State.INITIAL, State.CONFIGURING); - _logSubject = logSubject; - CurrentActor.get().message(_logSubject, ConfigStoreMessages.CREATED(this.getClass().getName())); + _stateManager.attainState(State.CONFIGURING); + _configRecoveryHandler = configRecoveryHandler; - if(!_configured) - { - commonConfiguration(name, storeConfiguration, logSubject); - _configured = true; - } - - // this recovers durable exchanges, queues, and bindings - recover(recoveryHandler); - - - stateTransition(State.RECOVERING, State.STARTED); + commonConfiguration(name, storeConfiguration); } - + @Override public void configureMessageStore(String name, MessageStoreRecoveryHandler recoveryHandler, - Configuration storeConfiguration, - LogSubject logSubject) throws Exception + TransactionLogRecoveryHandler tlogRecoveryHandler, + Configuration storeConfiguration) throws Exception { - if(!_configured) - { - - _logSubject = logSubject; - } - - CurrentActor.get().message(_logSubject, MessageStoreMessages.CREATED(this.getClass().getName())); - - if(!_configured) - { - - commonConfiguration(name, storeConfiguration, logSubject); - _configured = true; - } - - recoverMessages(recoveryHandler); + _tlogRecoveryHandler = tlogRecoveryHandler; + _messageRecoveryHandler = recoveryHandler; + _stateManager.attainState(State.CONFIGURED); } - - - public void configureTransactionLog(String name, - TransactionLogRecoveryHandler recoveryHandler, - Configuration storeConfiguration, - LogSubject logSubject) throws Exception + @Override + public void activate() throws Exception { + _stateManager.attainState(State.RECOVERING); - if(!_configured) - { - _logSubject = logSubject; - } - CurrentActor.get().message(_logSubject, TransactionLogMessages.CREATED(this.getClass().getName())); - - if(!_configured) - { - - _logSubject = logSubject; - - commonConfiguration(name, storeConfiguration, logSubject); - _configured = true; - } - - recoverQueueEntries(recoveryHandler); + // this recovers durable exchanges, queues, and bindings + recoverConfiguration(_configRecoveryHandler); + recoverMessages(_messageRecoveryHandler); + TransactionLogRecoveryHandler.DtxRecordRecoveryHandler dtxrh = recoverQueueEntries(_tlogRecoveryHandler); + recoverXids(dtxrh); + _stateManager.attainState(State.ACTIVE); } - - - private void commonConfiguration(String name, Configuration storeConfiguration, LogSubject logSubject) + private void commonConfiguration(String name, Configuration storeConfiguration) throws ClassNotFoundException, SQLException { initialiseDriver(); //Update to pick up QPID_WORK and use that as the default location not just derbyDB - final String databasePath = storeConfiguration.getString(ENVIRONMENT_PATH_PROPERTY, System.getProperty("QPID_WORK") + final String databasePath = storeConfiguration.getString(MessageStoreConstants.ENVIRONMENT_PATH_PROPERTY, System.getProperty("QPID_WORK") + File.separator + "derbyDB"); File environmentPath = new File(databasePath); @@ -321,7 +306,7 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor } } - CurrentActor.get().message(_logSubject, MessageStoreMessages.STORE_LOCATION(environmentPath.getAbsolutePath())); + _storeLocation = databasePath; createOrOpenDatabase(name, databasePath); } @@ -342,15 +327,14 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor Connection conn = newAutoCommitConnection(); createVersionTable(conn); - createExchangeTable(conn); - createQueueTable(conn); - createBindingsTable(conn); + createConfiguredObjectsTable(conn); createQueueEntryTable(conn); createMetaDataTable(conn); createMessageContentTable(conn); createLinkTable(conn); createBridgeTable(conn); - + createXidTable(conn); + createXidActionTable(conn); conn.close(); } @@ -384,15 +368,14 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor } - - private void createExchangeTable(final Connection conn) throws SQLException + private void createConfiguredObjectsTable(final Connection conn) throws SQLException { - if(!tableExists(EXCHANGE_TABLE_NAME, conn)) + if(!tableExists(CONFIGURED_OBJECTS_TABLE_NAME, conn)) { Statement stmt = conn.createStatement(); try { - stmt.execute(CREATE_EXCHANGE_TABLE); + stmt.execute(CREATE_CONFIGURED_OBJECTS_TABLE); } finally { @@ -401,30 +384,31 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor } } - private void createQueueTable(final Connection conn) throws SQLException + private void createQueueEntryTable(final Connection conn) throws SQLException { - if(!tableExists(QUEUE_TABLE_NAME, conn)) + if(!tableExists(QUEUE_ENTRY_TABLE_NAME, conn)) { Statement stmt = conn.createStatement(); try { - stmt.execute(CREATE_QUEUE_TABLE); + stmt.execute(CREATE_QUEUE_ENTRY_TABLE); } finally { stmt.close(); } } + } - private void createBindingsTable(final Connection conn) throws SQLException + private void createMetaDataTable(final Connection conn) throws SQLException { - if(!tableExists(BINDINGS_TABLE_NAME, conn)) + if(!tableExists(META_DATA_TABLE_NAME, conn)) { Statement stmt = conn.createStatement(); try { - stmt.execute(CREATE_BINDINGS_TABLE); + stmt.execute(CREATE_META_DATA_TABLE); } finally { @@ -434,14 +418,15 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor } - private void createQueueEntryTable(final Connection conn) throws SQLException + + private void createMessageContentTable(final Connection conn) throws SQLException { - if(!tableExists(QUEUE_ENTRY_TABLE_NAME, conn)) + if(!tableExists(MESSAGE_CONTENT_TABLE_NAME, conn)) { Statement stmt = conn.createStatement(); try { - stmt.execute(CREATE_QUEUE_ENTRY_TABLE); + stmt.execute(CREATE_MESSAGE_CONTENT_TABLE); } finally { @@ -451,49 +436,47 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor } - private void createMetaDataTable(final Connection conn) throws SQLException + private void createLinkTable(final Connection conn) throws SQLException { - if(!tableExists(META_DATA_TABLE_NAME, conn)) + if(!tableExists(LINKS_TABLE_NAME, conn)) { Statement stmt = conn.createStatement(); try { - stmt.execute(CREATE_META_DATA_TABLE); + stmt.execute(CREATE_LINKS_TABLE); } finally { stmt.close(); } } - } - private void createMessageContentTable(final Connection conn) throws SQLException + private void createBridgeTable(final Connection conn) throws SQLException { - if(!tableExists(MESSAGE_CONTENT_TABLE_NAME, conn)) + if(!tableExists(BRIDGES_TABLE_NAME, conn)) { Statement stmt = conn.createStatement(); try { - stmt.execute(CREATE_MESSAGE_CONTENT_TABLE); + stmt.execute(CREATE_BRIDGES_TABLE); } finally { stmt.close(); } } - } - private void createLinkTable(final Connection conn) throws SQLException + private void createXidTable(final Connection conn) throws SQLException { - if(!tableExists(LINKS_TABLE_NAME, conn)) + if(!tableExists(XID_TABLE_NAME, conn)) { Statement stmt = conn.createStatement(); try { - stmt.execute(CREATE_LINKS_TABLE); + stmt.execute(CREATE_XIDS_TABLE); } finally { @@ -503,14 +486,14 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor } - private void createBridgeTable(final Connection conn) throws SQLException + private void createXidActionTable(final Connection conn) throws SQLException { - if(!tableExists(BRIDGES_TABLE_NAME, conn)) + if(!tableExists(XID_ACTIONS_TABLE_NAME, conn)) { Statement stmt = conn.createStatement(); try { - stmt.execute(CREATE_BRIDGES_TABLE); + stmt.execute(CREATE_XID_ACTIONS_TABLE); } finally { @@ -519,9 +502,6 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor } } - - - private boolean tableExists(final String tableName, final Connection conn) throws SQLException { PreparedStatement stmt = conn.prepareStatement(TABLE_EXISTANCE_QUERY); @@ -542,34 +522,29 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor { stmt.close(); } - } - public void recover(ConfigurationRecoveryHandler recoveryHandler) throws AMQException + private void recoverConfiguration(ConfigurationRecoveryHandler recoveryHandler) throws AMQException { - stateTransition(State.CONFIGURING, State.RECOVERING); - - CurrentActor.get().message(_logSubject,MessageStoreMessages.RECOVERY_START()); - try { + List<ConfiguredObjectRecord> configuredObjects = loadConfiguredObjects(); ConfigurationRecoveryHandler.QueueRecoveryHandler qrh = recoveryHandler.begin(this); - loadQueues(qrh); + _configuredObjectHelper.recoverQueues(qrh, configuredObjects); ConfigurationRecoveryHandler.ExchangeRecoveryHandler erh = qrh.completeQueueRecovery(); - List<String> exchanges = loadExchanges(erh); + _configuredObjectHelper.recoverExchanges(erh, configuredObjects); + ConfigurationRecoveryHandler.BindingRecoveryHandler brh = erh.completeExchangeRecovery(); - recoverBindings(brh, exchanges); + _configuredObjectHelper.recoverBindings(brh, configuredObjects); + ConfigurationRecoveryHandler.BrokerLinkRecoveryHandler lrh = brh.completeBindingRecovery(); recoverBrokerLinks(lrh); } catch (SQLException e) { - throw new AMQStoreException("Error recovering persistent state: " + e.getMessage(), e); } - - } private void recoverBrokerLinks(final ConfigurationRecoveryHandler.BrokerLinkRecoveryHandler lrh) @@ -598,12 +573,12 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor Blob argumentsAsBlob = rs.getBlob(4); byte[] dataAsBytes = argumentsAsBlob.getBytes(1,(int) argumentsAsBlob.length()); - + DataInputStream dis = new DataInputStream(new ByteArrayInputStream(dataAsBytes)); int size = dis.readInt(); - + Map<String,String> arguments = new HashMap<String, String>(); - + for(int i = 0; i < size; i++) { arguments.put(dis.readUTF(), dis.readUTF()); @@ -710,179 +685,11 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor } - private void loadQueues(ConfigurationRecoveryHandler.QueueRecoveryHandler qrh) throws SQLException - { - Connection conn = newAutoCommitConnection(); - try - { - Statement stmt = conn.createStatement(); - try - { - ResultSet rs = stmt.executeQuery(SELECT_FROM_QUEUE); - try - { - - while(rs.next()) - { - String queueName = rs.getString(1); - String owner = rs.getString(2); - boolean exclusive = rs.getBoolean(3); - Blob argumentsAsBlob = rs.getBlob(4); - - byte[] dataAsBytes = argumentsAsBlob.getBytes(1,(int) argumentsAsBlob.length()); - FieldTable arguments; - if(dataAsBytes.length > 0) - { - - try - { - arguments = new FieldTable(new DataInputStream(new ByteArrayInputStream(dataAsBytes)),dataAsBytes.length); - } - catch (IOException e) - { - throw new RuntimeException("IO Exception should not be thrown",e); - } - } - else - { - arguments = null; - } - - qrh.queue(queueName, owner, exclusive, arguments); - - } - - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - } - finally - { - conn.close(); - } - } - - - private List<String> loadExchanges(ConfigurationRecoveryHandler.ExchangeRecoveryHandler erh) throws SQLException - { - - List<String> exchanges = new ArrayList<String>(); - Connection conn = null; - try - { - conn = newAutoCommitConnection(); - - Statement stmt = conn.createStatement(); - try - { - ResultSet rs = stmt.executeQuery(SELECT_FROM_EXCHANGE); - try - { - while(rs.next()) - { - String exchangeName = rs.getString(1); - String type = rs.getString(2); - boolean autoDelete = rs.getShort(3) != 0; - - exchanges.add(exchangeName); - - erh.exchange(exchangeName, type, autoDelete); - - } - return exchanges; - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - } - finally - { - if(conn != null) - { - conn.close(); - } - } - - } - - private void recoverBindings(ConfigurationRecoveryHandler.BindingRecoveryHandler brh, List<String> exchanges) throws SQLException - { - _logger.info("Recovering bindings..."); - - Connection conn = null; - try - { - conn = newAutoCommitConnection(); - - PreparedStatement stmt = conn.prepareStatement(SELECT_FROM_BINDINGS); - - try - { - ResultSet rs = stmt.executeQuery(); - - try - { - - while(rs.next()) - { - String exchangeName = rs.getString(1); - String queueName = rs.getString(2); - String bindingKey = rs.getString(3); - Blob arguments = rs.getBlob(4); - java.nio.ByteBuffer buf; - - if(arguments != null && arguments.length() != 0) - { - byte[] argumentBytes = arguments.getBytes(1, (int) arguments.length()); - buf = java.nio.ByteBuffer.wrap(argumentBytes); - } - else - { - buf = null; - } - - brh.binding(exchangeName, queueName, bindingKey, buf); - } - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - - } - finally - { - if(conn != null) - { - conn.close(); - } - } - } - - - + @Override public void close() throws Exception { - CurrentActor.get().message(_logSubject,MessageStoreMessages.CLOSED()); _closed.getAndSet(true); + _stateManager.stateTransition(State.ACTIVE, State.CLOSING); try { @@ -892,9 +699,9 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor _logger.error("Unable to shut down the store"); } catch (SQLException e) - { - if (e.getSQLState().equalsIgnoreCase(DERBY_SINGLE_DB_SHUTDOWN_CODE)) - { + { + if (e.getSQLState().equalsIgnoreCase(DERBY_SINGLE_DB_SHUTDOWN_CODE)) + { //expected and represents a clean shutdown of this database only, do nothing. } else @@ -902,8 +709,11 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor _logger.error("Exception whilst shutting down the store: " + e); } } + + _stateManager.stateTransition(State.CLOSING, State.CLOSED); } + @Override public StoredMessage addMessage(StorableMessageMetaData metaData) { if(metaData.isPersistent()) @@ -981,291 +791,67 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor } + @Override public void createExchange(Exchange exchange) throws AMQStoreException { - if (_state != State.RECOVERING) + if (_stateManager.isInState(State.ACTIVE)) { - try - { - Connection conn = newAutoCommitConnection(); - - try - { - - - PreparedStatement stmt = conn.prepareStatement(FIND_EXCHANGE); - try - { - stmt.setString(1, exchange.getNameShortString().toString()); - ResultSet rs = stmt.executeQuery(); - try - { - - // If we don't have any data in the result set then we can add this exchange - if (!rs.next()) - { - - PreparedStatement insertStmt = conn.prepareStatement(INSERT_INTO_EXCHANGE); - try - { - insertStmt.setString(1, exchange.getName().toString()); - insertStmt.setString(2, exchange.getTypeShortString().asString()); - insertStmt.setShort(3, exchange.isAutoDelete() ? (short) 1 : (short) 0); - insertStmt.execute(); - } - finally - { - insertStmt.close(); - } - } - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - - } - finally - { - conn.close(); - } - } - catch (SQLException e) - { - throw new AMQStoreException("Error writing Exchange with name " + exchange.getNameShortString() + " to database: " + e.getMessage(), e); - } + ConfiguredObjectRecord configuredObject = _configuredObjectHelper.createExchangeConfiguredObject(exchange); + insertConfiguredObject(configuredObject); } } + @Override public void removeExchange(Exchange exchange) throws AMQStoreException { - - try + int results = removeConfiguredObject(exchange.getId()); + if (results == 0) { - Connection conn = newAutoCommitConnection(); - try - { - PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_EXCHANGE); - try - { - stmt.setString(1, exchange.getNameShortString().toString()); - int results = stmt.executeUpdate(); - stmt.close(); - if(results == 0) - { - throw new AMQStoreException("Exchange " + exchange.getNameShortString() + " not found"); - } - } - finally - { - stmt.close(); - } - } - finally - { - conn.close(); - } - } - catch (SQLException e) - { - throw new AMQStoreException("Error deleting Exchange with name " + exchange.getNameShortString() + " from database: " + e.getMessage(), e); + throw new AMQStoreException("Exchange " + exchange.getName() + " with id " + exchange.getId() + " not found"); } } - public void bindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) + @Override + public void bindQueue(Binding binding) throws AMQStoreException { - if (_state != State.RECOVERING) + if (_stateManager.isInState(State.ACTIVE)) { - try - { - Connection conn = newAutoCommitConnection(); - - try - { - - PreparedStatement stmt = conn.prepareStatement(FIND_BINDING); - try - { - stmt.setString(1, exchange.getNameShortString().toString() ); - stmt.setString(2, queue.getNameShortString().toString()); - stmt.setString(3, routingKey == null ? null : routingKey.toString()); - - ResultSet rs = stmt.executeQuery(); - try - { - // If this binding is not already in the store then create it. - if (!rs.next()) - { - PreparedStatement insertStmt = conn.prepareStatement(INSERT_INTO_BINDINGS); - try - { - insertStmt.setString(1, exchange.getNameShortString().toString() ); - insertStmt.setString(2, queue.getNameShortString().toString()); - insertStmt.setString(3, routingKey == null ? null : routingKey.toString()); - if(args != null) - { - // TODO - In Java 6 we could use create/set Blob - byte[] bytes = args.getDataAsBytes(); - ByteArrayInputStream bis = new ByteArrayInputStream(bytes); - insertStmt.setBinaryStream(4, bis, bytes.length); - } - else - { - insertStmt.setNull(4, Types.BLOB); - } - - insertStmt.executeUpdate(); - } - finally - { - insertStmt.close(); - } - } - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - } - finally - { - conn.close(); - } - } - catch (SQLException e) - { - throw new AMQStoreException("Error writing binding for AMQQueue with name " + queue.getNameShortString() + " to exchange " - + exchange.getNameShortString() + " to database: " + e.getMessage(), e); - } - + ConfiguredObjectRecord configuredObject = _configuredObjectHelper.createBindingConfiguredObject(binding); + insertConfiguredObject(configuredObject); } - - } - public void unbindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) + @Override + public void unbindQueue(Binding binding) throws AMQStoreException { - Connection conn = null; - PreparedStatement stmt = null; - - try + int results = removeConfiguredObject(binding.getId()); + if (results == 0) { - conn = newAutoCommitConnection(); - // exchange_name varchar(255) not null, queue_name varchar(255) not null, binding_key varchar(255), arguments blob - stmt = conn.prepareStatement(DELETE_FROM_BINDINGS); - stmt.setString(1, exchange.getNameShortString().toString() ); - stmt.setString(2, queue.getNameShortString().toString()); - stmt.setString(3, routingKey == null ? null : routingKey.toString()); - - int result = stmt.executeUpdate(); - - if(result != 1) - { - throw new AMQStoreException("Queue binding for queue with name " + queue.getNameShortString() + " to exchange " - + exchange.getNameShortString() + " not found"); - } - } - catch (SQLException e) - { - throw new AMQStoreException("Error removing binding for AMQQueue with name " + queue.getNameShortString() + " to exchange " - + exchange.getNameShortString() + " in database: " + e.getMessage(), e); - } - finally - { - closePreparedStatement(stmt); - closeConnection(conn); + throw new AMQStoreException("Binding " + binding + " not found"); } } + @Override public void createQueue(AMQQueue queue) throws AMQStoreException { createQueue(queue, null); } + @Override public void createQueue(AMQQueue queue, FieldTable arguments) throws AMQStoreException { _logger.debug("public void createQueue(AMQQueue queue = " + queue + "): called"); - if (_state != State.RECOVERING) + if (_stateManager.isInState(State.ACTIVE)) { - try - { - Connection conn = newAutoCommitConnection(); - - PreparedStatement stmt = conn.prepareStatement(FIND_QUEUE); - try - { - stmt.setString(1, queue.getNameShortString().toString()); - ResultSet rs = stmt.executeQuery(); - try - { - - // If we don't have any data in the result set then we can add this queue - if (!rs.next()) - { - PreparedStatement insertStmt = conn.prepareStatement(INSERT_INTO_QUEUE); - - try - { - String owner = queue.getOwner() == null ? null : queue.getOwner().toString(); - - insertStmt.setString(1, queue.getNameShortString().toString()); - insertStmt.setString(2, owner); - insertStmt.setBoolean(3,queue.isExclusive()); - - final byte[] underlying; - if(arguments != null) - { - underlying = arguments.getDataAsBytes(); - } - else - { - underlying = new byte[0]; - } - - ByteArrayInputStream bis = new ByteArrayInputStream(underlying); - insertStmt.setBinaryStream(4,bis,underlying.length); - - insertStmt.execute(); - } - finally - { - insertStmt.close(); - } - } - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - conn.close(); - - } - catch (SQLException e) - { - throw new AMQStoreException("Error writing AMQQueue with name " + queue.getNameShortString() + " to database: " + e.getMessage(), e); - } + ConfiguredObjectRecord queueConfiguredObject = _configuredObjectHelper.createQueueConfiguredObject(queue, arguments); + insertConfiguredObject(queueConfiguredObject); } } - + /** * Updates the specified queue in the persistent store, IF it is already present. If the queue * is not present in the store, it will not be added. @@ -1275,61 +861,19 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor * @param queue The queue to update the entry for. * @throws AMQStoreException If the operation fails for any reason. */ + @Override public void updateQueue(final AMQQueue queue) throws AMQStoreException { - if (_state != State.RECOVERING) + if (_stateManager.isInState(State.ACTIVE)) { - try + ConfiguredObjectRecord queueConfiguredObject = loadConfiguredObject(queue.getId()); + if (queueConfiguredObject != null) { - Connection conn = newAutoCommitConnection(); - - try - { - PreparedStatement stmt = conn.prepareStatement(FIND_QUEUE); - try - { - stmt.setString(1, queue.getNameShortString().toString()); - - ResultSet rs = stmt.executeQuery(); - try - { - if (rs.next()) - { - PreparedStatement stmt2 = conn.prepareStatement(UPDATE_QUEUE_EXCLUSIVITY); - try - { - stmt2.setBoolean(1,queue.isExclusive()); - stmt2.setString(2, queue.getNameShortString().toString()); - - stmt2.execute(); - } - finally - { - stmt2.close(); - } - } - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - } - finally - { - conn.close(); - } - } - catch (SQLException e) - { - throw new AMQStoreException("Error updating AMQQueue with name " + queue.getNameShortString() + " to database: " + e.getMessage(), e); + ConfiguredObjectRecord newQueueRecord = _configuredObjectHelper.updateQueueConfiguredObject(queue, queueConfiguredObject); + updateConfiguredObject(newQueueRecord); } } - + } /** @@ -1355,7 +899,7 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor throw sqlEx; } } - + return connection; } @@ -1385,42 +929,24 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor return connection; } + @Override public void removeQueue(final AMQQueue queue) throws AMQStoreException { AMQShortString name = queue.getNameShortString(); _logger.debug("public void removeQueue(AMQShortString name = " + name + "): called"); - Connection conn = null; - PreparedStatement stmt = null; - try - { - conn = newAutoCommitConnection(); - stmt = conn.prepareStatement(DELETE_FROM_QUEUE); - stmt.setString(1, name.toString()); - int results = stmt.executeUpdate(); - - if (results == 0) - { - throw new AMQStoreException("Queue " + name + " not found"); - } - } - catch (SQLException e) + int results = removeConfiguredObject(queue.getId()); + if (results == 0) { - throw new AMQStoreException("Error deleting AMQQueue with name " + name + " from database: " + e.getMessage(), e); + throw new AMQStoreException("Queue " + name + " with id " + queue.getId() + " not found"); } - finally - { - closePreparedStatement(stmt); - closeConnection(conn); - } - - } + @Override public void createBrokerLink(final BrokerLink link) throws AMQStoreException { _logger.debug("public void createBrokerLink(BrokerLink = " + link + "): called"); - if (_state != State.RECOVERING) + if (_stateManager.isInState(State.ACTIVE)) { try { @@ -1429,7 +955,7 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor PreparedStatement stmt = conn.prepareStatement(FIND_LINK); try { - + stmt.setLong(1, link.getId().getLeastSignificantBits()); stmt.setLong(2, link.getId().getMostSignificantBits()); ResultSet rs = stmt.executeQuery(); @@ -1443,7 +969,7 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor try { - + insertStmt.setLong(1, link.getId().getLeastSignificantBits()); insertStmt.setLong(2, link.getId().getMostSignificantBits()); insertStmt.setLong(3, link.getCreateTime()); @@ -1512,6 +1038,7 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor return argumentBytes; } + @Override public void deleteBrokerLink(final BrokerLink link) throws AMQStoreException { _logger.debug("public void deleteBrokerLink( " + link + "): called"); @@ -1543,11 +1070,12 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor } + @Override public void createBridge(final Bridge bridge) throws AMQStoreException { _logger.debug("public void createBridge(BrokerLink = " + bridge + "): called"); - if (_state != State.RECOVERING) + if (_stateManager.isInState(State.ACTIVE)) { try { @@ -1613,6 +1141,7 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor } } + @Override public void deleteBridge(final Bridge bridge) throws AMQStoreException { _logger.debug("public void deleteBridge( " + bridge + "): called"); @@ -1643,6 +1172,7 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor } + @Override public Transaction newTransaction() { return new DerbyTransaction(); @@ -1650,8 +1180,6 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor public void enqueueMessage(ConnectionWrapper connWrapper, final TransactionLogResource queue, Long messageId) throws AMQStoreException { - String name = queue.getResourceName(); - Connection conn = connWrapper.getConnection(); @@ -1659,13 +1187,13 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor { if (_logger.isDebugEnabled()) { - _logger.debug("Enqueuing message " + messageId + " on queue " + name + "[Connection" + conn + "]"); + _logger.debug("Enqueuing message " + messageId + " on queue " + (queue instanceof AMQQueue ? ((AMQQueue)queue).getName() : "" ) + queue.getId()+ "[Connection" + conn + "]"); } - + PreparedStatement stmt = conn.prepareStatement(INSERT_INTO_QUEUE_ENTRY); try { - stmt.setString(1,name); + stmt.setString(1, queue.getId().toString()); stmt.setLong(2,messageId); stmt.executeUpdate(); } @@ -1677,7 +1205,7 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor catch (SQLException e) { _logger.error("Failed to enqueue: " + e.getMessage(), e); - throw new AMQStoreException("Error writing enqueued message with id " + messageId + " for queue " + name + throw new AMQStoreException("Error writing enqueued message with id " + messageId + " for queue " + (queue instanceof AMQQueue ? ((AMQQueue)queue).getName() : "" ) + " with id " + queue.getId() + " to database", e); } @@ -1685,8 +1213,6 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor public void dequeueMessage(ConnectionWrapper connWrapper, final TransactionLogResource queue, Long messageId) throws AMQStoreException { - String name = queue.getResourceName(); - Connection conn = connWrapper.getConnection(); @@ -1696,7 +1222,7 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_QUEUE_ENTRY); try { - stmt.setString(1,name); + stmt.setString(1, queue.getId().toString()); stmt.setLong(2,messageId); int results = stmt.executeUpdate(); @@ -1704,12 +1230,14 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor if(results != 1) { - throw new AMQStoreException("Unable to find message with id " + messageId + " on queue " + name); + throw new AMQStoreException("Unable to find message with id " + messageId + " on queue " + (queue instanceof AMQQueue ? ((AMQQueue)queue).getName() : "" ) + + " with id " + queue.getId()); } if (_logger.isDebugEnabled()) { - _logger.debug("Dequeuing message " + messageId + " on queue " + name ); + _logger.debug("Dequeuing message " + messageId + " on queue " + (queue instanceof AMQQueue ? ((AMQQueue)queue).getName() : "" ) + + " with id " + queue.getId()); } } finally @@ -1720,8 +1248,128 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor catch (SQLException e) { _logger.error("Failed to dequeue: " + e.getMessage(), e); - throw new AMQStoreException("Error deleting enqueued message with id " + messageId + " for queue " + name - + " from database", e); + throw new AMQStoreException("Error deleting enqueued message with id " + messageId + " for queue " + (queue instanceof AMQQueue ? ((AMQQueue)queue).getName() : "" ) + + " with id " + queue.getId() + " from database", e); + } + + } + + + private void removeXid(ConnectionWrapper connWrapper, long format, byte[] globalId, byte[] branchId) + throws AMQStoreException + { + Connection conn = connWrapper.getConnection(); + + + try + { + PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_XIDS); + try + { + stmt.setLong(1,format); + stmt.setBytes(2,globalId); + stmt.setBytes(3,branchId); + int results = stmt.executeUpdate(); + + + + if(results != 1) + { + throw new AMQStoreException("Unable to find message with xid"); + } + } + finally + { + stmt.close(); + } + + stmt = conn.prepareStatement(DELETE_FROM_XID_ACTIONS); + try + { + stmt.setLong(1,format); + stmt.setBytes(2,globalId); + stmt.setBytes(3,branchId); + int results = stmt.executeUpdate(); + + } + finally + { + stmt.close(); + } + + } + catch (SQLException e) + { + _logger.error("Failed to dequeue: " + e.getMessage(), e); + throw new AMQStoreException("Error deleting enqueued message with xid", e); + } + + } + + + private void recordXid(ConnectionWrapper connWrapper, long format, byte[] globalId, byte[] branchId, + Transaction.Record[] enqueues, Transaction.Record[] dequeues) throws AMQStoreException + { + Connection conn = connWrapper.getConnection(); + + + try + { + + PreparedStatement stmt = conn.prepareStatement(INSERT_INTO_XIDS); + try + { + stmt.setLong(1,format); + stmt.setBytes(2, globalId); + stmt.setBytes(3, branchId); + stmt.executeUpdate(); + } + finally + { + stmt.close(); + } + + stmt = conn.prepareStatement(INSERT_INTO_XID_ACTIONS); + + try + { + stmt.setLong(1,format); + stmt.setBytes(2, globalId); + stmt.setBytes(3, branchId); + + if(enqueues != null) + { + stmt.setString(4, "E"); + for(Transaction.Record record : enqueues) + { + stmt.setString(5, record.getQueue().getId().toString()); + stmt.setLong(6, record.getMessage().getMessageNumber()); + stmt.executeUpdate(); + } + } + + if(dequeues != null) + { + stmt.setString(4, "D"); + for(Transaction.Record record : dequeues) + { + stmt.setString(5, record.getQueue().getId().toString()); + stmt.setLong(6, record.getMessage().getMessageNumber()); + stmt.executeUpdate(); + } + } + + } + finally + { + stmt.close(); + } + + } + catch (SQLException e) + { + _logger.error("Failed to enqueue: " + e.getMessage(), e); + throw new AMQStoreException("Error writing xid ", e); } } @@ -1770,7 +1418,7 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor public StoreFuture commitTranAsync(ConnectionWrapper connWrapper) throws AMQStoreException { commitTran(connWrapper); - return new CommitStoreFuture(); + return StoreFuture.IMMEDIATE_FUTURE; } public void abortTran(ConnectionWrapper connWrapper) throws AMQStoreException @@ -1811,7 +1459,7 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor { _logger.debug("Adding metadata for message " +messageId); } - + PreparedStatement stmt = conn.prepareStatement(INSERT_INTO_META_DATA); try { @@ -1854,7 +1502,7 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor { stmt.close(); } - + } @@ -1919,7 +1567,7 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor - private void recoverQueueEntries(TransactionLogRecoveryHandler recoveryHandler) throws SQLException + private TransactionLogRecoveryHandler.DtxRecordRecoveryHandler recoverQueueEntries(TransactionLogRecoveryHandler recoveryHandler) throws SQLException { Connection conn = newAutoCommitConnection(); try @@ -1935,9 +1583,9 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor while(rs.next()) { - String queueName = rs.getString(1); + String id = rs.getString(1); long messageId = rs.getLong(2); - queueEntryHandler.queueEntry(queueName,messageId); + queueEntryHandler.queueEntry(UUID.fromString(id), messageId); } } finally @@ -1950,7 +1598,7 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor stmt.close(); } - queueEntryHandler.completeQueueEntryRecovery(); + return queueEntryHandler.completeQueueEntryRecovery(); } finally { @@ -1958,6 +1606,172 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor } } + private static final class Xid + { + + private final long _format; + private final byte[] _globalId; + private final byte[] _branchId; + + public Xid(long format, byte[] globalId, byte[] branchId) + { + _format = format; + _globalId = globalId; + _branchId = branchId; + } + + public long getFormat() + { + return _format; + } + + public byte[] getGlobalId() + { + return _globalId; + } + + public byte[] getBranchId() + { + return _branchId; + } + } + + private static class RecordImpl implements Transaction.Record, TransactionLogResource, EnqueableMessage + { + + private long _messageNumber; + private UUID _queueId; + + public RecordImpl(UUID queueId, long messageNumber) + { + _messageNumber = messageNumber; + _queueId = queueId; + } + + @Override + public TransactionLogResource getQueue() + { + return this; + } + + @Override + public EnqueableMessage getMessage() + { + return this; + } + + @Override + public long getMessageNumber() + { + return _messageNumber; + } + + @Override + public boolean isPersistent() + { + return true; + } + + @Override + public StoredMessage getStoredMessage() + { + throw new UnsupportedOperationException(); + } + + @Override + public UUID getId() + { + return _queueId; + } + } + + private void recoverXids(TransactionLogRecoveryHandler.DtxRecordRecoveryHandler dtxrh) throws SQLException + { + Connection conn = newAutoCommitConnection(); + try + { + List<Xid> xids = new ArrayList<Xid>(); + + Statement stmt = conn.createStatement(); + try + { + ResultSet rs = stmt.executeQuery(SELECT_ALL_FROM_XIDS); + try + { + while(rs.next()) + { + + long format = rs.getLong(1); + byte[] globalId = rs.getBytes(2); + byte[] branchId = rs.getBytes(3); + xids.add(new Xid(format, globalId, branchId)); + } + } + finally + { + rs.close(); + } + } + finally + { + stmt.close(); + } + + + + for(Xid xid : xids) + { + List<RecordImpl> enqueues = new ArrayList<RecordImpl>(); + List<RecordImpl> dequeues = new ArrayList<RecordImpl>(); + + PreparedStatement pstmt = conn.prepareStatement(SELECT_ALL_FROM_XID_ACTIONS); + + try + { + pstmt.setLong(1, xid.getFormat()); + pstmt.setBytes(2, xid.getGlobalId()); + pstmt.setBytes(3, xid.getBranchId()); + + ResultSet rs = pstmt.executeQuery(); + try + { + while(rs.next()) + { + + String actionType = rs.getString(1); + UUID queueId = UUID.fromString(rs.getString(2)); + long messageId = rs.getLong(3); + + RecordImpl record = new RecordImpl(queueId, messageId); + List<RecordImpl> records = "E".equals(actionType) ? enqueues : dequeues; + records.add(record); + } + } + finally + { + rs.close(); + } + } + finally + { + pstmt.close(); + } + + dtxrh.dtxRecord(xid.getFormat(), xid.getGlobalId(), xid.getBranchId(), + enqueues.toArray(new RecordImpl[enqueues.size()]), + dequeues.toArray(new RecordImpl[dequeues.size()])); + } + + + dtxrh.completeDtxRecordRecovery(); + } + finally + { + conn.close(); + } + + } + StorableMessageMetaData getMetaData(long messageId) throws SQLException { @@ -2007,11 +1821,11 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor } - private void addContent(Connection conn, long messageId, int offset, ByteBuffer src) + private void addContent(Connection conn, long messageId, ByteBuffer src) { if(_logger.isDebugEnabled()) { - _logger.debug("Adding content chunk offset " + offset + " for message " +messageId); + _logger.debug("Adding content for message " +messageId); } PreparedStatement stmt = null; @@ -2024,20 +1838,15 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor stmt = conn.prepareStatement(INSERT_INTO_MESSAGE_CONTENT); stmt.setLong(1,messageId); - stmt.setInt(2, offset); - stmt.setInt(3, offset+chunkData.length); - - - // TODO in Java 6 we could just use blobs ByteArrayInputStream bis = new ByteArrayInputStream(chunkData); - stmt.setBinaryStream(4, bis, chunkData.length); + stmt.setBinaryStream(2, bis, chunkData.length); stmt.executeUpdate(); } catch (SQLException e) { closeConnection(conn); - throw new RuntimeException("Error adding content chunk offset " + offset + " for message " + messageId + ": " + e.getMessage(), e); + throw new RuntimeException("Error adding content for message " + messageId + ": " + e.getMessage(), e); } finally { @@ -2058,33 +1867,32 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor stmt = conn.prepareStatement(SELECT_FROM_MESSAGE_CONTENT); stmt.setLong(1,messageId); - stmt.setInt(2, offset); - stmt.setInt(3, offset+dst.remaining()); ResultSet rs = stmt.executeQuery(); int written = 0; - while(rs.next()) + if (rs.next()) { - int offsetInMessage = rs.getInt(1); - Blob dataAsBlob = rs.getBlob(2); + + Blob dataAsBlob = rs.getBlob(1); final int size = (int) dataAsBlob.length(); byte[] dataAsBytes = dataAsBlob.getBytes(1, size); - int posInArray = offset + written - offsetInMessage; - int count = size - posInArray; - if(count > dst.remaining()) + if (offset > size) { - count = dst.remaining(); + throw new RuntimeException("Offset " + offset + " is greater than message size " + size + + " for message id " + messageId + "!"); + } - dst.put(dataAsBytes,posInArray,count); - written+=count; - if(dst.remaining() == 0) + written = size - offset; + if(written > dst.remaining()) { - break; + written = dst.remaining(); } + + dst.put(dataAsBytes, offset, written); } return written; @@ -2103,24 +1911,13 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor } + @Override public boolean isPersistent() { return true; } - private synchronized void stateTransition(State requiredState, State newState) throws AMQStoreException - { - if (_state != requiredState) - { - throw new AMQStoreException("Cannot transition to the state: " + newState + "; need to be in state: " + requiredState - + "; currently in state: " + _state); - } - - _state = newState; - } - - private class DerbyTransaction implements Transaction { private final ConnectionWrapper _connWrapper; @@ -2138,6 +1935,7 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor } } + @Override public void enqueueMessage(TransactionLogResource queue, EnqueableMessage message) throws AMQStoreException { if(message.getStoredMessage() instanceof StoredDerbyMessage) @@ -2155,28 +1953,47 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor DerbyMessageStore.this.enqueueMessage(_connWrapper, queue, message.getMessageNumber()); } + @Override public void dequeueMessage(TransactionLogResource queue, EnqueableMessage message) throws AMQStoreException { DerbyMessageStore.this.dequeueMessage(_connWrapper, queue, message.getMessageNumber()); } + @Override public void commitTran() throws AMQStoreException { DerbyMessageStore.this.commitTran(_connWrapper); } + @Override public StoreFuture commitTranAsync() throws AMQStoreException { return DerbyMessageStore.this.commitTranAsync(_connWrapper); } + @Override public void abortTran() throws AMQStoreException { DerbyMessageStore.this.abortTran(_connWrapper); } + + @Override + public void removeXid(long format, byte[] globalId, byte[] branchId) throws AMQStoreException + { + DerbyMessageStore.this.removeXid(_connWrapper, format, globalId, branchId); + } + + @Override + public void recordXid(long format, byte[] globalId, byte[] branchId, Record[] enqueues, Record[] dequeues) + throws AMQStoreException + { + DerbyMessageStore.this.recordXid(_connWrapper, format, globalId, branchId, enqueues, dequeues); + } } + + private class StoredDerbyMessage implements StoredMessage { @@ -2185,7 +2002,7 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor private volatile SoftReference<StorableMessageMetaData> _metaDataRef; private byte[] _data; private volatile SoftReference<byte[]> _dataRef; - + StoredDerbyMessage(long messageId, StorableMessageMetaData metaData) { @@ -2197,15 +2014,16 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor StorableMessageMetaData metaData, boolean persist) { _messageId = messageId; - + _metaDataRef = new SoftReference<StorableMessageMetaData>(metaData); if(persist) { - _metaData = metaData; + _metaData = metaData; } } + @Override public StorableMessageMetaData getMetaData() { StorableMessageMetaData metaData = _metaData == null ? _metaDataRef.get() : _metaData; @@ -2225,11 +2043,13 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor return metaData; } + @Override public long getMessageNumber() { return _messageId; } + @Override public void addContent(int offsetInMessage, java.nio.ByteBuffer src) { src = src.slice(); @@ -2249,9 +2069,10 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor System.arraycopy(oldData,0,_data,0,oldData.length); src.duplicate().get(_data, oldData.length, src.remaining()); } - + } + @Override public int getContent(int offsetInMessage, java.nio.ByteBuffer dst) { byte[] data = _dataRef == null ? null : _dataRef.get(); @@ -2268,6 +2089,7 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor } + @Override public ByteBuffer getContent(int offsetInMessage, int size) { ByteBuffer buf = ByteBuffer.allocate(size); @@ -2276,6 +2098,7 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor return buf; } + @Override public synchronized StoreFuture flushToStore() { try @@ -2285,7 +2108,7 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor Connection conn = newConnection(); store(conn); - + conn.commit(); conn.close(); } @@ -2298,7 +2121,7 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor } throw new RuntimeException(e); } - return IMMEDIATE_FUTURE; + return StoreFuture.IMMEDIATE_FUTURE; } private synchronized void store(final Connection conn) throws SQLException @@ -2308,7 +2131,7 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor try { storeMetaData(conn, _messageId, _metaData); - DerbyMessageStore.this.addContent(conn, _messageId, 0, + DerbyMessageStore.this.addContent(conn, _messageId, _data == null ? ByteBuffer.allocate(0) : ByteBuffer.wrap(_data)); } finally @@ -2324,6 +2147,7 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor } } + @Override public void remove() { DerbyMessageStore.this.removeMessage(_messageId); @@ -2360,4 +2184,266 @@ public class DerbyMessageStore implements MessageStore, DurableConfigurationStor } } -} + @Override + public void addEventListener(EventListener eventListener, Event... events) + { + _eventManager.addEventListener(eventListener, events); + } + + @Override + public String getStoreLocation() + { + return _storeLocation; + } + + private void insertConfiguredObject(ConfiguredObjectRecord configuredObject) throws AMQStoreException + { + if (_stateManager.isInState(State.ACTIVE)) + { + try + { + Connection conn = newAutoCommitConnection(); + try + { + PreparedStatement stmt = conn.prepareStatement(FIND_CONFIGURED_OBJECT); + try + { + stmt.setString(1, configuredObject.getId().toString()); + ResultSet rs = stmt.executeQuery(); + try + { + // If we don't have any data in the result set then we can add this configured object + if (!rs.next()) + { + PreparedStatement insertStmt = conn.prepareStatement(INSERT_INTO_CONFIGURED_OBJECTS); + try + { + insertStmt.setString(1, configuredObject.getId().toString()); + insertStmt.setString(2, configuredObject.getType()); + if(configuredObject.getAttributes() == null) + { + insertStmt.setNull(3, Types.BLOB); + } + else + { + byte[] attributesAsBytes = configuredObject.getAttributes().getBytes(UTF8_CHARSET); + ByteArrayInputStream bis = new ByteArrayInputStream(attributesAsBytes); + insertStmt.setBinaryStream(3, bis, attributesAsBytes.length); + } + insertStmt.execute(); + } + finally + { + insertStmt.close(); + } + } + } + finally + { + rs.close(); + } + } + finally + { + stmt.close(); + } + } + finally + { + conn.close(); + } + } + catch (SQLException e) + { + throw new AMQStoreException("Error inserting of configured object " + configuredObject + " into database: " + e.getMessage(), e); + } + } + } + + private int removeConfiguredObject(UUID id) throws AMQStoreException + { + int results = 0; + try + { + Connection conn = newAutoCommitConnection(); + try + { + PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_CONFIGURED_OBJECTS); + try + { + stmt.setString(1, id.toString()); + results = stmt.executeUpdate(); + } + finally + { + stmt.close(); + } + } + finally + { + conn.close(); + } + } + catch (SQLException e) + { + throw new AMQStoreException("Error deleting of configured object with id " + id + " from database: " + e.getMessage(), e); + } + return results; + } + + private void updateConfiguredObject(final ConfiguredObjectRecord configuredObject) throws AMQStoreException + { + if (_stateManager.isInState(State.ACTIVE)) + { + try + { + Connection conn = newAutoCommitConnection(); + try + { + PreparedStatement stmt = conn.prepareStatement(FIND_CONFIGURED_OBJECT); + try + { + stmt.setString(1, configuredObject.getId().toString()); + ResultSet rs = stmt.executeQuery(); + try + { + if (rs.next()) + { + PreparedStatement stmt2 = conn.prepareStatement(UPDATE_CONFIGURED_OBJECTS); + try + { + stmt2.setString(1, configuredObject.getType()); + if (configuredObject.getAttributes() != null) + { + byte[] attributesAsBytes = configuredObject.getAttributes().getBytes(UTF8_CHARSET); + ByteArrayInputStream bis = new ByteArrayInputStream(attributesAsBytes); + stmt2.setBinaryStream(2, bis, attributesAsBytes.length); + } + else + { + stmt2.setNull(2, Types.BLOB); + } + stmt2.setString(3, configuredObject.getId().toString()); + stmt2.execute(); + } + finally + { + stmt2.close(); + } + } + } + finally + { + rs.close(); + } + } + finally + { + stmt.close(); + } + } + finally + { + conn.close(); + } + } + catch (SQLException e) + { + throw new AMQStoreException("Error updating configured object " + configuredObject + " in database: " + e.getMessage(), e); + } + } + } + + private ConfiguredObjectRecord loadConfiguredObject(final UUID id) throws AMQStoreException + { + ConfiguredObjectRecord result = null; + try + { + Connection conn = newAutoCommitConnection(); + try + { + PreparedStatement stmt = conn.prepareStatement(FIND_CONFIGURED_OBJECT); + try + { + stmt.setString(1, id.toString()); + ResultSet rs = stmt.executeQuery(); + try + { + if (rs.next()) + { + String type = rs.getString(1); + Blob blob = rs.getBlob(2); + String attributes = null; + if (blob != null) + { + attributes = blobToString(blob); + } + result = new ConfiguredObjectRecord(id, type, attributes); + } + } + finally + { + rs.close(); + } + } + finally + { + stmt.close(); + } + } + finally + { + conn.close(); + } + } + catch (SQLException e) + { + throw new AMQStoreException("Error loading of configured object with id " + id + " from database: " + + e.getMessage(), e); + } + return result; + } + + private String blobToString(Blob blob) throws SQLException + { + byte[] bytes = blob.getBytes(1, (int)blob.length()); + return new String(bytes, UTF8_CHARSET); + } + + private List<ConfiguredObjectRecord> loadConfiguredObjects() throws SQLException + { + ArrayList<ConfiguredObjectRecord> results = new ArrayList<ConfiguredObjectRecord>(); + Connection conn = newAutoCommitConnection(); + try + { + PreparedStatement stmt = conn.prepareStatement(SELECT_FROM_CONFIGURED_OBJECTS); + try + { + ResultSet rs = stmt.executeQuery(); + try + { + while (rs.next()) + { + String id = rs.getString(1); + String objectType = rs.getString(2); + String attributes = blobToString(rs.getBlob(3)); + results.add(new ConfiguredObjectRecord(UUID.fromString(id), objectType, attributes)); + } + } + finally + { + rs.close(); + } + } + finally + { + stmt.close(); + } + } + finally + { + conn.close(); + } + return results; + } +}
\ No newline at end of file diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/derby/DerbyMessageStoreFactory.java b/java/broker/src/main/java/org/apache/qpid/server/store/derby/DerbyMessageStoreFactory.java new file mode 100644 index 0000000000..12d7f64a8d --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/store/derby/DerbyMessageStoreFactory.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.store.derby; + +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.MessageStoreFactory; + +public class DerbyMessageStoreFactory implements MessageStoreFactory +{ + + @Override + public MessageStore createMessageStore() + { + return new DerbyMessageStore(); + } + + @Override + public String getStoreClassName() + { + return DerbyMessageStore.class.getSimpleName(); + } + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java b/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java index f8a585b562..66825caa24 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java +++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java @@ -54,16 +54,12 @@ public interface Subscription void setNoLocal(boolean noLocal); - AMQShortString getConsumerTag(); - long getSubscriptionID(); boolean isSuspended(); boolean hasInterest(QueueEntry msg); - boolean isAutoClose(); - boolean isClosed(); boolean acquires(); @@ -105,11 +101,11 @@ public interface Subscription boolean isActive(); - void confirmAutoClose(); - public void set(String key, Object value); public Object get(String key); boolean isSessionTransactional(); + + void queueEmpty() throws AMQException; } diff --git a/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java index 32baa17fc7..1f25c215cc 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java +++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java @@ -375,7 +375,7 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage { return getQueue().getConfigStore(); } - + public Long getDelivered() { return _deliveredCount.get(); @@ -810,12 +810,22 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage { return _channel.isTransactional(); } - + public long getCreateTime() { return _createTime; } + public void queueEmpty() throws AMQException + { + if (isAutoClose()) + { + _queue.unregisterSubscription(this); + + confirmAutoClose(); + } + } + public void flushBatched() { _channel.getProtocolSession().setDeferFlush(false); diff --git a/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription_0_10.java b/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription_0_10.java index 9a7d69b10d..76d975a789 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription_0_10.java +++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription_0_10.java @@ -109,7 +109,7 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr public void stateChange(Subscription sub, State oldState, State newState) { - CurrentActor.get().message(SubscriptionMessages.STATE(newState.toString())); + CurrentActor.get().message(SubscriptionMessages.STATE(newState.toString())); } }; private AMQQueue _queue; @@ -199,12 +199,7 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr CurrentActor.get().message(this, SubscriptionMessages.CREATE(filterLogString, queue.isDurable() && exclusive, filterLogString.length() > 0)); } - - } - public AMQShortString getConsumerTag() - { - return new AMQShortString(_destination); } public boolean isSuspended() @@ -244,12 +239,6 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr return (_filters == null) || _filters.allAllow(entry); } - public boolean isAutoClose() - { - // no such thing in 0-10 - return false; - } - public boolean isClosed() { return getState() == State.CLOSED; @@ -302,7 +291,7 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr { return getQueue().getConfigStore(); } - + public Long getDelivered() { return _deliveredCount.get(); @@ -409,6 +398,10 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr { deliveryProps.setTimestamp(origDeliveryProps.getTimestamp()); } + if(origDeliveryProps.hasTtl()) + { + deliveryProps.setTtl(origDeliveryProps.getTtl()); + } } @@ -684,7 +677,10 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr { entry.setRedelivered(); entry.routeToAlternate(); - + if(entry.isAcquiredBy(this)) + { + entry.discard(); + } } void release(final QueueEntry entry, final boolean setRedelivered) @@ -816,11 +812,6 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr return getState() == State.ACTIVE; } - public void confirmAutoClose() - { - //No such thing in 0-10 - } - public void set(String key, Object value) { _properties.put(key, value); @@ -1019,6 +1010,10 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr return _session.isTransactional(); } + public void queueEmpty() + { + } + public long getCreateTime() { return _createTime; @@ -1026,7 +1021,7 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr public String toLogString() { - String queueInfo = MessageFormat.format(QUEUE_FORMAT, _queue.getVirtualHost().getName(), + String queueInfo = MessageFormat.format(QUEUE_FORMAT, _queue.getVirtualHost().getName(), _queue.getNameShortString()); String result = "[" + MessageFormat.format(SUBSCRIPTION_FORMAT, getSubscriptionID()) + "(" // queueString is "vh(/{0})/qu({1}) " so need to trim diff --git a/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java b/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java index 02c4ffa012..5460c89eab 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java +++ b/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java @@ -33,6 +33,7 @@ import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.security.auth.AuthenticationResult; import org.apache.qpid.server.security.auth.AuthenticationResult.AuthenticationStatus; import org.apache.qpid.server.subscription.Subscription_0_10; +import org.apache.qpid.server.virtualhost.State; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.transport.*; import org.slf4j.Logger; @@ -177,6 +178,11 @@ public class ServerConnectionDelegate extends ServerDelegate sconn.setState(Connection.State.CLOSING); sconn.invoke(new ConnectionClose(ConnectionCloseCode.CONNECTION_FORCED, "Permission denied '"+vhostName+"'")); } + else if (vhost.getState() != State.ACTIVE) + { + sconn.setState(Connection.State.CLOSING); + sconn.invoke(new ConnectionClose(ConnectionCloseCode.CONNECTION_FORCED, "Virtual host '"+vhostName+"' is not active")); + } else { sconn.setState(Connection.State.OPEN); @@ -231,23 +237,26 @@ public class ServerConnectionDelegate extends ServerDelegate @Override public void sessionDetach(Connection conn, SessionDetach dtc) { - // To ensure a clean detach, we unregister any remaining subscriptions. Unregister ensures - // that any in-progress delivery (SubFlushRunner/QueueRunner) is completed before the unregister + // To ensure a clean detach, we stop any remaining subscriptions. Stop ensures + // that any in-progress delivery (SubFlushRunner/QueueRunner) is completed before the stop // completes. - unregisterAllSubscriptions(conn, dtc); + stopAllSubscriptions(conn, dtc); + Session ssn = conn.getSession(dtc.getChannel()); + ((ServerSession)ssn).setClose(true); super.sessionDetach(conn, dtc); } - private void unregisterAllSubscriptions(Connection conn, SessionDetach dtc) + private void stopAllSubscriptions(Connection conn, SessionDetach dtc) { final ServerSession ssn = (ServerSession) conn.getSession(dtc.getChannel()); final Collection<Subscription_0_10> subs = ssn.getSubscriptions(); for (Subscription_0_10 subscription_0_10 : subs) { - ssn.unregister(subscription_0_10); + subscription_0_10.stop(); } } + @Override public void sessionAttach(final Connection conn, final SessionAttach atc) { @@ -255,8 +264,7 @@ public class ServerConnectionDelegate extends ServerDelegate if(isSessionNameUnique(atc.getName(), conn)) { - ssn = sessionAttachImpl(conn, atc); - conn.registerSession(ssn); + super.sessionAttach(conn, atc); ((ServerConnection)conn).checkForNotification(); } else @@ -306,4 +314,4 @@ public class ServerConnectionDelegate extends ServerDelegate { return _clientProperties == null ? null : (String) _clientProperties.get(ConnectionStartProperties.VERSION_0_10); } -}
\ No newline at end of file +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java b/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java index e4268ed2dc..6f979e035e 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java +++ b/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java @@ -21,6 +21,11 @@ package org.apache.qpid.server.transport; import static org.apache.qpid.server.logging.subjects.LogSubjectFormat.CHANNEL_FORMAT; +import org.apache.qpid.server.message.InboundMessage; +import org.apache.qpid.server.message.MessageMetaData_0_10; +import org.apache.qpid.server.message.MessageTransferMessage; +import org.apache.qpid.server.txn.RollbackOnlyDtxException; +import org.apache.qpid.server.txn.TimeoutDtxException; import static org.apache.qpid.util.Serial.gt; import java.security.Principal; @@ -44,6 +49,7 @@ import java.util.concurrent.atomic.AtomicLong; import javax.security.auth.Subject; import org.apache.qpid.AMQException; +import org.apache.qpid.AMQStoreException; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.protocol.ProtocolEngine; import org.apache.qpid.server.configuration.ConfigStore; @@ -66,25 +72,21 @@ import org.apache.qpid.server.queue.BaseQueue; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.security.AuthorizationHolder; import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.StoreFuture; import org.apache.qpid.server.subscription.Subscription_0_10; +import org.apache.qpid.server.txn.AlreadyKnownDtxException; import org.apache.qpid.server.txn.AsyncAutoCommitTransaction; +import org.apache.qpid.server.txn.DistributedTransaction; +import org.apache.qpid.server.txn.DtxNotSelectedException; +import org.apache.qpid.server.txn.IncorrectDtxStateException; +import org.apache.qpid.server.txn.JoinAndResumeDtxException; import org.apache.qpid.server.txn.LocalTransaction; +import org.apache.qpid.server.txn.NotAssociatedDtxException; import org.apache.qpid.server.txn.ServerTransaction; +import org.apache.qpid.server.txn.SuspendAndFailDtxException; +import org.apache.qpid.server.txn.UnknownDtxBranchException; import org.apache.qpid.server.virtualhost.VirtualHost; -import org.apache.qpid.transport.Binary; -import org.apache.qpid.transport.Connection; -import org.apache.qpid.transport.MessageCreditUnit; -import org.apache.qpid.transport.MessageFlow; -import org.apache.qpid.transport.MessageFlowMode; -import org.apache.qpid.transport.MessageSetFlowMode; -import org.apache.qpid.transport.MessageStop; -import org.apache.qpid.transport.MessageTransfer; -import org.apache.qpid.transport.Method; -import org.apache.qpid.transport.Range; -import org.apache.qpid.transport.RangeSet; -import org.apache.qpid.transport.RangeSetFactory; -import org.apache.qpid.transport.Session; -import org.apache.qpid.transport.SessionDelegate; +import org.apache.qpid.transport.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -109,7 +111,6 @@ public class ServerSession extends Session private ChannelLogSubject _logSubject; private final AtomicInteger _outstandingCredit = new AtomicInteger(UNLIMITED_CREDIT); - public static interface MessageDispositionChangeListener { public void onAccept(); @@ -133,7 +134,7 @@ public class ServerSession extends Session new ConcurrentSkipListMap<Integer, MessageDispositionChangeListener>(); private ServerTransaction _transaction; - + private final AtomicLong _txnStarts = new AtomicLong(0); private final AtomicLong _txnCommits = new AtomicLong(0); private final AtomicLong _txnRejects = new AtomicLong(0); @@ -152,7 +153,7 @@ public class ServerSession extends Session public ServerSession(Connection connection, SessionDelegate delegate, Binary name, long expiry, ConnectionConfig connConfig) { super(connection, delegate, name, expiry); - _connectionConfig = connConfig; + _connectionConfig = connConfig; _transaction = new AsyncAutoCommitTransaction(this.getMessageStore(),this); _logSubject = new ChannelLogSubject(this); _id = getConfigStore().createId(); @@ -352,14 +353,22 @@ public class ServerSession extends Session } } - public void removeDispositionListener(Method method) + public void removeDispositionListener(Method method) { _messageDispositionListenerMap.remove(method.getId()); } public void onClose() { - _transaction.rollback(); + if(_transaction instanceof LocalTransaction) + { + _transaction.rollback(); + } + else if(_transaction instanceof DistributedTransaction) + { + getVirtualHost().getDtxRegistry().endAssociations(this); + } + for(MessageDispositionChangeListener listener : _messageDispositionListenerMap.values()) { listener.onRelease(true); @@ -372,7 +381,7 @@ public class ServerSession extends Session { task.doTask(this); } - + CurrentActor.get().message(getLogSubject(), ChannelMessages.CLOSE()); } @@ -395,6 +404,9 @@ public class ServerSession extends Session public void onRollback() { + // The client has acknowledge the message and therefore have seen it. + // In the event of rollback, the message must be marked as redelivered. + entry.setRedelivered(); entry.release(); } }); @@ -418,7 +430,7 @@ public class ServerSession extends Session public void unregister(Subscription_0_10 sub) { - _subscriptions.remove(sub.getConsumerTag().toString()); + _subscriptions.remove(sub.getName()); try { sub.getSendLock(); @@ -455,10 +467,99 @@ public class ServerSession extends Session _txnStarts.incrementAndGet(); } + public void selectDtx() + { + _transaction = new DistributedTransaction(this, getMessageStore(), getVirtualHost()); + + } + + + public void startDtx(Xid xid, boolean join, boolean resume) + throws JoinAndResumeDtxException, + UnknownDtxBranchException, + AlreadyKnownDtxException, + DtxNotSelectedException + { + DistributedTransaction distributedTransaction = assertDtxTransaction(); + distributedTransaction.start(xid, join, resume); + } + + + public void endDtx(Xid xid, boolean fail, boolean suspend) + throws NotAssociatedDtxException, + UnknownDtxBranchException, + DtxNotSelectedException, + SuspendAndFailDtxException, TimeoutDtxException + { + DistributedTransaction distributedTransaction = assertDtxTransaction(); + distributedTransaction.end(xid, fail, suspend); + } + + + public long getTimeoutDtx(Xid xid) + throws UnknownDtxBranchException + { + return getVirtualHost().getDtxRegistry().getTimeout(xid); + } + + + public void setTimeoutDtx(Xid xid, long timeout) + throws UnknownDtxBranchException + { + getVirtualHost().getDtxRegistry().setTimeout(xid, timeout); + } + + + public void prepareDtx(Xid xid) + throws UnknownDtxBranchException, + IncorrectDtxStateException, AMQStoreException, RollbackOnlyDtxException, TimeoutDtxException + { + getVirtualHost().getDtxRegistry().prepare(xid); + } + + public void commitDtx(Xid xid, boolean onePhase) + throws UnknownDtxBranchException, + IncorrectDtxStateException, AMQStoreException, RollbackOnlyDtxException, TimeoutDtxException + { + getVirtualHost().getDtxRegistry().commit(xid, onePhase); + } + + + public void rollbackDtx(Xid xid) + throws UnknownDtxBranchException, + IncorrectDtxStateException, AMQStoreException, TimeoutDtxException + { + getVirtualHost().getDtxRegistry().rollback(xid); + } + + + public void forgetDtx(Xid xid) throws UnknownDtxBranchException, IncorrectDtxStateException + { + getVirtualHost().getDtxRegistry().forget(xid); + } + + public List<Xid> recoverDtx() + { + return getVirtualHost().getDtxRegistry().recover(); + } + + private DistributedTransaction assertDtxTransaction() throws DtxNotSelectedException + { + if(_transaction instanceof DistributedTransaction) + { + return (DistributedTransaction) _transaction; + } + else + { + throw new DtxNotSelectedException(); + } + } + + public void commit() { _transaction.commit(); - + _txnCommits.incrementAndGet(); _txnStarts.incrementAndGet(); decrementOutstandingTxnsIfNecessary(); @@ -467,13 +568,13 @@ public class ServerSession extends Session public void rollback() { _transaction.rollback(); - + _txnRejects.incrementAndGet(); _txnStarts.incrementAndGet(); decrementOutstandingTxnsIfNecessary(); } - + private void incrementOutstandingTxnsIfNecessary() { if(isTransactional()) @@ -483,7 +584,7 @@ public class ServerSession extends Session _txnCount.compareAndSet(0,1); } } - + private void decrementOutstandingTxnsIfNecessary() { if(isTransactional()) @@ -524,7 +625,7 @@ public class ServerSession extends Session { return _txnCount.get(); } - + public Principal getAuthorizedPrincipal() { return getConnection().getAuthorizedPrincipal(); @@ -620,11 +721,6 @@ public class ServerSession extends Session close(); } - public Object getID() - { - return getName(); - } - public AMQConnectionModel getConnectionModel() { return getConnection(); @@ -704,7 +800,7 @@ public class ServerSession extends Session { if(_blockingQueues.remove(queue) && _blockingQueues.isEmpty()) { - if(_blocking.compareAndSet(true,false)) + if(_blocking.compareAndSet(true,false) && !isClosing()) { _actor.message(_logSubject, ChannelMessages.FLOW_REMOVED()); @@ -720,6 +816,14 @@ public class ServerSession extends Session } } + public boolean onSameConnection(InboundMessage inbound) + { + return ((inbound instanceof MessageTransferMessage) + && ((MessageTransferMessage)inbound).getConnectionReference() == getConnection().getReference()) + || ((inbound instanceof MessageMetaData_0_10) + && (((MessageMetaData_0_10)inbound).getConnectionReference())== getConnection().getReference()); + } + public String toLogString() { @@ -746,7 +850,6 @@ public class ServerSession extends Session // unregister subscriptions in order to prevent sending of new messages // to subscriptions with closing session unregisterSubscriptions(); - super.close(); } @@ -759,6 +862,16 @@ public class ServerSession extends Session } } + void stopSubscriptions() + { + final Collection<Subscription_0_10> subscriptions = getSubscriptions(); + for (Subscription_0_10 subscription_0_10 : subscriptions) + { + subscription_0_10.stop(); + } + } + + public void receivedComplete() { final Collection<Subscription_0_10> subscriptions = getSubscriptions(); @@ -863,17 +976,17 @@ public class ServerSession extends Session return _unfinishedCommandsQueue.isEmpty() ? null : _unfinishedCommandsQueue.getLast(); } - public void recordFuture(final MessageStore.StoreFuture future, final ServerTransaction.Action action) + public void recordFuture(final StoreFuture future, final ServerTransaction.Action action) { _unfinishedCommandsQueue.add(new AsyncCommand(future, action)); } private static class AsyncCommand { - private final MessageStore.StoreFuture _future; + private final StoreFuture _future; private ServerTransaction.Action _action; - public AsyncCommand(final MessageStore.StoreFuture future, final ServerTransaction.Action action) + public AsyncCommand(final StoreFuture future, final ServerTransaction.Action action) { _future = future; _action = action; @@ -900,9 +1013,14 @@ public class ServerSession extends Session } } - @Override + protected void setClose(boolean close) + { + super.setClose(close); + } + public int compareTo(AMQSessionModel session) { - return getId().toString().compareTo(session.getID().toString()); + return getId().compareTo(session.getId()); } + } diff --git a/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java b/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java index 8e4fb3635b..79a8bc0e4c 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java +++ b/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java @@ -23,6 +23,7 @@ package org.apache.qpid.server.transport; import org.apache.log4j.Logger; import org.apache.qpid.AMQException; +import org.apache.qpid.AMQStoreException; import org.apache.qpid.AMQUnknownExchangeType; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; @@ -39,6 +40,7 @@ import org.apache.qpid.server.flow.WindowCreditManager; import org.apache.qpid.server.logging.messages.ExchangeMessages; import org.apache.qpid.server.message.MessageMetaData_0_10; import org.apache.qpid.server.message.MessageTransferMessage; +import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.AMQQueueFactory; import org.apache.qpid.server.queue.BaseQueue; @@ -47,10 +49,20 @@ import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.store.DurableConfigurationStore; import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.StoreFuture; import org.apache.qpid.server.store.StoredMessage; import org.apache.qpid.server.subscription.SubscriptionFactoryImpl; import org.apache.qpid.server.subscription.Subscription_0_10; +import org.apache.qpid.server.txn.AlreadyKnownDtxException; +import org.apache.qpid.server.txn.DtxNotSelectedException; +import org.apache.qpid.server.txn.IncorrectDtxStateException; +import org.apache.qpid.server.txn.JoinAndResumeDtxException; +import org.apache.qpid.server.txn.NotAssociatedDtxException; +import org.apache.qpid.server.txn.RollbackOnlyDtxException; import org.apache.qpid.server.txn.ServerTransaction; +import org.apache.qpid.server.txn.SuspendAndFailDtxException; +import org.apache.qpid.server.txn.TimeoutDtxException; +import org.apache.qpid.server.txn.UnknownDtxBranchException; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.transport.*; @@ -116,7 +128,7 @@ public class ServerSessionDelegate extends SessionDelegate serverSession.accept(method.getTransfers()); if(!serverSession.isTransactional()) { - serverSession.recordFuture(MessageStore.IMMEDIATE_FUTURE, + serverSession.recordFuture(StoreFuture.IMMEDIATE_FUTURE, new CommandProcessedAction(serverSession, method)); } } @@ -199,6 +211,10 @@ public class ServerSessionDelegate extends SessionDelegate { exception(session,method,ExecutionErrorCode.RESOURCE_LOCKED, "Exclusive Queue: " + queueName + " owned exclusively by another session"); } + else if(queue.isExclusive() && queue.getExclusiveOwningSession() != null && queue.getExclusiveOwningSession() != session) + { + exception(session,method,ExecutionErrorCode.RESOURCE_LOCKED, "Exclusive Queue: " + queueName + " owned exclusively by another session"); + } else { if(queue.isExclusive()) @@ -221,7 +237,6 @@ public class ServerSessionDelegate extends SessionDelegate } }); } - } FlowCreditManager_0_10 creditManager = new WindowCreditManager(0L,0L); @@ -281,6 +296,7 @@ public class ServerSessionDelegate extends SessionDelegate } final MessageMetaData_0_10 messageMetaData = new MessageMetaData_0_10(xfr); + messageMetaData.setConnectionReference(((ServerSession)ssn).getReference()); if (!getVirtualHost(ssn).getSecurityManager().authorisePublish(messageMetaData.isImmediate(), messageMetaData.getRoutingKey(), exchange.getName())) { @@ -342,7 +358,7 @@ public class ServerSessionDelegate extends SessionDelegate } else { - serverSession.recordFuture(MessageStore.IMMEDIATE_FUTURE, new CommandProcessedAction(serverSession, xfr)); + serverSession.recordFuture(StoreFuture.IMMEDIATE_FUTURE, new CommandProcessedAction(serverSession, xfr)); } } @@ -426,6 +442,235 @@ public class ServerSessionDelegate extends SessionDelegate ((ServerSession)session).rollback(); } + @Override + public void dtxSelect(Session session, DtxSelect method) + { + // TODO - check current tx mode + ((ServerSession)session).selectDtx(); + } + + @Override + public void dtxStart(Session session, DtxStart method) + { + XaResult result = new XaResult(); + result.setStatus(DtxXaStatus.XA_OK); + try + { + ((ServerSession)session).startDtx(method.getXid(), method.getJoin(), method.getResume()); + session.executionResult(method.getId(), result); + } + catch(JoinAndResumeDtxException e) + { + exception(session, method, ExecutionErrorCode.COMMAND_INVALID, e.getMessage()); + } + catch(UnknownDtxBranchException e) + { + exception(session, method, ExecutionErrorCode.NOT_ALLOWED, "Unknown xid " + method.getXid()); + } + catch(AlreadyKnownDtxException e) + { + exception(session, method, ExecutionErrorCode.NOT_ALLOWED, "Xid already started an neither join nor " + + "resume set" + method.getXid()); + } + catch(DtxNotSelectedException e) + { + exception(session, method, ExecutionErrorCode.COMMAND_INVALID, e.getMessage()); + } + + } + + @Override + public void dtxEnd(Session session, DtxEnd method) + { + XaResult result = new XaResult(); + result.setStatus(DtxXaStatus.XA_OK); + try + { + try + { + ((ServerSession)session).endDtx(method.getXid(), method.getFail(), method.getSuspend()); + } + catch (TimeoutDtxException e) + { + result.setStatus(DtxXaStatus.XA_RBTIMEOUT); + } + session.executionResult(method.getId(), result); + } + catch(UnknownDtxBranchException e) + { + exception(session, method, ExecutionErrorCode.ILLEGAL_STATE, e.getMessage()); + } + catch(NotAssociatedDtxException e) + { + exception(session, method, ExecutionErrorCode.ILLEGAL_STATE, e.getMessage()); + } + catch(DtxNotSelectedException e) + { + exception(session, method, ExecutionErrorCode.ILLEGAL_STATE, e.getMessage()); + } + catch(SuspendAndFailDtxException e) + { + exception(session, method, ExecutionErrorCode.COMMAND_INVALID, e.getMessage()); + } + + } + + @Override + public void dtxCommit(Session session, DtxCommit method) + { + XaResult result = new XaResult(); + result.setStatus(DtxXaStatus.XA_OK); + try + { + try + { + ((ServerSession)session).commitDtx(method.getXid(), method.getOnePhase()); + } + catch (RollbackOnlyDtxException e) + { + result.setStatus(DtxXaStatus.XA_RBROLLBACK); + } + catch (TimeoutDtxException e) + { + result.setStatus(DtxXaStatus.XA_RBTIMEOUT); + } + session.executionResult(method.getId(), result); + } + catch(UnknownDtxBranchException e) + { + exception(session, method, ExecutionErrorCode.NOT_FOUND, e.getMessage()); + } + catch(IncorrectDtxStateException e) + { + exception(session, method, ExecutionErrorCode.ILLEGAL_STATE, e.getMessage()); + } + catch(AMQStoreException e) + { + exception(session, method, ExecutionErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public void dtxForget(Session session, DtxForget method) + { + try + { + ((ServerSession)session).forgetDtx(method.getXid()); + } + catch(UnknownDtxBranchException e) + { + exception(session, method, ExecutionErrorCode.NOT_FOUND, e.getMessage()); + } + catch(IncorrectDtxStateException e) + { + exception(session, method, ExecutionErrorCode.ILLEGAL_STATE, e.getMessage()); + } + + } + + @Override + public void dtxGetTimeout(Session session, DtxGetTimeout method) + { + GetTimeoutResult result = new GetTimeoutResult(); + try + { + result.setTimeout(((ServerSession) session).getTimeoutDtx(method.getXid())); + session.executionResult(method.getId(), result); + } + catch(UnknownDtxBranchException e) + { + exception(session, method, ExecutionErrorCode.NOT_FOUND, e.getMessage()); + } + } + + @Override + public void dtxPrepare(Session session, DtxPrepare method) + { + XaResult result = new XaResult(); + result.setStatus(DtxXaStatus.XA_OK); + try + { + try + { + ((ServerSession)session).prepareDtx(method.getXid()); + } + catch (RollbackOnlyDtxException e) + { + result.setStatus(DtxXaStatus.XA_RBROLLBACK); + } + catch (TimeoutDtxException e) + { + result.setStatus(DtxXaStatus.XA_RBTIMEOUT); + } + session.executionResult((int) method.getId(), result); + } + catch(UnknownDtxBranchException e) + { + exception(session, method, ExecutionErrorCode.NOT_FOUND, e.getMessage()); + } + catch(IncorrectDtxStateException e) + { + exception(session, method, ExecutionErrorCode.ILLEGAL_STATE, e.getMessage()); + } + catch(AMQStoreException e) + { + exception(session, method, ExecutionErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public void dtxRecover(Session session, DtxRecover method) + { + RecoverResult result = new RecoverResult(); + List inDoubt = ((ServerSession)session).recoverDtx(); + result.setInDoubt(inDoubt); + session.executionResult(method.getId(), result); + } + + @Override + public void dtxRollback(Session session, DtxRollback method) + { + + XaResult result = new XaResult(); + result.setStatus(DtxXaStatus.XA_OK); + try + { + try + { + ((ServerSession)session).rollbackDtx(method.getXid()); + } + catch (TimeoutDtxException e) + { + result.setStatus(DtxXaStatus.XA_RBTIMEOUT); + } + session.executionResult(method.getId(), result); + } + catch(UnknownDtxBranchException e) + { + exception(session, method, ExecutionErrorCode.NOT_FOUND, e.getMessage()); + } + catch(IncorrectDtxStateException e) + { + exception(session, method, ExecutionErrorCode.ILLEGAL_STATE, e.getMessage()); + } + catch(AMQStoreException e) + { + exception(session, method, ExecutionErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public void dtxSetTimeout(Session session, DtxSetTimeout method) + { + try + { + ((ServerSession)session).setTimeoutDtx(method.getXid(), method.getTimeout()); + } + catch(UnknownDtxBranchException e) + { + exception(session, method, ExecutionErrorCode.NOT_FOUND, e.getMessage()); + } + } @Override public void executionSync(final Session ssn, final ExecutionSync sync) @@ -439,13 +684,12 @@ public class ServerSessionDelegate extends SessionDelegate { String exchangeName = method.getExchange(); VirtualHost virtualHost = getVirtualHost(session); - Exchange exchange = getExchange(session, exchangeName); + ExchangeRegistry exchangeRegistry = getExchangeRegistry(session); //we must check for any unsupported arguments present and throw not-implemented if(method.hasArguments()) { Map<String,Object> args = method.getArguments(); - //QPID-3392: currently we don't support any! if(!args.isEmpty()) { @@ -453,72 +697,113 @@ public class ServerSessionDelegate extends SessionDelegate return; } } - - if(method.getPassive()) + synchronized(exchangeRegistry) { - if(exchange == null) - { - exception(session, method, ExecutionErrorCode.NOT_FOUND, "not-found: exchange-name '"+exchangeName+"'"); + Exchange exchange = getExchange(session, exchangeName); - } - else + if(method.getPassive()) { - if(!exchange.getTypeShortString().toString().equals(method.getType()) && (method.getType() != null && method.getType().length() > 0)) + if(exchange == null) + { + exception(session, method, ExecutionErrorCode.NOT_FOUND, "not-found: exchange-name '" + exchangeName + "'"); + } + else { - exception(session, method, ExecutionErrorCode.NOT_ALLOWED, "Attempt to redeclare exchange: " + exchangeName + " of type " + exchange.getTypeShortString() + " to " + method.getType() +"."); + if (!exchange.getTypeShortString().toString().equals(method.getType()) + && (method.getType() != null && method.getType().length() > 0)) + { + exception(session, method, ExecutionErrorCode.NOT_ALLOWED, "Attempt to redeclare exchange: " + + exchangeName + " of type " + exchange.getTypeShortString() + " to " + method.getType() + "."); + } } } - - } - else - { - if (exchange == null) + else { - ExchangeRegistry exchangeRegistry = getExchangeRegistry(session); - ExchangeFactory exchangeFactory = virtualHost.getExchangeFactory(); - - - - try + if (exchange == null) { - - exchange = exchangeFactory.createExchange(method.getExchange(), - method.getType(), - method.getDurable(), - method.getAutoDelete()); - - String alternateExchangeName = method.getAlternateExchange(); - if(alternateExchangeName != null && alternateExchangeName.length() != 0) + if (exchangeName.startsWith("amq.")) { - Exchange alternate = getExchange(session, alternateExchangeName); - exchange.setAlternateExchange(alternate); + exception(session, method, ExecutionErrorCode.NOT_ALLOWED, "Attempt to declare exchange: " + + exchangeName + " which begins with reserved prefix 'amq.'."); } - - if (exchange.isDurable()) + else if (exchangeName.startsWith("qpid.")) { - DurableConfigurationStore store = virtualHost.getDurableConfigurationStore(); - store.createExchange(exchange); + exception(session, method, ExecutionErrorCode.NOT_ALLOWED, "Attempt to declare exchange: " + + exchangeName + " which begins with reserved prefix 'qpid.'."); + } + else + { + ExchangeFactory exchangeFactory = virtualHost.getExchangeFactory(); + try + { + exchange = exchangeFactory.createExchange(method.getExchange(), + method.getType(), + method.getDurable(), + method.getAutoDelete()); + String alternateExchangeName = method.getAlternateExchange(); + boolean validAlternate; + if(alternateExchangeName != null && alternateExchangeName.length() != 0) + { + Exchange alternate = getExchange(session, alternateExchangeName); + if(alternate == null) + { + validAlternate = false; + } + else + { + exchange.setAlternateExchange(alternate); + validAlternate = true; + } + } + else + { + validAlternate = true; + } + if(validAlternate) + { + if (exchange.isDurable()) + { + DurableConfigurationStore store = virtualHost.getMessageStore(); + store.createExchange(exchange); + } + exchangeRegistry.registerExchange(exchange); + } + else + { + exception(session, method, ExecutionErrorCode.NOT_FOUND, + "Unknown alternate exchange " + alternateExchangeName); + } + } + catch(AMQUnknownExchangeType e) + { + exception(session, method, ExecutionErrorCode.NOT_FOUND, "Unknown Exchange Type: " + method.getType()); + } + catch (AMQException e) + { + exception(session, method, e, "Cannot declare exchange '" + exchangeName); + } } - - exchangeRegistry.registerExchange(exchange); - } - catch(AMQUnknownExchangeType e) - { - exception(session, method, ExecutionErrorCode.NOT_FOUND, "Unknown Exchange Type: " + method.getType()); - } - catch (AMQException e) - { - exception(session, method, e, "Cannot declare exchange '" + exchangeName); } - } - else - { - if(!exchange.getTypeShortString().toString().equals(method.getType())) + else { - exception(session, method, ExecutionErrorCode.NOT_ALLOWED, "Attempt to redeclare exchange: " + exchangeName + " of type " + exchange.getTypeShortString() + " to " + method.getType() +"."); + if(!exchange.getTypeShortString().toString().equals(method.getType())) + { + exception(session, method, ExecutionErrorCode.NOT_ALLOWED, + "Attempt to redeclare exchange: " + exchangeName + + " of type " + exchange.getTypeShortString() + + " to " + method.getType() +"."); + } + else if(method.hasAlternateExchange() + && (exchange.getAlternateExchange() == null || + !method.getAlternateExchange().equals(exchange.getAlternateExchange().getName()))) + { + exception(session, method, ExecutionErrorCode.NOT_ALLOWED, + "Attempt to change alternate exchange of: " + exchangeName + + " from " + exchange.getAlternateExchange() + + " to " + method.getAlternateExchange() +"."); + } } } - } } @@ -633,7 +918,7 @@ public class ServerSessionDelegate extends SessionDelegate if (exchange.isDurable() && !exchange.isAutoDelete()) { - DurableConfigurationStore store = virtualHost.getDurableConfigurationStore(); + DurableConfigurationStore store = virtualHost.getMessageStore(); store.removeExchange(exchange); } } @@ -732,10 +1017,7 @@ public class ServerSessionDelegate extends SessionDelegate } else { - AMQShortString routingKey = new AMQShortString(method.getBindingKey()); - FieldTable fieldTable = FieldTable.convertToFieldTable(method.getArguments()); - - if (!exchange.isBound(routingKey, fieldTable, queue)) + if (!exchange.isBound(method.getBindingKey(), method.getArguments(), queue)) { try { @@ -847,12 +1129,6 @@ public class ServerSessionDelegate extends SessionDelegate if(method.hasBindingKey()) { - if(method.hasArguments()) - { - FieldTable args = FieldTable.convertToFieldTable(method.getArguments()); - - result.setArgsNotMatched(!exchange.isBound(new AMQShortString(method.getBindingKey()), args, queue)); - } if(queueMatched) { result.setKeyNotMatched(!exchange.isBound(method.getBindingKey(), queue)); @@ -861,23 +1137,28 @@ public class ServerSessionDelegate extends SessionDelegate { result.setKeyNotMatched(!exchange.isBound(method.getBindingKey())); } + + if(method.hasArguments()) + { + result.setArgsNotMatched(!exchange.isBound(result.getKeyNotMatched() ? null : method.getBindingKey(), method.getArguments(), queueMatched ? queue : null)); + } + } else if (method.hasArguments()) { - // TODO - + result.setArgsNotMatched(!exchange.isBound(null, method.getArguments(), queueMatched ? queue : null)); } - result.setQueueNotMatched(!exchange.isBound(queue)); - } else if(exchange != null && method.hasBindingKey()) { + result.setKeyNotMatched(!exchange.isBound(method.getBindingKey())); + if(method.hasArguments()) { - // TODO + result.setArgsNotMatched(!exchange.isBound(result.getKeyNotMatched() ? null : method.getBindingKey(), method.getArguments(), queue)); } - result.setKeyNotMatched(!exchange.isBound(method.getBindingKey())); + } @@ -886,11 +1167,15 @@ public class ServerSessionDelegate extends SessionDelegate { if(method.hasArguments()) { - // TODO + result.setArgsNotMatched(!exchange.isBound(method.getBindingKey(), method.getArguments(), null)); } result.setKeyNotMatched(!exchange.isBound(method.getBindingKey())); } + else if(exchange != null && method.hasArguments()) + { + result.setArgsNotMatched(!exchange.isBound(null, method.getArguments(), null)); + } session.executionResult((int) method.getId(), result); @@ -914,7 +1199,7 @@ public class ServerSessionDelegate extends SessionDelegate { VirtualHost virtualHost = getVirtualHost(session); - DurableConfigurationStore store = virtualHost.getDurableConfigurationStore(); + DurableConfigurationStore store = virtualHost.getMessageStore(); String queueName = method.getQueue(); AMQQueue queue; @@ -1104,8 +1389,8 @@ public class ServerSessionDelegate extends SessionDelegate { String owner = body.getExclusive() ? session.getClientID() : null; - final AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(queueName, body.getDurable(), owner, body.getAutoDelete(), - body.getExclusive(), virtualHost, body.getArguments()); + final AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(UUIDGenerator.generateUUID(), queueName, body.getDurable(), owner, + body.getAutoDelete(), body.getExclusive(), virtualHost, body.getArguments()); return queue; } @@ -1134,6 +1419,10 @@ public class ServerSessionDelegate extends SessionDelegate { exception(session,method,ExecutionErrorCode.RESOURCE_LOCKED, "Exclusive Queue: " + queueName + " owned exclusively by another session"); } + else if(queue.isExclusive() && queue.getExclusiveOwningSession() != null && queue.getExclusiveOwningSession() != session) + { + exception(session,method,ExecutionErrorCode.RESOURCE_LOCKED, "Exclusive Queue: " + queueName + " owned exclusively by another session"); + } else if (method.getIfEmpty() && !queue.isEmpty()) { exception(session, method, ExecutionErrorCode.PRECONDITION_FAILED, "Queue " + queueName + " not empty"); @@ -1153,7 +1442,7 @@ public class ServerSessionDelegate extends SessionDelegate queue.delete(); if (queue.isDurable() && !queue.isAutoDelete()) { - DurableConfigurationStore store = virtualHost.getDurableConfigurationStore(); + DurableConfigurationStore store = virtualHost.getMessageStore(); store.removeQueue(queue); } } @@ -1280,8 +1569,9 @@ public class ServerSessionDelegate extends SessionDelegate ServerSession serverSession = (ServerSession)session; - serverSession.unregisterSubscriptions(); + serverSession.stopSubscriptions(); serverSession.onClose(); + serverSession.unregisterSubscriptions(); } @Override diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/AlreadyKnownDtxException.java b/java/broker/src/main/java/org/apache/qpid/server/txn/AlreadyKnownDtxException.java new file mode 100644 index 0000000000..faa4ec592f --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/AlreadyKnownDtxException.java @@ -0,0 +1,32 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.server.txn; + +import org.apache.qpid.transport.Xid; + +public class AlreadyKnownDtxException extends DtxException +{ + public AlreadyKnownDtxException(Xid id) + { + super("Xid " + id + " cannot be started as it is already known"); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/AsyncAutoCommitTransaction.java b/java/broker/src/main/java/org/apache/qpid/server/txn/AsyncAutoCommitTransaction.java index a062c6732f..d446434d24 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/txn/AsyncAutoCommitTransaction.java +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/AsyncAutoCommitTransaction.java @@ -29,7 +29,8 @@ import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.queue.BaseQueue; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.store.MessageStore; -import org.apache.qpid.server.store.MessageStore.StoreFuture; +import org.apache.qpid.server.store.StoreFuture; +import org.apache.qpid.server.store.Transaction; import java.util.Collection; import java.util.List; @@ -71,16 +72,16 @@ public class AsyncAutoCommitTransaction implements ServerTransaction */ public void addPostTransactionAction(final Action immediateAction) { - addFuture(MessageStore.IMMEDIATE_FUTURE, immediateAction); + addFuture(StoreFuture.IMMEDIATE_FUTURE, immediateAction); } public void dequeue(BaseQueue queue, EnqueableMessage message, Action postTransactionAction) { - MessageStore.Transaction txn = null; + Transaction txn = null; try { - MessageStore.StoreFuture future; + StoreFuture future; if(message.isPersistent() && queue.isDurable()) { if (_logger.isDebugEnabled()) @@ -96,7 +97,7 @@ public class AsyncAutoCommitTransaction implements ServerTransaction } else { - future = MessageStore.IMMEDIATE_FUTURE; + future = StoreFuture.IMMEDIATE_FUTURE; } addFuture(future, postTransactionAction); postTransactionAction = null; @@ -113,7 +114,7 @@ public class AsyncAutoCommitTransaction implements ServerTransaction } - private void addFuture(final MessageStore.StoreFuture future, final Action action) + private void addFuture(final StoreFuture future, final Action action) { if(action != null) { @@ -130,7 +131,7 @@ public class AsyncAutoCommitTransaction implements ServerTransaction public void dequeue(Collection<QueueEntry> queueEntries, Action postTransactionAction) { - MessageStore.Transaction txn = null; + Transaction txn = null; try { for(QueueEntry entry : queueEntries) @@ -154,7 +155,7 @@ public class AsyncAutoCommitTransaction implements ServerTransaction } } - MessageStore.StoreFuture future; + StoreFuture future; if(txn != null) { future = txn.commitTranAsync(); @@ -162,7 +163,7 @@ public class AsyncAutoCommitTransaction implements ServerTransaction } else { - future = MessageStore.IMMEDIATE_FUTURE; + future = StoreFuture.IMMEDIATE_FUTURE; } addFuture(future, postTransactionAction); postTransactionAction = null; @@ -182,10 +183,10 @@ public class AsyncAutoCommitTransaction implements ServerTransaction public void enqueue(BaseQueue queue, EnqueableMessage message, Action postTransactionAction) { - MessageStore.Transaction txn = null; + Transaction txn = null; try { - MessageStore.StoreFuture future; + StoreFuture future; if(message.isPersistent() && queue.isDurable()) { if (_logger.isDebugEnabled()) @@ -200,7 +201,7 @@ public class AsyncAutoCommitTransaction implements ServerTransaction } else { - future = MessageStore.IMMEDIATE_FUTURE; + future = StoreFuture.IMMEDIATE_FUTURE; } addFuture(future, postTransactionAction); postTransactionAction = null; @@ -220,7 +221,7 @@ public class AsyncAutoCommitTransaction implements ServerTransaction public void enqueue(List<? extends BaseQueue> queues, EnqueableMessage message, Action postTransactionAction, long currentTime) { - MessageStore.Transaction txn = null; + Transaction txn = null; try { @@ -246,7 +247,7 @@ public class AsyncAutoCommitTransaction implements ServerTransaction } } - MessageStore.StoreFuture future; + StoreFuture future; if (txn != null) { future = txn.commitTranAsync(); @@ -254,7 +255,7 @@ public class AsyncAutoCommitTransaction implements ServerTransaction } else { - future = MessageStore.IMMEDIATE_FUTURE; + future = StoreFuture.IMMEDIATE_FUTURE; } addFuture(future, postTransactionAction); postTransactionAction = null; @@ -278,7 +279,7 @@ public class AsyncAutoCommitTransaction implements ServerTransaction { if(immediatePostTransactionAction != null) { - addFuture(MessageStore.IMMEDIATE_FUTURE, new Action() + addFuture(StoreFuture.IMMEDIATE_FUTURE, new Action() { public void postCommit() { @@ -305,7 +306,7 @@ public class AsyncAutoCommitTransaction implements ServerTransaction return false; } - private void rollbackIfNecessary(Action postTransactionAction, MessageStore.Transaction txn) + private void rollbackIfNecessary(Action postTransactionAction, Transaction txn) { if (txn != null) { diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/AutoCommitTransaction.java b/java/broker/src/main/java/org/apache/qpid/server/txn/AutoCommitTransaction.java index 597797b5f8..e5a7df6880 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/txn/AutoCommitTransaction.java +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/AutoCommitTransaction.java @@ -29,6 +29,7 @@ import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.queue.BaseQueue; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.Transaction; import java.util.Collection; import java.util.List; @@ -67,7 +68,7 @@ public class AutoCommitTransaction implements ServerTransaction public void dequeue(BaseQueue queue, EnqueableMessage message, Action postTransactionAction) { - MessageStore.Transaction txn = null; + Transaction txn = null; try { if(message.isPersistent() && queue.isDurable()) @@ -99,7 +100,7 @@ public class AutoCommitTransaction implements ServerTransaction public void dequeue(Collection<QueueEntry> queueEntries, Action postTransactionAction) { - MessageStore.Transaction txn = null; + Transaction txn = null; try { for(QueueEntry entry : queueEntries) @@ -146,7 +147,7 @@ public class AutoCommitTransaction implements ServerTransaction public void enqueue(BaseQueue queue, EnqueableMessage message, Action postTransactionAction) { - MessageStore.Transaction txn = null; + Transaction txn = null; try { if(message.isPersistent() && queue.isDurable()) @@ -179,7 +180,7 @@ public class AutoCommitTransaction implements ServerTransaction public void enqueue(List<? extends BaseQueue> queues, EnqueableMessage message, Action postTransactionAction, long currentTime) { - MessageStore.Transaction txn = null; + Transaction txn = null; try { @@ -247,7 +248,7 @@ public class AutoCommitTransaction implements ServerTransaction return false; } - private void rollbackIfNecessary(Action postTransactionAction, MessageStore.Transaction txn) + private void rollbackIfNecessary(Action postTransactionAction, Transaction txn) { if (txn != null) { diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/DistributedTransaction.java b/java/broker/src/main/java/org/apache/qpid/server/txn/DistributedTransaction.java new file mode 100644 index 0000000000..05d0110e9b --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/DistributedTransaction.java @@ -0,0 +1,247 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.server.txn; + +import org.apache.qpid.server.message.EnqueableMessage; +import org.apache.qpid.server.protocol.AMQSessionModel; +import org.apache.qpid.server.queue.BaseQueue; +import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.Transaction; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.transport.Xid; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public class DistributedTransaction implements ServerTransaction +{ + + private final AutoCommitTransaction _autoCommitTransaction; + + private volatile Transaction _transaction; + + private long _txnStartTime = 0L; + + private DtxBranch _branch; + private AMQSessionModel _session; + private VirtualHost _vhost; + + + public DistributedTransaction(AMQSessionModel session, MessageStore store, VirtualHost vhost) + { + _session = session; + _vhost = vhost; + _autoCommitTransaction = new AutoCommitTransaction(vhost.getMessageStore()); + } + + public long getTransactionStartTime() + { + return _txnStartTime; + } + + public void addPostTransactionAction(Action postTransactionAction) + { + if(_branch != null) + { + _branch.addPostTransactionAcion(postTransactionAction); + } + else + { + _autoCommitTransaction.addPostTransactionAction(postTransactionAction); + } + } + + public void dequeue(BaseQueue queue, EnqueableMessage message, Action postTransactionAction) + { + if(_branch != null) + { + _branch.dequeue(queue, message); + _branch.addPostTransactionAcion(postTransactionAction); + } + else + { + _autoCommitTransaction.dequeue(queue, message, postTransactionAction); + } + } + + public void dequeue(Collection<QueueEntry> messages, Action postTransactionAction) + { + if(_branch != null) + { + for(QueueEntry entry : messages) + { + _branch.dequeue(entry.getQueue(), entry.getMessage()); + } + _branch.addPostTransactionAcion(postTransactionAction); + } + else + { + _autoCommitTransaction.dequeue(messages, postTransactionAction); + } + } + + public void enqueue(BaseQueue queue, EnqueableMessage message, Action postTransactionAction) + { + if(_branch != null) + { + _branch.enqueue(queue, message); + _branch.addPostTransactionAcion(postTransactionAction); + enqueue(Collections.singletonList(queue), message, postTransactionAction, System.currentTimeMillis()); + } + else + { + _autoCommitTransaction.enqueue(queue, message, postTransactionAction); + } + } + + public void enqueue(List<? extends BaseQueue> queues, EnqueableMessage message, + Action postTransactionAction, long currentTime) + { + if(_branch != null) + { + for(BaseQueue queue : queues) + { + _branch.enqueue(queue, message); + } + _branch.addPostTransactionAcion(postTransactionAction); + } + else + { + _autoCommitTransaction.enqueue(queues, message, postTransactionAction, currentTime); + } + } + + public void commit() + { + throw new IllegalStateException("Cannot call tx.commit() on a distributed transaction"); + } + + public void commit(Runnable immediatePostTransactionAction) + { + throw new IllegalStateException("Cannot call tx.commit() on a distributed transaction"); + } + + public void rollback() + { + throw new IllegalStateException("Cannot call tx.rollback() on a distributed transaction"); + } + + public boolean isTransactional() + { + return _branch != null; + } + + public void start(Xid id, boolean join, boolean resume) + throws UnknownDtxBranchException, AlreadyKnownDtxException, JoinAndResumeDtxException + { + if(join && resume) + { + throw new JoinAndResumeDtxException(id); + } + + DtxBranch branch = _vhost.getDtxRegistry().getBranch(id); + + if(branch == null) + { + if(join || resume) + { + throw new UnknownDtxBranchException(id); + } + branch = new DtxBranch(id,_vhost.getMessageStore(), _vhost); + if(_vhost.getDtxRegistry().registerBranch(branch)) + { + _branch = branch; + branch.associateSession(_session); + } + else + { + throw new AlreadyKnownDtxException(id); + } + } + else + { + if(join) + { + branch.associateSession(_session); + } + else if(resume) + { + branch.resumeSession(_session); + } + else + { + throw new AlreadyKnownDtxException(id); + } + _branch = branch; + } + } + + public void end(Xid id, boolean fail, boolean suspend) + throws UnknownDtxBranchException, NotAssociatedDtxException, SuspendAndFailDtxException, TimeoutDtxException + { + DtxBranch branch = _vhost.getDtxRegistry().getBranch(id); + + if(suspend && fail) + { + branch.disassociateSession(_session); + _branch = null; + throw new SuspendAndFailDtxException(id); + } + + + if(branch == null) + { + throw new UnknownDtxBranchException(id); + } + else + { + if(!branch.isAssociated(_session)) + { + throw new NotAssociatedDtxException(id); + } + if(branch.expired() || branch.getState() == DtxBranch.State.TIMEDOUT) + { + branch.disassociateSession(_session); + throw new TimeoutDtxException(id); + } + + if(suspend) + { + branch.suspendSession(_session); + } + else + { + if(fail) + { + branch.setState(DtxBranch.State.ROLLBACK_ONLY); + } + branch.disassociateSession(_session); + } + + _branch = null; + + } + } + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/DtxBranch.java b/java/broker/src/main/java/org/apache/qpid/server/txn/DtxBranch.java new file mode 100644 index 0000000000..3ac71fc6a6 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/DtxBranch.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.txn; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ScheduledFuture; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQStoreException; +import org.apache.qpid.server.message.EnqueableMessage; +import org.apache.qpid.server.protocol.AMQSessionModel; +import org.apache.qpid.server.queue.BaseQueue; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.Transaction; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.transport.Xid; + +public class DtxBranch +{ + private static final Logger _logger = Logger.getLogger(DtxBranch.class); + + private final Xid _xid; + private final List<ServerTransaction.Action> _postTransactionActions = new ArrayList<ServerTransaction.Action>(); + private State _state = State.ACTIVE; + private long _timeout; + private Map<AMQSessionModel, State> _associatedSessions = new HashMap<AMQSessionModel, State>(); + private final List<Record> _enqueueRecords = new ArrayList<Record>(); + private final List<Record> _dequeueRecords = new ArrayList<Record>(); + + private Transaction _transaction; + private long _expiration; + private VirtualHost _vhost; + private ScheduledFuture<?> _timeoutFuture; + private MessageStore _store; + + + public enum State + { + ACTIVE, + PREPARED, + TIMEDOUT, + SUSPENDED, + FORGOTTEN, + HEUR_COM, + HEUR_RB, + ROLLBACK_ONLY + } + + + public DtxBranch(Xid xid, MessageStore store, VirtualHost vhost) + { + _xid = xid; + _store = store; + _vhost = vhost; + } + + public Xid getXid() + { + return _xid; + } + + public State getState() + { + return _state; + } + + public void setState(State state) + { + _state = state; + } + + public long getTimeout() + { + return _timeout; + } + + public void setTimeout(long timeout) + { + if(_timeoutFuture != null) + { + _timeoutFuture.cancel(false); + } + _timeout = timeout; + _expiration = timeout == 0 ? 0 : System.currentTimeMillis() + timeout; + + if(_timeout == 0) + { + _timeoutFuture = null; + } + else + { + _timeoutFuture = _vhost.scheduleTask(_timeout, new Runnable() + { + public void run() + { + setState(State.TIMEDOUT); + try + { + rollback(); + } + catch (AMQStoreException e) + { + _logger.error("Unexpected error when attempting to rollback XA transaction ("+ + _xid + ") due to timeout", e); + throw new RuntimeException(e); + } + } + }); + } + } + + public boolean expired() + { + return _timeout != 0 && _expiration < System.currentTimeMillis(); + } + + public synchronized boolean isAssociated(AMQSessionModel session) + { + return _associatedSessions.containsKey(session); + } + + public synchronized boolean hasAssociatedSessions() + { + return !_associatedSessions.isEmpty(); + } + + + public synchronized boolean hasAssociatedActiveSessions() + { + if(hasAssociatedSessions()) + { + for(State state : _associatedSessions.values()) + { + if(state != State.SUSPENDED) + { + return true; + } + } + } + return false; + } + + public synchronized void clearAssociations() + { + _associatedSessions.clear(); + } + + synchronized boolean associateSession(AMQSessionModel associatedSession) + { + return _associatedSessions.put(associatedSession, State.ACTIVE) != null; + } + + synchronized boolean disassociateSession(AMQSessionModel associatedSession) + { + return _associatedSessions.remove(associatedSession) != null; + } + + public synchronized boolean resumeSession(AMQSessionModel session) + { + if(_associatedSessions.containsKey(session) && _associatedSessions.get(session) == State.SUSPENDED) + { + _associatedSessions.put(session, State.ACTIVE); + return true; + } + return false; + } + + public synchronized boolean suspendSession(AMQSessionModel session) + { + if(_associatedSessions.containsKey(session) && _associatedSessions.get(session) == State.ACTIVE) + { + _associatedSessions.put(session, State.SUSPENDED); + return true; + } + return false; + } + + public void prepare() throws AMQStoreException + { + + Transaction txn = _store.newTransaction(); + txn.recordXid(_xid.getFormat(), + _xid.getGlobalId(), + _xid.getBranchId(), + _enqueueRecords.toArray(new Record[_enqueueRecords.size()]), + _dequeueRecords.toArray(new Record[_dequeueRecords.size()])); + txn.commitTran(); + + prePrepareTransaction(); + } + + public synchronized void rollback() throws AMQStoreException + { + if(_timeoutFuture != null) + { + _timeoutFuture.cancel(false); + _timeoutFuture = null; + } + + + if(_transaction != null) + { + // prepare has previously been called + + Transaction txn = _store.newTransaction(); + txn.removeXid(_xid.getFormat(), _xid.getGlobalId(), _xid.getBranchId()); + txn.commitTran(); + + _transaction.abortTran(); + } + + for(ServerTransaction.Action action : _postTransactionActions) + { + action.onRollback(); + } + _postTransactionActions.clear(); + } + + public void commit() throws AMQStoreException + { + if(_timeoutFuture != null) + { + _timeoutFuture.cancel(false); + _timeoutFuture = null; + } + + if(_transaction == null) + { + prePrepareTransaction(); + } + else + { + _transaction.removeXid(_xid.getFormat(), _xid.getGlobalId(), _xid.getBranchId()); + } + _transaction.commitTran(); + + for(ServerTransaction.Action action : _postTransactionActions) + { + action.postCommit(); + } + _postTransactionActions.clear(); + } + + public void prePrepareTransaction() throws AMQStoreException + { + _transaction = _store.newTransaction(); + + for(Record enqueue : _enqueueRecords) + { + if(enqueue.isDurable()) + { + _transaction.enqueueMessage(enqueue.getQueue(), enqueue.getMessage()); + } + } + + + for(Record enqueue : _dequeueRecords) + { + if(enqueue.isDurable()) + { + _transaction.dequeueMessage(enqueue.getQueue(), enqueue.getMessage()); + } + } + } + + + public void addPostTransactionAcion(ServerTransaction.Action postTransactionAction) + { + _postTransactionActions.add(postTransactionAction); + } + + + public void dequeue(BaseQueue queue, EnqueableMessage message) + { + _dequeueRecords.add(new Record(queue, message)); + } + + + public void enqueue(BaseQueue queue, EnqueableMessage message) + { + _enqueueRecords.add(new Record(queue, message)); + } + + private static final class Record implements Transaction.Record + { + private final BaseQueue _queue; + private final EnqueableMessage _message; + + public Record(BaseQueue queue, EnqueableMessage message) + { + _queue = queue; + _message = message; + } + + public BaseQueue getQueue() + { + return _queue; + } + + public EnqueableMessage getMessage() + { + return _message; + } + + public boolean isDurable() + { + return _message.isPersistent() && _queue.isDurable(); + } + } + + + public void close() + { + if(_transaction != null) + { + try + { + _state = null; + _transaction.abortTran(); + } + catch(AMQStoreException e) + { + _logger.error("Error while closing XA branch", e); + } + } + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/DtxException.java b/java/broker/src/main/java/org/apache/qpid/server/txn/DtxException.java new file mode 100644 index 0000000000..d18d0cb68b --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/DtxException.java @@ -0,0 +1,44 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.server.txn; + +public class DtxException extends Exception +{ + public DtxException() + { + } + + public DtxException(String message) + { + super(message); + } + + public DtxException(String message, Throwable cause) + { + super(message, cause); + } + + public DtxException(Throwable cause) + { + super(cause); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/DtxNotSelectedException.java b/java/broker/src/main/java/org/apache/qpid/server/txn/DtxNotSelectedException.java new file mode 100644 index 0000000000..c1289b1fdd --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/DtxNotSelectedException.java @@ -0,0 +1,30 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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; + +public class DtxNotSelectedException extends DtxException +{ + public DtxNotSelectedException() + { + super("Distribution transactions have not been selected on this session"); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/DtxRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/txn/DtxRegistry.java new file mode 100644 index 0000000000..5c54c1164f --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/DtxRegistry.java @@ -0,0 +1,333 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.server.txn; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.qpid.AMQStoreException; +import org.apache.qpid.server.protocol.AMQSessionModel; +import org.apache.qpid.transport.Xid; + +public class DtxRegistry +{ + private final Map<ComparableXid, DtxBranch> _branches = new HashMap<ComparableXid, DtxBranch>(); + + + private static final class ComparableXid + { + private final Xid _xid; + + private ComparableXid(Xid xid) + { + _xid = xid; + } + + @Override + public boolean equals(Object o) + { + if(this == o) + { + return true; + } + if(o == null || getClass() != o.getClass()) + { + return false; + } + + ComparableXid that = (ComparableXid) o; + + return compareBytes(_xid.getBranchId(), that._xid.getBranchId()) + && compareBytes(_xid.getGlobalId(), that._xid.getGlobalId()); + } + + private static boolean compareBytes(byte[] a, byte[] b) + { + if(a.length != b.length) + { + return false; + } + for(int i = 0; i < a.length; i++) + { + if(a[i] != b[i]) + { + return false; + } + } + return true; + } + + + @Override + public int hashCode() + { + int result = 0; + for(int i = 0; i < _xid.getGlobalId().length; i++) + { + result = 31 * result + (int) _xid.getGlobalId()[i]; + } + for(int i = 0; i < _xid.getBranchId().length; i++) + { + result = 31 * result + (int) _xid.getBranchId()[i]; + } + + return result; + } + } + + public synchronized DtxBranch getBranch(Xid xid) + { + return _branches.get(new ComparableXid(xid)); + } + + public synchronized boolean registerBranch(DtxBranch branch) + { + ComparableXid xid = new ComparableXid(branch.getXid()); + if(!_branches.containsKey(xid)) + { + _branches.put(xid, branch); + return true; + } + return false; + } + + synchronized boolean unregisterBranch(DtxBranch branch) + { + return (_branches.remove(new ComparableXid(branch.getXid())) != null); + } + + public void commit(Xid id, boolean onePhase) + throws IncorrectDtxStateException, UnknownDtxBranchException, AMQStoreException, RollbackOnlyDtxException, TimeoutDtxException + { + DtxBranch branch = getBranch(id); + if(branch != null) + { + synchronized (branch) + { + if(!branch.hasAssociatedActiveSessions()) + { + branch.clearAssociations(); + + if(branch.expired() || branch.getState() == DtxBranch.State.TIMEDOUT) + { + unregisterBranch(branch); + throw new TimeoutDtxException(id); + } + else if(branch.getState() == DtxBranch.State.ROLLBACK_ONLY) + { + throw new RollbackOnlyDtxException(id); + } + else if(onePhase && branch.getState() == DtxBranch.State.PREPARED) + { + throw new IncorrectDtxStateException("Cannot call one-phase commit on a prepared branch", id); + } + else if(!onePhase && branch.getState() != DtxBranch.State.PREPARED) + { + throw new IncorrectDtxStateException("Cannot call two-phase commit on a non-prepared branch", + id); + } + branch.commit(); + branch.setState(DtxBranch.State.FORGOTTEN); + unregisterBranch(branch); + } + else + { + throw new IncorrectDtxStateException("Branch was still associated with a session", id); + } + } + } + else + { + throw new UnknownDtxBranchException(id); + } + } + + public synchronized void prepare(Xid id) + throws UnknownDtxBranchException, + IncorrectDtxStateException, AMQStoreException, RollbackOnlyDtxException, TimeoutDtxException + { + DtxBranch branch = getBranch(id); + if(branch != null) + { + synchronized (branch) + { + if(!branch.hasAssociatedActiveSessions()) + { + branch.clearAssociations(); + + if(branch.expired() || branch.getState() == DtxBranch.State.TIMEDOUT) + { + unregisterBranch(branch); + throw new TimeoutDtxException(id); + } + else if(branch.getState() != DtxBranch.State.ACTIVE + && branch.getState() != DtxBranch.State.ROLLBACK_ONLY) + { + throw new IncorrectDtxStateException("Cannot prepare a transaction in state " + + branch.getState(), id); + } + else + { + branch.prepare(); + branch.setState(DtxBranch.State.PREPARED); + } + } + else + { + throw new IncorrectDtxStateException("Branch still has associated sessions", id); + } + } + } + else + { + throw new UnknownDtxBranchException(id); + } + } + + public void rollback(Xid id) + throws IncorrectDtxStateException, + UnknownDtxBranchException, + AMQStoreException, TimeoutDtxException + { + + DtxBranch branch = getBranch(id); + if(branch != null) + { + synchronized (branch) + { + if(branch.expired() || branch.getState() == DtxBranch.State.TIMEDOUT) + { + unregisterBranch(branch); + throw new TimeoutDtxException(id); + } + if(!branch.hasAssociatedActiveSessions()) + { + branch.clearAssociations(); + branch.rollback(); + branch.setState(DtxBranch.State.FORGOTTEN); + unregisterBranch(branch); + } + else + { + throw new IncorrectDtxStateException("Branch was still associated with a session", id); + } + } + } + else + { + throw new UnknownDtxBranchException(id); + } + } + + + public void forget(Xid id) throws UnknownDtxBranchException, IncorrectDtxStateException + { + DtxBranch branch = getBranch(id); + if(branch != null) + { + synchronized (branch) + { + if(!branch.hasAssociatedSessions()) + { + if(branch.getState() != DtxBranch.State.HEUR_COM && branch.getState() != DtxBranch.State.HEUR_RB) + { + throw new IncorrectDtxStateException("Branch should not be forgotten - " + + "it is not heuristically complete", id); + } + branch.setState(DtxBranch.State.FORGOTTEN); + unregisterBranch(branch); + } + else + { + throw new IncorrectDtxStateException("Branch was still associated with a session", id); + } + } + } + else + { + throw new UnknownDtxBranchException(id); + } + } + + public long getTimeout(Xid id) throws UnknownDtxBranchException + { + DtxBranch branch = getBranch(id); + if(branch != null) + { + return branch.getTimeout(); + } + else + { + throw new UnknownDtxBranchException(id); + } + } + + public void setTimeout(Xid id, long timeout) throws UnknownDtxBranchException + { + DtxBranch branch = getBranch(id); + if(branch != null) + { + branch.setTimeout(timeout); + } + else + { + throw new UnknownDtxBranchException(id); + } + } + + public synchronized List<Xid> recover() + { + List<Xid> inDoubt = new ArrayList<Xid>(); + for(DtxBranch branch : _branches.values()) + { + if(branch.getState() == DtxBranch.State.PREPARED) + { + inDoubt.add(branch.getXid()); + } + } + return inDoubt; + } + + public synchronized void endAssociations(AMQSessionModel session) + { + for(DtxBranch branch : _branches.values()) + { + if(branch.isAssociated(session)) + { + branch.setState(DtxBranch.State.ROLLBACK_ONLY); + branch.disassociateSession(session); + } + } + } + + + public synchronized void close() + { + for(DtxBranch branch : _branches.values()) + { + branch.close(); + } + _branches.clear(); + } + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/IncorrectDtxStateException.java b/java/broker/src/main/java/org/apache/qpid/server/txn/IncorrectDtxStateException.java new file mode 100644 index 0000000000..45f094e4b9 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/IncorrectDtxStateException.java @@ -0,0 +1,32 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.server.txn; + +import org.apache.qpid.transport.Xid; + +public class IncorrectDtxStateException extends DtxException +{ + public IncorrectDtxStateException(String message, Xid id) + { + super(message + " (xid: " + id + ")"); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/JoinAndResumeDtxException.java b/java/broker/src/main/java/org/apache/qpid/server/txn/JoinAndResumeDtxException.java new file mode 100644 index 0000000000..a25e5a4ed6 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/JoinAndResumeDtxException.java @@ -0,0 +1,32 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.server.txn; + +import org.apache.qpid.transport.Xid; + +public class JoinAndResumeDtxException extends DtxException +{ + public JoinAndResumeDtxException(Xid id) + { + super("Cannot start a branch with both join and resume set " + id); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransaction.java b/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransaction.java index 9b61f7543f..11401ebd65 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransaction.java +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransaction.java @@ -29,6 +29,7 @@ import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.queue.BaseQueue; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.Transaction; import java.util.ArrayList; import java.util.Collection; @@ -46,7 +47,7 @@ public class LocalTransaction implements ServerTransaction private final List<Action> _postTransactionActions = new ArrayList<Action>(); - private volatile MessageStore.Transaction _transaction; + private volatile Transaction _transaction; private MessageStore _transactionLog; private long _txnStartTime = 0L; diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/NotAssociatedDtxException.java b/java/broker/src/main/java/org/apache/qpid/server/txn/NotAssociatedDtxException.java new file mode 100644 index 0000000000..de070546a7 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/NotAssociatedDtxException.java @@ -0,0 +1,32 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.server.txn; + +import org.apache.qpid.transport.Xid; + +public class NotAssociatedDtxException extends DtxException +{ + public NotAssociatedDtxException(Xid id) + { + super("Xid " + id + " not associated with the current session"); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/RollbackOnlyDtxException.java b/java/broker/src/main/java/org/apache/qpid/server/txn/RollbackOnlyDtxException.java new file mode 100644 index 0000000000..6cf12d8631 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/RollbackOnlyDtxException.java @@ -0,0 +1,32 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.server.txn; + +import org.apache.qpid.transport.Xid; + +public class RollbackOnlyDtxException extends DtxException +{ + public RollbackOnlyDtxException(Xid id) + { + super("Transaction " + id + " may only be rolled back"); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/ServerTransaction.java b/java/broker/src/main/java/org/apache/qpid/server/txn/ServerTransaction.java index 7617544451..c568ae67aa 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/txn/ServerTransaction.java +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/ServerTransaction.java @@ -20,13 +20,12 @@ */ package org.apache.qpid.server.txn; +import java.util.Collection; +import java.util.List; import org.apache.qpid.server.message.EnqueableMessage; import org.apache.qpid.server.queue.BaseQueue; import org.apache.qpid.server.queue.QueueEntry; -import java.util.Collection; -import java.util.List; - /** * The ServerTransaction interface allows a set enqueue/dequeue operations to be diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/SuspendAndFailDtxException.java b/java/broker/src/main/java/org/apache/qpid/server/txn/SuspendAndFailDtxException.java new file mode 100644 index 0000000000..228844fd63 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/SuspendAndFailDtxException.java @@ -0,0 +1,32 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.server.txn; + +import org.apache.qpid.transport.Xid; + +public class SuspendAndFailDtxException extends DtxException +{ +public SuspendAndFailDtxException(Xid id) +{ + super("Cannot end a branch with both suspend and fail set " + id); +} +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/TimeoutDtxException.java b/java/broker/src/main/java/org/apache/qpid/server/txn/TimeoutDtxException.java new file mode 100644 index 0000000000..50f7708d8a --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/TimeoutDtxException.java @@ -0,0 +1,32 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.server.txn; + +import org.apache.qpid.transport.Xid; + +public class TimeoutDtxException extends DtxException +{ + public TimeoutDtxException(Xid id) + { + super("Transaction " + id + " has timed-out and may only be rolled back"); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/UnknownDtxBranchException.java b/java/broker/src/main/java/org/apache/qpid/server/txn/UnknownDtxBranchException.java new file mode 100644 index 0000000000..c23e518365 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/UnknownDtxBranchException.java @@ -0,0 +1,32 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.server.txn; + +import org.apache.qpid.transport.Xid; + +public class UnknownDtxBranchException extends DtxException +{ + public UnknownDtxBranchException(Xid id) + { + super("Unknown xid " + id); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/util/MapJsonSerializer.java b/java/broker/src/main/java/org/apache/qpid/server/util/MapJsonSerializer.java new file mode 100644 index 0000000000..2d9ba38555 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/util/MapJsonSerializer.java @@ -0,0 +1,69 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.io.StringWriter; +import java.util.Map; + +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.type.TypeReference; + +public class MapJsonSerializer +{ + private static final TypeReference<Map<String, Object>> MAP_TYPE_REFERENCE = new TypeReference<Map<String, Object>>() + { + }; + + private ObjectMapper _mapper; + + public MapJsonSerializer() + { + _mapper = new ObjectMapper(); + } + + public String serialize(Map<String, Object> attributeMap) + { + StringWriter stringWriter = new StringWriter(); + try + { + _mapper.writeValue(stringWriter, attributeMap); + } + catch (Exception e) + { + throw new RuntimeException("Failure to serialize map:" + attributeMap, e); + } + return stringWriter.toString(); + } + + public Map<String, Object> deserialize(String json) + { + Map<String, Object> attributesMap = null; + try + { + attributesMap = _mapper.readValue(json, MAP_TYPE_REFERENCE); + } + catch (Exception e) + { + throw new RuntimeException("Failure to deserialize json:" + json, e); + } + return attributesMap; + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/State.java b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/State.java new file mode 100644 index 0000000000..fb50b3e289 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/State.java @@ -0,0 +1,29 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.virtualhost; + +public enum State +{ + INITIALISING, + ACTIVE, + PASSIVE, + STOPPED +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java index afded3416e..489b985222 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java +++ b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java @@ -20,6 +20,9 @@ */ package org.apache.qpid.server.virtualhost; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ScheduledFuture; import org.apache.qpid.common.Closeable; import org.apache.qpid.server.binding.BindingFactory; import org.apache.qpid.server.configuration.ConfigStore; @@ -30,16 +33,14 @@ import org.apache.qpid.server.exchange.ExchangeFactory; import org.apache.qpid.server.exchange.ExchangeRegistry; import org.apache.qpid.server.federation.BrokerLink; import org.apache.qpid.server.management.ManagedObject; +import org.apache.qpid.server.protocol.v1_0.LinkRegistry; import org.apache.qpid.server.queue.QueueRegistry; import org.apache.qpid.server.registry.IApplicationRegistry; import org.apache.qpid.server.security.SecurityManager; -import org.apache.qpid.server.security.auth.manager.AuthenticationManager; import org.apache.qpid.server.stats.StatisticsGatherer; import org.apache.qpid.server.store.DurableConfigurationStore; import org.apache.qpid.server.store.MessageStore; - -import java.util.Map; -import java.util.UUID; +import org.apache.qpid.server.txn.DtxRegistry; public interface VirtualHost extends DurableConfigurationStore.Source, VirtualHostConfig, Closeable, StatisticsGatherer { @@ -57,10 +58,6 @@ public interface VirtualHost extends DurableConfigurationStore.Source, VirtualHo MessageStore getMessageStore(); - DurableConfigurationStore getDurableConfigurationStore(); - - AuthenticationManager getAuthenticationManager(); - SecurityManager getSecurityManager(); void close(); @@ -96,5 +93,13 @@ public interface VirtualHost extends DurableConfigurationStore.Source, VirtualHo ConfigStore getConfigStore(); + DtxRegistry getDtxRegistry(); + void removeBrokerConnection(BrokerLink brokerLink); + + LinkRegistry getLinkRegistry(String remoteContainerId); + + ScheduledFuture<?> scheduleTask(long delay, Runnable timeoutTask); + + State getState(); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostConfigRecoveryHandler.java b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostConfigRecoveryHandler.java index 266d23af97..e956806823 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostConfigRecoveryHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostConfigRecoveryHandler.java @@ -20,9 +20,17 @@ */ package org.apache.qpid.server.virtualhost; -import org.apache.log4j.Logger; +import java.io.DataInputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; +import java.util.UUID; +import org.apache.log4j.Logger; import org.apache.qpid.AMQException; +import org.apache.qpid.AMQStoreException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; import org.apache.qpid.server.binding.BindingFactory; @@ -32,29 +40,27 @@ import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.messages.TransactionLogMessages; import org.apache.qpid.server.logging.subjects.MessageStoreLogSubject; import org.apache.qpid.server.message.AMQMessage; +import org.apache.qpid.server.message.AbstractServerMessageImpl; import org.apache.qpid.server.message.EnqueableMessage; import org.apache.qpid.server.message.MessageTransferMessage; import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.AMQQueueFactory; +import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.store.ConfigurationRecoveryHandler; import org.apache.qpid.server.store.MessageStore; import org.apache.qpid.server.store.MessageStoreRecoveryHandler; import org.apache.qpid.server.store.StoredMessage; +import org.apache.qpid.server.store.Transaction; import org.apache.qpid.server.store.TransactionLogRecoveryHandler; import org.apache.qpid.server.store.TransactionLogResource; +import org.apache.qpid.server.txn.DtxBranch; +import org.apache.qpid.server.txn.DtxRegistry; +import org.apache.qpid.server.txn.ServerTransaction; +import org.apache.qpid.transport.Xid; +import org.apache.qpid.transport.util.Functions; import org.apache.qpid.util.ByteBufferInputStream; -import java.io.DataInputStream; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; -import java.util.UUID; - public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHandler, ConfigurationRecoveryHandler.QueueRecoveryHandler, ConfigurationRecoveryHandler.ExchangeRecoveryHandler, @@ -63,20 +69,19 @@ public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHa MessageStoreRecoveryHandler, MessageStoreRecoveryHandler.StoredMessageRecoveryHandler, TransactionLogRecoveryHandler, - TransactionLogRecoveryHandler.QueueEntryRecoveryHandler + TransactionLogRecoveryHandler.QueueEntryRecoveryHandler, + TransactionLogRecoveryHandler.DtxRecordRecoveryHandler { private static final Logger _logger = Logger.getLogger(VirtualHostConfigRecoveryHandler.class); - private final VirtualHost _virtualHost; private MessageStoreLogSubject _logSubject; - private List<ProcessAction> _actions; private MessageStore _store; private final Map<String, Integer> _queueRecoveries = new TreeMap<String, Integer>(); - private Map<Long, ServerMessage> _recoveredMessages = new HashMap<Long, ServerMessage>(); + private Map<Long, AbstractServerMessageImpl> _recoveredMessages = new HashMap<Long, AbstractServerMessageImpl>(); private Map<Long, StoredMessage> _unusedMessages = new HashMap<Long, StoredMessage>(); @@ -88,14 +93,14 @@ public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHa public VirtualHostConfigRecoveryHandler begin(MessageStore store) { - _logSubject = new MessageStoreLogSubject(_virtualHost,store); + _logSubject = new MessageStoreLogSubject(_virtualHost,store.getClass().getSimpleName()); _store = store; CurrentActor.get().message(_logSubject, TransactionLogMessages.RECOVERY_START(null, false)); return this; } - public void queue(String queueName, String owner, boolean exclusive, FieldTable arguments) + public void queue(UUID id, String queueName, String owner, boolean exclusive, FieldTable arguments) { try { @@ -103,7 +108,7 @@ public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHa if (q == null) { - q = AMQQueueFactory.createAMQQueueImpl(queueName, true, owner, false, exclusive, _virtualHost, + q = AMQQueueFactory.createAMQQueueImpl(id, queueName, true, owner, false, exclusive, _virtualHost, FieldTable.convertToMap(arguments)); _virtualHost.getQueueRegistry().registerQueue(q); } @@ -125,7 +130,7 @@ public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHa return this; } - public void exchange(String exchangeName, String type, boolean autoDelete) + public void exchange(UUID id, String exchangeName, String type, boolean autoDelete) { try { @@ -134,7 +139,7 @@ public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHa exchange = _virtualHost.getExchangeRegistry().getExchange(exchangeNameSS); if (exchange == null) { - exchange = _virtualHost.getExchangeFactory().createExchange(exchangeNameSS, new AMQShortString(type), true, autoDelete, 0); + exchange = _virtualHost.getExchangeFactory().createExchange(id, exchangeNameSS, new AMQShortString(type), true, autoDelete, 0); _virtualHost.getExchangeRegistry().registerExchange(exchange); } } @@ -158,7 +163,7 @@ public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHa public void message(StoredMessage message) { - ServerMessage serverMessage; + AbstractServerMessageImpl serverMessage; switch(message.getMetaData().getType()) { case META_DATA_0_8: @@ -193,47 +198,178 @@ public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHa { } - private static final class ProcessAction + public void dtxRecord(long format, byte[] globalId, byte[] branchId, + Transaction.Record[] enqueues, + Transaction.Record[] dequeues) { - private final AMQQueue _queue; - private final AMQMessage _message; - - public ProcessAction(AMQQueue queue, AMQMessage message) + Xid id = new Xid(format, globalId, branchId); + DtxRegistry dtxRegistry = _virtualHost.getDtxRegistry(); + DtxBranch branch = dtxRegistry.getBranch(id); + if(branch == null) { - _queue = queue; - _message = message; + branch = new DtxBranch(id, _store, _virtualHost); + dtxRegistry.registerBranch(branch); } + for(Transaction.Record record : enqueues) + { + final AMQQueue queue = _virtualHost.getQueueRegistry().getQueue(record.getQueue().getId()); + if(queue != null) + { + final long messageId = record.getMessage().getMessageNumber(); + final AbstractServerMessageImpl message = _recoveredMessages.get(messageId); + _unusedMessages.remove(messageId); + + if(message != null) + { + message.incrementReference(); - public void process() + branch.enqueue(queue,message); + + branch.addPostTransactionAcion(new ServerTransaction.Action() + { + + public void postCommit() + { + try + { + + queue.enqueue(message, true, null); + message.decrementReference(); + } + catch (AMQException e) + { + _logger.error("Unable to enqueue message " + message.getMessageNumber() + " into " + + "queue " + queue.getName() + " (from XA transaction)", e); + throw new RuntimeException(e); + } + } + + public void onRollback() + { + message.decrementReference(); + } + }); + } + else + { + StringBuilder xidString = xidAsString(id); + CurrentActor.get().message(_logSubject, + TransactionLogMessages.XA_INCOMPLETE_MESSAGE(xidString.toString(), + Long.toString(messageId))); + + } + + } + else + { + StringBuilder xidString = xidAsString(id); + CurrentActor.get().message(_logSubject, + TransactionLogMessages.XA_INCOMPLETE_QUEUE(xidString.toString(), + record.getQueue().getId().toString())); + + } + } + for(Transaction.Record record : dequeues) { - try + final AMQQueue queue = _virtualHost.getQueueRegistry().getQueue(record.getQueue().getId()); + if(queue != null) { - _queue.enqueue(_message); + final long messageId = record.getMessage().getMessageNumber(); + final AbstractServerMessageImpl message = _recoveredMessages.get(messageId); + _unusedMessages.remove(messageId); + + if(message != null) + { + final QueueEntry entry = queue.getMessageOnTheQueue(messageId); + + entry.acquire(); + + branch.dequeue(queue, message); + + branch.addPostTransactionAcion(new ServerTransaction.Action() + { + + public void postCommit() + { + entry.discard(); + } + + public void onRollback() + { + entry.release(); + } + }); + } + else + { + StringBuilder xidString = xidAsString(id); + CurrentActor.get().message(_logSubject, + TransactionLogMessages.XA_INCOMPLETE_MESSAGE(xidString.toString(), + Long.toString(messageId))); + + } + } - catch(AMQException e) + else { - throw new RuntimeException(e); + StringBuilder xidString = xidAsString(id); + CurrentActor.get().message(_logSubject, + TransactionLogMessages.XA_INCOMPLETE_QUEUE(xidString.toString(), + record.getQueue().getId().toString())); } + } + try + { + branch.setState(DtxBranch.State.PREPARED); + branch.prePrepareTransaction(); + } + catch (AMQStoreException e) + { + _logger.error("Unexpected database exception when attempting to prepare a recovered XA transaction " + + xidAsString(id), e); + throw new RuntimeException(e); + } + } + + private static StringBuilder xidAsString(Xid id) + { + return new StringBuilder("(") + .append(id.getFormat()) + .append(',') + .append(Functions.str(id.getGlobalId())) + .append(',') + .append(Functions.str(id.getBranchId())) + .append(')'); } - public void binding(String exchangeName, String queueName, String bindingKey, ByteBuffer buf) + public void completeDtxRecordRecovery() + { + for(StoredMessage m : _unusedMessages.values()) + { + _logger.warn("Message id " + m.getMessageNumber() + " in store, but not in any queue - removing...."); + m.remove(); + } + CurrentActor.get().message(_logSubject, TransactionLogMessages.RECOVERY_COMPLETE(null, false)); + } + + @Override + public void binding(UUID bindingId, UUID exchangeId, UUID queueId, String bindingKey, ByteBuffer buf) { - _actions = new ArrayList<ProcessAction>(); try { - Exchange exchange = _virtualHost.getExchangeRegistry().getExchange(exchangeName); + Exchange exchange = _virtualHost.getExchangeRegistry().getExchange(exchangeId); if (exchange == null) { - _logger.error("Unknown exchange: " + exchangeName + ", cannot bind queue : " + queueName); + _logger.error("Unknown exchange id " + exchangeId + ", cannot bind queue with id " + queueId); return; } - - AMQQueue queue = _virtualHost.getQueueRegistry().getQueue(new AMQShortString(queueName)); + + AMQQueue queue = _virtualHost.getQueueRegistry().getQueue(queueId); if (queue == null) { - _logger.error("Unknown queue: " + queueName + ", cannot be bound to exchange: " + exchangeName); + _logger.error("Unknown queue id " + queueId + ", cannot be bound to exchange: " + exchange.getName()); } else { @@ -257,10 +393,10 @@ public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHa if(bf.getBinding(bindingKey, queue, exchange, argumentMap) == null) { - _logger.info("Restoring binding: (Exchange: " + exchange.getNameShortString() + ", Queue: " + queueName + _logger.info("Restoring binding: (Exchange: " + exchange.getNameShortString() + ", Queue: " + queue.getName() + ", Routing Key: " + bindingKey + ", Arguments: " + argumentsFT + ")"); - bf.restoreBinding(bindingKey, queue, exchange, argumentMap); + bf.restoreBinding(bindingId, bindingKey, queue, exchange, argumentMap); } } } @@ -282,16 +418,14 @@ public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHa } - public void queueEntry(final String queueName, long messageId) + public void queueEntry(final UUID queueId, long messageId) { - AMQShortString queueNameShortString = new AMQShortString(queueName); - - AMQQueue queue = _virtualHost.getQueueRegistry().getQueue(queueNameShortString); - + AMQQueue queue = _virtualHost.getQueueRegistry().getQueue(queueId); try { if(queue != null) { + String queueName = queue.getName(); ServerMessage message = _recoveredMessages.get(messageId); _unusedMessages.remove(messageId); @@ -301,7 +435,7 @@ public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHa if (_logger.isDebugEnabled()) { - _logger.debug("On recovery, delivering " + message.getMessageNumber() + " to " + queue.getNameShortString()); + _logger.debug("On recovery, delivering " + message.getMessageNumber() + " to " + queueName); } Integer count = _queueRecoveries.get(queueName); @@ -316,23 +450,23 @@ public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHa } else { - _logger.warn("Message id " + messageId + " referenced in log as enqueued in queue " + queue.getNameShortString() + " is unknown, entry will be discarded"); - MessageStore.Transaction txn = _store.newTransaction(); + _logger.warn("Message id " + messageId + " referenced in log as enqueued in queue " + queueName + " is unknown, entry will be discarded"); + Transaction txn = _store.newTransaction(); txn.dequeueMessage(queue, new DummyMessage(messageId)); txn.commitTranAsync(); } } else { - _logger.warn("Message id " + messageId + " in log references queue " + queueName + " which is not in the configuration, entry will be discarded"); - MessageStore.Transaction txn = _store.newTransaction(); + _logger.warn("Message id " + messageId + " in log references queue with id " + queueId + " which is not in the configuration, entry will be discarded"); + Transaction txn = _store.newTransaction(); TransactionLogResource mockQueue = new TransactionLogResource() { - - public String getResourceName() + @Override + public UUID getId() { - return queueName; + return queueId; } }; txn.dequeueMessage(mockQueue, new DummyMessage(messageId)); @@ -344,20 +478,11 @@ public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHa { throw new RuntimeException(e); } - - - } - public void completeQueueEntryRecovery() + public DtxRecordRecoveryHandler completeQueueEntryRecovery() { - for(StoredMessage m : _unusedMessages.values()) - { - _logger.warn("Message id " + m.getMessageNumber() + " in store, but not in any queue - removing...."); - m.remove(); - } - for(Map.Entry<String,Integer> entry : _queueRecoveries.entrySet()) { CurrentActor.get().message(_logSubject, TransactionLogMessages.RECOVERED(entry.getValue(), entry.getKey())); @@ -365,7 +490,9 @@ public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHa CurrentActor.get().message(_logSubject, TransactionLogMessages.RECOVERY_COMPLETE(entry.getKey(), true)); } - CurrentActor.get().message(_logSubject, TransactionLogMessages.RECOVERY_COMPLETE(null, false)); + + + return this; } private static class DummyMessage implements EnqueableMessage diff --git a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostImpl.java b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostImpl.java index 51b60f7980..9b113525d4 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostImpl.java +++ b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostImpl.java @@ -20,14 +20,11 @@ */ package org.apache.qpid.server.virtualhost; -import org.apache.commons.configuration.Configuration; + import org.apache.commons.configuration.ConfigurationException; import org.apache.log4j.Logger; - import org.apache.qpid.AMQException; -import org.apache.qpid.AMQStoreException; import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.FieldTable; import org.apache.qpid.server.AMQBrokerManagerMBean; import org.apache.qpid.server.binding.BindingFactory; import org.apache.qpid.server.configuration.BrokerConfig; @@ -44,225 +41,189 @@ import org.apache.qpid.server.exchange.DefaultExchangeRegistry; import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.exchange.ExchangeFactory; import org.apache.qpid.server.exchange.ExchangeRegistry; -import org.apache.qpid.server.federation.Bridge; import org.apache.qpid.server.federation.BrokerLink; -import org.apache.qpid.server.logging.LogSubject; import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.messages.VirtualHostMessages; import org.apache.qpid.server.logging.subjects.MessageStoreLogSubject; import org.apache.qpid.server.management.AMQManagedObject; import org.apache.qpid.server.management.ManagedObject; +import org.apache.qpid.server.protocol.v1_0.LinkRegistry; import org.apache.qpid.server.protocol.AMQConnectionModel; import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.AMQQueueFactory; import org.apache.qpid.server.queue.DefaultQueueRegistry; import org.apache.qpid.server.queue.QueueRegistry; -import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.registry.IApplicationRegistry; import org.apache.qpid.server.security.SecurityManager; -import org.apache.qpid.server.security.auth.manager.AuthenticationManager; import org.apache.qpid.server.stats.StatisticsCounter; -import org.apache.qpid.server.store.ConfigurationRecoveryHandler; -import org.apache.qpid.server.store.DurableConfigurationStore; +import org.apache.qpid.server.store.Event; +import org.apache.qpid.server.store.EventListener; import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.MessageStoreFactory; +import org.apache.qpid.server.store.OperationalLoggingListener; +import org.apache.qpid.server.txn.DtxRegistry; import org.apache.qpid.server.virtualhost.plugins.VirtualHostPlugin; import org.apache.qpid.server.virtualhost.plugins.VirtualHostPluginFactory; +import javax.management.JMException; import javax.management.NotCompliantMBeanException; import javax.management.ObjectName; -import java.util.LinkedList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; + public class VirtualHostImpl implements VirtualHost { private static final Logger _logger = Logger.getLogger(VirtualHostImpl.class); + private static final int HOUSEKEEPING_SHUTDOWN_TIMEOUT = 5; + + private final UUID _id; + private final String _name; - private ConnectionRegistry _connectionRegistry; + private final long _createTime = System.currentTimeMillis(); - private QueueRegistry _queueRegistry; + private final ConcurrentHashMap<BrokerLink,BrokerLink> _links = new ConcurrentHashMap<BrokerLink, BrokerLink>(); - private ExchangeRegistry _exchangeRegistry; + private final ScheduledThreadPoolExecutor _houseKeepingTasks; - private ExchangeFactory _exchangeFactory; + private final IApplicationRegistry _appRegistry; - private MessageStore _messageStore; + private final SecurityManager _securityManager; - private VirtualHostMBean _virtualHostMBean; + private final BrokerConfig _brokerConfig; - private AMQBrokerManagerMBean _brokerMBean; + private final VirtualHostConfiguration _vhostConfig; - private final AuthenticationManager _authenticationManager; + private final VirtualHostMBean _virtualHostMBean; - private SecurityManager _securityManager; + private final AMQBrokerManagerMBean _brokerMBean; - private final ScheduledThreadPoolExecutor _houseKeepingTasks; - private final IApplicationRegistry _appRegistry; - private VirtualHostConfiguration _configuration; - private DurableConfigurationStore _durableConfigurationStore; - private BindingFactory _bindingFactory; - private BrokerConfig _broker; - private UUID _id; + private final QueueRegistry _queueRegistry; - private boolean _statisticsEnabled = false; - private StatisticsCounter _messagesDelivered, _dataDelivered, _messagesReceived, _dataReceived; + private final ExchangeRegistry _exchangeRegistry; - private final long _createTime = System.currentTimeMillis(); - private final ConcurrentHashMap<BrokerLink,BrokerLink> _links = new ConcurrentHashMap<BrokerLink, BrokerLink>(); - private static final int HOUSEKEEPING_SHUTDOWN_TIMEOUT = 5; + private final ExchangeFactory _exchangeFactory; - public IConnectionRegistry getConnectionRegistry() - { - return _connectionRegistry; - } + private final ConnectionRegistry _connectionRegistry; - public VirtualHostConfiguration getConfiguration() - { - return _configuration; - } + private final BindingFactory _bindingFactory; - public UUID getId() - { - return _id; - } + private final DtxRegistry _dtxRegistry; - public VirtualHostConfigType getConfigType() - { - return VirtualHostConfigType.getInstance(); - } + private final MessageStore _messageStore; - public ConfiguredObject getParent() - { - return getBroker(); - } + private State _state = State.INITIALISING; - public boolean isDurable() - { - return false; - } + private boolean _statisticsEnabled = false; - /** - * Virtual host JMX MBean class. - * - * This has some of the methods implemented from management intrerface for exchanges. Any - * implementaion of an Exchange MBean should extend this class. - */ - public class VirtualHostMBean extends AMQManagedObject implements ManagedVirtualHost - { - public VirtualHostMBean() throws NotCompliantMBeanException - { - super(ManagedVirtualHost.class, ManagedVirtualHost.TYPE); - } + private StatisticsCounter _messagesDelivered, _dataDelivered, _messagesReceived, _dataReceived; - public String getObjectInstanceName() - { - return ObjectName.quote(_name); - } + private final Map<String, LinkRegistry> _linkRegistry = new HashMap<String, LinkRegistry>(); - public String getName() + public VirtualHostImpl(IApplicationRegistry appRegistry, VirtualHostConfiguration hostConfig) throws Exception + { + if (hostConfig == null) { - return _name; + throw new IllegalArgumentException("HostConfig cannot be null"); } - public VirtualHostImpl getVirtualHost() + if (hostConfig.getName() == null || hostConfig.getName().length() == 0) { - return VirtualHostImpl.this; + throw new IllegalArgumentException("Illegal name (" + hostConfig.getName() + ") for virtualhost."); } - } - public VirtualHostImpl(IApplicationRegistry appRegistry, VirtualHostConfiguration hostConfig, MessageStore store) throws Exception - { - if (hostConfig == null) - { - throw new IllegalArgumentException("HostConfig cannot be null"); - } - _appRegistry = appRegistry; - _broker = _appRegistry.getBroker(); - _configuration = hostConfig; - _name = _configuration.getName(); + _brokerConfig = _appRegistry.getBroker(); + _vhostConfig = hostConfig; + _name = _vhostConfig.getName(); + _dtxRegistry = new DtxRegistry(); _id = _appRegistry.getConfigStore().createId(); CurrentActor.get().message(VirtualHostMessages.CREATED(_name)); - if (_name == null || _name.length() == 0) - { - throw new IllegalArgumentException("Illegal name (" + _name + ") for virtualhost."); - } - - _securityManager = new SecurityManager(_appRegistry.getSecurityManager()); - _securityManager.configureHostPlugins(_configuration); - _virtualHostMBean = new VirtualHostMBean(); + _securityManager = new SecurityManager(_appRegistry.getSecurityManager()); + _securityManager.configureHostPlugins(_vhostConfig); _connectionRegistry = new ConnectionRegistry(); - _houseKeepingTasks = new ScheduledThreadPoolExecutor(_configuration.getHouseKeepingThreadCount()); + _houseKeepingTasks = new ScheduledThreadPoolExecutor(_vhostConfig.getHouseKeepingThreadCount()); _queueRegistry = new DefaultQueueRegistry(this); _exchangeFactory = new DefaultExchangeFactory(this); - _exchangeFactory.initialise(_configuration); + _exchangeFactory.initialise(_vhostConfig); _exchangeRegistry = new DefaultExchangeRegistry(this); - StartupRoutingTable configFileRT = new StartupRoutingTable(); + _bindingFactory = new BindingFactory(this); - _durableConfigurationStore = configFileRT; + _brokerMBean = new AMQBrokerManagerMBean(_virtualHostMBean); - // This needs to be after the RT has been defined as it creates the default durable exchanges. - _exchangeRegistry.initialise(); + _messageStore = initialiseMessageStore(hostConfig.getMessageStoreFactoryClass()); - _bindingFactory = new BindingFactory(this); + configureMessageStore(hostConfig); - initialiseModel(_configuration); + activateNonHAMessageStore(); - if (store != null) - { - _messageStore = store; - if(store instanceof DurableConfigurationStore) - { - _durableConfigurationStore = (DurableConfigurationStore) store; - } - } - else - { - initialiseMessageStore(hostConfig); - } - - _authenticationManager = ApplicationRegistry.getInstance().getAuthenticationManager(); - - _brokerMBean = new AMQBrokerManagerMBean(_virtualHostMBean); - _brokerMBean.register(); - initialiseHouseKeeping(hostConfig.getHousekeepingCheckPeriod()); - initialiseStatistics(); } + public IConnectionRegistry getConnectionRegistry() + { + return _connectionRegistry; + } + + public VirtualHostConfiguration getConfiguration() + { + return _vhostConfig; + } + + public UUID getId() + { + return _id; + } + + public VirtualHostConfigType getConfigType() + { + return VirtualHostConfigType.getInstance(); + } + + public ConfiguredObject getParent() + { + return getBroker(); + } + + public boolean isDurable() + { + return false; + } + /** * Initialise a housekeeping task to iterate over queues cleaning expired messages with no consumers * and checking for idle or open transactions that have exceeded the permitted thresholds. * * @param period */ - private void initialiseHouseKeeping(long period) + private void initialiseHouseKeeping(long period) { + if (period != 0L) { - - scheduleHouseKeepingTask(period, new VirtualHostHouseKeepingTask()); - Map<String, VirtualHostPluginFactory> plugins = - ApplicationRegistry.getInstance().getPluginManager().getVirtualHostPlugins(); + Map<String, VirtualHostPluginFactory> plugins = _appRegistry.getPluginManager().getVirtualHostPlugins(); if (plugins != null) { @@ -292,50 +253,30 @@ public class VirtualHostImpl implements VirtualHost } } - private class VirtualHostHouseKeepingTask extends HouseKeepingTask + private void shutdownHouseKeeping() { - public VirtualHostHouseKeepingTask() - { - super(VirtualHostImpl.this); - } + _houseKeepingTasks.shutdown(); - public void execute() + try + { + if (!_houseKeepingTasks.awaitTermination(HOUSEKEEPING_SHUTDOWN_TIMEOUT, TimeUnit.SECONDS)) + { + _houseKeepingTasks.shutdownNow(); + } + } + catch (InterruptedException e) + { + _logger.warn("Interrupted during Housekeeping shutdown:", e); + Thread.currentThread().interrupt(); + } + } + + private void removeHouseKeepingTasks() + { + BlockingQueue<Runnable> taskQueue = _houseKeepingTasks.getQueue(); + for (final Runnable runnable : taskQueue) { - for (AMQQueue q : _queueRegistry.getQueues()) - { - _logger.debug("Checking message status for queue: " - + q.getName()); - try - { - q.checkMessageStatus(); - } - catch (Exception e) - { - _logger.error("Exception in housekeeping for queue: " - + q.getNameShortString().toString(), e); - //Don't throw exceptions as this will stop the - // house keeping task from running. - } - } - for (AMQConnectionModel connection : getConnectionRegistry().getConnections()) - { - _logger.debug("Checking for long running open transactions on connection " + connection); - for (AMQSessionModel session : connection.getSessionModels()) - { - _logger.debug("Checking for long running open transactions on session " + session); - try - { - session.checkTransactionStatus(_configuration.getTransactionTimeoutOpenWarn(), - _configuration.getTransactionTimeoutOpenClose(), - _configuration.getTransactionTimeoutIdleWarn(), - _configuration.getTransactionTimeoutIdleClose()); - } - catch (Exception e) - { - _logger.error("Exception in housekeeping for connection: " + connection.toString(), e); - } - } - } + _houseKeepingTasks.remove(runnable); } } @@ -351,6 +292,11 @@ public class VirtualHostImpl implements VirtualHost TimeUnit.MILLISECONDS); } + public ScheduledFuture<?> scheduleTask(long delay, Runnable task) + { + return _houseKeepingTasks.schedule(task, delay, TimeUnit.MILLISECONDS); + } + public long getHouseKeepingTaskCount() { return _houseKeepingTasks.getTaskCount(); @@ -378,65 +324,58 @@ public class VirtualHostImpl implements VirtualHost } - private void initialiseMessageStore(VirtualHostConfiguration hostConfig) throws Exception + private MessageStore initialiseMessageStore(final String messageStoreFactoryClass) throws Exception { - String messageStoreClass = hostConfig.getMessageStoreClass(); - - Class clazz = Class.forName(messageStoreClass); - Object o = clazz.newInstance(); - + final Class<?> clazz = Class.forName(messageStoreFactoryClass); + final Object o = clazz.newInstance(); - - if (!(o instanceof MessageStore)) + if (!(o instanceof MessageStoreFactory)) { - throw new ClassCastException("Message store class must implement " + MessageStore.class + ". Class " + clazz + - " does not."); + throw new ClassCastException("Message store factory class must implement " + MessageStoreFactory.class + + ". Class " + clazz + " does not."); } - MessageStore messageStore = (MessageStore) o; - VirtualHostConfigRecoveryHandler recoveryHandler = new VirtualHostConfigRecoveryHandler(this); - MessageStoreLogSubject storeLogSubject = new MessageStoreLogSubject(this, messageStore); + final MessageStoreFactory messageStoreFactory = (MessageStoreFactory) o; + final MessageStore messageStore = messageStoreFactory.createMessageStore(); + final MessageStoreLogSubject storeLogSubject = new MessageStoreLogSubject(this, messageStoreFactory.getStoreClassName()); + OperationalLoggingListener.listen(messageStore, storeLogSubject); + messageStore.addEventListener(new BeforeActivationListener(), Event.BEFORE_ACTIVATE); + messageStore.addEventListener(new AfterActivationListener(), Event.AFTER_ACTIVATE); + messageStore.addEventListener(new BeforeCloseListener(), Event.BEFORE_CLOSE); + messageStore.addEventListener(new BeforePassivationListener(), Event.BEFORE_PASSIVATE); - if(messageStore instanceof DurableConfigurationStore) - { - DurableConfigurationStore durableConfigurationStore = (DurableConfigurationStore) messageStore; - - durableConfigurationStore.configureConfigStore(this.getName(), - recoveryHandler, - hostConfig.getStoreConfiguration(), - storeLogSubject); + return messageStore; + } - _durableConfigurationStore = durableConfigurationStore; - } + private void configureMessageStore(VirtualHostConfiguration hostConfig) throws Exception + { - messageStore.configureMessageStore(this.getName(), - recoveryHandler, - hostConfig.getStoreConfiguration(), - storeLogSubject); - messageStore.configureTransactionLog(this.getName(), - recoveryHandler, - hostConfig.getStoreConfiguration(), - storeLogSubject); + VirtualHostConfigRecoveryHandler recoveryHandler = new VirtualHostConfigRecoveryHandler(this); - _messageStore = messageStore; + // TODO perhaps pass config on construction?? + _messageStore.configureConfigStore(getName(), recoveryHandler, hostConfig.getStoreConfiguration()); + _messageStore.configureMessageStore(getName(), recoveryHandler, recoveryHandler, hostConfig.getStoreConfiguration()); + } + private void activateNonHAMessageStore() throws Exception + { + _messageStore.activate(); } private void initialiseModel(VirtualHostConfiguration config) throws ConfigurationException, AMQException { _logger.debug("Loading configuration for virtualhost: " + config.getName()); - List exchangeNames = config.getExchanges(); + List<String> exchangeNames = config.getExchanges(); - for (Object exchangeNameObj : exchangeNames) + for (String exchangeName : exchangeNames) { - String exchangeName = String.valueOf(exchangeNameObj); configureExchange(config.getExchangeConfiguration(exchangeName)); } - String[] queueNames = config.getQueueNames(); + String[] queueNames = config.getQueueNames(); for (Object queueNameObj : queueNames) { @@ -447,45 +386,45 @@ public class VirtualHostImpl implements VirtualHost private void configureExchange(ExchangeConfiguration exchangeConfiguration) throws AMQException { - AMQShortString exchangeName = new AMQShortString(exchangeConfiguration.getName()); + AMQShortString exchangeName = new AMQShortString(exchangeConfiguration.getName()); Exchange exchange; exchange = _exchangeRegistry.getExchange(exchangeName); if (exchange == null) { - AMQShortString type = new AMQShortString(exchangeConfiguration.getType()); - boolean durable = exchangeConfiguration.getDurable(); - boolean autodelete = exchangeConfiguration.getAutoDelete(); + AMQShortString type = new AMQShortString(exchangeConfiguration.getType()); + boolean durable = exchangeConfiguration.getDurable(); + boolean autodelete = exchangeConfiguration.getAutoDelete(); Exchange newExchange = _exchangeFactory.createExchange(exchangeName, type, durable, autodelete, 0); _exchangeRegistry.registerExchange(newExchange); if (newExchange.isDurable()) { - _durableConfigurationStore.createExchange(newExchange); + _messageStore.createExchange(newExchange); } } } private void configureQueue(QueueConfiguration queueConfiguration) throws AMQException, ConfigurationException { - AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(queueConfiguration, this); + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(queueConfiguration, this); String queueName = queue.getName(); - if (queue.isDurable()) - { - getDurableConfigurationStore().createQueue(queue); - } + if (queue.isDurable()) + { + getMessageStore().createQueue(queue); + } //get the exchange name (returns default exchange name if none was specified) - String exchangeName = queueConfiguration.getExchange(); + String exchangeName = queueConfiguration.getExchange(); Exchange exchange = _exchangeRegistry.getExchange(exchangeName); - if (exchange == null) - { + if (exchange == null) + { throw new ConfigurationException("Attempt to bind queue '" + queueName + "' to unknown exchange:" + exchangeName); - } + } Exchange defaultExchange = _exchangeRegistry.getDefaultExchange(); @@ -531,17 +470,12 @@ public class VirtualHostImpl implements VirtualHost public BrokerConfig getBroker() { - return _broker; + return _brokerConfig; } public String getFederationTag() { - return _broker.getFederationTag(); - } - - public void setBroker(final BrokerConfig broker) - { - _broker = broker; + return _brokerConfig.getFederationTag(); } public long getCreateTime() @@ -569,16 +503,6 @@ public class VirtualHostImpl implements VirtualHost return _messageStore; } - public DurableConfigurationStore getDurableConfigurationStore() - { - return _durableConfigurationStore; - } - - public AuthenticationManager getAuthenticationManager() - { - return _authenticationManager; - } - public SecurityManager getSecurityManager() { return _securityManager; @@ -588,34 +512,8 @@ public class VirtualHostImpl implements VirtualHost { //Stop Connections _connectionRegistry.close(); - - //Stop the Queues processing - if (_queueRegistry != null) - { - for (AMQQueue queue : _queueRegistry.getQueues()) - { - queue.stop(); - } - } - - //Stop Housekeeping - if (_houseKeepingTasks != null) - { - _houseKeepingTasks.shutdown(); - - try - { - if (!_houseKeepingTasks.awaitTermination(HOUSEKEEPING_SHUTDOWN_TIMEOUT, TimeUnit.SECONDS)) - { - _houseKeepingTasks.shutdownNow(); - } - } - catch (InterruptedException e) - { - _logger.warn("Interrupted during Housekeeping shutdown:" + e.getMessage()); - // Swallowing InterruptedException ok as we are shutting down. - } - } + _queueRegistry.stopAllAndUnregisterMBeans(); + _dtxRegistry.close(); //Close MessageStore if (_messageStore != null) @@ -627,10 +525,12 @@ public class VirtualHostImpl implements VirtualHost } catch (Exception e) { - e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + _logger.error("Failed to close message store", e); } } + _state = State.STOPPED; + CurrentActor.get().message(VirtualHostMessages.CLOSED()); } @@ -742,7 +642,6 @@ public class VirtualHostImpl implements VirtualHost return blink; } - public void createBrokerConnection(final String transport, final String host, final int port, @@ -779,149 +678,182 @@ public class VirtualHostImpl implements VirtualHost } } + public synchronized LinkRegistry getLinkRegistry(String remoteContainerId) + { + LinkRegistry linkRegistry = _linkRegistry.get(remoteContainerId); + if(linkRegistry == null) + { + linkRegistry = new LinkRegistry(); + _linkRegistry.put(remoteContainerId, linkRegistry); + } + return linkRegistry; + } + public ConfigStore getConfigStore() { return getApplicationRegistry().getConfigStore(); } + public DtxRegistry getDtxRegistry() + { + return _dtxRegistry; + } + + @Override + public String toString() + { + return _name; + } + + @Override + public State getState() + { + return _state; + } + + /** - * Temporary Startup RT class to record the creation of persistent queues / exchanges. - * + * Virtual host JMX MBean class. * - * This is so we can replay the creation of queues/exchanges in to the real _RT after it has been loaded. - * This should be removed after the _RT has been fully split from the the TL + * This has some of the methods implemented from management interface for exchanges. Any + * Implementation of an Exchange MBean should extend this class. */ - private static class StartupRoutingTable implements DurableConfigurationStore + public class VirtualHostMBean extends AMQManagedObject implements ManagedVirtualHost { - private List<Exchange> exchange = new LinkedList<Exchange>(); - private List<CreateQueueTuple> queue = new LinkedList<CreateQueueTuple>(); - private List<CreateBindingTuple> bindings = new LinkedList<CreateBindingTuple>(); - private List<BrokerLink> links = new LinkedList<BrokerLink>(); - private List<Bridge> bridges = new LinkedList<Bridge>(); - - public void configure(VirtualHost virtualHost, String base, VirtualHostConfiguration config) throws Exception + public VirtualHostMBean() throws NotCompliantMBeanException { + super(ManagedVirtualHost.class, ManagedVirtualHost.TYPE); } - public void close() throws Exception + public String getObjectInstanceName() { + return ObjectName.quote(_name); } - public void removeMessage(Long messageId) throws AMQException + public String getName() { - //To change body of implemented methods use File | Settings | File Templates. + return _name; } - public void configureConfigStore(String name, - ConfigurationRecoveryHandler recoveryHandler, - Configuration config, - LogSubject logSubject) throws Exception + public VirtualHostImpl getVirtualHost() { - //To change body of implemented methods use File | Settings | File Templates. + return VirtualHostImpl.this; } + } - public void createExchange(Exchange exchange) throws AMQStoreException + private final class BeforeActivationListener implements EventListener + { + @Override + public void event(Event event) { - if (exchange.isDurable()) + try { - this.exchange.add(exchange); - } - } - - public void removeExchange(Exchange exchange) throws AMQStoreException - { - } - - public void bindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQStoreException - { - if (exchange.isDurable() && queue.isDurable()) + _exchangeRegistry.initialise(); + initialiseModel(_vhostConfig); + } catch (Exception e) { - bindings.add(new CreateBindingTuple(exchange, routingKey, queue, args)); + throw new RuntimeException("Failed to initialise virtual host after state change", e); } } + } - public void unbindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQStoreException - { - } - - public void createQueue(AMQQueue queue) throws AMQStoreException - { - createQueue(queue, null); - } - - public void createQueue(AMQQueue queue, FieldTable arguments) throws AMQStoreException + private final class AfterActivationListener implements EventListener + { + @Override + public void event(Event event) { - if (queue.isDurable()) + initialiseHouseKeeping(_vhostConfig.getHousekeepingCheckPeriod()); + try { - this.queue.add(new CreateQueueTuple(queue, arguments)); + _brokerMBean.register(); + } catch (JMException e) + { + throw new RuntimeException("Failed to register virtual host mbean for virtual host " + getName(), e); } - } - public void removeQueue(AMQQueue queue) throws AMQStoreException - { + _state = State.ACTIVE; } + } + public class BeforePassivationListener implements EventListener + { - private static class CreateQueueTuple + @Override + public void event(Event event) { - private AMQQueue queue; - private FieldTable arguments; + _connectionRegistry.close(IConnectionRegistry.VHOST_PASSIVATE_REPLY_TEXT); + _brokerMBean.unregister(); + removeHouseKeepingTasks(); - public CreateQueueTuple(AMQQueue queue, FieldTable arguments) - { - this.queue = queue; - this.arguments = arguments; - } + _queueRegistry.stopAllAndUnregisterMBeans(); + _exchangeRegistry.clearAndUnregisterMbeans(); + _dtxRegistry.close(); + + _state = State.PASSIVE; } + } - private static class CreateBindingTuple + private final class BeforeCloseListener implements EventListener + { + @Override + public void event(Event event) { - private AMQQueue queue; - private FieldTable arguments; - private Exchange exchange; - private AMQShortString routingKey; - - public CreateBindingTuple(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) - { - this.exchange = exchange; - this.routingKey = routingKey; - this.queue = queue; - arguments = args; - } + _brokerMBean.unregister(); + shutdownHouseKeeping(); } + } - public void updateQueue(AMQQueue queue) throws AMQStoreException + private class VirtualHostHouseKeepingTask extends HouseKeepingTask + { + public VirtualHostHouseKeepingTask() { + super(VirtualHostImpl.this); } - public void createBrokerLink(final BrokerLink link) throws AMQStoreException + public void execute() { - if(link.isDurable()) + for (AMQQueue q : _queueRegistry.getQueues()) { - links.add(link); + if (_logger.isDebugEnabled()) + { + _logger.debug("Checking message status for queue: " + + q.getName()); + } + try + { + q.checkMessageStatus(); + } catch (Exception e) + { + _logger.error("Exception in housekeeping for queue: " + + q.getNameShortString().toString(), e); + //Don't throw exceptions as this will stop the + // house keeping task from running. + } } - } - - public void deleteBrokerLink(final BrokerLink link) throws AMQStoreException - { - } - - public void createBridge(final Bridge bridge) throws AMQStoreException - { - if(bridge.isDurable()) + for (AMQConnectionModel connection : getConnectionRegistry().getConnections()) { - bridges.add(bridge); + if (_logger.isDebugEnabled()) + { + _logger.debug("Checking for long running open transactions on connection " + connection); + } + for (AMQSessionModel session : connection.getSessionModels()) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Checking for long running open transactions on session " + session); + } + try + { + session.checkTransactionStatus(_vhostConfig.getTransactionTimeoutOpenWarn(), + _vhostConfig.getTransactionTimeoutOpenClose(), + _vhostConfig.getTransactionTimeoutIdleWarn(), + _vhostConfig.getTransactionTimeoutIdleClose()); + } catch (Exception e) + { + _logger.error("Exception in housekeeping for connection: " + connection.toString(), e); + } + } } } - - public void deleteBridge(final Bridge bridge) throws AMQStoreException - { - } - } - - @Override - public String toString() - { - return _name; } } 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 index d34d1bbef3..5c500771c2 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/AMQBrokerManagerMBeanTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/AMQBrokerManagerMBeanTest.java @@ -37,6 +37,7 @@ 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.store.TestableMemoryMessageStore; +import org.apache.qpid.server.store.TestableMemoryMessageStoreFactory; import org.apache.qpid.server.util.TestApplicationRegistry; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.virtualhost.VirtualHostImpl; @@ -165,7 +166,7 @@ public class AMQBrokerManagerMBeanTest extends QpidTestCase XMLConfiguration configXml = new XMLConfiguration(); configXml.addProperty("virtualhosts.virtualhost(-1).name", "test"); - configXml.addProperty("virtualhosts.virtualhost(-1).test.store.class", TestableMemoryMessageStore.class.getName()); + configXml.addProperty("virtualhosts.virtualhost(-1).test.store.factoryclass", TestableMemoryMessageStoreFactory.class.getName()); ServerConfiguration configuration = new ServerConfiguration(configXml); diff --git a/java/broker/src/test/java/org/apache/qpid/server/AMQChannelTest.java b/java/broker/src/test/java/org/apache/qpid/server/AMQChannelTest.java new file mode 100644 index 0000000000..fc6cbcb248 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/AMQChannelTest.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; + +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.protocol.InternalTestProtocolSession; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.util.InternalBrokerBaseCase; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public class AMQChannelTest extends InternalBrokerBaseCase +{ + private VirtualHost _virtualHost; + private AMQProtocolSession _protocolSession; + + @Override + public void setUp() throws Exception + { + super.setUp(); + _virtualHost = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHosts().iterator().next(); + _protocolSession = new InternalTestProtocolSession(_virtualHost); + } + + public void testCompareTo() throws Exception + { + AMQChannel channel1 = new AMQChannel(_protocolSession, 1, _virtualHost.getMessageStore()); + + // create a channel with the same channelId but on a different session + AMQChannel channel2 = new AMQChannel(new InternalTestProtocolSession(_virtualHost), 1, _virtualHost.getMessageStore()); + assertFalse("Unexpected compare result", channel1.compareTo(channel2) == 0); + assertEquals("Unexpected compare result", 0, channel1.compareTo(channel1)); + } + +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/configuration/MockConnectionConfig.java b/java/broker/src/test/java/org/apache/qpid/server/configuration/MockConnectionConfig.java index 73e0bc5d27..c0777d2f8f 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/configuration/MockConnectionConfig.java +++ b/java/broker/src/test/java/org/apache/qpid/server/configuration/MockConnectionConfig.java @@ -1,3 +1,23 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.util.UUID; 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 index 50e7f0588b..c4c93acfb6 100644 --- 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 @@ -27,6 +27,7 @@ import org.apache.qpid.server.queue.AMQPriorityQueue; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.store.TestableMemoryMessageStore; +import org.apache.qpid.server.store.TestableMemoryMessageStoreFactory; import org.apache.qpid.server.util.InternalBrokerBaseCase; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -161,7 +162,7 @@ public class VirtualHostConfigurationTest extends InternalBrokerBaseCase getConfigXml().addProperty("virtualhosts.virtualhost." + getName() + "Extra.queues(-1).queue(-1).name", "r2d2"); getConfigXml().addProperty("virtualhosts.virtualhost." + getName() + "Extra.queues.queue.r2d2.deadLetterQueues", "true"); getConfigXml().addProperty("virtualhosts.virtualhost." + getName() + "Extra.queues(-1).queue(-1).name", "c3p0"); - getConfigXml().addProperty("virtualhosts.virtualhost." + getName() + "Extra.store.class", TestableMemoryMessageStore.class.getName()); + getConfigXml().addProperty("virtualhosts.virtualhost." + getName() + "Extra.store.factoryclass", TestableMemoryMessageStoreFactory.class.getName()); // Start the broker now. super.createBroker(); 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 index 27a0462e09..afd8fd9ed2 100644 --- 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 @@ -20,8 +20,19 @@ */ package org.apache.qpid.server.exchange; -import org.apache.log4j.Logger; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicLong; +import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.BasicContentHeaderProperties; @@ -31,11 +42,11 @@ import org.apache.qpid.framing.FieldTable; import org.apache.qpid.framing.FieldTableFactory; import org.apache.qpid.framing.abstraction.MessagePublishInfo; import org.apache.qpid.server.binding.Binding; -import org.apache.qpid.server.binding.BindingFactory; import org.apache.qpid.server.message.AMQMessage; import org.apache.qpid.server.message.AMQMessageHeader; import org.apache.qpid.server.message.MessageMetaData; import org.apache.qpid.server.message.ServerMessage; +import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.protocol.AMQProtocolSession; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.BaseQueue; @@ -44,23 +55,10 @@ import org.apache.qpid.server.queue.MockStoredMessage; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.queue.SimpleAMQQueue; import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.store.DurableConfigurationStore; -import org.apache.qpid.server.store.MemoryMessageStore; import org.apache.qpid.server.store.StoredMessage; import org.apache.qpid.server.subscription.Subscription; import org.apache.qpid.server.util.InternalBrokerBaseCase; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicLong; - public class AbstractHeadersExchangeTestBase extends InternalBrokerBaseCase { private static final Logger _log = Logger.getLogger(AbstractHeadersExchangeTestBase.class); @@ -68,24 +66,6 @@ public class AbstractHeadersExchangeTestBase extends InternalBrokerBaseCase 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 MemoryMessageStore _store = new MemoryMessageStore(); - - - private BindingFactory bindingFactory = new BindingFactory(new DurableConfigurationStore.Source() - { - - public DurableConfigurationStore getDurableConfigurationStore() - { - return _store; - } - }, - exchange); - private int count; public void testDoNothing() @@ -103,7 +83,6 @@ public class AbstractHeadersExchangeTestBase extends InternalBrokerBaseCase protected void unbind(TestQueue queue, String... bindings) throws AMQException { String queueName = queue.getName(); - //TODO - check this exchange.onUnbind(new Binding(null,queueName, queue, exchange, getHeadersMap(bindings))); } @@ -123,7 +102,7 @@ public class AbstractHeadersExchangeTestBase extends InternalBrokerBaseCase protected int route(Message m) throws AMQException { - m.getIncomingMessage().headersReceived(); + m.getIncomingMessage().headersReceived(System.currentTimeMillis()); m.route(exchange); if(m.getIncomingMessage().allContentReceived()) { @@ -297,7 +276,7 @@ public class AbstractHeadersExchangeTestBase extends InternalBrokerBaseCase public TestQueue(AMQShortString name) throws AMQException { - super(name, false, new AMQShortString("test"), true, false,ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test"), Collections.EMPTY_MAP); + super(UUIDGenerator.generateUUID(), name, false, new AMQShortString("test"), true, false,ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test"), Collections.EMPTY_MAP); ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test").getQueueRegistry().registerQueue(this); } @@ -538,12 +517,6 @@ public class AbstractHeadersExchangeTestBase extends InternalBrokerBaseCase } - public AMQMessage getUnderlyingMessage() - { - return Message.this; - } - - public ContentHeaderBody getContentHeader() { try 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 index 1fac4afe29..9034bf9c3a 100644 --- 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 @@ -26,6 +26,7 @@ import org.apache.qpid.exchange.ExchangeDefaults; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.management.common.mbeans.ManagedExchange; import org.apache.qpid.server.management.ManagedObject; +import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.AMQQueueFactory; import org.apache.qpid.server.queue.QueueRegistry; @@ -52,7 +53,7 @@ public class ExchangeMBeanTest extends InternalBrokerBaseCase public void testGeneralProperties() throws Exception { DirectExchange exchange = new DirectExchange(); - exchange.initialise(_virtualHost, ExchangeDefaults.DIRECT_EXCHANGE_NAME, false, 0, true); + exchange.initialise(UUIDGenerator.generateUUID(), _virtualHost, ExchangeDefaults.DIRECT_EXCHANGE_NAME, false, 0, true); ManagedObject managedObj = exchange.getManagedObject(); ManagedExchange mbean = (ManagedExchange)managedObj; @@ -67,7 +68,7 @@ public class ExchangeMBeanTest extends InternalBrokerBaseCase public void testDirectExchangeMBean() throws Exception { DirectExchange exchange = new DirectExchange(); - exchange.initialise(_virtualHost, ExchangeDefaults.DIRECT_EXCHANGE_NAME, false, 0, true); + exchange.initialise(UUIDGenerator.generateUUID(), _virtualHost, ExchangeDefaults.DIRECT_EXCHANGE_NAME, false, 0, true); ManagedObject managedObj = exchange.getManagedObject(); ManagedExchange mbean = (ManagedExchange)managedObj; @@ -82,7 +83,7 @@ public class ExchangeMBeanTest extends InternalBrokerBaseCase public void testTopicExchangeMBean() throws Exception { TopicExchange exchange = new TopicExchange(); - exchange.initialise(_virtualHost,ExchangeDefaults.TOPIC_EXCHANGE_NAME, false, 0, true); + exchange.initialise(UUIDGenerator.generateUUID(), _virtualHost,ExchangeDefaults.TOPIC_EXCHANGE_NAME, false, 0, true); ManagedObject managedObj = exchange.getManagedObject(); ManagedExchange mbean = (ManagedExchange)managedObj; @@ -97,7 +98,7 @@ public class ExchangeMBeanTest extends InternalBrokerBaseCase public void testHeadersExchangeMBean() throws Exception { HeadersExchange exchange = new HeadersExchange(); - exchange.initialise(_virtualHost,ExchangeDefaults.HEADERS_EXCHANGE_NAME, false, 0, true); + exchange.initialise(UUIDGenerator.generateUUID(), _virtualHost,ExchangeDefaults.HEADERS_EXCHANGE_NAME, false, 0, true); ManagedObject managedObj = exchange.getManagedObject(); ManagedExchange mbean = (ManagedExchange)managedObj; @@ -119,7 +120,7 @@ public class ExchangeMBeanTest extends InternalBrokerBaseCase public void testHeadersExchangeMBeanMatchPropertyNoValue() throws Exception { HeadersExchange exchange = new HeadersExchange(); - exchange.initialise(_virtualHost,ExchangeDefaults.HEADERS_EXCHANGE_NAME, false, 0, true); + exchange.initialise(UUIDGenerator.generateUUID(), _virtualHost,ExchangeDefaults.HEADERS_EXCHANGE_NAME, false, 0, true); ManagedObject managedObj = exchange.getManagedObject(); ManagedExchange mbean = (ManagedExchange)managedObj; @@ -137,7 +138,7 @@ public class ExchangeMBeanTest extends InternalBrokerBaseCase public void testInvalidHeaderBindingMalformed() throws Exception { HeadersExchange exchange = new HeadersExchange(); - exchange.initialise(_virtualHost,ExchangeDefaults.HEADERS_EXCHANGE_NAME, false, 0, true); + exchange.initialise(UUIDGenerator.generateUUID(), _virtualHost,ExchangeDefaults.HEADERS_EXCHANGE_NAME, false, 0, true); ManagedObject managedObj = exchange.getManagedObject(); ManagedExchange mbean = (ManagedExchange)managedObj; diff --git a/java/broker/src/test/java/org/apache/qpid/server/exchange/TopicExchangeTest.java b/java/broker/src/test/java/org/apache/qpid/server/exchange/TopicExchangeTest.java index dfa31e131f..00c8a18d9f 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/exchange/TopicExchangeTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/exchange/TopicExchangeTest.java @@ -350,7 +350,7 @@ public class TopicExchangeTest extends InternalBrokerBaseCase private int routeMessage(final IncomingMessage message) throws AMQException { - MessageMetaData mmd = message.headersReceived(); + MessageMetaData mmd = message.headersReceived(System.currentTimeMillis()); message.setStoredMessage(_store.addMessage(mmd)); message.enqueue(_exchange.route(message)); diff --git a/java/broker/src/test/java/org/apache/qpid/server/logging/messages/MessageStoreMessagesTest.java b/java/broker/src/test/java/org/apache/qpid/server/logging/messages/MessageStoreMessagesTest.java index cc032a0430..3377573b9d 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/logging/messages/MessageStoreMessagesTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/logging/messages/MessageStoreMessagesTest.java @@ -29,12 +29,10 @@ public class MessageStoreMessagesTest extends AbstractTestMessages { public void testMessageStoreCreated() { - String name = "DerbyMessageStore"; - - _logMessage = MessageStoreMessages.CREATED(name); + _logMessage = MessageStoreMessages.CREATED(); List<Object> log = performLog(); - String[] expected = {"Created :", name}; + String[] expected = {"Created"}; validateLogMessage(log, "MST-1001", expected); } @@ -70,56 +68,4 @@ public class MessageStoreMessagesTest extends AbstractTestMessages validateLogMessage(log, "MST-1004", expected); } -/* - public void testMessageStoreRecoveryStart_withQueue() - { - String queueName = "testQueue"; - - _logMessage = MessageStoreMessages.RECOVERY_START(queueName, true); - List<Object> log = performLog(); - - String[] expected = {"Recovery Start :", queueName}; - - validateLogMessage(log, "MST-1004", expected); - } - - public void testMessageStoreRecovered() - { - String queueName = "testQueue"; - Integer messasgeCount = 2000; - - _logMessage = MessageStoreMessages.MST_RECOVERED(messasgeCount, queueName); - List<Object> log = performLog(); - - // Here we use MessageFormat to ensure the messasgeCount of 2000 is - // reformated for display as '2,000' - String[] expected = {"Recovered ", - MessageFormat.format("{0,number}", messasgeCount), - "messages for queue", queueName}; - - validateLogMessage(log, "MST-1005", expected); - } - - public void testMessageStoreRecoveryComplete() - { - _logMessage = MessageStoreMessages.MST_RECOVERY_COMPLETE(null,false); - List<Object> log = performLog(); - - String[] expected = {"Recovery Complete"}; - - validateLogMessage(log, "MST-1006", expected); - } - - public void testMessageStoreRecoveryComplete_withQueue() - { - String queueName = "testQueue"; - - _logMessage = MessageStoreMessages.MST_RECOVERY_COMPLETE(queueName, true); - List<Object> log = performLog(); - - String[] expected = {"Recovery Complete :", queueName}; - - validateLogMessage(log, "MST-1006", expected); - } - */ } diff --git a/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/MessageStoreLogSubjectTest.java b/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/MessageStoreLogSubjectTest.java index 158fb667a9..c62b24c3b9 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/MessageStoreLogSubjectTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/MessageStoreLogSubjectTest.java @@ -37,13 +37,13 @@ public class MessageStoreLogSubjectTest extends AbstractTestLogSubject _testVhost = ApplicationRegistry.getInstance().getVirtualHostRegistry(). getVirtualHost("test"); - _subject = new MessageStoreLogSubject(_testVhost, _testVhost.getMessageStore()); + _subject = new MessageStoreLogSubject(_testVhost, _testVhost.getMessageStore().getClass().getSimpleName()); } /** * Validate that the logged Subject message is as expected: * MESSAGE [Blank][vh(/test)/ms(MemoryMessageStore)] <Log Message> - * @param message the message whos format needs validation + * @param message the message who's format needs validation */ @Override protected void validateLogStatement(String message) diff --git a/java/broker/src/test/java/org/apache/qpid/server/plugins/OsgiSystemPackageUtilTest.java b/java/broker/src/test/java/org/apache/qpid/server/plugins/OsgiSystemPackageUtilTest.java index d64d1212b0..267545c656 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/plugins/OsgiSystemPackageUtilTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/plugins/OsgiSystemPackageUtilTest.java @@ -73,11 +73,11 @@ public class OsgiSystemPackageUtilTest extends QpidTestCase _map.put("org.apache.qpid.xyz", "1.0.0"); _map.put("org.abc", "1.2.3"); - _util = new OsgiSystemPackageUtil(new Version("0.15"), _map); + _util = new OsgiSystemPackageUtil(new Version("0.17"), _map); final String systemPackageString = _util.getFormattedSystemPackageString(); - assertEquals("org.abc; version=1.2.3, org.apache.qpid.xyz; version=0.15.0", systemPackageString); + assertEquals("org.abc; version=1.2.3, org.apache.qpid.xyz; version=0.17.0", systemPackageString); } public void testWithQpidPackageWithoutQpidReleaseNumberSet() throws Exception 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 index 71d5211470..fe9bcc57a6 100644 --- 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 @@ -30,7 +30,7 @@ import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.AMQQueueFactory; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.store.MessageStore; -import org.apache.qpid.server.store.SkeletonMessageStore; +import org.apache.qpid.server.store.TestableMemoryMessageStore; import org.apache.qpid.server.util.InternalBrokerBaseCase; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -45,7 +45,7 @@ public class AMQProtocolSessionMBeanTest extends InternalBrokerBaseCase /** Used for debugging. */ private static final Logger log = Logger.getLogger(AMQProtocolSessionMBeanTest.class); - private MessageStore _messageStore = new SkeletonMessageStore(); + private MessageStore _messageStore = new TestableMemoryMessageStore(); private AMQProtocolEngine _protocolSession; private AMQChannel _channel; private AMQProtocolSessionMBean _mbean; 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 index db37cc0965..96c67941f9 100644 --- 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 @@ -34,6 +34,7 @@ import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; import org.apache.qpid.server.subscription.ClientDeliveryMethod; import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.subscription.SubscriptionImpl; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.transport.TestNetworkConnection; @@ -239,12 +240,12 @@ public class InternalTestProtocolSession extends AMQProtocolEngine implements Pr _channelDelivers.put(_channelId, consumers); } - LinkedList<DeliveryPair> consumerDelivers = consumers.get(sub.getConsumerTag()); + LinkedList<DeliveryPair> consumerDelivers = consumers.get(((SubscriptionImpl)sub).getConsumerTag()); if (consumerDelivers == null) { consumerDelivers = new LinkedList<DeliveryPair>(); - consumers.put(sub.getConsumerTag(), consumerDelivers); + consumers.put(((SubscriptionImpl)sub).getConsumerTag(), consumerDelivers); } consumerDelivers.add(new DeliveryPair(deliveryTag, (AMQMessage)entry.getMessage())); diff --git a/java/broker/src/test/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngineFactoryTest.java b/java/broker/src/test/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngineFactoryTest.java index d8b5cd02cf..6081be8efd 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngineFactoryTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngineFactoryTest.java @@ -35,6 +35,7 @@ import java.util.Set; public class MultiVersionProtocolEngineFactoryTest extends QpidTestCase { + protected void setUp() throws Exception { super.setUp(); @@ -93,7 +94,20 @@ public class MultiVersionProtocolEngineFactoryTest extends QpidTestCase (byte) 0, (byte) 10 }; - + + + private static final byte[] AMQP_1_0_0_HEADER = + new byte[] { + (byte)'A', + (byte)'M', + (byte)'Q', + (byte)'P', + (byte) 0, + (byte) 1, + (byte) 0, + (byte) 0 + }; + private byte[] getAmqpHeader(final AmqpProtocolVersion version) { switch(version) @@ -106,6 +120,8 @@ public class MultiVersionProtocolEngineFactoryTest extends QpidTestCase return AMQP_0_9_1_HEADER; case v0_10: return AMQP_0_10_HEADER; + case v1_0_0: + return AMQP_1_0_0_HEADER; default: fail("unknown AMQP version, appropriate header must be added for new protocol version"); return null; 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 index 91e35f07de..25d35aab16 100644 --- 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 @@ -35,6 +35,8 @@ import org.apache.qpid.server.subscription.SubscriptionFactoryImpl; import org.apache.qpid.server.util.InternalBrokerBaseCase; import javax.management.Notification; + +import java.nio.ByteBuffer; import java.util.ArrayList; /** This class tests all the alerts an AMQQueue can throw based on threshold values of different parameters */ @@ -300,7 +302,7 @@ public class AMQQueueAlertTest extends InternalBrokerBaseCase messages[i] = message(false, size); ArrayList<AMQQueue> qs = new ArrayList<AMQQueue>(); qs.add(getQueue()); - metaData[i] = messages[i].headersReceived(); + metaData[i] = messages[i].headersReceived(System.currentTimeMillis()); messages[i].setStoredMessage(getMessageStore().addMessage(metaData[i])); messages[i].enqueue(qs); @@ -309,30 +311,29 @@ public class AMQQueueAlertTest extends InternalBrokerBaseCase for (int i = 0; i < messageCount; i++) { - messages[i].addContentBodyFrame( - new ContentChunk() - { - - private byte[] _data = new byte[(int)size]; + ContentChunk contentChunk = new ContentChunk() + { + private byte[] _data = new byte[(int)size]; - public int getSize() - { - return (int) size; - } + public int getSize() + { + return (int) size; + } - public byte[] getData() - { - return _data; - } + public byte[] getData() + { + return _data; + } - public void reduceToFit() - { + public void reduceToFit() + { + } + }; - } - }); + messages[i].addContentBodyFrame(contentChunk); + messages[i].getStoredMessage().addContent(0, ByteBuffer.wrap(contentChunk.getData())); getQueue().enqueue(new AMQMessage(messages[i].getStoredMessage())); - } } 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 index 337ff194c3..e123a968a4 100644 --- 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 @@ -20,6 +20,8 @@ */ package org.apache.qpid.server.queue; +import java.util.UUID; + import org.apache.commons.configuration.XMLConfiguration; import org.apache.qpid.AMQException; @@ -35,6 +37,7 @@ import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.actors.TestLogActor; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.store.TestableMemoryMessageStore; +import org.apache.qpid.server.store.TestableMemoryMessageStoreFactory; import org.apache.qpid.server.util.TestApplicationRegistry; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.test.utils.QpidTestCase; @@ -53,7 +56,7 @@ public class AMQQueueFactoryTest extends QpidTestCase XMLConfiguration configXml = new XMLConfiguration(); configXml.addProperty("virtualhosts.virtualhost(-1).name", getName()); - configXml.addProperty("virtualhosts.virtualhost(-1)."+getName()+".store.class", TestableMemoryMessageStore.class.getName()); + configXml.addProperty("virtualhosts.virtualhost(-1)."+getName()+".store.factoryclass", TestableMemoryMessageStoreFactory.class.getName()); ServerConfiguration configuration = new ServerConfiguration(configXml); 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 index 933eb3b84f..45933e7064 100644 --- 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 @@ -27,6 +27,7 @@ import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.BasicContentHeaderProperties; import org.apache.qpid.framing.ContentBody; import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.abstraction.ContentChunk; import org.apache.qpid.framing.abstraction.MessagePublishInfo; import org.apache.qpid.management.common.mbeans.ManagedQueue; import org.apache.qpid.server.AMQChannel; @@ -45,6 +46,7 @@ import javax.management.openmbean.CompositeData; import javax.management.openmbean.CompositeDataSupport; import javax.management.openmbean.TabularData; import java.io.IOException; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; @@ -457,15 +459,16 @@ public class AMQQueueMBeanTest extends InternalBrokerBaseCase currentMessage.enqueue(qs); // route header - MessageMetaData mmd = currentMessage.headersReceived(); - currentMessage.setStoredMessage(getMessageStore().addMessage(mmd)); + MessageMetaData mmd = currentMessage.headersReceived(System.currentTimeMillis()); - // Add the body so we have something to test later - currentMessage.addContentBodyFrame( - getSession().getMethodRegistry() - .getProtocolVersionMethodConverter() - .convertToContentChunk( - new ContentBody(new byte[(int) MESSAGE_SIZE]))); + // Add the message to the store so we have something to test later + currentMessage.setStoredMessage(getMessageStore().addMessage(mmd)); + ContentChunk chunk = getSession().getMethodRegistry() + .getProtocolVersionMethodConverter() + .convertToContentChunk( + new ContentBody(new byte[(int) MESSAGE_SIZE])); + currentMessage.addContentBodyFrame(chunk); + currentMessage.getStoredMessage().addContent(0, ByteBuffer.wrap(chunk.getData())); AMQMessage m = new AMQMessage(currentMessage.getStoredMessage()); for(BaseQueue q : currentMessage.getDestinationQueues()) 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 index 1b35dfe34f..409b9fd92e 100644 --- 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 @@ -20,7 +20,6 @@ */ package org.apache.qpid.server.queue; -import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; @@ -37,7 +36,7 @@ import org.apache.qpid.server.protocol.AMQProtocolSession; import org.apache.qpid.server.protocol.InternalTestProtocolSession; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.store.StoredMessage; -import org.apache.qpid.server.store.TestMemoryMessageStore; +import org.apache.qpid.server.store.TestableMemoryMessageStore; import org.apache.qpid.server.subscription.Subscription; import org.apache.qpid.server.subscription.SubscriptionFactoryImpl; import org.apache.qpid.server.txn.AutoCommitTransaction; @@ -53,13 +52,11 @@ import java.util.Set; */ public class AckTest extends InternalBrokerBaseCase { - private static final Logger _log = Logger.getLogger(AckTest.class); - private Subscription _subscription; private AMQProtocolSession _protocolSession; - private TestMemoryMessageStore _messageStore; + private TestableMemoryMessageStore _messageStore; private AMQChannel _channel; @@ -73,7 +70,7 @@ public class AckTest extends InternalBrokerBaseCase { super.setUp(); _virtualHost = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test"); - _messageStore = new TestMemoryMessageStore(); + _messageStore = new TestableMemoryMessageStore(); _protocolSession = new InternalTestProtocolSession(_virtualHost); _channel = new AMQChannel(_protocolSession,5, _messageStore /*dont need exchange registry*/); @@ -143,7 +140,7 @@ public class AckTest extends InternalBrokerBaseCase ArrayList<AMQQueue> qs = new ArrayList<AMQQueue>(); qs.add(_queue); msg.enqueue(qs); - MessageMetaData mmd = msg.headersReceived(); + MessageMetaData mmd = msg.headersReceived(System.currentTimeMillis()); final StoredMessage storedMessage = _messageStore.addMessage(mmd); msg.setStoredMessage(storedMessage); final AMQMessage message = new AMQMessage(storedMessage); @@ -180,7 +177,7 @@ public class AckTest extends InternalBrokerBaseCase } catch (InterruptedException e) { - e.printStackTrace(); //TODO. + Thread.currentThread().interrupt(); } } 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 index 34dc5b4428..afaa417415 100644 --- 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 @@ -34,7 +34,6 @@ import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.security.AuthorizationHolder; import org.apache.qpid.server.subscription.Subscription; -import org.apache.qpid.server.txn.ServerTransaction; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.List; @@ -357,12 +356,12 @@ public class MockAMQQueue implements AMQQueue return null; } - public void moveMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName, ServerTransaction storeContext) + public void moveMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName) { } - public void copyMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName, ServerTransaction storeContext) + public void copyMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName) { } diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/MockStoredMessage.java b/java/broker/src/test/java/org/apache/qpid/server/queue/MockStoredMessage.java index fba3851507..950d59bef5 100755 --- a/java/broker/src/test/java/org/apache/qpid/server/queue/MockStoredMessage.java +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/MockStoredMessage.java @@ -25,7 +25,7 @@ import org.apache.qpid.framing.ContentHeaderBody; import org.apache.qpid.framing.FieldTable; import org.apache.qpid.framing.abstraction.MessagePublishInfo; import org.apache.qpid.server.message.MessageMetaData; -import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.StoreFuture; import org.apache.qpid.server.store.StoredMessage; import java.nio.ByteBuffer; @@ -105,9 +105,9 @@ public class MockStoredMessage implements StoredMessage<MessageMetaData> return buf; } - public MessageStore.StoreFuture flushToStore() + public StoreFuture flushToStore() { - return MessageStore.IMMEDIATE_FUTURE; + return StoreFuture.IMMEDIATE_FUTURE; } public void remove() diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/PriorityQueueListTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/PriorityQueueListTest.java new file mode 100644 index 0000000000..e8c0470915 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/PriorityQueueListTest.java @@ -0,0 +1,117 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.queue; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.apache.qpid.server.message.AMQMessageHeader; +import org.apache.qpid.server.message.MessageReference; +import org.apache.qpid.server.message.ServerMessage; +import org.apache.qpid.test.utils.QpidTestCase; + +public class PriorityQueueListTest extends QpidTestCase +{ + private static final byte[] PRIORITIES = {4, 5, 5, 4}; + PriorityQueueList _list = new PriorityQueueList(null, 10); + + private QueueEntry _priority4message1; + private QueueEntry _priority4message2; + private QueueEntry _priority5message1; + private QueueEntry _priority5message2; + + protected void setUp() + { + QueueEntry[] entries = new QueueEntry[PRIORITIES.length]; + + for (int i = 0; i < PRIORITIES.length; i++) + { + ServerMessage<?> message = mock(ServerMessage.class); + AMQMessageHeader header = mock(AMQMessageHeader.class); + @SuppressWarnings({ "rawtypes", "unchecked" }) + MessageReference<ServerMessage> ref = mock(MessageReference.class); + + when(message.getMessageHeader()).thenReturn(header); + when(message.newReference()).thenReturn(ref); + when(ref.getMessage()).thenReturn(message); + when(header.getPriority()).thenReturn(PRIORITIES[i]); + + entries[i] = _list.add(message); + } + + _priority4message1 = entries[0]; + _priority4message2 = entries[3]; + _priority5message1 = entries[1]; + _priority5message2 = entries[2]; + } + + public void testPriorityQueueEntryCompareToItself() + { + //check messages compare to themselves properly + assertEquals("message should compare 'equal' to itself", + 0, _priority4message1.compareTo(_priority4message1)); + + assertEquals("message should compare 'equal' to itself", + 0, _priority5message2.compareTo(_priority5message2)); + } + + public void testPriorityQueueEntryCompareToSamePriority() + { + //check messages with the same priority are ordered properly + assertEquals("first message should be 'earlier' than second message of the same priority", + -1, _priority4message1.compareTo(_priority4message2)); + + assertEquals("first message should be 'earlier' than second message of the same priority", + -1, _priority5message1.compareTo(_priority5message2)); + + //and in reverse + assertEquals("second message should be 'later' than first message of the same priority", + 1, _priority4message2.compareTo(_priority4message1)); + + assertEquals("second message should be 'later' than first message of the same priority", + 1, _priority5message2.compareTo(_priority5message1)); + } + + public void testPriorityQueueEntryCompareToDifferentPriority() + { + //check messages with higher priority are ordered 'earlier' than those with lower priority + assertEquals("first message with priority 5 should be 'earlier' than first message of priority 4", + -1, _priority5message1.compareTo(_priority4message1)); + assertEquals("first message with priority 5 should be 'earlier' than second message of priority 4", + -1, _priority5message1.compareTo(_priority4message2)); + + assertEquals("second message with priority 5 should be 'earlier' than first message of priority 4", + -1, _priority5message2.compareTo(_priority4message1)); + assertEquals("second message with priority 5 should be 'earlier' than second message of priority 4", + -1, _priority5message2.compareTo(_priority4message2)); + + //and in reverse + assertEquals("first message with priority 4 should be 'later' than first message of priority 5", + 1, _priority4message1.compareTo(_priority5message1)); + assertEquals("first message with priority 4 should be 'later' than second message of priority 5", + 1, _priority4message1.compareTo(_priority5message2)); + + assertEquals("second message with priority 4 should be 'later' than first message of priority 5", + 1, _priority4message2.compareTo(_priority5message1)); + assertEquals("second message with priority 4 should be 'later' than second message of priority 5", + 1, _priority4message2.compareTo(_priority5message2)); + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/QueueEntryListTestBase.java b/java/broker/src/test/java/org/apache/qpid/server/queue/QueueEntryListTestBase.java index 7f81aaed51..4b40c3b552 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/queue/QueueEntryListTestBase.java +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/QueueEntryListTestBase.java @@ -31,6 +31,7 @@ public abstract class QueueEntryListTestBase extends TestCase { protected static final AMQQueue _testQueue = new MockAMQQueue("test"); public abstract QueueEntryList<QueueEntry> getTestList(); + public abstract QueueEntryList<QueueEntry> getTestList(boolean newList); public abstract long getExpectedFirstMsgId(); public abstract int getExpectedListLength(); public abstract ServerMessage getTestMessageToAdd() throws AMQException; @@ -188,4 +189,36 @@ public abstract class QueueEntryListTestBase extends TestCase .getMessage().getMessageNumber(), third.getMessage().getMessageNumber()); } + /** + * Tests that after the last node of the list is marked deleted but has not yet been removed, + * the iterator still ignores it and returns that it is 'atTail()' and can't 'advance()' + * + * @see QueueEntryListTestBase#getTestList() + * @see QueueEntryListTestBase#getExpectedListLength() + */ + public void testIteratorIgnoresDeletedFinalNode() throws Exception + { + QueueEntryList<QueueEntry> list = getTestList(true); + int i = 0; + + QueueEntry queueEntry1 = list.add(new MockAMQMessage(i++)); + QueueEntry queueEntry2 = list.add(new MockAMQMessage(i++)); + + assertSame(queueEntry2, list.next(queueEntry1)); + assertNull(list.next(queueEntry2)); + + //'delete' the 2nd QueueEntry + assertTrue("Deleting node should have succeeded", queueEntry2.delete()); + + QueueEntryIterator<QueueEntry> iter = list.iterator(); + + //verify the iterator isn't 'atTail', can advance, and returns the 1st QueueEntry + assertFalse("Iterator should not have been 'atTail'", iter.atTail()); + assertTrue("Iterator should have been able to advance", iter.advance()); + assertSame("Iterator returned unexpected QueueEntry", queueEntry1, iter.getNode()); + + //verify the iterator is atTail() and can't advance + assertTrue("Iterator should have been 'atTail'", iter.atTail()); + assertFalse("Iterator should not have been able to advance", iter.advance()); + } } 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 index c345384e28..52ad4a7c5b 100644 --- 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 @@ -37,15 +37,16 @@ import org.apache.qpid.server.exchange.DirectExchange; import org.apache.qpid.server.message.AMQMessage; import org.apache.qpid.server.message.MessageMetaData; import org.apache.qpid.server.message.ServerMessage; +import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.queue.BaseQueue.PostEnqueueAction; import org.apache.qpid.server.queue.SimpleAMQQueue.QueueEntryFilter; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.store.StoredMessage; import org.apache.qpid.server.store.TestableMemoryMessageStore; +import org.apache.qpid.server.store.TestableMemoryMessageStoreFactory; import org.apache.qpid.server.subscription.MockSubscription; import org.apache.qpid.server.subscription.Subscription; import org.apache.qpid.server.txn.AutoCommitTransaction; -import org.apache.qpid.server.txn.LocalTransaction; import org.apache.qpid.server.txn.ServerTransaction; import org.apache.qpid.server.util.InternalBrokerBaseCase; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -62,7 +63,6 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase protected SimpleAMQQueue _queue; protected VirtualHost _virtualHost; - protected TestableMemoryMessageStore _store = new TestableMemoryMessageStore(); protected AMQShortString _qname = new AMQShortString("qname"); protected AMQShortString _owner = new AMQShortString("owner"); protected AMQShortString _routingKey = new AMQShortString("routing key"); @@ -107,7 +107,9 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase ApplicationRegistry applicationRegistry = (ApplicationRegistry)ApplicationRegistry.getInstance(); PropertiesConfiguration env = new PropertiesConfiguration(); - _virtualHost = new VirtualHostImpl(ApplicationRegistry.getInstance(), new VirtualHostConfiguration(getClass().getName(), env), _store); + final VirtualHostConfiguration vhostConfig = new VirtualHostConfiguration(getClass().getName(), env); + vhostConfig.setMessageStoreFactoryClass(TestableMemoryMessageStoreFactory.class.getName()); + _virtualHost = new VirtualHostImpl(ApplicationRegistry.getInstance(), vhostConfig); applicationRegistry.getVirtualHostRegistry().registerVirtualHost(_virtualHost); _queue = (SimpleAMQQueue) AMQQueueFactory.createAMQQueueImpl(_qname, false, _owner, false, false, _virtualHost, _arguments); @@ -136,7 +138,7 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase } try { - _queue = new SimpleAMQQueue(_qname, false, _owner, false, false,null, Collections.EMPTY_MAP); + _queue = new SimpleAMQQueue(UUIDGenerator.generateUUID(), _qname, false, _owner, false,false, null, Collections.EMPTY_MAP); assertNull("Queue was created", _queue); } catch (IllegalArgumentException e) @@ -478,7 +480,7 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase public void testAutoDeleteQueue() throws Exception { _queue.stop(); - _queue = new SimpleAMQQueue(_qname, false, null, true, false, _virtualHost, Collections.EMPTY_MAP); + _queue = new SimpleAMQQueue(UUIDGenerator.generateUUID(), _qname, false, null, true, false, _virtualHost, Collections.EMPTY_MAP); _queue.setDeleteOnNoConsumers(true); _queue.registerSubscription(_subscription, false); AMQMessage message = createMessage(new Long(25)); @@ -634,12 +636,13 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase // Send persistent message qs.add(_queue); - MessageMetaData metaData = msg.headersReceived(); - StoredMessage handle = _store.addMessage(metaData); + MessageMetaData metaData = msg.headersReceived(System.currentTimeMillis()); + TestableMemoryMessageStore store = (TestableMemoryMessageStore) _virtualHost.getMessageStore(); + StoredMessage handle = store.addMessage(metaData); msg.setStoredMessage(handle); - ServerTransaction txn = new AutoCommitTransaction(_store); + ServerTransaction txn = new AutoCommitTransaction(store); txn.enqueue(qs, msg, new ServerTransaction.Action() { @@ -654,7 +657,7 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase }, 0L); // Check that it is enqueued - AMQQueue data = _store.getMessages().get(1L); + AMQQueue data = store.getMessages().get(1L); assertNull(data); // Dequeue message @@ -665,7 +668,7 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase _queue.dequeue(entry,null); // Check that it is dequeued - data = _store.getMessages().get(1L); + data = store.getMessages().get(1L); assertNull(data); } @@ -689,8 +692,8 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase public void testProcessQueueWithUniqueSelectors() throws Exception { TestSimpleQueueEntryListFactory factory = new TestSimpleQueueEntryListFactory(); - SimpleAMQQueue testQueue = new SimpleAMQQueue("testQueue", false, "testOwner",false, - false, _virtualHost, factory, null) + SimpleAMQQueue testQueue = new SimpleAMQQueue(UUIDGenerator.generateUUID(), "testQueue", false,"testOwner", + false, false, _virtualHost, factory, null) { @Override public void deliverAsync(Subscription sub) @@ -839,7 +842,7 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase /** * Tests that dequeued message is not copied as part of invocation of - * {@link SimpleAMQQueue#copyMessagesToAnotherQueue(long, long, String, ServerTransaction)} + * {@link SimpleAMQQueue#copyMessagesToAnotherQueue(long, long, String)} */ public void testCopyMessagesWithDequeuedEntry() { @@ -856,14 +859,8 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase // create another queue SimpleAMQQueue queue = createQueue(anotherQueueName); - // create transaction - ServerTransaction txn = new LocalTransaction(_queue.getVirtualHost().getMessageStore()); - // copy messages into another queue - _queue.copyMessagesToAnotherQueue(0, messageNumber, anotherQueueName, txn); - - // commit transaction - txn.commit(); + _queue.copyMessagesToAnotherQueue(0, messageNumber, anotherQueueName); // get messages on another queue List<QueueEntry> entries = queue.getMessagesOnTheQueue(); @@ -889,7 +886,7 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase /** * Tests that dequeued message is not moved as part of invocation of - * {@link SimpleAMQQueue#moveMessagesToAnotherQueue(long, long, String, ServerTransaction)} + * {@link SimpleAMQQueue#moveMessagesToAnotherQueue(long, long, String)} */ public void testMovedMessagesWithDequeuedEntry() { @@ -906,14 +903,8 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase // create another queue SimpleAMQQueue queue = createQueue(anotherQueueName); - // create transaction - ServerTransaction txn = new LocalTransaction(_queue.getVirtualHost().getMessageStore()); - // move messages into another queue - _queue.moveMessagesToAnotherQueue(0, messageNumber, anotherQueueName, txn); - - // commit transaction - txn.commit(); + _queue.moveMessagesToAnotherQueue(0, messageNumber, anotherQueueName); // get messages on another queue List<QueueEntry> entries = queue.getMessagesOnTheQueue(); @@ -1038,8 +1029,8 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase int dequeueMessageIndex = 1; // create queue with overridden method deliverAsync - SimpleAMQQueue testQueue = new SimpleAMQQueue(new AMQShortString("test"), false, - new AMQShortString("testOwner"), false, false, _virtualHost, null) + SimpleAMQQueue testQueue = new SimpleAMQQueue(UUIDGenerator.generateUUID(), new AMQShortString("test"), + false, new AMQShortString("testOwner"), false, false, _virtualHost, null) { @Override public void deliverAsync(Subscription sub) @@ -1109,8 +1100,8 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase public void testEqueueDequeuedEntry() { // create a queue where each even entry is considered a dequeued - SimpleAMQQueue queue = new SimpleAMQQueue(new AMQShortString("test"), false, new AMQShortString("testOwner"), - false, false, _virtualHost, new QueueEntryListFactory() + SimpleAMQQueue queue = new SimpleAMQQueue(UUIDGenerator.generateUUID(), new AMQShortString("test"), false, + new AMQShortString("testOwner"), false, false, _virtualHost, new QueueEntryListFactory() { public QueueEntryList createQueueEntryList(AMQQueue queue) { @@ -1187,8 +1178,8 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase public void testActiveConsumerCount() throws Exception { - final SimpleAMQQueue queue = new SimpleAMQQueue(new AMQShortString("testActiveConsumerCount"), false, new AMQShortString("testOwner"), - false, false, _virtualHost, new SimpleQueueEntryList.Factory(), null); + final SimpleAMQQueue queue = new SimpleAMQQueue(UUIDGenerator.generateUUID(), new AMQShortString("testActiveConsumerCount"), false, + new AMQShortString("testOwner"), false, false, _virtualHost, new SimpleQueueEntryList.Factory(), null); //verify adding an active subscription increases the count final MockSubscription subscription1 = new MockSubscription(); diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryImplTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryImplTest.java index d8d78bbb84..ac18bbe687 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryImplTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryImplTest.java @@ -1,3 +1,23 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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; diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryListTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryListTest.java index 55c6143124..caf1eea09f 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryListTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryListTest.java @@ -23,6 +23,7 @@ package org.apache.qpid.server.queue; import org.apache.qpid.AMQException; import org.apache.qpid.server.message.AMQMessage; import org.apache.qpid.server.message.ServerMessage; +import org.apache.qpid.server.queue.SimpleQueueEntryList.QueueEntryIteratorImpl; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -59,11 +60,24 @@ public class SimpleQueueEntryListTest extends QueueEntryListTestBase System.clearProperty(SCAVENGE_PROP); } } - + @Override public QueueEntryList getTestList() { - return _sqel; + return getTestList(false); + } + + @Override + public QueueEntryList getTestList(boolean newList) + { + if(newList) + { + return new SimpleQueueEntryList(_testQueue); + } + else + { + return _sqel; + } } @Override @@ -216,5 +230,4 @@ public class SimpleQueueEntryListTest extends QueueEntryListTestBase next = next.getNextValidEntry(); assertNull("The next entry after the last should be null", next); } - } diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/SortedQueueEntryListTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/SortedQueueEntryListTest.java index 794888f780..38b12f8250 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/queue/SortedQueueEntryListTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/SortedQueueEntryListTest.java @@ -77,9 +77,23 @@ public class SortedQueueEntryListTest extends QueueEntryListTestBase } + @Override public QueueEntryList getTestList() { - return _sqel; + return getTestList(false); + } + + @Override + public QueueEntryList getTestList(boolean newList) + { + if(newList) + { + return new SelfValidatingSortedQueueEntryList(_testQueue, "KEY"); + } + else + { + return _sqel; + } } public int getExpectedListLength() 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 index 6a8a6dc2d0..9ff8f0a531 100644 --- 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 @@ -80,11 +80,10 @@ public class ApplicationRegistryShutdownTest extends InternalBrokerBaseCase } } - // Not using isEmpty as that is not in Java 5 - assertTrue("No new SASL mechanisms added by initialisation.", additions.size() != 0 ); + assertFalse("No new SASL mechanisms added by initialisation.", additions.isEmpty()); //Close the registry which will perform the close the AuthenticationManager - getRegistry().close(); + stopBroker(); //Validate that the SASL plugFins have been removed. Provider[] providersAfterClose = Security.getProviders(); 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 index ba0a715001..bef03057ec 100644 --- 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 @@ -22,13 +22,19 @@ package org.apache.qpid.server.security.auth.rmi; import junit.framework.TestCase; +import org.apache.commons.configuration.ConfigurationException; +import org.apache.commons.configuration.XMLConfiguration; +import org.apache.qpid.server.configuration.ServerConfiguration; import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; +import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.security.auth.AuthenticationResult; import org.apache.qpid.server.security.auth.AuthenticationResult.AuthenticationStatus; import org.apache.qpid.server.security.auth.manager.AuthenticationManager; +import org.apache.qpid.server.util.TestApplicationRegistry; import javax.management.remote.JMXPrincipal; import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; import java.util.Collections; @@ -47,7 +53,7 @@ public class RMIPasswordAuthenticatorTest extends TestCase protected void setUp() throws Exception { _rmipa = new RMIPasswordAuthenticator(); - + _credentials = new String[] {USERNAME, PASSWORD}; } @@ -114,8 +120,19 @@ public class RMIPasswordAuthenticatorTest extends TestCase /** * Tests case where authentication manager is not set. */ - public void testNullAuthenticationManager() + public void testNullAuthenticationManager() throws Exception { + ServerConfiguration serverConfig = new ServerConfiguration(new XMLConfiguration()); + TestApplicationRegistry reg = new TestApplicationRegistry(serverConfig) + { + @Override + protected AuthenticationManager createAuthenticationManager() throws ConfigurationException + { + return null; + } + }; + ApplicationRegistry.initialise(reg); + try { _rmipa.authenticate(_credentials); @@ -126,6 +143,10 @@ public class RMIPasswordAuthenticatorTest extends TestCase assertEquals("Unexpected exception message", RMIPasswordAuthenticator.UNABLE_TO_LOOKUP, se.getMessage()); } + finally + { + ApplicationRegistry.remove(); + } } /** @@ -254,6 +275,11 @@ public class RMIPasswordAuthenticatorTest extends TestCase return new AuthenticationResult(AuthenticationStatus.CONTINUE); } } + + public CallbackHandler getHandler(String mechanism) + { + return null; + } }; } } 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 index c3671d6a87..f5247634ac 100644 --- 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 @@ -54,7 +54,7 @@ public abstract class SaslServerTestCase extends TestCase } catch (SaslException e) { - assertEquals("Authentication failed", e.getCause().getMessage()); + assertTrue(e.getMessage().contains("Authentication failed")); exceptionCaught = true; } if (!exceptionCaught) diff --git a/java/broker/src/test/java/org/apache/qpid/server/store/DurableConfigurationStoreTest.java b/java/broker/src/test/java/org/apache/qpid/server/store/DurableConfigurationStoreTest.java new file mode 100644 index 0000000000..a1cbb2cbc8 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/store/DurableConfigurationStoreTest.java @@ -0,0 +1,377 @@ +package org.apache.qpid.server.store; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.isA; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.times; + +import java.io.File; +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import org.apache.commons.configuration.Configuration; +import org.apache.qpid.AMQStoreException; +import org.apache.qpid.common.AMQPFilterTypes; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.binding.Binding; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.logging.SystemOutMessageLogger; +import org.apache.qpid.server.logging.actors.CurrentActor; +import org.apache.qpid.server.logging.actors.TestLogActor; +import org.apache.qpid.server.logging.subjects.TestBlankSubject; +import org.apache.qpid.server.message.EnqueableMessage; +import org.apache.qpid.server.model.UUIDGenerator; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.MockStoredMessage; +import org.apache.qpid.server.store.ConfigurationRecoveryHandler; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.MessageStoreRecoveryHandler; +import org.apache.qpid.server.store.TransactionLogRecoveryHandler; +import org.apache.qpid.server.store.ConfigurationRecoveryHandler.BindingRecoveryHandler; +import org.apache.qpid.server.store.ConfigurationRecoveryHandler.ExchangeRecoveryHandler; +import org.apache.qpid.server.store.ConfigurationRecoveryHandler.QueueRecoveryHandler; +import org.apache.qpid.server.store.MessageStoreRecoveryHandler.StoredMessageRecoveryHandler; +import org.apache.qpid.server.store.Transaction.Record; +import org.apache.qpid.server.store.derby.DerbyMessageStoreFactory; +import org.apache.qpid.test.utils.QpidTestCase; +import org.apache.qpid.util.FileUtils; + +public class DurableConfigurationStoreTest extends QpidTestCase +{ + private static final String EXCHANGE_NAME = "exchangeName"; + private String _storePath; + private String _storeName; + private MessageStore _store; + private Configuration _configuration; + + private ConfigurationRecoveryHandler _recoveryHandler; + private QueueRecoveryHandler _queueRecoveryHandler; + private ExchangeRecoveryHandler _exchangeRecoveryHandler; + private BindingRecoveryHandler _bindingRecoveryHandler; + private ConfigurationRecoveryHandler.BrokerLinkRecoveryHandler _linkRecoveryHandler; + private MessageStoreRecoveryHandler _messageStoreRecoveryHandler; + private StoredMessageRecoveryHandler _storedMessageRecoveryHandler; + private TransactionLogRecoveryHandler _logRecoveryHandler; + private TransactionLogRecoveryHandler.QueueEntryRecoveryHandler _queueEntryRecoveryHandler; + private TransactionLogRecoveryHandler.DtxRecordRecoveryHandler _dtxRecordRecoveryHandler; + + private Exchange _exchange = mock(Exchange.class); + private static final String ROUTING_KEY = "routingKey"; + private static final String QUEUE_NAME = "queueName"; + private FieldTable _bindingArgs; + private UUID _queueId; + private UUID _exchangeId; + + public void setUp() throws Exception + { + super.setUp(); + + _queueId = UUIDGenerator.generateUUID(); + _exchangeId = UUIDGenerator.generateUUID(); + + _storeName = getName(); + _storePath = TMP_FOLDER + "/" + _storeName; + FileUtils.delete(new File(_storePath), true); + setTestSystemProperty("QPID_WORK", TMP_FOLDER); + _configuration = mock(Configuration.class); + _recoveryHandler = mock(ConfigurationRecoveryHandler.class); + _queueRecoveryHandler = mock(QueueRecoveryHandler.class); + _exchangeRecoveryHandler = mock(ExchangeRecoveryHandler.class); + _bindingRecoveryHandler = mock(BindingRecoveryHandler.class); + _storedMessageRecoveryHandler = mock(StoredMessageRecoveryHandler.class); + _logRecoveryHandler = mock(TransactionLogRecoveryHandler.class); + _messageStoreRecoveryHandler = mock(MessageStoreRecoveryHandler.class); + _queueEntryRecoveryHandler = mock(TransactionLogRecoveryHandler.QueueEntryRecoveryHandler.class); + _dtxRecordRecoveryHandler = mock(TransactionLogRecoveryHandler.DtxRecordRecoveryHandler.class); + + when(_messageStoreRecoveryHandler.begin()).thenReturn(_storedMessageRecoveryHandler); + when(_recoveryHandler.begin(isA(MessageStore.class))).thenReturn(_queueRecoveryHandler); + when(_queueRecoveryHandler.completeQueueRecovery()).thenReturn(_exchangeRecoveryHandler); + when(_exchangeRecoveryHandler.completeExchangeRecovery()).thenReturn(_bindingRecoveryHandler); + when(_bindingRecoveryHandler.completeBindingRecovery()).thenReturn(_linkRecoveryHandler); + when(_logRecoveryHandler.begin(any(MessageStore.class))).thenReturn(_queueEntryRecoveryHandler); + when(_queueEntryRecoveryHandler.completeQueueEntryRecovery()).thenReturn(_dtxRecordRecoveryHandler); + when(_exchange.getNameShortString()).thenReturn(AMQShortString.valueOf(EXCHANGE_NAME)); + when(_exchange.getId()).thenReturn(_exchangeId); + when(_configuration.getString(eq(MessageStoreConstants.ENVIRONMENT_PATH_PROPERTY), anyString())).thenReturn( + _storePath); + + _bindingArgs = new FieldTable(); + AMQShortString argKey = AMQPFilterTypes.JMS_SELECTOR.getValue(); + String argValue = "some selector expression"; + _bindingArgs.put(argKey, argValue); + + reopenStore(); + } + + public void tearDown() throws Exception + { + FileUtils.delete(new File(_storePath), true); + super.tearDown(); + } + + public void testCreateExchange() throws Exception + { + Exchange exchange = createTestExchange(); + _store.createExchange(exchange); + + reopenStore(); + verify(_exchangeRecoveryHandler).exchange(_exchangeId, getName(), getName() + "Type", true); + } + + public void testRemoveExchange() throws Exception + { + Exchange exchange = createTestExchange(); + _store.createExchange(exchange); + + _store.removeExchange(exchange); + + reopenStore(); + verify(_exchangeRecoveryHandler, never()).exchange(any(UUID.class), anyString(), anyString(), anyBoolean()); + } + + public void testBindQueue() throws Exception + { + AMQQueue queue = createTestQueue(QUEUE_NAME, "queueOwner", false); + Binding binding = new Binding(UUIDGenerator.generateUUID(), ROUTING_KEY, queue, _exchange, + FieldTable.convertToMap(_bindingArgs)); + _store.bindQueue(binding); + + reopenStore(); + + ByteBuffer argsAsBytes = ByteBuffer.wrap(_bindingArgs.getDataAsBytes()); + + verify(_bindingRecoveryHandler).binding(binding.getId(), _exchange.getId(), queue.getId(), ROUTING_KEY, argsAsBytes); + } + + public void testUnbindQueue() throws Exception + { + AMQQueue queue = createTestQueue(QUEUE_NAME, "queueOwner", false); + Binding binding = new Binding(UUIDGenerator.generateUUID(), ROUTING_KEY, queue, _exchange, + FieldTable.convertToMap(_bindingArgs)); + _store.bindQueue(binding); + + _store.unbindQueue(binding); + reopenStore(); + + verify(_bindingRecoveryHandler, never()).binding(any(UUID.class), any(UUID.class), any(UUID.class), anyString(), + isA(ByteBuffer.class)); + } + + public void testCreateQueueAMQQueue() throws Exception + { + AMQQueue queue = createTestQueue(getName(), getName() + "Owner", true); + _store.createQueue(queue); + + reopenStore(); + verify(_queueRecoveryHandler).queue(_queueId, getName(), getName() + "Owner", true, null); + } + + public void testCreateQueueAMQQueueFieldTable() throws Exception + { + AMQQueue queue = createTestQueue(getName(), getName() + "Owner", true); + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put("x-qpid-dlq-enabled", Boolean.TRUE); + attributes.put("x-qpid-maximum-delivery-count", new Integer(10)); + + FieldTable arguments = FieldTable.convertToFieldTable(attributes); + _store.createQueue(queue, arguments); + + reopenStore(); + verify(_queueRecoveryHandler).queue(_queueId, getName(), getName() + "Owner", true, arguments); + } + + public void testUpdateQueue() throws Exception + { + // create queue + AMQQueue queue = createTestQueue(getName(), getName() + "Owner", true); + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put("x-qpid-dlq-enabled", Boolean.TRUE); + attributes.put("x-qpid-maximum-delivery-count", new Integer(10)); + FieldTable arguments = FieldTable.convertToFieldTable(attributes); + _store.createQueue(queue, arguments); + + // update the queue to have exclusive=false + queue = createTestQueue(getName(), getName() + "Owner", false); + _store.updateQueue(queue); + + reopenStore(); + verify(_queueRecoveryHandler).queue(_queueId, getName(), getName() + "Owner", false, arguments); + } + + public void testRemoveQueue() throws Exception + { + // create queue + AMQQueue queue = createTestQueue(getName(), getName() + "Owner", true); + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put("x-qpid-dlq-enabled", Boolean.TRUE); + attributes.put("x-qpid-maximum-delivery-count", new Integer(10)); + FieldTable arguments = FieldTable.convertToFieldTable(attributes); + _store.createQueue(queue, arguments); + + // remove queue + _store.removeQueue(queue); + reopenStore(); + verify(_queueRecoveryHandler, never()).queue(any(UUID.class), anyString(), anyString(), anyBoolean(), + any(FieldTable.class)); + } + + private AMQQueue createTestQueue(String queueName, String queueOwner, boolean exclusive) throws AMQStoreException + { + AMQQueue queue = mock(AMQQueue.class); + when(queue.getName()).thenReturn(queueName); + when(queue.getNameShortString()).thenReturn(AMQShortString.valueOf(queueName)); + when(queue.getOwner()).thenReturn(AMQShortString.valueOf(queueOwner)); + when(queue.isExclusive()).thenReturn(exclusive); + when(queue.getId()).thenReturn(_queueId); + return queue; + } + + private Exchange createTestExchange() + { + Exchange exchange = mock(Exchange.class); + when(exchange.getNameShortString()).thenReturn(AMQShortString.valueOf(getName())); + when(exchange.getName()).thenReturn(getName()); + when(exchange.getTypeShortString()).thenReturn(AMQShortString.valueOf(getName() + "Type")); + when(exchange.isAutoDelete()).thenReturn(true); + when(exchange.getId()).thenReturn(_exchangeId); + return exchange; + } + + private void reopenStore() throws Exception + { + if (_store != null) + { + _store.close(); + } + _store = createStore(); + + _store.configureConfigStore(_storeName, _recoveryHandler, _configuration); + _store.configureMessageStore(_storeName, _messageStoreRecoveryHandler, _logRecoveryHandler, _configuration); + _store.activate(); + } + + protected MessageStore createStore() throws Exception + { + String storeFactoryClass = System.getProperty(MS_FACTORY_CLASS_NAME_KEY); + if (storeFactoryClass == null) + { + storeFactoryClass = DerbyMessageStoreFactory.class.getName(); + } + CurrentActor.set(new TestLogActor(new SystemOutMessageLogger())); + MessageStoreFactory factory = (MessageStoreFactory) Class.forName(storeFactoryClass).newInstance(); + return factory.createMessageStore(); + } + + public void testRecordXid() throws Exception + { + Record enqueueRecord = getTestRecord(1); + Record dequeueRecord = getTestRecord(2); + Record[] enqueues = { enqueueRecord }; + Record[] dequeues = { dequeueRecord }; + byte[] globalId = new byte[] { 1 }; + byte[] branchId = new byte[] { 2 }; + + Transaction transaction = _store.newTransaction(); + transaction.recordXid(1l, globalId, branchId, enqueues, dequeues); + transaction.commitTran(); + reopenStore(); + verify(_dtxRecordRecoveryHandler).dtxRecord(1l, globalId, branchId, enqueues, dequeues); + + transaction = _store.newTransaction(); + transaction.removeXid(1l, globalId, branchId); + transaction.commitTran(); + + reopenStore(); + verify(_dtxRecordRecoveryHandler, times(1)).dtxRecord(1l, globalId, branchId, enqueues, dequeues); + } + + private Record getTestRecord(long messageNumber) + { + UUID queueId1 = UUIDGenerator.generateUUID(); + TransactionLogResource queue1 = mock(TransactionLogResource.class); + when(queue1.getId()).thenReturn(queueId1); + EnqueableMessage message1 = mock(EnqueableMessage.class); + when(message1.isPersistent()).thenReturn(true); + when(message1.getMessageNumber()).thenReturn(messageNumber); + when(message1.getStoredMessage()).thenReturn(new MockStoredMessage(messageNumber)); + Record enqueueRecord = new TestRecord(queue1, message1); + return enqueueRecord; + } + + private static class TestRecord implements Record + { + private TransactionLogResource _queue; + private EnqueableMessage _message; + + public TestRecord(TransactionLogResource queue, EnqueableMessage message) + { + super(); + _queue = queue; + _message = message; + } + + @Override + public TransactionLogResource getQueue() + { + return _queue; + } + + @Override + public EnqueableMessage getMessage() + { + return _message; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((_message == null) ? 0 : new Long(_message.getMessageNumber()).hashCode()); + result = prime * result + ((_queue == null) ? 0 : _queue.getId().hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (!(obj instanceof Record)) + { + return false; + } + Record other = (Record) obj; + if (_message == null && other.getMessage() != null) + { + return false; + } + if (_queue == null && other.getQueue() != null) + { + return false; + } + if (_message.getMessageNumber() != other.getMessage().getMessageNumber()) + { + return false; + } + return _queue.getId().equals(other.getQueue().getId()); + } + + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/store/EventManagerTest.java b/java/broker/src/test/java/org/apache/qpid/server/store/EventManagerTest.java new file mode 100644 index 0000000000..2be79c5839 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/store/EventManagerTest.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.store; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.apache.qpid.server.store.Event.AFTER_ACTIVATE; +import static org.apache.qpid.server.store.Event.BEFORE_ACTIVATE; +import junit.framework.TestCase; + +public class EventManagerTest extends TestCase +{ + private EventManager _eventManager = new EventManager(); + private EventListener _mockListener = mock(EventListener.class); + + public void testEventListenerFires() + { + _eventManager.addEventListener(_mockListener, BEFORE_ACTIVATE); + _eventManager.notifyEvent(BEFORE_ACTIVATE); + verify(_mockListener).event(BEFORE_ACTIVATE); + } + + public void testEventListenerDoesntFire() + { + _eventManager.addEventListener(_mockListener, BEFORE_ACTIVATE); + _eventManager.notifyEvent(AFTER_ACTIVATE); + verifyZeroInteractions(_mockListener); + } + + public void testEventListenerFiresMulitpleTimes() + { + _eventManager.addEventListener(_mockListener, BEFORE_ACTIVATE); + _eventManager.addEventListener(_mockListener, AFTER_ACTIVATE); + + _eventManager.notifyEvent(BEFORE_ACTIVATE); + verify(_mockListener).event(BEFORE_ACTIVATE); + + _eventManager.notifyEvent(AFTER_ACTIVATE); + verify(_mockListener).event(AFTER_ACTIVATE); + } + + public void testMultipleListenersFireForSameEvent() + { + final EventListener mockListener1 = mock(EventListener.class); + final EventListener mockListener2 = mock(EventListener.class); + + _eventManager.addEventListener(mockListener1, BEFORE_ACTIVATE); + _eventManager.addEventListener(mockListener2, BEFORE_ACTIVATE); + _eventManager.notifyEvent(BEFORE_ACTIVATE); + + verify(mockListener1).event(BEFORE_ACTIVATE); + verify(mockListener2).event(BEFORE_ACTIVATE); + } +} 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 index 5fc074d877..3fb0776083 100644 --- 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 @@ -39,6 +39,7 @@ import org.apache.qpid.server.exchange.ExchangeType; import org.apache.qpid.server.exchange.TopicExchange; import org.apache.qpid.server.message.AMQMessage; import org.apache.qpid.server.message.MessageMetaData; +import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.queue.AMQPriorityQueue; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.AMQQueueFactory; @@ -58,6 +59,7 @@ import java.io.File; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.UUID; /** * This tests the MessageStores by using the available interfaces. @@ -101,7 +103,7 @@ public class MessageStoreTest extends InternalBrokerBaseCase String storePath = System.getProperty("QPID_WORK") + "/" + getName(); _config = new PropertiesConfiguration(); - _config.addProperty("store.class", getTestProfileMessageStoreClassName()); + _config.addProperty("store.factoryclass", getTestProfileMessageStoreFactoryClassName()); _config.addProperty("store.environment-path", storePath); cleanup(new File(storePath)); @@ -298,7 +300,7 @@ public class MessageStoreTest extends InternalBrokerBaseCase 1, queueRegistry.getQueues().size()); //test that removing the queue means it is not recovered next time - getVirtualHost().getDurableConfigurationStore().removeQueue(queueRegistry.getQueue(durableQueueName)); + getVirtualHost().getMessageStore().removeQueue(queueRegistry.getQueue(durableQueueName)); reloadVirtualHost(); @@ -351,7 +353,7 @@ public class MessageStoreTest extends InternalBrokerBaseCase origExchangeCount + 1, exchangeRegistry.getExchangeNames().size()); //test that removing the exchange means it is not recovered next time - getVirtualHost().getDurableConfigurationStore().removeExchange(exchangeRegistry.getExchange(directExchangeName)); + getVirtualHost().getMessageStore().removeExchange(exchangeRegistry.getExchange(directExchangeName)); reloadVirtualHost(); @@ -600,7 +602,7 @@ public class MessageStoreTest extends InternalBrokerBaseCase currentMessage.setExpiration(); - MessageMetaData mmd = currentMessage.headersReceived(); + MessageMetaData mmd = currentMessage.headersReceived(System.currentTimeMillis()); currentMessage.setStoredMessage(getVirtualHost().getMessageStore().addMessage(mmd)); currentMessage.getStoredMessage().flushToStore(); currentMessage.route(); @@ -707,7 +709,7 @@ public class MessageStoreTest extends InternalBrokerBaseCase if (queue.isDurable() && !queue.isAutoDelete()) { - getVirtualHost().getDurableConfigurationStore().createQueue(queue, queueArguments); + getVirtualHost().getMessageStore().createQueue(queue, queueArguments); } } catch (AMQException e) @@ -739,7 +741,7 @@ public class MessageStoreTest extends InternalBrokerBaseCase try { - exchange = type.newInstance(getVirtualHost(), name, durable, 0, false); + exchange = type.newInstance(UUIDGenerator.generateUUID(), getVirtualHost(), name, durable, 0, false); } catch (AMQException e) { @@ -751,7 +753,7 @@ public class MessageStoreTest extends InternalBrokerBaseCase getVirtualHost().getExchangeRegistry().registerExchange(exchange); if (durable) { - getVirtualHost().getDurableConfigurationStore().createExchange(exchange); + getVirtualHost().getMessageStore().createExchange(exchange); } } catch (AMQException e) diff --git a/java/broker/src/test/java/org/apache/qpid/server/store/OperationalLoggingListenerTest.java b/java/broker/src/test/java/org/apache/qpid/server/store/OperationalLoggingListenerTest.java new file mode 100644 index 0000000000..42746f9119 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/store/OperationalLoggingListenerTest.java @@ -0,0 +1,181 @@ +package org.apache.qpid.server.store; + +import java.util.ArrayList; +import java.util.List; +import junit.framework.TestCase; +import org.apache.qpid.server.logging.LogActor; +import org.apache.qpid.server.logging.LogMessage; +import org.apache.qpid.server.logging.LogSubject; +import org.apache.qpid.server.logging.RootMessageLogger; +import org.apache.qpid.server.logging.actors.CurrentActor; +import org.apache.qpid.server.logging.messages.ConfigStoreMessages; +import org.apache.qpid.server.logging.messages.MessageStoreMessages; +import org.apache.qpid.server.logging.messages.TransactionLogMessages; + +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * <p/> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p/> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 OperationalLoggingListenerTest extends TestCase +{ + + + public static final String STORE_LOCATION = "The moon!"; + + protected void setUp() throws Exception + { + super.setUp(); + + } + + public void testOperationalLoggingWithStoreLocation() throws Exception + { + TestMessageStore messageStore = new TestMessageStore(); + LogSubject logSubject = LOG_SUBJECT; + + OperationalLoggingListener.listen(messageStore, logSubject); + + performTests(messageStore, true); + + } + + public void testOperationalLogging() throws Exception + { + TestMessageStore messageStore = new TestMessageStore(); + LogSubject logSubject = LOG_SUBJECT; + + OperationalLoggingListener.listen(messageStore, logSubject); + + performTests(messageStore, false); + } + + private void performTests(TestMessageStore messageStore, boolean setStoreLocation) + { + final List<LogMessage> messages = new ArrayList<LogMessage>(); + + CurrentActor.set(new TestActor(messages)); + + if(setStoreLocation) + { + messageStore.setStoreLocation(STORE_LOCATION); + } + + + messageStore.attainState(State.CONFIGURING); + assertEquals("Unexpected number of operational log messages on configuring", 1, messages.size()); + assertEquals(messages.remove(0).toString(), ConfigStoreMessages.CREATED().toString()); + + messageStore.attainState(State.CONFIGURED); + assertEquals("Unexpected number of operational log messages on CONFIGURED", setStoreLocation ? 3 : 2, messages.size()); + assertEquals(messages.remove(0).toString(), MessageStoreMessages.CREATED().toString()); + assertEquals(messages.remove(0).toString(), TransactionLogMessages.CREATED().toString()); + if(setStoreLocation) + { + assertEquals(messages.remove(0).toString(), MessageStoreMessages.STORE_LOCATION(STORE_LOCATION).toString()); + } + + messageStore.attainState(State.RECOVERING); + assertEquals("Unexpected number of operational log messages on RECOVERING", 1, messages.size()); + assertEquals(messages.remove(0).toString(), MessageStoreMessages.RECOVERY_START().toString()); + + + messageStore.attainState(State.ACTIVE); + assertEquals("Unexpected number of operational log messages on ACTIVE", 1, messages.size()); + assertEquals(messages.remove(0).toString(), MessageStoreMessages.RECOVERY_COMPLETE().toString()); + + messageStore.attainState(State.CLOSING); + assertEquals("Unexpected number of operational log messages on CLOSING", 0, messages.size()); + + messageStore.attainState(State.CLOSED); + assertEquals("Unexpected number of operational log messages on CLOSED", 1, messages.size()); + assertEquals(messages.remove(0).toString(), MessageStoreMessages.CLOSED().toString()); + } + + @Override + protected void tearDown() throws Exception + { + super.tearDown(); + CurrentActor.remove(); + } + + + private static final LogSubject LOG_SUBJECT = new LogSubject() + { + public String toLogString() + { + return ""; + } + }; + + private static final class TestMessageStore extends NullMessageStore + { + + private final EventManager _eventManager = new EventManager(); + private final StateManager _stateManager = new StateManager(_eventManager); + private String _storeLocation; + + public void attainState(State state) + { + _stateManager.attainState(state); + } + + @Override + public String getStoreLocation() + { + return _storeLocation; + } + + public void setStoreLocation(String storeLocation) + { + _storeLocation = storeLocation; + } + + @Override + public void addEventListener(EventListener eventListener, Event... events) + { + _eventManager.addEventListener(eventListener, events); + } + } + + private static class TestActor implements LogActor + { + private final List<LogMessage> _messages; + + public TestActor(List<LogMessage> messages) + { + _messages = messages; + } + + public void message(LogSubject subject, LogMessage message) + { + _messages.add(message); + } + + public void message(LogMessage message) + { + _messages.add(message); + } + + public RootMessageLogger getRootMessageLogger() + { + return null; + } + + public String getLogMessage() + { + return null; + } + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/store/ReferenceCountingTest.java b/java/broker/src/test/java/org/apache/qpid/server/store/ReferenceCountingTest.java index 2ffa157ca8..4aa023a25c 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/store/ReferenceCountingTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/store/ReferenceCountingTest.java @@ -34,12 +34,12 @@ import org.apache.qpid.test.utils.QpidTestCase; */ public class ReferenceCountingTest extends QpidTestCase { - private TestMemoryMessageStore _store; + private TestableMemoryMessageStore _store; protected void setUp() throws Exception { - _store = new TestMemoryMessageStore(); + _store = new TestableMemoryMessageStore(); } /** 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 deleted file mode 100644 index 88b9acffe0..0000000000 --- a/java/broker/src/test/java/org/apache/qpid/server/store/SkeletonMessageStore.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.server.store; - -import org.apache.commons.configuration.Configuration; - -import org.apache.qpid.AMQStoreException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.server.logging.LogSubject; -import org.apache.qpid.server.message.EnqueableMessage; -import org.apache.qpid.server.queue.AMQQueue; - -/** - * 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 -{ - public void configureConfigStore(String name, - ConfigurationRecoveryHandler recoveryHandler, - Configuration config, - LogSubject logSubject) throws Exception - { - } - - public void configureMessageStore(String name, - MessageStoreRecoveryHandler recoveryHandler, - Configuration config, - LogSubject logSubject) throws Exception - { - } - - public void close() throws Exception - { - } - - public <M extends StorableMessageMetaData> StoredMessage<M> addMessage(M metaData) - { - return null; - } - - - public void createExchange(Exchange exchange) throws AMQStoreException - { - - } - - public void removeExchange(Exchange exchange) throws AMQStoreException - { - - } - - public void bindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQStoreException - { - - } - - public void unbindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQStoreException - { - - } - - public void createQueue(AMQQueue queue) throws AMQStoreException - { - } - - public void createQueue(AMQQueue queue, FieldTable arguments) throws AMQStoreException - { - } - - public boolean isPersistent() - { - return false; - } - - public void removeQueue(final AMQQueue queue) throws AMQStoreException - { - - } - - public void configureTransactionLog(String name, - TransactionLogRecoveryHandler recoveryHandler, - Configuration storeConfiguration, - LogSubject logSubject) throws Exception - { - - } - - public Transaction newTransaction() - { - return new Transaction() - { - - public void enqueueMessage(TransactionLogResource queue, EnqueableMessage message) throws AMQStoreException - { - - } - - public void dequeueMessage(TransactionLogResource queue, EnqueableMessage message) throws AMQStoreException - { - - } - - public void commitTran() throws AMQStoreException - { - - } - - public StoreFuture commitTranAsync() throws AMQStoreException - { - return new StoreFuture() - { - public boolean isComplete() - { - return true; - } - - public void waitForCompletion() - { - - } - }; - } - - public void abortTran() throws AMQStoreException - { - - } - }; - } - - public void updateQueue(AMQQueue queue) throws AMQStoreException - { - - } - -} diff --git a/java/broker/src/test/java/org/apache/qpid/server/store/StateManagerTest.java b/java/broker/src/test/java/org/apache/qpid/server/store/StateManagerTest.java new file mode 100644 index 0000000000..97c88ca1d3 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/store/StateManagerTest.java @@ -0,0 +1,195 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.store; + + +import java.util.EnumSet; +import junit.framework.TestCase; + +public class StateManagerTest extends TestCase implements EventListener +{ + + private StateManager _manager; + private Event _event; + + public void setUp() throws Exception + { + super.setUp(); + _manager = new StateManager(this); + } + + public void testInitialState() + { + assertEquals(State.INITIAL, _manager.getState()); + } + + public void testStateTransitionAllowed() + { + assertEquals(State.INITIAL, _manager.getState()); + + _manager.stateTransition(State.INITIAL, State.CONFIGURING); + assertEquals(State.CONFIGURING, _manager.getState()); + } + + public void testStateTransitionDisallowed() + { + assertEquals(State.INITIAL, _manager.getState()); + + try + { + _manager.stateTransition(State.ACTIVE, State.CLOSING); + fail("Exception not thrown"); + } + catch (IllegalStateException e) + { + // PASS + } + assertEquals(State.INITIAL, _manager.getState()); + } + + public void testIsInState() + { + assertEquals(State.INITIAL, _manager.getState()); + assertFalse(_manager.isInState(State.ACTIVE)); + assertTrue(_manager.isInState(State.INITIAL)); + } + + public void testIsNotInState() + { + assertEquals(State.INITIAL, _manager.getState()); + assertTrue(_manager.isNotInState(State.ACTIVE)); + assertFalse(_manager.isNotInState(State.INITIAL)); + } + + public void testCheckInState() + { + assertEquals(State.INITIAL, _manager.getState()); + + try + { + _manager.checkInState(State.ACTIVE); + fail("Exception not thrown"); + } + catch (IllegalStateException e) + { + // PASS + } + assertEquals(State.INITIAL, _manager.getState()); + } + + public void testValidStateTransitions() + { + assertEquals(State.INITIAL, _manager.getState()); + performValidTransition(StateManager.CONFIGURE); + performValidTransition(StateManager.CONFIGURE_COMPLETE); + performValidTransition(StateManager.RECOVER); + performValidTransition(StateManager.ACTIVATE); + performValidTransition(StateManager.QUIESCE); + performValidTransition(StateManager.QUIESCE_COMPLETE); + performValidTransition(StateManager.RESTART); + performValidTransition(StateManager.ACTIVATE); + performValidTransition(StateManager.CLOSE_ACTIVE); + performValidTransition(StateManager.CLOSE_COMPLETE); + + _manager = new StateManager(this); + performValidTransition(StateManager.CONFIGURE); + performValidTransition(StateManager.CONFIGURE_COMPLETE); + performValidTransition(StateManager.RECOVER); + performValidTransition(StateManager.ACTIVATE); + performValidTransition(StateManager.QUIESCE); + performValidTransition(StateManager.QUIESCE_COMPLETE); + performValidTransition(StateManager.CLOSE_QUIESCED); + performValidTransition(StateManager.CLOSE_COMPLETE); + } + + private void performValidTransition(StateManager.Transition transition) + { + _manager.attainState(transition.getEndState()); + assertEquals("Unexpected end state", transition.getEndState(), _manager.getState()); + assertEquals("Unexpected event", transition.getEvent(), _event); + _event = null; + } + + public void testInvalidStateTransitions() + { + assertEquals(State.INITIAL, _manager.getState()); + + + performInvalidTransitions(StateManager.CONFIGURE, State.CONFIGURED); + performInvalidTransitions(StateManager.CONFIGURE_COMPLETE, State.RECOVERING); + performInvalidTransitions(StateManager.RECOVER, State.ACTIVE); + performInvalidTransitions(StateManager.ACTIVATE, State.QUIESCING, State.CLOSING); + performInvalidTransitions(StateManager.QUIESCE, State.QUIESCED); + performInvalidTransitions(StateManager.QUIESCE_COMPLETE, State.RECOVERING, State.CLOSING); + performInvalidTransitions(StateManager.CLOSE_QUIESCED, State.CLOSED); + performInvalidTransitions(StateManager.CLOSE_COMPLETE); + + + + + } + + private void performInvalidTransitions(StateManager.Transition preTransition, State... validTransitions) + { + if(preTransition != null) + { + performValidTransition(preTransition); + } + + EnumSet<State> nextStates = EnumSet.allOf(State.class); + + if(validTransitions != null) + { + for(State state: validTransitions) + { + nextStates.remove(state); + } + } + + for(State nextState : nextStates) + { + performInvalidStateTransition(nextState); + } + + + } + + private void performInvalidStateTransition(State state) + { + try + { + _event = null; + State startState = _manager.getState(); + _manager.attainState(state); + fail("Invalid state transition performed: " + startState + " to " + state); + } + catch(IllegalStateException e) + { + // pass + } + assertNull("No event should have be fired", _event); + } + + public void event(Event event) + { + _event = event; + } +} 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 deleted file mode 100644 index 8a261b3b86..0000000000 --- a/java/broker/src/test/java/org/apache/qpid/server/store/TestMemoryMessageStore.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.server.store; - -import java.nio.ByteBuffer; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Adds some extra methods to the memory message store for testing purposes. - */ -public class TestMemoryMessageStore extends MemoryMessageStore -{ - private AtomicInteger _messageCount = new AtomicInteger(0); - - - public TestMemoryMessageStore() - { - } - - @Override - public StoredMessage addMessage(StorableMessageMetaData metaData) - { - return new TestableStoredMessage(super.addMessage(metaData)); - } - - public int getMessageCount() - { - return _messageCount.get(); - } - - private class TestableStoredMessage implements StoredMessage - { - private final StoredMessage _storedMessage; - - public TestableStoredMessage(StoredMessage storedMessage) - { - _messageCount.incrementAndGet(); - _storedMessage = storedMessage; - } - - public StorableMessageMetaData getMetaData() - { - return _storedMessage.getMetaData(); - } - - public long getMessageNumber() - { - return _storedMessage.getMessageNumber(); - } - - public void addContent(int offsetInMessage, ByteBuffer src) - { - _storedMessage.addContent(offsetInMessage, src); - } - - public int getContent(int offsetInMessage, ByteBuffer dst) - { - return _storedMessage.getContent(offsetInMessage, dst); - } - - - public ByteBuffer getContent(int offsetInMessage, int size) - { - return _storedMessage.getContent(offsetInMessage, size); - } - - public StoreFuture flushToStore() - { - return _storedMessage.flushToStore(); - } - - public void remove() - { - _storedMessage.remove(); - _messageCount.decrementAndGet(); - } - - } - -} 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 index e409734a17..210408f490 100644 --- 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 @@ -26,6 +26,7 @@ import org.apache.qpid.server.queue.AMQQueue; import java.nio.ByteBuffer; import java.util.HashMap; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; /** @@ -33,26 +34,8 @@ import java.util.concurrent.atomic.AtomicInteger; */ public class TestableMemoryMessageStore extends MemoryMessageStore { - - private MemoryMessageStore _mms = null; - private HashMap<Long, AMQQueue> _messages = new HashMap<Long, AMQQueue>(); - private AtomicInteger _messageCount = new AtomicInteger(0); - - public TestableMemoryMessageStore(MemoryMessageStore mms) - { - _mms = mms; - } - - public TestableMemoryMessageStore() - { - - } - - @Override - public void close() throws Exception - { - // Not required to do anything - } + private final Map<Long, AMQQueue> _messages = new HashMap<Long, AMQQueue>(); + private final AtomicInteger _messageCount = new AtomicInteger(0); @Override public StoredMessage addMessage(StorableMessageMetaData metaData) @@ -65,41 +48,47 @@ public class TestableMemoryMessageStore extends MemoryMessageStore return _messageCount.get(); } + public Map<Long, AMQQueue> getMessages() + { + return _messages; + } + private class TestableTransaction implements Transaction { + @Override public void enqueueMessage(TransactionLogResource queue, EnqueableMessage message) throws AMQStoreException { getMessages().put(message.getMessageNumber(), (AMQQueue)queue); } + @Override public void dequeueMessage(TransactionLogResource queue, EnqueableMessage message) throws AMQStoreException { getMessages().remove(message.getMessageNumber()); } + @Override public void commitTran() throws AMQStoreException { } + @Override public StoreFuture commitTranAsync() throws AMQStoreException { - return new StoreFuture() - { - public boolean isComplete() - { - return true; - } - - public void waitForCompletion() - { - - } - }; + return StoreFuture.IMMEDIATE_FUTURE; } public void abortTran() throws AMQStoreException { } + + public void removeXid(long format, byte[] globalId, byte[] branchId) + { + } + + public void recordXid(long format, byte[] globalId, byte[] branchId, Record[] enqueues, Record[] dequeues) + { + } } @@ -109,10 +98,6 @@ public class TestableMemoryMessageStore extends MemoryMessageStore return new TestableTransaction(); } - public HashMap<Long, AMQQueue> getMessages() - { - return _messages; - } private class TestableStoredMessage implements StoredMessage { diff --git a/java/broker/src/test/java/org/apache/qpid/server/store/TestableMemoryMessageStoreFactory.java b/java/broker/src/test/java/org/apache/qpid/server/store/TestableMemoryMessageStoreFactory.java new file mode 100644 index 0000000000..44070f22ad --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/store/TestableMemoryMessageStoreFactory.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.store; + +public class TestableMemoryMessageStoreFactory implements MessageStoreFactory +{ + + @Override + public MessageStore createMessageStore() + { + return new TestableMemoryMessageStore(); + } + + @Override + public String getStoreClassName() + { + return TestableMemoryMessageStore.class.getSimpleName(); + } + +} 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 index 1d6ccfbbc2..5ba9c0c015 100644 --- 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 @@ -277,6 +277,10 @@ public class MockSubscription implements Subscription return false; } + public void queueEmpty() throws AMQException + { + } + public void setActive(final boolean isActive) { _isActive = isActive; diff --git a/java/broker/src/test/java/org/apache/qpid/server/transport/ServerSessionTest.java b/java/broker/src/test/java/org/apache/qpid/server/transport/ServerSessionTest.java new file mode 100644 index 0000000000..d775b0f2f8 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/transport/ServerSessionTest.java @@ -0,0 +1,67 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.transport; + +import java.util.UUID; + +import org.apache.qpid.server.configuration.MockConnectionConfig; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.util.InternalBrokerBaseCase; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.transport.Binary; + +public class ServerSessionTest extends InternalBrokerBaseCase +{ + + private VirtualHost _virtualHost; + + @Override + public void setUp() throws Exception + { + super.setUp(); + _virtualHost = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHosts().iterator().next(); + } + + public void testCompareTo() throws Exception + { + ServerConnection connection = new ServerConnection(1); + connection.setConnectionConfig(createConnectionConfig()); + ServerSession session1 = new ServerSession(connection, new ServerSessionDelegate(), + new Binary(getName().getBytes()), 0 , connection.getConfig()); + + // create a session with the same name but on a different connection + ServerConnection connection2 = new ServerConnection(2); + connection2.setConnectionConfig(createConnectionConfig()); + ServerSession session2 = new ServerSession(connection2, new ServerSessionDelegate(), + new Binary(getName().getBytes()), 0 , connection2.getConfig()); + + assertFalse("Unexpected compare result", session1.compareTo(session2) == 0); + assertEquals("Unexpected compare result", 0, session1.compareTo(session1)); + } + + private MockConnectionConfig createConnectionConfig() + { + return new MockConnectionConfig(UUID.randomUUID(), null, null, + false, 1, _virtualHost, "address", Boolean.TRUE, Boolean.TRUE, Boolean.TRUE, + "authid", "remoteProcessName", new Integer(1967), new Integer(1970), _virtualHost.getConfigStore(), Boolean.FALSE); + } + +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/txn/MockStoreTransaction.java b/java/broker/src/test/java/org/apache/qpid/server/txn/MockStoreTransaction.java index fa0bb5be8b..af49238998 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/txn/MockStoreTransaction.java +++ b/java/broker/src/test/java/org/apache/qpid/server/txn/MockStoreTransaction.java @@ -20,19 +20,13 @@ */ package org.apache.qpid.server.txn; -import org.apache.commons.configuration.Configuration; import org.apache.commons.lang.NotImplementedException; - import org.apache.qpid.AMQStoreException; -import org.apache.qpid.server.logging.LogSubject; import org.apache.qpid.server.message.EnqueableMessage; import org.apache.qpid.server.store.MessageStore; -import org.apache.qpid.server.store.MessageStore.StoreFuture; -import org.apache.qpid.server.store.MessageStore.Transaction; -import org.apache.qpid.server.store.MessageStoreRecoveryHandler; -import org.apache.qpid.server.store.StorableMessageMetaData; -import org.apache.qpid.server.store.StoredMessage; -import org.apache.qpid.server.store.TransactionLogRecoveryHandler; +import org.apache.qpid.server.store.NullMessageStore; +import org.apache.qpid.server.store.StoreFuture; +import org.apache.qpid.server.store.Transaction; import org.apache.qpid.server.store.TransactionLogResource; /** @@ -112,44 +106,24 @@ class MockStoreTransaction implements Transaction _state = TransactionState.ABORTED; } - public static MessageStore createTestTransactionLog(final MockStoreTransaction storeTransaction) + public void removeXid(long format, byte[] globalId, byte[] branchId) { - return new MessageStore() - { - public void configureMessageStore(final String name, - final MessageStoreRecoveryHandler recoveryHandler, - final Configuration config, - final LogSubject logSubject) throws Exception - { - //TODO. - } - - public void close() throws Exception - { - //TODO. - } - - public <T extends StorableMessageMetaData> StoredMessage<T> addMessage(final T metaData) - { - return null; //TODO. - } - - public boolean isPersistent() - { - return false; //TODO. - } + } - public void configureTransactionLog(String name, TransactionLogRecoveryHandler recoveryHandler, - Configuration storeConfiguration, LogSubject logSubject) throws Exception - { - } + public void recordXid(long format, byte[] globalId, byte[] branchId, Record[] enqueues, Record[] dequeues) + { + } + public static MessageStore createTestTransactionLog(final MockStoreTransaction storeTransaction) + { + return new NullMessageStore() + { + @Override public Transaction newTransaction() { storeTransaction.setState(TransactionState.STARTED); return storeTransaction; } - - }; + }; } }
\ No newline at end of file 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 index 9df0aec545..8a34e92985 100644 --- 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 @@ -20,6 +20,8 @@ */ package org.apache.qpid.server.util; +import java.util.UUID; + import org.apache.commons.configuration.XMLConfiguration; import org.apache.qpid.AMQException; @@ -43,6 +45,7 @@ import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.registry.IApplicationRegistry; import org.apache.qpid.server.store.MessageStore; import org.apache.qpid.server.store.TestableMemoryMessageStore; +import org.apache.qpid.server.store.TestableMemoryMessageStoreFactory; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.test.utils.QpidTestCase; @@ -65,10 +68,10 @@ public class InternalBrokerBaseCase extends QpidTestCase super.setUp(); _configXml.addProperty("virtualhosts.virtualhost.name", "test"); - _configXml.addProperty("virtualhosts.virtualhost.test.store.class", TestableMemoryMessageStore.class.getName()); + _configXml.addProperty("virtualhosts.virtualhost.test.store.factoryclass", TestableMemoryMessageStoreFactory.class.getName()); _configXml.addProperty("virtualhosts.virtualhost(-1).name", getName()); - _configXml.addProperty("virtualhosts.virtualhost(-1)."+getName()+".store.class", TestableMemoryMessageStore.class.getName()); + _configXml.addProperty("virtualhosts.virtualhost(-1)."+getName()+".store.factoryclass", TestableMemoryMessageStoreFactory.class.getName()); createBroker(); } diff --git a/java/broker/src/test/java/org/apache/qpid/server/util/MapJsonSerializerTest.java b/java/broker/src/test/java/org/apache/qpid/server/util/MapJsonSerializerTest.java new file mode 100644 index 0000000000..56567523df --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/util/MapJsonSerializerTest.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.util; + +import java.util.HashMap; +import java.util.Map; + +import junit.framework.TestCase; + +public class MapJsonSerializerTest extends TestCase +{ + private MapJsonSerializer _serializer; + + protected void setUp() throws Exception + { + super.setUp(); + _serializer = new MapJsonSerializer(); + + } + + public void testSerializeDeserialize() + { + Map<String, Object> testMap = new HashMap<String, Object>(); + testMap.put("string", "Test String"); + testMap.put("integer", new Integer(10)); + testMap.put("long", new Long(Long.MAX_VALUE)); + testMap.put("boolean", Boolean.TRUE); + + String jsonString = _serializer.serialize(testMap); + Map<String, Object> deserializedMap = _serializer.deserialize(jsonString); + + assertEquals(deserializedMap, testMap); + } + +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/virtualhost/MockVirtualHost.java b/java/broker/src/test/java/org/apache/qpid/server/virtualhost/MockVirtualHost.java index 5fe664acdd..91174c5d10 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/virtualhost/MockVirtualHost.java +++ b/java/broker/src/test/java/org/apache/qpid/server/virtualhost/MockVirtualHost.java @@ -20,6 +20,7 @@ */ package org.apache.qpid.server.virtualhost; +import java.util.concurrent.ScheduledFuture; import org.apache.qpid.server.binding.BindingFactory; import org.apache.qpid.server.configuration.BrokerConfig; import org.apache.qpid.server.configuration.ConfigStore; @@ -32,13 +33,14 @@ import org.apache.qpid.server.exchange.ExchangeFactory; import org.apache.qpid.server.exchange.ExchangeRegistry; import org.apache.qpid.server.federation.BrokerLink; import org.apache.qpid.server.management.ManagedObject; +import org.apache.qpid.server.protocol.v1_0.LinkRegistry; import org.apache.qpid.server.queue.QueueRegistry; import org.apache.qpid.server.registry.IApplicationRegistry; import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.security.auth.manager.AuthenticationManager; import org.apache.qpid.server.stats.StatisticsCounter; -import org.apache.qpid.server.store.DurableConfigurationStore; import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.txn.DtxRegistry; import java.util.Map; import java.util.UUID; @@ -94,17 +96,17 @@ public class MockVirtualHost implements VirtualHost return null; } - public VirtualHostConfiguration getConfiguration() + public DtxRegistry getDtxRegistry() { return null; } - public IConnectionRegistry getConnectionRegistry() + public VirtualHostConfiguration getConfiguration() { return null; } - public DurableConfigurationStore getDurableConfigurationStore() + public IConnectionRegistry getConnectionRegistry() { return null; } @@ -170,6 +172,16 @@ public class MockVirtualHost implements VirtualHost } + public LinkRegistry getLinkRegistry(String remoteContainerId) + { + return null; + } + + public ScheduledFuture<?> scheduleTask(long delay, Runnable timeoutTask) + { + return null; + } + public void scheduleHouseKeepingTask(long period, HouseKeepingTask task) { @@ -269,4 +281,10 @@ public class MockVirtualHost implements VirtualHost { } + + @Override + public State getState() + { + return State.ACTIVE; + } }
\ No newline at end of file diff --git a/java/broker/src/test/java/org/apache/qpid/server/virtualhost/VirtualHostImplTest.java b/java/broker/src/test/java/org/apache/qpid/server/virtualhost/VirtualHostImplTest.java index df7b4da426..87eb0f9d16 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/virtualhost/VirtualHostImplTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/virtualhost/VirtualHostImplTest.java @@ -27,7 +27,7 @@ import org.apache.qpid.server.configuration.ServerConfiguration; import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.store.TestableMemoryMessageStore; +import org.apache.qpid.server.store.MemoryMessageStoreFactory; import org.apache.qpid.server.util.TestApplicationRegistry; import org.apache.qpid.test.utils.QpidTestCase; @@ -68,31 +68,6 @@ public class VirtualHostImplTest extends QpidTestCase customBindingTestImpl(new String[0]); } - private void customBindingTestImpl(final String[] routingKeys) throws Exception - { - String exchangeName = getName() +".direct"; - String vhostName = getName(); - String queueName = getName(); - - File config = writeConfigFile(vhostName, queueName, exchangeName, false, routingKeys); - VirtualHost vhost = createVirtualHost(vhostName, config); - assertNotNull("virtualhost should exist", vhost); - - AMQQueue queue = vhost.getQueueRegistry().getQueue(queueName); - assertNotNull("queue should exist", queue); - - Exchange defaultExch = vhost.getExchangeRegistry().getDefaultExchange(); - assertTrue("queue should have been bound to default exchange with its name", defaultExch.isBound(queueName, queue)); - - Exchange exch = vhost.getExchangeRegistry().getExchange(exchangeName); - assertTrue("queue should have been bound to " + exchangeName + " with its name", exch.isBound(queueName, queue)); - - for(String key: routingKeys) - { - assertTrue("queue should have been bound to " + exchangeName + " with key " + key, exch.isBound(key, queue)); - } - } - /** * Tests that specifying custom routing keys for a queue in the configuration file results in failure * to create the vhost (since this is illegal, only queue names are used with the default exchange) @@ -106,12 +81,32 @@ public class VirtualHostImplTest extends QpidTestCase createVirtualHost(getName(), config); fail("virtualhost creation should have failed due to illegal configuration"); } - catch (ConfigurationException e) + catch (RuntimeException e) { + assertEquals(ConfigurationException.class, e.getCause().getClass()); //expected } } + public void testVirtualHostBecomesActive() throws Exception + { + File config = writeConfigFile(getName(), getName(), getName() +".direct", false, new String[0]); + VirtualHost vhost = createVirtualHost(getName(), config); + assertNotNull(vhost); + assertEquals(State.ACTIVE, vhost.getState()); + } + + public void testVirtualHostBecomesStoppedOnClose() throws Exception + { + File config = writeConfigFile(getName(), getName(), getName() +".direct", false, new String[0]); + VirtualHost vhost = createVirtualHost(getName(), config); + assertNotNull(vhost); + assertEquals(State.ACTIVE, vhost.getState()); + vhost.close(); + assertEquals(State.STOPPED, vhost.getState()); + assertEquals(0, vhost.getHouseKeepingActiveCount()); + } + /** * Tests that specifying an unknown exchange to bind the queue to results in failure to create the vhost */ @@ -124,12 +119,39 @@ public class VirtualHostImplTest extends QpidTestCase createVirtualHost(getName(), config); fail("virtualhost creation should have failed due to illegal configuration"); } - catch (ConfigurationException e) + catch (RuntimeException e) { + assertEquals(ConfigurationException.class, e.getCause().getClass()); //expected } } + private void customBindingTestImpl(final String[] routingKeys) throws Exception + { + String exchangeName = getName() +".direct"; + String vhostName = getName(); + String queueName = getName(); + + File config = writeConfigFile(vhostName, queueName, exchangeName, false, routingKeys); + VirtualHost vhost = createVirtualHost(vhostName, config); + assertNotNull("virtualhost should exist", vhost); + + AMQQueue queue = vhost.getQueueRegistry().getQueue(queueName); + assertNotNull("queue should exist", queue); + + Exchange defaultExch = vhost.getExchangeRegistry().getDefaultExchange(); + assertTrue("queue should have been bound to default exchange with its name", defaultExch.isBound(queueName, queue)); + + Exchange exch = vhost.getExchangeRegistry().getExchange(exchangeName); + assertTrue("queue should have been bound to " + exchangeName + " with its name", exch.isBound(queueName, queue)); + + for(String key: routingKeys) + { + assertTrue("queue should have been bound to " + exchangeName + " with key " + key, exch.isBound(key, queue)); + } + } + + private VirtualHost createVirtualHost(String vhostName, File config) throws Exception { _configuration = new ServerConfiguration(new XMLConfiguration(config)); @@ -167,11 +189,11 @@ public class VirtualHostImplTest extends QpidTestCase writer.write("<virtualhosts>"); writer.write(" <default>" + vhostName + "</default>"); writer.write(" <virtualhost>"); - writer.write(" <store>"); - writer.write(" <class>" + TestableMemoryMessageStore.class.getName() + "</class>"); - writer.write(" </store>"); writer.write(" <name>" + vhostName + "</name>"); writer.write(" <" + vhostName + ">"); + writer.write(" <store>"); + writer.write(" <factoryclass>" + MemoryMessageStoreFactory.class.getName() + "</factoryclass>"); + writer.write(" </store>"); if(exchangeName != null && !dontDeclare) { writer.write(" <exchanges>"); |
