diff options
| author | Aidan Skinner <aidan@apache.org> | 2008-04-24 01:54:20 +0000 |
|---|---|---|
| committer | Aidan Skinner <aidan@apache.org> | 2008-04-24 01:54:20 +0000 |
| commit | 559e9702d5a7c26dddee708e267f2f685d4de89e (patch) | |
| tree | b3114d58b39092b4699186533a50553715e42b96 /java/client/src | |
| parent | 04fe7be0efbc3687a5a302fea6fbec82adbfedae (diff) | |
| download | qpid-python-559e9702d5a7c26dddee708e267f2f685d4de89e.tar.gz | |
QPID-832 merge M2.x
git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid@651133 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'java/client/src')
91 files changed, 4355 insertions, 1480 deletions
diff --git a/java/client/src/main/grammar/SelectorParser.jj b/java/client/src/main/grammar/SelectorParser.jj index 2dca11748e..a72da526ae 100644 --- a/java/client/src/main/grammar/SelectorParser.jj +++ b/java/client/src/main/grammar/SelectorParser.jj @@ -170,6 +170,8 @@ TOKEN [IGNORE_CASE] : TOKEN [IGNORE_CASE] :
{
< ID : ["a"-"z", "_", "$"] (["a"-"z","0"-"9","_", "$"])* >
+ | < QUOTED_ID : "\"" ( ("\"\"") | ~["\""] )* "\"" >
+
}
// ----------------------------------------------------------------------------
@@ -577,6 +579,7 @@ String stringLitteral() : PropertyExpression variable() :
{
Token t;
+ StringBuffer rc = new StringBuffer();
PropertyExpression left=null;
}
{
@@ -585,6 +588,20 @@ PropertyExpression variable() : {
left = new PropertyExpression(t.image);
}
+ |
+ t = <QUOTED_ID>
+ {
+ // Decode the sting value.
+ String image = t.image;
+ for( int i=1; i < image.length()-1; i++ ) {
+ char c = image.charAt(i);
+ if( c == '"' )
+ i++;
+ rc.append(c);
+ }
+ return new PropertyExpression(rc.toString());
+ }
+
)
{
return left;
diff --git a/java/client/src/main/java/org/apache/mina/transport/socket/nio/ExistingSocketConnector.java b/java/client/src/main/java/org/apache/mina/transport/socket/nio/ExistingSocketConnector.java new file mode 100644 index 0000000000..98716c0c3c --- /dev/null +++ b/java/client/src/main/java/org/apache/mina/transport/socket/nio/ExistingSocketConnector.java @@ -0,0 +1,478 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.mina.transport.socket.nio; + +import edu.emory.mathcs.backport.java.util.concurrent.Executor; +import org.apache.mina.common.ConnectFuture; +import org.apache.mina.common.ExceptionMonitor; +import org.apache.mina.common.IoConnector; +import org.apache.mina.common.IoConnectorConfig; +import org.apache.mina.common.IoHandler; +import org.apache.mina.common.IoServiceConfig; +import org.apache.mina.common.support.BaseIoConnector; +import org.apache.mina.common.support.DefaultConnectFuture; +import org.apache.mina.util.NamePreservingRunnable; +import org.apache.mina.util.NewThreadExecutor; +import org.apache.mina.util.Queue; + +import java.io.IOException; +import java.net.ConnectException; +import java.net.Socket; +import java.net.SocketAddress; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.SocketChannel; +import java.util.Iterator; +import java.util.Set; + +/** + * {@link IoConnector} for socket transport (TCP/IP). + * + * @author The Apache Directory Project (mina-dev@directory.apache.org) + * @version $Rev: 627427 $, $Date: 2008-02-13 14:39:10 +0000 (Wed, 13 Feb 2008) $ + */ +public class ExistingSocketConnector extends BaseIoConnector +{ + /** @noinspection StaticNonFinalField */ + private static volatile int nextId = 0; + + private final Object lock = new Object(); + private final int id = nextId++; + private final String threadName = "SocketConnector-" + id; + private SocketConnectorConfig defaultConfig = new SocketConnectorConfig(); + private final Queue connectQueue = new Queue(); + private final SocketIoProcessor[] ioProcessors; + private final int processorCount; + private final Executor executor; + + /** @noinspection FieldAccessedSynchronizedAndUnsynchronized */ + private Selector selector; + private Worker worker; + private int processorDistributor = 0; + private int workerTimeout = 60; // 1 min. + private Socket _openSocket = null; + + /** Create a connector with a single processing thread using a NewThreadExecutor */ + public ExistingSocketConnector() + { + this(1, new NewThreadExecutor()); + } + + /** + * Create a connector with the desired number of processing threads + * + * @param processorCount Number of processing threads + * @param executor Executor to use for launching threads + */ + public ExistingSocketConnector(int processorCount, Executor executor) + { + if (processorCount < 1) + { + throw new IllegalArgumentException("Must have at least one processor"); + } + + this.executor = executor; + this.processorCount = processorCount; + ioProcessors = new SocketIoProcessor[processorCount]; + + for (int i = 0; i < processorCount; i++) + { + ioProcessors[i] = new SocketIoProcessor("SocketConnectorIoProcessor-" + id + "." + i, executor); + } + } + + /** + * How many seconds to keep the connection thread alive between connection requests + * + * @return Number of seconds to keep connection thread alive + */ + public int getWorkerTimeout() + { + return workerTimeout; + } + + /** + * Set how many seconds the connection worker thread should remain alive once idle before terminating itself. + * + * @param workerTimeout Number of seconds to keep thread alive. Must be >=0 + */ + public void setWorkerTimeout(int workerTimeout) + { + if (workerTimeout < 0) + { + throw new IllegalArgumentException("Must be >= 0"); + } + this.workerTimeout = workerTimeout; + } + + public ConnectFuture connect(SocketAddress address, IoHandler handler, IoServiceConfig config) + { + return connect(address, null, handler, config); + } + + public ConnectFuture connect(SocketAddress address, SocketAddress localAddress, + IoHandler handler, IoServiceConfig config) + { + /** Changes here from the Mina OpenSocketConnector. + * Ignoreing all address as they are not needed */ + + if (handler == null) + { + throw new NullPointerException("handler"); + } + + + if (config == null) + { + config = getDefaultConfig(); + } + + if (_openSocket == null) + { + throw new IllegalArgumentException("Specifed Socket not active"); + } + + boolean success = false; + + try + { + DefaultConnectFuture future = new DefaultConnectFuture(); + newSession(_openSocket, handler, config, future); + success = true; + return future; + } + catch (IOException e) + { + return DefaultConnectFuture.newFailedFuture(e); + } + finally + { + if (!success && _openSocket != null) + { + try + { + _openSocket.close(); + } + catch (IOException e) + { + ExceptionMonitor.getInstance().exceptionCaught(e); + } + } + } + } + + public IoServiceConfig getDefaultConfig() + { + return defaultConfig; + } + + /** + * Sets the config this connector will use by default. + * + * @param defaultConfig the default config. + * + * @throws NullPointerException if the specified value is <code>null</code>. + */ + public void setDefaultConfig(SocketConnectorConfig defaultConfig) + { + if (defaultConfig == null) + { + throw new NullPointerException("defaultConfig"); + } + this.defaultConfig = defaultConfig; + } + + private synchronized void startupWorker() throws IOException + { + if (worker == null) + { + selector = Selector.open(); + worker = new Worker(); + executor.execute(new NamePreservingRunnable(worker)); + } + } + + private void registerNew() + { + if (connectQueue.isEmpty()) + { + return; + } + + for (; ;) + { + ConnectionRequest req; + synchronized (connectQueue) + { + req = (ConnectionRequest) connectQueue.pop(); + } + + if (req == null) + { + break; + } + + SocketChannel ch = req.channel; + try + { + ch.register(selector, SelectionKey.OP_CONNECT, req); + } + catch (IOException e) + { + req.setException(e); + } + } + } + + private void processSessions(Set keys) + { + Iterator it = keys.iterator(); + + while (it.hasNext()) + { + SelectionKey key = (SelectionKey) it.next(); + + if (!key.isConnectable()) + { + continue; + } + + SocketChannel ch = (SocketChannel) key.channel(); + ConnectionRequest entry = (ConnectionRequest) key.attachment(); + + boolean success = false; + try + { + ch.finishConnect(); + newSession(ch, entry.handler, entry.config, entry); + success = true; + } + catch (Throwable e) + { + entry.setException(e); + } + finally + { + key.cancel(); + if (!success) + { + try + { + ch.close(); + } + catch (IOException e) + { + ExceptionMonitor.getInstance().exceptionCaught(e); + } + } + } + } + + keys.clear(); + } + + private void processTimedOutSessions(Set keys) + { + long currentTime = System.currentTimeMillis(); + Iterator it = keys.iterator(); + + while (it.hasNext()) + { + SelectionKey key = (SelectionKey) it.next(); + + if (!key.isValid()) + { + continue; + } + + ConnectionRequest entry = (ConnectionRequest) key.attachment(); + + if (currentTime >= entry.deadline) + { + entry.setException(new ConnectException()); + try + { + key.channel().close(); + } + catch (IOException e) + { + ExceptionMonitor.getInstance().exceptionCaught(e); + } + finally + { + key.cancel(); + } + } + } + } + + private void newSession(Socket socket, IoHandler handler, IoServiceConfig config, ConnectFuture connectFuture) + throws IOException + { + SocketSessionImpl session = new SocketSessionImpl(this, + nextProcessor(), + getListeners(), + config, + socket.getChannel(), + handler, + socket.getRemoteSocketAddress()); + + newSession(session, config, connectFuture); + } + + private void newSession(SocketChannel ch, IoHandler handler, IoServiceConfig config, ConnectFuture connectFuture) + throws IOException + + { + SocketSessionImpl session = new SocketSessionImpl(this, + nextProcessor(), + getListeners(), + config, + ch, + handler, + ch.socket().getRemoteSocketAddress()); + + newSession(session, config, connectFuture); + } + + private void newSession(SocketSessionImpl session, IoServiceConfig config, ConnectFuture connectFuture) + throws IOException + { + try + { + getFilterChainBuilder().buildFilterChain(session.getFilterChain()); + config.getFilterChainBuilder().buildFilterChain(session.getFilterChain()); + config.getThreadModel().buildFilterChain(session.getFilterChain()); + } + catch (Throwable e) + { + throw (IOException) new IOException("Failed to create a session.").initCause(e); + } + session.getIoProcessor().addNew(session); + connectFuture.setSession(session); + } + + private SocketIoProcessor nextProcessor() + { + return ioProcessors[processorDistributor++ % processorCount]; + } + + public void setOpenSocket(Socket openSocket) + { + _openSocket = openSocket; + } + + private class Worker implements Runnable + { + private long lastActive = System.currentTimeMillis(); + + public void run() + { + Thread.currentThread().setName(ExistingSocketConnector.this.threadName); + + for (; ;) + { + try + { + int nKeys = selector.select(1000); + + registerNew(); + + if (nKeys > 0) + { + processSessions(selector.selectedKeys()); + } + + processTimedOutSessions(selector.keys()); + + if (selector.keys().isEmpty()) + { + if (System.currentTimeMillis() - lastActive > workerTimeout * 1000L) + { + synchronized (lock) + { + if (selector.keys().isEmpty() && + connectQueue.isEmpty()) + { + worker = null; + try + { + selector.close(); + } + catch (IOException e) + { + ExceptionMonitor.getInstance().exceptionCaught(e); + } + finally + { + selector = null; + } + break; + } + } + } + } + else + { + lastActive = System.currentTimeMillis(); + } + } + catch (IOException e) + { + ExceptionMonitor.getInstance().exceptionCaught(e); + + try + { + Thread.sleep(1000); + } + catch (InterruptedException e1) + { + ExceptionMonitor.getInstance().exceptionCaught(e1); + } + } + } + } + } + + private class ConnectionRequest extends DefaultConnectFuture + { + private final SocketChannel channel; + private final long deadline; + private final IoHandler handler; + private final IoServiceConfig config; + + private ConnectionRequest(SocketChannel channel, IoHandler handler, IoServiceConfig config) + { + this.channel = channel; + long timeout; + if (config instanceof IoConnectorConfig) + { + timeout = ((IoConnectorConfig) config).getConnectTimeoutMillis(); + } + else + { + timeout = ((IoConnectorConfig) getDefaultConfig()).getConnectTimeoutMillis(); + } + this.deadline = System.currentTimeMillis() + timeout; + this.handler = handler; + this.config = config; + } + } +} diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQAuthenticationException.java b/java/client/src/main/java/org/apache/qpid/client/AMQAuthenticationException.java index 6bae0166d1..05ac3dca9e 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQAuthenticationException.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQAuthenticationException.java @@ -39,4 +39,9 @@ public class AMQAuthenticationException extends AMQException { super(error, msg, cause); } + public boolean isHardError() + { + return true; + } + } diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java b/java/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java index b35f73d84a..f051450260 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java @@ -58,8 +58,9 @@ public class AMQBrokerDetails implements BrokerDetails if (transport != null) { //todo this list of valid transports should be enumerated somewhere - if ((!(transport.equalsIgnoreCase("vm") || - transport.equalsIgnoreCase("tcp")))) + if ((!(transport.equalsIgnoreCase(BrokerDetails.VM) || + transport.equalsIgnoreCase(BrokerDetails.TCP) || + transport.equalsIgnoreCase(BrokerDetails.SOCKET)))) { if (transport.equalsIgnoreCase("localhost")) { @@ -164,7 +165,10 @@ public class AMQBrokerDetails implements BrokerDetails } else { - setPort(port); + if (!_transport.equalsIgnoreCase(SOCKET)) + { + setPort(port); + } } String queryString = connection.getQuery(); @@ -271,13 +275,16 @@ public class AMQBrokerDetails implements BrokerDetails sb.append(_transport); sb.append("://"); - if (!(_transport.equalsIgnoreCase("vm"))) + if (!(_transport.equalsIgnoreCase(VM))) { sb.append(_host); } - sb.append(':'); - sb.append(_port); + if (!(_transport.equalsIgnoreCase(SOCKET))) + { + sb.append(':'); + sb.append(_port); + } sb.append(printOptionsURL()); diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java b/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java index 8ab16c65c0..3969eef8a9 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java @@ -1,5 +1,5 @@ /* - * +* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information @@ -20,17 +20,31 @@ */ package org.apache.qpid.client; -import java.io.IOException; -import java.net.ConnectException; -import java.nio.channels.UnresolvedAddressException; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.Map; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; +import org.apache.qpid.AMQConnectionFailureException; +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQProtocolException; +import org.apache.qpid.AMQUndeliveredException; +import org.apache.qpid.AMQUnresolvedAddressException; +import org.apache.qpid.client.failover.FailoverException; +import org.apache.qpid.client.failover.FailoverProtectedOperation; +import org.apache.qpid.client.failover.FailoverRetrySupport; +import org.apache.qpid.client.protocol.AMQProtocolHandler; +import org.apache.qpid.client.state.AMQState; +import org.apache.qpid.client.state.AMQStateManager; +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.*; +import org.apache.qpid.jms.BrokerDetails; +import org.apache.qpid.jms.ChannelLimitReachedException; +import org.apache.qpid.jms.Connection; +import org.apache.qpid.jms.ConnectionListener; +import org.apache.qpid.jms.ConnectionURL; +import org.apache.qpid.jms.FailoverPolicy; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.url.URLSyntaxException; +import org.apache.qpidity.transport.TransportConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.jms.ConnectionConsumer; import javax.jms.ConnectionMetaData; @@ -49,25 +63,117 @@ import javax.naming.NamingException; import javax.naming.Reference; import javax.naming.Referenceable; import javax.naming.StringRefAddr; - -import org.apache.qpid.*; -import org.apache.qpid.client.failover.FailoverException; -import org.apache.qpid.client.protocol.AMQProtocolHandler; -import org.apache.qpid.exchange.ExchangeDefaults; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.jms.BrokerDetails; -import org.apache.qpid.jms.Connection; -import org.apache.qpid.jms.ConnectionListener; -import org.apache.qpid.jms.ConnectionURL; -import org.apache.qpid.jms.FailoverPolicy; -import org.apache.qpid.protocol.AMQConstant; -import org.apache.qpid.url.URLSyntaxException; -import org.apache.qpidity.transport.TransportConstants; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import java.io.IOException; +import java.net.ConnectException; +import java.nio.channels.UnresolvedAddressException; +import java.text.MessageFormat; +import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; public class AMQConnection extends Closeable implements Connection, QueueConnection, TopicConnection, Referenceable { + public static final class ChannelToSessionMap + { + private final AMQSession[] _fastAccessSessions = new AMQSession[16]; + private final LinkedHashMap<Integer, AMQSession> _slowAccessSessions = new LinkedHashMap<Integer, AMQSession>(); + private int _size = 0; + private static final int FAST_CHANNEL_ACCESS_MASK = 0xFFFFFFF0; + + public AMQSession get(int channelId) + { + if((channelId & FAST_CHANNEL_ACCESS_MASK) == 0) + { + return _fastAccessSessions[channelId]; + } + else + { + return _slowAccessSessions.get(channelId); + } + } + + public AMQSession put(int channelId, AMQSession session) + { + AMQSession oldVal; + if((channelId & FAST_CHANNEL_ACCESS_MASK) == 0) + { + oldVal = _fastAccessSessions[channelId]; + _fastAccessSessions[channelId] = session; + } + else + { + oldVal = _slowAccessSessions.put(channelId, session); + } + if((oldVal != null) && (session == null)) + { + _size--; + } + else if((oldVal == null) && (session != null)) + { + _size++; + } + + return session; + + } + + + public AMQSession remove(int channelId) + { + AMQSession session; + if((channelId & FAST_CHANNEL_ACCESS_MASK) == 0) + { + session = _fastAccessSessions[channelId]; + _fastAccessSessions[channelId] = null; + } + else + { + session = _slowAccessSessions.remove(channelId); + } + + if(session != null) + { + _size--; + } + return session; + + } + + public Collection<AMQSession> values() + { + ArrayList<AMQSession> values = new ArrayList<AMQSession>(size()); + + for(int i = 0; i < 16; i++) + { + if(_fastAccessSessions[i] != null) + { + values.add(_fastAccessSessions[i]); + } + } + values.addAll(_slowAccessSessions.values()); + + return values; + } + + public int size() + { + return _size; + } + + public void clear() + { + _size = 0; + _slowAccessSessions.clear(); + for(int i = 0; i<16; i++) + { + _fastAccessSessions[i] = null; + } + } + } + + private static final Logger _logger = LoggerFactory.getLogger(AMQConnection.class); protected AtomicInteger _idFactory = new AtomicInteger(0); @@ -76,7 +182,9 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect * This is the "root" mutex that must be held when doing anything that could be impacted by failover. This must be * held by any child objects of this connection such as the session, producers and consumers. */ - protected final Object _failoverMutex = new Object(); + private final Object _failoverMutex = new Object(); + + private final Object _sessionCreationLock = new Object(); /** * A channel is roughly analogous to a session. The server can negotiate the maximum number of channels per session @@ -85,7 +193,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect protected long _maximumChannelCount; /** The maximum size of frame supported by the server */ - protected long _maximumFrameSize; + private long _maximumFrameSize; /** * The protocol handler dispatches protocol events for this connection. For example, when the connection is dropped @@ -95,30 +203,31 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect protected AMQProtocolHandler _protocolHandler; /** Maps from session id (Integer) to AMQSession instance */ - protected final Map<Integer, AMQSession> _sessions = new LinkedHashMap<Integer, AMQSession>(); + private final ChannelToSessionMap _sessions = new ChannelToSessionMap(); - protected String _clientName; + private String _clientName; /** The user name to use for authentication */ - protected String _username; + private String _username; /** The password to use for authentication */ - protected String _password; + private String _password; /** The virtual path to connect to on the AMQ server */ - protected String _virtualHost; + private String _virtualHost; + protected ExceptionListener _exceptionListener; - protected ConnectionListener _connectionListener; + private ConnectionListener _connectionListener; - protected ConnectionURL _connectionURL; + private ConnectionURL _connectionURL; /** * Whether this connection is started, i.e. whether messages are flowing to consumers. It has no meaning for message * publication. */ - protected boolean _started; + protected volatile boolean _started; /** Policy dictating how to failover */ protected FailoverPolicy _failoverPolicy; @@ -136,22 +245,23 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect /* * The connection meta data */ - protected QpidConnectionMetaData _connectionMetaData; + private QpidConnectionMetaData _connectionMetaData; /** Configuration info for SSL */ - protected SSLConfiguration _sslConfiguration; + private SSLConfiguration _sslConfiguration; - protected AMQShortString _defaultTopicExchangeName = ExchangeDefaults.TOPIC_EXCHANGE_NAME; - protected AMQShortString _defaultQueueExchangeName = ExchangeDefaults.DIRECT_EXCHANGE_NAME; - protected AMQShortString _temporaryTopicExchangeName = ExchangeDefaults.TOPIC_EXCHANGE_NAME; - protected AMQShortString _temporaryQueueExchangeName = ExchangeDefaults.DIRECT_EXCHANGE_NAME; + private AMQShortString _defaultTopicExchangeName = ExchangeDefaults.TOPIC_EXCHANGE_NAME; + private AMQShortString _defaultQueueExchangeName = ExchangeDefaults.DIRECT_EXCHANGE_NAME; + private AMQShortString _temporaryTopicExchangeName = ExchangeDefaults.TOPIC_EXCHANGE_NAME; + private AMQShortString _temporaryQueueExchangeName = ExchangeDefaults.DIRECT_EXCHANGE_NAME; /** Thread Pool for executing connection level processes. Such as returning bounced messages. */ - protected final ExecutorService _taskPool = Executors.newCachedThreadPool(); - protected static final long DEFAULT_TIMEOUT = 1000 * 30; + private final ExecutorService _taskPool = Executors.newCachedThreadPool(); + private static final long DEFAULT_TIMEOUT = 1000 * 30; + private ProtocolVersion _protocolVersion = ProtocolVersion.v0_9; // FIXME TGM, shouldn't need this protected AMQConnectionDelegate _delegate; - + // this connection maximum number of prefetched messages private long _maxPrefetch; @@ -259,7 +369,8 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect } _failoverPolicy = new FailoverPolicy(connectionURL); - if (_failoverPolicy.getCurrentBrokerDetails().getTransport().equals(BrokerDetails.VM)) + BrokerDetails brokerDetails = _failoverPolicy.getNextBrokerDetails(); + if (brokerDetails.getTransport().equals(BrokerDetails.VM)) { _delegate = new AMQConnectionDelegate_0_8(this); } @@ -272,6 +383,26 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect _delegate = new AMQConnectionDelegate_0_10(this); } + final ArrayList<JMSException> exceptions = new ArrayList<JMSException>(); + + class Listener implements ExceptionListener + { + public void onException(JMSException e) + { + exceptions.add(e); + } + } + + try + { + setExceptionListener(new Listener()); + } + catch (JMSException e) + { + // Shouldn't happen + throw new AMQException(null, null, e); + } + if (_logger.isInfoEnabled()) { _logger.info("Connection:" + connectionURL); @@ -288,6 +419,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect _clientName = connectionURL.getClientName(); _username = connectionURL.getUsername(); _password = connectionURL.getPassword(); + setVirtualHost(connectionURL.getVirtualHost()); if (connectionURL.getDefaultQueueExchangeName() != null) @@ -311,7 +443,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect } - _protocolHandler = new AMQProtocolHandler(this); + _protocolHandler = new AMQProtocolHandler(this); // We are not currently connected _connected = false; @@ -319,11 +451,13 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect Exception lastException = new Exception(); lastException.initCause(new ConnectException()); - while (!_connected && _failoverPolicy.failoverAllowed()) + // TMG FIXME this seems... wrong... + boolean retryAllowed = true; + while (!_connected && retryAllowed ) { try { - makeBrokerConnection(_failoverPolicy.getNextBrokerDetails()); + makeBrokerConnection(brokerDetails); lastException = null; _connected = true; } @@ -346,8 +480,10 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect if (_logger.isInfoEnabled()) { _logger.info("Unable to connect to broker at " + _failoverPolicy.getCurrentBrokerDetails(), - e.getCause()); + e.getCause()); } + retryAllowed = _failoverPolicy.failoverAllowed(); + brokerDetails = _failoverPolicy.getNextBrokerDetails(); } } @@ -359,8 +495,31 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect if (!_connected) { String message = null; + try + { + Thread.sleep(150); + } + catch (InterruptedException e) + { + // Eat it, we've hopefully got all the exceptions if this happened + } + if (exceptions.size() > 0) + { + JMSException e = exceptions.get(0); + int code = -1; + try + { + code = new Integer(e.getErrorCode()).intValue(); + } + catch (NumberFormatException nfe) + { + // Ignore this, we have some error codes and messages swapped around + } - if (lastException != null) + throw new AMQConnectionFailureException(AMQConstant.getConstant(code), + e.getMessage(), e); + } + else if (lastException != null) { if (lastException.getCause() != null) { @@ -397,7 +556,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect if (e.getCause() != null) { e.initCause(lastException); - } + } } throw e; @@ -406,6 +565,18 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect _connectionMetaData = new QpidConnectionMetaData(this); } + protected boolean checkException(Throwable thrown) + { + Throwable cause = thrown.getCause(); + + if (cause == null) + { + cause = thrown; + } + + return ((cause instanceof ConnectException) || (cause instanceof UnresolvedAddressException)); + } + private void getDelegate() throws AMQProtocolException { try @@ -444,20 +615,6 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect _virtualHost = virtualHost; } - protected boolean checkException(Throwable thrown) - { - Throwable cause = thrown.getCause(); - - if (cause == null) - { - cause = thrown; - } - - return ((cause instanceof ConnectException) || (cause instanceof UnresolvedAddressException)); - } - - - public boolean attemptReconnection(String host, int port) { BrokerDetails bd = new AMQBrokerDetails(host, port, _sslConfiguration); @@ -559,8 +716,55 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect public org.apache.qpid.jms.Session createSession(final boolean transacted, final int acknowledgeMode, final int prefetchHigh, final int prefetchLow) throws JMSException { + synchronized (_sessionCreationLock) + { + checkNotClosed(); + return _delegate.createSession(transacted, acknowledgeMode, prefetchHigh, prefetchLow); + } + } + + private void createChannelOverWire(int channelId, int prefetchHigh, int prefetchLow, boolean transacted) + throws AMQException, FailoverException + { + + ChannelOpenBody channelOpenBody = getProtocolHandler().getMethodRegistry().createChannelOpenBody(null); + + // TODO: Be aware of possible changes to parameter order as versions change. + + _protocolHandler.syncWrite(channelOpenBody.generateFrame(channelId), ChannelOpenOkBody.class); + + BasicQosBody basicQosBody = getProtocolHandler().getMethodRegistry().createBasicQosBody(0, prefetchHigh, false); + + // todo send low water mark when protocol allows. + // todo Be aware of possible changes to parameter order as versions change. + _protocolHandler.syncWrite(basicQosBody.generateFrame(channelId), BasicQosOkBody.class); + + if (transacted) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Issuing TxSelect for " + channelId); + } - return _delegate.createSession(transacted, acknowledgeMode, prefetchHigh, prefetchLow); + TxSelectBody body = getProtocolHandler().getMethodRegistry().createTxSelectBody(); + + // TODO: Be aware of possible changes to parameter order as versions change. + _protocolHandler.syncWrite(body.generateFrame(channelId), TxSelectOkBody.class); + } + } + + private void reopenChannel(int channelId, int prefetchHigh, int prefetchLow, boolean transacted) + throws AMQException, FailoverException + { + try + { + createChannelOverWire(channelId, prefetchHigh, prefetchLow, transacted); + } + catch (AMQException e) + { + deregisterSession(channelId); + throw new AMQException(null, "Error reopening channel " + channelId + " after failover: " + e, e); + } } public void setFailoverPolicy(FailoverPolicy policy) @@ -659,10 +863,10 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect if (!_started) { _started = true; - final Iterator it = _sessions.entrySet().iterator(); + final Iterator it = _sessions.values().iterator(); while (it.hasNext()) { - final AMQSession s = (AMQSession) ((Map.Entry) it.next()).getValue(); + final AMQSession s = (AMQSession) (it.next()); try { s.start(); @@ -673,6 +877,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect } } + } } @@ -704,49 +909,89 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect public void close(long timeout) throws JMSException { - synchronized (getFailoverMutex()) + close(new ArrayList<AMQSession>(_sessions.values()),timeout); + } + + public void close(List<AMQSession> sessions, long timeout) throws JMSException + { + synchronized(_sessionCreationLock) { - if (!_closed.getAndSet(true)) + if(!sessions.isEmpty()) { - try + AMQSession session = sessions.remove(0); + synchronized(session.getMessageDeliveryLock()) { - long startCloseTime = System.currentTimeMillis(); - - _taskPool.shutdown(); - closeAllSessions(null, timeout, startCloseTime); - - if (!_taskPool.isTerminated()) + close(sessions, timeout); + } + } + else + { + if (!_closed.getAndSet(true)) + { + synchronized (getFailoverMutex()) { try { - // adjust timeout - long taskPoolTimeout = adjustTimeout(timeout, startCloseTime); + long startCloseTime = System.currentTimeMillis(); + + closeAllSessions(null, timeout, startCloseTime); + + //This MUST occur after we have successfully closed all Channels/Sessions + _taskPool.shutdown(); - _taskPool.awaitTermination(taskPoolTimeout, TimeUnit.MILLISECONDS); + if (!_taskPool.isTerminated()) + { + try + { + // adjust timeout + long taskPoolTimeout = adjustTimeout(timeout, startCloseTime); + + _taskPool.awaitTermination(taskPoolTimeout, TimeUnit.MILLISECONDS); + } + catch (InterruptedException e) + { + _logger.info("Interrupted while shutting down connection thread pool."); + } + } + + // adjust timeout + timeout = adjustTimeout(timeout, startCloseTime); + _delegate.closeConneciton(timeout); + + //If the taskpool hasn't shutdown by now then give it shutdownNow. + // This will interupt any running tasks. + if (!_taskPool.isTerminated()) + { + List<Runnable> tasks = _taskPool.shutdownNow(); + for (Runnable r : tasks) + { + _logger.warn("Connection close forced taskpool to prevent execution:" + r); + } + } } - catch (InterruptedException e) + catch (AMQException e) { - _logger.info("Interrupted while shutting down connection thread pool."); + JMSException jmse = new JMSException("Error closing connection: " + e); + jmse.setLinkedException(e); + throw jmse; } } - - // adjust timeout - timeout = adjustTimeout(timeout, startCloseTime); - _delegate.closeConneciton(timeout); - //_protocolHandler.closeConnection(timeout); - - } - catch (AMQException e) - { - JMSException jmse = new JMSException("Error closing connection: " + e); - jmse.setLinkedException(e); - throw jmse; } } } } + private long adjustTimeout(long timeout, long startTime) + { + long now = System.currentTimeMillis(); + timeout -= now - startTime; + if (timeout < 0) + { + timeout = 0; + } + return timeout; + } /** * Marks all sessions and their children as closed without sending any protocol messages. Useful when you need to @@ -811,19 +1056,6 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect } } - - private long adjustTimeout(long timeout, long startTime) - { - long now = System.currentTimeMillis(); - timeout -= now - startTime; - if (timeout < 0) - { - timeout = 0; - } - - return timeout; - } - public ConnectionConsumer createConnectionConsumer(Destination destination, String messageSelector, ServerSessionPool sessionPool, int maxMessages) throws JMSException { @@ -889,11 +1121,11 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect return _maximumFrameSize; } - public Map getSessions() + public ChannelToSessionMap getSessions() { return _sessions; } - + public String getUsername() { return _username; @@ -1017,18 +1249,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect /** * Invoked by the AMQProtocolSession when a protocol session exception has occurred. This method sends the exception - * to a JMS exception liste - { - ArrayList sessions = new ArrayList(_sessions.values()); - _logger.info(MessageFormat.format("Resubscribing sessions = {0} sessions.size={1}", sessions, sessions.size())); // FIXME: removeKey? - for (Iterator it = sessions.iterator(); it.hasNext();) - { - AMQSession s = (AMQSession) it.next(); - // _protocolHandler.addSessionByChannel(s.getChannelId(), s); - reopenChannel(s.getChannelId(), s.getDefaultPrefetchHigh(), s.getDefaultPrefetchLow(), s.getTransacted()); - s.resubscribe(); - } - }ner, if configured, and propagates the exception to sessions, which in turn will + * to a JMS exception listener, if configured, and propagates the exception to sessions, which in turn will * propagate to consumers. This allows synchronous consumers to have exceptions thrown to them. * * @param cause the exception @@ -1085,8 +1306,12 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect { _exceptionListener.onException(je); } + else + { + _logger.error("Throwable Received but no listener set: " + cause.getMessage()); + } - if (!(cause instanceof AMQUndeliveredException) && !(cause instanceof AMQAuthenticationException)) + if (hardError(cause)) { try { @@ -1110,6 +1335,16 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect } } + private boolean hardError(Throwable cause) + { + if (cause instanceof AMQException) + { + return ((AMQException)cause).isHardError(); + } + + return true; + } + void registerSession(int channelId, AMQSession session) { _sessions.put(channelId, session); @@ -1120,6 +1355,24 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect _sessions.remove(channelId); } + /** + * For all sessions, and for all consumers in those sessions, resubscribe. This is called during failover handling. + * The caller must hold the failover mutex before calling this method. + */ + public void resubscribeSesssions() throws JMSException, AMQException, FailoverException + { + ArrayList sessions = new ArrayList(_sessions.values()); + _logger.info(MessageFormat.format("Resubscribing sessions = {0} sessions.size={1}", sessions, sessions.size())); // FIXME: removeKey? + for (Iterator it = sessions.iterator(); it.hasNext();) + { + AMQSession s = (AMQSession) it.next(); + // _protocolHandler.addSessionByChannel(s.getChannelId(), s); + reopenChannel(s.getChannelId(), s.getDefaultPrefetchHigh(), s.getDefaultPrefetchLow(), s.getTransacted()); + s.resubscribe(); + s.setFlowControl(true); + } + } + public String toString() { StringBuffer buf = new StringBuffer("AMQConnection:\n"); @@ -1207,6 +1460,22 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect return _sessions.get(channelId); } + public ProtocolVersion getProtocolVersion() + { + return _protocolVersion; + } + + public void setProtocolVersion(ProtocolVersion protocolVersion) + { + _protocolVersion = protocolVersion; + _protocolHandler.getProtocolSession().setProtocolVersion(protocolVersion); + } + + public boolean isFailingOver() + { + return (_protocolHandler.getFailoverLatch() != null); + } + /** * Get the maximum number of messages that this connection can pre-fetch. * diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_8.java b/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_8.java index c22fcc3c7a..637cff5b7e 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_8.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_8.java @@ -178,31 +178,25 @@ public class AMQConnectionDelegate_0_8 implements AMQConnectionDelegate private void createChannelOverWire(int channelId, int prefetchHigh, int prefetchLow, boolean transacted) throws AMQException, FailoverException { - + ChannelOpenBody channelOpenBody = _conn.getProtocolHandler().getMethodRegistry().createChannelOpenBody(null); // TODO: Be aware of possible changes to parameter order as versions change. - - _conn._protocolHandler.syncWrite(ChannelOpenBody.createAMQFrame(channelId, _conn._protocolHandler.getProtocolMajorVersion(), - _conn._protocolHandler.getProtocolMinorVersion(), null), // outOfBand - ChannelOpenOkBody.class); + _conn._protocolHandler.syncWrite(channelOpenBody.generateFrame(channelId), ChannelOpenOkBody.class); // todo send low water mark when protocol allows. // todo Be aware of possible changes to parameter order as versions change. - _conn._protocolHandler.syncWrite(BasicQosBody.createAMQFrame(channelId, _conn._protocolHandler.getProtocolMajorVersion(), - _conn._protocolHandler.getProtocolMinorVersion(), false, // global - prefetchHigh, // prefetchCount - 0), // prefetchSize - BasicQosOkBody.class); - + BasicQosBody basicQosBody = _conn.getProtocolHandler().getMethodRegistry().createBasicQosBody(0,prefetchHigh,false); + _conn._protocolHandler.syncWrite(basicQosBody.generateFrame(channelId),BasicQosOkBody.class); + if (transacted) { if (_logger.isDebugEnabled()) { _logger.debug("Issuing TxSelect for " + channelId); } - + TxSelectBody body = _conn.getProtocolHandler().getMethodRegistry().createTxSelectBody(); + // TODO: Be aware of possible changes to parameter order as versions change. - _conn._protocolHandler.syncWrite(TxSelectBody.createAMQFrame(channelId, _conn._protocolHandler.getProtocolMajorVersion(), - _conn._protocolHandler.getProtocolMinorVersion()), TxSelectOkBody.class); + _conn._protocolHandler.syncWrite(body.generateFrame(channelId), TxSelectOkBody.class); } } @@ -212,7 +206,7 @@ public class AMQConnectionDelegate_0_8 implements AMQConnectionDelegate */ public void resubscribeSessions() throws JMSException, AMQException, FailoverException { - ArrayList sessions = new ArrayList(_conn._sessions.values()); + ArrayList sessions = new ArrayList(_conn.getSessions().values()); _logger.info(MessageFormat.format("Resubscribing sessions = {0} sessions.size={1}", sessions, sessions.size())); // FIXME: removeKey? for (Iterator it = sessions.iterator(); it.hasNext();) { diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQConnectionFactory.java b/java/client/src/main/java/org/apache/qpid/client/AMQConnectionFactory.java index 3fcec67fe1..fd8063e99b 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQConnectionFactory.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQConnectionFactory.java @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -23,6 +23,7 @@ package org.apache.qpid.client; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Hashtable; +import java.util.UUID; import javax.jms.*; import javax.naming.Context; @@ -259,7 +260,7 @@ public class AMQConnectionFactory implements ConnectionFactory, QueueConnectionF } catch (UnknownHostException e) { - return null; + return "UnknownHost" + UUID.randomUUID(); } } @@ -352,7 +353,9 @@ public class AMQConnectionFactory implements ConnectionFactory, QueueConnectionF * @param name * @param ctx * @param env + * * @return AMQConnection,AMQTopic,AMQQueue, or AMQConnectionFactory. + * * @throws Exception */ public Object getObjectInstance(Object obj, Name name, Context ctx, Hashtable env) throws Exception @@ -408,8 +411,9 @@ public class AMQConnectionFactory implements ConnectionFactory, QueueConnectionF public Reference getReference() throws NamingException { - return new Reference(AMQConnectionFactory.class.getName(), - new StringRefAddr(AMQConnectionFactory.class.getName(), _connectionDetails.getURL()), + return new Reference( + AMQConnectionFactory.class.getName(), + new StringRefAddr(AMQConnectionFactory.class.getName(), _connectionDetails.getURL()), AMQConnectionFactory.class.getName(), null); // factory location } diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQConnectionURL.java b/java/client/src/main/java/org/apache/qpid/client/AMQConnectionURL.java index 770fab7a81..02959aff3b 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQConnectionURL.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQConnectionURL.java @@ -27,6 +27,7 @@ import java.util.Map; import org.apache.qpid.client.url.URLParser; import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ProtocolVersion; import org.apache.qpid.jms.BrokerDetails; import org.apache.qpid.jms.ConnectionURL; import org.apache.qpid.url.URLHelper; diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java b/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java index e02b3d6643..52080112c9 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java @@ -56,7 +56,9 @@ public abstract class AMQDestination implements Destination, Referenceable private String _url; private AMQShortString _urlAsShortString; - private boolean _validated; + private boolean _checkedForQueueBinding; + + private boolean _exchangeExistsChecked; private byte[] _byteEncoding; private static final int IS_DURABLE_MASK = 0x1; @@ -234,14 +236,25 @@ public abstract class AMQDestination implements Destination, Referenceable } - public boolean isValidated() + public boolean isCheckedForQueueBinding() + { + return _checkedForQueueBinding; + } + + public void setCheckedForQueueBinding(boolean checkedForQueueBinding) + { + _checkedForQueueBinding = checkedForQueueBinding; + } + + + public boolean isExchangeExistsChecked() { - return _validated; + return _exchangeExistsChecked; } - public void setValidated(boolean validated) + public void setExchangeExistsChecked(final boolean exchangeExistsChecked) { - _validated = validated; + _exchangeExistsChecked = exchangeExistsChecked; } public String toURL() diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQHeadersExchange.java b/java/client/src/main/java/org/apache/qpid/client/AMQHeadersExchange.java index b27dfc6dcb..b9e9a33cd6 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQHeadersExchange.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQHeadersExchange.java @@ -49,6 +49,6 @@ public class AMQHeadersExchange extends AMQDestination //Not sure what the best approach is here, probably to treat this like a topic //and allow server to generate names. As it is AMQ specific it doesn't need to //fit the JMS API expectations so this is not as yet critical. - return false; + return getAMQQueueName() == null; } } diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQQueueBrowser.java b/java/client/src/main/java/org/apache/qpid/client/AMQQueueBrowser.java index f56477c0fc..08fd49286b 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQQueueBrowser.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQQueueBrowser.java @@ -22,6 +22,7 @@ package org.apache.qpid.client; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.apache.qpid.AMQException; import javax.jms.IllegalStateException; import javax.jms.JMSException; @@ -50,7 +51,9 @@ public class AMQQueueBrowser implements QueueBrowser _messageSelector = ((messageSelector == null) || (messageSelector.trim().length() == 0)) ? null : messageSelector; // Create Consumer to verify message selector. BasicMessageConsumer consumer = - (BasicMessageConsumer) _session.createBrowserConsumer(_queue, _messageSelector, false); + (BasicMessageConsumer) _session.createBrowserConsumer(_queue, _messageSelector, false); + // Close this consumer as we are not looking to consume only to establish that, at least for now, + // the QB can be created consumer.close(); } @@ -88,36 +91,37 @@ public class AMQQueueBrowser implements QueueBrowser checkState(); final BasicMessageConsumer consumer = (BasicMessageConsumer) _session.createBrowserConsumer(_queue, _messageSelector, false); + _consumers.add(consumer); return new Enumeration() + { + + Message _nextMessage = consumer == null ? null : consumer.receive(1000); + + public boolean hasMoreElements() { - Message _nextMessage = consumer.receive(1000); + _logger.info("QB:hasMoreElements:" + (_nextMessage != null)); + return (_nextMessage != null); + } - public boolean hasMoreElements() + public Object nextElement() + { + Message msg = _nextMessage; + try { - _logger.info("QB:hasMoreElements:" + (_nextMessage != null)); - return (_nextMessage != null); + _logger.info("QB:nextElement about to receive"); + _nextMessage = consumer.receive(1000); + _logger.info("QB:nextElement received:" + _nextMessage); } - - public Object nextElement() + catch (JMSException e) { - Message msg = _nextMessage; - try - { - _logger.info("QB:nextElement about to receive"); - _nextMessage = consumer.receive(1000); - _logger.info("QB:nextElement received:" + _nextMessage); - } - catch (JMSException e) - { - _logger.warn("Exception caught while queue browsing", e); - _nextMessage = null; - } - - return msg; + _logger.warn("Exception caught while queue browsing", e); + _nextMessage = null; } - }; + return msg; + } + }; } public void close() throws JMSException diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQSession.java b/java/client/src/main/java/org/apache/qpid/client/AMQSession.java index ee55743d0e..404c0cd381 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQSession.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQSession.java @@ -20,14 +20,15 @@ */ package org.apache.qpid.client; - import java.io.Serializable; import java.net.URISyntaxException; import java.text.MessageFormat; import java.util.ArrayList; +import java.util.Collection; import java.util.Iterator; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -57,7 +58,9 @@ import javax.jms.Topic; import javax.jms.TopicPublisher; import javax.jms.TopicSession; import javax.jms.TopicSubscriber; +import javax.jms.TransactionRolledBackException; +import org.apache.qpid.AMQDisconnectedException; import org.apache.qpid.AMQException; import org.apache.qpid.AMQInvalidArgumentException; import org.apache.qpid.AMQInvalidRoutingKeyException; @@ -77,11 +80,12 @@ import org.apache.qpid.client.message.ReturnMessage; import org.apache.qpid.client.message.UnprocessedMessage; import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.client.util.FlowControllingBlockingQueue; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.framing.FieldTableFactory; +import org.apache.qpid.common.AMQPFilterTypes; +import org.apache.qpid.framing.*; +import org.apache.qpid.framing.amqp_0_9.MethodRegistry_0_9; import org.apache.qpid.jms.Session; import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.protocol.AMQMethodEvent; import org.apache.qpid.url.AMQBindingURL; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -106,6 +110,86 @@ import org.slf4j.LoggerFactory; */ public abstract class AMQSession extends Closeable implements Session, QueueSession, TopicSession { + public static final class IdToConsumerMap + { + private final BasicMessageConsumer[] _fastAccessConsumers = new BasicMessageConsumer[16]; + private final ConcurrentHashMap<Integer, BasicMessageConsumer> _slowAccessConsumers = new ConcurrentHashMap<Integer, BasicMessageConsumer>(); + + + public BasicMessageConsumer get(int id) + { + if((id & 0xFFFFFFF0) == 0) + { + return _fastAccessConsumers[id]; + } + else + { + return _slowAccessConsumers.get(id); + } + } + + public BasicMessageConsumer put(int id, BasicMessageConsumer consumer) + { + BasicMessageConsumer oldVal; + if((id & 0xFFFFFFF0) == 0) + { + oldVal = _fastAccessConsumers[id]; + _fastAccessConsumers[id] = consumer; + } + else + { + oldVal = _slowAccessConsumers.put(id, consumer); + } + + return consumer; + + } + + + public BasicMessageConsumer remove(int id) + { + BasicMessageConsumer consumer; + if((id & 0xFFFFFFF0) == 0) + { + consumer = _fastAccessConsumers[id]; + _fastAccessConsumers[id] = null; + } + else + { + consumer = _slowAccessConsumers.remove(id); + } + + return consumer; + + } + + public Collection<BasicMessageConsumer> values() + { + ArrayList<BasicMessageConsumer> values = new ArrayList<BasicMessageConsumer>(); + + for(int i = 0; i < 16; i++) + { + if(_fastAccessConsumers[i] != null) + { + values.add(_fastAccessConsumers[i]); + } + } + values.addAll(_slowAccessConsumers.values()); + + return values; + } + + + public void clear() + { + _slowAccessConsumers.clear(); + for(int i = 0; i<16; i++) + { + _fastAccessConsumers[i] = null; + } + } + } + /** Used for debugging. */ private static final Logger _logger = LoggerFactory.getLogger(AMQSession.class); @@ -155,7 +239,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess protected boolean _transacted; /** Holds the sessions acknowledgement mode. */ - protected int _acknowledgeMode; + protected final int _acknowledgeMode; /** Holds this session unique identifier, used to distinguish it from other sessions. */ protected int _channelId; @@ -231,8 +315,16 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess * Maps from identifying tags to message consumers, in order to pass dispatch incoming messages to the right * consumer. */ - protected Map<AMQShortString, BasicMessageConsumer> _consumers = - new ConcurrentHashMap<AMQShortString, BasicMessageConsumer>(); + protected final IdToConsumerMap _consumers = new IdToConsumerMap(); + + //Map<AMQShortString, BasicMessageConsumer> _consumers = + //new ConcurrentHashMap<AMQShortString, BasicMessageConsumer>(); + + /** + * Contains a list of consumers which have been removed but which might still have + * messages to acknowledge, eg in client ack or transacted modes + */ + private CopyOnWriteArrayList<BasicMessageConsumer> _removedConsumers = new CopyOnWriteArrayList<BasicMessageConsumer>(); /** Provides a count of consumers on destinations, in order to be able to know if a destination has consumers. */ private ConcurrentHashMap<Destination, AtomicInteger> _destinationConsumerCount = @@ -284,6 +376,32 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess protected final boolean _strictAMQPFATAL; private final Object _messageDeliveryLock = new Object(); + /** Session state : used to detect if commit is a) required b) allowed , i.e. does the tx span failover. */ + private boolean _dirty; + /** Has failover occured on this session */ + private boolean _failedOver; + + + + private static final class FlowControlIndicator + { + private volatile boolean _flowControl = true; + + public synchronized void setFlowControl(boolean flowControl) + { + _flowControl= flowControl; + notify(); + } + + public boolean getFlowControl() + { + return _flowControl; + } + } + + /** Flow control */ + private FlowControlIndicator _flowControl = new FlowControlIndicator(); + /** * Creates a new session on a connection. * @@ -330,24 +448,20 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess { public void aboveThreshold(int currentValue) { - if (_acknowledgeMode == NO_ACKNOWLEDGE) - { _logger.debug( "Above threshold(" + _defaultPrefetchHighMark + ") so suspending channel. Current value is " + currentValue); new Thread(new SuspenderRunner(true)).start(); - } + } public void underThreshold(int currentValue) { - if (_acknowledgeMode == NO_ACKNOWLEDGE) - { _logger.debug( "Below threshold(" + _defaultPrefetchLowMark + ") so unsuspending channel. Current value is " + currentValue); new Thread(new SuspenderRunner(false)).start(); - } + } }); } @@ -357,7 +471,22 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess } } - + /** + * Creates a new session on a connection with the default message factory factory. + * + * @param con The connection on which to create the session. + * @param channelId The unique identifier for the session. + * @param transacted Indicates whether or not the session is transactional. + * @param acknowledgeMode The acknoledgement mode for the session. + * @param defaultPrefetchHigh The maximum number of messages to prefetched before suspending the session. + * @param defaultPrefetchLow The number of prefetched messages at which to resume the session. + */ + AMQSession(AMQConnection con, int channelId, boolean transacted, int acknowledgeMode, int defaultPrefetchHigh, + int defaultPrefetchLow) + { + this(con, channelId, transacted, acknowledgeMode, MessageFactoryRegistry.newDefaultRegistry(), defaultPrefetchHigh, + defaultPrefetchLow); + } // ===== JMS Session methods. @@ -371,6 +500,12 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess close(-1); } + public BytesMessage createBytesMessage() throws JMSException + { + checkNotClosed(); + return new JMSBytesMessage(); + } + /** * Acknowledges all unacknowledged messages on the session, for all message consumers on the session. * @@ -381,6 +516,10 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess if (isClosed()) { throw new IllegalStateException("Session is already closed"); + } + else if (hasFailedOver()) + { + throw new IllegalStateException("has failed over"); } while (true) @@ -405,6 +544,12 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess */ public abstract void acknowledgeMessage(long deliveryTag, boolean multiple); + public MethodRegistry getMethodRegistry() + { + MethodRegistry methodRegistry = getProtocolHandler().getMethodRegistry(); + return methodRegistry; + } + /** * Binds the named queue, with the specified routing key, to the named exchange. * @@ -471,30 +616,26 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess { if (_logger.isInfoEnabled()) { - _logger.info("Closing session: " + this );//+ ":" - // + Arrays.asList(Thread.currentThread().getStackTrace()).subList(3, 6)); + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + _logger.info("Closing session: " + this); // + ":" + // + Arrays.asList(stackTrace).subList(3, stackTrace.length - 1)); } - if( _dispatcher != null ) - { - _dispatcher.setConnectionStopped(true); - } - synchronized (_messageDeliveryLock) + // Ensure we only try and close an open session. + if (!_closed.getAndSet(true)) { - - // We must close down all producers and consumers in an orderly fashion. This is the only method - // that can be called from a different thread of control from the one controlling the session. synchronized (_connection.getFailoverMutex()) { - // Ensure we only try and close an open session. - if (!_closed.getAndSet(true)) + // We must close down all producers and consumers in an orderly fashion. This is the only method + // that can be called from a different thread of control from the one controlling the session. + synchronized (_messageDeliveryLock) { // we pass null since this is not an error case closeProducersAndConsumers(null); try { - sendClose(timeout); + sendClose(timeout); } catch (AMQException e) { @@ -527,25 +668,44 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess */ public void closed(Throwable e) throws JMSException { - synchronized (_messageDeliveryLock) + // This method needs to be improved. Throwables only arrive here from the mina : exceptionRecived + // calls through connection.closeAllSessions which is also called by the public connection.close() + // with a null cause + // When we are closing the Session due to a protocol session error we simply create a new AMQException + // with the correct error code and text this is cleary WRONG as the instanceof check below will fail. + // We need to determin here if the connection should be + + if (e instanceof AMQDisconnectedException) + { + if (_dispatcher != null) + { + // Failover failed and ain't coming back. Knife the dispatcher. + _dispatcher.interrupt(); + } + } + + if (!_closed.getAndSet(true)) { synchronized (_connection.getFailoverMutex()) { - // An AMQException has an error code and message already and will be passed in when closure occurs as a - // result of a channel close request - _closed.set(true); - AMQException amqe; - if (e instanceof AMQException) - { - amqe = (AMQException) e; - } - else + synchronized (_messageDeliveryLock) { - amqe = new AMQException(null, "Closing session forcibly", e); - } + // An AMQException has an error code and message already and will be passed in when closure occurs as a + // result of a channel close request + AMQException amqe; + if (e instanceof AMQException) + { + amqe = (AMQException) e; + } + else + { + amqe = new AMQException("Closing session forcibly", e); + } + - _connection.deregisterSession(_channelId); - closeProducersAndConsumers(amqe); + _connection.deregisterSession(_channelId); + closeProducersAndConsumers(amqe); + } } } } @@ -565,10 +725,12 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess */ public void commit() throws JMSException { - checkTransacted(); + checkTransacted(); try { + + // TGM FIXME: what about failover? // Acknowledge all delivered messages while (true) { @@ -580,7 +742,6 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess acknowledgeMessage(tag, false); } - // Commits outstanding messages and acknowledgments sendCommit(); } @@ -600,16 +761,10 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess { // Remove the consumer from the map - BasicMessageConsumer consumer = (BasicMessageConsumer) _consumers.get(consumerTag); + BasicMessageConsumer consumer = _consumers.get(consumerTag.toIntValue()); if (consumer != null) { - // fixme this isn't right.. needs to check if _queue contains data for this consumer - if (consumer.isAutoClose()) // && _queue.isEmpty()) - { - consumer.closeWhenNoMessages(true); - } - - if (!consumer.isNoConsume()) + if (!consumer.isNoConsume()) // Normal Consumer { // Clean the Maps up first // Flush any pending messages for this consumerTag @@ -620,13 +775,12 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess else { _logger.info("Dispatcher is null so created stopped dispatcher"); - - startDistpatcherIfNecessary(true); + startDispatcherIfNecessary(true); } _dispatcher.rejectPending(consumer); } - else + else // Queue Browser { // Just close the consumer // fixme the CancelOK is being processed before the arriving messages.. @@ -634,13 +788,24 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess // has yet to receive before the close comes in. // consumer.markClosed(); + + + + if (consumer.isAutoClose()) + { + // There is a small window where the message is between the two queues in the dispatcher. + if (consumer.isClosed()) + { + if (_logger.isInfoEnabled()) + { + _logger.info("Closing consumer:" + consumer.debugIdentity()); + } + + deregisterConsumer(consumer); + } + } } } - else - { - _logger.warn("Unable to confirm cancellation of consumer (" + consumerTag + "). Not found in consumer map."); - } - } public QueueBrowser createBrowser(Queue queue) throws JMSException @@ -675,21 +840,11 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess messageSelector, null, true, true); } - public BytesMessage createBytesMessage() throws JMSException - { - synchronized (_connection.getFailoverMutex()) - { - checkNotClosed(); - - return new JMSBytesMessage(); - } - } - public MessageConsumer createConsumer(Destination destination) throws JMSException { checkValidDestination(destination); - return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, false, false, null, null, + return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, false, (destination instanceof Topic), null, null, false, false); } @@ -701,11 +856,12 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess false, false); } + public MessageConsumer createConsumer(Destination destination, String messageSelector) throws JMSException { checkValidDestination(destination); - return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, false, false, + return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, false, (destination instanceof Topic), messageSelector, null, false, false); } @@ -714,16 +870,27 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess { checkValidDestination(destination); - return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, noLocal, false, + return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, noLocal, (destination instanceof Topic), messageSelector, null, false, false); } + + public MessageConsumer createExclusiveConsumer(Destination destination, String messageSelector, boolean noLocal) + throws JMSException + { + checkValidDestination(destination); + + return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, noLocal, true, + messageSelector, null, false, false); + } + + public MessageConsumer createConsumer(Destination destination, int prefetch, boolean noLocal, boolean exclusive, String selector) throws JMSException { checkValidDestination(destination); - return createConsumerImpl(destination, prefetch, prefetch, noLocal, exclusive, selector, null, false, false); + return createConsumerImpl(destination, prefetch, prefetch / 2, noLocal, exclusive, selector, null, false, false); } public MessageConsumer createConsumer(Destination destination, int prefetchHigh, int prefetchLow, boolean noLocal, @@ -739,7 +906,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess { checkValidDestination(destination); - return createConsumerImpl(destination, prefetch, prefetch, noLocal, exclusive, selector, rawSelector, false, false); + return createConsumerImpl(destination, prefetch, prefetch / 2, noLocal, exclusive, selector, rawSelector, false, false); } public MessageConsumer createConsumer(Destination destination, int prefetchHigh, int prefetchLow, boolean noLocal, @@ -770,12 +937,8 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess public MapMessage createMapMessage() throws JMSException { - synchronized (_connection.getFailoverMutex()) - { - checkNotClosed(); - - return new JMSMapMessage(); - } + checkNotClosed(); + return new JMSMapMessage(); } public javax.jms.Message createMessage() throws JMSException @@ -785,12 +948,8 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess public ObjectMessage createObjectMessage() throws JMSException { - synchronized (_connection.getFailoverMutex()) - { - checkNotClosed(); - - return (ObjectMessage) new JMSObjectMessage(); - } + checkNotClosed(); + return (ObjectMessage) new JMSObjectMessage(); } public ObjectMessage createObjectMessage(Serializable object) throws JMSException @@ -827,7 +986,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess { checkNotClosed(); - return new TopicPublisherAdapter((BasicMessageProducer) createProducer(topic), topic); + return new TopicPublisherAdapter((BasicMessageProducer) createProducer(topic,false,false), topic); } public Queue createQueue(String queueName) throws JMSException @@ -874,7 +1033,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess { public Object execute() throws AMQException, FailoverException { - sendCreateQueue(name, autoDelete,durable,exclusive); + sendCreateQueue(name, autoDelete, durable, exclusive); return null; } }, _connection).execute(); @@ -989,9 +1148,10 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess AMQTopic dest = checkValidTopic(topic); // AMQTopic dest = new AMQTopic(topic.getTopicName()); - return new TopicSubscriberAdaptor(dest, (BasicMessageConsumer) createConsumer(dest)); + return new TopicSubscriberAdaptor(dest, (BasicMessageConsumer) createExclusiveConsumer(dest)); } + /** * Creates a non-durable subscriber with a message selector * @@ -1009,7 +1169,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess AMQTopic dest = checkValidTopic(topic); // AMQTopic dest = new AMQTopic(topic.getTopicName()); - return new TopicSubscriberAdaptor(dest, (BasicMessageConsumer) createConsumer(dest, messageSelector, noLocal)); + return new TopicSubscriberAdaptor(dest, (BasicMessageConsumer) createExclusiveConsumer(dest, messageSelector, noLocal)); } public abstract TemporaryQueue createTemporaryQueue() throws JMSException; @@ -1267,14 +1427,14 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess } } - public abstract void sendRecover() throws AMQException, FailoverException; + abstract void sendRecover() throws AMQException, FailoverException; public void rejectMessage(UnprocessedMessage message, boolean requeue) { - if (_logger.isTraceEnabled()) + if (_logger.isDebugEnabled()) { - _logger.trace("Rejecting Unacked message:" + message.getDeliveryTag()); + _logger.debug("Rejecting Unacked message:" + message.getDeliveryTag()); } rejectMessage(message.getDeliveryTag(), requeue); @@ -1282,9 +1442,9 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess public void rejectMessage(AbstractJMSMessage message, boolean requeue) { - if (_logger.isTraceEnabled()) + if (_logger.isDebugEnabled()) { - _logger.trace("Rejecting Abstract message:" + message.getDeliveryTag()); + _logger.debug("Rejecting Abstract message:" + message.getDeliveryTag()); } rejectMessage(message.getDeliveryTag(), requeue); @@ -1325,6 +1485,8 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess sendRollback(); + markClean(); + if (!isSuspended) { suspendChannel(false); @@ -1460,6 +1622,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess AMQDestination amqd = (AMQDestination) destination; + final AMQProtocolHandler protocolHandler = getProtocolHandler(); // TODO: Define selectors in AMQP // TODO: construct the rawSelector from the selector string if rawSelector == null final FieldTable ft = FieldTableFactory.newFieldTable(); @@ -1499,11 +1662,6 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess { JMSException ex = new JMSException("Error registering consumer: " + e); - if (_logger.isDebugEnabled()) - { - e.printStackTrace(); - } - ex.setLinkedException(e); throw ex; } @@ -1531,7 +1689,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess */ void deregisterConsumer(BasicMessageConsumer consumer) { - if (_consumers.remove(consumer.getConsumerTag()) != null) + if (_consumers.remove(consumer.getConsumerTag().toIntValue()) != null) { String subscriptionName = _reverseSubscriptionMap.remove(consumer); if (subscriptionName != null) @@ -1547,6 +1705,13 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess _destinationConsumerCount.remove(dest); } } + + // Consumers that are closed in a transaction must be stored + // so that messages they have received can be acknowledged on commit + if (_transacted) + { + _removedConsumers.add(consumer); + } } } @@ -1582,8 +1747,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess */ public abstract boolean isQueueBound(final AMQShortString exchangeName, final AMQShortString queueName, final AMQShortString routingKey) throws JMSException; - - + public abstract boolean isQueueBound(final AMQDestination destination) throws JMSException; /** @@ -1605,6 +1769,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess */ void resubscribe() throws AMQException { + _failedOver = true; resubscribeProducers(); resubscribeConsumers(); } @@ -1639,13 +1804,20 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess // If the event dispatcher is not running then start it too. if (hasMessageListeners()) { - startDistpatcherIfNecessary(); + startDispatcherIfNecessary(); } } - synchronized void startDistpatcherIfNecessary() + void startDispatcherIfNecessary() { + //If we are the dispatcher then we don't need to check we are started + if (Thread.currentThread() == _dispatcher) + { + return; + } + // If IMMEDIATE_PREFETCH is not set then we need to start fetching + // This is final per session so will be multi-thread safe. if (!_immediatePrefetch) { // We do this now if this is the first call on a started connection @@ -1662,10 +1834,10 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess } } - startDistpatcherIfNecessary(false); + startDispatcherIfNecessary(false); } - synchronized void startDistpatcherIfNecessary(boolean initiallyStopped) + synchronized void startDispatcherIfNecessary(boolean initiallyStopped) { if (_dispatcher == null) { @@ -1816,7 +1988,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess } else { - con.close(); + con.close(false); } } // at this point the _consumers map will be empty @@ -1891,20 +2063,22 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess private void consumeFromQueue(BasicMessageConsumer consumer, AMQShortString queueName, AMQProtocolHandler protocolHandler, boolean nowait, String messageSelector) throws AMQException, FailoverException { - //need to generate a consumer tag on the client so we can exploit the nowait flag - AMQShortString tag = new AMQShortString(Integer.toString(_nextTag++)); + int tagId = _nextTag++; + // need to generate a consumer tag on the client so we can exploit the nowait flag + AMQShortString tag = new AMQShortString(Integer.toString(tagId)); + consumer.setConsumerTag(tag); // we must register the consumer in the map before we actually start listening - _consumers.put(tag, consumer); + _consumers.put(tagId, consumer); try { - sendConsume(consumer,queueName,protocolHandler,nowait,messageSelector,tag); + sendConsume(consumer, queueName, protocolHandler, nowait, messageSelector, tag); } catch (AMQException e) { // clean-up the map in the event of an error - _consumers.remove(tag); + _consumers.remove(tagId); throw e; } } @@ -1945,6 +2119,34 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess declareExchange(amqd.getExchangeName(), amqd.getExchangeClass(), protocolHandler, nowait); } + + /** + * Returns the number of messages currently queued for the given destination. + * + * <p/>Note that this operation automatically retries in the event of fail-over. + * + * @param amqd The destination to be checked + * + * @return the number of queued messages. + * + * @throws AMQException If the queue cannot be declared for any reason. + */ + public long getQueueDepth(final AMQDestination amqd) + throws AMQException + { + return new FailoverNoopSupport<Long, AMQException>( + new FailoverProtectedOperation<Long, AMQException>() + { + public Long execute() throws AMQException, FailoverException + { + return requestQueueDepth(amqd); + } + }, _connection).execute(); + + } + + abstract Long requestQueueDepth(AMQDestination amqd) throws AMQException, FailoverException; + /** * Declares the named exchange and type of exchange. * @@ -1960,7 +2162,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess * @todo Be aware of possible changes to parameter order as versions change. */ private void declareExchange(final AMQShortString name, final AMQShortString type, - final AMQProtocolHandler protocolHandler, final boolean nowait) throws AMQException + final AMQProtocolHandler protocolHandler, final boolean nowait) throws AMQException { new FailoverNoopSupport<Object, AMQException>(new FailoverProtectedOperation<Object, AMQException>() { @@ -2013,7 +2215,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess amqd.setQueueName(protocolHandler.generateQueueName()); } - sendQueueDeclare(amqd,protocolHandler); + sendQueueDeclare(amqd, protocolHandler); return amqd.getAMQQueueName(); } @@ -2064,12 +2266,12 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess return _connection.getProtocolHandler(); } - protected byte getProtocolMajorVersion() + public byte getProtocolMajorVersion() { return getProtocolHandler().getProtocolMajorVersion(); } - protected byte getProtocolMinorVersion() + public byte getProtocolMinorVersion() { return getProtocolHandler().getProtocolMinorVersion(); } @@ -2227,7 +2429,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess if (_logger.isDebugEnabled()) { _logger.debug("Removing message(" + System.identityHashCode(message) + ") from _queue DT:" - + message.getDeliveryTag()); + + message.getDeliveryTag()); } messages.remove(); @@ -2250,6 +2452,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess for (Iterator it = consumers.iterator(); it.hasNext();) { BasicMessageConsumer consumer = (BasicMessageConsumer) it.next(); + consumer.failedOver(); registerConsumer(consumer, true); } } @@ -2276,11 +2479,10 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess // Bounced message is processed here, away from the mina thread AbstractJMSMessage bouncedMessage = _messageFactoryRegistry.createMessage(0, false, msg.getExchange(), - msg.getExchange(), msg.getContentHeader(), msg.getBodies()); - - AMQConstant errorCode = AMQConstant.getConstant(msg.getReplyCode()); - AMQShortString reason = msg.getReplyText(); - _logger.debug("Message returned with error code " + errorCode + " (" + reason + ")"); + msg.getRoutingKey(), msg.getContentHeader(), msg.getBodies()); + AMQConstant errorCode = AMQConstant.getConstant(msg.getReplyCode()); + AMQShortString reason = msg.getReplyText(); + _logger.debug("Message returned with error code " + errorCode + " (" + reason + ")"); // @TODO should this be moved to an exception handler of sorts. Somewhere errors are converted to correct execeptions. if (errorCode == AMQConstant.NO_CONSUMERS) @@ -2318,7 +2520,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess * * @todo Be aware of possible changes to parameter order as versions change. */ - protected void suspendChannel(boolean suspend) throws AMQException // , FailoverException + protected void suspendChannel(boolean suspend) throws AMQException // , FailoverException { synchronized (_suspensionLock) { @@ -2339,6 +2541,13 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess } } + public abstract void sendSuspendChannel(boolean suspend) throws AMQException, FailoverException; + + Object getMessageDeliveryLock() + { + return _messageDeliveryLock; + } + /** * Indicates whether this session consumers pre-fetche messages * @@ -2350,7 +2559,62 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess } - public abstract void sendSuspendChannel(boolean suspend) throws AMQException, FailoverException; + /** Signifies that the session has pending sends to commit. */ + public void markDirty() + { + _dirty = true; + } + + /** Signifies that the session has no pending sends to commit. */ + public void markClean() + { + _dirty = false; + _failedOver = false; + } + + /** + * Check to see if failover has occured since the last call to markClean(commit or rollback). + * + * @return boolean true if failover has occured. + */ + public boolean hasFailedOver() + { + return _failedOver; + } + + /** + * Check to see if any message have been sent in this transaction and have not been commited. + * + * @return boolean true if a message has been sent but not commited + */ + public boolean isDirty() + { + return _dirty; + } + + public void setTicket(int ticket) + { + _ticket = ticket; + } + + public void setFlowControl(final boolean active) + { + _flowControl.setFlowControl(active); + } + + + public void checkFlowControl() throws InterruptedException + { + synchronized(_flowControl) + { + while(!_flowControl.getFlowControl()) + { + _flowControl.wait(); + } + } + + } + /** Responsible for decoding a message fragment and passing it to the appropriate message consumer. */ class Dispatcher extends Thread @@ -2361,6 +2625,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess private final Object _lock = new Object(); private final AtomicLong _rollbackMark = new AtomicLong(-1); + private String dispatcherID = "" + System.identityHashCode(this); public Dispatcher() { @@ -2392,10 +2657,11 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess } // Reject messages on pre-receive queue - consumer.rollback(); + consumer.rollbackPendingMessages(); // Reject messages on pre-dispatch queue rejectMessagesForConsumerTag(consumer.getConsumerTag(), true); + //Let the dispatcher deal with this when it gets to them. // closeConsumer consumer.markClosed(); @@ -2436,6 +2702,13 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess } + for (int i = 0; i < _removedConsumers.size(); i++) + { + // Sends acknowledgement to server + _removedConsumers.get(i).rollback(); + _removedConsumers.remove(i); + } + setConnectionStopped(isStopped); } @@ -2484,9 +2757,16 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess } else { - synchronized (_messageDeliveryLock) + if (message.getDeliveryTag() <= _rollbackMark.get()) { - dispatchMessage(message); + rejectMessage(message, true); + } + else + { + synchronized (_messageDeliveryLock) + { + dispatchMessage(message); + } } } @@ -2535,38 +2815,38 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess //This if block is not needed anymore as bounce messages are handled separately //if (message.getDeliverBody() != null) //{ - final BasicMessageConsumer consumer = - (BasicMessageConsumer) _consumers.get(new AMQShortString(message.getConsumerTag())); + final BasicMessageConsumer consumer = + _consumers.get(message.getConsumerTag().toIntValue()); - if ((consumer == null) || consumer.isClosed()) + if ((consumer == null) || consumer.isClosed()) + { + if (_dispatcherLogger.isInfoEnabled()) { - if (_dispatcherLogger.isInfoEnabled()) + if (consumer == null) { - if (consumer == null) - { - _dispatcherLogger.info("Received a message(" + System.identityHashCode(message) + ")" + "[" - + message.getDeliveryTag() + "] from queue " - + message.getConsumerTag() + " )without a handler - rejecting(requeue)..."); - } - else - { - _dispatcherLogger.info("Received a message(" + System.identityHashCode(message) + ")" + "[" - + message.getDeliveryTag() + "] from queue " + " consumer(" - + consumer.debugIdentity() + ") is closed rejecting(requeue)..."); - } + _dispatcherLogger.info("Dispatcher(" + dispatcherID + ")Received a message(" + System.identityHashCode(message) + ")" + "[" + + message.getDeliveryTag() + "] from queue " + + message.getConsumerTag() + " )without a handler - rejecting(requeue)..."); } - // Don't reject if we're already closing - if (!_closed.get()) + else { - rejectMessage(message, true); + _dispatcherLogger.info("Received a message(" + System.identityHashCode(message) + ")" + "[" + + message.getDeliveryTag() + "] from queue " + " consumer(" + + message.getConsumerTag() + ") is closed rejecting(requeue)..."); } } - else + // Don't reject if we're already closing + if (!_closed.get()) { - consumer.notifyMessage(message, _channelId); + rejectMessage(message, true); } } - //} + else + { + consumer.notifyMessage(message); + } + + } } /*public void requestAccess(AMQShortString realm, boolean exclusive, boolean passive, boolean active, boolean write, diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQSessionDirtyException.java b/java/client/src/main/java/org/apache/qpid/client/AMQSessionDirtyException.java new file mode 100644 index 0000000000..a1b240ed54 --- /dev/null +++ b/java/client/src/main/java/org/apache/qpid/client/AMQSessionDirtyException.java @@ -0,0 +1,42 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; + +/** + * AMQSessionDirtyException represents all failures to send data on a transacted session that is + * no longer in a state that the client expects. i.e. failover has occured so previously sent messages + * will not be part of the transaction. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Represent attempt to perform additional sends on a dirty session. + * </table> + */ +public class AMQSessionDirtyException extends AMQException +{ + public AMQSessionDirtyException(String msg) + { + super(AMQConstant.RESOURCE_ERROR, msg, null); + } +} diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java b/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java index d72668bb53..6089c54314 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java @@ -395,16 +395,16 @@ public class AMQSession_0_10 extends AMQSession try { preAcquire = ( ! consumer.isNoConsume() && consumer.getMessageSelector() == null) || !(consumer.getDestination() instanceof AMQQueue); + getQpidSession().messageSubscribe(queueName.toString(), tag.toString(), + getAcknowledgeMode() == NO_ACKNOWLEDGE ? Session.TRANSFER_CONFIRM_MODE_NOT_REQUIRED:Session.TRANSFER_CONFIRM_MODE_REQUIRED, + preAcquire ? Session.TRANSFER_ACQUIRE_MODE_PRE_ACQUIRE : Session.TRANSFER_ACQUIRE_MODE_NO_ACQUIRE, + new MessagePartListenerAdapter((BasicMessageConsumer_0_10) consumer), null, + consumer.isExclusive() ? Option.EXCLUSIVE : Option.NO_OPTION); } catch (JMSException e) { throw new AMQException(AMQConstant.INTERNAL_ERROR, "problem when registering consumer", e); } - getQpidSession().messageSubscribe(queueName.toString(), tag.toString(), - (Boolean.getBoolean("noAck") ?Session.TRANSFER_CONFIRM_MODE_NOT_REQUIRED:Session.TRANSFER_CONFIRM_MODE_REQUIRED), - preAcquire ? Session.TRANSFER_ACQUIRE_MODE_PRE_ACQUIRE : Session.TRANSFER_ACQUIRE_MODE_NO_ACQUIRE, - new MessagePartListenerAdapter((BasicMessageConsumer_0_10) consumer), null, - consumer.isExclusive() ? Option.EXCLUSIVE : Option.NO_OPTION); if (! prefetch()) { @@ -746,4 +746,10 @@ public class AMQSession_0_10 extends AMQSession return subscriber; } + + Long requestQueueDepth(AMQDestination amqd) + { + return getQpidSession().queueQuery(amqd.getQueueName()).get().getMessageCount(); + } + } diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_8.java b/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_8.java index dd0fd9c457..740bd5dace 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_8.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_8.java @@ -30,8 +30,10 @@ import org.apache.qpid.client.failover.FailoverProtectedOperation; import org.apache.qpid.client.failover.FailoverRetrySupport; import org.apache.qpid.client.message.MessageFactoryRegistry; import org.apache.qpid.client.protocol.AMQProtocolHandler; +import org.apache.qpid.client.state.listener.SpecificMethodFrameListener; import org.apache.qpid.common.AMQPFilterTypes; import org.apache.qpid.framing.*; +import org.apache.qpid.framing.amqp_0_9.MethodRegistry_0_9; import org.apache.qpid.jms.Session; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.protocol.AMQMethodEvent; @@ -79,11 +81,16 @@ public class AMQSession_0_8 extends AMQSession defaultPrefetchLow); } + private ProtocolVersion getProtocolVersion() + { + return getProtocolHandler().getProtocolVersion(); + } + public void acknowledgeMessage(long deliveryTag, boolean multiple) { - final AMQFrame ackFrame = - BasicAckBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), deliveryTag, - multiple); + BasicAckBody body = getMethodRegistry().createBasicAckBody(deliveryTag, multiple); + + final AMQFrame ackFrame = body.generateFrame(_channelId); if (_logger.isDebugEnabled()) { @@ -96,28 +103,17 @@ public class AMQSession_0_8 extends AMQSession public void sendQueueBind(final AMQShortString queueName, final AMQShortString routingKey, final FieldTable arguments, final AMQShortString exchangeName, final AMQDestination dest) throws AMQException, FailoverException { - AMQFrame queueBind = QueueBindBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), arguments, // arguments - exchangeName, // exchange - false, // nowait - queueName, // queue - routingKey, // routingKey - getTicket()); // ticket - - getProtocolHandler().syncWrite(queueBind, QueueBindOkBody.class); + getProtocolHandler().syncWrite(getProtocolHandler().getMethodRegistry().createQueueBindBody + (getTicket(),queueName,exchangeName,routingKey,false,arguments). + generateFrame(_channelId), QueueBindOkBody.class); } public void sendClose(long timeout) throws AMQException, FailoverException { getProtocolHandler().closeSession(this); - - final AMQFrame frame = ChannelCloseBody.createAMQFrame - (getChannelId(), getProtocolMajorVersion(), getProtocolMinorVersion(), - 0, // classId - 0, // methodId - AMQConstant.REPLY_SUCCESS.getCode(), // replyCode - new AMQShortString("JMS client closing channel")); // replyText - - getProtocolHandler().syncWrite(frame, ChannelCloseOkBody.class, timeout); + getProtocolHandler().syncWrite(getProtocolHandler().getMethodRegistry().createChannelCloseBody(AMQConstant.REPLY_SUCCESS.getCode(), + new AMQShortString("JMS client closing channel"), 0, 0).generateFrame(_channelId), + ChannelCloseOkBody.class, timeout); // When control resumes at this point, a reply will have been received that // indicates the broker has closed the channel successfully. } @@ -126,21 +122,14 @@ public class AMQSession_0_8 extends AMQSession { final AMQProtocolHandler handler = getProtocolHandler(); - handler.syncWrite(TxCommitBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion()), TxCommitOkBody.class); + handler.syncWrite(getProtocolHandler().getMethodRegistry().createTxCommitBody().generateFrame(_channelId), TxCommitOkBody.class); } public void sendCreateQueue(AMQShortString name, final boolean autoDelete, final boolean durable, final boolean exclusive) throws AMQException, FailoverException { - AMQFrame queueDeclare = QueueDeclareBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), null, // arguments - autoDelete, // autoDelete - durable, // durable - exclusive, // exclusive - false, // nowait - false, // passive - name, // queue - getTicket()); // ticket - + QueueDeclareBody body = getMethodRegistry().createQueueDeclareBody(getTicket(),name,false,durable,exclusive,autoDelete,false,null); + AMQFrame queueDeclare = body.generateFrame(_channelId); getProtocolHandler().syncWrite(queueDeclare, QueueDeclareOkBody.class); } @@ -151,16 +140,29 @@ public class AMQSession_0_8 extends AMQSession if (isStrictAMQP()) { // We can't use the BasicRecoverBody-OK method as it isn't part of the spec. - _connection.getProtocolHandler().writeFrame( - BasicRecoverBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), false)); // requeue + + BasicRecoverBody body = getMethodRegistry().createBasicRecoverBody(false); + _connection.getProtocolHandler().writeFrame(body.generateFrame(_channelId)); _logger.warn("Session Recover cannot be guaranteed with STRICT_AMQP. Messages may arrive out of order."); } else { - - _connection.getProtocolHandler().syncWrite( - BasicRecoverBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), false) // requeue - , BasicRecoverOkBody.class); + // in Qpid the 0-8 spec was hacked to have a recover-ok method... this is bad + // in 0-9 we used the cleaner addition of a new sync recover method with its own ok + if(getProtocolHandler().getProtocolVersion().equals(ProtocolVersion.v8_0)) + { + BasicRecoverBody body = getMethodRegistry().createBasicRecoverBody(false); + _connection.getProtocolHandler().syncWrite(body.generateFrame(_channelId), BasicRecoverOkBody.class); + } + else if(getProtocolVersion().equals(ProtocolVersion.v0_9)) + { + BasicRecoverSyncBody body = ((MethodRegistry_0_9)getMethodRegistry()).createBasicRecoverSyncBody(false); + _connection.getProtocolHandler().syncWrite(body.generateFrame(_channelId), BasicRecoverSyncOkBody.class); + } + else + { + throw new RuntimeException("Unsupported version of the AMQP Protocol: " + getProtocolVersion()); + } } } @@ -189,13 +191,13 @@ public class AMQSession_0_8 extends AMQSession { if (_logger.isDebugEnabled()) { - _logger.debug("Rejecting delivery tag:" + deliveryTag); + _logger.debug("Rejecting delivery tag:" + deliveryTag + ":SessionHC:" + this.hashCode()); } - AMQFrame basicRejectBody = BasicRejectBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), deliveryTag, - requeue); + BasicRejectBody body = getMethodRegistry().createBasicRejectBody(deliveryTag, requeue); + AMQFrame frame = body.generateFrame(_channelId); - _connection.getProtocolHandler().writeFrame(basicRejectBody); + _connection.getProtocolHandler().writeFrame(frame); } } @@ -214,10 +216,8 @@ public class AMQSession_0_8 extends AMQSession { public AMQMethodEvent execute() throws AMQException, FailoverException { - AMQFrame boundFrame = ExchangeBoundBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), - exchangeName, // exchange - queueName, // queue - routingKey); // routingKey + AMQFrame boundFrame = getProtocolHandler().getMethodRegistry().createExchangeBoundBody + (exchangeName, routingKey, queueName).generateFrame(_channelId); return getProtocolHandler().syncWrite(boundFrame, ExchangeBoundOkBody.class); @@ -227,7 +227,7 @@ public class AMQSession_0_8 extends AMQSession // Extract and return the response code from the query. ExchangeBoundOkBody responseBody = (ExchangeBoundOkBody) response.getMethod(); - return (responseBody.replyCode == 0); + return (responseBody.getReplyCode() == 0); } catch (AMQException e) { @@ -238,7 +238,6 @@ public class AMQSession_0_8 extends AMQSession public void sendConsume(BasicMessageConsumer consumer, AMQShortString queueName, AMQProtocolHandler protocolHandler, boolean nowait, String messageSelector, AMQShortString tag) throws AMQException, FailoverException { - FieldTable arguments = FieldTableFactory.newFieldTable(); if ((messageSelector != null) && !messageSelector.equals("")) { @@ -255,18 +254,17 @@ public class AMQSession_0_8 extends AMQSession arguments.put(AMQPFilterTypes.NO_CONSUME.getValue(), Boolean.TRUE); } - consumer.setConsumerTag(tag); - // we must register the consumer in the map before we actually start listening - _consumers.put(tag, consumer); - // TODO: Be aware of possible changes to parameter order as versions change. - AMQFrame jmsConsume = BasicConsumeBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), arguments, // arguments - tag, // consumerTag - consumer.isExclusive(), // exclusive - consumer.getAcknowledgeMode() == Session.NO_ACKNOWLEDGE, // noAck - consumer.isNoLocal(), // noLocal - nowait, // nowait - queueName, // queue - getTicket()); // ticket + BasicConsumeBody body = getMethodRegistry().createBasicConsumeBody(getTicket(), + queueName, + tag, + consumer.isNoLocal(), + consumer.getAcknowledgeMode() == Session.NO_ACKNOWLEDGE, + consumer.isExclusive(), + nowait, + arguments); + + + AMQFrame jmsConsume = body.generateFrame(_channelId); if (nowait) { @@ -281,48 +279,37 @@ public class AMQSession_0_8 extends AMQSession public void sendExchangeDeclare(final AMQShortString name, final AMQShortString type, final AMQProtocolHandler protocolHandler, final boolean nowait) throws AMQException, FailoverException { - AMQFrame exchangeDeclare = ExchangeDeclareBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), null, // arguments - false, // autoDelete - false, // durable - name, // exchange - false, // internal - nowait, // nowait - false, // passive - getTicket(), // ticket - type); // type + ExchangeDeclareBody body = getMethodRegistry().createExchangeDeclareBody(getTicket(),name,type,false,false,false,false,nowait,null); + AMQFrame exchangeDeclare = body.generateFrame(_channelId); protocolHandler.syncWrite(exchangeDeclare, ExchangeDeclareOkBody.class); } public void sendQueueDeclare(final AMQDestination amqd, final AMQProtocolHandler protocolHandler) throws AMQException, FailoverException { - AMQFrame queueDeclare = QueueDeclareBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), null, // arguments - amqd.isAutoDelete(), // autoDelete - amqd.isDurable(), // durable - amqd.isExclusive(), // exclusive - false, // nowait - false, // passive - amqd.getAMQQueueName(), // queue - getTicket()); // ticket + QueueDeclareBody body = getMethodRegistry().createQueueDeclareBody(getTicket(),amqd.getAMQQueueName(),false,amqd.isDurable(),amqd.isExclusive(),amqd.isAutoDelete(),false,null); + + AMQFrame queueDeclare = body.generateFrame(_channelId); protocolHandler.syncWrite(queueDeclare, QueueDeclareOkBody.class); } public void sendQueueDelete(final AMQShortString queueName) throws AMQException, FailoverException { - AMQFrame queueDeleteFrame = QueueDeleteBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), false, // ifEmpty - false, // ifUnused - true, // nowait - queueName, // queue - getTicket()); // ticket + QueueDeleteBody body = getMethodRegistry().createQueueDeleteBody(getTicket(), + queueName, + false, + false, + true); + AMQFrame queueDeleteFrame = body.generateFrame(_channelId); getProtocolHandler().syncWrite(queueDeleteFrame, QueueDeleteOkBody.class); } public void sendSuspendChannel(boolean suspend) throws AMQException, FailoverException { - AMQFrame channelFlowFrame = ChannelFlowBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), !suspend); - + ChannelFlowBody body = getMethodRegistry().createChannelFlowBody(!suspend); + AMQFrame channelFlowFrame = body.generateFrame(_channelId); _connection.getProtocolHandler().syncWrite(channelFlowFrame, ChannelFlowOkBody.class); } @@ -348,8 +335,9 @@ public class AMQSession_0_8 extends AMQSession public void sendRollback() throws AMQException, FailoverException { - _connection.getProtocolHandler().syncWrite(TxRollbackBody.createAMQFrame(_channelId, - getProtocolMajorVersion(), getProtocolMinorVersion()), TxRollbackOkBody.class); + TxRollbackBody body = getMethodRegistry().createTxRollbackBody(); + AMQFrame frame = body.generateFrame(getChannelId()); + getProtocolHandler().syncWrite(frame, TxRollbackOkBody.class); } public TemporaryQueue createTemporaryQueue() throws JMSException @@ -360,68 +348,109 @@ public class AMQSession_0_8 extends AMQSession } public TopicSubscriber createDurableSubscriber(Topic topic, String name) throws JMSException - { - - checkNotClosed(); - AMQTopic origTopic = checkValidTopic(topic); - AMQTopic dest = AMQTopic.createDurableTopic(origTopic, name, _connection); - TopicSubscriberAdaptor subscriber = _subscriptions.get(name); - if (subscriber != null) - { - if (subscriber.getTopic().equals(topic)) - { - throw new IllegalStateException("Already subscribed to topic " + topic + " with subscription exchange " - + name); - } - else - { - unsubscribe(name); - } - } - else - { - AMQShortString topicName; - if (topic instanceof AMQTopic) - { - topicName = ((AMQTopic) topic).getRoutingKey(); - } - else - { - topicName = new AMQShortString(topic.getTopicName()); - } - - if (_strictAMQP) - { - if (_strictAMQPFATAL) - { - throw new UnsupportedOperationException("JMS Durable not currently supported by AMQP."); - } - else - { - _logger.warn("Unable to determine if subscription already exists for '" + topicName + "' " - + "for creation durableSubscriber. Requesting queue deletion regardless."); - } - - deleteQueue(dest.getAMQQueueName()); - } - else - { - // if the queue is bound to the exchange but NOT for this topic, then the JMS spec - // says we must trash the subscription. - if (isQueueBound(dest.getExchangeName(), dest.getAMQQueueName()) - && !isQueueBound(dest.getExchangeName(), dest.getAMQQueueName(), topicName)) - { - deleteQueue(dest.getAMQQueueName()); - } - } - } - - subscriber = new TopicSubscriberAdaptor(dest, (BasicMessageConsumer) createConsumer(dest)); - - _subscriptions.put(name, subscriber); - _reverseSubscriptionMap.put(subscriber.getMessageConsumer(), name); - - return subscriber; - } + { + + checkNotClosed(); + AMQTopic origTopic = checkValidTopic(topic); + AMQTopic dest = AMQTopic.createDurableTopic(origTopic, name, _connection); + TopicSubscriberAdaptor subscriber = _subscriptions.get(name); + if (subscriber != null) + { + if (subscriber.getTopic().equals(topic)) + { + throw new IllegalStateException("Already subscribed to topic " + topic + " with subscription exchange " + + name); + } + else + { + unsubscribe(name); + } + } + else + { + AMQShortString topicName; + if (topic instanceof AMQTopic) + { + topicName = ((AMQTopic) topic).getRoutingKey(); + } + else + { + topicName = new AMQShortString(topic.getTopicName()); + } + + if (_strictAMQP) + { + if (_strictAMQPFATAL) + { + throw new UnsupportedOperationException("JMS Durable not currently supported by AMQP."); + } + else + { + _logger.warn("Unable to determine if subscription already exists for '" + topicName + "' " + + "for creation durableSubscriber. Requesting queue deletion regardless."); + } + + deleteQueue(dest.getAMQQueueName()); + } + else + { + // if the queue is bound to the exchange but NOT for this topic, then the JMS spec + // says we must trash the subscription. + if (isQueueBound(dest.getExchangeName(), dest.getAMQQueueName()) + && !isQueueBound(dest.getExchangeName(), dest.getAMQQueueName(), topicName)) + { + deleteQueue(dest.getAMQQueueName()); + } + } + } + + subscriber = new TopicSubscriberAdaptor(dest, (BasicMessageConsumer) createConsumer(dest)); + + _subscriptions.put(name, subscriber); + _reverseSubscriptionMap.put(subscriber.getMessageConsumer(), name); + + return subscriber; + } + + class QueueDeclareOkHandler extends SpecificMethodFrameListener + { + + private long _messageCount; + private long _consumerCount; + + public QueueDeclareOkHandler() + { + super(getChannelId(), QueueDeclareOkBody.class); + } + + public boolean processMethod(int channelId, AMQMethodBody frame) //throws AMQException + { + boolean matches = super.processMethod(channelId, frame); + if (matches) + { + QueueDeclareOkBody declareOk = (QueueDeclareOkBody) frame; + _messageCount = declareOk.getMessageCount(); + _consumerCount = declareOk.getConsumerCount(); + } + return matches; + } + + } + + Long requestQueueDepth(AMQDestination amqd) throws AMQException, FailoverException + { + AMQFrame queueDeclare = + getMethodRegistry().createQueueDeclareBody(getTicket(), + amqd.getAMQQueueName(), + true, + amqd.isDurable(), + amqd.isExclusive(), + amqd.isAutoDelete(), + false, + null).generateFrame(_channelId); + QueueDeclareOkHandler okHandler = new QueueDeclareOkHandler(); + getProtocolHandler().writeCommandFrameAndWaitForReply(queueDeclare, okHandler); + return okHandler._messageCount; + } } diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryTopic.java b/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryTopic.java index 6c954ec3df..7b5781530b 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryTopic.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryTopic.java @@ -24,6 +24,7 @@ import org.apache.qpid.framing.AMQShortString; import javax.jms.JMSException; import javax.jms.TemporaryTopic; +import java.util.UUID; /** * AMQ implementation of TemporaryTopic. @@ -38,7 +39,7 @@ class AMQTemporaryTopic extends AMQTopic implements TemporaryTopic, TemporaryDes */ public AMQTemporaryTopic(AMQSession session) { - super(session.getTemporaryTopicExchangeName(),new AMQShortString("TempQueue" + Long.toString(System.currentTimeMillis()))); + super(session.getTemporaryTopicExchangeName(),new AMQShortString("tmp_" + UUID.randomUUID())); _session = session; } diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java b/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java index fcc36cd3fd..40041afdc6 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java @@ -120,8 +120,7 @@ public class AMQTopic extends AMQDestination implements Topic public boolean isNameRequired() { - // Topics always rely on a server generated queue name. - return false; + return !isDurable(); } /** @@ -136,4 +135,17 @@ public class AMQTopic extends AMQDestination implements Topic public void setQueueName(String queueName) { } + + public boolean equals(Object o) + { + return (o instanceof AMQTopic) + && ((AMQTopic)o).getExchangeName().equals(getExchangeName()) + && ((AMQTopic)o).getRoutingKey().equals(getRoutingKey()); + + } + + public int hashCode() + { + return getExchangeName().hashCode() + getRoutingKey().hashCode(); + } } diff --git a/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java b/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java index 015a2ccc57..1741903bb8 100644 --- a/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java +++ b/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java @@ -20,45 +20,47 @@ */ package org.apache.qpid.client; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; - -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.MessageListener; - +import org.apache.qpid.AMQException; +import org.apache.qpid.client.failover.FailoverException; import org.apache.qpid.client.message.AbstractJMSMessage; import org.apache.qpid.client.message.MessageFactoryRegistry; import org.apache.qpid.client.message.UnprocessedMessage; import org.apache.qpid.client.protocol.AMQProtocolHandler; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.*; import org.apache.qpid.jms.MessageConsumer; import org.apache.qpid.jms.Session; -import org.apache.qpid.AMQException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.SortedSet; +import java.util.ArrayList; +import java.util.Collections; +import java.util.TreeSet; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + public abstract class BasicMessageConsumer<H, B> extends Closeable implements MessageConsumer { private static final Logger _logger = LoggerFactory.getLogger(BasicMessageConsumer.class); - /** - * The connection being used by this consumer - */ - protected AMQConnection _connection; + /** The connection being used by this consumer */ + protected final AMQConnection _connection; - protected String _messageSelector; + protected final String _messageSelector; - private boolean _noLocal; + private final boolean _noLocal; - private AMQDestination _destination; + private final AMQDestination _destination; /** * When true indicates that a blocking receive call is in progress @@ -69,15 +71,11 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me */ private final AtomicReference<MessageListener> _messageListener = new AtomicReference<MessageListener>(); - /** - * The consumer tag allows us to close the consumer by sending a jmsCancel method to the broker - */ + /** The consumer tag allows us to close the consumer by sending a jmsCancel method to the broker */ protected AMQShortString _consumerTag; - /** - * We need to know the channel id when constructing frames - */ - protected int _channelId; + /** We need to know the channel id when constructing frames */ + protected final int _channelId; /** * Used in the blocking receive methods to receive a message from the Session thread. <p/> Or to notify of errors @@ -85,40 +83,40 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me */ protected final ArrayBlockingQueue _synchronousQueue; - protected MessageFactoryRegistry _messageFactory; + protected final MessageFactoryRegistry _messageFactory; protected final AMQSession _session; - protected AMQProtocolHandler _protocolHandler; + protected final AMQProtocolHandler _protocolHandler; /** * We need to store the "raw" field table so that we can resubscribe in the event of failover being required */ - private FieldTable _rawSelectorFieldTable; + private final FieldTable _rawSelectorFieldTable; /** * We store the high water prefetch field in order to be able to reuse it when resubscribing in the event of * failover */ - private int _prefetchHigh; + private final int _prefetchHigh; /** * We store the low water prefetch field in order to be able to reuse it when resubscribing in the event of * failover */ - private int _prefetchLow; + private final int _prefetchLow; /** * We store the exclusive field in order to be able to reuse it when resubscribing in the event of failover */ - private boolean _exclusive; + private final boolean _exclusive; /** * The acknowledge mode in force for this consumer. Note that the AMQP protocol allows different ack modes per * consumer whereas JMS defines this at the session level, hence why we associate it with the consumer in our * implementation. */ - private int _acknowledgeMode; + private final int _acknowledgeMode; /** * Number of messages unacknowledged in DUPS_OK_ACKNOWLEDGE mode @@ -132,6 +130,19 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me private boolean _dups_ok_acknowledge_send; /** + * List of tags delievered, The last of which which should be acknowledged on commit in transaction mode. + */ + private ConcurrentLinkedQueue<Long> _receivedDeliveryTags = new ConcurrentLinkedQueue<Long>(); + + /** The last tag that was "multiple" acknowledged on this session (if transacted) */ + private long _lastAcked; + + /** set of tags which have previously been acked; but not part of the multiple ack (transacted mode only) */ + private final SortedSet<Long> _previouslyAcked = new TreeSet<Long>(); + + private final Object _commitLock = new Object(); + + /** * The thread that was used to call receive(). This is important for being able to interrupt that thread if a * receive() is in progress. */ @@ -148,12 +159,13 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me * autoClose denotes that the consumer will automatically cancel itself when there are no more messages to receive * on the queue. This is used for queue browsing. */ - private boolean _autoClose; - private boolean _closeWhenNoMessages; + private final boolean _autoClose; - private boolean _noConsume; + private final boolean _noConsume; private List<StackTraceElement> _closedStack = null; + + protected BasicMessageConsumer(int channelId, AMQConnection connection, AMQDestination destination, String messageSelector, boolean noLocal, MessageFactoryRegistry messageFactory, AMQSession session, AMQProtocolHandler protocolHandler, @@ -172,7 +184,7 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me _prefetchHigh = prefetchHigh; _prefetchLow = prefetchLow; _exclusive = exclusive; - _acknowledgeMode = acknowledgeMode; + _synchronousQueue = new ArrayBlockingQueue(prefetchHigh, true); _autoClose = autoClose; _noConsume = noConsume; @@ -182,6 +194,10 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me { _acknowledgeMode = Session.NO_ACKNOWLEDGE; } + else + { + _acknowledgeMode = acknowledgeMode; + } } public AMQDestination getDestination() @@ -254,14 +270,14 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me if (messageListener != null) { - // handle case where connection has already been started, and the dispatcher has alreaded started + //todo: handle case where connection has already been started, and the dispatcher has alreaded started // putting values on the _synchronousQueue synchronized (_session) { _messageListener.set(messageListener); _session.setHasMessageListeners(); - _session.startDistpatcherIfNecessary(); + _session.startDispatcherIfNecessary(); } } } @@ -273,12 +289,32 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me { _session.addUnacknowledgedMessage(jmsMsg.getDeliveryTag()); } - + _session.setInRecovery(false); } - private void acquireReceiving() throws JMSException + /** + * @param immediate if true then return immediately if the connection is failing over + * + * @return boolean if the acquisition was successful + * + * @throws JMSException if a listener has already been set or another thread is receiving + * @throws InterruptedException if interrupted + */ + private boolean acquireReceiving(boolean immediate) throws JMSException, InterruptedException { + if (_connection.isFailingOver()) + { + if (immediate) + { + return false; + } + else + { + _connection.blockUntilNotFailingOver(); + } + } + if (!_receiving.compareAndSet(false, true)) { throw new javax.jms.IllegalStateException("Another thread is already receiving."); @@ -290,6 +326,7 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me } _receivingThread = Thread.currentThread(); + return true; } private void releaseReceiving() @@ -343,26 +380,30 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me checkPreConditions(); - acquireReceiving(); - - _session.startDistpatcherIfNecessary(); - try { - if (closeOnAutoClose()) + acquireReceiving(false); + } + catch (InterruptedException e) + { + _logger.warn("Interrupted acquire: " + e); + if (isClosed()) { return null; } + } - Object o = getMessageFromQueue(l); + _session.startDispatcherIfNecessary(); + try + { + Object o = getMessageFromQueue(l); final AbstractJMSMessage m = returnMessageOrThrow(o); if (m != null) { preApplicationProcessing(m); postDeliver(m); } - return m; } catch (InterruptedException e) @@ -395,35 +436,34 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me return o; } - private boolean closeOnAutoClose() throws JMSException - { - if (isAutoClose() && _closeWhenNoMessages && _synchronousQueue.isEmpty()) - { - close(false); - - return true; - } - else - { - return false; - } - } - public Message receiveNoWait() throws JMSException { checkPreConditions(); - acquireReceiving(); - - _session.startDistpatcherIfNecessary(); - try { - if (closeOnAutoClose()) + if (!acquireReceiving(true)) { + //If we couldn't acquire the receiving thread then return null. + // This will occur if failing over. return null; } + } + catch (InterruptedException e) + { + /* + * This seems slightly shoddy but should never actually be executed + * since we told acquireReceiving to return immediately and it shouldn't + * block on anything. + */ + + return null; + } + + _session.startDispatcherIfNecessary(); + try + { Object o = getMessageFromQueue(-1); final AbstractJMSMessage m = returnMessageOrThrow(o); if (m != null) @@ -450,7 +490,7 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me * We can get back either a Message or an exception from the queue. This method examines the argument and deals with * it by throwing it (if an exception) or returning it (in any other case). * - * @param o + * @param o the object to return or throw * @return a message only if o is a Message * @throws JMSException if the argument is a throwable. If it is a JMSException it is rethrown as is, but if not a * JMSException is created with the linked exception set appropriately @@ -481,61 +521,69 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me public void close(boolean sendClose) throws JMSException { - // synchronized (_closed) - if (_logger.isInfoEnabled()) { _logger.info("Closing consumer:" + debugIdentity()); } - synchronized (_connection.getFailoverMutex()) + if (!_closed.getAndSet(true)) { - if (!_closed.getAndSet(true)) + if (_logger.isDebugEnabled()) { - if (_logger.isTraceEnabled()) - { - if (_closedStack != null) - { - _logger.trace(_consumerTag + " close():" + Arrays.asList(Thread.currentThread().getStackTrace()) - .subList(3, 6)); - _logger.trace(_consumerTag + " previously:" + _closedStack.toString()); - } - else - { - _closedStack = Arrays.asList(Thread.currentThread().getStackTrace()).subList(3, 6); - } - } - - if (sendClose) + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + if (_closedStack != null) { - // TODO: Be aware of possible changes to parameter order as versions change. - sendCancel(); + _logger.debug(_consumerTag + " previously:" + _closedStack.toString()); } else { - // //fixme this probably is not right - // if (!isNoConsume()) - //{ // done in BasicCancelOK Handler but not sending one so just deregister. - // deregisterConsumer(); - //} + _closedStack = Arrays.asList(stackTrace).subList(3, stackTrace.length - 1); } + } - deregisterConsumer(); - - if (_messageListener != null && _receiving.get() && _receivingThread != null) + if (sendClose) + { + // The Synchronized block only needs to protect network traffic. + synchronized (_connection.getFailoverMutex()) { - if (_logger.isInfoEnabled()) + try { - _logger.info("Interrupting thread: " + _receivingThread); + sendCancel(); } + catch (AMQException e) + { + throw new JMSAMQException("Error closing consumer: " + e, e); + } + catch (FailoverException e) + { + throw new JMSAMQException("FailoverException interrupted basic cancel.", e); + } + } + } + else + { + // //fixme this probably is not right + // if (!isNoConsume()) + { // done in BasicCancelOK Handler but not sending one so just deregister. + deregisterConsumer(); + } + } - _receivingThread.interrupt(); + // This will occur if session.close is called closing all consumers we may be blocked waiting for a receive + // so we need to let it know it is time to close. + if ((_messageListener != null) && _receiving.get()) + { + if (_logger.isInfoEnabled()) + { + _logger.info("Interrupting thread: " + _receivingThread); } + + _receivingThread.interrupt(); } } } - public abstract void sendCancel() throws JMSAMQException; + abstract void sendCancel() throws AMQException, FailoverException; /** * Called when you need to invalidate a consumer. Used for example when failover has occurred and the client has @@ -547,13 +595,14 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me { _closed.set(true); - if (_logger.isTraceEnabled()) + if (_logger.isDebugEnabled()) { if (_closedStack != null) { - _logger.trace(_consumerTag + " markClosed():" + Arrays - .asList(Thread.currentThread().getStackTrace()).subList(3, 8)); - _logger.trace(_consumerTag + " previously:" + _closedStack.toString()); + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + _logger.debug(_consumerTag + " markClosed():" + + Arrays.asList(stackTrace).subList(3, stackTrace.length - 1)); + _logger.debug(_consumerTag + " previously:" + _closedStack.toString()); } else { @@ -570,9 +619,8 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me * message listener or a synchronous receive() caller. * * @param messageFrame the raw unprocessed mesage - * @param channelId channel on which this message was sent */ - void notifyMessage(UnprocessedMessage messageFrame, int channelId) + void notifyMessage(UnprocessedMessage messageFrame) { final boolean debug = _logger.isDebugEnabled(); @@ -584,6 +632,7 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me try { AbstractJMSMessage jmsMessage = createJMSMessageFromUnprocessedMessage(messageFrame); + if (debug) { _logger.debug("Message is of type: " + jmsMessage.getClass().getName()); @@ -594,11 +643,9 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me // if (!_closed.get()) { - jmsMessage.setConsumer(this); - preDeliver(jmsMessage); - notifyMessage(jmsMessage, channelId); + notifyMessage(jmsMessage); } // else // { @@ -624,12 +671,8 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me public abstract AbstractJMSMessage createJMSMessageFromUnprocessedMessage(UnprocessedMessage<H, B> messageFrame) throws Exception; - - /** - * @param jmsMessage this message has already been processed so can't redo preDeliver - * @param channelId - */ - public void notifyMessage(AbstractJMSMessage jmsMessage, int channelId) + /** @param jmsMessage this message has already been processed so can't redo preDeliver */ + public void notifyMessage(AbstractJMSMessage jmsMessage) { try { @@ -700,26 +743,6 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me break; case Session.DUPS_OK_ACKNOWLEDGE: - if (++_outstanding >= _prefetchHigh) - { - _dups_ok_acknowledge_send = true; - } - - if (_outstanding <= _prefetchLow) - { - _dups_ok_acknowledge_send = false; - } - - if (_dups_ok_acknowledge_send) - { - if (!_session.isInRecovery()) - { - _session.acknowledgeMessage(msg.getDeliveryTag(), true); - } - } - - break; - case Session.AUTO_ACKNOWLEDGE: // we do not auto ack a message if the application code called recover() if (!_session.isInRecovery()) @@ -728,7 +751,6 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me } break; - case Session.SESSION_TRANSACTED: if (isNoConsume()) { @@ -743,22 +765,105 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me } } + + /** + * Acknowledge up to last message delivered (if any). Used when commiting. + * + * @return the lastDeliveryTag to acknowledge + */ + Long getLastDelivered() + { + if (!_receivedDeliveryTags.isEmpty()) + { + Long lastDeliveryTag = _receivedDeliveryTags.poll(); + + while (!_receivedDeliveryTags.isEmpty()) + { + lastDeliveryTag = _receivedDeliveryTags.poll(); + } + + assert _receivedDeliveryTags.isEmpty(); + + return lastDeliveryTag; + } + + return null; + } + + /** + * Acknowledge up to last message delivered (if any). Used when commiting. + */ + void acknowledgeDelivered() + { + synchronized(_commitLock) + { + ArrayList<Long> tagsToAck = new ArrayList<Long>(); + + while (!_receivedDeliveryTags.isEmpty()) + { + tagsToAck.add(_receivedDeliveryTags.poll()); + } + + Collections.sort(tagsToAck); + + long prevAcked = _lastAcked; + long oldAckPoint = -1; + + while(oldAckPoint != prevAcked) + { + oldAckPoint = prevAcked; + + Iterator<Long> tagsToAckIterator = tagsToAck.iterator(); + + while(tagsToAckIterator.hasNext() && tagsToAckIterator.next() == prevAcked+1) + { + tagsToAckIterator.remove(); + prevAcked++; + } + + Iterator<Long> previousAckIterator = _previouslyAcked.iterator(); + while(previousAckIterator.hasNext() && previousAckIterator.next() == prevAcked+1) + { + previousAckIterator.remove(); + prevAcked++; + } + + } + if(prevAcked != _lastAcked) + { + _session.acknowledgeMessage(prevAcked, true); + _lastAcked = prevAcked; + } + + Iterator<Long> tagsToAckIterator = tagsToAck.iterator(); + + while(tagsToAckIterator.hasNext()) + { + Long tag = tagsToAckIterator.next(); + _session.acknowledgeMessage(tag, false); + _previouslyAcked.add(tag); + } + } + } + + void notifyError(Throwable cause) { // synchronized (_closed) { _closed.set(true); - if (_logger.isTraceEnabled()) + if (_logger.isDebugEnabled()) { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); if (_closedStack != null) { - _logger.trace(_consumerTag + " notifyError():" + Arrays - .asList(Thread.currentThread().getStackTrace()).subList(3, 8)); - _logger.trace(_consumerTag + " previously" + _closedStack.toString()); + _logger.debug(_consumerTag + " notifyError():" + + Arrays.asList(stackTrace).subList(3, stackTrace.length - 1)); + _logger.debug(_consumerTag + " previously" + _closedStack.toString()); } else { - _closedStack = Arrays.asList(Thread.currentThread().getStackTrace()).subList(3, 8); + _closedStack = Arrays.asList(stackTrace).subList(3, stackTrace.length - 1); } } } @@ -823,20 +928,13 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me return _noConsume; } - public void closeWhenNoMessages(boolean b) + public void rollback() { - _closeWhenNoMessages = b; - - if (_closeWhenNoMessages && _synchronousQueue.isEmpty() && _receiving.get() && (_messageListener != null)) - { - _receivingThread.interrupt(); - } - + rollbackPendingMessages(); } - public void rollback() + public void rollbackPendingMessages() { - // rollback pending messages if (_synchronousQueue.size() > 0) { if (_logger.isDebugEnabled()) @@ -847,6 +945,9 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me Iterator iterator = _synchronousQueue.iterator(); + int initialSize = _synchronousQueue.size(); + + boolean removed = false; while (iterator.hasNext()) { @@ -855,22 +956,30 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me { _session.rejectMessage(((AbstractJMSMessage) o), true); - if (_logger.isTraceEnabled()) + if (_logger.isDebugEnabled()) { - _logger.trace("Rejected message:" + ((AbstractJMSMessage) o).getDeliveryTag()); + _logger.debug("Rejected message:" + ((AbstractJMSMessage) o).getDeliveryTag()); } iterator.remove(); + removed = true; } else { - _logger.error("Queue contained a :" + o - .getClass() + " unable to reject as it is not an AbstractJMSMessage. Will be cleared"); + _logger.error("Queue contained a :" + o.getClass() + + " unable to reject as it is not an AbstractJMSMessage. Will be cleared"); iterator.remove(); + removed = true; + } } + + if (removed && (initialSize == _synchronousQueue.size())) + { + _logger.error("Queue had content removed but didn't change in size." + initialSize); } + if (_synchronousQueue.size() != 0) { _logger.warn("Queue was not empty after rejecting all messages Remaining:" + _synchronousQueue.size()); @@ -883,7 +992,7 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me public String debugIdentity() { - return String.valueOf(_consumerTag); + return String.valueOf(_consumerTag) + "[" + System.identityHashCode(this) + "]"; } public void clearReceiveQueue() @@ -891,7 +1000,6 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me _synchronousQueue.clear(); } - public void start() { // do nothing as this is a 0_10 feature @@ -923,4 +1031,12 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me { _session.addBindingKey(this,amqd,routingKey); } + + /** to be called when a failover has occured */ + public void failedOver() + { + clearReceiveQueue(); + // TGM FIXME: think this should just be removed + // clearUnackedMessages(); + } } diff --git a/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_10.java b/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_10.java index 4f2d5d8c34..d92899087b 100644 --- a/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_10.java +++ b/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_10.java @@ -117,7 +117,7 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<Struct[], By * @param jmsMessage this message has already been processed so can't redo preDeliver * @param channelId */ - public void notifyMessage(AbstractJMSMessage jmsMessage, int channelId) + public void notifyMessage(AbstractJMSMessage jmsMessage) { boolean messageOk = false; try @@ -147,7 +147,7 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<Struct[], By MessageCreditUnit.MESSAGE, 1); } _logger.debug("messageOk, trying to notify"); - super.notifyMessage(jmsMessage, channelId); + super.notifyMessage(jmsMessage); } } @@ -162,7 +162,7 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<Struct[], By { int channelId = getSession().getChannelId(); long deliveryId = message.getMessageTransferId(); - String consumerTag = getConsumerTag().toString(); + AMQShortString consumerTag = getConsumerTag(); AMQShortString exchange; AMQShortString routingKey; boolean redelivered = false; @@ -211,20 +211,13 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<Struct[], By * This method is invoked when this consumer is stopped. * It tells the broker to stop delivering messages to this consumer. */ - public void sendCancel() throws JMSAMQException + void sendCancel() throws AMQException { ((AMQSession_0_10) getSession()).getQpidSession().messageCancel(getConsumerTag().toString()); ((AMQSession_0_10) getSession()).getQpidSession().sync(); // confirm cancel getSession().confirmConsumerCancelled(getConsumerTag()); - try - { - ((AMQSession_0_10) getSession()).getCurrentException(); - } - catch (AMQException e) - { - throw new JMSAMQException("Problem when stopping consumer", e); - } + ((AMQSession_0_10) getSession()).getCurrentException(); } void notifyMessage(UnprocessedMessage messageFrame, int channelId) @@ -261,7 +254,7 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<Struct[], By } ((UnprocessedMessage_0_10) messageFrame).setReplyToURL(replyToUrl); } - super.notifyMessage(messageFrame, channelId); + super.notifyMessage(messageFrame); } protected void preApplicationProcessing(AbstractJMSMessage jmsMsg) throws JMSException @@ -486,5 +479,4 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<Struct[], By } return o; } - -}
\ No newline at end of file +} diff --git a/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_8.java b/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_8.java index 1bc9e4d855..5414e25539 100644 --- a/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_8.java +++ b/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_8.java @@ -51,30 +51,17 @@ public class BasicMessageConsumer_0_8 extends BasicMessageConsumer<ContentHeader acknowledgeMode, noConsume, autoClose); } - public void sendCancel() throws JMSAMQException + void sendCancel() throws AMQException, FailoverException { - final AMQFrame cancelFrame = - BasicCancelBody.createAMQFrame(_channelId, _protocolHandler.getProtocolMajorVersion(), - _protocolHandler.getProtocolMinorVersion(), _consumerTag, // consumerTag - false); // nowait + BasicCancelBody body = getSession().getMethodRegistry().createBasicCancelBody(_consumerTag, false); - try - { - _protocolHandler.syncWrite(cancelFrame, BasicCancelOkBody.class); + final AMQFrame cancelFrame = body.generateFrame(_channelId); - if (_logger.isDebugEnabled()) - { - _logger.debug("CancelOk'd for consumer:" + debugIdentity()); - } + _protocolHandler.syncWrite(cancelFrame, BasicCancelOkBody.class); - } - catch (AMQException e) + if (_logger.isDebugEnabled()) { - throw new JMSAMQException("Error closing consumer: " + e, e); - } - catch (FailoverException e) - { - throw new JMSAMQException("FailoverException interrupted basic cancel.", e); + _logger.debug("CancelOk'd for consumer:" + debugIdentity()); } } @@ -86,5 +73,5 @@ public class BasicMessageConsumer_0_8 extends BasicMessageConsumer<ContentHeader messageFrame.getRoutingKey(), messageFrame.getContentHeader(), messageFrame.getBodies()); } - + }
\ No newline at end of file diff --git a/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java b/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java index 4c45ec6638..5bb2955399 100644 --- a/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java +++ b/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java @@ -36,11 +36,18 @@ import javax.jms.StreamMessage; import javax.jms.TextMessage; import javax.jms.Topic; +import org.apache.mina.common.ByteBuffer; import org.apache.qpid.AMQException; import org.apache.qpid.client.message.AbstractJMSMessage; import org.apache.qpid.client.message.MessageConverter; import org.apache.qpid.client.protocol.AMQProtocolHandler; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.BasicContentHeaderProperties; +import org.apache.qpid.framing.BasicPublishBody; +import org.apache.qpid.framing.CompositeAMQDataBlock; import org.apache.qpid.framing.ContentBody; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.ExchangeDeclareBody; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -51,7 +58,7 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac private AMQConnection _connection; /** - * If true, messages will not get a timestamp. + * If true, messages will not get a timestamp. */ protected boolean _disableTimestamps; @@ -103,7 +110,7 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac private long _producerId; /** - * The session used to create this producer + * The session used to create this producer */ protected AMQSession _session; @@ -118,8 +125,8 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac private static final ContentBody[] NO_CONTENT_BODIES = new ContentBody[0]; protected BasicMessageProducer(AMQConnection connection, AMQDestination destination, boolean transacted, int channelId, - AMQSession session, AMQProtocolHandler protocolHandler, long producerId, boolean immediate, boolean mandatory, - boolean waitUntilSent) + AMQSession session, AMQProtocolHandler protocolHandler, long producerId, boolean immediate, boolean mandatory, + boolean waitUntilSent) { _connection = connection; _destination = destination; @@ -146,7 +153,7 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac } } - public abstract void declareDestination(AMQDestination destination); + abstract void declareDestination(AMQDestination destination); public void setDisableMessageID(boolean b) throws JMSException { @@ -181,7 +188,7 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac if ((i != DeliveryMode.NON_PERSISTENT) && (i != DeliveryMode.PERSISTENT)) { throw new JMSException("DeliveryMode must be either NON_PERSISTENT or PERSISTENT. Value of " + i - + " is illegal"); + + " is illegal"); } _deliveryMode = i; @@ -293,7 +300,7 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac { validateDestination(destination); sendImpl((AMQDestination) destination, message, _deliveryMode, _messagePriority, _timeToLive, _mandatory, - _immediate); + _immediate); } } @@ -310,7 +317,7 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac } public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive, - boolean mandatory) throws JMSException + boolean mandatory) throws JMSException { checkPreConditions(); checkDestination(destination); @@ -322,7 +329,7 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac } public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive, - boolean mandatory, boolean immediate) throws JMSException + boolean mandatory, boolean immediate) throws JMSException { checkPreConditions(); checkDestination(destination); @@ -334,7 +341,7 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac } public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive, - boolean mandatory, boolean immediate, boolean waitUntilSent) throws JMSException + boolean mandatory, boolean immediate, boolean waitUntilSent) throws JMSException { checkPreConditions(); checkDestination(destination); @@ -342,7 +349,7 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac { validateDestination(destination); sendImpl((AMQDestination) destination, message, deliveryMode, priority, timeToLive, mandatory, immediate, - waitUntilSent); + waitUntilSent); } } @@ -388,7 +395,7 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac else { throw new JMSException("Unable to send message, due to class conversion error: " - + message.getClass().getName()); + + message.getClass().getName()); } } } @@ -398,14 +405,19 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac if (!(destination instanceof AMQDestination)) { throw new JMSException("Unsupported destination class: " - + ((destination != null) ? destination.getClass() : null)); + + ((destination != null) ? destination.getClass() : null)); } - declareDestination((AMQDestination) destination); + AMQDestination amqDestination = (AMQDestination) destination; + if(!amqDestination.isExchangeExistsChecked()) + { + declareDestination(amqDestination); + amqDestination.setExchangeExistsChecked(true); + } } protected void sendImpl(AMQDestination destination, Message message, int deliveryMode, int priority, long timeToLive, - boolean mandatory, boolean immediate) throws JMSException + boolean mandatory, boolean immediate) throws JMSException { sendImpl(destination, message, deliveryMode, priority, timeToLive, mandatory, immediate, _waitUntilSent); } @@ -420,16 +432,27 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac * @param timeToLive * @param mandatory * @param immediate + * * @throws JMSException */ protected void sendImpl(AMQDestination destination, Message origMessage, int deliveryMode, int priority, long timeToLive, - boolean mandatory, boolean immediate, boolean wait) throws JMSException + boolean mandatory, boolean immediate, boolean wait) throws JMSException { checkTemporaryDestination(destination); origMessage.setJMSDestination(destination); AbstractJMSMessage message = convertToNativeMessage(origMessage); + if (_transacted) + { + if (_session.hasFailedOver() && _session.isDirty()) + { + throw new JMSAMQException("Failover has occurred and session is dirty so unable to send.", + new AMQSessionDirtyException("Failover has occurred and session is dirty " + + "so unable to send.")); + } + } + if (_disableMessageId) { message.setJMSMessageID(null); @@ -456,14 +479,27 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac type = AMQDestination.UNKNOWN_TYPE; } - // message.getJmsHeaders().setInteger(CustomJMSXProperty.JMS_QPID_DESTTYPE.getShortStringName(), type); + sendMessage(destination, origMessage, message, deliveryMode, priority, timeToLive, mandatory, immediate, wait); + + if (message != origMessage) + { + _logger.debug("Updating original message"); + origMessage.setJMSPriority(message.getJMSPriority()); + origMessage.setJMSTimestamp(message.getJMSTimestamp()); + _logger.debug("Setting JMSExpiration:" + message.getJMSExpiration()); + origMessage.setJMSExpiration(message.getJMSExpiration()); + origMessage.setJMSMessageID(message.getJMSMessageID()); + } - sendMessage(destination, origMessage, message, deliveryMode, priority, timeToLive, - mandatory, immediate, wait); + if (_transacted) + { + _session.markDirty(); + } } - public abstract void sendMessage(AMQDestination destination, Message origMessage, AbstractJMSMessage message, int deliveryMode, - int priority, long timeToLive, boolean mandatory, boolean immediate, boolean wait)throws JMSException; + abstract void sendMessage(AMQDestination destination, Message origMessage, AbstractJMSMessage message, + int deliveryMode, int priority, long timeToLive, boolean mandatory, + boolean immediate, boolean wait)throws JMSException; private void checkTemporaryDestination(AMQDestination destination) throws JMSException { @@ -520,7 +556,7 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac if ((_destination != null) && (suppliedDestination != null)) { throw new UnsupportedOperationException( - "This message producer was created with a Destination, therefore you cannot use an unidentified Destination"); + "This message producer was created with a Destination, therefore you cannot use an unidentified Destination"); } if (suppliedDestination == null) diff --git a/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_10.java b/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_10.java index 2e31c43b4c..5fd6c23f68 100644 --- a/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_10.java +++ b/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_10.java @@ -55,7 +55,7 @@ public class BasicMessageProducer_0_10 extends BasicMessageProducer mandatory, waitUntilSent); } - public void declareDestination(AMQDestination destination) + void declareDestination(AMQDestination destination) { ((AMQSession_0_10) getSession()).getQpidSession().exchangeDeclare(destination.getExchangeName().toString(), destination.getExchangeClass().toString(), @@ -67,9 +67,9 @@ public class BasicMessageProducer_0_10 extends BasicMessageProducer /** * Sends a message to a given destination */ - public void sendMessage(AMQDestination destination, Message origMessage, AbstractJMSMessage message, - int deliveryMode, int priority, long timeToLive, boolean mandatory, boolean immediate, - boolean wait) throws JMSException + void sendMessage(AMQDestination destination, Message origMessage, AbstractJMSMessage message, + int deliveryMode, int priority, long timeToLive, boolean mandatory, boolean immediate, + boolean wait) throws JMSException { message.prepareForSending(); if (message.get010Message() == null) diff --git a/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_8.java b/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_8.java index 0e79069463..ff991b1a03 100644 --- a/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_8.java +++ b/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_8.java @@ -45,38 +45,37 @@ public class BasicMessageProducer_0_8 extends BasicMessageProducer super(connection, destination,transacted,channelId,session, protocolHandler, producerId, immediate, mandatory,waitUntilSent); } - public void declareDestination(AMQDestination destination) + void declareDestination(AMQDestination destination) { + + ExchangeDeclareBody body = getSession().getMethodRegistry().createExchangeDeclareBody(_session.getTicket(), + destination.getExchangeName(), + destination.getExchangeClass(), + false, + false, + false, + false, + true, + null); // Declare the exchange // Note that the durable and internal arguments are ignored since passive is set to false - // TODO: Be aware of possible changes to parameter order as versions change. - AMQFrame declare = - ExchangeDeclareBody.createAMQFrame(_channelId, _protocolHandler.getProtocolMajorVersion(), - _protocolHandler.getProtocolMinorVersion(), null, // arguments - false, // autoDelete - false, // durable - destination.getExchangeName(), // exchange - false, // internal - true, // nowait - false, // passive - _session.getTicket(), // ticket - destination.getExchangeClass()); // type + + AMQFrame declare = body.generateFrame(_channelId); + _protocolHandler.writeFrame(declare); } - public void sendMessage(AMQDestination destination, Message origMessage,AbstractJMSMessage message, - int deliveryMode,int priority, long timeToLive, boolean mandatory, boolean immediate, boolean wait) throws JMSException + void sendMessage(AMQDestination destination, Message origMessage,AbstractJMSMessage message, + int deliveryMode,int priority, long timeToLive, boolean mandatory, boolean immediate, + boolean wait) throws JMSException { -// AMQP version change: Hardwire the version to 0-8 (major=8, minor=0) - // TODO: Connect this to the session version obtained from ProtocolInitiation for this session. - // Be aware of possible changes to parameter order as versions change. - AMQFrame publishFrame = - BasicPublishBody.createAMQFrame(_channelId, _protocolHandler.getProtocolMajorVersion(), - _protocolHandler.getProtocolMinorVersion(), destination.getExchangeName(), // exchange - immediate, // immediate - mandatory, // mandatory - destination.getRoutingKey(), // routingKey - _session.getTicket()); // ticket + BasicPublishBody body = getSession().getMethodRegistry().createBasicPublishBody(_session.getTicket(), + destination.getExchangeName(), + destination.getRoutingKey(), + mandatory, + immediate); + + AMQFrame publishFrame = body.generateFrame(_channelId); message.prepareForSending(); ByteBuffer payload = message.getData(); @@ -114,13 +113,13 @@ public class BasicMessageProducer_0_8 extends BasicMessageProducer _logger.debug("Sending content body frames to " + destination); } - // weight argument of zero indicates no child content headers, just bodies - // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0) - // TODO: Connect this to the session version obtained from ProtocolInitiation for this session. + + // TODO: This is a hacky way of getting the AMQP class-id for the Basic class + int classIfForBasic = getSession().getMethodRegistry().createBasicQosOkBody().getClazz(); + AMQFrame contentHeaderFrame = ContentHeaderBody.createAMQFrame(_channelId, - BasicConsumeBody.getClazz(_protocolHandler.getProtocolMajorVersion(), - _protocolHandler.getProtocolMinorVersion()), 0, contentHeaderProperties, size); + classIfForBasic, 0, contentHeaderProperties, size); if (_logger.isDebugEnabled()) { _logger.debug("Sending content header frame to " + destination); @@ -129,17 +128,19 @@ public class BasicMessageProducer_0_8 extends BasicMessageProducer frames[0] = publishFrame; frames[1] = contentHeaderFrame; CompositeAMQDataBlock compositeFrame = new CompositeAMQDataBlock(frames); - _protocolHandler.writeFrame(compositeFrame, wait); - if (message != origMessage) + try + { + _session.checkFlowControl(); + } + catch (InterruptedException e) { - _logger.debug("Updating original message"); - origMessage.setJMSPriority(message.getJMSPriority()); - origMessage.setJMSTimestamp(message.getJMSTimestamp()); - _logger.debug("Setting JMSExpiration:" + message.getJMSExpiration()); - origMessage.setJMSExpiration(message.getJMSExpiration()); - origMessage.setJMSMessageID(message.getJMSMessageID()); + JMSException jmsEx = new JMSException("Interrupted while waiting for flow control to be removed"); + jmsEx.setLinkedException(e); + throw jmsEx; } + + _protocolHandler.writeFrame(compositeFrame, wait); } /** diff --git a/java/client/src/main/java/org/apache/qpid/client/CustomJMSXProperty.java b/java/client/src/main/java/org/apache/qpid/client/CustomJMSXProperty.java index a5e89ef4fc..8cb285c1ac 100644 --- a/java/client/src/main/java/org/apache/qpid/client/CustomJMSXProperty.java +++ b/java/client/src/main/java/org/apache/qpid/client/CustomJMSXProperty.java @@ -29,9 +29,10 @@ import org.apache.qpid.framing.AMQShortString; public enum CustomJMSXProperty
{
JMS_AMQP_NULL,
- JMS_QPID_DESTTYPE,
+ JMS_QPID_DESTTYPE,
JMSXGroupID,
- JMSXGroupSeq;
+ JMSXGroupSeq,
+ JMSXUserID;
private final AMQShortString _nameAsShortString;
@@ -47,7 +48,7 @@ public enum CustomJMSXProperty }
private static Enumeration _names;
-
+
public static synchronized Enumeration asEnumeration()
{
if(_names == null)
diff --git a/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java b/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java index 493e2b5ec0..27783bcacf 100644 --- a/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java +++ b/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java @@ -22,11 +22,9 @@ package org.apache.qpid.client; import javax.jms.Destination; -import javax.jms.IllegalStateException; import javax.jms.InvalidDestinationException; import javax.jms.JMSException; import javax.jms.Message; -import javax.jms.MessageProducer; import javax.jms.Queue; import javax.jms.QueueSender; @@ -200,19 +198,19 @@ public class QueueSenderAdapter implements QueueSender } AMQDestination destination = (AMQDestination) queue; - if (!destination.isValidated() && checkQueueBeforePublish()) + if (!destination.isCheckedForQueueBinding() && checkQueueBeforePublish()) { if (_delegate.getSession().isStrictAMQP()) { _delegate._logger.warn("AMQP does not support destination validation before publish, "); - destination.setValidated(true); + destination.setCheckedForQueueBinding(true); } else { if (_delegate.isBound(destination)) { - destination.setValidated(true); + destination.setCheckedForQueueBinding(true); } else { diff --git a/java/client/src/main/java/org/apache/qpid/client/TopicSubscriberAdaptor.java b/java/client/src/main/java/org/apache/qpid/client/TopicSubscriberAdaptor.java index dd8ca8e197..29fa03fd1d 100644 --- a/java/client/src/main/java/org/apache/qpid/client/TopicSubscriberAdaptor.java +++ b/java/client/src/main/java/org/apache/qpid/client/TopicSubscriberAdaptor.java @@ -21,18 +21,18 @@ package org.apache.qpid.client; import org.apache.qpid.AMQException; -import org.apache.qpid.jms.TopicSubscriber; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.Topic; +import javax.jms.TopicSubscriber; /** * Wraps a MessageConsumer to fulfill the extended TopicSubscriber contract * */ -class TopicSubscriberAdaptor implements org.apache.qpid.jms.TopicSubscriber +class TopicSubscriberAdaptor implements TopicSubscriber { private final Topic _topic; private final BasicMessageConsumer _consumer; diff --git a/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java b/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java index fb2d72267b..76c899f565 100644 --- a/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java +++ b/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java @@ -123,13 +123,17 @@ public class FailoverHandler implements Runnable // We wake up listeners. If they can handle failover, they will extend the // FailoverRetrySupport class and will in turn block on the latch until failover // has completed before retrying the operation. - _amqProtocolHandler.propagateExceptionToWaiters(new FailoverException("Failing over about to start")); + _amqProtocolHandler.notifyFailoverStarting(); // Since failover impacts several structures we protect them all with a single mutex. These structures // are also in child objects of the connection. This allows us to manipulate them without affecting // client code which runs in a separate thread. synchronized (_amqProtocolHandler.getConnection().getFailoverMutex()) { + //Clear the exception now that we have the failover mutex there can be no one else waiting for a frame so + // we can clear the exception. + _amqProtocolHandler.failoverInProgress(); + // We switch in a new state manager temporarily so that the interaction to get to the "connection open" // state works, without us having to terminate any existing "state waiters". We could theoretically // have a state waiter waiting until the connection is closed for some reason. Or in future we may have @@ -141,6 +145,9 @@ public class FailoverHandler implements Runnable _logger.info("Failover process veto-ed by client"); _amqProtocolHandler.setStateManager(existingStateManager); + + //todo: ritchiem these exceptions are useless... Would be better to attempt to propogate exception that + // prompted the failover event. if (_host != null) { _amqProtocolHandler.getConnection().exceptionReceived(new AMQDisconnectedException("Redirect was vetoed by client", null)); diff --git a/java/client/src/main/java/org/apache/qpid/client/failover/FailoverRetrySupport.java b/java/client/src/main/java/org/apache/qpid/client/failover/FailoverRetrySupport.java index 120a07f0fc..e756d7baf9 100644 --- a/java/client/src/main/java/org/apache/qpid/client/failover/FailoverRetrySupport.java +++ b/java/client/src/main/java/org/apache/qpid/client/failover/FailoverRetrySupport.java @@ -122,6 +122,13 @@ public class FailoverRetrySupport<T, E extends Exception> implements FailoverSup {
_log.debug("Failover exception caught during operation: " + e, e);
}
+ catch (IllegalStateException e)
+ {
+ if (!(e.getMessage().startsWith("Fail-over interupted no-op failover support")))
+ {
+ throw e;
+ }
+ }
}
}
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/AccessRequestOkMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/AccessRequestOkMethodHandler.java new file mode 100644 index 0000000000..a150d1446a --- /dev/null +++ b/java/client/src/main/java/org/apache/qpid/client/handler/AccessRequestOkMethodHandler.java @@ -0,0 +1,36 @@ +package org.apache.qpid.client.handler;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.qpid.framing.*;
+import org.apache.qpid.client.state.StateAwareMethodListener;
+import org.apache.qpid.client.state.AMQStateManager;
+import org.apache.qpid.client.protocol.AMQProtocolSession;
+import org.apache.qpid.client.AMQNoConsumersException;
+import org.apache.qpid.client.AMQNoRouteException;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.AMQInvalidRoutingKeyException;
+import org.apache.qpid.AMQChannelClosedException;
+import org.apache.qpid.protocol.AMQConstant;
+
+public class AccessRequestOkMethodHandler implements StateAwareMethodListener<AccessRequestOkBody>
+{
+ private static final Logger _logger = LoggerFactory.getLogger(AccessRequestOkMethodHandler.class);
+
+ private static AccessRequestOkMethodHandler _handler = new AccessRequestOkMethodHandler();
+
+ public static AccessRequestOkMethodHandler getInstance()
+ {
+ return _handler;
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AccessRequestOkBody method, int channelId)
+ throws AMQException
+ {
+ _logger.debug("AccessRequestOk method received");
+ final AMQProtocolSession session = stateManager.getProtocolSession();
+ session.setTicket(method.getTicket(), channelId);
+
+ }
+}
diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/BasicCancelOkMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/BasicCancelOkMethodHandler.java index 8f0ee05b3e..e3e08667d8 100644 --- a/java/client/src/main/java/org/apache/qpid/client/handler/BasicCancelOkMethodHandler.java +++ b/java/client/src/main/java/org/apache/qpid/client/handler/BasicCancelOkMethodHandler.java @@ -25,12 +25,13 @@ import org.apache.qpid.client.protocol.AMQProtocolSession; import org.apache.qpid.client.state.AMQStateManager; import org.apache.qpid.client.state.StateAwareMethodListener; import org.apache.qpid.framing.BasicCancelOkBody; +import org.apache.qpid.framing.AMQMethodBody; import org.apache.qpid.protocol.AMQMethodEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class BasicCancelOkMethodHandler implements StateAwareMethodListener +public class BasicCancelOkMethodHandler implements StateAwareMethodListener<BasicCancelOkBody> { private static final Logger _logger = LoggerFactory.getLogger(BasicCancelOkMethodHandler.class); @@ -44,16 +45,18 @@ public class BasicCancelOkMethodHandler implements StateAwareMethodListener private BasicCancelOkMethodHandler() { } - public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) + public void methodReceived(AMQStateManager stateManager, BasicCancelOkBody body, int channelId) throws AMQException { - BasicCancelOkBody body = (BasicCancelOkBody) evt.getMethod(); + AMQProtocolSession session = stateManager.getProtocolSession(); + + if (_logger.isInfoEnabled()) { - _logger.info("New BasicCancelOk method received for consumer:" + body.consumerTag); + _logger.info("New BasicCancelOk method received for consumer:" + body.getConsumerTag()); } - protocolSession.confirmConsumerCancelled(evt.getChannelId(), body.consumerTag); + session.confirmConsumerCancelled(channelId, body.getConsumerTag()); } } diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.java index 92ba6fd136..4deaa314ec 100644 --- a/java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.java +++ b/java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.java @@ -21,16 +21,16 @@ package org.apache.qpid.client.handler; import org.apache.qpid.AMQException; +import org.apache.qpid.client.message.UnprocessedMessage; import org.apache.qpid.client.message.UnprocessedMessage_0_8; import org.apache.qpid.client.protocol.AMQProtocolSession; import org.apache.qpid.client.state.AMQStateManager; import org.apache.qpid.client.state.StateAwareMethodListener; import org.apache.qpid.framing.BasicDeliverBody; -import org.apache.qpid.protocol.AMQMethodEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class BasicDeliverMethodHandler implements StateAwareMethodListener +public class BasicDeliverMethodHandler implements StateAwareMethodListener<BasicDeliverBody> { private static final Logger _logger = LoggerFactory.getLogger(BasicDeliverMethodHandler.class); @@ -41,18 +41,18 @@ public class BasicDeliverMethodHandler implements StateAwareMethodListener return _instance; } - public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) + public void methodReceived(AMQStateManager stateManager, BasicDeliverBody body, int channelId) throws AMQException { - BasicDeliverBody deliveryBody = (BasicDeliverBody) evt.getMethod(); + final AMQProtocolSession session = stateManager.getProtocolSession(); final UnprocessedMessage_0_8 msg = new UnprocessedMessage_0_8( - evt.getChannelId(), - deliveryBody.deliveryTag, - deliveryBody.consumerTag.asString(), - deliveryBody.getExchange(), - deliveryBody.getRoutingKey(), - deliveryBody.getRedelivered()); + channelId, + body.getDeliveryTag(), + body.getConsumerTag(), + body.getExchange(), + body.getRoutingKey(), + body.getRedelivered()); _logger.debug("New JmsDeliver method received"); - protocolSession.unprocessedMessageReceived(msg); + session.unprocessedMessageReceived(msg); } } diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/BasicReturnMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/BasicReturnMethodHandler.java index bb5a56e5bd..682c3ac2c1 100644 --- a/java/client/src/main/java/org/apache/qpid/client/handler/BasicReturnMethodHandler.java +++ b/java/client/src/main/java/org/apache/qpid/client/handler/BasicReturnMethodHandler.java @@ -22,15 +22,18 @@ package org.apache.qpid.client.handler; import org.apache.qpid.AMQException; import org.apache.qpid.client.message.ReturnMessage; +import org.apache.qpid.client.message.UnprocessedMessage; +import org.apache.qpid.client.message.UnprocessedMessage_0_8; import org.apache.qpid.client.protocol.AMQProtocolSession; import org.apache.qpid.client.state.AMQStateManager; import org.apache.qpid.client.state.StateAwareMethodListener; import org.apache.qpid.framing.BasicReturnBody; import org.apache.qpid.protocol.AMQMethodEvent; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class BasicReturnMethodHandler implements StateAwareMethodListener +public class BasicReturnMethodHandler implements StateAwareMethodListener<BasicReturnBody> { private static final Logger _logger = LoggerFactory.getLogger(BasicReturnMethodHandler.class); @@ -41,18 +44,20 @@ public class BasicReturnMethodHandler implements StateAwareMethodListener return _instance; } - public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) - throws AMQException + + public void methodReceived(AMQStateManager stateManager, BasicReturnBody body, int channelId) + throws AMQException { - BasicReturnBody returnBody = (BasicReturnBody)evt.getMethod(); _logger.debug("New JmsBounce method received"); - final ReturnMessage msg = new ReturnMessage(evt.getChannelId(), - returnBody.getExchange(), - returnBody.getRoutingKey(), - returnBody.getReplyText(), - returnBody.getReplyCode() - ); - - protocolSession.unprocessedMessageReceived(msg); + final AMQProtocolSession session = stateManager.getProtocolSession(); + final ReturnMessage msg = new ReturnMessage(channelId, + body.getExchange(), + body.getRoutingKey(), + body.getReplyText(), + body.getReplyCode() + ); + + session.unprocessedMessageReceived(msg); } + } diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java index 9ed3ef7a60..ee4cf14d58 100644 --- a/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java +++ b/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java @@ -38,7 +38,7 @@ import org.apache.qpid.protocol.AMQMethodEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class ChannelCloseMethodHandler implements StateAwareMethodListener +public class ChannelCloseMethodHandler implements StateAwareMethodListener<ChannelCloseBody> { private static final Logger _logger = LoggerFactory.getLogger(ChannelCloseMethodHandler.class); @@ -49,22 +49,26 @@ public class ChannelCloseMethodHandler implements StateAwareMethodListener return _handler; } - public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) + public void methodReceived(AMQStateManager stateManager, ChannelCloseBody method, int channelId) throws AMQException { _logger.debug("ChannelClose method received"); - ChannelCloseBody method = (ChannelCloseBody) evt.getMethod(); + final AMQProtocolSession session = stateManager.getProtocolSession(); - AMQConstant errorCode = AMQConstant.getConstant(method.replyCode); - AMQShortString reason = method.replyText; + + AMQConstant errorCode = AMQConstant.getConstant(method.getReplyCode()); + AMQShortString reason = method.getReplyText(); if (_logger.isDebugEnabled()) { _logger.debug("Channel close reply code: " + errorCode + ", reason: " + reason); } - // TODO: Be aware of possible changes to parameter order as versions change. - AMQFrame frame = ChannelCloseOkBody.createAMQFrame(evt.getChannelId(), method.getMajor(), method.getMinor()); - protocolSession.writeFrame(frame); + + + ChannelCloseOkBody body = session.getMethodRegistry().createChannelCloseOkBody(); + AMQFrame frame = body.generateFrame(channelId); + session.writeFrame(frame); + if (errorCode != AMQConstant.REPLY_SUCCESS) { if (_logger.isDebugEnabled()) @@ -100,6 +104,11 @@ public class ChannelCloseMethodHandler implements StateAwareMethodListener } // fixme why is this only done when the close is expected... // should the above forced closes not also cause a close? - protocolSession.channelClosed(evt.getChannelId(), errorCode, String.valueOf(reason)); + // ---------- + // Closing the session only when it is expected allows the errors to be processed + // Calling this here will prevent failover. So we should do this for all exceptions + // that should never cause failover. Such as authentication errors. + + session.channelClosed(channelId, errorCode, String.valueOf(reason)); } } diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseOkMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseOkMethodHandler.java index e1fe2697e5..8d3277d4de 100644 --- a/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseOkMethodHandler.java +++ b/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseOkMethodHandler.java @@ -21,6 +21,7 @@ package org.apache.qpid.client.handler; import org.apache.qpid.AMQException; +import org.apache.qpid.framing.ChannelCloseOkBody; import org.apache.qpid.client.protocol.AMQProtocolSession; import org.apache.qpid.client.state.AMQStateManager; import org.apache.qpid.client.state.StateAwareMethodListener; @@ -29,7 +30,7 @@ import org.apache.qpid.protocol.AMQMethodEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class ChannelCloseOkMethodHandler implements StateAwareMethodListener +public class ChannelCloseOkMethodHandler implements StateAwareMethodListener<ChannelCloseOkBody> { private static final Logger _logger = LoggerFactory.getLogger(ChannelCloseOkMethodHandler.class); @@ -40,11 +41,12 @@ public class ChannelCloseOkMethodHandler implements StateAwareMethodListener return _instance; } - public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) + public void methodReceived(AMQStateManager stateManager, ChannelCloseOkBody method, int channelId) throws AMQException { - _logger.info("Received channel-close-ok for channel-id " + evt.getChannelId()); + _logger.info("Received channel-close-ok for channel-id " + channelId); + final AMQProtocolSession session = stateManager.getProtocolSession(); // todo this should do the local closure } } diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowMethodHandler.java new file mode 100644 index 0000000000..b47fe751d6 --- /dev/null +++ b/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowMethodHandler.java @@ -0,0 +1,54 @@ +package org.apache.qpid.client.handler; + +import org.apache.qpid.framing.ChannelFlowBody; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.client.state.AMQStateManager; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.AMQException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT 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 ChannelFlowMethodHandler implements StateAwareMethodListener<ChannelFlowBody> +{ + private static final Logger _logger = LoggerFactory.getLogger(ChannelFlowMethodHandler.class); + private static final ChannelFlowMethodHandler _instance = new ChannelFlowMethodHandler(); + + public static ChannelFlowMethodHandler getInstance() + { + return _instance; + } + + private ChannelFlowMethodHandler() + { } + + public void methodReceived(AMQStateManager stateManager, ChannelFlowBody body, int channelId) + throws AMQException + { + + final AMQProtocolSession session = stateManager.getProtocolSession(); + session.setFlowControl(channelId, body.getActive()); + } + + +} diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowOkMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowOkMethodHandler.java index ca3f46d08b..96de8af54b 100644 --- a/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowOkMethodHandler.java +++ b/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowOkMethodHandler.java @@ -30,7 +30,7 @@ import org.apache.qpid.protocol.AMQMethodEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class ChannelFlowOkMethodHandler implements StateAwareMethodListener +public class ChannelFlowOkMethodHandler implements StateAwareMethodListener<ChannelFlowOkBody> { private static final Logger _logger = LoggerFactory.getLogger(ChannelFlowOkMethodHandler.class); private static final ChannelFlowOkMethodHandler _instance = new ChannelFlowOkMethodHandler(); @@ -43,10 +43,12 @@ public class ChannelFlowOkMethodHandler implements StateAwareMethodListener private ChannelFlowOkMethodHandler() { } - public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) - throws AMQException + public void methodReceived(AMQStateManager stateManager, ChannelFlowOkBody body, int channelId) + throws AMQException { - ChannelFlowOkBody method = (ChannelFlowOkBody) evt.getMethod(); - _logger.debug("Received Channel.Flow-Ok message, active = " + method.active); + + _logger.debug("Received Channel.Flow-Ok message, active = " + body.getActive()); } + + } diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl.java b/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl.java new file mode 100644 index 0000000000..de976b05bd --- /dev/null +++ b/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl.java @@ -0,0 +1,528 @@ +/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.handler;
+
+import java.util.Map;
+import java.util.HashMap;
+
+import org.apache.qpid.framing.*;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.state.AMQStateManager;
+import org.apache.qpid.client.state.AMQMethodNotImplementedException;
+
+
+public class ClientMethodDispatcherImpl implements MethodDispatcher
+{
+
+
+ private static final BasicCancelOkMethodHandler _basicCancelOkMethodHandler = BasicCancelOkMethodHandler.getInstance();
+ private static final BasicDeliverMethodHandler _basicDeliverMethodHandler = BasicDeliverMethodHandler.getInstance();
+ private static final BasicReturnMethodHandler _basicReturnMethodHandler = BasicReturnMethodHandler.getInstance();
+ private static final ChannelCloseMethodHandler _channelCloseMethodHandler = ChannelCloseMethodHandler.getInstance();
+ private static final ChannelFlowOkMethodHandler _channelFlowOkMethodHandler = ChannelFlowOkMethodHandler.getInstance();
+ private static final ConnectionCloseMethodHandler _connectionCloseMethodHandler = ConnectionCloseMethodHandler.getInstance();
+ private static final ConnectionOpenOkMethodHandler _connectionOpenOkMethodHandler = ConnectionOpenOkMethodHandler.getInstance();
+ private static final ConnectionRedirectMethodHandler _connectionRedirectMethodHandler = ConnectionRedirectMethodHandler.getInstance();
+ private static final ConnectionSecureMethodHandler _connectionSecureMethodHandler = ConnectionSecureMethodHandler.getInstance();
+ private static final ConnectionStartMethodHandler _connectionStartMethodHandler = ConnectionStartMethodHandler.getInstance();
+ private static final ConnectionTuneMethodHandler _connectionTuneMethodHandler = ConnectionTuneMethodHandler.getInstance();
+ private static final ExchangeBoundOkMethodHandler _exchangeBoundOkMethodHandler = ExchangeBoundOkMethodHandler.getInstance();
+ private static final QueueDeleteOkMethodHandler _queueDeleteOkMethodHandler = QueueDeleteOkMethodHandler.getInstance();
+
+
+
+ private static interface DispatcherFactory
+ {
+ public ClientMethodDispatcherImpl createMethodDispatcher(AMQStateManager stateManager);
+ }
+
+ private static final Map<ProtocolVersion, DispatcherFactory> _dispatcherFactories =
+ new HashMap<ProtocolVersion, DispatcherFactory>();
+
+ static
+ {
+ _dispatcherFactories.put(ProtocolVersion.v8_0,
+ new DispatcherFactory()
+ {
+ public ClientMethodDispatcherImpl createMethodDispatcher(AMQStateManager stateManager)
+ {
+ return new ClientMethodDispatcherImpl_8_0(stateManager);
+ }
+ });
+
+ _dispatcherFactories.put(ProtocolVersion.v0_9,
+ new DispatcherFactory()
+ {
+ public ClientMethodDispatcherImpl createMethodDispatcher(AMQStateManager stateManager)
+ {
+ return new ClientMethodDispatcherImpl_0_9(stateManager);
+ }
+ });
+
+ }
+
+
+ public static ClientMethodDispatcherImpl newMethodDispatcher(ProtocolVersion version, AMQStateManager stateManager)
+ {
+ DispatcherFactory factory = _dispatcherFactories.get(version);
+ return factory.createMethodDispatcher(stateManager);
+ }
+
+
+
+
+ private AMQStateManager _stateManager;
+
+ public ClientMethodDispatcherImpl(AMQStateManager stateManager)
+ {
+ _stateManager = stateManager;
+ }
+
+
+ public AMQStateManager getStateManager()
+ {
+ return _stateManager;
+ }
+
+ public boolean dispatchAccessRequestOk(AccessRequestOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchBasicCancelOk(BasicCancelOkBody body, int channelId) throws AMQException
+ {
+ _basicCancelOkMethodHandler.methodReceived(_stateManager,body,channelId);
+ return true;
+ }
+
+ public boolean dispatchBasicConsumeOk(BasicConsumeOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchBasicDeliver(BasicDeliverBody body, int channelId) throws AMQException
+ {
+ _basicDeliverMethodHandler.methodReceived(_stateManager,body,channelId);
+ return true;
+ }
+
+ public boolean dispatchBasicGetEmpty(BasicGetEmptyBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchBasicGetOk(BasicGetOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchBasicQosOk(BasicQosOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchBasicReturn(BasicReturnBody body, int channelId) throws AMQException
+ {
+ _basicReturnMethodHandler.methodReceived(_stateManager,body,channelId);
+ return true;
+ }
+
+ public boolean dispatchChannelClose(ChannelCloseBody body, int channelId) throws AMQException
+ {
+ _channelCloseMethodHandler.methodReceived(_stateManager,body,channelId);
+ return true;
+ }
+
+ public boolean dispatchChannelCloseOk(ChannelCloseOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchChannelFlow(ChannelFlowBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchChannelFlowOk(ChannelFlowOkBody body, int channelId) throws AMQException
+ {
+ _channelFlowOkMethodHandler.methodReceived(_stateManager,body,channelId);
+ return true;
+ }
+
+ public boolean dispatchChannelOpenOk(ChannelOpenOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchConnectionClose(ConnectionCloseBody body, int channelId) throws AMQException
+ {
+ _connectionCloseMethodHandler.methodReceived(_stateManager,body,channelId);
+ return true;
+ }
+
+ public boolean dispatchConnectionCloseOk(ConnectionCloseOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchConnectionOpenOk(ConnectionOpenOkBody body, int channelId) throws AMQException
+ {
+ _connectionOpenOkMethodHandler.methodReceived(_stateManager,body,channelId);
+ return true;
+ }
+
+ public boolean dispatchConnectionRedirect(ConnectionRedirectBody body, int channelId) throws AMQException
+ {
+ _connectionRedirectMethodHandler.methodReceived(_stateManager,body,channelId);
+ return true;
+ }
+
+ public boolean dispatchConnectionSecure(ConnectionSecureBody body, int channelId) throws AMQException
+ {
+ _connectionSecureMethodHandler.methodReceived(_stateManager,body,channelId);
+ return true;
+ }
+
+ public boolean dispatchConnectionStart(ConnectionStartBody body, int channelId) throws AMQException
+ {
+ _connectionStartMethodHandler.methodReceived(_stateManager,body,channelId);
+ return true;
+ }
+
+ public boolean dispatchConnectionTune(ConnectionTuneBody body, int channelId) throws AMQException
+ {
+ _connectionTuneMethodHandler.methodReceived(_stateManager,body,channelId);
+ return true;
+ }
+
+ public boolean dispatchQueueDeleteOk(QueueDeleteOkBody body, int channelId) throws AMQException
+ {
+ _queueDeleteOkMethodHandler.methodReceived(_stateManager,body,channelId);
+ return true;
+ }
+
+ public boolean dispatchQueuePurgeOk(QueuePurgeOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchStreamCancelOk(StreamCancelOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchStreamConsumeOk(StreamConsumeOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchAccessRequest(AccessRequestBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchBasicAck(BasicAckBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchBasicCancel(BasicCancelBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchBasicConsume(BasicConsumeBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchBasicGet(BasicGetBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchBasicPublish(BasicPublishBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchBasicQos(BasicQosBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchBasicRecover(BasicRecoverBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchBasicReject(BasicRejectBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchChannelOpen(ChannelOpenBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchConnectionOpen(ConnectionOpenBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchConnectionSecureOk(ConnectionSecureOkBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchConnectionStartOk(ConnectionStartOkBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchConnectionTuneOk(ConnectionTuneOkBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchDtxSelect(DtxSelectBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchDtxStart(DtxStartBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchExchangeBound(ExchangeBoundBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchExchangeDeclare(ExchangeDeclareBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchExchangeDelete(ExchangeDeleteBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchFileAck(FileAckBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchFileCancel(FileCancelBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchFileConsume(FileConsumeBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchFilePublish(FilePublishBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchFileQos(FileQosBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchFileReject(FileRejectBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchQueueBind(QueueBindBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchQueueDeclare(QueueDeclareBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchQueueDelete(QueueDeleteBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchQueuePurge(QueuePurgeBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchStreamCancel(StreamCancelBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchStreamConsume(StreamConsumeBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchStreamPublish(StreamPublishBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchStreamQos(StreamQosBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchTunnelRequest(TunnelRequestBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchTxCommit(TxCommitBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchTxRollback(TxRollbackBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchTxSelect(TxSelectBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchDtxSelectOk(DtxSelectOkBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchDtxStartOk(DtxStartOkBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchExchangeBoundOk(ExchangeBoundOkBody body, int channelId) throws AMQException
+ {
+ _exchangeBoundOkMethodHandler.methodReceived(_stateManager,body,channelId);
+ return true;
+ }
+
+ public boolean dispatchExchangeDeclareOk(ExchangeDeclareOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchExchangeDeleteOk(ExchangeDeleteOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchFileCancelOk(FileCancelOkBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchFileConsumeOk(FileConsumeOkBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchFileDeliver(FileDeliverBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchFileOpen(FileOpenBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchFileOpenOk(FileOpenOkBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchFileQosOk(FileQosOkBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchFileReturn(FileReturnBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchFileStage(FileStageBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchQueueBindOk(QueueBindOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchQueueDeclareOk(QueueDeclareOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchStreamDeliver(StreamDeliverBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchStreamQosOk(StreamQosOkBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchStreamReturn(StreamReturnBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchTxCommitOk(TxCommitOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchTxRollbackOk(TxRollbackOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchTxSelectOk(TxSelectOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+}
diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_0_9.java b/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_0_9.java new file mode 100644 index 0000000000..ae6d5e8283 --- /dev/null +++ b/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_0_9.java @@ -0,0 +1,155 @@ +/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.handler;
+
+import org.apache.qpid.framing.*;
+import org.apache.qpid.framing.amqp_0_9.MethodDispatcher_0_9;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.state.AMQStateManager;
+import org.apache.qpid.client.state.AMQMethodNotImplementedException;
+
+
+public class ClientMethodDispatcherImpl_0_9 extends ClientMethodDispatcherImpl implements MethodDispatcher_0_9
+{
+ public ClientMethodDispatcherImpl_0_9(AMQStateManager stateManager)
+ {
+ super(stateManager);
+ }
+
+
+ public boolean dispatchBasicRecoverSyncOk(BasicRecoverSyncOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchBasicRecoverSync(BasicRecoverSyncBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchChannelOk(ChannelOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchChannelPing(ChannelPingBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchChannelPong(ChannelPongBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchChannelResume(ChannelResumeBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchMessageAppend(MessageAppendBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchMessageCancel(MessageCancelBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchMessageCheckpoint(MessageCheckpointBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchMessageClose(MessageCloseBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchMessageConsume(MessageConsumeBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchMessageEmpty(MessageEmptyBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchMessageGet(MessageGetBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchMessageOffset(MessageOffsetBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchMessageOk(MessageOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchMessageOpen(MessageOpenBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchMessageQos(MessageQosBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchMessageRecover(MessageRecoverBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchMessageReject(MessageRejectBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchMessageResume(MessageResumeBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchMessageTransfer(MessageTransferBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchQueueUnbind(QueueUnbindBody body, int channelId) throws AMQException
+ {
+ throw new AMQMethodNotImplementedException(body);
+ }
+
+ public boolean dispatchQueueUnbindOk(QueueUnbindOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+
+}
diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_8_0.java b/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_8_0.java new file mode 100644 index 0000000000..6bd6874cde --- /dev/null +++ b/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_8_0.java @@ -0,0 +1,85 @@ +/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+ package org.apache.qpid.client.handler;
+
+import org.apache.qpid.framing.*;
+import org.apache.qpid.framing.amqp_8_0.MethodDispatcher_8_0;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.state.AMQStateManager;
+
+public class ClientMethodDispatcherImpl_8_0 extends ClientMethodDispatcherImpl implements MethodDispatcher_8_0
+{
+ public ClientMethodDispatcherImpl_8_0(AMQStateManager stateManager)
+ {
+ super(stateManager);
+ }
+
+ public boolean dispatchBasicRecoverOk(BasicRecoverOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchChannelAlert(ChannelAlertBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchTestContent(TestContentBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchTestContentOk(TestContentOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchTestInteger(TestIntegerBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchTestIntegerOk(TestIntegerOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchTestString(TestStringBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchTestStringOk(TestStringOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchTestTable(TestTableBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchTestTableOk(TestTableOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+}
diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionCloseMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionCloseMethodHandler.java index 752f44237d..950a3288fc 100644 --- a/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionCloseMethodHandler.java +++ b/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionCloseMethodHandler.java @@ -36,7 +36,7 @@ import org.apache.qpid.protocol.AMQMethodEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class ConnectionCloseMethodHandler implements StateAwareMethodListener +public class ConnectionCloseMethodHandler implements StateAwareMethodListener<ConnectionCloseBody> { private static final Logger _logger = LoggerFactory.getLogger(ConnectionCloseMethodHandler.class); @@ -50,32 +50,35 @@ public class ConnectionCloseMethodHandler implements StateAwareMethodListener private ConnectionCloseMethodHandler() { } - public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) - throws AMQException + public void methodReceived(AMQStateManager stateManager, ConnectionCloseBody method, int channelId) + throws AMQException { _logger.info("ConnectionClose frame received"); - ConnectionCloseBody method = (ConnectionCloseBody) evt.getMethod(); + final AMQProtocolSession session = stateManager.getProtocolSession(); + // does it matter // stateManager.changeState(AMQState.CONNECTION_CLOSING); - AMQConstant errorCode = AMQConstant.getConstant(method.replyCode); - AMQShortString reason = method.replyText; + AMQConstant errorCode = AMQConstant.getConstant(method.getReplyCode()); + AMQShortString reason = method.getReplyText(); try { + + ConnectionCloseOkBody closeOkBody = session.getMethodRegistry().createConnectionCloseOkBody(); // TODO: check whether channel id of zero is appropriate // Be aware of possible changes to parameter order as versions change. - protocolSession.writeFrame(ConnectionCloseOkBody.createAMQFrame((short) 0, method.getMajor(), - method.getMinor())); + session.writeFrame(closeOkBody.generateFrame(0)); if (errorCode != AMQConstant.REPLY_SUCCESS) { - if (errorCode == AMQConstant.NOT_ALLOWED) + if (errorCode == AMQConstant.NOT_ALLOWED || (errorCode == AMQConstant.ACCESS_REFUSED)) { - _logger.info("Authentication Error:" + Thread.currentThread().getName()); + _logger.info("Error :" + errorCode +":"+ Thread.currentThread().getName()); - protocolSession.closeProtocolSession(); + // todo ritchiem : Why do this here when it is going to be done in the finally block? + session.closeProtocolSession(); // todo this is a bit of a fudge (could be conssidered such as each new connection needs a new state manager or at least a fresh state. stateManager.changeState(AMQState.CONNECTION_NOT_STARTED); @@ -94,9 +97,13 @@ public class ConnectionCloseMethodHandler implements StateAwareMethodListener { // this actually closes the connection in the case where it is not an error. - protocolSession.closeProtocolSession(); + session.closeProtocolSession(); + // ritchiem: Doing this though will cause any waiting connection start to be released without being able to + // see what the cause was. stateManager.changeState(AMQState.CONNECTION_CLOSED); } } + + } diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionOpenOkMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionOpenOkMethodHandler.java index 2e0f273c32..fd7acac84f 100644 --- a/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionOpenOkMethodHandler.java +++ b/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionOpenOkMethodHandler.java @@ -21,13 +21,14 @@ package org.apache.qpid.client.handler; import org.apache.qpid.AMQException; +import org.apache.qpid.framing.ConnectionOpenOkBody; import org.apache.qpid.client.protocol.AMQProtocolSession; import org.apache.qpid.client.state.AMQState; import org.apache.qpid.client.state.AMQStateManager; import org.apache.qpid.client.state.StateAwareMethodListener; import org.apache.qpid.protocol.AMQMethodEvent; -public class ConnectionOpenOkMethodHandler implements StateAwareMethodListener +public class ConnectionOpenOkMethodHandler implements StateAwareMethodListener<ConnectionOpenOkBody> { private static final ConnectionOpenOkMethodHandler _instance = new ConnectionOpenOkMethodHandler(); @@ -40,9 +41,11 @@ public class ConnectionOpenOkMethodHandler implements StateAwareMethodListener { } - public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) throws AMQException + public void methodReceived(AMQStateManager stateManager, ConnectionOpenOkBody body, int channelId) + throws AMQException { stateManager.changeState(AMQState.CONNECTION_OPEN); } + } diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionRedirectMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionRedirectMethodHandler.java index 213c0eba6e..cac68c9467 100644 --- a/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionRedirectMethodHandler.java +++ b/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionRedirectMethodHandler.java @@ -30,7 +30,7 @@ import org.apache.qpid.protocol.AMQMethodEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class ConnectionRedirectMethodHandler implements StateAwareMethodListener +public class ConnectionRedirectMethodHandler implements StateAwareMethodListener<ConnectionRedirectBody> { private static final Logger _logger = LoggerFactory.getLogger(ConnectionRedirectMethodHandler.class); @@ -46,13 +46,13 @@ public class ConnectionRedirectMethodHandler implements StateAwareMethodListener private ConnectionRedirectMethodHandler() { } - public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) - throws AMQException + public void methodReceived(AMQStateManager stateManager, ConnectionRedirectBody method, int channelId) + throws AMQException { _logger.info("ConnectionRedirect frame received"); - ConnectionRedirectBody method = (ConnectionRedirectBody) evt.getMethod(); + final AMQProtocolSession session = stateManager.getProtocolSession(); - String host = method.host.toString(); + String host = method.getHost().toString(); // the host is in the form hostname:port with the port being optional int portIndex = host.indexOf(':'); @@ -68,6 +68,7 @@ public class ConnectionRedirectMethodHandler implements StateAwareMethodListener } - protocolSession.failover(host, port); + session.failover(host, port); } + } diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionSecureMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionSecureMethodHandler.java index b7776705fe..900aa2abac 100644 --- a/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionSecureMethodHandler.java +++ b/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionSecureMethodHandler.java @@ -32,7 +32,7 @@ import org.apache.qpid.framing.ConnectionSecureBody; import org.apache.qpid.framing.ConnectionSecureOkBody; import org.apache.qpid.protocol.AMQMethodEvent; -public class ConnectionSecureMethodHandler implements StateAwareMethodListener +public class ConnectionSecureMethodHandler implements StateAwareMethodListener<ConnectionSecureBody> { private static final ConnectionSecureMethodHandler _instance = new ConnectionSecureMethodHandler(); @@ -41,27 +41,26 @@ public class ConnectionSecureMethodHandler implements StateAwareMethodListener return _instance; } - public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) throws AMQException + public void methodReceived(AMQStateManager stateManager, ConnectionSecureBody body, int channelId) + throws AMQException { - SaslClient client = protocolSession.getSaslClient(); + final AMQProtocolSession session = stateManager.getProtocolSession(); + SaslClient client = session.getSaslClient(); if (client == null) { throw new AMQException(null, "No SASL client set up - cannot proceed with authentication", null); } - ConnectionSecureBody body = (ConnectionSecureBody) evt.getMethod(); + try { // Evaluate server challenge - byte[] response = client.evaluateChallenge(body.challenge); - // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0) - // TODO: Connect this to the session version obtained from ProtocolInitiation for this session. - // Be aware of possible changes to parameter order as versions change. - AMQFrame responseFrame = ConnectionSecureOkBody.createAMQFrame(evt.getChannelId(), - body.getMajor(), body.getMinor(), - response); // response - protocolSession.writeFrame(responseFrame); + byte[] response = client.evaluateChallenge(body.getChallenge()); + + ConnectionSecureOkBody secureOkBody = session.getMethodRegistry().createConnectionSecureOkBody(response); + + session.writeFrame(secureOkBody.generateFrame(channelId)); } catch (SaslException e) { @@ -70,4 +69,6 @@ public class ConnectionSecureMethodHandler implements StateAwareMethodListener } + + } diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionStartMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionStartMethodHandler.java index 59b493a6f7..d3746f137e 100644 --- a/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionStartMethodHandler.java +++ b/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionStartMethodHandler.java @@ -48,7 +48,7 @@ import java.io.UnsupportedEncodingException; import java.util.HashSet; import java.util.StringTokenizer; -public class ConnectionStartMethodHandler implements StateAwareMethodListener +public class ConnectionStartMethodHandler implements StateAwareMethodListener<ConnectionStartBody> { private static final Logger _log = LoggerFactory.getLogger(ConnectionStartMethodHandler.class); @@ -62,15 +62,16 @@ public class ConnectionStartMethodHandler implements StateAwareMethodListener private ConnectionStartMethodHandler() { } - public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) - throws AMQException + public void methodReceived(AMQStateManager stateManager, ConnectionStartBody body, int channelId) + throws AMQException { _log.debug("public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, " + "AMQMethodEvent evt): called"); - ConnectionStartBody body = (ConnectionStartBody) evt.getMethod(); + final AMQProtocolSession session = stateManager.getProtocolSession(); + - ProtocolVersion pv = new ProtocolVersion((byte) body.versionMajor, (byte) body.versionMinor); + ProtocolVersion pv = new ProtocolVersion((byte) body.getVersionMajor(), (byte) body.getVersionMinor()); // For the purposes of interop, we can make the client accept the broker's version string. // If it does, it then internally records the version as being the latest one that it understands. @@ -83,26 +84,26 @@ public class ConnectionStartMethodHandler implements StateAwareMethodListener if (pv.isSupported()) { - protocolSession.setProtocolVersion(pv.getMajorVersion(), pv.getMinorVersion()); + session.setProtocolVersion(pv); try { // Used to hold the SASL mechanism to authenticate with. String mechanism; - if (body.mechanisms == null) + if (body.getMechanisms()== null) { throw new AMQException(null, "mechanism not specified in ConnectionStart method frame", null); } else { - mechanism = chooseMechanism(body.mechanisms); + mechanism = chooseMechanism(body.getMechanisms()); _log.debug("mechanism = " + mechanism); } if (mechanism == null) { - throw new AMQException(null, "No supported security mechanism found, passed: " + new String(body.mechanisms), null); + throw new AMQException(null, "No supported security mechanism found, passed: " + new String(body.getMechanisms()), null); } byte[] saslResponse; @@ -110,7 +111,7 @@ public class ConnectionStartMethodHandler implements StateAwareMethodListener { SaslClient sc = Sasl.createSaslClient(new String[] { mechanism }, null, "AMQP", "localhost", null, - createCallbackHandler(mechanism, protocolSession)); + createCallbackHandler(mechanism, session)); if (sc == null) { throw new AMQException(null, "Client SASL configuration error: no SaslClient could be created for mechanism " + mechanism @@ -118,21 +119,21 @@ public class ConnectionStartMethodHandler implements StateAwareMethodListener + " details of how to register non-standard SASL client providers.", null); } - protocolSession.setSaslClient(sc); + session.setSaslClient(sc); saslResponse = (sc.hasInitialResponse() ? sc.evaluateChallenge(new byte[0]) : null); } catch (SaslException e) { - protocolSession.setSaslClient(null); + session.setSaslClient(null); throw new AMQException(null, "Unable to create SASL client: " + e, e); } - if (body.locales == null) + if (body.getLocales() == null) { throw new AMQException(null, "Locales is not defined in Connection Start method", null); } - final String locales = new String(body.locales, "utf8"); + final String locales = new String(body.getLocales(), "utf8"); final StringTokenizer tokenizer = new StringTokenizer(locales, " "); String selectedLocale = null; if (tokenizer.hasMoreTokens()) @@ -148,23 +149,20 @@ public class ConnectionStartMethodHandler implements StateAwareMethodListener FieldTable clientProperties = FieldTableFactory.newFieldTable(); clientProperties.setString(new AMQShortString(ClientProperties.instance.toString()), - protocolSession.getClientID()); + session.getClientID()); clientProperties.setString(new AMQShortString(ClientProperties.product.toString()), QpidProperties.getProductName()); clientProperties.setString(new AMQShortString(ClientProperties.version.toString()), QpidProperties.getReleaseVersion()); clientProperties.setString(new AMQShortString(ClientProperties.platform.toString()), getFullSystemInfo()); + + ConnectionStartOkBody connectionStartOkBody = session.getMethodRegistry().createConnectionStartOkBody(clientProperties,new AMQShortString(mechanism),saslResponse,new AMQShortString(locales)); // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0) // TODO: Connect this to the session version obtained from ProtocolInitiation for this session. // Be aware of possible changes to parameter order as versions change. - protocolSession.writeFrame(ConnectionStartOkBody.createAMQFrame(evt.getChannelId(), - protocolSession.getProtocolMajorVersion(), protocolSession.getProtocolMinorVersion(), - clientProperties, // clientProperties - new AMQShortString(selectedLocale), // locale - new AMQShortString(mechanism), // mechanism - saslResponse)); // response - + session.writeFrame(connectionStartOkBody.generateFrame(channelId)); + } catch (UnsupportedEncodingException e) { @@ -173,10 +171,10 @@ public class ConnectionStartMethodHandler implements StateAwareMethodListener } else { - _log.error("Broker requested Protocol [" + body.versionMajor + "-" + body.versionMinor + _log.error("Broker requested Protocol [" + body.getVersionMajor() + "-" + body.getVersionMinor() + "] which is not supported by this version of the client library"); - protocolSession.closeProtocolSession(); + session.closeProtocolSession(); } } @@ -235,4 +233,5 @@ public class ConnectionStartMethodHandler implements StateAwareMethodListener throw new AMQException(null, "Unable to create callback handler: " + e, e); } } + } diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionTuneMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionTuneMethodHandler.java index 68556991d7..fc0e40b745 100644 --- a/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionTuneMethodHandler.java +++ b/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionTuneMethodHandler.java @@ -26,17 +26,13 @@ import org.apache.qpid.client.protocol.AMQProtocolSession; import org.apache.qpid.client.state.AMQState; import org.apache.qpid.client.state.AMQStateManager; import org.apache.qpid.client.state.StateAwareMethodListener; -import org.apache.qpid.framing.AMQFrame; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.ConnectionOpenBody; -import org.apache.qpid.framing.ConnectionTuneBody; -import org.apache.qpid.framing.ConnectionTuneOkBody; +import org.apache.qpid.framing.*; import org.apache.qpid.protocol.AMQMethodEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class ConnectionTuneMethodHandler implements StateAwareMethodListener +public class ConnectionTuneMethodHandler implements StateAwareMethodListener<ConnectionTuneBody> { private static final Logger _logger = LoggerFactory.getLogger(ConnectionTuneMethodHandler.class); @@ -50,48 +46,41 @@ public class ConnectionTuneMethodHandler implements StateAwareMethodListener protected ConnectionTuneMethodHandler() { } - public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) - throws AMQException + public void methodReceived(AMQStateManager stateManager, ConnectionTuneBody frame, int channelId) + throws AMQException { _logger.debug("ConnectionTune frame received"); - ConnectionTuneBody frame = (ConnectionTuneBody) evt.getMethod(); + final AMQProtocolSession session = stateManager.getProtocolSession(); + final MethodRegistry methodRegistry = session.getMethodRegistry(); - ConnectionTuneParameters params = protocolSession.getConnectionTuneParameters(); + + ConnectionTuneParameters params = session.getConnectionTuneParameters(); if (params == null) { params = new ConnectionTuneParameters(); } - params.setFrameMax(frame.frameMax); - params.setChannelMax(frame.channelMax); - params.setHeartbeat(Integer.getInteger("amqj.heartbeat.delay", frame.heartbeat)); - protocolSession.setConnectionTuneParameters(params); + params.setFrameMax(frame.getFrameMax()); + params.setChannelMax(frame.getChannelMax()); + params.setHeartbeat(Integer.getInteger("amqj.heartbeat.delay", frame.getHeartbeat())); + session.setConnectionTuneParameters(params); stateManager.changeState(AMQState.CONNECTION_NOT_OPENED); - protocolSession.writeFrame(createTuneOkFrame(evt.getChannelId(), params, frame.getMajor(), frame.getMinor())); - String host = protocolSession.getAMQConnection().getVirtualHost(); + ConnectionTuneOkBody tuneOkBody = methodRegistry.createConnectionTuneOkBody(params.getChannelMax(), + params.getFrameMax(), + params.getHeartbeat()); + // Be aware of possible changes to parameter order as versions change. + session.writeFrame(tuneOkBody.generateFrame(channelId)); + + String host = session.getAMQConnection().getVirtualHost(); AMQShortString virtualHost = new AMQShortString("/" + host); - protocolSession.writeFrame(createConnectionOpenFrame(evt.getChannelId(), virtualHost, null, true, frame.getMajor(), - frame.getMinor())); - } + ConnectionOpenBody openBody = methodRegistry.createConnectionOpenBody(virtualHost,null,true); - protected AMQFrame createConnectionOpenFrame(int channel, AMQShortString path, AMQShortString capabilities, - boolean insist, byte major, byte minor) - { // Be aware of possible changes to parameter order as versions change. - return ConnectionOpenBody.createAMQFrame(channel, major, minor, // AMQP version (major, minor) - capabilities, // capabilities - insist, // insist - path); // virtualHost + session.writeFrame(openBody.generateFrame(channelId)); } - protected AMQFrame createTuneOkFrame(int channel, ConnectionTuneParameters params, byte major, byte minor) - { - // Be aware of possible changes to parameter order as versions change. - return ConnectionTuneOkBody.createAMQFrame(channel, major, minor, params.getChannelMax(), // channelMax - params.getFrameMax(), // frameMax - params.getHeartbeat()); // heartbeat - } + } diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ExchangeBoundOkMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/ExchangeBoundOkMethodHandler.java index 862a9be8d4..8de40beb10 100644 --- a/java/client/src/main/java/org/apache/qpid/client/handler/ExchangeBoundOkMethodHandler.java +++ b/java/client/src/main/java/org/apache/qpid/client/handler/ExchangeBoundOkMethodHandler.java @@ -33,7 +33,7 @@ import org.slf4j.LoggerFactory; /** * @author Apache Software Foundation */ -public class ExchangeBoundOkMethodHandler implements StateAwareMethodListener +public class ExchangeBoundOkMethodHandler implements StateAwareMethodListener<ExchangeBoundOkBody> { private static final Logger _logger = LoggerFactory.getLogger(ExchangeBoundOkMethodHandler.class); private static final ExchangeBoundOkMethodHandler _instance = new ExchangeBoundOkMethodHandler(); @@ -46,14 +46,14 @@ public class ExchangeBoundOkMethodHandler implements StateAwareMethodListener private ExchangeBoundOkMethodHandler() { } - public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) - throws AMQException + public void methodReceived(AMQStateManager stateManager, ExchangeBoundOkBody body, int channelId) + throws AMQException { if (_logger.isDebugEnabled()) { - ExchangeBoundOkBody body = (ExchangeBoundOkBody) evt.getMethod(); - _logger.debug("Received Exchange.Bound-Ok message, response code: " + body.replyCode + " text: " - + body.replyText); + _logger.debug("Received Exchange.Bound-Ok message, response code: " + body.getReplyCode() + " text: " + + body.getReplyText()); } } + } diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/QueueDeleteOkMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/QueueDeleteOkMethodHandler.java index 65060d44d2..41225c0569 100644 --- a/java/client/src/main/java/org/apache/qpid/client/handler/QueueDeleteOkMethodHandler.java +++ b/java/client/src/main/java/org/apache/qpid/client/handler/QueueDeleteOkMethodHandler.java @@ -33,7 +33,7 @@ import org.slf4j.LoggerFactory; /** * @author Apache Software Foundation */ -public class QueueDeleteOkMethodHandler implements StateAwareMethodListener +public class QueueDeleteOkMethodHandler implements StateAwareMethodListener<QueueDeleteOkBody> { private static final Logger _logger = LoggerFactory.getLogger(QueueDeleteOkMethodHandler.class); private static final QueueDeleteOkMethodHandler _instance = new QueueDeleteOkMethodHandler(); @@ -46,13 +46,14 @@ public class QueueDeleteOkMethodHandler implements StateAwareMethodListener private QueueDeleteOkMethodHandler() { } - public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) - throws AMQException - { + public void methodReceived(AMQStateManager stateManager, QueueDeleteOkBody body, int channelId) + throws AMQException + { if (_logger.isDebugEnabled()) { - QueueDeleteOkBody body = (QueueDeleteOkBody) evt.getMethod(); - _logger.debug("Received Queue.Delete-Ok message, message count: " + body.messageCount); + _logger.debug("Received Queue.Delete-Ok message, message count: " + body.getMessageCount()); } } + + } diff --git a/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java b/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java index cfbf687401..f57f0ff252 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java @@ -63,8 +63,8 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach protected boolean _changedData = true; private Destination _destination; private JMSHeaderAdapter _headerAdapter; - private BasicMessageConsumer _consumer; - private boolean _strictAMQP; + private static final boolean STRICT_AMQP_COMPLIANCE = + Boolean.parseBoolean(System.getProperties().getProperty(AMQSession.STRICT_AMQP, AMQSession.STRICT_AMQP_DEFAULT)); /** * This is 0_10 specific @@ -108,6 +108,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach return _010message; } + protected AbstractJMSMessage(ByteBuffer data) { super(new BasicContentHeaderProperties()); @@ -122,8 +123,6 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach _changedData = (data == null); _headerAdapter = new JMSHeaderAdapter(((BasicContentHeaderProperties) _contentHeaderProperties).getHeaders()); - _strictAMQP = - Boolean.parseBoolean(System.getProperties().getProperty(AMQSession.STRICT_AMQP, AMQSession.STRICT_AMQP_DEFAULT)); } protected AbstractJMSMessage(long deliveryTag, BasicContentHeaderProperties contentHeader, AMQShortString exchange, @@ -171,7 +170,10 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach { if (getContentHeaderProperties().getMessageIdAsString() == null) { - getContentHeaderProperties().setMessageId("ID:" + UUID.randomUUID()); + StringBuilder b = new StringBuilder(39); + b.append("ID:"); + b.append(UUID.randomUUID()); + getContentHeaderProperties().setMessageId(b.toString()); } return getContentHeaderProperties().getMessageIdAsString(); @@ -351,7 +353,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach public boolean getBooleanProperty(AMQShortString propertyName) throws JMSException { - if (_strictAMQP) + if (STRICT_AMQP_COMPLIANCE) { throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); } @@ -361,7 +363,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach public boolean getBooleanProperty(String propertyName) throws JMSException { - if (_strictAMQP) + if (STRICT_AMQP_COMPLIANCE) { throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); } @@ -371,7 +373,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach public byte getByteProperty(String propertyName) throws JMSException { - if (_strictAMQP) + if (STRICT_AMQP_COMPLIANCE) { throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); } @@ -381,7 +383,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach public byte[] getBytesProperty(AMQShortString propertyName) throws JMSException { - if (_strictAMQP) + if (STRICT_AMQP_COMPLIANCE) { throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); } @@ -391,7 +393,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach public short getShortProperty(String propertyName) throws JMSException { - if (_strictAMQP) + if (STRICT_AMQP_COMPLIANCE) { throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); } @@ -401,7 +403,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach public int getIntProperty(String propertyName) throws JMSException { - if (_strictAMQP) + if (STRICT_AMQP_COMPLIANCE) { throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); } @@ -411,7 +413,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach public long getLongProperty(String propertyName) throws JMSException { - if (_strictAMQP) + if (STRICT_AMQP_COMPLIANCE) { throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); } @@ -421,7 +423,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach public float getFloatProperty(String propertyName) throws JMSException { - if (_strictAMQP) + if (STRICT_AMQP_COMPLIANCE) { throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); } @@ -431,7 +433,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach public double getDoubleProperty(String propertyName) throws JMSException { - if (_strictAMQP) + if (STRICT_AMQP_COMPLIANCE) { throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); } @@ -441,12 +443,20 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach public String getStringProperty(String propertyName) throws JMSException { - if (_strictAMQP) + //NOTE: if the JMSX Property is a non AMQP property then we must check _strictAMQP and throw as below. + if (propertyName.equals(CustomJMSXProperty.JMSXUserID.toString())) { - throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); + return ((BasicContentHeaderProperties) _contentHeaderProperties).getUserIdAsString(); } + else + { + if (STRICT_AMQP_COMPLIANCE) + { + throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); + } - return getJmsHeaders().getString(propertyName); + return getJmsHeaders().getString(propertyName); + } } public Object getObjectProperty(String propertyName) throws JMSException @@ -461,7 +471,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach public void setBooleanProperty(AMQShortString propertyName, boolean b) throws JMSException { - if (_strictAMQP) + if (STRICT_AMQP_COMPLIANCE) { throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); } @@ -472,7 +482,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach public void setBooleanProperty(String propertyName, boolean b) throws JMSException { - if (_strictAMQP) + if (STRICT_AMQP_COMPLIANCE) { throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); } @@ -483,7 +493,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach public void setByteProperty(String propertyName, byte b) throws JMSException { - if (_strictAMQP) + if (STRICT_AMQP_COMPLIANCE) { throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); } @@ -494,7 +504,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach public void setBytesProperty(AMQShortString propertyName, byte[] bytes) throws JMSException { - if (_strictAMQP) + if (STRICT_AMQP_COMPLIANCE) { throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); } @@ -505,7 +515,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach public void setShortProperty(String propertyName, short i) throws JMSException { - if (_strictAMQP) + if (STRICT_AMQP_COMPLIANCE) { throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); } @@ -523,7 +533,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach public void setLongProperty(String propertyName, long l) throws JMSException { - if (_strictAMQP) + if (STRICT_AMQP_COMPLIANCE) { throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); } @@ -534,7 +544,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach public void setFloatProperty(String propertyName, float f) throws JMSException { - if (_strictAMQP) + if (STRICT_AMQP_COMPLIANCE) { throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); } @@ -545,7 +555,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach public void setDoubleProperty(String propertyName, double v) throws JMSException { - if (_strictAMQP) + if (STRICT_AMQP_COMPLIANCE) { throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); } @@ -741,11 +751,6 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach } } - public void setConsumer(BasicMessageConsumer basicMessageConsumer) - { - _consumer = basicMessageConsumer; - } - public void receivedFromServer() { _changedData = false; diff --git a/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessageFactory.java b/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessageFactory.java index 48992659f6..0dbad3726c 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessageFactory.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessageFactory.java @@ -79,8 +79,17 @@ public abstract class AbstractJMSMessageFactory implements MessageFactory while (it.hasNext()) { ContentBody cb = (ContentBody) it.next(); - data.put(cb.payload); - cb.payload.release(); + final ByteBuffer payload = cb.payload; + if(payload.isDirect() || payload.isReadOnly()) + { + data.put(payload); + } + else + { + data.put(payload.array(), payload.arrayOffset(), payload.limit()); + } + + payload.release(); } data.flip(); diff --git a/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java b/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java index 1fb5e637c9..fed1f1c609 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java @@ -56,7 +56,11 @@ public class JMSMapMessage extends AbstractBytesTypedMessage implements javax.jm JMSMapMessage(ByteBuffer data) throws JMSException { super(data); // this instantiates a content header - populateMapFromData(); + if(data != null) + { + populateMapFromData(); + } + } JMSMapMessage(long messageNbr, BasicContentHeaderProperties contentHeader, AMQShortString exchange, AMQShortString routingKey, @@ -77,7 +81,7 @@ public class JMSMapMessage extends AbstractBytesTypedMessage implements javax.jm public String toBodyString() throws JMSException { - return _map.toString(); + return _map == null ? "" : _map.toString(); } public AMQShortString getMimeTypeAsShortString() diff --git a/java/client/src/main/java/org/apache/qpid/client/message/ReturnMessage.java b/java/client/src/main/java/org/apache/qpid/client/message/ReturnMessage.java index 593c7795b0..c866a5028e 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/ReturnMessage.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/ReturnMessage.java @@ -9,7 +9,7 @@ public class ReturnMessage extends UnprocessedMessage_0_8 public ReturnMessage(int channelId,AMQShortString exchange,AMQShortString routingKey,AMQShortString replyText,int replyCode) { - super(channelId,-1,"",exchange,routingKey,false); + super(channelId,-1,null,exchange,routingKey,false); _replyText = replyText; _replyCode = replyCode; } diff --git a/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage.java b/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage.java index 5cb943cd33..17efd7e24f 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage.java @@ -23,6 +23,20 @@ package org.apache.qpid.client.message; import java.util.List; import org.apache.qpid.framing.AMQShortString; +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.AMQChannelException; +import org.apache.qpid.AMQConnectionException; +import org.apache.qpid.AMQException; +import org.apache.qpid.client.BasicMessageConsumer; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.BasicDeliverBody; +import org.apache.qpid.framing.BasicReturnBody; +import org.apache.qpid.framing.ContentBody; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.MethodDispatcher; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; /** @@ -36,12 +50,12 @@ public abstract class UnprocessedMessage<H,B> { private final int _channelId; private final long _deliveryId; - private final String _consumerTag; + private final AMQShortString _consumerTag; protected AMQShortString _exchange; protected AMQShortString _routingKey; protected boolean _redelivered; - public UnprocessedMessage(int channelId,long deliveryId,String consumerTag,AMQShortString exchange,AMQShortString routingKey,boolean redelivered) + public UnprocessedMessage(int channelId,long deliveryId,AMQShortString consumerTag,AMQShortString exchange,AMQShortString routingKey,boolean redelivered) { _channelId = channelId; _deliveryId = deliveryId; @@ -65,7 +79,7 @@ public abstract class UnprocessedMessage<H,B> return _deliveryId; } - public String getConsumerTag() + public AMQShortString getConsumerTag() { return _consumerTag; } @@ -84,14 +98,13 @@ public abstract class UnprocessedMessage<H,B> { return _redelivered; } - public abstract List<B> getBodies(); - + public abstract H getContentHeader(); - + // specific to 0_10 public String getReplyToURL() { return ""; - } + } } diff --git a/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage_0_10.java b/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage_0_10.java index 79d829ca39..09f41a9ba6 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage_0_10.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage_0_10.java @@ -43,7 +43,7 @@ public class UnprocessedMessage_0_10 extends UnprocessedMessage<Struct[],ByteBuf /** List of ContentBody instances. Due to fragmentation you don't know how big this will be in general */ private List<ByteBuffer> _bodies = new ArrayList<ByteBuffer>(); - public UnprocessedMessage_0_10(int channelId,long deliveryId,String consumerTag,AMQShortString exchange,AMQShortString routingKey,boolean redelivered) + public UnprocessedMessage_0_10(int channelId,long deliveryId,AMQShortString consumerTag,AMQShortString exchange,AMQShortString routingKey,boolean redelivered) { super(channelId,deliveryId,consumerTag,exchange,routingKey,redelivered); } diff --git a/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage_0_8.java b/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage_0_8.java index 8e32de382b..78da8cdca2 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage_0_8.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage_0_8.java @@ -26,6 +26,7 @@ import java.util.List; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.BasicDeliverBody; +import org.apache.qpid.framing.BasicReturnBody; import org.apache.qpid.framing.ContentBody; import org.apache.qpid.framing.ContentHeaderBody; @@ -46,11 +47,22 @@ public class UnprocessedMessage_0_8 extends UnprocessedMessage<ContentHeaderBody /** List of ContentBody instances. Due to fragmentation you don't know how big this will be in general */ private List<ContentBody> _bodies; - public UnprocessedMessage_0_8(int channelId,long deliveryId,String consumerTag,AMQShortString exchange,AMQShortString routingKey,boolean redelivered) + public UnprocessedMessage_0_8(int channelId,long deliveryId,AMQShortString consumerTag,AMQShortString exchange,AMQShortString routingKey,boolean redelivered) { super(channelId,deliveryId,consumerTag,exchange,routingKey,redelivered); } + public UnprocessedMessage_0_8(int channelId, BasicReturnBody body) + { + //FIXME: TGM, SRSLY 4RL + super(channelId, 0, null, body.getExchange(), body.getRoutingKey(), false); + } + + public UnprocessedMessage_0_8(int channelId, BasicDeliverBody body) + { + super(channelId, body.getDeliveryTag(), body.getConsumerTag(), body.getExchange(), body.getRoutingKey(), false); + } + public void receiveBody(ContentBody body) { @@ -119,8 +131,8 @@ public class UnprocessedMessage_0_8 extends UnprocessedMessage<ContentHeaderBody } if(_deliverBody != null) { - buf.append("Delivery tag " + _deliverBody.deliveryTag); - buf.append("Consumer tag " + _deliverBody.consumerTag); + buf.append("Delivery tag " + _deliverBody.getDeliveryTag()); + buf.append("Consumer tag " + _deliverBody.getConsumerTag()); buf.append("Deliver Body " + _deliverBody); } diff --git a/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java b/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java index 2c435aba6c..97b3660240 100644 --- a/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java +++ b/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java @@ -21,10 +21,15 @@ package org.apache.qpid.client.protocol; import org.apache.mina.common.IdleStatus; +import org.apache.mina.common.IoFilterChain; import org.apache.mina.common.IoHandlerAdapter; import org.apache.mina.common.IoSession; +import org.apache.mina.filter.ReadThrottleFilterBuilder; import org.apache.mina.filter.SSLFilter; +import org.apache.mina.filter.WriteBufferLimitFilterBuilder; +import org.apache.mina.filter.codec.ProtocolCodecException; import org.apache.mina.filter.codec.ProtocolCodecFilter; +import org.apache.mina.filter.executor.ExecutorFilter; import org.apache.qpid.AMQConnectionClosedException; import org.apache.qpid.AMQDisconnectedException; import org.apache.qpid.AMQException; @@ -39,16 +44,7 @@ import org.apache.qpid.client.state.AMQState; import org.apache.qpid.client.state.AMQStateManager; import org.apache.qpid.client.state.listener.SpecificMethodFrameListener; import org.apache.qpid.codec.AMQCodecFactory; -import org.apache.qpid.framing.AMQBody; -import org.apache.qpid.framing.AMQDataBlock; -import org.apache.qpid.framing.AMQFrame; -import org.apache.qpid.framing.AMQMethodBody; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.ConnectionCloseBody; -import org.apache.qpid.framing.ConnectionCloseOkBody; -import org.apache.qpid.framing.ContentBody; -import org.apache.qpid.framing.ContentHeaderBody; -import org.apache.qpid.framing.HeartbeatBody; +import org.apache.qpid.framing.*; import org.apache.qpid.pool.ReadWriteThreadModel; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.protocol.AMQMethodEvent; @@ -57,7 +53,9 @@ import org.apache.qpid.ssl.SSLContextFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.util.Iterator; +import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CountDownLatch; @@ -155,9 +153,19 @@ public class AMQProtocolHandler extends IoHandlerAdapter /** Used to provide a condition to wait upon for operations that are required to wait for failover to complete. */ private CountDownLatch _failoverLatch; + + /** The last failover exception that occured */ + private FailoverException _lastFailoverException; + /** Defines the default timeout to use for synchronous protocol commands. */ private final long DEFAULT_SYNC_TIMEOUT = 1000 * 30; + /** Default buffer size for pending messages reads */ + private static final String DEFAULT_READ_BUFFER_LIMIT = "262144"; + + /** Default buffer size for pending messages writes */ + private static final String DEFAULT_WRITE_BUFFER_LIMIT = "262144"; + /** * Creates a new protocol handler, associated with the specified client connection instance. * @@ -211,9 +219,34 @@ public class AMQProtocolHandler extends IoHandlerAdapter } catch (RuntimeException e) { - e.printStackTrace(); + _logger.error(e.getMessage(), e); } + if (Boolean.getBoolean("protectio")) + { + try + { + //Add IO Protection Filters + IoFilterChain chain = session.getFilterChain(); + + session.getFilterChain().addLast("tempExecutorFilterForFilterBuilder", new ExecutorFilter()); + + ReadThrottleFilterBuilder readfilter = new ReadThrottleFilterBuilder(); + readfilter.setMaximumConnectionBufferSize(Integer.parseInt(System.getProperty("qpid.read.buffer.limit", DEFAULT_READ_BUFFER_LIMIT))); + readfilter.attach(chain); + + WriteBufferLimitFilterBuilder writefilter = new WriteBufferLimitFilterBuilder(); + writefilter.setMaximumConnectionBufferSize(Integer.parseInt(System.getProperty("qpid.write.buffer.limit", DEFAULT_WRITE_BUFFER_LIMIT))); + writefilter.attach(chain); + session.getFilterChain().remove("tempExecutorFilterForFilterBuilder"); + + _logger.info("Using IO Read/Write Filter Protection"); + } + catch (Exception e) + { + _logger.error("Unable to attach IO Read/Write Filter Protection :" + e.getMessage()); + } + } _protocolSession = new AMQProtocolSession(this, session, _connection, getStateManager()); _protocolSession.init(); } @@ -327,13 +360,27 @@ public class AMQProtocolHandler extends IoHandlerAdapter if (_failoverState == FailoverState.NOT_STARTED) { // if (!(cause instanceof AMQUndeliveredException) && (!(cause instanceof AMQAuthenticationException))) - if (cause instanceof AMQConnectionClosedException) + if ((cause instanceof AMQConnectionClosedException) || cause instanceof IOException) { _logger.info("Exception caught therefore going to attempt failover: " + cause, cause); // this will attemp failover sessionClosed(session); } + else + { + + if (cause instanceof ProtocolCodecException) + { + _logger.info("Protocol Exception caught NOT going to attempt failover as " + + "cause isn't AMQConnectionClosedException: " + cause, cause); + + AMQException amqe = new AMQException("Protocol handler error: " + cause, cause); + propagateExceptionToWaiters(amqe); + } + _connection.exceptionReceived(cause); + + } // FIXME Need to correctly handle other exceptions. Things like ... // if (cause instanceof AMQChannelClosedException) @@ -349,7 +396,7 @@ public class AMQProtocolHandler extends IoHandlerAdapter // we notify the state manager of the error in case we have any clients waiting on a state // change. Those "waiters" will be interrupted and can handle the exception - AMQException amqe = new AMQException(null, "Protocol handler error: " + cause, cause); + AMQException amqe = new AMQException("Protocol handler error: " + cause, cause); propagateExceptionToWaiters(amqe); _connection.exceptionReceived(cause); } @@ -364,7 +411,7 @@ public class AMQProtocolHandler extends IoHandlerAdapter */ public void propagateExceptionToWaiters(Exception e) { - getStateManager().error(e); + if (!_frameListeners.isEmpty()) { final Iterator it = _frameListeners.iterator(); @@ -376,98 +423,111 @@ public class AMQProtocolHandler extends IoHandlerAdapter } } - private static int _messageReceivedCount; - - public void messageReceived(IoSession session, Object message) throws Exception + public void notifyFailoverStarting() { - final boolean debug = _logger.isDebugEnabled(); - final long msgNumber = ++_messageReceivedCount; - - if (debug && ((msgNumber % 1000) == 0)) + // Set the last exception in the sync block to ensure the ordering with add. + // either this gets done and the add does the ml.error + // or the add completes first and the iterator below will do ml.error + synchronized (_frameListeners) { - _logger.debug("Received " + _messageReceivedCount + " protocol messages"); + _lastFailoverException = new FailoverException("Failing over about to start"); } - AMQFrame frame = (AMQFrame) message; + propagateExceptionToWaiters(_lastFailoverException); + } - final AMQBody bodyFrame = frame.getBodyFrame(); + public void failoverInProgress() + { + _lastFailoverException = null; + } - HeartbeatDiagnostics.received(bodyFrame instanceof HeartbeatBody); + private static int _messageReceivedCount; - switch (bodyFrame.getFrameType()) + public void messageReceived(IoSession session, Object message) throws Exception + { + if(message instanceof AMQFrame) { - case AMQMethodBody.TYPE: + final boolean debug = _logger.isDebugEnabled(); + final long msgNumber = ++_messageReceivedCount; - if (debug) - { - _logger.debug("(" + System.identityHashCode(this) + ")Method frame received: " + frame); - } + if (debug && ((msgNumber % 1000) == 0)) + { + _logger.debug("Received " + _messageReceivedCount + " protocol messages"); + } - final AMQMethodEvent<AMQMethodBody> evt = - new AMQMethodEvent<AMQMethodBody>(frame.getChannel(), (AMQMethodBody) bodyFrame); + AMQFrame frame = (AMQFrame) message; - try - { + final AMQBody bodyFrame = frame.getBodyFrame(); - boolean wasAnyoneInterested = getStateManager().methodReceived(evt); - if (!_frameListeners.isEmpty()) - { - Iterator it = _frameListeners.iterator(); - while (it.hasNext()) - { - final AMQMethodListener listener = (AMQMethodListener) it.next(); - wasAnyoneInterested = listener.methodReceived(evt) || wasAnyoneInterested; - } - } - - if (!wasAnyoneInterested) - { - throw new AMQException(null, "AMQMethodEvent " + evt + " was not processed by any listener. Listeners:" - + _frameListeners, null); - } - } - catch (AMQException e) - { - getStateManager().error(e); - if (!_frameListeners.isEmpty()) - { - Iterator it = _frameListeners.iterator(); - while (it.hasNext()) - { - final AMQMethodListener listener = (AMQMethodListener) it.next(); - listener.error(e); - } - } - - exceptionCaught(session, e); - } + HeartbeatDiagnostics.received(bodyFrame instanceof HeartbeatBody); - break; + bodyFrame.handle(frame.getChannel(),_protocolSession); - case ContentHeaderBody.TYPE: + _connection.bytesReceived(_protocolSession.getIoSession().getReadBytes()); + } + else if (message instanceof ProtocolInitiation) + { + // We get here if the server sends a response to our initial protocol header + // suggesting an alternate ProtocolVersion; the server will then close the + // connection. + ProtocolInitiation protocolInit = (ProtocolInitiation) message; + ProtocolVersion pv = protocolInit.checkVersion(); + getConnection().setProtocolVersion(pv); + + // get round a bug in old versions of qpid whereby the connection is not closed + _stateManager.changeState(AMQState.CONNECTION_CLOSED); + } + } - _protocolSession.messageContentHeaderReceived(frame.getChannel(), (ContentHeaderBody) bodyFrame); - break; + public void methodBodyReceived(final int channelId, final AMQBody bodyFrame, IoSession session)//, final IoSession session) + throws AMQException + { - case ContentBody.TYPE: + if (_logger.isDebugEnabled()) + { + _logger.debug("(" + System.identityHashCode(this) + ")Method frame received: " + bodyFrame); + } - _protocolSession.messageContentBodyReceived(frame.getChannel(), (ContentBody) bodyFrame); - break; + final AMQMethodEvent<AMQMethodBody> evt = + new AMQMethodEvent<AMQMethodBody>(channelId, (AMQMethodBody) bodyFrame); - case HeartbeatBody.TYPE: + try + { - if (debug) + boolean wasAnyoneInterested = getStateManager().methodReceived(evt); + if (!_frameListeners.isEmpty()) + { + //This iterator is safe from the error state as the frame listeners always add before they send so their + // will be ready and waiting for this response. + Iterator it = _frameListeners.iterator(); + while (it.hasNext()) { - _logger.debug("Received heartbeat"); + final AMQMethodListener listener = (AMQMethodListener) it.next(); + wasAnyoneInterested = listener.methodReceived(evt) || wasAnyoneInterested; } + } - break; - - default: + if (!wasAnyoneInterested) + { + throw new AMQException(null, "AMQMethodEvent " + evt + " was not processed by any listener. Listeners:" + + _frameListeners, null); + } + } + catch (AMQException e) + { + if (!_frameListeners.isEmpty()) + { + Iterator it = _frameListeners.iterator(); + while (it.hasNext()) + { + final AMQMethodListener listener = (AMQMethodListener) it.next(); + listener.error(e); + } + } + exceptionCaught(session, e); } - _connection.bytesReceived(_protocolSession.getIoSession().getReadBytes()); } private static int _messagesOut; @@ -506,6 +566,12 @@ public class AMQProtocolHandler extends IoHandlerAdapter getStateManager().attainState(s); } + public AMQState attainState(Set<AMQState> states) throws AMQException + { + return getStateManager().attainState(states); + } + + /** * Convenience method that writes a frame to the protocol session. Equivalent to calling * getProtocolSession().write(). @@ -547,7 +613,15 @@ public class AMQProtocolHandler extends IoHandlerAdapter { try { - _frameListeners.add(listener); + synchronized (_frameListeners) + { + if (_lastFailoverException != null) + { + throw _lastFailoverException; + } + + _frameListeners.add(listener); + } _protocolSession.writeFrame(frame); AMQMethodEvent e = listener.blockForFrame(timeout); @@ -556,10 +630,6 @@ public class AMQProtocolHandler extends IoHandlerAdapter // When control resumes before this line, a reply will have been received // that matches the criteria defined in the blocking listener } - catch (AMQException e) - { - throw e; - } finally { // If we don't removeKey the listener then no-one will @@ -600,16 +670,11 @@ public class AMQProtocolHandler extends IoHandlerAdapter { getStateManager().changeState(AMQState.CONNECTION_CLOSING); - // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0) - // TODO: Connect this to the session version obtained from ProtocolInitiation for this session. - // Be aware of possible changes to parameter order as versions change. - final AMQFrame frame = - ConnectionCloseBody.createAMQFrame(0, _protocolSession.getProtocolMajorVersion(), - _protocolSession.getProtocolMinorVersion(), // AMQP version (major, minor) - 0, // classId - 0, // methodId - AMQConstant.REPLY_SUCCESS.getCode(), // replyCode - new AMQShortString("JMS client is closing the connection.")); // replyText + ConnectionCloseBody body = _protocolSession.getMethodRegistry().createConnectionCloseBody(AMQConstant.REPLY_SUCCESS.getCode(), // replyCode + new AMQShortString("JMS client is closing the connection."),0,0); + + + final AMQFrame frame = body.generateFrame(0); try { @@ -682,7 +747,10 @@ public class AMQProtocolHandler extends IoHandlerAdapter public void setStateManager(AMQStateManager stateManager) { _stateManager = stateManager; - _protocolSession.setStateManager(stateManager); + if (_protocolSession != null) + { + _protocolSession.setStateManager(stateManager); + } } public AMQProtocolSession getProtocolSession() @@ -709,4 +777,14 @@ public class AMQProtocolHandler extends IoHandlerAdapter { return _protocolSession.getProtocolMinorVersion(); } + + public MethodRegistry getMethodRegistry() + { + return getStateManager().getMethodRegistry(); + } + + public ProtocolVersion getProtocolVersion() + { + return _protocolSession.getProtocolVersion(); + } } diff --git a/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java b/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java index f5008496d9..6e782e0bfc 100644 --- a/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java +++ b/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java @@ -21,40 +21,31 @@ package org.apache.qpid.client.protocol; import org.apache.commons.lang.StringUtils; - import org.apache.mina.common.CloseFuture; import org.apache.mina.common.IdleStatus; import org.apache.mina.common.IoSession; import org.apache.mina.common.WriteFuture; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.JMSException; +import javax.security.sasl.SaslClient; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import org.apache.qpid.AMQException; import org.apache.qpid.client.AMQConnection; import org.apache.qpid.client.AMQSession; import org.apache.qpid.client.ConnectionTuneParameters; -// import org.apache.qpid.client.message.UnexpectedBodyReceivedException; import org.apache.qpid.client.message.ReturnMessage; import org.apache.qpid.client.message.UnprocessedMessage; import org.apache.qpid.client.message.UnprocessedMessage_0_8; import org.apache.qpid.client.state.AMQStateManager; -import org.apache.qpid.framing.AMQDataBlock; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.ContentBody; -import org.apache.qpid.framing.ContentHeaderBody; -import org.apache.qpid.framing.MainRegistry; -import org.apache.qpid.framing.ProtocolInitiation; -import org.apache.qpid.framing.ProtocolVersion; -import org.apache.qpid.framing.VersionSpecificRegistry; +import org.apache.qpid.framing.*; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.jms.JMSException; -import javax.security.sasl.SaslClient; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; +import org.apache.qpid.client.handler.ClientMethodDispatcherImpl; /** * Wrapper for protocol session that provides type-safe access to session attributes. <p/> The underlying protocol @@ -95,18 +86,27 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession * Maps from a channel id to an unprocessed message. This is used to tie together the JmsDeliverBody (which arrives * first) with the subsequent content header and content bodies. */ - protected ConcurrentMap<Integer,UnprocessedMessage_0_8> _channelId2UnprocessedMsgMap = new ConcurrentHashMap<Integer,UnprocessedMessage_0_8>(); + private final ConcurrentMap<Integer,UnprocessedMessage> _channelId2UnprocessedMsgMap = new ConcurrentHashMap<Integer,UnprocessedMessage>(); + private final UnprocessedMessage[] _channelId2UnprocessedMsgArray = new UnprocessedMessage[16]; /** Counter to ensure unique queue names */ protected int _queueId = 1; protected final Object _queueIdLock = new Object(); - private byte _protocolMinorVersion; - private byte _protocolMajorVersion; - private VersionSpecificRegistry _registry = - MainRegistry.getVersionSpecificRegistry(ProtocolVersion.getLatestSupportedVersion()); + private ProtocolVersion _protocolVersion; +// private VersionSpecificRegistry _registry = +// MainRegistry.getVersionSpecificRegistry(ProtocolVersion.getLatestSupportedVersion()); + + + private MethodRegistry _methodRegistry = + MethodRegistry.getMethodRegistry(ProtocolVersion.getLatestSupportedVersion()); + + + private MethodDispatcher _methodDispatcher; + private final AMQConnection _connection; + private static final int FAST_CHANNEL_ACCESS_MASK = 0xFFFFFFF0; public AMQProtocolSession(AMQProtocolHandler protocolHandler, IoSession protocolSession, AMQConnection connection) { @@ -126,6 +126,9 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession _minaProtocolSession.setWriteTimeout(LAST_WRITE_FUTURE_JOIN_TIMEOUT); _stateManager = stateManager; _stateManager.setProtocolSession(this); + _protocolVersion = connection.getProtocolVersion(); + _methodDispatcher = ClientMethodDispatcherImpl.newMethodDispatcher(ProtocolVersion.getLatestSupportedVersion(), + stateManager); _connection = connection; } @@ -135,7 +138,7 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession // start the process of setting up the connection. This is the first place that // data is written to the server. - _minaProtocolSession.write(new ProtocolInitiation(ProtocolVersion.getLatestSupportedVersion())); + _minaProtocolSession.write(new ProtocolInitiation(_connection.getProtocolVersion())); } public String getClientID() @@ -164,6 +167,8 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession public void setStateManager(AMQStateManager stateManager) { _stateManager = stateManager; + _methodDispatcher = ClientMethodDispatcherImpl.newMethodDispatcher(_protocolVersion, + stateManager); } public String getVirtualHost() @@ -230,14 +235,25 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession * * @throws AMQException if this was not expected */ - public void unprocessedMessageReceived(UnprocessedMessage_0_8 message) throws AMQException + public void unprocessedMessageReceived(UnprocessedMessage message) throws AMQException { - _channelId2UnprocessedMsgMap.put(message.getChannelId(), message); + final int channelId = message.getChannelId(); + if((channelId & FAST_CHANNEL_ACCESS_MASK) == 0) + { + _channelId2UnprocessedMsgArray[channelId] = message; + } + else + { + _channelId2UnprocessedMsgMap.put(channelId, message); + } } - public void messageContentHeaderReceived(int channelId, ContentHeaderBody contentHeader) throws AMQException + public void contentHeaderReceived(int channelId, ContentHeaderBody contentHeader) throws AMQException { - UnprocessedMessage_0_8 msg = (UnprocessedMessage_0_8) _channelId2UnprocessedMsgMap.get(channelId); + final UnprocessedMessage msg = (channelId & FAST_CHANNEL_ACCESS_MASK) == 0 ? _channelId2UnprocessedMsgArray[channelId] + : _channelId2UnprocessedMsgMap.get(channelId); + + if (msg == null) { throw new AMQException(null, "Error: received content header without having received a BasicDeliver frame first", null); @@ -255,9 +271,19 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession } } - public void messageContentBodyReceived(int channelId, ContentBody contentBody) throws AMQException + public void contentBodyReceived(final int channelId, ContentBody contentBody) throws AMQException { - UnprocessedMessage_0_8 msg = _channelId2UnprocessedMsgMap.get(channelId); + UnprocessedMessage_0_8 msg; + final boolean fastAccess = (channelId & FAST_CHANNEL_ACCESS_MASK) == 0; + if(fastAccess) + { + msg = (UnprocessedMessage_0_8) _channelId2UnprocessedMsgArray[channelId]; + } + else + { + msg = (UnprocessedMessage_0_8) _channelId2UnprocessedMsgMap.get(channelId); + } + if (msg == null) { throw new AMQException(null, "Error: received content body without having received a JMSDeliver frame first", null); @@ -265,7 +291,14 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession if (msg.getContentHeader() == null) { - _channelId2UnprocessedMsgMap.remove(channelId); + if(fastAccess) + { + _channelId2UnprocessedMsgArray[channelId] = null; + } + else + { + _channelId2UnprocessedMsgMap.remove(channelId); + } throw new AMQException(null, "Error: received content body without having received a ContentHeader frame first", null); } @@ -285,6 +318,11 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession } } + public void heartbeatBodyReceived(int channelId, HeartbeatBody body) throws AMQException + { + + } + /** * Deliver a message to the appropriate session, removing the unprocessed message from our map * @@ -295,7 +333,14 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession { AMQSession session = getSession(channelId); session.messageReceived(msg); - _channelId2UnprocessedMsgMap.remove(channelId); + if((channelId & FAST_CHANNEL_ACCESS_MASK) == 0) + { + _channelId2UnprocessedMsgArray[channelId] = null; + } + else + { + _channelId2UnprocessedMsgMap.remove(channelId); + } } protected AMQSession getSession(int channelId) @@ -440,26 +485,64 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession session.confirmConsumerCancelled(consumerTag); } - public void setProtocolVersion(final byte versionMajor, final byte versionMinor) + public void setProtocolVersion(final ProtocolVersion pv) { - _protocolMajorVersion = versionMajor; - _protocolMinorVersion = versionMinor; - _registry = MainRegistry.getVersionSpecificRegistry(versionMajor, versionMinor); + _protocolVersion = pv; + _methodRegistry = MethodRegistry.getMethodRegistry(pv); + _methodDispatcher = ClientMethodDispatcherImpl.newMethodDispatcher(pv, _stateManager); + + // _registry = MainRegistry.getVersionSpecificRegistry(versionMajor, versionMinor); } public byte getProtocolMinorVersion() { - return _protocolMinorVersion; + return _protocolVersion.getMinorVersion(); } public byte getProtocolMajorVersion() { - return _protocolMajorVersion; + return _protocolVersion.getMajorVersion(); } - public VersionSpecificRegistry getRegistry() + public ProtocolVersion getProtocolVersion() { - return _registry; + return _protocolVersion; } +// public VersionSpecificRegistry getRegistry() +// { +// return _registry; +// } + + public MethodRegistry getMethodRegistry() + { + return _methodRegistry; + } + + public MethodDispatcher getMethodDispatcher() + { + return _methodDispatcher; + } + + + public void setTicket(int ticket, int channelId) + { + final AMQSession session = getSession(channelId); + session.setTicket(ticket); + } + public void setMethodDispatcher(MethodDispatcher methodDispatcher) + { + _methodDispatcher = methodDispatcher; + } + + public void setFlowControl(final int channelId, final boolean active) + { + final AMQSession session = getSession(channelId); + session.setFlowControl(active); + } + + public void methodFrameReceived(final int channel, final AMQMethodBody amqMethodBody) throws AMQException + { + _protocolHandler.methodBodyReceived(channel, amqMethodBody, _minaProtocolSession); + } } diff --git a/java/client/src/main/java/org/apache/qpid/client/state/AMQMethodNotImplementedException.java b/java/client/src/main/java/org/apache/qpid/client/state/AMQMethodNotImplementedException.java new file mode 100644 index 0000000000..5185278eef --- /dev/null +++ b/java/client/src/main/java/org/apache/qpid/client/state/AMQMethodNotImplementedException.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.client.state;
+
+import org.apache.qpid.framing.AMQMethodBody;
+import org.apache.qpid.AMQException;
+
+public class AMQMethodNotImplementedException extends AMQException
+{
+ public AMQMethodNotImplementedException(AMQMethodBody body)
+ {
+ super(null, "Unexpected Method Received: " + body.getClass().getName(), null);
+ }
+}
diff --git a/java/client/src/main/java/org/apache/qpid/client/state/AMQState.java b/java/client/src/main/java/org/apache/qpid/client/state/AMQState.java index 4996f59345..d32d10542f 100644 --- a/java/client/src/main/java/org/apache/qpid/client/state/AMQState.java +++ b/java/client/src/main/java/org/apache/qpid/client/state/AMQState.java @@ -24,8 +24,22 @@ package org.apache.qpid.client.state; * States used in the AMQ protocol. Used by the finite state machine to determine * valid responses. */ -public class AMQState +public enum AMQState { + + CONNECTION_NOT_STARTED(1, "CONNECTION_NOT_STARTED"), + + CONNECTION_NOT_TUNED(2, "CONNECTION_NOT_TUNED"), + + CONNECTION_NOT_OPENED(3, "CONNECTION_NOT_OPENED"), + + CONNECTION_OPEN(4, "CONNECTION_OPEN"), + + CONNECTION_CLOSING(5, "CONNECTION_CLOSING"), + + CONNECTION_CLOSED(6, "CONNECTION_CLOSED"); + + private final int _id; private final String _name; @@ -41,16 +55,6 @@ public class AMQState return "AMQState: id = " + _id + " name: " + _name; } - public static final AMQState CONNECTION_NOT_STARTED = new AMQState(1, "CONNECTION_NOT_STARTED"); - - public static final AMQState CONNECTION_NOT_TUNED = new AMQState(2, "CONNECTION_NOT_TUNED"); - - public static final AMQState CONNECTION_NOT_OPENED = new AMQState(3, "CONNECTION_NOT_OPENED"); - public static final AMQState CONNECTION_OPEN = new AMQState(4, "CONNECTION_OPEN"); - public static final AMQState CONNECTION_CLOSING = new AMQState(5, "CONNECTION_CLOSING"); - - public static final AMQState CONNECTION_CLOSED = new AMQState(6, "CONNECTION_CLOSED"); - } diff --git a/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java b/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java index 9f430d76a7..eda1a1f5fd 100644 --- a/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java +++ b/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java @@ -21,49 +21,22 @@ package org.apache.qpid.client.state; import org.apache.qpid.AMQException; -import org.apache.qpid.client.handler.BasicCancelOkMethodHandler; -import org.apache.qpid.client.handler.BasicDeliverMethodHandler; -import org.apache.qpid.client.handler.BasicReturnMethodHandler; -import org.apache.qpid.client.handler.ChannelCloseMethodHandler; -import org.apache.qpid.client.handler.ChannelCloseOkMethodHandler; -import org.apache.qpid.client.handler.ChannelFlowOkMethodHandler; -import org.apache.qpid.client.handler.ConnectionCloseMethodHandler; -import org.apache.qpid.client.handler.ConnectionOpenOkMethodHandler; -import org.apache.qpid.client.handler.ConnectionSecureMethodHandler; -import org.apache.qpid.client.handler.ConnectionStartMethodHandler; -import org.apache.qpid.client.handler.ConnectionTuneMethodHandler; -import org.apache.qpid.client.handler.ExchangeBoundOkMethodHandler; -import org.apache.qpid.client.handler.QueueDeleteOkMethodHandler; import org.apache.qpid.client.protocol.AMQProtocolSession; -import org.apache.qpid.framing.AMQMethodBody; -import org.apache.qpid.framing.BasicCancelOkBody; -import org.apache.qpid.framing.BasicDeliverBody; -import org.apache.qpid.framing.BasicReturnBody; -import org.apache.qpid.framing.ChannelCloseBody; -import org.apache.qpid.framing.ChannelCloseOkBody; -import org.apache.qpid.framing.ChannelFlowOkBody; -import org.apache.qpid.framing.ConnectionCloseBody; -import org.apache.qpid.framing.ConnectionOpenOkBody; -import org.apache.qpid.framing.ConnectionSecureBody; -import org.apache.qpid.framing.ConnectionStartBody; -import org.apache.qpid.framing.ConnectionTuneBody; -import org.apache.qpid.framing.ExchangeBoundOkBody; -import org.apache.qpid.framing.QueueDeleteOkBody; +import org.apache.qpid.framing.*; import org.apache.qpid.protocol.AMQMethodEvent; import org.apache.qpid.protocol.AMQMethodListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.HashMap; import java.util.Iterator; -import java.util.Map; +import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; /** * The state manager is responsible for managing the state of the protocol session. <p/> For each AMQProtocolHandler * there is a separate state manager. */ -public class AMQStateManager implements AMQMethodListener +public class AMQStateManager { private static final Logger _logger = LoggerFactory.getLogger(AMQStateManager.class); @@ -72,15 +45,15 @@ public class AMQStateManager implements AMQMethodListener /** The current state */ private AMQState _currentState; + /** * Maps from an AMQState instance to a Map from Class to StateTransitionHandler. The class must be a subclass of * AMQFrame. */ - protected final Map _state2HandlersMap = new HashMap(); - private final CopyOnWriteArraySet _stateListeners = new CopyOnWriteArraySet(); + private final Object _stateLock = new Object(); - private static final long MAXIMUM_STATE_WAIT_TIME = 30000L; + private static final long MAXIMUM_STATE_WAIT_TIME = Long.parseLong(System.getProperty("amqj.MaximumStateWait", "30000")); public AMQStateManager() { @@ -96,54 +69,11 @@ public class AMQStateManager implements AMQMethodListener { _protocolSession = protocolSession; _currentState = state; - if (register) - { - registerListeners(); - } - } - protected void registerListeners() - { - Map frame2handlerMap = new HashMap(); - - // we need to register a map for the null (i.e. all state) handlers otherwise you get - // a stack overflow in the handler searching code when you present it with a frame for which - // no handlers are registered - // - _state2HandlersMap.put(null, frame2handlerMap); - - frame2handlerMap = new HashMap(); - frame2handlerMap.put(ConnectionStartBody.class, ConnectionStartMethodHandler.getInstance()); - frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance()); - _state2HandlersMap.put(AMQState.CONNECTION_NOT_STARTED, frame2handlerMap); - - frame2handlerMap = new HashMap(); - frame2handlerMap.put(ConnectionTuneBody.class, ConnectionTuneMethodHandler.getInstance()); - frame2handlerMap.put(ConnectionSecureBody.class, ConnectionSecureMethodHandler.getInstance()); - frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance()); - _state2HandlersMap.put(AMQState.CONNECTION_NOT_TUNED, frame2handlerMap); - - frame2handlerMap = new HashMap(); - frame2handlerMap.put(ConnectionOpenOkBody.class, ConnectionOpenOkMethodHandler.getInstance()); - frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance()); - _state2HandlersMap.put(AMQState.CONNECTION_NOT_OPENED, frame2handlerMap); - - // - // ConnectionOpen handlers - // - frame2handlerMap = new HashMap(); - frame2handlerMap.put(ChannelCloseBody.class, ChannelCloseMethodHandler.getInstance()); - frame2handlerMap.put(ChannelCloseOkBody.class, ChannelCloseOkMethodHandler.getInstance()); - frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance()); - frame2handlerMap.put(BasicDeliverBody.class, BasicDeliverMethodHandler.getInstance()); - frame2handlerMap.put(BasicReturnBody.class, BasicReturnMethodHandler.getInstance()); - frame2handlerMap.put(BasicCancelOkBody.class, BasicCancelOkMethodHandler.getInstance()); - frame2handlerMap.put(ChannelFlowOkBody.class, ChannelFlowOkMethodHandler.getInstance()); - frame2handlerMap.put(QueueDeleteOkBody.class, QueueDeleteOkMethodHandler.getInstance()); - frame2handlerMap.put(ExchangeBoundOkBody.class, ExchangeBoundOkMethodHandler.getInstance()); - _state2HandlersMap.put(AMQState.CONNECTION_OPEN, frame2handlerMap); } + + public AMQState getCurrentState() { return _currentState; @@ -160,72 +90,17 @@ public class AMQStateManager implements AMQMethodListener } } - public void error(Exception e) - { - _logger.debug("State manager receive error notification: " + e); - synchronized (_stateListeners) - { - final Iterator it = _stateListeners.iterator(); - while (it.hasNext()) - { - final StateListener l = (StateListener) it.next(); - l.error(e); - } - } - } public <B extends AMQMethodBody> boolean methodReceived(AMQMethodEvent<B> evt) throws AMQException { - StateAwareMethodListener handler = findStateTransitionHandler(_currentState, evt.getMethod()); - if (handler != null) - { - handler.methodReceived(this, _protocolSession, evt); - - return true; - } - return false; + B method = evt.getMethod(); + + // StateAwareMethodListener handler = findStateTransitionHandler(_currentState, evt.getMethod()); + method.execute(_protocolSession.getMethodDispatcher(), evt.getChannelId()); + return true; } - protected StateAwareMethodListener findStateTransitionHandler(AMQState currentState, AMQMethodBody frame) - // throws IllegalStateTransitionException - { - final Class clazz = frame.getClass(); - if (_logger.isDebugEnabled()) - { - _logger.debug("Looking for state[" + currentState + "] transition handler for frame " + clazz); - } - - final Map classToHandlerMap = (Map) _state2HandlersMap.get(currentState); - - if (classToHandlerMap == null) - { - // if no specialised per state handler is registered look for a - // handler registered for "all" states - return findStateTransitionHandler(null, frame); - } - - final StateAwareMethodListener handler = (StateAwareMethodListener) classToHandlerMap.get(clazz); - if (handler == null) - { - if (currentState == null) - { - _logger.debug("No state transition handler defined for receiving frame " + frame); - - return null; - } - else - { - // if no specialised per state handler is registered look for a - // handler registered for "all" states - return findStateTransitionHandler(null, frame); - } - } - else - { - return handler; - } - } public void attainState(final AMQState s) throws AMQException { @@ -272,4 +147,46 @@ public class AMQStateManager implements AMQMethodListener { _protocolSession = session; } + + public MethodRegistry getMethodRegistry() + { + return getProtocolSession().getMethodRegistry(); + } + + public AMQState attainState(Set<AMQState> stateSet) throws AMQException + { + synchronized (_stateLock) + { + final long waitUntilTime = System.currentTimeMillis() + MAXIMUM_STATE_WAIT_TIME; + long waitTime = MAXIMUM_STATE_WAIT_TIME; + + while (!stateSet.contains(_currentState) && (waitTime > 0)) + { + try + { + _stateLock.wait(MAXIMUM_STATE_WAIT_TIME); + } + catch (InterruptedException e) + { + _logger.warn("Thread interrupted"); + } + + if (!stateSet.contains(_currentState)) + { + waitTime = waitUntilTime - System.currentTimeMillis(); + } + } + + if (!stateSet.contains(_currentState)) + { + _logger.warn("State not achieved within permitted time. Current state " + _currentState + + ", desired state: " + stateSet); + throw new AMQException(null, "State not achieved within permitted time. Current state " + _currentState + + ", desired state: " + stateSet, null); + } + return _currentState; + } + + + } } diff --git a/java/client/src/main/java/org/apache/qpid/client/state/StateAwareMethodListener.java b/java/client/src/main/java/org/apache/qpid/client/state/StateAwareMethodListener.java index b3932533ce..8c65f56af3 100644 --- a/java/client/src/main/java/org/apache/qpid/client/state/StateAwareMethodListener.java +++ b/java/client/src/main/java/org/apache/qpid/client/state/StateAwareMethodListener.java @@ -21,6 +21,7 @@ package org.apache.qpid.client.state; import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQMethodBody; import org.apache.qpid.client.protocol.AMQProtocolSession; import org.apache.qpid.protocol.AMQMethodEvent; @@ -29,8 +30,9 @@ import org.apache.qpid.protocol.AMQMethodEvent; * the opportunity to update state. * */ -public interface StateAwareMethodListener +public interface StateAwareMethodListener<B extends AMQMethodBody> { - void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, - AMQMethodEvent evt) throws AMQException; + + void methodReceived(AMQStateManager stateManager, B body, int channelId) throws AMQException; + } diff --git a/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java b/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java index 5482e48699..b2f7ae8395 100644 --- a/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java +++ b/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java @@ -24,6 +24,7 @@ import org.apache.mina.common.ByteBuffer; import org.apache.mina.common.ConnectFuture; import org.apache.mina.common.IoConnector; import org.apache.mina.common.SimpleByteBufferAllocator; +import org.apache.mina.transport.socket.nio.ExistingSocketConnector; import org.apache.mina.transport.socket.nio.SocketConnectorConfig; import org.apache.mina.transport.socket.nio.SocketSessionConfig; @@ -36,6 +37,9 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.net.InetSocketAddress; +import java.net.Socket; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; public class SocketTransportConnection implements ITransportConnection { @@ -83,8 +87,34 @@ public class SocketTransportConnection implements ITransportConnection _logger.info("send-buffer-size = " + scfg.getSendBufferSize()); scfg.setReceiveBufferSize(Integer.getInteger("amqj.receiveBufferSize", DEFAULT_BUFFER_SIZE)); _logger.info("recv-buffer-size = " + scfg.getReceiveBufferSize()); - final InetSocketAddress address = new InetSocketAddress(brokerDetail.getHost(), brokerDetail.getPort()); - _logger.info("Attempting connection to " + address); + + final InetSocketAddress address; + + if (brokerDetail.getTransport().equals(BrokerDetails.SOCKET)) + { + address = null; + + Socket socket = TransportConnection.removeOpenSocket(brokerDetail.getHost()); + + if (socket != null) + { + _logger.info("Using existing Socket:" + socket); + + ((ExistingSocketConnector) ioConnector).setOpenSocket(socket); + } + else + { + throw new IllegalArgumentException("Active Socket must be provided for broker " + + "with 'socket://<SocketID>' transport:" + brokerDetail); + } + } + else + { + address = new InetSocketAddress(brokerDetail.getHost(), brokerDetail.getPort()); + _logger.info("Attempting connection to " + address); + } + + ConnectFuture future = ioConnector.connect(address, protocolHandler); // wait for connection to complete diff --git a/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java b/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java index 883cb2340a..a871c754b5 100644 --- a/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java +++ b/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java @@ -23,6 +23,8 @@ package org.apache.qpid.client.transport; import org.apache.mina.common.IoConnector; import org.apache.mina.common.IoHandlerAdapter; import org.apache.mina.common.IoServiceConfig; +import org.apache.mina.transport.socket.nio.ExistingSocketConnector; +import org.apache.mina.transport.socket.nio.MultiThreadSocketConnector; import org.apache.mina.transport.socket.nio.SocketConnector; import org.apache.mina.transport.vmpipe.VmPipeAcceptor; import org.apache.mina.transport.vmpipe.VmPipeAddress; @@ -34,8 +36,10 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.HashMap; -import java.util.Iterator; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.net.Socket; + /** * The TransportConnection is a helper class responsible for connecting to an AMQ server. It sets up the underlying @@ -54,12 +58,25 @@ public class TransportConnection private static final int TCP = 0; private static final int VM = 1; + private static final int SOCKET = 2; private static Logger _logger = LoggerFactory.getLogger(TransportConnection.class); private static final String DEFAULT_QPID_SERVER = "org.apache.qpid.server.protocol.AMQPFastProtocolHandler"; - public static ITransportConnection getInstance(BrokerDetails details) throws AMQTransportConnectionException + private static Map<String, Socket> _openSocketRegister = new ConcurrentHashMap<String, Socket>(); + + public static void registerOpenSocket(String socketID, Socket openSocket) + { + _openSocketRegister.put(socketID, openSocket); + } + + public static Socket removeOpenSocket(String socketID) + { + return _openSocketRegister.remove(socketID); + } + + public static synchronized ITransportConnection getInstance(BrokerDetails details) throws AMQTransportConnectionException { int transport = getTransport(details.getTransport()); @@ -68,7 +85,7 @@ public class TransportConnection throw new AMQNoTransportForProtocolException(details, null, null); } - if (transport == _currentInstance) + /* if (transport == _currentInstance) { if (transport == VM) { @@ -83,13 +100,23 @@ public class TransportConnection } } - _currentInstance = transport; + _currentInstance = transport;*/ + ITransportConnection instance; switch (transport) { - + case SOCKET: + instance = + new SocketTransportConnection(new SocketTransportConnection.SocketConnectorFactory() + { + public IoConnector newSocketConnector() + { + return new ExistingSocketConnector(); + } + }); + break; case TCP: - _instance = new SocketTransportConnection(new SocketTransportConnection.SocketConnectorFactory() + instance = new SocketTransportConnection(new SocketTransportConnection.SocketConnectorFactory() { public IoConnector newSocketConnector() { @@ -97,38 +124,44 @@ public class TransportConnection // FIXME - this needs to be sorted to use the new Mina MultiThread SA. if (Boolean.getBoolean("qpidnio")) { - _logger.error("Using Qpid NIO - sysproperty 'qpidnio' is set."); - // result = new org.apache.qpid.nio.SocketConnector(); // non-blocking connector + _logger.warn("Using Qpid MultiThreaded NIO - " + (System.getProperties().containsKey("qpidnio") + ? "Qpid NIO is new default" + : "Sysproperty 'qpidnio' is set")); + result = new MultiThreadSocketConnector(); } - // else - + else { _logger.info("Using Mina NIO"); result = new SocketConnector(); // non-blocking connector } - // Don't have the connector's worker thread wait around for other connections (we only use // one SocketConnector per connection at the moment anyway). This allows short-running // clients (like unit tests) to complete quickly. result.setWorkerTimeout(0); - return result; } }); break; - case VM: { - _instance = getVMTransport(details, Boolean.getBoolean("amqj.AutoCreateVMBroker")); + instance = getVMTransport(details, Boolean.getBoolean("amqj.AutoCreateVMBroker")); break; } + default: + // FIXME: TGM + throw new AMQNoTransportForProtocolException(details, null, null); } - return _instance; + return instance; } private static int getTransport(String transport) { + if (transport.equals(BrokerDetails.SOCKET)) + { + return SOCKET; + } + if (transport.equals(BrokerDetails.TCP)) { return TCP; @@ -151,7 +184,15 @@ public class TransportConnection { if (AutoCreate) { - createVMBroker(port); + if (AutoCreate) + { + createVMBroker(port); + } + else + { + throw new AMQVMBrokerCreationException(null, port, "VM Broker on port " + port + + " does not exist. Auto create disabled.", null); + } } else { @@ -281,16 +322,17 @@ public class TransportConnection public static void killAllVMBrokers() { _logger.info("Killing all VM Brokers"); - _acceptor.unbindAll(); - - Iterator keys = _inVmPipeAddress.keySet().iterator(); - - while (keys.hasNext()) + if (_acceptor != null) { - int id = (Integer) keys.next(); - _inVmPipeAddress.remove(id); + _acceptor.unbindAll(); } - + synchronized (_inVmPipeAddress) + { + _inVmPipeAddress.clear(); + } + _acceptor = null; + _currentInstance = -1; + _currentVMPort = -1; } public static void killVMBroker(int port) @@ -300,6 +342,8 @@ public class TransportConnection { _logger.info("Killing VM Broker:" + port); _inVmPipeAddress.remove(port); + // This does need to be sychronized as otherwise mina can hang + // if a new connection is made _acceptor.unbind(pipe); } } diff --git a/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java b/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java index d9137dc8b1..dca6efba67 100644 --- a/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java +++ b/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java @@ -22,15 +22,12 @@ package org.apache.qpid.client.transport; import org.apache.mina.common.ConnectFuture; import org.apache.mina.common.IoServiceConfig; +import org.apache.mina.transport.vmpipe.QpidVmPipeConnector; import org.apache.mina.transport.vmpipe.VmPipeAddress; import org.apache.mina.transport.vmpipe.VmPipeConnector; - import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.jms.BrokerDetails; -import org.apache.qpid.pool.PoolingFilter; -import org.apache.qpid.pool.ReferenceCountingExecutorService; import org.apache.qpid.pool.ReadWriteThreadModel; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,10 +46,10 @@ public class VmPipeTransportConnection implements ITransportConnection public void connect(AMQProtocolHandler protocolHandler, BrokerDetails brokerDetail) throws IOException { - final VmPipeConnector ioConnector = new VmPipeConnector(); + final VmPipeConnector ioConnector = new QpidVmPipeConnector(); final IoServiceConfig cfg = ioConnector.getDefaultConfig(); - cfg.setThreadModel(ReadWriteThreadModel.getInstance()); + cfg.setThreadModel(ReadWriteThreadModel.getInstance()); final VmPipeAddress address = new VmPipeAddress(_port); _logger.info("Attempting connection to " + address); diff --git a/java/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java b/java/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java index 133285fe87..72ba16086d 100644 --- a/java/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java +++ b/java/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java @@ -33,8 +33,10 @@ public interface BrokerDetails */ public static final String OPTIONS_RETRY = "retries"; public static final String OPTIONS_CONNECT_TIMEOUT = "connecttimeout"; + public static final String OPTIONS_CONNECT_DELAY = "connectdelay"; public static final int DEFAULT_PORT = 5672; + public static final String SOCKET = "socket"; public static final String TCP = "tcp"; public static final String VM = "vm"; diff --git a/java/client/src/main/java/org/apache/qpid/jms/ConnectionURL.java b/java/client/src/main/java/org/apache/qpid/jms/ConnectionURL.java index 11376467d7..da8cd4f750 100644 --- a/java/client/src/main/java/org/apache/qpid/jms/ConnectionURL.java +++ b/java/client/src/main/java/org/apache/qpid/jms/ConnectionURL.java @@ -21,6 +21,7 @@ package org.apache.qpid.jms; import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ProtocolVersion; import java.util.List; @@ -45,7 +46,7 @@ public interface ConnectionURL public static final String OPTIONS_TEMPORARY_QUEUE_EXCHANGE = "temporaryQueueExchange"; public static final byte URL_0_8 = 1; public static final byte URL_0_10 = 2; - + String getURL(); String getFailoverMethod(); @@ -89,4 +90,5 @@ public interface ConnectionURL AMQShortString getTemporaryQueueExchangeName(); AMQShortString getTemporaryTopicExchangeName(); + } diff --git a/java/client/src/main/java/org/apache/qpid/jms/FailoverPolicy.java b/java/client/src/main/java/org/apache/qpid/jms/FailoverPolicy.java index 6ec883ff0b..8e3ccc3b02 100644 --- a/java/client/src/main/java/org/apache/qpid/jms/FailoverPolicy.java +++ b/java/client/src/main/java/org/apache/qpid/jms/FailoverPolicy.java @@ -34,7 +34,6 @@ public class FailoverPolicy private static final long MINUTE = 60000L; private static final long DEFAULT_METHOD_TIMEOUT = 1 * MINUTE; - private static final long DEFAULT_FAILOVER_TIMEOUT = 4 * MINUTE; private FailoverMethod[] _methods = new FailoverMethod[1]; @@ -161,16 +160,7 @@ public class FailoverPolicy } else { - if ((now - _lastFailTime) >= DEFAULT_FAILOVER_TIMEOUT) - { - _logger.info("Failover timeout"); - - return false; - } - else - { - _lastMethodTime = now; - } + _lastMethodTime = now; } } else diff --git a/java/client/src/main/java/org/apache/qpid/jms/MessageProducer.java b/java/client/src/main/java/org/apache/qpid/jms/MessageProducer.java index b91fc2d960..b830c377b8 100644 --- a/java/client/src/main/java/org/apache/qpid/jms/MessageProducer.java +++ b/java/client/src/main/java/org/apache/qpid/jms/MessageProducer.java @@ -50,4 +50,8 @@ public interface MessageProducer extends javax.jms.MessageProducer void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive, boolean mandatory, boolean immediate) throws JMSException; + + void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive, + boolean mandatory, boolean immediate, boolean waitUntilSent) throws JMSException; + } diff --git a/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverRoundRobinServers.java b/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverRoundRobinServers.java index 74a46de63b..9c172da6a2 100644 --- a/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverRoundRobinServers.java +++ b/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverRoundRobinServers.java @@ -22,7 +22,6 @@ package org.apache.qpid.jms.failover; import org.apache.qpid.jms.BrokerDetails; import org.apache.qpid.jms.ConnectionURL; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,34 +34,22 @@ public class FailoverRoundRobinServers implements FailoverMethod /** The default number of times to retry each server */ public static final int DEFAULT_SERVER_RETRIES = 0; - /** - * The index into the hostDetails array of the broker to which we are connected - */ + /** The index into the hostDetails array of the broker to which we are connected */ private int _currentBrokerIndex = -1; - /** - * The number of times to retry connecting for each server - */ + /** The number of times to retry connecting for each server */ private int _serverRetries; - /** - * The current number of retry attempts made - */ + /** The current number of retry attempts made */ private int _currentServerRetry; - /** - * The number of times to cycle through the servers - */ + /** The number of times to cycle through the servers */ private int _cycleRetries; - /** - * The current number of cycles performed. - */ + /** The current number of cycles performed. */ private int _currentCycleRetries; - /** - * Array of BrokerDetail used to make connections. - */ + /** Array of BrokerDetail used to make connections. */ private ConnectionURL _connectionDetails; public FailoverRoundRobinServers(ConnectionURL connectionDetails) @@ -128,6 +115,8 @@ public class FailoverRoundRobinServers implements FailoverMethod public BrokerDetails getNextBrokerDetails() { + boolean doDelay = false; + if (_currentBrokerIndex == (_connectionDetails.getBrokerCount() - 1)) { if (_currentServerRetry < _serverRetries) @@ -143,6 +132,7 @@ public class FailoverRoundRobinServers implements FailoverMethod else { _logger.info("Retrying " + _connectionDetails.getBrokerDetails(_currentBrokerIndex)); + doDelay=true; } _currentServerRetry++; @@ -175,6 +165,7 @@ public class FailoverRoundRobinServers implements FailoverMethod else { _logger.info("Retrying " + _connectionDetails.getBrokerDetails(_currentBrokerIndex)); + doDelay=true; } _currentServerRetry++; @@ -189,7 +180,28 @@ public class FailoverRoundRobinServers implements FailoverMethod } } - return _connectionDetails.getBrokerDetails(_currentBrokerIndex); + BrokerDetails broker = _connectionDetails.getBrokerDetails(_currentBrokerIndex); + + String delayStr = broker.getProperty(BrokerDetails.OPTIONS_CONNECT_DELAY); + if (delayStr != null && doDelay) + { + Long delay = Long.parseLong(delayStr); + _logger.info("Delay between connect retries:" + delay); + try + { + Thread.sleep(delay); + } + catch (InterruptedException ie) + { + return null; + } + } + else + { + _logger.info("No delay between connect retries, use tcp://host:port?connectdelay='value' to enable."); + } + + return broker; } public void setBroker(BrokerDetails broker) diff --git a/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverSingleServer.java b/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverSingleServer.java index d59c1fb98d..b1bc3cee96 100644 --- a/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverSingleServer.java +++ b/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverSingleServer.java @@ -22,25 +22,23 @@ package org.apache.qpid.jms.failover; import org.apache.qpid.jms.BrokerDetails; import org.apache.qpid.jms.ConnectionURL; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class FailoverSingleServer implements FailoverMethod { + private static final Logger _logger = LoggerFactory.getLogger(FailoverSingleServer.class); + /** The default number of times to rety a conection to this server */ public static final int DEFAULT_SERVER_RETRIES = 1; - /** - * The details of the Single Server - */ + /** The details of the Single Server */ private BrokerDetails _brokerDetail; - /** - * The number of times to retry connecting to the sever - */ + /** The number of times to retry connecting to the sever */ private int _retries; - /** - * The current number of attempts made to the server - */ + /** The current number of attempts made to the server */ private int _currentRetries; @@ -78,7 +76,7 @@ public class FailoverSingleServer implements FailoverMethod public BrokerDetails getCurrentBrokerDetails() { - return _brokerDetail; + return _brokerDetail; } public BrokerDetails getNextBrokerDetails() @@ -91,11 +89,29 @@ public class FailoverSingleServer implements FailoverMethod { if (_currentRetries < _retries) { - _currentRetries ++; + _currentRetries++; } + } + - return _brokerDetail; + String delayStr = _brokerDetail.getProperty(BrokerDetails.OPTIONS_CONNECT_DELAY); + if (delayStr != null && _currentRetries != 1) + { + Long delay = Long.parseLong(delayStr); + _logger.info("Delay between connect retries:" + delay); + try + { + + Thread.sleep(delay); + } + catch (InterruptedException ie) + { + _logger.info("No delay between connect retries, use tcp://host:port?connectdelay='value' to enable."); + return null; + } } + + return _brokerDetail; } public void setBroker(BrokerDetails broker) @@ -138,10 +154,10 @@ public class FailoverSingleServer implements FailoverMethod public String toString() { - return "SingleServer:\n"+ - "Max Retries:"+_retries+ - "\nCurrent Retry:"+_currentRetries+ - "\n"+_brokerDetail+"\n"; + return "SingleServer:\n" + + "Max Retries:" + _retries + + "\nCurrent Retry:" + _currentRetries + + "\n" + _brokerDetail + "\n"; } } diff --git a/java/client/src/main/java/org/apache/qpid/jndi/PropertiesFileInitialContextFactory.java b/java/client/src/main/java/org/apache/qpid/jndi/PropertiesFileInitialContextFactory.java index 42a168bad8..43ac56dee9 100644 --- a/java/client/src/main/java/org/apache/qpid/jndi/PropertiesFileInitialContextFactory.java +++ b/java/client/src/main/java/org/apache/qpid/jndi/PropertiesFileInitialContextFactory.java @@ -99,8 +99,6 @@ public class PropertiesFileInitialContextFactory implements InitialContextFactor _logger.warn("Unable to load property file specified in Provider_URL:" + environment.get(Context.PROVIDER_URL)); } - - createConnectionFactories(data, environment); createDestinations(data, environment); diff --git a/java/client/src/old_test/java/org/apache/qpid/fragmentation/TestLargePublisher.java b/java/client/src/old_test/java/org/apache/qpid/fragmentation/TestLargePublisher.java index a246352d8b..2fe01fc126 100644 --- a/java/client/src/old_test/java/org/apache/qpid/fragmentation/TestLargePublisher.java +++ b/java/client/src/old_test/java/org/apache/qpid/fragmentation/TestLargePublisher.java @@ -183,7 +183,7 @@ public class TestLargePublisher } catch (UnknownHostException e) { - e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + e.printStackTrace(); } catch (AMQException e) { diff --git a/java/client/src/old_test/java/org/apache/qpid/pubsub1/TestPublisher.java b/java/client/src/old_test/java/org/apache/qpid/pubsub1/TestPublisher.java index 33891142b5..37b4ff1498 100644 --- a/java/client/src/old_test/java/org/apache/qpid/pubsub1/TestPublisher.java +++ b/java/client/src/old_test/java/org/apache/qpid/pubsub1/TestPublisher.java @@ -133,7 +133,7 @@ public class TestPublisher } catch (JMSException e) { - e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + e.printStackTrace(); } } @@ -163,7 +163,7 @@ public class TestPublisher } catch (UnknownHostException e) { - e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + e.printStackTrace(); } catch (AMQException e) { diff --git a/java/client/src/test/java/org/apache/qpid/client/AMQQueueDeferredOrderingTest.java b/java/client/src/test/java/org/apache/qpid/client/AMQQueueDeferredOrderingTest.java index 8724c65b61..fe418535d6 100644 --- a/java/client/src/test/java/org/apache/qpid/client/AMQQueueDeferredOrderingTest.java +++ b/java/client/src/test/java/org/apache/qpid/client/AMQQueueDeferredOrderingTest.java @@ -126,7 +126,7 @@ public class AMQQueueDeferredOrderingTest extends TestCase _logger.info("Consuming messages"); for (int i = 0; i < NUM_MESSAGES; i++) { - Message msg = consumer.receive(1500); + Message msg = consumer.receive(3000); assertNotNull("Message should not be null", msg); assertTrue("Message should be a text message", msg instanceof TextMessage); assertEquals("Message content does not match expected", Integer.toString(i), ((TextMessage) msg).getText()); diff --git a/java/client/src/test/java/org/apache/qpid/client/MessageListenerMultiConsumerTest.java b/java/client/src/test/java/org/apache/qpid/client/MessageListenerMultiConsumerTest.java index 75e50ee09b..136b9b94b6 100644 --- a/java/client/src/test/java/org/apache/qpid/client/MessageListenerMultiConsumerTest.java +++ b/java/client/src/test/java/org/apache/qpid/client/MessageListenerMultiConsumerTest.java @@ -20,13 +20,17 @@ */ package org.apache.qpid.client; +import junit.framework.TestCase; +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.jndi.PropertiesFileInitialContextFactory; import org.apache.qpid.testutil.QpidTestCase; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.jms.Connection; +import javax.jms.ConnectionFactory; import javax.jms.Message; import javax.jms.MessageConsumer; import javax.jms.MessageListener; @@ -34,7 +38,9 @@ import javax.jms.MessageProducer; import javax.jms.Queue; import javax.jms.Session; import javax.naming.Context; +import javax.naming.spi.InitialContextFactory; +import java.util.Hashtable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; diff --git a/java/client/src/test/java/org/apache/qpid/client/MessageListenerTest.java b/java/client/src/test/java/org/apache/qpid/client/MessageListenerTest.java index 2eb511f8cd..12b84b1495 100644 --- a/java/client/src/test/java/org/apache/qpid/client/MessageListenerTest.java +++ b/java/client/src/test/java/org/apache/qpid/client/MessageListenerTest.java @@ -20,13 +20,17 @@ */ package org.apache.qpid.client; +import junit.framework.TestCase; +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.jndi.PropertiesFileInitialContextFactory; import org.apache.qpid.testutil.QpidTestCase; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.jms.Connection; +import javax.jms.ConnectionFactory; import javax.jms.Message; import javax.jms.MessageConsumer; import javax.jms.MessageListener; @@ -34,6 +38,9 @@ import javax.jms.MessageProducer; import javax.jms.Queue; import javax.jms.Session; import javax.naming.Context; +import javax.naming.spi.InitialContextFactory; + +import java.util.Hashtable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; diff --git a/java/client/src/test/java/org/apache/qpid/client/ResetMessageListenerTest.java b/java/client/src/test/java/org/apache/qpid/client/ResetMessageListenerTest.java index 882915fb8f..c920499a07 100644 --- a/java/client/src/test/java/org/apache/qpid/client/ResetMessageListenerTest.java +++ b/java/client/src/test/java/org/apache/qpid/client/ResetMessageListenerTest.java @@ -29,7 +29,16 @@ import org.apache.qpid.testutil.QpidTestCase; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.jms.*; +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.TextMessage; import javax.naming.Context; import javax.naming.spi.InitialContextFactory; @@ -65,12 +74,15 @@ public class ResetMessageListenerTest extends QpidTestCase private final CountDownLatch _allFirstMessagesSent = new CountDownLatch(2); // all messages Sent Lock private final CountDownLatch _allSecondMessagesSent = new CountDownLatch(2); // all messages Sent Lock private final CountDownLatch _allFirstMessagesSent010 = new CountDownLatch(MSG_COUNT); // all messages Sent Lock - private final CountDownLatch _allSecondMessagesSent010 = new CountDownLatch(MSG_COUNT); // all messages Sent Lock + private final CountDownLatch _allSecondMessagesSent010 = new CountDownLatch(MSG_COUNT); // all messages Sent Lock + + private String oldImmediatePrefetch; protected void setUp() throws Exception { super.setUp(); + oldImmediatePrefetch = System.getProperty(AMQSession.IMMEDIATE_PREFETCH); System.setProperty(AMQSession.IMMEDIATE_PREFETCH, "true"); _clientConnection = getConnection("guest", "guest"); @@ -109,8 +121,12 @@ public class ResetMessageListenerTest extends QpidTestCase { _clientConnection.close(); - _producerConnection.close(); super.tearDown(); + if (oldImmediatePrefetch == null) + { + oldImmediatePrefetch = AMQSession.IMMEDIATE_PREFETCH_DEFAULT; + } + System.setProperty(AMQSession.IMMEDIATE_PREFETCH, oldImmediatePrefetch); } public void testAsynchronousRecieve() @@ -238,7 +254,7 @@ public class ResetMessageListenerTest extends QpidTestCase try { - _allSecondMessagesSent.await(1000, TimeUnit.MILLISECONDS); + _allSecondMessagesSent.await(5000, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/basic/PropertyValueTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/basic/PropertyValueTest.java index 737daeb350..61ba3aad3a 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/basic/PropertyValueTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/basic/PropertyValueTest.java @@ -60,6 +60,7 @@ public class PropertyValueTest extends QpidTestCase implements MessageListener private final List<String> messages = new ArrayList<String>(); private int _count = 1; public String _connectionString = "vm://:1"; + private static final String USERNAME = "guest"; protected void setUp() throws Exception { @@ -171,7 +172,7 @@ public class PropertyValueTest extends QpidTestCase implements MessageListener m.setJMSReplyTo(q); m.setStringProperty("TempQueue", q.toString()); - _logger.trace("Message:" + m); + _logger.debug("Message:" + m); Assert.assertEquals("Check temp queue has been set correctly", m.getJMSReplyTo().toString(), m.getStringProperty("TempQueue")); @@ -287,7 +288,14 @@ public class PropertyValueTest extends QpidTestCase implements MessageListener ((AMQMessage) m).setVoidProperty(new AMQShortString("void")); Assert.assertTrue("Check void properties are correctly transported", - ((AMQMessage) m).getPropertyHeaders().containsKey("void")); + ((AMQMessage) m).getPropertyHeaders().containsKey("void")); + + //JMSXUserID + if (m.getStringProperty("JMSXUserID") != null) + { + Assert.assertEquals("Check 'JMSXUserID' is supported ", USERNAME, + m.getStringProperty("JMSXUserID")); + } } received.clear(); diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/basic/SelectorTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/basic/SelectorTest.java index ed4f6041df..987b30ce28 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/basic/SelectorTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/basic/SelectorTest.java @@ -21,7 +21,7 @@ package org.apache.qpid.test.unit.basic; import junit.framework.TestCase; - +import org.apache.qpid.AMQException; import org.apache.qpid.client.AMQConnection; import org.apache.qpid.client.AMQDestination; import org.apache.qpid.client.AMQQueue; @@ -29,11 +29,14 @@ import org.apache.qpid.client.AMQSession; import org.apache.qpid.client.BasicMessageProducer; import org.apache.qpid.client.transport.TransportConnection; import org.apache.qpid.testutil.QpidTestCase; +import org.apache.qpid.url.URLSyntaxException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.jms.Connection; import javax.jms.DeliveryMode; +import javax.jms.InvalidSelectorException; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageListener; @@ -47,11 +50,12 @@ public class SelectorTest extends QpidTestCase implements MessageListener private AMQSession _session; private int count; public String _connectionString = "vm://:1"; + private static final String INVALID_SELECTOR = "Cost LIKE 5"; protected void setUp() throws Exception { super.setUp(); - init((AMQConnection) getConnection("guest", "guest")); + init((AMQConnection) getConnection("guest", "guest")); } protected void tearDown() throws Exception @@ -59,19 +63,19 @@ public class SelectorTest extends QpidTestCase implements MessageListener super.tearDown(); } - private void init(AMQConnection connection) throws Exception + private void init(AMQConnection connection) throws JMSException { init(connection, new AMQQueue(connection, randomize("SessionStartTest"), true)); } - private void init(AMQConnection connection, AMQDestination destination) throws Exception + private void init(AMQConnection connection, AMQDestination destination) throws JMSException { _connection = connection; _destination = destination; connection.start(); String selector = null; - // selector = "Cost = 2 AND JMSDeliveryMode=" + DeliveryMode.NON_PERSISTENT; + selector = "Cost = 2 AND \"property-with-hyphen\" = 'wibble'"; // selector = "JMSType = Special AND Cost = 2 AND AMQMessageID > 0 AND JMSDeliveryMode=" + DeliveryMode.NON_PERSISTENT; _session = (AMQSession) connection.createSession(false, AMQSession.NO_ACKNOWLEDGE); @@ -79,13 +83,17 @@ public class SelectorTest extends QpidTestCase implements MessageListener _session.createConsumer(destination, selector).setMessageListener(this); } - public synchronized void test() throws JMSException, InterruptedException + public synchronized void test() throws Exception { try { + + init((AMQConnection) getConnection("guest", "guest", randomize("Client"))); + Message msg = _session.createTextMessage("Message"); msg.setJMSPriority(1); msg.setIntProperty("Cost", 2); + msg.setStringProperty("property-with-hyphen", "wibble"); msg.setJMSType("Special"); _logger.info("Sending Message:" + msg); @@ -105,10 +113,147 @@ public class SelectorTest extends QpidTestCase implements MessageListener // throw new RuntimeException("Did not get message!"); } } + catch (JMSException e) + { + _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage()); + if (!(e instanceof InvalidSelectorException)) + { + fail("Wrong exception:" + e.getMessage()); + } + else + { + System.out.println("SUCCESS!!"); + } + } + catch (InterruptedException e) + { + _logger.debug("IE :" + e.getClass().getSimpleName() + ":" + e.getMessage()); + } + catch (URLSyntaxException e) + { + _logger.debug("URL:" + e.getClass().getSimpleName() + ":" + e.getMessage()); + fail("Wrong exception"); + } + catch (AMQException e) + { + _logger.debug("AMQ:" + e.getClass().getSimpleName() + ":" + e.getMessage()); + fail("Wrong exception"); + } + finally { - _session.close(); - _connection.close(); + if (_session != null) + { + _session.close(); + } + if (_connection != null) + { + _connection.close(); + } + } + } + + + public void testInvalidSelectors() throws Exception + { + Connection connection = null; + + try + { + connection = getConnection("guest", "guest", randomize("Client")); + _session = (AMQSession) connection.createSession(false, AMQSession.NO_ACKNOWLEDGE); + } + catch (JMSException e) + { + fail(e.getMessage()); + } + catch (AMQException e) + { + fail(e.getMessage()); + } + catch (URLSyntaxException e) + { + fail("Error:" + e.getMessage()); + } + + //Try Creating a Browser + try + { + _session.createBrowser(_session.createQueue("Ping"), INVALID_SELECTOR); + } + catch (JMSException e) + { + _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage()); + if (!(e instanceof InvalidSelectorException)) + { + fail("Wrong exception:" + e.getMessage()); + } + else + { + _logger.debug("SUCCESS!!"); + } + } + + //Try Creating a Consumer + try + { + _session.createConsumer(_session.createQueue("Ping"), INVALID_SELECTOR); + } + catch (JMSException e) + { + _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage()); + if (!(e instanceof InvalidSelectorException)) + { + fail("Wrong exception:" + e.getMessage()); + } + else + { + _logger.debug("SUCCESS!!"); + } + } + + //Try Creating a Receiever + try + { + _session.createReceiver(_session.createQueue("Ping"), INVALID_SELECTOR); + } + catch (JMSException e) + { + _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage()); + if (!(e instanceof InvalidSelectorException)) + { + fail("Wrong exception:" + e.getMessage()); + } + else + { + _logger.debug("SUCCESS!!"); + } + } + + finally + { + if (_session != null) + { + try + { + _session.close(); + } + catch (JMSException e) + { + fail("Error cleaning up:" + e.getMessage()); + } + } + if (_connection != null) + { + try + { + _connection.close(); + } + catch (JMSException e) + { + fail("Error cleaning up:" + e.getMessage()); + } + } } } @@ -127,9 +272,29 @@ public class SelectorTest extends QpidTestCase implements MessageListener public static void main(String[] argv) throws Exception { SelectorTest test = new SelectorTest(); - test._connectionString = (argv.length == 0) ? "localhost:5672" : argv[0]; - test.setUp(); - test.test(); + test._connectionString = (argv.length == 0) ? "localhost:3000" : argv[0]; + + try + { + while (true) + { + if (test._connectionString.contains("vm://:1")) + { + test.setUp(); + } + test.test(); + + if (test._connectionString.contains("vm://:1")) + { + test.tearDown(); + } + } + } + catch (Exception e) + { + System.err.println(e.getMessage()); + e.printStackTrace(); + } } public static junit.framework.Test suite() diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/basic/close/CloseTests.java b/java/client/src/test/java/org/apache/qpid/test/unit/basic/close/CloseTest.java index 10c054a863..6f1ddebb0c 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/basic/close/CloseTests.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/basic/close/CloseTest.java @@ -35,9 +35,9 @@ import javax.jms.MessageConsumer; import javax.jms.MessageProducer; import javax.jms.Session; -public class CloseTests extends QpidTestCase +public class CloseTest extends QpidTestCase { - private static final Logger _logger = LoggerFactory.getLogger(CloseTests.class); + private static final Logger _logger = LoggerFactory.getLogger(CloseTest.class); private static final String BROKER = "vm://:1"; diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/client/AMQSessionTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/client/AMQSessionTest.java index 6a4e01affd..965c22af4a 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/client/AMQSessionTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/client/AMQSessionTest.java @@ -75,18 +75,11 @@ public class AMQSessionTest extends QpidTestCase public void testCreateDurableSubscriber() throws JMSException { - try - { - TopicSubscriber subscriber = _session.createDurableSubscriber(_topic, "mysubname"); - assertEquals("Topic names should match from durable TopicSubscriber", _topic.getTopicName(), subscriber.getTopic().getTopicName()); + TopicSubscriber subscriber = _session.createDurableSubscriber(_topic, "mysubname"); + assertEquals("Topic names should match from durable TopicSubscriber", _topic.getTopicName(), subscriber.getTopic().getTopicName()); - subscriber = _session.createDurableSubscriber(_topic, "mysubname", "abc", false); - assertEquals("Topic names should match from durable TopicSubscriber with selector", _topic.getTopicName(), subscriber.getTopic().getTopicName()); - } - catch (Throwable e) - { - e.printStackTrace(); - } + subscriber = _session.createDurableSubscriber(_topic, "mysubname2", "abc", false); + assertEquals("Topic names should match from durable TopicSubscriber with selector", _topic.getTopicName(), subscriber.getTopic().getTopicName()); } public void testCreateQueueReceiver() throws JMSException diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseMethodHandlerNoCloseOk.java b/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseMethodHandlerNoCloseOk.java index 85fcf6d95a..b6776a1a44 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseMethodHandlerNoCloseOk.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseMethodHandlerNoCloseOk.java @@ -37,7 +37,7 @@ import org.apache.qpid.protocol.AMQMethodEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class ChannelCloseMethodHandlerNoCloseOk implements StateAwareMethodListener +public class ChannelCloseMethodHandlerNoCloseOk implements StateAwareMethodListener<ChannelCloseBody> { private static final Logger _logger = LoggerFactory.getLogger(ChannelCloseMethodHandlerNoCloseOk.class); @@ -48,14 +48,15 @@ public class ChannelCloseMethodHandlerNoCloseOk implements StateAwareMethodListe return _handler; } - public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) + public void methodReceived(AMQStateManager stateManager, ChannelCloseBody method, int channelId) throws AMQException { _logger.debug("ChannelClose method received"); - ChannelCloseBody method = (ChannelCloseBody) evt.getMethod(); + final AMQProtocolSession session = stateManager.getProtocolSession(); - AMQConstant errorCode = AMQConstant.getConstant(method.replyCode); - AMQShortString reason = method.replyText; + + AMQConstant errorCode = AMQConstant.getConstant(method.getReplyCode()); + AMQShortString reason = method.getReplyText(); if (_logger.isDebugEnabled()) { _logger.debug("Channel close reply code: " + errorCode + ", reason: " + reason); @@ -95,6 +96,6 @@ public class ChannelCloseMethodHandlerNoCloseOk implements StateAwareMethodListe } - protocolSession.channelClosed(evt.getChannelId(), errorCode, String.valueOf(reason)); + session.channelClosed(channelId, errorCode, String.valueOf(reason)); } } diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseTest.java index 08d6b0bcab..45a9ca1dd6 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseTest.java @@ -26,17 +26,13 @@ import org.apache.qpid.AMQException; import org.apache.qpid.AMQTimeoutException; import org.apache.qpid.testutil.QpidTestCase; import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.handler.ClientMethodDispatcherImpl; import org.apache.qpid.client.failover.FailoverException; +import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.client.protocol.AMQProtocolSession; import org.apache.qpid.client.state.AMQStateManager; import org.apache.qpid.client.transport.TransportConnection; -import org.apache.qpid.framing.AMQFrame; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.ChannelCloseOkBody; -import org.apache.qpid.framing.ChannelOpenBody; -import org.apache.qpid.framing.ChannelOpenOkBody; -import org.apache.qpid.framing.ExchangeDeclareBody; -import org.apache.qpid.framing.ExchangeDeclareOkBody; +import org.apache.qpid.framing.*; import org.apache.qpid.jms.ConnectionListener; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.url.URLSyntaxException; @@ -53,6 +49,9 @@ import javax.jms.MessageProducer; import javax.jms.Queue; import javax.jms.Session; import javax.jms.TextMessage; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; public class ChannelCloseTest extends QpidTestCase implements ExceptionListener, ConnectionListener { @@ -140,8 +139,11 @@ public class ChannelCloseTest extends QpidTestCase implements ExceptionListener, /* close channel and send guff then send ok no errors + REMOVE TEST - The behaviour after server has sent close is undefined. + the server should be free to fail as it may wish to reclaim its resources + immediately after close. */ - public void testSendingMethodsAfterClose() throws Exception + /*public void testSendingMethodsAfterClose() throws Exception { // this is testing an 0.8 connection if(isBroker08()) @@ -167,7 +169,19 @@ public class ChannelCloseTest extends QpidTestCase implements ExceptionListener, // Set StateManager to manager that ignores Close-oks AMQProtocolSession protocolSession= ((AMQConnection) _connection).getProtocolHandler().getProtocolSession(); - AMQStateManager newStateManager=new NoCloseOKStateManager(protocolSession); + + MethodDispatcher d = protocolSession.getMethodDispatcher(); + + MethodDispatcher wrappedDispatcher = (MethodDispatcher) + Proxy.newProxyInstance(d.getClass().getClassLoader(), + d.getClass().getInterfaces(), + new MethodDispatcherProxyHandler( + (ClientMethodDispatcherImpl) d)); + + protocolSession.setMethodDispatcher(wrappedDispatcher); + + + AMQStateManager newStateManager=new NoCloseOKStateManager(protocolSession); newStateManager.changeState(oldStateManager.getCurrentState()); ((AMQConnection) _connection).getProtocolHandler().setStateManager(newStateManager); @@ -257,7 +271,7 @@ public class ChannelCloseTest extends QpidTestCase implements ExceptionListener, } } } - +*/ private void createChannelAndTest(int channel) throws FailoverException { // Create A channel @@ -284,10 +298,9 @@ public class ChannelCloseTest extends QpidTestCase implements ExceptionListener, private void sendClose(int channel) { - AMQFrame frame = - ChannelCloseOkBody.createAMQFrame(channel, - ((AMQConnection) _connection).getProtocolHandler().getProtocolMajorVersion(), - ((AMQConnection) _connection).getProtocolHandler().getProtocolMinorVersion()); + ChannelCloseOkBody body = + ((AMQConnection) _connection).getProtocolHandler().getMethodRegistry().createChannelCloseOkBody(); + AMQFrame frame = body.generateFrame(channel); ((AMQConnection) _connection).getProtocolHandler().writeFrame(frame); } @@ -345,35 +358,43 @@ public class ChannelCloseTest extends QpidTestCase implements ExceptionListener, private void declareExchange(int channelId, String _type, String _name, boolean nowait) throws AMQException, FailoverException { - AMQFrame exchangeDeclare = - ExchangeDeclareBody.createAMQFrame(channelId, - ((AMQConnection) _connection).getProtocolHandler().getProtocolMajorVersion(), - ((AMQConnection) _connection).getProtocolHandler().getProtocolMinorVersion(), null, // arguments - false, // autoDelete - false, // durable - new AMQShortString(_name), // exchange - false, // internal - nowait, // nowait - true, // passive - 0, // ticket - new AMQShortString(_type)); // type - - if (nowait) - { - ((AMQConnection) _connection).getProtocolHandler().writeFrame(exchangeDeclare); - } - else - { - ((AMQConnection) _connection).getProtocolHandler().syncWrite(exchangeDeclare, ExchangeDeclareOkBody.class, - SYNC_TIMEOUT); - } + ExchangeDeclareBody body = + ((AMQConnection) _connection).getProtocolHandler() + .getMethodRegistry() + .createExchangeDeclareBody(0, + new AMQShortString(_name), + new AMQShortString(_type), + true, + false, + false, + false, + nowait, + null); + AMQFrame exchangeDeclare = body.generateFrame(channelId); + AMQProtocolHandler protocolHandler = ((AMQConnection) _connection).getProtocolHandler(); + + + if (nowait) + { + protocolHandler.writeFrame(exchangeDeclare); + } + else + { + protocolHandler.syncWrite(exchangeDeclare, ExchangeDeclareOkBody.class, SYNC_TIMEOUT); + } + +// return null; +// } +// }, (AMQConnection)_connection).execute(); + } private void createChannel(int channelId) throws AMQException, FailoverException { - ((AMQConnection) _connection).getProtocolHandler().syncWrite(ChannelOpenBody.createAMQFrame(channelId, - ((AMQConnection) _connection).getProtocolHandler().getProtocolMajorVersion(), - ((AMQConnection) _connection).getProtocolHandler().getProtocolMinorVersion(), null), // outOfBand + ChannelOpenBody body = + ((AMQConnection) _connection).getProtocolHandler().getMethodRegistry().createChannelOpenBody(null); + + ((AMQConnection) _connection).getProtocolHandler().syncWrite(body.generateFrame(channelId), // outOfBand ChannelOpenOkBody.class); } @@ -402,4 +423,28 @@ public class ChannelCloseTest extends QpidTestCase implements ExceptionListener, public void failoverComplete() { } + + private static final class MethodDispatcherProxyHandler implements InvocationHandler + { + private final ClientMethodDispatcherImpl _underlyingDispatcher; + private final ChannelCloseMethodHandlerNoCloseOk _handler = ChannelCloseMethodHandlerNoCloseOk.getInstance(); + + + public MethodDispatcherProxyHandler(ClientMethodDispatcherImpl dispatcher) + { + _underlyingDispatcher = dispatcher; + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable + { + if(method.getName().equals("dispatchChannelClose")) + { + _handler.methodReceived(_underlyingDispatcher.getStateManager(), + (ChannelCloseBody) args[0], (Integer)args[1]); + } + Method dispatcherMethod = _underlyingDispatcher.getClass().getMethod(method.getName(), method.getParameterTypes()); + return dispatcherMethod.invoke(_underlyingDispatcher, args); + + } + } } diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/NoCloseOKStateManager.java b/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/NoCloseOKStateManager.java index d128f30727..c7eb745566 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/NoCloseOKStateManager.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/NoCloseOKStateManager.java @@ -59,49 +59,7 @@ public class NoCloseOKStateManager extends AMQStateManager super(protocolSession); } - protected void registerListeners() - { - Map frame2handlerMap = new HashMap(); - - // we need to register a map for the null (i.e. all state) handlers otherwise you get - // a stack overflow in the handler searching code when you present it with a frame for which - // no handlers are registered - // - _state2HandlersMap.put(null, frame2handlerMap); - - frame2handlerMap = new HashMap(); - frame2handlerMap.put(ConnectionStartBody.class, ConnectionStartMethodHandler.getInstance()); - frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance()); - _state2HandlersMap.put(AMQState.CONNECTION_NOT_STARTED, frame2handlerMap); - - frame2handlerMap = new HashMap(); - frame2handlerMap.put(ConnectionTuneBody.class, ConnectionTuneMethodHandler.getInstance()); - frame2handlerMap.put(ConnectionSecureBody.class, ConnectionSecureMethodHandler.getInstance()); - frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance()); - _state2HandlersMap.put(AMQState.CONNECTION_NOT_TUNED, frame2handlerMap); - - frame2handlerMap = new HashMap(); - frame2handlerMap.put(ConnectionOpenOkBody.class, ConnectionOpenOkMethodHandler.getInstance()); - frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance()); - _state2HandlersMap.put(AMQState.CONNECTION_NOT_OPENED, frame2handlerMap); - - // - // ConnectionOpen handlers - // - frame2handlerMap = new HashMap(); - // Use Test Handler for Close methods to not send Close-OKs - frame2handlerMap.put(ChannelCloseBody.class, ChannelCloseMethodHandlerNoCloseOk.getInstance()); - - frame2handlerMap.put(ChannelCloseOkBody.class, ChannelCloseOkMethodHandler.getInstance()); - frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance()); - frame2handlerMap.put(BasicDeliverBody.class, BasicDeliverMethodHandler.getInstance()); - frame2handlerMap.put(BasicReturnBody.class, BasicReturnMethodHandler.getInstance()); - frame2handlerMap.put(BasicCancelOkBody.class, BasicCancelOkMethodHandler.getInstance()); - frame2handlerMap.put(ChannelFlowOkBody.class, ChannelFlowOkMethodHandler.getInstance()); - frame2handlerMap.put(QueueDeleteOkBody.class, QueueDeleteOkMethodHandler.getInstance()); - frame2handlerMap.put(ExchangeBoundOkBody.class, ExchangeBoundOkMethodHandler.getInstance()); - _state2HandlersMap.put(AMQState.CONNECTION_OPEN, frame2handlerMap); - } + } diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionTest.java index 7eb74e2492..f856e8c20b 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionTest.java @@ -33,6 +33,7 @@ import org.apache.qpid.jms.Session; import junit.framework.TestCase; import javax.jms.Connection; +import javax.jms.JMSException; import javax.jms.QueueSession; import javax.jms.TopicSession; @@ -55,25 +56,30 @@ public class ConnectionTest extends TestCase TransportConnection.killVMBroker(1); } - public void testSimpleConnection() + public void testSimpleConnection() throws Exception { + AMQConnection conn = null; try { - AMQConnection conn = new AMQConnection(_broker, "guest", "guest", "fred", "test"); - conn.close(); + conn = new AMQConnection(_broker, "guest", "guest", "fred", "test"); } catch (Exception e) { fail("Connection to " + _broker + " should succeed. Reason: " + e); } + finally + { + conn.close(); + } } - public void testDefaultExchanges() + public void testDefaultExchanges() throws Exception { + AMQConnection conn = null; try { - AMQConnection conn = new AMQConnection("amqp://guest:guest@clientid/test?brokerlist='" + conn = new AMQConnection("amqp://guest:guest@clientid/test?brokerlist='" + _broker + "?retries='1''&defaultQueueExchange='test.direct'" + "&defaultTopicExchange='test.topic'" @@ -106,37 +112,53 @@ public class ConnectionTest extends TestCase topicSession.close(); - - conn.close(); } catch (Exception e) { fail("Connection to " + _broker + " should succeed. Reason: " + e); } + finally + { + conn.close(); + } } - //fixme AMQAuthenticationException is not propogaged - public void PasswordFailureConnection() throws Exception + //See QPID-771 + public void testPasswordFailureConnection() throws Exception { + AMQConnection conn = null; try { - new AMQConnection("amqp://guest:rubbishpassword@clientid/test?brokerlist='" + _broker + "?retries='1''"); + conn = new AMQConnection("amqp://guest:rubbishpassword@clientid/test?brokerlist='" + _broker + "?retries='1''"); fail("Connection should not be established password is wrong."); } catch (AMQException amqe) { - if (!(amqe instanceof AMQAuthenticationException)) + if (amqe.getCause().getClass() == Exception.class) + { + System.err.println("QPID-594 : WARNING RACE CONDITION. Unable to determine cause of Connection Failure."); + return; + } + + assertEquals("Exception was wrong type", JMSException.class, amqe.getCause().getClass()); + Exception linked = ((JMSException) amqe.getCause()).getLinkedException(); + assertEquals("Exception was wrong type", AMQAuthenticationException.class, linked.getClass()); + } + finally + { + if (conn != null) { - fail("Correct exception not thrown. Excpected 'AMQAuthenticationException' got: " + amqe); + conn.close(); } } } public void testConnectionFailure() throws Exception { + AMQConnection conn = null; try { - new AMQConnection("amqp://guest:guest@clientid/testpath?brokerlist='" + _broker_NotRunning + "?retries='0''"); + conn = new AMQConnection("amqp://guest:guest@clientid/testpath?brokerlist='" + _broker_NotRunning + "?retries='0''"); fail("Connection should not be established"); } catch (AMQException amqe) @@ -146,14 +168,22 @@ public class ConnectionTest extends TestCase fail("Correct exception not thrown. Excpected 'AMQConnectionException' got: " + amqe); } } + finally + { + if (conn != null) + { + conn.close(); + } + } } public void testUnresolvedHostFailure() throws Exception { + AMQConnection conn = null; try { - new AMQConnection("amqp://guest:guest@clientid/testpath?brokerlist='" + _broker_BadDNS + "?retries='0''"); + conn = new AMQConnection("amqp://guest:guest@clientid/testpath?brokerlist='" + _broker_BadDNS + "?retries='0''"); fail("Connection should not be established"); } catch (AMQException amqe) @@ -163,6 +193,38 @@ public class ConnectionTest extends TestCase fail("Correct exception not thrown. Excpected 'AMQUnresolvedAddressException' got: " + amqe); } } + finally + { + if (conn != null) + { + conn.close(); + } + } + + } + + public void testUnresolvedVirtualHostFailure() throws Exception + { + AMQConnection conn = null; + try + { + conn = new AMQConnection("amqp://guest:guest@clientid/rubbishhost?brokerlist='" + _broker + "?retries='0''"); + fail("Connection should not be established"); + } + catch (AMQException amqe) + { + if (!(amqe instanceof AMQConnectionFailureException)) + { + fail("Correct exception not thrown. Excpected 'AMQConnectionFailureException' got: " + amqe); + } + } + finally + { + if (conn != null) + { + conn.close(); + } + } } public void testClientIdCannotBeChanged() throws Exception @@ -180,7 +242,10 @@ public class ConnectionTest extends TestCase } finally { - connection.close(); + if (connection != null) + { + connection.close(); + } } } @@ -188,7 +253,14 @@ public class ConnectionTest extends TestCase { Connection connection = new AMQConnection(_broker, "guest", "guest", null, "test"); - assertNotNull(connection.getClientID()); + try + { + assertNotNull(connection.getClientID()); + } + finally + { + connection.close(); + } connection.close(); } diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/client/connectionurl/ConnectionURLTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/client/connectionurl/ConnectionURLTest.java index bfbba61913..27adc4dd77 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/client/connectionurl/ConnectionURLTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/client/connectionurl/ConnectionURLTest.java @@ -473,6 +473,25 @@ public class ConnectionURLTest extends TestCase } + public void testSocketProtocol() throws URLSyntaxException + { + String url = "amqp://guest:guest@id/test" + "?brokerlist='socket://VM-Unique-socketID'"; + + try + { + AMQConnectionURL curl = new AMQConnectionURL(url); + assertNotNull(curl); + assertEquals(1, curl.getBrokerCount()); + assertNotNull(curl.getBrokerDetails(0)); + assertEquals(BrokerDetails.SOCKET, curl.getBrokerDetails(0).getTransport()); + assertEquals("VM-Unique-socketID", curl.getBrokerDetails(0).getHost()); + assertEquals("URL does not toString as expected", url, curl.toString()); + } + catch (URLSyntaxException e) + { + fail(e.getMessage()); + } + } public static junit.framework.Test suite() { diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/client/protocol/AMQProtocolSessionTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/client/protocol/AMQProtocolSessionTest.java index 3776ff767f..4cdd7dd7e8 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/client/protocol/AMQProtocolSessionTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/client/protocol/AMQProtocolSessionTest.java @@ -27,8 +27,9 @@ import org.apache.qpid.client.AMQConnection; import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.client.protocol.AMQProtocolSession; import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.testutil.QpidTestCase; -public class AMQProtocolSessionTest extends TestCase +public class AMQProtocolSessionTest extends QpidTestCase { private static class AMQProtSession extends AMQProtocolSession { @@ -50,7 +51,7 @@ public class AMQProtocolSessionTest extends TestCase } //private Strings for test values and expected results - private String _brokenAddress; + private String _brokenAddress = "tcp://myAddress;:";; private String _generatedAddress; private String _emptyAddress; private String _generatedAddress_2; @@ -64,7 +65,7 @@ public class AMQProtocolSessionTest extends TestCase super.setUp(); //don't care about the values set here apart from the dummy IoSession - _testSession = new AMQProtSession(null,new TestIoSession(),null); + _testSession = new AMQProtSession(null,new TestIoSession(), (AMQConnection) getConnection("guest", "guest")); //initialise addresses for test and expected results _port = 123; diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/close/CloseBeforeAckTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/close/CloseBeforeAckTest.java index e78c295ce5..d25986d991 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/close/CloseBeforeAckTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/close/CloseBeforeAckTest.java @@ -29,8 +29,8 @@ import org.apache.qpid.testutil.QpidTestCase; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import uk.co.thebadgerset.junit.concurrency.TestRunnable;
-import uk.co.thebadgerset.junit.concurrency.ThreadTestCoordinator;
+import org.apache.qpid.junit.concurrency.TestRunnable;
+import org.apache.qpid.junit.concurrency.ThreadTestCoordinator;
import javax.jms.Connection;
import javax.jms.Message;
diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/ct/DurableSubscriberTests.java b/java/client/src/test/java/org/apache/qpid/test/unit/ct/DurableSubscriberTest.java index af19db5128..9caba63fe4 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/ct/DurableSubscriberTests.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/ct/DurableSubscriberTest.java @@ -25,7 +25,7 @@ import org.apache.qpid.testutil.QpidTestCase; * Crash Recovery tests for durable subscription * */ -public class DurableSubscriberTests extends QpidTestCase +public class DurableSubscriberTest extends QpidTestCase { private final String _topicName = "durableSubscriberTopic"; diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSPropertiesTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSPropertiesTest.java index c4b94a6f36..6883a09f1b 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSPropertiesTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSPropertiesTest.java @@ -39,6 +39,7 @@ import javax.jms.MessageProducer; import javax.jms.ObjectMessage; import javax.jms.Queue; import javax.jms.Session; +import java.util.Enumeration; /** * @author Apache Software Foundation @@ -84,6 +85,12 @@ public class JMSPropertiesTest extends QpidTestCase sentMsg.setJMSType(JMS_TYPE); sentMsg.setJMSReplyTo(JMS_REPLY_TO); + String JMSXGroupID_VALUE = "group"; + sentMsg.setStringProperty("JMSXGroupID", JMSXGroupID_VALUE); + + int JMSXGroupSeq_VALUE = 1; + sentMsg.setIntProperty("JMSXGroupSeq", JMSXGroupSeq_VALUE); + // send it producer.send(sentMsg); @@ -100,6 +107,30 @@ public class JMSPropertiesTest extends QpidTestCase // assertEquals("JMS Delivery Mode mismatch",sentMsg.getJMSDeliveryMode(),rm.getJMSDeliveryMode()); assertEquals("JMS Type mismatch", sentMsg.getJMSType(), rm.getJMSType()); assertEquals("JMS Reply To mismatch", sentMsg.getJMSReplyTo(), rm.getJMSReplyTo()); + assertTrue("JMSMessageID Does not start ID:", rm.getJMSMessageID().startsWith("ID:")); + + //Validate that the JMSX values are correct + assertEquals("JMSXGroupID is not as expected:", JMSXGroupID_VALUE, rm.getStringProperty("JMSXGroupID")); + assertEquals("JMSXGroupSeq is not as expected:", JMSXGroupSeq_VALUE, rm.getIntProperty("JMSXGroupSeq")); + + boolean JMSXGroupID_Available = false; + boolean JMSXGroupSeq_Available = false; + Enumeration props = con.getMetaData().getJMSXPropertyNames(); + while (props.hasMoreElements()) + { + String name = (String) props.nextElement(); + if (name.equals("JMSXGroupID")) + { + JMSXGroupID_Available = true; + } + if (name.equals("JMSXGroupSeq")) + { + JMSXGroupSeq_Available = true; + } + } + + assertTrue("JMSXGroupID not available.",JMSXGroupID_Available); + assertTrue("JMSXGroupSeq not available.",JMSXGroupSeq_Available); con.close(); } diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java index 23bd2a960a..50ed1dee9e 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java @@ -41,6 +41,14 @@ import javax.jms.Session; import javax.jms.TextMessage; import javax.jms.TopicSubscriber; +/** + * @todo Code to check that a consumer gets only one particular method could be factored into a re-usable method (as + * a static on a base test helper class, e.g. TestUtils. + * + * @todo Code to create test end-points using session per connection, or all sessions on one connection, to be factored + * out to make creating this test variation simpler. Want to make this variation available through LocalCircuit, + * driven by the test model. + */ public class DurableSubscriptionTest extends QpidTestCase { private static final Logger _logger = LoggerFactory.getLogger(DurableSubscriptionTest.class); @@ -113,12 +121,26 @@ public class DurableSubscriptionTest extends QpidTestCase con.close(); } - public void testDurability() throws Exception + public void testDurabilityAUTOACK() throws Exception { + durabilityImpl(Session.AUTO_ACKNOWLEDGE); + } + public void testDurabilityNOACKSessionPerConnection() throws Exception + { + durabilityImplSessionPerConnection(AMQSession.NO_ACKNOWLEDGE); + } + + public void testDurabilityAUTOACKSessionPerConnection() throws Exception + { + durabilityImplSessionPerConnection(Session.AUTO_ACKNOWLEDGE); + } + + private void durabilityImpl(int ackMode) throws Exception + { AMQConnection con = (AMQConnection) getConnection("guest", "guest"); - AMQTopic topic = new AMQTopic(con, "MyDurableSubscriptionTestTopic"); - Session session1 = con.createSession(false, AMQSession.NO_ACKNOWLEDGE); + AMQTopic topic = new AMQTopic(con, "MyTopic"); + Session session1 = con.createSession(false, ackMode); MessageConsumer consumer1 = session1.createConsumer(topic); Session sessionProd = con.createSession(false, AMQSession.NO_ACKNOWLEDGE); @@ -144,10 +166,82 @@ public class DurableSubscriptionTest extends QpidTestCase consumer2.close(); - Session session3 = con.createSession(false, AMQSession.NO_ACKNOWLEDGE); + producer.send(session1.createTextMessage("B")); + + _logger.info("Receive message on consumer 1 :expecting B"); + msg = consumer1.receive(500); + assertNotNull("Consumer 1 should get message 'B'.", msg); + assertEquals("Incorrect Message recevied on consumer1.", "B", ((TextMessage) msg).getText()); + _logger.info("Receive message on consumer 1 :expecting null"); + msg = consumer1.receive(500); + assertNull("There should be no more messages for consumption on consumer1.", msg); + + Session session3 = con.createSession(false, ackMode); MessageConsumer consumer3 = session3.createDurableSubscriber(topic, "MySubscription"); - producer.send(session1.createTextMessage("B")); + _logger.info("Receive message on consumer 3 :expecting B"); + msg = consumer3.receive(500); + assertNotNull("Consumer 3 should get message 'B'.", msg); + assertEquals("Incorrect Message recevied on consumer4.", "B", ((TextMessage) msg).getText()); + _logger.info("Receive message on consumer 3 :expecting null"); + msg = consumer3.receive(500); + assertNull("There should be no more messages for consumption on consumer3.", msg); + + consumer1.close(); + consumer3.close(); + + con.close(); + } + + private void durabilityImplSessionPerConnection(int ackMode) throws Exception + { + Message msg; + // Create producer. + AMQConnection con0 = (AMQConnection) getConnection("guest", "guest"); + con0.start(); + Session session0 = con0.createSession(false, ackMode); + + AMQTopic topic = new AMQTopic(con0, "MyTopic"); + + Session sessionProd = con0.createSession(false, ackMode); + MessageProducer producer = sessionProd.createProducer(topic); + + // Create consumer 1. + AMQConnection con1 = (AMQConnection) getConnection("guest", "guest"); + con1.start(); + Session session1 = con1.createSession(false, ackMode); + + MessageConsumer consumer1 = session0.createConsumer(topic); + + // Create consumer 2. + AMQConnection con2 = (AMQConnection) getConnection("guest", "guest"); + con2.start(); + Session session2 = con2.createSession(false, ackMode); + + TopicSubscriber consumer2 = session2.createDurableSubscriber(topic, "MySubscription"); + + // Send message and check that both consumers get it and only it. + producer.send(session0.createTextMessage("A")); + + msg = consumer1.receive(500); + assertNotNull("Message should be available", msg); + assertEquals("Message Text doesn't match", "A", ((TextMessage) msg).getText()); + msg = consumer1.receive(500); + assertNull("There should be no more messages for consumption on consumer1.", msg); + + msg = consumer2.receive(); + assertNotNull(msg); + assertEquals("Consumer 2 should also received the first msg.", "A", ((TextMessage) msg).getText()); + msg = consumer2.receive(500); + assertNull("There should be no more messages for consumption on consumer2.", msg); + + // Detach the durable subscriber. + consumer2.close(); + session2.close(); + con2.close(); + + // Send message and receive on open consumer. + producer.send(session0.createTextMessage("B")); _logger.info("Receive message on consumer 1 :expecting B"); msg = consumer1.receive(1000); @@ -156,17 +250,27 @@ public class DurableSubscriptionTest extends QpidTestCase msg = consumer1.receive(1000); assertEquals(null, msg); + // Re-attach a new consumer to the durable subscription, and check that it gets the message that it missed. + AMQConnection con3 = (AMQConnection) getConnection("guest", "guest"); + con3.start(); + Session session3 = con3.createSession(false, ackMode); + + TopicSubscriber consumer3 = session3.createDurableSubscriber(topic, "MySubscription"); + _logger.info("Receive message on consumer 3 :expecting B"); - msg = consumer3.receive(1000); - assertEquals("B", ((TextMessage) msg).getText()); + msg = consumer3.receive(500); + assertNotNull("Consumer 3 should get message 'B'.", msg); + assertEquals("Incorrect Message recevied on consumer4.", "B", ((TextMessage) msg).getText()); _logger.info("Receive message on consumer 3 :expecting null"); - msg = consumer3.receive(1000); - assertEquals(null, msg); - // we need to unsubscribe as the session is NO_ACKNOWLEDGE - // messages for the durable subscriber are not deleted so the test cannot - // be run twice in a row - session2.unsubscribe("MySubscription"); - con.close(); + msg = consumer3.receive(500); + assertNull("There should be no more messages for consumption on consumer3.", msg); + + consumer1.close(); + consumer3.close(); + + con0.close(); + con1.close(); + con3.close(); } public static junit.framework.Test suite() diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java index 495cc98f31..f2f35644c6 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java @@ -409,9 +409,9 @@ public class CommitRollbackTest extends QpidTestCase } else { - _logger.warn("Got 2, message prefetched wasn't cleared or messages was in transit when rollback occured"); + _logger.warn("Got 2, message prefetched wasn't cleared or messages was in transit when rollback occured"); assertFalse("Already received message two", _gottwo); - + assertFalse("Already received message redelivered two", _gottwoRedelivered); _gottwo = true; } } @@ -419,6 +419,13 @@ public class CommitRollbackTest extends QpidTestCase verifyMessages(_consumer.receive(1000)); } + /** + * This test sends two messages receives on of them but doesn't ack it. + * The consumer is then closed + * the first message should be returned as redelivered. + * the second message should be delivered normally. + * @throws Exception + */ public void testSend2ThenCloseAfter1andTryAgain() throws Exception { assertTrue("session is not transacted", _session.getTransacted()); @@ -437,6 +444,7 @@ public class CommitRollbackTest extends QpidTestCase assertTrue("Messasge is marked as redelivered" + result, !result.getJMSRedelivered()); _logger.info("Closing Consumer"); + _consumer.close(); _logger.info("Creating New consumer"); |
