diff options
Diffstat (limited to 'java/client/src/main')
93 files changed, 4028 insertions, 4372 deletions
diff --git a/java/client/src/main/java/client.bnd b/java/client/src/main/java/client.bnd index 0ddd163d4f..98696dc7d8 100755 --- a/java/client/src/main/java/client.bnd +++ b/java/client/src/main/java/client.bnd @@ -17,7 +17,7 @@ # under the License. # -ver: 0.9.0 +ver: 0.13.0 Bundle-SymbolicName: qpid-client Bundle-Version: ${ver} diff --git a/java/client/src/main/java/org/apache/mina/transport/socket/nio/ExistingSocketConnector.java b/java/client/src/main/java/org/apache/mina/transport/socket/nio/ExistingSocketConnector.java deleted file mode 100644 index 98716c0c3c..0000000000 --- a/java/client/src/main/java/org/apache/mina/transport/socket/nio/ExistingSocketConnector.java +++ /dev/null @@ -1,478 +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.mina.transport.socket.nio; - -import edu.emory.mathcs.backport.java.util.concurrent.Executor; -import org.apache.mina.common.ConnectFuture; -import org.apache.mina.common.ExceptionMonitor; -import org.apache.mina.common.IoConnector; -import org.apache.mina.common.IoConnectorConfig; -import org.apache.mina.common.IoHandler; -import org.apache.mina.common.IoServiceConfig; -import org.apache.mina.common.support.BaseIoConnector; -import org.apache.mina.common.support.DefaultConnectFuture; -import org.apache.mina.util.NamePreservingRunnable; -import org.apache.mina.util.NewThreadExecutor; -import org.apache.mina.util.Queue; - -import java.io.IOException; -import java.net.ConnectException; -import java.net.Socket; -import java.net.SocketAddress; -import java.nio.channels.SelectionKey; -import java.nio.channels.Selector; -import java.nio.channels.SocketChannel; -import java.util.Iterator; -import java.util.Set; - -/** - * {@link IoConnector} for socket transport (TCP/IP). - * - * @author The Apache Directory Project (mina-dev@directory.apache.org) - * @version $Rev: 627427 $, $Date: 2008-02-13 14:39:10 +0000 (Wed, 13 Feb 2008) $ - */ -public class ExistingSocketConnector extends BaseIoConnector -{ - /** @noinspection StaticNonFinalField */ - private static volatile int nextId = 0; - - private final Object lock = new Object(); - private final int id = nextId++; - private final String threadName = "SocketConnector-" + id; - private SocketConnectorConfig defaultConfig = new SocketConnectorConfig(); - private final Queue connectQueue = new Queue(); - private final SocketIoProcessor[] ioProcessors; - private final int processorCount; - private final Executor executor; - - /** @noinspection FieldAccessedSynchronizedAndUnsynchronized */ - private Selector selector; - private Worker worker; - private int processorDistributor = 0; - private int workerTimeout = 60; // 1 min. - private Socket _openSocket = null; - - /** Create a connector with a single processing thread using a NewThreadExecutor */ - public ExistingSocketConnector() - { - this(1, new NewThreadExecutor()); - } - - /** - * Create a connector with the desired number of processing threads - * - * @param processorCount Number of processing threads - * @param executor Executor to use for launching threads - */ - public ExistingSocketConnector(int processorCount, Executor executor) - { - if (processorCount < 1) - { - throw new IllegalArgumentException("Must have at least one processor"); - } - - this.executor = executor; - this.processorCount = processorCount; - ioProcessors = new SocketIoProcessor[processorCount]; - - for (int i = 0; i < processorCount; i++) - { - ioProcessors[i] = new SocketIoProcessor("SocketConnectorIoProcessor-" + id + "." + i, executor); - } - } - - /** - * How many seconds to keep the connection thread alive between connection requests - * - * @return Number of seconds to keep connection thread alive - */ - public int getWorkerTimeout() - { - return workerTimeout; - } - - /** - * Set how many seconds the connection worker thread should remain alive once idle before terminating itself. - * - * @param workerTimeout Number of seconds to keep thread alive. Must be >=0 - */ - public void setWorkerTimeout(int workerTimeout) - { - if (workerTimeout < 0) - { - throw new IllegalArgumentException("Must be >= 0"); - } - this.workerTimeout = workerTimeout; - } - - public ConnectFuture connect(SocketAddress address, IoHandler handler, IoServiceConfig config) - { - return connect(address, null, handler, config); - } - - public ConnectFuture connect(SocketAddress address, SocketAddress localAddress, - IoHandler handler, IoServiceConfig config) - { - /** Changes here from the Mina OpenSocketConnector. - * Ignoreing all address as they are not needed */ - - if (handler == null) - { - throw new NullPointerException("handler"); - } - - - if (config == null) - { - config = getDefaultConfig(); - } - - if (_openSocket == null) - { - throw new IllegalArgumentException("Specifed Socket not active"); - } - - boolean success = false; - - try - { - DefaultConnectFuture future = new DefaultConnectFuture(); - newSession(_openSocket, handler, config, future); - success = true; - return future; - } - catch (IOException e) - { - return DefaultConnectFuture.newFailedFuture(e); - } - finally - { - if (!success && _openSocket != null) - { - try - { - _openSocket.close(); - } - catch (IOException e) - { - ExceptionMonitor.getInstance().exceptionCaught(e); - } - } - } - } - - public IoServiceConfig getDefaultConfig() - { - return defaultConfig; - } - - /** - * Sets the config this connector will use by default. - * - * @param defaultConfig the default config. - * - * @throws NullPointerException if the specified value is <code>null</code>. - */ - public void setDefaultConfig(SocketConnectorConfig defaultConfig) - { - if (defaultConfig == null) - { - throw new NullPointerException("defaultConfig"); - } - this.defaultConfig = defaultConfig; - } - - private synchronized void startupWorker() throws IOException - { - if (worker == null) - { - selector = Selector.open(); - worker = new Worker(); - executor.execute(new NamePreservingRunnable(worker)); - } - } - - private void registerNew() - { - if (connectQueue.isEmpty()) - { - return; - } - - for (; ;) - { - ConnectionRequest req; - synchronized (connectQueue) - { - req = (ConnectionRequest) connectQueue.pop(); - } - - if (req == null) - { - break; - } - - SocketChannel ch = req.channel; - try - { - ch.register(selector, SelectionKey.OP_CONNECT, req); - } - catch (IOException e) - { - req.setException(e); - } - } - } - - private void processSessions(Set keys) - { - Iterator it = keys.iterator(); - - while (it.hasNext()) - { - SelectionKey key = (SelectionKey) it.next(); - - if (!key.isConnectable()) - { - continue; - } - - SocketChannel ch = (SocketChannel) key.channel(); - ConnectionRequest entry = (ConnectionRequest) key.attachment(); - - boolean success = false; - try - { - ch.finishConnect(); - newSession(ch, entry.handler, entry.config, entry); - success = true; - } - catch (Throwable e) - { - entry.setException(e); - } - finally - { - key.cancel(); - if (!success) - { - try - { - ch.close(); - } - catch (IOException e) - { - ExceptionMonitor.getInstance().exceptionCaught(e); - } - } - } - } - - keys.clear(); - } - - private void processTimedOutSessions(Set keys) - { - long currentTime = System.currentTimeMillis(); - Iterator it = keys.iterator(); - - while (it.hasNext()) - { - SelectionKey key = (SelectionKey) it.next(); - - if (!key.isValid()) - { - continue; - } - - ConnectionRequest entry = (ConnectionRequest) key.attachment(); - - if (currentTime >= entry.deadline) - { - entry.setException(new ConnectException()); - try - { - key.channel().close(); - } - catch (IOException e) - { - ExceptionMonitor.getInstance().exceptionCaught(e); - } - finally - { - key.cancel(); - } - } - } - } - - private void newSession(Socket socket, IoHandler handler, IoServiceConfig config, ConnectFuture connectFuture) - throws IOException - { - SocketSessionImpl session = new SocketSessionImpl(this, - nextProcessor(), - getListeners(), - config, - socket.getChannel(), - handler, - socket.getRemoteSocketAddress()); - - newSession(session, config, connectFuture); - } - - private void newSession(SocketChannel ch, IoHandler handler, IoServiceConfig config, ConnectFuture connectFuture) - throws IOException - - { - SocketSessionImpl session = new SocketSessionImpl(this, - nextProcessor(), - getListeners(), - config, - ch, - handler, - ch.socket().getRemoteSocketAddress()); - - newSession(session, config, connectFuture); - } - - private void newSession(SocketSessionImpl session, IoServiceConfig config, ConnectFuture connectFuture) - throws IOException - { - try - { - getFilterChainBuilder().buildFilterChain(session.getFilterChain()); - config.getFilterChainBuilder().buildFilterChain(session.getFilterChain()); - config.getThreadModel().buildFilterChain(session.getFilterChain()); - } - catch (Throwable e) - { - throw (IOException) new IOException("Failed to create a session.").initCause(e); - } - session.getIoProcessor().addNew(session); - connectFuture.setSession(session); - } - - private SocketIoProcessor nextProcessor() - { - return ioProcessors[processorDistributor++ % processorCount]; - } - - public void setOpenSocket(Socket openSocket) - { - _openSocket = openSocket; - } - - private class Worker implements Runnable - { - private long lastActive = System.currentTimeMillis(); - - public void run() - { - Thread.currentThread().setName(ExistingSocketConnector.this.threadName); - - for (; ;) - { - try - { - int nKeys = selector.select(1000); - - registerNew(); - - if (nKeys > 0) - { - processSessions(selector.selectedKeys()); - } - - processTimedOutSessions(selector.keys()); - - if (selector.keys().isEmpty()) - { - if (System.currentTimeMillis() - lastActive > workerTimeout * 1000L) - { - synchronized (lock) - { - if (selector.keys().isEmpty() && - connectQueue.isEmpty()) - { - worker = null; - try - { - selector.close(); - } - catch (IOException e) - { - ExceptionMonitor.getInstance().exceptionCaught(e); - } - finally - { - selector = null; - } - break; - } - } - } - } - else - { - lastActive = System.currentTimeMillis(); - } - } - catch (IOException e) - { - ExceptionMonitor.getInstance().exceptionCaught(e); - - try - { - Thread.sleep(1000); - } - catch (InterruptedException e1) - { - ExceptionMonitor.getInstance().exceptionCaught(e1); - } - } - } - } - } - - private class ConnectionRequest extends DefaultConnectFuture - { - private final SocketChannel channel; - private final long deadline; - private final IoHandler handler; - private final IoServiceConfig config; - - private ConnectionRequest(SocketChannel channel, IoHandler handler, IoServiceConfig config) - { - this.channel = channel; - long timeout; - if (config instanceof IoConnectorConfig) - { - timeout = ((IoConnectorConfig) config).getConnectTimeoutMillis(); - } - else - { - timeout = ((IoConnectorConfig) getDefaultConfig()).getConnectTimeoutMillis(); - } - this.deadline = System.currentTimeMillis() + timeout; - this.handler = handler; - this.config = config; - } - } -} diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQAnyDestination.java b/java/client/src/main/java/org/apache/qpid/client/AMQAnyDestination.java index a201f7d61e..999b22299c 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQAnyDestination.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQAnyDestination.java @@ -72,6 +72,17 @@ public class AMQAnyDestination extends AMQDestination implements Queue, Topic public String getTopicName() throws JMSException { - return super.getRoutingKey().toString(); + if (getRoutingKey() != null) + { + return getRoutingKey().asString(); + } + else if (getSubject() != null) + { + return getSubject(); + } + else + { + return null; + } } } 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 ee52cd50af..c0d4d8a893 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,7 +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.transport.ConnectionSettings; import org.apache.qpid.url.URLHelper; import org.apache.qpid.url.URLSyntaxException; @@ -38,8 +38,6 @@ public class AMQBrokerDetails implements BrokerDetails private Map<String, String> _options = new HashMap<String, String>(); - private SSLConfiguration _sslConfiguration; - public AMQBrokerDetails(){} public AMQBrokerDetails(String url) throws URLSyntaxException @@ -56,9 +54,7 @@ public class AMQBrokerDetails implements BrokerDetails if (transport != null) { //todo this list of valid transports should be enumerated somewhere - if ((!(transport.equalsIgnoreCase(BrokerDetails.VM) || - transport.equalsIgnoreCase(BrokerDetails.TCP) || - transport.equalsIgnoreCase(BrokerDetails.SOCKET)))) + if (!(transport.equalsIgnoreCase(BrokerDetails.TCP))) { if (transport.equalsIgnoreCase("localhost")) { @@ -105,6 +101,21 @@ public class AMQBrokerDetails implements BrokerDetails if (host == null) { host = ""; + + String auth = connection.getAuthority(); + if (auth != null) + { + // contains both host & port myhost:5672 + if (auth.contains(":")) + { + host = auth.substring(0,auth.indexOf(":")); + } + else + { + host = auth; + } + } + } setHost(host); @@ -167,10 +178,7 @@ public class AMQBrokerDetails implements BrokerDetails } else { - if (!_transport.equalsIgnoreCase(SOCKET)) - { - setPort(port); - } + setPort(port); } String queryString = connection.getQuery(); @@ -190,11 +198,10 @@ public class AMQBrokerDetails implements BrokerDetails } } - public AMQBrokerDetails(String host, int port, SSLConfiguration sslConfiguration) + public AMQBrokerDetails(String host, int port) { _host = host; _port = port; - _sslConfiguration = sslConfiguration; } public String getHost() @@ -270,33 +277,15 @@ public class AMQBrokerDetails implements BrokerDetails setProperty(OPTIONS_CONNECT_TIMEOUT, Long.toString(timeout)); } - public SSLConfiguration getSSLConfiguration() - { - return _sslConfiguration; - } - - public void setSSLConfiguration(SSLConfiguration sslConfig) - { - _sslConfiguration = sslConfig; - } - public String toString() { StringBuffer sb = new StringBuffer(); sb.append(_transport); sb.append("://"); - - if (!(_transport.equalsIgnoreCase(VM))) - { - sb.append(_host); - } - - if (!(_transport.equalsIgnoreCase(SOCKET))) - { - sb.append(':'); - sb.append(_port); - } + sb.append(_host); + sb.append(':'); + sb.append(_port); sb.append(printOptionsURL()); @@ -314,9 +303,8 @@ public class AMQBrokerDetails implements BrokerDetails return _host.equalsIgnoreCase(bd.getHost()) && (_port == bd.getPort()) && - _transport.equalsIgnoreCase(bd.getTransport()) && - compareSSLConfigurations(bd.getSSLConfiguration()); - //todo do we need to compare all the options as well? + _transport.equalsIgnoreCase(bd.getTransport()); + //TODO do we need to compare all the options as well? } @Override @@ -357,24 +345,6 @@ public class AMQBrokerDetails implements BrokerDetails return optionsURL.toString(); } - // Do we need to do a more in-depth comparison? - private boolean compareSSLConfigurations(SSLConfiguration other) - { - boolean retval = false; - if (_sslConfiguration == null && - other == null) - { - retval = true; - } - else if (_sslConfiguration != null && - other != null) - { - retval = true; - } - - return retval; - } - public static String checkTransport(String broker) { if ((!broker.contains("://"))) @@ -396,4 +366,82 @@ public class AMQBrokerDetails implements BrokerDetails { _options = props; } + + public ConnectionSettings buildConnectionSettings() + { + ConnectionSettings conSettings = new ConnectionSettings(); + + conSettings.setHost(getHost()); + conSettings.setPort(getPort()); + + // ------------ sasl options --------------- + if (getProperty(BrokerDetails.OPTIONS_SASL_MECHS) != null) + { + conSettings.setSaslMechs( + getProperty(BrokerDetails.OPTIONS_SASL_MECHS)); + } + + // Sun SASL Kerberos client uses the + // protocol + servername as the service key. + + if (getProperty(BrokerDetails.OPTIONS_SASL_PROTOCOL_NAME) != null) + { + conSettings.setSaslProtocol( + getProperty(BrokerDetails.OPTIONS_SASL_PROTOCOL_NAME)); + } + + + if (getProperty(BrokerDetails.OPTIONS_SASL_SERVER_NAME) != null) + { + conSettings.setSaslServerName( + getProperty(BrokerDetails.OPTIONS_SASL_SERVER_NAME)); + } + + conSettings.setUseSASLEncryption( + getBooleanProperty(BrokerDetails.OPTIONS_SASL_ENCRYPTION)); + + // ------------- ssl options --------------------- + conSettings.setUseSSL(getBooleanProperty(BrokerDetails.OPTIONS_SSL)); + + if (getProperty(BrokerDetails.OPTIONS_TRUST_STORE) != null) + { + conSettings.setTrustStorePath( + getProperty(BrokerDetails.OPTIONS_TRUST_STORE)); + } + + if (getProperty(BrokerDetails.OPTIONS_TRUST_STORE_PASSWORD) != null) + { + conSettings.setTrustStorePassword( + getProperty(BrokerDetails.OPTIONS_TRUST_STORE_PASSWORD)); + } + + if (getProperty(BrokerDetails.OPTIONS_KEY_STORE) != null) + { + conSettings.setKeyStorePath( + getProperty(BrokerDetails.OPTIONS_KEY_STORE)); + } + + if (getProperty(BrokerDetails.OPTIONS_KEY_STORE_PASSWORD) != null) + { + conSettings.setKeyStorePassword( + getProperty(BrokerDetails.OPTIONS_KEY_STORE_PASSWORD)); + } + + if (getProperty(BrokerDetails.OPTIONS_SSL_CERT_ALIAS) != null) + { + conSettings.setCertAlias( + getProperty(BrokerDetails.OPTIONS_SSL_CERT_ALIAS)); + } + // ---------------------------- + + conSettings.setVerifyHostname(getBooleanProperty(BrokerDetails.OPTIONS_SSL_VERIFY_HOSTNAME)); + + if (getProperty(BrokerDetails.OPTIONS_TCP_NO_DELAY) != null) + { + conSettings.setTcpNodelay( + getBooleanProperty(BrokerDetails.OPTIONS_TCP_NO_DELAY)); + } + + return conSettings; + } } 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 ab59fee020..941534c7ff 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 @@ -111,7 +111,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect /** Maps from session id (Integer) to AMQSession instance */ private final ChannelToSessionMap _sessions = new ChannelToSessionMap(); - private String _clientName; + private final String _clientName; /** The user name to use for authentication */ private String _username; @@ -126,7 +126,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect private ConnectionListener _connectionListener; - private ConnectionURL _connectionURL; + private final ConnectionURL _connectionURL; /** * Whether this connection is started, i.e. whether messages are flowing to consumers. It has no meaning for message @@ -147,9 +147,6 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect */ private QpidConnectionMetaData _connectionMetaData; - /** Configuration info for SSL */ - private SSLConfiguration _sslConfiguration; - private AMQShortString _defaultTopicExchangeName = ExchangeDefaults.TOPIC_EXCHANGE_NAME; private AMQShortString _defaultQueueExchangeName = ExchangeDefaults.DIRECT_EXCHANGE_NAME; private AMQShortString _temporaryTopicExchangeName = ExchangeDefaults.TOPIC_EXCHANGE_NAME; @@ -173,11 +170,15 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect //Indicates the sync publish options (persistent|all) //By default it's async publish private String _syncPublish = ""; - - // Indicates whether to use the old map message format or the + + // Indicates whether to use the old map message format or the // new amqp-0-10 encoded format. private boolean _useLegacyMapMessageFormat; + //used to track the last failover time for + //Address resolution purposes + private volatile long _lastFailoverTime = 0; + /** * @param broker brokerdetails * @param username username @@ -194,69 +195,33 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect this(new AMQConnectionURL( ConnectionURL.AMQ_PROTOCOL + "://" + username + ":" + password + "@" + ((clientName == null) ? "" : clientName) + "/" + virtualHost + "?brokerlist='" - + AMQBrokerDetails.checkTransport(broker) + "'"), null); - } - - /** - * @param broker brokerdetails - * @param username username - * @param password password - * @param clientName clientid - * @param virtualHost virtualhost - * - * @throws AMQException - * @throws URLSyntaxException - */ - public AMQConnection(String broker, String username, String password, String clientName, String virtualHost, - SSLConfiguration sslConfig) throws AMQException, URLSyntaxException - { - this(new AMQConnectionURL( - ConnectionURL.AMQ_PROTOCOL + "://" + username + ":" + password + "@" - + ((clientName == null) ? "" : clientName) + "/" + virtualHost + "?brokerlist='" - + AMQBrokerDetails.checkTransport(broker) + "'"), sslConfig); + + AMQBrokerDetails.checkTransport(broker) + "'")); } public AMQConnection(String host, int port, String username, String password, String clientName, String virtualHost) throws AMQException, URLSyntaxException { - this(host, port, false, username, password, clientName, virtualHost, null); - } - - public AMQConnection(String host, int port, String username, String password, String clientName, String virtualHost, - SSLConfiguration sslConfig) throws AMQException, URLSyntaxException - { - this(host, port, false, username, password, clientName, virtualHost, sslConfig); - } - - public AMQConnection(String host, int port, boolean useSSL, String username, String password, String clientName, - String virtualHost, SSLConfiguration sslConfig) throws AMQException, URLSyntaxException - { this(new AMQConnectionURL( - useSSL - ? (ConnectionURL.AMQ_PROTOCOL + "://" + username + ":" + password + "@" - + ((clientName == null) ? "" : clientName) + virtualHost + "?brokerlist='tcp://" + host + ":" + port - + "'" + "," + BrokerDetails.OPTIONS_SSL + "='true'") - : (ConnectionURL.AMQ_PROTOCOL + "://" + username + ":" + password + "@" - + ((clientName == null) ? "" : clientName) + virtualHost + "?brokerlist='tcp://" + host + ":" + port - + "'" + "," + BrokerDetails.OPTIONS_SSL + "='false'")), sslConfig); + ConnectionURL.AMQ_PROTOCOL + "://" + username + ":" + password + "@" + + ((clientName == null) ? "" : clientName) + virtualHost + "?brokerlist='tcp://" + host + ":" + port + "'")); } public AMQConnection(String connection) throws AMQException, URLSyntaxException { - this(new AMQConnectionURL(connection), null); - } - - public AMQConnection(String connection, SSLConfiguration sslConfig) throws AMQException, URLSyntaxException - { - this(new AMQConnectionURL(connection), sslConfig); + this(new AMQConnectionURL(connection)); } /** * @todo Some horrible stuff going on here with setting exceptions to be non-null to detect if an exception * was thrown during the connection! Intention not clear. Use a flag anyway, not exceptions... Will fix soon. */ - public AMQConnection(ConnectionURL connectionURL, SSLConfiguration sslConfig) throws AMQException + public AMQConnection(ConnectionURL connectionURL) throws AMQException { + if (connectionURL == null) + { + throw new IllegalArgumentException("Connection must be specified"); + } + // set this connection maxPrefetch if (connectionURL.getOption(ConnectionURL.OPTIONS_MAXPREFETCH) != null) { @@ -264,7 +229,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect } else { - // use the defaul value set for all connections + // use the default value set for all connections _maxPrefetch = Integer.parseInt(System.getProperties().getProperty(ClientProperties.MAX_PREFETCH_PROP_NAME, ClientProperties.MAX_PREFETCH_DEFAULT)); } @@ -278,7 +243,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect } else { - // use the defaul value set for all connections + // use the default value set for all connections _syncPersistence = Boolean.getBoolean(ClientProperties.SYNC_PERSISTENT_PROP_NAME); if (_syncPersistence) { @@ -293,7 +258,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect } else { - // use the defaul value set for all connections + // use the default value set for all connections _syncAck = Boolean.getBoolean(ClientProperties.SYNC_ACK_PROP_NAME); } @@ -306,7 +271,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect // use the default value set for all connections _syncPublish = System.getProperty((ClientProperties.SYNC_PUBLISH_PROP_NAME),_syncPublish); } - + if (connectionURL.getOption(ConnectionURL.OPTIONS_USE_LEGACY_MAP_MESSAGE_FORMAT) != null) { _useLegacyMapMessageFormat = Boolean.parseBoolean( @@ -317,16 +282,16 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect // use the default value set for all connections _useLegacyMapMessageFormat = Boolean.getBoolean(ClientProperties.USE_LEGACY_MAP_MESSAGE_FORMAT); } - + String amqpVersion = System.getProperty((ClientProperties.AMQP_VERSION), "0-10"); _logger.debug("AMQP version " + amqpVersion); - + _failoverPolicy = new FailoverPolicy(connectionURL, this); BrokerDetails brokerDetails = _failoverPolicy.getCurrentBrokerDetails(); - if (brokerDetails.getTransport().equals(BrokerDetails.VM) || "0-8".equals(amqpVersion)) + if ("0-8".equals(amqpVersion)) { _delegate = new AMQConnectionDelegate_8_0(this); - } + } else if ("0-9".equals(amqpVersion)) { _delegate = new AMQConnectionDelegate_0_9(this); @@ -345,12 +310,6 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect _logger.info("Connection:" + connectionURL); } - _sslConfiguration = sslConfig; - if (connectionURL == null) - { - throw new IllegalArgumentException("Connection must be specified"); - } - _connectionURL = connectionURL; _clientName = connectionURL.getClientName(); @@ -418,6 +377,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect brokerDetails = _failoverPolicy.getNextBrokerDetails(); } } + verifyClientID(); if (_logger.isDebugEnabled()) { @@ -504,7 +464,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); - //Update our session to use this new protocol version + //Update our session to use this new protocol version _protocolHandler.getProtocolSession().setProtocolVersion(_delegate.getProtocolVersion()); } @@ -535,14 +495,6 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect } } - protected AMQConnection(String username, String password, String clientName, String virtualHost) - { - _clientName = clientName; - _username = username; - _password = password; - setVirtualHost(virtualHost); - } - private void setVirtualHost(String virtualHost) { if (virtualHost != null && virtualHost.startsWith("/")) @@ -555,7 +507,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect public boolean attemptReconnection(String host, int port) { - BrokerDetails bd = new AMQBrokerDetails(host, port, _sslConfiguration); + BrokerDetails bd = new AMQBrokerDetails(host, port); _failoverPolicy.setBroker(bd); @@ -696,20 +648,6 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect } } - private void reopenChannel(int channelId, int prefetchHigh, int prefetchLow, boolean transacted) - throws AMQException, FailoverException - { - try - { - createChannelOverWire(channelId, prefetchHigh, prefetchLow, transacted); - } - catch (AMQException e) - { - deregisterSession(channelId); - throw new AMQException(null, "Error reopening channel " + channelId + " after failover: " + e, e); - } - } - public void setFailoverPolicy(FailoverPolicy policy) { _failoverPolicy = policy; @@ -1096,7 +1034,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect { _username = id; } - + public String getPassword() { return _password; @@ -1142,6 +1080,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect */ public boolean firePreFailover(boolean redirect) { + _lastFailoverTime = System.currentTimeMillis(); boolean proceed = true; if (_connectionListener != null) { @@ -1249,7 +1188,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect if (code != null) { - je = new JMSException(Integer.toString(code.getCode()), "Exception thrown against " + toString() + ": " + cause); + je = new JMSException("Exception thrown against " + toString() + ": " + cause, Integer.toString(code.getCode())); } else { @@ -1272,7 +1211,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect { je.setLinkedException((Exception) cause); } - + je.initCause(cause); } @@ -1305,7 +1244,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect { _logger.info("Not a hard-error connection not closing: " + cause); } - + // deliver the exception if there is a listener if (_exceptionListener != null) { @@ -1315,7 +1254,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect { _logger.error("Throwable Received but no listener set: " + cause); } - + // if we are closing the connection, close sessions first if (closer) { @@ -1372,6 +1311,20 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect return buf.toString(); } + /** + * Returns connection url. + * @return connection url + */ + public ConnectionURL getConnectionURL() + { + return _connectionURL; + } + + /** + * Returns stringified connection url. This url is suitable only for display + * as {@link AMQConnectionURL#toString()} converts any password to asterisks. + * @return connection url + */ public String toURL() { return _connectionURL.toString(); @@ -1383,11 +1336,6 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect AMQConnectionFactory.class.getName(), null); // factory location } - public SSLConfiguration getSSLConfiguration() - { - return _sslConfiguration; - } - public AMQShortString getDefaultTopicExchangeName() { return _defaultTopicExchangeName; @@ -1442,7 +1390,18 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect { return _delegate.getProtocolVersion(); } - + + public String getBrokerUUID() + { + if(getProtocolVersion().equals(ProtocolVersion.v0_10)) + { + return ((AMQConnectionDelegate_0_10)_delegate).getUUID(); + } + else + { + return null; + } + } public boolean isFailingOver() { return (_protocolHandler.getFailoverLatch() != null); @@ -1485,9 +1444,32 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect { return _sessions.getNextChannelId(); } - + public boolean isUseLegacyMapMessageFormat() { return _useLegacyMapMessageFormat; } + + private void verifyClientID() throws AMQException + { + if (Boolean.getBoolean(ClientProperties.QPID_VERIFY_CLIENT_ID)) + { + try + { + if (!_delegate.verifyClientID()) + { + throw new AMQException(AMQConstant.ALREADY_EXISTS,"ClientID must be unique"); + } + } + catch(JMSException e) + { + throw new AMQException(e.getMessage(),e); + } + } + } + + public long getLastFailoverTime() + { + return _lastFailoverTime; + } } 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 9560bd5c7c..8768f93c8c 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 @@ -57,10 +57,12 @@ public interface AMQConnectionDelegate void closeConnection(long timeout) throws JMSException, AMQException; <T, E extends Exception> T executeRetrySupport(FailoverProtectedOperation<T,E> operation) throws E; - + int getMaxChannelID(); int getMinChannelID(); ProtocolVersion getProtocolVersion(); + + boolean verifyClientID() throws JMSException, AMQException; } 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 b0bd8f8e97..0d48dd5822 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,6 +1,6 @@ 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 @@ -8,16 +8,16 @@ package org.apache.qpid.client; * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. - * + * */ @@ -35,6 +35,7 @@ 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.client.transport.ClientConnectionDelegate; import org.apache.qpid.configuration.ClientProperties; import org.apache.qpid.framing.ProtocolVersion; import org.apache.qpid.jms.BrokerDetails; @@ -43,10 +44,13 @@ import org.apache.qpid.jms.Session; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.transport.Connection; import org.apache.qpid.transport.ConnectionClose; +import org.apache.qpid.transport.ConnectionCloseCode; import org.apache.qpid.transport.ConnectionException; import org.apache.qpid.transport.ConnectionListener; import org.apache.qpid.transport.ConnectionSettings; import org.apache.qpid.transport.ProtocolVersionException; +import org.apache.qpid.transport.SessionDetachCode; +import org.apache.qpid.transport.SessionException; import org.apache.qpid.transport.TransportException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -59,6 +63,10 @@ public class AMQConnectionDelegate_0_10 implements AMQConnectionDelegate, Connec private static final Logger _logger = LoggerFactory.getLogger(AMQConnectionDelegate_0_10.class); /** + * The name of the UUID property + */ + private static final String UUID_NAME = "qpid.federation_tag"; + /** * The AMQ Connection. */ private AMQConnection _conn; @@ -69,6 +77,12 @@ public class AMQConnectionDelegate_0_10 implements AMQConnectionDelegate, Connec org.apache.qpid.transport.Connection _qpidConnection; private ConnectionException exception = null; + static + { + // Register any configured SASL client factories. + org.apache.qpid.client.security.DynamicSaslRegistrar.registerSaslProviders(); + } + //--- constructor public AMQConnectionDelegate_0_10(AMQConnection conn) { @@ -80,7 +94,14 @@ public class AMQConnectionDelegate_0_10 implements AMQConnectionDelegate, Connec /** * create a Session and start it if required. */ + public Session createSession(boolean transacted, int acknowledgeMode, int prefetchHigh, int prefetchLow) + throws JMSException + { + return createSession(transacted,acknowledgeMode,prefetchHigh,prefetchLow,null); + } + + public Session createSession(boolean transacted, int acknowledgeMode, int prefetchHigh, int prefetchLow, String name) throws JMSException { _conn.checkNotClosed(); @@ -95,7 +116,7 @@ public class AMQConnectionDelegate_0_10 implements AMQConnectionDelegate, Connec try { session = new AMQSession_0_10(_qpidConnection, _conn, channelId, transacted, acknowledgeMode, prefetchHigh, - prefetchLow); + prefetchLow,name); _conn.registerSession(channelId, session); if (_conn._started) { @@ -173,8 +194,8 @@ public class AMQConnectionDelegate_0_10 implements AMQConnectionDelegate, Connec + _conn.getPassword()); } - ConnectionSettings conSettings = new ConnectionSettings(); - retriveConnectionSettings(conSettings,brokerDetail); + ConnectionSettings conSettings = retriveConnectionSettings(brokerDetail); + _qpidConnection.setConnectionDelegate(new ClientConnectionDelegate(conSettings, _conn.getConnectionURL())); _qpidConnection.connect(conSettings); _conn._connected = true; @@ -211,6 +232,8 @@ public class AMQConnectionDelegate_0_10 implements AMQConnectionDelegate, Connec public void resubscribeSessions() throws JMSException, AMQException, FailoverException { + _logger.info("Resuming connection"); + getQpidConnection().resume(); List<AMQSession> sessions = new ArrayList<AMQSession>(_conn.getSessions().values()); _logger.info(String.format("Resubscribing sessions = %s sessions.size=%d", sessions, sessions.size())); for (AMQSession s : sessions) @@ -254,28 +277,33 @@ public class AMQConnectionDelegate_0_10 implements AMQConnectionDelegate, Connec } ConnectionClose close = exc.getClose(); - if (close == null) + if (close == null || close.getReplyCode() == ConnectionCloseCode.CONNECTION_FORCED) { _conn.getProtocolHandler().setFailoverLatch(new CountDownLatch(1)); - - try + + _qpidConnection.notifyFailoverRequired(); + + synchronized (_conn.getFailoverMutex()) { - if (_conn.firePreFailover(false) && _conn.attemptReconnection()) + try { - _conn.failoverPrep(); - _conn.resubscribeSessions(); - _conn.fireFailoverComplete(); - return; + if (_conn.firePreFailover(false) && _conn.attemptReconnection()) + { + _conn.failoverPrep(); + _conn.resubscribeSessions(); + _conn.fireFailoverComplete(); + return; + } + } + catch (Exception e) + { + _logger.error("error during failover", e); + } + finally + { + _conn.getProtocolHandler().getFailoverLatch().countDown(); + _conn.getProtocolHandler().setFailoverLatch(null); } - } - catch (Exception e) - { - _logger.error("error during failover", e); - } - finally - { - _conn.getProtocolHandler().getFailoverLatch().countDown(); - _conn.getProtocolHandler().setFailoverLatch(null); } } @@ -301,6 +329,18 @@ public class AMQConnectionDelegate_0_10 implements AMQConnectionDelegate, Connec public <T, E extends Exception> T executeRetrySupport(FailoverProtectedOperation<T,E> operation) throws E { + if (_conn.isFailingOver()) + { + try + { + _conn.blockUntilNotFailingOver(); + } + catch (InterruptedException e) + { + //ignore + } + } + try { return operation.execute(); @@ -326,78 +366,20 @@ public class AMQConnectionDelegate_0_10 implements AMQConnectionDelegate, Connec { return ProtocolVersion.v0_10; } - - private void retriveConnectionSettings(ConnectionSettings conSettings, BrokerDetails brokerDetail) + + public String getUUID() + { + return (String)_qpidConnection.getServerProperties().get(UUID_NAME); + } + + private ConnectionSettings retriveConnectionSettings(BrokerDetails brokerDetail) { + ConnectionSettings conSettings = brokerDetail.buildConnectionSettings(); - conSettings.setHost(brokerDetail.getHost()); - conSettings.setPort(brokerDetail.getPort()); conSettings.setVhost(_conn.getVirtualHost()); conSettings.setUsername(_conn.getUsername()); conSettings.setPassword(_conn.getPassword()); - - // ------------ sasl options --------------- - if (brokerDetail.getProperty(BrokerDetails.OPTIONS_SASL_MECHS) != null) - { - conSettings.setSaslMechs( - brokerDetail.getProperty(BrokerDetails.OPTIONS_SASL_MECHS)); - } - // Sun SASL Kerberos client uses the - // protocol + servername as the service key. - - if (brokerDetail.getProperty(BrokerDetails.OPTIONS_SASL_PROTOCOL_NAME) != null) - { - conSettings.setSaslProtocol( - brokerDetail.getProperty(BrokerDetails.OPTIONS_SASL_PROTOCOL_NAME)); - } - - - if (brokerDetail.getProperty(BrokerDetails.OPTIONS_SASL_SERVER_NAME) != null) - { - conSettings.setSaslServerName( - brokerDetail.getProperty(BrokerDetails.OPTIONS_SASL_SERVER_NAME)); - } - - conSettings.setUseSASLEncryption( - brokerDetail.getBooleanProperty(BrokerDetails.OPTIONS_SASL_ENCRYPTION)); - - // ------------- ssl options --------------------- - conSettings.setUseSSL(brokerDetail.getBooleanProperty(BrokerDetails.OPTIONS_SSL)); - - if (brokerDetail.getProperty(BrokerDetails.OPTIONS_TRUST_STORE) != null) - { - conSettings.setTrustStorePath( - brokerDetail.getProperty(BrokerDetails.OPTIONS_TRUST_STORE)); - } - - if (brokerDetail.getProperty(BrokerDetails.OPTIONS_TRUST_STORE_PASSWORD) != null) - { - conSettings.setTrustStorePassword( - brokerDetail.getProperty(BrokerDetails.OPTIONS_TRUST_STORE_PASSWORD)); - } - - if (brokerDetail.getProperty(BrokerDetails.OPTIONS_KEY_STORE) != null) - { - conSettings.setKeyStorePath( - brokerDetail.getProperty(BrokerDetails.OPTIONS_KEY_STORE)); - } - - if (brokerDetail.getProperty(BrokerDetails.OPTIONS_KEY_STORE_PASSWORD) != null) - { - conSettings.setKeyStorePassword( - brokerDetail.getProperty(BrokerDetails.OPTIONS_KEY_STORE_PASSWORD)); - } - - if (brokerDetail.getProperty(BrokerDetails.OPTIONS_SSL_CERT_ALIAS) != null) - { - conSettings.setCertAlias( - brokerDetail.getProperty(BrokerDetails.OPTIONS_SSL_CERT_ALIAS)); - } - // ---------------------------- - - conSettings.setVerifyHostname(brokerDetail.getBooleanProperty(BrokerDetails.OPTIONS_SSL_VERIFY_HOSTNAME)); - // Pass client name from connection URL Map<String, Object> clientProps = new HashMap<String, Object>(); try @@ -409,16 +391,12 @@ public class AMQConnectionDelegate_0_10 implements AMQConnectionDelegate, Connec { // Ignore } - - if (brokerDetail.getProperty(BrokerDetails.OPTIONS_TCP_NO_DELAY) != null) - { - conSettings.setTcpNodelay( - brokerDetail.getBooleanProperty(BrokerDetails.OPTIONS_TCP_NO_DELAY)); - } - + conSettings.setHeartbeatInterval(getHeartbeatInterval(brokerDetail)); + + return conSettings; } - + // The idle_timeout prop is in milisecs while // the new heartbeat prop is in secs private int getHeartbeatInterval(BrokerDetails brokerDetail) @@ -433,7 +411,7 @@ public class AMQConnectionDelegate_0_10 implements AMQConnectionDelegate, Connec { heartbeat = Integer.parseInt(brokerDetail.getProperty(BrokerDetails.OPTIONS_HEARTBEAT)); } - else if (Integer.getInteger(ClientProperties.IDLE_TIMEOUT_PROP_NAME) != null) + else if (Integer.getInteger(ClientProperties.IDLE_TIMEOUT_PROP_NAME) != null) { heartbeat = Integer.getInteger(ClientProperties.IDLE_TIMEOUT_PROP_NAME)/1000; _logger.warn("JVM arg -Didle_timeout=<mili_secs> is deprecated, please use -Dqpid.heartbeat=<secs>"); @@ -441,12 +419,37 @@ public class AMQConnectionDelegate_0_10 implements AMQConnectionDelegate, Connec else { heartbeat = Integer.getInteger(ClientProperties.HEARTBEAT,ClientProperties.HEARTBEAT_DEFAULT); - } + } return heartbeat; } - + protected org.apache.qpid.transport.Connection getQpidConnection() { return _qpidConnection; } + + public boolean verifyClientID() throws JMSException, AMQException + { + int prefetch = (int)_conn.getMaxPrefetch(); + AMQSession_0_10 ssn = (AMQSession_0_10)createSession(false, 1,prefetch,prefetch,_conn.getClientID()); + org.apache.qpid.transport.Session ssn_0_10 = ssn.getQpidSession(); + try + { + ssn_0_10.awaitOpen(); + } + catch(SessionException se) + { + //if due to non unique client id for user return false, otherwise wrap and re-throw. + if (ssn_0_10.getDetachCode() != null && + ssn_0_10.getDetachCode() == SessionDetachCode.SESSION_BUSY) + { + return false; + } + else + { + throw new AMQException(AMQConstant.INTERNAL_ERROR, "Unexpected SessionException thrown while awaiting session opening", se); + } + } + return true; + } } 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 40b332d216..b1a22155d6 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 @@ -23,6 +23,8 @@ package org.apache.qpid.client; import java.io.IOException; import java.net.ConnectException; import java.nio.channels.UnresolvedAddressException; +import java.security.GeneralSecurityException; +import java.security.Security; import java.text.MessageFormat; import java.util.ArrayList; import java.util.EnumSet; @@ -31,15 +33,17 @@ import java.util.Set; import javax.jms.JMSException; import javax.jms.XASession; +import javax.net.ssl.SSLContext; import org.apache.qpid.AMQException; +import org.apache.qpid.AMQTimeoutException; import org.apache.qpid.client.failover.FailoverException; import org.apache.qpid.client.failover.FailoverProtectedOperation; import org.apache.qpid.client.failover.FailoverRetrySupport; import org.apache.qpid.client.protocol.AMQProtocolSession; import org.apache.qpid.client.state.AMQState; +import org.apache.qpid.client.state.AMQStateManager; import org.apache.qpid.client.state.StateWaiter; -import org.apache.qpid.client.transport.TransportConnection; import org.apache.qpid.framing.BasicQosBody; import org.apache.qpid.framing.BasicQosOkBody; import org.apache.qpid.framing.ChannelOpenBody; @@ -49,6 +53,13 @@ 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.ssl.SSLContextFactory; +import org.apache.qpid.transport.ConnectionSettings; +import org.apache.qpid.transport.network.NetworkConnection; +import org.apache.qpid.transport.network.OutgoingNetworkTransport; +import org.apache.qpid.transport.network.Transport; +import org.apache.qpid.transport.network.security.SecurityLayer; +import org.apache.qpid.transport.network.security.SecurityLayerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -60,8 +71,30 @@ public class AMQConnectionDelegate_8_0 implements AMQConnectionDelegate public void closeConnection(long timeout) throws JMSException, AMQException { - _conn.getProtocolHandler().closeConnection(timeout); + final AMQStateManager stateManager = _conn.getProtocolHandler().getStateManager(); + final AMQState currentState = stateManager.getCurrentState(); + if (currentState.equals(AMQState.CONNECTION_CLOSED)) + { + _logger.debug("Connection already closed."); + } + else if (currentState.equals(AMQState.CONNECTION_CLOSING)) + { + _logger.debug("Connection already closing, awaiting closed state."); + final StateWaiter closeWaiter = new StateWaiter(stateManager, currentState, EnumSet.of(AMQState.CONNECTION_CLOSED)); + try + { + closeWaiter.await(timeout); + } + catch (AMQTimeoutException te) + { + throw new AMQTimeoutException("Close did not complete in timely fashion", te); + } + } + else + { + _conn.getProtocolHandler().closeConnection(timeout); + } } public AMQConnectionDelegate_8_0(AMQConnection conn) @@ -89,15 +122,34 @@ 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")) - { - TransportConnection.getInstance(brokerDetail).connect(_conn._protocolHandler, brokerDetail); - } - else + ConnectionSettings settings = brokerDetail.buildConnectionSettings(); + settings.setProtocol(brokerDetail.getTransport()); + + SSLContext sslContext = null; + if (settings.isUseSSL()) { - _conn.getProtocolHandler().createIoTransportSession(brokerDetail); + try + { + sslContext = SSLContextFactory.buildClientContext( + settings.getTrustStorePath(), + settings.getTrustStorePassword(), + settings.getTrustStoreCertType(), + settings.getKeyStorePath(), + settings.getKeyStorePassword(), + settings.getKeyStoreCertType(), + settings.getCertAlias()); + } + catch (GeneralSecurityException e) + { + throw new AMQException("Unable to create SSLContext: " + e.getMessage(), e); + } } + + SecurityLayer securityLayer = SecurityLayerFactory.newInstance(settings); + + OutgoingNetworkTransport transport = Transport.getOutgoingTransportInstance(getProtocolVersion()); + NetworkConnection network = transport.connect(settings, securityLayer.receiver(_conn._protocolHandler), sslContext); + _conn._protocolHandler.setNetworkConnection(network, securityLayer.sender(network.getSender())); _conn._protocolHandler.getProtocolSession().init(); // this blocks until the connection has been set up or when an error // has prevented the connection being set up @@ -322,4 +374,9 @@ public class AMQConnectionDelegate_8_0 implements AMQConnectionDelegate { return ProtocolVersion.v8_0; } + + public boolean verifyClientID() throws JMSException + { + return true; + } } 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 ec4c668d7e..f0c003e02a 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 @@ -44,210 +44,34 @@ public class AMQConnectionFactory implements ConnectionFactory, QueueConnectionF ObjectFactory, Referenceable, XATopicConnectionFactory, XAQueueConnectionFactory, XAConnectionFactory { - private String _host; - private int _port; - private String _defaultUsername; - private String _defaultPassword; - private String _virtualPath; + private final ConnectionURL _connectionDetails; - private ConnectionURL _connectionDetails; - private SSLConfiguration _sslConfig; - - public AMQConnectionFactory() - { - } - - /** - * This is the Only constructor used! - * It is used form the context and from the JNDI objects. - */ - public AMQConnectionFactory(String url) throws URLSyntaxException - { - _connectionDetails = new AMQConnectionURL(url); - } - - /** - * This constructor is never used! - */ - public AMQConnectionFactory(ConnectionURL url) + public AMQConnectionFactory(final String url) throws URLSyntaxException { - _connectionDetails = url; - } - - /** - * This constructor is never used! - */ - public AMQConnectionFactory(String broker, String username, String password, String clientName, String virtualHost) - throws URLSyntaxException - { - this(new AMQConnectionURL( - ConnectionURL.AMQ_PROTOCOL + "://" + username + ":" + password + "@" + clientName + "/" + virtualHost + "?brokerlist='" + broker + "'")); - } - - /** - * This constructor is never used! - */ - public AMQConnectionFactory(String host, int port, String virtualPath) - { - this(host, port, "guest", "guest", virtualPath); - } - - /** - * This constructor is never used! - */ - public AMQConnectionFactory(String host, int port, String defaultUsername, String defaultPassword, - String virtualPath) - { - _host = host; - _port = port; - _defaultUsername = defaultUsername; - _defaultPassword = defaultPassword; - _virtualPath = virtualPath; - -//todo when setting Host/Port has been resolved then we can use this otherwise those methods won't work with the following line. -// _connectionDetails = new AMQConnectionURL( -// ConnectionURL.AMQ_PROTOCOL + "://" + -// _defaultUsername + ":" + _defaultPassword + "@" + -// virtualPath + "?brokerlist='tcp://" + host + ":" + port + "'"); - } - - /** - * @return The _defaultPassword. - */ - public final String getDefaultPassword(String password) - { - if (_connectionDetails != null) + if (url == null) { - return _connectionDetails.getPassword(); + throw new IllegalArgumentException("url cannot be null"); } - else - { - return _defaultPassword; - } - } - - /** - * @param password The _defaultPassword to set. - */ - public final void setDefaultPassword(String password) - { - if (_connectionDetails != null) - { - _connectionDetails.setPassword(password); - } - _defaultPassword = password; - } - - /** - * Getter for SSLConfiguration - * - * @return SSLConfiguration if set, otherwise null - */ - public final SSLConfiguration getSSLConfiguration() - { - return _sslConfig; - } - /** - * Setter for SSLConfiguration - * - * @param sslConfig config to store - */ - public final void setSSLConfiguration(SSLConfiguration sslConfig) - { - _sslConfig = sslConfig; - } - - /** - * @return The _defaultPassword. - */ - public final String getDefaultUsername(String password) - { - if (_connectionDetails != null) - { - return _connectionDetails.getUsername(); - } - else - { - return _defaultUsername; - } + _connectionDetails = new AMQConnectionURL(url); } - /** - * @param username The _defaultUsername to set. - */ - public final void setDefaultUsername(String username) + public AMQConnectionFactory(ConnectionURL url) { - if (_connectionDetails != null) + if (url == null) { - _connectionDetails.setUsername(username); + throw new IllegalArgumentException("url cannot be null"); } - _defaultUsername = username; - } - - /** - * @return The _host . - */ - public final String getHost() - { - //todo this doesn't make sense in a multi broker URL as we have no current as that is done by AMQConnection - return _host; - } - /** - * @param host The _host to set. - */ - public final void setHost(String host) - { - //todo if _connectionDetails is set then run _connectionDetails.addBrokerDetails() - // Should perhaps have this method changed to setBroker(host,port) - _host = host; - } - - /** - * @return _port The _port to set. - */ - public final int getPort() - { - //todo see getHost - return _port; - } - - /** - * @param port The port to set. - */ - public final void setPort(int port) - { - //todo see setHost - _port = port; + _connectionDetails = url; } /** - * @return he _virtualPath. + * @return the virtualPath of the connection details. */ public final String getVirtualPath() { - if (_connectionDetails != null) - { - return _connectionDetails.getVirtualHost(); - } - else - { - return _virtualPath; - } - } - - /** - * @param path The _virtualPath to set. - */ - public final void setVirtualPath(String path) - { - if (_connectionDetails != null) - { - _connectionDetails.setVirtualHost(path); - } - - _virtualPath = path; + return _connectionDetails.getVirtualHost(); } public static String getUniqueClientID() @@ -267,19 +91,11 @@ public class AMQConnectionFactory implements ConnectionFactory, QueueConnectionF { try { - if (_connectionDetails != null) - { - if (_connectionDetails.getClientName() == null || _connectionDetails.getClientName().equals("")) - { - _connectionDetails.setClientName(getUniqueClientID()); - } - return new AMQConnection(_connectionDetails, _sslConfig); - } - else + if (_connectionDetails.getClientName() == null || _connectionDetails.getClientName().equals("")) { - return new AMQConnection(_host, _port, _defaultUsername, _defaultPassword, getUniqueClientID(), - _virtualPath); + _connectionDetails.setClientName(getUniqueClientID()); } + return new AMQConnection(_connectionDetails); } catch (Exception e) { @@ -288,8 +104,6 @@ public class AMQConnectionFactory implements ConnectionFactory, QueueConnectionF jmse.initCause(e); throw jmse; } - - } public Connection createConnection(String userName, String password) throws JMSException @@ -299,34 +113,35 @@ public class AMQConnectionFactory implements ConnectionFactory, QueueConnectionF public Connection createConnection(String userName, String password, String id) throws JMSException { - try + if (_connectionDetails != null) { - if (_connectionDetails != null) + try { - _connectionDetails.setUsername(userName); - _connectionDetails.setPassword(password); + ConnectionURL connectionDetails = new AMQConnectionURL(_connectionDetails.toString()); + connectionDetails.setUsername(userName); + connectionDetails.setPassword(password); if (id != null && !id.equals("")) { - _connectionDetails.setClientName(id); + connectionDetails.setClientName(id); } - else if (_connectionDetails.getClientName() == null || _connectionDetails.getClientName().equals("")) + else if (connectionDetails.getClientName() == null || connectionDetails.getClientName().equals("")) { - _connectionDetails.setClientName(getUniqueClientID()); + connectionDetails.setClientName(getUniqueClientID()); } - return new AMQConnection(_connectionDetails, _sslConfig); + return new AMQConnection(connectionDetails); } - else + catch (Exception e) { - return new AMQConnection(_host, _port, userName, password, (id != null ? id : getUniqueClientID()), _virtualPath); + JMSException jmse = new JMSException("Error creating connection: " + e.getMessage()); + jmse.setLinkedException(e); + jmse.initCause(e); + throw jmse; } } - catch (Exception e) + else { - JMSException jmse = new JMSException("Error creating connection: " + e.getMessage()); - jmse.setLinkedException(e); - jmse.initCause(e); - throw jmse; + throw new JMSException("The connection factory wasn't created with a proper URL, the connection details are empty"); } } @@ -361,12 +176,6 @@ public class AMQConnectionFactory implements ConnectionFactory, QueueConnectionF return _connectionDetails.toString(); } - - public final void setConnectionURLString(String url) throws URLSyntaxException - { - _connectionDetails = new AMQConnectionURL(url); - } - /** * JNDI interface to create objects from References. * @@ -457,7 +266,7 @@ public class AMQConnectionFactory implements ConnectionFactory, QueueConnectionF { try { - return new XAConnectionImpl(_connectionDetails, _sslConfig); + return new XAConnectionImpl(_connectionDetails); } catch (Exception e) { @@ -484,19 +293,30 @@ public class AMQConnectionFactory implements ConnectionFactory, QueueConnectionF { if (_connectionDetails != null) { - _connectionDetails.setUsername(username); - _connectionDetails.setPassword(password); - - if (_connectionDetails.getClientName() == null || _connectionDetails.getClientName().equals("")) + try { - _connectionDetails.setClientName(getUniqueClientID()); + ConnectionURL connectionDetails = new AMQConnectionURL(_connectionDetails.toString()); + connectionDetails.setUsername(username); + connectionDetails.setPassword(password); + + if (connectionDetails.getClientName() == null || connectionDetails.getClientName().equals("")) + { + connectionDetails.setClientName(getUniqueClientID()); + } + return new XAConnectionImpl(connectionDetails); + } + catch (Exception e) + { + JMSException jmse = new JMSException("Error creating XA Connection: " + e.getMessage()); + jmse.setLinkedException(e); + jmse.initCause(e); + throw jmse; } } else { - throw new JMSException("A URL must be specified to access XA connections"); - } - return createXAConnection(); + throw new JMSException("The connection factory wasn't created with a proper URL, the connection details are empty"); + } } 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 93b4c51a8f..f9f50d9150 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQConnectionURL.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQConnectionURL.java @@ -27,18 +27,14 @@ import java.util.Map; import org.apache.qpid.client.url.URLParser; import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.ProtocolVersion; import org.apache.qpid.jms.BrokerDetails; import org.apache.qpid.jms.ConnectionURL; import org.apache.qpid.url.URLHelper; import org.apache.qpid.url.URLSyntaxException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class AMQConnectionURL implements ConnectionURL { - private static final Logger _logger = LoggerFactory.getLogger(AMQConnectionURL.class); - + private String _url; private String _failoverMethod; private Map<String, String> _failoverOptions; @@ -295,17 +291,4 @@ public class AMQConnectionURL implements ConnectionURL return sb.toString(); } - - public static void main(String[] args) throws URLSyntaxException - { - String url2 = - "amqp://ritchiem:bob@temp/testHost?brokerlist='tcp://localhost:5672;tcp://fancyserver:3000/',failover='roundrobin'"; - // "amqp://user:pass@clientid/virtualhost?brokerlist='tcp://host:1?option1=\'value\',option2=\'value\';vm://:3?option1=\'value\'',failover='method?option1=\'value\',option2='value''"; - - ConnectionURL connectionurl2 = new AMQConnectionURL(url2); - - System.out.println(url2); - System.out.println(connectionurl2); - - } } 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 eb9682a3cf..f9a38138ba 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 @@ -21,9 +21,8 @@ package org.apache.qpid.client; import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; import javax.jms.Destination; import javax.naming.NamingException; @@ -34,8 +33,6 @@ import javax.naming.StringRefAddr; import org.apache.qpid.client.messaging.address.AddressHelper; import org.apache.qpid.client.messaging.address.Link; import org.apache.qpid.client.messaging.address.Node; -import org.apache.qpid.client.messaging.address.QpidExchangeOptions; -import org.apache.qpid.client.messaging.address.QpidQueueOptions; import org.apache.qpid.configuration.ClientProperties; import org.apache.qpid.exchange.ExchangeDefaults; import org.apache.qpid.framing.AMQShortString; @@ -63,7 +60,7 @@ public abstract class AMQDestination implements Destination, Referenceable private boolean _browseOnly; - private boolean _isAddressResolved; + private AtomicLong _addressResolved = new AtomicLong(0); private AMQShortString _queueName; @@ -78,15 +75,10 @@ public abstract class AMQDestination implements Destination, Referenceable private boolean _exchangeExistsChecked; - private byte[] _byteEncoding; - private static final int IS_DURABLE_MASK = 0x1; - private static final int IS_EXCLUSIVE_MASK = 0x2; - private static final int IS_AUTODELETE_MASK = 0x4; - public static final int QUEUE_TYPE = 1; public static final int TOPIC_TYPE = 2; public static final int UNKNOWN_TYPE = 3; - + // ----- Fields required to support new address syntax ------- public enum DestSyntax { @@ -323,7 +315,11 @@ public abstract class AMQDestination implements Destination, Referenceable { if(_urlAsShortString == null) { - toURL(); + if (_url == null) + { + toURL(); + } + _urlAsShortString = new AMQShortString(_url); } return _urlAsShortString; } @@ -370,7 +366,6 @@ public abstract class AMQDestination implements Destination, Referenceable // calculated URL now out of date _url = null; _urlAsShortString = null; - _byteEncoding = null; } public AMQShortString getRoutingKey() @@ -508,59 +503,10 @@ public abstract class AMQDestination implements Destination, Referenceable sb.deleteCharAt(sb.length() - 1); url = sb.toString(); _url = url; - _urlAsShortString = new AMQShortString(url); } return url; } - public byte[] toByteEncoding() - { - byte[] encoding = _byteEncoding; - if(encoding == null) - { - int size = _exchangeClass.length() + 1 + - _exchangeName.length() + 1 + - 0 + // in place of the destination name - (_queueName == null ? 0 : _queueName.length()) + 1 + - 1; - encoding = new byte[size]; - int pos = 0; - - pos = _exchangeClass.writeToByteArray(encoding, pos); - pos = _exchangeName.writeToByteArray(encoding, pos); - - encoding[pos++] = (byte)0; - - if(_queueName == null) - { - encoding[pos++] = (byte)0; - } - else - { - pos = _queueName.writeToByteArray(encoding,pos); - } - byte options = 0; - if(_isDurable) - { - options |= IS_DURABLE_MASK; - } - if(_isExclusive) - { - options |= IS_EXCLUSIVE_MASK; - } - if(_isAutoDelete) - { - options |= IS_AUTODELETE_MASK; - } - encoding[pos] = options; - - - _byteEncoding = encoding; - - } - return encoding; - } - public boolean equals(Object o) { if (this == o) @@ -614,53 +560,6 @@ public abstract class AMQDestination implements Destination, Referenceable null); // factory location } - - public static Destination createDestination(byte[] byteEncodedDestination) - { - AMQShortString exchangeClass; - AMQShortString exchangeName; - AMQShortString routingKey; - AMQShortString queueName; - boolean isDurable; - boolean isExclusive; - boolean isAutoDelete; - - int pos = 0; - exchangeClass = AMQShortString.readFromByteArray(byteEncodedDestination, pos); - pos+= exchangeClass.length() + 1; - exchangeName = AMQShortString.readFromByteArray(byteEncodedDestination, pos); - pos+= exchangeName.length() + 1; - routingKey = AMQShortString.readFromByteArray(byteEncodedDestination, pos); - pos+= (routingKey == null ? 0 : routingKey.length()) + 1; - queueName = AMQShortString.readFromByteArray(byteEncodedDestination, pos); - pos+= (queueName == null ? 0 : queueName.length()) + 1; - int options = byteEncodedDestination[pos]; - isDurable = (options & IS_DURABLE_MASK) != 0; - isExclusive = (options & IS_EXCLUSIVE_MASK) != 0; - isAutoDelete = (options & IS_AUTODELETE_MASK) != 0; - - if (exchangeClass.equals(ExchangeDefaults.DIRECT_EXCHANGE_CLASS)) - { - return new AMQQueue(exchangeName,routingKey,queueName,isExclusive,isAutoDelete,isDurable); - } - else if (exchangeClass.equals(ExchangeDefaults.TOPIC_EXCHANGE_CLASS)) - { - return new AMQTopic(exchangeName,routingKey,isAutoDelete,queueName,isDurable); - } - else if (exchangeClass.equals(ExchangeDefaults.HEADERS_EXCHANGE_CLASS)) - { - return new AMQHeadersExchange(routingKey); - } - else - { - return new AMQAnyDestination(exchangeName,exchangeClass, - routingKey,isExclusive, - isAutoDelete,queueName, - isDurable, new AMQShortString[0]); - } - - } - public static Destination createDestination(BindingURL binding) { AMQShortString type = binding.getExchangeClass(); @@ -842,12 +741,12 @@ public abstract class AMQDestination implements Destination, Referenceable public boolean isAddressResolved() { - return _isAddressResolved; + return _addressResolved.get() > 0; } - public void setAddressResolved(boolean addressResolved) + public void setAddressResolved(long addressResolved) { - _isAddressResolved = addressResolved; + _addressResolved.set(addressResolved); } private static Address createAddressFromString(String str) @@ -895,7 +794,7 @@ public abstract class AMQDestination implements Destination, Referenceable return _browseOnly; } - public void setBrowseOnly(boolean b) + private void setBrowseOnly(boolean b) { _browseOnly = b; } @@ -925,7 +824,7 @@ public abstract class AMQDestination implements Destination, Referenceable dest.setTargetNode(_targetNode); dest.setSourceNode(_sourceNode); dest.setLink(_link); - dest.setAddressResolved(_isAddressResolved); + dest.setAddressResolved(_addressResolved.get()); return dest; } @@ -938,4 +837,9 @@ public abstract class AMQDestination implements Destination, Referenceable { _isDurable = b; } + + public boolean isResolvedAfter(long time) + { + return _addressResolved.get() > time; + } } 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 1f940b62f0..d34290e007 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 @@ -70,7 +70,6 @@ import org.apache.qpid.AMQDisconnectedException; import org.apache.qpid.AMQException; import org.apache.qpid.AMQInvalidArgumentException; import org.apache.qpid.AMQInvalidRoutingKeyException; -import org.apache.qpid.client.AMQDestination.AddressOption; import org.apache.qpid.client.AMQDestination.DestSyntax; import org.apache.qpid.client.failover.FailoverException; import org.apache.qpid.client.failover.FailoverNoopSupport; @@ -88,8 +87,6 @@ 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.state.AMQState; -import org.apache.qpid.client.state.AMQStateManager; import org.apache.qpid.client.util.FlowControllingBlockingQueue; import org.apache.qpid.common.AMQPFilterTypes; import org.apache.qpid.framing.AMQShortString; @@ -97,7 +94,10 @@ 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.protocol.AMQConstant; import org.apache.qpid.thread.Threading; +import org.apache.qpid.transport.SessionException; +import org.apache.qpid.transport.TransportException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -213,8 +213,6 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic */ 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 @@ -310,7 +308,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic protected final FlowControllingBlockingQueue _queue; /** Holds the highest received delivery tag. */ - private final AtomicLong _highestDeliveryTag = new AtomicLong(-1); + protected final AtomicLong _highestDeliveryTag = new AtomicLong(-1); private final AtomicLong _rollbackMark = new AtomicLong(-1); /** All the not yet acknowledged message tags */ @@ -364,7 +362,13 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic * Set when recover is called. This is to handle the case where recover() is called by application code during * onMessage() processing to ensure that an auto ack is not sent. */ - private boolean _inRecovery; + private volatile boolean _sessionInRecovery; + + /** + * Set when the dispatcher should direct incoming messages straight into the UnackedMessage list instead of + * to the syncRecieveQueue or MessageListener. Used during cleanup, e.g. in Session.recover(). + */ + private volatile boolean _usingDispatcherForCleanup; /** Used to indicates that the connection to which this session belongs, has been stopped. */ private boolean _connectionStopped; @@ -567,6 +571,8 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic close(-1); } + public abstract AMQException getLastException(); + public void checkNotClosed() throws JMSException { try @@ -575,16 +581,20 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic } catch (IllegalStateException ise) { - // if the Connection has closed then we should throw any exception that has occurred that we were not waiting for - AMQStateManager manager = _connection.getProtocolHandler().getStateManager(); + AMQException ex = getLastException(); + if (ex != null) + { + IllegalStateException ssnClosed = new IllegalStateException( + "Session has been closed", ex.getErrorCode().toString()); - if (manager.getCurrentState().equals(AMQState.CONNECTION_CLOSED) && manager.getLastException() != null) + ssnClosed.setLinkedException(ex); + ssnClosed.initCause(ex); + throw ssnClosed; + } + else { - ise.setLinkedException(manager.getLastException()); - ise.initCause(ise.getLinkedException()); + throw ise; } - - throw ise; } } @@ -600,29 +610,36 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic * Acknowledges all unacknowledged messages on the session, for all message consumers on the session. * * @throws IllegalStateException If the session is closed. + * @throws JMSException if there is a problem during acknowledge process. */ - public void acknowledge() throws IllegalStateException + public void acknowledge() throws IllegalStateException, JMSException { if (isClosed()) { throw new IllegalStateException("Session is already closed"); } - else if (hasFailedOver()) + else if (hasFailedOverDirty()) { + //perform an implicit recover in this scenario + recover(); + + //notify the consumer throw new IllegalStateException("has failed over"); } - while (true) + try { - Long tag = _unacknowledgedMessageTags.poll(); - if (tag == null) - { - break; - } - acknowledgeMessage(tag, false); + acknowledgeImpl(); + markClean(); + } + catch (TransportException e) + { + throw toJMSException("Exception while acknowledging message(s):" + e.getMessage(), e); } } + protected abstract void acknowledgeImpl() throws JMSException; + /** * Acknowledge one or many messages. * @@ -757,6 +774,10 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic _logger.debug( "Got FailoverException during channel close, ignored as channel already marked as closed."); } + catch (TransportException e) + { + throw toJMSException("Error closing session:" + e.getMessage(), e); + } finally { _connection.deregisterSession(_channelId); @@ -827,51 +848,44 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic * @throws JMSException If the JMS provider fails to commit the transaction due to some internal error. This does * not mean that the commit is known to have failed, merely that it is not known whether it * failed or not. - * @todo Be aware of possible changes to parameter order as versions change. */ public void commit() throws JMSException { checkTransacted(); - try + //Check that we are clean to commit. + if (_failedOverDirty) { - //Check that we are clean to commit. - if (_failedOverDirty) + if (_logger.isDebugEnabled()) { - rollback(); - - throw new TransactionRolledBackException("Connection failover has occured since last send. " + - "Forced rollback"); + _logger.debug("Session " + _channelId + " was dirty whilst failing over. Rolling back."); } + rollback(); + throw new TransactionRolledBackException("Connection failover has occured with uncommitted transaction activity." + + "The session transaction was rolled back."); + } - // Acknowledge all delivered messages - while (true) - { - Long tag = _deliveredMessageTags.poll(); - if (tag == null) - { - break; - } - - acknowledgeMessage(tag, false); - } - // Commits outstanding messages and acknowledgments - sendCommit(); + try + { + commitImpl(); markClean(); } catch (AMQException e) { - throw new JMSAMQException("Failed to commit: " + e.getMessage() + ":" + e.getCause(), e); + throw new JMSAMQException("Exception during commit: " + e.getMessage() + ":" + e.getCause(), e); } catch (FailoverException e) { throw new JMSAMQException("Fail-over interrupted commit. Status of the commit is uncertain.", e); } + catch(TransportException e) + { + throw toJMSException("Session exception occured while trying to commit: " + e.getMessage(), e); + } } - public abstract void sendCommit() throws AMQException, FailoverException; - + protected abstract void commitImpl() throws AMQException, FailoverException, TransportException; public void confirmConsumerCancelled(int consumerTag) { @@ -949,7 +963,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic return new AMQQueueBrowser(this, (AMQQueue) queue, messageSelector); } - public MessageConsumer createBrowserConsumer(Destination destination, String messageSelector, boolean noLocal) + protected MessageConsumer createBrowserConsumer(Destination destination, String messageSelector, boolean noLocal) throws JMSException { checkValidDestination(destination); @@ -963,15 +977,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic checkValidDestination(destination); return createConsumerImpl(destination, _prefetchHighMark, _prefetchLowMark, false, (destination instanceof Topic), null, null, - ((destination instanceof AMQDestination) && ((AMQDestination)destination).isBrowseOnly()), false); - } - - public C createExclusiveConsumer(Destination destination) throws JMSException - { - checkValidDestination(destination); - - return createConsumerImpl(destination, _prefetchHighMark, _prefetchLowMark, false, true, null, null, - ((destination instanceof AMQDestination) && ((AMQDestination)destination).isBrowseOnly()), false); + isBrowseOnlyDestination(destination), false); } public MessageConsumer createConsumer(Destination destination, String messageSelector) throws JMSException @@ -979,7 +985,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic checkValidDestination(destination); return createConsumerImpl(destination, _prefetchHighMark, _prefetchLowMark, false, (destination instanceof Topic), - messageSelector, null, ((destination instanceof AMQDestination) && ((AMQDestination)destination).isBrowseOnly()), false); + messageSelector, null, isBrowseOnlyDestination(destination), false); } public MessageConsumer createConsumer(Destination destination, String messageSelector, boolean noLocal) @@ -988,16 +994,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic checkValidDestination(destination); return createConsumerImpl(destination, _prefetchHighMark, _prefetchLowMark, noLocal, (destination instanceof Topic), - messageSelector, null, ((destination instanceof AMQDestination) && ((AMQDestination)destination).isBrowseOnly()), false); - } - - public MessageConsumer createExclusiveConsumer(Destination destination, String messageSelector, boolean noLocal) - throws JMSException - { - checkValidDestination(destination); - - return createConsumerImpl(destination, _prefetchHighMark, _prefetchLowMark, noLocal, true, - messageSelector, null, false, false); + messageSelector, null, isBrowseOnlyDestination(destination), false); } public MessageConsumer createConsumer(Destination destination, int prefetch, boolean noLocal, boolean exclusive, @@ -1005,23 +1002,15 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic { checkValidDestination(destination); - return createConsumerImpl(destination, prefetch, prefetch / 2, noLocal, exclusive, selector, null, ((destination instanceof AMQDestination) && ((AMQDestination)destination).isBrowseOnly()), false); + return createConsumerImpl(destination, prefetch, prefetch / 2, noLocal, exclusive, selector, null, isBrowseOnlyDestination(destination), false); } public MessageConsumer createConsumer(Destination destination, int prefetchHigh, int prefetchLow, boolean noLocal, - boolean exclusive, String selector) throws JMSException + boolean exclusive, String selector) throws JMSException { checkValidDestination(destination); - return createConsumerImpl(destination, prefetchHigh, prefetchLow, noLocal, exclusive, selector, null, ((destination instanceof AMQDestination) && ((AMQDestination)destination).isBrowseOnly()), false); - } - - public MessageConsumer createConsumer(Destination destination, int prefetch, boolean noLocal, boolean exclusive, - String selector, FieldTable rawSelector) throws JMSException - { - checkValidDestination(destination); - - return createConsumerImpl(destination, prefetch, prefetch / 2, noLocal, exclusive, selector, rawSelector, ((destination instanceof AMQDestination) && ((AMQDestination)destination).isBrowseOnly()), false); + return createConsumerImpl(destination, prefetchHigh, prefetchLow, noLocal, exclusive, selector, null, isBrowseOnlyDestination(destination), false); } public MessageConsumer createConsumer(Destination destination, int prefetchHigh, int prefetchLow, boolean noLocal, @@ -1029,7 +1018,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic { checkValidDestination(destination); - return createConsumerImpl(destination, prefetchHigh, prefetchLow, noLocal, exclusive, selector, rawSelector, ((destination instanceof AMQDestination) && ((AMQDestination)destination).isBrowseOnly()), + return createConsumerImpl(destination, prefetchHigh, prefetchLow, noLocal, exclusive, selector, rawSelector, isBrowseOnlyDestination(destination), false); } @@ -1043,8 +1032,33 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic throws JMSException { checkNotClosed(); - AMQTopic origTopic = checkValidTopic(topic, true); + Topic origTopic = checkValidTopic(topic, true); + AMQTopic dest = AMQTopic.createDurableTopic(origTopic, name, _connection); + if (dest.getDestSyntax() == DestSyntax.ADDR && + !dest.isAddressResolved()) + { + try + { + handleAddressBasedDestination(dest,false,true); + if (dest.getAddressType() != AMQDestination.TOPIC_TYPE) + { + throw new JMSException("Durable subscribers can only be created for Topics"); + } + dest.getSourceNode().setDurable(true); + } + catch(AMQException e) + { + JMSException ex = new JMSException("Error when verifying destination"); + ex.initCause(e); + ex.setLinkedException(e); + throw ex; + } + catch(TransportException e) + { + throw toJMSException("Error when verifying destination", e); + } + } String messageSelector = ((selector == null) || (selector.trim().length() == 0)) ? null : selector; @@ -1056,15 +1070,9 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic // Not subscribed to this name in the current session if (subscriber == null) { - AMQShortString topicName; - if (topic instanceof AMQTopic) - { - topicName = ((AMQTopic) topic).getRoutingKey(); - } else - { - topicName = new AMQShortString(topic.getTopicName()); - } - + // After the address is resolved routing key will not be null. + AMQShortString topicName = dest.getRoutingKey(); + if (_strictAMQP) { if (_strictAMQPFATAL) @@ -1135,6 +1143,10 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic return subscriber; } + catch (TransportException e) + { + throw toJMSException("Exception while creating durable subscriber:" + e.getMessage(), e); + } finally { _subscriberDetails.unlock(); @@ -1195,12 +1207,6 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic return createProducerImpl(destination, mandatory, immediate); } - public P createProducer(Destination destination, boolean mandatory, boolean immediate, - boolean waitUntilSent) throws JMSException - { - return createProducerImpl(destination, mandatory, immediate, waitUntilSent); - } - public TopicPublisher createPublisher(Topic topic) throws JMSException { checkNotClosed(); @@ -1225,7 +1231,6 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic else { AMQQueue queue = new AMQQueue(queueName); - queue.setCreate(AddressOption.ALWAYS); return queue; } @@ -1307,8 +1312,8 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic public QueueReceiver createQueueReceiver(Destination destination) throws JMSException { checkValidDestination(destination); - AMQQueue dest = (AMQQueue) destination; - C consumer = (C) createConsumer(destination); + Queue dest = validateQueue(destination); + C consumer = (C) createConsumer(dest); return new QueueReceiverAdaptor(dest, consumer); } @@ -1326,8 +1331,8 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic public QueueReceiver createQueueReceiver(Destination destination, String messageSelector) throws JMSException { checkValidDestination(destination); - AMQQueue dest = (AMQQueue) destination; - C consumer = (C) createConsumer(destination, messageSelector); + Queue dest = validateQueue(destination); + C consumer = (C) createConsumer(dest, messageSelector); return new QueueReceiverAdaptor(dest, consumer); } @@ -1344,7 +1349,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic public QueueReceiver createReceiver(Queue queue) throws JMSException { checkNotClosed(); - AMQQueue dest = (AMQQueue) queue; + Queue dest = validateQueue(queue); C consumer = (C) createConsumer(dest); return new QueueReceiverAdaptor(dest, consumer); @@ -1363,17 +1368,28 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic public QueueReceiver createReceiver(Queue queue, String messageSelector) throws JMSException { checkNotClosed(); - AMQQueue dest = (AMQQueue) queue; + Queue dest = validateQueue(queue); C consumer = (C) createConsumer(dest, messageSelector); return new QueueReceiverAdaptor(dest, consumer); } + + private Queue validateQueue(Destination dest) throws InvalidDestinationException + { + if (dest instanceof AMQDestination && dest instanceof javax.jms.Queue) + { + return (Queue)dest; + } + else + { + throw new InvalidDestinationException("The destination object used is not from this provider or of type javax.jms.Queue"); + } + } public QueueSender createSender(Queue queue) throws JMSException { checkNotClosed(); - // return (QueueSender) createProducer(queue); return new QueueSenderAdapter(createProducer(queue), queue); } @@ -1408,10 +1424,10 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic public TopicSubscriber createSubscriber(Topic topic) throws JMSException { checkNotClosed(); - AMQTopic dest = checkValidTopic(topic); + checkValidTopic(topic); - // AMQTopic dest = new AMQTopic(topic.getTopicName()); - return new TopicSubscriberAdaptor(dest, (C) createExclusiveConsumer(dest)); + return new TopicSubscriberAdaptor<C>(topic, + createConsumerImpl(topic, _prefetchHighMark, _prefetchLowMark, false, true, null, null, false, false)); } /** @@ -1428,10 +1444,11 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic public TopicSubscriber createSubscriber(Topic topic, String messageSelector, boolean noLocal) throws JMSException { checkNotClosed(); - AMQTopic dest = checkValidTopic(topic); + checkValidTopic(topic); - // AMQTopic dest = new AMQTopic(topic.getTopicName()); - return new TopicSubscriberAdaptor(dest, (C) createExclusiveConsumer(dest, messageSelector, noLocal)); + return new TopicSubscriberAdaptor<C>(topic, + createConsumerImpl(topic, _prefetchHighMark, _prefetchLowMark, noLocal, + true, messageSelector, null, false, false)); } public TemporaryQueue createTemporaryQueue() throws JMSException @@ -1533,10 +1550,8 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic abstract public void sync() throws AMQException; - public int getAcknowledgeMode() throws JMSException + public int getAcknowledgeMode() { - checkNotClosed(); - return _acknowledgeMode; } @@ -1596,10 +1611,8 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic return _ticket; } - public boolean getTransacted() throws JMSException + public boolean getTransacted() { - checkNotClosed(); - return _transacted; } @@ -1695,13 +1708,14 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic // Ensure that the session is not transacted. checkNotTransacted(); - // flush any acks we are holding in the buffer. - flushAcknowledgments(); - - // this is set only here, and the before the consumer's onMessage is called it is set to false - _inRecovery = true; + try { + // flush any acks we are holding in the buffer. + flushAcknowledgments(); + + // this is only set true here, and only set false when the consumers preDeliver method is called + _sessionInRecovery = true; boolean isSuspended = isSuspended(); @@ -1709,9 +1723,18 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic { suspendChannel(true); } - + + // Set to true to short circuit delivery of anything currently + //in the pre-dispatch queue. + _usingDispatcherForCleanup = true; + syncDispatchQueue(); - + + // Set to false before sending the recover as 0-8/9/9-1 will + //send messages back before the recover completes, and we + //probably shouldn't clean those! ;-) + _usingDispatcherForCleanup = false; + if (_dispatcher != null) { _dispatcher.recover(); @@ -1720,10 +1743,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic sendRecover(); markClean(); - - // Set inRecovery to false before you start message flow again again. - _inRecovery = false; - + if (!isSuspended) { suspendChannel(false); @@ -1737,7 +1757,10 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic { throw new JMSAMQException("Recovery was interrupted by fail-over. Recovery status is not known.", e); } - + catch(TransportException e) + { + throw toJMSException("Recover failed: " + e.getMessage(), e); + } } protected abstract void sendRecover() throws AMQException, FailoverException; @@ -1795,9 +1818,7 @@ 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()); + setRollbackMark(); syncDispatchQueue(); @@ -1822,6 +1843,10 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic { throw new JMSAMQException("Fail-over interrupted rollback. Status of the rollback is uncertain.", e); } + catch (TransportException e) + { + throw toJMSException("Failure to rollback:" + e.getMessage(), e); + } } } @@ -1868,7 +1893,14 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic */ public void unsubscribe(String name) throws JMSException { - unsubscribe(name, false); + try + { + unsubscribe(name, false); + } + catch (TransportException e) + { + throw toJMSException("Exception while unsubscribing:" + e.getMessage(), e); + } } /** @@ -1945,6 +1977,12 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic { checkTemporaryDestination(destination); + if(!noConsume && isBrowseOnlyDestination(destination)) + { + throw new InvalidDestinationException("The consumer being created is not 'noConsume'," + + "but a 'browseOnly' Destination has been supplied."); + } + final String messageSelector; if (_strictAMQP && !((selector == null) || selector.equals(""))) @@ -1989,8 +2027,16 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic // argument, as specifying null for the arguments when querying means they should not be checked at all ft.put(AMQPFilterTypes.JMS_SELECTOR.getValue(), messageSelector == null ? "" : messageSelector); - C consumer = createMessageConsumer(amqd, prefetchHigh, prefetchLow, - noLocal, exclusive, messageSelector, ft, noConsume, autoClose); + C consumer; + try + { + consumer = createMessageConsumer(amqd, prefetchHigh, prefetchLow, + noLocal, exclusive, messageSelector, ft, noConsume, autoClose); + } + catch(TransportException e) + { + throw toJMSException("Exception while creating consumer: " + e.getMessage(), e); + } if (_messageListener != null) { @@ -2027,7 +2073,10 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic ex.initCause(e); throw ex; } - + catch (TransportException e) + { + throw toJMSException("Exception while registering consumer:" + e.getMessage(), e); + } return consumer; } }, _connection).execute(); @@ -2092,7 +2141,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic boolean isInRecovery() { - return _inRecovery; + return _sessionInRecovery; } boolean isQueueBound(AMQShortString exchangeName, AMQShortString queueName) throws JMSException @@ -2214,7 +2263,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic void setInRecovery(boolean inRecovery) { - _inRecovery = inRecovery; + _sessionInRecovery = inRecovery; } boolean isStarted() @@ -2395,7 +2444,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, boolean durable) throws JMSException + protected Topic checkValidTopic(Topic topic, boolean durable) throws JMSException { if (topic == null) { @@ -2414,17 +2463,17 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic ("Cannot create a durable subscription with a temporary topic: " + topic); } - if (!(topic instanceof AMQTopic)) + if (!(topic instanceof AMQDestination && topic instanceof javax.jms.Topic)) { throw new javax.jms.InvalidDestinationException( "Cannot create a subscription on topic created for another JMS Provider, class of topic provided is: " + topic.getClass().getName()); } - return (AMQTopic) topic; + return topic; } - protected AMQTopic checkValidTopic(Topic topic) throws JMSException + protected Topic checkValidTopic(Topic topic) throws JMSException { return checkValidTopic(topic, false); } @@ -2553,15 +2602,9 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic public abstract void sendConsume(C consumer, AMQShortString queueName, AMQProtocolHandler protocolHandler, boolean nowait, String messageSelector, int tag) throws AMQException, FailoverException; - private P createProducerImpl(Destination destination, boolean mandatory, boolean immediate) + private P createProducerImpl(final Destination destination, final boolean mandatory, final boolean immediate) throws JMSException { - return createProducerImpl(destination, mandatory, immediate, DEFAULT_WAIT_ON_SEND); - } - - private P createProducerImpl(final Destination destination, final boolean mandatory, - final boolean immediate, final boolean waitUntilSent) throws JMSException - { return new FailoverRetrySupport<P, JMSException>( new FailoverProtectedOperation<P, JMSException>() { @@ -2569,8 +2612,18 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic { checkNotClosed(); long producerId = getNextProducerId(); - P producer = createMessageProducer(destination, mandatory, - immediate, waitUntilSent, producerId); + + P producer; + try + { + producer = createMessageProducer(destination, mandatory, + immediate, producerId); + } + catch (TransportException e) + { + throw toJMSException("Exception while creating producer:" + e.getMessage(), e); + } + registerProducer(producerId, producer); return producer; @@ -2579,7 +2632,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic } public abstract P createMessageProducer(final Destination destination, final boolean mandatory, - final boolean immediate, final boolean waitUntilSent, long producerId) throws JMSException; + final boolean immediate, final long producerId) throws JMSException; private void declareExchange(AMQDestination amqd, AMQProtocolHandler protocolHandler, boolean nowait) throws AMQException { @@ -2722,6 +2775,21 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic } } + /** + * Undeclares the specified temporary queue/topic. + * + * <p/>Note that this operation automatically retries in the event of fail-over. + * + * @param amqQueue The name of the temporary destination to delete. + * + * @throws JMSException If the queue could not be deleted for any reason. + * @todo Be aware of possible changes to parameter order as versions change. + */ + protected void deleteTemporaryDestination(final TemporaryDestination amqQueue) throws JMSException + { + deleteQueue(amqQueue.getAMQQueueName()); + } + public abstract void sendQueueDelete(final AMQShortString queueName) throws AMQException, FailoverException; private long getNextProducerId() @@ -2819,6 +2887,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic { declareQueue(amqd, protocolHandler, consumer.isNoLocal(), nowait); } + bindQueue(amqd.getAMQQueueName(), amqd.getRoutingKey(), consumer.getArguments(), amqd.getExchangeName(), amqd, nowait); } AMQShortString queueName = amqd.getAMQQueueName(); @@ -2826,8 +2895,6 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic // store the consumer queue name consumer.setQueuename(queueName); - 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) { @@ -2978,6 +3045,10 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic { throw new AMQException(null, "Fail-over interrupted suspend/unsuspend channel.", e); } + catch (TransportException e) + { + throw new AMQException(AMQConstant.getConstant(getErrorCode(e)), e.getMessage(), e); + } } } @@ -3016,21 +3087,11 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic * * @return boolean true if failover has occured. */ - public boolean hasFailedOver() + public boolean hasFailedOverDirty() { return _failedOverDirty; } - /** - * Check to see if any message have been sent in this transaction and have not been commited. - * - * @return boolean true if a message has been sent but not commited - */ - public boolean isDirty() - { - return _dirty; - } - public void setTicket(int ticket) { _ticket = ticket; @@ -3143,7 +3204,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic setConnectionStopped(true); } - _rollbackMark.set(_highestDeliveryTag.get()); + setRollbackMark(); _dispatcherLogger.debug("Session Pre Dispatch Queue cleared"); @@ -3292,9 +3353,14 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic if (!(message instanceof CloseConsumerMessage) && tagLE(deliveryTag, _rollbackMark.get())) { + if (_logger.isDebugEnabled()) + { + _logger.debug("Rejecting message because delivery tag " + deliveryTag + + " <= rollback mark " + _rollbackMark.get()); + } rejectMessage(message, true); } - else if (isInRecovery()) + else if (_usingDispatcherForCleanup) { _unacknowledgedMessageTags.add(deliveryTag); } @@ -3353,6 +3419,11 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic // Don't reject if we're already closing if (!_closed.get()) { + if (_logger.isDebugEnabled()) + { + _logger.debug("Rejecting message with delivery tag " + message.getDeliveryTag() + + " for closing consumer " + String.valueOf(consumer == null? null: consumer._consumerTag)); + } rejectMessage(message, true); } } @@ -3450,4 +3521,48 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic { return _closing.get()|| _connection.isClosing(); } + + public boolean isDeclareExchanges() + { + return DECLARE_EXCHANGES; + } + + JMSException toJMSException(String message, TransportException e) + { + int code = getErrorCode(e); + JMSException jmse = new JMSException(message, Integer.toString(code)); + jmse.setLinkedException(e); + jmse.initCause(e); + return jmse; + } + + private int getErrorCode(TransportException e) + { + int code = AMQConstant.INTERNAL_ERROR.getCode(); + if (e instanceof SessionException) + { + SessionException se = (SessionException) e; + if(se.getException() != null && se.getException().getErrorCode() != null) + { + code = se.getException().getErrorCode().getValue(); + } + } + return code; + } + + private boolean isBrowseOnlyDestination(Destination destination) + { + return ((destination instanceof AMQDestination) && ((AMQDestination)destination).isBrowseOnly()); + } + + private void setRollbackMark() + { + // Let the dispatcher know that all the incomming messages + // should be rolled back(reject/release) + _rollbackMark.set(_highestDeliveryTag.get()); + if (_logger.isDebugEnabled()) + { + _logger.debug("Rollback mark is set to " + _rollbackMark.get()); + } + } } 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 517a7a5ce8..2869e96a87 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 @@ -47,6 +47,8 @@ import org.apache.qpid.client.message.AMQMessageDelegateFactory; import org.apache.qpid.client.message.FieldTableSupport; import org.apache.qpid.client.message.MessageFactoryRegistry; import org.apache.qpid.client.message.UnprocessedMessage_0_10; +import org.apache.qpid.client.messaging.address.Link; +import org.apache.qpid.client.messaging.address.Link.Reliability; import org.apache.qpid.client.messaging.address.Node.ExchangeNode; import org.apache.qpid.client.messaging.address.Node.QueueNode; import org.apache.qpid.client.protocol.AMQProtocolHandler; @@ -56,6 +58,7 @@ import org.apache.qpid.framing.FieldTable; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.transport.ExchangeBoundResult; import org.apache.qpid.transport.ExchangeQueryResult; +import org.apache.qpid.transport.ExecutionErrorCode; import org.apache.qpid.transport.ExecutionException; import org.apache.qpid.transport.MessageAcceptMode; import org.apache.qpid.transport.MessageAcquireMode; @@ -69,6 +72,7 @@ import org.apache.qpid.transport.RangeSet; import org.apache.qpid.transport.Session; import org.apache.qpid.transport.SessionException; import org.apache.qpid.transport.SessionListener; +import org.apache.qpid.transport.TransportException; import org.apache.qpid.util.Serial; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -156,13 +160,20 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic */ AMQSession_0_10(org.apache.qpid.transport.Connection qpidConnection, AMQConnection con, int channelId, boolean transacted, int acknowledgeMode, MessageFactoryRegistry messageFactoryRegistry, - int defaultPrefetchHighMark, int defaultPrefetchLowMark) + int defaultPrefetchHighMark, int defaultPrefetchLowMark,String name) { super(con, channelId, transacted, acknowledgeMode, messageFactoryRegistry, defaultPrefetchHighMark, defaultPrefetchLowMark); _qpidConnection = qpidConnection; - _qpidSession = _qpidConnection.createSession(1); + if (name == null) + { + _qpidSession = _qpidConnection.createSession(1); + } + else + { + _qpidSession = _qpidConnection.createSession(name,1); + } _qpidSession.setSessionListener(this); if (_transacted) { @@ -189,11 +200,12 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic * @param qpidConnection The connection */ AMQSession_0_10(org.apache.qpid.transport.Connection qpidConnection, AMQConnection con, int channelId, - boolean transacted, int acknowledgeMode, int defaultPrefetchHigh, int defaultPrefetchLow) + boolean transacted, int acknowledgeMode, int defaultPrefetchHigh, int defaultPrefetchLow, + String name) { this(qpidConnection, con, channelId, transacted, acknowledgeMode, MessageFactoryRegistry.newDefaultRegistry(), - defaultPrefetchHigh, defaultPrefetchLow); + defaultPrefetchHigh, defaultPrefetchLow,name); } private void addUnacked(int id) @@ -258,7 +270,7 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic long prefetch = getAMQConnection().getMaxPrefetch(); - if (unackedCount >= prefetch/2 || maxAckDelay <= 0) + if (unackedCount >= prefetch/2 || maxAckDelay <= 0 || _acknowledgeMode == javax.jms.Session.AUTO_ACKNOWLEDGE) { flushAcknowledgments(); } @@ -282,23 +294,34 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic } } - void messageAcknowledge(RangeSet ranges, boolean accept) + void messageAcknowledge(final RangeSet ranges, final boolean accept) { messageAcknowledge(ranges,accept,false); } - void messageAcknowledge(RangeSet ranges, boolean accept,boolean setSyncBit) + void messageAcknowledge(final RangeSet ranges, final boolean accept, final boolean setSyncBit) { - Session ssn = getQpidSession(); - for (Range range : ranges) + final Session ssn = getQpidSession(); + flushProcessed(ranges,accept); + if (accept) { - ssn.processed(range); + ssn.messageAccept(ranges, UNRELIABLE, setSyncBit ? SYNC : NONE); } - ssn.flushProcessed(accept ? BATCH : NONE); - if (accept) + } + + /** + * Flush any outstanding commands. This causes session complete to be sent. + * @param ranges the range of command ids. + * @param batch true if batched. + */ + void flushProcessed(final RangeSet ranges, final boolean batch) + { + final Session ssn = getQpidSession(); + for (final Range range : ranges) { - ssn.messageAccept(ranges, UNRELIABLE,setSyncBit? SYNC : NONE); + ssn.processed(range); } + ssn.flushProcessed(batch ? BATCH : NONE); } /** @@ -314,7 +337,7 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic public void sendQueueBind(final AMQShortString queueName, final AMQShortString routingKey, final FieldTable arguments, final AMQShortString exchangeName, final AMQDestination destination, final boolean nowait) - throws AMQException, FailoverException + throws AMQException { if (destination.getDestSyntax() == DestSyntax.BURL) { @@ -400,25 +423,6 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic } } - - /** - * Commit the receipt and the delivery of all messages exchanged by this session resources. - */ - public void sendCommit() throws AMQException, FailoverException - { - getQpidSession().setAutoSync(true); - try - { - getQpidSession().txCommit(); - } - finally - { - getQpidSession().setAutoSync(false); - } - // We need to sync so that we get notify of an error. - sync(); - } - /** * Create a queue with a given name. * @@ -451,6 +455,14 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic public void sendRecover() throws AMQException, FailoverException { // release all unacked messages + RangeSet ranges = gatherUnackedRangeSet(); + getQpidSession().messageRelease(ranges, Option.SET_REDELIVERED); + // We need to sync so that we get notify of an error. + sync(); + } + + private RangeSet gatherUnackedRangeSet() + { RangeSet ranges = new RangeSet(); while (true) { @@ -459,11 +471,11 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic { break; } - ranges.add((int) (long) tag); + + ranges.add(tag.intValue()); } - getQpidSession().messageRelease(ranges, Option.SET_REDELIVERED); - // We need to sync so that we get notify of an error. - sync(); + + return ranges; } @@ -537,7 +549,6 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic } public boolean isQueueBound(final String exchangeName, final String queueName, final String bindingKey,Map<String,Object> args) - throws JMSException { boolean res; ExchangeBoundResult bindingQueryResult = @@ -600,10 +611,16 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic (Map<? extends String, ? extends Object>) consumer.getDestination().getLink().getSubscription().getArgs()); } + boolean acceptModeNone = getAcknowledgeMode() == NO_ACKNOWLEDGE; + + if (consumer.getDestination().getLink() != null) + { + acceptModeNone = consumer.getDestination().getLink().getReliability() == Link.Reliability.UNRELIABLE; + } getQpidSession().messageSubscribe (queueName.toString(), String.valueOf(tag), - getAcknowledgeMode() == NO_ACKNOWLEDGE ? MessageAcceptMode.NONE : MessageAcceptMode.EXPLICIT, + acceptModeNone ? MessageAcceptMode.NONE : MessageAcceptMode.EXPLICIT, preAcquire ? MessageAcquireMode.PRE_ACQUIRED : MessageAcquireMode.NOT_ACQUIRED, null, 0, arguments, consumer.isExclusive() ? Option.EXCLUSIVE : Option.NONE); } @@ -659,13 +676,12 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic * Create an 0_10 message producer */ public BasicMessageProducer_0_10 createMessageProducer(final Destination destination, final boolean mandatory, - final boolean immediate, final boolean waitUntilSent, - long producerId) throws JMSException + final boolean immediate, final long producerId) throws JMSException { try { return new BasicMessageProducer_0_10(_connection, (AMQDestination) destination, _transacted, _channelId, this, - getProtocolHandler(), producerId, immediate, mandatory, waitUntilSent); + getProtocolHandler(), producerId, immediate, mandatory); } catch (AMQException e) { @@ -675,6 +691,10 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic throw ex; } + catch(TransportException e) + { + throw toJMSException("Exception while creating message producer:" + e.getMessage(), e); + } } @@ -767,7 +787,7 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic else { QueueNode node = (QueueNode)amqd.getSourceNode(); - getQpidSession().queueDeclare(queueName.toString(), "" , + getQpidSession().queueDeclare(queueName.toString(), node.getAlternateExchange() , node.getDeclareArgs(), node.isAutoDelete() ? Option.AUTO_DELETE : Option.NONE, node.isDurable() ? Option.DURABLE : Option.NONE, @@ -904,7 +924,26 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic setCurrentException(exc); } - public void closed(Session ssn) {} + public void closed(Session ssn) + { + try + { + super.closed(null); + if (flushTask != null) + { + flushTask.cancel(); + flushTask = null; + } + } catch (Exception e) + { + _logger.error("Error closing JMS session", e); + } + } + + public AMQException getLastException() + { + return getCurrentException(); + } protected AMQShortString declareQueue(final AMQDestination amqd, final AMQProtocolHandler protocolHandler, final boolean noLocal, final boolean nowait) @@ -958,27 +997,26 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic } } - @Override public void commit() throws JMSException + public void commitImpl() throws AMQException, FailoverException, TransportException { - checkTransacted(); - try + if( _txSize > 0 ) { - if( _txSize > 0 ) - { - messageAcknowledge(_txRangeSet, true); - _txRangeSet.clear(); - _txSize = 0; - } - sendCommit(); + messageAcknowledge(_txRangeSet, true); + _txRangeSet.clear(); + _txSize = 0; } - catch (AMQException e) + + getQpidSession().setAutoSync(true); + try { - throw new JMSAMQException("Failed to commit: " + e.getMessage(), e); + getQpidSession().txCommit(); } - catch (FailoverException e) + finally { - throw new JMSAMQException("Fail-over interrupted commit. Status of the commit is uncertain.", e); + getQpidSession().setAutoSync(false); } + // We need to sync so that we get notify of an error. + sync(); } protected final boolean tagLE(long tag1, long tag2) @@ -1020,11 +1058,9 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic code = ee.getErrorCode().getValue(); } AMQException amqe = new AMQException(AMQConstant.getConstant(code), se.getMessage(), se.getCause()); - - _connection.exceptionReceived(amqe); - _currentException = amqe; } + _connection.exceptionReceived(_currentException); } public AMQMessageDelegateFactory getMessageDelegateFactory() @@ -1068,22 +1104,37 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic return match; } - public boolean isQueueExist(AMQDestination dest,QueueNode node,boolean assertNode) + public boolean isQueueExist(AMQDestination dest,QueueNode node,boolean assertNode) throws AMQException { boolean match = true; - QueueQueryResult result = getQpidSession().queueQuery(dest.getAddressName(), Option.NONE).get(); - match = dest.getAddressName().equals(result.getQueue()); - - if (match && assertNode) + try { - match = (result.getDurable() == node.isDurable()) && - (result.getAutoDelete() == node.isAutoDelete()) && - (result.getExclusive() == node.isExclusive()) && - (matchProps(result.getArguments(),node.getDeclareArgs())); + QueueQueryResult result = getQpidSession().queueQuery(dest.getAddressName(), Option.NONE).get(); + match = dest.getAddressName().equals(result.getQueue()); + + if (match && assertNode) + { + match = (result.getDurable() == node.isDurable()) && + (result.getAutoDelete() == node.isAutoDelete()) && + (result.getExclusive() == node.isExclusive()) && + (matchProps(result.getArguments(),node.getDeclareArgs())); + } + else if (match) + { + // should I use the queried details to update the local data structure. + } } - else if (match) + catch(SessionException e) { - // should I use the queried details to update the local data structure. + if (e.getException().getErrorCode() == ExecutionErrorCode.RESOURCE_DELETED) + { + match = false; + } + else + { + throw new AMQException(AMQConstant.getConstant(e.getException().getErrorCode().getValue()), + "Error querying queue",e); + } } return match; @@ -1128,8 +1179,8 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic boolean isConsumer, boolean noWait) throws AMQException { - if (dest.isAddressResolved()) - { + if (dest.isAddressResolved() && dest.isResolvedAfter(_connection.getLastFailoverTime())) + { if (isConsumer && AMQDestination.TOPIC_TYPE == dest.getAddressType()) { createSubscriptionQueue(dest); @@ -1149,6 +1200,22 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic int type = resolveAddressType(dest); + if (type == AMQDestination.QUEUE_TYPE && + dest.getLink().getReliability() == Reliability.UNSPECIFIED) + { + dest.getLink().setReliability(Reliability.AT_LEAST_ONCE); + } + else if (type == AMQDestination.TOPIC_TYPE && + dest.getLink().getReliability() == Reliability.UNSPECIFIED) + { + dest.getLink().setReliability(Reliability.UNRELIABLE); + } + else if (type == AMQDestination.TOPIC_TYPE && + dest.getLink().getReliability() == Reliability.AT_LEAST_ONCE) + { + throw new AMQException("AT-LEAST-ONCE is not yet supported for Topics"); + } + switch (type) { case AMQDestination.QUEUE_TYPE: @@ -1162,6 +1229,8 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic { setLegacyFiledsForQueueType(dest); send0_10QueueDeclare(dest,null,false,noWait); + sendQueueBind(dest.getAMQQueueName(), dest.getRoutingKey(), + null,dest.getExchangeName(),dest, false); break; } } @@ -1200,7 +1269,7 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic "The name '" + dest.getAddressName() + "' supplied in the address doesn't resolve to an exchange or a queue"); } - dest.setAddressResolved(true); + dest.setAddressResolved(System.currentTimeMillis()); } } @@ -1270,6 +1339,8 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic dest.getQueueName(),// should have one by now dest.getSubject(), Collections.<String,Object>emptyMap())); + sendQueueBind(dest.getAMQQueueName(), dest.getRoutingKey(), + null,dest.getExchangeName(),dest, false); } public void setLegacyFiledsForQueueType(AMQDestination dest) @@ -1307,5 +1378,26 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic sb.append(">"); return sb.toString(); } - + + protected void acknowledgeImpl() + { + RangeSet range = gatherUnackedRangeSet(); + + if(range.size() > 0 ) + { + messageAcknowledge(range, true); + getQpidSession().sync(); + } + } + + @Override + void resubscribe() throws AMQException + { + // Also reset the delivery tag tracker, to insure we dont + // return the first <total number of msgs received on session> + // messages sent by the brokers following the first rollback + // after failover + _highestDeliveryTag.set(-1); + super.resubscribe(); + } } 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 f41b1c94fa..369c8a6e9d 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 @@ -38,6 +38,7 @@ import org.apache.qpid.client.message.ReturnMessage; import org.apache.qpid.client.message.UnprocessedMessage; import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.client.state.AMQState; +import org.apache.qpid.client.state.AMQStateManager; import org.apache.qpid.client.state.listener.SpecificMethodFrameListener; import org.apache.qpid.common.AMQPFilterTypes; import org.apache.qpid.framing.AMQFrame; @@ -75,12 +76,12 @@ import org.apache.qpid.framing.amqp_0_91.MethodRegistry_0_91; import org.apache.qpid.jms.Session; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.transport.TransportException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public final class AMQSession_0_8 extends AMQSession<BasicMessageConsumer_0_8, BasicMessageProducer_0_8> { - /** Used for debugging. */ private static final Logger _logger = LoggerFactory.getLogger(AMQSession.class); @@ -90,7 +91,7 @@ public final class AMQSession_0_8 extends AMQSession<BasicMessageConsumer_0_8, B * @param con The connection on which to create the session. * @param channelId The unique identifier for the session. * @param transacted Indicates whether or not the session is transactional. - * @param acknowledgeMode The acknoledgement mode for the session. + * @param acknowledgeMode The acknowledgement mode for the session. * @param messageFactoryRegistry The message factory factory for the session. * @param defaultPrefetchHighMark The maximum number of messages to prefetched before suspending the session. * @param defaultPrefetchLowMark The number of prefetched messages at which to resume the session. @@ -108,7 +109,7 @@ public final class AMQSession_0_8 extends AMQSession<BasicMessageConsumer_0_8, B * @param con The connection on which to create the session. * @param channelId The unique identifier for the session. * @param transacted Indicates whether or not the session is transactional. - * @param acknowledgeMode The acknoledgement mode for the session. + * @param acknowledgeMode The acknowledgement mode for the session. * @param defaultPrefetchHigh The maximum number of messages to prefetched before suspending the session. * @param defaultPrefetchLow The number of prefetched messages at which to resume the session. */ @@ -124,6 +125,20 @@ public final class AMQSession_0_8 extends AMQSession<BasicMessageConsumer_0_8, B return getProtocolHandler().getProtocolVersion(); } + protected void acknowledgeImpl() + { + while (true) + { + Long tag = _unacknowledgedMessageTags.poll(); + if (tag == null) + { + break; + } + + acknowledgeMessage(tag, false); + } + } + public void acknowledgeMessage(long deliveryTag, boolean multiple) { BasicAckBody body = getMethodRegistry().createBasicAckBody(deliveryTag, multiple); @@ -153,7 +168,7 @@ public final class AMQSession_0_8 extends AMQSession<BasicMessageConsumer_0_8, B // 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 + // We can't close the session if we are already in the process of // closing/closed the connection. if (!(getProtocolHandler().getStateManager().getCurrentState().equals(AMQState.CONNECTION_CLOSED) @@ -169,8 +184,20 @@ public final class AMQSession_0_8 extends AMQSession<BasicMessageConsumer_0_8, B } } - public void sendCommit() throws AMQException, FailoverException + public void commitImpl() throws AMQException, FailoverException, TransportException { + // Acknowledge all delivered messages + while (true) + { + Long tag = _deliveredMessageTags.poll(); + if (tag == null) + { + break; + } + + acknowledgeMessage(tag, false); + } + final AMQProtocolHandler handler = getProtocolHandler(); handler.syncWrite(getProtocolHandler().getMethodRegistry().createTxCommitBody().generateFrame(_channelId), TxCommitOkBody.class); @@ -400,12 +427,12 @@ public final class AMQSession_0_8 extends AMQSession<BasicMessageConsumer_0_8, B public BasicMessageProducer_0_8 createMessageProducer(final Destination destination, final boolean mandatory, - final boolean immediate, final boolean waitUntilSent, long producerId) throws JMSException + final boolean immediate, long producerId) throws JMSException { try { return new BasicMessageProducer_0_8(_connection, (AMQDestination) destination, _transacted, _channelId, - this, getProtocolHandler(), producerId, immediate, mandatory, waitUntilSent); + this, getProtocolHandler(), producerId, immediate, mandatory); } catch (AMQException e) { @@ -577,6 +604,18 @@ public final class AMQSession_0_8 extends AMQSession<BasicMessageConsumer_0_8, B } + @Override + protected void deleteTemporaryDestination(final TemporaryDestination amqQueue) + throws JMSException + { + // Currently TemporaryDestination is set to be auto-delete which, for 0-8..0-9-1, means that the queue will be deleted + // by the server when there are no more subscriptions to that queue/topic (rather than when the client disconnects). + // This is not quite right for JMSCompliance as the queue/topic should remain until the connection closes, or the + // client explicitly deletes it. + + /* intentional no-op */ + } + public boolean isQueueBound(String exchangeName, String queueName, String bindingKey, Map<String, Object> args) throws JMSException { @@ -584,4 +623,34 @@ public final class AMQSession_0_8 extends AMQSession<BasicMessageConsumer_0_8, B queueName == null ? null : new AMQShortString(queueName), bindingKey == null ? null : new AMQShortString(bindingKey)); } + + + public AMQException getLastException() + { + // if the Connection has closed then we should throw any exception that + // has occurred that we were not waiting for + AMQStateManager manager = _connection.getProtocolHandler() + .getStateManager(); + + Exception e = manager.getLastException(); + if (manager.getCurrentState().equals(AMQState.CONNECTION_CLOSED) + && e != null) + { + if (e instanceof AMQException) + { + return (AMQException) e; + } + else + { + AMQException amqe = new AMQException(AMQConstant + .getConstant(AMQConstant.INTERNAL_ERROR.getCode()), + e.getMessage(), e.getCause()); + return amqe; + } + } + else + { + return null; + } + } } diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryQueue.java b/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryQueue.java index f54cb782c8..28f838057e 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryQueue.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryQueue.java @@ -20,14 +20,13 @@ */ package org.apache.qpid.client; +import java.util.UUID; + import javax.jms.JMSException; import javax.jms.TemporaryQueue; import org.apache.qpid.framing.AMQShortString; -import java.util.Random; -import java.util.UUID; - /** AMQ implementation of a TemporaryQueue. */ final class AMQTemporaryQueue extends AMQQueue implements TemporaryQueue, TemporaryDestination { @@ -50,11 +49,15 @@ final class AMQTemporaryQueue extends AMQQueue implements TemporaryQueue, Tempor { throw new JMSException("Temporary Queue has consumers so cannot be deleted"); } - _deleted = true; - // Currently TemporaryQueue is set to be auto-delete which means that the queue will be deleted - // by the server when there are no more subscriptions to that queue. This is probably not - // quite right for JMSCompliance. + try + { + _session.deleteTemporaryDestination(this); + } + finally + { + _deleted = true; + } } public AMQSession getSession() diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryTopic.java b/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryTopic.java index 7b5781530b..db54b320dc 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryTopic.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryTopic.java @@ -53,10 +53,14 @@ class AMQTemporaryTopic extends AMQTopic implements TemporaryTopic, TemporaryDes throw new JMSException("Temporary Topic has consumers so cannot be deleted"); } - _deleted = true; - // Currently TemporaryQueue is set to be auto-delete which means that the queue will be deleted - // by the server when there are no more subscriptions to that queue. This is probably not - // quite right for JMSCompliance. + try + { + _session.deleteTemporaryDestination(this); + } + finally + { + _deleted = true; + } } public AMQSession getSession() diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java b/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java index 6217cb534a..780dbcafc2 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java @@ -22,6 +22,7 @@ package org.apache.qpid.client; import java.net.URISyntaxException; +import javax.jms.InvalidDestinationException; import javax.jms.JMSException; import javax.jms.Topic; @@ -95,39 +96,47 @@ public class AMQTopic extends AMQDestination implements Topic super(exchangeName, exchangeClass, routingKey, isExclusive, isAutoDelete, queueName, isDurable,bindingKeys); } - public static AMQTopic createDurableTopic(AMQTopic topic, String subscriptionName, AMQConnection connection) + public static AMQTopic createDurableTopic(Topic topic, String subscriptionName, AMQConnection connection) throws JMSException { - if (topic.getDestSyntax() == DestSyntax.ADDR) + if (topic instanceof AMQDestination && topic instanceof javax.jms.Topic) { - try + AMQDestination qpidTopic = (AMQDestination)topic; + if (qpidTopic.getDestSyntax() == DestSyntax.ADDR) { - AMQTopic t = new AMQTopic(topic.getAddress()); - AMQShortString queueName = getDurableTopicQueueName(subscriptionName, connection); - // link is never null if dest was created using an address string. - t.getLink().setName(queueName.asString()); - t.getSourceNode().setAutoDelete(false); - t.getSourceNode().setDurable(true); - - // The legacy fields are also populated just in case. - t.setQueueName(queueName); - t.setAutoDelete(false); - t.setDurable(true); - return t; + try + { + AMQTopic t = new AMQTopic(qpidTopic.getAddress()); + AMQShortString queueName = getDurableTopicQueueName(subscriptionName, connection); + // link is never null if dest was created using an address string. + t.getLink().setName(queueName.asString()); + t.getSourceNode().setAutoDelete(false); + t.getSourceNode().setDurable(true); + + // The legacy fields are also populated just in case. + t.setQueueName(queueName); + t.setAutoDelete(false); + t.setDurable(true); + return t; + } + catch(Exception e) + { + JMSException ex = new JMSException("Error creating durable topic"); + ex.initCause(e); + ex.setLinkedException(e); + throw ex; + } } - catch(Exception e) + else { - JMSException ex = new JMSException("Error creating durable topic"); - ex.initCause(e); - ex.setLinkedException(e); - throw ex; + return new AMQTopic(qpidTopic.getExchangeName(), qpidTopic.getRoutingKey(), false, + getDurableTopicQueueName(subscriptionName, connection), + true); } } else { - return new AMQTopic(topic.getExchangeName(), topic.getRoutingKey(), false, - getDurableTopicQueueName(subscriptionName, connection), - true); + throw new InvalidDestinationException("The destination object used is not from this provider or of type javax.jms.Topic"); } } @@ -138,13 +147,17 @@ public class AMQTopic extends AMQDestination implements Topic public String getTopicName() throws JMSException { - if (super.getRoutingKey() == null && super.getSubject() != null) + if (getRoutingKey() != null) { - return super.getSubject(); + return getRoutingKey().asString(); + } + else if (getSubject() != null) + { + return getSubject(); } else { - return super.getRoutingKey().toString(); + return null; } } @@ -163,12 +176,18 @@ public class AMQTopic extends AMQDestination implements Topic public AMQShortString getRoutingKey() { - if (super.getRoutingKey() == null && super.getSubject() != null) + if (super.getRoutingKey() != null) + { + return super.getRoutingKey(); + } + else if (getSubject() != null) { - return new AMQShortString(super.getSubject()); + return new AMQShortString(getSubject()); } else { + setRoutingKey(new AMQShortString("")); + setSubject(""); return super.getRoutingKey(); } } 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 0a78403268..3b807591b0 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 @@ -27,6 +27,7 @@ import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.framing.*; import org.apache.qpid.jms.MessageConsumer; import org.apache.qpid.jms.Session; +import org.apache.qpid.transport.TransportException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,10 +37,7 @@ import javax.jms.MessageListener; import java.util.Arrays; import java.util.Iterator; import java.util.List; -import java.util.SortedSet; import java.util.ArrayList; -import java.util.Collections; -import java.util.TreeSet; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.LinkedBlockingQueue; @@ -117,29 +115,10 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa protected final int _acknowledgeMode; /** - * Number of messages unacknowledged in DUPS_OK_ACKNOWLEDGE mode - */ - private int _outstanding; - - /** - * Switch to enable sending of acknowledgements when using DUPS_OK_ACKNOWLEDGE mode. Enabled when _outstannding - * number of msgs >= _prefetchHigh and disabled at < _prefetchLow - */ - private boolean _dups_ok_acknowledge_send; - - /** * List of tags delievered, The last of which which should be acknowledged on commit in transaction mode. */ private ConcurrentLinkedQueue<Long> _receivedDeliveryTags = new ConcurrentLinkedQueue<Long>(); - /** The last tag that was "multiple" acknowledged on this session (if transacted) */ - private long _lastAcked; - - /** set of tags which have previously been acked; but not part of the multiple ack (transacted mode only) */ - private final SortedSet<Long> _previouslyAcked = new TreeSet<Long>(); - - private final Object _commitLock = new Object(); - /** * The thread that was used to call receive(). This is important for being able to interrupt that thread if a * receive() is in progress. @@ -289,17 +268,6 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa } } - protected void preApplicationProcessing(AbstractJMSMessage jmsMsg) throws JMSException - { - if (_session.getAcknowledgeMode() == Session.CLIENT_ACKNOWLEDGE) - { - _session.addUnacknowledgedMessage(jmsMsg.getDeliveryTag()); - } - - _session.setInRecovery(false); - preDeliver(jmsMsg); - } - /** * @param immediate if true then return immediately if the connection is failing over * @@ -322,14 +290,14 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa } } - if (!_receiving.compareAndSet(false, true)) + if (isMessageListenerSet()) { - throw new javax.jms.IllegalStateException("Another thread is already receiving."); + throw new javax.jms.IllegalStateException("A listener has already been set."); } - if (isMessageListenerSet()) + if (!_receiving.compareAndSet(false, true)) { - throw new javax.jms.IllegalStateException("A listener has already been set."); + throw new javax.jms.IllegalStateException("Another thread is already receiving."); } _receivingThread = Thread.currentThread(); @@ -408,7 +376,7 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa final AbstractJMSMessage m = returnMessageOrThrow(o); if (m != null) { - preApplicationProcessing(m); + preDeliver(m); postDeliver(m); } return m; @@ -419,6 +387,10 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa return null; } + catch(TransportException e) + { + throw _session.toJMSException("Exception while receiving:" + e.getMessage(), e); + } finally { releaseReceiving(); @@ -477,7 +449,7 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa final AbstractJMSMessage m = returnMessageOrThrow(o); if (m != null) { - preApplicationProcessing(m); + preDeliver(m); postDeliver(m); } @@ -489,6 +461,10 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa return null; } + catch(TransportException e) + { + throw _session.toJMSException("Exception while receiving:" + e.getMessage(), e); + } finally { releaseReceiving(); @@ -571,6 +547,7 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa if (!_session.isClosed() || _session.isClosing()) { sendCancel(); + cleanupQueue(); } } catch (AMQException e) @@ -581,6 +558,10 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa { throw new JMSAMQException("FailoverException interrupted basic cancel.", e); } + catch (TransportException e) + { + throw _session.toJMSException("Exception while closing consumer: " + e.getMessage(), e); + } } } else @@ -608,6 +589,8 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa } abstract void sendCancel() throws AMQException, FailoverException; + + abstract void cleanupQueue() throws AMQException, FailoverException; /** * Called when you need to invalidate a consumer. Used for example when failover has occurred and the client has @@ -718,7 +701,7 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa { if (isMessageListenerSet()) { - preApplicationProcessing(jmsMessage); + preDeliver(jmsMessage); getMessageListener().onMessage(jmsMessage); postDeliver(jmsMessage); } @@ -742,49 +725,42 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa } } - void preDeliver(AbstractJMSMessage msg) + protected void preDeliver(AbstractJMSMessage msg) { + _session.setInRecovery(false); + switch (_acknowledgeMode) { - case Session.PRE_ACKNOWLEDGE: _session.acknowledgeMessage(msg.getDeliveryTag(), false); break; - + case Session.AUTO_ACKNOWLEDGE: + //fall through + case Session.DUPS_OK_ACKNOWLEDGE: + _session.addUnacknowledgedMessage(msg.getDeliveryTag()); + break; case Session.CLIENT_ACKNOWLEDGE: // we set the session so that when the user calls acknowledge() it can call the method on session // to send out the appropriate frame msg.setAMQSession(_session); + _session.addUnacknowledgedMessage(msg.getDeliveryTag()); + _session.markDirty(); break; case Session.SESSION_TRANSACTED: - if (isNoConsume()) - { - _session.acknowledgeMessage(msg.getDeliveryTag(), false); - } - else - { - _session.addDeliveredMessage(msg.getDeliveryTag()); - _session.markDirty(); - } - + _session.addDeliveredMessage(msg.getDeliveryTag()); + _session.markDirty(); + break; + case Session.NO_ACKNOWLEDGE: + //do nothing. + //path used for NO-ACK consumers, and browsers (see constructor). break; } - } - void postDeliver(AbstractJMSMessage msg) throws JMSException + void postDeliver(AbstractJMSMessage msg) { switch (_acknowledgeMode) { - - case Session.CLIENT_ACKNOWLEDGE: - if (isNoConsume()) - { - _session.acknowledgeMessage(msg.getDeliveryTag(), false); - } - _session.markDirty(); - break; - case Session.DUPS_OK_ACKNOWLEDGE: case Session.AUTO_ACKNOWLEDGE: // we do not auto ack a message if the application code called recover() @@ -822,63 +798,6 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa return null; } - /** - * Acknowledge up to last message delivered (if any). Used when commiting. - */ - void acknowledgeDelivered() - { - synchronized(_commitLock) - { - ArrayList<Long> tagsToAck = new ArrayList<Long>(); - - while (!_receivedDeliveryTags.isEmpty()) - { - tagsToAck.add(_receivedDeliveryTags.poll()); - } - - Collections.sort(tagsToAck); - - long prevAcked = _lastAcked; - long oldAckPoint = -1; - - while(oldAckPoint != prevAcked) - { - oldAckPoint = prevAcked; - - Iterator<Long> tagsToAckIterator = tagsToAck.iterator(); - - while(tagsToAckIterator.hasNext() && tagsToAckIterator.next() == prevAcked+1) - { - tagsToAckIterator.remove(); - prevAcked++; - } - - Iterator<Long> previousAckIterator = _previouslyAcked.iterator(); - while(previousAckIterator.hasNext() && previousAckIterator.next() == prevAcked+1) - { - previousAckIterator.remove(); - prevAcked++; - } - - } - if(prevAcked != _lastAcked) - { - _session.acknowledgeMessage(prevAcked, true); - _lastAcked = prevAcked; - } - - Iterator<Long> tagsToAckIterator = tagsToAck.iterator(); - - while(tagsToAckIterator.hasNext()) - { - Long tag = tagsToAckIterator.next(); - _session.acknowledgeMessage(tag, false); - _previouslyAcked.add(tag); - } - } - } - - void notifyError(Throwable cause) { // synchronized (_closed) @@ -957,7 +876,7 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa public boolean isNoConsume() { - return _noConsume || _destination.isBrowseOnly() ; + return _noConsume; } public void rollback() 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 b5f3501e5a..548e274571 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 @@ -19,10 +19,11 @@ package org.apache.qpid.client; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.apache.qpid.client.AMQDestination.AddressOption; import org.apache.qpid.client.AMQDestination.DestSyntax; +import org.apache.qpid.client.failover.FailoverException; import org.apache.qpid.client.message.*; import org.apache.qpid.client.protocol.AMQProtocolHandler; -import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; import org.apache.qpid.AMQException; import org.apache.qpid.AMQInternalException; @@ -65,19 +66,13 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM private boolean _preAcquire = true; /** - * Indicate whether this consumer is started. - */ - private boolean _isStarted = false; - - /** * Specify whether this consumer is performing a sync receive */ private final AtomicBoolean _syncReceive = new AtomicBoolean(false); private String _consumerTagString; private long capacity = 0; - - //--- constructor + protected BasicMessageConsumer_0_10(int channelId, AMQConnection connection, AMQDestination destination, String messageSelector, boolean noLocal, MessageFactoryRegistry messageFactory, AMQSession session, AMQProtocolHandler protocolHandler, @@ -103,7 +98,6 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM _preAcquire = false; } } - _isStarted = connection.started(); // Destination setting overrides connection defaults if (destination.getDestSyntax() == DestSyntax.ADDR && @@ -156,13 +150,20 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM { if (isMessageListenerSet() && capacity == 0) { - _0_10session.getQpidSession().messageFlow(getConsumerTagString(), - MessageCreditUnit.MESSAGE, 1, - Option.UNRELIABLE); + messageFlow(); } _logger.debug("messageOk, trying to notify"); super.notifyMessage(jmsMessage); } + else + { + // if we are synchronously waiting for a message + // and messages are not pre-fetched we then need to request another one + if(capacity == 0) + { + messageFlow(); + } + } } catch (AMQException e) { @@ -171,8 +172,6 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM } } - //----- overwritten methods - /** * This method is invoked when this consumer is stopped. * It tells the broker to stop delivering messages to this consumer. @@ -202,11 +201,18 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM super.notifyMessage(messageFrame); } - @Override protected void preApplicationProcessing(AbstractJMSMessage jmsMsg) throws JMSException + @Override + protected void preDeliver(AbstractJMSMessage jmsMsg) { - super.preApplicationProcessing(jmsMsg); - if (!_session.getTransacted() && _session.getAcknowledgeMode() != org.apache.qpid.jms.Session.CLIENT_ACKNOWLEDGE) + super.preDeliver(jmsMsg); + + if (_acknowledgeMode == org.apache.qpid.jms.Session.NO_ACKNOWLEDGE) { + //For 0-10 we need to ensure that all messages are indicated processed in some way to + //ensure their AMQP command-id is marked completed, and so we must send a completion + //even for no-ack messages even though there isnt actually an 'acknowledgement' occurring. + //Add message to the unacked message list to ensure we dont lose record of it before + //sending a completion of some sort. _session.addUnacknowledgedMessage(jmsMsg.getDeliveryTag()); } } @@ -218,7 +224,6 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM return _messageFactory.createMessage(msg.getMessageTransfer()); } - // private methods /** * Check whether a message can be delivered to this consumer. * @@ -247,6 +252,7 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM _logger.debug("messageOk " + messageOk); _logger.debug("_preAcquire " + _preAcquire); } + if (!messageOk) { if (_preAcquire) @@ -263,19 +269,12 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM { if (_logger.isDebugEnabled()) { - _logger.debug("Message not OK, releasing"); + _logger.debug("filterMessage - not ack'ing message as not acquired"); } - releaseMessage(message); - } - // if we are syncrhonously waiting for a message - // and messages are not prefetched we then need to request another one - if(capacity == 0) - { - _0_10session.getQpidSession().messageFlow(getConsumerTagString(), - MessageCreditUnit.MESSAGE, 1, - Option.UNRELIABLE); + flushUnwantedMessage(message); } } + // now we need to acquire this message if needed // this is the case of queue with a message selector set if (!_preAcquire && messageOk && !isNoConsume()) @@ -287,6 +286,7 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM messageOk = acquireMessage(message); _logger.debug("filterMessage - message acquire status : " + messageOk); } + return messageOk; } @@ -297,38 +297,38 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM * @param message The message to be acknowledged * @throws AMQException If the message cannot be acquired due to some internal error. */ - private void acknowledgeMessage(AbstractJMSMessage message) throws AMQException + private void acknowledgeMessage(final AbstractJMSMessage message) throws AMQException { - if (!_preAcquire) - { - RangeSet ranges = new RangeSet(); - ranges.add((int) message.getDeliveryTag()); - _0_10session.messageAcknowledge - (ranges, - _acknowledgeMode != org.apache.qpid.jms.Session.NO_ACKNOWLEDGE); + final RangeSet ranges = new RangeSet(); + ranges.add((int) message.getDeliveryTag()); + _0_10session.messageAcknowledge + (ranges, + _acknowledgeMode != org.apache.qpid.jms.Session.NO_ACKNOWLEDGE); - AMQException amqe = _0_10session.getCurrentException(); - if (amqe != null) - { - throw amqe; - } + final AMQException amqe = _0_10session.getCurrentException(); + if (amqe != null) + { + throw amqe; } } /** - * Release a message + * Flush an unwanted message. For 0-10 we need to ensure that all messages are indicated + * processed to ensure their AMQP command-id is marked completed. * - * @param message The message to be released - * @throws AMQException If the message cannot be released due to some internal error. + * @param message The unwanted message to be flushed + * @throws AMQException If the unwanted message cannot be flushed due to some internal error. */ - private void releaseMessage(AbstractJMSMessage message) throws AMQException + private void flushUnwantedMessage(final AbstractJMSMessage message) throws AMQException { - if (_preAcquire) + final RangeSet ranges = new RangeSet(); + ranges.add((int) message.getDeliveryTag()); + _0_10session.flushProcessed(ranges,false); + + final AMQException amqe = _0_10session.getCurrentException(); + if (amqe != null) { - RangeSet ranges = new RangeSet(); - ranges.add((int) message.getDeliveryTag()); - _0_10session.getQpidSession().messageRelease(ranges); - _0_10session.sync(); + throw amqe; } } @@ -339,54 +339,60 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM * @return true if the message has been acquired, false otherwise. * @throws AMQException If the message cannot be acquired due to some internal error. */ - private boolean acquireMessage(AbstractJMSMessage message) throws AMQException + private boolean acquireMessage(final AbstractJMSMessage message) throws AMQException { boolean result = false; - if (!_preAcquire) - { - RangeSet ranges = new RangeSet(); - ranges.add((int) message.getDeliveryTag()); + final RangeSet ranges = new RangeSet(); + ranges.add((int) message.getDeliveryTag()); - Acquired acq = _0_10session.getQpidSession().messageAcquire(ranges).get(); + final Acquired acq = _0_10session.getQpidSession().messageAcquire(ranges).get(); - RangeSet acquired = acq.getTransfers(); - if (acquired != null && acquired.size() > 0) - { - result = true; - } + final RangeSet acquired = acq.getTransfers(); + if (acquired != null && acquired.size() > 0) + { + result = true; } return result; } + private void messageFlow() + { + _0_10session.getQpidSession().messageFlow(getConsumerTagString(), + MessageCreditUnit.MESSAGE, 1, + Option.UNRELIABLE); + } public void setMessageListener(final MessageListener messageListener) throws JMSException { super.setMessageListener(messageListener); - if (messageListener != null && capacity == 0) - { - _0_10session.getQpidSession().messageFlow(getConsumerTagString(), - MessageCreditUnit.MESSAGE, 1, - Option.UNRELIABLE); - } - if (messageListener != null && !_synchronousQueue.isEmpty()) + try { - Iterator messages=_synchronousQueue.iterator(); - while (messages.hasNext()) + if (messageListener != null && capacity == 0) + { + messageFlow(); + } + if (messageListener != null && !_synchronousQueue.isEmpty()) { - AbstractJMSMessage message=(AbstractJMSMessage) messages.next(); - messages.remove(); - _session.rejectMessage(message, true); + Iterator messages=_synchronousQueue.iterator(); + while (messages.hasNext()) + { + AbstractJMSMessage message=(AbstractJMSMessage) messages.next(); + messages.remove(); + _session.rejectMessage(message, true); + } } } + catch(TransportException e) + { + throw _session.toJMSException("Exception while setting message listener:"+ e.getMessage(), e); + } } public void failedOverPost() { if (_0_10session.isStarted() && _syncReceive.get()) { - _0_10session.getQpidSession().messageFlow - (getConsumerTagString(), MessageCreditUnit.MESSAGE, 1, - Option.UNRELIABLE); + messageFlow(); } } @@ -407,9 +413,7 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM } if (_0_10session.isStarted() && capacity == 0 && _synchronousQueue.isEmpty()) { - _0_10session.getQpidSession().messageFlow(getConsumerTagString(), - MessageCreditUnit.MESSAGE, 1, - Option.UNRELIABLE); + messageFlow(); } Object o = super.getMessageFromQueue(l); if (o == null && _0_10session.isStarted()) @@ -440,7 +444,7 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM return o; } - void postDeliver(AbstractJMSMessage msg) throws JMSException + void postDeliver(AbstractJMSMessage msg) { super.postDeliver(msg); if (_acknowledgeMode == org.apache.qpid.jms.Session.NO_ACKNOWLEDGE && !_session.isInRecovery()) @@ -449,10 +453,8 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM } if (_acknowledgeMode == org.apache.qpid.jms.Session.AUTO_ACKNOWLEDGE && - !_session.isInRecovery() && - _session.getAMQConnection().getSyncAck()) + !_session.isInRecovery() && _session.getAMQConnection().getSyncAck()) { - ((AMQSession_0_10) getSession()).flushAcknowledgments(); ((AMQSession_0_10) getSession()).getQpidSession().sync(); } } @@ -509,4 +511,18 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM return _exclusive; } } + + void cleanupQueue() throws AMQException, FailoverException + { + AMQDestination dest = this.getDestination(); + if (dest != null && dest.getDestSyntax() == AMQDestination.DestSyntax.ADDR) + { + if (dest.getDelete() == AddressOption.ALWAYS || + dest.getDelete() == AddressOption.RECEIVER ) + { + ((AMQSession_0_10) getSession()).getQpidSession().queueDelete( + this.getDestination().getQueueName()); + } + } + } } 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 cdbf57769d..00acd5e866 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 @@ -88,4 +88,8 @@ public class BasicMessageConsumer_0_8 extends BasicMessageConsumer<UnprocessedMe return receive(); } + void cleanupQueue() throws AMQException, FailoverException + { + + } } 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 8756ac4d05..bf4de782a5 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 @@ -39,6 +39,7 @@ import org.apache.qpid.client.message.AbstractJMSMessage; import org.apache.qpid.client.message.MessageConverter; import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.framing.ContentBody; +import org.apache.qpid.transport.TransportException; import org.apache.qpid.util.UUIDGen; import org.apache.qpid.util.UUIDs; import org.slf4j.Logger; @@ -113,8 +114,6 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac private final boolean _mandatory; - private final boolean _waitUntilSent; - private boolean _disableMessageId; private UUIDGen _messageIdGenerator = UUIDs.newGenerator(); @@ -126,8 +125,7 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac 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) throws AMQException + AMQSession session, AMQProtocolHandler protocolHandler, long producerId, boolean immediate, boolean mandatory) throws AMQException { _connection = connection; _destination = destination; @@ -143,7 +141,6 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac _immediate = immediate; _mandatory = mandatory; - _waitUntilSent = waitUntilSent; _userID = connection.getUsername(); setPublishMode(); } @@ -266,7 +263,7 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac return _destination; } - public void close() + public void close() throws JMSException { _closed.set(true); _session.deregisterProducer(_producerId); @@ -363,19 +360,6 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac } } - public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive, - boolean mandatory, boolean immediate, boolean waitUntilSent) throws JMSException - { - checkPreConditions(); - checkDestination(destination); - synchronized (_connection.getFailoverMutex()) - { - validateDestination(destination); - sendImpl((AMQDestination) destination, message, deliveryMode, priority, timeToLive, mandatory, immediate, - waitUntilSent); - } - } - private AbstractJMSMessage convertToNativeMessage(Message message) throws JMSException { if (message instanceof AbstractJMSMessage) @@ -450,12 +434,6 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac } } - protected void sendImpl(AMQDestination destination, Message message, int deliveryMode, int priority, long timeToLive, - boolean mandatory, boolean immediate) throws JMSException - { - sendImpl(destination, message, deliveryMode, priority, timeToLive, mandatory, immediate, _waitUntilSent); - } - /** * The caller of this method must hold the failover mutex. * @@ -470,23 +448,13 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac * @throws JMSException */ protected void sendImpl(AMQDestination destination, Message origMessage, int deliveryMode, int priority, long timeToLive, - boolean mandatory, boolean immediate, boolean wait) throws JMSException + boolean mandatory, boolean immediate) throws JMSException { checkTemporaryDestination(destination); origMessage.setJMSDestination(destination); AbstractJMSMessage message = convertToNativeMessage(origMessage); - if (_transacted) - { - if (_session.hasFailedOver() && _session.isDirty()) - { - throw new JMSAMQException("Failover has occurred and session is dirty so unable to send.", - new AMQSessionDirtyException("Failover has occurred and session is dirty " + - "so unable to send.")); - } - } - UUID messageId = null; if (_disableMessageId) { @@ -498,7 +466,14 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac message.setJMSMessageID(messageId); } - sendMessage(destination, origMessage, message, messageId, deliveryMode, priority, timeToLive, mandatory, immediate, wait); + try + { + sendMessage(destination, origMessage, message, messageId, deliveryMode, priority, timeToLive, mandatory, immediate); + } + catch (TransportException e) + { + throw getSession().toJMSException("Exception whilst sending:" + e.getMessage(), e); + } if (message != origMessage) { @@ -518,7 +493,7 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac abstract void sendMessage(AMQDestination destination, Message origMessage, AbstractJMSMessage message, UUID messageId, int deliveryMode, int priority, long timeToLive, boolean mandatory, - boolean immediate, boolean wait) throws JMSException; + boolean immediate) throws JMSException; private void checkTemporaryDestination(AMQDestination destination) throws JMSException { @@ -596,6 +571,13 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac public boolean isBound(AMQDestination destination) throws JMSException { - return _session.isQueueBound(destination.getExchangeName(), null, destination.getRoutingKey()); + try + { + return _session.isQueueBound(destination.getExchangeName(), null, destination.getRoutingKey()); + } + catch (TransportException e) + { + throw getSession().toJMSException("Exception whilst checking destination binding:" + e.getMessage(), e); + } } } 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 53c0457120..16afa51c74 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 @@ -19,6 +19,7 @@ package org.apache.qpid.client; import static org.apache.qpid.transport.Option.NONE; import static org.apache.qpid.transport.Option.SYNC; +import static org.apache.qpid.transport.Option.UNRELIABLE; import java.nio.ByteBuffer; import java.util.HashMap; @@ -30,9 +31,12 @@ import javax.jms.JMSException; import javax.jms.Message; import org.apache.qpid.AMQException; +import org.apache.qpid.client.AMQDestination.AddressOption; import org.apache.qpid.client.AMQDestination.DestSyntax; import org.apache.qpid.client.message.AMQMessageDelegate_0_10; import org.apache.qpid.client.message.AbstractJMSMessage; +import org.apache.qpid.client.message.QpidMessageProperties; +import org.apache.qpid.client.messaging.address.Link.Reliability; import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.transport.DeliveryProperties; import org.apache.qpid.transport.Header; @@ -42,6 +46,7 @@ import org.apache.qpid.transport.MessageDeliveryMode; import org.apache.qpid.transport.MessageDeliveryPriority; import org.apache.qpid.transport.MessageProperties; import org.apache.qpid.transport.Option; +import org.apache.qpid.transport.TransportException; import org.apache.qpid.util.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -56,10 +61,9 @@ public class BasicMessageProducer_0_10 extends BasicMessageProducer BasicMessageProducer_0_10(AMQConnection connection, AMQDestination destination, boolean transacted, int channelId, AMQSession session, AMQProtocolHandler protocolHandler, long producerId, - boolean immediate, boolean mandatory, boolean waitUntilSent) throws AMQException + boolean immediate, boolean mandatory) throws AMQException { - super(connection, destination, transacted, channelId, session, protocolHandler, producerId, immediate, - mandatory, waitUntilSent); + super(connection, destination, transacted, channelId, session, protocolHandler, producerId, immediate, mandatory); userIDBytes = Strings.toUTF8(_userID); } @@ -68,12 +72,15 @@ public class BasicMessageProducer_0_10 extends BasicMessageProducer { if (destination.getDestSyntax() == DestSyntax.BURL) { - String name = destination.getExchangeName().toString(); - ((AMQSession_0_10) getSession()).getQpidSession().exchangeDeclare - (name, - destination.getExchangeClass().toString(), - null, null, - name.startsWith("amq.") ? Option.PASSIVE : Option.NONE); + if (getSession().isDeclareExchanges()) + { + String name = destination.getExchangeName().toString(); + ((AMQSession_0_10) getSession()).getQpidSession().exchangeDeclare + (name, + destination.getExchangeClass().toString(), + null, null, + name.startsWith("amq.") ? Option.PASSIVE : Option.NONE); + } } else { @@ -96,7 +103,7 @@ public class BasicMessageProducer_0_10 extends BasicMessageProducer */ void sendMessage(AMQDestination destination, Message origMessage, AbstractJMSMessage message, UUID messageId, int deliveryMode, int priority, long timeToLive, boolean mandatory, - boolean immediate, boolean wait) throws JMSException + boolean immediate) throws JMSException { message.prepareForSending(); @@ -171,7 +178,7 @@ public class BasicMessageProducer_0_10 extends BasicMessageProducer if (destination.getDestSyntax() == AMQDestination.DestSyntax.ADDR && (destination.getSubject() != null || - (messageProps.getApplicationHeaders() != null && messageProps.getApplicationHeaders().get("qpid.subject") != null)) + (messageProps.getApplicationHeaders() != null && messageProps.getApplicationHeaders().get(QpidMessageProperties.QPID_SUBJECT) != null)) ) { Map<String,Object> appProps = messageProps.getApplicationHeaders(); @@ -181,20 +188,21 @@ public class BasicMessageProducer_0_10 extends BasicMessageProducer messageProps.setApplicationHeaders(appProps); } - if (appProps.get("qpid.subject") == null) + if (appProps.get(QpidMessageProperties.QPID_SUBJECT) == null) { // use default subject in address string - appProps.put("qpid.subject",destination.getSubject()); + appProps.put(QpidMessageProperties.QPID_SUBJECT,destination.getSubject()); } - if (destination.getTargetNode().getType() == AMQDestination.TOPIC_TYPE) + if (destination.getAddressType() == AMQDestination.TOPIC_TYPE) { deliveryProp.setRoutingKey((String) - messageProps.getApplicationHeaders().get("qpid.subject")); + messageProps.getApplicationHeaders().get(QpidMessageProperties.QPID_SUBJECT)); } } - - messageProps.setContentLength(message.getContentLength()); + + ByteBuffer data = message.getData(); + messageProps.setContentLength(data.remaining()); // send the message try @@ -210,14 +218,17 @@ public class BasicMessageProducer_0_10 extends BasicMessageProducer deliveryMode == DeliveryMode.PERSISTENT) ); - org.apache.mina.common.ByteBuffer data = message.getData(); - ByteBuffer buffer = data == null ? ByteBuffer.allocate(0) : data.buf().slice(); + boolean unreliable = (destination.getDestSyntax() == DestSyntax.ADDR) && + (destination.getLink().getReliability() == Reliability.UNRELIABLE); + + + ByteBuffer buffer = data == null ? ByteBuffer.allocate(0) : data.slice(); ssn.messageTransfer(destination.getExchangeName() == null ? "" : destination.getExchangeName().toString(), MessageAcceptMode.NONE, MessageAcquireMode.PRE_ACQUIRED, new Header(deliveryProp, messageProps), - buffer, sync ? SYNC : NONE); + buffer, sync ? SYNC : NONE, unreliable ? UNRELIABLE : NONE); if (sync) { ssn.sync(); @@ -227,17 +238,41 @@ public class BasicMessageProducer_0_10 extends BasicMessageProducer } catch (Exception e) { - JMSException jmse = new JMSException("Exception when sending message"); + JMSException jmse = new JMSException("Exception when sending message:" + e.getMessage()); jmse.setLinkedException(e); jmse.initCause(e); throw jmse; } } - + @Override public boolean isBound(AMQDestination destination) throws JMSException { return _session.isQueueBound(destination); } + + @Override + public void close() throws JMSException + { + super.close(); + AMQDestination dest = _destination; + if (dest != null && dest.getDestSyntax() == AMQDestination.DestSyntax.ADDR) + { + if (dest.getDelete() == AddressOption.ALWAYS || + dest.getDelete() == AddressOption.SENDER ) + { + try + { + ((AMQSession_0_10) getSession()).getQpidSession().queueDelete( + _destination.getQueueName()); + } + catch(TransportException e) + { + throw getSession().toJMSException("Exception while closing producer:" + e.getMessage(), e); + } + } + } + } + } 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 27f7486890..34d2ade723 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 @@ -27,14 +27,13 @@ import javax.jms.Message; import javax.jms.Topic; import javax.jms.Queue; -import org.apache.mina.common.ByteBuffer; +import java.nio.ByteBuffer; + import org.apache.qpid.AMQException; import org.apache.qpid.client.message.AbstractJMSMessage; -import org.apache.qpid.client.message.AMQMessageDelegate; import org.apache.qpid.client.message.AMQMessageDelegate_0_8; import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.framing.AMQFrame; -import org.apache.qpid.framing.BasicConsumeBody; import org.apache.qpid.framing.BasicContentHeaderProperties; import org.apache.qpid.framing.BasicPublishBody; import org.apache.qpid.framing.CompositeAMQDataBlock; @@ -46,10 +45,9 @@ public class BasicMessageProducer_0_8 extends BasicMessageProducer { BasicMessageProducer_0_8(AMQConnection connection, AMQDestination destination, boolean transacted, int channelId, - AMQSession session, AMQProtocolHandler protocolHandler, long producerId, boolean immediate, boolean mandatory, - boolean waitUntilSent) throws AMQException + AMQSession session, AMQProtocolHandler protocolHandler, long producerId, boolean immediate, boolean mandatory) throws AMQException { - super(connection, destination,transacted,channelId,session, protocolHandler, producerId, immediate, mandatory,waitUntilSent); + super(connection, destination,transacted,channelId,session, protocolHandler, producerId, immediate, mandatory); } void declareDestination(AMQDestination destination) @@ -74,7 +72,7 @@ public class BasicMessageProducer_0_8 extends BasicMessageProducer void sendMessage(AMQDestination destination, Message origMessage, AbstractJMSMessage message, UUID messageId, int deliveryMode,int priority, long timeToLive, boolean mandatory, - boolean immediate, boolean wait) throws JMSException + boolean immediate) throws JMSException { BasicPublishBody body = getSession().getMethodRegistry().createBasicPublishBody(_session.getTicket(), destination.getExchangeName(), @@ -169,7 +167,7 @@ public class BasicMessageProducer_0_8 extends BasicMessageProducer throw jmse; } - _protocolHandler.writeFrame(compositeFrame, wait); + _protocolHandler.writeFrame(compositeFrame); } /** @@ -186,7 +184,9 @@ public class BasicMessageProducer_0_8 extends BasicMessageProducer if (frames.length == (offset + 1)) { - frames[offset] = ContentBody.createAMQFrame(channelId, new ContentBody(payload)); + byte[] data = new byte[payload.remaining()]; + payload.get(data); + frames[offset] = ContentBody.createAMQFrame(channelId, new ContentBody(data)); } else { @@ -198,7 +198,10 @@ public class BasicMessageProducer_0_8 extends BasicMessageProducer payload.position((int) framePayloadMax * (i - offset)); int length = (remaining >= framePayloadMax) ? (int) framePayloadMax : (int) remaining; payload.limit(payload.position() + length); - frames[i] = ContentBody.createAMQFrame(channelId, new ContentBody(payload.slice())); + byte[] data = new byte[payload.remaining()]; + payload.get(data); + + frames[i] = ContentBody.createAMQFrame(channelId, new ContentBody(data)); remaining -= length; } diff --git a/java/client/src/main/java/org/apache/qpid/client/ChannelToSessionMap.java b/java/client/src/main/java/org/apache/qpid/client/ChannelToSessionMap.java index 2b7e3d44da..2fdb35de49 100644 --- a/java/client/src/main/java/org/apache/qpid/client/ChannelToSessionMap.java +++ b/java/client/src/main/java/org/apache/qpid/client/ChannelToSessionMap.java @@ -1,3 +1,23 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ package org.apache.qpid.client; import java.util.ArrayList; diff --git a/java/client/src/main/java/org/apache/qpid/client/CustomJMSXProperty.java b/java/client/src/main/java/org/apache/qpid/client/CustomJMSXProperty.java index 7cc548915c..e81e754da2 100644 --- a/java/client/src/main/java/org/apache/qpid/client/CustomJMSXProperty.java +++ b/java/client/src/main/java/org/apache/qpid/client/CustomJMSXProperty.java @@ -23,6 +23,7 @@ package org.apache.qpid.client; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; +import java.util.List; import org.apache.qpid.framing.AMQShortString; @@ -34,6 +35,18 @@ public enum CustomJMSXProperty JMSXGroupSeq, JMSXUserID; + private static List<String> _names; + + static + { + CustomJMSXProperty[] properties = values(); + _names = new ArrayList<String>(properties.length); + for(CustomJMSXProperty property : properties) + { + _names.add(property.toString()); + } + + } private final AMQShortString _nameAsShortString; @@ -47,20 +60,8 @@ public enum CustomJMSXProperty return _nameAsShortString; } - private static Enumeration _names; - - public static synchronized Enumeration asEnumeration() + public static Enumeration asEnumeration() { - if(_names == null) - { - CustomJMSXProperty[] properties = values(); - ArrayList<String> nameList = new ArrayList<String>(properties.length); - for(CustomJMSXProperty property : properties) - { - nameList.add(property.toString()); - } - _names = Collections.enumeration(nameList); - } - return _names; + return Collections.enumeration(_names); } } diff --git a/java/client/src/main/java/org/apache/qpid/client/QpidConnectionMetaData.java b/java/client/src/main/java/org/apache/qpid/client/QpidConnectionMetaData.java index 3bb5707417..5cf767ac35 100644 --- a/java/client/src/main/java/org/apache/qpid/client/QpidConnectionMetaData.java +++ b/java/client/src/main/java/org/apache/qpid/client/QpidConnectionMetaData.java @@ -30,9 +30,11 @@ import org.apache.qpid.common.QpidProperties; public class QpidConnectionMetaData implements ConnectionMetaData { + private AMQConnection con; QpidConnectionMetaData(AMQConnection conn) { + this.con = conn; } public int getJMSMajorVersion() throws JMSException @@ -62,12 +64,12 @@ public class QpidConnectionMetaData implements ConnectionMetaData public int getProviderMajorVersion() throws JMSException { - return 0; + return con.getProtocolVersion().getMajorVersion(); } public int getProviderMinorVersion() throws JMSException { - return 8; + return con.getProtocolVersion().getMinorVersion(); } public String getProviderVersion() throws JMSException @@ -78,8 +80,7 @@ public class QpidConnectionMetaData implements ConnectionMetaData private String getProtocolVersion() { - // TODO - Implement based on connection negotiated protocol - return "0.8"; + return con.getProtocolVersion().toString(); } public String getBrokerVersion() diff --git a/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java b/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java index 27783bcacf..295c6a4091 100644 --- a/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java +++ b/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java @@ -50,25 +50,25 @@ public class QueueSenderAdapter implements QueueSender public void send(Message msg) throws JMSException { - checkPreConditions(); + checkQueuePreConditions(_queue); _delegate.send(msg); } public void send(Queue queue, Message msg) throws JMSException { - checkPreConditions(queue); + checkQueuePreConditions(queue); _delegate.send(queue, msg); } public void publish(Message msg, int deliveryMode, int priority, long timeToLive) throws JMSException { - checkPreConditions(); + checkQueuePreConditions(_queue); _delegate.send(msg, deliveryMode, priority, timeToLive); } public void send(Queue queue, Message msg, int deliveryMode, int priority, long timeToLive) throws JMSException { - checkPreConditions(queue); + checkQueuePreConditions(queue); _delegate.send(queue, msg, deliveryMode, priority, timeToLive); } @@ -122,19 +122,19 @@ public class QueueSenderAdapter implements QueueSender public void send(Destination dest, Message msg) throws JMSException { - checkPreConditions((Queue) dest); + checkQueuePreConditions((Queue) dest); _delegate.send(dest, msg); } public void send(Message msg, int deliveryMode, int priority, long timeToLive) throws JMSException { - checkPreConditions(); + checkQueuePreConditions(_queue); _delegate.send(msg, deliveryMode, priority, timeToLive); } public void send(Destination dest, Message msg, int deliveryMode, int priority, long timeToLive) throws JMSException { - checkPreConditions((Queue) dest); + checkQueuePreConditions((Queue) dest); _delegate.send(dest, msg, deliveryMode, priority, timeToLive); } @@ -170,11 +170,6 @@ public class QueueSenderAdapter implements QueueSender private void checkPreConditions() throws JMSException { - checkPreConditions(_queue); - } - - private void checkPreConditions(Queue queue) throws JMSException - { if (closed) { throw new javax.jms.IllegalStateException("Publisher is closed"); @@ -186,39 +181,43 @@ public class QueueSenderAdapter implements QueueSender { throw new javax.jms.IllegalStateException("Invalid Session"); } + } - if (queue == null) - { - throw new UnsupportedOperationException("Queue is null."); - } - - if (!(queue instanceof AMQDestination)) - { - throw new InvalidDestinationException("Queue: " + queue + " is not a valid Qpid queue"); - } - - AMQDestination destination = (AMQDestination) queue; - if (!destination.isCheckedForQueueBinding() && checkQueueBeforePublish()) - { - - if (_delegate.getSession().isStrictAMQP()) - { - _delegate._logger.warn("AMQP does not support destination validation before publish, "); - destination.setCheckedForQueueBinding(true); - } - else - { - if (_delegate.isBound(destination)) - { - destination.setCheckedForQueueBinding(true); - } - else - { - throw new InvalidDestinationException("Queue: " + queue - + " is not a valid destination (no bindings on server"); - } - } - } + private void checkQueuePreConditions(Queue queue) throws JMSException + { + checkPreConditions() ; + + if (queue == null) + { + throw new UnsupportedOperationException("Queue is null."); + } + + if (!(queue instanceof AMQDestination)) + { + throw new InvalidDestinationException("Queue: " + queue + " is not a valid Qpid queue"); + } + + AMQDestination destination = (AMQDestination) queue; + if (!destination.isCheckedForQueueBinding() && checkQueueBeforePublish()) + { + if (_delegate.getSession().isStrictAMQP()) + { + _delegate._logger.warn("AMQP does not support destination validation before publish, "); + destination.setCheckedForQueueBinding(true); + } + else + { + if (_delegate.isBound(destination)) + { + destination.setCheckedForQueueBinding(true); + } + else + { + throw new InvalidDestinationException("Queue: " + queue + + " is not a valid destination (no bindings on server"); + } + } + } } private boolean checkQueueBeforePublish() diff --git a/java/client/src/main/java/org/apache/qpid/client/TemporaryDestination.java b/java/client/src/main/java/org/apache/qpid/client/TemporaryDestination.java index 7f8e80c73a..ca137f5a51 100644 --- a/java/client/src/main/java/org/apache/qpid/client/TemporaryDestination.java +++ b/java/client/src/main/java/org/apache/qpid/client/TemporaryDestination.java @@ -24,13 +24,16 @@ package org.apache.qpid.client; import javax.jms.Destination; import javax.jms.JMSException; +import org.apache.qpid.framing.AMQShortString; + /** - * Provides support for covenience interface implemented by both AMQTemporaryTopic and AMQTemporaryQueue + * Provides support for convenience interface implemented by both AMQTemporaryTopic and AMQTemporaryQueue * so that operations related to their "temporary-ness" can be abstracted out. */ interface TemporaryDestination extends Destination { + public AMQShortString getAMQQueueName(); public void delete() throws JMSException; public AMQSession getSession(); public boolean isDeleted(); 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 43025bd724..97048f39f4 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 @@ -31,9 +31,9 @@ public class XAConnectionImpl extends AMQConnection implements XAConnection, XAQ /** * Create a XAConnection from a connectionURL */ - public XAConnectionImpl(ConnectionURL connectionURL, SSLConfiguration sslConfig) throws AMQException + public XAConnectionImpl(ConnectionURL connectionURL) throws AMQException { - super(connectionURL, sslConfig); + super(connectionURL); } //-- interface XAConnection 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 8a75082202..5b94b342eb 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 @@ -21,10 +21,14 @@ import javax.transaction.xa.XAException; import javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; -import org.apache.qpid.AMQInvalidArgumentException; import org.apache.qpid.dtx.XidImpl; -import org.apache.qpid.transport.*; - +import org.apache.qpid.transport.DtxXaStatus; +import org.apache.qpid.transport.ExecutionErrorCode; +import org.apache.qpid.transport.Future; +import org.apache.qpid.transport.Option; +import org.apache.qpid.transport.RecoverResult; +import org.apache.qpid.transport.SessionException; +import org.apache.qpid.transport.XaResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -211,9 +215,28 @@ public class XAResourceImpl implements XAResource * @throws XAException An error has occurred. Possible exception values are XAER_RMERR, XAER_RMFAIL. */ public boolean isSameRM(XAResource xaResource) throws XAException - { - // TODO : get the server identity of xaResource and compare it with our own one - return false; + { + if(this == xaResource) + { + return true; + } + if(!(xaResource instanceof XAResourceImpl)) + { + return false; + } + + XAResourceImpl other = (XAResourceImpl)xaResource; + + String myUUID = ((AMQSession_0_10)_xaSession).getAMQConnection().getBrokerUUID(); + String otherUUID = ((AMQSession_0_10)other._xaSession).getAMQConnection().getBrokerUUID(); + + if(_logger.isDebugEnabled()) + { + _logger.debug("Comparing my UUID " + myUUID + " with other UUID " + otherUUID); + } + + return (myUUID != null && otherUUID != null && myUUID.equals(otherUUID)); + } /** 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 354b67cd35..6b9121811d 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 @@ -52,7 +52,7 @@ public class XASessionImpl extends AMQSession_0_10 implements XASession, XATopic { super(qpidConnection, con, channelId, false, // this is not a transacted session Session.AUTO_ACKNOWLEDGE, // the ack mode is transacted - MessageFactoryRegistry.newDefaultRegistry(), defaultPrefetchHigh, defaultPrefetchLow); + MessageFactoryRegistry.newDefaultRegistry(), defaultPrefetchHigh, defaultPrefetchLow,null); createSession(); _xaResource = new XAResourceImpl(this); } 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 e9e52cc97c..28d19ce817 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 @@ -59,8 +59,8 @@ import org.slf4j.LoggerFactory; * <tr><td> Automatically retry the continuation accross fail-overs until it succeeds, or raises an exception. * </table> * - * @todo Another continuation. Could use an interface Continuation (as described in other todos, for example, see - * {@link org.apache.qpid.pool.Job}). Then have a wrapping continuation (this), which blocks on an arbitrary + * @todo Another continuation. Could use an interface Continuation (as described in other todos) + * Then have a wrapping continuation (this), which blocks on an arbitrary * Condition or Latch (specified in constructor call), that this blocks on before calling the wrapped Continuation. * Must work on Java 1.4, so check retrotranslator works on Lock/Condition or latch first. Argument and return type * to match wrapped condition as type parameters. Rename to AsyncConditionalContinuation or something like that. 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 2cf19bf391..b9d4d6fa95 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 @@ -78,7 +78,7 @@ public class ChannelCloseMethodHandler implements StateAwareMethodListener<Chann { throw new AMQNoRouteException("Error: " + reason, null, null); } - else if (errorCode == AMQConstant.INVALID_ARGUMENT) + else if (errorCode == AMQConstant.ARGUMENT_INVALID) { _logger.debug("Broker responded with Invalid Argument."); 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 c81ad6422f..939bd181a3 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 @@ -20,6 +20,13 @@ */ package org.apache.qpid.client.handler; +import java.io.UnsupportedEncodingException; +import java.util.StringTokenizer; + +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; + import org.apache.qpid.AMQException; import org.apache.qpid.client.protocol.AMQProtocolSession; import org.apache.qpid.client.security.AMQCallbackHandler; @@ -34,18 +41,9 @@ import org.apache.qpid.framing.ConnectionStartOkBody; import org.apache.qpid.framing.FieldTable; import org.apache.qpid.framing.FieldTableFactory; import org.apache.qpid.framing.ProtocolVersion; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.security.sasl.Sasl; -import javax.security.sasl.SaslClient; -import javax.security.sasl.SaslException; - -import java.io.UnsupportedEncodingException; -import java.util.HashSet; -import java.util.StringTokenizer; - public class ConnectionStartMethodHandler implements StateAwareMethodListener<ConnectionStartBody> { private static final Logger _log = LoggerFactory.getLogger(ConnectionStartMethodHandler.class); @@ -197,40 +195,20 @@ public class ConnectionStartMethodHandler implements StateAwareMethodListener<Co private String chooseMechanism(byte[] availableMechanisms) throws UnsupportedEncodingException { final String mechanisms = new String(availableMechanisms, "utf8"); - StringTokenizer tokenizer = new StringTokenizer(mechanisms, " "); - HashSet mechanismSet = new HashSet(); - while (tokenizer.hasMoreTokens()) - { - mechanismSet.add(tokenizer.nextToken()); - } - - String preferredMechanisms = CallbackHandlerRegistry.getInstance().getMechanisms(); - StringTokenizer prefTokenizer = new StringTokenizer(preferredMechanisms, " "); - while (prefTokenizer.hasMoreTokens()) - { - String mech = prefTokenizer.nextToken(); - if (mechanismSet.contains(mech)) - { - return mech; - } - } - - return null; + return CallbackHandlerRegistry.getInstance().selectMechanism(mechanisms); } private AMQCallbackHandler createCallbackHandler(String mechanism, AMQProtocolSession protocolSession) throws AMQException { - Class mechanismClass = CallbackHandlerRegistry.getInstance().getCallbackHandlerClass(mechanism); try { - Object instance = mechanismClass.newInstance(); - AMQCallbackHandler cbh = (AMQCallbackHandler) instance; - cbh.initialise(protocolSession); + AMQCallbackHandler instance = CallbackHandlerRegistry.getInstance().createCallbackHandler(mechanism); + instance.initialise(protocolSession.getAMQConnection().getConnectionURL()); - return cbh; + return instance; } - catch (Exception e) + catch (IllegalArgumentException e) { throw new AMQException(null, "Unable to create callback handler: " + e, e); } 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 c2821591d8..a9434edf49 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 @@ -26,9 +26,7 @@ import org.apache.qpid.client.AMQSession; import javax.jms.Destination; import javax.jms.JMSException; -import java.nio.ByteBuffer; import java.util.Enumeration; -import java.util.Map; import java.util.UUID; public interface AMQMessageDelegate @@ -130,9 +128,9 @@ public interface AMQMessageDelegate void removeProperty(final String propertyName) throws JMSException; - void setAMQSession(final AMQSession s); + void setAMQSession(final AMQSession<?,?> s); - AMQSession getAMQSession(); + AMQSession<?,?> getAMQSession(); long getDeliveryTag(); diff --git a/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegateFactory.java b/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegateFactory.java index 8c3f2fd08f..e5b95f54f4 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegateFactory.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegateFactory.java @@ -21,11 +21,6 @@ package org.apache.qpid.client.message; -import org.apache.mina.common.ByteBuffer; -import org.apache.qpid.framing.BasicContentHeaderProperties; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.AMQException; - public interface AMQMessageDelegateFactory<D extends AMQMessageDelegate> { public static AMQMessageDelegateFactory DEFAULT_FACTORY = null; 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 92e61984d2..f360b546b2 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 @@ -22,10 +22,12 @@ package org.apache.qpid.client.message; import java.lang.ref.SoftReference; +import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; @@ -35,12 +37,10 @@ import javax.jms.Destination; import javax.jms.JMSException; import javax.jms.MessageFormatException; import javax.jms.MessageNotWriteableException; -import javax.jms.Session; import org.apache.qpid.AMQException; import org.apache.qpid.AMQPInvalidClassException; import org.apache.qpid.client.AMQDestination; -import org.apache.qpid.client.AMQSession; import org.apache.qpid.client.AMQSession_0_10; import org.apache.qpid.client.CustomJMSXProperty; import org.apache.qpid.framing.AMQShortString; @@ -53,6 +53,9 @@ import org.apache.qpid.transport.MessageDeliveryMode; import org.apache.qpid.transport.MessageDeliveryPriority; import org.apache.qpid.transport.MessageProperties; import org.apache.qpid.transport.ReplyTo; +import org.apache.qpid.transport.TransportException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * This extends AbstractAMQMessageDelegate which contains common code between @@ -61,6 +64,7 @@ import org.apache.qpid.transport.ReplyTo; */ public class AMQMessageDelegate_0_10 extends AbstractAMQMessageDelegate { + private static final Logger _logger = LoggerFactory.getLogger(AMQMessageDelegate_0_10.class); private static final Map<ReplyTo, SoftReference<Destination>> _destinationCache = Collections.synchronizedMap(new HashMap<ReplyTo, SoftReference<Destination>>()); public static final String JMS_TYPE = "x-jms-type"; @@ -70,13 +74,8 @@ public class AMQMessageDelegate_0_10 extends AbstractAMQMessageDelegate private Destination _destination; - private MessageProperties _messageProps; private DeliveryProperties _deliveryProps; - /** If the acknowledge mode is CLIENT_ACKNOWLEDGE the session is required */ - private AMQSession _session; - private final long _deliveryTag; - protected AMQMessageDelegate_0_10() { @@ -86,15 +85,29 @@ public class AMQMessageDelegate_0_10 extends AbstractAMQMessageDelegate protected AMQMessageDelegate_0_10(MessageProperties messageProps, DeliveryProperties deliveryProps, long deliveryTag) { + super(deliveryTag); _messageProps = messageProps; _deliveryProps = deliveryProps; - _deliveryTag = deliveryTag; _readableProperties = (_messageProps != null); AMQDestination dest; - dest = generateDestination(new AMQShortString(_deliveryProps.getExchange()), + if (AMQDestination.getDefaultDestSyntax() == AMQDestination.DestSyntax.BURL) + { + dest = generateDestination(new AMQShortString(_deliveryProps.getExchange()), new AMQShortString(_deliveryProps.getRoutingKey())); + } + else + { + String subject = null; + if (messageProps != null && messageProps.getApplicationHeaders() != null) + { + subject = (String)messageProps.getApplicationHeaders().get(QpidMessageProperties.QPID_SUBJECT); + } + dest = (AMQDestination) convertToAddressBasedDestination(_deliveryProps.getExchange(), + _deliveryProps.getRoutingKey(), subject); + } + setJMSDestination(dest); } @@ -185,7 +198,6 @@ public class AMQMessageDelegate_0_10 extends AbstractAMQMessageDelegate } } - public long getJMSTimestamp() throws JMSException { return _deliveryProps.getTimestamp(); @@ -240,13 +252,50 @@ public class AMQMessageDelegate_0_10 extends AbstractAMQMessageDelegate String exchange = replyTo.getExchange(); String routingKey = replyTo.getRoutingKey(); - dest = generateDestination(new AMQShortString(exchange), new AMQShortString(routingKey)); + if (AMQDestination.getDefaultDestSyntax() == AMQDestination.DestSyntax.BURL) + { + + dest = generateDestination(new AMQShortString(exchange), new AMQShortString(routingKey)); + } + else + { + dest = convertToAddressBasedDestination(exchange,routingKey,null); + } _destinationCache.put(replyTo, new SoftReference<Destination>(dest)); } return dest; } } + + private Destination convertToAddressBasedDestination(String exchange, String routingKey, String subject) + { + String addr; + if ("".equals(exchange)) // type Queue + { + subject = (subject == null) ? "" : "/" + subject; + addr = routingKey + subject; + } + else + { + addr = exchange + "/" + routingKey; + } + + try + { + return AMQDestination.createDestination("ADDR:" + addr); + } + catch(Exception e) + { + // An exception is only thrown here if the address syntax is invalid. + // Logging the exception, but not throwing as this is only important to Qpid developers. + // An exception here means a bug in the code. + _logger.error("Exception when constructing an address string from the ReplyTo struct"); + + // falling back to the old way of doing it to ensure the application continues. + return generateDestination(new AMQShortString(exchange), new AMQShortString(routingKey)); + } + } public void setJMSReplyTo(Destination destination) throws JMSException { @@ -268,14 +317,14 @@ public class AMQMessageDelegate_0_10 extends AbstractAMQMessageDelegate { try { - int type = ((AMQSession_0_10)_session).resolveAddressType(amqd); + int type = ((AMQSession_0_10)getAMQSession()).resolveAddressType(amqd); if (type == AMQDestination.QUEUE_TYPE) { - ((AMQSession_0_10)_session).setLegacyFiledsForQueueType(amqd); + ((AMQSession_0_10)getAMQSession()).setLegacyFiledsForQueueType(amqd); } else { - ((AMQSession_0_10)_session).setLegacyFiledsForTopicType(amqd); + ((AMQSession_0_10)getAMQSession()).setLegacyFiledsForTopicType(amqd); } } catch(AMQException ex) @@ -285,6 +334,14 @@ public class AMQMessageDelegate_0_10 extends AbstractAMQMessageDelegate e.setLinkedException(ex); throw e; } + catch (TransportException e) + { + JMSException jmse = new JMSException("Exception occured while figuring out the node type:" + e.getMessage()); + jmse.initCause(e); + jmse.setLinkedException(e); + throw jmse; + } + } final ReplyTo replyTo = new ReplyTo(amqd.getExchangeName().toString(), amqd.getRoutingKey().toString()); @@ -335,7 +392,7 @@ public class AMQMessageDelegate_0_10 extends AbstractAMQMessageDelegate Destination replyTo = getJMSReplyTo(); if(replyTo != null) { - return ((AMQDestination)replyTo).toURL(); + return ((AMQDestination)replyTo).toString(); } else { @@ -632,6 +689,16 @@ public class AMQMessageDelegate_0_10 extends AbstractAMQMessageDelegate { return new String(_messageProps.getUserId()); } + else if (QpidMessageProperties.AMQP_0_10_APP_ID.equals(propertyName) && + _messageProps.getAppId() != null) + { + return new String(_messageProps.getAppId()); + } + else if (QpidMessageProperties.AMQP_0_10_ROUTING_KEY.equals(propertyName) && + _deliveryProps.getRoutingKey() != null) + { + return _deliveryProps.getRoutingKey(); + } else { checkPropertyName(propertyName); @@ -670,7 +737,19 @@ public class AMQMessageDelegate_0_10 extends AbstractAMQMessageDelegate public Enumeration getPropertyNames() throws JMSException { - return java.util.Collections.enumeration(getApplicationHeaders().keySet()); + List<String> props = new ArrayList<String>(); + Map<String, Object> propertyMap = getApplicationHeaders(); + for (String prop: getApplicationHeaders().keySet()) + { + Object value = propertyMap.get(prop); + if (value instanceof Boolean || value instanceof Number + || value instanceof String) + { + props.add(prop); + } + } + + return java.util.Collections.enumeration(props); } public void setBooleanProperty(String propertyName, boolean b) throws JMSException @@ -726,7 +805,14 @@ public class AMQMessageDelegate_0_10 extends AbstractAMQMessageDelegate { checkPropertyName(propertyName); checkWritableProperties(); - setApplicationHeader(propertyName, value); + if (QpidMessageProperties.AMQP_0_10_APP_ID.equals(propertyName)) + { + _messageProps.setAppId(value.getBytes()); + } + else + { + setApplicationHeader(propertyName, value); + } } private static final Set<Class> ALLOWED = new HashSet(); @@ -811,64 +897,6 @@ public class AMQMessageDelegate_0_10 extends AbstractAMQMessageDelegate _readableProperties = false; } - - public void acknowledgeThis() throws JMSException - { - // the JMS 1.1 spec says in section 3.6 that calls to acknowledge are ignored when client acknowledge - // is not specified. In our case, we only set the session field where client acknowledge mode is specified. - if (_session != null && _session.getAcknowledgeMode() == Session.CLIENT_ACKNOWLEDGE) - { - if (_session.getAMQConnection().isClosed()) - { - throw new javax.jms.IllegalStateException("Connection is already closed"); - } - - // we set multiple to true here since acknowledgment implies acknowledge of all previous messages - // received on the session - _session.acknowledgeMessage(_deliveryTag, true); - } - } - - public void acknowledge() throws JMSException - { - if (_session != null && _session.getAcknowledgeMode() == Session.CLIENT_ACKNOWLEDGE) - { - _session.acknowledge(); - } - } - - - /** - * The session is set when CLIENT_ACKNOWLEDGE mode is used so that the CHANNEL ACK can be sent when the user calls - * acknowledge() - * - * @param s the AMQ session that delivered this message - */ - public void setAMQSession(AMQSession s) - { - _session = s; - } - - public AMQSession getAMQSession() - { - return _session; - } - - /** - * Get the AMQ message number assigned to this message - * - * @return the message number - */ - public long getDeliveryTag() - { - return _deliveryTag; - } - - - - - - protected void checkPropertyName(CharSequence propertyName) { if (propertyName == null) 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 cec4268a7b..9ab03412fe 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 @@ -30,7 +30,6 @@ import java.util.UUID; import javax.jms.Destination; import javax.jms.JMSException; import javax.jms.MessageNotWriteableException; -import javax.jms.Session; import org.apache.qpid.client.AMQDestination; import org.apache.qpid.client.AMQQueue; @@ -60,15 +59,12 @@ public class AMQMessageDelegate_0_8 extends AbstractAMQMessageDelegate Boolean.parseBoolean(System.getProperties().getProperty(AMQSession.STRICT_AMQP, AMQSession.STRICT_AMQP_DEFAULT)); private ContentHeaderProperties _contentHeaderProperties; - /** If the acknowledge mode is CLIENT_ACKNOWLEDGE the session is required */ - 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) { + super(deliveryTag); _contentHeaderProperties = properties; - _deliveryTag = deliveryTag; _readableProperties = (_contentHeaderProperties != null); _headerAdapter = new JMSHeaderAdapter(_readableProperties ? ((BasicContentHeaderProperties) _contentHeaderProperties).getHeaders() : (new BasicContentHeaderProperties()).getHeaders() ); @@ -499,7 +495,6 @@ public class AMQMessageDelegate_0_8 extends AbstractAMQMessageDelegate { throw new MessageNotWriteableException("You need to call clearProperties() to make the message writable"); } - _contentHeaderProperties.updated(); } @@ -519,58 +514,4 @@ public class AMQMessageDelegate_0_8 extends AbstractAMQMessageDelegate _readableProperties = false; } - - - public void acknowledgeThis() throws JMSException - { - // the JMS 1.1 spec says in section 3.6 that calls to acknowledge are ignored when client acknowledge - // is not specified. In our case, we only set the session field where client acknowledge mode is specified. - if (_session != null && _session.getAcknowledgeMode() == Session.CLIENT_ACKNOWLEDGE) - { - if (_session.getAMQConnection().isClosed()) - { - throw new javax.jms.IllegalStateException("Connection is already closed"); - } - - // we set multiple to true here since acknowledgement implies acknowledge of all previous messages - // received on the session - _session.acknowledgeMessage(_deliveryTag, true); - } - } - - public void acknowledge() throws JMSException - { - if (_session != null && _session.getAcknowledgeMode() == Session.CLIENT_ACKNOWLEDGE) - { - _session.acknowledge(); - } - } - - - /** - * The session is set when CLIENT_ACKNOWLEDGE mode is used so that the CHANNEL ACK can be sent when the user calls - * acknowledge() - * - * @param s the AMQ session that delivered this message - */ - public void setAMQSession(AMQSession s) - { - _session = s; - } - - public AMQSession getAMQSession() - { - return _session; - } - - /** - * Get the AMQ message number assigned to this message - * - * @return the message number - */ - public long getDeliveryTag() - { - return _deliveryTag; - } - } diff --git a/java/client/src/main/java/org/apache/qpid/client/message/AMQPEncodedMapMessage.java b/java/client/src/main/java/org/apache/qpid/client/message/AMQPEncodedMapMessage.java index 6e22292ee0..be71c8c657 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/AMQPEncodedMapMessage.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/AMQPEncodedMapMessage.java @@ -23,11 +23,12 @@ package org.apache.qpid.client.message; import java.util.List; import java.util.Map; +import java.util.UUID; +import java.nio.ByteBuffer; import javax.jms.JMSException; import javax.jms.MessageFormatException; -import org.apache.mina.common.ByteBuffer; import org.apache.qpid.AMQException; import org.apache.qpid.transport.codec.BBDecoder; import org.apache.qpid.transport.codec.BBEncoder; @@ -65,7 +66,7 @@ public class AMQPEncodedMapMessage extends JMSMapMessage if ((value instanceof Boolean) || (value instanceof Byte) || (value instanceof Short) || (value instanceof Integer) || (value instanceof Long) || (value instanceof Character) || (value instanceof Float) || (value instanceof Double) || (value instanceof String) || (value instanceof byte[]) - || (value instanceof List) || (value instanceof Map) || (value == null)) + || (value instanceof List) || (value instanceof Map) || (value instanceof UUID) || (value == null)) { _map.put(propName, value); } @@ -80,18 +81,19 @@ public class AMQPEncodedMapMessage extends JMSMapMessage @ Override public ByteBuffer getData() { - writeMapToData(); - return _data; + BBEncoder encoder = new BBEncoder(1024); + encoder.writeMap(_map); + return encoder.segment(); } @ Override - protected void populateMapFromData() throws JMSException + protected void populateMapFromData(ByteBuffer data) throws JMSException { - if (_data != null) + if (data != null) { - _data.rewind(); + data.rewind(); BBDecoder decoder = new BBDecoder(); - decoder.init(_data.buf()); + decoder.init(data); _map = decoder.readMap(); } else @@ -100,16 +102,8 @@ public class AMQPEncodedMapMessage extends JMSMapMessage } } - @ Override - protected void writeMapToData() - { - BBEncoder encoder = new BBEncoder(1024); - encoder.writeMap(_map); - _data = ByteBuffer.wrap(encoder.segment()); - } - // for testing - Map<String,Object> getMap() + public Map<String,Object> getMap() { return _map; } diff --git a/java/client/src/main/java/org/apache/qpid/client/message/AMQPEncodedMapMessageFactory.java b/java/client/src/main/java/org/apache/qpid/client/message/AMQPEncodedMapMessageFactory.java index 4978d1ce85..2c38f153cb 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/AMQPEncodedMapMessageFactory.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/AMQPEncodedMapMessageFactory.java @@ -1,6 +1,6 @@ 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 @@ -8,22 +8,23 @@ package org.apache.qpid.client.message; * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT 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 javax.jms.JMSException; -import org.apache.mina.common.ByteBuffer; +import java.nio.ByteBuffer; + import org.apache.qpid.AMQException; public class AMQPEncodedMapMessageFactory extends AbstractJMSMessageFactory @@ -36,7 +37,7 @@ public class AMQPEncodedMapMessageFactory extends AbstractJMSMessageFactory return new AMQPEncodedMapMessage(delegate,data); } - @Override + public AbstractJMSMessage createMessage( AMQMessageDelegateFactory delegateFactory) throws JMSException { 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 index 89fbc9722c..1b6c0c751d 100644 --- 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 @@ -23,9 +23,13 @@ package org.apache.qpid.client.message; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import javax.jms.JMSException; +import javax.jms.Session; + import org.apache.qpid.client.AMQAnyDestination; import org.apache.qpid.client.AMQDestination; import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQSession; import org.apache.qpid.client.AMQTopic; import org.apache.qpid.exchange.ExchangeDefaults; import org.apache.qpid.framing.AMQShortString; @@ -78,7 +82,25 @@ public abstract class AbstractAMQMessageDelegate implements AMQMessageDelegate new ExchangeInfo(ExchangeDefaults.HEADERS_EXCHANGE_NAME.toString(), ExchangeDefaults.HEADERS_EXCHANGE_CLASS.toString(), AMQDestination.QUEUE_TYPE)); - + } + + /** If the acknowledge mode is CLIENT_ACKNOWLEDGE the session is required */ + private AMQSession<?,?> _session; + private final long _deliveryTag; + + protected AbstractAMQMessageDelegate(long deliveryTag) + { + _deliveryTag = deliveryTag; + } + + /** + * Get the AMQ message number assigned to this message + * + * @return the message number + */ + public long getDeliveryTag() + { + return _deliveryTag; } /** @@ -157,6 +179,47 @@ public abstract class AbstractAMQMessageDelegate implements AMQMessageDelegate { return _exchangeMap.containsKey(exchange); } + + public void acknowledgeThis() throws JMSException + { + // the JMS 1.1 spec says in section 3.6 that calls to acknowledge are ignored when client acknowledge + // is not specified. In our case, we only set the session field where client acknowledge mode is specified. + if (_session != null && _session.getAcknowledgeMode() == Session.CLIENT_ACKNOWLEDGE) + { + if (_session.getAMQConnection().isClosed()) + { + throw new javax.jms.IllegalStateException("Connection is already closed"); + } + + // we set multiple to true here since acknowledgement implies acknowledge of all previous messages + // received on the session + _session.acknowledgeMessage(getDeliveryTag(), true); + } + } + + public void acknowledge() throws JMSException + { + if (_session != null && _session.getAcknowledgeMode() == Session.CLIENT_ACKNOWLEDGE) + { + _session.acknowledge(); + } + } + + /** + * The session is set when CLIENT_ACKNOWLEDGE mode is used so that the CHANNEL ACK can be sent when the user calls + * acknowledge() + * + * @param s the AMQ session that delivered this message + */ + public void setAMQSession(AMQSession<?,?> s) + { + _session = s; + } + + public AMQSession<?,?> getAMQSession() + { + return _session; + } } class ExchangeInfo @@ -202,5 +265,5 @@ class ExchangeInfo public void setDestType(int destType) { this.destType = destType; - } + } } 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 deleted file mode 100644 index 3846ee043d..0000000000 --- a/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesMessage.java +++ /dev/null @@ -1,124 +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.client.message; - -import java.io.IOException; -import java.nio.charset.Charset; - -import javax.jms.JMSException; -import javax.jms.MessageEOFException; - -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 - */ -public abstract class AbstractBytesMessage extends AbstractJMSMessage -{ - - /** - * The default initial size of the buffer. The buffer expands automatically. - */ - private static final int DEFAULT_BUFFER_INITIAL_SIZE = 1024; - - AbstractBytesMessage(AMQMessageDelegateFactory delegateFactory) - { - this(delegateFactory, null); - } - - /** - * Construct a bytes message with existing data. - * - * @param delegateFactory - * @param data the data that comprises this message. If data is null, you get a 1024 byte buffer that is - */ - AbstractBytesMessage(AMQMessageDelegateFactory delegateFactory, ByteBuffer data) - { - super(delegateFactory, data); // this instanties a content header - setContentType(getMimeType()); - - if (_data == null) - { - allocateInitialBuffer(); - } - } - - protected void allocateInitialBuffer() - { - _data = ByteBuffer.allocate(DEFAULT_BUFFER_INITIAL_SIZE); - _data.setAutoExpand(true); - } - - AbstractBytesMessage(AMQMessageDelegate delegate, ByteBuffer data) throws AMQException - { - super(delegate, data); - setContentType(getMimeType()); - } - - - public void clearBodyImpl() throws JMSException - { - allocateInitialBuffer(); - } - - public String toBodyString() throws JMSException - { - try - { - if (_data != null) - { - return Functions.str(_data.buf(), 100,0); - } - else - { - return ""; - } - - } - catch (Exception e) - { - JMSException jmse = new JMSException(e.toString()); - jmse.setLinkedException(e); - jmse.initCause(e); - throw jmse; - } - - } - - /** - * Check that there is at least a certain number of bytes available to read - * - * @param len the number of bytes - * @throws javax.jms.MessageEOFException if there are less than len bytes available to read - */ - protected void checkAvailable(int len) throws MessageEOFException - { - if (_data.remaining() < len) - { - throw new MessageEOFException("Unable to read " + len + " bytes"); - } - } -} diff --git a/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesTypedMessage.java b/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesTypedMessage.java index 85818dcd2b..ddeb62fbf6 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesTypedMessage.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesTypedMessage.java @@ -21,784 +21,96 @@ package org.apache.qpid.client.message; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; +import java.nio.ByteBuffer; import javax.jms.JMSException; -import javax.jms.MessageEOFException; -import javax.jms.MessageFormatException; import javax.jms.MessageNotReadableException; import javax.jms.MessageNotWriteableException; -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 */ -public abstract class AbstractBytesTypedMessage extends AbstractBytesMessage +public abstract class AbstractBytesTypedMessage extends AbstractJMSMessage { + protected boolean _readableMessage = false; - protected static final byte BOOLEAN_TYPE = (byte) 1; - - protected static final byte BYTE_TYPE = (byte) 2; - - protected static final byte BYTEARRAY_TYPE = (byte) 3; - - protected static final byte SHORT_TYPE = (byte) 4; - - protected static final byte CHAR_TYPE = (byte) 5; - - protected static final byte INT_TYPE = (byte) 6; - - protected static final byte LONG_TYPE = (byte) 7; - - protected static final byte FLOAT_TYPE = (byte) 8; - - protected static final byte DOUBLE_TYPE = (byte) 9; - - protected static final byte STRING_TYPE = (byte) 10; - - protected static final byte NULL_STRING_TYPE = (byte) 11; - - /** - * This is set when reading a byte array. The readBytes(byte[]) method supports multiple calls to read - * a byte array in multiple chunks, hence this is used to track how much is left to be read - */ - private int _byteArrayRemaining = -1; - - AbstractBytesTypedMessage(AMQMessageDelegateFactory delegateFactory) - { - - this(delegateFactory, null); - } - - /** - * Construct a stream message with existing data. - * - * @param delegateFactory - * @param data the data that comprises this message. If data is null, you get a 1024 byte buffer that is - */ - AbstractBytesTypedMessage(AMQMessageDelegateFactory delegateFactory, ByteBuffer data) + AbstractBytesTypedMessage(AMQMessageDelegateFactory delegateFactory, boolean fromReceivedMessage) { - super(delegateFactory, data); // this instanties a content header + super(delegateFactory, fromReceivedMessage); // this instanties a content header + _readableMessage = fromReceivedMessage; } - AbstractBytesTypedMessage(AMQMessageDelegate delegate, ByteBuffer data) throws AMQException + AbstractBytesTypedMessage(AMQMessageDelegate delegate, boolean fromReceivedMessage) throws AMQException { - super(delegate, data); - } + super(delegate, fromReceivedMessage); + _readableMessage = fromReceivedMessage; - - protected byte readWireType() throws MessageFormatException, MessageEOFException, - MessageNotReadableException - { - checkReadable(); - checkAvailable(1); - return _data.get(); } - protected void writeTypeDiscriminator(byte type) throws MessageNotWriteableException + protected void checkReadable() throws MessageNotReadableException { - checkWritable(); - _data.put(type); - _changedData = true; - } - - protected boolean readBoolean() throws JMSException - { - int position = _data.position(); - byte wireType = readWireType(); - boolean result; - try + if (!_readableMessage) { - switch (wireType) - { - case BOOLEAN_TYPE: - checkAvailable(1); - result = readBooleanImpl(); - break; - case STRING_TYPE: - checkAvailable(1); - result = Boolean.parseBoolean(readStringImpl()); - break; - default: - _data.position(position); - throw new MessageFormatException("Unable to convert " + wireType + " to a boolean"); - } - return result; - } - catch (RuntimeException e) - { - _data.position(position); - throw e; + throw new MessageNotReadableException("You need to call reset() to make the message readable"); } } - private boolean readBooleanImpl() + @Override + protected void checkWritable() throws MessageNotWriteableException { - return _data.get() != 0; - } - - protected byte readByte() throws JMSException - { - int position = _data.position(); - byte wireType = readWireType(); - byte result; - try + super.checkWritable(); + if(_readableMessage) { - switch (wireType) - { - case BYTE_TYPE: - checkAvailable(1); - result = readByteImpl(); - break; - case STRING_TYPE: - checkAvailable(1); - result = Byte.parseByte(readStringImpl()); - break; - default: - _data.position(position); - throw new MessageFormatException("Unable to convert " + wireType + " to a byte"); - } + throw new MessageNotWriteableException("You need to call clearBody() to make the message writable"); } - catch (RuntimeException e) - { - _data.position(position); - throw e; - } - return result; } - private byte readByteImpl() + public void clearBody() throws JMSException { - return _data.get(); + super.clearBody(); + _readableMessage = false; } - protected short readShort() throws JMSException - { - int position = _data.position(); - byte wireType = readWireType(); - short result; - try - { - switch (wireType) - { - case SHORT_TYPE: - checkAvailable(2); - result = readShortImpl(); - break; - case STRING_TYPE: - checkAvailable(1); - result = Short.parseShort(readStringImpl()); - break; - case BYTE_TYPE: - checkAvailable(1); - result = readByteImpl(); - break; - default: - _data.position(position); - throw new MessageFormatException("Unable to convert " + wireType + " to a short"); - } - } - catch (RuntimeException e) - { - _data.position(position); - throw e; - } - return result; - } - private short readShortImpl() + public String toBodyString() throws JMSException { - return _data.getShort(); - } - - /** - * Note that this method reads a unicode character as two bytes from the stream - * - * @return the character read from the stream - * @throws javax.jms.JMSException - */ - protected char readChar() throws JMSException - { - int position = _data.position(); - byte wireType = readWireType(); try { - if(wireType == NULL_STRING_TYPE){ - throw new NullPointerException(); + ByteBuffer data = getData(); + if (data != null) + { + return Functions.str(data, 100, 0); + } + else + { + return ""; } - if (wireType != CHAR_TYPE) - { - _data.position(position); - throw new MessageFormatException("Unable to convert " + wireType + " to a char"); - } - else - { - checkAvailable(2); - return readCharImpl(); - } - } - catch (RuntimeException e) - { - _data.position(position); - throw e; - } - } - - private char readCharImpl() - { - return _data.getChar(); - } - - protected int readInt() throws JMSException - { - int position = _data.position(); - byte wireType = readWireType(); - int result; - try - { - switch (wireType) - { - case INT_TYPE: - checkAvailable(4); - result = readIntImpl(); - break; - case SHORT_TYPE: - checkAvailable(2); - result = readShortImpl(); - break; - case STRING_TYPE: - checkAvailable(1); - result = Integer.parseInt(readStringImpl()); - break; - case BYTE_TYPE: - checkAvailable(1); - result = readByteImpl(); - break; - default: - _data.position(position); - throw new MessageFormatException("Unable to convert " + wireType + " to an int"); - } - return result; - } - catch (RuntimeException e) - { - _data.position(position); - throw e; - } - } - - protected int readIntImpl() - { - return _data.getInt(); - } - - protected long readLong() throws JMSException - { - int position = _data.position(); - byte wireType = readWireType(); - long result; - try - { - switch (wireType) - { - case LONG_TYPE: - checkAvailable(8); - result = readLongImpl(); - break; - case INT_TYPE: - checkAvailable(4); - result = readIntImpl(); - break; - case SHORT_TYPE: - checkAvailable(2); - result = readShortImpl(); - break; - case STRING_TYPE: - checkAvailable(1); - result = Long.parseLong(readStringImpl()); - break; - case BYTE_TYPE: - checkAvailable(1); - result = readByteImpl(); - break; - default: - _data.position(position); - throw new MessageFormatException("Unable to convert " + wireType + " to a long"); - } - return result; - } - catch (RuntimeException e) - { - _data.position(position); - throw e; - } - } - - private long readLongImpl() - { - return _data.getLong(); - } - - protected float readFloat() throws JMSException - { - int position = _data.position(); - byte wireType = readWireType(); - float result; - try - { - switch (wireType) - { - case FLOAT_TYPE: - checkAvailable(4); - result = readFloatImpl(); - break; - case STRING_TYPE: - checkAvailable(1); - result = Float.parseFloat(readStringImpl()); - break; - default: - _data.position(position); - throw new MessageFormatException("Unable to convert " + wireType + " to a float"); - } - return result; - } - catch (RuntimeException e) - { - _data.position(position); - throw e; - } - } - - private float readFloatImpl() - { - return _data.getFloat(); - } - - protected double readDouble() throws JMSException - { - int position = _data.position(); - byte wireType = readWireType(); - double result; - try - { - switch (wireType) - { - case DOUBLE_TYPE: - checkAvailable(8); - result = readDoubleImpl(); - break; - case FLOAT_TYPE: - checkAvailable(4); - result = readFloatImpl(); - break; - case STRING_TYPE: - checkAvailable(1); - result = Double.parseDouble(readStringImpl()); - break; - default: - _data.position(position); - throw new MessageFormatException("Unable to convert " + wireType + " to a double"); - } - return result; - } - catch (RuntimeException e) - { - _data.position(position); - throw e; - } - } - - private double readDoubleImpl() - { - return _data.getDouble(); - } - - protected String readString() throws JMSException - { - int position = _data.position(); - byte wireType = readWireType(); - String result; - try - { - switch (wireType) - { - case STRING_TYPE: - checkAvailable(1); - result = readStringImpl(); - break; - case NULL_STRING_TYPE: - result = null; - throw new NullPointerException("data is null"); - case BOOLEAN_TYPE: - checkAvailable(1); - result = String.valueOf(readBooleanImpl()); - break; - case LONG_TYPE: - checkAvailable(8); - result = String.valueOf(readLongImpl()); - break; - case INT_TYPE: - checkAvailable(4); - result = String.valueOf(readIntImpl()); - break; - case SHORT_TYPE: - checkAvailable(2); - result = String.valueOf(readShortImpl()); - break; - case BYTE_TYPE: - checkAvailable(1); - result = String.valueOf(readByteImpl()); - break; - case FLOAT_TYPE: - checkAvailable(4); - result = String.valueOf(readFloatImpl()); - break; - case DOUBLE_TYPE: - checkAvailable(8); - result = String.valueOf(readDoubleImpl()); - break; - case CHAR_TYPE: - checkAvailable(2); - result = String.valueOf(readCharImpl()); - break; - default: - _data.position(position); - throw new MessageFormatException("Unable to convert " + wireType + " to a String"); - } - return result; - } - catch (RuntimeException e) - { - _data.position(position); - throw e; - } - } - - protected String readStringImpl() throws JMSException - { - try - { - return _data.getString(Charset.forName("UTF-8").newDecoder()); } - catch (CharacterCodingException e) + catch (Exception e) { - JMSException jmse = new JMSException("Error decoding byte stream as a UTF8 string: " + e); + JMSException jmse = new JMSException(e.toString()); jmse.setLinkedException(e); jmse.initCause(e); throw jmse; } - } - - protected int readBytes(byte[] bytes) throws JMSException - { - if (bytes == null) - { - throw new IllegalArgumentException("byte array must not be null"); - } - checkReadable(); - // first call - if (_byteArrayRemaining == -1) - { - // type discriminator checked separately so you get a MessageFormatException rather than - // an EOF even in the case where both would be applicable - checkAvailable(1); - byte wireType = readWireType(); - if (wireType != BYTEARRAY_TYPE) - { - throw new MessageFormatException("Unable to convert " + wireType + " to a byte array"); - } - checkAvailable(4); - int size = _data.getInt(); - // length of -1 indicates null - if (size == -1) - { - return -1; - } - else - { - if (size > _data.remaining()) - { - throw new MessageEOFException("Byte array has stated length " + size + " but message only contains " + - _data.remaining() + " bytes"); - } - else - { - _byteArrayRemaining = size; - } - } - } - else if (_byteArrayRemaining == 0) - { - _byteArrayRemaining = -1; - return -1; - } - - int returnedSize = readBytesImpl(bytes); - if (returnedSize < bytes.length) - { - _byteArrayRemaining = -1; - } - return returnedSize; - } - - private int readBytesImpl(byte[] bytes) - { - int count = (_byteArrayRemaining >= bytes.length ? bytes.length : _byteArrayRemaining); - _byteArrayRemaining -= count; - - if (count == 0) - { - return 0; - } - else - { - _data.get(bytes, 0, count); - return count; - } - } - - protected Object readObject() throws JMSException - { - int position = _data.position(); - byte wireType = readWireType(); - Object result = null; - try - { - switch (wireType) - { - case BOOLEAN_TYPE: - checkAvailable(1); - result = readBooleanImpl(); - break; - case BYTE_TYPE: - checkAvailable(1); - result = readByteImpl(); - break; - case BYTEARRAY_TYPE: - checkAvailable(4); - int size = _data.getInt(); - if (size == -1) - { - result = null; - } - else - { - _byteArrayRemaining = size; - byte[] bytesResult = new byte[size]; - readBytesImpl(bytesResult); - result = bytesResult; - } - break; - case SHORT_TYPE: - checkAvailable(2); - result = readShortImpl(); - break; - case CHAR_TYPE: - checkAvailable(2); - result = readCharImpl(); - break; - case INT_TYPE: - checkAvailable(4); - result = readIntImpl(); - break; - case LONG_TYPE: - checkAvailable(8); - result = readLongImpl(); - break; - case FLOAT_TYPE: - checkAvailable(4); - result = readFloatImpl(); - break; - case DOUBLE_TYPE: - checkAvailable(8); - result = readDoubleImpl(); - break; - case NULL_STRING_TYPE: - result = null; - break; - case STRING_TYPE: - checkAvailable(1); - result = readStringImpl(); - break; - } - return result; - } - catch (RuntimeException e) - { - _data.position(position); - throw e; - } - } - - protected void writeBoolean(boolean b) throws JMSException - { - writeTypeDiscriminator(BOOLEAN_TYPE); - _data.put(b ? (byte) 1 : (byte) 0); - } - - protected void writeByte(byte b) throws JMSException - { - writeTypeDiscriminator(BYTE_TYPE); - _data.put(b); - } - - protected void writeShort(short i) throws JMSException - { - writeTypeDiscriminator(SHORT_TYPE); - _data.putShort(i); - } - - protected void writeChar(char c) throws JMSException - { - writeTypeDiscriminator(CHAR_TYPE); - _data.putChar(c); - } - - protected void writeInt(int i) throws JMSException - { - writeTypeDiscriminator(INT_TYPE); - writeIntImpl(i); - } - - protected void writeIntImpl(int i) - { - _data.putInt(i); - } - - protected void writeLong(long l) throws JMSException - { - writeTypeDiscriminator(LONG_TYPE); - _data.putLong(l); - } - protected void writeFloat(float v) throws JMSException - { - writeTypeDiscriminator(FLOAT_TYPE); - _data.putFloat(v); } - protected void writeDouble(double v) throws JMSException - { - writeTypeDiscriminator(DOUBLE_TYPE); - _data.putDouble(v); - } - protected void writeString(String string) throws JMSException - { - if (string == null) - { - writeTypeDiscriminator(NULL_STRING_TYPE); - } - else - { - writeTypeDiscriminator(STRING_TYPE); - try - { - writeStringImpl(string); - } - catch (CharacterCodingException e) - { - JMSException jmse = new JMSException("Unable to encode string: " + e); - jmse.setLinkedException(e); - jmse.initCause(e); - throw jmse; - } - } - } - - protected void writeStringImpl(String string) - throws CharacterCodingException - { - _data.putString(string, Charset.forName("UTF-8").newEncoder()); - // we must write the null terminator ourselves - _data.put((byte) 0); - } + abstract public void reset(); - protected void writeBytes(byte[] bytes) throws JMSException - { - writeBytes(bytes, 0, bytes == null ? 0 : bytes.length); - } - protected void writeBytes(byte[] bytes, int offset, int length) throws JMSException - { - writeTypeDiscriminator(BYTEARRAY_TYPE); - if (bytes == null) - { - _data.putInt(-1); - } - else - { - _data.putInt(length); - _data.put(bytes, offset, length); - } - } - protected void writeObject(Object object) throws JMSException - { - checkWritable(); - Class clazz; - if (object == null) - { - // string handles the output of null values - clazz = String.class; - } - else - { - clazz = object.getClass(); - } - - if (clazz == Byte.class) - { - writeByte((Byte) object); - } - else if (clazz == Boolean.class) - { - writeBoolean((Boolean) object); - } - else if (clazz == byte[].class) - { - writeBytes((byte[]) object); - } - else if (clazz == Short.class) - { - writeShort((Short) object); - } - else if (clazz == Character.class) - { - writeChar((Character) object); - } - else if (clazz == Integer.class) - { - writeInt((Integer) object); - } - else if (clazz == Long.class) - { - writeLong((Long) object); - } - else if (clazz == Float.class) - { - writeFloat((Float) object); - } - else if (clazz == Double.class) - { - writeDouble((Double) object); - } - else if (clazz == String.class) - { - writeString((String) object); - } - else - { - throw new MessageFormatException("Only primitives plus byte arrays and String are valid types"); - } - } } 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 6ba55b207a..f713554bfb 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 @@ -20,66 +20,38 @@ */ package org.apache.qpid.client.message; -import java.io.IOException; +import java.nio.ByteBuffer; import java.util.Enumeration; import java.util.UUID; import javax.jms.Destination; import javax.jms.JMSException; -import javax.jms.MessageNotReadableException; import javax.jms.MessageNotWriteableException; -import org.apache.mina.common.ByteBuffer; import org.apache.qpid.AMQException; import org.apache.qpid.client.AMQSession; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.BasicContentHeaderProperties; public abstract class AbstractJMSMessage implements org.apache.qpid.jms.Message { - - protected ByteBuffer _data; - protected boolean _readableMessage = false; - protected boolean _changedData = true; - /** If the acknowledge mode is CLIENT_ACKNOWLEDGE the session is required */ - - - protected AMQMessageDelegate _delegate; private boolean _redelivered; + private boolean _receivedFromServer; - protected AbstractJMSMessage(AMQMessageDelegateFactory delegateFactory, ByteBuffer data) + protected AbstractJMSMessage(AMQMessageDelegateFactory delegateFactory, boolean fromReceivedData) { _delegate = delegateFactory.createDelegate(); - _data = data; - if (_data != null) - { - _data.acquire(); - } - - - _readableMessage = (data != null); - _changedData = (data == null); - + setContentType(getMimeType()); } - protected AbstractJMSMessage(AMQMessageDelegate delegate, ByteBuffer data) throws AMQException + protected AbstractJMSMessage(AMQMessageDelegate delegate, boolean fromReceivedData) throws AMQException { _delegate = delegate; - - _data = data; - if (_data != null) - { - _data.acquire(); - } - - _readableMessage = data != null; - + setContentType(getMimeType()); } public String getJMSMessageID() throws JMSException @@ -329,12 +301,9 @@ public abstract class AbstractJMSMessage implements org.apache.qpid.jms.Message public void clearBody() throws JMSException { - clearBodyImpl(); - _readableMessage = false; - + _receivedFromServer = false; } - public void acknowledgeThis() throws JMSException { _delegate.acknowledgeThis(); @@ -345,14 +314,7 @@ public abstract class AbstractJMSMessage implements org.apache.qpid.jms.Message _delegate.acknowledge(); } - /** - * This forces concrete classes to implement clearBody() - * - * @throws JMSException - */ - public abstract void clearBodyImpl() throws JMSException; - - /** + /* * Get a String representation of the body of the message. Used in the toString() method which outputs this before * message properties. */ @@ -413,63 +375,24 @@ public abstract class AbstractJMSMessage implements org.apache.qpid.jms.Message return _delegate; } - public ByteBuffer getData() - { - // make sure we rewind the data just in case any method has moved the - // position beyond the start - if (_data != null) - { - reset(); - } + abstract public ByteBuffer getData() throws JMSException; - return _data; - } - - protected void checkReadable() throws MessageNotReadableException - { - if (!_readableMessage) - { - throw new MessageNotReadableException("You need to call reset() to make the message readable"); - } - } protected void checkWritable() throws MessageNotWriteableException { - if (_readableMessage) + if (_receivedFromServer) { throw new MessageNotWriteableException("You need to call clearBody() to make the message writable"); } } - public void reset() - { - if (!_changedData) - { - _data.rewind(); - } - else - { - _data.flip(); - _changedData = false; - } - } - public int getContentLength() + public void setReceivedFromServer() { - if(_data != null) - { - return _data.remaining(); - } - else - { - return 0; - } + _receivedFromServer = true; } - public void receivedFromServer() - { - _changedData = false; - } + /** * The session is set when CLIENT_ACKNOWLEDGE mode is used so that the CHANNEL ACK can be sent when the user calls 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 e719c9a4b2..967a1fb49f 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 @@ -20,8 +20,6 @@ */ package org.apache.qpid.client.message; -import org.apache.mina.common.ByteBuffer; - import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.ContentBody; @@ -38,6 +36,8 @@ import javax.jms.JMSException; import java.util.Iterator; import java.util.List; +import java.nio.ByteBuffer; + public abstract class AbstractJMSMessageFactory implements MessageFactory { private static final Logger _logger = LoggerFactory.getLogger(AbstractJMSMessageFactory.class); @@ -57,7 +57,7 @@ public abstract class AbstractJMSMessageFactory implements MessageFactory _logger.debug("Non-fragmented message body (bodySize=" + contentHeader.bodySize + ")"); } - data = ((ContentBody) bodies.get(0)).payload; + data = ByteBuffer.wrap(((ContentBody) bodies.get(0))._payload); } else if (bodies != null) { @@ -72,7 +72,7 @@ public abstract class AbstractJMSMessageFactory implements MessageFactory while (it.hasNext()) { ContentBody cb = (ContentBody) it.next(); - final ByteBuffer payload = cb.payload; + final ByteBuffer payload = ByteBuffer.wrap(cb._payload); if(payload.isDirect() || payload.isReadOnly()) { data.put(payload); @@ -82,7 +82,6 @@ public abstract class AbstractJMSMessageFactory implements MessageFactory data.put(payload.array(), payload.arrayOffset(), payload.limit()); } - payload.release(); } data.flip(); @@ -99,7 +98,7 @@ public abstract class AbstractJMSMessageFactory implements MessageFactory } AMQMessageDelegate delegate = new AMQMessageDelegate_0_8(messageNbr, - (BasicContentHeaderProperties) contentHeader.properties, + (BasicContentHeaderProperties) contentHeader.getProperties(), exchange, routingKey); return createMessage(delegate, data); @@ -109,7 +108,7 @@ public abstract class AbstractJMSMessageFactory implements MessageFactory protected AbstractJMSMessage create010MessageWithBody(long messageNbr, MessageProperties msgProps, - DeliveryProperties deliveryProps, + DeliveryProperties deliveryProps, java.nio.ByteBuffer body) throws AMQException { ByteBuffer data; @@ -118,7 +117,7 @@ public abstract class AbstractJMSMessageFactory implements MessageFactory if (body != null) { - data = ByteBuffer.wrap(body); + data = body; } else // body == null { @@ -155,7 +154,7 @@ public abstract class AbstractJMSMessageFactory implements MessageFactory { final AbstractJMSMessage msg = create08MessageWithBody(messageNbr, contentHeader, exchange, routingKey, bodies); msg.setJMSRedelivered(redelivered); - msg.receivedFromServer(); + msg.setReceivedFromServer(); return msg; } @@ -166,7 +165,7 @@ public abstract class AbstractJMSMessageFactory implements MessageFactory final AbstractJMSMessage msg = create010MessageWithBody(messageNbr,msgProps,deliveryProps, body); msg.setJMSRedelivered(redelivered); - msg.receivedFromServer(); + msg.setReceivedFromServer(); return msg; } 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 b87275a9ce..e252bdb719 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 @@ -20,6 +20,7 @@ */ package org.apache.qpid.client.message; +import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; @@ -28,47 +29,56 @@ import java.nio.charset.CharsetEncoder; import javax.jms.BytesMessage; import javax.jms.JMSException; +import javax.jms.MessageEOFException; import javax.jms.MessageFormatException; -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 JMSBytesMessage extends AbstractBytesMessage implements BytesMessage +public class JMSBytesMessage extends AbstractBytesTypedMessage implements BytesMessage { public static final String MIME_TYPE = "application/octet-stream"; + private TypedBytesContentReader _typedBytesContentReader; + private TypedBytesContentWriter _typedBytesContentWriter; - public JMSBytesMessage(AMQMessageDelegateFactory delegateFactory) - { - this(delegateFactory,null); - } - - /** - * Construct a bytes message with existing data. - * - * @param delegateFactory - * @param data the data that comprises this message. If data is null, you get a 1024 byte buffer that is - */ - JMSBytesMessage(AMQMessageDelegateFactory delegateFactory, ByteBuffer data) + public JMSBytesMessage(AMQMessageDelegateFactory delegateFactory) { - - super(delegateFactory, data); // this instanties a content header + super(delegateFactory,false); + _typedBytesContentWriter = new TypedBytesContentWriter(); } JMSBytesMessage(AMQMessageDelegate delegate, ByteBuffer data) throws AMQException { - super(delegate, data); + super(delegate, data!=null); + _typedBytesContentReader = new TypedBytesContentReader(data); } public void reset() { - super.reset(); _readableMessage = true; + + if(_typedBytesContentReader != null) + { + _typedBytesContentReader.reset(); + } + else if (_typedBytesContentWriter != null) + { + _typedBytesContentReader = new TypedBytesContentReader(_typedBytesContentWriter.getData()); + } + } + + @Override + public void clearBody() throws JMSException + { + super.clearBody(); + _typedBytesContentReader = null; + _typedBytesContentWriter = new TypedBytesContentWriter(); + } protected String getMimeType() @@ -76,45 +86,57 @@ public class JMSBytesMessage extends AbstractBytesMessage implements BytesMessag return MIME_TYPE; } + @Override + public java.nio.ByteBuffer getData() throws JMSException + { + return _typedBytesContentWriter == null ? _typedBytesContentReader.getData() : _typedBytesContentWriter.getData(); + } + public long getBodyLength() throws JMSException { checkReadable(); - return _data.limit(); + return _typedBytesContentReader.size(); } public boolean readBoolean() throws JMSException { checkReadable(); checkAvailable(1); - return _data.get() != 0; + + return _typedBytesContentReader.readBooleanImpl(); + } + + private void checkAvailable(final int i) throws MessageEOFException + { + _typedBytesContentReader.checkAvailable(1); } public byte readByte() throws JMSException { checkReadable(); checkAvailable(1); - return _data.get(); + return _typedBytesContentReader.readByteImpl(); } public int readUnsignedByte() throws JMSException { checkReadable(); checkAvailable(1); - return _data.getUnsigned(); + return _typedBytesContentReader.readByteImpl() & 0xFF; } public short readShort() throws JMSException { checkReadable(); checkAvailable(2); - return _data.getShort(); + return _typedBytesContentReader.readShortImpl(); } public int readUnsignedShort() throws JMSException { checkReadable(); checkAvailable(2); - return _data.getUnsignedShort(); + return _typedBytesContentReader.readShortImpl() & 0xFFFF; } /** @@ -127,35 +149,35 @@ public class JMSBytesMessage extends AbstractBytesMessage implements BytesMessag { checkReadable(); checkAvailable(2); - return _data.getChar(); + return _typedBytesContentReader.readCharImpl(); } public int readInt() throws JMSException { checkReadable(); checkAvailable(4); - return _data.getInt(); + return _typedBytesContentReader.readIntImpl(); } public long readLong() throws JMSException { checkReadable(); checkAvailable(8); - return _data.getLong(); + return _typedBytesContentReader.readLongImpl(); } public float readFloat() throws JMSException { checkReadable(); checkAvailable(4); - return _data.getFloat(); + return _typedBytesContentReader.readFloatImpl(); } public double readDouble() throws JMSException { checkReadable(); checkAvailable(8); - return _data.getDouble(); + return _typedBytesContentReader.readDoubleImpl(); } public String readUTF() throws JMSException @@ -164,34 +186,7 @@ public class JMSBytesMessage extends AbstractBytesMessage implements BytesMessag // we check only for one byte since theoretically the string could be only a // single byte when using UTF-8 encoding - try - { - short length = readShort(); - if(length == 0) - { - return ""; - } - else - { - CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder(); - ByteBuffer encodedString = _data.slice(); - encodedString.limit(length); - _data.position(_data.position()+length); - CharBuffer string = decoder.decode(encodedString.buf()); - - return string.toString(); - } - - - - } - catch (CharacterCodingException e) - { - JMSException jmse = new JMSException("Error decoding byte stream as a UTF8 string: " + e); - jmse.setLinkedException(e); - jmse.initCause(e); - throw jmse; - } + return _typedBytesContentReader.readLengthPrefixedUTF(); } public int readBytes(byte[] bytes) throws JMSException @@ -201,14 +196,14 @@ public class JMSBytesMessage extends AbstractBytesMessage implements BytesMessag throw new IllegalArgumentException("byte array must not be null"); } checkReadable(); - int count = (_data.remaining() >= bytes.length ? bytes.length : _data.remaining()); + int count = (_typedBytesContentReader.remaining() >= bytes.length ? bytes.length : _typedBytesContentReader.remaining()); if (count == 0) { return -1; } else { - _data.get(bytes, 0, count); + _typedBytesContentReader.readRawBytes(bytes, 0, count); return count; } } @@ -224,110 +219,82 @@ public class JMSBytesMessage extends AbstractBytesMessage implements BytesMessag throw new IllegalArgumentException("maxLength must be <= bytes.length"); } checkReadable(); - int count = (_data.remaining() >= maxLength ? maxLength : _data.remaining()); + int count = (_typedBytesContentReader.remaining() >= maxLength ? maxLength : _typedBytesContentReader.remaining()); if (count == 0) { return -1; } else { - _data.get(bytes, 0, count); + _typedBytesContentReader.readRawBytes(bytes, 0, count); return count; } } + public void writeBoolean(boolean b) throws JMSException { checkWritable(); - _changedData = true; - _data.put(b ? (byte) 1 : (byte) 0); + _typedBytesContentWriter.writeBooleanImpl(b); } public void writeByte(byte b) throws JMSException { checkWritable(); - _changedData = true; - _data.put(b); + _typedBytesContentWriter.writeByteImpl(b); } public void writeShort(short i) throws JMSException { checkWritable(); - _changedData = true; - _data.putShort(i); + _typedBytesContentWriter.writeShortImpl(i); } public void writeChar(char c) throws JMSException { checkWritable(); - _changedData = true; - _data.putChar(c); + _typedBytesContentWriter.writeCharImpl(c); } public void writeInt(int i) throws JMSException { checkWritable(); - _changedData = true; - _data.putInt(i); + _typedBytesContentWriter.writeIntImpl(i); } public void writeLong(long l) throws JMSException { checkWritable(); - _changedData = true; - _data.putLong(l); + _typedBytesContentWriter.writeLongImpl(l); } public void writeFloat(float v) throws JMSException { checkWritable(); - _changedData = true; - _data.putFloat(v); + _typedBytesContentWriter.writeFloatImpl(v); } public void writeDouble(double v) throws JMSException { checkWritable(); - _changedData = true; - _data.putDouble(v); + _typedBytesContentWriter.writeDoubleImpl(v); } public void writeUTF(String string) throws JMSException { checkWritable(); - try - { - CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder(); - java.nio.ByteBuffer encodedString = encoder.encode(CharBuffer.wrap(string)); - - _data.putShort((short)encodedString.limit()); - _data.put(encodedString); - _changedData = true; - //_data.putString(string, Charset.forName("UTF-8").newEncoder()); - // we must add the null terminator manually - //_data.put((byte)0); - } - catch (CharacterCodingException e) - { - JMSException jmse = new JMSException("Unable to encode string: " + e); - jmse.setLinkedException(e); - jmse.initCause(e); - throw jmse; - } + _typedBytesContentWriter.writeLengthPrefixedUTF(string); } public void writeBytes(byte[] bytes) throws JMSException { - checkWritable(); - _data.put(bytes); - _changedData = true; + writeBytes(bytes, 0, bytes.length); } public void writeBytes(byte[] bytes, int offset, int length) throws JMSException { checkWritable(); - _data.put(bytes, offset, length); - _changedData = true; + _typedBytesContentWriter.writeBytesRaw(bytes, offset, length); } public void writeObject(Object object) throws JMSException diff --git a/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessageFactory.java b/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessageFactory.java index cb04ebee1b..89561b88eb 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessageFactory.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessageFactory.java @@ -22,11 +22,12 @@ package org.apache.qpid.client.message; import javax.jms.JMSException; -import org.apache.mina.common.ByteBuffer; import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.BasicContentHeaderProperties; +import java.nio.ByteBuffer; + public class JMSBytesMessageFactory extends AbstractJMSMessageFactory { protected AbstractJMSMessage createMessage(AMQMessageDelegate delegate, ByteBuffer data) throws AMQException diff --git a/java/client/src/main/java/org/apache/qpid/client/message/JMSHeaderAdapter.java b/java/client/src/main/java/org/apache/qpid/client/message/JMSHeaderAdapter.java index e295d4a2a0..52c0eb263b 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/JMSHeaderAdapter.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/JMSHeaderAdapter.java @@ -20,12 +20,15 @@ */ package org.apache.qpid.client.message; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.OutputStream; import java.util.Enumeration; import javax.jms.JMSException; import javax.jms.MessageFormatException; -import org.apache.mina.common.ByteBuffer; +import java.nio.ByteBuffer; import org.apache.qpid.AMQPInvalidClassException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; @@ -282,7 +285,7 @@ public final class JMSHeaderAdapter s = String.valueOf(o); } } - }//else return s // null; + }//else return s // null; } return s; @@ -458,9 +461,29 @@ public final class JMSHeaderAdapter return getHeaders().isEmpty(); } - public void writeToBuffer(ByteBuffer data) + public void writeToBuffer(final ByteBuffer data) { - getHeaders().writeToBuffer(data); + try + { + getHeaders().writeToBuffer(new DataOutputStream(new OutputStream() + { + @Override + public void write(final int b) + { + data.put((byte)b); + } + + @Override + public void write(final byte[] b, final int off, final int len) + { + data.put(b, off, len); + } + })); + } + catch (IOException e) + { + throw new IllegalArgumentException("Unexpected IO Exception - should never happen", e); + } } public Enumeration getMapNames() diff --git a/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java b/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java index 306ffeeadf..fad24a968e 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java @@ -20,11 +20,8 @@ */ package org.apache.qpid.client.message; -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.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,13 +29,14 @@ import org.slf4j.LoggerFactory; import javax.jms.JMSException; import javax.jms.MessageFormatException; +import java.nio.ByteBuffer; import java.nio.charset.CharacterCodingException; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; -public class JMSMapMessage extends AbstractBytesTypedMessage implements javax.jms.MapMessage +public class JMSMapMessage extends AbstractJMSMessage implements javax.jms.MapMessage { private static final Logger _logger = LoggerFactory.getLogger(JMSMapMessage.class); @@ -54,10 +52,10 @@ public class JMSMapMessage extends AbstractBytesTypedMessage implements javax.jm JMSMapMessage(AMQMessageDelegateFactory delegateFactory, ByteBuffer data) throws JMSException { - super(delegateFactory, data); // this instantiates a content header + super(delegateFactory, data!=null); // this instantiates a content header if(data != null) { - populateMapFromData(); + populateMapFromData(data); } } @@ -65,10 +63,10 @@ public class JMSMapMessage extends AbstractBytesTypedMessage implements javax.jm JMSMapMessage(AMQMessageDelegate delegate, ByteBuffer data) throws AMQException { - super(delegate, data); + super(delegate, data != null); try { - populateMapFromData(); + populateMapFromData(data); } catch (JMSException je) { @@ -89,18 +87,10 @@ public class JMSMapMessage extends AbstractBytesTypedMessage implements javax.jm return MIME_TYPE; } - public ByteBuffer getData() - { - // What if _data is null? - writeMapToData(); - - return super.getData(); - } - @Override - public void clearBodyImpl() throws JMSException + public void clearBody() throws JMSException { - super.clearBodyImpl(); + super.clearBody(); _map.clear(); } @@ -458,17 +448,18 @@ public class JMSMapMessage extends AbstractBytesTypedMessage implements javax.jm return _map.containsKey(propName); } - protected void populateMapFromData() throws JMSException + protected void populateMapFromData(ByteBuffer data) throws JMSException { - if (_data != null) + TypedBytesContentReader reader = new TypedBytesContentReader(data); + if (data != null) { - _data.rewind(); + data.rewind(); - final int entries = readIntImpl(); + final int entries = reader.readIntImpl(); for (int i = 0; i < entries; i++) { - String propName = readStringImpl(); - Object value = readObject(); + String propName = reader.readStringImpl(); + Object value = reader.readObject(); _map.put(propName, value); } } @@ -478,35 +469,21 @@ public class JMSMapMessage extends AbstractBytesTypedMessage implements javax.jm } } - protected void writeMapToData() + public ByteBuffer getData() + throws JMSException { - allocateInitialBuffer(); + TypedBytesContentWriter writer = new TypedBytesContentWriter(); + final int size = _map.size(); - writeIntImpl(size); + writer.writeIntImpl(size); for (Map.Entry<String, Object> entry : _map.entrySet()) { - try - { - writeStringImpl(entry.getKey()); - } - catch (CharacterCodingException e) - { - throw new IllegalArgumentException("Cannot encode property key name " + entry.getKey(), e); - - } + writer.writeNullTerminatedStringImpl(entry.getKey()); - try - { - writeObject(entry.getValue()); - } - catch (JMSException e) - { - Object value = entry.getValue(); - throw new IllegalArgumentException("Cannot encode property key name " + entry.getKey() + " value : " + value - + " (type: " + value.getClass().getName() + ").", e); - } + writer.writeObject(entry.getValue()); } + return writer.getData(); } } diff --git a/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessageFactory.java b/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessageFactory.java index eccb90560b..89408a5c3c 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessageFactory.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessageFactory.java @@ -14,18 +14,16 @@ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations - * under the License. + * under the License. + * * - * */ package org.apache.qpid.client.message; import javax.jms.JMSException; -import org.apache.mina.common.ByteBuffer; +import java.nio.ByteBuffer; import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.BasicContentHeaderProperties; public class JMSMapMessageFactory extends AbstractJMSMessageFactory { 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 637d9dd692..c981c951c3 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 @@ -20,26 +20,28 @@ */ package org.apache.qpid.client.message; -import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; +import java.io.*; +import java.nio.ByteBuffer; import javax.jms.JMSException; import javax.jms.MessageFormatException; import javax.jms.ObjectMessage; -import org.apache.mina.common.ByteBuffer; - import org.apache.qpid.AMQException; +import org.apache.qpid.client.util.ClassLoadingAwareObjectInputStream; public class JMSObjectMessage extends AbstractJMSMessage implements ObjectMessage { public static final String MIME_TYPE = "application/java-object-stream"; + private static final int DEFAULT_OUTPUT_BUFFER_SIZE = 256; + + private Serializable _readData; + private ByteBuffer _data; + private Exception _exception; + + private static final ByteBuffer EMPTY_BYTE_BUFFER = ByteBuffer.allocate(0); - private static final int DEFAULT_BUFFER_SIZE = 1024; /** * Creates empty, writable message for use by producers @@ -47,41 +49,57 @@ public class JMSObjectMessage extends AbstractJMSMessage implements ObjectMessag */ public JMSObjectMessage(AMQMessageDelegateFactory delegateFactory) { - this(delegateFactory, null); - } - - private JMSObjectMessage(AMQMessageDelegateFactory delegateFactory, ByteBuffer data) - { - super(delegateFactory, data); - if (data == null) - { - _data = ByteBuffer.allocate(DEFAULT_BUFFER_SIZE); - _data.setAutoExpand(true); - } - - setContentType(getMimeType()); + super(delegateFactory, false); } /** * Creates read only message for delivery to consumers */ - JMSObjectMessage(AMQMessageDelegate delegate, ByteBuffer data) throws AMQException + JMSObjectMessage(AMQMessageDelegate delegate, final ByteBuffer data) throws AMQException { - super(delegate, data); + super(delegate, data!=null); + + try + { + ClassLoadingAwareObjectInputStream in = new ClassLoadingAwareObjectInputStream(new InputStream() + { + + + @Override + public int read() throws IOException + { + return data.get(); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException + { + len = data.remaining() < len ? data.remaining() : len; + data.get(b, off, len); + return len; + } + }); + + _readData = (Serializable) in.readObject(); + } + catch (IOException e) + { + _exception = e; + } + catch (ClassNotFoundException e) + { + _exception = e; + } } - public void clearBodyImpl() throws JMSException + public void clearBody() throws JMSException { - if (_data != null) - { - _data.release(); - _data = null; - } - - - + super.clearBody(); + _exception = null; + _readData = null; + _data = null; } public String toBodyString() throws JMSException @@ -94,83 +112,116 @@ public class JMSObjectMessage extends AbstractJMSMessage implements ObjectMessag return MIME_TYPE; } - public void setObject(Serializable serializable) throws JMSException + @Override + public ByteBuffer getData() throws JMSException { - checkWritable(); - - if (_data == null) + if(_exception != null) + { + final MessageFormatException messageFormatException = + new MessageFormatException("Unable to deserialize message"); + messageFormatException.setLinkedException(_exception); + throw messageFormatException; + } + if(_readData == null) { - _data = ByteBuffer.allocate(DEFAULT_BUFFER_SIZE); - _data.setAutoExpand(true); + + return _data == null ? EMPTY_BYTE_BUFFER : _data.duplicate(); } else { - _data.rewind(); + try + { + ByteArrayOutputStream baos = new ByteArrayOutputStream(DEFAULT_OUTPUT_BUFFER_SIZE); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(_readData); + oos.flush(); + return ByteBuffer.wrap(baos.toByteArray()); + } + catch (IOException e) + { + final JMSException jmsException = new JMSException("Unable to encode object of type: " + + _readData.getClass().getName() + ", value " + _readData); + jmsException.setLinkedException(e); + throw jmsException; + } } + } + + public void setObject(Serializable serializable) throws JMSException + { + checkWritable(); + clearBody(); try { - ObjectOutputStream out = new ObjectOutputStream(_data.asOutputStream()); - out.writeObject(serializable); - out.flush(); - out.close(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(DEFAULT_OUTPUT_BUFFER_SIZE); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(serializable); + oos.flush(); + _data = ByteBuffer.wrap(baos.toByteArray()); } catch (IOException e) { - MessageFormatException mfe = new MessageFormatException("Message not serializable: " + e); - mfe.setLinkedException(e); - mfe.initCause(e); - throw mfe; + final JMSException jmsException = new JMSException("Unable to encode object of type: " + + serializable.getClass().getName() + ", value " + serializable); + jmsException.setLinkedException(e); + throw jmsException; } } public Serializable getObject() throws JMSException { - ObjectInputStream in = null; - if (_data == null) + if(_exception != null) { - return null; + final MessageFormatException messageFormatException = new MessageFormatException("Unable to deserialize message"); + messageFormatException.setLinkedException(_exception); + throw messageFormatException; } - - try + else if(_readData != null || _data == null) { - _data.rewind(); - in = new ObjectInputStream(_data.asInputStream()); - - return (Serializable) in.readObject(); + return _readData; } - catch (IOException e) - { - MessageFormatException mfe = new MessageFormatException("Could not deserialize message: " + e); - mfe.setLinkedException(e); - mfe.initCause(e); - throw mfe; - } - catch (ClassNotFoundException e) - { - MessageFormatException mfe = new MessageFormatException("Could not deserialize message: " + e); - mfe.setLinkedException(e); - mfe.initCause(e); - throw mfe; - } - finally + else { - // _data.rewind(); - close(in); - } - } + Exception exception = null; - private static void close(InputStream in) - { - try - { - if (in != null) + final ByteBuffer data = _data.duplicate(); + try + { + ClassLoadingAwareObjectInputStream in = new ClassLoadingAwareObjectInputStream(new InputStream() + { + @Override + public int read() throws IOException + { + return data.get(); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException + { + len = data.remaining() < len ? data.remaining() : len; + data.get(b, off, len); + return len; + } + }); + + return (Serializable) in.readObject(); + } + catch (ClassNotFoundException e) + { + exception = e; + } + catch (IOException e) { - in.close(); + exception = e; } + + JMSException jmsException = new JMSException("Could not deserialize object"); + jmsException.setLinkedException(exception); + throw jmsException; } - catch (IOException ignore) - { } + } + } diff --git a/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessageFactory.java b/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessageFactory.java index 03851dfa01..4660c91c1f 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessageFactory.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessageFactory.java @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -22,10 +22,8 @@ package org.apache.qpid.client.message; import javax.jms.JMSException; -import org.apache.mina.common.ByteBuffer; +import java.nio.ByteBuffer; import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.BasicContentHeaderProperties; public class JMSObjectMessageFactory extends AbstractJMSMessageFactory { diff --git a/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessage.java b/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessage.java index ad2620852b..5c93f6b6f0 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessage.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessage.java @@ -23,7 +23,8 @@ package org.apache.qpid.client.message; import javax.jms.JMSException; import javax.jms.StreamMessage; -import org.apache.mina.common.ByteBuffer; +import java.nio.ByteBuffer; + import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.BasicContentHeaderProperties; @@ -36,65 +37,76 @@ public class JMSStreamMessage extends AbstractBytesTypedMessage implements Strea public static final String MIME_TYPE="jms/stream-message"; - - /** - * This is set when reading a byte array. The readBytes(byte[]) method supports multiple calls to read - * a byte array in multiple chunks, hence this is used to track how much is left to be read - */ - private int _byteArrayRemaining = -1; + private TypedBytesContentReader _typedBytesContentReader; + private TypedBytesContentWriter _typedBytesContentWriter; public JMSStreamMessage(AMQMessageDelegateFactory delegateFactory) { - this(delegateFactory,null); + super(delegateFactory,false); + _typedBytesContentWriter = new TypedBytesContentWriter(); } - /** - * Construct a stream message with existing data. - * - * @param delegateFactory - * @param data the data that comprises this message. If data is null, you get a 1024 byte buffer that is - */ - JMSStreamMessage(AMQMessageDelegateFactory delegateFactory, ByteBuffer data) - { - super(delegateFactory, data); // this instanties a content header - } JMSStreamMessage(AMQMessageDelegate delegate, ByteBuffer data) throws AMQException { - - super(delegate, data); + super(delegate, data!=null); + _typedBytesContentReader = new TypedBytesContentReader(data); } - public void reset() { - super.reset(); _readableMessage = true; + + if(_typedBytesContentReader != null) + { + _typedBytesContentReader.reset(); + } + else if (_typedBytesContentWriter != null) + { + _typedBytesContentReader = new TypedBytesContentReader(_typedBytesContentWriter.getData()); + } + } + + @Override + public void clearBody() throws JMSException + { + super.clearBody(); + _typedBytesContentReader = null; + _typedBytesContentWriter = new TypedBytesContentWriter(); + } + protected String getMimeType() { return MIME_TYPE; } - + @Override + public java.nio.ByteBuffer getData() throws JMSException + { + return _typedBytesContentWriter == null ? _typedBytesContentReader.getData() : _typedBytesContentWriter.getData(); + } public boolean readBoolean() throws JMSException { - return super.readBoolean(); + checkReadable(); + return _typedBytesContentReader.readBoolean(); } public byte readByte() throws JMSException { - return super.readByte(); + checkReadable(); + return _typedBytesContentReader.readByte(); } public short readShort() throws JMSException { - return super.readShort(); + checkReadable(); + return _typedBytesContentReader.readShort(); } /** @@ -105,102 +117,127 @@ public class JMSStreamMessage extends AbstractBytesTypedMessage implements Strea */ public char readChar() throws JMSException { - return super.readChar(); + checkReadable(); + return _typedBytesContentReader.readChar(); } public int readInt() throws JMSException { - return super.readInt(); + checkReadable(); + return _typedBytesContentReader.readInt(); } public long readLong() throws JMSException { - return super.readLong(); + checkReadable(); + return _typedBytesContentReader.readLong(); } public float readFloat() throws JMSException { - return super.readFloat(); + checkReadable(); + return _typedBytesContentReader.readFloat(); } public double readDouble() throws JMSException { - return super.readDouble(); + checkReadable(); + return _typedBytesContentReader.readDouble(); } public String readString() throws JMSException { - return super.readString(); + checkReadable(); + return _typedBytesContentReader.readString(); } public int readBytes(byte[] bytes) throws JMSException { - return super.readBytes(bytes); + if(bytes == null) + { + throw new IllegalArgumentException("Must provide non-null array to read into"); + } + + checkReadable(); + return _typedBytesContentReader.readBytes(bytes); } public Object readObject() throws JMSException { - return super.readObject(); + checkReadable(); + return _typedBytesContentReader.readObject(); } public void writeBoolean(boolean b) throws JMSException { - super.writeBoolean(b); + checkWritable(); + _typedBytesContentWriter.writeBoolean(b); } public void writeByte(byte b) throws JMSException { - super.writeByte(b); + checkWritable(); + _typedBytesContentWriter.writeByte(b); } public void writeShort(short i) throws JMSException { - super.writeShort(i); + checkWritable(); + _typedBytesContentWriter.writeShort(i); } public void writeChar(char c) throws JMSException { - super.writeChar(c); + checkWritable(); + _typedBytesContentWriter.writeChar(c); } public void writeInt(int i) throws JMSException { - super.writeInt(i); + checkWritable(); + _typedBytesContentWriter.writeInt(i); } public void writeLong(long l) throws JMSException { - super.writeLong(l); + checkWritable(); + _typedBytesContentWriter.writeLong(l); } public void writeFloat(float v) throws JMSException { - super.writeFloat(v); + checkWritable(); + _typedBytesContentWriter.writeFloat(v); } public void writeDouble(double v) throws JMSException { - super.writeDouble(v); + checkWritable(); + _typedBytesContentWriter.writeDouble(v); } public void writeString(String string) throws JMSException { - super.writeString(string); + checkWritable(); + _typedBytesContentWriter.writeString(string); } public void writeBytes(byte[] bytes) throws JMSException { - super.writeBytes(bytes); + checkWritable(); + _typedBytesContentWriter.writeBytes(bytes); } public void writeBytes(byte[] bytes, int offset, int length) throws JMSException { - super.writeBytes(bytes,offset,length); + checkWritable(); + _typedBytesContentWriter.writeBytes(bytes, offset, length); } public void writeObject(Object object) throws JMSException { - super.writeObject(object); + checkWritable(); + _typedBytesContentWriter.writeObject(object); } } diff --git a/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessageFactory.java b/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessageFactory.java index 5e25db9ae0..359f5157f3 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessageFactory.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessageFactory.java @@ -22,10 +22,9 @@ package org.apache.qpid.client.message; import javax.jms.JMSException; -import org.apache.mina.common.ByteBuffer; +import java.nio.ByteBuffer; + import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.BasicContentHeaderProperties; public class JMSStreamMessageFactory extends AbstractJMSMessageFactory { 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 fc2006a119..acf3a0ca14 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 @@ -20,15 +20,21 @@ */ package org.apache.qpid.client.message; +import java.io.DataInputStream; import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; import javax.jms.JMSException; +import javax.jms.MessageFormatException; -import org.apache.mina.common.ByteBuffer; import org.apache.qpid.AMQException; import org.apache.qpid.client.CustomJMSXProperty; +import org.apache.qpid.framing.AMQFrameDecodingException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.BasicContentHeaderProperties; import org.apache.qpid.util.Strings; @@ -37,6 +43,7 @@ public class JMSTextMessage extends AbstractJMSMessage implements javax.jms.Text { private static final String MIME_TYPE = "text/plain"; + private Exception _exception; private String _decodedValue; /** @@ -45,36 +52,41 @@ public class JMSTextMessage extends AbstractJMSMessage implements javax.jms.Text private static final String PAYLOAD_NULL_PROPERTY = CustomJMSXProperty.JMS_AMQP_NULL.toString(); private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); - public JMSTextMessage(AMQMessageDelegateFactory delegateFactory) throws JMSException - { - this(delegateFactory, null, null); - } + private CharsetDecoder _decoder = DEFAULT_CHARSET.newDecoder(); + private CharsetEncoder _encoder = DEFAULT_CHARSET.newEncoder(); + + private static final ByteBuffer EMPTY_BYTE_BUFFER = ByteBuffer.allocate(0); - JMSTextMessage(AMQMessageDelegateFactory delegateFactory, ByteBuffer data, String encoding) throws JMSException + public JMSTextMessage(AMQMessageDelegateFactory delegateFactory) throws JMSException { - super(delegateFactory, data); // this instantiates a content header - setContentType(getMimeType()); - setEncoding(encoding); + super(delegateFactory, false); // this instantiates a content header } JMSTextMessage(AMQMessageDelegate delegate, ByteBuffer data) throws AMQException { - super(delegate, data); - setContentType(getMimeType()); - _data = data; - } + super(delegate, data!=null); - - public void clearBodyImpl() throws JMSException - { - if (_data != null) + try { - _data.release(); - _data = null; + if(propertyExists(PAYLOAD_NULL_PROPERTY)) + { + _decodedValue = null; + } + else + { + _decodedValue = _decoder.decode(data).toString(); + } + } + catch (CharacterCodingException e) + { + _exception = e; + } + catch (JMSException e) + { + _exception = e; } - _decodedValue = null; } public String toBodyString() throws JMSException @@ -87,95 +99,62 @@ public class JMSTextMessage extends AbstractJMSMessage implements javax.jms.Text return MIME_TYPE; } - public void setText(String text) throws JMSException + @Override + public ByteBuffer getData() throws JMSException { - checkWritable(); - - clearBody(); + _encoder.reset(); try { - if (text != null) + if(_exception != null) + { + final MessageFormatException messageFormatException = new MessageFormatException("Cannot decode original message"); + messageFormatException.setLinkedException(_exception); + throw messageFormatException; + } + else if(_decodedValue == null) + { + return EMPTY_BYTE_BUFFER; + } + else { - final String encoding = getEncoding(); - if (encoding == null || encoding.equalsIgnoreCase("UTF-8")) - { - _data = ByteBuffer.wrap(Strings.toUTF8(text)); - setEncoding("UTF-8"); - } - else - { - _data = ByteBuffer.wrap(text.getBytes(encoding)); - } - _data.position(_data.limit()); - _changedData=true; + return _encoder.encode(CharBuffer.wrap(_decodedValue)); } - _decodedValue = text; } - catch (UnsupportedEncodingException e) + catch (CharacterCodingException e) { - // should never occur - JMSException jmse = new JMSException("Unable to decode text data"); - jmse.setLinkedException(e); - jmse.initCause(e); - throw jmse; + final JMSException jmsException = new JMSException("Cannot encode string in UFT-8: " + _decodedValue); + jmsException.setLinkedException(e); + throw jmsException; } } - public String getText() throws JMSException + @Override + public void clearBody() throws JMSException { - if (_data == null && _decodedValue == null) - { - return null; - } - else if (_decodedValue != null) - { - return _decodedValue; - } - else - { - _data.rewind(); + super.clearBody(); + _decodedValue = null; + _exception = null; + } - if (propertyExists(PAYLOAD_NULL_PROPERTY) && getBooleanProperty(PAYLOAD_NULL_PROPERTY)) - { - return null; - } - if (getEncoding() != null) - { - try - { - _decodedValue = _data.getString(Charset.forName(getEncoding()).newDecoder()); - } - catch (CharacterCodingException e) - { - JMSException jmse = new JMSException("Could not decode string data: " + e); - jmse.setLinkedException(e); - jmse.initCause(e); - throw jmse; - } - } - else - { - try - { - _decodedValue = _data.getString(DEFAULT_CHARSET.newDecoder()); - } - catch (CharacterCodingException e) - { - JMSException jmse = new JMSException("Could not decode string data: " + e); - jmse.setLinkedException(e); - jmse.initCause(e); - throw jmse; - } - } - return _decodedValue; - } + public void setText(String text) throws JMSException + { + checkWritable(); + + clearBody(); + _decodedValue = text; + + } + + public String getText() throws JMSException + { + return _decodedValue; } @Override public void prepareForSending() throws JMSException { super.prepareForSending(); - if (_data == null) + if (_decodedValue == null) { setBooleanProperty(PAYLOAD_NULL_PROPERTY, true); } diff --git a/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessageFactory.java b/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessageFactory.java index 1f4d64c78f..d1af32c10a 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessageFactory.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessageFactory.java @@ -22,7 +22,7 @@ package org.apache.qpid.client.message; import javax.jms.JMSException; -import org.apache.mina.common.ByteBuffer; +import java.nio.ByteBuffer; import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.BasicContentHeaderProperties; 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 4e4061cf4d..cdb75fc9a9 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 @@ -104,7 +104,7 @@ public class MessageFactoryRegistry AMQShortString routingKey, ContentHeaderBody contentHeader, List bodies) throws AMQException, JMSException { - BasicContentHeaderProperties properties = (BasicContentHeaderProperties) contentHeader.properties; + BasicContentHeaderProperties properties = (BasicContentHeaderProperties) contentHeader.getProperties(); // Get the message content type. This may be null for pure AMQP messages, but will always be set for JMS over // AMQP. When the type is null, it can only be assumed that the message is a byte message. diff --git a/java/client/src/main/java/org/apache/qpid/client/SSLConfiguration.java b/java/client/src/main/java/org/apache/qpid/client/message/QpidMessageProperties.java index 2280cc9870..b30afafa35 100644 --- a/java/client/src/main/java/org/apache/qpid/client/SSLConfiguration.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/QpidMessageProperties.java @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -18,44 +18,17 @@ * under the License. * */ +package org.apache.qpid.client.message; -package org.apache.qpid.client; +/** + * Place holder for Qpid specific message properties + */ +public class QpidMessageProperties +{ -public class SSLConfiguration { - - private String _keystorePath; - - private String _keystorePassword; - - private String _certType = "SunX509"; - - public void setKeystorePath(String path) - { - _keystorePath = path; - } - - public String getKeystorePath() - { - return _keystorePath; - } - - public void setKeystorePassword(String password) - { - _keystorePassword = password; - } - - public String getKeystorePassword() - { - return _keystorePassword; - } - - public void setCertType(String type) - { - _certType = type; - } - - public String getCertType() - { - return _certType; - } + public static final String QPID_SUBJECT = "qpid.subject"; + + // AMQP 0-10 related properties + public static final String AMQP_0_10_APP_ID = "x-amqp-0-10.app-id"; + public static final String AMQP_0_10_ROUTING_KEY = "x-amqp-0-10.routing-key"; } diff --git a/java/client/src/main/java/org/apache/qpid/client/message/TypedBytesCodes.java b/java/client/src/main/java/org/apache/qpid/client/message/TypedBytesCodes.java new file mode 100644 index 0000000000..26a0b41cdc --- /dev/null +++ b/java/client/src/main/java/org/apache/qpid/client/message/TypedBytesCodes.java @@ -0,0 +1,46 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.message; + +public interface TypedBytesCodes +{ + static final byte BOOLEAN_TYPE = (byte) 1; + + static final byte BYTE_TYPE = (byte) 2; + + static final byte BYTEARRAY_TYPE = (byte) 3; + + static final byte SHORT_TYPE = (byte) 4; + + static final byte CHAR_TYPE = (byte) 5; + + static final byte INT_TYPE = (byte) 6; + + static final byte LONG_TYPE = (byte) 7; + + static final byte FLOAT_TYPE = (byte) 8; + + static final byte DOUBLE_TYPE = (byte) 9; + + static final byte STRING_TYPE = (byte) 10; + + static final byte NULL_STRING_TYPE = (byte) 11; +} diff --git a/java/client/src/main/java/org/apache/qpid/client/message/TypedBytesContentReader.java b/java/client/src/main/java/org/apache/qpid/client/message/TypedBytesContentReader.java new file mode 100644 index 0000000000..1ae25eb1ed --- /dev/null +++ b/java/client/src/main/java/org/apache/qpid/client/message/TypedBytesContentReader.java @@ -0,0 +1,674 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 javax.jms.JMSException; +import javax.jms.MessageEOFException; +import javax.jms.MessageFormatException; +import javax.jms.MessageNotReadableException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; + +class TypedBytesContentReader implements TypedBytesCodes +{ + + private final ByteBuffer _data; + private final int _position; + private final int _limit; + + + private static final Charset UTF8_CHARSET = Charset.forName("UTF-8"); + + private final CharsetDecoder _charsetDecoder = UTF8_CHARSET.newDecoder(); + + private int _byteArrayRemaining = -1; + + + public TypedBytesContentReader(final ByteBuffer data) + { + _data = data.duplicate(); + _position = _data.position(); + _limit = _data.limit(); + } + + /** + * Check that there is at least a certain number of bytes available to read + * + * @param len the number of bytes + * @throws javax.jms.MessageEOFException if there are less than len bytes available to read + */ + protected void checkAvailable(int len) throws MessageEOFException + { + if (_data.remaining() < len) + { + throw new MessageEOFException("Unable to read " + len + " bytes"); + } + } + + protected byte readWireType() throws MessageFormatException, MessageEOFException, + MessageNotReadableException + { + checkAvailable(1); + return _data.get(); + } + + protected boolean readBoolean() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + boolean result; + try + { + switch (wireType) + { + case BOOLEAN_TYPE: + checkAvailable(1); + result = readBooleanImpl(); + break; + case STRING_TYPE: + checkAvailable(1); + result = Boolean.parseBoolean(readStringImpl()); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a boolean"); + } + return result; + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + boolean readBooleanImpl() + { + return _data.get() != 0; + } + + protected byte readByte() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + byte result; + try + { + switch (wireType) + { + case BYTE_TYPE: + checkAvailable(1); + result = readByteImpl(); + break; + case STRING_TYPE: + checkAvailable(1); + result = Byte.parseByte(readStringImpl()); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a byte"); + } + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + return result; + } + + byte readByteImpl() + { + return _data.get(); + } + + protected short readShort() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + short result; + try + { + switch (wireType) + { + case SHORT_TYPE: + checkAvailable(2); + result = readShortImpl(); + break; + case STRING_TYPE: + checkAvailable(1); + result = Short.parseShort(readStringImpl()); + break; + case BYTE_TYPE: + checkAvailable(1); + result = readByteImpl(); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a short"); + } + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + return result; + } + + short readShortImpl() + { + return _data.getShort(); + } + + /** + * Note that this method reads a unicode character as two bytes from the stream + * + * @return the character read from the stream + * @throws javax.jms.JMSException + */ + protected char readChar() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + try + { + if (wireType == NULL_STRING_TYPE) + { + throw new NullPointerException(); + } + + if (wireType != CHAR_TYPE) + { + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a char"); + } + else + { + checkAvailable(2); + return readCharImpl(); + } + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + char readCharImpl() + { + return _data.getChar(); + } + + protected int readInt() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + int result; + try + { + switch (wireType) + { + case INT_TYPE: + checkAvailable(4); + result = readIntImpl(); + break; + case SHORT_TYPE: + checkAvailable(2); + result = readShortImpl(); + break; + case STRING_TYPE: + checkAvailable(1); + result = Integer.parseInt(readStringImpl()); + break; + case BYTE_TYPE: + checkAvailable(1); + result = readByteImpl(); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to an int"); + } + return result; + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + protected int readIntImpl() + { + return _data.getInt(); + } + + protected long readLong() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + long result; + try + { + switch (wireType) + { + case LONG_TYPE: + checkAvailable(8); + result = readLongImpl(); + break; + case INT_TYPE: + checkAvailable(4); + result = readIntImpl(); + break; + case SHORT_TYPE: + checkAvailable(2); + result = readShortImpl(); + break; + case STRING_TYPE: + checkAvailable(1); + result = Long.parseLong(readStringImpl()); + break; + case BYTE_TYPE: + checkAvailable(1); + result = readByteImpl(); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a long"); + } + return result; + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + long readLongImpl() + { + return _data.getLong(); + } + + protected float readFloat() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + float result; + try + { + switch (wireType) + { + case FLOAT_TYPE: + checkAvailable(4); + result = readFloatImpl(); + break; + case STRING_TYPE: + checkAvailable(1); + result = Float.parseFloat(readStringImpl()); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a float"); + } + return result; + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + float readFloatImpl() + { + return _data.getFloat(); + } + + protected double readDouble() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + double result; + try + { + switch (wireType) + { + case DOUBLE_TYPE: + checkAvailable(8); + result = readDoubleImpl(); + break; + case FLOAT_TYPE: + checkAvailable(4); + result = readFloatImpl(); + break; + case STRING_TYPE: + checkAvailable(1); + result = Double.parseDouble(readStringImpl()); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a double"); + } + return result; + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + double readDoubleImpl() + { + return _data.getDouble(); + } + + protected String readString() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + String result; + try + { + switch (wireType) + { + case STRING_TYPE: + checkAvailable(1); + result = readStringImpl(); + break; + case NULL_STRING_TYPE: + result = null; + throw new NullPointerException("data is null"); + case BOOLEAN_TYPE: + checkAvailable(1); + result = String.valueOf(readBooleanImpl()); + break; + case LONG_TYPE: + checkAvailable(8); + result = String.valueOf(readLongImpl()); + break; + case INT_TYPE: + checkAvailable(4); + result = String.valueOf(readIntImpl()); + break; + case SHORT_TYPE: + checkAvailable(2); + result = String.valueOf(readShortImpl()); + break; + case BYTE_TYPE: + checkAvailable(1); + result = String.valueOf(readByteImpl()); + break; + case FLOAT_TYPE: + checkAvailable(4); + result = String.valueOf(readFloatImpl()); + break; + case DOUBLE_TYPE: + checkAvailable(8); + result = String.valueOf(readDoubleImpl()); + break; + case CHAR_TYPE: + checkAvailable(2); + result = String.valueOf(readCharImpl()); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a String"); + } + return result; + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + protected String readStringImpl() throws JMSException + { + try + { + _charsetDecoder.reset(); + ByteBuffer dup = _data.duplicate(); + int pos = _data.position(); + byte b; + while((b = _data.get()) != 0); + dup.limit(_data.position()-1); + return _charsetDecoder.decode(dup).toString(); + + } + catch (CharacterCodingException e) + { + JMSException jmse = new JMSException("Error decoding byte stream as a UTF8 string: " + e); + jmse.setLinkedException(e); + jmse.initCause(e); + throw jmse; + } + } + + protected int readBytes(byte[] bytes) throws JMSException + { + if (bytes == null) + { + throw new IllegalArgumentException("byte array must not be null"); + } + // first call + if (_byteArrayRemaining == -1) + { + // type discriminator checked separately so you get a MessageFormatException rather than + // an EOF even in the case where both would be applicable + checkAvailable(1); + byte wireType = readWireType(); + if (wireType != BYTEARRAY_TYPE) + { + throw new MessageFormatException("Unable to convert " + wireType + " to a byte array"); + } + checkAvailable(4); + int size = _data.getInt(); + // length of -1 indicates null + if (size == -1) + { + return -1; + } + else + { + if (size > _data.remaining()) + { + throw new MessageEOFException("Byte array has stated length " + + size + + " but message only contains " + + + _data.remaining() + + " bytes"); + } + else + { + _byteArrayRemaining = size; + } + } + } + else if (_byteArrayRemaining == 0) + { + _byteArrayRemaining = -1; + return -1; + } + + int returnedSize = readBytesImpl(bytes); + if (returnedSize < bytes.length) + { + _byteArrayRemaining = -1; + } + return returnedSize; + } + + private int readBytesImpl(byte[] bytes) + { + int count = (_byteArrayRemaining >= bytes.length ? bytes.length : _byteArrayRemaining); + _byteArrayRemaining -= count; + + if (count == 0) + { + return 0; + } + else + { + _data.get(bytes, 0, count); + return count; + } + } + + protected Object readObject() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + Object result = null; + try + { + switch (wireType) + { + case BOOLEAN_TYPE: + checkAvailable(1); + result = readBooleanImpl(); + break; + case BYTE_TYPE: + checkAvailable(1); + result = readByteImpl(); + break; + case BYTEARRAY_TYPE: + checkAvailable(4); + int size = _data.getInt(); + if (size == -1) + { + result = null; + } + else + { + _byteArrayRemaining = size; + byte[] bytesResult = new byte[size]; + readBytesImpl(bytesResult); + result = bytesResult; + } + break; + case SHORT_TYPE: + checkAvailable(2); + result = readShortImpl(); + break; + case CHAR_TYPE: + checkAvailable(2); + result = readCharImpl(); + break; + case INT_TYPE: + checkAvailable(4); + result = readIntImpl(); + break; + case LONG_TYPE: + checkAvailable(8); + result = readLongImpl(); + break; + case FLOAT_TYPE: + checkAvailable(4); + result = readFloatImpl(); + break; + case DOUBLE_TYPE: + checkAvailable(8); + result = readDoubleImpl(); + break; + case NULL_STRING_TYPE: + result = null; + break; + case STRING_TYPE: + checkAvailable(1); + result = readStringImpl(); + break; + } + return result; + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + public void reset() + { + _byteArrayRemaining = -1; + _data.position(_position); + _data.limit(_limit); + } + + public ByteBuffer getData() + { + ByteBuffer buf = _data.duplicate(); + buf.position(_position); + buf.limit(_limit); + return buf; + } + + public long size() + { + return _limit - _position; + } + + public int remaining() + { + return _data.remaining(); + } + + public void readRawBytes(final byte[] bytes, final int offset, final int count) + { + _data.get(bytes, offset, count); + } + + public String readLengthPrefixedUTF() throws JMSException + { + try + { + short length = readShortImpl(); + if(length == 0) + { + return ""; + } + else + { + _charsetDecoder.reset(); + ByteBuffer encodedString = _data.slice(); + encodedString.limit(length); + _data.position(_data.position()+length); + CharBuffer string = _charsetDecoder.decode(encodedString); + + return string.toString(); + } + } + catch(CharacterCodingException e) + { + JMSException jmse = new JMSException("Error decoding byte stream as a UTF8 string: " + e); + jmse.setLinkedException(e); + jmse.initCause(e); + throw jmse; + } + } +} diff --git a/java/client/src/main/java/org/apache/qpid/client/message/TypedBytesContentWriter.java b/java/client/src/main/java/org/apache/qpid/client/message/TypedBytesContentWriter.java new file mode 100644 index 0000000000..7c91db3a32 --- /dev/null +++ b/java/client/src/main/java/org/apache/qpid/client/message/TypedBytesContentWriter.java @@ -0,0 +1,370 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 javax.jms.JMSException; +import javax.jms.MessageFormatException; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; + +class TypedBytesContentWriter implements TypedBytesCodes +{ + private final ByteArrayOutputStream _baos = new ByteArrayOutputStream(); + private final DataOutputStream _data = new DataOutputStream(_baos); + private static final Charset UTF8 = Charset.forName("UTF-8"); + + protected void writeTypeDiscriminator(byte type) throws JMSException + { + try + { + _data.writeByte(type); + } + catch (IOException e) + { + throw handle(e); + } + } + + private JMSException handle(final IOException e) + { + JMSException jmsEx = new JMSException("Unable to write value: " + e.getMessage()); + jmsEx.setLinkedException(e); + return jmsEx; + } + + + protected void writeBoolean(boolean b) throws JMSException + { + writeTypeDiscriminator(BOOLEAN_TYPE); + writeBooleanImpl(b); + } + + public void writeBooleanImpl(final boolean b) throws JMSException + { + try + { + _data.writeByte(b ? (byte) 1 : (byte) 0); + } + catch (IOException e) + { + throw handle(e); + } + } + + protected void writeByte(byte b) throws JMSException + { + writeTypeDiscriminator(BYTE_TYPE); + writeByteImpl(b); + } + + public void writeByteImpl(final byte b) throws JMSException + { + try + { + _data.writeByte(b); + } + catch (IOException e) + { + throw handle(e); + } + } + + protected void writeShort(short i) throws JMSException + { + writeTypeDiscriminator(SHORT_TYPE); + writeShortImpl(i); + } + + public void writeShortImpl(final short i) throws JMSException + { + try + { + _data.writeShort(i); + } + catch (IOException e) + { + throw handle(e); + } + } + + protected void writeChar(char c) throws JMSException + { + writeTypeDiscriminator(CHAR_TYPE); + writeCharImpl(c); + } + + public void writeCharImpl(final char c) throws JMSException + { + try + { + _data.writeChar(c); + } + catch (IOException e) + { + throw handle(e); + } + } + + protected void writeInt(int i) throws JMSException + { + writeTypeDiscriminator(INT_TYPE); + writeIntImpl(i); + } + + protected void writeIntImpl(int i) throws JMSException + { + try + { + _data.writeInt(i); + } + catch (IOException e) + { + throw handle(e); + } + } + + protected void writeLong(long l) throws JMSException + { + writeTypeDiscriminator(LONG_TYPE); + writeLongImpl(l); + } + + public void writeLongImpl(final long l) throws JMSException + { + try + { + _data.writeLong(l); + } + catch (IOException e) + { + throw handle(e); + } + } + + protected void writeFloat(float v) throws JMSException + { + writeTypeDiscriminator(FLOAT_TYPE); + writeFloatImpl(v); + } + + public void writeFloatImpl(final float v) throws JMSException + { + try + { + _data.writeFloat(v); + } + catch (IOException e) + { + throw handle(e); + } + } + + protected void writeDouble(double v) throws JMSException + { + writeTypeDiscriminator(DOUBLE_TYPE); + writeDoubleImpl(v); + } + + public void writeDoubleImpl(final double v) throws JMSException + { + try + { + _data.writeDouble(v); + } + catch (IOException e) + { + throw handle(e); + } + } + + protected void writeString(String string) throws JMSException + { + if (string == null) + { + writeTypeDiscriminator(NULL_STRING_TYPE); + } + else + { + writeTypeDiscriminator(STRING_TYPE); + writeNullTerminatedStringImpl(string); + } + } + + protected void writeNullTerminatedStringImpl(String string) + throws JMSException + { + try + { + _data.write(string.getBytes(UTF8)); + _data.writeByte((byte) 0); + } + catch (IOException e) + { + throw handle(e); + } + + } + + protected void writeBytes(byte[] bytes) throws JMSException + { + writeBytes(bytes, 0, bytes == null ? 0 : bytes.length); + } + + protected void writeBytes(byte[] bytes, int offset, int length) throws JMSException + { + writeTypeDiscriminator(BYTEARRAY_TYPE); + writeBytesImpl(bytes, offset, length); + } + + public void writeBytesImpl(final byte[] bytes, final int offset, final int length) throws JMSException + { + try + { + if (bytes == null) + { + _data.writeInt(-1); + } + else + { + _data.writeInt(length); + _data.write(bytes, offset, length); + } + } + catch (IOException e) + { + throw handle(e); + } + } + + public void writeBytesRaw(final byte[] bytes, final int offset, final int length) throws JMSException + { + try + { + if (bytes != null) + { + _data.write(bytes, offset, length); + } + } + catch (IOException e) + { + throw handle(e); + } + } + + + protected void writeObject(Object object) throws JMSException + { + Class clazz; + + if (object == null) + { + // string handles the output of null values + clazz = String.class; + } + else + { + clazz = object.getClass(); + } + + if (clazz == Byte.class) + { + writeByte((Byte) object); + } + else if (clazz == Boolean.class) + { + writeBoolean((Boolean) object); + } + else if (clazz == byte[].class) + { + writeBytes((byte[]) object); + } + else if (clazz == Short.class) + { + writeShort((Short) object); + } + else if (clazz == Character.class) + { + writeChar((Character) object); + } + else if (clazz == Integer.class) + { + writeInt((Integer) object); + } + else if (clazz == Long.class) + { + writeLong((Long) object); + } + else if (clazz == Float.class) + { + writeFloat((Float) object); + } + else if (clazz == Double.class) + { + writeDouble((Double) object); + } + else if (clazz == String.class) + { + writeString((String) object); + } + else + { + throw new MessageFormatException("Only primitives plus byte arrays and String are valid types"); + } + } + + public ByteBuffer getData() + { + return ByteBuffer.wrap(_baos.toByteArray()); + } + + public void writeLengthPrefixedUTF(final String string) throws JMSException + { + try + { + CharsetEncoder encoder = UTF8.newEncoder(); + java.nio.ByteBuffer encodedString = encoder.encode(CharBuffer.wrap(string)); + + writeShortImpl((short) encodedString.limit()); + while(encodedString.hasRemaining()) + { + _data.writeByte(encodedString.get()); + } + } + catch (CharacterCodingException e) + { + JMSException jmse = new JMSException("Unable to encode string: " + e); + jmse.setLinkedException(e); + jmse.initCause(e); + throw jmse; + } + catch (IOException e) + { + throw handle(e); + } + + } +} diff --git a/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage_0_8.java b/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage_0_8.java index 685e646d85..ce87a112c9 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage_0_8.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage_0_8.java @@ -87,9 +87,9 @@ public class UnprocessedMessage_0_8 extends UnprocessedMessage public void receiveBody(ContentBody body) { - if (body.payload != null) + if (body._payload != null) { - final long payloadSize = body.payload.remaining(); + final long payloadSize = body._payload.length; if (_bodies == null) { diff --git a/java/client/src/main/java/org/apache/qpid/client/messaging/address/AddressHelper.java b/java/client/src/main/java/org/apache/qpid/client/messaging/address/AddressHelper.java index 00503cc650..368ec60525 100644 --- a/java/client/src/main/java/org/apache/qpid/client/messaging/address/AddressHelper.java +++ b/java/client/src/main/java/org/apache/qpid/client/messaging/address/AddressHelper.java @@ -27,6 +27,7 @@ import java.util.Map; import org.apache.qpid.client.AMQDestination; import org.apache.qpid.client.AMQDestination.Binding; +import org.apache.qpid.client.messaging.address.Link.Reliability; import org.apache.qpid.client.messaging.address.Link.Subscription; import org.apache.qpid.client.messaging.address.Node.ExchangeNode; import org.apache.qpid.client.messaging.address.Node.QueueNode; @@ -54,7 +55,7 @@ public class AddressHelper public static final String EXCLUSIVE = "exclusive"; public static final String AUTO_DELETE = "auto-delete"; public static final String TYPE = "type"; - public static final String ALT_EXCHANGE = "alt-exchange"; + public static final String ALT_EXCHANGE = "alternate-exchange"; public static final String BINDINGS = "bindings"; public static final String BROWSE = "browse"; public static final String MODE = "mode"; @@ -231,14 +232,9 @@ public class AddressHelper private boolean getDurability(Map map) { - if (map != null && map.get(DURABLE) != null) - { - return Boolean.parseBoolean((String)map.get(DURABLE)); - } - else - { - return false; - } + Accessor access = new MapAccessor(map); + Boolean result = access.getBoolean(DURABLE); + return (result == null) ? false : result.booleanValue(); } /** @@ -262,7 +258,7 @@ public class AddressHelper } } - public Link getLink() + public Link getLink() throws Exception { Link link = new Link(); link.setSubscription(new Subscription()); @@ -272,6 +268,25 @@ public class AddressHelper : linkProps.getBoolean(DURABLE)); link.setName(linkProps.getString(NAME)); + String reliability = linkProps.getString(RELIABILITY); + if ( reliability != null) + { + if (reliability.equalsIgnoreCase("unreliable")) + { + link.setReliability(Reliability.UNRELIABLE); + } + else if (reliability.equalsIgnoreCase("at-least-once")) + { + link.setReliability(Reliability.AT_LEAST_ONCE); + } + else + { + throw new Exception("The reliability mode '" + + reliability + "' is not yet supported"); + } + + } + if (((Map) address.getOptions().get(LINK)).get(CAPACITY) instanceof Map) { MapAccessor capacityProps = new MapAccessor( diff --git a/java/client/src/main/java/org/apache/qpid/client/messaging/address/Link.java b/java/client/src/main/java/org/apache/qpid/client/messaging/address/Link.java index a7d19d1bd5..5f97d625b4 100644 --- a/java/client/src/main/java/org/apache/qpid/client/messaging/address/Link.java +++ b/java/client/src/main/java/org/apache/qpid/client/messaging/address/Link.java @@ -20,6 +20,8 @@ */ package org.apache.qpid.client.messaging.address; +import static org.apache.qpid.client.messaging.address.Link.Reliability.UNSPECIFIED; + import java.util.HashMap; import java.util.Map; @@ -29,6 +31,8 @@ public class Link { public enum FilterType { SQL92, XQUERY, SUBJECT } + public enum Reliability { UNRELIABLE, AT_MOST_ONCE, AT_LEAST_ONCE, EXACTLY_ONCE, UNSPECIFIED } + protected String name; protected String _filter; protected FilterType _filterType = FilterType.SUBJECT; @@ -38,7 +42,18 @@ public class Link protected int _producerCapacity = 0; protected Node node; protected Subscription subscription; + protected Reliability reliability = UNSPECIFIED; + public Reliability getReliability() + { + return reliability; + } + + public void setReliability(Reliability reliability) + { + this.reliability = reliability; + } + public Node getNode() { return node; 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 eb5af119b2..284954edba 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,7 +20,9 @@ */ package org.apache.qpid.client.protocol; +import java.io.DataOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.util.ArrayList; @@ -28,10 +30,8 @@ import java.util.Iterator; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; -import org.apache.mina.filter.codec.ProtocolCodecException; import org.apache.qpid.AMQConnectionClosedException; import org.apache.qpid.AMQDisconnectedException; import org.apache.qpid.AMQException; @@ -46,6 +46,7 @@ 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.configuration.ClientProperties; import org.apache.qpid.framing.AMQBody; import org.apache.qpid.framing.AMQDataBlock; import org.apache.qpid.framing.AMQFrame; @@ -57,16 +58,13 @@ 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.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.protocol.ProtocolEngine; import org.apache.qpid.thread.Threading; -import org.apache.qpid.transport.NetworkDriver; -import org.apache.qpid.transport.network.io.IoTransport; +import org.apache.qpid.transport.Sender; +import org.apache.qpid.transport.network.NetworkConnection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -164,20 +162,22 @@ public class AMQProtocolHandler implements ProtocolEngine private FailoverException _lastFailoverException; /** Defines the default timeout to use for synchronous protocol commands. */ - private final long DEFAULT_SYNC_TIMEOUT = Long.getLong("amqj.default_syncwrite_timeout", 1000 * 30); + private final long DEFAULT_SYNC_TIMEOUT = Long.getLong(ClientProperties.QPID_SYNC_OP_TIMEOUT, + Long.getLong(ClientProperties.AMQJ_DEFAULT_SYNCWRITE_TIMEOUT, + ClientProperties.DEFAULT_SYNC_OPERATION_TIMEOUT)); /** 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; + private NetworkConnection _network; + private Sender<ByteBuffer> _sender; + /** * Creates a new protocol handler, associated with the specified client connection instance. * @@ -189,43 +189,10 @@ public class AMQProtocolHandler implements ProtocolEngine _protocolSession = new AMQProtocolSession(this, _connection); _stateManager = new AMQStateManager(_protocolSession); _codecFactory = new AMQCodecFactory(false, _protocolSession); - _poolReference.setThreadFactory(new ThreadFactory() - { - - public Thread newThread(final Runnable runnable) - { - try - { - return Threading.getThreadFactory().createThread(runnable); - } - catch (Exception e) - { - throw new RuntimeException("Failed to create thread", e); - } - } - }); - _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 - */ - public void createIoTransportSession(BrokerDetails brokerDetail) - { - _protocolSession = new AMQProtocolSession(this, _connection); - _stateManager.setProtocolSession(_protocolSession); - IoTransport.connect_0_9(getProtocolSession(), - brokerDetail.getHost(), - brokerDetail.getPort(), - brokerDetail.getBooleanProperty(BrokerDetails.OPTIONS_SSL)); - _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 * where the connection died, an attempt to failover automatically to a new connection may be started. The failover @@ -315,7 +282,7 @@ public class AMQProtocolHandler implements ProtocolEngine // failover: HeartbeatDiagnostics.timeout(); _logger.warn("Timed out while waiting for heartbeat from peer."); - _networkDriver.close(); + _network.close(); } public void writerIdle() @@ -337,22 +304,12 @@ public class AMQProtocolHandler implements ProtocolEngine { _logger.info("Exception caught therefore going to attempt failover: " + cause, cause); // this will attempt failover - _networkDriver.close(); + _network.close(); closed(); } else { - - if (cause instanceof ProtocolCodecException) - { - _logger.info("Protocol Exception caught NOT going to attempt failover as " + - "cause isn't AMQConnectionClosedException: " + cause, cause); - - AMQException amqe = new AMQException("Protocol handler error: " + cause, cause); - propagateExceptionToAllWaiters(amqe); - } _connection.exceptionReceived(cause); - } // FIXME Need to correctly handle other exceptions. Things like ... @@ -446,76 +403,63 @@ public class AMQProtocolHandler implements ProtocolEngine public void received(ByteBuffer msg) { + _readBytes += msg.remaining(); try { - _readBytes += msg.remaining(); final ArrayList<AMQDataBlock> dataBlocks = _codecFactory.getDecoder().decodeBuffer(msg); - Job.fireAsynchEvent(_poolReference.getPool(), _readJob, new Runnable() + // Decode buffer + + for (AMQDataBlock message : dataBlocks) { - public void run() - { - // Decode buffer + if (PROTOCOL_DEBUG) + { + _protocolLogger.info(String.format("RECV: [%s] %s", this, message)); + } - for (AMQDataBlock message : dataBlocks) + if(message instanceof AMQFrame) { + final boolean debug = _logger.isDebugEnabled(); + final long msgNumber = ++_messageReceivedCount; - try + if (debug && ((msgNumber % 1000) == 0)) { - 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(); - _logger.info("Broker suggested using protocol version:" + _suggestedProtocolVersion); - - // 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); + _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(); + _logger.info("Broker suggested using protocol version:" + _suggestedProtocolVersion); + + // 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); } + + } public void methodBodyReceived(final int channelId, final AMQBody bodyFrame) @@ -570,28 +514,13 @@ public class AMQProtocolHandler implements ProtocolEngine return getStateManager().createWaiter(states); } - /** - * Convenience method that writes a frame to the protocol session. Equivalent to calling - * getProtocolSession().write(). - * - * @param frame the frame to write - */ - public void writeFrame(AMQDataBlock frame) + public synchronized void writeFrame(AMQDataBlock frame) { - writeFrame(frame, false); - } - - public void writeFrame(AMQDataBlock frame, boolean wait) - { - final ByteBuffer buf = frame.toNioByteBuffer(); + final ByteBuffer buf = asByteBuffer(frame); _writtenBytes += buf.remaining(); - Job.fireAsynchEvent(_poolReference.getPool(), _writeJob, new Runnable() - { - public void run() - { - _networkDriver.send(buf); - } - }); + _sender.send(buf); + _sender.flush(); + if (PROTOCOL_DEBUG) { _protocolLogger.debug(String.format("SEND: [%s] %s", this, frame)); @@ -608,12 +537,41 @@ public class AMQProtocolHandler implements ProtocolEngine _connection.bytesSent(_writtenBytes); - if (wait) + } + + private ByteBuffer asByteBuffer(AMQDataBlock block) + { + final ByteBuffer buf = ByteBuffer.allocate((int) block.getSize()); + + try { - _networkDriver.flush(); + block.writePayload(new DataOutputStream(new OutputStream() + { + + + @Override + public void write(int b) throws IOException + { + buf.put((byte) b); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException + { + buf.put(b, off, len); + } + })); + } + catch (IOException e) + { + throw new RuntimeException(e); } + + buf.flip(); + return buf; } + /** * Convenience method that writes a frame to the protocol session and waits for a particular response. Equivalent to * calling getProtocolSession().write() then waiting for the response. @@ -707,24 +665,23 @@ public class AMQProtocolHandler implements ProtocolEngine * <p/>If a failover exception occurs whilst closing the connection it is ignored, as the connection is closed * anyway. * - * @param timeout The timeout to wait for an acknowledgement to the close request. + * @param timeout The timeout to wait for an acknowledgment to the close request. * * @throws AMQException If the close fails for any reason. */ public void closeConnection(long timeout) throws AMQException { - 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); - - //If the connection is already closed then don't do a syncWrite if (!getStateManager().getCurrentState().equals(AMQState.CONNECTION_CLOSED)) { + // Connection is already closed then don't do a syncWrite try { + final 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); + syncWrite(frame, ConnectionCloseOkBody.class, timeout); - _networkDriver.close(); + _network.close(); closed(); } catch (AMQTimeoutException e) @@ -733,10 +690,9 @@ public class AMQProtocolHandler implements ProtocolEngine } catch (FailoverException e) { - _logger.debug("FailoverException interrupted connection close, ignoring as connection close anyway."); + _logger.debug("FailoverException interrupted connection close, ignoring as connection closed anyway."); } } - _poolReference.releaseExecutorService(); } /** @return the number of bytes read from this protocol session */ @@ -844,17 +800,23 @@ public class AMQProtocolHandler implements ProtocolEngine public SocketAddress getRemoteAddress() { - return _networkDriver.getRemoteAddress(); + return _network.getRemoteAddress(); } public SocketAddress getLocalAddress() { - return _networkDriver.getLocalAddress(); + return _network.getLocalAddress(); + } + + public void setNetworkConnection(NetworkConnection network) + { + setNetworkConnection(network, network.getSender()); } - public void setNetworkDriver(NetworkDriver driver) + public void setNetworkConnection(NetworkConnection network, Sender<ByteBuffer> sender) { - _networkDriver = driver; + _network = network; + _sender = sender; } /** @param delay delay in seconds (not ms) */ @@ -862,15 +824,15 @@ public class AMQProtocolHandler implements ProtocolEngine { if (delay > 0) { - getNetworkDriver().setMaxWriteIdle(delay); - getNetworkDriver().setMaxReadIdle(HeartbeatConfig.CONFIG.getTimeout(delay)); + _network.setMaxWriteIdle(delay); + _network.setMaxReadIdle(HeartbeatConfig.CONFIG.getTimeout(delay)); HeartbeatDiagnostics.init(delay, HeartbeatConfig.CONFIG.getTimeout(delay)); } } - public NetworkDriver getNetworkDriver() + public NetworkConnection getNetworkConnection() { - return _networkDriver; + return _network; } public ProtocolVersion getSuggestedProtocolVersion() 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 7976760696..b7253e6e9c 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,27 +20,36 @@ */ package org.apache.qpid.client.protocol; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import javax.jms.JMSException; import javax.security.sasl.SaslClient; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import org.apache.qpid.AMQException; import org.apache.qpid.client.AMQConnection; import org.apache.qpid.client.AMQSession; import org.apache.qpid.client.ConnectionTuneParameters; +import org.apache.qpid.client.handler.ClientMethodDispatcherImpl; import org.apache.qpid.client.message.UnprocessedMessage; import org.apache.qpid.client.message.UnprocessedMessage_0_8; import org.apache.qpid.client.state.AMQStateManager; -import org.apache.qpid.client.state.AMQState; -import org.apache.qpid.framing.*; +import org.apache.qpid.framing.AMQDataBlock; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ContentBody; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.HeartbeatBody; +import org.apache.qpid.framing.MethodDispatcher; +import org.apache.qpid.framing.MethodRegistry; +import org.apache.qpid.framing.ProtocolInitiation; +import org.apache.qpid.framing.ProtocolVersion; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; import org.apache.qpid.transport.Sender; -import org.apache.qpid.client.handler.ClientMethodDispatcherImpl; +import org.apache.qpid.transport.TransportException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Wrapper for protocol session that provides type-safe access to session attributes. <p/> The underlying protocol @@ -148,16 +157,6 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession return getAMQConnection().getVirtualHost(); } - public String getUsername() - { - return getAMQConnection().getUsername(); - } - - public String getPassword() - { - return getAMQConnection().getPassword(); - } - public SaslClient getSaslClient() { return _saslClient; @@ -299,22 +298,11 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession return _connection.getSession(channelId); } - /** - * Convenience method that writes a frame to the protocol session. Equivalent to calling - * getProtocolSession().write(). - * - * @param frame the frame to write - */ public void writeFrame(AMQDataBlock frame) { _protocolHandler.writeFrame(frame); } - public void writeFrame(AMQDataBlock frame, boolean wait) - { - _protocolHandler.writeFrame(frame, wait); - } - /** * Starts the process of closing a session * @@ -375,7 +363,15 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession public void closeProtocolSession() throws AMQException { - _protocolHandler.closeConnection(0); + try + { + _protocolHandler.getNetworkConnection().close(); + } + catch(TransportException e) + { + //ignore such exceptions, they were already logged + //and this is a forcible close. + } } public void failover(String host, int port) diff --git a/java/client/src/main/java/org/apache/qpid/client/protocol/ProtocolBufferMonitorFilter.java b/java/client/src/main/java/org/apache/qpid/client/protocol/ProtocolBufferMonitorFilter.java deleted file mode 100644 index bbd0a7b144..0000000000 --- a/java/client/src/main/java/org/apache/qpid/client/protocol/ProtocolBufferMonitorFilter.java +++ /dev/null @@ -1,115 +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.client.protocol; - -import org.apache.mina.common.IoFilterAdapter; -import org.apache.mina.common.IoSession; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * A MINA filter that monitors the numbers of messages pending to be sent by MINA. It outputs a message - * when a threshold has been exceeded, and has a frequency configuration so that messages are not output - * too often. - * - */ -public class ProtocolBufferMonitorFilter extends IoFilterAdapter -{ - private static final Logger _logger = LoggerFactory.getLogger(ProtocolBufferMonitorFilter.class); - - public static final long DEFAULT_FREQUENCY = 5000; - - public static final int DEFAULT_THRESHOLD = 3000; - - private int _bufferedMessages = 0; - - private int _threshold; - - private long _lastMessageOutputTime; - - private long _outputFrequencyInMillis; - - public ProtocolBufferMonitorFilter() - { - _threshold = DEFAULT_THRESHOLD; - _outputFrequencyInMillis = DEFAULT_FREQUENCY; - } - - public ProtocolBufferMonitorFilter(int threshold, long frequency) - { - _threshold = threshold; - _outputFrequencyInMillis = frequency; - } - - public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception - { - _bufferedMessages++; - if (_bufferedMessages > _threshold) - { - long now = System.currentTimeMillis(); - if ((now - _lastMessageOutputTime) > _outputFrequencyInMillis) - { - _logger.warn("Protocol message buffer exceeded threshold of " + _threshold + ". Current backlog: " - + _bufferedMessages); - _lastMessageOutputTime = now; - } - } - - nextFilter.messageReceived(session, message); - } - - public void messageSent(NextFilter nextFilter, IoSession session, Object message) throws Exception - { - _bufferedMessages--; - nextFilter.messageSent(session, message); - } - - public int getBufferedMessages() - { - return _bufferedMessages; - } - - public int getThreshold() - { - return _threshold; - } - - public void setThreshold(int threshold) - { - _threshold = threshold; - } - - public long getOutputFrequencyInMillis() - { - return _outputFrequencyInMillis; - } - - public void setOutputFrequencyInMillis(long outputFrequencyInMillis) - { - _outputFrequencyInMillis = outputFrequencyInMillis; - } - - public long getLastMessageOutputTime() - { - return _lastMessageOutputTime; - } -} diff --git a/java/client/src/main/java/org/apache/qpid/client/security/AMQCallbackHandler.java b/java/client/src/main/java/org/apache/qpid/client/security/AMQCallbackHandler.java index fbca444208..67dd1a58b6 100644 --- a/java/client/src/main/java/org/apache/qpid/client/security/AMQCallbackHandler.java +++ b/java/client/src/main/java/org/apache/qpid/client/security/AMQCallbackHandler.java @@ -22,9 +22,9 @@ package org.apache.qpid.client.security; import javax.security.auth.callback.CallbackHandler; -import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.jms.ConnectionURL; public interface AMQCallbackHandler extends CallbackHandler { - void initialise(AMQProtocolSession protocolSession); + void initialise(ConnectionURL connectionURL); } diff --git a/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.java b/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.java index 140cbdeb75..14bae68561 100644 --- a/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.java +++ b/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.java @@ -20,17 +20,22 @@ */ package org.apache.qpid.client.security; -import org.apache.qpid.util.FileUtils; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.IOException; import java.io.InputStream; +import java.util.Collection; +import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Properties; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.TreeMap; + +import org.apache.qpid.util.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * CallbackHandlerRegistry is a registry for call back handlers for user authentication and interaction during user @@ -42,7 +47,7 @@ import java.util.Properties; * "amp.callbackhandler.properties". The format of the properties file is: * * <p/><pre> - * CallbackHanlder.mechanism=fully.qualified.class.name + * CallbackHanlder.n.mechanism=fully.qualified.class.name where n is an ordinal * </pre> * * <p/>Where mechanism is an IANA-registered mechanism name and the fully qualified class name refers to a @@ -66,51 +71,15 @@ public class CallbackHandlerRegistry public static final String DEFAULT_RESOURCE_NAME = "org/apache/qpid/client/security/CallbackHandlerRegistry.properties"; /** A static reference to the singleton instance of this registry. */ - private static CallbackHandlerRegistry _instance = new CallbackHandlerRegistry(); + private static final CallbackHandlerRegistry _instance; /** Holds a map from SASL mechanism names to call back handlers. */ - private Map<String, Class> _mechanismToHandlerClassMap = new HashMap<String, Class>(); - - /** Holds a space delimited list of mechanisms that callback handlers exist for. */ - private String _mechanisms; - - /** - * Gets the singleton instance of this registry. - * - * @return The singleton instance of this registry. - */ - public static CallbackHandlerRegistry getInstance() - { - return _instance; - } + private Map<String, Class<AMQCallbackHandler>> _mechanismToHandlerClassMap = new HashMap<String, Class<AMQCallbackHandler>>(); - /** - * Gets the callback handler class for a given SASL mechanism name. - * - * @param mechanism The SASL mechanism name. - * - * @return The callback handler class for the mechanism, or null if none is configured for that mechanism. - */ - public Class getCallbackHandlerClass(String mechanism) - { - return (Class) _mechanismToHandlerClassMap.get(mechanism); - } + /** Ordered collection of mechanisms for which callback handlers exist. */ + private Collection<String> _mechanisms; - /** - * Gets a space delimited list of supported SASL mechanisms. - * - * @return A space delimited list of supported SASL mechanisms. - */ - public String getMechanisms() - { - return _mechanisms; - } - - /** - * Creates the call back handler registry from its configuration resource or file. This also has the side effect - * of configuring and registering the SASL client factory implementations using {@link DynamicSaslRegistrar}. - */ - private CallbackHandlerRegistry() + static { // Register any configured SASL client factories. DynamicSaslRegistrar.registerSaslProviders(); @@ -120,12 +89,12 @@ public class CallbackHandlerRegistry FileUtils.openFileOrDefaultResource(filename, DEFAULT_RESOURCE_NAME, CallbackHandlerRegistry.class.getClassLoader()); + final Properties props = new Properties(); + try { - Properties props = new Properties(); + props.load(is); - parseProperties(props); - _logger.info("Callback handlers available for SASL mechanisms: " + _mechanisms); } catch (IOException e) { @@ -146,32 +115,68 @@ public class CallbackHandlerRegistry } } } + + _instance = new CallbackHandlerRegistry(props); + _logger.info("Callback handlers available for SASL mechanisms: " + _instance._mechanisms); + } - /*private InputStream openPropertiesInputStream(String filename) + /** + * Gets the singleton instance of this registry. + * + * @return The singleton instance of this registry. + */ + public static CallbackHandlerRegistry getInstance() + { + return _instance; + } + + public AMQCallbackHandler createCallbackHandler(final String mechanism) { - boolean useDefault = true; - InputStream is = null; - if (filename != null) + final Class<AMQCallbackHandler> mechanismClass = _mechanismToHandlerClassMap.get(mechanism); + + if (mechanismClass == null) { - try - { - is = new BufferedInputStream(new FileInputStream(new File(filename))); - useDefault = false; - } - catch (FileNotFoundException e) - { - _logger.error("Unable to read from file " + filename + ": " + e, e); - } + throw new IllegalArgumentException("Mechanism " + mechanism + " not known"); } - if (useDefault) + try + { + return mechanismClass.newInstance(); + } + catch (InstantiationException e) + { + throw new IllegalArgumentException("Unable to create an instance of mechanism " + mechanism, e); + } + catch (IllegalAccessException e) { - is = CallbackHandlerRegistry.class.getResourceAsStream(DEFAULT_RESOURCE_NAME); + throw new IllegalArgumentException("Unable to create an instance of mechanism " + mechanism, e); } + } - return is; - }*/ + /** + * Gets collections of supported SASL mechanism names, ordered by preference + * + * @return collection of SASL mechanism names. + */ + public Collection<String> getMechanisms() + { + return Collections.unmodifiableCollection(_mechanisms); + } + + /** + * Creates the call back handler registry from its configuration resource or file. + * + * This also has the side effect of configuring and registering the SASL client factory + * implementations using {@link DynamicSaslRegistrar}. + * + * This constructor is default protection to allow for effective unit testing. Clients must use + * {@link #getInstance()} to obtain the singleton instance. + */ + CallbackHandlerRegistry(final Properties props) + { + parseProperties(props); + } /** * Scans the specified properties as a mapping from IANA registered SASL mechanism to call back handler @@ -183,20 +188,20 @@ public class CallbackHandlerRegistry */ private void parseProperties(Properties props) { + + final Map<Integer, String> mechanisms = new TreeMap<Integer, String>(); + Enumeration e = props.propertyNames(); while (e.hasMoreElements()) { - String propertyName = (String) e.nextElement(); - int period = propertyName.indexOf("."); - if (period < 0) - { - _logger.warn("Unable to parse property " + propertyName + " when configuring SASL providers"); + final String propertyName = (String) e.nextElement(); + final String[] parts = propertyName.split("\\.", 2); - continue; - } + checkPropertyNameFormat(propertyName, parts); - String mechanism = propertyName.substring(period + 1); - String className = props.getProperty(propertyName); + final String mechanism = parts[0]; + final int ordinal = getPropertyOrdinal(propertyName, parts); + final String className = props.getProperty(propertyName); Class clazz = null; try { @@ -205,20 +210,11 @@ public class CallbackHandlerRegistry { _logger.warn("SASL provider " + clazz + " does not implement " + AMQCallbackHandler.class + ". Skipping"); - continue; } - _mechanismToHandlerClassMap.put(mechanism, clazz); - if (_mechanisms == null) - { - _mechanisms = mechanism; - } - else - { - // one time cost - _mechanisms = _mechanisms + " " + mechanism; - } + + mechanisms.put(ordinal, mechanism); } catch (ClassNotFoundException ex) { @@ -227,5 +223,91 @@ public class CallbackHandlerRegistry continue; } } + + _mechanisms = mechanisms.values(); // order guaranteed by keys of treemap (i.e. our ordinals) + + + } + + private void checkPropertyNameFormat(final String propertyName, final String[] parts) + { + if (parts.length != 2) + { + throw new IllegalArgumentException("Unable to parse property " + propertyName + " when configuring SASL providers"); + } + } + + private int getPropertyOrdinal(final String propertyName, final String[] parts) + { + try + { + return Integer.parseInt(parts[1]); + } + catch(NumberFormatException nfe) + { + throw new IllegalArgumentException("Unable to parse property " + propertyName + " when configuring SASL providers", nfe); + } + } + + /** + * Selects a SASL mechanism that is mutually available to both parties. If more than one + * mechanism is mutually available the one appearing first (by ordinal) will be returned. + * + * @param peerMechanismList space separated list of mechanisms + * @return selected mechanism, or null if none available + */ + public String selectMechanism(final String peerMechanismList) + { + final Set<String> peerList = mechListToSet(peerMechanismList); + + return selectMechInternal(peerList, Collections.<String>emptySet()); + } + + /** + * Selects a SASL mechanism that is mutually available to both parties. + * + * @param peerMechanismList space separated list of mechanisms + * @param restrictionList space separated list of mechanisms + * @return selected mechanism, or null if none available + */ + public String selectMechanism(final String peerMechanismList, final String restrictionList) + { + final Set<String> peerList = mechListToSet(peerMechanismList); + final Set<String> restrictionSet = mechListToSet(restrictionList); + + return selectMechInternal(peerList, restrictionSet); + } + + private String selectMechInternal(final Set<String> peerSet, final Set<String> restrictionSet) + { + for (final String mech : _mechanisms) + { + if (peerSet.contains(mech)) + { + if (restrictionSet.isEmpty() || restrictionSet.contains(mech)) + { + return mech; + } + } + } + + return null; + } + + private Set<String> mechListToSet(final String mechanismList) + { + if (mechanismList == null) + { + return Collections.emptySet(); + } + + final StringTokenizer tokenizer = new StringTokenizer(mechanismList, " "); + final Set<String> mechanismSet = new HashSet<String>(tokenizer.countTokens()); + while (tokenizer.hasMoreTokens()) + { + mechanismSet.add(tokenizer.nextToken()); + } + return Collections.unmodifiableSet(mechanismSet); } + } 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 1fcfde3579..b04a756e80 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 @@ -16,7 +16,17 @@ # specific language governing permissions and limitations # under the License. # -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 + +# +# Format: +# <mechanism name>.ordinal=<implementation> +# +# @see CallbackHandlerRegistry +# + +EXTERNAL.1=org.apache.qpid.client.security.UsernamePasswordCallbackHandler +GSSAPI.2=org.apache.qpid.client.security.UsernamePasswordCallbackHandler +CRAM-MD5-HASHED.3=org.apache.qpid.client.security.UsernameHashedPasswordCallbackHandler +CRAM-MD5.4=org.apache.qpid.client.security.UsernamePasswordCallbackHandler +AMQPLAIN.5=org.apache.qpid.client.security.UsernamePasswordCallbackHandler +PLAIN.6=org.apache.qpid.client.security.UsernamePasswordCallbackHandler diff --git a/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties b/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties index 1bff43142b..b903208927 100644 --- a/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties +++ b/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties @@ -18,3 +18,4 @@ # AMQPLAIN=org.apache.qpid.client.security.amqplain.AmqPlainSaslClientFactory CRAM-MD5-HASHED=org.apache.qpid.client.security.crammd5hashed.CRAMMD5HashedSaslClientFactory +ANONYMOUS=org.apache.qpid.client.security.anonymous.AnonymousSaslClientFactory diff --git a/java/client/src/main/java/org/apache/qpid/client/security/UsernameHashedPasswordCallbackHandler.java b/java/client/src/main/java/org/apache/qpid/client/security/UsernameHashedPasswordCallbackHandler.java index 66176dac3c..6ec83f0a23 100644 --- a/java/client/src/main/java/org/apache/qpid/client/security/UsernameHashedPasswordCallbackHandler.java +++ b/java/client/src/main/java/org/apache/qpid/client/security/UsernameHashedPasswordCallbackHandler.java @@ -20,30 +20,29 @@ */ package org.apache.qpid.client.security; -import org.apache.qpid.client.protocol.AMQProtocolSession; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import javax.security.auth.callback.Callback; import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.UnsupportedCallbackException; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; +import org.apache.qpid.jms.ConnectionURL; public class UsernameHashedPasswordCallbackHandler implements AMQCallbackHandler { - private static final Logger _logger = LoggerFactory.getLogger(UsernameHashedPasswordCallbackHandler.class); + private ConnectionURL _connectionURL; - private AMQProtocolSession _protocolSession; - - public void initialise(AMQProtocolSession protocolSession) + /** + * @see org.apache.qpid.client.security.AMQCallbackHandler#initialise(org.apache.qpid.jms.ConnectionURL) + */ + @Override + public void initialise(ConnectionURL connectionURL) { - _protocolSession = protocolSession; + _connectionURL = connectionURL; } public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException @@ -53,13 +52,13 @@ public class UsernameHashedPasswordCallbackHandler implements AMQCallbackHandler Callback cb = callbacks[i]; if (cb instanceof NameCallback) { - ((NameCallback) cb).setName(_protocolSession.getUsername()); + ((NameCallback) cb).setName(_connectionURL.getUsername()); } else if (cb instanceof PasswordCallback) { try { - ((PasswordCallback) cb).setPassword(getHash(_protocolSession.getPassword())); + ((PasswordCallback) cb).setPassword(getHash(_connectionURL.getPassword())); } catch (NoSuchAlgorithmException e) { @@ -99,4 +98,5 @@ public class UsernameHashedPasswordCallbackHandler implements AMQCallbackHandler return hash; } + } diff --git a/java/client/src/main/java/org/apache/qpid/client/security/UsernamePasswordCallbackHandler.java b/java/client/src/main/java/org/apache/qpid/client/security/UsernamePasswordCallbackHandler.java index c50c62710f..ad088722c8 100644 --- a/java/client/src/main/java/org/apache/qpid/client/security/UsernamePasswordCallbackHandler.java +++ b/java/client/src/main/java/org/apache/qpid/client/security/UsernamePasswordCallbackHandler.java @@ -27,15 +27,19 @@ import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.UnsupportedCallbackException; -import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.jms.ConnectionURL; public class UsernamePasswordCallbackHandler implements AMQCallbackHandler { - private AMQProtocolSession _protocolSession; + private ConnectionURL _connectionURL; - public void initialise(AMQProtocolSession protocolSession) + /** + * @see org.apache.qpid.client.security.AMQCallbackHandler#initialise(org.apache.qpid.jms.ConnectionURL) + */ + @Override + public void initialise(final ConnectionURL connectionURL) { - _protocolSession = protocolSession; + _connectionURL = connectionURL; } public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException @@ -45,11 +49,11 @@ public class UsernamePasswordCallbackHandler implements AMQCallbackHandler Callback cb = callbacks[i]; if (cb instanceof NameCallback) { - ((NameCallback)cb).setName(_protocolSession.getUsername()); + ((NameCallback)cb).setName(_connectionURL.getUsername()); } else if (cb instanceof PasswordCallback) { - ((PasswordCallback)cb).setPassword(_protocolSession.getPassword().toCharArray()); + ((PasswordCallback)cb).setPassword(_connectionURL.getPassword().toCharArray()); } else { @@ -57,4 +61,5 @@ public class UsernamePasswordCallbackHandler implements AMQCallbackHandler } } } + } diff --git a/java/client/src/main/java/org/apache/qpid/client/security/anonymous/AnonymousSaslClient.java b/java/client/src/main/java/org/apache/qpid/client/security/anonymous/AnonymousSaslClient.java new file mode 100644 index 0000000000..0f56b2ef6c --- /dev/null +++ b/java/client/src/main/java/org/apache/qpid/client/security/anonymous/AnonymousSaslClient.java @@ -0,0 +1,52 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.security.anonymous; + +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; + +public class AnonymousSaslClient implements SaslClient +{ + public String getMechanismName() { + return "ANONYMOUS"; + } + public boolean hasInitialResponse() { + return true; + } + public byte[] evaluateChallenge(byte[] challenge) throws SaslException { + return new byte[0]; + } + public boolean isComplete() { + return true; + } + public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException + { + throw new IllegalStateException("No security layer supported"); + } + public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException + { + throw new IllegalStateException("No security layer supported"); + } + public Object getNegotiatedProperty(String propName) { + return null; + } + public void dispose() throws SaslException {} +} diff --git a/java/client/src/main/java/org/apache/qpid/client/security/anonymous/AnonymousSaslClientFactory.java b/java/client/src/main/java/org/apache/qpid/client/security/anonymous/AnonymousSaslClientFactory.java new file mode 100644 index 0000000000..de698f87c6 --- /dev/null +++ b/java/client/src/main/java/org/apache/qpid/client/security/anonymous/AnonymousSaslClientFactory.java @@ -0,0 +1,52 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.security.anonymous; + +import java.util.Arrays; +import java.util.Map; + +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslClientFactory; +import javax.security.sasl.SaslException; +import javax.security.auth.callback.CallbackHandler; + +public class AnonymousSaslClientFactory implements SaslClientFactory +{ + public SaslClient createSaslClient(String[] mechanisms, String authId, + String protocol, String server, + Map props, CallbackHandler cbh) throws SaslException + { + if (Arrays.asList(mechanisms).contains("ANONYMOUS")) { + return new AnonymousSaslClient(); + } else { + return null; + } + } + public String[] getMechanismNames(Map props) + { + if (props == null || props.isEmpty()) { + return new String[]{"ANONYMOUS"}; + } else { + return new String[0]; + } + } +} 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 9c7d62670c..0d6fc727c1 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,7 +31,6 @@ 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/> @@ -48,7 +47,7 @@ import java.io.IOException; * * The two step process is required as there is an inherit race condition between starting a process that will cause * the state to change and then attempting to wait for that change. The interest in the change must be first set up so - * that any asynchrous errors that occur can be delivered to the correct waiters. + * that any asynchronous errors that occur can be delivered to the correct waiters. */ public class AMQStateManager implements AMQMethodListener { @@ -84,7 +83,10 @@ public class AMQStateManager implements AMQMethodListener public AMQState getCurrentState() { - return _currentState; + synchronized (_stateLock) + { + return _currentState; + } } public void changeState(AMQState newState) @@ -114,7 +116,7 @@ public class AMQStateManager implements AMQMethodListener } /** - * Setting of the ProtocolSession will be required when Failover has been successfuly compeleted. + * Setting of the ProtocolSession will be required when Failover has been successfully completed. * * The new {@link AMQProtocolSession} that has been re-established needs to be provided as that is now the * connection to the network. @@ -131,9 +133,9 @@ public class AMQStateManager implements AMQMethodListener } /** - * Propogate error to waiters + * Propagate error to waiters * - * @param error The error to propogate. + * @param error The error to propagate. */ public void error(Exception error) { @@ -177,7 +179,7 @@ public class AMQStateManager implements AMQMethodListener } /** - * Create and add a new waiter to the notifcation list. + * Create and add a new waiter to the notification list. * * @param states The waiter will attempt to wait for one of these desired set states to be achived. * 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 79f438d35d..732480e1c9 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 @@ -34,7 +34,7 @@ import java.util.Set; * * On construction the current state and a set of States to await for is provided. * - * When await() is called the state at constuction is compared against the awaitStates. If the state at construction is + * When await() is called the state at construction is compared against the awaitStates. If the state at construction is * a desired state then await() returns immediately. * * Otherwise it will block for the set timeout for a desired state to be achieved. @@ -48,9 +48,9 @@ public class StateWaiter extends BlockingWaiter<AMQState> { private static final Logger _logger = LoggerFactory.getLogger(StateWaiter.class); - Set<AMQState> _awaitStates; - private AMQState _startState; - private AMQStateManager _stateManager; + private final Set<AMQState> _awaitStates; + private final AMQState _startState; + private final AMQStateManager _stateManager; /** * @@ -78,9 +78,9 @@ public class StateWaiter extends BlockingWaiter<AMQState> } /** - * Await for the requried State to be achieved within the default timeout. + * Await for the required State to be achieved within the default timeout. * @return The achieved state that was requested. - * @throws AMQException The exception that prevented the required state from being achived. + * @throws AMQException The exception that prevented the required state from being achieved. */ public AMQState await() throws AMQException { @@ -88,13 +88,13 @@ public class StateWaiter extends BlockingWaiter<AMQState> } /** - * Await for the requried State to be achieved. + * Await for the required State to be achieved. * * <b>It is the responsibility of this class to remove the waiter from the StateManager * - * @param timeout The time in milliseconds to wait for any of the states to be achived. + * @param timeout The time in milliseconds to wait for any of the states to be achieved. * @return The achieved state that was requested. - * @throws AMQException The exception that prevented the required state from being achived. + * @throws AMQException The exception that prevented the required state from being achieved. */ public AMQState await(long timeout) throws AMQException { diff --git a/java/client/src/main/java/org/apache/qpid/client/transport/ClientConnectionDelegate.java b/java/client/src/main/java/org/apache/qpid/client/transport/ClientConnectionDelegate.java new file mode 100644 index 0000000000..1b483f6948 --- /dev/null +++ b/java/client/src/main/java/org/apache/qpid/client/transport/ClientConnectionDelegate.java @@ -0,0 +1,168 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.transport; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; + +import org.apache.qpid.client.security.AMQCallbackHandler; +import org.apache.qpid.client.security.CallbackHandlerRegistry; +import org.apache.qpid.jms.ConnectionURL; +import org.apache.qpid.transport.ClientDelegate; +import org.apache.qpid.transport.Connection; +import org.apache.qpid.transport.ConnectionException; +import org.apache.qpid.transport.ConnectionOpenOk; +import org.apache.qpid.transport.ConnectionSettings; +import org.apache.qpid.transport.util.Logger; +import org.apache.qpid.util.Strings; +import org.ietf.jgss.GSSContext; +import org.ietf.jgss.GSSException; +import org.ietf.jgss.GSSManager; +import org.ietf.jgss.GSSName; +import org.ietf.jgss.Oid; + +/** + * + */ +public class ClientConnectionDelegate extends ClientDelegate +{ + private static final Logger LOGGER = Logger.get(ClientDelegate.class); + + private static final String KRB5_OID_STR = "1.2.840.113554.1.2.2"; + protected static final Oid KRB5_OID; + + static + { + Oid oid; + try + { + oid = new Oid(KRB5_OID_STR); + } + catch (GSSException ignore) + { + oid = null; + } + + KRB5_OID = oid; + } + + private final ConnectionURL _connectionURL; + + /** + * @param settings + * @param connectionURL + */ + public ClientConnectionDelegate(ConnectionSettings settings, ConnectionURL connectionURL) + { + super(settings); + this._connectionURL = connectionURL; + } + + @Override + protected SaslClient createSaslClient(List<Object> brokerMechs) throws ConnectionException, SaslException + { + final String brokerMechanisms = Strings.join(" ", brokerMechs); + final String restrictionList = _conSettings.getSaslMechs(); + final String selectedMech = CallbackHandlerRegistry.getInstance().selectMechanism(brokerMechanisms, restrictionList); + if (selectedMech == null) + { + throw new ConnectionException("Client and broker have no SASL mechanisms in common." + + " Broker allows : " + brokerMechanisms + + " Client has : " + CallbackHandlerRegistry.getInstance().getMechanisms() + + " Client restricted itself to : " + (restrictionList != null ? restrictionList : "no restriction")); + } + + Map<String,Object> saslProps = new HashMap<String,Object>(); + if (_conSettings.isUseSASLEncryption()) + { + saslProps.put(Sasl.QOP, "auth-conf"); + } + + final AMQCallbackHandler handler = CallbackHandlerRegistry.getInstance().createCallbackHandler(selectedMech); + handler.initialise(_connectionURL); + final SaslClient sc = Sasl.createSaslClient(new String[] {selectedMech}, null, _conSettings.getSaslProtocol(), _conSettings.getSaslServerName(), saslProps, handler); + + return sc; + } + + @Override + public void connectionOpenOk(Connection conn, ConnectionOpenOk ok) + { + SaslClient sc = conn.getSaslClient(); + if (sc != null) + { + if (sc.getMechanismName().equals("GSSAPI")) + { + String id = getKerberosUser(); + if (id != null) + { + conn.setUserID(id); + } + } + else if (sc.getMechanismName().equals("EXTERNAL")) + { + if (conn.getSecurityLayer() != null) + { + conn.setUserID(conn.getSecurityLayer().getUserID()); + } + } + } + + super.connectionOpenOk(conn, ok); + } + + private String getKerberosUser() + { + LOGGER.debug("Obtaining userID from kerberos"); + String service = _conSettings.getSaslProtocol() + "@" + _conSettings.getSaslServerName(); + GSSManager manager = GSSManager.getInstance(); + + try + { + GSSName acceptorName = manager.createName(service, + GSSName.NT_HOSTBASED_SERVICE, KRB5_OID); + + GSSContext secCtx = manager.createContext(acceptorName, + KRB5_OID, + null, + GSSContext.INDEFINITE_LIFETIME); + + secCtx.initSecContext(new byte[0], 0, 1); + + if (secCtx.getSrcName() != null) + { + return secCtx.getSrcName().toString(); + } + + } + catch (GSSException e) + { + LOGGER.warn("Unable to retrieve userID from Kerberos due to error",e); + } + + 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 deleted file mode 100644 index 1ac8f62e32..0000000000 --- a/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java +++ /dev/null @@ -1,90 +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.client.transport; - -import java.io.IOException; -import java.net.InetSocketAddress; - -import org.apache.mina.common.ByteBuffer; -import org.apache.mina.common.IoConnector; -import org.apache.mina.common.SimpleByteBufferAllocator; -import org.apache.qpid.client.SSLConfiguration; -import org.apache.qpid.client.protocol.AMQProtocolHandler; -import org.apache.qpid.jms.BrokerDetails; -import org.apache.qpid.ssl.SSLContextFactory; -import org.apache.qpid.transport.network.mina.MINANetworkDriver; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class SocketTransportConnection implements ITransportConnection -{ - private static final Logger _logger = LoggerFactory.getLogger(SocketTransportConnection.class); - private static final int DEFAULT_BUFFER_SIZE = 32 * 1024; - - private SocketConnectorFactory _socketConnectorFactory; - - static interface SocketConnectorFactory - { - IoConnector newSocketConnector(); - } - - public SocketTransportConnection(SocketConnectorFactory socketConnectorFactory) - { - _socketConnectorFactory = socketConnectorFactory; - } - - public void connect(AMQProtocolHandler protocolHandler, BrokerDetails brokerDetail) throws IOException - { - ByteBuffer.setUseDirectBuffers(Boolean.getBoolean("amqj.enableDirectBuffers")); - - // the MINA default is currently to use the pooled allocator although this may change in future - // once more testing of the performance of the simple allocator has been done - if (!Boolean.getBoolean("amqj.enablePooledAllocator")) - { - _logger.info("Using SimpleByteBufferAllocator"); - ByteBuffer.setAllocator(new SimpleByteBufferAllocator()); - } - - final IoConnector ioConnector = _socketConnectorFactory.newSocketConnector(); - final InetSocketAddress address; - - if (brokerDetail.getTransport().equals(BrokerDetails.SOCKET)) - { - address = null; - } - else - { - address = new InetSocketAddress(brokerDetail.getHost(), brokerDetail.getPort()); - _logger.info("Attempting connection to " + address); - } - - SSLConfiguration sslConfig = protocolHandler.getConnection().getSSLConfiguration(); - SSLContextFactory sslFactory = null; - if (sslConfig != null) - { - 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 deleted file mode 100644 index aef3a563af..0000000000 --- a/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java +++ /dev/null @@ -1,351 +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.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; -import org.apache.mina.transport.socket.nio.ExistingSocketConnector; -import org.apache.mina.transport.socket.nio.MultiThreadSocketConnector; -import org.apache.mina.transport.socket.nio.SocketConnector; -import org.apache.mina.transport.vmpipe.VmPipeAcceptor; -import org.apache.mina.transport.vmpipe.VmPipeAddress; -import org.apache.qpid.client.vmbroker.AMQVMBrokerCreationException; -import org.apache.qpid.jms.BrokerDetails; -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; - -/** - * 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 - * protocol events. <p/> Could be extended in future to support different transport types by turning this into concrete - * class/interface combo. - */ -public class TransportConnection -{ - private static ITransportConnection _instance; - - private static final Map _inVmPipeAddress = new HashMap(); - private static VmPipeAcceptor _acceptor; - private static int _currentInstance = -1; - private static int _currentVMPort = -1; - - private static final int TCP = 0; - private static final int VM = 1; - private static final int SOCKET = 2; - - private static Logger _logger = LoggerFactory.getLogger(TransportConnection.class); - - private static final String DEFAULT_QPID_SERVER = "org.apache.qpid.server.protocol.AMQProtocolEngineFactory"; - - private static Map<String, Socket> _openSocketRegister = new ConcurrentHashMap<String, Socket>(); - - public static void registerOpenSocket(String socketID, Socket openSocket) - { - _openSocketRegister.put(socketID, openSocket); - } - - public static Socket removeOpenSocket(String socketID) - { - return _openSocketRegister.remove(socketID); - } - - public static synchronized ITransportConnection getInstance(final BrokerDetails details) throws AMQTransportConnectionException - { - int transport = getTransport(details.getTransport()); - - if (transport == -1) - { - throw new AMQNoTransportForProtocolException(details, null, null); - } - - switch (transport) - { - case SOCKET: - return new SocketTransportConnection(new SocketTransportConnection.SocketConnectorFactory() - { - public IoConnector newSocketConnector() - { - 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: - return new SocketTransportConnection(new SocketTransportConnection.SocketConnectorFactory() - { - public IoConnector newSocketConnector() - { - SocketConnector result; - // FIXME - this needs to be sorted to use the new Mina MultiThread SA. - if (Boolean.getBoolean("qpidnio")) - { - _logger.warn("Using Qpid MultiThreaded NIO - " + (System.getProperties().containsKey("qpidnio") - ? "Qpid NIO is new default" - : "Sysproperty 'qpidnio' is set")); - result = new MultiThreadSocketConnector(1, new QpidThreadExecutor()); - } - else - { - _logger.info("Using Mina NIO"); - 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 - // clients (like unit tests) to complete quickly. - result.setWorkerTimeout(0); - return result; - } - }); - case VM: - { - return getVMTransport(details, Boolean.getBoolean("amqj.AutoCreateVMBroker")); - } - default: - throw new AMQNoTransportForProtocolException(details, "Transport not recognised:" + transport, null); - } - } - - private static int getTransport(String transport) - { - if (transport.equals(BrokerDetails.SOCKET)) - { - return SOCKET; - } - - if (transport.equals(BrokerDetails.TCP)) - { - return TCP; - } - - if (transport.equals(BrokerDetails.VM)) - { - return VM; - } - - return -1; - } - - private static ITransportConnection getVMTransport(BrokerDetails details, boolean AutoCreate) - throws AMQVMBrokerCreationException - { - int port = details.getPort(); - - synchronized (_inVmPipeAddress) - { - if (!_inVmPipeAddress.containsKey(port)) - { - if (AutoCreate) - { - _logger.warn("Auto Creating InVM Broker on port:" + port); - createVMBroker(port); - } - else - { - throw new AMQVMBrokerCreationException(null, port, "VM Broker on port " + port - + " does not exist. Auto create disabled.", null); - } - } - } - - return new VmPipeTransportConnection(port); - } - - public static void createVMBroker(int port) throws AMQVMBrokerCreationException - { - synchronized(TransportConnection.class) - { - if (_acceptor == null) - { - _acceptor = new VmPipeAcceptor(); - - IoServiceConfig config = _acceptor.getDefaultConfig(); - } - } - synchronized (_inVmPipeAddress) - { - - if (!_inVmPipeAddress.containsKey(port)) - { - _logger.info("Creating InVM Qpid.AMQP listening on port " + port); - IoHandlerAdapter provider = null; - try - { - VmPipeAddress pipe = new VmPipeAddress(port); - - provider = createBrokerInstance(port); - - _acceptor.bind(pipe, provider); - - _inVmPipeAddress.put(port, pipe); - _logger.info("Created InVM Qpid.AMQP listening on port " + port); - } - catch (IOException e) - { - _logger.error("Got IOException.", e); - - // Try and unbind provider - try - { - VmPipeAddress pipe = new VmPipeAddress(port); - - try - { - _acceptor.unbind(pipe); - } - catch (Exception ignore) - { - // ignore - } - - if (provider == null) - { - provider = createBrokerInstance(port); - } - - _acceptor.bind(pipe, provider); - _inVmPipeAddress.put(port, pipe); - _logger.info("Created InVM Qpid.AMQP listening on port " + port); - } - catch (IOException justUseFirstException) - { - String because; - if (e.getCause() == null) - { - because = e.toString(); - } - else - { - because = e.getCause().toString(); - } - - throw new AMQVMBrokerCreationException(null, port, because + " Stopped binding of InVM Qpid.AMQP", e); - } - } - - } - else - { - _logger.info("InVM Qpid.AMQP on port " + port + " already exits."); - } - } - } - - private static IoHandlerAdapter createBrokerInstance(int port) throws AMQVMBrokerCreationException - { - String protocolProviderClass = System.getProperty("amqj.protocolprovider.class", DEFAULT_QPID_SERVER); - _logger.info("Creating Qpid protocol provider: " + protocolProviderClass); - - // can't use introspection to get Provider as it is a server class. - // need to go straight to IoHandlerAdapter but that requries the queues and exchange from the ApplicationRegistry which we can't access. - - // get right constructor and pass in instancec ID - "port" - IoHandlerAdapter provider; - try - { - Class[] cnstr = {Integer.class}; - Object[] params = {port}; - - 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); - } - catch (Exception e) - { - _logger.info("Unable to create InVM Qpid.AMQP on port " + port + ". Because: " + e.getCause()); - String because; - if (e.getCause() == null) - { - because = e.toString(); - } - else - { - because = e.getCause().toString(); - } - - AMQVMBrokerCreationException amqbce = - new AMQVMBrokerCreationException(null, port, because + " Stopped InVM Qpid.AMQP creation", e); - throw amqbce; - } - - return provider; - } - - public static void killAllVMBrokers() - { - _logger.info("Killing all VM Brokers"); - synchronized(TransportConnection.class) - { - if (_acceptor != null) - { - _acceptor.unbindAll(); - } - synchronized (_inVmPipeAddress) - { - _inVmPipeAddress.clear(); - } - _acceptor = null; - } - _currentInstance = -1; - _currentVMPort = -1; - } - - public static void killVMBroker(int port) - { - synchronized (_inVmPipeAddress) - { - VmPipeAddress pipe = (VmPipeAddress) _inVmPipeAddress.get(port); - if (pipe != null) - { - _logger.info("Killing VM Broker:" + port); - _inVmPipeAddress.remove(port); - // This does need to be sychronized as otherwise mina can hang - // if a new connection is made - _acceptor.unbind(pipe); - } - } - } - -} diff --git a/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java b/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java deleted file mode 100644 index 87cc2e7a5a..0000000000 --- a/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java +++ /dev/null @@ -1,63 +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.client.transport; - -import java.io.IOException; - -import org.apache.mina.common.ConnectFuture; -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.transport.network.mina.MINANetworkDriver; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class VmPipeTransportConnection implements ITransportConnection -{ - private static final Logger _logger = LoggerFactory.getLogger(VmPipeTransportConnection.class); - - private int _port; - - private MINANetworkDriver _networkDriver; - - public VmPipeTransportConnection(int port) - { - _port = port; - } - - public void connect(AMQProtocolHandler protocolHandler, BrokerDetails brokerDetail) throws IOException - { - final VmPipeConnector ioConnector = new QpidVmPipeConnector(); - - final VmPipeAddress address = new VmPipeAddress(_port); - _logger.info("Attempting connection to " + address); - _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 f3f74dd332..03167561ef 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 @@ -45,7 +45,7 @@ public class URLParser private void parseURL(String fullURL) throws URLSyntaxException { // Connection URL format - // amqp://[user:pass@][clientid]/virtualhost?brokerlist='tcp://host:port?option=\'value\',option=\'value\';vm://:3/virtualpath?option=\'value\'',failover='method?option=\'value\',option='value''" + // amqp://[user:pass@][clientid]/virtualhost?brokerlist='tcp://host:port?option=\'value\',option=\'value\';tcp://host:port?option=\'value\'',failover='method?option=\'value\',option='value''" // Options are of course optional except for requiring a single broker in the broker list. try { @@ -195,7 +195,7 @@ public class URLParser { String brokerlist = _url.getOptions().get(AMQConnectionURL.OPTIONS_BROKERLIST); - // brokerlist tcp://host:port?option='value',option='value';vm://:3/virtualpath?option='value' + // brokerlist tcp://host:port?option='value',option='value';tcp://host:port/virtualpath?option='value' StringTokenizer st = new StringTokenizer(brokerlist, "" + URLHelper.BROKER_SEPARATOR); while (st.hasMoreTokens()) 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 208658a5ff..bec41644fc 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 @@ -28,9 +28,8 @@ import java.util.concurrent.locks.ReentrantLock; import org.apache.qpid.AMQException; import org.apache.qpid.AMQTimeoutException; import org.apache.qpid.client.failover.FailoverException; -import org.apache.qpid.framing.AMQMethodBody; -import org.apache.qpid.protocol.AMQMethodEvent; -import org.apache.qpid.protocol.AMQMethodListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * BlockingWaiter is a 'rendezvous' which delegates handling of @@ -64,6 +63,8 @@ import org.apache.qpid.protocol.AMQMethodListener; */ public abstract class BlockingWaiter<T> { + private static final Logger _logger = LoggerFactory.getLogger(BlockingWaiter.class); + /** This flag is used to indicate that the blocked for method has been received. */ private volatile boolean _ready = false; @@ -180,7 +181,7 @@ public abstract class BlockingWaiter<T> } catch (InterruptedException e) { - System.err.println(e.getMessage()); + _logger.error(e.getMessage(), e); // IGNORE -- //fixme this isn't ideal as being interrupted isn't equivellant to sucess // if (!_ready && timeout != -1) // { @@ -228,12 +229,12 @@ public abstract class BlockingWaiter<T> } /** - * This is a callback, called when an error has occured that should interupt any waiter. + * This is a callback, called when an error has occurred that should interrupt any waiter. * It is also called from within this class to avoid code repetition but it should only be called by the MINA threads. * * Once closed any notification of an exception will be ignored. * - * @param e The exception being propogated. + * @param e The exception being propagated. */ public void error(Exception e) { @@ -255,7 +256,7 @@ public abstract class BlockingWaiter<T> } else { - System.err.println("WARNING: new error '" + e == null ? "null" : e.getMessage() + "' arrived while old one not yet processed:" + _error.getMessage()); + _logger.error("WARNING: new error '" + e == null ? "null" : e.getMessage() + "' arrived while old one not yet processed:" + _error.getMessage()); } if (_waiting.get()) @@ -272,7 +273,7 @@ public abstract class BlockingWaiter<T> } catch (InterruptedException e1) { - System.err.println(e.getMessage()); + _logger.error(e1.getMessage(), e1); } } _errorAck = false; diff --git a/java/client/src/main/java/org/apache/qpid/client/util/ClassLoadingAwareObjectInputStream.java b/java/client/src/main/java/org/apache/qpid/client/util/ClassLoadingAwareObjectInputStream.java new file mode 100644 index 0000000000..669a0f1abf --- /dev/null +++ b/java/client/src/main/java/org/apache/qpid/client/util/ClassLoadingAwareObjectInputStream.java @@ -0,0 +1,134 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.util; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectStreamClass; +import java.lang.reflect.Proxy; +import java.util.HashMap; + + +/** + * <code>ClassLoadingAwareObjectInputStream</code> is an Extention of Object input stream to be used + * to de-serialize JMS Object Messages. + * + * <p>This was introduced to resolve the class loading issues which can happen when we use the client + * libraries in a complex class loading Environment.</p> + */ +public class ClassLoadingAwareObjectInputStream extends ObjectInputStream +{ + /** <p>Class loader instance which loaded this class. + * It will be used to load classes when we failed to load classes from dynamic class loading</p> */ + private static final ClassLoader _ON_FAULT_CLASS_LOADER = + ClassLoadingAwareObjectInputStream.class.getClassLoader(); + + /** <p>Maps primitive type names to corresponding class objects.</p> */ + private static final HashMap<String, Class> _primitives = new HashMap<String, Class>(8, 1.0F); + + + public ClassLoadingAwareObjectInputStream(InputStream in) throws IOException + { + super(in); + } + + @Override + protected Class resolveClass(ObjectStreamClass classDesc) + throws IOException, ClassNotFoundException + { + + // Here we use TTCL as our primary class loader to load the classes + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + + return load(classDesc.getName(), cl); + } + + @Override + protected Class resolveProxyClass(String[] interfaces) + throws IOException, ClassNotFoundException + { + // Here we use TTCL as our primary class loader to load the classes + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + + Class[] cinterfaces = new Class[interfaces.length]; + for (int i = 0; i < interfaces.length; i++) + { + cinterfaces[i] = load(interfaces[i], cl); + } + + try + { + return Proxy.getProxyClass(cinterfaces[0].getClassLoader(), cinterfaces); + } + catch (IllegalArgumentException e) + { + throw new ClassNotFoundException(null, e); + } + } + + /** + * <p> + * Method we used to load class that are needed to de-serialize the objects. </p> + * <p> + * Here we first look up for the objects from the given class loader and if its not there + * we will be using the class loader of this class. + * </p> + * @param className Class name to lookup + * @param cl primary class loader which we 1st use to lookup + * @return Class instance we are looking for + * @throws ClassNotFoundException if both primary and secondary lockup's failed. + */ + private Class load(String className, ClassLoader cl) + throws ClassNotFoundException + { + try + { + return Class.forName(className, false, cl); + } + catch (ClassNotFoundException e) + { + final Class clazz = _primitives.get(className); + + if (clazz != null) + { + return clazz; + } + else + { + return Class.forName(className, false, _ON_FAULT_CLASS_LOADER); + } + } + } + + static + { + _primitives.put("boolean", boolean.class); + _primitives.put("byte", byte.class); + _primitives.put("char", char.class); + _primitives.put("short", short.class); + _primitives.put("int", int.class); + _primitives.put("long", long.class); + _primitives.put("float", float.class); + _primitives.put("double", double.class); + _primitives.put("void", void.class); + } +} diff --git a/java/client/src/main/java/org/apache/qpid/client/vmbroker/AMQVMBrokerCreationException.java b/java/client/src/main/java/org/apache/qpid/client/vmbroker/AMQVMBrokerCreationException.java deleted file mode 100644 index dc0d9b8c78..0000000000 --- a/java/client/src/main/java/org/apache/qpid/client/vmbroker/AMQVMBrokerCreationException.java +++ /dev/null @@ -1,60 +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.client.vmbroker; - -import org.apache.qpid.client.transport.AMQTransportConnectionException; -import org.apache.qpid.protocol.AMQConstant; - -/** - * AMQVMBrokerCreationException represents failure to create an in VM broker on the vm transport medium. - * - * <p/><table id="crc"><caption>CRC Card</caption> - * <tr><th> Responsibilities <th> Collaborations - * <tr><td> Represent failure to create an in VM broker. - * </table> - * - * @todo Error code never used. This is not an AMQException. - */ -public class AMQVMBrokerCreationException extends AMQTransportConnectionException -{ - private int _port; - - /** - * @param port - * - * @deprecated - */ - public AMQVMBrokerCreationException(int port) - { - this(null, port, "Unable to create vm broker", null); - } - - public AMQVMBrokerCreationException(AMQConstant errorCode, int port, String message, Throwable cause) - { - super(errorCode, message, cause); - _port = port; - } - - public String toString() - { - return super.toString() + " on port " + _port; - } -} diff --git a/java/client/src/main/java/org/apache/qpid/filter/JMSSelectorFilter.java b/java/client/src/main/java/org/apache/qpid/filter/JMSSelectorFilter.java index 4159986090..40718c6435 100644 --- a/java/client/src/main/java/org/apache/qpid/filter/JMSSelectorFilter.java +++ b/java/client/src/main/java/org/apache/qpid/filter/JMSSelectorFilter.java @@ -37,9 +37,9 @@ public class JMSSelectorFilter implements MessageFilter public JMSSelectorFilter(String selector) throws AMQInternalException { _selector = selector; - if (JMSSelectorFilter._logger.isDebugEnabled()) + if (_logger.isDebugEnabled()) { - JMSSelectorFilter._logger.debug("Created JMSSelectorFilter with selector:" + _selector); + _logger.debug("Created JMSSelectorFilter with selector:" + _selector); } _matcher = new SelectorParser().parse(selector); } @@ -49,16 +49,16 @@ public class JMSSelectorFilter implements MessageFilter try { boolean match = _matcher.matches(message); - if (JMSSelectorFilter._logger.isDebugEnabled()) + if (_logger.isDebugEnabled()) { - JMSSelectorFilter._logger.debug(message + " match(" + match + ") selector(" + System + _logger.debug(message + " match(" + match + ") selector(" + System .identityHashCode(_selector) + "):" + _selector); } return match; } catch (AMQInternalException e) { - JMSSelectorFilter._logger.warn("Caght exception when evaluating message selector for message " + message, e); + _logger.warn("Caught exception when evaluating message selector for message " + message, e); } return false; } 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 b7b6bd57bc..574a1b3888 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 @@ -19,6 +19,7 @@ package org.apache.qpid.filter; import java.util.HashMap; +import javax.jms.DeliveryMode; import javax.jms.JMSException; import org.apache.qpid.AMQInternalException; @@ -32,7 +33,7 @@ import org.slf4j.LoggerFactory; public class PropertyExpression implements Expression { // Constants - defined the same as JMS - private static final int NON_PERSISTENT = 1; + private static enum JMSDeliveryMode { NON_PERSISTENT, PERSISTENT } private static final int DEFAULT_PRIORITY = 4; private static final Logger _logger = LoggerFactory.getLogger(PropertyExpression.class); @@ -79,22 +80,24 @@ public class PropertyExpression implements Expression { public Object evaluate(AbstractJMSMessage message) { + + JMSDeliveryMode mode = JMSDeliveryMode.NON_PERSISTENT; try { - int mode = message.getJMSDeliveryMode(); + mode = message.getJMSDeliveryMode() == DeliveryMode.PERSISTENT ? + JMSDeliveryMode.PERSISTENT : JMSDeliveryMode.NON_PERSISTENT; + if (_logger.isDebugEnabled()) { _logger.debug("JMSDeliveryMode is :" + mode); } - - return mode; } catch (JMSException e) { _logger.warn("Error evaluating property",e); } - return NON_PERSISTENT; + return mode.toString(); } }); 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 6d81f728c9..0c2f4ce57d 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 @@ -22,7 +22,7 @@ package org.apache.qpid.jms; import java.util.Map; -import org.apache.qpid.client.SSLConfiguration; +import org.apache.qpid.transport.ConnectionSettings; public interface BrokerDetails { @@ -52,9 +52,7 @@ public interface BrokerDetails public static final int DEFAULT_PORT = 5672; - public static final String SOCKET = "socket"; public static final String TCP = "tcp"; - public static final String VM = "vm"; public static final String DEFAULT_TRANSPORT = TCP; @@ -106,14 +104,12 @@ public interface BrokerDetails long getTimeout(); void setTimeout(long timeout); - - SSLConfiguration getSSLConfiguration(); - - void setSSLConfiguration(SSLConfiguration sslConfiguration); boolean getBooleanProperty(String propName); String toString(); boolean equals(Object o); + + ConnectionSettings buildConnectionSettings(); } 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 0e8ca60686..26641982d7 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 @@ -27,7 +27,7 @@ import java.util.List; /** Connection URL format - amqp://[user:pass@][clientid]/virtualhost?brokerlist='tcp://host:port?option=\'value\'&option=\'value\';vm://:3/virtualpath?option=\'value\''&failover='method?option=\'value\'&option='value''" + amqp://[user:pass@][clientid]/virtualhost?brokerlist='tcp://host:port?option=\'value\'&option=\'value\';tcp://host:port/virtualpath?option=\'value\''&failover='method?option=\'value\'&option='value''" Options are of course optional except for requiring a single broker in the broker list. The option seperator is defined to be either '&' or ',' */ 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 7cdcd32306..56abf03c81 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,7 +20,6 @@ */ 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; @@ -51,7 +50,7 @@ public class FailoverPolicy private long _lastMethodTime; private long _lastFailTime; - public FailoverPolicy(ConnectionURL connectionDetails, AMQConnection conn) + public FailoverPolicy(ConnectionURL connectionDetails, Connection conn) { FailoverMethod method; @@ -83,7 +82,7 @@ public class FailoverPolicy */ if (failoverMethod.equals(FailoverMethod.SINGLE_BROKER)) { - method = new FailoverRoundRobinServers(connectionDetails); + method = new FailoverSingleServer(connectionDetails); } else { diff --git a/java/client/src/main/java/org/apache/qpid/jms/MessageProducer.java b/java/client/src/main/java/org/apache/qpid/jms/MessageProducer.java index b830c377b8..4ad917fa83 100644 --- a/java/client/src/main/java/org/apache/qpid/jms/MessageProducer.java +++ b/java/client/src/main/java/org/apache/qpid/jms/MessageProducer.java @@ -51,7 +51,4 @@ public interface MessageProducer extends javax.jms.MessageProducer int priority, long timeToLive, boolean mandatory, boolean immediate) throws JMSException; - void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive, - boolean mandatory, boolean immediate, boolean waitUntilSent) throws JMSException; - } diff --git a/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverExchangeMethod.java b/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverExchangeMethod.java index 9e6000c472..cb3ab718e9 100644 --- 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 @@ -32,9 +32,9 @@ 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.Connection; import org.apache.qpid.jms.ConnectionURL; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -58,7 +58,7 @@ 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; + private Connection _conn; /** Protects the broker list when modifications happens */ private Object _brokerListLock = new Object(); @@ -80,7 +80,7 @@ public class FailoverExchangeMethod implements FailoverMethod, MessageListener /** Denotes the number of failed attempts **/ private int _failedAttemps = 0; - public FailoverExchangeMethod(ConnectionURL connectionDetails, AMQConnection conn) + public FailoverExchangeMethod(ConnectionURL connectionDetails, Connection conn) { _connectionDetails = connectionDetails; _originalBrokerDetail = _connectionDetails.getBrokerDetails(0); @@ -140,7 +140,6 @@ public class FailoverExchangeMethod implements FailoverMethod, MessageListener 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()) && 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 fec5af55c1..b480f56c07 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 @@ -36,6 +36,7 @@ import javax.jms.Queue; import javax.jms.Topic; import javax.naming.Context; import javax.naming.NamingException; +import javax.naming.ConfigurationException; import javax.naming.spi.InitialContextFactory; import org.apache.qpid.client.AMQConnectionFactory; @@ -139,7 +140,7 @@ public class PropertiesFileInitialContextFactory implements InitialContextFactor return new ReadOnlyContext(environment, data); } - protected void createConnectionFactories(Map data, Hashtable environment) + protected void createConnectionFactories(Map data, Hashtable environment) throws ConfigurationException { for (Iterator iter = environment.entrySet().iterator(); iter.hasNext();) { @@ -157,7 +158,7 @@ public class PropertiesFileInitialContextFactory implements InitialContextFactor } } - protected void createDestinations(Map data, Hashtable environment) + protected void createDestinations(Map data, Hashtable environment) throws ConfigurationException { for (Iterator iter = environment.entrySet().iterator(); iter.hasNext();) { @@ -225,7 +226,7 @@ public class PropertiesFileInitialContextFactory implements InitialContextFactor /** * Factory method to create new Connection Factory instances */ - protected ConnectionFactory createFactory(String url) + protected ConnectionFactory createFactory(String url) throws ConfigurationException { try { @@ -233,16 +234,18 @@ public class PropertiesFileInitialContextFactory implements InitialContextFactor } catch (URLSyntaxException urlse) { - _logger.warn("Unable to createFactories:" + urlse); - } + _logger.warn("Unable to create factory:" + urlse); - return null; + ConfigurationException ex = new ConfigurationException("Failed to parse entry: " + urlse + " due to : " + urlse.getMessage()); + ex.initCause(urlse); + throw ex; + } } /** * Factory method to create new Destination instances from an AMQP BindingURL */ - protected Destination createDestination(String str) + protected Destination createDestination(String str) throws ConfigurationException { try { @@ -252,7 +255,9 @@ public class PropertiesFileInitialContextFactory implements InitialContextFactor { _logger.warn("Unable to create destination:" + e, e); - return null; + ConfigurationException ex = new ConfigurationException("Failed to parse entry: " + str + " due to : " + e.getMessage()); + ex.initCause(e); + throw ex; } } |
