diff options
| author | Rafael H. Schloming <rhs@apache.org> | 2009-12-26 12:42:57 +0000 |
|---|---|---|
| committer | Rafael H. Schloming <rhs@apache.org> | 2009-12-26 12:42:57 +0000 |
| commit | 248f1fe188fe2307b9dcf2c87a83b653eaa1920c (patch) | |
| tree | d5d0959a70218946ff72e107a6c106e32479a398 /java/client/src | |
| parent | 3c83a0e3ec7cf4dc23e83a340b25f5fc1676f937 (diff) | |
| download | qpid-python-248f1fe188fe2307b9dcf2c87a83b653eaa1920c.tar.gz | |
synchronized with trunk except for ruby dir
git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/qpid.rnr@893970 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'java/client/src')
109 files changed, 4174 insertions, 3432 deletions
diff --git a/java/client/src/main/java/client.bnd b/java/client/src/main/java/client.bnd new file mode 100755 index 0000000000..a8a33532fb --- /dev/null +++ b/java/client/src/main/java/client.bnd @@ -0,0 +1,7 @@ +ver: 0.6.0
+
+Bundle-SymbolicName: qpid-client
+Bundle-Version: ${ver}
+Export-Package: *;version=${ver}
+Bundle-RequiredExecutionEnvironment: J2SE-1.5 + diff --git a/java/client/src/main/java/log4j.xml b/java/client/src/main/java/log4j.xml new file mode 100644 index 0000000000..c27acba818 --- /dev/null +++ b/java/client/src/main/java/log4j.xml @@ -0,0 +1,36 @@ +<!-- + + - + - Licensed to the Apache Software Foundation (ASF) under one + - or more contributor license agreements. See the NOTICE file + - distributed with this work for additional information + - regarding copyright ownership. The ASF licenses this file + - to you under the Apache License, Version 2.0 (the + - "License"); you may not use this file except in compliance + - with the License. You may obtain a copy of the License at + - + - http://www.apache.org/licenses/LICENSE-2.0 + - + - Unless required by applicable law or agreed to in writing, + - software distributed under the License is distributed on an + - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + - KIND, either express or implied. See the License for the + - specific language governing permissions and limitations + - under the License. + - +--> + +<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"> + <appender name="console" class="org.apache.log4j.ConsoleAppender"> + <param name="Target" value="System.out"/> + <layout class="org.apache.log4j.PatternLayout"> + <param name="ConversionPattern" value="%-5p %c{1} - %m%n"/> + </layout> + </appender> + + <logger name="org.apache.qpid"> + <level value="warn"/> + <appender-ref ref="console" /> + </logger> + +</log4j:configuration> diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQAnyDestination.java b/java/client/src/main/java/org/apache/qpid/client/AMQAnyDestination.java new file mode 100644 index 0000000000..4bb2c12cc8 --- /dev/null +++ b/java/client/src/main/java/org/apache/qpid/client/AMQAnyDestination.java @@ -0,0 +1,56 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.url.BindingURL; + +/** + * In order to support JMS 1.0 the Qpid implementation maps the + * direct exchange to JMS Queue and topic exchange to JMS Topic. + * + * The JMS 1.1 spec provides a javax.Destination as an abstraction + * to represent any type of destination. + * The abstract class AMQDestination has most of the functionality + * to support any destination defined in AMQP 0-10 spec. + */ +public class AMQAnyDestination extends AMQDestination +{ + public AMQAnyDestination(BindingURL binding) + { + super(binding); + } + + public AMQAnyDestination(AMQShortString exchangeName,AMQShortString exchangeClass, + AMQShortString routingKey,boolean isExclusive, + boolean isAutoDelete, AMQShortString queueName, + boolean isDurable, AMQShortString[] bindingKeys) + { + super(exchangeName, exchangeClass, routingKey, isExclusive, isAutoDelete, queueName, isDurable, bindingKeys); + } + + @Override + public boolean isNameRequired() + { + return getAMQQueueName() == null; + } + +} 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 05ac3dca9e..6bae0166d1 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,9 +39,4 @@ 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 f051450260..414638dea4 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 @@ -26,6 +26,7 @@ import java.util.HashMap; import java.util.Map; import org.apache.qpid.jms.BrokerDetails; +import org.apache.qpid.jms.ConnectionURL; import org.apache.qpid.url.URLHelper; import org.apache.qpid.url.URLSyntaxException; @@ -35,18 +36,15 @@ public class AMQBrokerDetails implements BrokerDetails private int _port; private String _transport; - private Map<String, String> _options; + private Map<String, String> _options = new HashMap<String, String>(); private SSLConfiguration _sslConfiguration; - public AMQBrokerDetails() - { - _options = new HashMap<String, String>(); - } - + public AMQBrokerDetails(){} + public AMQBrokerDetails(String url) throws URLSyntaxException - { - this(); + { + // URL should be of format tcp://host:port?option='value',option='value' try { @@ -81,6 +79,10 @@ public class AMQBrokerDetails implements BrokerDetails } } } + else if (url.indexOf("//") == -1) + { + throw new URLSyntaxException(url, "Missing '//' after the transport In broker URL",transport.length()+1,1); + } } else { @@ -252,6 +254,16 @@ public class AMQBrokerDetails implements BrokerDetails return BrokerDetails.DEFAULT_CONNECT_TIMEOUT; } + + public boolean useSSL() + { + if (_options.containsKey(ConnectionURL.OPTIONS_SSL)) + { + return Boolean.parseBoolean(_options.get(ConnectionURL.OPTIONS_SSL)); + } + + return false; + } public void setTimeout(long timeout) { 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 27294562e5..0b9be5951f 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 @@ -20,24 +20,21 @@ */ package org.apache.qpid.client; -import org.apache.qpid.AMQConnectionFailureException; -import org.apache.qpid.AMQException; -import org.apache.qpid.AMQProtocolException; -import org.apache.qpid.AMQUnresolvedAddressException; -import org.apache.qpid.client.failover.FailoverException; -import org.apache.qpid.client.protocol.AMQProtocolHandler; -import org.apache.qpid.client.configuration.ClientProperties; -import org.apache.qpid.exchange.ExchangeDefaults; -import org.apache.qpid.framing.*; -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.slf4j.Logger; -import org.slf4j.LoggerFactory; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.net.ConnectException; +import java.net.UnknownHostException; +import java.nio.channels.UnresolvedAddressException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import javax.jms.ConnectionConsumer; import javax.jms.ConnectionMetaData; @@ -56,17 +53,34 @@ import javax.naming.NamingException; import javax.naming.Reference; import javax.naming.Referenceable; import javax.naming.StringRefAddr; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.net.ConnectException; -import java.net.UnknownHostException; -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; + +import org.apache.qpid.AMQConnectionFailureException; +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQProtocolException; +import org.apache.qpid.AMQUnresolvedAddressException; +import org.apache.qpid.AMQDisconnectedException; +import org.apache.qpid.client.configuration.ClientProperties; +import org.apache.qpid.client.failover.FailoverException; +import org.apache.qpid.client.failover.FailoverProtectedOperation; +import org.apache.qpid.client.protocol.AMQProtocolHandler; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.BasicQosBody; +import org.apache.qpid.framing.BasicQosOkBody; +import org.apache.qpid.framing.ChannelOpenBody; +import org.apache.qpid.framing.ChannelOpenOkBody; +import org.apache.qpid.framing.ProtocolVersion; +import org.apache.qpid.framing.TxSelectBody; +import org.apache.qpid.framing.TxSelectOkBody; +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.slf4j.Logger; +import org.slf4j.LoggerFactory; public class AMQConnection extends Closeable implements Connection, QueueConnection, TopicConnection, Referenceable { @@ -76,6 +90,9 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect private final LinkedHashMap<Integer, AMQSession> _slowAccessSessions = new LinkedHashMap<Integer, AMQSession>(); private int _size = 0; private static final int FAST_CHANNEL_ACCESS_MASK = 0xFFFFFFF0; + private AtomicInteger _idFactory = new AtomicInteger(0); + private int _maxChannelID; + private boolean _cycledIds; public AMQSession get(int channelId) { @@ -165,11 +182,57 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect _fastAccessSessions[i] = null; } } + + /* + * Synchronized on whole method so that we don't need to consider the + * increment-then-reset path in too much detail + */ + public synchronized int getNextChannelId() + { + int id = 0; + if (!_cycledIds) + { + id = _idFactory.incrementAndGet(); + if (id == _maxChannelID) + { + _cycledIds = true; + _idFactory.set(0); // Go back to the start + } + } + else + { + boolean done = false; + while (!done) + { + // Needs to work second time through + id = _idFactory.incrementAndGet(); + if (id > _maxChannelID) + { + _idFactory.set(0); + id = _idFactory.incrementAndGet(); + } + if ((id & FAST_CHANNEL_ACCESS_MASK) == 0) + { + done = (_fastAccessSessions[id] == null); + } + else + { + done = (!_slowAccessSessions.keySet().contains(id)); + } + } + } + + return id; + } + + public void setMaxChannelID(int maxChannelID) + { + _maxChannelID = maxChannelID; + } } private static final Logger _logger = LoggerFactory.getLogger(AMQConnection.class); - protected AtomicInteger _idFactory = new AtomicInteger(0); /** * This is the "root" mutex that must be held when doing anything that could be impacted by failover. This must be @@ -245,16 +308,22 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect /** Thread Pool for executing connection level processes. Such as returning bounced messages. */ 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; + private int _maxPrefetch; //Indicates whether persistent messages are synchronized private boolean _syncPersistence; + //Indicates whether we need to sync on every message ack + private boolean _syncAck; + + //Indicates the sync publish options (persistent|all) + //By default it's async publish + private String _syncPublish = ""; + /** * @param broker brokerdetails * @param username username @@ -335,37 +404,76 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect public AMQConnection(ConnectionURL connectionURL, SSLConfiguration sslConfig) throws AMQException { // set this connection maxPrefetch - if (connectionURL.getOption(ConnectionURL.AMQ_MAXPREFETCH) != null) + if (connectionURL.getOption(ConnectionURL.OPTIONS_MAXPREFETCH) != null) { - _maxPrefetch = Long.parseLong(connectionURL.getOption(ConnectionURL.AMQ_MAXPREFETCH)); + _maxPrefetch = Integer.parseInt(connectionURL.getOption(ConnectionURL.OPTIONS_MAXPREFETCH)); } else { // use the defaul value set for all connections - _maxPrefetch = Long.valueOf(System.getProperties().getProperty(ClientProperties.MAX_PREFETCH_PROP_NAME, - ClientProperties.MAX_PREFETCH_DEFAULT)); + _maxPrefetch = Integer.parseInt(System.getProperties().getProperty(ClientProperties.MAX_PREFETCH_PROP_NAME, + ClientProperties.MAX_PREFETCH_DEFAULT)); } - if (connectionURL.getOption(ConnectionURL.AMQ_SYNC_PERSISTENCE) != null) + if (connectionURL.getOption(ConnectionURL.OPTIONS_SYNC_PERSISTENCE) != null) { - _syncPersistence = Boolean.parseBoolean(connectionURL.getOption(ConnectionURL.AMQ_SYNC_PERSISTENCE)); + _syncPersistence = + Boolean.parseBoolean(connectionURL.getOption(ConnectionURL.OPTIONS_SYNC_PERSISTENCE)); + _logger.warn("sync_persistence is a deprecated property, " + + "please use sync_publish={persistent|all} instead"); } else { // use the defaul value set for all connections _syncPersistence = Boolean.getBoolean(ClientProperties.SYNC_PERSISTENT_PROP_NAME); + if (_syncPersistence) + { + _logger.warn("sync_persistence is a deprecated property, " + + "please use sync_publish={persistent|all} instead"); + } } - _failoverPolicy = new FailoverPolicy(connectionURL); - BrokerDetails brokerDetails = _failoverPolicy.getNextBrokerDetails(); - if (brokerDetails.getTransport().equals(BrokerDetails.VM)) + if (connectionURL.getOption(ConnectionURL.OPTIONS_SYNC_ACK) != null) + { + _syncAck = Boolean.parseBoolean(connectionURL.getOption(ConnectionURL.OPTIONS_SYNC_ACK)); + } + else + { + // use the defaul value set for all connections + _syncAck = Boolean.getBoolean(ClientProperties.SYNC_ACK_PROP_NAME); + } + + if (connectionURL.getOption(ConnectionURL.OPTIONS_SYNC_PUBLISH) != null) + { + _syncPublish = connectionURL.getOption(ConnectionURL.OPTIONS_SYNC_PUBLISH); + } + else + { + // use the default value set for all connections + _syncPublish = System.getProperty((ClientProperties.SYNC_ACK_PROP_NAME),_syncPublish); + } + + String amqpVersion = System.getProperty((ClientProperties.AMQP_VERSION), "0-10"); + + _failoverPolicy = new FailoverPolicy(connectionURL, this); + BrokerDetails brokerDetails = _failoverPolicy.getCurrentBrokerDetails(); + if (brokerDetails.getTransport().equals(BrokerDetails.VM) || "0-8".equals(amqpVersion)) { _delegate = new AMQConnectionDelegate_8_0(this); + } + else if ("0-9".equals(amqpVersion)) + { + _delegate = new AMQConnectionDelegate_0_9(this); + } + else if ("0-91".equals(amqpVersion) || "0-9-1".equals(amqpVersion)) + { + _delegate = new AMQConnectionDelegate_9_1(this); } else { _delegate = new AMQConnectionDelegate_0_10(this); } + _sessions.setMaxChannelID(_delegate.getMaxChannelID()); if (_logger.isInfoEnabled()) { @@ -413,7 +521,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect boolean retryAllowed = true; Exception connectionException = null; - while (!_connected && retryAllowed) + while (!_connected && retryAllowed && brokerDetails != null) { ProtocolVersion pe = null; try @@ -439,7 +547,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect } else if (!_connected) { - retryAllowed = _failoverPolicy.failoverAllowed(); + retryAllowed = _failoverPolicy.failoverAllowed(); brokerDetails = _failoverPolicy.getNextBrokerDetails(); } } @@ -458,7 +566,6 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect if (connectionException.getCause() != null) { message = connectionException.getCause().getMessage(); - connectionException.getCause().printStackTrace(); } else { @@ -518,6 +625,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect Class partypes[] = new Class[1]; partypes[0] = AMQConnection.class; _delegate = (AMQConnectionDelegate) c.getConstructor(partypes).newInstance(this); + _sessions.setMaxChannelID(_delegate.getMaxChannelID()); } catch (ClassNotFoundException e) { @@ -591,12 +699,12 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect public boolean attemptReconnection() { - while (_failoverPolicy.failoverAllowed()) + BrokerDetails broker = null; + while (_failoverPolicy.failoverAllowed() && (broker = _failoverPolicy.getNextBrokerDetails()) != null) { try { - makeBrokerConnection(_failoverPolicy.getNextBrokerDetails()); - + makeBrokerConnection(broker); return true; } catch (Exception e) @@ -628,6 +736,11 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect return _delegate.makeBrokerConnection(brokerDetail); } + public <T, E extends Exception> T executeRetrySupport(FailoverProtectedOperation<T,E> operation) throws E + { + return _delegate.executeRetrySupport(operation); + } + /** * Get the details of the currently active broker * @@ -653,7 +766,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect public org.apache.qpid.jms.Session createSession(final boolean transacted, final int acknowledgeMode) throws JMSException { - return createSession(transacted, acknowledgeMode, AMQSession.DEFAULT_PREFETCH_HIGH_MARK); + return createSession(transacted, acknowledgeMode, _maxPrefetch); } public org.apache.qpid.jms.Session createSession(final boolean transacted, final int acknowledgeMode, final int prefetch) @@ -871,7 +984,12 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect { if (!_closed.getAndSet(true)) { - doClose(sessions, timeout); + _closing.set(true); + try{ + doClose(sessions, timeout); + }finally{ + _closing.set(false); + } } } @@ -917,7 +1035,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect // adjust timeout timeout = adjustTimeout(timeout, startCloseTime); - _delegate.closeConneciton(timeout); + _delegate.closeConnection(timeout); //If the taskpool hasn't shutdown by now then give it shutdownNow. // This will interupt any running tasks. @@ -932,6 +1050,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect } catch (AMQException e) { + _logger.error("error:", e); JMSException jmse = new JMSException("Error closing connection: " + e); jmse.setLinkedException(e); throw jmse; @@ -1191,6 +1310,11 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect return _failoverMutex; } + public void failoverPrep() + { + _delegate.failoverPrep(); + } + public void resubscribeSessions() throws JMSException, AMQException, FailoverException { _delegate.resubscribeSessions(); @@ -1245,6 +1369,17 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect else { //Should never get here as all AMQEs are required to have an ErrorCode! + // Other than AMQDisconnectedEx! + + if (cause instanceof AMQDisconnectedException) + { + Exception last = _protocolHandler.getStateManager().getLastException(); + if (last != null) + { + _logger.info("StateManager had an exception for us to use a cause of our Disconnected Exception"); + cause = last; + } + } je = new JMSException("Exception thrown against " + toString() + ": " + cause); } @@ -1259,8 +1394,10 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect // in the case of an IOException, MINA has closed the protocol session so we set _closed to true // so that any generic client code that tries to close the connection will not mess up this error // handling sequence - if (cause instanceof IOException) + if (cause instanceof IOException || cause instanceof AMQDisconnectedException) { + // If we have an IOE/AMQDisconnect there is no connection to close on. + _closing.set(false); closer = !_closed.getAndSet(true); _protocolHandler.getProtocolSession().notifyError(je); @@ -1317,7 +1454,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect _sessions.put(channelId, session); } - void deregisterSession(int channelId) + public void deregisterSession(int channelId) { _sessions.remove(channelId); } @@ -1411,13 +1548,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect public ProtocolVersion getProtocolVersion() { - return _protocolVersion; - } - - public void setProtocolVersion(ProtocolVersion protocolVersion) - { - _protocolVersion = protocolVersion; - _protocolHandler.getProtocolSession().setProtocolVersion(protocolVersion); + return _delegate.getProtocolVersion(); } public boolean isFailingOver() @@ -1444,4 +1575,27 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect { return _syncPersistence; } + + /** + * Indicates whether we need to sync on every message ack + */ + public boolean getSyncAck() + { + return _syncAck; + } + + public String getSyncPublish() + { + return _syncPublish; + } + + public void setIdleTimeout(long l) + { + _delegate.setIdleTimeout(l); + } + + public int getNextChannelID() + { + return _sessions.getNextChannelId(); + } } diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate.java b/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate.java index 7f36ec6e99..23dc244dee 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate.java @@ -27,20 +27,40 @@ import javax.jms.XASession; import org.apache.qpid.AMQException; import org.apache.qpid.client.failover.FailoverException; +import org.apache.qpid.client.failover.FailoverProtectedOperation; import org.apache.qpid.framing.ProtocolVersion; import org.apache.qpid.jms.BrokerDetails; import org.apache.qpid.jms.Session; public interface AMQConnectionDelegate { - public ProtocolVersion makeBrokerConnection(BrokerDetails brokerDetail) throws IOException, AMQException; + ProtocolVersion makeBrokerConnection(BrokerDetails brokerDetail) throws IOException, AMQException; - public Session createSession(final boolean transacted, final int acknowledgeMode, - final int prefetchHigh, final int prefetchLow) throws JMSException; + Session createSession(final boolean transacted, final int acknowledgeMode, + final int prefetchHigh, final int prefetchLow) throws JMSException; - public XASession createXASession(int prefetchHigh, int prefetchLow) throws JMSException; + /** + * Create an XASession with default prefetch values of: + * High = MaxPrefetch + * Low = MaxPrefetch / 2 + * @return XASession + * @throws JMSException thrown if there is a problem creating the session. + */ + XASession createXASession() throws JMSException; - public void resubscribeSessions() throws JMSException, AMQException, FailoverException; + XASession createXASession(int prefetchHigh, int prefetchLow) throws JMSException; - public void closeConneciton(long timeout) throws JMSException, AMQException; + void failoverPrep(); + + void resubscribeSessions() throws JMSException, AMQException, FailoverException; + + void closeConnection(long timeout) throws JMSException, AMQException; + + <T, E extends Exception> T executeRetrySupport(FailoverProtectedOperation<T,E> operation) throws E; + + void setIdleTimeout(long l); + + int getMaxChannelID(); + + ProtocolVersion getProtocolVersion(); } diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_10.java b/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_10.java index a2df2f3cf2..af21eb7ed0 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_10.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_10.java @@ -1,26 +1,52 @@ package org.apache.qpid.client; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import javax.jms.ExceptionListener; import javax.jms.JMSException; import javax.jms.XASession; import org.apache.qpid.AMQException; -import org.apache.qpid.AMQProtocolException; -import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.client.configuration.ClientProperties; import org.apache.qpid.client.failover.FailoverException; +import org.apache.qpid.client.failover.FailoverProtectedOperation; import org.apache.qpid.framing.ProtocolVersion; import org.apache.qpid.jms.BrokerDetails; import org.apache.qpid.jms.Session; -import org.apache.qpid.nclient.Client; -import org.apache.qpid.nclient.ClosedListener; -import org.apache.qpid.ErrorCode; -import org.apache.qpid.QpidException; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.transport.Connection; +import org.apache.qpid.transport.ConnectionClose; +import org.apache.qpid.transport.ConnectionException; +import org.apache.qpid.transport.ConnectionListener; import org.apache.qpid.transport.ProtocolVersionException; +import org.apache.qpid.transport.TransportException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class AMQConnectionDelegate_0_10 implements AMQConnectionDelegate, ClosedListener +public class AMQConnectionDelegate_0_10 implements AMQConnectionDelegate, ConnectionListener { /** * This class logger. @@ -35,12 +61,15 @@ public class AMQConnectionDelegate_0_10 implements AMQConnectionDelegate, Closed /** * The QpidConeection instance that is mapped with thie JMS connection. */ - org.apache.qpid.nclient.Connection _qpidConnection; + org.apache.qpid.transport.Connection _qpidConnection; + private ConnectionException exception = null; //--- constructor public AMQConnectionDelegate_0_10(AMQConnection conn) { _conn = conn; + _qpidConnection = new Connection(); + _qpidConnection.setConnectionListener(this); } /** @@ -50,7 +79,7 @@ public class AMQConnectionDelegate_0_10 implements AMQConnectionDelegate, Closed throws JMSException { _conn.checkNotClosed(); - int channelId = _conn._idFactory.incrementAndGet(); + int channelId = _conn.getNextChannelID(); AMQSession session; try { @@ -71,12 +100,24 @@ public class AMQConnectionDelegate_0_10 implements AMQConnectionDelegate, Closed } /** + * Create an XASession with default prefetch values of: + * High = MaxPrefetch + * Low = MaxPrefetch / 2 + * @return XASession + * @throws JMSException + */ + public XASession createXASession() throws JMSException + { + return createXASession((int) _conn.getMaxPrefetch(), (int) _conn.getMaxPrefetch() / 2); + } + + /** * create an XA Session and start it if required. */ public XASession createXASession(int prefetchHigh, int prefetchLow) throws JMSException { _conn.checkNotClosed(); - int channelId = _conn._idFactory.incrementAndGet(); + int channelId = _conn.getNextChannelID(); XASessionImpl session; try { @@ -104,70 +145,165 @@ public class AMQConnectionDelegate_0_10 implements AMQConnectionDelegate, Closed */ public ProtocolVersion makeBrokerConnection(BrokerDetails brokerDetail) throws IOException, AMQException { - _qpidConnection = Client.createConnection(); try { if (_logger.isDebugEnabled()) { - _logger.debug("creating connection with broker " + " host: " + brokerDetail - .getHost() + " port: " + brokerDetail.getPort() + " virtualhost: " + _conn - .getVirtualHost() + "user name: " + _conn.getUsername() + "password: " + _conn.getPassword()); + _logger.debug("connecting to host: " + brokerDetail.getHost() + + " port: " + brokerDetail.getPort() + + " vhost: " + _conn.getVirtualHost() + + " username: " + _conn.getUsername() + + " password: " + _conn.getPassword()); + } + + if (brokerDetail.getProperty(BrokerDetails.OPTIONS_IDLE_TIMEOUT) != null) + { + this.setIdleTimeout(Long.parseLong(brokerDetail.getProperty(BrokerDetails.OPTIONS_IDLE_TIMEOUT))); } + else + { + // use the default value set for all connections + this.setIdleTimeout(Long.getLong(ClientProperties.IDLE_TIMEOUT_PROP_NAME,0)); + } + + String saslMechs = brokerDetail.getProperty("sasl_mechs")!= null? + brokerDetail.getProperty("sasl_mechs"): + System.getProperty("qpid.sasl_mechs","PLAIN"); + _qpidConnection.connect(brokerDetail.getHost(), brokerDetail.getPort(), _conn.getVirtualHost(), - _conn.getUsername(), _conn.getPassword()); - _qpidConnection.setClosedListener(this); + _conn.getUsername(), _conn.getPassword(), brokerDetail.useSSL(),saslMechs); _conn._connected = true; + _conn._failoverPolicy.attainedConnection(); } catch(ProtocolVersionException pe) { return new ProtocolVersion(pe.getMajor(), pe.getMinor()); } - catch (QpidException e) - { + catch (ConnectionException e) + { throw new AMQException(AMQConstant.CHANNEL_ERROR, "cannot connect to broker", e); } return null; } - /** - * Not supported at this level. - */ + public void failoverPrep() + { + List<AMQSession> sessions = new ArrayList<AMQSession>(_conn.getSessions().values()); + for (AMQSession s : sessions) + { + s.failoverPrep(); + } + } + public void resubscribeSessions() throws JMSException, AMQException, FailoverException { - //NOT implemented as railover is handled at a lower level - throw new FailoverException("failing to reconnect during failover, operation not supported."); + List<AMQSession> sessions = new ArrayList<AMQSession>(_conn.getSessions().values()); + _logger.info(String.format("Resubscribing sessions = %s sessions.size=%s", sessions, sessions.size())); + for (AMQSession s : sessions) + { + ((AMQSession_0_10) s)._qpidConnection = _qpidConnection; + s.resubscribe(); + } } - public void closeConneciton(long timeout) throws JMSException, AMQException + public void closeConnection(long timeout) throws JMSException, AMQException { try { _qpidConnection.close(); } - catch (QpidException e) + catch (TransportException e) + { + throw new AMQException(e.getMessage(), e); + } + } + + public void opened(Connection conn) {} + + public void exception(Connection conn, ConnectionException exc) + { + if (exception != null) { - throw new AMQException(AMQConstant.CHANNEL_ERROR, "cannot close connection", e); + _logger.error("previous exception", exception); } + exception = exc; } - public void onClosed(ErrorCode errorCode, String reason, Throwable t) + public void closed(Connection conn) { - if (_logger.isDebugEnabled()) + ConnectionException exc = exception; + exception = null; + + if (exc == null) + { + return; + } + + ConnectionClose close = exc.getClose(); + if (close == null) + { + try + { + if (_conn.firePreFailover(false) && _conn.attemptReconnection()) + { + _conn.failoverPrep(); + _qpidConnection.resume(); + _conn.fireFailoverComplete(); + return; + } + } + catch (Exception e) + { + _logger.error("error during failover", e); + } + } + + ExceptionListener listener = _conn._exceptionListener; + if (listener == null) { - _logger.debug("Received a connection close from the broker: Error code : " + errorCode.getCode(), t); + _logger.error("connection exception: " + conn, exc); } - if (_conn._exceptionListener != null) + else { - JMSException ex = new JMSException(reason,String.valueOf(errorCode.getCode())); - if (t != null) + String code = null; + if (close != null) { - ex.initCause(t); + code = close.getReplyCode().toString(); } - _conn._exceptionListener.onException(ex); + JMSException ex = new JMSException(exc.getMessage(), code); + ex.initCause(exc); + listener.onException(ex); + } + } + + public <T, E extends Exception> T executeRetrySupport(FailoverProtectedOperation<T,E> operation) throws E + { + try + { + return operation.execute(); } + catch (FailoverException e) + { + throw new RuntimeException(e); + } + } + + public void setIdleTimeout(long l) + { + _qpidConnection.setIdleTimeout(l); + } + + public int getMaxChannelID() + { + return Integer.MAX_VALUE; + } + + public ProtocolVersion getProtocolVersion() + { + return ProtocolVersion.v0_10; } } diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_9.java b/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_9.java index d95e2e3dff..70ecedfd8b 100755 --- a/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_9.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_9.java @@ -20,6 +20,8 @@ */ package org.apache.qpid.client; +import org.apache.qpid.framing.ProtocolVersion; + public class AMQConnectionDelegate_0_9 extends AMQConnectionDelegate_8_0 { @@ -28,5 +30,11 @@ public class AMQConnectionDelegate_0_9 extends AMQConnectionDelegate_8_0 { super(conn); } + + @Override + public ProtocolVersion getProtocolVersion() + { + return ProtocolVersion.v0_9; + } } diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_8_0.java b/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_8_0.java index 2ec8737d16..6f44f68b37 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_8_0.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_8_0.java @@ -48,7 +48,6 @@ import org.apache.qpid.framing.TxSelectBody; import org.apache.qpid.framing.TxSelectOkBody; import org.apache.qpid.jms.BrokerDetails; import org.apache.qpid.jms.ChannelLimitReachedException; -import org.apache.qpid.transport.network.io.IoTransport; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -58,7 +57,7 @@ public class AMQConnectionDelegate_8_0 implements AMQConnectionDelegate private AMQConnection _conn; - public void closeConneciton(long timeout) throws JMSException, AMQException + public void closeConnection(long timeout) throws JMSException, AMQException { _conn.getProtocolHandler().closeConnection(timeout); @@ -90,15 +89,15 @@ public class AMQConnectionDelegate_8_0 implements AMQConnectionDelegate StateWaiter waiter = _conn._protocolHandler.createWaiter(openOrClosedStates); // TODO: use system property thingy for this - if (System.getProperty("UseTransportIo", "false").equals("false")) + if (System.getProperty("UseTransportIo", "false").equals("false")) { TransportConnection.getInstance(brokerDetail).connect(_conn._protocolHandler, brokerDetail); - } - else + } + else { _conn.getProtocolHandler().createIoTransportSession(brokerDetail); } - + _conn._protocolHandler.getProtocolSession().init(); // this blocks until the connection has been set up or when an error // has prevented the connection being set up @@ -108,9 +107,13 @@ public class AMQConnectionDelegate_8_0 implements AMQConnectionDelegate { _conn._failoverPolicy.attainedConnection(); _conn._connected = true; + return null; + } + else + { + return _conn._protocolHandler.getSuggestedProtocolVersion(); } - return null; } public org.apache.qpid.jms.Session createSession(final boolean transacted, final int acknowledgeMode, final int prefetch) @@ -139,7 +142,7 @@ public class AMQConnectionDelegate_8_0 implements AMQConnectionDelegate { public org.apache.qpid.jms.Session execute() throws JMSException, FailoverException { - int channelId = _conn._idFactory.incrementAndGet(); + int channelId = _conn.getNextChannelID(); if (_logger.isDebugEnabled()) { @@ -192,6 +195,18 @@ public class AMQConnectionDelegate_8_0 implements AMQConnectionDelegate }, _conn).execute(); } + /** + * Create an XASession with default prefetch values of: + * High = MaxPrefetch + * Low = MaxPrefetch / 2 + * @return XASession + * @throws JMSException thrown if there is a problem creating the session. + */ + public XASession createXASession() throws JMSException + { + return createXASession((int) _conn.getMaxPrefetch(), (int) _conn.getMaxPrefetch() / 2); + } + private void createChannelOverWire(int channelId, int prefetchHigh, int prefetchLow, boolean transacted) throws AMQException, FailoverException { @@ -203,7 +218,7 @@ public class AMQConnectionDelegate_8_0 implements AMQConnectionDelegate // todo Be aware of possible changes to parameter order as versions change. BasicQosBody basicQosBody = _conn.getProtocolHandler().getMethodRegistry().createBasicQosBody(0,prefetchHigh,false); _conn._protocolHandler.syncWrite(basicQosBody.generateFrame(channelId),BasicQosOkBody.class); - + if (transacted) { if (_logger.isDebugEnabled()) @@ -211,12 +226,17 @@ public class AMQConnectionDelegate_8_0 implements AMQConnectionDelegate _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(body.generateFrame(channelId), TxSelectOkBody.class); } } + public void failoverPrep() + { + // do nothing + } + /** * 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. @@ -247,4 +267,52 @@ public class AMQConnectionDelegate_8_0 implements AMQConnectionDelegate throw new AMQException(null, "Error reopening channel " + channelId + " after failover: " + e, e); } } + + public <T, E extends Exception> T executeRetrySupport(FailoverProtectedOperation<T,E> operation) throws E + { + while (true) + { + try + { + _conn.blockUntilNotFailingOver(); + } + catch (InterruptedException e) + { + _logger.debug("Interrupted: " + e, e); + + return null; + } + + synchronized (_conn.getFailoverMutex()) + { + try + { + return operation.execute(); + } + catch (FailoverException e) + { + _logger.debug("Failover exception caught during operation: " + e, e); + } + catch (IllegalStateException e) + { + if (!(e.getMessage().startsWith("Fail-over interupted no-op failover support"))) + { + throw e; + } + } + } + } + } + + public void setIdleTimeout(long l){} + + public int getMaxChannelID() + { + return (int) (Math.pow(2, 16)-1); + } + + public ProtocolVersion getProtocolVersion() + { + return ProtocolVersion.v8_0; + } } diff --git a/java/client/src/main/java/org/apache/qpid/nclient/ClosedListener.java b/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_9_1.java index 4cf0cab1ec..442dd7b286 100644..100755 --- a/java/client/src/main/java/org/apache/qpid/nclient/ClosedListener.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_9_1.java @@ -1,4 +1,5 @@ /* + * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information @@ -15,25 +16,24 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. + * */ -package org.apache.qpid.nclient; +package org.apache.qpid.client; -import org.apache.qpid.ErrorCode; +import org.apache.qpid.framing.ProtocolVersion; -/** - * If the communication layer detects a serious problem with a <CODE>connection</CODE>, it - * informs the connection's ExceptionListener - */ -public interface ClosedListener +public class AMQConnectionDelegate_9_1 extends AMQConnectionDelegate_8_0 { - /** - * If the communication layer detects a serious problem with a connection, it - * informs the connection's ExceptionListener - * @param errorCode TODO - * @param reason TODO - * @param t TODO - * @see Connection - */ - public void onClosed(ErrorCode errorCode, String reason, Throwable t); + + public AMQConnectionDelegate_9_1(AMQConnection conn) + { + super(conn); + } + + @Override + public ProtocolVersion getProtocolVersion() + { + return ProtocolVersion.v0_91; + } }
\ No newline at end of file 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 01a915f2cc..2d6f4434fe 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 @@ -354,6 +354,17 @@ public class AMQConnectionFactory implements ConnectionFactory, QueueConnectionF return _connectionDetails; } + public String getConnectionURLString() + { + return _connectionDetails.toString(); + } + + + public final void setConnectionURLString(String url) throws URLSyntaxException + { + _connectionDetails = new AMQConnectionURL(url); + } + /** * JNDI interface to create objects from References. * 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 21b63c1fdb..60e4a5960a 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 @@ -237,14 +237,7 @@ public class AMQConnectionURL implements ConnectionURL if (_password != null) { sb.append(':'); - if (_logger.isDebugEnabled()) - { - sb.append(_password); - } - else - { - sb.append("********"); - } + sb.append("********"); } sb.append('@'); 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 6c78b754bb..3f2c1af5c2 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 @@ -82,8 +82,8 @@ public abstract class AMQDestination implements Destination, Referenceable _isExclusive = Boolean.parseBoolean(binding.getOption(BindingURL.OPTION_EXCLUSIVE)); _isAutoDelete = Boolean.parseBoolean(binding.getOption(BindingURL.OPTION_AUTODELETE)); _isDurable = Boolean.parseBoolean(binding.getOption(BindingURL.OPTION_DURABLE)); - _queueName = binding.getQueueName() == null ? null : new AMQShortString(binding.getQueueName()); - _routingKey = binding.getRoutingKey() == null ? null : new AMQShortString(binding.getRoutingKey()); + _queueName = binding.getQueueName() == null ? null : binding.getQueueName(); + _routingKey = binding.getRoutingKey() == null ? null : binding.getRoutingKey(); _bindingKeys = binding.getBindingKeys() == null || binding.getBindingKeys().length == 0 ? new AMQShortString[0] : binding.getBindingKeys(); } @@ -122,10 +122,11 @@ public abstract class AMQDestination implements Destination, Referenceable protected AMQDestination(AMQShortString exchangeName, AMQShortString exchangeClass, AMQShortString routingKey, boolean isExclusive, boolean isAutoDelete, AMQShortString queueName, boolean isDurable,AMQShortString[] bindingKeys) { - // If used with a fannout exchange, the routing key can be null - if ( !ExchangeDefaults.FANOUT_EXCHANGE_CLASS.equals(exchangeClass) && routingKey == null) + if ( (ExchangeDefaults.DIRECT_EXCHANGE_CLASS.equals(exchangeClass) || + ExchangeDefaults.TOPIC_EXCHANGE_CLASS.equals(exchangeClass)) + && routingKey == null) { - throw new IllegalArgumentException("routingKey exchange must not be null"); + throw new IllegalArgumentException("routing/binding key must not be null"); } if (exchangeName == null) { @@ -270,7 +271,7 @@ public abstract class AMQDestination implements Destination, Referenceable sb.append("://"); sb.append(_exchangeName); - sb.append("//"); + sb.append("/"+_routingKey+"/"); if (_queueName != null) { @@ -472,11 +473,12 @@ public abstract class AMQDestination implements Destination, Referenceable } else { - throw new IllegalArgumentException("Unknown Exchange Class:" + exchangeClass); + return new AMQAnyDestination(exchangeName,exchangeClass, + routingKey,isExclusive, + isAutoDelete,queueName, + isDurable, new AMQShortString[0]); } - - } public static Destination createDestination(BindingURL binding) @@ -495,13 +497,9 @@ public abstract class AMQDestination implements Destination, Referenceable { return new AMQHeadersExchange(binding); } - else if (type.equals(ExchangeDefaults.FANOUT_EXCHANGE_CLASS)) - { - return new AMQQueue(binding); - } else { - throw new IllegalArgumentException("Unknown Exchange Class:" + type + " in binding:" + binding); + return new AMQAnyDestination(binding); } } } 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 08fd49286b..d96544adf8 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,14 +22,12 @@ 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; import javax.jms.Message; import javax.jms.Queue; import javax.jms.QueueBrowser; - import java.util.ArrayList; import java.util.Enumeration; import java.util.concurrent.atomic.AtomicBoolean; @@ -90,38 +88,11 @@ public class AMQQueueBrowser implements QueueBrowser { checkState(); final BasicMessageConsumer consumer = - (BasicMessageConsumer) _session.createBrowserConsumer(_queue, _messageSelector, false); + (BasicMessageConsumer) _session.createBrowserConsumer(_queue, _messageSelector, false); _consumers.add(consumer); - return new Enumeration() - { - - Message _nextMessage = consumer == null ? null : consumer.receive(1000); - - public boolean hasMoreElements() - { - _logger.info("QB:hasMoreElements:" + (_nextMessage != null)); - return (_nextMessage != null); - } - - public Object nextElement() - { - 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; - } - }; + return new QueueBrowserEnumeration(consumer); } public void close() throws JMSException @@ -134,4 +105,39 @@ public class AMQQueueBrowser implements QueueBrowser _consumers.clear(); } + private class QueueBrowserEnumeration implements Enumeration + { + Message _nextMessage; + private BasicMessageConsumer _consumer; + + public QueueBrowserEnumeration(BasicMessageConsumer consumer) throws JMSException + { + _nextMessage = consumer == null ? null : consumer.receiveBrowse(); + _logger.info("QB:created with first element:" + _nextMessage); + _consumer = consumer; + } + + public boolean hasMoreElements() + { + _logger.info("QB:hasMoreElements:" + (_nextMessage != null)); + return (_nextMessage != null); + } + + public Object nextElement() + { + Message msg = _nextMessage; + try + { + _logger.info("QB:nextElement about to receive"); + _nextMessage = _consumer.receiveBrowse(); + _logger.info("QB:nextElement received:" + _nextMessage); + } + catch (JMSException e) + { + _logger.warn("Exception caught while queue browsing", e); + _nextMessage = null; + } + return msg; + } + } } 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 5203a27f42..43f6fd8ad2 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 @@ -30,6 +30,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; @@ -58,25 +59,37 @@ 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; +import org.apache.qpid.AMQChannelClosedException; import org.apache.qpid.client.failover.FailoverException; import org.apache.qpid.client.failover.FailoverNoopSupport; import org.apache.qpid.client.failover.FailoverProtectedOperation; import org.apache.qpid.client.failover.FailoverRetrySupport; -import org.apache.qpid.client.message.*; +import org.apache.qpid.client.message.AMQMessageDelegateFactory; +import org.apache.qpid.client.message.AbstractJMSMessage; +import org.apache.qpid.client.message.CloseConsumerMessage; +import org.apache.qpid.client.message.JMSBytesMessage; +import org.apache.qpid.client.message.JMSMapMessage; +import org.apache.qpid.client.message.JMSObjectMessage; +import org.apache.qpid.client.message.JMSStreamMessage; +import org.apache.qpid.client.message.JMSTextMessage; +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.client.util.FlowControllingBlockingQueue; -import org.apache.qpid.client.state.AMQStateManager; import org.apache.qpid.client.state.AMQState; +import org.apache.qpid.client.state.AMQStateManager; +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.framing.MethodRegistry; import org.apache.qpid.jms.Session; +import org.apache.qpid.thread.Threading; import org.apache.qpid.url.AMQBindingURL; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -100,7 +113,6 @@ import org.slf4j.LoggerFactory; public abstract class AMQSession<C extends BasicMessageConsumer, P extends BasicMessageProducer> extends Closeable implements Session, QueueSession, TopicSession { - public static final class IdToConsumerMap<C extends BasicMessageConsumer> { private final BasicMessageConsumer[] _fastAccessConsumers = new BasicMessageConsumer[16]; @@ -181,24 +193,38 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic /** Used for debugging. */ private static final Logger _logger = LoggerFactory.getLogger(AMQSession.class); - - /** The default maximum number of prefetched message at which to suspend the channel. */ - public static final int DEFAULT_PREFETCH_HIGH_MARK = 5000; - - /** The default minimum number of prefetched messages at which to resume the channel. */ - public static final int DEFAULT_PREFETCH_LOW_MARK = 2500; - /** * The default value for immediate flag used by producers created by this session is false. That is, a consumer does * not need to be attached to a queue. */ - protected static final boolean DEFAULT_IMMEDIATE = false; + protected final boolean DEFAULT_IMMEDIATE = Boolean.parseBoolean(System.getProperty("qpid.default_immediate", "false")); /** * The default value for mandatory flag used by producers created by this session is true. That is, server will not * silently drop messages where no queue is connected to the exchange for the message. */ - protected static final boolean DEFAULT_MANDATORY = true; + protected final boolean DEFAULT_MANDATORY = Boolean.parseBoolean(System.getProperty("qpid.default_mandatory", "true")); + + protected final boolean DEFAULT_WAIT_ON_SEND = Boolean.parseBoolean(System.getProperty("qpid.default_wait_on_send", "false")); + + /** + * The period to wait while flow controlled before sending a log message confirming that the session is still + * waiting on flow control being revoked + */ + protected final long FLOW_CONTROL_WAIT_PERIOD = Long.getLong("qpid.flow_control_wait_notify_period",5000L); + + /** + * The period to wait while flow controlled before declaring a failure + */ + public static final long DEFAULT_FLOW_CONTROL_WAIT_FAILURE = 120000L; + protected final long FLOW_CONTROL_WAIT_FAILURE = Long.getLong("qpid.flow_control_wait_failure", + DEFAULT_FLOW_CONTROL_WAIT_FAILURE); + + protected final boolean DECLARE_QUEUES = + Boolean.parseBoolean(System.getProperty("qpid.declare_queues", "true")); + + protected final boolean DECLARE_EXCHANGES = + Boolean.parseBoolean(System.getProperty("qpid.declare_exchanges", "true")); /** System property to enable strict AMQP compliance. */ public static final String STRICT_AMQP = "STRICT_AMQP"; @@ -233,10 +259,10 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic private int _ticket; /** Holds the high mark for prefetched message, at which the session is suspended. */ - private int _defaultPrefetchHighMark = DEFAULT_PREFETCH_HIGH_MARK; + private int _prefetchHighMark; /** Holds the low mark for prefetched messages, below which the session is resumed. */ - private int _defaultPrefetchLowMark = DEFAULT_PREFETCH_LOW_MARK; + private int _prefetchLowMark; /** Holds the message listener, if any, which is attached to this session. */ private MessageListener _messageListener = null; @@ -268,6 +294,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic /** Holds the highest received delivery tag. */ private final AtomicLong _highestDeliveryTag = new AtomicLong(-1); + private final AtomicLong _rollbackMark = new AtomicLong(-1); /** All the not yet acknowledged message tags */ protected ConcurrentLinkedQueue<Long> _unacknowledgedMessageTags = new ConcurrentLinkedQueue<Long>(); @@ -278,6 +305,8 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic /** Holds the dispatcher thread for this session. */ protected Dispatcher _dispatcher; + protected Thread _dispatcherThread; + /** Holds the message factory factory for this session. */ protected MessageFactoryRegistry _messageFactoryRegistry; @@ -414,13 +443,13 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic _channelId = channelId; _messageFactoryRegistry = messageFactoryRegistry; - _defaultPrefetchHighMark = defaultPrefetchHighMark; - _defaultPrefetchLowMark = defaultPrefetchLowMark; + _prefetchHighMark = defaultPrefetchHighMark; + _prefetchLowMark = defaultPrefetchLowMark; if (_acknowledgeMode == NO_ACKNOWLEDGE) { _queue = - new FlowControllingBlockingQueue(_defaultPrefetchHighMark, _defaultPrefetchLowMark, + new FlowControllingBlockingQueue(_prefetchHighMark, _prefetchLowMark, new FlowControllingBlockingQueue.ThresholdListener() { private final AtomicBoolean _suspendState = new AtomicBoolean(); @@ -428,7 +457,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic public void aboveThreshold(int currentValue) { _logger.debug( - "Above threshold(" + _defaultPrefetchHighMark + "Above threshold(" + _prefetchHighMark + ") so suspending channel. Current value is " + currentValue); _suspendState.set(true); new Thread(new SuspenderRunner(_suspendState)).start(); @@ -438,7 +467,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic public void underThreshold(int currentValue) { _logger.debug( - "Below threshold(" + _defaultPrefetchLowMark + "Below threshold(" + _prefetchLowMark + ") so unsuspending channel. Current value is " + currentValue); _suspendState.set(false); new Thread(new SuspenderRunner(_suspendState)).start(); @@ -448,7 +477,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic } else { - _queue = new FlowControllingBlockingQueue(_defaultPrefetchHighMark, null); + _queue = new FlowControllingBlockingQueue(_prefetchHighMark, null); } } @@ -568,12 +597,19 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic public void bindQueue(final AMQShortString queueName, final AMQShortString routingKey, final FieldTable arguments, final AMQShortString exchangeName, final AMQDestination destination) throws AMQException { + bindQueue(queueName, routingKey, arguments, exchangeName, destination, false); + } + + public void bindQueue(final AMQShortString queueName, final AMQShortString routingKey, final FieldTable arguments, + final AMQShortString exchangeName, final AMQDestination destination, + final boolean nowait) throws AMQException + { /*new FailoverRetrySupport<Object, AMQException>(new FailoverProtectedOperation<Object, AMQException>()*/ new FailoverNoopSupport<Object, AMQException>(new FailoverProtectedOperation<Object, AMQException>() { public Object execute() throws AMQException, FailoverException { - sendQueueBind(queueName, routingKey, arguments, exchangeName, destination); + sendQueueBind(queueName, routingKey, arguments, exchangeName, destination, nowait); return null; } }, _connection).execute(); @@ -588,7 +624,8 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic } public abstract void sendQueueBind(final AMQShortString queueName, final AMQShortString routingKey, final FieldTable arguments, - final AMQShortString exchangeName, AMQDestination destination) throws AMQException, FailoverException; + final AMQShortString exchangeName, AMQDestination destination, + final boolean nowait) throws AMQException, FailoverException; /** * Closes the session. @@ -608,6 +645,11 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic */ public void close(long timeout) throws JMSException { + close(timeout, true); + } + + private void close(long timeout, boolean sendClose) throws JMSException + { if (_logger.isInfoEnabled()) { StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); @@ -618,6 +660,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic // Ensure we only try and close an open session. if (!_closed.getAndSet(true)) { + _closing.set(true); synchronized (getFailoverMutex()) { // We must close down all producers and consumers in an orderly fashion. This is the only method @@ -629,7 +672,16 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic try { - sendClose(timeout); + // If the connection is open or we are in the process + // of closing the connection then send a cance + // no point otherwise as the connection will be gone + if (!_connection.isClosed() || _connection.isClosing()) + { + if (sendClose) + { + sendClose(timeout); + } + } } catch (AMQException e) { @@ -674,31 +726,32 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic if (_dispatcher != null) { // Failover failed and ain't coming back. Knife the dispatcher. - _dispatcher.interrupt(); + _dispatcherThread.interrupt(); } - } + + } + + //if we don't have an exception then we can perform closing operations + _closing.set(e == null); if (!_closed.getAndSet(true)) { - synchronized (getFailoverMutex()) + synchronized (_messageDeliveryLock) { - synchronized (_messageDeliveryLock) + // 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) { - // 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); + amqe = (AMQException) e; } + else + { + amqe = new AMQException("Closing session forcibly", e); + } + + _connection.deregisterSession(_channelId); + closeProducersAndConsumers(amqe); } } } @@ -721,8 +774,16 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic try { + //Check that we are clean to commit. + if (_failedOverDirty) + { + rollback(); + + throw new TransactionRolledBackException("Connection failover has occured since last send. " + + "Forced rollback"); + } + - // TGM FIXME: what about failover? // Acknowledge all delivered messages while (true) { @@ -740,7 +801,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic } catch (AMQException e) { - throw new JMSAMQException("Failed to commit: " + e.getMessage(), e); + throw new JMSAMQException("Failed to commit: " + e.getMessage() + ":" + e.getCause(), e); } catch (FailoverException e) { @@ -832,7 +893,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic { checkValidDestination(destination); - return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, noLocal, false, + return createConsumerImpl(destination, _prefetchHighMark, _prefetchLowMark, noLocal, false, messageSelector, null, true, true); } @@ -840,7 +901,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic { checkValidDestination(destination); - return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, false, (destination instanceof Topic), null, null, + return createConsumerImpl(destination, _prefetchHighMark, _prefetchLowMark, false, (destination instanceof Topic), null, null, false, false); } @@ -848,7 +909,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic { checkValidDestination(destination); - return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, false, true, null, null, + return createConsumerImpl(destination, _prefetchHighMark, _prefetchLowMark, false, true, null, null, false, false); } @@ -856,7 +917,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic { checkValidDestination(destination); - return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, false, (destination instanceof Topic), + return createConsumerImpl(destination, _prefetchHighMark, _prefetchLowMark, false, (destination instanceof Topic), messageSelector, null, false, false); } @@ -865,7 +926,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic { checkValidDestination(destination); - return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, noLocal, (destination instanceof Topic), + return createConsumerImpl(destination, _prefetchHighMark, _prefetchLowMark, noLocal, (destination instanceof Topic), messageSelector, null, false, false); } @@ -874,7 +935,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic { checkValidDestination(destination); - return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, noLocal, true, + return createConsumerImpl(destination, _prefetchHighMark, _prefetchLowMark, noLocal, true, messageSelector, null, false, false); } @@ -917,7 +978,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic throws JMSException { checkNotClosed(); - checkValidTopic(topic); + checkValidTopic(topic, true); if (_subscriptions.containsKey(name)) { _subscriptions.get(name).close(); @@ -1000,6 +1061,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic } catch (URISyntaxException urlse) { + _logger.error("", urlse); JMSException jmse = new JMSException(urlse.getReason()); jmse.setLinkedException(urlse); @@ -1194,7 +1256,28 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic return new TopicSubscriberAdaptor(dest, (C) createExclusiveConsumer(dest, messageSelector, noLocal)); } - public abstract TemporaryQueue createTemporaryQueue() throws JMSException; + public TemporaryQueue createTemporaryQueue() throws JMSException + { + checkNotClosed(); + try + { + AMQTemporaryQueue result = new AMQTemporaryQueue(this); + + // this is done so that we can produce to a temporary queue before we create a consumer + result.setQueueName(result.getRoutingKey()); + createQueue(result.getAMQQueueName(), result.isAutoDelete(), + result.isDurable(), result.isExclusive()); + bindQueue(result.getAMQQueueName(), result.getRoutingKey(), + new FieldTable(), result.getExchangeName(), result); + return result; + } + catch (Exception e) + { + JMSException ex = new JMSException("Cannot create temporary queue"); + ex.setLinkedException(e); + throw ex; + } + } public TemporaryTopic createTemporaryTopic() throws JMSException { @@ -1275,17 +1358,17 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic public int getDefaultPrefetch() { - return _defaultPrefetchHighMark; + return _prefetchHighMark; } public int getDefaultPrefetchHigh() { - return _defaultPrefetchHighMark; + return _prefetchHighMark; } public int getDefaultPrefetchLow() { - return _defaultPrefetchLowMark; + return _prefetchLowMark; } public AMQShortString getDefaultQueueExchangeName() @@ -1430,6 +1513,8 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic sendRecover(); + markClean(); + if (!isSuspended) { suspendChannel(false); @@ -1498,6 +1583,14 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic suspendChannel(true); } + // Let the dispatcher know that all the incomming messages + // should be rolled back(reject/release) + _rollbackMark.set(_highestDeliveryTag.get()); + + syncDispatchQueue(); + + _dispatcher.rollback(); + releaseForRollback(); sendRollback(); @@ -1645,11 +1738,11 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic // if (rawSelector != null) // ft.put("headers", rawSelector.getDataAsBytes()); // rawSelector is used by HeadersExchange and is not a JMS Selector - if (rawSelector != null) + if (rawSelector != null) { ft.addAll(rawSelector); } - + if (messageSelector != null) { ft.put(new AMQShortString("x-filter-jms-selector"), messageSelector); @@ -1682,6 +1775,11 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic } catch (AMQException e) { + if (e instanceof AMQChannelClosedException) + { + close(-1, false); + } + JMSException ex = new JMSException("Error registering consumer: " + e); ex.setLinkedException(e); @@ -1783,6 +1881,63 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic } + void failoverPrep() + { + syncDispatchQueue(); + } + + void syncDispatchQueue() + { + if (Thread.currentThread() == _dispatcherThread) + { + while (!_closed.get() && !_queue.isEmpty()) + { + Dispatchable disp; + try + { + disp = (Dispatchable) _queue.take(); + } + catch (InterruptedException e) + { + throw new RuntimeException(e); + } + + // Check just in case _queue becomes empty, it shouldn't but + // better than an NPE. + if (disp == null) + { + _logger.debug("_queue became empty during sync."); + break; + } + + disp.dispatch(AMQSession.this); + } + } + else + { + startDispatcherIfNecessary(); + + final CountDownLatch signal = new CountDownLatch(1); + + _queue.add(new Dispatchable() + { + public void dispatch(AMQSession ssn) + { + signal.countDown(); + } + }); + + try + { + signal.await(); + } + catch (InterruptedException e) + { + throw new RuntimeException(e); + } + } + } + /** * Resubscribes all producers and consumers. This is called when performing failover. * @@ -1794,6 +1949,8 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic { _failedOverDirty = true; } + + _rollbackMark.set(-1); resubscribeProducers(); resubscribeConsumers(); } @@ -1808,6 +1965,11 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic _inRecovery = inRecovery; } + boolean isStarted() + { + return _startedAtLeastOnce.get(); + } + /** * Starts the session, which ensures that it is not suspended and that its event dispatcher is running. * @@ -1834,7 +1996,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic void startDispatcherIfNecessary() { //If we are the dispatcher then we don't need to check we are started - if (Thread.currentThread() == _dispatcher) + if (Thread.currentThread() == _dispatcherThread) { return; } @@ -1865,9 +2027,23 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic if (_dispatcher == null) { _dispatcher = new Dispatcher(); - _dispatcher.setDaemon(true); + try + { + _dispatcherThread = Threading.getThreadFactory().createThread(_dispatcher); + + } + catch(Exception e) + { + throw new Error("Error creating Dispatcher thread",e); + } + _dispatcherThread.setName("Dispatcher-Channel-" + _channelId); + _dispatcherThread.setDaemon(true); _dispatcher.setConnectionStopped(initiallyStopped); - _dispatcher.start(); + _dispatcherThread.start(); + if (_dispatcherLogger.isInfoEnabled()) + { + _dispatcherLogger.info(_dispatcherThread.getName() + " created"); + } } else { @@ -1967,7 +2143,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic /* * I could have combined the last 3 methods, but this way it improves readability */ - protected AMQTopic checkValidTopic(Topic topic) throws JMSException + protected AMQTopic checkValidTopic(Topic topic, boolean durable) throws JMSException { if (topic == null) { @@ -1980,6 +2156,12 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic "Cannot create a subscription on a temporary topic created in another session"); } + if ((topic instanceof TemporaryDestination) && durable) + { + throw new javax.jms.InvalidDestinationException + ("Cannot create a durable subscription with a temporary topic: " + topic); + } + if (!(topic instanceof AMQTopic)) { throw new javax.jms.InvalidDestinationException( @@ -1990,6 +2172,11 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic return (AMQTopic) topic; } + protected AMQTopic checkValidTopic(Topic topic) throws JMSException + { + return checkValidTopic(topic, false); + } + /** * Called to close message consumers cleanly. This may or may <b>not</b> be as a result of an error. * @@ -2110,7 +2297,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic private P createProducerImpl(Destination destination, boolean mandatory, boolean immediate) throws JMSException { - return createProducerImpl(destination, mandatory, immediate, false); + return createProducerImpl(destination, mandatory, immediate, DEFAULT_WAIT_ON_SEND); } private P createProducerImpl(final Destination destination, final boolean mandatory, @@ -2216,7 +2403,13 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic * @todo Be aware of possible changes to parameter order as versions change. */ protected AMQShortString declareQueue(final AMQDestination amqd, final AMQProtocolHandler protocolHandler, - final boolean noLocal) + final boolean noLocal) throws AMQException + { + return declareQueue(amqd, protocolHandler, noLocal, false); + } + + protected AMQShortString declareQueue(final AMQDestination amqd, final AMQProtocolHandler protocolHandler, + final boolean noLocal, final boolean nowait) throws AMQException { /*return new FailoverRetrySupport<AMQShortString, AMQException>(*/ @@ -2231,14 +2424,15 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic amqd.setQueueName(protocolHandler.generateQueueName()); } - sendQueueDeclare(amqd, protocolHandler); + sendQueueDeclare(amqd, protocolHandler, nowait); return amqd.getAMQQueueName(); } }, _connection).execute(); } - public abstract void sendQueueDeclare(final AMQDestination amqd, final AMQProtocolHandler protocolHandler) throws AMQException, FailoverException; + public abstract void sendQueueDeclare(final AMQDestination amqd, final AMQProtocolHandler protocolHandler, + final boolean nowait) throws AMQException, FailoverException; /** * Undeclares the specified queue. @@ -2351,14 +2545,21 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic AMQProtocolHandler protocolHandler = getProtocolHandler(); - declareExchange(amqd, protocolHandler, false); + if (DECLARE_EXCHANGES) + { + declareExchange(amqd, protocolHandler, nowait); + } - AMQShortString queueName = declareQueue(amqd, protocolHandler, consumer.isNoLocal()); + if (DECLARE_QUEUES || amqd.isNameRequired()) + { + declareQueue(amqd, protocolHandler, consumer.isNoLocal(), nowait); + } + AMQShortString queueName = amqd.getAMQQueueName(); // store the consumer queue name consumer.setQueuename(queueName); - bindQueue(queueName, amqd.getRoutingKey(), consumer.getArguments(), amqd.getExchangeName(), amqd); + bindQueue(queueName, amqd.getRoutingKey(), consumer.getArguments(), amqd.getExchangeName(), amqd, nowait); // If IMMEDIATE_PREFETCH is not required then suspsend the channel to delay prefetch if (!_immediatePrefetch) @@ -2390,11 +2591,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic try { - consumeFromQueue(consumer, queueName, protocolHandler, nowait, consumer.getMessageSelector()); - } - catch (JMSException e) // thrown by getMessageSelector - { - throw new AMQException(null, e.getMessage(), e); + consumeFromQueue(consumer, queueName, protocolHandler, nowait, consumer._messageSelector); } catch (FailoverException e) { @@ -2465,9 +2662,10 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic _consumers.clear(); for (C consumer : consumers) - { - consumer.failedOver(); + { + consumer.failedOverPre(); registerConsumer(consumer, true); + consumer.failedOverPost(); } } @@ -2570,50 +2768,67 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic public void setFlowControl(final boolean active) { _flowControl.setFlowControl(active); + _logger.warn("Broker enforced flow control " + (active ? "no longer in effect" : "has been enforced")); } - public void checkFlowControl() throws InterruptedException + public void checkFlowControl() throws InterruptedException, JMSException { + long expiryTime = 0L; synchronized (_flowControl) { - while (!_flowControl.getFlowControl()) + while (!_flowControl.getFlowControl() && + (expiryTime == 0L ? (expiryTime = System.currentTimeMillis() + FLOW_CONTROL_WAIT_FAILURE) + : expiryTime) >= System.currentTimeMillis() ) { - _flowControl.wait(); + + _flowControl.wait(FLOW_CONTROL_WAIT_PERIOD); + _logger.warn("Message send delayed by " + (System.currentTimeMillis() + FLOW_CONTROL_WAIT_FAILURE - expiryTime)/1000 + "s due to broker enforced flow control"); } + if(!_flowControl.getFlowControl()) + { + _logger.error("Message send failed due to timeout waiting on broker enforced flow control"); + throw new JMSException("Unable to send message for " + FLOW_CONTROL_WAIT_FAILURE/1000 + " seconds due to broker enforced flow control"); + } + } + + } + + public interface Dispatchable + { + void dispatch(AMQSession ssn); + } + + public void dispatch(UnprocessedMessage message) + { + if (_dispatcher == null) + { + throw new java.lang.IllegalStateException("dispatcher is not started"); } + _dispatcher.dispatchMessage(message); } /** Used for debugging in the dispatcher. */ private static final Logger _dispatcherLogger = LoggerFactory.getLogger("org.apache.qpid.client.AMQSession.Dispatcher"); - /** Responsible for decoding a message fragment and passing it to the appropriate message consumer. */ - class Dispatcher extends Thread + class Dispatcher implements Runnable { /** Track the 'stopped' state of the dispatcher, a session starts in the stopped state. */ private final AtomicBoolean _closed = new AtomicBoolean(false); private final Object _lock = new Object(); - private final AtomicLong _rollbackMark = new AtomicLong(-1); private String dispatcherID = "" + System.identityHashCode(this); - - public Dispatcher() { - super("Dispatcher-Channel-" + _channelId); - if (_dispatcherLogger.isInfoEnabled()) - { - _dispatcherLogger.info(getName() + " created"); - } } public void close() { _closed.set(true); - interrupt(); + _dispatcherThread.interrupt(); // fixme awaitTermination @@ -2692,7 +2907,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic { if (_dispatcherLogger.isInfoEnabled()) { - _dispatcherLogger.info(getName() + " started"); + _dispatcherLogger.info(_dispatcherThread.getName() + " started"); } UnprocessedMessage message; @@ -2715,37 +2930,10 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic try { - while (!_closed.get() && ((message = (UnprocessedMessage) _queue.take()) != null)) + Dispatchable disp; + while (!_closed.get() && ((disp = (Dispatchable) _queue.take()) != null)) { - long deliveryTag = message.getDeliveryTag(); - - synchronized (_lock) - { - - while (connectionStopped()) - { - _lock.wait(); - } - - if (!(message instanceof CloseConsumerMessage) - && tagLE(deliveryTag, _rollbackMark.get())) - { - rejectMessage(message, true); - } - else - { - synchronized (_messageDeliveryLock) - { - dispatchMessage(message); - } - } - } - - long current = _rollbackMark.get(); - if (updateRollbackMark(current, deliveryTag)) - { - _rollbackMark.compareAndSet(current, deliveryTag); - } + disp.dispatch(AMQSession.this); } } catch (InterruptedException e) @@ -2755,7 +2943,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic if (_dispatcherLogger.isInfoEnabled()) { - _dispatcherLogger.info(getName() + " thread terminating for channel " + _channelId); + _dispatcherLogger.info(_dispatcherThread.getName() + " thread terminating for channel " + _channelId); } } @@ -2786,11 +2974,47 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic private void dispatchMessage(UnprocessedMessage message) { - //This if block is not needed anymore as bounce messages are handled separately - //if (message.getDeliverBody() != null) - //{ - final C consumer = - _consumers.get(message.getConsumerTag()); + long deliveryTag = message.getDeliveryTag(); + + synchronized (_lock) + { + + try + { + while (connectionStopped()) + { + _lock.wait(); + } + } + catch (InterruptedException e) + { + // pass + } + + if (!(message instanceof CloseConsumerMessage) + && tagLE(deliveryTag, _rollbackMark.get())) + { + rejectMessage(message, true); + } + else + { + synchronized (_messageDeliveryLock) + { + notifyConsumer(message); + } + } + } + + long current = _rollbackMark.get(); + if (updateRollbackMark(current, deliveryTag)) + { + _rollbackMark.compareAndSet(current, deliveryTag); + } + } + + private void notifyConsumer(UnprocessedMessage message) + { + final C consumer = _consumers.get(message.getConsumerTag()); if ((consumer == null) || consumer.isClosed()) { @@ -2798,7 +3022,8 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic { if (consumer == null) { - _dispatcherLogger.info("Dispatcher(" + dispatcherID + ")Received a message(" + System.identityHashCode(message) + ")" + "[" + _dispatcherLogger.info("Dispatcher(" + dispatcherID + ")Received a message(" + + System.identityHashCode(message) + ")" + "[" + message.getDeliveryTag() + "] from queue " + message.getConsumerTag() + " )without a handler - rejecting(requeue)..."); } @@ -2806,7 +3031,8 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic { if (consumer.isNoConsume()) { - _dispatcherLogger.info("Received a message(" + System.identityHashCode(message) + ")" + "[" + _dispatcherLogger.info("Received a message(" + + System.identityHashCode(message) + ")" + "[" + message.getDeliveryTag() + "] from queue " + " consumer(" + message.getConsumerTag() + ") is closed and a browser so dropping..."); //DROP MESSAGE @@ -2815,7 +3041,8 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic } else { - _dispatcherLogger.info("Received a message(" + System.identityHashCode(message) + ")" + "[" + _dispatcherLogger.info("Received a message(" + + System.identityHashCode(message) + ")" + "[" + message.getDeliveryTag() + "] from queue " + " consumer(" + message.getConsumerTag() + ") is closed rejecting(requeue)..."); } @@ -2831,7 +3058,6 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic { consumer.notifyMessage(message); } - } } @@ -2889,4 +3115,27 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic } } } + + /** + * Checks if the Session and its parent connection are closed + * + * @return <tt>true</tt> if this is closed, <tt>false</tt> otherwise. + */ + @Override + public boolean isClosed() + { + return _closed.get() || _connection.isClosed(); + } + + /** + * Checks if the Session and its parent connection are capable of performing + * closing operations + * + * @return <tt>true</tt> if we are closing, <tt>false</tt> otherwise. + */ + @Override + public boolean isClosing() + { + return _closing.get()|| _connection.isClosing(); + } } 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 aa0ff66545..2324d441cc 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 @@ -28,31 +28,44 @@ import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.client.message.MessageFactoryRegistry; import org.apache.qpid.client.message.FiledTableSupport; import org.apache.qpid.client.message.AMQMessageDelegateFactory; +import org.apache.qpid.client.message.UnprocessedMessage_0_10; import org.apache.qpid.util.Serial; -import org.apache.qpid.nclient.Session; -import org.apache.qpid.nclient.util.MessagePartListenerAdapter; -import org.apache.qpid.ErrorCode; -import org.apache.qpid.QpidException; +import org.apache.qpid.transport.ExecutionException; +import org.apache.qpid.transport.MessageAcceptMode; +import org.apache.qpid.transport.MessageAcquireMode; import org.apache.qpid.transport.MessageCreditUnit; import org.apache.qpid.transport.MessageFlowMode; +import org.apache.qpid.transport.MessageTransfer; import org.apache.qpid.transport.RangeSet; import org.apache.qpid.transport.Option; import org.apache.qpid.transport.ExchangeBoundResult; import org.apache.qpid.transport.Future; +import org.apache.qpid.transport.Range; +import org.apache.qpid.transport.Session; +import org.apache.qpid.transport.SessionException; +import org.apache.qpid.transport.SessionListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.apache.qpid.transport.Option.*; + import javax.jms.*; import javax.jms.IllegalStateException; +import java.lang.ref.WeakReference; + +import java.util.Date; import java.util.HashMap; import java.util.UUID; import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; /** * This is a 0.10 Session */ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, BasicMessageProducer_0_10> + implements SessionListener { /** @@ -60,6 +73,36 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic */ private static final Logger _logger = LoggerFactory.getLogger(AMQSession_0_10.class); + private static Timer timer = new Timer("ack-flusher", true); + private static class Flusher extends TimerTask + { + + private WeakReference<AMQSession_0_10> session; + public Flusher(AMQSession_0_10 session) + { + this.session = new WeakReference<AMQSession_0_10>(session); + } + + public void run() { + AMQSession_0_10 ssn = session.get(); + if (ssn == null) + { + cancel(); + } + else + { + try + { + ssn.flushAcknowledgments(true); + } + catch (Throwable t) + { + _logger.error("error flushing acks", t); + } + } + } + } + /** * The underlying QpidSession @@ -70,11 +113,13 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic * The latest qpid Exception that has been reaised. */ private Object _currentExceptionLock = new Object(); - private QpidException _currentException; + private SessionException _currentException; // a ref on the qpid connection - protected org.apache.qpid.nclient.Connection _qpidConnection; + protected org.apache.qpid.transport.Connection _qpidConnection; + private long maxAckDelay = Long.getLong("qpid.session.max_ack_delay", 1000); + private TimerTask flushTask = null; private RangeSet unacked = new RangeSet(); private int unackedCount = 0; @@ -97,7 +142,7 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic * @param defaultPrefetchLowMark The number of prefetched messages at which to resume the session. * @param qpidConnection The qpid connection */ - AMQSession_0_10(org.apache.qpid.nclient.Connection qpidConnection, AMQConnection con, int channelId, + AMQSession_0_10(org.apache.qpid.transport.Connection qpidConnection, AMQConnection con, int channelId, boolean transacted, int acknowledgeMode, MessageFactoryRegistry messageFactoryRegistry, int defaultPrefetchHighMark, int defaultPrefetchLowMark) { @@ -105,15 +150,18 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic super(con, channelId, transacted, acknowledgeMode, messageFactoryRegistry, defaultPrefetchHighMark, defaultPrefetchLowMark); _qpidConnection = qpidConnection; - // create the qpid session with an expiry <= 0 so that the session does not expire - _qpidSession = qpidConnection.createSession(0); - // set the exception listnere for this session - _qpidSession.setClosedListener(new QpidSessionExceptionListener()); - // set transacted if required + _qpidSession = _qpidConnection.createSession(1); + _qpidSession.setSessionListener(this); if (_transacted) { _qpidSession.txSelect(); } + + if (maxAckDelay > 0) + { + flushTask = new Flusher(this); + timer.schedule(flushTask, new Date(), maxAckDelay); + } } /** @@ -127,7 +175,7 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic * @param defaultPrefetchLow The number of prefetched messages at which to resume the session. * @param qpidConnection The connection */ - AMQSession_0_10(org.apache.qpid.nclient.Connection qpidConnection, AMQConnection con, int channelId, + AMQSession_0_10(org.apache.qpid.transport.Connection qpidConnection, AMQConnection con, int channelId, boolean transacted, int acknowledgeMode, int defaultPrefetchHigh, int defaultPrefetchLow) { @@ -137,18 +185,30 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic private void addUnacked(int id) { - unacked.add(id); - unackedCount++; + synchronized (unacked) + { + unacked.add(id); + unackedCount++; + } } private void clearUnacked() { - unacked.clear(); - unackedCount = 0; + synchronized (unacked) + { + unacked.clear(); + unackedCount = 0; + } } //------- overwritten methods of class AMQSession + void failoverPrep() + { + super.failoverPrep(); + clearUnacked(); + } + /** * Acknowledge one or many messages. * @@ -185,7 +245,7 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic long prefetch = getAMQConnection().getMaxPrefetch(); - if (unackedCount >= prefetch/2) + if (unackedCount >= prefetch/2 || maxAckDelay <= 0) { flushAcknowledgments(); } @@ -193,11 +253,38 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic void flushAcknowledgments() { - if (unackedCount > 0) + flushAcknowledgments(false); + } + + void flushAcknowledgments(boolean setSyncBit) + { + synchronized (unacked) + { + if (unackedCount > 0) + { + messageAcknowledge + (unacked, _acknowledgeMode != org.apache.qpid.jms.Session.NO_ACKNOWLEDGE,setSyncBit); + clearUnacked(); + } + } + } + + void messageAcknowledge(RangeSet ranges, boolean accept) + { + messageAcknowledge(ranges,accept,false); + } + + void messageAcknowledge(RangeSet ranges, boolean accept,boolean setSyncBit) + { + Session ssn = getQpidSession(); + for (Range range : ranges) + { + ssn.processed(range); + } + ssn.flushProcessed(accept ? BATCH : NONE); + if (accept) { - getQpidSession().messageAcknowledge - (unacked, _acknowledgeMode != org.apache.qpid.jms.Session.NO_ACKNOWLEDGE); - clearUnacked(); + ssn.messageAccept(ranges, UNRELIABLE,setSyncBit? SYNC : NONE); } } @@ -212,7 +299,8 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic * @param arguments 0_8 specific */ public void sendQueueBind(final AMQShortString queueName, final AMQShortString routingKey, - final FieldTable arguments, final AMQShortString exchangeName, final AMQDestination destination) + final FieldTable arguments, final AMQShortString exchangeName, + final AMQDestination destination, final boolean nowait) throws AMQException, FailoverException { Map args = FiledTableSupport.convertToMap(arguments); @@ -227,9 +315,12 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic _logger.debug("Binding queue : " + queueName.toString() + " exchange: " + exchangeName.toString() + " using binding key " + rk.asString()); getQpidSession().exchangeBind(queueName.toString(), exchangeName.toString(), rk.toString(), args); } - // We need to sync so that we get notify of an error. - getQpidSession().sync(); - getCurrentException(); + if (!nowait) + { + // We need to sync so that we get notify of an error. + getQpidSession().sync(); + getCurrentException(); + } } @@ -242,6 +333,11 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic */ public void sendClose(long timeout) throws AMQException, FailoverException { + if (flushTask != null) + { + flushTask.cancel(); + flushTask = null; + } flushAcknowledgments(); getQpidSession().sync(); getQpidSession().close(); @@ -318,10 +414,6 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic public void releaseForRollback() { - if (_dispatcher != null) - { - _dispatcher.rollback(); - } getQpidSession().messageRelease(_txRangeSet, Option.SET_REDELIVERED); _txRangeSet.clear(); _txSize = 0; @@ -376,7 +468,7 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic public boolean isQueueBound(final AMQShortString exchangeName, final AMQShortString queueName, final AMQShortString routingKey,AMQShortString[] bindingKeys) throws JMSException { - String rk = ""; + String rk = null; boolean res; if (bindingKeys != null && bindingKeys.length>0) { @@ -416,11 +508,11 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic preAcquire = ( ! consumer.isNoConsume() && (consumer.getMessageSelector() == null || consumer.getMessageSelector().equals("")) ) || !(consumer.getDestination() instanceof AMQQueue); - getQpidSession().messageSubscribe(queueName.toString(), String.valueOf(tag), - 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, - (BasicMessageConsumer_0_10) consumer, null, - consumer.isExclusive() ? Option.EXCLUSIVE : Option.NONE); + getQpidSession().messageSubscribe + (queueName.toString(), String.valueOf(tag), + getAcknowledgeMode() == NO_ACKNOWLEDGE ? MessageAcceptMode.NONE : MessageAcceptMode.EXPLICIT, + preAcquire ? MessageAcquireMode.PRE_ACQUIRED : MessageAcquireMode.NOT_ACQUIRED, null, 0, null, + consumer.isExclusive() ? Option.EXCLUSIVE : Option.NONE); } catch (JMSException e) { @@ -437,18 +529,24 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic { getQpidSession().messageSetFlowMode(consumerTag, MessageFlowMode.WINDOW); } - getQpidSession().messageFlow(consumerTag, MessageCreditUnit.BYTE, 0xFFFFFFFF); + getQpidSession().messageFlow(consumerTag, MessageCreditUnit.BYTE, 0xFFFFFFFF, + Option.UNRELIABLE); // We need to sync so that we get notify of an error. // only if not immediat prefetch - if(prefetch() && (consumer.isStrated() || _immediatePrefetch)) + if(prefetch() && (isStarted() || _immediatePrefetch)) { // set the flow getQpidSession().messageFlow(consumerTag, MessageCreditUnit.MESSAGE, - getAMQConnection().getMaxPrefetch()); + getAMQConnection().getMaxPrefetch(), + Option.UNRELIABLE); + } + + if (!nowait) + { + getQpidSession().sync(); + getCurrentException(); } - getQpidSession().sync(); - getCurrentException(); } /** @@ -470,19 +568,24 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic final AMQProtocolHandler protocolHandler, final boolean nowait) throws AMQException, FailoverException { - getQpidSession().exchangeDeclare(name.toString(), type.toString(), null, null); - // autoDelete --> false - // durable --> false - // passive -- false + getQpidSession().exchangeDeclare(name.toString(), + type.toString(), + null, + null, + name.toString().startsWith("amq.")? Option.PASSIVE:Option.NONE); // We need to sync so that we get notify of an error. - getQpidSession().sync(); - getCurrentException(); + if (!nowait) + { + getQpidSession().sync(); + getCurrentException(); + } } /** * Declare a queue with the given queueName */ - public void sendQueueDeclare(final AMQDestination amqd, final AMQProtocolHandler protocolHandler) + public void sendQueueDeclare(final AMQDestination amqd, final AMQProtocolHandler protocolHandler, + final boolean nowait) throws AMQException, FailoverException { // do nothing this is only used by 0_8 @@ -492,7 +595,7 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic * Declare a queue with the given queueName */ public AMQShortString send0_10QueueDeclare(final AMQDestination amqd, final AMQProtocolHandler protocolHandler, - final boolean noLocal) + final boolean noLocal, final boolean nowait) throws AMQException, FailoverException { AMQShortString res; @@ -516,9 +619,12 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic amqd.isDurable() ? Option.DURABLE : Option.NONE, !amqd.isDurable() && amqd.isExclusive() ? Option.EXCLUSIVE : Option.NONE); // passive --> false - // We need to sync so that we get notify of an error. - getQpidSession().sync(); - getCurrentException(); + if (!nowait) + { + // We need to sync so that we get notify of an error. + getQpidSession().sync(); + getCurrentException(); + } return res; } @@ -544,7 +650,8 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic { for (BasicMessageConsumer consumer : _consumers.values()) { - getQpidSession().messageStop(String.valueOf(consumer.getConsumerTag())); + getQpidSession().messageStop(String.valueOf(consumer.getConsumerTag()), + Option.UNRELIABLE); } } else @@ -560,17 +667,20 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic if (consumer.getMessageListener() != null) { getQpidSession().messageFlow(consumerTag, - MessageCreditUnit.MESSAGE, 1); + MessageCreditUnit.MESSAGE, 1, + Option.UNRELIABLE); } } else { getQpidSession() .messageFlow(consumerTag, MessageCreditUnit.MESSAGE, - getAMQConnection().getMaxPrefetch()); + getAMQConnection().getMaxPrefetch(), + Option.UNRELIABLE); } getQpidSession() - .messageFlow(consumerTag, MessageCreditUnit.BYTE, 0xFFFFFFFF); + .messageFlow(consumerTag, MessageCreditUnit.BYTE, 0xFFFFFFFF, + Option.UNRELIABLE); } catch (Exception e) { @@ -598,7 +708,7 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic * * @return The associated Qpid Session. */ - protected org.apache.qpid.nclient.Session getQpidSession() + protected Session getQpidSession() { return _qpidSession; } @@ -615,55 +725,56 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic { if (_currentException != null) { - QpidException toBeThrown = _currentException; + SessionException se = _currentException; _currentException = null; - throw new AMQException(AMQConstant.getConstant(toBeThrown.getErrorCode().getCode()), - toBeThrown.getMessage(), toBeThrown); + ExecutionException ee = se.getException(); + int code; + if (ee == null) + { + code = 0; + } + else + { + code = ee.getErrorCode().getValue(); + } + throw new AMQException + (AMQConstant.getConstant(code), se.getMessage(), se); } } } + public void opened(Session ssn) {} - public TemporaryQueue createTemporaryQueue() throws JMSException + public void resumed(Session ssn) { - checkNotClosed(); - AMQTemporaryQueue result = new AMQTemporaryQueue(this); + _qpidConnection = ssn.getConnection(); try { - // this is done so that we can produce to a temporary queue beofre we create a consumer - sendCreateQueue(result.getRoutingKey(), result.isAutoDelete(), result.isDurable(), result.isExclusive(),null); - sendQueueBind(result.getRoutingKey(), result.getRoutingKey(), new FieldTable(), result.getExchangeName(),result); - result.setQueueName(result.getRoutingKey()); + resubscribe(); } - catch (Exception e) + catch (AMQException e) { - throw new JMSException("Cannot create temporary queue" ); + throw new RuntimeException(e); } - return result; } + public void message(Session ssn, MessageTransfer xfr) + { + messageReceived(new UnprocessedMessage_0_10(xfr)); + } - - - //------ Inner classes - /** - * Lstener for qpid protocol exceptions - */ - private class QpidSessionExceptionListener implements org.apache.qpid.nclient.ClosedListener + public void exception(Session ssn, SessionException exc) { - public void onClosed(ErrorCode errorCode, String reason, Throwable t) + synchronized (_currentExceptionLock) { - synchronized (_currentExceptionLock) - { - // todo check the error code for finding out if we need to notify the - // JMS connection exception listener - _currentException = new QpidException(reason, errorCode, t); - } + _currentException = exc; } } + public void closed(Session ssn) {} + protected AMQShortString declareQueue(final AMQDestination amqd, final AMQProtocolHandler protocolHandler, - final boolean noLocal) + final boolean noLocal, final boolean nowait) throws AMQException { /*return new FailoverRetrySupport<AMQShortString, AMQException>(*/ @@ -678,44 +789,21 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic String binddingKey = ""; for(AMQShortString key : amqd.getBindingKeys()) { - binddingKey = binddingKey + "_" + key.toString(); + binddingKey = binddingKey + "_" + key.toString(); } amqd.setQueueName(new AMQShortString( binddingKey + "@" + amqd.getExchangeName().toString() + "_" + UUID.randomUUID())); } - return send0_10QueueDeclare(amqd, protocolHandler, noLocal); + return send0_10QueueDeclare(amqd, protocolHandler, noLocal, nowait); } }, _connection).execute(); } - - void start() throws AMQException - { - super.start(); - for(BasicMessageConsumer c: _consumers.values()) - { - c.start(); - } - } - - - void stop() throws AMQException - { - super.stop(); - for(BasicMessageConsumer c: _consumers.values()) - { - c.stop(); - } - } - - - - public TopicSubscriber createDurableSubscriber(Topic topic, String name) throws JMSException { checkNotClosed(); - AMQTopic origTopic=checkValidTopic(topic); + AMQTopic origTopic=checkValidTopic(topic, true); AMQTopic dest=AMQTopic.createDurable010Topic(origTopic, name, _connection); TopicSubscriberAdaptor<BasicMessageConsumer_0_10> subscriber=_subscriptions.get(name); @@ -786,19 +874,19 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic /** * Store non committed messages for this session * With 0.10 messages are consumed with window mode, we must send a completion - * before the window size is reached so credits don't dry up. + * before the window size is reached so credits don't dry up. * @param id */ @Override protected void addDeliveredMessage(long id) { _txRangeSet.add((int) id); _txSize++; - // this is a heuristic, we may want to have that configurable + // this is a heuristic, we may want to have that configurable if (_connection.getMaxPrefetch() == 1 || _connection.getMaxPrefetch() != 0 && _txSize % (_connection.getMaxPrefetch() / 2) == 0) { // send completed so consumer credits don't dry up - getQpidSession().messageAcknowledge(_txRangeSet, false); + messageAcknowledge(_txRangeSet, false); } } @@ -809,7 +897,7 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic { if( _txSize > 0 ) { - getQpidSession().messageAcknowledge(_txRangeSet, true); + messageAcknowledge(_txRangeSet, true); _txRangeSet.clear(); _txSize = 0; } 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 2442b157f1..862e23385a 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 @@ -33,8 +33,10 @@ import org.apache.qpid.client.message.*; import org.apache.qpid.client.message.AMQMessageDelegateFactory; import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.client.state.listener.SpecificMethodFrameListener; +import org.apache.qpid.client.state.AMQState; import org.apache.qpid.common.AMQPFilterTypes; import org.apache.qpid.framing.*; +import org.apache.qpid.framing.amqp_0_91.MethodRegistry_0_91; import org.apache.qpid.framing.amqp_0_9.MethodRegistry_0_9; import org.apache.qpid.jms.Session; import org.apache.qpid.protocol.AMQConstant; @@ -102,10 +104,12 @@ public final class AMQSession_0_8 extends AMQSession<BasicMessageConsumer_0_8, B } getProtocolHandler().writeFrame(ackFrame); + _unacknowledgedMessageTags.remove(deliveryTag); } public void sendQueueBind(final AMQShortString queueName, final AMQShortString routingKey, final FieldTable arguments, - final AMQShortString exchangeName, final AMQDestination dest) throws AMQException, FailoverException + final AMQShortString exchangeName, final AMQDestination dest, + final boolean nowait) throws AMQException, FailoverException { getProtocolHandler().syncWrite(getProtocolHandler().getMethodRegistry().createQueueBindBody (getTicket(),queueName,exchangeName,routingKey,false,arguments). @@ -114,12 +118,23 @@ public final class AMQSession_0_8 extends AMQSession<BasicMessageConsumer_0_8, B public void sendClose(long timeout) throws AMQException, FailoverException { - getProtocolHandler().closeSession(this); - 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. + // we also need to check the state manager for 08/09 as the + // _connection variable may not be updated in time by the error receiving + // thread. + // We can't close the session if we are alreadying in the process of + // closing/closed the connection. + + if (!(getProtocolHandler().getStateManager().getCurrentState().equals(AMQState.CONNECTION_CLOSED) + || getProtocolHandler().getStateManager().getCurrentState().equals(AMQState.CONNECTION_CLOSING))) + { + + getProtocolHandler().closeSession(this); + 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. + } } public void sendCommit() throws AMQException, FailoverException @@ -172,6 +187,11 @@ public final class AMQSession_0_8 extends AMQSession<BasicMessageConsumer_0_8, B BasicRecoverSyncBody body = ((MethodRegistry_0_9)getMethodRegistry()).createBasicRecoverSyncBody(false); _connection.getProtocolHandler().syncWrite(body.generateFrame(_channelId), BasicRecoverSyncOkBody.class); } + else if(getProtocolVersion().equals(ProtocolVersion.v0_91)) + { + BasicRecoverSyncBody body = ((MethodRegistry_0_91)getMethodRegistry()).createBasicRecoverSyncBody(false); + _connection.getProtocolHandler().syncWrite(body.generateFrame(_channelId), BasicRecoverSyncOkBody.class); + } else { throw new RuntimeException("Unsupported version of the AMQP Protocol: " + getProtocolVersion()); @@ -181,6 +201,12 @@ public final class AMQSession_0_8 extends AMQSession<BasicMessageConsumer_0_8, B public void releaseForRollback() { + // Reject all the messages that have been received in this session and + // have not yet been acknowledged. Should look to remove + // _deliveredMessageTags and use _txRangeSet as used by 0-10. + // Otherwise messages will be able to arrive out of order to a second + // consumer on the queue. Whilst this is within the JMS spec it is not + // user friendly and avoidable. while (true) { Long tag = _deliveredMessageTags.poll(); @@ -191,11 +217,6 @@ public final class AMQSession_0_8 extends AMQSession<BasicMessageConsumer_0_8, B rejectMessage(tag, true); } - - if (_dispatcher != null) - { - _dispatcher.rollback(); - } } public void rejectMessage(long deliveryTag, boolean requeue) @@ -297,13 +318,16 @@ public final class AMQSession_0_8 extends AMQSession<BasicMessageConsumer_0_8, B public void sendExchangeDeclare(final AMQShortString name, final AMQShortString type, final AMQProtocolHandler protocolHandler, final boolean nowait) throws AMQException, FailoverException { - ExchangeDeclareBody body = getMethodRegistry().createExchangeDeclareBody(getTicket(),name,type,false,false,false,false,nowait,null); + ExchangeDeclareBody body = getMethodRegistry().createExchangeDeclareBody(getTicket(),name,type, + name.toString().startsWith("amq."), + false,false,false,false,null); AMQFrame exchangeDeclare = body.generateFrame(_channelId); protocolHandler.syncWrite(exchangeDeclare, ExchangeDeclareOkBody.class); } - public void sendQueueDeclare(final AMQDestination amqd, final AMQProtocolHandler protocolHandler) throws AMQException, FailoverException + public void sendQueueDeclare(final AMQDestination amqd, final AMQProtocolHandler protocolHandler, + final boolean nowait) throws AMQException, FailoverException { QueueDeclareBody body = getMethodRegistry().createQueueDeclareBody(getTicket(),amqd.getAMQQueueName(),false,amqd.isDurable(),amqd.isExclusive(),amqd.isAutoDelete(),false,null); @@ -418,18 +442,11 @@ public final class AMQSession_0_8 extends AMQSession<BasicMessageConsumer_0_8, B getProtocolHandler().syncWrite(frame, TxRollbackOkBody.class); } - public TemporaryQueue createTemporaryQueue() throws JMSException - { - checkNotClosed(); - - return new AMQTemporaryQueue(this); - } - public TopicSubscriber createDurableSubscriber(Topic topic, String name) throws JMSException { checkNotClosed(); - AMQTopic origTopic = checkValidTopic(topic); + AMQTopic origTopic = checkValidTopic(topic, true); AMQTopic dest = AMQTopic.createDurableTopic(origTopic, name, _connection); TopicSubscriberAdaptor<BasicMessageConsumer_0_8> subscriber = _subscriptions.get(name); if (subscriber != null) @@ -493,7 +510,7 @@ public final class AMQSession_0_8 extends AMQSession<BasicMessageConsumer_0_8, B - public void setPrefecthLimits(final int messagePrefetch, final long sizePrefetch) throws AMQException + public void setPrefetchLimits(final int messagePrefetch, final long sizePrefetch) throws AMQException { new FailoverRetrySupport<Object, AMQException>( new FailoverProtectedOperation<Object, AMQException>() diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQTopicSessionAdaptor.java b/java/client/src/main/java/org/apache/qpid/client/AMQTopicSessionAdaptor.java index f44f8414fa..ec482a8f79 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQTopicSessionAdaptor.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQTopicSessionAdaptor.java @@ -115,7 +115,7 @@ public class AMQTopicSessionAdaptor implements TopicSession, AMQSessionAdapter public ObjectMessage createObjectMessage(Serializable serializable) throws JMSException { - return _session.createObjectMessage(); + return _session.createObjectMessage(serializable); } public StreamMessage createStreamMessage() throws JMSException 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 dfd228370c..bea43cc232 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 @@ -40,8 +40,9 @@ 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.BlockingQueue; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; @@ -78,7 +79,7 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa * Used in the blocking receive methods to receive a message from the Session thread. <p/> Or to notify of errors * <p/> Argument true indicates we want strict FIFO semantics */ - protected final ArrayBlockingQueue _synchronousQueue; + protected final BlockingQueue _synchronousQueue; protected final MessageFactoryRegistry _messageFactory; @@ -182,7 +183,7 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa _prefetchLow = prefetchLow; _exclusive = exclusive; - _synchronousQueue = new ArrayBlockingQueue(prefetchHigh, true); + _synchronousQueue = new LinkedBlockingQueue(); _autoClose = autoClose; _noConsume = noConsume; @@ -440,7 +441,9 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa o = _synchronousQueue.take(); } return o; - } + } + + abstract Message receiveBrowse() throws JMSException; public Message receiveNoWait() throws JMSException { @@ -540,6 +543,7 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa if (!_closed.getAndSet(true)) { + _closing.set(true); if (_logger.isDebugEnabled()) { StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); @@ -560,7 +564,13 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa { try { - sendCancel(); + // If the session is open or we are in the process + // of closing the session then send a cance + // no point otherwise as the connection will be gone + if (!_session.isClosed() || _session.isClosing()) + { + sendCancel(); + } } catch (AMQException e) { @@ -769,15 +779,16 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa else { _session.addDeliveredMessage(msg.getDeliveryTag()); + _session.markDirty(); } break; } + } void postDeliver(AbstractJMSMessage msg) throws JMSException { - msg.setJMSDestination(_destination); switch (_acknowledgeMode) { @@ -1036,23 +1047,6 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa _synchronousQueue.clear(); } - public void start() - { - // do nothing as this is a 0_10 feature - } - - - public void stop() - { - // do nothing as this is a 0_10 feature - } - - public boolean isStrated() - { - // do nothing as this is a 0_10 feature - return false; - } - public AMQShortString getQueuename() { return _queuename; @@ -1069,10 +1063,13 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa } /** to be called when a failover has occured */ - public void failedOver() + public void failedOverPre() { clearReceiveQueue(); // TGM FIXME: think this should just be removed // clearUnackedMessages(); } + + public void failedOverPost() {} + } 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 2a37298a43..1b2d6876bd 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 @@ -31,6 +31,7 @@ import org.apache.qpid.filter.JMSSelectorFilter; import javax.jms.InvalidSelectorException; import javax.jms.JMSException; +import javax.jms.Message; import javax.jms.MessageListener; import java.util.Iterator; import java.util.concurrent.atomic.AtomicBoolean; @@ -39,7 +40,6 @@ import java.util.concurrent.atomic.AtomicBoolean; * This is a 0.10 message consumer. */ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedMessage_0_10> - implements org.apache.qpid.nclient.MessagePartListener { /** @@ -114,9 +114,6 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM return _consumerTagString; } - - // ----- Interface org.apache.qpid.client.util.MessageListener - /** * * This is invoked by the session thread when emptying the session message queue. @@ -152,35 +149,14 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM if (isMessageListenerSet() && ! getSession().prefetch()) { _0_10session.getQpidSession().messageFlow(getConsumerTagString(), - MessageCreditUnit.MESSAGE, 1); + MessageCreditUnit.MESSAGE, 1, + Option.UNRELIABLE); } _logger.debug("messageOk, trying to notify"); super.notifyMessage(jmsMessage); } } - - - /** - * This method is invoked by the transport layer when a message is delivered for this - * consumer. The message is transformed and pass to the session. - * @param xfr an 0.10 message transfer - */ - public void messageTransfer(MessageTransfer xfr) - - //public void onMessage(Message message) - { - int channelId = getSession().getChannelId(); - int consumerTag = getConsumerTag(); - - UnprocessedMessage_0_10 newMessage = - new UnprocessedMessage_0_10(consumerTag, xfr); - - - getSession().messageReceived(newMessage); - // else ignore this message - } - //----- overwritten methods /** @@ -272,7 +248,8 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM if(! getSession().prefetch()) { _0_10session.getQpidSession().messageFlow(getConsumerTagString(), - MessageCreditUnit.MESSAGE, 1); + MessageCreditUnit.MESSAGE, 1, + Option.UNRELIABLE); } } // now we need to acquire this message if needed @@ -284,9 +261,7 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM _logger.debug("filterMessage - trying to acquire message"); } messageOk = acquireMessage(message); - _logger.debug("filterMessage - *************************************"); _logger.debug("filterMessage - message acquire status : " + messageOk); - _logger.debug("filterMessage - *************************************"); } return messageOk; } @@ -304,8 +279,9 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM { RangeSet ranges = new RangeSet(); ranges.add((int) message.getDeliveryTag()); - _0_10session.getQpidSession().messageAcknowledge(ranges, - _acknowledgeMode != org.apache.qpid.jms.Session.NO_ACKNOWLEDGE ); + _0_10session.messageAcknowledge + (ranges, + _acknowledgeMode != org.apache.qpid.jms.Session.NO_ACKNOWLEDGE); _0_10session.getCurrentException(); } } @@ -360,7 +336,8 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM if (messageListener != null && ! getSession().prefetch()) { _0_10session.getQpidSession().messageFlow(getConsumerTagString(), - MessageCreditUnit.MESSAGE, 1); + MessageCreditUnit.MESSAGE, 1, + Option.UNRELIABLE); } if (messageListener != null && !_synchronousQueue.isEmpty()) { @@ -374,26 +351,16 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM } } - public boolean isStrated() + public void failedOverPost() { - return _isStarted; - } - - public void start() - { - _isStarted = true; - if (_syncReceive.get()) + if (_0_10session.isStarted() && _syncReceive.get()) { - _0_10session.getQpidSession().messageFlow(getConsumerTagString(), - MessageCreditUnit.MESSAGE, 1); + _0_10session.getQpidSession().messageFlow + (getConsumerTagString(), MessageCreditUnit.MESSAGE, 1, + Option.UNRELIABLE); } } - public void stop() - { - _isStarted = false; - } - /** * When messages are not prefetched we need to request a message from the * broker. @@ -405,16 +372,35 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM */ public Object getMessageFromQueue(long l) throws InterruptedException { - if (isStrated() && ! getSession().prefetch() && _synchronousQueue.isEmpty()) - { - _0_10session.getQpidSession().messageFlow(getConsumerTagString(), - MessageCreditUnit.MESSAGE, 1); - } if (! getSession().prefetch()) { _syncReceive.set(true); } + if (_0_10session.isStarted() && ! getSession().prefetch() && _synchronousQueue.isEmpty()) + { + _0_10session.getQpidSession().messageFlow(getConsumerTagString(), + MessageCreditUnit.MESSAGE, 1, + Option.UNRELIABLE); + } Object o = super.getMessageFromQueue(l); + if (o == null && _0_10session.isStarted()) + { + _0_10session.getQpidSession().messageFlush + (getConsumerTagString(), Option.UNRELIABLE, Option.SYNC); + _0_10session.getQpidSession().sync(); + _0_10session.getQpidSession().messageFlow + (getConsumerTagString(), MessageCreditUnit.BYTE, + 0xFFFFFFFF, Option.UNRELIABLE); + if (getSession().prefetch()) + { + _0_10session.getQpidSession().messageFlow + (getConsumerTagString(), MessageCreditUnit.MESSAGE, + _0_10session.getAMQConnection().getMaxPrefetch(), + Option.UNRELIABLE); + } + _0_10session.syncDispatchQueue(); + o = super.getMessageFromQueue(-1); + } if (! getSession().prefetch()) { _syncReceive.set(false); @@ -425,10 +411,50 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM void postDeliver(AbstractJMSMessage msg) throws JMSException { super.postDeliver(msg); - if(_acknowledgeMode == org.apache.qpid.jms.Session.NO_ACKNOWLEDGE && !_session.isInRecovery()) + if (_acknowledgeMode == org.apache.qpid.jms.Session.NO_ACKNOWLEDGE && !_session.isInRecovery()) + { + _session.acknowledgeMessage(msg.getDeliveryTag(), false); + } + + if (_acknowledgeMode == org.apache.qpid.jms.Session.AUTO_ACKNOWLEDGE && + !_session.isInRecovery() && + _session.getAMQConnection().getSyncAck()) { - _session.acknowledgeMessage(msg.getDeliveryTag(), false); - } + ((AMQSession_0_10) getSession()).flushAcknowledgments(); + ((AMQSession_0_10) getSession()).getQpidSession().sync(); + } + } + + Message receiveBrowse() throws JMSException + { + return receiveNoWait(); } + @Override public void rollbackPendingMessages() + { + if (_synchronousQueue.size() > 0) + { + RangeSet ranges = new RangeSet(); + Iterator iterator = _synchronousQueue.iterator(); + while (iterator.hasNext()) + { + + Object o = iterator.next(); + if (o instanceof AbstractJMSMessage) + { + ranges.add((int) ((AbstractJMSMessage) o).getDeliveryTag()); + iterator.remove(); + } + else + { + _logger.error("Queue contained a :" + o.getClass() + + " unable to reject as it is not an AbstractJMSMessage. Will be cleared"); + iterator.remove(); + } + } + + _0_10session.getQpidSession().messageRelease(ranges, Option.SET_REDELIVERED); + clearReceiveQueue(); + } + } } 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 494a8fb43d..308f04f082 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 @@ -22,6 +22,7 @@ package org.apache.qpid.client; import javax.jms.InvalidSelectorException; import javax.jms.JMSException; +import javax.jms.Message; import org.apache.qpid.AMQException; import org.apache.qpid.QpidException; @@ -38,9 +39,9 @@ public class BasicMessageConsumer_0_8 extends BasicMessageConsumer<UnprocessedMe protected final Logger _logger = LoggerFactory.getLogger(getClass()); protected BasicMessageConsumer_0_8(int channelId, AMQConnection connection, AMQDestination destination, - String messageSelector, boolean noLocal, MessageFactoryRegistry messageFactory, AMQSession session, - AMQProtocolHandler protocolHandler, FieldTable arguments, int prefetchHigh, int prefetchLow, - boolean exclusive, int acknowledgeMode, boolean noConsume, boolean autoClose) throws JMSException + String messageSelector, boolean noLocal, MessageFactoryRegistry messageFactory, AMQSession session, + AMQProtocolHandler protocolHandler, FieldTable arguments, int prefetchHigh, int prefetchLow, + boolean exclusive, int acknowledgeMode, boolean noConsume, boolean autoClose) throws JMSException { super(channelId, connection, destination,messageSelector,noLocal,messageFactory,session, protocolHandler, arguments, prefetchHigh, prefetchLow, exclusive, @@ -73,13 +74,18 @@ public class BasicMessageConsumer_0_8 extends BasicMessageConsumer<UnprocessedMe } } - public AbstractJMSMessage createJMSMessageFromUnprocessedMessage(AMQMessageDelegateFactory delegateFactory, UnprocessedMessage_0_8 messageFrame)throws Exception - { + public AbstractJMSMessage createJMSMessageFromUnprocessedMessage(AMQMessageDelegateFactory delegateFactory, UnprocessedMessage_0_8 messageFrame)throws Exception + { return _messageFactory.createMessage(messageFrame.getDeliveryTag(), - messageFrame.isRedelivered(), messageFrame.getExchange(), - messageFrame.getRoutingKey(), messageFrame.getContentHeader(), messageFrame.getBodies()); + messageFrame.isRedelivered(), messageFrame.getExchange(), + messageFrame.getRoutingKey(), messageFrame.getContentHeader(), messageFrame.getBodies()); + + } + Message receiveBrowse() throws JMSException + { + return receive(); } } 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 beaa47ed1e..df59be25d0 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 @@ -46,19 +46,21 @@ import org.slf4j.LoggerFactory; public abstract class BasicMessageProducer extends Closeable implements org.apache.qpid.jms.MessageProducer { + enum PublishMode { ASYNC_PUBLISH_ALL, SYNC_PUBLISH_PERSISTENT, SYNC_PUBLISH_ALL }; + protected final Logger _logger = LoggerFactory.getLogger(getClass()); private AMQConnection _connection; /** - * If true, messages will not get a timestamp. + * If true, messages will not get a timestamp. */ protected boolean _disableTimestamps; /** * Priority of messages created by this producer. */ - private int _messagePriority; + private int _messagePriority = Message.DEFAULT_PRIORITY; /** * Time to live of messages. Specified in milliseconds but AMQ has 1 second resolution. @@ -103,7 +105,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; @@ -117,8 +119,12 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac private UUIDGen _messageIdGenerator = UUIDs.newGenerator(); + protected String _userID; // ref user id used in the connection. + private static final ContentBody[] NO_CONTENT_BODIES = new ContentBody[0]; + protected PublishMode publishMode = PublishMode.ASYNC_PUBLISH_ALL; + protected BasicMessageProducer(AMQConnection connection, AMQDestination destination, boolean transacted, int channelId, AMQSession session, AMQProtocolHandler protocolHandler, long producerId, boolean immediate, boolean mandatory, boolean waitUntilSent) @@ -138,6 +144,27 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac _immediate = immediate; _mandatory = mandatory; _waitUntilSent = waitUntilSent; + _userID = connection.getUsername(); + setPublishMode(); + } + + void setPublishMode() + { + // Publish mode could be configured at destination level as well. + // Will add support for this when we provide a more robust binding URL + + String syncPub = _connection.getSyncPublish(); + // Support for deprecated option sync_persistence + if (syncPub.equals("persistent") || _connection.getSyncPersistence()) + { + publishMode = PublishMode.SYNC_PUBLISH_PERSISTENT; + } + else if (syncPub.equals("all")) + { + publishMode = PublishMode.SYNC_PUBLISH_ALL; + } + + _logger.info("MessageProducer " + toString() + " using publish mode : " + publishMode); } void resubscribe() throws AMQException @@ -250,6 +277,7 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac checkPreConditions(); checkInitialDestination(); + synchronized (_connection.getFailoverMutex()) { sendImpl(_destination, message, _deliveryMode, _messagePriority, _timeToLive, _mandatory, _immediate); @@ -521,6 +549,10 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac { throw new javax.jms.IllegalStateException("Invalid Session"); } + if(_session.getAMQConnection().isClosed()) + { + throw new javax.jms.IllegalStateException("Connection closed"); + } } private void checkInitialDestination() 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 02c5526e03..a1b5ce6f4c 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 @@ -35,7 +35,7 @@ import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.BasicContentHeaderProperties; import org.apache.qpid.url.AMQBindingURL; -import org.apache.qpid.nclient.util.ByteBufferMessage; +import org.apache.qpid.util.Strings; import org.apache.qpid.njms.ExceptionHelper; import org.apache.qpid.transport.*; import static org.apache.qpid.transport.Option.*; @@ -45,6 +45,7 @@ import static org.apache.qpid.transport.Option.*; */ public class BasicMessageProducer_0_10 extends BasicMessageProducer { + private byte[] userIDBytes; BasicMessageProducer_0_10(AMQConnection connection, AMQDestination destination, boolean transacted, int channelId, AMQSession session, AMQProtocolHandler protocolHandler, long producerId, @@ -52,13 +53,18 @@ public class BasicMessageProducer_0_10 extends BasicMessageProducer { super(connection, destination, transacted, channelId, session, protocolHandler, producerId, immediate, mandatory, waitUntilSent); + + userIDBytes = Strings.toUTF8(_userID); } void declareDestination(AMQDestination destination) { - ((AMQSession_0_10) getSession()).getQpidSession().exchangeDeclare(destination.getExchangeName().toString(), - destination.getExchangeClass().toString(), - null, null); + String name = destination.getExchangeName().toString(); + ((AMQSession_0_10) getSession()).getQpidSession().exchangeDeclare + (name, + destination.getExchangeClass().toString(), + null, null, + name.startsWith("amq.") ? Option.PASSIVE : Option.NONE); } //--- Overwritten methods @@ -77,6 +83,9 @@ public class BasicMessageProducer_0_10 extends BasicMessageProducer DeliveryProperties deliveryProp = delegate.getDeliveryProperties(); MessageProperties messageProps = delegate.getMessageProperties(); + // On the receiving side, this will be read in to the JMSXUserID as well. + messageProps.setUserId(userIDBytes); + if (messageId != null) { messageProps.setMessageId(messageId); @@ -86,20 +95,22 @@ public class BasicMessageProducer_0_10 extends BasicMessageProducer messageProps.clearMessageId(); } + long currentTime = 0; + if (timeToLive > 0 || !_disableTimestamps) + { + currentTime = System.currentTimeMillis(); + } + + if (timeToLive > 0) + { + deliveryProp.setTtl(timeToLive); + message.setJMSExpiration(currentTime + timeToLive); + } + if (!_disableTimestamps) { - final long currentTime = System.currentTimeMillis(); - deliveryProp.setTimestamp(currentTime); - if (timeToLive > 0) - { - deliveryProp.setExpiration(currentTime + timeToLive); - message.setJMSExpiration(currentTime + timeToLive); - } - else - { - deliveryProp.setExpiration(0); - message.setJMSExpiration(0); - } + + deliveryProp.setTimestamp(currentTime); message.setJMSTimestamp(currentTime); } @@ -145,9 +156,13 @@ public class BasicMessageProducer_0_10 extends BasicMessageProducer ((AMQSession_0_10) getSession()).getQpidSession(); // if true, we need to sync the delivery of this message - boolean sync = (deliveryMode == DeliveryMode.PERSISTENT && - getSession().getAMQConnection().getSyncPersistence()); + boolean sync = false; + sync = ( (publishMode == PublishMode.SYNC_PUBLISH_ALL) || + (publishMode == PublishMode.SYNC_PUBLISH_PERSISTENT && + deliveryMode == DeliveryMode.PERSISTENT) + ); + org.apache.mina.common.ByteBuffer data = message.getData(); ByteBuffer buffer = data == null ? ByteBuffer.allocate(0) : data.buf().slice(); @@ -159,11 +174,12 @@ public class BasicMessageProducer_0_10 extends BasicMessageProducer { ssn.sync(); } + + } catch (RuntimeException rte) { JMSException ex = new JMSException("Exception when sending message"); - rte.printStackTrace(); ex.setLinkedException(rte); throw ex; } 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 c547fcb488..f726cd02a2 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 @@ -24,6 +24,8 @@ import java.util.UUID; import javax.jms.JMSException; import javax.jms.Message; +import javax.jms.Topic; +import javax.jms.Queue; import org.apache.mina.common.ByteBuffer; import org.apache.qpid.client.message.AbstractJMSMessage; @@ -86,6 +88,26 @@ public class BasicMessageProducer_0_8 extends BasicMessageProducer AMQMessageDelegate_0_8 delegate = (AMQMessageDelegate_0_8) message.getDelegate(); BasicContentHeaderProperties contentHeaderProperties = delegate.getContentHeaderProperties(); + contentHeaderProperties.setUserId(_userID); + + //Set the JMS_QPID_DESTTYPE for 0-8/9 messages + int type; + if (destination instanceof Topic) + { + type = AMQDestination.TOPIC_TYPE; + } + else if (destination instanceof Queue) + { + type = AMQDestination.QUEUE_TYPE; + } + else + { + type = AMQDestination.UNKNOWN_TYPE; + } + + //Set JMS_QPID_DESTTYPE + delegate.getContentHeaderProperties().getHeaders().setInteger(CustomJMSXProperty.JMS_QPID_DESTTYPE.getShortStringName(), type); + if (!_disableTimestamps) { final long currentTime = System.currentTimeMillis(); diff --git a/java/client/src/main/java/org/apache/qpid/client/Closeable.java b/java/client/src/main/java/org/apache/qpid/client/Closeable.java index 7e119343a1..e6771e122c 100644 --- a/java/client/src/main/java/org/apache/qpid/client/Closeable.java +++ b/java/client/src/main/java/org/apache/qpid/client/Closeable.java @@ -52,6 +52,13 @@ public abstract class Closeable protected final AtomicBoolean _closed = new AtomicBoolean(false); /** + * Are we in the process of closing. We have this distinction so we can + * still signal we are in the process of closing so other objects can tell + * the difference and tidy up. + */ + protected final AtomicBoolean _closing = new AtomicBoolean(false); + + /** * Checks if this is closed, and raises a JMSException if it is. * * @throws JMSException If this is closed. @@ -75,6 +82,17 @@ public abstract class Closeable } /** + * Checks if this is closis. + * + * @return <tt>true</tt> if we are closing, <tt>false</tt> otherwise. + */ + public boolean isClosing() + { + return _closing.get(); + } + + + /** * Closes this object. * * @throws JMSException If this cannot be closed for any reason. diff --git a/java/client/src/main/java/org/apache/qpid/client/XAConnectionImpl.java b/java/client/src/main/java/org/apache/qpid/client/XAConnectionImpl.java index 476536e42b..43025bd724 100644 --- a/java/client/src/main/java/org/apache/qpid/client/XAConnectionImpl.java +++ b/java/client/src/main/java/org/apache/qpid/client/XAConnectionImpl.java @@ -47,7 +47,7 @@ public class XAConnectionImpl extends AMQConnection implements XAConnection, XAQ public synchronized XASession createXASession() throws JMSException { checkNotClosed(); - return _delegate.createXASession(AMQSession.DEFAULT_PREFETCH_HIGH_MARK, AMQSession.DEFAULT_PREFETCH_HIGH_MARK); + return _delegate.createXASession(); } //-- Interface XAQueueConnection diff --git a/java/client/src/main/java/org/apache/qpid/client/XAResourceImpl.java b/java/client/src/main/java/org/apache/qpid/client/XAResourceImpl.java index 6763c72ecd..35adda9348 100644 --- a/java/client/src/main/java/org/apache/qpid/client/XAResourceImpl.java +++ b/java/client/src/main/java/org/apache/qpid/client/XAResourceImpl.java @@ -88,8 +88,7 @@ public class XAResourceImpl implements XAResource { // we need to restore the qpid session that has been closed _xaSession.createSession(); - // we should get a single exception - convertExecutionErrorToXAErr(e.getExceptions().get(0).getErrorCode()); + convertExecutionErrorToXAErr(e.getException().getErrorCode()); } checkStatus(result.getStatus()); } @@ -142,8 +141,7 @@ public class XAResourceImpl implements XAResource { // we need to restore the qpid session that has been closed _xaSession.createSession(); - // we should get a single exception - convertExecutionErrorToXAErr(e.getExceptions().get(0).getErrorCode()); + convertExecutionErrorToXAErr(e.getException().getErrorCode()); } checkStatus(result.getStatus()); } @@ -171,8 +169,7 @@ public class XAResourceImpl implements XAResource { // we need to restore the qpid session that has been closed _xaSession.createSession(); - // we should get a single exception - convertExecutionErrorToXAErr(e.getExceptions().get(0).getErrorCode()); + convertExecutionErrorToXAErr(e.getException().getErrorCode()); } } @@ -201,8 +198,7 @@ public class XAResourceImpl implements XAResource { // we need to restore the qpid session that has been closed _xaSession.createSession(); - // we should get a single exception - convertExecutionErrorToXAErr(e.getExceptions().get(0).getErrorCode()); + convertExecutionErrorToXAErr(e.getException().getErrorCode()); } } return result; @@ -248,8 +244,7 @@ public class XAResourceImpl implements XAResource { // we need to restore the qpid session that has been closed _xaSession.createSession(); - // we should get a single exception - convertExecutionErrorToXAErr(e.getExceptions().get(0).getErrorCode()); + convertExecutionErrorToXAErr(e.getException().getErrorCode()); } DtxXaStatus status = result.getStatus(); int outcome = XAResource.XA_OK; @@ -291,8 +286,7 @@ public class XAResourceImpl implements XAResource { // we need to restore the qpid session that has been closed _xaSession.createSession(); - // we should get a single exception - convertExecutionErrorToXAErr( e.getExceptions().get(0).getErrorCode()); + convertExecutionErrorToXAErr( e.getException().getErrorCode()); } Xid[] result = new Xid[res.getInDoubt().size()]; int i = 0; @@ -329,8 +323,7 @@ public class XAResourceImpl implements XAResource { // we need to restore the qpid session that has been closed _xaSession.createSession(); - // we should get a single exception - convertExecutionErrorToXAErr( e.getExceptions().get(0).getErrorCode()); + convertExecutionErrorToXAErr( e.getException().getErrorCode()); } checkStatus(result.getStatus()); } @@ -413,8 +406,7 @@ public class XAResourceImpl implements XAResource { // we need to restore the qpid session that has been closed _xaSession.createSession(); - // we should get a single exception - convertExecutionErrorToXAErr(e.getExceptions().get(0).getErrorCode()); + convertExecutionErrorToXAErr(e.getException().getErrorCode()); // TODO: The amqp spec does not allow to make the difference // between an already known XID and a wrong arguments (join and resume are set) // TODO: make sure amqp addresses that diff --git a/java/client/src/main/java/org/apache/qpid/client/XASessionImpl.java b/java/client/src/main/java/org/apache/qpid/client/XASessionImpl.java index 51b4b7899f..354b67cd35 100644 --- a/java/client/src/main/java/org/apache/qpid/client/XASessionImpl.java +++ b/java/client/src/main/java/org/apache/qpid/client/XASessionImpl.java @@ -17,7 +17,6 @@ */ package org.apache.qpid.client; -import org.apache.qpid.nclient.DtxSession; import org.apache.qpid.client.message.MessageFactoryRegistry; import javax.jms.*; @@ -36,7 +35,7 @@ public class XASessionImpl extends AMQSession_0_10 implements XASession, XATopic /** * This XASession Qpid DtxSession */ - private DtxSession _qpidDtxSession; + private org.apache.qpid.transport.Session _qpidDtxSession; /** * The standard session @@ -48,7 +47,7 @@ public class XASessionImpl extends AMQSession_0_10 implements XASession, XATopic /** * Create a JMS XASession */ - public XASessionImpl(org.apache.qpid.nclient.Connection qpidConnection, AMQConnection con, int channelId, + public XASessionImpl(org.apache.qpid.transport.Connection qpidConnection, AMQConnection con, int channelId, int defaultPrefetchHigh, int defaultPrefetchLow) { super(qpidConnection, con, channelId, false, // this is not a transacted session @@ -65,7 +64,9 @@ public class XASessionImpl extends AMQSession_0_10 implements XASession, XATopic */ public void createSession() { - _qpidDtxSession = _qpidConnection.createDTXSession(0); + _qpidDtxSession = _qpidConnection.createSession(0); + _qpidDtxSession.setSessionListener(this); + _qpidDtxSession.dtxSelect(); } @@ -126,7 +127,7 @@ public class XASessionImpl extends AMQSession_0_10 implements XASession, XATopic * * @return The associated Qpid Session. */ - protected org.apache.qpid.nclient.DtxSession getQpidSession() + protected org.apache.qpid.transport.Session getQpidSession() { return _qpidDtxSession; } diff --git a/java/client/src/main/java/org/apache/qpid/client/configuration/ClientProperties.java b/java/client/src/main/java/org/apache/qpid/client/configuration/ClientProperties.java index edda18c715..e5050b4fbd 100644 --- a/java/client/src/main/java/org/apache/qpid/client/configuration/ClientProperties.java +++ b/java/client/src/main/java/org/apache/qpid/client/configuration/ClientProperties.java @@ -33,13 +33,13 @@ public class ClientProperties public static final String IGNORE_SET_CLIENTID_PROP_NAME = "ignore_setclientID"; /** - * This property is currently used within the 0.10 code path only + * This property is currently used within the 0.10 code path only * The maximum number of pre-fetched messages per destination * This property is used for all the connection unless it is overwritten by the connectionURL * type: long */ public static final String MAX_PREFETCH_PROP_NAME = "max_prefetch"; - public static final String MAX_PREFETCH_DEFAULT = "1000"; + public static final String MAX_PREFETCH_DEFAULT = "500"; /** * When true a sync command is sent after every persistent messages. @@ -47,6 +47,31 @@ public class ClientProperties */ public static final String SYNC_PERSISTENT_PROP_NAME = "sync_persistence"; + /** + * When true a sync command is sent after sending a message ack. + * type: boolean + */ + public static final String SYNC_ACK_PROP_NAME = "sync_ack"; + + /** + * sync_publish property - {persistent|all} + * If set to 'persistent',then persistent messages will be publish synchronously + * If set to 'all', then all messages regardless of the delivery mode will be + * published synchronously. + */ + public static final String SYNC_PUBLISH_PROP_NAME = "sync_publish"; + + /** + * This value will be used in the following settings + * To calculate the SO_TIMEOUT option of the socket (2*idle_timeout) + * If this values is between the max and min values specified for heartbeat + * by the broker in TuneOK it will be used as the heartbeat interval. + * If not a warning will be printed and the max value specified for + * heartbeat in TuneOK will be used + */ + public static final String IDLE_TIMEOUT_PROP_NAME = "idle_timeout"; + + /** * ========================================================== * Those properties are used when the io size should be bounded @@ -75,4 +100,6 @@ public class ClientProperties */ public static final String WRITE_BUFFER_LIMIT_PROP_NAME = "qpid.read.buffer.limit"; public static final String WRITE_BUFFER_LIMIT_DEFAULT = "262144"; + + public static final String AMQP_VERSION = "qpid.amqp.version"; } 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 927f660932..f74dbba939 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 @@ -20,11 +20,10 @@ */ package org.apache.qpid.client.failover; -import org.apache.mina.common.IoSession; - import org.apache.qpid.AMQDisconnectedException; import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.client.state.AMQStateManager; +import org.apache.qpid.client.state.AMQState; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -81,9 +80,6 @@ public class FailoverHandler implements Runnable /** Used for debugging. */ private static final Logger _logger = LoggerFactory.getLogger(FailoverHandler.class); - /** Holds the MINA session for the connection that has failed, not the connection that is being failed onto. */ - private final IoSession _session; - /** Holds the protocol handler for the failed connection, upon which the new connection is to be set up. */ private AMQProtocolHandler _amqProtocolHandler; @@ -97,12 +93,10 @@ public class FailoverHandler implements Runnable * Creates a failover handler on a protocol session, for a particular MINA session (network connection). * * @param amqProtocolHandler The protocol handler that spans the failover. - * @param session The MINA session, for the failing connection. */ - public FailoverHandler(AMQProtocolHandler amqProtocolHandler, IoSession session) + public FailoverHandler(AMQProtocolHandler amqProtocolHandler) { _amqProtocolHandler = amqProtocolHandler; - _session = session; } /** @@ -140,6 +134,8 @@ public class FailoverHandler implements Runnable // a slightly more complex state model therefore I felt it was worthwhile doing this. AMQStateManager existingStateManager = _amqProtocolHandler.getStateManager(); + + // Use a fresh new StateManager for the reconnection attempts _amqProtocolHandler.setStateManager(new AMQStateManager()); @@ -194,9 +190,25 @@ public class FailoverHandler implements Runnable } else { - // Set the new Protocol Session in the StateManager. + // Set the new Protocol Session in the StateManager. existingStateManager.setProtocolSession(_amqProtocolHandler.getProtocolSession()); + // Now that the ProtocolHandler has been reconnected clean up + // the state of the old state manager. As if we simply reinstate + // it any old exception that had occured prior to failover may + // prohibit reconnection. + // e.g. During testing when the broker is shutdown gracefully. + // The broker + // Clear any exceptions we gathered + if (existingStateManager.getCurrentState() != AMQState.CONNECTION_OPEN) + { + // Clear the state of the previous state manager as it may + // have received an exception + existingStateManager.clearLastException(); + existingStateManager.changeState(AMQState.CONNECTION_OPEN); + } + + //Restore Existing State Manager _amqProtocolHandler.setStateManager(existingStateManager); try @@ -221,7 +233,7 @@ public class FailoverHandler implements Runnable _amqProtocolHandler.setFailoverState(FailoverState.FAILED); /*try {*/ - _amqProtocolHandler.exceptionCaught(_session, e); + _amqProtocolHandler.exception(e); /*} catch (Exception ex) { 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 cf7e978c03..e9e52cc97c 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 @@ -99,37 +99,6 @@ public class FailoverRetrySupport<T, E extends Exception> implements FailoverSup */ public T execute() throws E { - while (true) - { - try - { - connection.blockUntilNotFailingOver(); - } - catch (InterruptedException e) - { - _log.debug("Interrupted: " + e, e); - - return null; - } - - synchronized (connection.getFailoverMutex()) - { - try - { - return operation.execute(); - } - catch (FailoverException e) - { - _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; - } - } - } - } + return connection.executeRetrySupport(operation); } } 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 index 365fed6aa5..af47673a43 100644 --- 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 @@ -1,4 +1,25 @@ package org.apache.qpid.client.handler; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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 2b6745ebe4..2cf19bf391 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 @@ -32,7 +32,6 @@ import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.ChannelCloseBody; import org.apache.qpid.framing.ChannelCloseOkBody; import org.apache.qpid.protocol.AMQConstant; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -48,7 +47,7 @@ public class ChannelCloseMethodHandler implements StateAwareMethodListener<Chann } public void methodReceived(AMQProtocolSession session, ChannelCloseBody method, int channelId) - throws AMQException + throws AMQException { _logger.debug("ChannelClose method received"); @@ -59,52 +58,62 @@ public class ChannelCloseMethodHandler implements StateAwareMethodListener<Chann _logger.debug("Channel close reply code: " + errorCode + ", reason: " + reason); } - - ChannelCloseOkBody body = session.getMethodRegistry().createChannelCloseOkBody(); AMQFrame frame = body.generateFrame(channelId); session.writeFrame(frame); - - if (errorCode != AMQConstant.REPLY_SUCCESS) + try { - if (_logger.isDebugEnabled()) - { - _logger.debug("Channel close received with errorCode " + errorCode + ", and reason " + reason); - } - - if (errorCode == AMQConstant.NO_CONSUMERS) - { - throw new AMQNoConsumersException("Error: " + reason, null, null); - } - else if (errorCode == AMQConstant.NO_ROUTE) - { - throw new AMQNoRouteException("Error: " + reason, null, null); - } - else if (errorCode == AMQConstant.INVALID_ARGUMENT) + if (errorCode != AMQConstant.REPLY_SUCCESS) { - _logger.debug("Broker responded with Invalid Argument."); + if (_logger.isDebugEnabled()) + { + _logger.debug("Channel close received with errorCode " + errorCode + ", and reason " + reason); + } + + if (errorCode == AMQConstant.NO_CONSUMERS) + { + throw new AMQNoConsumersException("Error: " + reason, null, null); + } + else if (errorCode == AMQConstant.NO_ROUTE) + { + throw new AMQNoRouteException("Error: " + reason, null, null); + } + else if (errorCode == AMQConstant.INVALID_ARGUMENT) + { + _logger.debug("Broker responded with Invalid Argument."); + + throw new org.apache.qpid.AMQInvalidArgumentException(String.valueOf(reason), null); + } + else if (errorCode == AMQConstant.INVALID_ROUTING_KEY) + { + _logger.debug("Broker responded with Invalid Routing Key."); + + throw new AMQInvalidRoutingKeyException(String.valueOf(reason), null); + } + else + { + + throw new AMQChannelClosedException(errorCode, "Error: " + reason, null); + } - throw new org.apache.qpid.AMQInvalidArgumentException(String.valueOf(reason), null); - } - else if (errorCode == AMQConstant.INVALID_ROUTING_KEY) - { - _logger.debug("Broker responded with Invalid Routing Key."); - - throw new AMQInvalidRoutingKeyException(String.valueOf(reason), null); - } - else - { - throw new AMQChannelClosedException(errorCode, "Error: " + reason, null); } - } - // fixme why is this only done when the close is expected... - // should the above forced closes not also cause a close? - // ---------- - // 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)); + finally + { + // fixme why is this only done when the close is expected... + // should the above forced closes not also cause a close? + // ---------- + // 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. + // ---- + // 2009-09-07 - ritchiem + // calling channelClosed will only close this session and will not + // prevent failover. If we don't close the session here then we will + // have problems during the session close as it will attempt to + // close the session that the broker has closed, + + session.channelClosed(channelId, errorCode, String.valueOf(reason)); + } } } 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 index 9c791730ca..909ac8aae8 100644 --- 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 @@ -39,6 +39,7 @@ public class ClientMethodDispatcherImpl implements MethodDispatcher private static final BasicReturnMethodHandler _basicReturnMethodHandler = BasicReturnMethodHandler.getInstance(); private static final ChannelCloseMethodHandler _channelCloseMethodHandler = ChannelCloseMethodHandler.getInstance(); private static final ChannelFlowOkMethodHandler _channelFlowOkMethodHandler = ChannelFlowOkMethodHandler.getInstance(); + private static final ChannelFlowMethodHandler _channelFlowMethodHandler = ChannelFlowMethodHandler.getInstance(); private static final ConnectionCloseMethodHandler _connectionCloseMethodHandler = ConnectionCloseMethodHandler.getInstance(); private static final ConnectionOpenOkMethodHandler _connectionOpenOkMethodHandler = ConnectionOpenOkMethodHandler.getInstance(); private static final ConnectionRedirectMethodHandler _connectionRedirectMethodHandler = ConnectionRedirectMethodHandler.getInstance(); @@ -78,6 +79,16 @@ public class ClientMethodDispatcherImpl implements MethodDispatcher } }); + + _dispatcherFactories.put(ProtocolVersion.v0_91, + new DispatcherFactory() + { + public ClientMethodDispatcherImpl createMethodDispatcher(AMQProtocolSession session) + { + return new ClientMethodDispatcherImpl_0_91(session); + } + }); + } public static ClientMethodDispatcherImpl newMethodDispatcher(ProtocolVersion version, AMQProtocolSession session) @@ -159,7 +170,8 @@ public class ClientMethodDispatcherImpl implements MethodDispatcher public boolean dispatchChannelFlow(ChannelFlowBody body, int channelId) throws AMQException { - return false; + _channelFlowMethodHandler.methodReceived(_session, body, channelId); + return true; } public boolean dispatchChannelFlowOk(ChannelFlowOkBody body, int channelId) throws AMQException diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_0_91.java b/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_0_91.java new file mode 100644 index 0000000000..f15340ae00 --- /dev/null +++ b/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_0_91.java @@ -0,0 +1,158 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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_91.MethodDispatcher_0_91; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.state.AMQStateManager; +import org.apache.qpid.client.state.AMQMethodNotImplementedException; +import org.apache.qpid.client.protocol.AMQProtocolSession; + +public class ClientMethodDispatcherImpl_0_91 extends ClientMethodDispatcherImpl implements MethodDispatcher_0_91 +{ + public ClientMethodDispatcherImpl_0_91(AMQProtocolSession session) + { + super(session); + } + + 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 dispatchBasicRecoverOk(BasicRecoverOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchQueueUnbindOk(QueueUnbindOkBody body, int channelId) throws AMQException + { + return false; + } + +}
\ No newline at end of file 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 e639a33450..e40cafd72f 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 @@ -40,7 +40,6 @@ public class ConnectionOpenOkMethodHandler implements StateAwareMethodListener<C } public void methodReceived(AMQProtocolSession session, ConnectionOpenOkBody body, int channelId) - throws AMQException { session.getStateManager().changeState(AMQState.CONNECTION_OPEN); } 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 8857f1115a..c9212a54c1 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 @@ -68,6 +68,14 @@ public class ConnectionStartMethodHandler implements StateAwareMethodListener<Co ProtocolVersion pv = new ProtocolVersion((byte) body.getVersionMajor(), (byte) body.getVersionMinor()); + // 0-9-1 is indistinguishable from 0-9 using only major and minor ... if we established the connection as 0-9-1 + // and now get back major = 0 , minor = 9 then we can assume it means 0-9-1 + + if(pv.equals(ProtocolVersion.v0_9) && session.getProtocolVersion().equals(ProtocolVersion.v0_91)) + { + pv = ProtocolVersion.v0_91; + } + // 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. // It needs to do this since frame lookup is done by version. 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 e4e58c317d..287b5957a1 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 @@ -45,7 +45,6 @@ public class ConnectionTuneMethodHandler implements StateAwareMethodListener<Con { } public void methodReceived(AMQProtocolSession session, ConnectionTuneBody frame, int channelId) - throws AMQException { _logger.debug("ConnectionTune frame received"); final MethodRegistry methodRegistry = session.getMethodRegistry(); diff --git a/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate.java b/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate.java index 7f43e4b4b2..314508805d 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate.java @@ -22,7 +22,6 @@ package org.apache.qpid.client.message; import org.apache.qpid.client.AMQSession; -import org.apache.qpid.framing.BasicContentHeaderProperties; import javax.jms.Destination; import javax.jms.JMSException; diff --git a/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate_0_10.java b/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate_0_10.java index 8b4488f1f9..1479f06632 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate_0_10.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate_0_10.java @@ -29,7 +29,6 @@ import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; import org.apache.qpid.AMQException; import org.apache.qpid.AMQPInvalidClassException; -import org.apache.qpid.nclient.*; import org.apache.qpid.jms.Message; import org.apache.qpid.url.BindingURL; import org.apache.qpid.url.AMQBindingURL; @@ -42,12 +41,13 @@ import javax.jms.MessageNotWriteableException; import javax.jms.MessageFormatException; import javax.jms.DeliveryMode; import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.net.URISyntaxException; -import java.nio.charset.Charset; -import org.apache.qpid.exchange.ExchangeDefaults; -public class AMQMessageDelegate_0_10 implements AMQMessageDelegate +/** + * This extends AbstractAMQMessageDelegate which contains common code between + * both the 0_8 and 0_10 Message types. + * + */ +public class AMQMessageDelegate_0_10 extends AbstractAMQMessageDelegate { private static final Map<ReplyTo, Destination> _destinationCache = Collections.synchronizedMap(new ReferenceMap()); @@ -65,27 +65,6 @@ public class AMQMessageDelegate_0_10 implements AMQMessageDelegate private AMQSession _session; private final long _deliveryTag; - private static Map<AMQShortString,Integer> _exchangeTypeMap = new ConcurrentHashMap<AMQShortString, Integer>(); - private static Map<String,Integer> _exchangeTypeStringMap = new ConcurrentHashMap<String, Integer>(); - private static Map<String, Integer> _exchangeTypeToDestinationType = new ConcurrentHashMap<String, Integer>();; - - static - { - _exchangeTypeMap.put(ExchangeDefaults.DIRECT_EXCHANGE_NAME, AMQDestination.QUEUE_TYPE); - _exchangeTypeMap.put(AMQShortString.EMPTY_STRING, AMQDestination.QUEUE_TYPE); - _exchangeTypeMap.put(ExchangeDefaults.TOPIC_EXCHANGE_NAME, AMQDestination.TOPIC_TYPE); - _exchangeTypeMap.put(ExchangeDefaults.FANOUT_EXCHANGE_NAME, AMQDestination.TOPIC_TYPE); - - _exchangeTypeStringMap.put(ExchangeDefaults.DIRECT_EXCHANGE_NAME.toString(), AMQDestination.QUEUE_TYPE); - _exchangeTypeStringMap.put("", AMQDestination.QUEUE_TYPE); - _exchangeTypeStringMap.put(ExchangeDefaults.TOPIC_EXCHANGE_NAME.toString(), AMQDestination.TOPIC_TYPE); - _exchangeTypeStringMap.put(ExchangeDefaults.FANOUT_EXCHANGE_NAME.toString(), AMQDestination.TOPIC_TYPE); - - - _exchangeTypeToDestinationType.put(ExchangeDefaults.DIRECT_EXCHANGE_CLASS.toString(), AMQDestination.QUEUE_TYPE); - _exchangeTypeToDestinationType.put(ExchangeDefaults.TOPIC_EXCHANGE_NAME.toString(), AMQDestination.TOPIC_TYPE); - _exchangeTypeToDestinationType.put(ExchangeDefaults.FANOUT_EXCHANGE_NAME.toString(), AMQDestination.TOPIC_TYPE); - } protected AMQMessageDelegate_0_10() { @@ -93,87 +72,49 @@ public class AMQMessageDelegate_0_10 implements AMQMessageDelegate _readableProperties = false; } - protected AMQMessageDelegate_0_10(long deliveryTag, MessageProperties messageProps, DeliveryProperties deliveryProps, AMQShortString exchange, - AMQShortString routingKey) throws AMQException + protected AMQMessageDelegate_0_10(MessageProperties messageProps, DeliveryProperties deliveryProps, long deliveryTag) { - this(messageProps, deliveryProps, deliveryTag); - - - AMQDestination dest; - - dest = generateDestination(exchange, routingKey); - setJMSDestination(dest); - } + _messageProps = messageProps; + _deliveryProps = deliveryProps; + _deliveryTag = deliveryTag; + _readableProperties = (_messageProps != null); - private AMQDestination generateDestination(AMQShortString exchange, AMQShortString routingKey) - { AMQDestination dest; - switch(getExchangeType(exchange)) - { - case AMQDestination.QUEUE_TYPE: - dest = new AMQQueue(exchange, routingKey, routingKey); - break; - case AMQDestination.TOPIC_TYPE: - dest = new AMQTopic(exchange, routingKey, null); - break; - default: - dest = new AMQUndefinedDestination(exchange, routingKey, null); - - } - - return dest; - } - - private int getExchangeType(AMQShortString exchange) - { - Integer type = _exchangeTypeMap.get(exchange == null ? AMQShortString.EMPTY_STRING : exchange); - - if(type == null) - { - return AMQDestination.UNKNOWN_TYPE; - } - - return type; + dest = generateDestination(new AMQShortString(_deliveryProps.getExchange()), + new AMQShortString(_deliveryProps.getRoutingKey())); + setJMSDestination(dest); } - - public static void updateExchangeTypeMapping(Header header, org.apache.qpid.nclient.Session session) + /** + * Use the 0-10 ExchangeQuery call to validate the exchange type. + * + * This is used primarily to provide the correct JMSDestination value. + * + * The query is performed synchronously iff the map exchange is not already + * present in the exchange Map. + * + * @param header The message headers, from which the exchange name can be extracted + * @param session The 0-10 session to use to call ExchangeQuery + */ + public static void updateExchangeTypeMapping(Header header, org.apache.qpid.transport.Session session) { DeliveryProperties deliveryProps = header.get(DeliveryProperties.class); - if(deliveryProps != null) + if (deliveryProps != null) { String exchange = deliveryProps.getExchange(); - if(exchange != null && !_exchangeTypeStringMap.containsKey(exchange)) + if (exchange != null && !exchangeMapContains(exchange)) { - - AMQShortString exchangeShortString = new AMQShortString(exchange); Future<ExchangeQueryResult> future = - session.exchangeQuery(exchange.toString()); + session.exchangeQuery(exchange.toString()); ExchangeQueryResult res = future.get(); - Integer type = _exchangeTypeToDestinationType.get(res.getType()); - if(type == null) - { - type = AMQDestination.UNKNOWN_TYPE; - } - _exchangeTypeStringMap.put(exchange, type); - _exchangeTypeMap.put(exchangeShortString, type); - + updateExchangeType(exchange, res.getType()); } } } - protected AMQMessageDelegate_0_10(MessageProperties messageProps, DeliveryProperties deliveryProps, long deliveryTag) - { - _messageProps = messageProps; - _deliveryProps = deliveryProps; - _deliveryTag = deliveryTag; - _readableProperties = (_messageProps != null); - - } - public String getJMSMessageID() throws JMSException { @@ -287,7 +228,8 @@ public class AMQMessageDelegate_0_10 implements AMQMessageDelegate { if (destination == null) { - throw new IllegalArgumentException("Null destination not allowed"); + _messageProps.setReplyTo(null); + return; } if (!(destination instanceof AMQDestination)) diff --git a/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate_0_8.java b/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate_0_8.java index 4c20a44849..5157632280 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate_0_8.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate_0_8.java @@ -46,7 +46,7 @@ import java.util.Enumeration; import java.util.UUID; import java.net.URISyntaxException; -public class AMQMessageDelegate_0_8 implements AMQMessageDelegate +public class AMQMessageDelegate_0_8 extends AbstractAMQMessageDelegate { private static final Map _destinationCache = Collections.synchronizedMap(new ReferenceMap()); @@ -65,6 +65,16 @@ public class AMQMessageDelegate_0_8 implements AMQMessageDelegate private AMQSession _session; private final long _deliveryTag; + // The base set of items that needs to be set. + private AMQMessageDelegate_0_8(BasicContentHeaderProperties properties, long deliveryTag) + { + _contentHeaderProperties = properties; + _deliveryTag = deliveryTag; + _readableProperties = (_contentHeaderProperties != null); + _headerAdapter = new JMSHeaderAdapter(((BasicContentHeaderProperties) _contentHeaderProperties).getHeaders()); + } + + // Used for the creation of new messages protected AMQMessageDelegate_0_8() { this(new BasicContentHeaderProperties(), -1); @@ -73,6 +83,7 @@ public class AMQMessageDelegate_0_8 implements AMQMessageDelegate } + // Used when generating a received message object protected AMQMessageDelegate_0_8(long deliveryTag, BasicContentHeaderProperties contentHeader, AMQShortString exchange, AMQShortString routingKey) { @@ -80,41 +91,33 @@ public class AMQMessageDelegate_0_8 implements AMQMessageDelegate Integer type = contentHeader.getHeaders().getInteger(CustomJMSXProperty.JMS_QPID_DESTTYPE.getShortStringName()); - if(type == null) + AMQDestination dest = null; + + // If we have a type set the attempt to use that. + if (type != null) { - type = AMQDestination.UNKNOWN_TYPE; + switch (type.intValue()) + { + case AMQDestination.QUEUE_TYPE: + dest = new AMQQueue(exchange, routingKey, routingKey); + break; + case AMQDestination.TOPIC_TYPE: + dest = new AMQTopic(exchange, routingKey, null); + break; + default: + // Use the generateDestination method + dest = null; + } } - AMQDestination dest; - - switch(type.intValue()) + if (dest == null) { - case AMQDestination.QUEUE_TYPE: - dest = new AMQQueue(exchange, routingKey, routingKey); - break; - case AMQDestination.TOPIC_TYPE: - dest = new AMQTopic(exchange, routingKey, null); - break; - default: - dest = new AMQUndefinedDestination(exchange, routingKey, null); + dest = generateDestination(exchange, routingKey); } - - - // Destination dest = AMQDestination.createDestination(url); setJMSDestination(dest); - - - } - protected AMQMessageDelegate_0_8(BasicContentHeaderProperties properties, long deliveryTag) - { - _contentHeaderProperties = properties; - _deliveryTag = deliveryTag; - _readableProperties = (_contentHeaderProperties != null); - _headerAdapter = new JMSHeaderAdapter(((BasicContentHeaderProperties) _contentHeaderProperties).getHeaders()); - } public String getJMSMessageID() throws JMSException @@ -124,12 +127,18 @@ public class AMQMessageDelegate_0_8 implements AMQMessageDelegate public void setJMSMessageID(String messageId) throws JMSException { - getContentHeaderProperties().setMessageId(messageId); + if (messageId != null) + { + getContentHeaderProperties().setMessageId(messageId); + } } public void setJMSMessageID(UUID messageId) throws JMSException { - getContentHeaderProperties().setMessageId("ID:" + messageId); + if (messageId != null) + { + getContentHeaderProperties().setMessageId("ID:" + messageId); + } } @@ -196,7 +205,8 @@ public class AMQMessageDelegate_0_8 implements AMQMessageDelegate { if (destination == null) { - throw new IllegalArgumentException("Null destination not allowed"); + getContentHeaderProperties().setReplyTo((String) null); + return; // We're done here } if (!(destination instanceof AMQDestination)) diff --git a/java/client/src/main/java/org/apache/qpid/client/message/AbstractAMQMessageDelegate.java b/java/client/src/main/java/org/apache/qpid/client/message/AbstractAMQMessageDelegate.java new file mode 100644 index 0000000000..534fb19b76 --- /dev/null +++ b/java/client/src/main/java/org/apache/qpid/client/message/AbstractAMQMessageDelegate.java @@ -0,0 +1,151 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.message; + +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQTopic; +import org.apache.qpid.client.AMQUndefinedDestination; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * This abstract class provides exchange lookup functionality that is shared + * between all MessageDelegates. Update facilities are provided so that the 0-10 + * code base can update the mappings. The 0-8 code base does not have the + * facility to update the exchange map so it can only use the default mappings. + * + * That said any updates that a 0-10 client performs will also benefit any 0-8 + * connections in this VM. + * + */ +public abstract class AbstractAMQMessageDelegate implements AMQMessageDelegate +{ + + private static Map<AMQShortString, Integer> _exchangeTypeMap = new ConcurrentHashMap<AMQShortString, Integer>(); + private static Map<String, Integer> _exchangeTypeStringMap = new ConcurrentHashMap<String, Integer>(); + private static Map<String, Integer> _exchangeTypeToDestinationType = new ConcurrentHashMap<String, Integer>(); + + /** + * Add default Mappings for the Direct, Default, Topic and Fanout exchanges. + */ + static + { + _exchangeTypeMap.put(ExchangeDefaults.DIRECT_EXCHANGE_NAME, AMQDestination.QUEUE_TYPE); + _exchangeTypeMap.put(AMQShortString.EMPTY_STRING, AMQDestination.QUEUE_TYPE); + _exchangeTypeMap.put(ExchangeDefaults.TOPIC_EXCHANGE_NAME, AMQDestination.TOPIC_TYPE); + _exchangeTypeMap.put(ExchangeDefaults.FANOUT_EXCHANGE_NAME, AMQDestination.TOPIC_TYPE); + + _exchangeTypeStringMap.put(ExchangeDefaults.DIRECT_EXCHANGE_NAME.toString(), AMQDestination.QUEUE_TYPE); + _exchangeTypeStringMap.put("", AMQDestination.QUEUE_TYPE); + _exchangeTypeStringMap.put(ExchangeDefaults.TOPIC_EXCHANGE_NAME.toString(), AMQDestination.TOPIC_TYPE); + _exchangeTypeStringMap.put(ExchangeDefaults.FANOUT_EXCHANGE_NAME.toString(), AMQDestination.TOPIC_TYPE); + + _exchangeTypeToDestinationType.put(ExchangeDefaults.DIRECT_EXCHANGE_NAME.toString(), AMQDestination.QUEUE_TYPE); + _exchangeTypeToDestinationType.put(ExchangeDefaults.TOPIC_EXCHANGE_NAME.toString(), AMQDestination.TOPIC_TYPE); + _exchangeTypeToDestinationType.put(ExchangeDefaults.FANOUT_EXCHANGE_NAME.toString(), AMQDestination.TOPIC_TYPE); + } + + /** + * Called when a Destination is requried. + * + * This will create the AMQDestination that is the correct type and value + * based on the incomming values. + * @param exchange The exchange name + * @param routingKey The routing key to be used for the Destination + * @return AMQDestination of the correct subtype + */ + protected AMQDestination generateDestination(AMQShortString exchange, AMQShortString routingKey) + { + AMQDestination dest; + switch (getExchangeType(exchange)) + { + case AMQDestination.QUEUE_TYPE: + dest = new AMQQueue(exchange, routingKey, routingKey); + break; + case AMQDestination.TOPIC_TYPE: + dest = new AMQTopic(exchange, routingKey, null); + break; + default: + dest = new AMQUndefinedDestination(exchange, routingKey, null); + } + + return dest; + } + + /** + * Update the exchange name to type mapping. + * + * If the newType is not known then an UNKNOWN_TYPE is created. Only if the + * exchange is of a known type: amq.direct, amq.topic, amq.fanout can we + * create a suitable AMQDestination representation + * + * @param exchange the name of the exchange + * @param newtype the AMQP exchange class name i.e. amq.direct + */ + protected static void updateExchangeType(String exchange, String newtype) + { + Integer type = _exchangeTypeToDestinationType.get(newtype); + if (type == null) + { + type = AMQDestination.UNKNOWN_TYPE; + } + _exchangeTypeStringMap.put(exchange, type); + _exchangeTypeMap.put(new AMQShortString(exchange), type); + } + + /** + * Accessor method to allow lookups of the given exchange name. + * + * This check allows the prevention of extra work required such as asking + * the broker for the exchange class name. + * + * @param exchange the exchange name to lookup + * @return true if there is a mapping for this exchange + */ + protected static boolean exchangeMapContains(String exchange) + { + return _exchangeTypeStringMap.containsKey(exchange); + } + + /** + * Returns an int representing the exchange type. This is used in the + * createDestination method to ensure the correct AMQDestiation is created. + * + * @param exchange the exchange name to lookup + * @return int representing the Exchange type + */ + private int getExchangeType(AMQShortString exchange) + { + Integer type = _exchangeTypeMap.get(exchange == null ? AMQShortString.EMPTY_STRING : exchange); + + if (type == null) + { + return AMQDestination.UNKNOWN_TYPE; + } + + return type; + } + +} diff --git a/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesMessage.java b/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesMessage.java index c0d51fa726..535b55ced5 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesMessage.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesMessage.java @@ -31,6 +31,7 @@ import org.apache.mina.common.ByteBuffer; import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.BasicContentHeaderProperties; +import org.apache.qpid.transport.util.Functions; /** * @author Apache Software Foundation @@ -84,53 +85,26 @@ public abstract class AbstractBytesMessage extends AbstractJMSMessage } public String toBodyString() throws JMSException - { - checkReadable(); + { try { - return getText(); + if (_data != null) + { + return Functions.str(_data.buf(), 100,0); + } + else + { + return ""; + } + } - catch (IOException e) + catch (Exception e) { JMSException jmse = new JMSException(e.toString()); jmse.setLinkedException(e); throw jmse; } - } - - /** - * We reset the stream before and after reading the data. This means that toString() will always output - * the entire message and also that the caller can then immediately start reading as if toString() had - * never been called. - * - * @return - * @throws IOException - */ - private String getText() throws IOException - { - // this will use the default platform encoding - if (_data == null) - { - return null; - } - - int pos = _data.position(); - _data.rewind(); - // one byte left is for the end of frame marker - if (_data.remaining() == 0) - { - // this is really redundant since pos must be zero - _data.position(pos); - - return null; - } - else - { - String data = _data.getString(Charset.forName("UTF8").newDecoder()); - _data.position(pos); - - return data; - } + } /** 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 0700ce5d23..288a4ea85c 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 @@ -367,13 +367,14 @@ public abstract class AbstractJMSMessage implements org.apache.qpid.jms.Message try { StringBuffer buf = new StringBuffer("Body:\n"); + buf.append(toBodyString()); buf.append("\nJMS Correlation ID: ").append(getJMSCorrelationID()); buf.append("\nJMS timestamp: ").append(getJMSTimestamp()); buf.append("\nJMS expiration: ").append(getJMSExpiration()); buf.append("\nJMS priority: ").append(getJMSPriority()); buf.append("\nJMS delivery mode: ").append(getJMSDeliveryMode()); - //buf.append("\nJMS reply to: ").append(String.valueOf(getJMSReplyTo())); + buf.append("\nJMS reply to: ").append(getReplyToString()); buf.append("\nJMS Redelivered: ").append(_redelivered); buf.append("\nJMS Destination: ").append(getJMSDestination()); buf.append("\nJMS Type: ").append(getJMSType()); @@ -392,7 +393,7 @@ public abstract class AbstractJMSMessage implements org.apache.qpid.jms.Message while(propertyNames.hasMoreElements()) { String propertyName = (String) propertyNames.nextElement(); - buf.append(propertyName).append(":\t").append(getObjectProperty(propertyName)); + buf.append("\t").append(propertyName).append(" = ").append(getObjectProperty(propertyName)).append("\n"); } } @@ -401,7 +402,7 @@ public abstract class AbstractJMSMessage implements org.apache.qpid.jms.Message } catch (JMSException e) { - return e.toString(); + throw new RuntimeException(e); } } 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 54a845ceef..e719c9a4b2 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 @@ -27,7 +27,6 @@ import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.ContentBody; import org.apache.qpid.framing.ContentHeaderBody; import org.apache.qpid.framing.BasicContentHeaderProperties; -import org.apache.qpid.transport.Struct; import org.apache.qpid.transport.MessageProperties; import org.apache.qpid.transport.DeliveryProperties; @@ -109,7 +108,8 @@ public abstract class AbstractJMSMessageFactory implements MessageFactory protected abstract AbstractJMSMessage createMessage(AMQMessageDelegate delegate, ByteBuffer data) throws AMQException; - protected AbstractJMSMessage create010MessageWithBody(long messageNbr, Struct[] contentHeader, + protected AbstractJMSMessage create010MessageWithBody(long messageNbr, MessageProperties msgProps, + DeliveryProperties deliveryProps, java.nio.ByteBuffer body) throws AMQException { ByteBuffer data; @@ -130,11 +130,7 @@ public abstract class AbstractJMSMessageFactory implements MessageFactory _logger.debug("Creating message from buffer with position=" + data.position() + " and remaining=" + data .remaining()); } - // set the properties of this message - MessageProperties mprop = (MessageProperties) contentHeader[0]; - DeliveryProperties devprop = (DeliveryProperties) contentHeader[1]; - - AMQMessageDelegate delegate = new AMQMessageDelegate_0_10(mprop, devprop, messageNbr); + AMQMessageDelegate delegate = new AMQMessageDelegate_0_10(msgProps, deliveryProps, messageNbr); AbstractJMSMessage message = createMessage(delegate, data); return message; @@ -163,12 +159,12 @@ public abstract class AbstractJMSMessageFactory implements MessageFactory return msg; } - public AbstractJMSMessage createMessage(long messageNbr, boolean redelivered, Struct[] contentHeader, - java.nio.ByteBuffer body) + public AbstractJMSMessage createMessage(long messageNbr, boolean redelivered, MessageProperties msgProps, + DeliveryProperties deliveryProps, java.nio.ByteBuffer body) throws JMSException, AMQException { final AbstractJMSMessage msg = - create010MessageWithBody(messageNbr, contentHeader, body); + create010MessageWithBody(messageNbr,msgProps,deliveryProps, body); msg.setJMSRedelivered(redelivered); msg.receivedFromServer(); return msg; diff --git a/java/client/src/main/java/org/apache/qpid/client/message/FiledTableSupport.java b/java/client/src/main/java/org/apache/qpid/client/message/FiledTableSupport.java index 3db628017f..5b8a4ce878 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/FiledTableSupport.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/FiledTableSupport.java @@ -1,4 +1,25 @@ package org.apache.qpid.client.message; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + import java.util.Enumeration; import java.util.HashMap; diff --git a/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessage.java b/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessage.java index cd9d7ccf8b..8681dae2bd 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessage.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessage.java @@ -381,10 +381,4 @@ public class JMSBytesMessage extends AbstractBytesMessage implements BytesMessag throw new MessageFormatException("Only primitives plus byte arrays and String are valid types"); } } - - public String toString() - { - return String.valueOf(System.identityHashCode(this)); - } - } diff --git a/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessage.java b/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessage.java index 39b9597af1..56e9a5dc73 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessage.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessage.java @@ -25,8 +25,6 @@ import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; -import java.nio.charset.CharacterCodingException; -import java.nio.charset.Charset; import javax.jms.JMSException; import javax.jms.MessageFormatException; @@ -35,8 +33,6 @@ import javax.jms.ObjectMessage; import org.apache.mina.common.ByteBuffer; import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.BasicContentHeaderProperties; public class JMSObjectMessage extends AbstractJMSMessage implements ObjectMessage { @@ -157,7 +153,7 @@ public class JMSObjectMessage extends AbstractJMSMessage implements ObjectMessag } finally { - _data.rewind(); + // _data.rewind(); close(in); } } diff --git a/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessage.java b/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessage.java index c290149cef..f83ae6ace0 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessage.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessage.java @@ -100,6 +100,7 @@ public class JMSTextMessage extends AbstractJMSMessage implements javax.jms.Text if (encoding == null || encoding.equalsIgnoreCase("UTF-8")) { _data = ByteBuffer.wrap(Strings.toUTF8(text)); + setEncoding("UTF-8"); } else { diff --git a/java/client/src/main/java/org/apache/qpid/client/message/MessageFactory.java b/java/client/src/main/java/org/apache/qpid/client/message/MessageFactory.java index e1275c37f7..f3d96cd855 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/MessageFactory.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/MessageFactory.java @@ -27,7 +27,8 @@ import javax.jms.JMSException; import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.ContentHeaderBody; -import org.apache.qpid.transport.Struct; +import org.apache.qpid.transport.DeliveryProperties; +import org.apache.qpid.transport.MessageProperties; public interface MessageFactory @@ -39,7 +40,8 @@ public interface MessageFactory throws JMSException, AMQException; AbstractJMSMessage createMessage(long deliveryTag, boolean redelivered, - Struct[] contentHeader, + MessageProperties msgProps, + DeliveryProperties deliveryProps, java.nio.ByteBuffer body) throws JMSException, AMQException; diff --git a/java/client/src/main/java/org/apache/qpid/client/message/MessageFactoryRegistry.java b/java/client/src/main/java/org/apache/qpid/client/message/MessageFactoryRegistry.java index 948d6d0d7d..a7d41e2cde 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/MessageFactoryRegistry.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/MessageFactoryRegistry.java @@ -48,6 +48,7 @@ public class MessageFactoryRegistry private final Map<String, MessageFactory> _mimeStringToFactoryMap = new HashMap<String, MessageFactory>(); private final Map<AMQShortString, MessageFactory> _mimeShortStringToFactoryMap = new HashMap<AMQShortString, MessageFactory>(); + private final MessageFactory _default = new JMSBytesMessageFactory(); /** * Construct a new registry with the default message factories registered @@ -63,7 +64,7 @@ public class MessageFactoryRegistry mf.registerFactory(JMSBytesMessage.MIME_TYPE, new JMSBytesMessageFactory()); mf.registerFactory(JMSObjectMessage.MIME_TYPE, new JMSObjectMessageFactory()); mf.registerFactory(JMSStreamMessage.MIME_TYPE, new JMSStreamMessageFactory()); - mf.registerFactory(null, new JMSBytesMessageFactory()); + mf.registerFactory(null, mf._default); return mf; } @@ -113,39 +114,43 @@ public class MessageFactoryRegistry MessageFactory mf = _mimeShortStringToFactoryMap.get(contentTypeShortString); if (mf == null) { - throw new AMQException(null, "Unsupport MIME type of " + properties.getContentTypeAsString(), null); - } - else - { - return mf.createMessage(deliveryTag, redelivered, contentHeader, exchange, routingKey, bodies); + mf = _default; } + + return mf.createMessage(deliveryTag, redelivered, contentHeader, exchange, routingKey, bodies); } public AbstractJMSMessage createMessage(MessageTransfer transfer) throws AMQException, JMSException { MessageProperties mprop = transfer.getHeader().get(MessageProperties.class); - String messageType = mprop.getContentType(); - if (messageType == null) + String messageType = ""; + if ( mprop == null || mprop.getContentType() == null) { _logger.debug("no message type specified, building a byte message"); messageType = JMSBytesMessage.MIME_TYPE; } + else + { + messageType = mprop.getContentType(); + } MessageFactory mf = _mimeStringToFactoryMap.get(messageType); if (mf == null) { - throw new AMQException(null, "Unsupport MIME type of " + messageType, null); + mf = _default; } - else + + boolean redelivered = false; + DeliveryProperties deliverProps; + if((deliverProps = transfer.getHeader().get(DeliveryProperties.class)) != null) { - boolean redelivered = false; - DeliveryProperties deliverProps; - if((deliverProps = transfer.getHeader().get(DeliveryProperties.class)) != null) - { - redelivered = deliverProps.getRedelivered(); - } - return mf.createMessage(transfer.getId(), redelivered, transfer.getHeader().getStructs(), transfer.getBody()); + redelivered = deliverProps.getRedelivered(); } + return mf.createMessage(transfer.getId(), + redelivered, + mprop == null? new MessageProperties():mprop, + deliverProps == null? new DeliveryProperties():deliverProps, + transfer.getBody()); } @@ -159,11 +164,9 @@ public class MessageFactoryRegistry MessageFactory mf = _mimeStringToFactoryMap.get(mimeType); if (mf == null) { - throw new AMQException(null, "Unsupport MIME type of " + mimeType, null); - } - else - { - return mf.createMessage(delegateFactory); + mf = _default; } + + return mf.createMessage(delegateFactory); } } 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 ed590772d9..6e5f33a65c 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 @@ -1,4 +1,25 @@ package org.apache.qpid.client.message; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + import org.apache.qpid.framing.AMQShortString; 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 713c87260c..e2cb36a030 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 @@ -20,6 +20,7 @@ */ package org.apache.qpid.client.message; +import org.apache.qpid.client.AMQSession; import org.apache.qpid.client.BasicMessageConsumer; @@ -30,7 +31,7 @@ import org.apache.qpid.client.BasicMessageConsumer; * Note that the actual work of creating a JMS message for the client code's use is done outside of the MINA dispatcher * thread in order to minimise the amount of work done in the MINA dispatcher thread. */ -public abstract class UnprocessedMessage +public abstract class UnprocessedMessage implements AMQSession.Dispatchable { private final int _consumerTag; @@ -49,5 +50,9 @@ public abstract class UnprocessedMessage return _consumerTag; } + public void dispatch(AMQSession ssn) + { + ssn.dispatch(this); + } }
\ No newline at end of file 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 6b1301a33f..f31bc88509 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 @@ -33,9 +33,9 @@ public class UnprocessedMessage_0_10 extends UnprocessedMessage { private MessageTransfer _transfer; - public UnprocessedMessage_0_10(int consumerTag, MessageTransfer xfr) + public UnprocessedMessage_0_10(MessageTransfer xfr) { - super(consumerTag); + super(Integer.parseInt(xfr.getDestination())); _transfer = xfr; } diff --git a/java/client/src/main/java/org/apache/qpid/client/protocol/AMQIoTransportProtocolSession.java b/java/client/src/main/java/org/apache/qpid/client/protocol/AMQIoTransportProtocolSession.java deleted file mode 100644 index 1de0e7bdfc..0000000000 --- a/java/client/src/main/java/org/apache/qpid/client/protocol/AMQIoTransportProtocolSession.java +++ /dev/null @@ -1,125 +0,0 @@ -package org.apache.qpid.client.protocol; - -import java.util.UUID; - -import javax.security.sasl.SaslClient; - -import org.apache.commons.lang.StringUtils; -import org.apache.mina.common.IdleStatus; -import org.apache.qpid.AMQException; -import org.apache.qpid.client.AMQConnection; -import org.apache.qpid.client.ConnectionTuneParameters; -import org.apache.qpid.client.handler.ClientMethodDispatcherImpl; -import org.apache.qpid.client.state.AMQState; -import org.apache.qpid.framing.AMQDataBlock; -import org.apache.qpid.framing.AMQMethodBody; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.ProtocolInitiation; -import org.apache.qpid.framing.ProtocolVersion; -import org.apache.qpid.transport.Sender; - -public class AMQIoTransportProtocolSession extends AMQProtocolSession -{ - - protected Sender<java.nio.ByteBuffer> _ioSender; - private SaslClient _saslClient; - private ConnectionTuneParameters _connectionTuneParameters; - - public AMQIoTransportProtocolSession(AMQProtocolHandler protocolHandler, AMQConnection connection) - { - super(protocolHandler, connection); - } - - @Override - public void closeProtocolSession(boolean waitLast) throws AMQException - { - _ioSender.close(); - _protocolHandler.getStateManager().changeState(AMQState.CONNECTION_CLOSED); - } - - @Override - public void init() - { - _ioSender.send(new ProtocolInitiation(_connection.getProtocolVersion()).toNioByteBuffer()); - _ioSender.flush(); - } - - @Override - protected AMQShortString generateQueueName() - { - int id; - synchronized (_queueIdLock) - { - id = _queueId++; - } - return new AMQShortString("tmp_" + UUID.randomUUID() + "_" + id); - } - - @Override - public AMQConnection getAMQConnection() - { - return _connection; - } - - @Override - public SaslClient getSaslClient() - { - return _saslClient; - } - - @Override - public void setSaslClient(SaslClient client) - { - _saslClient = client; - } - - /** @param delay delay in seconds (not ms) */ - @Override - void initHeartbeats(int delay) - { - if (delay > 0) - { - // FIXME: actually do something here - HeartbeatDiagnostics.init(delay, HeartbeatConfig.CONFIG.getTimeout(delay)); - } - } - - @Override - public void methodFrameReceived(final int channel, final AMQMethodBody amqMethodBody) throws AMQException - { - // FIXME? - _protocolHandler.methodBodyReceived(channel, amqMethodBody, null); - } - - @Override - public void writeFrame(AMQDataBlock frame, boolean wait) - { - _ioSender.send(frame.toNioByteBuffer()); - if (wait) - { - _ioSender.flush(); - } - } - - @Override - public void setSender(Sender<java.nio.ByteBuffer> sender) - { - _ioSender = sender; - } - - @Override - public ConnectionTuneParameters getConnectionTuneParameters() - { - return _connectionTuneParameters; - } - - @Override - public void setConnectionTuneParameters(ConnectionTuneParameters params) - { - _connectionTuneParameters = params; - AMQConnection con = getAMQConnection(); - con.setMaximumChannelCount(params.getChannelMax()); - con.setMaximumFrameSize(params.getFrameMax()); - initHeartbeats((int) params.getHeartbeat()); - } -} 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 e92817f713..a567c2c215 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 @@ -20,24 +20,23 @@ */ 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 java.io.IOException; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + 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; import org.apache.qpid.AMQTimeoutException; import org.apache.qpid.client.AMQConnection; import org.apache.qpid.client.AMQSession; -import org.apache.qpid.client.SSLConfiguration; -import org.apache.qpid.client.configuration.ClientProperties; import org.apache.qpid.client.failover.FailoverException; import org.apache.qpid.client.failover.FailoverHandler; import org.apache.qpid.client.failover.FailoverState; @@ -46,23 +45,29 @@ import org.apache.qpid.client.state.AMQStateManager; import org.apache.qpid.client.state.StateWaiter; import org.apache.qpid.client.state.listener.SpecificMethodFrameListener; import org.apache.qpid.codec.AMQCodecFactory; -import org.apache.qpid.framing.*; +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.HeartbeatBody; +import org.apache.qpid.framing.MethodRegistry; +import org.apache.qpid.framing.ProtocolInitiation; +import org.apache.qpid.framing.ProtocolVersion; import org.apache.qpid.jms.BrokerDetails; -import org.apache.qpid.pool.ReadWriteThreadModel; +import org.apache.qpid.pool.Job; +import org.apache.qpid.pool.ReferenceCountingExecutorService; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.protocol.AMQMethodEvent; import org.apache.qpid.protocol.AMQMethodListener; -import org.apache.qpid.ssl.SSLContextFactory; +import org.apache.qpid.protocol.ProtocolEngine; +import org.apache.qpid.transport.NetworkDriver; import org.apache.qpid.transport.network.io.IoTransport; 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; - /** * AMQProtocolHandler is the client side protocol handler for AMQP, it handles all protocol events received from the * network by MINA. The primary purpose of AMQProtocolHandler is to translate the generic event model of MINA into the @@ -102,17 +107,10 @@ import java.util.concurrent.CountDownLatch; * * <p/><table id="crc"><caption>CRC Card</caption> * <tr><th> Responsibilities <th> Collaborations - * <tr><td> Create the filter chain to filter this handlers events. - * <td> {@link ProtocolCodecFilter}, {@link SSLContextFactory}, {@link SSLFilter}, {@link ReadWriteThreadModel}. - * * <tr><td> Maintain fail-over state. * <tr><td> * </table> * - * @todo Explain the system property: amqj.shared_read_write_pool. How does putting the protocol codec filter before the - * async write filter make it a shared pool? The pooling filter uses the same thread pool for reading and writing - * anyway, see {@link org.apache.qpid.pool.PoolingFilter}, docs for comments. Will putting the protocol codec - * filter before it mean not doing the read/write asynchronously but in the main filter thread? * @todo Use a single handler instance, by shifting everything to do with the 'protocol session' state, including * failover state, into AMQProtocolSession, and tracking that from AMQConnection? The lifecycles of * AMQProtocolSesssion and AMQConnection will be the same, so if there is high cohesion between them, they could @@ -120,13 +118,15 @@ import java.util.concurrent.CountDownLatch; * held per protocol handler, per protocol session, per network connection, per channel, in seperate classes, so * that lifecycles of the fields match lifecycles of their containing objects. */ -public class AMQProtocolHandler extends IoHandlerAdapter +public class AMQProtocolHandler implements ProtocolEngine { /** Used for debugging. */ private static final Logger _logger = LoggerFactory.getLogger(AMQProtocolHandler.class); private static final Logger _protocolLogger = LoggerFactory.getLogger("qpid.protocol"); private static final boolean PROTOCOL_DEBUG = (System.getProperty("amqj.protocol.logging.level") != null); + private static final long MAXIMUM_STATE_WAIT_TIME = Long.parseLong(System.getProperty("amqj.MaximumStateWait", "30000")); + /** * The connection that this protocol handler is associated with. There is a 1-1 mapping between connection * instances and protocol handler instances. @@ -137,7 +137,7 @@ public class AMQProtocolHandler extends IoHandlerAdapter private volatile AMQProtocolSession _protocolSession; /** Holds the state of the protocol session. */ - private AMQStateManager _stateManager = new AMQStateManager(); + private AMQStateManager _stateManager; /** Holds the method listeners, */ private final CopyOnWriteArraySet<AMQMethodListener> _frameListeners = new CopyOnWriteArraySet<AMQMethodListener>(); @@ -162,7 +162,19 @@ public class AMQProtocolHandler extends IoHandlerAdapter private FailoverException _lastFailoverException; /** Defines the default timeout to use for synchronous protocol commands. */ - private final long DEFAULT_SYNC_TIMEOUT = 1000 * 30; + private final long DEFAULT_SYNC_TIMEOUT = Long.getLong("amqj.default_syncwrite_timeout", 1000 * 30); + + /** Object to lock on when changing the latch */ + private Object _failoverLatchChange = new Object(); + private AMQCodecFactory _codecFactory; + private Job _readJob; + private Job _writeJob; + private ReferenceCountingExecutorService _poolReference = ReferenceCountingExecutorService.getInstance(); + private NetworkDriver _networkDriver; + private ProtocolVersion _suggestedProtocolVersion; + + private long _writtenBytes; + private long _readBytes; /** * Creates a new protocol handler, associated with the specified client connection instance. @@ -172,101 +184,30 @@ public class AMQProtocolHandler extends IoHandlerAdapter public AMQProtocolHandler(AMQConnection con) { _connection = con; - } - - /** - * Invoked by MINA when a MINA session for a new connection is created. This method sets up the filter chain on the - * session, which filters the events handled by this handler. The filter chain consists of, handing off events - * to an asynchronous thread pool, optionally encoding/decoding ssl, encoding/decoding AMQP. - * - * @param session The MINA session. - * - * @throws Exception Any underlying exceptions are allowed to fall through to MINA. - */ - public void sessionCreated(IoSession session) throws Exception - { - _logger.debug("Protocol session created for session " + System.identityHashCode(session)); - _failoverHandler = new FailoverHandler(this, session); - - final ProtocolCodecFilter pcf = new ProtocolCodecFilter(new AMQCodecFactory(false)); - - if (Boolean.getBoolean("amqj.shared_read_write_pool")) - { - session.getFilterChain().addBefore("AsynchronousWriteFilter", "protocolFilter", pcf); - } - else - { - session.getFilterChain().addLast("protocolFilter", pcf); - } - // we only add the SSL filter where we have an SSL connection - if (_connection.getSSLConfiguration() != null) - { - SSLConfiguration sslConfig = _connection.getSSLConfiguration(); - SSLContextFactory sslFactory = - new SSLContextFactory(sslConfig.getKeystorePath(), sslConfig.getKeystorePassword(), sslConfig.getCertType()); - SSLFilter sslFilter = new SSLFilter(sslFactory.buildClientContext()); - sslFilter.setUseClientMode(true); - session.getFilterChain().addBefore("protocolFilter", "ssl", sslFilter); - } - - try - { - ReadWriteThreadModel threadModel = ReadWriteThreadModel.getInstance(); - threadModel.getAsynchronousReadFilter().createNewJobForSession(session); - threadModel.getAsynchronousWriteFilter().createNewJobForSession(session); - } - catch (RuntimeException e) - { - _logger.error(e.getMessage(), e); - } - - if (Boolean.getBoolean(ClientProperties.PROTECTIO_PROP_NAME)) - { - 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( - ClientProperties.READ_BUFFER_LIMIT_PROP_NAME, ClientProperties.READ_BUFFER_LIMIT_DEFAULT))); - readfilter.attach(chain); - - WriteBufferLimitFilterBuilder writefilter = new WriteBufferLimitFilterBuilder(); - writefilter.setMaximumConnectionBufferSize(Integer.parseInt(System.getProperty( - ClientProperties.WRITE_BUFFER_LIMIT_PROP_NAME, ClientProperties.WRITE_BUFFER_LIMIT_DEFAULT))); - 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); - - _stateManager.setProtocolSession(_protocolSession); - - _protocolSession.init(); + _protocolSession = new AMQProtocolSession(this, _connection); + _stateManager = new AMQStateManager(_protocolSession); + _codecFactory = new AMQCodecFactory(false, _protocolSession); + _readJob = new Job(_poolReference, Job.MAX_JOB_EVENTS, true); + _writeJob = new Job(_poolReference, Job.MAX_JOB_EVENTS, false); + _poolReference.acquireExecutorService(); + _failoverHandler = new FailoverHandler(this); } /** * Called when we want to create a new IoTransport session - * @param brokerDetail + * @param brokerDetail */ public void createIoTransportSession(BrokerDetails brokerDetail) { _protocolSession = new AMQProtocolSession(this, _connection); _stateManager.setProtocolSession(_protocolSession); IoTransport.connect_0_9(getProtocolSession(), - brokerDetail.getHost(), brokerDetail.getPort()); + brokerDetail.getHost(), + brokerDetail.getPort(), + brokerDetail.useSSL()); _protocolSession.init(); } - + /** * Called when the network connection is closed. This can happen, either because the client explicitly requested * that the connection be closed, in which case nothing is done, or because the connection died. In the case @@ -274,16 +215,10 @@ public class AMQProtocolHandler extends IoHandlerAdapter * process will be started, provided that it is the clients policy to allow failover, and provided that a failover * has not already been started or failed. * - * <p/>It is important to note that when the connection dies this method may be called or {@link #exceptionCaught} - * may be called first followed by this method. This depends on whether the client was trying to send data at the - * time of the failure. - * - * @param session The MINA session. - * * @todo Clarify: presumably exceptionCaught is called when the client is sending during a connection failure and * not otherwise? The above comment doesn't make that clear. */ - public void sessionClosed(IoSession session) + public void closed() { if (_connection.isClosed()) { @@ -322,7 +257,8 @@ public class AMQProtocolHandler extends IoHandlerAdapter { _logger.debug("sessionClose() not allowed to failover"); _connection.exceptionReceived(new AMQDisconnectedException( - "Server closed connection and reconnection " + "not permitted.", null)); + "Server closed connection and reconnection " + "not permitted.", + _stateManager.getLastException())); } else { @@ -337,41 +273,37 @@ public class AMQProtocolHandler extends IoHandlerAdapter /** See {@link FailoverHandler} to see rationale for separate thread. */ private void startFailoverThread() { - Thread failoverThread = new Thread(_failoverHandler); - failoverThread.setName("Failover"); - // Do not inherit daemon-ness from current thread as this can be a daemon - // thread such as a AnonymousIoService thread. - failoverThread.setDaemon(false); - failoverThread.start(); + if(!_connection.isClosed()) + { + Thread failoverThread = new Thread(_failoverHandler); + failoverThread.setName("Failover"); + // Do not inherit daemon-ness from current thread as this can be a daemon + // thread such as a AnonymousIoService thread. + failoverThread.setDaemon(false); + failoverThread.start(); + } } - public void sessionIdle(IoSession session, IdleStatus status) throws Exception + public void readerIdle() { - _logger.debug("Protocol Session [" + this + ":" + session + "] idle: " + status); - if (IdleStatus.WRITER_IDLE.equals(status)) - { - // write heartbeat frame: - _logger.debug("Sent heartbeat"); - session.write(HeartbeatBody.FRAME); - HeartbeatDiagnostics.sent(); - } - else if (IdleStatus.READER_IDLE.equals(status)) - { - // failover: - HeartbeatDiagnostics.timeout(); - _logger.warn("Timed out while waiting for heartbeat from peer."); - session.close(); - } + _logger.debug("Protocol Session [" + this + "] idle: reader"); + // failover: + HeartbeatDiagnostics.timeout(); + _logger.warn("Timed out while waiting for heartbeat from peer."); + _networkDriver.close(); + } + + public void writerIdle() + { + _logger.debug("Protocol Session [" + this + "] idle: reader"); + writeFrame(HeartbeatBody.FRAME); + HeartbeatDiagnostics.sent(); } /** - * Invoked when any exception is thrown by a user IoHandler implementation or by MINA. If the cause is an - * IOException, MINA will close the connection automatically. - * - * @param session The MINA session. - * @param cause The exception that triggered this event. + * Invoked when any exception is thrown by the NetworkDriver */ - public void exceptionCaught(IoSession session, Throwable cause) + public void exception(Throwable cause) { if (_failoverState == FailoverState.NOT_STARTED) { @@ -379,9 +311,9 @@ public class AMQProtocolHandler extends IoHandlerAdapter if ((cause instanceof AMQConnectionClosedException) || cause instanceof IOException) { _logger.info("Exception caught therefore going to attempt failover: " + cause, cause); - // this will attemp failover - - sessionClosed(session); + // this will attempt failover + _networkDriver.close(); + closed(); } else { @@ -428,12 +360,12 @@ public class AMQProtocolHandler extends IoHandlerAdapter * @param e the exception to propagate * * @see #propagateExceptionToFrameListeners - * @see #propagateExceptionToStateWaiters */ public void propagateExceptionToAllWaiters(Exception e) { + getStateManager().error(e); + propagateExceptionToFrameListeners(e); - propagateExceptionToStateWaiters(e); } /** @@ -450,33 +382,20 @@ public class AMQProtocolHandler extends IoHandlerAdapter */ public void propagateExceptionToFrameListeners(Exception e) { - if (!_frameListeners.isEmpty()) + synchronized (_frameListeners) { - final Iterator it = _frameListeners.iterator(); - while (it.hasNext()) + if (!_frameListeners.isEmpty()) { - final AMQMethodListener ml = (AMQMethodListener) it.next(); - ml.error(e); + final Iterator it = _frameListeners.iterator(); + while (it.hasNext()) + { + final AMQMethodListener ml = (AMQMethodListener) it.next(); + ml.error(e); + } } } } - /** - * This caters for the case where we only need to propogate an exception to the the state manager to interupt any - * thing waiting for a state change. - * - * Currently (2008-07-15) the state manager is only used during 0-8/0-9 Connection establishement. - * - * Normally the state manager would not need to be notified without notifiying the frame listeners so in normal - * cases {@link #propagateExceptionToAllWaiters} would be the correct choice. - * - * @param e the exception to propagate - */ - public void propagateExceptionToStateWaiters(Exception e) - { - getStateManager().error(e); - } - public void notifyFailoverStarting() { // Set the last exception in the sync block to ensure the ordering with add. @@ -499,48 +418,81 @@ public class AMQProtocolHandler extends IoHandlerAdapter private static int _messageReceivedCount; - public void messageReceived(IoSession session, Object message) throws Exception - { - if (PROTOCOL_DEBUG) - { - _protocolLogger.info(String.format("RECV: [%s] %s", this, message)); - } - if(message instanceof AMQFrame) + public void received(ByteBuffer msg) + { + try { - final boolean debug = _logger.isDebugEnabled(); - final long msgNumber = ++_messageReceivedCount; + _readBytes += msg.remaining(); + final ArrayList<AMQDataBlock> dataBlocks = _codecFactory.getDecoder().decodeBuffer(msg); - if (debug && ((msgNumber % 1000) == 0)) + Job.fireAsynchEvent(_poolReference.getPool(), _readJob, new Runnable() { - _logger.debug("Received " + _messageReceivedCount + " protocol messages"); - } - - AMQFrame frame = (AMQFrame) message; - - final AMQBody bodyFrame = frame.getBodyFrame(); - HeartbeatDiagnostics.received(bodyFrame instanceof HeartbeatBody); - - bodyFrame.handle(frame.getChannel(), _protocolSession); - - _connection.bytesReceived(_protocolSession.getIoSession().getReadBytes()); + public void run() + { + // Decode buffer + + for (AMQDataBlock message : dataBlocks) + { + + try + { + if (PROTOCOL_DEBUG) + { + _protocolLogger.info(String.format("RECV: [%s] %s", this, message)); + } + + if(message instanceof AMQFrame) + { + final boolean debug = _logger.isDebugEnabled(); + final long msgNumber = ++_messageReceivedCount; + + if (debug && ((msgNumber % 1000) == 0)) + { + _logger.debug("Received " + _messageReceivedCount + " protocol messages"); + } + + AMQFrame frame = (AMQFrame) message; + + final AMQBody bodyFrame = frame.getBodyFrame(); + + HeartbeatDiagnostics.received(bodyFrame instanceof HeartbeatBody); + + bodyFrame.handle(frame.getChannel(), _protocolSession); + + _connection.bytesReceived(_readBytes); + } + 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; + _suggestedProtocolVersion = protocolInit.checkVersion(); + + // get round a bug in old versions of qpid whereby the connection is not closed + _stateManager.changeState(AMQState.CONNECTION_CLOSED); + } + } + catch (Exception e) + { + _logger.error("Exception processing frame", e); + propagateExceptionToFrameListeners(e); + exception(e); + } + } + } + }); } - else if (message instanceof ProtocolInitiation) + catch (Exception e) { - // 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); + propagateExceptionToFrameListeners(e); + exception(e); } } - public void methodBodyReceived(final int channelId, final AMQBody bodyFrame, IoSession session)//, final IoSession session) + public void methodBodyReceived(final int channelId, final AMQBody bodyFrame) throws AMQException { @@ -556,18 +508,20 @@ public class AMQProtocolHandler extends IoHandlerAdapter { boolean wasAnyoneInterested = getStateManager().methodReceived(evt); - if (!_frameListeners.isEmpty()) + synchronized (_frameListeners) { - //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()) + if (!_frameListeners.isEmpty()) { - final AMQMethodListener listener = (AMQMethodListener) it.next(); - wasAnyoneInterested = listener.methodReceived(evt) || wasAnyoneInterested; + //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()) + { + 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:" @@ -578,32 +532,13 @@ public class AMQProtocolHandler extends IoHandlerAdapter { propagateExceptionToFrameListeners(e); - exceptionCaught(session, e); + exception(e); } } private static int _messagesOut; - public void messageSent(IoSession session, Object message) throws Exception - { - if (PROTOCOL_DEBUG) - { - _protocolLogger.debug(String.format("SEND: [%s] %s", this, message)); - } - - final long sentMessages = _messagesOut++; - - final boolean debug = _logger.isDebugEnabled(); - - if (debug && ((sentMessages % 1000) == 0)) - { - _logger.debug("Sent " + _messagesOut + " protocol messages"); - } - - _connection.bytesSent(session.getWrittenBytes()); - } - public StateWaiter createWaiter(Set<AMQState> states) throws AMQException { return getStateManager().createWaiter(states); @@ -617,12 +552,40 @@ public class AMQProtocolHandler extends IoHandlerAdapter */ public void writeFrame(AMQDataBlock frame) { - _protocolSession.writeFrame(frame); + writeFrame(frame, false); } public void writeFrame(AMQDataBlock frame, boolean wait) { - _protocolSession.writeFrame(frame, wait); + final ByteBuffer buf = frame.toNioByteBuffer(); + _writtenBytes += buf.remaining(); + Job.fireAsynchEvent(_poolReference.getPool(), _writeJob, new Runnable() + { + public void run() + { + _networkDriver.send(buf); + } + }); + if (PROTOCOL_DEBUG) + { + _protocolLogger.debug(String.format("SEND: [%s] %s", this, frame)); + } + + final long sentMessages = _messagesOut++; + + final boolean debug = _logger.isDebugEnabled(); + + if (debug && ((sentMessages % 1000) == 0)) + { + _logger.debug("Sent " + _messagesOut + " protocol messages"); + } + + _connection.bytesSent(_writtenBytes); + + if (wait) + { + _networkDriver.flush(); + } } /** @@ -657,9 +620,30 @@ public class AMQProtocolHandler extends IoHandlerAdapter throw _lastFailoverException; } + if(_stateManager.getCurrentState() == AMQState.CONNECTION_CLOSED || + _stateManager.getCurrentState() == AMQState.CONNECTION_CLOSING) + { + Exception e = _stateManager.getLastException(); + if (e != null) + { + if (e instanceof AMQException) + { + AMQException amqe = (AMQException) e; + + throw amqe.cloneForCurrentThread(); + } + else + { + throw new AMQException(AMQConstant.INTERNAL_ERROR, e.getMessage(), e); + } + } + } + _frameListeners.add(listener); + //FIXME: At this point here we should check or before add we should check _stateManager is in an open + // state so as we don't check we are likely just to time out here as I believe is being seen in QPID-1255 } - _protocolSession.writeFrame(frame); + writeFrame(frame); return listener.blockForFrame(timeout); // When control resumes before this line, a reply will have been received @@ -703,38 +687,42 @@ public class AMQProtocolHandler extends IoHandlerAdapter */ public void closeConnection(long timeout) throws AMQException { - getStateManager().changeState(AMQState.CONNECTION_CLOSING); - 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 - { - syncWrite(frame, ConnectionCloseOkBody.class, timeout); - _protocolSession.closeProtocolSession(); - } - catch (AMQTimeoutException e) - { - _protocolSession.closeProtocolSession(false); - } - catch (FailoverException e) + //If the connection is already closed then don't do a syncWrite + if (!getStateManager().getCurrentState().equals(AMQState.CONNECTION_CLOSED)) { - _logger.debug("FailoverException interrupted connection close, ignoring as connection close anyway."); + try + { + syncWrite(frame, ConnectionCloseOkBody.class, timeout); + _networkDriver.close(); + closed(); + } + catch (AMQTimeoutException e) + { + closed(); + } + catch (FailoverException e) + { + _logger.debug("FailoverException interrupted connection close, ignoring as connection close anyway."); + } } + _poolReference.releaseExecutorService(); } /** @return the number of bytes read from this protocol session */ public long getReadBytes() { - return _protocolSession.getIoSession().getReadBytes(); + return _readBytes; } /** @return the number of bytes written to this protocol session */ public long getWrittenBytes() { - return _protocolSession.getIoSession().getWrittenBytes(); + return _writtenBytes; } public void failover(String host, int port) @@ -747,9 +735,15 @@ public class AMQProtocolHandler extends IoHandlerAdapter public void blockUntilNotFailingOver() throws InterruptedException { - if (_failoverLatch != null) + synchronized(_failoverLatchChange) { - _failoverLatch.await(); + if (_failoverLatch != null) + { + if(!_failoverLatch.await(MAXIMUM_STATE_WAIT_TIME, TimeUnit.MILLISECONDS)) + { + + } + } } } @@ -765,7 +759,10 @@ public class AMQProtocolHandler extends IoHandlerAdapter public void setFailoverLatch(CountDownLatch failoverLatch) { - _failoverLatch = failoverLatch; + synchronized (_failoverLatchChange) + { + _failoverLatch = failoverLatch; + } } public AMQConnection getConnection() @@ -781,6 +778,7 @@ public class AMQProtocolHandler extends IoHandlerAdapter public void setStateManager(AMQStateManager stateManager) { _stateManager = stateManager; + _stateManager.setProtocolSession(_protocolSession); } public AMQProtocolSession getProtocolSession() @@ -817,4 +815,41 @@ public class AMQProtocolHandler extends IoHandlerAdapter { return _protocolSession.getProtocolVersion(); } + + public SocketAddress getRemoteAddress() + { + return _networkDriver.getRemoteAddress(); + } + + public SocketAddress getLocalAddress() + { + return _networkDriver.getLocalAddress(); + } + + public void setNetworkDriver(NetworkDriver driver) + { + _networkDriver = driver; + } + + /** @param delay delay in seconds (not ms) */ + void initHeartbeats(int delay) + { + if (delay > 0) + { + getNetworkDriver().setMaxWriteIdle(delay); + getNetworkDriver().setMaxReadIdle(HeartbeatConfig.CONFIG.getTimeout(delay)); + HeartbeatDiagnostics.init(delay, HeartbeatConfig.CONFIG.getTimeout(delay)); + } + } + + public NetworkDriver getNetworkDriver() + { + return _networkDriver; + } + + public ProtocolVersion getSuggestedProtocolVersion() + { + return _suggestedProtocolVersion; + } + } 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 5e12a5e6f8..2d59146b43 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 @@ -20,11 +20,6 @@ */ 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; @@ -33,6 +28,7 @@ import javax.security.sasl.SaslClient; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import org.apache.commons.lang.StringUtils; import org.apache.qpid.AMQException; import org.apache.qpid.client.AMQConnection; import org.apache.qpid.client.AMQSession; @@ -65,10 +61,6 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession protected static final String SASL_CLIENT = "SASLClient"; - protected final IoSession _minaProtocolSession; - - protected WriteFuture _lastWriteFuture; - /** * The handler from which this session was created and which is used to handle protocol events. We send failover * events to the handler. @@ -102,28 +94,15 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession protected final AMQConnection _connection; - private static final int FAST_CHANNEL_ACCESS_MASK = 0xFFFFFFF0; + private ConnectionTuneParameters _connectionTuneParameters; - public AMQProtocolSession(AMQProtocolHandler protocolHandler, IoSession protocolSession, AMQConnection connection) - { - _protocolHandler = protocolHandler; - _minaProtocolSession = protocolSession; - _minaProtocolSession.setAttachment(this); - // properties of the connection are made available to the event handlers - _minaProtocolSession.setAttribute(AMQ_CONNECTION, connection); - // fixme - real value needed - _minaProtocolSession.setWriteTimeout(LAST_WRITE_FUTURE_JOIN_TIMEOUT); - _protocolVersion = connection.getProtocolVersion(); - _methodDispatcher = ClientMethodDispatcherImpl.newMethodDispatcher(ProtocolVersion.getLatestSupportedVersion(), - this); - _connection = connection; + private SaslClient _saslClient; - } + private static final int FAST_CHANNEL_ACCESS_MASK = 0xFFFFFFF0; public AMQProtocolSession(AMQProtocolHandler protocolHandler, AMQConnection connection) { _protocolHandler = protocolHandler; - _minaProtocolSession = null; _protocolVersion = connection.getProtocolVersion(); _methodDispatcher = ClientMethodDispatcherImpl.newMethodDispatcher(ProtocolVersion.getLatestSupportedVersion(), this); @@ -134,7 +113,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(_connection.getProtocolVersion())); + _protocolHandler.writeFrame(new ProtocolInitiation(_connection.getProtocolVersion())); } public String getClientID() @@ -175,14 +154,9 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession return getAMQConnection().getPassword(); } - public IoSession getIoSession() - { - return _minaProtocolSession; - } - public SaslClient getSaslClient() { - return (SaslClient) _minaProtocolSession.getAttribute(SASL_CLIENT); + return _saslClient; } /** @@ -192,28 +166,21 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession */ public void setSaslClient(SaslClient client) { - if (client == null) - { - _minaProtocolSession.removeAttribute(SASL_CLIENT); - } - else - { - _minaProtocolSession.setAttribute(SASL_CLIENT, client); - } + _saslClient = client; } public ConnectionTuneParameters getConnectionTuneParameters() { - return (ConnectionTuneParameters) _minaProtocolSession.getAttribute(CONNECTION_TUNE_PARAMETERS); + return _connectionTuneParameters; } public void setConnectionTuneParameters(ConnectionTuneParameters params) { - _minaProtocolSession.setAttribute(CONNECTION_TUNE_PARAMETERS, params); + _connectionTuneParameters = params; AMQConnection con = getAMQConnection(); con.setMaximumChannelCount(params.getChannelMax()); con.setMaximumFrameSize(params.getFrameMax()); - initHeartbeats((int) params.getHeartbeat()); + _protocolHandler.initHeartbeats((int) params.getHeartbeat()); } /** @@ -225,7 +192,7 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession * @throws AMQException if this was not expected */ public void unprocessedMessageReceived(final int channelId, UnprocessedMessage message) throws AMQException - { + { if ((channelId & FAST_CHANNEL_ACCESS_MASK) == 0) { _channelId2UnprocessedMsgArray[channelId] = message; @@ -335,21 +302,12 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession */ public void writeFrame(AMQDataBlock frame) { - writeFrame(frame, false); + _protocolHandler.writeFrame(frame); } public void writeFrame(AMQDataBlock frame, boolean wait) { - WriteFuture f = _minaProtocolSession.write(frame); - if (wait) - { - // fixme -- time out? - f.join(); - } - else - { - _lastWriteFuture = f; - } + _protocolHandler.writeFrame(frame, wait); } /** @@ -407,33 +365,12 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession public AMQConnection getAMQConnection() { - return (AMQConnection) _minaProtocolSession.getAttribute(AMQ_CONNECTION); + return _connection; } public void closeProtocolSession() throws AMQException { - closeProtocolSession(true); - } - - public void closeProtocolSession(boolean waitLast) throws AMQException - { - _logger.debug("Waiting for last write to join."); - if (waitLast && (_lastWriteFuture != null)) - { - _lastWriteFuture.join(LAST_WRITE_FUTURE_JOIN_TIMEOUT); - } - - _logger.debug("Closing protocol session"); - - final CloseFuture future = _minaProtocolSession.close(); - - // There is no recovery we can do if the join on the close failes so simply mark the connection CLOSED - // then wait for the connection to close. - // ritchiem: Could this release BlockingWaiters to early? The close has been done as much as possible so any - // error now shouldn't matter. - - _protocolHandler.getStateManager().changeState(AMQState.CONNECTION_CLOSED); - future.join(LAST_WRITE_FUTURE_JOIN_TIMEOUT); + _protocolHandler.closeConnection(0); } public void failover(String host, int port) @@ -449,22 +386,11 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession id = _queueId++; } // get rid of / and : and ; from address for spec conformance - String localAddress = StringUtils.replaceChars(_minaProtocolSession.getLocalAddress().toString(), "/;:", ""); + String localAddress = StringUtils.replaceChars(_protocolHandler.getLocalAddress().toString(), "/;:", ""); return new AMQShortString("tmp_" + localAddress + "_" + id); } - /** @param delay delay in seconds (not ms) */ - void initHeartbeats(int delay) - { - if (delay > 0) - { - _minaProtocolSession.setIdleTime(IdleStatus.WRITER_IDLE, delay); - _minaProtocolSession.setIdleTime(IdleStatus.READER_IDLE, HeartbeatConfig.CONFIG.getTimeout(delay)); - HeartbeatDiagnostics.init(delay, HeartbeatConfig.CONFIG.getTimeout(delay)); - } - } - public void confirmConsumerCancelled(int channelId, AMQShortString consumerTag) { final AMQSession session = getSession(channelId); @@ -477,9 +403,7 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession _protocolVersion = pv; _methodRegistry = MethodRegistry.getMethodRegistry(pv); _methodDispatcher = ClientMethodDispatcherImpl.newMethodDispatcher(pv, this); - - // _registry = MainRegistry.getVersionSpecificRegistry(versionMajor, versionMinor); - } + } public byte getProtocolMinorVersion() { @@ -496,11 +420,6 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession return _protocolVersion; } -// public VersionSpecificRegistry getRegistry() -// { -// return _registry; -// } - public MethodRegistry getMethodRegistry() { return _methodRegistry; @@ -530,7 +449,7 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession public void methodFrameReceived(final int channel, final AMQMethodBody amqMethodBody) throws AMQException { - _protocolHandler.methodBodyReceived(channel, amqMethodBody, _minaProtocolSession); + _protocolHandler.methodBodyReceived(channel, amqMethodBody); } public void notifyError(Exception error) @@ -542,4 +461,11 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession { // No-op, interface munging } + + + @Override + public String toString() + { + return "AMQProtocolSession[" + _connection + ']'; + } } diff --git a/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties b/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties index 89ee8337f8..1fcfde3579 100644 --- a/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties +++ b/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties @@ -18,4 +18,5 @@ # CallbackHandler.CRAM-MD5-HASHED=org.apache.qpid.client.security.UsernameHashedPasswordCallbackHandler CallbackHandler.CRAM-MD5=org.apache.qpid.client.security.UsernamePasswordCallbackHandler +CallbackHandler.AMQPLAIN=org.apache.qpid.client.security.UsernamePasswordCallbackHandler CallbackHandler.PLAIN=org.apache.qpid.client.security.UsernamePasswordCallbackHandler diff --git a/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.java b/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.java index 803b34b7fa..2b4261b4b7 100644 --- a/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.java +++ b/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.java @@ -85,8 +85,19 @@ public class DynamicSaslRegistrar if (factories.size() > 0) { - Security.insertProviderAt(new JCAProvider(factories), 0); - _logger.debug("Dynamic SASL provider added as a security provider"); + // Ensure we are used before the defaults + if (Security.insertProviderAt(new JCAProvider(factories), 1) == -1) + { + _logger.error("Unable to load custom SASL providers."); + } + else + { + _logger.info("Additional SASL providers successfully registered."); + } + } + else + { + _logger.warn("No additional SASL providers registered."); } } catch (IOException e) @@ -185,6 +196,7 @@ public class DynamicSaslRegistrar continue; } + _logger.debug("Registering class "+ clazz.getName() +" for mechanism "+mechanism); factoriesToRegister.put(mechanism, (Class<? extends SaslClientFactory>) clazz); } catch (Exception ex) diff --git a/java/client/src/main/java/org/apache/qpid/client/security/JCAProvider.java b/java/client/src/main/java/org/apache/qpid/client/security/JCAProvider.java index 5a2c5ac5c1..828d26ed0d 100644 --- a/java/client/src/main/java/org/apache/qpid/client/security/JCAProvider.java +++ b/java/client/src/main/java/org/apache/qpid/client/security/JCAProvider.java @@ -26,6 +26,7 @@ import org.slf4j.LoggerFactory; import javax.security.sasl.SaslClientFactory; import java.security.Provider; +import java.security.Security; import java.util.Map; /** @@ -49,10 +50,10 @@ public class JCAProvider extends Provider */ public JCAProvider(Map<String, Class<? extends SaslClientFactory>> providerMap) { - super("AMQSASLProvider", 1.0, "A JCA provider that registers all " + super("AMQSASLProvider-Client", 1.0, "A JCA provider that registers all " + "AMQ SASL providers that want to be registered"); register(providerMap); - // Security.addProvider(this); +// Security.addProvider(this); } /** @@ -64,7 +65,7 @@ public class JCAProvider extends Provider { for (Map.Entry<String, Class<? extends SaslClientFactory>> me : providerMap.entrySet()) { - put("SaslClientFactory." + me.getKey(), me.getValue().getName()); + put( "SaslClientFactory."+me.getKey(), me.getValue().getName()); log.debug("Registered SASL Client factory for " + me.getKey() + " as " + me.getValue().getName()); } } 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 f8645139f2..9c7d62670c 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 @@ -31,6 +31,7 @@ import org.slf4j.LoggerFactory; import java.util.Set; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import java.io.IOException; /** * The state manager is responsible for managing the state of the protocol session. <p/> @@ -86,7 +87,7 @@ public class AMQStateManager implements AMQMethodListener return _currentState; } - public void changeState(AMQState newState) throws AMQException + public void changeState(AMQState newState) { _logger.debug("State changing to " + newState + " from old state " + _currentState); @@ -136,6 +137,22 @@ public class AMQStateManager implements AMQMethodListener */ public void error(Exception error) { + if (error instanceof AMQException) + { + // AMQException should be being notified before closing the + // ProtocolSession. Which will change the State to CLOSED. + // if we have a hard error. + if (((AMQException)error).isHardError()) + { + changeState(AMQState.CONNECTION_CLOSING); + } + } + else + { + // Be on the safe side here and mark the connection closed + changeState(AMQState.CONNECTION_CLOSED); + } + if (_waiters.size() == 0) { _logger.error("No Waiters for error saving as last error:" + error.getMessage()); @@ -196,4 +213,9 @@ public class AMQStateManager implements AMQMethodListener { return _lastException; } + + public void clearLastException() + { + _lastException = null; + } } diff --git a/java/client/src/main/java/org/apache/qpid/client/state/StateWaiter.java b/java/client/src/main/java/org/apache/qpid/client/state/StateWaiter.java index 4695b195d5..79f438d35d 100644 --- a/java/client/src/main/java/org/apache/qpid/client/state/StateWaiter.java +++ b/java/client/src/main/java/org/apache/qpid/client/state/StateWaiter.java @@ -113,7 +113,6 @@ public class StateWaiter extends BlockingWaiter<AMQState> { _logger.error("Failover occured whilst waiting for states:" + _awaitStates); - e.printStackTrace(); return null; } } 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 b2f7ae8395..1ac8f62e32 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 @@ -20,27 +20,20 @@ */ package org.apache.qpid.client.transport; +import java.io.IOException; +import java.net.InetSocketAddress; + 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; - +import org.apache.qpid.client.SSLConfiguration; import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.jms.BrokerDetails; -import org.apache.qpid.pool.ReadWriteThreadModel; - +import org.apache.qpid.ssl.SSLContextFactory; +import org.apache.qpid.transport.network.mina.MINANetworkDriver; import org.slf4j.Logger; 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 { private static final Logger _logger = LoggerFactory.getLogger(SocketTransportConnection.class); @@ -71,61 +64,27 @@ public class SocketTransportConnection implements ITransportConnection } final IoConnector ioConnector = _socketConnectorFactory.newSocketConnector(); - SocketConnectorConfig cfg = (SocketConnectorConfig) ioConnector.getDefaultConfig(); - - // if we do not use our own thread model we get the MINA default which is to use - // its own leader-follower model - boolean readWriteThreading = Boolean.getBoolean("amqj.shared_read_write_pool"); - if (readWriteThreading) - { - cfg.setThreadModel(ReadWriteThreadModel.getInstance()); - } - - SocketSessionConfig scfg = (SocketSessionConfig) cfg.getSessionConfig(); - scfg.setTcpNoDelay("true".equalsIgnoreCase(System.getProperty("amqj.tcpNoDelay", "true"))); - scfg.setSendBufferSize(Integer.getInteger("amqj.sendBufferSize", DEFAULT_BUFFER_SIZE)); - _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; 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 - if (future.join(brokerDetail.getTimeout())) - { - // we call getSession which throws an IOException if there has been an error connecting - future.getSession(); - } - else + + SSLConfiguration sslConfig = protocolHandler.getConnection().getSSLConfiguration(); + SSLContextFactory sslFactory = null; + if (sslConfig != null) { - throw new IOException("Timeout waiting for connection."); + sslFactory = new SSLContextFactory(sslConfig.getKeystorePath(), sslConfig.getKeystorePassword(), sslConfig.getCertType()); } + + MINANetworkDriver driver = new MINANetworkDriver(ioConnector); + driver.open(brokerDetail.getPort(), address.getAddress(), protocolHandler, null, sslFactory); + protocolHandler.setNetworkDriver(driver); } } 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 6c12821c74..a4f8bb0166 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 @@ -20,6 +20,12 @@ */ package org.apache.qpid.client.transport; +import java.io.IOException; +import java.net.Socket; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + import org.apache.mina.common.IoConnector; import org.apache.mina.common.IoHandlerAdapter; import org.apache.mina.common.IoServiceConfig; @@ -30,16 +36,12 @@ import org.apache.mina.transport.vmpipe.VmPipeAcceptor; import org.apache.mina.transport.vmpipe.VmPipeAddress; import org.apache.qpid.client.vmbroker.AMQVMBrokerCreationException; import org.apache.qpid.jms.BrokerDetails; -import org.apache.qpid.pool.ReadWriteThreadModel; +import org.apache.qpid.protocol.ProtocolEngineFactory; +import org.apache.qpid.thread.QpidThreadExecutor; +import org.apache.qpid.transport.network.mina.MINANetworkDriver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; -import java.util.HashMap; -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 * connector, which currently always uses TCP/IP sockets. It creates the "protocol handler" which deals with MINA @@ -61,7 +63,7 @@ public class TransportConnection private static Logger _logger = LoggerFactory.getLogger(TransportConnection.class); - private static final String DEFAULT_QPID_SERVER = "org.apache.qpid.server.protocol.AMQPFastProtocolHandler"; + private static final String DEFAULT_QPID_SERVER = "org.apache.qpid.server.protocol.AMQProtocolEngineFactory"; private static Map<String, Socket> _openSocketRegister = new ConcurrentHashMap<String, Socket>(); @@ -75,7 +77,7 @@ public class TransportConnection return _openSocketRegister.remove(socketID); } - public static synchronized ITransportConnection getInstance(BrokerDetails details) throws AMQTransportConnectionException + public static synchronized ITransportConnection getInstance(final BrokerDetails details) throws AMQTransportConnectionException { int transport = getTransport(details.getTransport()); @@ -91,7 +93,22 @@ public class TransportConnection { public IoConnector newSocketConnector() { - return new ExistingSocketConnector(); + ExistingSocketConnector connector = new ExistingSocketConnector(1,new QpidThreadExecutor()); + + Socket socket = TransportConnection.removeOpenSocket(details.getHost()); + + if (socket != null) + { + _logger.info("Using existing Socket:" + socket); + + ((ExistingSocketConnector) connector).setOpenSocket(socket); + } + else + { + throw new IllegalArgumentException("Active Socket must be provided for broker " + + "with 'socket://<SocketID>' transport:" + details); + } + return connector; } }); case TCP: @@ -106,12 +123,12 @@ public class TransportConnection _logger.warn("Using Qpid MultiThreaded NIO - " + (System.getProperties().containsKey("qpidnio") ? "Qpid NIO is new default" : "Sysproperty 'qpidnio' is set")); - result = new MultiThreadSocketConnector(); + result = new MultiThreadSocketConnector(1, new QpidThreadExecutor()); } else { _logger.info("Using Mina NIO"); - result = new SocketConnector(); // non-blocking connector + result = new SocketConnector(1, new QpidThreadExecutor()); // 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 @@ -189,8 +206,6 @@ public class TransportConnection _acceptor = new VmPipeAcceptor(); IoServiceConfig config = _acceptor.getDefaultConfig(); - - config.setThreadModel(ReadWriteThreadModel.getInstance()); } synchronized (_inVmPipeAddress) { @@ -275,7 +290,10 @@ public class TransportConnection { Class[] cnstr = {Integer.class}; Object[] params = {port}; - provider = (IoHandlerAdapter) Class.forName(protocolProviderClass).getConstructor(cnstr).newInstance(params); + + provider = new MINANetworkDriver(); + ProtocolEngineFactory engineFactory = (ProtocolEngineFactory) Class.forName(protocolProviderClass).getConstructor(cnstr).newInstance(params); + ((MINANetworkDriver) provider).setProtocolEngineFactory(engineFactory, true); // Give the broker a second to create _logger.info("Created VMBroker Instance:" + port); } 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 dca6efba67..504d475740 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 @@ -20,25 +20,26 @@ */ package org.apache.qpid.client.transport; +import java.io.IOException; + 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.ReadWriteThreadModel; +import org.apache.qpid.transport.network.mina.MINANetworkDriver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; - public class VmPipeTransportConnection implements ITransportConnection { private static final Logger _logger = LoggerFactory.getLogger(VmPipeTransportConnection.class); private static int _port; + private MINANetworkDriver _networkDriver; + public VmPipeTransportConnection(int port) { _port = port; @@ -47,16 +48,16 @@ public class VmPipeTransportConnection implements ITransportConnection public void connect(AMQProtocolHandler protocolHandler, BrokerDetails brokerDetail) throws IOException { final VmPipeConnector ioConnector = new QpidVmPipeConnector(); - final IoServiceConfig cfg = ioConnector.getDefaultConfig(); - - cfg.setThreadModel(ReadWriteThreadModel.getInstance()); final VmPipeAddress address = new VmPipeAddress(_port); _logger.info("Attempting connection to " + address); - ConnectFuture future = ioConnector.connect(address, protocolHandler); + _networkDriver = new MINANetworkDriver(ioConnector, protocolHandler); + protocolHandler.setNetworkDriver(_networkDriver); + ConnectFuture future = ioConnector.connect(address, _networkDriver); // wait for connection to complete future.join(); // we call getSession which throws an IOException if there has been an error connecting future.getSession(); + _networkDriver.setProtocolEngine(protocolHandler); } } diff --git a/java/client/src/main/java/org/apache/qpid/client/url/URLParser.java b/java/client/src/main/java/org/apache/qpid/client/url/URLParser.java index b975713ad7..f3f74dd332 100644 --- a/java/client/src/main/java/org/apache/qpid/client/url/URLParser.java +++ b/java/client/src/main/java/org/apache/qpid/client/url/URLParser.java @@ -1,4 +1,25 @@ package org.apache.qpid.client.url; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + import java.net.URI; import java.net.URISyntaxException; @@ -37,22 +58,31 @@ public class URLParser if ((connection.getHost() == null) || connection.getHost().equals("")) { - String uid = AMQConnectionFactory.getUniqueClientID(); - if (uid == null) - { - throw URLHelper.parseError(-1, "Client Name not specified", fullURL); + String tmp = connection.getAuthority(); + // hack to read a clientid such as "my_clientID" + if (tmp != null && tmp.indexOf('@') < tmp.length()-1) + { + _url.setClientName(tmp.substring(tmp.indexOf('@')+1,tmp.length())); } else { - _url.setClientName(uid); + String uid = AMQConnectionFactory.getUniqueClientID(); + if (uid == null) + { + throw URLHelper.parseError(-1, "Client Name not specified", fullURL); + } + else + { + _url.setClientName(uid); + } } - } + } else { _url.setClientName(connection.getHost()); } - + String userInfo = connection.getUserInfo(); if (userInfo == null) diff --git a/java/client/src/main/java/org/apache/qpid/client/util/BlockingWaiter.java b/java/client/src/main/java/org/apache/qpid/client/util/BlockingWaiter.java index 67cda957fb..a3d015eadc 100644 --- a/java/client/src/main/java/org/apache/qpid/client/util/BlockingWaiter.java +++ b/java/client/src/main/java/org/apache/qpid/client/util/BlockingWaiter.java @@ -253,7 +253,7 @@ public abstract class BlockingWaiter<T> } else { - System.err.println("WARNING: new error arrived while old one not yet processed"); + System.err.println("WARNING: new error '" + e == null ? "null" : e.getMessage() + "' arrived while old one not yet processed:" + _error.getMessage()); } try diff --git a/java/client/src/main/java/org/apache/qpid/client/util/FlowControllingBlockingQueue.java b/java/client/src/main/java/org/apache/qpid/client/util/FlowControllingBlockingQueue.java index bddbc329ab..ee7fc533a3 100644 --- a/java/client/src/main/java/org/apache/qpid/client/util/FlowControllingBlockingQueue.java +++ b/java/client/src/main/java/org/apache/qpid/client/util/FlowControllingBlockingQueue.java @@ -22,10 +22,11 @@ package org.apache.qpid.client.util; import java.util.Iterator; import java.util.Queue; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ConcurrentLinkedQueue; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * A blocking queue that emits events above a user specified threshold allowing the caller to take action (e.g. flow * control) to try to prevent the queue growing (much) further. The underlying queue itself is not bounded therefore the @@ -36,6 +37,8 @@ import java.util.concurrent.ConcurrentLinkedQueue; */ public class FlowControllingBlockingQueue { + private static final Logger _logger = LoggerFactory.getLogger(FlowControllingBlockingQueue.class); + /** This queue is bounded and is used to store messages before being dispatched to the consumer */ private final Queue _queue = new ConcurrentLinkedQueue(); @@ -46,6 +49,8 @@ public class FlowControllingBlockingQueue /** We require a separate count so we can track whether we have reached the threshold */ private int _count; + + private boolean disableFlowControl; public boolean isEmpty() { @@ -69,6 +74,10 @@ public class FlowControllingBlockingQueue _flowControlHighThreshold = highThreshold; _flowControlLowThreshold = lowThreshold; _listener = listener; + if (highThreshold == 0) + { + disableFlowControl = true; + } } public Object take() throws InterruptedException @@ -84,7 +93,7 @@ public class FlowControllingBlockingQueue } } } - if (_listener != null) + if (!disableFlowControl && _listener != null) { synchronized (_listener) { @@ -93,6 +102,7 @@ public class FlowControllingBlockingQueue _listener.underThreshold(_count); } } + } return o; @@ -106,7 +116,7 @@ public class FlowControllingBlockingQueue notifyAll(); } - if (_listener != null) + if (!disableFlowControl && _listener != null) { synchronized (_listener) { diff --git a/java/client/src/main/java/org/apache/qpid/filter/PropertyExpression.java b/java/client/src/main/java/org/apache/qpid/filter/PropertyExpression.java index 2c05f5ce0f..09152f7f1b 100644 --- a/java/client/src/main/java/org/apache/qpid/filter/PropertyExpression.java +++ b/java/client/src/main/java/org/apache/qpid/filter/PropertyExpression.java @@ -204,6 +204,24 @@ public class PropertyExpression implements Expression } } }); + + JMS_PROPERTY_EXPRESSIONS.put("JMSMessageID", new Expression() + { + public Object evaluate(AbstractJMSMessage message) + { + try + { + return message.getJMSMessageID(); + } + catch (Exception e) + { + _logger.warn("Error evaluating property",e); + + return null; + } + + } + }); } 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 0316255b2c..7227ab247c 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 @@ -34,6 +34,8 @@ 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 String OPTIONS_IDLE_TIMEOUT = "idle_timeout"; + public static final String OPTIONS_SASL_MECHS = "sasl_mechs"; public static final int DEFAULT_PORT = 5672; public static final String SOCKET = "socket"; @@ -55,7 +57,7 @@ public interface BrokerDetails public static final String VIRTUAL_HOST = "virtualhost"; public static final String CLIENT_ID = "client_id"; public static final String USERNAME = "username"; - public static final String PASSWORD = "password"; + public static final String PASSWORD = "password"; String getHost(); @@ -94,6 +96,8 @@ public interface BrokerDetails SSLConfiguration getSSLConfiguration(); void setSSLConfiguration(SSLConfiguration sslConfiguration); + + boolean useSSL(); String toString(); 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 da8cd4f750..03ab967c36 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 @@ -33,9 +33,11 @@ import java.util.List; */ public interface ConnectionURL { - public static final String AMQ_SYNC_PERSISTENCE = "sync_persistence"; - public static final String AMQ_MAXPREFETCH = "maxprefetch"; public static final String AMQ_PROTOCOL = "amqp"; + public static final String OPTIONS_SYNC_PERSISTENCE = "sync_persistence"; + public static final String OPTIONS_MAXPREFETCH = "maxprefetch"; + public static final String OPTIONS_SYNC_ACK = "sync_ack"; + public static final String OPTIONS_SYNC_PUBLISH = "sync_publish"; public static final String OPTIONS_BROKERLIST = "brokerlist"; public static final String OPTIONS_FAILOVER = "failover"; public static final String OPTIONS_FAILOVER_CYCLE = "cyclecount"; 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 8e3ccc3b02..7cdcd32306 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 @@ -20,9 +20,12 @@ */ package org.apache.qpid.jms; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.jms.failover.FailoverExchangeMethod; import org.apache.qpid.jms.failover.FailoverMethod; import org.apache.qpid.jms.failover.FailoverRoundRobinServers; import org.apache.qpid.jms.failover.FailoverSingleServer; +import org.apache.qpid.jms.failover.NoFailover; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -48,7 +51,7 @@ public class FailoverPolicy private long _lastMethodTime; private long _lastFailTime; - public FailoverPolicy(ConnectionURL connectionDetails) + public FailoverPolicy(ConnectionURL connectionDetails, AMQConnection conn) { FailoverMethod method; @@ -88,6 +91,14 @@ public class FailoverPolicy { method = new FailoverRoundRobinServers(connectionDetails); } + else if (failoverMethod.equals(FailoverMethod.FAILOVER_EXCHANGE)) + { + method = new FailoverExchangeMethod(connectionDetails, conn); + } + else if (failoverMethod.equals(FailoverMethod.NO_FAILOVER)) + { + method = new NoFailover(connectionDetails); + } else { try @@ -272,7 +283,7 @@ public class FailoverPolicy public FailoverMethod getCurrentMethod() { - if ((_currentMethod >= 0) && (_currentMethod < (_methods.length - 1))) + if ((_currentMethod >= 0) && (_currentMethod < (_methods.length))) { return _methods[_currentMethod]; } diff --git a/java/client/src/main/java/org/apache/qpid/jms/TopicSubscriber.java b/java/client/src/main/java/org/apache/qpid/jms/TopicSubscriber.java index 80cfa18ec1..1dbe464230 100644 --- a/java/client/src/main/java/org/apache/qpid/jms/TopicSubscriber.java +++ b/java/client/src/main/java/org/apache/qpid/jms/TopicSubscriber.java @@ -1,4 +1,25 @@ package org.apache.qpid.jms; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + import org.apache.qpid.AMQException; diff --git a/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverExchangeMethod.java b/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverExchangeMethod.java new file mode 100644 index 0000000000..960661daea --- /dev/null +++ b/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverExchangeMethod.java @@ -0,0 +1,317 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.jms.failover; + +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.List; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.Session; + +import org.apache.qpid.client.AMQAnyDestination; +import org.apache.qpid.client.AMQBrokerDetails; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.jms.BrokerDetails; +import org.apache.qpid.jms.ConnectionURL; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * When using the Failover exchange a single broker is supplied in the URL. + * The connection will then connect to the cluster using the above broker details. + * Once connected, the membership details of the cluster will be obtained via + * subscribing to a queue bound to the failover exchange. + * + * The failover exchange will provide a list of broker URLs in the format "transport:ip:port" + * Out of this list we only select brokers that match the transport of the original + * broker supplied in the connection URL. + * + * Also properties defined for the original broker will be applied to all the brokers selected + * from the list. + */ + +public class FailoverExchangeMethod implements FailoverMethod, MessageListener +{ + private static final Logger _logger = LoggerFactory.getLogger(FailoverExchangeMethod.class); + + /** This is not safe to use until attainConnection is called */ + private AMQConnection _conn; + + /** Protects the broker list when modifications happens */ + private Object _brokerListLock = new Object(); + + /** The session used to subscribe to failover exchange */ + private Session _ssn; + + private BrokerDetails _originalBrokerDetail; + + /** The index into the hostDetails array of the broker to which we are connected */ + private int _currentBrokerIndex = 0; + + /** The broker currently selected **/ + private BrokerDetails _currentBrokerDetail; + + /** Array of BrokerDetail used to make connections. */ + private ConnectionURL _connectionDetails; + + /** Denotes the number of failed attempts **/ + private int _failedAttemps = 0; + + public FailoverExchangeMethod(ConnectionURL connectionDetails, AMQConnection conn) + { + _connectionDetails = connectionDetails; + _originalBrokerDetail = _connectionDetails.getBrokerDetails(0); + + // This is not safe to use until attainConnection is called, as this ref will not initialized fully. + // The reason being this constructor is called inside the AMWConnection constructor. + // It would be best if we find a way to pass this ref after AMQConnection is fully initialized. + _conn = conn; + } + + private void subscribeForUpdates() throws JMSException + { + if (_ssn == null) + { + _ssn = _conn.createSession(false,Session.AUTO_ACKNOWLEDGE); + MessageConsumer cons = _ssn.createConsumer( + new AMQAnyDestination(new AMQShortString("amq.failover"), + new AMQShortString("amq.failover"), + new AMQShortString(""), + true,true,null,false, + new AMQShortString[0])); + cons.setMessageListener(this); + } + } + + public void onMessage(Message m) + { + _logger.info("Failover exchange notified cluster membership change"); + + String currentBrokerIP = ""; + try + { + currentBrokerIP = InetAddress.getByName(_currentBrokerDetail.getHost()).getHostAddress(); + } + catch(Exception e) + { + _logger.warn("Unable to resolve current broker host name",e); + } + + List<BrokerDetails> brokerList = new ArrayList<BrokerDetails>(); + try + { + List<String> list = (List<String>)m.getObjectProperty("amq.failover"); + for (String brokerEntry:list) + { + String[] urls = brokerEntry.substring(5) .split(","); + // Iterate until you find the correct transport + // Need to reconsider the logic when the C++ broker supports + // SSL URLs. + for (String url:urls) + { + String[] tokens = url.split(":"); + if (tokens[0].equalsIgnoreCase(_originalBrokerDetail.getTransport())) + { + BrokerDetails broker = new AMQBrokerDetails(); + broker.setTransport(tokens[0]); + broker.setHost(tokens[1]); + broker.setPort(Integer.parseInt(tokens[2])); + broker.setProperties(_originalBrokerDetail.getProperties()); + broker.setSSLConfiguration(_originalBrokerDetail.getSSLConfiguration()); + brokerList.add(broker); + + if (currentBrokerIP.equals(broker.getHost()) && + _currentBrokerDetail.getPort() == broker.getPort()) + { + _currentBrokerIndex = brokerList.indexOf(broker); + } + + break; + } + } + } + } + catch(JMSException e) + { + _logger.error("Error parsing the message sent by failover exchange",e); + } + + synchronized (_brokerListLock) + { + _connectionDetails.setBrokerDetails(brokerList); + } + + _logger.info("============================================================"); + _logger.info("Updated cluster membership details " + _connectionDetails); + _logger.info("============================================================"); + } + + public void attainedConnection() + { + try + { + _failedAttemps = 0; + _logger.info("============================================================"); + _logger.info("Attained connection "); + _logger.info("============================================================"); + subscribeForUpdates(); + } + catch (JMSException e) + { + throw new RuntimeException("Unable to subscribe for cluster membership updates",e); + } + } + + public BrokerDetails getCurrentBrokerDetails() + { + synchronized (_brokerListLock) + { + _currentBrokerDetail = _connectionDetails.getBrokerDetails(_currentBrokerIndex); + return _currentBrokerDetail; + } + } + + public BrokerDetails getNextBrokerDetails() + { + synchronized(_brokerListLock) + { + if (_currentBrokerIndex == (_connectionDetails.getBrokerCount() - 1)) + { + _currentBrokerIndex = 0; + } + else + { + _currentBrokerIndex++; + } + + BrokerDetails broker = _connectionDetails.getBrokerDetails(_currentBrokerIndex); + + // When the broker list is updated it will include the current broker as well + // There is no point trying it again, so trying the next one. + if (_currentBrokerDetail != null && + broker.getHost().equals(_currentBrokerDetail.getHost()) && + broker.getPort() == _currentBrokerDetail.getPort()) + { + if (_connectionDetails.getBrokerCount() > 1) + { + return getNextBrokerDetails(); + } + else + { + _failedAttemps ++; + return null; + } + } + + String delayStr = broker.getProperty(BrokerDetails.OPTIONS_CONNECT_DELAY); + if (delayStr != null) + { + 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."); + } + + _failedAttemps ++; + _currentBrokerDetail = broker; + return broker; + } + } + + public boolean failoverAllowed() + { + // We allow to Failover provided + // our broker list is not empty and + // we haven't gone through all of them + + boolean b = _connectionDetails.getBrokerCount() > 0 && + _failedAttemps <= _connectionDetails.getBrokerCount(); + + + _logger.info("============================================================"); + _logger.info(toString()); + _logger.info("FailoverAllowed " + b); + _logger.info("============================================================"); + + return b; + } + + public void reset() + { + _failedAttemps = 0; + } + + public void setBroker(BrokerDetails broker) + { + // not sure if this method is needed + } + + public void setRetries(int maxRetries) + { + // no max retries we keep trying as long + // as we get updates + } + + public String methodName() + { + return "Failover Exchange"; + } + + public String toString() + { + StringBuffer sb = new StringBuffer(); + sb.append("FailoverExchange:\n"); + sb.append("\n Current Broker Index:"); + sb.append(_currentBrokerIndex); + sb.append("\n Failed Attempts:"); + sb.append(_failedAttemps); + sb.append("\n Orignal broker details:"); + sb.append(_originalBrokerDetail).append("\n"); + sb.append("\n -------- Broker List -----------\n"); + for (int i = 0; i < _connectionDetails.getBrokerCount(); i++) + { + if (i == _currentBrokerIndex) + { + sb.append(">"); + } + + sb.append(_connectionDetails.getBrokerDetails(i)); + sb.append("\n"); + } + sb.append("--------------------------------\n"); + return sb.toString(); + } +} diff --git a/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverMethod.java b/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverMethod.java index d7ec46dea3..1cef067e5f 100644 --- a/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverMethod.java +++ b/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverMethod.java @@ -27,7 +27,10 @@ public interface FailoverMethod { public static final String SINGLE_BROKER = "singlebroker"; public static final String ROUND_ROBIN = "roundrobin"; + public static final String FAILOVER_EXCHANGE= "failover_exchange"; public static final String RANDOM = "random"; + public static final String NO_FAILOVER = "nofailover"; + /** * Reset the Failover to initial conditions */ 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 9c172da6a2..41ba4974ec 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 @@ -30,27 +30,27 @@ public class FailoverRoundRobinServers implements FailoverMethod private static final Logger _logger = LoggerFactory.getLogger(FailoverRoundRobinServers.class); /** The default number of times to cycle through all servers */ - public static final int DEFAULT_CYCLE_RETRIES = 0; + public static final int DEFAULT_CYCLE_RETRIES = 1; /** 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 */ - private int _currentBrokerIndex = -1; + private int _currentBrokerIndex = 0; /** The number of times to retry connecting for each server */ private int _serverRetries; /** The current number of retry attempts made */ - private int _currentServerRetry; + private int _currentServerRetry = 0; /** The number of times to cycle through the servers */ private int _cycleRetries; /** The current number of cycles performed. */ - private int _currentCycleRetries; + private int _currentCycleRetries = 0; /** Array of BrokerDetail used to make connections. */ - private ConnectionURL _connectionDetails; + protected ConnectionURL _connectionDetails; public FailoverRoundRobinServers(ConnectionURL connectionDetails) { @@ -62,10 +62,12 @@ public class FailoverRoundRobinServers implements FailoverMethod _connectionDetails = connectionDetails; // There is no current broker at startup so set it to -1. - _currentBrokerIndex = -1; + _currentBrokerIndex = 0; String cycleRetries = _connectionDetails.getFailoverOption(ConnectionURL.OPTIONS_FAILOVER_CYCLE); + _cycleRetries = DEFAULT_CYCLE_RETRIES; + if (cycleRetries != null) { try @@ -74,42 +76,39 @@ public class FailoverRoundRobinServers implements FailoverMethod } catch (NumberFormatException nfe) { - _cycleRetries = DEFAULT_CYCLE_RETRIES; + _logger.warn("Cannot set cycle Retries, " + cycleRetries + " is not a number. Using default: " + DEFAULT_CYCLE_RETRIES); } } _currentCycleRetries = 0; _serverRetries = 0; - _currentServerRetry = -1; + _currentServerRetry = 0; } public void reset() { _currentBrokerIndex = 0; _currentCycleRetries = 0; - _currentServerRetry = -1; + _currentServerRetry = 0; } public boolean failoverAllowed() { - return ((_currentCycleRetries < _cycleRetries) || (_currentServerRetry < _serverRetries) - || (_currentBrokerIndex < (_connectionDetails.getBrokerCount() - 1))); + _logger.info("==== Checking failoverAllowed() ===="); + _logger.info(toString()); + _logger.info("===================================="); + return ((_currentCycleRetries < _cycleRetries) || (_currentServerRetry < _serverRetries)); } public void attainedConnection() { _currentCycleRetries = 0; - _currentServerRetry = -1; + _currentServerRetry = 0; } public BrokerDetails getCurrentBrokerDetails() { - if (_currentBrokerIndex == -1) - { - return null; - } - return _connectionDetails.getBrokerDetails(_currentBrokerIndex); } @@ -121,20 +120,8 @@ public class FailoverRoundRobinServers implements FailoverMethod { if (_currentServerRetry < _serverRetries) { - if (_currentBrokerIndex == -1) - { - _currentBrokerIndex = 0; - - setBroker(_connectionDetails.getBrokerDetails(_currentBrokerIndex)); - - _logger.info("First run using " + _connectionDetails.getBrokerDetails(_currentBrokerIndex)); - } - else - { - _logger.info("Retrying " + _connectionDetails.getBrokerDetails(_currentBrokerIndex)); - doDelay=true; - } - + _logger.info("Trying " + _connectionDetails.getBrokerDetails(_currentBrokerIndex)); + doDelay= _currentBrokerIndex != 0; _currentServerRetry++; } else @@ -154,19 +141,8 @@ public class FailoverRoundRobinServers implements FailoverMethod { if (_currentServerRetry < _serverRetries) { - if (_currentBrokerIndex == -1) - { - _currentBrokerIndex = 0; - - setBroker(_connectionDetails.getBrokerDetails(_currentBrokerIndex)); - - _logger.info("First run using " + _connectionDetails.getBrokerDetails(_currentBrokerIndex)); - } - else - { - _logger.info("Retrying " + _connectionDetails.getBrokerDetails(_currentBrokerIndex)); - doDelay=true; - } + _logger.info("Trying " + _connectionDetails.getBrokerDetails(_currentBrokerIndex)); + doDelay= _currentBrokerIndex != 0; _currentServerRetry++; } @@ -198,7 +174,11 @@ public class FailoverRoundRobinServers implements FailoverMethod } else { - _logger.info("No delay between connect retries, use tcp://host:port?connectdelay='value' to enable."); + // Only display if option not set. Not if deDelay==false. + if (delayStr == null) + { + _logger.info("No delay between connect retries, use tcp://host:port?connectdelay='value' to enable."); + } } return broker; @@ -225,7 +205,7 @@ public class FailoverRoundRobinServers implements FailoverMethod } } - _currentServerRetry = -1; + _currentServerRetry = 0; _currentBrokerIndex = index; } 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 9fa006233b..d033a49f5c 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 @@ -30,16 +30,16 @@ 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; + public static final int DEFAULT_SERVER_RETRIES = 0; /** The details of the Single Server */ private BrokerDetails _brokerDetail; /** The number of times to retry connecting to the sever */ - private int _retries; + protected int _retries; /** The current number of attempts made to the server */ - private int _currentRetries; + protected int _currentRetries = 0; public FailoverSingleServer(ConnectionURL connectionDetails) @@ -61,7 +61,7 @@ public class FailoverSingleServer implements FailoverMethod public void reset() { - _currentRetries = -1; + _currentRetries = 0; } public boolean failoverAllowed() @@ -157,7 +157,7 @@ public class FailoverSingleServer implements FailoverMethod public String toString() { - return "SingleServer:\n" + + return methodName()+":\n" + "Max Retries:" + _retries + "\nCurrent Retry:" + _currentRetries + "\n" + _brokerDetail + "\n"; diff --git a/java/client/src/main/java/org/apache/qpid/jms/failover/NoFailover.java b/java/client/src/main/java/org/apache/qpid/jms/failover/NoFailover.java new file mode 100644 index 0000000000..1231324397 --- /dev/null +++ b/java/client/src/main/java/org/apache/qpid/jms/failover/NoFailover.java @@ -0,0 +1,62 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.jms.failover; + +import org.apache.qpid.jms.BrokerDetails; +import org.apache.qpid.jms.ConnectionURL; + +/** + * Extend the Single Server Model to gain retry functionality but once connected do not attempt to failover. + */ +public class NoFailover extends FailoverSingleServer +{ + private boolean _connected = false; + + public NoFailover(BrokerDetails brokerDetail) + { + super(brokerDetail); + } + + public NoFailover(ConnectionURL connectionDetails) + { + super(connectionDetails); + } + + @Override + public void attainedConnection() + { + _connected=true; + _currentRetries = _retries; + } + + @Override + public String methodName() + { + return "NoFailover"; + } + + @Override + public String toString() + { + return super.toString() + (_connected ? "Connection attained." : "Never connected.") + "\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 43ac56dee9..8b702c008f 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 @@ -48,6 +48,7 @@ import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.url.AMQBindingURL; import org.apache.qpid.url.BindingURL; import org.apache.qpid.url.URLSyntaxException; +import org.apache.qpid.util.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -84,9 +85,20 @@ public class PropertiesFileInitialContextFactory implements InitialContextFactor Properties p = new Properties(); p.load(new BufferedInputStream(new FileInputStream(file))); + Strings.Resolver resolver = new Strings.ChainedResolver + (Strings.SYSTEM_RESOLVER, new Strings.PropertiesResolver(p)); - environment.putAll(p); - System.getProperties().putAll(p); + for (Map.Entry me : p.entrySet()) + { + String key = (String) me.getKey(); + String value = (String) me.getValue(); + String expanded = Strings.expand(value, resolver); + environment.put(key, expanded); + if (System.getProperty(key) == null) + { + System.setProperty(key, expanded); + } + } _logger.info("Loaded Context Properties:" + environment.toString()); } else @@ -96,7 +108,8 @@ public class PropertiesFileInitialContextFactory implements InitialContextFactor } catch (IOException ioe) { - _logger.warn("Unable to load property file specified in Provider_URL:" + environment.get(Context.PROVIDER_URL)); + _logger.warn("Unable to load property file specified in Provider_URL:" + environment.get(Context.PROVIDER_URL) +"\n" + + "Due to:"+ioe.getMessage()); } createConnectionFactories(data, environment); @@ -126,7 +139,7 @@ public class PropertiesFileInitialContextFactory implements InitialContextFactor if (key.startsWith(CONNECTION_FACTORY_PREFIX)) { String jndiName = key.substring(CONNECTION_FACTORY_PREFIX.length()); - ConnectionFactory cf = createFactory(entry.getValue().toString()); + ConnectionFactory cf = createFactory(entry.getValue().toString().trim()); if (cf != null) { data.put(jndiName, cf); @@ -144,7 +157,7 @@ public class PropertiesFileInitialContextFactory implements InitialContextFactor if (key.startsWith(DESTINATION_PREFIX)) { String jndiName = key.substring(DESTINATION_PREFIX.length()); - Destination dest = createDestination(entry.getValue().toString()); + Destination dest = createDestination(entry.getValue().toString().trim()); if (dest != null) { data.put(jndiName, dest); @@ -162,7 +175,7 @@ public class PropertiesFileInitialContextFactory implements InitialContextFactor if (key.startsWith(QUEUE_PREFIX)) { String jndiName = key.substring(QUEUE_PREFIX.length()); - Queue q = createQueue(entry.getValue().toString()); + Queue q = createQueue(entry.getValue().toString().trim()); if (q != null) { data.put(jndiName, q); @@ -180,7 +193,7 @@ public class PropertiesFileInitialContextFactory implements InitialContextFactor if (key.startsWith(TOPIC_PREFIX)) { String jndiName = key.substring(TOPIC_PREFIX.length()); - Topic t = createTopic(entry.getValue().toString()); + Topic t = createTopic(entry.getValue().toString().trim()); if (t != null) { if (_logger.isDebugEnabled()) @@ -283,7 +296,7 @@ public class PropertiesFileInitialContextFactory implements InitialContextFactor int i = 0; for (String key:keys) { - bindings[i] = new AMQShortString(key); + bindings[i] = new AMQShortString(key.trim()); i++; } // The Destination has a dual nature. If this was used for a producer the key is used diff --git a/java/client/src/main/java/org/apache/qpid/nclient/Client.java b/java/client/src/main/java/org/apache/qpid/nclient/Client.java deleted file mode 100644 index af0e724e42..0000000000 --- a/java/client/src/main/java/org/apache/qpid/nclient/Client.java +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.qpid.nclient; - -import java.util.List; -import java.util.UUID; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -import org.apache.qpid.client.url.URLParser_0_10; -import org.apache.qpid.jms.BrokerDetails; -import org.apache.qpid.url.QpidURL; -import org.apache.qpid.ErrorCode; -import org.apache.qpid.QpidException; -import org.apache.qpid.nclient.impl.ClientSession; -import org.apache.qpid.nclient.impl.ClientSessionDelegate; -import org.apache.qpid.transport.Channel; -import org.apache.qpid.transport.ClientDelegate; -import org.apache.qpid.transport.Connection; -import org.apache.qpid.transport.ConnectionClose; -import org.apache.qpid.transport.ConnectionCloseCode; -import org.apache.qpid.transport.ConnectionCloseOk; -import org.apache.qpid.transport.ProtocolHeader; -import org.apache.qpid.transport.ProtocolVersionException; -import org.apache.qpid.transport.SessionDelegate; -import org.apache.qpid.transport.network.io.IoTransport; -import org.apache.qpid.transport.network.mina.MinaHandler; -import org.apache.qpid.transport.network.nio.NioHandler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -public class Client implements org.apache.qpid.nclient.Connection -{ - private Connection _conn; - private ClosedListener _closedListner; - private final Lock _lock = new ReentrantLock(); - private static Logger _logger = LoggerFactory.getLogger(Client.class); - private Condition closeOk; - private boolean closed = false; - private long timeout = 60000; - - private ProtocolHeader header = null; - - /** - * - * @return returns a new connection to the broker. - */ - public static org.apache.qpid.nclient.Connection createConnection() - { - return new Client(); - } - - public void connect(String host, int port,String virtualHost,String username, String password) throws QpidException - { - - final Condition negotiationComplete = _lock.newCondition(); - closeOk = _lock.newCondition(); - _lock.lock(); - - ClientDelegate connectionDelegate = new ClientDelegate() - { - private boolean receivedClose = false; - public SessionDelegate getSessionDelegate() - { - return new ClientSessionDelegate(); - } - - public void exception(Throwable t) - { - if (_closedListner != null) - { - _closedListner.onClosed(ErrorCode.CONNECTION_ERROR,ErrorCode.CONNECTION_ERROR.getDesc(),t); - } - else - { - throw new RuntimeException("connection closed",t); - } - } - - public void closed() - { - if (_closedListner != null && !this.receivedClose) - { - _closedListner.onClosed(ErrorCode.CONNECTION_ERROR,ErrorCode.CONNECTION_ERROR.getDesc(),null); - } - } - - @Override public void connectionCloseOk(Channel context, ConnectionCloseOk struct) - { - _lock.lock(); - try - { - closed = true; - this.receivedClose = true; - closeOk.signalAll(); - } - finally - { - _lock.unlock(); - } - } - - @Override public void connectionClose(Channel context, ConnectionClose connectionClose) - { - super.connectionClose(context, connectionClose); - ErrorCode errorCode = ErrorCode.get(connectionClose.getReplyCode().getValue()); - if (_closedListner == null && errorCode != ErrorCode.NO_ERROR) - { - throw new RuntimeException - (new QpidException("Server closed the connection: Reason " + - connectionClose.getReplyText(), - errorCode, - null)); - } - else - { - _closedListner.onClosed(errorCode, connectionClose.getReplyText(),null); - } - - this.receivedClose = true; - } - @Override public void init(Channel ch, ProtocolHeader hdr) - { - // TODO: once the merge is done we'll need to update this code - // for handling 0.8 protocol version type i.e. major=8 and mino - if (hdr.getMajor() != 0 || hdr.getMinor() != 10) - { - Client.this.header = hdr; - _lock.lock(); - negotiationComplete.signalAll(); - _lock.unlock(); - } - } - }; - - connectionDelegate.setCondition(_lock,negotiationComplete); - connectionDelegate.setUsername(username); - connectionDelegate.setPassword(password); - connectionDelegate.setVirtualHost(virtualHost); - - String transport = System.getProperty("transport","io"); - if (transport.equalsIgnoreCase("nio")) - { - _logger.info("using NIO Transport"); - _conn = NioHandler.connect(host, port,connectionDelegate); - } - else if (transport.equalsIgnoreCase("io")) - { - _logger.info("using Plain IO Transport"); - _conn = IoTransport.connect(host, port,connectionDelegate); - } - else - { - _logger.info("using MINA Transport"); - _conn = MinaHandler.connect(host, port,connectionDelegate); - // _conn = NativeHandler.connect(host, port,connectionDelegate); - } - - // XXX: hardcoded version numbers - _conn.send(new ProtocolHeader(1, 0, 10)); - - try - { - negotiationComplete.await(timeout, TimeUnit.MILLISECONDS); - if (header != null) - { - _conn.close(); - throw new ProtocolVersionException(header.getMajor(), header.getMinor()); - } - } - catch (InterruptedException e) - { - throw new RuntimeException(e); - } - finally - { - _lock.unlock(); - } - } - - public void connect(String url)throws QpidException - { - URLParser_0_10 parser = null; - try - { - parser = new URLParser_0_10(url); - } - catch(Exception e) - { - throw new QpidException("Error parsing the URL",ErrorCode.UNDEFINED,e); - } - List<BrokerDetails> brokers = parser.getAllBrokerDetails(); - BrokerDetails brokerDetail = brokers.get(0); - connect(brokerDetail.getHost(), brokerDetail.getPort(), brokerDetail.getProperty("virtualhost"), - brokerDetail.getProperty("username")== null? "guest":brokerDetail.getProperty("username"), - brokerDetail.getProperty("password")== null? "guest":brokerDetail.getProperty("password")); - } - - /* - * Until the dust settles with the URL disucssion - * I am not going to implement this. - */ - public void connect(QpidURL url) throws QpidException - { - throw new UnsupportedOperationException("Not implemented"); - } - - /* { - // temp impl to tests - BrokerDetails details = url.getAllBrokerDetails().get(0); - connect(details.getHost(), - details.getPort(), - details.getVirtualHost(), - details.getUserName(), - details.getPassword()); - } -*/ - - public void close() throws QpidException - { - Channel ch = _conn.getChannel(0); - ch.connectionClose(ConnectionCloseCode.NORMAL, "client is closing"); - _lock.lock(); - try - { - try - { - long start = System.currentTimeMillis(); - long elapsed = 0; - while (!closed && elapsed < timeout) - { - closeOk.await(timeout - elapsed, TimeUnit.MILLISECONDS); - elapsed = System.currentTimeMillis() - start; - } - if(!closed) - { - throw new QpidException("Timed out when closing connection", ErrorCode.CONNECTION_ERROR, null); - } - } - catch (InterruptedException e) - { - throw new QpidException("Interrupted when closing connection", ErrorCode.CONNECTION_ERROR, null); - } - } - finally - { - _lock.unlock(); - } - _conn.close(); - } - - public Session createSession(long expiryInSeconds) - { - Channel ch = _conn.getChannel(); - ClientSession ssn = new ClientSession(UUID.randomUUID().toString().getBytes()); - ssn.attach(ch); - ssn.sessionAttach(ssn.getName()); - ssn.sessionRequestTimeout(expiryInSeconds); - return ssn; - } - - public DtxSession createDTXSession(int expiryInSeconds) - { - ClientSession clientSession = (ClientSession) createSession(expiryInSeconds); - clientSession.dtxSelect(); - return (DtxSession) clientSession; - } - - public void setClosedListener(ClosedListener closedListner) - { - - _closedListner = closedListner; - } - -} diff --git a/java/client/src/main/java/org/apache/qpid/nclient/Connection.java b/java/client/src/main/java/org/apache/qpid/nclient/Connection.java deleted file mode 100644 index 2d5b50b33a..0000000000 --- a/java/client/src/main/java/org/apache/qpid/nclient/Connection.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.qpid.nclient; - -import org.apache.qpid.QpidException; - -/** - * This represents a physical connection to a broker. - */ -public interface Connection -{ - /** - * Establish the connection using the given parameters - * - * @param host host name - * @param port port number - * @param virtualHost the virtual host name - * @param username user name - * @param password password - * @throws QpidException If the communication layer fails to establish the connection. - */ - public void connect(String host, int port,String virtualHost,String username, String password) throws QpidException; - - /** - * Establish the connection with the broker identified by the URL. - * - * @param url Specifies the URL of the broker. - * @throws QpidException If the communication layer fails to connect with the broker, an exception is thrown. - */ - public void connect(String url) throws QpidException; - - /** - * Close this connection. - * - * @throws QpidException if the communication layer fails to close the connection. - */ - public void close() throws QpidException; - - /** - * Create a session for this connection. - * <p> The returned session is suspended - * (i.e. this session is not attached to an underlying channel) - * - * @param expiryInSeconds Expiry time expressed in seconds, if the value is less than - * or equal to 0 then the session does not expire. - * @return A newly created (suspended) session. - */ - public Session createSession(long expiryInSeconds); - - /** - * Create a DtxSession for this connection. - * <p> A Dtx Session must be used when resources have to be manipulated as - * part of a global transaction. - * <p> The retuned DtxSession is suspended - * (i.e. this session is not attached with an underlying channel) - * - * @param expiryInSeconds Expiry time expressed in seconds, if the value is less than or equal - * to 0 then the session does not expire. - * @return A newly created (suspended) DtxSession. - */ - public DtxSession createDTXSession(int expiryInSeconds); - - /** - * If the communication layer detects a serious problem with a connection, it - * informs the connection's ClosedListener - * - * @param exceptionListner The ClosedListener - */ - public void setClosedListener(ClosedListener exceptionListner); -} diff --git a/java/client/src/main/java/org/apache/qpid/nclient/DtxSession.java b/java/client/src/main/java/org/apache/qpid/nclient/DtxSession.java deleted file mode 100644 index 8a859f2d84..0000000000 --- a/java/client/src/main/java/org/apache/qpid/nclient/DtxSession.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.qpid.nclient; - -import org.apache.qpid.transport.Future; -import org.apache.qpid.transport.GetTimeoutResult; -import org.apache.qpid.transport.Option; -import org.apache.qpid.transport.RecoverResult; -import org.apache.qpid.transport.XaResult; -import org.apache.qpid.transport.Xid; - -/** - * The resources for this session are controlled under the scope of a distributed transaction. - */ -public interface DtxSession extends Session -{ - - /** - * This method is called when messages should be produced and consumed on behalf a transaction - * branch identified by xid. - * possible options are: - * <ul> - * <li> {@link Option#JOIN}: Indicate that the start applies to joining a transaction previously seen. - * <li> {@link Option#RESUME}: Indicate that the start applies to resuming a suspended transaction branch specified. - * </ul> - * - * @param xid Specifies the xid of the transaction branch to be started. - * @param options Possible options are: {@link Option#JOIN} and {@link Option#RESUME}. - * @return Confirms to the client that the transaction branch is started or specify the error condition. - */ - public Future<XaResult> dtxStart(Xid xid, Option... options); - - /** - * This method is called when the work done on behalf of a transaction branch finishes or needs to - * be suspended. - * possible options are: - * <ul> - * <li> {@link Option#FAIL}: indicates that this portion of work has failed; - * otherwise this portion of work has - * completed successfully. - * <li> {@link Option#SUSPEND}: Indicates that the transaction branch is - * temporarily suspended in an incomplete state. - * </ul> - * - * @param xid Specifies the xid of the transaction branch to be ended. - * @param options Available options are: {@link Option#FAIL} and {@link Option#SUSPEND}. - * @return Confirms to the client that the transaction branch is ended or specifies the error condition. - */ - public Future<XaResult> dtxEnd(Xid xid, Option... options); - - /** - * Commit the work done on behalf of a transaction branch. This method commits the work associated - * with xid. Any produced messages are made available and any consumed messages are discarded. - * The only possible option is: - * <ul> - * <li> {@link Option#ONE_PHASE}: When set, one-phase commit optimization is used. - * </ul> - * - * @param xid Specifies the xid of the transaction branch to be committed. - * @param options Available option is: {@link Option#ONE_PHASE} - * @return Confirms to the client that the transaction branch is committed or specifies the error condition. - */ - public Future<XaResult> dtxCommit(Xid xid, Option... options); - - /** - * This method is called to forget about a heuristically completed transaction branch. - * - * @param xid Specifies the xid of the transaction branch to be forgotten. - */ - public void dtxForget(Xid xid, Option ... options); - - /** - * This method obtains the current transaction timeout value in seconds. If set-timeout was not - * used prior to invoking this method, the return value is the default timeout value; otherwise, the - * value used in the previous set-timeout call is returned. - * - * @param xid Specifies the xid of the transaction branch used for getting the timeout. - * @return The current transaction timeout value in seconds. - */ - public Future<GetTimeoutResult> dtxGetTimeout(Xid xid, Option ... options); - - /** - * This method prepares any message produced or consumed on behalf of xid, ready for commitment. - * - * @param xid Specifies the xid of the transaction branch to be prepared. - * @return The status of the prepare operation can be any one of: - * xa-ok: Normal execution. - * <p/> - * xa-rdonly: The transaction branch was read-only and has been committed. - * <p/> - * xa-rbrollback: The broker marked the transaction branch rollback-only for an unspecified - * reason. - * <p/> - * xa-rbtimeout: The work represented by this transaction branch took too long. - */ - public Future<XaResult> dtxPrepare(Xid xid, Option ... options); - - /** - * This method is called to obtain a list of transaction branches that are in a prepared or - * heuristically completed state. - * @return a array of xids to be recovered. - */ - public Future<RecoverResult> dtxRecover(Option ... options); - - /** - * This method rolls back the work associated with xid. Any produced messages are discarded and - * any consumed messages are re-queued. - * - * @param xid Specifies the xid of the transaction branch to be rolled back. - * @return Confirms to the client that the transaction branch is rolled back or specifies the error condition. - */ - public Future<XaResult> dtxRollback(Xid xid, Option ... options); - - /** - * Sets the specified transaction branch timeout value in seconds. - * - * @param xid Specifies the xid of the transaction branch for setting the timeout. - * @param timeout The transaction timeout value in seconds. - */ - public void dtxSetTimeout(Xid xid, long timeout, Option ... options); -} diff --git a/java/client/src/main/java/org/apache/qpid/nclient/JMSTestCase.java b/java/client/src/main/java/org/apache/qpid/nclient/JMSTestCase.java deleted file mode 100644 index 4e1b9058e6..0000000000 --- a/java/client/src/main/java/org/apache/qpid/nclient/JMSTestCase.java +++ /dev/null @@ -1,115 +0,0 @@ - package org.apache.qpid.nclient; - -import java.util.Enumeration; - -import javax.jms.ExceptionListener; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.MessageListener; -import javax.jms.Queue; -import javax.jms.QueueBrowser; - -import org.apache.qpid.client.AMQConnection; -import org.apache.qpid.client.AMQQueue; -import org.apache.qpid.client.AMQTopic; -import org.apache.qpid.framing.AMQShortString; - -public class JMSTestCase -{ - - public static void main(String[] args) - { - - try - { - javax.jms.Connection con = new AMQConnection("qpid:password=pass;username=name@tcp:localhost:5672"); - con.start(); - - javax.jms.Session ssn = con.createSession(false, 1); - - javax.jms.Destination dest = new AMQQueue(new AMQShortString("direct"),"test"); - javax.jms.MessageProducer prod = ssn.createProducer(dest); - QueueBrowser browser = ssn.createBrowser((Queue)dest, "Test = 'test'"); - - javax.jms.TextMessage msg = ssn.createTextMessage(); - msg.setStringProperty("TEST", "test"); - msg.setText("Should get this"); - prod.send(msg); - - javax.jms.TextMessage msg2 = ssn.createTextMessage(); - msg2.setStringProperty("TEST", "test2"); - msg2.setText("Shouldn't get this"); - prod.send(msg2); - - - Enumeration enu = browser.getEnumeration(); - for (;enu.hasMoreElements();) - { - System.out.println(enu.nextElement()); - System.out.println("\n"); - } - - javax.jms.MessageConsumer cons = ssn.createConsumer(dest, "Test = 'test'"); - javax.jms.TextMessage m = null; // (javax.jms.TextMessage)cons.receive(); - cons.setMessageListener(new MessageListener() - { - public void onMessage(Message m) - { - javax.jms.TextMessage m2 = (javax.jms.TextMessage)m; - try - { - System.out.println("headers : " + m2.toString()); - System.out.println("m : " + m2.getText()); - System.out.println("\n\n"); - } - catch(Exception e) - { - e.printStackTrace(); - } - } - - }); - - con.setExceptionListener(new ExceptionListener() - { - public void onException(JMSException e) - { - e.printStackTrace(); - } - }); - - System.out.println("Waiting"); - while (m == null) - { - - } - - System.out.println("Exiting"); - - /*javax.jms.TextMessage msg = ssn.createTextMessage(); - msg.setText("This is a test message"); - msg.setBooleanProperty("targetMessage", false); - prod.send(msg); - - msg.setBooleanProperty("targetMessage", true); - prod.send(msg); - - javax.jms.TextMessage m = (javax.jms.TextMessage)cons.receiveNoWait(); - - if (m == null) - { - System.out.println("message is null"); - } - else - { - System.out.println("message is not null" + m); - }*/ - - } - catch(Exception e) - { - e.printStackTrace(); - } - } - -} diff --git a/java/client/src/main/java/org/apache/qpid/nclient/Session.java b/java/client/src/main/java/org/apache/qpid/nclient/Session.java deleted file mode 100644 index 0d84394c7c..0000000000 --- a/java/client/src/main/java/org/apache/qpid/nclient/Session.java +++ /dev/null @@ -1,544 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.qpid.nclient; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.Map; - -import org.apache.qpid.transport.*; -import org.apache.qpid.api.Message; - -/** - * <p>A session is associated with a connection. - * When it is created, a session is not associated with an underlying channel. - * The session is single threaded. </p> - * <p/> - * All the Session commands are asynchronous. Synchronous behavior is achieved through invoking the sync method. - * For example, <code>command1</code> will be synchronously invoked by using the following sequence: - * <ul> - * <li> <code>session.command1()</code> - * <li> <code>session.sync()</code> - * </ul> - */ -public interface Session -{ - public static final short TRANSFER_ACQUIRE_MODE_NO_ACQUIRE = 1; - public static final short TRANSFER_ACQUIRE_MODE_PRE_ACQUIRE = 0; - public static final short TRANSFER_CONFIRM_MODE_REQUIRED = 0; - public static final short TRANSFER_CONFIRM_MODE_NOT_REQUIRED = 1; - public static final short MESSAGE_FLOW_MODE_CREDIT = 0; - public static final short MESSAGE_FLOW_MODE_WINDOW = 1; - public static final short MESSAGE_FLOW_UNIT_MESSAGE = 0; - public static final short MESSAGE_FLOW_UNIT_BYTE = 1; - public static final long MESSAGE_FLOW_MAX_BYTES = 0xFFFFFFFF; - public static final short MESSAGE_REJECT_CODE_GENERIC = 0; - public static final short MESSAGE_REJECT_CODE_IMMEDIATE_DELIVERY_FAILED = 1; - public static final short MESSAGE_ACQUIRE_ANY_AVAILABLE_MESSAGE = 0; - public static final short MESSAGE_ACQUIRE_MESSAGES_IF_ALL_ARE_AVAILABLE = 1; - - //------------------------------------------------------ - // Session housekeeping methods - //------------------------------------------------------ - - /** - * Sync method will block the session until all outstanding commands - * are executed. - */ - public void sync(); - - public void close(); - - public void sessionDetach(byte[] name, Option ... options); - - public void sessionRequestTimeout(long expiry, Option ... options); - - public byte[] getName(); - - public void setAutoSync(boolean value); - - //------------------------------------------------------ - // Messaging methods - // Producer - //------------------------------------------------------ - /** - * Transfer a message to a specified exchange. - * <p/> - * <p>This transfer provides a complete message - * using a single method. The method is internally mapped to messageTransfer() and headers() followed - * by data() and endData(). - * <b><i>This method should only be used by small messages.</b></i></p> - * - * @param destination The exchange the message is being sent to. - * @param msg The Message to be sent. - * @param confirmMode <ul> </li>off ({@link Session#TRANSFER_CONFIRM_MODE_NOT_REQUIRED}): confirmation - * is not required. Once a message has been transferred in pre-acquire - * mode (or once acquire has been sent in no-acquire mode) the message is considered - * transferred. - * <p/> - * <li> on ({@link Session#TRANSFER_CONFIRM_MODE_REQUIRED}): an acquired message - * is not considered transferred until the original - * transfer is complete. A complete transfer is signaled by execution.complete. - * </ul> - * @param acquireMode <ul> - * <li> no-acquire ({@link Session#TRANSFER_ACQUIRE_MODE_NO_ACQUIRE}): the message - * must be explicitly acquired. - * <li> pre-acquire ({@link Session#TRANSFER_ACQUIRE_MODE_PRE_ACQUIRE}): the message is - * acquired when the transfer starts. - * </ul> - * @throws java.io.IOException If transferring a message fails due to some internal communication error, an exception is thrown. - */ - public void messageTransfer(String destination, Message msg, short confirmMode, short acquireMode) - throws IOException; - - - /** - * This command transfers a message between two peers. - * - * @param destination Specifies the destination to which the message is to be transferred. - * @param acceptMode Indicates whether message.accept, session.complete, - * or nothing at all is required to indicate successful transfer of the message. - * - * @param acquireMode Indicates whether or not the transferred message has been acquired. - */ - public void messageTransfer(String destination, MessageAcceptMode acceptMode, MessageAcquireMode acquireMode, - Header header, ByteBuffer body, Option ... options); - - /** - * This command transfers a message between two peers. - * - * @param destination Specifies the destination to which the message is to be transferred. - * @param acceptMode Indicates whether message.accept, session.complete, - * or nothing at all is required to indicate successful transfer of the message. - * - * @param acquireMode Indicates whether or not the transferred message has been acquired. - */ - public void messageTransfer(String destination, MessageAcceptMode acceptMode, MessageAcquireMode acquireMode, - Header header, byte[] body, Option ... options); - - /** - * This command transfers a message between two peers. - * - * @param destination Specifies the destination to which the message is to be transferred. - * @param acceptMode Indicates whether message.accept, session.complete, - * or nothing at all is required to indicate successful transfer of the message. - * - * @param acquireMode Indicates whether or not the transferred message has been acquired. - */ - public void messageTransfer(String destination, MessageAcceptMode acceptMode, MessageAcquireMode acquireMode, - Header header, String body, Option ... options); - - //------------------------------------------------------ - // Messaging methods - // Consumer - //------------------------------------------------------ - - /** - * Associate a message listener with a destination. - * <p> The destination is bound to a queue, and messages are filtered based - * on the provider filter map (message filtering is specific to the provider and in some cases might not be handled). - * <p> The valid options are: - * <ul> - * <li>{@link Option#EXCLUSIVE}: <p> Requests exclusive subscription access, so that only this - * subscription can access the queue. - * <li>{@link Option#NONE}: <p> This is an empty option, and has no effect. - * </ul> - * - * @param queue The queue that the receiver is receiving messages from. - * @param destination The destination, or delivery tag, for the subscriber. - * @param confirmMode <ul> </li>off ({@link Session#TRANSFER_CONFIRM_MODE_NOT_REQUIRED}): confirmation - * is not required. Once a message has been transferred in pre-acquire - * mode (or once acquire has been sent in no-acquire mode) the message is considered - * transferred. - * <p/> - * <li> on ({@link Session#TRANSFER_CONFIRM_MODE_REQUIRED}): an acquired message - * is not considered transferred until the original - * transfer is complete. A complete transfer is signaled by execution.complete. - * </ul> - * @param acquireMode <ul> - * <li> no-acquire ({@link Session#TRANSFER_ACQUIRE_MODE_NO_ACQUIRE}): the message must - * be explicitly acquired. - * <li> pre-acquire ({@link Session#TRANSFER_ACQUIRE_MODE_PRE_ACQUIRE}): the message is - * acquired when the transfer starts. - * </ul> - * @param listener The listener for this destination. To transfer large messages - * use a {@link org.apache.qpid.nclient.MessagePartListener}. - * @param options Set of options. Valid options are {{@link Option#EXCLUSIVE} - * and {@link Option#NONE}. - * @param filter A set of filters for the subscription. The syntax and semantics of these filters varies - * according to the provider's implementation. - */ - public void messageSubscribe(String queue, String destination, short confirmMode, short acquireMode, - MessagePartListener listener, Map<String, Object> filter, Option... options); - - /** - * This method cancels a consumer. The server will not send any more messages to the specified destination. - * This does not affect already delivered messages. - * The client may receive a - * number of messages in between sending the cancel method and receiving - * notification that the cancellation has been completed. - * - * @param destination The destination to be cancelled. - */ - public void messageCancel(String destination, Option ... options); - - /** - * Associate a message listener with a destination. - * <p> Only one listener is permitted for each destination. When a new listener is created, - * it replaces the previous message listener. To prevent message loss, this occurs only when the original listener - * has completed processing a message. - * - * @param destination The destination the listener is associated with. - * @param listener The new listener for this destination. - */ - public void setMessageListener(String destination, MessagePartListener listener); - - /** - * Sets the mode of flow control used for a given destination. - * <p> With credit based flow control, the broker continually maintains its current - * credit balance with the recipient. The credit balance consists of two values, a message - * count, and a byte count. Whenever message data is sent, both counts must be decremented. - * If either value reaches zero, the flow of message data must stop. Additional credit is - * received via the {@link Session#messageFlow} method. - * <p> Window based flow control is identical to credit based flow control, however message - * acknowledgment implicitly grants a single unit of message credit, and the size of the - * message in byte credits for each acknowledged message. - * - * @param destination The destination to set the flow mode on. - * @param mode <ul> <li>credit ({@link Session#MESSAGE_FLOW_MODE_CREDIT}): choose credit based flow control - * <li> window ({@link Session#MESSAGE_FLOW_MODE_WINDOW}): choose window based flow control</ul> - */ - public void messageSetFlowMode(String destination, MessageFlowMode mode, Option ... options); - - - /** - * This method controls the flow of message data to a given destination. It is used by the - * recipient of messages to dynamically match the incoming rate of message flow to its - * processing or forwarding capacity. Upon receipt of this method, the sender must add "value" - * number of the specified unit to the available credit balance for the specified destination. - * A value of 0 indicates an infinite amount of credit. This disables any limit for - * the given unit until the credit balance is zeroed with {@link Session#messageStop} - * or {@link Session#messageFlush}. - * - * @param destination The destination to set the flow. - * @param unit Specifies the unit of credit balance. - * <p/> - * One of: <ul> - * <li> message ({@link Session#MESSAGE_FLOW_UNIT_MESSAGE}) - * <li> byte ({@link Session#MESSAGE_FLOW_UNIT_BYTE}) - * </ul> - * @param value Number of credits, a value of 0 indicates an infinite amount of credit. - */ - public void messageFlow(String destination, MessageCreditUnit unit, long value, Option ... options); - - /** - * Forces the broker to exhaust its credit supply. - * <p> The credit on the broker will remain at zero once - * this method is completed. - * - * @param destination The destination on which the credit supply is to be exhausted. - */ - public void messageFlush(String destination, Option ... options); - - /** - * On receipt of this method, the brokers set credit to zero for a given - * destination. When confirmation of this method - * is issued credit is set to zero. No further messages will be sent until - * further credit is received. - * - * @param destination The destination on which to reset credit. - */ - public void messageStop(String destination, Option ... options); - - /** - * Acknowledge the receipt of a range of messages. - * <p>Messages must already be acquired, either by receiving them in - * pre-acquire mode or by explicitly acquiring them. - * - * @param ranges Range of messages to be acknowledged. - * @param accept pecify whether to send a message accept to the broker - */ - public void messageAcknowledge(RangeSet ranges, boolean accept); - - /** - * Reject a range of acquired messages. - * <p>The broker will deliver rejected messages to the - * alternate-exchange on the queue from which it came. If no alternate-exchange is - * defined for that queue the broker will discard the message. - * - * @param ranges Range of messages to be rejected. - * @param code The reject code must be one of {@link Session#MESSAGE_REJECT_CODE_GENERIC} or - * {@link Session#MESSAGE_REJECT_CODE_IMMEDIATE_DELIVERY_FAILED} (immediate delivery was attempted but - * failed). - * @param text String describing the reason for a message transfer rejection. - */ - public void messageReject(RangeSet ranges, MessageRejectCode code, String text, Option ... options); - - /** - * As it is possible that the broker does not manage to reject some messages, after completion of - * {@link Session#messageReject} this method will return the ranges of rejected messages. - * <p> Note that {@link Session#messageReject} and this methods are asynchronous therefore for accessing to the - * previously rejected messages this method must be invoked in conjunction with {@link Session#sync()}. - * <p> A recommended invocation sequence would be: - * <ul> - * <li> {@link Session#messageReject} - * <li> {@link Session#sync()} - * <li> {@link Session#getRejectedMessages()} - * </ul> - * - * @return The rejected message ranges - */ - public RangeSet getRejectedMessages(); - - /** - * Try to acquire ranges of messages hence releasing them form the queue. - * This means that once acknowledged, a message will not be delivered to any other receiver. - * <p> As those messages may have been consumed by another receivers hence, - * message acquisition can fail. - * The outcome of the acquisition is returned as an array of ranges of qcquired messages. - * <p> This method should only be called on non-acquired messages. - * - * @param ranges Ranges of messages to be acquired. - * @return Indicates the acquired messages - */ - public Future<Acquired> messageAcquire(RangeSet ranges, Option ... options); - - /** - * Give up responsibility for processing ranges of messages. - * <p> Released messages are re-enqueued. - * - * @param ranges Ranges of messages to be released. - * @param options Valid option is: {@link Option#SET_REDELIVERED}) - */ - public void messageRelease(RangeSet ranges, Option ... options); - - // ----------------------------------------------- - // Local transaction methods - // ---------------------------------------------- - /** - * Selects the session for local transaction support. - */ - public void txSelect(Option ... options); - - /** - * Commit the receipt and delivery of all messages exchanged by this session's resources. - * - * @throws IllegalStateException If this session is not transacted, an exception will be thrown. - */ - public void txCommit(Option ... options) throws IllegalStateException; - - /** - * Roll back the receipt and delivery of all messages exchanged by this session's resources. - * - * @throws IllegalStateException If this session is not transacted, an exception will be thrown. - */ - public void txRollback(Option ... options) throws IllegalStateException; - - //--------------------------------------------- - // Queue methods - //--------------------------------------------- - - /** - * Declare a queue with the given queueName - * <p> Following are the valid options: - * <ul> - * <li> {@link Option#AUTO_DELETE}: <p> If this field is set and the exclusive field is also set, - * then the queue is deleted when the connection closes. - * If this field is set and the exclusive field is not set the queue is deleted when all - * the consumers have finished using it. - * <li> {@link Option#DURABLE}: <p> If set when creating a new queue, - * the queue will be marked as durable. Durable queues - * remain active when a server restarts. Non-durable queues (transient queues) are purged - * if/when a server restarts. Note that durable queues do not necessarily hold persistent - * messages, although it does not make sense to send persistent messages to a transient - * queue. - * <li> {@link Option#EXCLUSIVE}: <p> Exclusive queues can only be used from one connection at a time. - * Once a connection declares an exclusive queue, that queue cannot be used by any other connections until the - * declaring connection closes. - * <li> {@link Option#PASSIVE}: <p> If set, the server will not create the queue. - * This field allows the client to assert the presence of a queue without modifying the server state. - * <li>{@link Option#NONE}: <p> Has no effect as it represents an empty option. - * </ul> - * <p>In the absence of a particular option, the defaul value is false for each option - * - * @param queueName The name of the delcared queue. - * @param alternateExchange If a message is rejected by a queue, then it is sent to the alternate-exchange. A message - * may be rejected by a queue for the following reasons: - * <oL> <li> The queue is deleted when it is not empty; - * <li> Immediate delivery of a message is requested, but there are no consumers connected to - * the queue. </ol> - * @param arguments Used for backward compatibility - * @param options Set of Options ( valide options are: {@link Option#AUTO_DELETE}, {@link Option#DURABLE}, - * {@link Option#EXCLUSIVE}, {@link Option#PASSIVE} and {@link Option#NONE}) - * @see Option - */ - public void queueDeclare(String queueName, String alternateExchange, Map<String, Object> arguments, - Option... options); - - /** - * Bind a queue with an exchange. - * - * @param queueName Specifies the name of the queue to bind. If the queue name is empty, refers to the current - * queue for the session, which is the last declared queue. - * @param exchangeName The exchange name. - * @param routingKey Specifies the routing key for the binding. The routing key is used for routing messages - * depending on the exchange configuration. Not all exchanges use a routing key - refer to - * the specific exchange documentation. If the queue name is empty, the server uses the last - * queue declared on the session. If the routing key is also empty, the server uses this - * queue name for the routing key as well. If the queue name is provided but the routing key - * is empty, the server does the binding with that empty routing key. The meaning of empty - * routing keys depends on the exchange implementation. - * @param arguments Used for backward compatibility - */ - public void exchangeBind(String queueName, String exchangeName, String routingKey, Map<String, Object> arguments, - Option ... options); - - /** - * Unbind a queue from an exchange. - * - * @param queueName Specifies the name of the queue to unbind. - * @param exchangeName The name of the exchange to unbind from. - * @param routingKey Specifies the routing key of the binding to unbind. - */ - public void exchangeUnbind(String queueName, String exchangeName, String routingKey, Option ... options); - - /** - * This method removes all messages from a queue. It does not cancel consumers. Purged messages - * are deleted without any formal "undo" mechanism. - * - * @param queueName Specifies the name of the queue to purge. If the queue name is empty, refers to the - * current queue for the session, which is the last declared queue. - */ - public void queuePurge(String queueName, Option ... options); - - /** - * This method deletes a queue. When a queue is deleted any pending messages are sent to a - * dead-letter queue if this is defined in the server configuration, and all consumers on the - * queue are cancelled. - * <p> Following are the valid options: - * <ul> - * <li> {@link Option#IF_EMPTY}: <p> If set, the server will only delete the queue if it has no messages. - * <li> {@link Option#IF_UNUSED}: <p> If set, the server will only delete the queue if it has no consumers. - * If the queue has consumers the server does does not delete it but raises a channel exception instead. - * <li>{@link Option#NONE}: <p> Has no effect as it represents an empty option. - * </ul> - * </p> - * <p/> - * <p>In the absence of a particular option, the defaul value is false for each option</p> - * - * @param queueName Specifies the name of the queue to delete. If the queue name is empty, refers to the - * current queue for the session, which is the last declared queue. - * @param options Set of options (Valid options are: {@link Option#IF_EMPTY}, {@link Option#IF_UNUSED} - * and {@link Option#NONE}) - * @see Option - */ - public void queueDelete(String queueName, Option... options); - - - /** - * This method is used to request information on a particular queue. - * - * @param queueName The name of the queue for which information is requested. - * @return Information on the specified queue. - */ - public Future<QueueQueryResult> queueQuery(String queueName, Option ... options); - - - /** - * This method is used to request information on a particular binding. - * - * @param exchange The exchange name. - * @param queue The queue name. - * @param routingKey The routing key - * @param arguments bacward compatibilties params. - * @return Information on the specified binding. - */ - public Future<ExchangeBoundResult> exchangeBound(String exchange, String queue, String routingKey, - Map<String, Object> arguments, Option ... options); - - // -------------------------------------- - // exhcange methods - // -------------------------------------- - - /** - * This method creates an exchange. If the exchange already exists, - * the method verifies the class and checks the details are correct. - * <p>Valid options are: - * <ul> - * <li>{@link Option#AUTO_DELETE}: <p>If set, the exchange is deleted when all queues have finished using it. - * <li>{@link Option#DURABLE}: <p>If set, the exchange will - * be marked as durable. Durable exchanges remain active when a server restarts. Non-durable exchanges (transient - * exchanges) are purged when a server restarts. - * <li>{@link Option#PASSIVE}: <p>If set, the server will not create the exchange. - * The client can use this to check whether an exchange exists without modifying the server state. - * <li> {@link Option#NONE}: <p>This option is an empty option, and has no effect. - * </ul> - * <p>In the absence of a particular option, the defaul value is false for each option</p> - * - * @param exchangeName The exchange name. - * @param type Each exchange belongs to one of a set of exchange types implemented by the server. The - * exchange types define the functionality of the exchange - i.e. how messages are routed - * through it. It is not valid or meaningful to attempt to change the type of an existing - * exchange. Default exchange types are: direct, topic, headers and fanout. - * @param alternateExchange In the event that a message cannot be routed, this is the name of the exchange to which - * the message will be sent. - * @param options Set of options (valid options are: {@link Option#AUTO_DELETE}, {@link Option#DURABLE}, - * {@link Option#PASSIVE}, {@link Option#NONE}) - * @param arguments Used for backward compatibility - * @see Option - */ - public void exchangeDeclare(String exchangeName, String type, String alternateExchange, - Map<String, Object> arguments, Option... options); - - /** - * This method deletes an exchange. When an exchange is deleted all queue bindings on the - * exchange are cancelled. - * <p> Following are the valid options: - * <ul> - * <li> {@link Option#IF_UNUSED}: <p> If set, the server will only delete the exchange if it has no queue bindings. If the - * exchange has queue bindings the server does not delete it but raises a channel exception - * instead. - * <li> {@link Option#NONE}: <p> Has no effect as it represents an empty option. - * </ul> - * <p>Note that if an option is not set, it will default to false. - * - * @param exchangeName The name of exchange to be deleted. - * @param options Set of options. Valid options are: {@link Option#IF_UNUSED}, {@link Option#NONE}. - * @see Option - */ - public void exchangeDelete(String exchangeName, Option... options); - - - /** - * This method is used to request information about a particular exchange. - * - * @param exchangeName The name of the exchange about which information is requested. If not set, the method will - * return information about the default exchange. - * @return Information on the specified exchange. - */ - public Future<ExchangeQueryResult> exchangeQuery(String exchangeName, Option ... options); - - /** - * If the session receives a sessionClosed with an error code it - * informs the session's exceptionListener - * - * @param exceptionListner The exceptionListener - */ - public void setClosedListener(ClosedListener exceptionListner); -} diff --git a/java/client/src/main/java/org/apache/qpid/nclient/impl/ClientSession.java b/java/client/src/main/java/org/apache/qpid/nclient/impl/ClientSession.java deleted file mode 100644 index 27805a1f39..0000000000 --- a/java/client/src/main/java/org/apache/qpid/nclient/impl/ClientSession.java +++ /dev/null @@ -1,142 +0,0 @@ -package org.apache.qpid.nclient.impl; - -import java.io.EOFException; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import org.apache.qpid.QpidException; -import org.apache.qpid.api.Message; -import org.apache.qpid.nclient.ClosedListener; -import org.apache.qpid.nclient.MessagePartListener; -import org.apache.qpid.transport.DeliveryProperties; -import org.apache.qpid.transport.Header; -import org.apache.qpid.transport.MessageAcceptMode; -import org.apache.qpid.transport.MessageAcquireMode; -import org.apache.qpid.transport.MessageProperties; -import org.apache.qpid.transport.Option; -import org.apache.qpid.transport.Range; -import org.apache.qpid.transport.RangeSet; - -import static org.apache.qpid.transport.Option.*; - -/** - * Implements a Qpid Sesion. - */ -public class ClientSession extends org.apache.qpid.transport.Session implements org.apache.qpid.nclient.DtxSession -{ - static - { - String max = "message_size_before_sync"; // KB's - try - { - MAX_NOT_SYNC_DATA_LENGH = new Long(System.getProperties().getProperty(max, "200000000")); - } - catch (NumberFormatException e) - { - // use default size - MAX_NOT_SYNC_DATA_LENGH = 200000000; - } - String flush = "message_size_before_flush"; - try - { - MAX_NOT_FLUSH_DATA_LENGH = new Long(System.getProperties().getProperty(flush, "2000000")); - } - catch (NumberFormatException e) - { - // use default size - MAX_NOT_FLUSH_DATA_LENGH = 20000000; - } - } - - private static long MAX_NOT_SYNC_DATA_LENGH; - private static long MAX_NOT_FLUSH_DATA_LENGH; - - private Map<String,MessagePartListener> _messageListeners = new ConcurrentHashMap<String,MessagePartListener>(); - private ClosedListener _exceptionListner; - private RangeSet _rejectedMessages; - private long _currentDataSizeNotSynced; - private long _currentDataSizeNotFlushed; - - public ClientSession(byte[] name) - { - super(name); - } - - public void messageAcknowledge(RangeSet ranges, boolean accept) - { - for (Range range : ranges) - { - super.processed(range); - } - super.flushProcessed(accept ? BATCH : NONE); - if (accept) - { - messageAccept(ranges); - } - } - - public void messageSubscribe(String queue, String destination, short acceptMode, short acquireMode, MessagePartListener listener, Map<String, Object> filter, Option... options) - { - setMessageListener(destination,listener); - super.messageSubscribe(queue, destination, MessageAcceptMode.get(acceptMode), - MessageAcquireMode.get(acquireMode), null, 0, filter, - options); - } - - public void messageTransfer(String destination, Message msg, short acceptMode, short acquireMode) throws IOException - { - DeliveryProperties dp = msg.getDeliveryProperties(); - MessageProperties mp = msg.getMessageProperties(); - ByteBuffer body = msg.readData(); - int size = body.remaining(); - super.messageTransfer - (destination, MessageAcceptMode.get(acceptMode), - MessageAcquireMode.get(acquireMode), - new Header(dp, mp), body); - _currentDataSizeNotSynced += size; - _currentDataSizeNotFlushed += size; - } - - public void sync() - { - super.sync(); - _currentDataSizeNotSynced = 0; - } - - public RangeSet getRejectedMessages() - { - return _rejectedMessages; - } - - public void setMessageListener(String destination, MessagePartListener listener) - { - if (listener == null) - { - throw new IllegalArgumentException("Cannot set message listener to null"); - } - _messageListeners.put(destination, listener); - } - - public void setClosedListener(ClosedListener exceptionListner) - { - _exceptionListner = exceptionListner; - } - - void setRejectedMessages(RangeSet rejectedMessages) - { - _rejectedMessages = rejectedMessages; - } - - void notifyException(QpidException ex) - { - _exceptionListner.onClosed(null, null, null); - } - - Map<String,MessagePartListener> getMessageListeners() - { - return _messageListeners; - } -} diff --git a/java/client/src/main/java/org/apache/qpid/nclient/impl/ClientSessionDelegate.java b/java/client/src/main/java/org/apache/qpid/nclient/impl/ClientSessionDelegate.java deleted file mode 100644 index 620cf14c33..0000000000 --- a/java/client/src/main/java/org/apache/qpid/nclient/impl/ClientSessionDelegate.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.apache.qpid.nclient.impl; - -import java.nio.ByteBuffer; - -import org.apache.qpid.ErrorCode; - -import org.apache.qpid.nclient.MessagePartListener; - -import org.apache.qpid.QpidException; -import org.apache.qpid.transport.Header; -import org.apache.qpid.transport.MessageReject; -import org.apache.qpid.transport.MessageTransfer; -import org.apache.qpid.transport.Range; -import org.apache.qpid.transport.Session; -import org.apache.qpid.transport.SessionDetached; -import org.apache.qpid.transport.SessionDelegate; - - -public class ClientSessionDelegate extends SessionDelegate -{ - - // -------------------------------------------- - // Message methods - // -------------------------------------------- - @Override public void messageTransfer(Session session, MessageTransfer xfr) - { - MessagePartListener listener = ((ClientSession)session).getMessageListeners() - .get(xfr.getDestination()); - listener.messageTransfer(xfr); - } - - @Override public void messageReject(Session session, MessageReject struct) - { - for (Range range : struct.getTransfers()) - { - for (long l = range.getLower(); l <= range.getUpper(); l++) - { - System.out.println("message rejected: " + - session.getCommand((int) l)); - } - } - ((ClientSession)session).setRejectedMessages(struct.getTransfers()); - ((ClientSession)session).notifyException(new QpidException("Message Rejected",ErrorCode.MESSAGE_REJECTED,null)); - session.processed(struct); - } - -} diff --git a/java/client/src/main/java/org/apache/qpid/nclient/impl/Constants.java b/java/client/src/main/java/org/apache/qpid/nclient/impl/Constants.java deleted file mode 100644 index f689e9abde..0000000000 --- a/java/client/src/main/java/org/apache/qpid/nclient/impl/Constants.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -package org.apache.qpid.nclient.impl; - -/** - * This class holds all the 0.10 client constants which value can be set - * through properties. - */ -public class Constants -{ - static - { - - String max="message_size_before_sync";// KB's - try - { - MAX_NOT_SYNC_DATA_LENGH=new Long(System.getProperties().getProperty(max, "200000000")); - } - catch (NumberFormatException e) - { - // use default size - MAX_NOT_SYNC_DATA_LENGH=200000000; - } - String flush="message_size_before_flush"; - try - { - MAX_NOT_FLUSH_DATA_LENGH=new Long(System.getProperties().getProperty(flush, "2000000")); - } - catch (NumberFormatException e) - { - // use default size - MAX_NOT_FLUSH_DATA_LENGH=20000000; - } - } - - /** - * The total message size in KBs that can be transferted before - * client and broker are synchronized. - * A sync will result in the client library releasing the sent messages - * from memory. (messages are kept - * in memory so client can reconnect to a broker in the event of a failure) - * <p> - * Property name: message_size_before_sync - * <p> - * Default value: 200000000 - */ - public static long MAX_NOT_SYNC_DATA_LENGH; - /** - * The total message size in KBs that can be transferted before - * messages are flushed. - * When a flush returns all messages have reached the broker. - * <p> - * Property name: message_size_before_flush - * <p> - * Default value: 200000000 - */ - public static long MAX_NOT_FLUSH_DATA_LENGH; - -} diff --git a/java/client/src/main/java/org/apache/qpid/nclient/impl/DemoClient.java b/java/client/src/main/java/org/apache/qpid/nclient/impl/DemoClient.java deleted file mode 100644 index 88b5dc6392..0000000000 --- a/java/client/src/main/java/org/apache/qpid/nclient/impl/DemoClient.java +++ /dev/null @@ -1,93 +0,0 @@ -package org.apache.qpid.nclient.impl; - -import org.apache.qpid.ErrorCode; -import org.apache.qpid.api.Message; -import org.apache.qpid.nclient.Client; -import org.apache.qpid.nclient.Connection; -import org.apache.qpid.nclient.ClosedListener; -import org.apache.qpid.nclient.Session; -import org.apache.qpid.nclient.util.MessageListener; -import org.apache.qpid.nclient.util.MessagePartListenerAdapter; -import org.apache.qpid.transport.DeliveryProperties; -import org.apache.qpid.transport.Header; -import org.apache.qpid.transport.MessageAcceptMode; -import org.apache.qpid.transport.MessageAcquireMode; -import org.apache.qpid.transport.MessageProperties; - -import java.nio.ByteBuffer; -import java.util.UUID; - -public class DemoClient -{ - public static MessagePartListenerAdapter createAdapter() - { - return new MessagePartListenerAdapter(new MessageListener() - { - public void onMessage(Message m) - { - System.out.println("\n================== Received Msg =================="); - System.out.println("Message Id : " + m.getMessageProperties().getMessageId()); - System.out.println(m.toString()); - System.out.println("================== End Msg ==================\n"); - } - - }); - } - - public static final void main(String[] args) - { - Connection conn = Client.createConnection(); - try{ - conn.connect("0.0.0.0", 5672, "test", "guest", "guest"); - }catch(Exception e){ - e.printStackTrace(); - } - - Session ssn = conn.createSession(50000); - ssn.setClosedListener(new ClosedListener() - { - public void onClosed(ErrorCode errorCode, String reason, Throwable t) - { - System.out.println("ErrorCode : " + errorCode + " reason : " + reason); - } - }); - ssn.queueDeclare("queue1", null, null); - ssn.exchangeBind("queue1", "amq.direct", "queue1",null); - ssn.sync(); - - ssn.messageSubscribe("queue1", "myDest", (short)0, (short)0,createAdapter(), null); - - // queue - ssn.messageTransfer("amq.direct", MessageAcceptMode.NONE, MessageAcquireMode.PRE_ACQUIRED, - new Header(new DeliveryProperties().setRoutingKey("queue1"), - new MessageProperties().setMessageId(UUID.randomUUID())), - ByteBuffer.wrap("this is the data".getBytes())); - - //reject - ssn.messageTransfer("amq.direct", MessageAcceptMode.NONE, MessageAcquireMode.PRE_ACQUIRED, - new Header(new DeliveryProperties().setRoutingKey("stocks")), - ByteBuffer.wrap("this should be rejected".getBytes())); - ssn.sync(); - - // topic subs - ssn.messageSubscribe("topic1", "myDest2", (short)0, (short)0,createAdapter(), null); - ssn.messageSubscribe("topic2", "myDest3", (short)0, (short)0,createAdapter(), null); - ssn.messageSubscribe("topic3", "myDest4", (short)0, (short)0,createAdapter(), null); - ssn.sync(); - - ssn.queueDeclare("topic1", null, null); - ssn.exchangeBind("topic1", "amq.topic", "stock.*",null); - ssn.queueDeclare("topic2", null, null); - ssn.exchangeBind("topic2", "amq.topic", "stock.us.*",null); - ssn.queueDeclare("topic3", null, null); - ssn.exchangeBind("topic3", "amq.topic", "stock.us.rh",null); - ssn.sync(); - - // topic - ssn.messageTransfer("amq.topic", MessageAcceptMode.NONE, MessageAcquireMode.PRE_ACQUIRED, - new Header(new DeliveryProperties().setRoutingKey("stock.us.ibm"), - new MessageProperties().setMessageId(UUID.randomUUID())), - ByteBuffer.wrap("Topic message".getBytes())); - } - -} diff --git a/java/client/src/main/java/org/apache/qpid/nclient/interop/BasicInteropTest.java b/java/client/src/main/java/org/apache/qpid/nclient/interop/BasicInteropTest.java deleted file mode 100644 index 9ea9297e14..0000000000 --- a/java/client/src/main/java/org/apache/qpid/nclient/interop/BasicInteropTest.java +++ /dev/null @@ -1,155 +0,0 @@ -package org.apache.qpid.nclient.interop; - -import java.nio.ByteBuffer; -import java.util.HashMap; -import java.util.Map; - -import org.apache.qpid.ErrorCode; -import org.apache.qpid.QpidException; -import org.apache.qpid.api.Message; -import org.apache.qpid.nclient.Client; -import org.apache.qpid.nclient.Connection; -import org.apache.qpid.nclient.ClosedListener; -import org.apache.qpid.nclient.Session; -import org.apache.qpid.nclient.util.MessageListener; -import org.apache.qpid.nclient.util.MessagePartListenerAdapter; -import org.apache.qpid.transport.DeliveryProperties; -import org.apache.qpid.transport.Header; -import org.apache.qpid.transport.MessageAcceptMode; -import org.apache.qpid.transport.MessageAcquireMode; -import org.apache.qpid.transport.MessageCreditUnit; -import org.apache.qpid.transport.MessageFlowMode; -import org.apache.qpid.transport.MessageProperties; -import org.apache.qpid.transport.RangeSet; - -public class BasicInteropTest implements ClosedListener -{ - - private Session session; - private Connection conn; - private String host; - - public BasicInteropTest(String host) - { - this.host = host; - } - - public void close() throws QpidException - { - conn.close(); - } - - public void testCreateConnection(){ - System.out.println("------- Creating connection--------"); - conn = Client.createConnection(); - try{ - conn.connect(host, 5672, "test", "guest", "guest"); - }catch(Exception e){ - System.out.println("------- Error Creating connection--------"); - e.printStackTrace(); - System.exit(1); - } - System.out.println("------- Connection created Suscessfully --------"); - } - - public void testCreateSession(){ - System.out.println("------- Creating session --------"); - session = conn.createSession(0); - System.out.println("------- Session created sucessfully --------"); - } - - public void testExchange(){ - System.out.println("------- Creating an exchange --------"); - session.exchangeDeclare("test", "direct", "", null); - session.sync(); - System.out.println("------- Exchange created --------"); - } - - public void testQueue(){ - System.out.println("------- Creating a queue --------"); - session.queueDeclare("testQueue", "", null); - session.sync(); - System.out.println("------- Queue created --------"); - - System.out.println("------- Binding a queue --------"); - session.exchangeBind("testQueue", "test", "testKey", null); - session.sync(); - System.out.println("------- Queue bound --------"); - } - - public void testSendMessage(){ - System.out.println("------- Sending a message --------"); - Map<String,Object> props = new HashMap<String,Object>(); - props.put("name", "rajith"); - props.put("age", 10); - props.put("spf", 8.5); - session.messageTransfer("test", MessageAcceptMode.NONE, MessageAcquireMode.PRE_ACQUIRED, - new Header(new DeliveryProperties().setRoutingKey("testKey"), - new MessageProperties().setApplicationHeaders(props)), - ByteBuffer.wrap("TestMessage".getBytes())); - - session.sync(); - System.out.println("------- Message sent --------"); - } - - public void testSubscribe() - { - System.out.println("------- Sending a subscribe --------"); - session.messageSubscribe("testQueue", "myDest", - Session.TRANSFER_CONFIRM_MODE_REQUIRED, - Session.TRANSFER_ACQUIRE_MODE_PRE_ACQUIRE, - new MessagePartListenerAdapter(new MessageListener(){ - - public void onMessage(Message message) - { - System.out.println("--------Message Received--------"); - System.out.println(message.toString()); - System.out.println("--------/Message Received--------"); - RangeSet ack = new RangeSet(); - ack.add(message.getMessageTransferId(),message.getMessageTransferId()); - session.messageAcknowledge(ack, true); - } - - }), - null); - - System.out.println("------- Setting Credit mode --------"); - session.messageSetFlowMode("myDest", MessageFlowMode.WINDOW); - System.out.println("------- Setting Credit --------"); - session.messageFlow("myDest", MessageCreditUnit.MESSAGE, 1); - session.messageFlow("myDest", MessageCreditUnit.BYTE, -1); - } - - public void testMessageFlush() - { - session.messageFlush("myDest"); - session.sync(); - } - - public void onClosed(ErrorCode errorCode, String reason, Throwable t) - { - System.out.println("------- Broker Notified an error --------"); - System.out.println("------- " + errorCode + " --------"); - System.out.println("------- " + reason + " --------"); - System.out.println("------- /Broker Notified an error --------"); - } - - public static void main(String[] args) throws QpidException - { - String host = "0.0.0.0"; - if (args.length>0) - { - host = args[0]; - } - - BasicInteropTest t = new BasicInteropTest(host); - t.testCreateConnection(); - t.testCreateSession(); - t.testExchange(); - t.testQueue(); - t.testSubscribe(); - t.testSendMessage(); - t.testMessageFlush(); - t.close(); - } -} diff --git a/java/client/src/main/java/org/apache/qpid/nclient/util/ByteBufferMessage.java b/java/client/src/main/java/org/apache/qpid/nclient/util/ByteBufferMessage.java index 64c89c960c..14bfb4f95e 100644 --- a/java/client/src/main/java/org/apache/qpid/nclient/util/ByteBufferMessage.java +++ b/java/client/src/main/java/org/apache/qpid/nclient/util/ByteBufferMessage.java @@ -1,4 +1,25 @@ package org.apache.qpid.nclient.util; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + import java.io.IOException; import java.nio.ByteBuffer; diff --git a/java/client/src/main/java/org/apache/qpid/nclient/util/FileMessage.java b/java/client/src/main/java/org/apache/qpid/nclient/util/FileMessage.java deleted file mode 100644 index 179c91c2e9..0000000000 --- a/java/client/src/main/java/org/apache/qpid/nclient/util/FileMessage.java +++ /dev/null @@ -1,96 +0,0 @@ -package org.apache.qpid.nclient.util; - -import java.io.EOFException; -import java.io.FileInputStream; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; - -import org.apache.qpid.transport.DeliveryProperties; -import org.apache.qpid.transport.MessageProperties; -import org.apache.qpid.transport.Header; -import org.apache.qpid.api.Message; - -/** - * FileMessage provides pull style semantics for - * larges messages backed by a disk. - * Instead of loading all data into memeory it uses - * FileChannel to map regions of the file into memeory - * at a time. - * - * The write methods are not supported. - * - * From the standpoint of performance it is generally - * only worth mapping relatively large files into memory. - * - * FileMessage msg = new FileMessage(in,delProps,msgProps); - * session.messageTransfer(dest,msg,0,0); - * - * The messageTransfer method will read the file in chunks - * and stream it. - * - */ -public class FileMessage extends ReadOnlyMessage implements Message -{ - private FileChannel _fileChannel; - private int _chunkSize; - private long _fileSize; - private long _pos = 0; - - public FileMessage(FileInputStream in,int chunkSize,DeliveryProperties deliveryProperties,MessageProperties messageProperties)throws IOException - { - _messageProperties = messageProperties; - _deliveryProperties = deliveryProperties; - - _fileChannel = in.getChannel(); - _chunkSize = chunkSize; - _fileSize = _fileChannel.size(); - - if (_fileSize <= _chunkSize) - { - _chunkSize = (int)_fileSize; - } - } - - public void setHeader(Header header) { - //To change body of implemented methods use File | Settings | File Templates. - } - - public Header getHeader() { - return null; //To change body of implemented methods use File | Settings | File Templates. - } - - public void readData(byte[] target) throws IOException - { - throw new UnsupportedOperationException(); - } - - public ByteBuffer readData() throws IOException - { - if (_pos == _fileSize) - { - throw new EOFException(); - } - - if (_pos + _chunkSize > _fileSize) - { - _chunkSize = (int)(_fileSize - _pos); - } - MappedByteBuffer bb = _fileChannel.map(FileChannel.MapMode.READ_ONLY, _pos, _chunkSize); - _pos += _chunkSize; - return bb; - } - - /** - * This message is used by an application user to - * provide data to the client library using pull style - * semantics. Since the message is not transfered yet, it - * does not have a transfer id. Hence this method is not - * applicable to this implementation. - */ - public int getMessageTransferId() - { - throw new UnsupportedOperationException(); - } -} diff --git a/java/client/src/main/java/org/apache/qpid/nclient/util/MessagePartListenerAdapter.java b/java/client/src/main/java/org/apache/qpid/nclient/util/MessagePartListenerAdapter.java index 0e54e04a99..10fd8d2a80 100644 --- a/java/client/src/main/java/org/apache/qpid/nclient/util/MessagePartListenerAdapter.java +++ b/java/client/src/main/java/org/apache/qpid/nclient/util/MessagePartListenerAdapter.java @@ -1,4 +1,25 @@ package org.apache.qpid.nclient.util; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + import java.io.IOException; import java.nio.ByteBuffer; diff --git a/java/client/src/main/java/org/apache/qpid/nclient/util/ReadOnlyMessage.java b/java/client/src/main/java/org/apache/qpid/nclient/util/ReadOnlyMessage.java deleted file mode 100644 index 6583a95c7e..0000000000 --- a/java/client/src/main/java/org/apache/qpid/nclient/util/ReadOnlyMessage.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.apache.qpid.nclient.util; - -import java.nio.ByteBuffer; - -import org.apache.qpid.transport.DeliveryProperties; -import org.apache.qpid.transport.MessageProperties; -import org.apache.qpid.api.Message; - -public abstract class ReadOnlyMessage implements Message -{ - MessageProperties _messageProperties; - DeliveryProperties _deliveryProperties; - - public void appendData(byte[] src) - { - throw new UnsupportedOperationException("This Message is read only after the initial source"); - } - - public void appendData(ByteBuffer src) - { - throw new UnsupportedOperationException("This Message is read only after the initial source"); - } - - public DeliveryProperties getDeliveryProperties() - { - return _deliveryProperties; - } - - public MessageProperties getMessageProperties() - { - return _messageProperties; - } - - public void clearData() - { - throw new UnsupportedOperationException("This Message is read only after the initial source, cannot clear data"); - } -} diff --git a/java/client/src/main/java/org/apache/qpid/nclient/util/StreamingMessage.java b/java/client/src/main/java/org/apache/qpid/nclient/util/StreamingMessage.java deleted file mode 100644 index a4574438ac..0000000000 --- a/java/client/src/main/java/org/apache/qpid/nclient/util/StreamingMessage.java +++ /dev/null @@ -1,68 +0,0 @@ -package org.apache.qpid.nclient.util; - -import java.io.EOFException; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.SocketChannel; - -import org.apache.qpid.transport.DeliveryProperties; -import org.apache.qpid.transport.MessageProperties; -import org.apache.qpid.transport.Header; -import org.apache.qpid.api.Message; - -public class StreamingMessage extends ReadOnlyMessage implements Message -{ - SocketChannel _socChannel; - private int _chunkSize; - private ByteBuffer _readBuf; - - public Header getHeader() { - return null; //To change body of implemented methods use File | Settings | File Templates. - } - - public void setHeader(Header header) { - //To change body of implemented methods use File | Settings | File Templates. - } - - public StreamingMessage(SocketChannel in,int chunkSize,DeliveryProperties deliveryProperties,MessageProperties messageProperties)throws IOException - { - _messageProperties = messageProperties; - _deliveryProperties = deliveryProperties; - - _socChannel = in; - _chunkSize = chunkSize; - _readBuf = ByteBuffer.allocate(_chunkSize); - } - - public void readData(byte[] target) throws IOException - { - throw new UnsupportedOperationException(); - } - - public ByteBuffer readData() throws IOException - { - if(_socChannel.isConnected() && _socChannel.isOpen()) - { - _readBuf.clear(); - _socChannel.read(_readBuf); - } - else - { - throw new EOFException("The underlying socket/channel has closed"); - } - - return _readBuf.duplicate(); - } - - /** - * This message is used by an application user to - * provide data to the client library using pull style - * semantics. Since the message is not transfered yet, it - * does not have a transfer id. Hence this method is not - * applicable to this implementation. - */ - public int getMessageTransferId() - { - throw new UnsupportedOperationException(); - } -} diff --git a/java/client/src/test/java/org/apache/qpid/client/AMQQueueTest.java b/java/client/src/test/java/org/apache/qpid/client/AMQQueueTest.java new file mode 100644 index 0000000000..7789f87ace --- /dev/null +++ b/java/client/src/test/java/org/apache/qpid/client/AMQQueueTest.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.framing.AMQShortString; + +import junit.framework.TestCase; + +public class AMQQueueTest extends TestCase +{ + AMQShortString exchange = new AMQShortString("test.exchange"); + AMQShortString routingkey = new AMQShortString("test-route"); + AMQShortString qname = new AMQShortString("test-queue"); + AMQShortString[] oneBinding = new AMQShortString[]{new AMQShortString("bindingA")}; + AMQShortString[] bindings = new AMQShortString[]{new AMQShortString("bindingB"), + new AMQShortString("bindingC")}; + + public void testToURLNoBindings() + { + AMQQueue dest = new AMQQueue(exchange, routingkey, qname); + String url = dest.toURL(); + assertEquals("direct://test.exchange/test-route/test-queue?routingkey='test-route'", url); + } +} diff --git a/java/client/src/test/java/org/apache/qpid/client/MockAMQConnection.java b/java/client/src/test/java/org/apache/qpid/client/MockAMQConnection.java new file mode 100644 index 0000000000..da44822ec3 --- /dev/null +++ b/java/client/src/test/java/org/apache/qpid/client/MockAMQConnection.java @@ -0,0 +1,94 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.state.AMQState; +import org.apache.qpid.framing.ProtocolVersion; +import org.apache.qpid.jms.ConnectionURL; +import org.apache.qpid.jms.BrokerDetails; +import org.apache.qpid.url.URLSyntaxException; + +import java.io.IOException; + +public class MockAMQConnection extends AMQConnection +{ + public MockAMQConnection(String broker, String username, String password, String clientName, String virtualHost) + throws AMQException, URLSyntaxException + { + super(broker, username, password, clientName, virtualHost); + } + + public MockAMQConnection(String broker, String username, String password, String clientName, String virtualHost, SSLConfiguration sslConfig) + throws AMQException, URLSyntaxException + { + super(broker, username, password, clientName, virtualHost, sslConfig); + } + + public MockAMQConnection(String host, int port, String username, String password, String clientName, String virtualHost) + throws AMQException, URLSyntaxException + { + super(host, port, username, password, clientName, virtualHost); + } + + public MockAMQConnection(String host, int port, String username, String password, String clientName, String virtualHost, SSLConfiguration sslConfig) + throws AMQException, URLSyntaxException + { + super(host, port, username, password, clientName, virtualHost, sslConfig); + } + + public MockAMQConnection(String host, int port, boolean useSSL, String username, String password, String clientName, String virtualHost, SSLConfiguration sslConfig) + throws AMQException, URLSyntaxException + { + super(host, port, useSSL, username, password, clientName, virtualHost, sslConfig); + } + + public MockAMQConnection(String connection) + throws AMQException, URLSyntaxException + { + super(connection); + } + + public MockAMQConnection(String connection, SSLConfiguration sslConfig) + throws AMQException, URLSyntaxException + { + super(connection, sslConfig); + } + + public MockAMQConnection(ConnectionURL connectionURL, SSLConfiguration sslConfig) + throws AMQException + { + super(connectionURL, sslConfig); + } + + protected MockAMQConnection(String username, String password, String clientName, String virtualHost) + { + super(username, password, clientName, virtualHost); + } + + @Override + public ProtocolVersion makeBrokerConnection(BrokerDetails brokerDetail) throws IOException + { + _connected = true; + _protocolHandler.getStateManager().changeState(AMQState.CONNECTION_OPEN); + return null; + } +} diff --git a/java/client/src/test/java/org/apache/qpid/client/message/AbstractJMSMessageTest.java b/java/client/src/test/java/org/apache/qpid/client/message/AbstractJMSMessageTest.java new file mode 100644 index 0000000000..b4774113be --- /dev/null +++ b/java/client/src/test/java/org/apache/qpid/client/message/AbstractJMSMessageTest.java @@ -0,0 +1,36 @@ +package org.apache.qpid.client.message; + +import javax.jms.JMSException; + +import junit.framework.TestCase; + +public class AbstractJMSMessageTest extends TestCase +{ + + public void testSetNullJMSReplyTo08() throws JMSException + { + JMSTextMessage message = new JMSTextMessage(AMQMessageDelegateFactory.FACTORY_0_8); + try + { + message.setJMSReplyTo(null); + } + catch (IllegalArgumentException e) + { + fail("Null destination should be allowed"); + } + } + + public void testSetNullJMSReplyTo10() throws JMSException + { + JMSTextMessage message = new JMSTextMessage(AMQMessageDelegateFactory.FACTORY_0_10); + try + { + message.setJMSReplyTo(null); + } + catch (IllegalArgumentException e) + { + fail("Null destination should be allowed"); + } + } + +} diff --git a/java/client/src/test/java/org/apache/qpid/client/protocol/AMQProtocolHandlerTest.java b/java/client/src/test/java/org/apache/qpid/client/protocol/AMQProtocolHandlerTest.java new file mode 100644 index 0000000000..f520a21ba0 --- /dev/null +++ b/java/client/src/test/java/org/apache/qpid/client/protocol/AMQProtocolHandlerTest.java @@ -0,0 +1,289 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.protocol; + +import junit.framework.TestCase; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.AMQBody; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.framing.amqp_8_0.BasicRecoverOkBodyImpl; +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.transport.TestNetworkDriver; +import org.apache.qpid.client.MockAMQConnection; +import org.apache.qpid.client.AMQAuthenticationException; +import org.apache.qpid.client.state.AMQState; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * This is a test address QPID-1431 where frame listeners would fail to be notified of an incomming exception. + * + * Currently we do checks at the Session level to ensure that the connection/session are open. However, it is possible + * for the connection to close AFTER this check has been performed. + * + * Performing a similar check at the frameListener level in AMQProtocolHandler makes most sence as this will prevent + * listening when there can be no returning frames. + * + * With the correction in place it also means that the new listener will either make it on to the list for notification + * or it will be notified of any existing exception due to the connection being closed. + * + * There may still be an issue in this space if the client utilises a second thread to close the session as there will + * be no exception set to throw and so the wait will occur. That said when the session is closed the framelisteners + * should be notified. Not sure this is tested. + */ +public class AMQProtocolHandlerTest extends TestCase +{ + private static final Logger _logger = LoggerFactory.getLogger(AMQProtocolHandlerTest.class); + + // The handler to test + AMQProtocolHandler _handler; + + // A frame to block upon whilst waiting the exception + AMQFrame _blockFrame; + + // Latch to know when the listener receives an exception + private CountDownLatch _handleCountDown; + // The listener that will receive an exception + BlockToAccessFrameListener _listener; + + @Override + public void setUp() throws Exception + { + //Create a new ProtocolHandler with a fake connection. + _handler = new AMQProtocolHandler(new MockAMQConnection("amqp://guest:guest@client/test?brokerlist='vm://:1'")); + _handler.setNetworkDriver(new TestNetworkDriver()); + AMQBody body = BasicRecoverOkBodyImpl.getFactory().newInstance(null, 1); + _blockFrame = new AMQFrame(0, body); + + _handleCountDown = new CountDownLatch(1); + + _logger.info("Creating _Listener that should also receive the thrown exception."); + _listener = new BlockToAccessFrameListener(1); + } + + /** + * There are two paths based on the type of exception thrown. + * + * This tests that when an AMQException is thrown we get the same type of AMQException back with the real exception + * wrapped as the cause. + * + * @throws InterruptedException - if we are unable to wait for the test signals + */ + public void testFrameListenerUpdateWithAMQException() throws InterruptedException + { + AMQException trigger = new AMQAuthenticationException(AMQConstant.ACCESS_REFUSED, + "AMQPHTest", new RuntimeException()); + + performWithException(trigger); + + + AMQException receivedException = (AMQException) _listener.getReceivedException(); + + assertEquals("Return exception was not the expected type", + AMQAuthenticationException.class, receivedException.getClass()); + + assertEquals("The _Listener did not receive the correct error code", + trigger.getErrorCode(), receivedException.getErrorCode()); + } + + /** + * There are two paths based on the type of exception thrown. + * + * This tests that when a generic Exception is thrown we get the exception back wrapped in a AMQException + * as the cause. + * @throws InterruptedException - if we are unable to wait for the test signals + */ + public void testFrameListenerUpdateWithException() throws InterruptedException + { + + Exception trigger = new Exception(new RuntimeException()); + + performWithException(trigger); + + assertEquals("The _Listener did not receive the correct error code", + AMQConstant.INTERNAL_ERROR, ((AMQException)_listener.getReceivedException()).getErrorCode()); + } + + /** + * This is the main test method for both test cases. + * + * What occurs is that we create a new thread that will block (<30s[DEFAULT]) for a frame or exception to occur . + * + * We use a CountDownLatch to ensure that the new thread is running before we then yield and sleep to help ensure + * the new thread has entered the synchronized block in the writeCommandFrameAndWaitForReply. + * + * We can then ack like an the incomming exception handler in (ConnectionCloseMethodHandler). + * + * We fire the error to the stateManager, which in this case will recored the error as there are no state listeners. + * + * We then set the connection to be closed, as we would normally close the socket at this point. + * + * Then fire the exception in to any frameListeners. + * + * The blocked listener (created above) when receiving the error simulates the user by creating a new request to + * block for a frame. + * + * This request should fail. Prior to the fix this will fail with a NPE as we are attempting to use a null listener + * in the writeCommand.... call L:268. + * + * This highlights that the listener would be added dispite there being a pending error state that the listener will + * miss as it is not currently part of the _frameListeners set that is being notified by the iterator. + * + * The method waits to ensure that an exception is received before returning. + * + * The calling methods validate that exception that was received based on the one they sent in. + * + * @param trigger The exception to throw through the handler + */ + private void performWithException(Exception trigger) throws InterruptedException + { + + final CountDownLatch callingWriteCommand = new CountDownLatch(1); + + //Set an initial listener that will allow us to create a new blocking method + new Thread(new Runnable() + { + public void run() + { + + try + { + + _logger.info("At initial block, signalling to fire new exception"); + callingWriteCommand.countDown(); + + _handler.writeCommandFrameAndWaitForReply(_blockFrame, _listener); + } + catch (Exception e) + { + e.printStackTrace(); + fail(e.getMessage()); + } + } + }).start(); + + _logger.info("Waiting for 'initial block' to start "); + if (!callingWriteCommand.await(1000, TimeUnit.MILLISECONDS)) + { + fail("Failed to start new thread to block for frame"); + } + + // Do what we can to ensure that this thread does not continue before the above thread has hit the synchronized + // block in the writeCommandFrameAndWaitForReply + Thread.yield(); + Thread.sleep(1000); + + _logger.info("Firing Erorr through state manager. There should be not state waiters here."); + _handler.getStateManager().error(trigger); + + _logger.info("Setting state to be CONNECTION_CLOSED."); + + _handler.getStateManager().changeState(AMQState.CONNECTION_CLOSED); + + _logger.info("Firing exception"); + _handler.propagateExceptionToFrameListeners(trigger); + + _logger.info("Awaiting notifcation from handler that exception arrived."); + + if (!_handleCountDown.await(2000, TimeUnit.MILLISECONDS)) + { + fail("Failed to handle exception and timeout has not occured"); + } + + + assertNotNull("The _Listener did not receive the exception", _listener.getReceivedException()); + + assertTrue("Received exception not an AMQException", + _listener.getReceivedException() instanceof AMQException); + + AMQException receivedException = (AMQException) _listener.getReceivedException(); + + assertTrue("The _Listener did not receive the correct message", + receivedException.getMessage().startsWith(trigger.getMessage())); + + + assertEquals("The _Listener did not receive the correct cause", + trigger, receivedException.getCause()); + + assertEquals("The _Listener did not receive the correct sub cause", + trigger.getCause(), receivedException.getCause().getCause()); + + } + + class BlockToAccessFrameListener extends BlockingMethodFrameListener + { + private Exception _receivedException = null; + + /** + * Creates a new method listener, that filters incoming method to just those that match the specified channel id. + * + * @param channelId The channel id to filter incoming methods with. + */ + public BlockToAccessFrameListener(int channelId) + { + super(channelId); + _logger.info("Creating a listener:" + this); + } + + public boolean processMethod(int channelId, AMQMethodBody frame) + { + return true; + } + + @Override + public void error(Exception e) + { + _logger.info("Exception(" + e + ") Received by:" + this); + // Create a new Thread to start the blocking registration. + new Thread(new Runnable() + { + + public void run() + { + //Set an initial listener that will allow us to create a new blocking method + try + { + _handler.writeCommandFrameAndWaitForReply(_blockFrame, null, 2000L); + _logger.info("listener(" + this + ") Wait completed"); + } + catch (Exception e) + { + _logger.info("listener(" + this + ") threw exception:" + e.getMessage()); + _receivedException = e; + } + + _logger.info("listener(" + this + ") completed"); + _handleCountDown.countDown(); + } + }).start(); + } + + public Exception getReceivedException() + { + return _receivedException; + } + } + +} diff --git a/java/client/src/test/java/org/apache/qpid/client/protocol/MockIoSession.java b/java/client/src/test/java/org/apache/qpid/client/protocol/MockIoSession.java new file mode 100644 index 0000000000..f0938a4bc0 --- /dev/null +++ b/java/client/src/test/java/org/apache/qpid/client/protocol/MockIoSession.java @@ -0,0 +1,312 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.protocol; + +import org.apache.mina.common.*; +import org.apache.mina.common.support.DefaultCloseFuture; +import org.apache.mina.common.support.DefaultWriteFuture; +import org.apache.mina.common.support.AbstractIoFilterChain; +import org.apache.qpid.client.protocol.AMQProtocolSession; + +import java.net.SocketAddress; +import java.net.InetSocketAddress; +import java.util.Set; + +public class MockIoSession implements IoSession +{ + private AMQProtocolSession _protocolSession; + + /** + * Stores the last response written + */ + private Object _lastWrittenObject; + + private boolean _closing; + private IoFilterChain _filterChain; + + public MockIoSession() + { + _filterChain = new AbstractIoFilterChain(this) + { + protected void doWrite(IoSession ioSession, IoFilter.WriteRequest writeRequest) throws Exception + { + + } + + protected void doClose(IoSession ioSession) throws Exception + { + + } + }; + } + + public Object getLastWrittenObject() + { + return _lastWrittenObject; + } + + public IoService getService() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public IoServiceConfig getServiceConfig() + { + return null; + } + + public IoHandler getHandler() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public IoSessionConfig getConfig() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public IoFilterChain getFilterChain() + { + return _filterChain; + } + + public WriteFuture write(Object message) + { + WriteFuture wf = new DefaultWriteFuture(null); + _lastWrittenObject = message; + return wf; + } + + public CloseFuture close() + { + _closing = true; + CloseFuture cf = new DefaultCloseFuture(null); + cf.setClosed(); + return cf; + } + + public Object getAttachment() + { + return _protocolSession; + } + + public Object setAttachment(Object attachment) + { + Object current = _protocolSession; + _protocolSession = (AMQProtocolSession) attachment; + return current; + } + + public Object getAttribute(String key) + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public Object setAttribute(String key, Object value) + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public Object setAttribute(String key) + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public Object removeAttribute(String key) + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean containsAttribute(String key) + { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + public Set getAttributeKeys() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public TransportType getTransportType() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean isConnected() + { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean isClosing() + { + return _closing; + } + + public CloseFuture getCloseFuture() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public SocketAddress getRemoteAddress() + { + return new InetSocketAddress("127.0.0.1", 1234); //To change body of implemented methods use File | Settings | File Templates. + } + + public SocketAddress getLocalAddress() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public SocketAddress getServiceAddress() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public int getIdleTime(IdleStatus status) + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public long getIdleTimeInMillis(IdleStatus status) + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public void setIdleTime(IdleStatus status, int idleTime) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public int getWriteTimeout() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public long getWriteTimeoutInMillis() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public void setWriteTimeout(int writeTimeout) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public TrafficMask getTrafficMask() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public void setTrafficMask(TrafficMask trafficMask) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void suspendRead() + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void suspendWrite() + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void resumeRead() + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void resumeWrite() + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public long getReadBytes() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public long getWrittenBytes() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public long getReadMessages() + { + return 0L; + } + + public long getWrittenMessages() + { + return 0L; + } + + public long getWrittenWriteRequests() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public int getScheduledWriteRequests() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public int getScheduledWriteBytes() + { + return 0; //TODO + } + + public long getCreationTime() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public long getLastIoTime() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public long getLastReadTime() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public long getLastWriteTime() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean isIdle(IdleStatus status) + { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + public int getIdleCount(IdleStatus status) + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public long getLastIdleTime(IdleStatus status) + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } +} diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/client/BrokerDetails/BrokerDetailsTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/client/BrokerDetails/BrokerDetailsTest.java index 7bf96b99c1..1b27ff6300 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/client/BrokerDetails/BrokerDetailsTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/client/BrokerDetails/BrokerDetailsTest.java @@ -20,14 +20,19 @@ */ package org.apache.qpid.test.unit.client.BrokerDetails; +import java.util.HashMap; +import java.util.Map; + import junit.framework.TestCase; import org.apache.qpid.client.AMQBrokerDetails; +import org.apache.qpid.client.AMQConnectionURL; +import org.apache.qpid.jms.ConnectionURL; +import org.apache.qpid.jms.BrokerDetails; import org.apache.qpid.url.URLSyntaxException; public class BrokerDetailsTest extends TestCase { - public void testMultiParameters() throws URLSyntaxException { String url = "tcp://localhost:5672?timeout='200',immediatedelivery='true'"; 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 6f4c26945c..7400b524fd 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 @@ -375,6 +375,19 @@ public class ConnectionURLTest extends TestCase assertTrue(connectionurl.getBrokerCount() == 1); } + public void testClientIDWithUnderscore() throws URLSyntaxException + { + String url = "amqp://user:pass@client_id/test?brokerlist='tcp://localhost:5672'"; + + ConnectionURL connectionurl = new AMQConnectionURL(url); + + assertTrue(connectionurl.getUsername().equals("user")); + assertTrue(connectionurl.getPassword().equals("pass")); + assertTrue(connectionurl.getVirtualHost().equals("/test")); + assertTrue(connectionurl.getClientName().equals("client_id")); + + assertTrue(connectionurl.getBrokerCount() == 1); + } public void testWrongOptionSeparatorInOptions() { @@ -486,7 +499,8 @@ public class ConnectionURLTest extends TestCase 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()); + assertEquals("URL does not toString as expected", + url.replace(":guest", ":********"), curl.toString()); } catch (URLSyntaxException e) { diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/client/destinationurl/DestinationURLTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/client/destinationurl/DestinationURLTest.java index 2a66b86985..22e432a44f 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/client/destinationurl/DestinationURLTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/client/destinationurl/DestinationURLTest.java @@ -177,6 +177,18 @@ public class DestinationURLTest extends TestCase assertTrue("Failed to throw an URISyntaxException when both the bindingkey and routingkey is specified",exceptionThrown); } + + public void testDestinationWithDurableTopic() throws URISyntaxException + { + + String url = "topic://amq.topic//testTopicD?durable='true'&autodelete='true'&clientid='test'&subscription='testQueueD'"; + + AMQBindingURL dest = new AMQBindingURL(url); + + assertTrue(dest.getExchangeClass().equals("topic")); + assertTrue(dest.getExchangeName().equals("amq.topic")); + assertTrue(dest.getQueueName().equals("test:testQueueD")); + } public static junit.framework.Test suite() { diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/client/message/BytesMessageTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/client/message/BytesMessageTest.java index bbabf0b57d..65013e7e6d 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/client/message/BytesMessageTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/client/message/BytesMessageTest.java @@ -559,7 +559,7 @@ public class BytesMessageTest extends TestCase JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); bm.reset(); String result = bm.toBodyString(); - assertNull(result); + assertEquals("\"\"", result); } public static junit.framework.Test suite() diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/client/message/StreamMessageTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/client/message/StreamMessageTest.java index 802f1e6c2e..085dd81079 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/client/message/StreamMessageTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/client/message/StreamMessageTest.java @@ -435,7 +435,7 @@ public class StreamMessageTest extends TestCase JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); bm.reset(); String result = bm.toBodyString(); - assertNull(result); + assertEquals("\"\"", result); } private void checkConversionsFail(StreamMessage sm, int[] conversions) throws JMSException diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/jndi/ConnectionFactoryTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/jndi/ConnectionFactoryTest.java new file mode 100644 index 0000000000..9e76b0d468 --- /dev/null +++ b/java/client/src/test/java/org/apache/qpid/test/unit/jndi/ConnectionFactoryTest.java @@ -0,0 +1,75 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.unit.jndi; + +import junit.framework.TestCase; +import org.apache.qpid.client.AMQConnectionFactory; +import org.apache.qpid.jms.BrokerDetails; +import org.apache.qpid.jms.ConnectionURL; +import org.apache.qpid.url.URLSyntaxException; + +public class ConnectionFactoryTest extends TestCase +{ + + //URL will be returned with the password field swapped for '********' + // so ensure that these two strings are kept in sync. + public static final String URL = "amqp://guest:guest@clientID/test?brokerlist='tcp://localhost:5672'"; + public static final String URL_STAR_PWD = "amqp://guest:********@clientID/test?brokerlist='tcp://localhost:5672'"; + + public void testConnectionURLString() + { + AMQConnectionFactory factory = new AMQConnectionFactory(); + + assertNull("ConnectionURL should have no value at start", + factory.getConnectionURL()); + + try + { + factory.setConnectionURLString(URL); + } + catch (URLSyntaxException e) + { + fail(e.getMessage()); + } + + //URL will be returned with the password field swapped for '********' + assertEquals("Connection URL not correctly set", URL_STAR_PWD, factory.getConnectionURLString()); + + // Further test that the processed ConnectionURL is as expected after + // the set call + ConnectionURL connectionurl = factory.getConnectionURL(); + + assertNull("Failover is set.", connectionurl.getFailoverMethod()); + assertEquals("guest", connectionurl.getUsername()); + assertEquals("guest", connectionurl.getPassword()); + assertEquals("clientID", connectionurl.getClientName()); + assertEquals("/test", connectionurl.getVirtualHost()); + + assertEquals(1, connectionurl.getBrokerCount()); + + BrokerDetails service = connectionurl.getBrokerDetails(0); + + assertEquals("tcp", service.getTransport()); + assertEquals("localhost", service.getHost()); + assertEquals(5672, service.getPort()); + + } +} diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/jndi/JNDIPropertyFileTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/jndi/JNDIPropertyFileTest.java new file mode 100644 index 0000000000..a1b14d5723 --- /dev/null +++ b/java/client/src/test/java/org/apache/qpid/test/unit/jndi/JNDIPropertyFileTest.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.test.unit.jndi; + +import java.util.Properties; + +import javax.jms.Queue; +import javax.jms.Topic; +import javax.naming.Context; +import javax.naming.InitialContext; + +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.framing.AMQShortString; + +import junit.framework.TestCase; + +public class JNDIPropertyFileTest extends TestCase +{ + Context ctx; + + public JNDIPropertyFileTest() throws Exception + { + Properties properties = new Properties(); + properties.load(this.getClass().getResourceAsStream("JNDITest.properties")); + + //Create the initial context + ctx = new InitialContext(properties); + } + + public void testQueueNamesWithTrailingSpaces() throws Exception + { + Queue queue = (Queue)ctx.lookup("QueueNameWithSpace"); + assertEquals("QueueNameWithSpace",queue.getQueueName()); + } + + public void testTopicNamesWithTrailingSpaces() throws Exception + { + Topic topic = (Topic)ctx.lookup("TopicNameWithSpace"); + assertEquals("TopicNameWithSpace",topic.getTopicName()); + } + + public void testMultipleTopicNamesWithTrailingSpaces() throws Exception + { + Topic topic = (Topic)ctx.lookup("MultipleTopicNamesWithSpace"); + int i = 0; + for (AMQShortString bindingKey: ((AMQDestination)topic).getBindingKeys()) + { + i++; + assertEquals("Topic" + i + "WithSpace",bindingKey.asString()); + } + } +} diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/jndi/JNDITest.properties b/java/client/src/test/java/org/apache/qpid/test/unit/jndi/JNDITest.properties new file mode 100644 index 0000000000..07017a05a6 --- /dev/null +++ b/java/client/src/test/java/org/apache/qpid/test/unit/jndi/JNDITest.properties @@ -0,0 +1,28 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +java.naming.factory.initial = org.apache.qpid.jndi.PropertiesFileInitialContextFactory + +# Queue name with spaces +queue.QueueNameWithSpace = QueueNameWithSpace + +# Topic name with spaces +topic.TopicNameWithSpace = TopicNameWithSpace + +# Multiple topic names with spaces +topic.MultipleTopicNamesWithSpace = Topic1WithSpace , Topic2WithSpace , Topic3WithSpace diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/message/TestAMQSession.java b/java/client/src/test/java/org/apache/qpid/test/unit/message/TestAMQSession.java index a881f6a822..566a222897 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/message/TestAMQSession.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/message/TestAMQSession.java @@ -44,7 +44,9 @@ public class TestAMQSession extends AMQSession<BasicMessageConsumer_0_8, BasicMe } - public void sendQueueBind(AMQShortString queueName, AMQShortString routingKey, FieldTable arguments, AMQShortString exchangeName, AMQDestination destination) throws AMQException, FailoverException + public void sendQueueBind(AMQShortString queueName, AMQShortString routingKey, FieldTable arguments, + AMQShortString exchangeName, AMQDestination destination, + boolean nowait) throws AMQException, FailoverException { } @@ -129,7 +131,8 @@ public class TestAMQSession extends AMQSession<BasicMessageConsumer_0_8, BasicMe } - public void sendQueueDeclare(AMQDestination amqd, AMQProtocolHandler protocolHandler) throws AMQException, FailoverException + public void sendQueueDeclare(AMQDestination amqd, AMQProtocolHandler protocolHandler, + boolean nowait) throws AMQException, FailoverException { } |
