You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ta...@apache.org on 2019/07/23 19:21:19 UTC

[qpid-jms] branch master updated: QPIDJMS-467 Provide client API stack traces in JMS Exceptions

This is an automated email from the ASF dual-hosted git repository.

tabish pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/qpid-jms.git


The following commit(s) were added to refs/heads/master by this push:
     new f212e7c  QPIDJMS-467 Provide client API stack traces in JMS Exceptions
f212e7c is described below

commit f212e7c2c3e21d44e90068f9c0bf6ab374a68f8e
Author: Timothy Bish <ta...@gmail.com>
AuthorDate: Tue Jul 23 14:51:37 2019 -0400

    QPIDJMS-467 Provide client API stack traces in JMS Exceptions
    
    Only throw ProviderException types from the provider layer and allow
    for conversion to the correct JMS exception type in the JMS layer to
    allow the stack traces in the thrown exceptions to map to which JMS
    API method was called to trigger the error.  Thrown exceptions will
    provide the cause for the error from the proivider layer from the
    calling method in the JMS layer.
---
 .../java/org/apache/qpid/jms/JmsConnection.java    |  13 +-
 .../org/apache/qpid/jms/JmsConnectionConsumer.java |   3 +-
 .../qpid/jms/JmsLocalTransactionContext.java       |  13 +-
 .../org/apache/qpid/jms/JmsMessageConsumer.java    |  10 +-
 .../org/apache/qpid/jms/JmsMessageProducer.java    |  10 +-
 .../main/java/org/apache/qpid/jms/JmsSession.java  |  48 ++-
 .../exceptions/JmsConnectionClosedException.java   |   7 +-
 .../exceptions/JmsConnectionFailedException.java   |   6 +-
 .../qpid/jms/exceptions/JmsExceptionSupport.java   |   4 +
 .../apache/qpid/jms/message/JmsObjectMessage.java  |  15 +-
 .../apache/qpid/jms/message/JmsStreamMessage.java  |   2 +-
 .../org/apache/qpid/jms/provider/AsyncResult.java  |   2 +-
 .../qpid/jms/provider/BalancedProviderFuture.java  |  11 +-
 .../jms/provider/ConservativeProviderFuture.java   |  11 +-
 .../qpid/jms/provider/DefaultProviderListener.java |   9 +-
 .../apache/qpid/jms/provider/NoOpAsyncResult.java  |   2 +-
 .../jms/provider/ProgressiveProviderFuture.java    |  11 +-
 .../org/apache/qpid/jms/provider/Provider.java     |  74 ++---
 .../qpid/jms/provider/ProviderException.java       |  11 +-
 .../apache/qpid/jms/provider/ProviderFuture.java   |  21 +-
 .../qpid/jms/provider/ProviderFutureFactory.java   |   6 +-
 .../apache/qpid/jms/provider/ProviderListener.java |   9 +-
 .../qpid/jms/provider/ProviderSynchronization.java |   2 +-
 .../apache/qpid/jms/provider/ProviderWrapper.java  |  39 ++-
 .../qpid/jms/provider/WrappedAsyncResult.java      |   2 +-
 .../jms/provider/amqp/AmqpAbstractResource.java    |  26 +-
 .../amqp/AmqpAnonymousFallbackProducer.java        |  14 +-
 .../qpid/jms/provider/amqp/AmqpConnection.java     |  21 +-
 .../jms/provider/amqp/AmqpConnectionSession.java   |  19 +-
 .../qpid/jms/provider/amqp/AmqpConsumer.java       |  25 +-
 .../qpid/jms/provider/amqp/AmqpEventSink.java      |  23 +-
 .../jms/provider/amqp/AmqpExceptionBuilder.java    |   4 +-
 .../qpid/jms/provider/amqp/AmqpFixedProducer.java  |  59 ++--
 .../qpid/jms/provider/amqp/AmqpProducer.java       |  10 +-
 .../qpid/jms/provider/amqp/AmqpProvider.java       | 165 ++++++----
 .../jms/provider/amqp/AmqpSaslAuthenticator.java   |  27 +-
 .../apache/qpid/jms/provider/amqp/AmqpSession.java |   5 +-
 .../apache/qpid/jms/provider/amqp/AmqpSupport.java | 106 +++---
 .../jms/provider/amqp/AmqpTransactionContext.java  |  43 ++-
 .../provider/amqp/AmqpTransactionCoordinator.java  |  53 ++-
 .../amqp/builders/AmqpClosedConnectionBuilder.java |   1 -
 .../amqp/builders/AmqpConnectionBuilder.java       |  16 +-
 .../amqp/builders/AmqpConsumerBuilder.java         |  30 +-
 .../amqp/builders/AmqpProducerBuilder.java         |  10 +-
 .../amqp/builders/AmqpResourceBuilder.java         |  45 ++-
 .../{ => exceptions}/ProviderClosedException.java  |  11 +-
 .../ProviderConnectionRedirectedException.java}    |   6 +-
 ...ProviderConnectionRemotelyClosedException.java} |  22 +-
 ...iderConnectionResourceAllocationException.java} |  18 +-
 ...oviderConnectionResourceNotFoundException.java} |  20 +-
 .../ProviderConnectionSecurityException.java}      |  23 +-
 .../ProviderConnectionSecuritySaslException.java   |  56 ++++
 .../ProviderDeliveryModifiedException.java         |  50 +++
 .../ProviderDeliveryReleasedException.java}        |  15 +-
 .../exceptions/ProviderExceptionSupport.java       |  73 +++++
 .../{ => exceptions}/ProviderFailedException.java  |  11 +-
 .../ProviderIOException.java}                      |  15 +-
 .../ProviderIdleTimeoutException.java}             |  15 +-
 .../ProviderIllegalStateException.java}            |  22 +-
 .../ProviderInvalidClientIDException.java}         |  20 +-
 .../ProviderInvalidDestinationException.java}      |  22 +-
 .../ProviderOperationTimedOutException.java}       |  24 +-
 .../ProviderResourceAllocationException.java}      |  18 +-
 .../ProviderResourceClosedException.java           |   4 +-
 .../ProviderResourceNotFoundException.java}        |  21 +-
 .../ProviderSecurityException.java}                |  26 +-
 .../exceptions/ProviderSendTimedOutException.java  |  51 +++
 .../ProviderTransactionInDoubtException.java}      |  24 +-
 .../ProviderTransactionRolledBackException.java}   |  27 +-
 .../ProviderUnsupportedOperationException.java}    |  15 +-
 .../jms/provider/failover/FailoverProvider.java    | 180 +++++------
 .../apache/qpid/jms/sasl/SaslMechanismFinder.java  |  18 +-
 .../SaslSecurityRuntimeException.java}             |  23 +-
 .../apache/qpid/jms/JmsConnectionFailedTest.java   |   4 +-
 .../jms/consumer/JmsMessageConsumerFailedTest.java |   4 +-
 .../jms/integration/ConnectionIntegrationTest.java |  10 +-
 .../FailedConnectionsIntegrationTest.java          |   8 +-
 .../jms/producer/JmsMessageProducerFailedTest.java |   4 +-
 .../qpid/jms/producer/JmsMessageProducerTest.java  |   9 +-
 .../qpid/jms/provider/NoOpAsyncResultTest.java     |   6 +-
 .../qpid/jms/provider/ProviderFutureTest.java      |  31 +-
 .../qpid/jms/provider/ProviderWrapperTest.java     |   9 +-
 .../qpid/jms/provider/WrappedAsyncResultTest.java  |   3 +-
 .../qpid/jms/provider/amqp/AmqpProviderTest.java   |  11 +-
 .../provider/amqp/AmqpSaslAuthenticatorTest.java   |   8 +-
 .../qpid/jms/provider/amqp/AmqpSupportTest.java    |  24 +-
 .../provider/failover/FailoverIntegrationTest.java | 354 ++++++++++++++++++++-
 .../failover/FailoverProviderClosedTest.java       |  30 +-
 .../FailoverProviderOfflineBehaviorTest.java       |   4 +-
 .../provider/failover/FailoverProviderTest.java    |   3 +-
 .../qpid/jms/provider/mock/MockProvider.java       |  96 +++---
 .../qpid/jms/provider/mock/MockRemotePeer.java     |  12 +-
 .../qpid/jms/session/JmsSessionFailedTest.java     |   4 +-
 .../jms/provider/discovery/DiscoveryAgent.java     |   7 +-
 .../jms/provider/discovery/DiscoveryProvider.java  |   4 +-
 .../discovery/file/FileWatcherDiscoveryAgent.java  |   5 +-
 .../multicast/MulticastDiscoveryAgent.java         |  48 +--
 .../jms/failover/JmsTxProducerFailoverTest.java    |   6 +
 98 files changed, 1706 insertions(+), 871 deletions(-)

diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnection.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnection.java
index da5f704..2cb59fe 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnection.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnection.java
@@ -16,7 +16,6 @@
  */
 package org.apache.qpid.jms;
 
-import java.io.IOException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.Map;
@@ -104,7 +103,7 @@ public class JmsConnection implements AutoCloseable, Connection, TopicConnection
     private final AtomicBoolean closed = new AtomicBoolean();
     private final AtomicBoolean closing = new AtomicBoolean();
     private final AtomicBoolean started = new AtomicBoolean();
-    private final AtomicReference<IOException> failureCause = new AtomicReference<>();
+    private final AtomicReference<Exception> failureCause = new AtomicReference<>();
     private final JmsConnectionInfo connectionInfo;
     private final ThreadPoolExecutor executor;
 
@@ -1226,7 +1225,7 @@ public class JmsConnection implements AutoCloseable, Connection, TopicConnection
     }
 
     @Override
-    public void onFailedMessageSend(JmsOutboundMessageDispatch envelope, Throwable cause) {
+    public void onFailedMessageSend(JmsOutboundMessageDispatch envelope, ProviderException cause) {
         JmsSession session = sessions.get(envelope.getProducerId().getParentId());
         if (session != null) {
             session.onFailedMessageSend(envelope, cause);
@@ -1347,7 +1346,7 @@ public class JmsConnection implements AutoCloseable, Connection, TopicConnection
     }
 
     @Override
-    public void onConnectionFailure(final IOException ex) {
+    public void onConnectionFailure(final ProviderException ex) {
         providerFailed(ex);
 
         // Signal that connection dropped we need to mark transactions as
@@ -1406,7 +1405,7 @@ public class JmsConnection implements AutoCloseable, Connection, TopicConnection
     }
 
     @Override
-    public void onResourceClosed(final JmsResource resource, final Throwable cause) {
+    public void onResourceClosed(final JmsResource resource, final ProviderException cause) {
         // Closure of the Connection itself is notified via onConnectionFailure
 
         // Run on the connection executor to free the provider to go do more work and avoid
@@ -1509,7 +1508,7 @@ public class JmsConnection implements AutoCloseable, Connection, TopicConnection
     }
 
     @Override
-    public void onProviderException(final Exception cause) {
+    public void onProviderException(final ProviderException cause) {
         // Report this to any registered exception listener, let the receiver
         // decide if it should be fatal.
         onAsyncException(cause);
@@ -1542,7 +1541,7 @@ public class JmsConnection implements AutoCloseable, Connection, TopicConnection
         }
     }
 
-    protected void providerFailed(IOException cause) {
+    protected void providerFailed(ProviderException cause) {
         failureCause.compareAndSet(null, cause);
     }
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnectionConsumer.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnectionConsumer.java
index 1476f49..cfc6346 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnectionConsumer.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnectionConsumer.java
@@ -43,6 +43,7 @@ import org.apache.qpid.jms.meta.JmsConsumerInfo;
 import org.apache.qpid.jms.meta.JmsResource.ResourceState;
 import org.apache.qpid.jms.policy.JmsRedeliveryPolicy;
 import org.apache.qpid.jms.provider.ProviderConstants.ACK_TYPE;
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.jms.provider.ProviderSynchronization;
 import org.apache.qpid.jms.util.MessageQueue;
 import org.slf4j.Logger;
@@ -97,7 +98,7 @@ public class JmsConnectionConsumer implements ConnectionConsumer, JmsMessageDisp
             }
 
             @Override
-            public void onPendingFailure(Throwable cause) {
+            public void onPendingFailure(ProviderException cause) {
             }
         });
     }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsLocalTransactionContext.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsLocalTransactionContext.java
index 45bd10a..1452478 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsLocalTransactionContext.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsLocalTransactionContext.java
@@ -30,6 +30,7 @@ import org.apache.qpid.jms.meta.JmsTransactionId;
 import org.apache.qpid.jms.meta.JmsTransactionInfo;
 import org.apache.qpid.jms.provider.Provider;
 import org.apache.qpid.jms.provider.ProviderConstants.ACK_TYPE;
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.jms.provider.ProviderFuture;
 import org.apache.qpid.jms.provider.ProviderSynchronization;
 import org.slf4j.Logger;
@@ -84,7 +85,7 @@ public class JmsLocalTransactionContext implements JmsTransactionContext {
                 }
 
                 @Override
-                public void onPendingFailure(Throwable cause) {
+                public void onPendingFailure(ProviderException cause) {
                     LOG.trace("TX:{} has a failed send.", getTransactionId());
                     participants.put(envelope.getProducerId(), envelope.getProducerId());
                     if (outcome != null) {
@@ -112,7 +113,7 @@ public class JmsLocalTransactionContext implements JmsTransactionContext {
                     }
 
                     @Override
-                    public void onPendingFailure(Throwable cause) {
+                    public void onPendingFailure(ProviderException cause) {
                         LOG.trace("TX:{} has failed a acknowledge.", getTransactionId());
                         participants.put(envelope.getConsumerId(), envelope.getConsumerId());
                     }
@@ -144,7 +145,7 @@ public class JmsLocalTransactionContext implements JmsTransactionContext {
                 }
 
                 @Override
-                public void onPendingFailure(Throwable cause) {
+                public void onPendingFailure(ProviderException cause) {
                     JmsLocalTransactionContext.this.transactionInfo = transactionInfo;
                     transactionInfo.setInDoubt(true);
                 }
@@ -191,7 +192,7 @@ public class JmsLocalTransactionContext implements JmsTransactionContext {
                         }
 
                         @Override
-                        public void onPendingFailure(Throwable cause) {
+                        public void onPendingFailure(ProviderException cause) {
                             reset();
                             JmsLocalTransactionContext.this.transactionInfo = nextTx;
                         }
@@ -269,7 +270,7 @@ public class JmsLocalTransactionContext implements JmsTransactionContext {
                     }
 
                     @Override
-                    public void onPendingFailure(Throwable cause) {
+                    public void onPendingFailure(ProviderException cause) {
                         reset();
                         JmsLocalTransactionContext.this.transactionInfo = nextTx;
                     }
@@ -358,7 +359,7 @@ public class JmsLocalTransactionContext implements JmsTransactionContext {
                     }
 
                     @Override
-                    public void onPendingFailure(Throwable cause) {
+                    public void onPendingFailure(ProviderException cause) {
                         transactionInfo.setInDoubt(true);
                     }
                 });
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsMessageConsumer.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsMessageConsumer.java
index d616faf..a437f76 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsMessageConsumer.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsMessageConsumer.java
@@ -18,7 +18,6 @@ package org.apache.qpid.jms;
 
 import static org.apache.qpid.jms.message.JmsMessageSupport.lookupAckTypeForDisposition;
 
-import java.io.IOException;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.concurrent.locks.Lock;
@@ -44,6 +43,7 @@ import org.apache.qpid.jms.policy.JmsPrefetchPolicy;
 import org.apache.qpid.jms.policy.JmsRedeliveryPolicy;
 import org.apache.qpid.jms.provider.Provider;
 import org.apache.qpid.jms.provider.ProviderConstants.ACK_TYPE;
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.jms.provider.ProviderFuture;
 import org.apache.qpid.jms.provider.ProviderSynchronization;
 import org.apache.qpid.jms.util.FifoMessageQueue;
@@ -124,7 +124,7 @@ public class JmsMessageConsumer implements AutoCloseable, MessageConsumer, JmsMe
             }
 
             @Override
-            public void onPendingFailure(Throwable cause) {
+            public void onPendingFailure(ProviderException cause) {
             }
         });
 
@@ -664,11 +664,11 @@ public class JmsMessageConsumer implements AutoCloseable, MessageConsumer, JmsMe
             try {
                 provider.create(consumerInfo, request);
                 request.sync();
-            } catch (IOException ioe) {
+            } catch (ProviderException poe) {
                 if (connection.isCloseLinksThatFailOnReconnect()) {
-                    session.consumerClosed(consumerInfo, ioe);
+                    session.consumerClosed(consumerInfo, poe);
                 } else {
-                    throw ioe;
+                    throw poe;
                 }
             }
         }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsMessageProducer.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsMessageProducer.java
index 921dc70..4a09eff 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsMessageProducer.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsMessageProducer.java
@@ -16,7 +16,6 @@
  */
 package org.apache.qpid.jms;
 
-import java.io.IOException;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.AtomicReference;
@@ -36,6 +35,7 @@ import org.apache.qpid.jms.meta.JmsProducerId;
 import org.apache.qpid.jms.meta.JmsProducerInfo;
 import org.apache.qpid.jms.meta.JmsResource.ResourceState;
 import org.apache.qpid.jms.provider.Provider;
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.jms.provider.ProviderFuture;
 import org.apache.qpid.jms.provider.ProviderSynchronization;
 
@@ -78,7 +78,7 @@ public class JmsMessageProducer implements AutoCloseable, MessageProducer {
             }
 
             @Override
-            public void onPendingFailure(Throwable cause) {
+            public void onPendingFailure(ProviderException cause) {
             }
         });
     }
@@ -366,11 +366,11 @@ public class JmsMessageProducer implements AutoCloseable, MessageProducer {
             try {
                 provider.create(producerInfo, request);
                 request.sync();
-            } catch (IOException ioe) {
+            } catch (ProviderException poe) {
                 if (connection.isCloseLinksThatFailOnReconnect()) {
-                    session.producerClosed(producerInfo, ioe);
+                    session.producerClosed(producerInfo, poe);
                 } else {
-                    throw ioe;
+                    throw poe;
                 }
             }
         }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsSession.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsSession.java
index b69ec35..c25f86a 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsSession.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsSession.java
@@ -91,6 +91,7 @@ import org.apache.qpid.jms.policy.JmsPresettlePolicy;
 import org.apache.qpid.jms.policy.JmsRedeliveryPolicy;
 import org.apache.qpid.jms.provider.Provider;
 import org.apache.qpid.jms.provider.ProviderConstants.ACK_TYPE;
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.jms.provider.ProviderFuture;
 import org.apache.qpid.jms.provider.ProviderSynchronization;
 import org.apache.qpid.jms.selector.SelectorParser;
@@ -162,7 +163,7 @@ public class JmsSession implements AutoCloseable, Session, QueueSession, TopicSe
             }
 
             @Override
-            public void onPendingFailure(Throwable cause) {
+            public void onPendingFailure(ProviderException cause) {
             }
         });
 
@@ -180,7 +181,7 @@ public class JmsSession implements AutoCloseable, Session, QueueSession, TopicSe
                     }
 
                     @Override
-                    public void onPendingFailure(Throwable cause) {
+                    public void onPendingFailure(ProviderException cause) {
                         connection.removeSession(sessionInfo);
                     }
                 });
@@ -327,20 +328,37 @@ public class JmsSession implements AutoCloseable, Session, QueueSession, TopicSe
 
     protected void shutdown(Throwable cause) throws JMSException {
         if (closed.compareAndSet(false, true)) {
+            JMSException shutdownError = null;
+
             try {
                 sessionInfo.setState(ResourceState.CLOSED);
                 setFailureCause(cause);
-                stop();
 
-                for (JmsMessageConsumer consumer : new ArrayList<JmsMessageConsumer>(this.consumers.values())) {
-                    consumer.shutdown(cause);
-                }
+                try {
+                    stop();
 
-                for (JmsMessageProducer producer : new ArrayList<JmsMessageProducer>(this.producers.values())) {
-                    producer.shutdown(cause);
+                    for (JmsMessageConsumer consumer : new ArrayList<JmsMessageConsumer>(this.consumers.values())) {
+                        consumer.shutdown(cause);
+                    }
+
+                    for (JmsMessageProducer producer : new ArrayList<JmsMessageProducer>(this.producers.values())) {
+                        producer.shutdown(cause);
+                    }
+                } catch (JMSException jmsEx) {
+                    shutdownError = jmsEx;
                 }
 
-                transactionContext.shutdown();
+                boolean inDoubt = transactionContext.isInDoubt();
+                try {
+                    transactionContext.shutdown();
+                } catch (JMSException jmsEx) {
+                    if (!inDoubt) {
+                        LOG.warn("Rollback of active transaction failed due to error: ", jmsEx);
+                        shutdownError = shutdownError == null ? jmsEx : shutdownError;
+                    } else {
+                        LOG.trace("Rollback of indoubt transaction failed due to error: ", jmsEx);
+                    }
+                }
 
                 // Ensure that no asynchronous completion sends remain blocked after close.
                 synchronized (sessionInfo) {
@@ -357,6 +375,16 @@ public class JmsSession implements AutoCloseable, Session, QueueSession, TopicSe
                 } catch (InterruptedException e) {
                     LOG.trace("Session close awaiting send completions was interrupted");
                 }
+
+                if (shutdownError != null) {
+                    throw shutdownError;
+                }
+            } catch (Throwable e) {
+                if (shutdownError != null) {
+                    throw shutdownError;
+                } else {
+                    throw JmsExceptionSupport.create(e);
+                }
             } finally {
                 connection.removeSession(sessionInfo);
             }
@@ -923,7 +951,7 @@ public class JmsSession implements AutoCloseable, Session, QueueSession, TopicSe
                     }
 
                     @Override
-                    public void onPendingFailure(Throwable cause) {
+                    public void onPendingFailure(ProviderException cause) {
                         // Provider has rejected the send request so we will throw the
                         // exception that is to follow so no completion will be needed.
                     }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/exceptions/JmsConnectionClosedException.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/exceptions/JmsConnectionClosedException.java
index 94cf989..c8e9a3f 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/exceptions/JmsConnectionClosedException.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/exceptions/JmsConnectionClosedException.java
@@ -16,17 +16,16 @@
  */
 package org.apache.qpid.jms.exceptions;
 
-import java.io.IOException;
-
 import javax.jms.IllegalStateException;
 
 /**
  * An exception thrown when attempt is made to use a connection when the connection has been closed.
  */
 public class JmsConnectionClosedException extends IllegalStateException {
+
     private static final long serialVersionUID = -7975982446284065025L;
 
-    public JmsConnectionClosedException(IOException cause) {
+    public JmsConnectionClosedException(Exception cause) {
         super("The JMS connection has been closed: " + extractMessage(cause));
         initCause(cause);
         setLinkedException(cause);
@@ -36,7 +35,7 @@ public class JmsConnectionClosedException extends IllegalStateException {
         super("The JMS connection has been closed", "AlreadyClosed");
     }
 
-    private static String extractMessage(IOException cause) {
+    private static String extractMessage(Exception cause) {
         String m = cause.getMessage();
         if (m == null || m.length() == 0) {
             m = cause.toString();
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/exceptions/JmsConnectionFailedException.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/exceptions/JmsConnectionFailedException.java
index 51d7fd2..a875beb 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/exceptions/JmsConnectionFailedException.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/exceptions/JmsConnectionFailedException.java
@@ -16,8 +16,6 @@
  */
 package org.apache.qpid.jms.exceptions;
 
-import java.io.IOException;
-
 import javax.jms.IllegalStateException;
 
 /**
@@ -27,7 +25,7 @@ public class JmsConnectionFailedException extends IllegalStateException {
 
     private static final long serialVersionUID = -3386897790274799220L;
 
-    public JmsConnectionFailedException(IOException cause) {
+    public JmsConnectionFailedException(Exception cause) {
         super("The JMS connection has failed: " + extractMessage(cause));
         initCause(cause);
         setLinkedException(cause);
@@ -37,7 +35,7 @@ public class JmsConnectionFailedException extends IllegalStateException {
         super("The JMS connection has failed due to a Transport problem", "Connection Failed");
     }
 
-    private static String extractMessage(IOException cause) {
+    private static String extractMessage(Exception cause) {
         String m = cause.getMessage();
         if (m == null || m.length() == 0) {
             m = cause.toString();
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/exceptions/JmsExceptionSupport.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/exceptions/JmsExceptionSupport.java
index 5f255f2..7aad61a 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/exceptions/JmsExceptionSupport.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/exceptions/JmsExceptionSupport.java
@@ -40,6 +40,8 @@ import javax.jms.TransactionInProgressRuntimeException;
 import javax.jms.TransactionRolledBackException;
 import javax.jms.TransactionRolledBackRuntimeException;
 
+import org.apache.qpid.jms.provider.ProviderException;
+
 /**
  * Exception support class.
  *
@@ -74,6 +76,8 @@ public final class JmsExceptionSupport {
 
         if (cause.getCause() instanceof JMSException) {
             return (JMSException) cause.getCause();
+        } else if (cause instanceof ProviderException) {
+            return ((ProviderException) cause).toJMSException();
         }
 
         if (message == null || message.isEmpty()) {
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsObjectMessage.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsObjectMessage.java
index e8e0d22..4973e69 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsObjectMessage.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsObjectMessage.java
@@ -46,7 +46,10 @@ public class JmsObjectMessage extends JmsMessage implements ObjectMessage {
         try {
             this.facade.setObject(newObject);
         } catch (Exception e) {
-            throw new MessageFormatException("Failed to serialize object");
+            MessageFormatException jmsEx = new MessageFormatException("Failed to serialize object:" + e.getMessage());
+            jmsEx.setLinkedException(e);
+            jmsEx.initCause(e);
+            throw jmsEx;
         }
     }
 
@@ -55,7 +58,10 @@ public class JmsObjectMessage extends JmsMessage implements ObjectMessage {
         try {
             return this.facade.getObject();
         } catch (Exception e) {
-            throw new MessageFormatException("Failed to read object");
+            MessageFormatException jmsEx = new MessageFormatException("Failed to read object: " + e.getMessage());
+            jmsEx.setLinkedException(e);
+            jmsEx.initCause(e);
+            throw jmsEx;
         }
     }
 
@@ -74,7 +80,10 @@ public class JmsObjectMessage extends JmsMessage implements ObjectMessage {
         try {
             return (T) getObject();
         } catch (JMSException e) {
-            throw new MessageFormatException("Failed to read Object: " + e.getMessage());
+            MessageFormatException jmsEx = new MessageFormatException("Failed to read object: " + e.getMessage());
+            jmsEx.setLinkedException(e);
+            jmsEx.initCause(e);
+            throw jmsEx;
         }
     }
 
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsStreamMessage.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsStreamMessage.java
index e55f8a0..2a6706e 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsStreamMessage.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsStreamMessage.java
@@ -136,7 +136,7 @@ public class JmsStreamMessage extends JmsMessage implements StreamMessage {
         if (value instanceof Character) {
             result = (Character) value;
         } else if (value == null) {
-                throw new NullPointerException("Cannot convert NULL value to char.");
+            throw new NullPointerException("Cannot convert NULL value to char.");
         } else {
             throw new MessageFormatException(
                 "stream value: " + value.getClass().getSimpleName() + " cannot be converted to a boolean.");
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/AsyncResult.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/AsyncResult.java
index 81acf43..8870678 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/AsyncResult.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/AsyncResult.java
@@ -28,7 +28,7 @@ public interface AsyncResult {
      * @param result
      *        The error that resulted in this asynchronous operation failing.
      */
-    void onFailure(Throwable result);
+    void onFailure(ProviderException result);
 
     /**
      * If the operation succeeds the resulting value produced is set to null and
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/BalancedProviderFuture.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/BalancedProviderFuture.java
index 846a796..8d71181 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/BalancedProviderFuture.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/BalancedProviderFuture.java
@@ -16,10 +16,9 @@
  */
 package org.apache.qpid.jms.provider;
 
-import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 
-import org.apache.qpid.jms.util.IOExceptionSupport;
+import org.apache.qpid.jms.provider.exceptions.ProviderExceptionSupport;
 
 /**
  * A more balanced implementation of a ProviderFuture that works better on some
@@ -42,7 +41,7 @@ public class BalancedProviderFuture extends ProviderFuture {
     }
 
     @Override
-    public boolean sync(long amount, TimeUnit unit) throws IOException {
+    public boolean sync(long amount, TimeUnit unit) throws ProviderException {
         try {
             if (isComplete() || amount == 0) {
                 failOnError();
@@ -96,12 +95,12 @@ public class BalancedProviderFuture extends ProviderFuture {
             }
         } catch (InterruptedException e) {
             Thread.interrupted();
-            throw IOExceptionSupport.create(e);
+            throw ProviderExceptionSupport.createOrPassthroughFatal(e);
         }
     }
 
     @Override
-    public void sync() throws IOException {
+    public void sync() throws ProviderException {
         try {
             if (isComplete()) {
                 failOnError();
@@ -143,7 +142,7 @@ public class BalancedProviderFuture extends ProviderFuture {
             }
         } catch (InterruptedException e) {
             Thread.interrupted();
-            throw IOExceptionSupport.create(e);
+            throw ProviderExceptionSupport.createOrPassthroughFatal(e);
         }
     }
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ConservativeProviderFuture.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ConservativeProviderFuture.java
index e055130..1b2dc3b 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ConservativeProviderFuture.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ConservativeProviderFuture.java
@@ -16,10 +16,9 @@
  */
 package org.apache.qpid.jms.provider;
 
-import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 
-import org.apache.qpid.jms.util.IOExceptionSupport;
+import org.apache.qpid.jms.provider.exceptions.ProviderExceptionSupport;
 
 /**
  * A more conservative implementation of a ProviderFuture that is better on some
@@ -38,7 +37,7 @@ public class ConservativeProviderFuture extends ProviderFuture {
     }
 
     @Override
-    public boolean sync(long amount, TimeUnit unit) throws IOException {
+    public boolean sync(long amount, TimeUnit unit) throws ProviderException {
         try {
             if (isComplete() || amount == 0) {
                 failOnError();
@@ -84,12 +83,12 @@ public class ConservativeProviderFuture extends ProviderFuture {
             }
         } catch (InterruptedException e) {
             Thread.interrupted();
-            throw IOExceptionSupport.create(e);
+            throw ProviderExceptionSupport.createOrPassthroughFatal(e);
         }
     }
 
     @Override
-    public void sync() throws IOException {
+    public void sync() throws ProviderException {
         try {
             if (isComplete()) {
                 failOnError();
@@ -122,7 +121,7 @@ public class ConservativeProviderFuture extends ProviderFuture {
             }
         } catch (InterruptedException e) {
             Thread.interrupted();
-            throw IOExceptionSupport.create(e);
+            throw ProviderExceptionSupport.createOrPassthroughFatal(e);
         }
     }
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/DefaultProviderListener.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/DefaultProviderListener.java
index 1d98e01..1a2c7a9 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/DefaultProviderListener.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/DefaultProviderListener.java
@@ -16,7 +16,6 @@
  */
 package org.apache.qpid.jms.provider;
 
-import java.io.IOException;
 import java.net.URI;
 
 import org.apache.qpid.jms.message.JmsInboundMessageDispatch;
@@ -37,7 +36,7 @@ public class DefaultProviderListener implements ProviderListener {
     }
 
     @Override
-    public void onFailedMessageSend(JmsOutboundMessageDispatch envelope, Throwable cause) {
+    public void onFailedMessageSend(JmsOutboundMessageDispatch envelope, ProviderException cause) {
     }
 
     @Override
@@ -49,7 +48,7 @@ public class DefaultProviderListener implements ProviderListener {
     }
 
     @Override
-    public void onConnectionFailure(IOException ex) {
+    public void onConnectionFailure(ProviderException ex) {
     }
 
     @Override
@@ -65,10 +64,10 @@ public class DefaultProviderListener implements ProviderListener {
     }
 
     @Override
-    public void onResourceClosed(JmsResource resource, Throwable cause) {
+    public void onResourceClosed(JmsResource resource, ProviderException cause) {
     }
 
     @Override
-    public void onProviderException(Exception cause) {
+    public void onProviderException(ProviderException cause) {
     }
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/NoOpAsyncResult.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/NoOpAsyncResult.java
index 5bc7bdd..0c4ad48 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/NoOpAsyncResult.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/NoOpAsyncResult.java
@@ -24,7 +24,7 @@ public class NoOpAsyncResult implements AsyncResult {
     public final static NoOpAsyncResult INSTANCE = new NoOpAsyncResult();
 
     @Override
-    public void onFailure(Throwable result) {
+    public void onFailure(ProviderException result) {
 
     }
 
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProgressiveProviderFuture.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProgressiveProviderFuture.java
index bd4da7f..d94a9b0 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProgressiveProviderFuture.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProgressiveProviderFuture.java
@@ -16,11 +16,10 @@
  */
 package org.apache.qpid.jms.provider;
 
-import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.LockSupport;
 
-import org.apache.qpid.jms.util.IOExceptionSupport;
+import org.apache.qpid.jms.provider.exceptions.ProviderExceptionSupport;
 
 /**
  * An optimized version of a ProviderFuture that makes use of spin waits and other
@@ -46,7 +45,7 @@ public class ProgressiveProviderFuture extends ProviderFuture {
     }
 
     @Override
-    public boolean sync(long amount, TimeUnit unit) throws IOException {
+    public boolean sync(long amount, TimeUnit unit) throws ProviderException {
         try {
             if (isComplete() || amount == 0) {
                 failOnError();
@@ -108,12 +107,12 @@ public class ProgressiveProviderFuture extends ProviderFuture {
             }
         } catch (InterruptedException e) {
             Thread.interrupted();
-            throw IOExceptionSupport.create(e);
+            throw ProviderExceptionSupport.createOrPassthroughFatal(e);
         }
     }
 
     @Override
-    public void sync() throws IOException {
+    public void sync() throws ProviderException {
         try {
             if (isComplete()) {
                 failOnError();
@@ -161,7 +160,7 @@ public class ProgressiveProviderFuture extends ProviderFuture {
             }
         } catch (InterruptedException e) {
             Thread.interrupted();
-            throw IOExceptionSupport.create(e);
+            throw ProviderExceptionSupport.createOrPassthroughFatal(e);
         }
     }
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/Provider.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/Provider.java
index 85a626d..a822cd7 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/Provider.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/Provider.java
@@ -16,12 +16,9 @@
  */
 package org.apache.qpid.jms.provider;
 
-import java.io.IOException;
 import java.net.URI;
 import java.util.List;
 
-import javax.jms.JMSException;
-
 import org.apache.qpid.jms.message.JmsInboundMessageDispatch;
 import org.apache.qpid.jms.message.JmsMessageFactory;
 import org.apache.qpid.jms.message.JmsOutboundMessageDispatch;
@@ -48,19 +45,19 @@ public interface Provider {
      * @param connectionInfo
      * 		The JmsConnectionInfo that contains the properties that define this connection.
      *
-     * @throws IOException if the remote resource can not be contacted.
+     * @throws ProviderException if the remote resource can not be contacted.
      */
-    void connect(JmsConnectionInfo connectionInfo) throws IOException;
+    void connect(JmsConnectionInfo connectionInfo) throws ProviderException;
 
     /**
      * Starts the Provider.  The start method provides a place for the Provider to perform
      * and pre-start configuration checks to ensure that the current state is valid and that
      * all contracts have been met prior to starting.
      *
-     * @throws IOException if an error occurs during start processing.
+     * @throws ProviderException if an error occurs during start processing.
      * @throws IllegalStateException if the Provider is improperly configured.
      */
-    void start() throws IOException, IllegalStateException;
+    void start() throws ProviderException, IllegalStateException;
 
     /**
      * Closes this Provider terminating all connections and canceling any pending
@@ -107,10 +104,9 @@ public interface Provider {
      * @param request
      *        The request object that should be signaled when this operation completes.
      *
-     * @throws IOException if an error occurs or the Provider is already closed.
-     * @throws JMSException if an error occurs due to JMS violation such as bad credentials.
+     * @throws ProviderException if an error occurs or the Provider is already closed.
      */
-    void create(JmsResource resource, AsyncResult request) throws IOException, JMSException;
+    void create(JmsResource resource, AsyncResult request) throws ProviderException;
 
     /**
      * Starts the Provider version of the given JmsResource.
@@ -125,7 +121,7 @@ public interface Provider {
      * once all Connection resources are restored.
      *
      * The provider is required to implement this method and not throw any error other than
-     * an IOException if a communication error occurs.  The start operation is not required to
+     * an ProviderException if a communication error occurs.  The start operation is not required to
      * have any effect on the provider resource but must not throw UnsupportedOperation etc.
      *
      * @param resource
@@ -133,10 +129,9 @@ public interface Provider {
      * @param request
      *        The request object that should be signaled when this operation completes.
      *
-     * @throws IOException if an error occurs or the Provider is already closed.
-     * @throws JMSException if an error occurs due to JMS violation such as already closed resource.
+     * @throws ProviderException if an error occurs or the Provider is already closed.
      */
-    void start(JmsResource resource, AsyncResult request) throws IOException, JMSException;
+    void start(JmsResource resource, AsyncResult request) throws ProviderException;
 
     /**
      * Stops (pauses) the Provider version of the given JmsResource, the resource would then
@@ -152,7 +147,7 @@ public interface Provider {
      * rollback.
      *
      * The provider is required to implement this method and not throw any error other than
-     * an IOException if a communication error occurs.  The stop operation is not required to
+     * an ProviderException if a communication error occurs.  The stop operation is not required to
      * have any effect on the provider resource but must not throw UnsupportedOperation etc.
      *
      * @param resource
@@ -160,10 +155,9 @@ public interface Provider {
      * @param request
      *        The request object that should be signaled when this operation completes.
      *
-     * @throws IOException if an error occurs or the Provider is already closed.
-     * @throws JMSException if an error occurs due to JMS violation such as already closed resource.
+     * @throws ProviderException if an error occurs or the Provider is already closed.
      */
-    void stop(JmsResource resource, AsyncResult request) throws IOException, JMSException;
+    void stop(JmsResource resource, AsyncResult request) throws ProviderException;
 
     /**
      * Instruct the Provider to dispose of a given JmsResource.
@@ -180,10 +174,9 @@ public interface Provider {
      * @param request
      *        The request object that should be signaled when this operation completes.
      *
-     * @throws IOException if an error occurs or the Provider is already closed.
-     * @throws JMSException if an error occurs due to JMS violation such as not authorized.
+     * @throws ProviderException if an error occurs or the Provider is already closed.
      */
-    void destroy(JmsResource resource, AsyncResult request) throws IOException, JMSException;
+    void destroy(JmsResource resource, AsyncResult request) throws ProviderException;
 
     /**
      * Sends the JmsMessage contained in the outbound dispatch envelope.
@@ -193,10 +186,9 @@ public interface Provider {
      * @param request
      *        The request object that should be signaled when this operation completes.
      *
-     * @throws IOException if an error occurs or the Provider is already closed.
-     * @throws JMSException if an error that maps to JMS occurs such as not authorized.
+     * @throws ProviderException if an error occurs or the Provider is already closed.
      */
-    void send(JmsOutboundMessageDispatch envelope, AsyncResult request) throws IOException, JMSException;
+    void send(JmsOutboundMessageDispatch envelope, AsyncResult request) throws ProviderException;
 
     /**
      * Called to acknowledge all messages that have been delivered in a given session.
@@ -212,10 +204,9 @@ public interface Provider {
      * @param request
      *        The request object that should be signaled when this operation completes.
      *
-     * @throws IOException if an error occurs or the Provider is already closed.
-     * @throws JMSException if an error occurs due to JMS violation such as unmatched ack.
+     * @throws ProviderException if an error occurs or the Provider is already closed.
      */
-    void acknowledge(JmsSessionId sessionId, ACK_TYPE ackType, AsyncResult request) throws IOException, JMSException;
+    void acknowledge(JmsSessionId sessionId, ACK_TYPE ackType, AsyncResult request) throws ProviderException;
 
     /**
      * Called to acknowledge a JmsMessage has been delivered, consumed, re-delivered...etc.
@@ -230,11 +221,9 @@ public interface Provider {
      * @param request
      *        The request object that should be signaled when this operation completes.
      *
-     * @throws IOException if an error occurs or the Provider is already closed.
-     * @throws JMSException if an error occurs due to JMS violation such as unmatched ack.
+     * @throws ProviderException if an error occurs or the Provider is already closed.
      */
-    void acknowledge(JmsInboundMessageDispatch envelope, ACK_TYPE ackType, AsyncResult request)
-        throws IOException, JMSException;
+    void acknowledge(JmsInboundMessageDispatch envelope, ACK_TYPE ackType, AsyncResult request) throws ProviderException;
 
     /**
      * Called to commit an open transaction, and start a new one if a new transaction info
@@ -255,10 +244,9 @@ public interface Provider {
      * @param request
      *        The request object that should be signaled when this operation completes.
      *
-     * @throws IOException if an error occurs or the Provider is already closed.
-     * @throws JMSException if an error occurs due to JMS violation such not authorized.
+     * @throws ProviderException if an error occurs or the Provider is already closed.
      */
-    void commit(JmsTransactionInfo transactionInfo, JmsTransactionInfo nextTransactionInfo, AsyncResult request) throws IOException, JMSException;
+    void commit(JmsTransactionInfo transactionInfo, JmsTransactionInfo nextTransactionInfo, AsyncResult request) throws ProviderException;
 
     /**
      * Called to roll back an open transaction, and start a new one if a new transaction info
@@ -275,10 +263,9 @@ public interface Provider {
      * @param request
      *        The request object that should be signaled when this operation completes.
      *
-     * @throws IOException if an error occurs or the Provider is already closed.
-     * @throws JMSException if an error occurs due to JMS violation such not authorized.
+     * @throws ProviderException if an error occurs or the Provider is already closed.
      */
-    void rollback(JmsTransactionInfo transactionInfo, JmsTransactionInfo nextTransactionInfo, AsyncResult request) throws IOException, JMSException;
+    void rollback(JmsTransactionInfo transactionInfo, JmsTransactionInfo nextTransactionInfo, AsyncResult request) throws ProviderException;
 
     /**
      * Called to recover all unacknowledged messages for a Session in client Ack mode.
@@ -288,9 +275,9 @@ public interface Provider {
      * @param request
      *        The request object that should be signaled when this operation completes.
      *
-     * @throws IOException if an error occurs or the Provider is already closed.
+     * @throws ProviderException if an error occurs or the Provider is already closed.
      */
-    void recover(JmsSessionId sessionId, AsyncResult request) throws IOException;
+    void recover(JmsSessionId sessionId, AsyncResult request) throws ProviderException;
 
     /**
      * Remove a durable topic subscription by name.
@@ -303,10 +290,9 @@ public interface Provider {
      * @param request
      *        The request object that should be signaled when this operation completes.
      *
-     * @throws IOException if an error occurs or the Provider is already closed.
-     * @throws JMSException if an error occurs due to JMS violation such not authorized.
+     * @throws ProviderException if an error occurs or the Provider is already closed.
      */
-    void unsubscribe(String subscription, AsyncResult request) throws IOException, JMSException;
+    void unsubscribe(String subscription, AsyncResult request) throws ProviderException;
 
     /**
      * Request a remote peer send a Message to this client.  A message pull request is
@@ -326,9 +312,9 @@ public interface Provider {
      * @param request
      *        The request object that should be signaled when this operation completes.
      *
-     * @throws IOException if an error occurs or the Provider is already closed.
+     * @throws ProviderException if an error occurs or the Provider is already closed.
      */
-    void pull(JmsConsumerId consumerId, long timeout, AsyncResult request) throws IOException;
+    void pull(JmsConsumerId consumerId, long timeout, AsyncResult request) throws ProviderException;
 
     /**
      * Gets the Provider specific Message factory for use in the JMS layer when a Session
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java
index e680df6..f67561f 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java
@@ -16,9 +16,9 @@
  */
 package org.apache.qpid.jms.provider;
 
-import java.io.IOException;
+import javax.jms.JMSException;
 
-public class ProviderException extends IOException {
+public class ProviderException extends Exception {
 
     private static final long serialVersionUID = -5094579928657311571L;
 
@@ -29,4 +29,11 @@ public class ProviderException extends IOException {
     public ProviderException(String message, Throwable cause) {
         super(message, cause);
     }
+
+    public JMSException toJMSException() {
+        final JMSException jmsEx = new JMSException(getMessage());
+        jmsEx.initCause(this);
+        jmsEx.setLinkedException(this);
+        return jmsEx;
+    }
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderFuture.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderFuture.java
index 75805a6..b6f86ac 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderFuture.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderFuture.java
@@ -16,12 +16,9 @@
  */
 package org.apache.qpid.jms.provider;
 
-import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
 
-import org.apache.qpid.jms.util.IOExceptionSupport;
-
 /**
  * Asynchronous Provider Future class.
  */
@@ -39,7 +36,7 @@ public abstract class ProviderFuture implements AsyncResult {
              AtomicIntegerFieldUpdater.newUpdater(ProviderFuture.class,"state");
 
     private volatile int state = INCOMPLETE;
-    protected Throwable error;
+    protected ProviderException error;
     protected int waiting;
 
     public ProviderFuture() {
@@ -56,7 +53,7 @@ public abstract class ProviderFuture implements AsyncResult {
     }
 
     @Override
-    public void onFailure(Throwable result) {
+    public void onFailure(ProviderException result) {
         if (STATE_FIELD_UPDATER.compareAndSet(this, INCOMPLETE, COMPLETING)) {
             error = result;
             if (synchronization != null) {
@@ -93,9 +90,9 @@ public abstract class ProviderFuture implements AsyncResult {
     /**
      * Waits for a response to some Provider requested operation.
      *
-     * @throws IOException if an error occurs while waiting for the response.
+     * @throws ProviderException if an error occurs while waiting for the response.
      */
-    public abstract void sync() throws IOException;
+    public abstract void sync() throws ProviderException;
 
     /**
      * Timed wait for a response to a Provider operation.
@@ -108,14 +105,14 @@ public abstract class ProviderFuture implements AsyncResult {
      * @return true if the operation succeeded and false if the waiting time elapsed while
      * 	       waiting for the operation to complete.
      *
-     * @throws IOException if an error occurs while waiting for the response.
+     * @throws ProviderException if an error occurs while waiting for the response.
      */
-    public abstract boolean sync(long amount, TimeUnit unit) throws IOException;
+    public abstract boolean sync(long amount, TimeUnit unit) throws ProviderException;
 
-    protected void failOnError() throws IOException {
-        Throwable cause = error;
+    protected void failOnError() throws ProviderException {
+        ProviderException cause = error;
         if (cause != null) {
-            throw IOExceptionSupport.create(cause);
+            throw cause;
         }
     }
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderFutureFactory.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderFutureFactory.java
index 95868c0..8d2323b 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderFutureFactory.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderFutureFactory.java
@@ -119,7 +119,7 @@ public abstract class ProviderFutureFactory {
             return new ConservativeProviderFuture() {
 
                 @Override
-                public void onFailure(Throwable t) {
+                public void onFailure(ProviderException t) {
                     this.onSuccess();
                 }
             };
@@ -143,7 +143,7 @@ public abstract class ProviderFutureFactory {
             return new BalancedProviderFuture() {
 
                 @Override
-                public void onFailure(Throwable t) {
+                public void onFailure(ProviderException t) {
                     this.onSuccess();
                 }
             };
@@ -167,7 +167,7 @@ public abstract class ProviderFutureFactory {
             return new ProgressiveProviderFuture() {
 
                 @Override
-                public void onFailure(Throwable t) {
+                public void onFailure(ProviderException t) {
                     this.onSuccess();
                 }
             };
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderListener.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderListener.java
index a96de4f..5233787 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderListener.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderListener.java
@@ -16,7 +16,6 @@
  */
 package org.apache.qpid.jms.provider;
 
-import java.io.IOException;
 import java.net.URI;
 
 import org.apache.qpid.jms.message.JmsInboundMessageDispatch;
@@ -55,7 +54,7 @@ public interface ProviderListener {
      * @param cause
      *      the exception that describes the cause of the failed send.
      */
-    void onFailedMessageSend(JmsOutboundMessageDispatch envelope, Throwable cause);
+    void onFailedMessageSend(JmsOutboundMessageDispatch envelope, ProviderException cause);
 
     /**
      * Called from a fault tolerant Provider instance to signal that the underlying
@@ -131,7 +130,7 @@ public interface ProviderListener {
      * @param ex
      *        The exception that indicates the cause of this Provider failure.
      */
-    void onConnectionFailure(IOException ex);
+    void onConnectionFailure(ProviderException ex);
 
     /**
      * Called to indicate that a currently active resource has been closed
@@ -144,7 +143,7 @@ public interface ProviderListener {
      * @param cause
      *        optional exception object that indicates the cause of the close.
      */
-    void onResourceClosed(JmsResource resource, Throwable cause);
+    void onResourceClosed(JmsResource resource, ProviderException cause);
 
     /**
      * Called to indicate that a some client operation caused or received an
@@ -153,6 +152,6 @@ public interface ProviderListener {
      * @param cause
      *        the exception object that is being reported to the listener.
      */
-    void onProviderException(Exception cause);
+    void onProviderException(ProviderException cause);
 
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderSynchronization.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderSynchronization.java
index acdf33f..38165af 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderSynchronization.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderSynchronization.java
@@ -25,6 +25,6 @@ public interface ProviderSynchronization {
 
     void onPendingSuccess();
 
-    void onPendingFailure(Throwable cause);
+    void onPendingFailure(ProviderException cause);
 
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderWrapper.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderWrapper.java
index 2a4eb09..1ee8636 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderWrapper.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderWrapper.java
@@ -16,12 +16,9 @@
  */
 package org.apache.qpid.jms.provider;
 
-import java.io.IOException;
 import java.net.URI;
 import java.util.List;
 
-import javax.jms.JMSException;
-
 import org.apache.qpid.jms.message.JmsInboundMessageDispatch;
 import org.apache.qpid.jms.message.JmsMessageFactory;
 import org.apache.qpid.jms.message.JmsOutboundMessageDispatch;
@@ -53,12 +50,12 @@ public class ProviderWrapper<E extends Provider> implements Provider, ProviderLi
     }
 
     @Override
-    public void connect(JmsConnectionInfo connectionInfo) throws IOException {
+    public void connect(JmsConnectionInfo connectionInfo) throws ProviderException {
         next.connect(connectionInfo);
     }
 
     @Override
-    public void start() throws IOException, IllegalStateException {
+    public void start() throws ProviderException, IllegalStateException {
         if (this.listener == null) {
             throw new IllegalStateException("Cannot start with null ProviderListener");
         }
@@ -81,62 +78,62 @@ public class ProviderWrapper<E extends Provider> implements Provider, ProviderLi
     }
 
     @Override
-    public void create(JmsResource resource, AsyncResult request) throws IOException, JMSException, UnsupportedOperationException {
+    public void create(JmsResource resource, AsyncResult request) throws ProviderException {
         next.create(resource, request);
     }
 
     @Override
-    public void start(JmsResource resource, AsyncResult request) throws IOException, JMSException {
+    public void start(JmsResource resource, AsyncResult request) throws ProviderException {
         next.start(resource, request);
     }
 
     @Override
-    public void stop(JmsResource resource, AsyncResult request) throws IOException, JMSException {
+    public void stop(JmsResource resource, AsyncResult request) throws ProviderException {
         next.stop(resource, request);
     }
 
     @Override
-    public void destroy(JmsResource resourceId, AsyncResult request) throws IOException, JMSException, UnsupportedOperationException {
+    public void destroy(JmsResource resourceId, AsyncResult request) throws ProviderException {
         next.destroy(resourceId, request);
     }
 
     @Override
-    public void send(JmsOutboundMessageDispatch envelope, AsyncResult request) throws IOException, JMSException {
+    public void send(JmsOutboundMessageDispatch envelope, AsyncResult request) throws ProviderException {
         next.send(envelope, request);
     }
 
     @Override
-    public void acknowledge(JmsSessionId sessionId, ACK_TYPE ackType, AsyncResult request) throws IOException, JMSException {
+    public void acknowledge(JmsSessionId sessionId, ACK_TYPE ackType, AsyncResult request) throws ProviderException {
         next.acknowledge(sessionId, ackType, request);
     }
 
     @Override
-    public void acknowledge(JmsInboundMessageDispatch envelope, ACK_TYPE ackType, AsyncResult request) throws IOException, JMSException {
+    public void acknowledge(JmsInboundMessageDispatch envelope, ACK_TYPE ackType, AsyncResult request) throws ProviderException {
         next.acknowledge(envelope, ackType, request);
     }
 
     @Override
-    public void commit(JmsTransactionInfo transactionInfo, JmsTransactionInfo nextTransactionInfo, AsyncResult request) throws IOException, JMSException, UnsupportedOperationException {
+    public void commit(JmsTransactionInfo transactionInfo, JmsTransactionInfo nextTransactionInfo, AsyncResult request) throws ProviderException {
         next.commit(transactionInfo, nextTransactionInfo, request);
     }
 
     @Override
-    public void rollback(JmsTransactionInfo transactionInfo, JmsTransactionInfo nextTransactionInfo, AsyncResult request) throws IOException, JMSException, UnsupportedOperationException {
+    public void rollback(JmsTransactionInfo transactionInfo, JmsTransactionInfo nextTransactionInfo, AsyncResult request) throws ProviderException {
         next.rollback(transactionInfo, nextTransactionInfo, request);
     }
 
     @Override
-    public void recover(JmsSessionId sessionId, AsyncResult request) throws IOException, UnsupportedOperationException {
+    public void recover(JmsSessionId sessionId, AsyncResult request) throws ProviderException {
         next.recover(sessionId, request);
     }
 
     @Override
-    public void unsubscribe(String subscription, AsyncResult request) throws IOException, JMSException, UnsupportedOperationException {
+    public void unsubscribe(String subscription, AsyncResult request) throws ProviderException {
         next.unsubscribe(subscription, request);
     }
 
     @Override
-    public void pull(JmsConsumerId consumerId, long timeout, AsyncResult request) throws IOException, UnsupportedOperationException {
+    public void pull(JmsConsumerId consumerId, long timeout, AsyncResult request) throws ProviderException {
         next.pull(consumerId, timeout, request);
     }
 
@@ -176,7 +173,7 @@ public class ProviderWrapper<E extends Provider> implements Provider, ProviderLi
     }
 
     @Override
-    public void onFailedMessageSend(JmsOutboundMessageDispatch envelope, Throwable cause) {
+    public void onFailedMessageSend(JmsOutboundMessageDispatch envelope, ProviderException cause) {
         listener.onFailedMessageSend(envelope, cause);
     }
 
@@ -206,17 +203,17 @@ public class ProviderWrapper<E extends Provider> implements Provider, ProviderLi
     }
 
     @Override
-    public void onConnectionFailure(IOException ex) {
+    public void onConnectionFailure(ProviderException ex) {
         listener.onConnectionFailure(ex);
     }
 
     @Override
-    public void onResourceClosed(JmsResource resource, Throwable cause) {
+    public void onResourceClosed(JmsResource resource, ProviderException cause) {
         listener.onResourceClosed(resource, cause);
     }
 
     @Override
-    public void onProviderException(Exception cause) {
+    public void onProviderException(ProviderException cause) {
         listener.onProviderException(cause);
     }
 
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/WrappedAsyncResult.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/WrappedAsyncResult.java
index e382afe..ca482aa 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/WrappedAsyncResult.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/WrappedAsyncResult.java
@@ -38,7 +38,7 @@ public abstract class WrappedAsyncResult implements AsyncResult {
     }
 
     @Override
-    public void onFailure(Throwable result) {
+    public void onFailure(ProviderException result) {
         wrapped.onFailure(result);
     }
 
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpAbstractResource.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpAbstractResource.java
index b6e6925..4507959 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpAbstractResource.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpAbstractResource.java
@@ -16,14 +16,14 @@
  */
 package org.apache.qpid.jms.provider.amqp;
 
-import java.io.IOException;
 import java.util.concurrent.ScheduledFuture;
 
-import org.apache.qpid.jms.JmsOperationTimedOutException;
 import org.apache.qpid.jms.meta.JmsConnectionInfo;
 import org.apache.qpid.jms.meta.JmsResource;
 import org.apache.qpid.jms.meta.JmsResource.ResourceState;
 import org.apache.qpid.jms.provider.AsyncResult;
+import org.apache.qpid.jms.provider.ProviderException;
+import org.apache.qpid.jms.provider.exceptions.ProviderOperationTimedOutException;
 import org.apache.qpid.proton.engine.Delivery;
 import org.apache.qpid.proton.engine.Endpoint;
 import org.apache.qpid.proton.engine.EndpointState;
@@ -123,7 +123,7 @@ public abstract class AmqpAbstractResource<R extends JmsResource, E extends Endp
                     }
 
                     @Override
-                    public void onFailure(Throwable result) {
+                    public void onFailure(ProviderException result) {
                         closeResource(getParent().getProvider(), result, false);
                     }
 
@@ -132,13 +132,13 @@ public abstract class AmqpAbstractResource<R extends JmsResource, E extends Endp
                         return closeRequest != null ? closeRequest.isComplete() : true;
                     }
 
-                }, closeTimeout, new JmsOperationTimedOutException("Timed Out Waiting for close response: " + this));
+                }, closeTimeout, new ProviderOperationTimedOutException("Timed Out Waiting for close response: " + this));
         }
 
         closeOrDetachEndpoint();
     }
 
-    public void closeResource(AmqpProvider provider, Throwable cause, boolean remotelyClosed) {
+    public void closeResource(AmqpProvider provider, ProviderException cause, boolean remotelyClosed) {
         if (parent != null) {
             parent.removeChildResource(this);
         }
@@ -187,7 +187,7 @@ public abstract class AmqpAbstractResource<R extends JmsResource, E extends Endp
         }
     }
 
-    public void handleResourceClosure(AmqpProvider provider, Throwable error) {
+    public void handleResourceClosure(AmqpProvider provider, ProviderException error) {
         // Nothing do be done here, subclasses can override as needed.
     }
 
@@ -245,33 +245,35 @@ public abstract class AmqpAbstractResource<R extends JmsResource, E extends Endp
     //----- AmqpResource implementation --------------------------------------//
 
     @Override
-    public final void processRemoteOpen(AmqpProvider provider) throws IOException {
+    public final void processRemoteOpen(AmqpProvider provider) throws ProviderException {
         // Open is handled by the resource builder
     }
 
     @Override
-    public void processRemoteDetach(AmqpProvider provider) throws IOException {
+    public void processRemoteDetach(AmqpProvider provider) throws ProviderException {
         processRemoteClose(provider);
     }
 
     @Override
-    public void processRemoteClose(AmqpProvider provider) throws IOException {
+    public void processRemoteClose(AmqpProvider provider) throws ProviderException {
         getResourceInfo().setState(ResourceState.REMOTELY_CLOSED);
 
         if (isAwaitingClose()) {
             closeResource(provider, null, true); // Close was expected so ignore any endpoint errors.
         } else {
-            closeResource(provider, AmqpSupport.convertToException(provider, getEndpoint(), getEndpoint().getRemoteCondition()), true);
+            // For resources other than the Connection layer a remote close is not fatal, the client
+            // can conceivably continue on or opt to close down on its own.
+            closeResource(provider, AmqpSupport.convertToNonFatalException(provider, getEndpoint(), getEndpoint().getRemoteCondition()), true);
         }
     }
 
     @Override
-    public void processDeliveryUpdates(AmqpProvider provider, Delivery delivery) throws IOException {
+    public void processDeliveryUpdates(AmqpProvider provider, Delivery delivery) throws ProviderException {
         // Nothing do be done here, subclasses can override as needed.
     }
 
     @Override
-    public void processFlowUpdates(AmqpProvider provider) throws IOException {
+    public void processFlowUpdates(AmqpProvider provider) throws ProviderException {
         // Nothing do be done here, subclasses can override as needed.
     }
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpAnonymousFallbackProducer.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpAnonymousFallbackProducer.java
index a73ec92..292b360 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpAnonymousFallbackProducer.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpAnonymousFallbackProducer.java
@@ -16,16 +16,14 @@
  */
 package org.apache.qpid.jms.provider.amqp;
 
-import java.io.IOException;
 import java.util.Map;
 
-import javax.jms.JMSException;
-
 import org.apache.qpid.jms.JmsDestination;
 import org.apache.qpid.jms.message.JmsOutboundMessageDispatch;
 import org.apache.qpid.jms.meta.JmsProducerId;
 import org.apache.qpid.jms.meta.JmsProducerInfo;
 import org.apache.qpid.jms.provider.AsyncResult;
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.jms.provider.WrappedAsyncResult;
 import org.apache.qpid.jms.provider.amqp.builders.AmqpProducerBuilder;
 import org.apache.qpid.jms.util.IdGenerator;
@@ -69,7 +67,7 @@ public class AmqpAnonymousFallbackProducer extends AmqpProducer {
     }
 
     @Override
-    public void send(JmsOutboundMessageDispatch envelope, AsyncResult request) throws IOException, JMSException {
+    public void send(JmsOutboundMessageDispatch envelope, AsyncResult request) throws ProviderException {
         LOG.trace("Started send chain for anonymous producer: {}", getProducerId());
 
         // Force sends marked as asynchronous to be sent synchronous so that the temporary
@@ -155,7 +153,7 @@ public class AmqpAnonymousFallbackProducer extends AmqpProducer {
          * producer a failure will trigger the original send request to fail.
          */
         @Override
-        public void onFailure(Throwable result) {
+        public void onFailure(ProviderException result) {
             LOG.debug("Send failed during {} step in chain: {}", this.getClass().getName(), getProducerId());
             super.onFailure(result);
         }
@@ -179,7 +177,7 @@ public class AmqpAnonymousFallbackProducer extends AmqpProducer {
             AnonymousSendCompleteRequest send = new AnonymousSendCompleteRequest(this);
             try {
                 getProducer().send(envelope, send);
-            } catch (Exception e) {
+            } catch (ProviderException e) {
                 super.onFailure(e);
             }
         }
@@ -201,7 +199,7 @@ public class AmqpAnonymousFallbackProducer extends AmqpProducer {
         }
 
         @Override
-        public void onFailure(Throwable result) {
+        public void onFailure(ProviderException result) {
             LOG.trace("Send phase of anonymous send failed: {} ", getProducerId());
             if (!connection.isAnonymousProducerCache()) {
                 AnonymousCloseRequest close = new AnonymousCloseRequest(this);
@@ -258,7 +256,7 @@ public class AmqpAnonymousFallbackProducer extends AmqpProducer {
         }
 
         @Override
-        public void onFailure(Throwable result) {
+        public void onFailure(ProviderException result) {
             AmqpAnonymousFallbackProducer.this.connection.getProvider().fireProviderException(result);
         }
 
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpConnection.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpConnection.java
index a257701..30c6e02 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpConnection.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpConnection.java
@@ -16,7 +16,6 @@
  */
 package org.apache.qpid.jms.provider.amqp;
 
-import java.io.IOException;
 import java.net.URI;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -25,8 +24,6 @@ import java.util.Map;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
 
-import javax.jms.JMSException;
-
 import org.apache.qpid.jms.JmsDestination;
 import org.apache.qpid.jms.JmsTemporaryDestination;
 import org.apache.qpid.jms.meta.JmsConnectionInfo;
@@ -35,10 +32,10 @@ import org.apache.qpid.jms.meta.JmsSessionId;
 import org.apache.qpid.jms.meta.JmsSessionInfo;
 import org.apache.qpid.jms.provider.AsyncResult;
 import org.apache.qpid.jms.provider.ProviderException;
-import org.apache.qpid.jms.provider.ProviderResourceClosedException;
 import org.apache.qpid.jms.provider.amqp.builders.AmqpSessionBuilder;
 import org.apache.qpid.jms.provider.amqp.builders.AmqpTemporaryDestinationBuilder;
 import org.apache.qpid.jms.provider.amqp.message.AmqpJmsMessageFactory;
+import org.apache.qpid.jms.provider.exceptions.ProviderConnectionRemotelyClosedException;
 import org.apache.qpid.proton.engine.Connection;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -89,8 +86,8 @@ public class AmqpConnection extends AmqpAbstractResource<JmsConnectionInfo, Conn
 
     public void unsubscribe(String subscriptionName, AsyncResult request) {
         // Check if there is an active (i.e open subscriber) shared or exclusive durable subscription using this name
-        if(subTracker.isActiveDurableSub(subscriptionName)) {
-            request.onFailure(new JMSException("Can't remove an active durable subscription: " + subscriptionName));
+        if (subTracker.isActiveDurableSub(subscriptionName)) {
+            request.onFailure(new ProviderException("Can't remove an active durable subscription: " + subscriptionName));
             return;
         }
 
@@ -124,7 +121,7 @@ public class AmqpConnection extends AmqpAbstractResource<JmsConnectionInfo, Conn
     }
 
     @Override
-    public void handleResourceClosure(AmqpProvider provider, Throwable cause) {
+    public void handleResourceClosure(AmqpProvider provider, ProviderException cause) {
         if (connectionSession != null) {
             connectionSession.handleResourceClosure(getProvider(), cause);
         }
@@ -141,17 +138,15 @@ public class AmqpConnection extends AmqpAbstractResource<JmsConnectionInfo, Conn
     }
 
     @Override
-    public void processRemoteClose(AmqpProvider provider) throws IOException {
+    public void processRemoteClose(AmqpProvider provider) throws ProviderException {
         getResourceInfo().setState(ResourceState.REMOTELY_CLOSED);
 
         if (isAwaitingClose()) {
             closeResource(provider, null, true); // Close was expected so ignore any endpoint errors.
         } else {
-            Exception cause = AmqpSupport.convertToException(provider, getEndpoint(), getEndpoint().getRemoteCondition());
-
-            if (!(cause instanceof ProviderException)) {
-                cause = new ProviderResourceClosedException(cause.getMessage(), cause);
-            }
+            // This will create a fatal level exception that stops the provider possibly triggering reconnect
+            ProviderConnectionRemotelyClosedException cause = AmqpSupport.convertToConnectionClosedException(
+                provider, getEndpoint(), getEndpoint().getRemoteCondition());
 
             closeResource(provider, cause, true);
         }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpConnectionSession.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpConnectionSession.java
index beb5a0b..ba516a5 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpConnectionSession.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpConnectionSession.java
@@ -16,19 +16,19 @@
  */
 package org.apache.qpid.jms.provider.amqp;
 
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-import javax.jms.InvalidDestinationException;
-
 import org.apache.qpid.jms.meta.JmsSessionInfo;
 import org.apache.qpid.jms.provider.AsyncResult;
 import org.apache.qpid.jms.provider.NoOpAsyncResult;
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.jms.provider.WrappedAsyncResult;
 import org.apache.qpid.jms.provider.amqp.builders.AmqpResourceBuilder;
+import org.apache.qpid.jms.provider.exceptions.ProviderExceptionSupport;
+import org.apache.qpid.jms.provider.exceptions.ProviderInvalidDestinationException;
 import org.apache.qpid.proton.amqp.Symbol;
 import org.apache.qpid.proton.amqp.messaging.Target;
 import org.apache.qpid.proton.amqp.transport.ReceiverSettleMode;
@@ -99,7 +99,7 @@ public class AmqpConnectionSession extends AmqpSession {
     }
 
     @Override
-    public void handleResourceClosure(AmqpProvider provider, Throwable cause) {
+    public void handleResourceClosure(AmqpProvider provider, ProviderException cause) {
         List<AsyncResult> pending = new ArrayList<>(pendingUnsubs.values());
         for (AsyncResult unsubscribeRequest : pending) {
             unsubscribeRequest.onFailure(cause);
@@ -115,12 +115,12 @@ public class AmqpConnectionSession extends AmqpSession {
         }
 
         @Override
-        public void processRemoteClose(AmqpProvider provider) throws IOException {
+        public void processRemoteClose(AmqpProvider provider) throws ProviderException {
             // For unsubscribe we care if the remote signaled an error on the close since
             // that would indicate that the unsubscribe did not succeed and we want to throw
             // that from the unsubscribe call.
             if (getEndpoint().getRemoteCondition().getCondition() != null) {
-                closeResource(provider, AmqpSupport.convertToException(provider, getEndpoint(), getEndpoint().getRemoteCondition()), true);
+                closeResource(provider, AmqpSupport.convertToNonFatalException(provider, getEndpoint(), getEndpoint().getRemoteCondition()), true);
             } else {
                 closeResource(provider, null, true);
             }
@@ -191,16 +191,17 @@ public class AmqpConnectionSession extends AmqpSession {
                 subscriber.close(getWrappedRequest());
             } else {
                 subscriber.close(NoOpAsyncResult.INSTANCE);
-                getWrappedRequest().onFailure(new InvalidDestinationException("Cannot remove a subscription that does not exist"));
+                getWrappedRequest().onFailure(
+                    new ProviderInvalidDestinationException("Cannot remove a subscription that does not exist"));
             }
         }
 
         @Override
-        public void onFailure(Throwable cause) {
+        public void onFailure(ProviderException cause) {
             DurableSubscriptionReattach subscriber = subscriberBuilder.getResource();
             LOG.trace("Failed to reattach to subscription '{}' using link name '{}'", subscriptionName, subscriber.getLinkName());
             pendingUnsubs.remove(subscriptionName);
-            subscriber.closeResource(getProvider(), cause, false);
+            subscriber.closeResource(getProvider(), ProviderExceptionSupport.createNonFatalOrPassthrough(cause), false);
             super.onFailure(cause);
         }
     }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpConsumer.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpConsumer.java
index 197980d..0acbbb5 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpConsumer.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpConsumer.java
@@ -20,24 +20,23 @@ import static org.apache.qpid.jms.provider.amqp.AmqpSupport.MODIFIED_FAILED;
 import static org.apache.qpid.jms.provider.amqp.AmqpSupport.MODIFIED_FAILED_UNDELIVERABLE;
 import static org.apache.qpid.jms.provider.amqp.AmqpSupport.REJECTED;
 
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.ListIterator;
 import java.util.concurrent.ScheduledFuture;
 
 import org.apache.qpid.jms.JmsDestination;
-import org.apache.qpid.jms.JmsOperationTimedOutException;
-import org.apache.qpid.jms.exceptions.JmsExceptionSupport;
 import org.apache.qpid.jms.message.JmsInboundMessageDispatch;
 import org.apache.qpid.jms.message.JmsMessage;
 import org.apache.qpid.jms.meta.JmsConsumerId;
 import org.apache.qpid.jms.meta.JmsConsumerInfo;
 import org.apache.qpid.jms.provider.AsyncResult;
 import org.apache.qpid.jms.provider.ProviderConstants.ACK_TYPE;
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.jms.provider.ProviderListener;
 import org.apache.qpid.jms.provider.WrappedAsyncResult;
 import org.apache.qpid.jms.provider.amqp.message.AmqpCodec;
-import org.apache.qpid.jms.util.IOExceptionSupport;
+import org.apache.qpid.jms.provider.exceptions.ProviderExceptionSupport;
+import org.apache.qpid.jms.provider.exceptions.ProviderOperationTimedOutException;
 import org.apache.qpid.proton.amqp.Binary;
 import org.apache.qpid.proton.amqp.messaging.Accepted;
 import org.apache.qpid.proton.amqp.messaging.Released;
@@ -117,7 +116,7 @@ public class AmqpConsumer extends AmqpAbstractResource<JmsConsumerInfo, Receiver
                     // and leave the consumer open since the TX needs it to remain active.
                     final ScheduledFuture<?> future = getSession().schedule(() -> {
                         LOG.trace("Consumer {} stop timed out awaiting message processing", getConsumerId());
-                        Exception cause = new JmsOperationTimedOutException("Consumer stop timed out awaiting message processing");
+                        ProviderException cause = new ProviderOperationTimedOutException("Consumer stop timed out awaiting message processing");
                         if (session.isTransacted() && session.getTransactionContext().isInTransaction(getConsumerId())) {
                             stopRequest.onFailure(cause);
                             stopRequest = null;
@@ -150,7 +149,7 @@ public class AmqpConsumer extends AmqpAbstractResource<JmsConsumerInfo, Receiver
                 // and leave the consumer open since the TX needs it to remain active.
                 final ScheduledFuture<?> future = getSession().schedule(() -> {
                     LOG.trace("Consumer {} drain request timed out", getConsumerId());
-                    Exception cause = new JmsOperationTimedOutException("Remote did not respond to a drain request in time");
+                    ProviderException cause = new ProviderOperationTimedOutException("Remote did not respond to a drain request in time");
                     if (session.isTransacted() && session.getTransactionContext().isInTransaction(getConsumerId())) {
                         stopRequest.onFailure(cause);
                         stopRequest = null;
@@ -178,7 +177,7 @@ public class AmqpConsumer extends AmqpAbstractResource<JmsConsumerInfo, Receiver
     }
 
     @Override
-    public void processFlowUpdates(AmqpProvider provider) throws IOException {
+    public void processFlowUpdates(AmqpProvider provider) throws ProviderException {
         // Check if we tried to stop and have now run out of credit, and
         // processed all locally queued messages
         if (stopRequest != null) {
@@ -482,7 +481,7 @@ public class AmqpConsumer extends AmqpAbstractResource<JmsConsumerInfo, Receiver
     }
 
     @Override
-    public void processDeliveryUpdates(AmqpProvider provider, Delivery delivery) throws IOException {
+    public void processDeliveryUpdates(AmqpProvider provider, Delivery delivery) throws ProviderException {
         if(delivery.getDefaultDeliveryState() == null){
             delivery.setDefaultDeliveryState(Released.getInstance());
         }
@@ -499,7 +498,7 @@ public class AmqpConsumer extends AmqpAbstractResource<JmsConsumerInfo, Receiver
                     }
                 }
             } catch (Exception e) {
-                throw IOExceptionSupport.create(e);
+                throw ProviderExceptionSupport.createNonFatalOrPassthrough(e);
             }
         }
 
@@ -632,7 +631,7 @@ public class AmqpConsumer extends AmqpAbstractResource<JmsConsumerInfo, Receiver
     }
 
     @Override
-    public void handleResourceClosure(AmqpProvider provider, Throwable cause) {
+    public void handleResourceClosure(AmqpProvider provider, ProviderException cause) {
         AmqpConnection connection = session.getConnection();
         AmqpSubscriptionTracker subTracker = connection.getSubTracker();
         JmsConsumerInfo consumerInfo = getResourceInfo();
@@ -718,9 +717,9 @@ public class AmqpConsumer extends AmqpAbstractResource<JmsConsumerInfo, Receiver
     private final class DeferredCloseRequest implements AsyncResult {
 
         @Override
-        public void onFailure(Throwable result) {
+        public void onFailure(ProviderException result) {
             LOG.trace("Failed deferred close of consumer: {} - {}", getConsumerId(), result.getMessage());
-            getParent().getProvider().fireNonFatalProviderException(JmsExceptionSupport.create(result));
+            getParent().getProvider().fireNonFatalProviderException(ProviderExceptionSupport.createNonFatalOrPassthrough(result));
         }
 
         @Override
@@ -747,7 +746,7 @@ public class AmqpConsumer extends AmqpAbstractResource<JmsConsumerInfo, Receiver
         }
 
         @Override
-        public void onFailure(Throwable cause) {
+        public void onFailure(ProviderException cause) {
             sheduledTask.cancel(false);
             origRequest.onFailure(cause);
         }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpEventSink.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpEventSink.java
index 2e25ad1..51a50aa 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpEventSink.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpEventSink.java
@@ -16,8 +16,7 @@
  */
 package org.apache.qpid.jms.provider.amqp;
 
-import java.io.IOException;
-
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.proton.engine.Delivery;
 
 /**
@@ -32,9 +31,9 @@ public interface AmqpEventSink {
      * @param provider
      *        the AmqpProvider instance for easier access to fire events.
      *
-     * @throws IOException if an error occurs while processing the update.
+     * @throws ProviderException if an error occurs while processing the update.
      */
-    void processRemoteOpen(AmqpProvider provider) throws IOException;
+    void processRemoteOpen(AmqpProvider provider) throws ProviderException;
 
     /**
      * Event handler for remote peer detach of this resource.
@@ -42,9 +41,9 @@ public interface AmqpEventSink {
      * @param provider
      *        the AmqpProvider instance for easier access to fire events.
      *
-     * @throws IOException if an error occurs while processing the update.
+     * @throws ProviderException if an error occurs while processing the update.
      */
-    void processRemoteDetach(AmqpProvider provider) throws IOException;
+    void processRemoteDetach(AmqpProvider provider) throws ProviderException;
 
     /**
      * Event handler for remote peer close of this resource.
@@ -52,9 +51,9 @@ public interface AmqpEventSink {
      * @param provider
      *        the AmqpProvider instance for easier access to fire events.
      *
-     * @throws IOException if an error occurs while processing the update.
+     * @throws ProviderException if an error occurs while processing the update.
      */
-    void processRemoteClose(AmqpProvider provider) throws IOException;
+    void processRemoteClose(AmqpProvider provider) throws ProviderException;
 
     /**
      * Called when the Proton Engine signals an Delivery related event has been triggered
@@ -65,9 +64,9 @@ public interface AmqpEventSink {
      * @param delivery
      *        the Delivery that has an update to its state which needs handled.
      *
-     * @throws IOException if an error occurs while processing the update.
+     * @throws ProviderException if an error occurs while processing the update.
      */
-    void processDeliveryUpdates(AmqpProvider provider, Delivery delivery) throws IOException;
+    void processDeliveryUpdates(AmqpProvider provider, Delivery delivery) throws ProviderException;
 
     /**
      * Called when the Proton Engine signals an Flow related event has been triggered
@@ -76,8 +75,8 @@ public interface AmqpEventSink {
      * @param provider
      *        the AmqpProvider instance for easier access to fire events.
      *
-     * @throws IOException if an error occurs while processing the update.
+     * @throws ProviderException if an error occurs while processing the update.
      */
-    void processFlowUpdates(AmqpProvider provider) throws IOException;
+    void processFlowUpdates(AmqpProvider provider) throws ProviderException;
 
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpExceptionBuilder.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpExceptionBuilder.java
index 2ecf245..dac187a 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpExceptionBuilder.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpExceptionBuilder.java
@@ -18,6 +18,8 @@
  */
 package org.apache.qpid.jms.provider.amqp;
 
+import org.apache.qpid.jms.provider.ProviderException;
+
 /**
  * Used to provide a source for an exception based on some event such as
  * operation timed out, etc.
@@ -29,6 +31,6 @@ public interface AmqpExceptionBuilder {
      *
      * @return a new Exception instance that describes a failure condition.
      */
-    Exception createException();
+    ProviderException createException();
 
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpFixedProducer.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpFixedProducer.java
index c3010d2..d54f1f4 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpFixedProducer.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpFixedProducer.java
@@ -16,7 +16,6 @@
  */
 package org.apache.qpid.jms.provider.amqp;
 
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
@@ -24,16 +23,19 @@ import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.concurrent.ScheduledFuture;
 
-import javax.jms.IllegalStateException;
-import javax.jms.JMSException;
-
-import org.apache.qpid.jms.JmsSendTimedOutException;
 import org.apache.qpid.jms.message.JmsOutboundMessageDispatch;
 import org.apache.qpid.jms.meta.JmsConnectionInfo;
 import org.apache.qpid.jms.meta.JmsProducerInfo;
 import org.apache.qpid.jms.provider.AsyncResult;
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.jms.provider.amqp.message.AmqpReadableBuffer;
-import org.apache.qpid.jms.util.IOExceptionSupport;
+import org.apache.qpid.jms.provider.exceptions.ProviderDeliveryModifiedException;
+import org.apache.qpid.jms.provider.exceptions.ProviderDeliveryReleasedException;
+import org.apache.qpid.jms.provider.exceptions.ProviderExceptionSupport;
+import org.apache.qpid.jms.provider.exceptions.ProviderIllegalStateException;
+import org.apache.qpid.jms.provider.exceptions.ProviderSendTimedOutException;
+import org.apache.qpid.jms.provider.exceptions.ProviderUnsupportedOperationException;
+import org.apache.qpid.proton.amqp.messaging.Modified;
 import org.apache.qpid.proton.amqp.messaging.Rejected;
 import org.apache.qpid.proton.amqp.transaction.TransactionalState;
 import org.apache.qpid.proton.amqp.transport.DeliveryState;
@@ -84,14 +86,14 @@ public class AmqpFixedProducer extends AmqpProducer {
     }
 
     @Override
-    public void send(JmsOutboundMessageDispatch envelope, AsyncResult request) throws IOException, JMSException {
+    public void send(JmsOutboundMessageDispatch envelope, AsyncResult request) throws ProviderException {
         if (isClosed()) {
-            request.onFailure(new IllegalStateException("The MessageProducer is closed"));
+            request.onFailure(new ProviderIllegalStateException("The MessageProducer is closed"));
         }
 
         if (!delayedDeliverySupported && envelope.getMessage().getFacade().isDeliveryTimeTransmitted()) {
             // Don't allow sends with delay if the remote has not said it can handle them
-            request.onFailure(new JMSException("Remote does not support delayed message delivery"));
+            request.onFailure(new ProviderUnsupportedOperationException("Remote does not support delayed message delivery"));
         } else if (getEndpoint().getCredit() <= 0) {
             LOG.trace("Holding Message send until credit is available.");
 
@@ -115,7 +117,7 @@ public class AmqpFixedProducer extends AmqpProducer {
         }
     }
 
-    private void doSend(JmsOutboundMessageDispatch envelope, InFlightSend send) throws IOException, JMSException {
+    private void doSend(JmsOutboundMessageDispatch envelope, InFlightSend send) throws ProviderException {
         LOG.trace("Producer sending message: {}", envelope);
 
         boolean presettle = envelope.isPresettle() || isPresettle();
@@ -168,12 +170,16 @@ public class AmqpFixedProducer extends AmqpProducer {
                 send.getOriginalRequest().onSuccess();
             }
 
-            provider.getTransport().flush();
+            try {
+                provider.getTransport().flush();
+            } catch (Throwable ex) {
+                throw ProviderExceptionSupport.createOrPassthroughFatal(ex);
+            }
         }
     }
 
     @Override
-    public void processFlowUpdates(AmqpProvider provider) throws IOException {
+    public void processFlowUpdates(AmqpProvider provider) throws ProviderException {
         if (!blocked.isEmpty() && getEndpoint().getCredit() > 0) {
             Iterator<InFlightSend> blockedSends = blocked.values().iterator();
             while (getEndpoint().getCredit() > 0 && blockedSends.hasNext()) {
@@ -188,8 +194,6 @@ public class AmqpFixedProducer extends AmqpProducer {
                     }
 
                     doSend(held.getEnvelope(), held);
-                } catch (JMSException e) {
-                    throw IOExceptionSupport.create(e);
                 } finally {
                     blockedSends.remove();
                 }
@@ -205,7 +209,7 @@ public class AmqpFixedProducer extends AmqpProducer {
     }
 
     @Override
-    public void processDeliveryUpdates(AmqpProvider provider, Delivery delivery) throws IOException {
+    public void processDeliveryUpdates(AmqpProvider provider, Delivery delivery) throws ProviderException {
         DeliveryState state = delivery.getRemoteState();
         if (state != null) {
             InFlightSend send = (InFlightSend) delivery.getContext();
@@ -222,7 +226,7 @@ public class AmqpFixedProducer extends AmqpProducer {
     }
 
     private void applyDeliveryStateUpdate(InFlightSend send, Delivery delivery, DeliveryState state) {
-        Exception deliveryError = null;
+        ProviderException deliveryError = null;
         if (state == null) {
             return;
         }
@@ -243,15 +247,16 @@ public class AmqpFixedProducer extends AmqpProducer {
                     remoteError = getEndpoint().getRemoteCondition();
                 }
 
-                deliveryError = AmqpSupport.convertToException(getParent().getProvider(), getEndpoint(), remoteError);
+                deliveryError = AmqpSupport.convertToNonFatalException(getParent().getProvider(), getEndpoint(), remoteError);
                 break;
             case Released:
                 LOG.trace("Outcome of delivery was released: {}", delivery);
-                deliveryError = new JMSException("Delivery failed: released by receiver");
+                deliveryError = new ProviderDeliveryReleasedException("Delivery failed: released by receiver");
                 break;
             case Modified:
                 LOG.trace("Outcome of delivery was modified: {}", delivery);
-                deliveryError = new JMSException("Delivery failed: failure at remote");
+                Modified modified = (Modified) state;
+                deliveryError = new ProviderDeliveryModifiedException("Delivery failed: failure at remote", modified);
                 break;
             default:
                 LOG.warn("Message send updated with unsupported state: {}", state);
@@ -286,10 +291,10 @@ public class AmqpFixedProducer extends AmqpProducer {
     }
 
     @Override
-    public void handleResourceClosure(AmqpProvider provider, Throwable error) {
+    public void handleResourceClosure(AmqpProvider provider, ProviderException error) {
         if (error == null) {
             // TODO: create/use a more specific/appropriate exception type?
-            error = new JMSException("Producer closed remotely before message transfer result was notified");
+            error = new ProviderException("Producer closed remotely before message transfer result was notified");
         }
 
         Collection<InFlightSend> inflightSends = new ArrayList<InFlightSend>(sent.values());
@@ -327,16 +332,16 @@ public class AmqpFixedProducer extends AmqpProducer {
         }
 
         @Override
-        public void onFailure(Throwable cause) {
+        public void onFailure(ProviderException cause) {
             handleSendCompletion(false);
 
             if (request.isComplete()) {
                 // Asynchronous sends can still be awaiting a completion in which case we
                 // send to them otherwise send to the listener to be reported.
                 if (envelope.isCompletionRequired()) {
-                    getParent().getProvider().getProviderListener().onFailedMessageSend(envelope, cause);
+                    getParent().getProvider().getProviderListener().onFailedMessageSend(envelope, ProviderExceptionSupport.createNonFatalOrPassthrough(cause));
                 } else {
-                    getParent().getProvider().fireNonFatalProviderException(IOExceptionSupport.create(cause));
+                    getParent().getProvider().fireNonFatalProviderException(ProviderExceptionSupport.createNonFatalOrPassthrough(cause));
                 }
             } else {
                 request.onFailure(cause);
@@ -414,11 +419,11 @@ public class AmqpFixedProducer extends AmqpProducer {
         }
 
         @Override
-        public Exception createException() {
+        public ProviderException createException() {
             if (delivery == null) {
-                return new JmsSendTimedOutException("Timed out waiting for credit to send Message", envelope.getMessage());
+                return new ProviderSendTimedOutException("Timed out waiting for credit to send Message", envelope.getMessage());
             } else {
-                return new JmsSendTimedOutException("Timed out waiting for disposition of sent Message", envelope.getMessage());
+                return new ProviderSendTimedOutException("Timed out waiting for disposition of sent Message", envelope.getMessage());
             }
         }
     }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpProducer.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpProducer.java
index 6999d76..8afc5d0 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpProducer.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpProducer.java
@@ -16,14 +16,11 @@
  */
 package org.apache.qpid.jms.provider.amqp;
 
-import java.io.IOException;
-
-import javax.jms.JMSException;
-
 import org.apache.qpid.jms.message.JmsOutboundMessageDispatch;
 import org.apache.qpid.jms.meta.JmsProducerId;
 import org.apache.qpid.jms.meta.JmsProducerInfo;
 import org.apache.qpid.jms.provider.AsyncResult;
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.proton.engine.Sender;
 
 /**
@@ -55,10 +52,9 @@ public abstract class AmqpProducer extends AmqpAbstractResource<JmsProducerInfo,
      * @param request
      *        The AsyncRequest that will be notified on send success or failure.
      *
-     * @throws IOException if an error occurs sending the message
-     * @throws JMSException if an error occurs while preparing the message for send.
+     * @throws ProviderException if an error occurs sending the message
      */
-    public abstract void send(JmsOutboundMessageDispatch envelope, AsyncResult request) throws IOException, JMSException;
+    public abstract void send(JmsOutboundMessageDispatch envelope, AsyncResult request) throws ProviderException;
 
     /**
      * @return true if this is an anonymous producer or false if fixed to a given destination.
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpProvider.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpProvider.java
index f5d1ab4..c3c85a1 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpProvider.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpProvider.java
@@ -16,10 +16,8 @@
  */
 package org.apache.qpid.jms.provider.amqp;
 
-import java.io.IOException;
 import java.net.URI;
 import java.nio.ByteBuffer;
-import java.security.ProviderException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
@@ -35,8 +33,6 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import javax.jms.JMSException;
-import javax.jms.JMSSecurityRuntimeException;
 import javax.net.ssl.SSLContext;
 
 import org.apache.qpid.jms.JmsConnectionExtensions;
@@ -58,20 +54,26 @@ import org.apache.qpid.jms.meta.JmsTransactionInfo;
 import org.apache.qpid.jms.provider.AsyncResult;
 import org.apache.qpid.jms.provider.NoOpAsyncResult;
 import org.apache.qpid.jms.provider.Provider;
-import org.apache.qpid.jms.provider.ProviderClosedException;
 import org.apache.qpid.jms.provider.ProviderConstants.ACK_TYPE;
-import org.apache.qpid.jms.provider.ProviderFailedException;
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.jms.provider.ProviderFuture;
 import org.apache.qpid.jms.provider.ProviderFutureFactory;
 import org.apache.qpid.jms.provider.ProviderListener;
 import org.apache.qpid.jms.provider.ProviderSynchronization;
 import org.apache.qpid.jms.provider.amqp.builders.AmqpClosedConnectionBuilder;
 import org.apache.qpid.jms.provider.amqp.builders.AmqpConnectionBuilder;
+import org.apache.qpid.jms.provider.exceptions.ProviderClosedException;
+import org.apache.qpid.jms.provider.exceptions.ProviderExceptionSupport;
+import org.apache.qpid.jms.provider.exceptions.ProviderFailedException;
+import org.apache.qpid.jms.provider.exceptions.ProviderIdleTimeoutException;
+import org.apache.qpid.jms.provider.exceptions.ProviderIllegalStateException;
+import org.apache.qpid.jms.provider.exceptions.ProviderOperationTimedOutException;
+import org.apache.qpid.jms.provider.exceptions.ProviderTransactionInDoubtException;
 import org.apache.qpid.jms.sasl.Mechanism;
 import org.apache.qpid.jms.sasl.SaslMechanismFinder;
+import org.apache.qpid.jms.sasl.SaslSecurityRuntimeException;
 import org.apache.qpid.jms.transports.Transport;
 import org.apache.qpid.jms.transports.TransportListener;
-import org.apache.qpid.jms.util.IOExceptionSupport;
 import org.apache.qpid.jms.util.PropertyUtil;
 import org.apache.qpid.jms.util.QpidJMSThreadFactory;
 import org.apache.qpid.proton.engine.Collector;
@@ -168,7 +170,7 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
     }
 
     @Override
-    public void connect(final JmsConnectionInfo connectionInfo) throws IOException {
+    public void connect(final JmsConnectionInfo connectionInfo) throws ProviderException {
         checkClosedOrFailed();
 
         if (serializer != null) {
@@ -287,12 +289,12 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
                 connectRequest.onSuccess();
             }
         } catch (Throwable t) {
-            connectRequest.onFailure(IOExceptionSupport.create(t));
+            connectRequest.onFailure(ProviderExceptionSupport.createOrPassthroughFatal(t));
         }
 
         if (connectionInfo.getConnectTimeout() != JmsConnectionInfo.INFINITE) {
             if (!connectRequest.sync(connectionInfo.getConnectTimeout(), TimeUnit.MILLISECONDS)) {
-                throw new IOException("Timed out while waiting to connect");
+                throw new ProviderOperationTimedOutException("Timed out while waiting to connect");
             }
         } else {
             connectRequest.sync();
@@ -300,7 +302,7 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
     }
 
     @Override
-    public void start() throws IOException, IllegalStateException {
+    public void start() throws ProviderException, IllegalStateException {
         checkClosedOrFailed();
 
         if (listener == null) {
@@ -375,7 +377,7 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
                 } else {
                     request.sync(getCloseTimeout(), TimeUnit.MILLISECONDS);
                 }
-            } catch (IOException e) {
+            } catch (ProviderException e) {
                 LOG.warn("Error caught while closing Provider: {}", e.getMessage() != null ? e.getMessage() : "<Unknown Error>");
             } finally {
                 if (transport != null) {
@@ -390,7 +392,7 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
     }
 
     @Override
-    public void create(final JmsResource resource, final AsyncResult request) throws IOException, JMSException {
+    public void create(final JmsResource resource, final AsyncResult request) throws ProviderException {
         checkClosedOrFailed();
         checkConnected();
 
@@ -436,7 +438,7 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
                             }
 
                             @Override
-                            public void onFailure(Throwable result) {
+                            public void onFailure(ProviderException result) {
                                 request.onFailure(result);
                             }
 
@@ -467,13 +469,13 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
 
                 pumpToProtonTransport(request);
             } catch (Throwable t) {
-                request.onFailure(t);
+                request.onFailure(ProviderExceptionSupport.createNonFatalOrPassthrough(t));
             }
         });
     }
 
     @Override
-    public void start(final JmsResource resource, final AsyncResult request) throws IOException {
+    public void start(final JmsResource resource, final AsyncResult request) throws ProviderException {
         checkClosedOrFailed();
         checkConnected();
 
@@ -493,13 +495,13 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
 
                 pumpToProtonTransport(request);
             } catch (Throwable t) {
-                request.onFailure(t);
+                request.onFailure(ProviderExceptionSupport.createNonFatalOrPassthrough(t));
             }
         });
     }
 
     @Override
-    public void stop(final JmsResource resource, final AsyncResult request) throws IOException {
+    public void stop(final JmsResource resource, final AsyncResult request) throws ProviderException {
         checkClosedOrFailed();
         checkConnected();
 
@@ -519,13 +521,13 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
 
                 pumpToProtonTransport(request);
             } catch (Throwable t) {
-                request.onFailure(t);
+                request.onFailure(ProviderExceptionSupport.createNonFatalOrPassthrough(t));
             }
         });
     }
 
     @Override
-    public void destroy(final JmsResource resource, final AsyncResult request) throws IOException {
+    public void destroy(final JmsResource resource, final AsyncResult request) throws ProviderException {
         checkClosedOrFailed();
         checkConnected();
 
@@ -547,7 +549,7 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
                             }
 
                             @Override
-                            public void onFailure(Throwable result) {
+                            public void onFailure(ProviderException result) {
                                 onComplete();
                                 request.onFailure(result);
                             }
@@ -585,7 +587,7 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
                             }
 
                             @Override
-                            public void onFailure(Throwable result) {
+                            public void onFailure(ProviderException result) {
                                 onComplete();
                                 request.onFailure(result);
                             }
@@ -620,13 +622,13 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
 
                 pumpToProtonTransport(request);
             } catch (Throwable t) {
-                request.onFailure(t);
+                request.onFailure(ProviderExceptionSupport.createNonFatalOrPassthrough(t));
             }
         });
     }
 
     @Override
-    public void send(final JmsOutboundMessageDispatch envelope, final AsyncResult request) throws IOException {
+    public void send(final JmsOutboundMessageDispatch envelope, final AsyncResult request) throws ProviderException {
         checkClosedOrFailed();
         checkConnected();
 
@@ -638,13 +640,13 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
                 AmqpProducer producer = (AmqpProducer) producerId.getProviderHint();
                 producer.send(envelope, request);
             } catch (Throwable t) {
-                request.onFailure(t);
+                request.onFailure(ProviderExceptionSupport.createNonFatalOrPassthrough(t));
             }
         });
     }
 
     @Override
-    public void acknowledge(final JmsSessionId sessionId, final ACK_TYPE ackType, final AsyncResult request) throws IOException {
+    public void acknowledge(final JmsSessionId sessionId, final ACK_TYPE ackType, final AsyncResult request) throws ProviderException {
         checkClosedOrFailed();
         checkConnected();
 
@@ -653,17 +655,21 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
             try {
                 checkClosedOrFailed();
                 AmqpSession amqpSession = connection.getSession(sessionId);
-                amqpSession.acknowledge(ackType);
-                pumpToProtonTransport(request);
-                request.onSuccess();
+                if (amqpSession != null) {
+                    amqpSession.acknowledge(ackType);
+                    pumpToProtonTransport(request);
+                    request.onSuccess();
+                } else {
+                    throw new ProviderIllegalStateException("Cannot acknowledge message from session that does not exist.");
+                }
             } catch (Throwable t) {
-                request.onFailure(t);
+                request.onFailure(ProviderExceptionSupport.createNonFatalOrPassthrough(t));
             }
         });
     }
 
     @Override
-    public void acknowledge(final JmsInboundMessageDispatch envelope, final ACK_TYPE ackType, final AsyncResult request) throws IOException {
+    public void acknowledge(final JmsInboundMessageDispatch envelope, final ACK_TYPE ackType, final AsyncResult request) throws ProviderException {
         checkClosedOrFailed();
         checkConnected();
 
@@ -686,13 +692,13 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
                     transport.flush();
                 }
             } catch (Throwable t) {
-                request.onFailure(t);
+                request.onFailure(ProviderExceptionSupport.createNonFatalOrPassthrough(t));
             }
         });
     }
 
     @Override
-    public void commit(final JmsTransactionInfo transactionInfo, JmsTransactionInfo nextTransactionId, final AsyncResult request) throws IOException {
+    public void commit(final JmsTransactionInfo transactionInfo, JmsTransactionInfo nextTransactionId, final AsyncResult request) throws ProviderException {
         checkClosedOrFailed();
         checkConnected();
 
@@ -701,16 +707,24 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
             try {
                 checkClosedOrFailed();
                 AmqpSession session = connection.getSession(transactionInfo.getSessionId());
-                session.commit(transactionInfo, nextTransactionId, request);
-                pumpToProtonTransport(request);
+                if (session != null) {
+                    session.commit(transactionInfo, nextTransactionId, request);
+                    pumpToProtonTransport(request);
+                } else {
+                    if (transactionInfo.isInDoubt()) {
+                        throw new ProviderTransactionInDoubtException("Commit of in-doubt transaction failed because no session exists");
+                    } else {
+                        throw new ProviderIllegalStateException("Commit of transaction failed because no session exists");
+                    }
+                }
             } catch (Throwable t) {
-                request.onFailure(t);
+                request.onFailure(ProviderExceptionSupport.createNonFatalOrPassthrough(t));
             }
         });
     }
 
     @Override
-    public void rollback(final JmsTransactionInfo transactionInfo, JmsTransactionInfo nextTransactionId, final AsyncResult request) throws IOException {
+    public void rollback(final JmsTransactionInfo transactionInfo, JmsTransactionInfo nextTransactionId, final AsyncResult request) throws ProviderException {
         checkClosedOrFailed();
         checkConnected();
 
@@ -719,16 +733,24 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
             try {
                 checkClosedOrFailed();
                 AmqpSession session = connection.getSession(transactionInfo.getSessionId());
-                session.rollback(transactionInfo, nextTransactionId, request);
-                pumpToProtonTransport(request);
+                if (session != null) {
+                    session.rollback(transactionInfo, nextTransactionId, request);
+                    pumpToProtonTransport(request);
+                } else {
+                    if (transactionInfo.isInDoubt()) {
+                        throw new ProviderTransactionInDoubtException("Rollback of in-doubt transaction failed because no session exists");
+                    } else {
+                        throw new ProviderIllegalStateException("Rollback of transaction failed because no session exists");
+                    }
+                }
             } catch (Throwable t) {
-                request.onFailure(t);
+                request.onFailure(ProviderExceptionSupport.createNonFatalOrPassthrough(t));
             }
         });
     }
 
     @Override
-    public void recover(final JmsSessionId sessionId, final AsyncResult request) throws IOException {
+    public void recover(final JmsSessionId sessionId, final AsyncResult request) throws ProviderException {
         checkClosedOrFailed();
         checkConnected();
 
@@ -737,17 +759,21 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
             try {
                 checkClosedOrFailed();
                 AmqpSession session = connection.getSession(sessionId);
-                session.recover();
-                pumpToProtonTransport(request);
-                request.onSuccess();
+                if (session != null) {
+                    session.recover();
+                    pumpToProtonTransport(request);
+                    request.onSuccess();
+                } else {
+                    throw new ProviderIllegalStateException("Cannot recover messages from session that does not exist");
+                }
             } catch (Throwable t) {
-                request.onFailure(t);
+                request.onFailure(ProviderExceptionSupport.createNonFatalOrPassthrough(t));
             }
         });
     }
 
     @Override
-    public void unsubscribe(final String subscription, final AsyncResult request) throws IOException {
+    public void unsubscribe(final String subscription, final AsyncResult request) throws ProviderException {
         checkClosedOrFailed();
         checkConnected();
 
@@ -758,13 +784,13 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
                 connection.unsubscribe(subscription, request);
                 pumpToProtonTransport(request);
             } catch (Throwable t) {
-                request.onFailure(t);
+                request.onFailure(ProviderExceptionSupport.createNonFatalOrPassthrough(t));
             }
         });
     }
 
     @Override
-    public void pull(final JmsConsumerId consumerId, final long timeout, final AsyncResult request) throws IOException {
+    public void pull(final JmsConsumerId consumerId, final long timeout, final AsyncResult request) throws ProviderException {
         checkClosedOrFailed();
         checkConnected();
 
@@ -776,7 +802,7 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
                 consumer.pull(timeout, request);
                 pumpToProtonTransport(request);
             } catch (Throwable t) {
-                request.onFailure(t);
+                request.onFailure(ProviderExceptionSupport.createNonFatalOrPassthrough(t));
             }
         });
     }
@@ -799,7 +825,7 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
                 }
             } catch (Throwable t) {
                 LOG.warn("Caught problem during task processing: {}", t.getMessage(), t);
-                fireProviderException(t);
+                fireProviderException(ProviderExceptionSupport.createNonFatalOrPassthrough(t));
             }
         });
     }
@@ -825,7 +851,7 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
             pumpToProtonTransport();
         } catch (Throwable t) {
             LOG.warn("Caught problem during data processing: {}", t.getMessage(), t);
-            fireProviderException(t);
+            fireProviderException(ProviderExceptionSupport.createOrPassthroughFatal(t));
         }
     }
 
@@ -844,7 +870,7 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
                 if (!closed.get()) {
                     // We can't send any more output, so close the transport
                     protonTransport.close_head();
-                    fireProviderException(error);
+                    fireProviderException(ProviderExceptionSupport.createOrPassthroughFatal(error));
                 }
             });
         }
@@ -864,7 +890,7 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
                 if (!closed.get()) {
                     // We can't send any more output, so close the transport
                     protonTransport.close_head();
-                    fireProviderException(new IOException("Transport connection remotely closed."));
+                    fireProviderException(new ProviderFailedException("Transport connection remotely closed."));
                 }
             });
         }
@@ -893,7 +919,7 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
                 org.apache.qpid.proton.engine.Transport t = protonConnection.getTransport();
                 t.close_head();
             } finally {
-                fireProviderException(ex);
+                fireProviderException(ProviderExceptionSupport.createOrPassthroughFatal(ex));
             }
         }
     }
@@ -972,7 +998,7 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
             try {
                 LOG.warn("Caught problem during update processing: {}", t.getMessage(), t);
             } finally {
-                fireProviderException(t);
+                fireProviderException(ProviderExceptionSupport.createOrPassthroughFatal(t));
             }
         }
     }
@@ -1016,9 +1042,10 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
             if (flush && bytesWritten > 0) {
                 transport.flush();
             }
-        } catch (IOException e) {
-            fireProviderException(e);
-            request.onFailure(e);
+        } catch (Throwable thrown) {
+            ProviderException pex = ProviderExceptionSupport.createOrPassthroughFatal(thrown);
+            fireProviderException(pex);
+            request.onFailure(pex);
             return false;
         }
 
@@ -1044,14 +1071,14 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
         }
     }
 
-    void fireNonFatalProviderException(Exception ex) {
+    void fireNonFatalProviderException(ProviderException ex) {
         ProviderListener listener = this.listener;
         if (listener != null) {
             listener.onProviderException(ex);
         }
     }
 
-    void fireProviderException(Throwable ex) {
+    void fireProviderException(ProviderException ex) {
         if (connectionRequest != null) {
             connectionRequest.onFailure(ex);
             connectionRequest = null;
@@ -1066,11 +1093,11 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
 
         ProviderListener listener = this.listener;
         if (listener != null) {
-            listener.onConnectionFailure(IOExceptionSupport.create(ex));
+            listener.onConnectionFailure(ProviderExceptionSupport.createNonFatalOrPassthrough(ex));
         }
     }
 
-    void fireResourceClosed(JmsResource resource, Throwable cause) {
+    void fireResourceClosed(JmsResource resource, ProviderException cause) {
         ProviderListener listener = this.listener;
         if (listener != null) {
             listener.onResourceClosed(resource, cause);
@@ -1384,7 +1411,7 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
      *
      * @return a {@link ScheduledFuture} that can be stored by the caller.
      */
-    public ScheduledFuture<?> scheduleRequestTimeout(final AsyncResult request, long timeout, final Exception error) {
+    public ScheduledFuture<?> scheduleRequestTimeout(final AsyncResult request, long timeout, final ProviderException error) {
         if (timeout != JmsConnectionInfo.INFINITE) {
             return serializer.schedule(() -> {
                 request.onFailure(error);
@@ -1422,7 +1449,7 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
 
     //----- Internal implementation ------------------------------------------//
 
-    private void checkClosedOrFailed() throws ProviderClosedException, ProviderFailedException {
+    private void checkClosedOrFailed() throws ProviderException {
         if (closed.get()) {
             throw new ProviderClosedException("This Provider is already closed");
         }
@@ -1432,13 +1459,13 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
         }
     }
 
-    private void checkConnected() throws ProviderClosedException, ProviderFailedException {
+    private void checkConnected() throws ProviderException {
         if (serializer == null) {
-            throw new ProviderException("Transport has not been properly connected.");
+            throw new ProviderClosedException("Transport has not been properly connected.");
         }
     }
 
-    private Mechanism findSaslMechanism(String[] remoteMechanisms) throws JMSSecurityRuntimeException {
+    private Mechanism findSaslMechanism(String[] remoteMechanisms) throws SaslSecurityRuntimeException {
         final String username;
         if (connectionInfo.getExtensionMap().containsKey(JmsConnectionExtensions.USERNAME_OVERRIDE)) {
             username = (String) connectionInfo.getExtensionMap().get(
@@ -1469,7 +1496,7 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
 
             mechanism.init(Collections.unmodifiableMap(saslOptions));
         } catch (Exception ex) {
-            throw new RuntimeException("Failed to apply sasl options to mechanism: " + mechanism.getName() + ", reason: " + ex.toString(), ex);
+            throw new SaslSecurityRuntimeException("Failed to apply sasl options to mechanism: " + mechanism.getName() + ", reason: " + ex.toString(), ex);
         }
 
         return mechanism;
@@ -1490,7 +1517,7 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
                 if (protonTransport.isClosed()) {
                     LOG.info("IdleTimeoutCheck closed the transport due to the peer exceeding our requested idle-timeout.");
                     if (pumpSucceeded) {
-                        fireProviderException(new IOException("Transport closed due to the peer exceeding our requested idle-timeout"));
+                        fireProviderException(new ProviderIdleTimeoutException("Transport closed due to the peer exceeding our requested idle-timeout"));
                     }
                 } else {
                     if (deadline != 0) {
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSaslAuthenticator.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSaslAuthenticator.java
index 6b9aa7e..4c5cac5 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSaslAuthenticator.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSaslAuthenticator.java
@@ -16,16 +16,16 @@
  */
 package org.apache.qpid.jms.provider.amqp;
 
-import org.apache.qpid.jms.exceptions.JMSSecuritySaslException;
+import java.util.function.Function;
+
+import org.apache.qpid.jms.provider.exceptions.ProviderConnectionSecurityException;
+import org.apache.qpid.jms.provider.exceptions.ProviderConnectionSecuritySaslException;
 import org.apache.qpid.jms.sasl.Mechanism;
+import org.apache.qpid.jms.sasl.SaslSecurityRuntimeException;
 import org.apache.qpid.proton.engine.Sasl;
 import org.apache.qpid.proton.engine.Sasl.SaslOutcome;
 import org.apache.qpid.proton.engine.Transport;
 
-import java.util.function.Function;
-import javax.jms.JMSSecurityException;
-import javax.jms.JMSSecurityRuntimeException;
-
 /**
  * Manage the SASL authentication process
  */
@@ -35,7 +35,7 @@ public class AmqpSaslAuthenticator {
 
     private Mechanism mechanism;
     private boolean complete;
-    private JMSSecurityException failureCause;
+    private ProviderConnectionSecurityException failureCause;
 
     /**
      * Create the authenticator and initialize it.
@@ -51,7 +51,7 @@ public class AmqpSaslAuthenticator {
        return complete;
     }
 
-    public JMSSecurityException getFailureCause() {
+    public ProviderConnectionSecurityException getFailureCause() {
         return failureCause;
     }
 
@@ -71,8 +71,8 @@ public class AmqpSaslAuthenticator {
             if (remoteMechanisms != null && remoteMechanisms.length != 0) {
                 try {
                     mechanism = mechanismFinder.apply(remoteMechanisms);
-                } catch (JMSSecurityRuntimeException jmssre){
-                    recordFailure("Could not find a suitable SASL mechanism. " + jmssre.getMessage(), jmssre);
+                } catch (SaslSecurityRuntimeException ssre){
+                    recordFailure("Could not find a suitable SASL mechanism. " + ssre.getMessage(), ssre);
                     return;
                 }
 
@@ -131,7 +131,7 @@ public class AmqpSaslAuthenticator {
         }
 
         SaslOutcome outcome = sasl.getOutcome();
-        if(outcome.equals(SaslOutcome.PN_SASL_TEMP)) {
+        if (outcome.equals(SaslOutcome.PN_SASL_TEMP)) {
             message.append(", due to temporary system error.");
         }
 
@@ -157,12 +157,7 @@ public class AmqpSaslAuthenticator {
     }
 
     private void recordFailure(String message, Throwable cause, int outcome) {
-        failureCause = new JMSSecuritySaslException(message, outcome);
-        if (cause instanceof Exception) {
-            failureCause.setLinkedException((Exception) cause);
-        }
-        failureCause.initCause(cause);
-
+        failureCause = new ProviderConnectionSecuritySaslException(message, outcome, cause);
         complete = true;
     }
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSession.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSession.java
index c71a485..62d3071 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSession.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSession.java
@@ -35,6 +35,7 @@ import org.apache.qpid.jms.meta.JmsTransactionId;
 import org.apache.qpid.jms.meta.JmsTransactionInfo;
 import org.apache.qpid.jms.provider.AsyncResult;
 import org.apache.qpid.jms.provider.ProviderConstants.ACK_TYPE;
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.jms.provider.amqp.builders.AmqpConsumerBuilder;
 import org.apache.qpid.jms.provider.amqp.builders.AmqpProducerBuilder;
 import org.apache.qpid.proton.engine.Session;
@@ -232,7 +233,7 @@ public class AmqpSession extends AmqpAbstractResource<JmsSessionInfo, Session> i
     }
 
     @Override
-    public void handleResourceClosure(AmqpProvider provider, Throwable error) {
+    public void handleResourceClosure(AmqpProvider provider, ProviderException error) {
         List<AmqpConsumer> consumerList = new ArrayList<>(consumers.values());
         for (AmqpConsumer consumer : consumerList) {
             consumer.handleResourceClosure(provider, error);
@@ -251,7 +252,7 @@ public class AmqpSession extends AmqpAbstractResource<JmsSessionInfo, Session> i
      * @param error
      *        The error to forward on to the Provider error event handler.
      */
-    public void reportError(Exception error) {
+    public void reportError(ProviderException error) {
         getConnection().getProvider().fireProviderException(error);
     }
 
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSupport.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSupport.java
index f93d49f..eec4bd3 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSupport.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSupport.java
@@ -16,19 +16,19 @@
  */
 package org.apache.qpid.jms.provider.amqp;
 
-import java.io.IOException;
 import java.util.Map;
 
-import javax.jms.InvalidClientIDException;
-import javax.jms.InvalidDestinationException;
-import javax.jms.JMSException;
-import javax.jms.JMSSecurityException;
-import javax.jms.ResourceAllocationException;
-import javax.jms.TransactionRolledBackException;
-
-import org.apache.qpid.jms.JmsConnectionRemotelyClosedException;
-import org.apache.qpid.jms.JmsResourceNotFoundException;
-import org.apache.qpid.jms.provider.ProviderRedirectedException;
+import org.apache.qpid.jms.provider.ProviderException;
+import org.apache.qpid.jms.provider.exceptions.ProviderConnectionRedirectedException;
+import org.apache.qpid.jms.provider.exceptions.ProviderConnectionRemotelyClosedException;
+import org.apache.qpid.jms.provider.exceptions.ProviderConnectionResourceAllocationException;
+import org.apache.qpid.jms.provider.exceptions.ProviderConnectionResourceNotFoundException;
+import org.apache.qpid.jms.provider.exceptions.ProviderConnectionSecurityException;
+import org.apache.qpid.jms.provider.exceptions.ProviderInvalidClientIDException;
+import org.apache.qpid.jms.provider.exceptions.ProviderInvalidDestinationException;
+import org.apache.qpid.jms.provider.exceptions.ProviderResourceAllocationException;
+import org.apache.qpid.jms.provider.exceptions.ProviderSecurityException;
+import org.apache.qpid.jms.provider.exceptions.ProviderTransactionRolledBackException;
 import org.apache.qpid.proton.amqp.Symbol;
 import org.apache.qpid.proton.amqp.messaging.Modified;
 import org.apache.qpid.proton.amqp.messaging.Rejected;
@@ -36,7 +36,6 @@ import org.apache.qpid.proton.amqp.transaction.TransactionErrors;
 import org.apache.qpid.proton.amqp.transport.AmqpError;
 import org.apache.qpid.proton.amqp.transport.ConnectionError;
 import org.apache.qpid.proton.amqp.transport.ErrorCondition;
-import org.apache.qpid.proton.engine.Connection;
 import org.apache.qpid.proton.engine.Endpoint;
 
 public class AmqpSupport {
@@ -103,7 +102,7 @@ public class AmqpSupport {
 
     /**
      * Given an ErrorCondition instance create a new Exception that best matches
-     * the error type.
+     * the error type that indicates the connection creation failed for some reason.
      *
      * @param provider
      * 		the AMQP provider instance that originates this exception
@@ -114,13 +113,45 @@ public class AmqpSupport {
      *
      * @return a new Exception instance that best matches the ErrorCondition value.
      */
-    public static Exception convertToException(AmqpProvider provider, Endpoint endpoint, ErrorCondition errorCondition) {
-        return convertToException(provider, endpoint, errorCondition, null);
+    public static ProviderConnectionRemotelyClosedException convertToConnectionClosedException(AmqpProvider provider, Endpoint endpoint, ErrorCondition errorCondition) {
+        ProviderConnectionRemotelyClosedException remoteError = null;
+
+        if (errorCondition != null && errorCondition.getCondition() != null) {
+            Symbol error = errorCondition.getCondition();
+            String message = extractErrorMessage(errorCondition);
+
+            if (error.equals(AmqpError.UNAUTHORIZED_ACCESS)) {
+                remoteError = new ProviderConnectionSecurityException(message);
+            } else if (error.equals(AmqpError.RESOURCE_LIMIT_EXCEEDED)) {
+                remoteError = new ProviderConnectionResourceAllocationException(message);
+            } else if (error.equals(ConnectionError.CONNECTION_FORCED)) {
+                remoteError = new ProviderConnectionRemotelyClosedException(message);
+            } else if (error.equals(AmqpError.NOT_FOUND)) {
+                remoteError = new ProviderConnectionResourceNotFoundException(message);
+            } else if (error.equals(ConnectionError.REDIRECT)) {
+                remoteError = createRedirectException(provider, error, message, errorCondition);
+            } else if (error.equals(AmqpError.INVALID_FIELD)) {
+                Map<?, ?> info = errorCondition.getInfo();
+                if (info != null && CONTAINER_ID.equals(info.get(INVALID_FIELD))) {
+                    remoteError = new ProviderInvalidClientIDException(message);
+                } else {
+                    remoteError = new ProviderConnectionRemotelyClosedException(message);
+                }
+            } else {
+                remoteError = new ProviderConnectionRemotelyClosedException(message);
+            }
+        } else if (remoteError == null) {
+            remoteError = new ProviderConnectionRemotelyClosedException("Unknown error from remote peer");
+        }
+
+        return remoteError;
     }
 
     /**
      * Given an ErrorCondition instance create a new Exception that best matches
-     * the error type.
+     * the error type that indicates a non-fatal error usually at the link level
+     * such as link closed remotely or link create failed due to security access
+     * issues.
      *
      * @param provider
      * 		the AMQP provider instance that originates this exception
@@ -128,46 +159,29 @@ public class AmqpSupport {
      *      The target of the error.
      * @param errorCondition
      *      The ErrorCondition returned from the remote peer.
-     * @param defaultException
-     *      The default exception to throw if no error information is provided from the remote.
      *
      * @return a new Exception instance that best matches the ErrorCondition value.
      */
-    public static Exception convertToException(AmqpProvider provider, Endpoint endpoint, ErrorCondition errorCondition, Exception defaultException) {
-        Exception remoteError = defaultException;
+    public static ProviderException convertToNonFatalException(AmqpProvider provider, Endpoint endpoint, ErrorCondition errorCondition) {
+        ProviderException remoteError = null;
 
         if (errorCondition != null && errorCondition.getCondition() != null) {
             Symbol error = errorCondition.getCondition();
             String message = extractErrorMessage(errorCondition);
 
             if (error.equals(AmqpError.UNAUTHORIZED_ACCESS)) {
-                remoteError = new JMSSecurityException(message);
+                remoteError = new ProviderSecurityException(message);
             } else if (error.equals(AmqpError.RESOURCE_LIMIT_EXCEEDED)) {
-                remoteError = new ResourceAllocationException(message);
-            } else if (error.equals(ConnectionError.CONNECTION_FORCED)) {
-                remoteError = new JmsConnectionRemotelyClosedException(message);
+                remoteError = new ProviderResourceAllocationException(message);
             } else if (error.equals(AmqpError.NOT_FOUND)) {
-                if (endpoint instanceof Connection) {
-                    remoteError = new JmsResourceNotFoundException(message);
-                } else {
-                    remoteError = new InvalidDestinationException(message);
-                }
+                remoteError = new ProviderInvalidDestinationException(message);
             } else if (error.equals(TransactionErrors.TRANSACTION_ROLLBACK)) {
-                remoteError = new TransactionRolledBackException(message);
-            } else if (error.equals(ConnectionError.REDIRECT)) {
-                remoteError = createRedirectException(provider, error, message, errorCondition);
-            } else if (error.equals(AmqpError.INVALID_FIELD)) {
-                Map<?, ?> info = errorCondition.getInfo();
-                if (info != null && CONTAINER_ID.equals(info.get(INVALID_FIELD))) {
-                    remoteError = new InvalidClientIDException(message);
-                } else {
-                    remoteError = new JMSException(message);
-                }
+                remoteError = new ProviderTransactionRolledBackException(message);
             } else {
-                remoteError = new JMSException(message);
+                remoteError = new ProviderException(message);
             }
         } else if (remoteError == null) {
-            remoteError = new JMSException("Unknown error from remote peer");
+            remoteError = new ProviderException("Unknown error from remote peer");
         }
 
         return remoteError;
@@ -213,20 +227,20 @@ public class AmqpSupport {
      *
      * @return an Exception that captures the details of the redirection error.
      */
-    public static Exception createRedirectException(AmqpProvider provider, Symbol error, String message, ErrorCondition condition) {
-        Exception result = null;
+    public static ProviderConnectionRemotelyClosedException createRedirectException(AmqpProvider provider, Symbol error, String message, ErrorCondition condition) {
+        ProviderConnectionRemotelyClosedException result = null;
         Map<?, ?> info = condition.getInfo();
 
         if (info == null) {
-            result = new IOException(message + " : Redirection information not set.");
+            result = new ProviderConnectionRemotelyClosedException(message + " : Redirection information not set.");
         } else {
             @SuppressWarnings("unchecked")
             AmqpRedirect redirect = new AmqpRedirect((Map<Symbol, Object>) info, provider);
 
             try {
-                result = new ProviderRedirectedException(message, redirect.validate().toURI());
+                result = new ProviderConnectionRedirectedException(message, redirect.validate().toURI());
             } catch (Exception ex) {
-                result = new IOException(message + " : " + ex.getMessage());
+                result = new ProviderConnectionRemotelyClosedException(message + " : " + ex.getMessage());
             }
         }
 
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpTransactionContext.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpTransactionContext.java
index 04910b0..b81cee1 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpTransactionContext.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpTransactionContext.java
@@ -16,20 +16,19 @@
  */
 package org.apache.qpid.jms.provider.amqp;
 
-import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
 
-import javax.jms.IllegalStateException;
-import javax.jms.TransactionRolledBackException;
-
 import org.apache.qpid.jms.meta.JmsConsumerId;
 import org.apache.qpid.jms.meta.JmsProducerId;
 import org.apache.qpid.jms.meta.JmsSessionInfo;
 import org.apache.qpid.jms.meta.JmsTransactionId;
 import org.apache.qpid.jms.meta.JmsTransactionInfo;
 import org.apache.qpid.jms.provider.AsyncResult;
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.jms.provider.amqp.builders.AmqpTransactionCoordinatorBuilder;
+import org.apache.qpid.jms.provider.exceptions.ProviderIllegalStateException;
+import org.apache.qpid.jms.provider.exceptions.ProviderTransactionRolledBackException;
 import org.apache.qpid.proton.amqp.Binary;
 import org.apache.qpid.proton.amqp.messaging.Accepted;
 import org.apache.qpid.proton.amqp.transaction.TransactionalState;
@@ -67,9 +66,9 @@ public class AmqpTransactionContext implements AmqpResourceParent {
         this.session = session;
     }
 
-    public void begin(final JmsTransactionId txId, final AsyncResult request) throws Exception {
+    public void begin(final JmsTransactionId txId, final AsyncResult request) throws ProviderException {
         if (current != null) {
-            throw new IOException("Begin called while a TX is still Active.");
+            throw new ProviderIllegalStateException("Begin called while a TX is still Active.");
         }
 
         final AsyncResult declareCompletion = new AsyncResult() {
@@ -86,7 +85,7 @@ public class AmqpTransactionContext implements AmqpResourceParent {
             }
 
             @Override
-            public void onFailure(Throwable result) {
+            public void onFailure(ProviderException result) {
                 current = null;
                 cachedAcceptedState = null;
                 cachedTransactedState = null;
@@ -108,13 +107,13 @@ public class AmqpTransactionContext implements AmqpResourceParent {
                 public void onSuccess() {
                     try {
                         coordinator.declare(txId, declareCompletion);
-                    } catch (Exception e) {
+                    } catch (ProviderException e) {
                         request.onFailure(e);
                     }
                 }
 
                 @Override
-                public void onFailure(Throwable result) {
+                public void onFailure(ProviderException result) {
                     request.onFailure(result);
                 }
 
@@ -128,14 +127,14 @@ public class AmqpTransactionContext implements AmqpResourceParent {
         }
     }
 
-    public void commit(final JmsTransactionInfo transactionInfo, JmsTransactionInfo nextTransactionInfo, final AsyncResult request) throws Exception {
+    public void commit(final JmsTransactionInfo transactionInfo, JmsTransactionInfo nextTransactionInfo, final AsyncResult request) throws ProviderException {
         if (!transactionInfo.getId().equals(current)) {
             if (!transactionInfo.isInDoubt() && current == null) {
-                throw new IllegalStateException("Commit called with no active Transaction.");
+                throw new ProviderIllegalStateException("Commit called with no active Transaction.");
             } else if (!transactionInfo.isInDoubt() && current != null) {
-                throw new IllegalStateException("Attempt to Commit a transaction other than the current one");
+                throw new ProviderIllegalStateException("Attempt to Commit a transaction other than the current one");
             } else {
-                throw new TransactionRolledBackException("Transaction in doubt and cannot be committed.");
+                throw new ProviderTransactionRolledBackException("Transaction in doubt and cannot be committed.");
             }
         }
 
@@ -159,12 +158,12 @@ public class AmqpTransactionContext implements AmqpResourceParent {
         }
     }
 
-    public void rollback(JmsTransactionInfo transactionInfo, JmsTransactionInfo nextTransactionInfo, final AsyncResult request) throws Exception {
+    public void rollback(JmsTransactionInfo transactionInfo, JmsTransactionInfo nextTransactionInfo, final AsyncResult request) throws ProviderException {
         if (!transactionInfo.getId().equals(current)) {
             if (!transactionInfo.isInDoubt() && current == null) {
-                throw new IllegalStateException("Rollback called with no active Transaction.");
+                throw new ProviderIllegalStateException("Rollback called with no active Transaction.");
             } else if (!transactionInfo.isInDoubt() && current != null) {
-                throw new IllegalStateException("Attempt to rollback a transaction other than the current one");
+                throw new ProviderIllegalStateException("Attempt to rollback a transaction other than the current one");
             } else {
                 request.onSuccess();
                 return;
@@ -300,14 +299,14 @@ public class AmqpTransactionContext implements AmqpResourceParent {
     private abstract class Completion implements AsyncResult {
 
         protected boolean complete;
-        protected Throwable failure;
+        protected ProviderException failure;
 
         @Override
         public boolean isComplete() {
             return complete;
         }
 
-        public Throwable getFailureCause() {
+        public ProviderException getFailureCause() {
             return failure;
         }
     }
@@ -321,7 +320,7 @@ public class AmqpTransactionContext implements AmqpResourceParent {
         }
 
         @Override
-        public void onFailure(Throwable result) {
+        public void onFailure(ProviderException result) {
             complete = true;
             failure = result;
             parent.onDeclareFailure(result);
@@ -365,7 +364,7 @@ public class AmqpTransactionContext implements AmqpResourceParent {
         }
 
         @Override
-        public void onFailure(Throwable result) {
+        public void onFailure(ProviderException result) {
             complete = true;
             failure = result;
             onDischargeFailure(result);
@@ -405,7 +404,7 @@ public class AmqpTransactionContext implements AmqpResourceParent {
             }
         }
 
-        public void onDeclareFailure(Throwable failure) {
+        public void onDeclareFailure(ProviderException failure) {
             // If the discharge has not completed yet we wait until it does
             // so we end up with the correct result.
             if (isComplete()) {
@@ -417,7 +416,7 @@ public class AmqpTransactionContext implements AmqpResourceParent {
             }
         }
 
-        public void onDischargeFailure(Throwable failure) {
+        public void onDischargeFailure(ProviderException failure) {
             cleanup();
 
             // If the declare already returned a result we can proceed otherwise
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpTransactionCoordinator.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpTransactionCoordinator.java
index caf6921..0e8c6e4 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpTransactionCoordinator.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpTransactionCoordinator.java
@@ -16,21 +16,20 @@
  */
 package org.apache.qpid.jms.provider.amqp;
 
-import java.io.IOException;
 import java.nio.BufferOverflowException;
 import java.util.concurrent.ScheduledFuture;
 
-import javax.jms.IllegalStateException;
-import javax.jms.JMSException;
-import javax.jms.TransactionRolledBackException;
-
-import org.apache.qpid.jms.JmsOperationTimedOutException;
 import org.apache.qpid.jms.meta.JmsConnectionInfo;
 import org.apache.qpid.jms.meta.JmsSessionInfo;
 import org.apache.qpid.jms.meta.JmsTransactionId;
 import org.apache.qpid.jms.provider.AsyncResult;
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.jms.provider.amqp.AmqpTransactionContext.DischargeCompletion;
-import org.apache.qpid.jms.util.IOExceptionSupport;
+import org.apache.qpid.jms.provider.exceptions.ProviderExceptionSupport;
+import org.apache.qpid.jms.provider.exceptions.ProviderIllegalStateException;
+import org.apache.qpid.jms.provider.exceptions.ProviderOperationTimedOutException;
+import org.apache.qpid.jms.provider.exceptions.ProviderTransactionInDoubtException;
+import org.apache.qpid.jms.provider.exceptions.ProviderTransactionRolledBackException;
 import org.apache.qpid.proton.amqp.Binary;
 import org.apache.qpid.proton.amqp.messaging.AmqpValue;
 import org.apache.qpid.proton.amqp.messaging.Rejected;
@@ -64,7 +63,7 @@ public class AmqpTransactionCoordinator extends AmqpAbstractResource<JmsSessionI
     }
 
     @Override
-    public void processDeliveryUpdates(AmqpProvider provider, Delivery delivery) throws IOException {
+    public void processDeliveryUpdates(AmqpProvider provider, Delivery delivery) throws ProviderException {
 
         try {
             if (delivery != null && delivery.remotelySettled()) {
@@ -87,17 +86,15 @@ public class AmqpTransactionCoordinator extends AmqpAbstractResource<JmsSessionI
                 } else if (state instanceof Rejected) {
                     LOG.debug("Last TX request failed: {}", txId);
                     Rejected rejected = (Rejected) state;
-                    Exception cause = AmqpSupport.convertToException(
-                        getParent().getProvider(), getEndpoint(), rejected.getError());
-                    JMSException failureCause = null;
-                    if (COMMIT_MARKER.equals(txId.getProviderContext())){
-                        failureCause = new TransactionRolledBackException(cause.getMessage());
+                    ProviderException cause = AmqpSupport.convertToNonFatalException(getParent().getProvider(), getEndpoint(), rejected.getError());
+                    if (COMMIT_MARKER.equals(txId.getProviderContext()) && !(cause instanceof ProviderTransactionRolledBackException)){
+                        cause = new ProviderTransactionRolledBackException(cause.getMessage(), cause);
                     } else {
-                        failureCause = new JMSException(cause.getMessage());
+                        cause = new ProviderTransactionInDoubtException(cause.getMessage(), cause);
                     }
 
                     txId.setProviderHint(null);
-                    pendingRequest.onFailure(failureCause);
+                    pendingRequest.onFailure(cause);
                 } else {
                     LOG.debug("Last TX request succeeded: {}", txId);
                     pendingRequest.onSuccess();
@@ -113,20 +110,20 @@ public class AmqpTransactionCoordinator extends AmqpAbstractResource<JmsSessionI
             }
 
             super.processDeliveryUpdates(provider, delivery);
-        } catch (Exception e) {
-            throw IOExceptionSupport.create(e);
+        } catch (Throwable e) {
+            throw ProviderExceptionSupport.createNonFatalOrPassthrough(e);
         }
     }
 
-    public void declare(JmsTransactionId txId, AsyncResult request) throws Exception {
+    public void declare(JmsTransactionId txId, AsyncResult request) throws ProviderException {
 
         if (isClosed()) {
-            request.onFailure(new JMSException("Cannot start new transaction: Coordinator remotely closed"));
+            request.onFailure(new ProviderIllegalStateException("Cannot start new transaction: Coordinator remotely closed"));
             return;
         }
 
         if (txId.getProviderHint() != null) {
-            throw new IllegalStateException("Declar called while a TX is still Active.");
+            throw new ProviderIllegalStateException("Declar called while a TX is still Active.");
         }
 
         Message message = Message.Factory.create();
@@ -142,15 +139,15 @@ public class AmqpTransactionCoordinator extends AmqpAbstractResource<JmsSessionI
         sendTxCommand(message);
     }
 
-    public void discharge(JmsTransactionId txId, DischargeCompletion request) throws Exception {
+    public void discharge(JmsTransactionId txId, DischargeCompletion request) throws ProviderException {
 
         if (isClosed()) {
-            Exception failureCause = null;
+            ProviderException failureCause = null;
 
             if (request.isCommit()) {
-                failureCause = new TransactionRolledBackException("Transaction inbout: Coordinator remotely closed");
+                failureCause = new ProviderTransactionRolledBackException("Transaction inbout: Coordinator remotely closed");
             } else {
-                failureCause = new JMSException("Rollback cannot complete: Coordinator remotely closed");
+                failureCause = new ProviderIllegalStateException("Rollback cannot complete: Coordinator remotely closed");
             }
 
             request.onFailure(failureCause);
@@ -158,7 +155,7 @@ public class AmqpTransactionCoordinator extends AmqpAbstractResource<JmsSessionI
         }
 
         if (txId.getProviderHint() == null) {
-            throw new IllegalStateException("Discharge called with no active Transaction.");
+            throw new ProviderIllegalStateException("Discharge called with no active Transaction.");
         }
 
         // Store the context of this action in the transaction ID for later completion.
@@ -182,7 +179,7 @@ public class AmqpTransactionCoordinator extends AmqpAbstractResource<JmsSessionI
     //----- Base class overrides ---------------------------------------------//
 
     @Override
-    public void closeResource(AmqpProvider provider, Throwable cause, boolean localClose) {
+    public void closeResource(AmqpProvider provider, ProviderException cause, boolean localClose) {
 
         // Alert any pending operation that the link failed to complete the pending
         // begin / commit / rollback operation.
@@ -244,13 +241,13 @@ public class AmqpTransactionCoordinator extends AmqpAbstractResource<JmsSessionI
     private ScheduledFuture<?> scheduleTimeoutIfNeeded(String cause, AsyncResult pendingRequest) {
         AmqpProvider provider = getParent().getProvider();
         if (provider.getRequestTimeout() != JmsConnectionInfo.INFINITE) {
-            return provider.scheduleRequestTimeout(pendingRequest, provider.getRequestTimeout(), new JmsOperationTimedOutException(cause));
+            return provider.scheduleRequestTimeout(pendingRequest, provider.getRequestTimeout(), new ProviderOperationTimedOutException(cause));
         } else {
             return null;
         }
     }
 
-    private void sendTxCommand(Message message) throws IOException {
+    private void sendTxCommand(Message message) throws ProviderException {
         int encodedSize = 0;
         byte[] buffer = OUTBOUND_BUFFER;
         while (true) {
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/builders/AmqpClosedConnectionBuilder.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/builders/AmqpClosedConnectionBuilder.java
index ba940df..6373558 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/builders/AmqpClosedConnectionBuilder.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/builders/AmqpClosedConnectionBuilder.java
@@ -54,5 +54,4 @@ public class AmqpClosedConnectionBuilder extends AmqpConnectionBuilder {
     protected boolean isClosePending() {
         return true;
     }
-
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/builders/AmqpConnectionBuilder.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/builders/AmqpConnectionBuilder.java
index d599b82..fb8d625 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/builders/AmqpConnectionBuilder.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/builders/AmqpConnectionBuilder.java
@@ -32,10 +32,12 @@ import javax.jms.Session;
 import org.apache.qpid.jms.meta.JmsConnectionInfo;
 import org.apache.qpid.jms.meta.JmsSessionInfo;
 import org.apache.qpid.jms.provider.AsyncResult;
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.jms.provider.amqp.AmqpConnection;
 import org.apache.qpid.jms.provider.amqp.AmqpProvider;
 import org.apache.qpid.jms.provider.amqp.AmqpRedirect;
 import org.apache.qpid.jms.provider.amqp.AmqpSupport;
+import org.apache.qpid.jms.provider.exceptions.ProviderConnectionRemotelyClosedException;
 import org.apache.qpid.jms.util.MetaDataSupport;
 import org.apache.qpid.proton.amqp.Symbol;
 import org.apache.qpid.proton.engine.Connection;
@@ -83,7 +85,7 @@ public class AmqpConnectionBuilder extends AmqpResourceBuilder<AmqpConnection, A
                     }
 
                     @Override
-                    public void onFailure(Throwable result) {
+                    public void onFailure(ProviderException result) {
                         LOG.debug("AMQP Connection Session failed to open.");
                         request.onFailure(result);
                     }
@@ -91,7 +93,7 @@ public class AmqpConnectionBuilder extends AmqpResourceBuilder<AmqpConnection, A
             }
 
             @Override
-            public void onFailure(Throwable result) {
+            public void onFailure(ProviderException result) {
                 request.onFailure(result);
             }
 
@@ -152,6 +154,16 @@ public class AmqpConnectionBuilder extends AmqpResourceBuilder<AmqpConnection, A
     }
 
     @Override
+    protected ProviderException getOpenAbortExceptionFromRemote() {
+        return AmqpSupport.convertToConnectionClosedException(parent.getProvider(), getEndpoint(), getEndpoint().getRemoteCondition());
+    }
+
+    @Override
+    protected ProviderException getDefaultOpenAbortException() {
+        return new ProviderConnectionRemotelyClosedException("Open failed unexpectedly.");
+    }
+
+    @Override
     protected boolean isClosePending() {
         return getResource().getProperties().isConnectionOpenFailed();
     }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/builders/AmqpConsumerBuilder.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/builders/AmqpConsumerBuilder.java
index ae45aca..9aec2e2 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/builders/AmqpConsumerBuilder.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/builders/AmqpConsumerBuilder.java
@@ -28,11 +28,11 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
-import javax.jms.InvalidDestinationException;
 import javax.jms.JMSRuntimeException;
 
 import org.apache.qpid.jms.JmsDestination;
 import org.apache.qpid.jms.meta.JmsConsumerInfo;
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.jms.provider.amqp.AmqpConnection;
 import org.apache.qpid.jms.provider.amqp.AmqpConsumer;
 import org.apache.qpid.jms.provider.amqp.AmqpProvider;
@@ -42,6 +42,8 @@ import org.apache.qpid.jms.provider.amqp.AmqpSupport;
 import org.apache.qpid.jms.provider.amqp.filters.AmqpJmsNoLocalType;
 import org.apache.qpid.jms.provider.amqp.filters.AmqpJmsSelectorType;
 import org.apache.qpid.jms.provider.amqp.message.AmqpDestinationHelper;
+import org.apache.qpid.jms.provider.exceptions.ProviderInvalidDestinationException;
+import org.apache.qpid.jms.provider.exceptions.ProviderUnsupportedOperationException;
 import org.apache.qpid.proton.amqp.DescribedType;
 import org.apache.qpid.proton.amqp.Symbol;
 import org.apache.qpid.proton.amqp.messaging.Accepted;
@@ -110,7 +112,7 @@ public class AmqpConsumerBuilder extends AmqpResourceBuilder<AmqpConsumer, AmqpS
             receiverLinkName = subTracker.reserveNextSubscriptionLinkName(subscriptionName, resourceInfo);
         }
 
-        if(receiverLinkName == null) {
+        if (receiverLinkName == null) {
             receiverLinkName = "qpid-jms:receiver:" + resourceInfo.getId() + ":" + address;
         }
 
@@ -133,21 +135,21 @@ public class AmqpConsumerBuilder extends AmqpResourceBuilder<AmqpConsumer, AmqpS
 
     @Override
     protected void afterOpened() {
-        if(validateSharedSubsLinkCapability) {
+        if (validateSharedSubsLinkCapability) {
             Symbol[] remoteOfferedCapabilities = endpoint.getRemoteOfferedCapabilities();
 
             boolean supported = false;
-            if(remoteOfferedCapabilities != null) {
+            if (remoteOfferedCapabilities != null) {
                 List<Symbol> list = Arrays.asList(remoteOfferedCapabilities);
                 if (list.contains(SHARED_SUBS)) {
                     supported = true;
                 }
             }
 
-            if(!supported) {
+            if (!supported) {
                 sharedSubsNotSupported = true;
 
-                if(resourceInfo.isDurable()) {
+                if (resourceInfo.isDurable()) {
                     endpoint.detach();
                 } else {
                     endpoint.close();
@@ -176,18 +178,18 @@ public class AmqpConsumerBuilder extends AmqpResourceBuilder<AmqpConsumer, AmqpS
     }
 
     @Override
-    protected Exception getOpenAbortException() {
-        if(sharedSubsNotSupported) {
-            return new JMSRuntimeException("Remote peer does not support shared subscriptions");
+    protected ProviderException getDefaultOpenAbortException() {
+        if (sharedSubsNotSupported) {
+            return new ProviderUnsupportedOperationException("Remote peer does not support shared subscriptions");
         }
 
         // Verify the attach response contained a non-null Source
         org.apache.qpid.proton.amqp.transport.Source source = endpoint.getRemoteSource();
         if (source != null) {
-            return super.getOpenAbortException();
+            return super.getDefaultOpenAbortException();
         } else {
             // No link terminus was created, the peer has detach/closed us, create IDE.
-            return new InvalidDestinationException("Link creation was refused");
+            return new ProviderInvalidDestinationException("Link creation was refused");
         }
     }
 
@@ -222,11 +224,11 @@ public class AmqpConsumerBuilder extends AmqpResourceBuilder<AmqpConsumer, AmqpS
         LinkedList<Symbol> capabilities = new LinkedList<>();
 
         Symbol typeCapability =  AmqpDestinationHelper.toTypeCapability(resourceInfo.getDestination());
-        if(typeCapability != null){
+        if (typeCapability != null){
             capabilities.add(typeCapability);
         }
 
-        if(resourceInfo.isShared()) {
+        if (resourceInfo.isShared()) {
             capabilities.add(AmqpSupport.SHARED);
 
             if(!resourceInfo.isExplicitClientID()) {
@@ -234,7 +236,7 @@ public class AmqpConsumerBuilder extends AmqpResourceBuilder<AmqpConsumer, AmqpS
             }
         }
 
-        if(!capabilities.isEmpty()) {
+        if (!capabilities.isEmpty()) {
             Symbol[] capArray = capabilities.toArray(new Symbol[capabilities.size()]);
             source.setCapabilities(capArray);
         }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/builders/AmqpProducerBuilder.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/builders/AmqpProducerBuilder.java
index f66c20b..731d70d 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/builders/AmqpProducerBuilder.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/builders/AmqpProducerBuilder.java
@@ -21,11 +21,10 @@ import static org.apache.qpid.jms.provider.amqp.AmqpSupport.DELAYED_DELIVERY;
 import java.util.Arrays;
 import java.util.List;
 
-import javax.jms.InvalidDestinationException;
-
 import org.apache.qpid.jms.JmsDestination;
 import org.apache.qpid.jms.meta.JmsProducerInfo;
 import org.apache.qpid.jms.provider.AsyncResult;
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.jms.provider.amqp.AmqpAnonymousFallbackProducer;
 import org.apache.qpid.jms.provider.amqp.AmqpConnection;
 import org.apache.qpid.jms.provider.amqp.AmqpFixedProducer;
@@ -33,6 +32,7 @@ import org.apache.qpid.jms.provider.amqp.AmqpProducer;
 import org.apache.qpid.jms.provider.amqp.AmqpSession;
 import org.apache.qpid.jms.provider.amqp.AmqpSupport;
 import org.apache.qpid.jms.provider.amqp.message.AmqpDestinationHelper;
+import org.apache.qpid.jms.provider.exceptions.ProviderInvalidDestinationException;
 import org.apache.qpid.proton.amqp.Symbol;
 import org.apache.qpid.proton.amqp.messaging.Accepted;
 import org.apache.qpid.proton.amqp.messaging.Modified;
@@ -141,14 +141,14 @@ public class AmqpProducerBuilder extends AmqpResourceBuilder<AmqpProducer, AmqpS
     }
 
     @Override
-    protected Exception getOpenAbortException() {
+    protected ProviderException getDefaultOpenAbortException() {
         // Verify the attach response contained a non-null target
         org.apache.qpid.proton.amqp.transport.Target target = getEndpoint().getRemoteTarget();
         if (target != null) {
-            return super.getOpenAbortException();
+            return super.getDefaultOpenAbortException();
         } else {
             // No link terminus was created, the peer has detach/closed us, create IDE.
-            return new InvalidDestinationException("Link creation was refused");
+            return new ProviderInvalidDestinationException("Link creation was refused");
         }
     }
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/builders/AmqpResourceBuilder.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/builders/AmqpResourceBuilder.java
index 9bca051..408bda7 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/builders/AmqpResourceBuilder.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/builders/AmqpResourceBuilder.java
@@ -16,20 +16,20 @@
  */
 package org.apache.qpid.jms.provider.amqp.builders;
 
-import java.io.IOException;
 import java.util.concurrent.ScheduledFuture;
 
-import org.apache.qpid.jms.JmsOperationTimedOutException;
 import org.apache.qpid.jms.meta.JmsConnectionInfo;
 import org.apache.qpid.jms.meta.JmsResource;
 import org.apache.qpid.jms.meta.JmsResource.ResourceState;
 import org.apache.qpid.jms.provider.AsyncResult;
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.jms.provider.amqp.AmqpEventSink;
 import org.apache.qpid.jms.provider.amqp.AmqpExceptionBuilder;
 import org.apache.qpid.jms.provider.amqp.AmqpProvider;
 import org.apache.qpid.jms.provider.amqp.AmqpResource;
 import org.apache.qpid.jms.provider.amqp.AmqpResourceParent;
 import org.apache.qpid.jms.provider.amqp.AmqpSupport;
+import org.apache.qpid.jms.provider.exceptions.ProviderOperationTimedOutException;
 import org.apache.qpid.proton.engine.Delivery;
 import org.apache.qpid.proton.engine.Endpoint;
 import org.apache.qpid.proton.engine.EndpointState;
@@ -94,7 +94,7 @@ public abstract class AmqpResourceBuilder<TARGET extends AmqpResource, PARENT ex
                 }
 
                 @Override
-                public void onFailure(Throwable result) {
+                public void onFailure(ProviderException result) {
                     handleClosed(provider, result);
                 }
 
@@ -120,27 +120,27 @@ public abstract class AmqpResourceBuilder<TARGET extends AmqpResource, PARENT ex
     //----- Event handlers ---------------------------------------------------//
 
     @Override
-    public void processRemoteOpen(AmqpProvider provider) throws IOException {
+    public void processRemoteOpen(AmqpProvider provider) throws ProviderException {
         handleOpened(provider);
     }
 
     @Override
-    public void processRemoteClose(AmqpProvider provider) throws IOException {
+    public void processRemoteClose(AmqpProvider provider) throws ProviderException {
         handleClosed(provider, null);
     }
 
     @Override
-    public void processRemoteDetach(AmqpProvider provider) throws IOException {
+    public void processRemoteDetach(AmqpProvider provider) throws ProviderException {
         // No implementation needed here for this event.
     }
 
     @Override
-    public void processDeliveryUpdates(AmqpProvider provider, Delivery delivery) throws IOException {
+    public void processDeliveryUpdates(AmqpProvider provider, Delivery delivery) throws ProviderException {
         // No implementation needed here for this event.
     }
 
     @Override
-    public void processFlowUpdates(AmqpProvider provider) throws IOException {
+    public void processFlowUpdates(AmqpProvider provider) throws ProviderException {
         // No implementation needed here for this event.
     }
 
@@ -167,11 +167,11 @@ public abstract class AmqpResourceBuilder<TARGET extends AmqpResource, PARENT ex
         } else {
             // TODO: Perhaps the validate method should thrown an exception so that we
             // can return a specific error message to the create initiator.
-            handleClosed(provider, new IOException("Failed to open requested endpoint"));
+            handleClosed(provider, new ProviderException("Failed to open requested endpoint"));
         }
     }
 
-    protected final void handleClosed(AmqpProvider provider, Throwable cause) {
+    protected final void handleClosed(AmqpProvider provider, ProviderException cause) {
         // If the resource being built is closed during the creation process
         // then this is always an error.
 
@@ -180,13 +180,13 @@ public abstract class AmqpResourceBuilder<TARGET extends AmqpResource, PARENT ex
         // Perform any post processing relating to closure during creation attempt
         afterClosed(getResource(), getResourceInfo());
 
-        Throwable openError;
+        ProviderException openError;
         if (hasRemoteError()) {
-            openError = AmqpSupport.convertToException(parent.getProvider(), getEndpoint(), getEndpoint().getRemoteCondition());
+            openError = getOpenAbortExceptionFromRemote();
         } else if (cause != null) {
             openError = cause;
         } else {
-            openError = getOpenAbortException();
+            openError = getDefaultOpenAbortException();
         }
 
         if (requestTimeoutTask != null) {
@@ -204,8 +204,8 @@ public abstract class AmqpResourceBuilder<TARGET extends AmqpResource, PARENT ex
     }
 
     @Override
-    public Exception createException() {
-        return new JmsOperationTimedOutException("Request to open resource " + getResource() + " timed out");
+    public ProviderException createException() {
+        return new ProviderOperationTimedOutException("Request to open resource " + getResource() + " timed out");
     }
 
     //----- Implementation methods used to customize the build process -------//
@@ -284,8 +284,19 @@ public abstract class AmqpResourceBuilder<TARGET extends AmqpResource, PARENT ex
      *
      * @return an Exception to describes the open failure for this resource.
      */
-    protected Exception getOpenAbortException() {
-        return new IOException("Open failed unexpectedly.");
+    protected ProviderException getDefaultOpenAbortException() {
+        return new ProviderException("Open failed unexpectedly.");
+    }
+
+    /**
+     * When aborting the open operation, this method will attempt to create an
+     * appropriate exception from the remote error condition if one is set and will
+     * revert to creating the default variant if not.
+     *
+     * @return an Exception to describes the open failure for this resource.
+     */
+    protected ProviderException getOpenAbortExceptionFromRemote() {
+        return AmqpSupport.convertToNonFatalException(parent.getProvider(), getEndpoint(), getEndpoint().getRemoteCondition());
     }
 
     /**
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderClosedException.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderClosedException.java
similarity index 76%
copy from qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderClosedException.java
copy to qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderClosedException.java
index cc7c140..88a5403 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderClosedException.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderClosedException.java
@@ -14,9 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.qpid.jms.provider;
+package org.apache.qpid.jms.provider.exceptions;
 
-public class ProviderClosedException extends ProviderException {
+import org.apache.qpid.jms.exceptions.JmsConnectionClosedException;
+
+public class ProviderClosedException extends ProviderIOException {
 
     private static final long serialVersionUID = 1L;
 
@@ -27,4 +29,9 @@ public class ProviderClosedException extends ProviderException {
     public ProviderClosedException(String message, Throwable cause) {
         super(message, cause);
     }
+
+    @Override
+    public JmsConnectionClosedException toJMSException() {
+        return new JmsConnectionClosedException(this);
+    }
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderRedirectedException.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderConnectionRedirectedException.java
similarity index 85%
rename from qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderRedirectedException.java
rename to qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderConnectionRedirectedException.java
index f39602d..1119893 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderRedirectedException.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderConnectionRedirectedException.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.qpid.jms.provider;
+package org.apache.qpid.jms.provider.exceptions;
 
 import java.io.IOException;
 import java.net.URI;
@@ -23,13 +23,13 @@ import java.net.URI;
  * {@link IOException} derivative that defines that the remote peer has requested that this
  * connection be redirected to some alternative peer.
  */
-public class ProviderRedirectedException extends ProviderException {
+public class ProviderConnectionRedirectedException extends ProviderConnectionRemotelyClosedException {
 
     private static final long serialVersionUID = 5872211116061710369L;
 
     private final URI redirect;
 
-    public ProviderRedirectedException(String reason, URI redirect) {
+    public ProviderConnectionRedirectedException(String reason, URI redirect) {
         super(reason);
 
         this.redirect = redirect;
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderConnectionRemotelyClosedException.java
similarity index 54%
copy from qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java
copy to qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderConnectionRemotelyClosedException.java
index e680df6..8ddff8b 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderConnectionRemotelyClosedException.java
@@ -14,19 +14,29 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.qpid.jms.provider;
+package org.apache.qpid.jms.provider.exceptions;
 
-import java.io.IOException;
+import javax.jms.JMSException;
 
-public class ProviderException extends IOException {
+import org.apache.qpid.jms.JmsConnectionRemotelyClosedException;
 
-    private static final long serialVersionUID = -5094579928657311571L;
+public class ProviderConnectionRemotelyClosedException extends ProviderIOException {
 
-    public ProviderException(String message) {
+    private static final long serialVersionUID = 5728349272688210550L;
+
+    public ProviderConnectionRemotelyClosedException(String message) {
         super(message);
     }
 
-    public ProviderException(String message, Throwable cause) {
+    public ProviderConnectionRemotelyClosedException(String message, Throwable cause) {
         super(message, cause);
     }
+
+    @Override
+    public JMSException toJMSException() {
+        JmsConnectionRemotelyClosedException jmsEx = new JmsConnectionRemotelyClosedException(getMessage());
+        jmsEx.initCause(this);
+        jmsEx.setLinkedException(this);
+        return jmsEx;
+    }
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderClosedException.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderConnectionResourceAllocationException.java
similarity index 59%
copy from qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderClosedException.java
copy to qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderConnectionResourceAllocationException.java
index cc7c140..514f120 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderClosedException.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderConnectionResourceAllocationException.java
@@ -14,17 +14,27 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.qpid.jms.provider;
+package org.apache.qpid.jms.provider.exceptions;
 
-public class ProviderClosedException extends ProviderException {
+import javax.jms.ResourceAllocationException;
+
+public class ProviderConnectionResourceAllocationException extends ProviderConnectionRemotelyClosedException {
 
     private static final long serialVersionUID = 1L;
 
-    public ProviderClosedException(String message) {
+    public ProviderConnectionResourceAllocationException(String message) {
         super(message);
     }
 
-    public ProviderClosedException(String message, Throwable cause) {
+    public ProviderConnectionResourceAllocationException(String message, Throwable cause) {
         super(message, cause);
     }
+
+    @Override
+    public ResourceAllocationException toJMSException() {
+        ResourceAllocationException jmsEx = new ResourceAllocationException(getMessage());
+        jmsEx.initCause(this);
+        jmsEx.setLinkedException(this);
+        return jmsEx;
+    }
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderConnectionResourceNotFoundException.java
similarity index 54%
copy from qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java
copy to qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderConnectionResourceNotFoundException.java
index e680df6..9116430 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderConnectionResourceNotFoundException.java
@@ -14,19 +14,27 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.qpid.jms.provider;
+package org.apache.qpid.jms.provider.exceptions;
 
-import java.io.IOException;
+import org.apache.qpid.jms.JmsResourceNotFoundException;
 
-public class ProviderException extends IOException {
+public class ProviderConnectionResourceNotFoundException extends ProviderConnectionRemotelyClosedException {
 
-    private static final long serialVersionUID = -5094579928657311571L;
+    private static final long serialVersionUID = -6757753762024560537L;
 
-    public ProviderException(String message) {
+    public ProviderConnectionResourceNotFoundException(String message) {
         super(message);
     }
 
-    public ProviderException(String message, Throwable cause) {
+    public ProviderConnectionResourceNotFoundException(String message, Throwable cause) {
         super(message, cause);
     }
+
+    @Override
+    public JmsResourceNotFoundException toJMSException() {
+        final JmsResourceNotFoundException jmsEx = new JmsResourceNotFoundException(getMessage());
+        jmsEx.initCause(this);
+        jmsEx.setLinkedException(this);
+        return jmsEx;
+    }
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderConnectionSecurityException.java
similarity index 53%
copy from qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java
copy to qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderConnectionSecurityException.java
index e680df6..891fbdf 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderConnectionSecurityException.java
@@ -14,19 +14,30 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.qpid.jms.provider;
+package org.apache.qpid.jms.provider.exceptions;
 
-import java.io.IOException;
+import javax.jms.JMSSecurityException;
 
-public class ProviderException extends IOException {
+/**
+ * Connection level Security Exception used to indicate a security violation has occurred.
+ */
+public class ProviderConnectionSecurityException extends ProviderConnectionRemotelyClosedException {
 
-    private static final long serialVersionUID = -5094579928657311571L;
+    private static final long serialVersionUID = -1895132556606592253L;
 
-    public ProviderException(String message) {
+    public ProviderConnectionSecurityException(String message) {
         super(message);
     }
 
-    public ProviderException(String message, Throwable cause) {
+    public ProviderConnectionSecurityException(String message, Throwable cause) {
         super(message, cause);
     }
+
+    @Override
+    public JMSSecurityException toJMSException() {
+        JMSSecurityException jmsEx = new JMSSecurityException(getMessage());
+        jmsEx.initCause(this);
+        jmsEx.setLinkedException(this);
+        return jmsEx;
+    }
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderConnectionSecuritySaslException.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderConnectionSecuritySaslException.java
new file mode 100644
index 0000000..ad5c2ce
--- /dev/null
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderConnectionSecuritySaslException.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.qpid.jms.provider.exceptions;
+
+import org.apache.qpid.jms.exceptions.JMSSecuritySaslException;
+
+/**
+ * Security Exception used to indicate a security violation has occurred.
+ */
+public class ProviderConnectionSecuritySaslException extends ProviderConnectionSecurityException {
+
+    private static final long serialVersionUID = 313318720407251822L;
+    private static final int SASL_SYS_TEMP = 4;
+
+    private int outcome = -1;
+
+    public ProviderConnectionSecuritySaslException(String message) {
+        this(message, -1, null);
+    }
+
+    public ProviderConnectionSecuritySaslException(String message, int outcome) {
+        this(message, outcome, null);
+    }
+
+    public ProviderConnectionSecuritySaslException(String message, int outcome, Throwable cause) {
+        super(message, cause);
+
+        this.outcome = outcome;
+    }
+
+    public boolean isSysTempFailure() {
+        return outcome == SASL_SYS_TEMP;
+    }
+
+    @Override
+    public JMSSecuritySaslException toJMSException() {
+        JMSSecuritySaslException jmsEx = new JMSSecuritySaslException(getMessage(), outcome);
+        jmsEx.initCause(this);
+        jmsEx.setLinkedException(this);
+        return jmsEx;
+    }
+}
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderDeliveryModifiedException.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderDeliveryModifiedException.java
new file mode 100644
index 0000000..80eac39
--- /dev/null
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderDeliveryModifiedException.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.qpid.jms.provider.exceptions;
+
+import org.apache.qpid.jms.provider.ProviderException;
+import org.apache.qpid.proton.amqp.messaging.Modified;
+
+/**
+ * Thrown when a send fails because the remote modified the delivery
+ */
+public class ProviderDeliveryModifiedException extends ProviderException {
+
+    private static final long serialVersionUID = 4099784529012859035L;
+
+    private final Modified modification;
+
+    public ProviderDeliveryModifiedException(String message, Modified modification) {
+        super(message);
+
+        this.modification = modification;
+    }
+
+    public ProviderDeliveryModifiedException(String message, Throwable cause, Modified modification) {
+        super(message, cause);
+
+        this.modification = modification;
+    }
+
+    public boolean isDeliveryFailed() {
+        return modification.getDeliveryFailed() == null ? false : modification.getDeliveryFailed().booleanValue();
+    }
+
+    public boolean isUndeliverableHere() {
+        return modification.getUndeliverableHere() == null ? false : modification.getUndeliverableHere().booleanValue();
+    }
+}
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderDeliveryReleasedException.java
similarity index 65%
copy from qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java
copy to qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderDeliveryReleasedException.java
index e680df6..5d1ab7f 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderDeliveryReleasedException.java
@@ -14,19 +14,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.qpid.jms.provider;
+package org.apache.qpid.jms.provider.exceptions;
 
-import java.io.IOException;
+import org.apache.qpid.jms.provider.ProviderException;
 
-public class ProviderException extends IOException {
+/**
+ * Thrown when a send fails because the remote released the delivery
+ */
+public class ProviderDeliveryReleasedException extends ProviderException {
 
-    private static final long serialVersionUID = -5094579928657311571L;
+    private static final long serialVersionUID = 4749969190587880823L;
 
-    public ProviderException(String message) {
+    public ProviderDeliveryReleasedException(String message) {
         super(message);
     }
 
-    public ProviderException(String message, Throwable cause) {
+    public ProviderDeliveryReleasedException(String message, Throwable cause) {
         super(message, cause);
     }
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderExceptionSupport.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderExceptionSupport.java
new file mode 100644
index 0000000..c48c547
--- /dev/null
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderExceptionSupport.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.qpid.jms.provider.exceptions;
+
+import org.apache.qpid.jms.provider.ProviderException;
+import org.apache.qpid.jms.sasl.SaslSecurityRuntimeException;
+
+public class ProviderExceptionSupport {
+
+    /**
+     * Checks the given cause to determine if it's already an ProviderIOException type and
+     * if not creates a new ProviderIOException to wrap it.
+     *
+     * @param cause
+     *        The initiating exception that should be cast or wrapped.
+     *
+     * @return an ProviderIOException instance.
+     */
+    public static ProviderIOException createOrPassthroughFatal(Throwable cause) {
+        if (cause instanceof ProviderIOException) {
+            return (ProviderIOException) cause;
+        }
+
+        String message = cause.getMessage();
+        if (message == null || message.length() == 0) {
+            message = cause.toString();
+        }
+
+        if (cause instanceof SaslSecurityRuntimeException) {
+            return new ProviderConnectionSecurityException(message, cause);
+        } else {
+            return new ProviderIOException(message, cause);
+        }
+    }
+
+    /**
+     * Checks the given cause to determine if it's already an ProviderException type and
+     * if not creates a new ProviderException to wrap it.  If the inbound exception is a
+     * fatal type then it will pass through this method untouched to preserve the fatal
+     * status of the error.
+     *
+     * @param cause
+     *        The initiating exception that should be cast or wrapped.
+     *
+     * @return an ProviderException instance.
+     */
+    public static ProviderException createNonFatalOrPassthrough(Throwable cause) {
+        if (cause instanceof ProviderException) {
+            return (ProviderException) cause;
+        }
+
+        String message = cause.getMessage();
+        if (message == null || message.length() == 0) {
+            message = cause.toString();
+        }
+
+        return new ProviderException(message, cause);
+    }
+}
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderFailedException.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderFailedException.java
similarity index 76%
copy from qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderFailedException.java
copy to qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderFailedException.java
index 78cfc02..0cae551 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderFailedException.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderFailedException.java
@@ -14,9 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.qpid.jms.provider;
+package org.apache.qpid.jms.provider.exceptions;
 
-public class ProviderFailedException extends ProviderException {
+import org.apache.qpid.jms.exceptions.JmsConnectionFailedException;
+
+public class ProviderFailedException extends ProviderIOException {
 
     private static final long serialVersionUID = 1L;
 
@@ -27,4 +29,9 @@ public class ProviderFailedException extends ProviderException {
     public ProviderFailedException(String message, Throwable cause) {
         super(message, cause);
     }
+
+    @Override
+    public JmsConnectionFailedException toJMSException() {
+        return new JmsConnectionFailedException(this);
+    }
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderIOException.java
similarity index 66%
copy from qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java
copy to qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderIOException.java
index e680df6..da58f2f 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderIOException.java
@@ -14,19 +14,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.qpid.jms.provider;
+package org.apache.qpid.jms.provider.exceptions;
 
-import java.io.IOException;
+import org.apache.qpid.jms.provider.ProviderException;
 
-public class ProviderException extends IOException {
+/**
+ * Exception type that is thrown when the provider has encountered an unrecoverable error.
+ */
+public class ProviderIOException extends ProviderException {
 
-    private static final long serialVersionUID = -5094579928657311571L;
+    private static final long serialVersionUID = 7022573614211991693L;
 
-    public ProviderException(String message) {
+    public ProviderIOException(String message) {
         super(message);
     }
 
-    public ProviderException(String message, Throwable cause) {
+    public ProviderIOException(String message, Throwable cause) {
         super(message, cause);
     }
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderIdleTimeoutException.java
similarity index 68%
copy from qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java
copy to qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderIdleTimeoutException.java
index e680df6..cc4c55b 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderIdleTimeoutException.java
@@ -14,19 +14,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.qpid.jms.provider;
+package org.apache.qpid.jms.provider.exceptions;
 
-import java.io.IOException;
-
-public class ProviderException extends IOException {
+/**
+ * Thrown when the Provider fails a connection due to idle timeout.
+ */
+public class ProviderIdleTimeoutException extends ProviderIOException {
 
-    private static final long serialVersionUID = -5094579928657311571L;
+    private static final long serialVersionUID = 7925210908123213499L;
 
-    public ProviderException(String message) {
+    public ProviderIdleTimeoutException(String message) {
         super(message);
     }
 
-    public ProviderException(String message, Throwable cause) {
+    public ProviderIdleTimeoutException(String message, Throwable cause) {
         super(message, cause);
     }
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderFailedException.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderIllegalStateException.java
similarity index 56%
copy from qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderFailedException.java
copy to qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderIllegalStateException.java
index 78cfc02..ec590f6 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderFailedException.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderIllegalStateException.java
@@ -14,17 +14,29 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.qpid.jms.provider;
+package org.apache.qpid.jms.provider.exceptions;
 
-public class ProviderFailedException extends ProviderException {
+import javax.jms.IllegalStateException;
 
-    private static final long serialVersionUID = 1L;
+import org.apache.qpid.jms.provider.ProviderException;
 
-    public ProviderFailedException(String message) {
+public class ProviderIllegalStateException extends ProviderException {
+
+    private static final long serialVersionUID = -2188225056209312580L;
+
+    public ProviderIllegalStateException(String message) {
         super(message);
     }
 
-    public ProviderFailedException(String message, Throwable cause) {
+    public ProviderIllegalStateException(String message, Throwable cause) {
         super(message, cause);
     }
+
+    @Override
+    public IllegalStateException toJMSException() {
+        final IllegalStateException jmsEx = new IllegalStateException(getMessage());
+        jmsEx.initCause(this);
+        jmsEx.setLinkedException(this);
+        return jmsEx;
+    }
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderInvalidClientIDException.java
similarity index 57%
copy from qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java
copy to qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderInvalidClientIDException.java
index e680df6..99fe9aa 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderInvalidClientIDException.java
@@ -14,19 +14,27 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.qpid.jms.provider;
+package org.apache.qpid.jms.provider.exceptions;
 
-import java.io.IOException;
+import javax.jms.InvalidClientIDException;
 
-public class ProviderException extends IOException {
+public class ProviderInvalidClientIDException extends ProviderConnectionRemotelyClosedException {
 
-    private static final long serialVersionUID = -5094579928657311571L;
+    private static final long serialVersionUID = 904517921855721540L;
 
-    public ProviderException(String message) {
+    public ProviderInvalidClientIDException(String message) {
         super(message);
     }
 
-    public ProviderException(String message, Throwable cause) {
+    public ProviderInvalidClientIDException(String message, Throwable cause) {
         super(message, cause);
     }
+
+    @Override
+    public InvalidClientIDException toJMSException() {
+        final InvalidClientIDException jmsEx = new InvalidClientIDException(getMessage());
+        jmsEx.initCause(this);
+        jmsEx.setLinkedException(this);
+        return jmsEx;
+    }
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderInvalidDestinationException.java
similarity index 55%
copy from qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java
copy to qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderInvalidDestinationException.java
index e680df6..b7d426a 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderInvalidDestinationException.java
@@ -14,19 +14,29 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.qpid.jms.provider;
+package org.apache.qpid.jms.provider.exceptions;
 
-import java.io.IOException;
+import javax.jms.InvalidDestinationException;
 
-public class ProviderException extends IOException {
+import org.apache.qpid.jms.provider.ProviderException;
 
-    private static final long serialVersionUID = -5094579928657311571L;
+public class ProviderInvalidDestinationException extends ProviderException {
 
-    public ProviderException(String message) {
+    private static final long serialVersionUID = 2356310049638567033L;
+
+    public ProviderInvalidDestinationException(String message) {
         super(message);
     }
 
-    public ProviderException(String message, Throwable cause) {
+    public ProviderInvalidDestinationException(String message, Throwable cause) {
         super(message, cause);
     }
+
+    @Override
+    public InvalidDestinationException toJMSException() {
+        final InvalidDestinationException jmsEx = new InvalidDestinationException(getMessage());
+        jmsEx.initCause(this);
+        jmsEx.setLinkedException(this);
+        return jmsEx;
+    }
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderFailedException.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderOperationTimedOutException.java
similarity index 51%
copy from qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderFailedException.java
copy to qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderOperationTimedOutException.java
index 78cfc02..8446ec3 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderFailedException.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderOperationTimedOutException.java
@@ -14,17 +14,31 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.qpid.jms.provider;
+package org.apache.qpid.jms.provider.exceptions;
 
-public class ProviderFailedException extends ProviderException {
+import org.apache.qpid.jms.JmsOperationTimedOutException;
+import org.apache.qpid.jms.provider.ProviderException;
 
-    private static final long serialVersionUID = 1L;
+/**
+ * Indicates that an operation in the provider timed out waiting for completion
+ */
+public class ProviderOperationTimedOutException extends ProviderException {
+
+    private static final long serialVersionUID = 4182665270566847828L;
 
-    public ProviderFailedException(String message) {
+    public ProviderOperationTimedOutException(String message) {
         super(message);
     }
 
-    public ProviderFailedException(String message, Throwable cause) {
+    public ProviderOperationTimedOutException(String message, Throwable cause) {
         super(message, cause);
     }
+
+    @Override
+    public JmsOperationTimedOutException toJMSException() {
+        JmsOperationTimedOutException jmsEx = new JmsOperationTimedOutException(getMessage());
+        jmsEx.initCause(this);
+        jmsEx.setLinkedException(this);
+        return jmsEx;
+    }
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderClosedException.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderResourceAllocationException.java
similarity index 60%
rename from qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderClosedException.java
rename to qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderResourceAllocationException.java
index cc7c140..65905e9 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderClosedException.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderResourceAllocationException.java
@@ -14,17 +14,27 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.qpid.jms.provider;
+package org.apache.qpid.jms.provider.exceptions;
 
-public class ProviderClosedException extends ProviderException {
+import javax.jms.ResourceAllocationException;
+
+public class ProviderResourceAllocationException extends ProviderResourceClosedException {
 
     private static final long serialVersionUID = 1L;
 
-    public ProviderClosedException(String message) {
+    public ProviderResourceAllocationException(String message) {
         super(message);
     }
 
-    public ProviderClosedException(String message, Throwable cause) {
+    public ProviderResourceAllocationException(String message, Throwable cause) {
         super(message, cause);
     }
+
+    @Override
+    public ResourceAllocationException toJMSException() {
+        ResourceAllocationException jmsEx = new ResourceAllocationException(getMessage());
+        jmsEx.initCause(this);
+        jmsEx.setLinkedException(this);
+        return jmsEx;
+    }
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderResourceClosedException.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderResourceClosedException.java
similarity index 91%
rename from qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderResourceClosedException.java
rename to qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderResourceClosedException.java
index 088c838..e37b335 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderResourceClosedException.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderResourceClosedException.java
@@ -14,7 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.qpid.jms.provider;
+package org.apache.qpid.jms.provider.exceptions;
+
+import org.apache.qpid.jms.provider.ProviderException;
 
 public class ProviderResourceClosedException extends ProviderException {
 
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderResourceNotFoundException.java
similarity index 54%
copy from qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java
copy to qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderResourceNotFoundException.java
index e680df6..91b37e8 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderResourceNotFoundException.java
@@ -14,19 +14,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.qpid.jms.provider;
+package org.apache.qpid.jms.provider.exceptions;
 
-import java.io.IOException;
+import org.apache.qpid.jms.JmsResourceNotFoundException;
+import org.apache.qpid.jms.provider.ProviderException;
 
-public class ProviderException extends IOException {
+public class ProviderResourceNotFoundException extends ProviderException {
 
-    private static final long serialVersionUID = -5094579928657311571L;
+    private static final long serialVersionUID = -6757753762024560537L;
 
-    public ProviderException(String message) {
+    public ProviderResourceNotFoundException(String message) {
         super(message);
     }
 
-    public ProviderException(String message, Throwable cause) {
+    public ProviderResourceNotFoundException(String message, Throwable cause) {
         super(message, cause);
     }
+
+    @Override
+	public JmsResourceNotFoundException toJMSException() {
+        final JmsResourceNotFoundException jmsEx = new JmsResourceNotFoundException(getMessage());
+        jmsEx.initCause(this);
+        jmsEx.setLinkedException(this);
+        return jmsEx;
+    }
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderFailedException.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderSecurityException.java
similarity index 51%
copy from qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderFailedException.java
copy to qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderSecurityException.java
index 78cfc02..20b3777 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderFailedException.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderSecurityException.java
@@ -14,17 +14,33 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.qpid.jms.provider;
+package org.apache.qpid.jms.provider.exceptions;
 
-public class ProviderFailedException extends ProviderException {
+import javax.jms.JMSSecurityException;
 
-    private static final long serialVersionUID = 1L;
+import org.apache.qpid.jms.provider.ProviderException;
 
-    public ProviderFailedException(String message) {
+/**
+ * Security Exception used to indicate a security violation has occurred that is non-fatal
+ * such as link creation blocked because user does not have access etc.
+ */
+public class ProviderSecurityException extends ProviderException {
+
+    private static final long serialVersionUID = -1895132556606592253L;
+
+    public ProviderSecurityException(String message) {
         super(message);
     }
 
-    public ProviderFailedException(String message, Throwable cause) {
+    public ProviderSecurityException(String message, Throwable cause) {
         super(message, cause);
     }
+
+    @Override
+    public JMSSecurityException toJMSException() {
+        JMSSecurityException jmsEx = new JMSSecurityException(getMessage());
+        jmsEx.initCause(this);
+        jmsEx.setLinkedException(this);
+        return jmsEx;
+    }
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderSendTimedOutException.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderSendTimedOutException.java
new file mode 100644
index 0000000..4aa6b99
--- /dev/null
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderSendTimedOutException.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.qpid.jms.provider.exceptions;
+
+import org.apache.qpid.jms.JmsSendTimedOutException;
+import org.apache.qpid.jms.message.JmsMessage;
+
+/**
+ * Thrown when a message send operation times out in the Provider layer.
+ */
+public class ProviderSendTimedOutException extends ProviderOperationTimedOutException {
+
+    private static final long serialVersionUID = 222325890763309867L;
+
+    private final JmsMessage unsentMessage;
+
+    public ProviderSendTimedOutException(String reason) {
+        this(reason, null);
+    }
+
+    public ProviderSendTimedOutException(String reason, JmsMessage unsentMessage) {
+        super(reason, null);
+        this.unsentMessage = unsentMessage;
+    }
+
+    public JmsMessage getUnsentMessage() {
+        return unsentMessage;
+    }
+
+    @Override
+    public JmsSendTimedOutException toJMSException() {
+        JmsSendTimedOutException jmsEx = new JmsSendTimedOutException(getMessage());
+        jmsEx.initCause(this);
+        jmsEx.setLinkedException(this);
+        return jmsEx;
+    }
+}
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderFailedException.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderTransactionInDoubtException.java
similarity index 51%
copy from qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderFailedException.java
copy to qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderTransactionInDoubtException.java
index 78cfc02..b83c191 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderFailedException.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderTransactionInDoubtException.java
@@ -14,17 +14,31 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.qpid.jms.provider;
+package org.apache.qpid.jms.provider.exceptions;
 
-public class ProviderFailedException extends ProviderException {
+import org.apache.qpid.jms.JmsTransactionInDoubtException;
+import org.apache.qpid.jms.provider.ProviderException;
 
-    private static final long serialVersionUID = 1L;
+/**
+ * Thrown when a transaction operation fails and state is now unknown.
+ */
+public class ProviderTransactionInDoubtException extends ProviderException {
+
+    private static final long serialVersionUID = -5532644122754198664L;
 
-    public ProviderFailedException(String message) {
+    public ProviderTransactionInDoubtException(String message) {
         super(message);
     }
 
-    public ProviderFailedException(String message, Throwable cause) {
+    public ProviderTransactionInDoubtException(String message, Throwable cause) {
         super(message, cause);
     }
+
+    @Override
+	public JmsTransactionInDoubtException toJMSException() {
+        final JmsTransactionInDoubtException jmsEx = new JmsTransactionInDoubtException(getMessage());
+        jmsEx.initCause(this);
+        jmsEx.setLinkedException(this);
+        return jmsEx;
+    }
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderFailedException.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderTransactionRolledBackException.java
similarity index 50%
rename from qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderFailedException.java
rename to qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderTransactionRolledBackException.java
index 78cfc02..73e7feb 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderFailedException.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderTransactionRolledBackException.java
@@ -14,17 +14,32 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.qpid.jms.provider;
+package org.apache.qpid.jms.provider.exceptions;
 
-public class ProviderFailedException extends ProviderException {
+import javax.jms.TransactionRolledBackException;
 
-    private static final long serialVersionUID = 1L;
+import org.apache.qpid.jms.provider.ProviderException;
 
-    public ProviderFailedException(String message) {
-        super(message);
+/**
+ * Thrown when a message send operation times out in the Provider layer.
+ */
+public class ProviderTransactionRolledBackException extends ProviderException {
+
+    private static final long serialVersionUID = 222325890763309867L;
+
+    public ProviderTransactionRolledBackException(String message) {
+        super(message, null);
     }
 
-    public ProviderFailedException(String message, Throwable cause) {
+    public ProviderTransactionRolledBackException(String message, Throwable cause) {
         super(message, cause);
     }
+
+    @Override
+    public TransactionRolledBackException toJMSException() {
+        TransactionRolledBackException jmsEx = new TransactionRolledBackException(getMessage());
+        jmsEx.initCause(this);
+        jmsEx.setLinkedException(this);
+        return jmsEx;
+    }
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderUnsupportedOperationException.java
similarity index 64%
copy from qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java
copy to qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderUnsupportedOperationException.java
index e680df6..650f200 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/exceptions/ProviderUnsupportedOperationException.java
@@ -14,19 +14,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.qpid.jms.provider;
+package org.apache.qpid.jms.provider.exceptions;
 
-import java.io.IOException;
+import org.apache.qpid.jms.provider.ProviderException;
 
-public class ProviderException extends IOException {
+/**
+ * Thrown when an action request is not supported through this provider.
+ */
+public class ProviderUnsupportedOperationException extends ProviderException {
 
-    private static final long serialVersionUID = -5094579928657311571L;
+    private static final long serialVersionUID = -680156277783719903L;
 
-    public ProviderException(String message) {
+    public ProviderUnsupportedOperationException(String message) {
         super(message);
     }
 
-    public ProviderException(String message, Throwable cause) {
+    public ProviderUnsupportedOperationException(String message, Throwable cause) {
         super(message, cause);
     }
 }
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/failover/FailoverProvider.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/failover/FailoverProvider.java
index dc35ba5..238e3fc 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/failover/FailoverProvider.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/failover/FailoverProvider.java
@@ -16,7 +16,6 @@
  */
 package org.apache.qpid.jms.provider.failover;
 
-import java.io.IOException;
 import java.net.URI;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -34,13 +33,6 @@ import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
-import javax.jms.JMSException;
-import javax.jms.JMSSecurityException;
-import javax.jms.TransactionRolledBackException;
-
-import org.apache.qpid.jms.JmsOperationTimedOutException;
-import org.apache.qpid.jms.JmsSendTimedOutException;
-import org.apache.qpid.jms.exceptions.JMSSecuritySaslException;
 import org.apache.qpid.jms.message.JmsInboundMessageDispatch;
 import org.apache.qpid.jms.message.JmsMessageFactory;
 import org.apache.qpid.jms.message.JmsOutboundMessageDispatch;
@@ -53,14 +45,23 @@ import org.apache.qpid.jms.provider.AsyncResult;
 import org.apache.qpid.jms.provider.DefaultProviderListener;
 import org.apache.qpid.jms.provider.Provider;
 import org.apache.qpid.jms.provider.ProviderConstants.ACK_TYPE;
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.jms.provider.ProviderFactory;
 import org.apache.qpid.jms.provider.ProviderFuture;
 import org.apache.qpid.jms.provider.ProviderFutureFactory;
 import org.apache.qpid.jms.provider.ProviderListener;
-import org.apache.qpid.jms.provider.ProviderRedirectedException;
 import org.apache.qpid.jms.provider.ProviderSynchronization;
 import org.apache.qpid.jms.provider.WrappedAsyncResult;
-import org.apache.qpid.jms.util.IOExceptionSupport;
+import org.apache.qpid.jms.provider.exceptions.ProviderClosedException;
+import org.apache.qpid.jms.provider.exceptions.ProviderConnectionRedirectedException;
+import org.apache.qpid.jms.provider.exceptions.ProviderConnectionSecurityException;
+import org.apache.qpid.jms.provider.exceptions.ProviderConnectionSecuritySaslException;
+import org.apache.qpid.jms.provider.exceptions.ProviderExceptionSupport;
+import org.apache.qpid.jms.provider.exceptions.ProviderFailedException;
+import org.apache.qpid.jms.provider.exceptions.ProviderIOException;
+import org.apache.qpid.jms.provider.exceptions.ProviderOperationTimedOutException;
+import org.apache.qpid.jms.provider.exceptions.ProviderSendTimedOutException;
+import org.apache.qpid.jms.provider.exceptions.ProviderTransactionRolledBackException;
 import org.apache.qpid.jms.util.QpidJMSThreadFactory;
 import org.apache.qpid.jms.util.ThreadPoolUtils;
 import org.slf4j.Logger;
@@ -113,7 +114,7 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
 
     // Current state of connection / reconnection
     private final ReconnectControls reconnectControl = new ReconnectControls();
-    private IOException failureCause;
+    private ProviderException failureCause;
     private volatile URI connectedURI;
     private volatile JmsConnectionInfo connectionInfo;
 
@@ -146,7 +147,7 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
     }
 
     @Override
-    public void connect(JmsConnectionInfo connectionInfo) throws IOException {
+    public void connect(JmsConnectionInfo connectionInfo) throws ProviderException {
         checkClosed();
         this.connectionInfo = connectionInfo;
         LOG.debug("Initiating initial connection attempt task");
@@ -154,7 +155,7 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
     }
 
     @Override
-    public void start() throws IOException, IllegalStateException {
+    public void start() throws ProviderException, IllegalStateException {
         checkClosed();
 
         if (listener == null) {
@@ -176,7 +177,7 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
                 // further work while we do a graceful shutdown of the executor service.
                 lock.readLock().lock();
                 try {
-                    IOException error = failureCause != null ? failureCause : new IOException("Connection closed");
+                    ProviderException error = failureCause != null ? failureCause : new ProviderClosedException("Connection closed");
                     final List<FailoverRequest> pending = new ArrayList<FailoverRequest>(requests.values());
                     for (FailoverRequest pendingRequest : pending) {
                         if (!pendingRequest.isComplete()) {
@@ -205,7 +206,7 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
                 } else {
                     request.sync(getCloseTimeout(), TimeUnit.MILLISECONDS);
                 }
-            } catch (IOException e) {
+            } catch (ProviderException e) {
                 LOG.warn("Error caught while closing Provider: {}", e.getMessage() != null ? e.getMessage() : "<Unknown Error>");
             } finally {
                 ThreadPoolUtils.shutdownGraceful(serializer);
@@ -214,13 +215,13 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
     }
 
     @Override
-    public void create(final JmsResource resource, AsyncResult request) throws IOException, JMSException, UnsupportedOperationException {
+    public void create(final JmsResource resource, AsyncResult request) throws ProviderException {
         checkClosed();
         final FailoverRequest pending;
         if (resource instanceof JmsConnectionInfo) {
             pending = new CreateConnectionRequest(request) {
                 @Override
-                public void doTask(Provider provider) throws Exception {
+                public void doTask(Provider provider) throws ProviderException {
                     JmsConnectionInfo connectionInfo = (JmsConnectionInfo) resource;
 
                     // Collect the timeouts we will handle in this provider.
@@ -239,7 +240,7 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
         } else {
             pending = new FailoverRequest(request, requestTimeout) {
                 @Override
-                public void doTask(Provider provider) throws Exception {
+                public void doTask(Provider provider) throws ProviderException {
                     provider.create(resource, this);
                 }
 
@@ -267,11 +268,11 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
     }
 
     @Override
-    public void start(final JmsResource resource, final AsyncResult request) throws IOException, JMSException {
+    public void start(final JmsResource resource, final AsyncResult request) throws ProviderException {
         checkClosed();
         final FailoverRequest pending = new FailoverRequest(request, requestTimeout) {
             @Override
-            public void doTask(Provider provider) throws Exception {
+            public void doTask(Provider provider) throws ProviderException {
                 provider.start(resource, this);
             }
 
@@ -285,11 +286,11 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
     }
 
     @Override
-    public void stop(final JmsResource resource, final AsyncResult request) throws IOException, JMSException {
+    public void stop(final JmsResource resource, final AsyncResult request) throws ProviderException {
         checkClosed();
         final FailoverRequest pending = new FailoverRequest(request, requestTimeout) {
             @Override
-            public void doTask(Provider provider) throws Exception {
+            public void doTask(Provider provider) throws ProviderException {
                 provider.stop(resource, this);
             }
 
@@ -303,11 +304,11 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
     }
 
     @Override
-    public void destroy(final JmsResource resourceId, AsyncResult request) throws IOException, JMSException, UnsupportedOperationException {
+    public void destroy(final JmsResource resourceId, AsyncResult request) throws ProviderException {
         checkClosed();
         final FailoverRequest pending = new FailoverRequest(request, requestTimeout) {
             @Override
-            public void doTask(Provider provider) throws IOException, JMSException, UnsupportedOperationException {
+            public void doTask(Provider provider) throws ProviderException {
                 if (resourceId instanceof JmsConnectionInfo) {
                    closingConnection.set(true);
                 }
@@ -330,11 +331,11 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
     }
 
     @Override
-    public void send(final JmsOutboundMessageDispatch envelope, AsyncResult request) throws IOException, JMSException {
+    public void send(final JmsOutboundMessageDispatch envelope, AsyncResult request) throws ProviderException {
         checkClosed();
         final FailoverRequest pending = new FailoverRequest(request, sendTimeout) {
             @Override
-            public void doTask(Provider provider) throws Exception {
+            public void doTask(Provider provider) throws ProviderException {
                 provider.send(envelope, this);
             }
 
@@ -344,8 +345,8 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
             }
 
             @Override
-            public JMSException createTimedOutException() {
-                return new JmsSendTimedOutException("Timed out waiting on " + this, envelope.getMessage());
+            public ProviderException createTimedOutException() {
+                return new ProviderSendTimedOutException("Timed out waiting on " + this, envelope.getMessage());
             }
         };
 
@@ -353,11 +354,11 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
     }
 
     @Override
-    public void acknowledge(final JmsSessionId sessionId, final ACK_TYPE ackType, AsyncResult request) throws IOException, JMSException {
+    public void acknowledge(final JmsSessionId sessionId, final ACK_TYPE ackType, AsyncResult request) throws ProviderException {
         checkClosed();
         final FailoverRequest pending = new FailoverRequest(request, requestTimeout) {
             @Override
-            public void doTask(Provider provider) throws Exception {
+            public void doTask(Provider provider) throws ProviderException {
                 provider.acknowledge(sessionId, ackType, this);
             }
 
@@ -377,11 +378,11 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
     }
 
     @Override
-    public void acknowledge(final JmsInboundMessageDispatch envelope, final ACK_TYPE ackType, AsyncResult request) throws IOException, JMSException {
+    public void acknowledge(final JmsInboundMessageDispatch envelope, final ACK_TYPE ackType, AsyncResult request) throws ProviderException {
         checkClosed();
         final FailoverRequest pending = new FailoverRequest(request, requestTimeout) {
             @Override
-            public void doTask(Provider provider) throws Exception {
+            public void doTask(Provider provider) throws ProviderException {
                 provider.acknowledge(envelope, ackType, this);
             }
 
@@ -401,11 +402,11 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
     }
 
     @Override
-    public void commit(final JmsTransactionInfo transactionInfo, JmsTransactionInfo nextTransactionInfo, AsyncResult request) throws IOException, JMSException, UnsupportedOperationException {
+    public void commit(final JmsTransactionInfo transactionInfo, JmsTransactionInfo nextTransactionInfo, AsyncResult request) throws ProviderException {
         checkClosed();
         final FailoverRequest pending = new FailoverRequest(request, requestTimeout) {
             @Override
-            public void doTask(Provider provider) throws Exception {
+            public void doTask(Provider provider) throws ProviderException {
                 provider.commit(transactionInfo, nextTransactionInfo, this);
             }
 
@@ -420,10 +421,8 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
             }
 
             @Override
-            protected Exception createOfflineFailureException(IOException error) {
-                Exception ex = new TransactionRolledBackException("Commit failed, connection offline: " + error.getMessage());
-                ex.initCause(error);
-                return ex;
+            protected ProviderException createOfflineFailureException(ProviderException error) {
+                return new ProviderTransactionRolledBackException("Commit failed, connection offline: " + error.getMessage(), error);
             }
         };
 
@@ -431,11 +430,11 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
     }
 
     @Override
-    public void rollback(final JmsTransactionInfo transactionInfo, JmsTransactionInfo nextTransactionInfo, AsyncResult request) throws IOException, JMSException, UnsupportedOperationException {
+    public void rollback(final JmsTransactionInfo transactionInfo, JmsTransactionInfo nextTransactionInfo, AsyncResult request) throws ProviderException {
         checkClosed();
         final FailoverRequest pending = new FailoverRequest(request, requestTimeout) {
             @Override
-            public void doTask(Provider provider) throws Exception {
+            public void doTask(Provider provider) throws ProviderException {
                 provider.rollback(transactionInfo, nextTransactionInfo, this);
             }
 
@@ -454,11 +453,11 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
     }
 
     @Override
-    public void recover(final JmsSessionId sessionId, final AsyncResult request) throws IOException, UnsupportedOperationException {
+    public void recover(final JmsSessionId sessionId, final AsyncResult request) throws ProviderException {
         checkClosed();
         final FailoverRequest pending = new FailoverRequest(request, requestTimeout) {
             @Override
-            public void doTask(Provider provider) throws Exception {
+            public void doTask(Provider provider) throws ProviderException {
                 provider.recover(sessionId, this);
             }
 
@@ -477,11 +476,11 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
     }
 
     @Override
-    public void unsubscribe(final String subscription, AsyncResult request) throws IOException, JMSException, UnsupportedOperationException {
+    public void unsubscribe(final String subscription, AsyncResult request) throws ProviderException {
         checkClosed();
         final FailoverRequest pending = new FailoverRequest(request, requestTimeout) {
             @Override
-            public void doTask(Provider provider) throws Exception {
+            public void doTask(Provider provider) throws ProviderException {
                 provider.unsubscribe(subscription, this);
             }
 
@@ -495,11 +494,11 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
     }
 
     @Override
-    public void pull(final JmsConsumerId consumerId, final long timeout, final AsyncResult request) throws IOException, UnsupportedOperationException {
+    public void pull(final JmsConsumerId consumerId, final long timeout, final AsyncResult request) throws ProviderException {
         checkClosed();
         final FailoverRequest pending = new FailoverRequest(request) {
             @Override
-            public void doTask(Provider provider) throws Exception {
+            public void doTask(Provider provider) throws ProviderException {
                 provider.pull(consumerId, timeout, this);
             }
 
@@ -531,7 +530,7 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
      * @param cause
      *        the error that triggered the failure of the provider.
      */
-    private void handleProviderFailure(final Provider provider, final IOException cause) {
+    private void handleProviderFailure(final Provider provider, final ProviderException cause) {
         if (closingConnection.get() || closed.get() || failed.get()) {
             return;
         }
@@ -560,8 +559,8 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
                     }
 
                     if (reconnectControl.isReconnectAllowed(cause)) {
-                        if (cause instanceof ProviderRedirectedException) {
-                            ProviderRedirectedException redirect = (ProviderRedirectedException) cause;
+                        if (cause instanceof ProviderConnectionRedirectedException) {
+                            ProviderConnectionRedirectedException redirect = (ProviderConnectionRedirectedException) cause;
                             try {
                                 uris.addFirst(redirect.getRedirectionURI());
                             } catch (Exception error) {
@@ -696,7 +695,7 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
                 }
             } catch (Throwable error) {
                 LOG.trace("Connection attempt:[{}] to: {} failed", reconnectControl.reconnectAttempts, provider.getRemoteURI());
-                handleProviderFailure(provider, IOExceptionSupport.create(error));
+                handleProviderFailure(provider, ProviderExceptionSupport.createOrPassthroughFatal(error));
             } finally {
                 lock.writeLock().unlock();
             }
@@ -725,7 +724,7 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
                     return;
                 }
 
-                Throwable failure = null;
+                ProviderException failure = null;
                 Provider provider = null;
 
                 long reconnectAttempts = reconnectControl.recordNextAttempt();
@@ -736,7 +735,8 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
                             URI target = uris.getNext();
                             if (target == null) {
                                 LOG.trace("Failover URI collection unexpectedly modified during connection attempt.");
-                                failure = new ConcurrentModificationException("Failover URIs changed unexpectedly");
+                                failure = ProviderExceptionSupport.createOrPassthroughFatal(
+                                    new ConcurrentModificationException("Failover URIs changed unexpectedly"));
                                 continue;
                             }
 
@@ -750,7 +750,7 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
                             } catch (Throwable e) {
                                 LOG.info("Connection attempt:[{}] to: {} failed", reconnectAttempts,
                                     target.getScheme() + "://" + target.getHost() + ":" + target.getPort());
-                                failure = e;
+                                failure = ProviderExceptionSupport.createOrPassthroughFatal(e);
                                 try {
                                     if (provider != null) {
                                         provider.close();
@@ -760,7 +760,7 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
                                     provider = null;
                                 }
 
-                                if (reconnectControl.isStoppageCause(e)) {
+                                if (reconnectControl.isStoppageCause(failure)) {
                                     LOG.trace("Stopping attempt due to type of failure");
                                     break;
                                 }
@@ -768,12 +768,12 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
                         }
                     } else {
                         LOG.debug("No remote URI available to connect to in failover list");
-                        failure = new IOException(
+                        failure = new ProviderFailedException(
                             "No remote URI available for reconnection during connection attempt: " + reconnectAttempts);
                     }
                 } catch (Throwable unknownFailure) {
                     LOG.warn("Connection attempt:[{}] failed abnormally.", reconnectAttempts);
-                    failure = failure == null ? unknownFailure : failure;
+                    failure = failure == null ? ProviderExceptionSupport.createOrPassthroughFatal(unknownFailure) : failure;
                 } finally {
                     if (provider == null) {
                         LOG.trace("Connection attempt:[{}] failed error: {}", reconnectControl.reconnectAttempts, failure.getMessage());
@@ -794,15 +794,15 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
      *
      * @param lastFailure the last failure encountered while trying to (re)connect.
      */
-    private void reportReconnectFailure(final Throwable lastFailure) {
+    private void reportReconnectFailure(final ProviderException lastFailure) {
         serializer.execute(() -> {
             LOG.error("Failed to connect after: " + reconnectControl.reconnectAttempts + " attempt(s)");
             if (failed.compareAndSet(false, true)) {
                 if (lastFailure == null) {
-                    failureCause = new IOException(
+                    failureCause = new ProviderIOException(
                         "Failed to connect after: " + reconnectControl.reconnectAttempts + " attempt(s)");
                 } else {
-                    failureCause = IOExceptionSupport.create(lastFailure);
+                    failureCause = ProviderExceptionSupport.createOrPassthroughFatal(lastFailure);
                 }
                 if (listener != null) {
                     listener.onConnectionFailure(failureCause);
@@ -811,9 +811,9 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
         });
     }
 
-    protected void checkClosed() throws IOException {
+    protected void checkClosed() throws ProviderException {
         if (closed.get()) {
-            throw new IOException("The Provider is already closed");
+            throw new ProviderClosedException("The Provider is already closed");
         }
     }
 
@@ -838,7 +838,7 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
     }
 
     @Override
-    public void onFailedMessageSend(final JmsOutboundMessageDispatch envelope, Throwable cause) {
+    public void onFailedMessageSend(final JmsOutboundMessageDispatch envelope, ProviderException cause) {
         if (closingConnection.get() || closed.get() || failed.get()) {
             return;
         }
@@ -847,7 +847,7 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
     }
 
     @Override
-    public void onConnectionFailure(final IOException ex) {
+    public void onConnectionFailure(final ProviderException ex) {
         if (closingConnection.get() || closed.get() || failed.get()) {
             return;
         }
@@ -857,7 +857,7 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
     }
 
     @Override
-    public void onResourceClosed(JmsResource resource, Throwable cause) {
+    public void onResourceClosed(JmsResource resource, ProviderException cause) {
         if (closingConnection.get() || closed.get() || failed.get()) {
             return;
         }
@@ -866,7 +866,7 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
     }
 
     @Override
-    public void onProviderException(final Exception ex) {
+    public void onProviderException(final ProviderException ex) {
         if (closingConnection.get() || closed.get() || failed.get()) {
             return;
         }
@@ -935,7 +935,6 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
         return null;
     }
 
-
     @Override
     public List<URI> getAlternateURIs() {
         Provider provider = this.provider;
@@ -1154,21 +1153,16 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
                 activeProvider = provider;
                 requests.put(id, this);
                 if (activeProvider == null) {
-                    whenOffline(new IOException("Connection failed."));
+                    whenOffline(new ProviderIOException("Connection failed."));
                 } else {
                     try {
                         LOG.debug("Executing Failover Task: {} ({})", this, id);
                         doTask(activeProvider);
-                    } catch (UnsupportedOperationException e) {
-                        requests.remove(id);
-                        getWrappedRequest().onFailure(e);
-                    } catch (JMSException jmsEx) {
-                        requests.remove(id);
-                        getWrappedRequest().onFailure(jmsEx);
                     } catch (Throwable e) {
                         LOG.debug("Caught exception while executing task: {} - {}", this, e.getMessage());
-                        whenOffline(IOExceptionSupport.create(e));
-                        handleProviderFailure(activeProvider, IOExceptionSupport.create(e));
+                        ProviderException providerEx = ProviderExceptionSupport.createNonFatalOrPassthrough(e);
+                        whenOffline(providerEx);
+                        handleProviderFailure(activeProvider, providerEx);
                     }
                 }
             } finally {
@@ -1177,15 +1171,15 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
         }
 
         @Override
-        public void onFailure(final Throwable error) {
+        public void onFailure(final ProviderException error) {
             lock.readLock().lock();
             try {
-                if (error instanceof JMSException || closingConnection.get() || closed.get() || failed.get()) {
+                if (!(error instanceof ProviderIOException) || closingConnection.get() || closed.get() || failed.get()) {
                     requests.remove(id);
                     super.onFailure(error);
                 } else {
                     LOG.debug("Request received error: {}", error.getMessage());
-                    IOException ioError = IOExceptionSupport.create(error);
+                    ProviderException ioError = ProviderExceptionSupport.createOrPassthroughFatal(error);
                     whenOffline(ioError);
                     handleProviderFailure(activeProvider, ioError);
                 }
@@ -1211,9 +1205,9 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
          * @param provider
          * 		The provider instance to use when performing this action.
          *
-         * @throws Exception if an error occurs during task execution.
+         * @throws ProviderException if an error occurs during task execution.
          */
-        public abstract void doTask(Provider provider) throws Exception;
+        public abstract void doTask(Provider provider) throws ProviderException;
 
         /**
          * Should the request just succeed when the Provider is not connected.
@@ -1244,15 +1238,15 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
             }
         }
 
-        protected JMSException createTimedOutException() {
-            return new JmsOperationTimedOutException("Timed out waiting on " +  this);
+        protected ProviderException createTimedOutException() {
+            return new ProviderOperationTimedOutException("Timed out waiting on " +  this);
         }
 
-        protected Exception createOfflineFailureException(IOException error) {
-            return IOExceptionSupport.create(error);
+        protected ProviderException createOfflineFailureException(ProviderException error) {
+            return ProviderExceptionSupport.createNonFatalOrPassthrough(error);
         }
 
-        private void whenOffline(IOException error) {
+        private void whenOffline(ProviderException error) {
             if (failureWhenOffline()) {
                 requests.remove(id);
                 getWrappedRequest().onFailure(createOfflineFailureException(error));
@@ -1292,7 +1286,7 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
         }
 
         @Override
-        public void onFailure(final Throwable result) {
+        public void onFailure(final ProviderException result) {
             lock.readLock().lock();
             try {
                 if (closingConnection.get() || closed.get() || failed.get()) {
@@ -1303,7 +1297,7 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
                     // If we managed to receive an Open frame it might contain
                     // a failover update so process it before handling the error.
                     processAlternates(provider.getAlternateURIs());
-                    handleProviderFailure(activeProvider, IOExceptionSupport.create(result));
+                    handleProviderFailure(activeProvider, ProviderExceptionSupport.createOrPassthroughFatal(result));
                 }
             } finally {
                 lock.readLock().unlock();
@@ -1361,7 +1355,7 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
                     serializer.schedule(runnable, delay, TimeUnit.MILLISECONDS);
                 }
             } catch (Throwable unrecoverable) {
-                reportReconnectFailure(unrecoverable);
+                reportReconnectFailure(ProviderExceptionSupport.createOrPassthroughFatal(unrecoverable));
             }
         }
 
@@ -1389,7 +1383,7 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
             return false;
         }
 
-        public boolean isReconnectAllowed(Throwable cause) {
+        public boolean isReconnectAllowed(ProviderException cause) {
             // If a connection attempts fail due to Security errors than we abort
             // reconnection as there is a configuration issue and we want to avoid
             // a spinning reconnect cycle that can never complete.
@@ -1400,11 +1394,11 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
             return !isLimitExceeded();
         }
 
-        private boolean isStoppageCause(Throwable cause) {
-            if(cause.getCause() instanceof JMSSecuritySaslException) {
-                JMSSecuritySaslException saslFailure = (JMSSecuritySaslException) cause.getCause();
+        private boolean isStoppageCause(ProviderException cause) {
+            if (cause instanceof ProviderConnectionSecuritySaslException) {
+                ProviderConnectionSecuritySaslException saslFailure = (ProviderConnectionSecuritySaslException) cause;
                 return !saslFailure.isSysTempFailure();
-            } else if (cause.getCause() instanceof JMSSecurityException ) {
+            } else if (cause instanceof ProviderConnectionSecurityException ) {
                 return true;
             }
 
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/SaslMechanismFinder.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/SaslMechanismFinder.java
index a547ae5..10b2cdc 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/SaslMechanismFinder.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/SaslMechanismFinder.java
@@ -23,8 +23,6 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 
-import javax.jms.JMSSecurityRuntimeException;
-
 import org.apache.qpid.jms.util.FactoryFinder;
 import org.apache.qpid.jms.util.ResourceNotFoundException;
 import org.slf4j.Logger;
@@ -53,7 +51,7 @@ public class SaslMechanismFinder {
      * found.
      *
      * @param username
-     *        the username, or null if there is none
+     *        the user name, or null if there is none
      * @param password
      *        the password, or null if there is none
      * @param localPrincipal
@@ -66,9 +64,10 @@ public class SaslMechanismFinder {
      *        list of mechanism names that are supported by the remote peer.
      *
      * @return the best matching Mechanism for the supported remote set.
-     * @throws JMSSecurityRuntimeException if no matching mechanism can be identified
+     *
+     * @throws SaslSecurityRuntimeException if no matching mechanism can be identified
      */
-    public static Mechanism findMatchingMechanism(String username, String password, Principal localPrincipal, Set<String> mechRestrictions, String... remoteMechanisms) throws JMSSecurityRuntimeException {
+    public static Mechanism findMatchingMechanism(String username, String password, Principal localPrincipal, Set<String> mechRestrictions, String... remoteMechanisms) throws SaslSecurityRuntimeException {
 
         Mechanism match = null;
         List<Mechanism> found = new ArrayList<Mechanism>();
@@ -80,10 +79,10 @@ public class SaslMechanismFinder {
                 Mechanism mech = factory.createMechanism();
 
                 boolean mechConfigured = mechRestrictions != null && mechRestrictions.contains(remoteMechanism);
-                if(mechRestrictions != null && !mechConfigured) {
+                if (mechRestrictions != null && !mechConfigured) {
                     LOG.debug("Skipping {} mechanism because it is not in the configured mechanisms restriction set", remoteMechanism);
-                } else if(mech.isApplicable(username, password, localPrincipal)) {
-                    if(mech.isEnabledByDefault() || mechConfigured) {
+                } else if (mech.isApplicable(username, password, localPrincipal)) {
+                    if (mech.isEnabledByDefault() || mechConfigured) {
                         found.add(mech);
                     } else {
                         LOG.debug("Skipping {} mechanism as it must be explicitly enabled in the configured sasl mechanisms", mech);
@@ -100,7 +99,8 @@ public class SaslMechanismFinder {
             Collections.sort(found);
             match = found.get(found.size() - 1);
         } else {
-            throw new JMSSecurityRuntimeException("No supported mechanism, or none usable with the available credentials. Server offered: " + remoteMechanismNames);
+            throw new SaslSecurityRuntimeException(
+                "No supported mechanism, or none usable with the available credentials. Server offered: " + remoteMechanismNames);
         }
 
         LOG.info("Best match for SASL auth was: {}", match);
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/SaslSecurityRuntimeException.java
similarity index 54%
copy from qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java
copy to qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/SaslSecurityRuntimeException.java
index e680df6..dbe6866 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderException.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/SaslSecurityRuntimeException.java
@@ -14,19 +14,30 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.qpid.jms.provider;
+package org.apache.qpid.jms.sasl;
 
-import java.io.IOException;
+/**
+ * Thrown from the SASL layer when a Runtime exception is encountered.
+ */
+public class SaslSecurityRuntimeException extends RuntimeException {
 
-public class ProviderException extends IOException {
+    private static final long serialVersionUID = -2832134025203759605L;
 
-    private static final long serialVersionUID = -5094579928657311571L;
+    public SaslSecurityRuntimeException() {}
 
-    public ProviderException(String message) {
+    public SaslSecurityRuntimeException(String message) {
         super(message);
     }
 
-    public ProviderException(String message, Throwable cause) {
+    public SaslSecurityRuntimeException(Throwable cause) {
+        super(cause);
+    }
+
+    public SaslSecurityRuntimeException(String message, Throwable cause) {
         super(message, cause);
     }
+
+    public SaslSecurityRuntimeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+        super(message, cause, enableSuppression, writableStackTrace);
+    }
 }
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/JmsConnectionFailedTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/JmsConnectionFailedTest.java
index 961c84b..4e6ecab 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/JmsConnectionFailedTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/JmsConnectionFailedTest.java
@@ -18,12 +18,12 @@ package org.apache.qpid.jms;
 
 import static org.junit.Assert.assertTrue;
 
-import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 
 import javax.jms.ExceptionListener;
 import javax.jms.JMSException;
 
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.jms.test.Wait;
 
 /**
@@ -42,7 +42,7 @@ public class JmsConnectionFailedTest extends JmsConnectionClosedTest {
         });
         connection.start();
 
-        providerListener.onConnectionFailure(new IOException());
+        providerListener.onConnectionFailure(new ProviderException("Something went wrong"));
 
         final JmsConnection jmsConnection = connection;
         assertTrue(Wait.waitFor(new Wait.Condition() {
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/consumer/JmsMessageConsumerFailedTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/consumer/JmsMessageConsumerFailedTest.java
index 1700648..924cd37 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/consumer/JmsMessageConsumerFailedTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/consumer/JmsMessageConsumerFailedTest.java
@@ -18,7 +18,6 @@ package org.apache.qpid.jms.consumer;
 
 import static org.junit.Assert.assertTrue;
 
-import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 
 import javax.jms.ExceptionListener;
@@ -28,6 +27,7 @@ import javax.jms.Queue;
 import javax.jms.Session;
 
 import org.apache.qpid.jms.JmsConnection;
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.jms.test.Wait;
 
 /**
@@ -48,7 +48,7 @@ public class JmsMessageConsumerFailedTest extends JmsMessageConsumerClosedTest {
             }
         });
         connection.start();
-        providerListener.onConnectionFailure(new IOException());
+        providerListener.onConnectionFailure(new ProviderException("Something went wrong"));
 
         final JmsConnection jmsConnection = connection;
         assertTrue(Wait.waitFor(new Wait.Condition() {
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ConnectionIntegrationTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ConnectionIntegrationTest.java
index 272dcd1..9a272e9 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ConnectionIntegrationTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ConnectionIntegrationTest.java
@@ -65,8 +65,8 @@ import org.apache.qpid.jms.JmsConnection;
 import org.apache.qpid.jms.JmsConnectionFactory;
 import org.apache.qpid.jms.JmsConnectionRemotelyClosedException;
 import org.apache.qpid.jms.JmsDefaultConnectionListener;
-import org.apache.qpid.jms.provider.ProviderRedirectedException;
 import org.apache.qpid.jms.provider.amqp.AmqpSupport;
+import org.apache.qpid.jms.provider.exceptions.ProviderConnectionRedirectedException;
 import org.apache.qpid.jms.test.QpidJmsTestCase;
 import org.apache.qpid.jms.test.Wait;
 import org.apache.qpid.jms.test.testpeer.TestAmqpPeer;
@@ -546,9 +546,9 @@ public class ConnectionIntegrationTest extends QpidJmsTestCase {
             assertTrue("Connection should report failure", done.await(5, TimeUnit.SECONDS));
 
             assertTrue(asyncError.get() instanceof JMSException);
-            assertTrue(asyncError.get().getCause() instanceof ProviderRedirectedException);
+            assertTrue(asyncError.get().getCause() instanceof ProviderConnectionRedirectedException);
 
-            ProviderRedirectedException redirect = (ProviderRedirectedException) asyncError.get().getCause();
+            ProviderConnectionRedirectedException redirect = (ProviderConnectionRedirectedException) asyncError.get().getCause();
             URI redirectionURI = redirect.getRedirectionURI();
 
             assertNotNull(redirectionURI);
@@ -653,6 +653,8 @@ public class ConnectionIntegrationTest extends QpidJmsTestCase {
                 // Expected
                 assertNotNull("Expected exception to have a message", jmse.getMessage());
                 assertTrue("Expected breadcrumb to be present in message", jmse.getMessage().contains(BREAD_CRUMB));
+            } catch (Throwable t) {
+                fail("Caught unexpected exception: " + t);
             }
 
             connection.close();
@@ -689,6 +691,8 @@ public class ConnectionIntegrationTest extends QpidJmsTestCase {
                 // Expected
                 assertNotNull("Expected exception to have a message", jmse.getMessage());
                 assertTrue("Expected breadcrumb to be present in message", jmse.getMessage().contains(BREAD_CRUMB));
+            } catch (Throwable t) {
+                fail("Caught unexpected exception: " + t);
             }
 
             connection.close();
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/FailedConnectionsIntegrationTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/FailedConnectionsIntegrationTest.java
index fd5819c..6bf93de 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/FailedConnectionsIntegrationTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/FailedConnectionsIntegrationTest.java
@@ -37,8 +37,8 @@ import javax.jms.JMSException;
 
 import org.apache.qpid.jms.JmsConnectionFactory;
 import org.apache.qpid.jms.JmsOperationTimedOutException;
-import org.apache.qpid.jms.provider.ProviderRedirectedException;
 import org.apache.qpid.jms.provider.amqp.AmqpSupport;
+import org.apache.qpid.jms.provider.exceptions.ProviderConnectionRedirectedException;
 import org.apache.qpid.jms.test.QpidJmsTestCase;
 import org.apache.qpid.jms.test.testpeer.TestAmqpPeer;
 import org.apache.qpid.jms.test.testpeer.basictypes.AmqpError;
@@ -85,7 +85,7 @@ public class FailedConnectionsIntegrationTest extends QpidJmsTestCase {
             } catch (JmsOperationTimedOutException jmsEx) {
                 // Expected
             } catch (Exception ex) {
-                fail("Should have thrown JMSException: " + ex);
+                fail("Should have thrown JmsOperationTimedOutException: " + ex);
             }
 
             testPeer.waitForAllHandlersToComplete(1000);
@@ -204,8 +204,8 @@ public class FailedConnectionsIntegrationTest extends QpidJmsTestCase {
                 establishAnonymousConnecton(testPeer, true);
                 fail("Should have thrown JMSException");
             } catch (JMSException jmsex) {
-                assertTrue(jmsex.getCause() instanceof ProviderRedirectedException);
-                ProviderRedirectedException redirectEx = (ProviderRedirectedException) jmsex.getCause();
+                assertTrue(jmsex.getCause() instanceof ProviderConnectionRedirectedException);
+                ProviderConnectionRedirectedException redirectEx = (ProviderConnectionRedirectedException) jmsex.getCause();
 
                 URI redirectionURI = redirectEx.getRedirectionURI();
 
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/producer/JmsMessageProducerFailedTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/producer/JmsMessageProducerFailedTest.java
index f801800..d01a808 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/producer/JmsMessageProducerFailedTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/producer/JmsMessageProducerFailedTest.java
@@ -18,7 +18,6 @@ package org.apache.qpid.jms.producer;
 
 import static org.junit.Assert.assertTrue;
 
-import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 
 import javax.jms.ExceptionListener;
@@ -27,6 +26,7 @@ import javax.jms.MessageProducer;
 import javax.jms.Session;
 
 import org.apache.qpid.jms.JmsConnection;
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.jms.test.Wait;
 
 /**
@@ -48,7 +48,7 @@ public class JmsMessageProducerFailedTest extends JmsMessageProducerClosedTest {
             }
         });
         connection.start();
-        providerListener.onConnectionFailure(new IOException());
+        providerListener.onConnectionFailure(new ProviderException("Something went wrong"));
 
         final JmsConnection jmsConnection = connection;
         assertTrue(Wait.waitFor(new Wait.Condition() {
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/producer/JmsMessageProducerTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/producer/JmsMessageProducerTest.java
index 81e78dc..32e59b7 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/producer/JmsMessageProducerTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/producer/JmsMessageProducerTest.java
@@ -48,6 +48,7 @@ import org.apache.qpid.jms.JmsQueue;
 import org.apache.qpid.jms.JmsSession;
 import org.apache.qpid.jms.JmsTopic;
 import org.apache.qpid.jms.message.JmsOutboundMessageDispatch;
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.jms.provider.mock.MockRemotePeer;
 import org.apache.qpid.jms.test.Wait;
 import org.junit.After;
@@ -557,7 +558,7 @@ public class JmsMessageProducerTest extends JmsConnectionTestSupport {
                 return remotePoor.getPendingCompletions(destination).size() == MESSAGE_COUNT;
             }
         }));
-        remotePoor.failAllPendingSends(destination, new JMSException("Could not send message"));
+        remotePoor.failAllPendingSends(destination, new ProviderException("Could not send message"));
 
         assertTrue("Not all completions triggered", Wait.waitFor(new Wait.Condition() {
 
@@ -604,7 +605,7 @@ public class JmsMessageProducerTest extends JmsConnectionTestSupport {
 
         for (JmsOutboundMessageDispatch envelope : pending) {
             LOG.info("Trigger failure of message: {}", envelope.getMessage().getJMSMessageID());
-            remotePoor.failPendingSend(envelope, new JMSException("Failed to send message"));
+            remotePoor.failPendingSend(envelope, new ProviderException("Failed to send message"));
         }
 
         assertTrue("Not all failures triggered", Wait.waitFor(new Wait.Condition() {
@@ -657,7 +658,7 @@ public class JmsMessageProducerTest extends JmsConnectionTestSupport {
                 remotePoor.completePendingSend(envelope);
             } else {
                 LOG.info("Trigger failure of message: {}", envelope.getMessage().getJMSMessageID());
-                remotePoor.failPendingSend(envelope, new JMSException("Failed to send message"));
+                remotePoor.failPendingSend(envelope, new ProviderException("Failed to send message"));
             }
         }
 
@@ -768,7 +769,7 @@ public class JmsMessageProducerTest extends JmsConnectionTestSupport {
             }
         }));
 
-        remotePoor.failAllPendingSends(destination, new JMSException("Could not send message"));
+        remotePoor.failAllPendingSends(destination, new ProviderException("Could not send message"));
 
         assertTrue("Completion never got expected ISE", done.await(10, TimeUnit.SECONDS));
 
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/NoOpAsyncResultTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/NoOpAsyncResultTest.java
index 2712068..28bb3b3 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/NoOpAsyncResultTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/NoOpAsyncResultTest.java
@@ -18,8 +18,6 @@ package org.apache.qpid.jms.provider;
 
 import static org.junit.Assert.assertTrue;
 
-import java.io.IOException;
-
 import org.junit.Test;
 
 public class NoOpAsyncResultTest {
@@ -36,7 +34,7 @@ public class NoOpAsyncResultTest {
 
         assertTrue(result.isComplete());
         result.onSuccess();
-        result.onFailure(new IOException());
+        result.onFailure(new ProviderException("Error"));
         assertTrue(result.isComplete());
     }
 
@@ -45,7 +43,7 @@ public class NoOpAsyncResultTest {
         NoOpAsyncResult result = new NoOpAsyncResult();
 
         assertTrue(result.isComplete());
-        result.onFailure(new IOException());
+        result.onFailure(new ProviderException("Error"));
         result.onSuccess();
         assertTrue(result.isComplete());
     }
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/ProviderFutureTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/ProviderFutureTest.java
index 601c879..419e8e3 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/ProviderFutureTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/ProviderFutureTest.java
@@ -21,7 +21,6 @@ import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
@@ -70,7 +69,7 @@ public class ProviderFutureTest {
         future.onSuccess();
         try {
             future.sync();
-        } catch (IOException cause) {
+        } catch (Exception cause) {
             fail("Should throw an error");
         }
     }
@@ -81,7 +80,7 @@ public class ProviderFutureTest {
 
         try {
             assertFalse(future.sync(1, TimeUnit.SECONDS));
-        } catch (IOException cause) {
+        } catch (Exception cause) {
             fail("Should throw an error");
         }
     }
@@ -89,13 +88,13 @@ public class ProviderFutureTest {
     @Test(timeout = 10000)
     public void testOnFailure() {
         ProviderFuture future = futuresFactory.createFuture();
-        IOException ex = new IOException();
+        ProviderException ex = new ProviderException("Failed");
 
         future.onFailure(ex);
         try {
             future.sync(5, TimeUnit.SECONDS);
             fail("Should throw an error");
-        } catch (IOException cause) {
+        } catch (ProviderException cause) {
             assertSame(cause, ex);
         }
     }
@@ -111,14 +110,14 @@ public class ProviderFutureTest {
             }
 
             @Override
-            public void onPendingFailure(Throwable cause) {
+            public void onPendingFailure(ProviderException cause) {
             }
         });
 
         future.onSuccess();
         try {
             future.sync(5, TimeUnit.SECONDS);
-        } catch (IOException cause) {
+        } catch (ProviderException cause) {
             fail("Should throw an error");
         }
 
@@ -135,18 +134,18 @@ public class ProviderFutureTest {
             }
 
             @Override
-            public void onPendingFailure(Throwable cause) {
+            public void onPendingFailure(ProviderException cause) {
                 syncCalled.set(true);
             }
         });
 
-        IOException ex = new IOException();
+        ProviderException ex = new ProviderException("Failed");
 
         future.onFailure(ex);
         try {
             future.sync(5, TimeUnit.SECONDS);
             fail("Should throw an error");
-        } catch (IOException cause) {
+        } catch (ProviderException cause) {
             assertSame(cause, ex);
         }
 
@@ -156,13 +155,13 @@ public class ProviderFutureTest {
     @Test(timeout = 10000)
     public void testSuccessfulStateIsFixed() {
         ProviderFuture future = futuresFactory.createFuture();
-        IOException ex = new IOException();
+        ProviderException ex = new ProviderException("Failed");
 
         future.onSuccess();
         future.onFailure(ex);
         try {
             future.sync(5, TimeUnit.SECONDS);
-        } catch (IOException cause) {
+        } catch (ProviderException cause) {
             fail("Should throw an error");
         }
     }
@@ -170,14 +169,14 @@ public class ProviderFutureTest {
     @Test(timeout = 10000)
     public void testFailedStateIsFixed() {
         ProviderFuture future = futuresFactory.createFuture();
-        IOException ex = new IOException();
+        ProviderException ex = new ProviderException("Failed");
 
         future.onFailure(ex);
         future.onSuccess();
         try {
             future.sync(5, TimeUnit.SECONDS);
             fail("Should throw an error");
-        } catch (IOException cause) {
+        } catch (ProviderException cause) {
             assertSame(cause, ex);
         }
     }
@@ -197,7 +196,7 @@ public class ProviderFutureTest {
                 try {
                     syncing.countDown();
                     future.sync();
-                } catch (IOException cause) {
+                } catch (Exception cause) {
                     if (cause.getCause() instanceof InterruptedException) {
                         interrupted.set(true);
                     }
@@ -230,7 +229,7 @@ public class ProviderFutureTest {
                 try {
                     syncing.countDown();
                     future.sync(20, TimeUnit.SECONDS);
-                } catch (IOException cause) {
+                } catch (ProviderException cause) {
                     if (cause.getCause() instanceof InterruptedException) {
                         interrupted.set(true);
                     }
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/ProviderWrapperTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/ProviderWrapperTest.java
index 89d6a9b..65b0b17 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/ProviderWrapperTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/ProviderWrapperTest.java
@@ -19,7 +19,6 @@ package org.apache.qpid.jms.provider;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.fail;
 
-import java.io.IOException;
 import java.net.URI;
 
 import org.apache.qpid.jms.message.JmsInboundMessageDispatch;
@@ -299,7 +298,7 @@ public class ProviderWrapperTest extends QpidJmsTestCase{
         ProviderWrapper<Provider> wrapper = new ProviderWrapper<Provider>(mockProvider);
         JmsOutboundMessageDispatch envelope = Mockito.mock(JmsOutboundMessageDispatch.class);
         ProviderListener listener = Mockito.mock(ProviderListener.class);
-        IOException ex = new IOException();
+        ProviderException ex = new ProviderException("Error");
 
         wrapper.setProviderListener(listener);
         wrapper.onFailedMessageSend(envelope, ex);
@@ -354,7 +353,7 @@ public class ProviderWrapperTest extends QpidJmsTestCase{
         ProviderListener listener = Mockito.mock(ProviderListener.class);
         JmsSessionId sessionId = new JmsSessionId("ID:TEST", 1);
         JmsSessionInfo session = new JmsSessionInfo(sessionId);
-        IOException ex = new IOException();
+        ProviderException ex = new ProviderException("Error");
 
         wrapper.setProviderListener(listener);
         wrapper.onResourceClosed(session, ex);
@@ -367,7 +366,7 @@ public class ProviderWrapperTest extends QpidJmsTestCase{
         Provider mockProvider = Mockito.mock(Provider.class);
         ProviderWrapper<Provider> wrapper = new ProviderWrapper<Provider>(mockProvider);
         ProviderListener listener = Mockito.mock(ProviderListener.class);
-        IOException ex = new IOException();
+        ProviderException ex = new ProviderException("Error");
 
         wrapper.setProviderListener(listener);
         wrapper.onProviderException(ex);
@@ -380,7 +379,7 @@ public class ProviderWrapperTest extends QpidJmsTestCase{
         Provider mockProvider = Mockito.mock(Provider.class);
         ProviderWrapper<Provider> wrapper = new ProviderWrapper<Provider>(mockProvider);
         ProviderListener listener = Mockito.mock(ProviderListener.class);
-        IOException ex = new IOException();
+        ProviderException ex = new ProviderException("Something went wrong");
 
         wrapper.setProviderListener(listener);
         wrapper.onConnectionFailure(ex);
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/WrappedAsyncResultTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/WrappedAsyncResultTest.java
index 6fea518..287b873 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/WrappedAsyncResultTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/WrappedAsyncResultTest.java
@@ -20,7 +20,6 @@ import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
-import java.io.IOException;
 import java.util.Collections;
 
 import org.junit.Test;
@@ -63,7 +62,7 @@ public class WrappedAsyncResultTest {
 
         assertFalse(future.isComplete());
         assertFalse(wrapped.isComplete());
-        wrapped.onFailure(new IOException());
+        wrapped.onFailure(new ProviderException("Error"));
         assertTrue(wrapped.isComplete());
         assertTrue(future.isComplete());
     }
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/amqp/AmqpProviderTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/amqp/AmqpProviderTest.java
index fd4be1b..074b038 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/amqp/AmqpProviderTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/amqp/AmqpProviderTest.java
@@ -23,10 +23,8 @@ import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import java.io.IOException;
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.security.ProviderException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -38,6 +36,7 @@ import org.apache.qpid.jms.meta.JmsResource;
 import org.apache.qpid.jms.meta.JmsResourceId;
 import org.apache.qpid.jms.meta.JmsResourceVistor;
 import org.apache.qpid.jms.provider.DefaultProviderListener;
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.jms.provider.ProviderFuture;
 import org.apache.qpid.jms.test.QpidJmsTestCase;
 import org.apache.qpid.jms.test.testpeer.TestAmqpPeer;
@@ -318,18 +317,18 @@ public class AmqpProviderTest extends QpidJmsTestCase {
             try {
                 provider.start();
                 fail("Should have thrown an IOException when closed.");
-            } catch (IOException ex) {}
+            } catch (Exception ex) {}
 
             try {
                 provider.connect(connectionInfo);
                 fail("Should have thrown an IOException when closed.");
-            } catch (IOException ex) {}
+            } catch (Exception ex) {}
 
             ProviderFuture request = provider.newProviderFuture();
             try {
                 provider.unsubscribe("subscription-name", request);
                 fail("Should have thrown an IOException when closed.");
-            } catch (IOException ex) {}
+            } catch (Exception ex) {}
 
             provider.close();
 
@@ -444,7 +443,7 @@ public class AmqpProviderTest extends QpidJmsTestCase {
             try {
                 request.sync();
                 fail("Request should have failed");
-            } catch (IOException e) {
+            } catch (Exception e) {
                 // Expected
             }
 
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/amqp/AmqpSaslAuthenticatorTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/amqp/AmqpSaslAuthenticatorTest.java
index 1658561..22630c3 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/amqp/AmqpSaslAuthenticatorTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/amqp/AmqpSaslAuthenticatorTest.java
@@ -33,11 +33,11 @@ import java.util.Deque;
 import java.util.Map;
 import java.util.function.Function;
 
-import javax.jms.JMSSecurityRuntimeException;
 import javax.security.sasl.SaslException;
 
-import org.apache.qpid.jms.exceptions.JMSSecuritySaslException;
+import org.apache.qpid.jms.provider.exceptions.ProviderConnectionSecuritySaslException;
 import org.apache.qpid.jms.sasl.Mechanism;
+import org.apache.qpid.jms.sasl.SaslSecurityRuntimeException;
 import org.apache.qpid.proton.engine.Sasl;
 import org.apache.qpid.proton.engine.Sasl.SaslOutcome;
 import org.apache.qpid.proton.engine.Sasl.SaslState;
@@ -79,7 +79,7 @@ public class AmqpSaslAuthenticatorTest {
     @Test
     public void testNoSaslMechanismAgreed() throws Exception {
         Function<String[], Mechanism> mechanismFunction = mechanismName -> {
-            throw new JMSSecurityRuntimeException("reasons");
+            throw new SaslSecurityRuntimeException("reasons");
         };
 
         AmqpSaslAuthenticator authenticator = new AmqpSaslAuthenticator(mechanismFunction);
@@ -164,7 +164,7 @@ public class AmqpSaslAuthenticatorTest {
         assertTrue(authenticator.isComplete());
         assertFalse(authenticator.wasSuccessful());
         assertNotNull(authenticator.getFailureCause());
-        assertTrue(authenticator.getFailureCause() instanceof JMSSecuritySaslException);
+        assertTrue(authenticator.getFailureCause() instanceof ProviderConnectionSecuritySaslException);
         assertTrue(authenticator.getFailureCause().getMessage().contains("Client failed to authenticate"));
         assertTrue(authenticator.getFailureCause().getMessage().contains("due to temporary system error"));
     }
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/amqp/AmqpSupportTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/amqp/AmqpSupportTest.java
index 15f29d8..5153903 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/amqp/AmqpSupportTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/amqp/AmqpSupportTest.java
@@ -21,13 +21,13 @@ import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
-import java.io.IOException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.HashMap;
 import java.util.Map;
 
-import org.apache.qpid.jms.provider.ProviderRedirectedException;
+import org.apache.qpid.jms.provider.ProviderException;
+import org.apache.qpid.jms.provider.exceptions.ProviderConnectionRedirectedException;
 import org.apache.qpid.proton.amqp.Symbol;
 import org.apache.qpid.proton.amqp.transport.AmqpError;
 import org.apache.qpid.proton.amqp.transport.ErrorCondition;
@@ -58,9 +58,9 @@ public class AmqpSupportTest {
         Exception result = AmqpSupport.createRedirectException(mockProvider, error, message, condition);
 
         assertNotNull(result);
-        assertTrue(result instanceof ProviderRedirectedException);
+        assertTrue(result instanceof ProviderConnectionRedirectedException);
 
-        ProviderRedirectedException pre = (ProviderRedirectedException) result;
+        ProviderConnectionRedirectedException pre = (ProviderConnectionRedirectedException) result;
 
         URI redirection = pre.getRedirectionURI();
 
@@ -83,8 +83,8 @@ public class AmqpSupportTest {
         Exception result = AmqpSupport.createRedirectException(mockProvider, error, message, condition);
 
         assertNotNull(result);
-        assertFalse(result instanceof ProviderRedirectedException);
-        assertTrue(result instanceof IOException);
+        assertFalse(result instanceof ProviderConnectionRedirectedException);
+        assertTrue(result instanceof ProviderException);
     }
 
     @Test
@@ -108,8 +108,8 @@ public class AmqpSupportTest {
         Exception result = AmqpSupport.createRedirectException(mockProvider, error, message, condition);
 
         assertNotNull(result);
-        assertFalse(result instanceof ProviderRedirectedException);
-        assertTrue(result instanceof IOException);
+        assertFalse(result instanceof ProviderConnectionRedirectedException);
+        assertTrue(result instanceof ProviderException);
     }
 
     @Test
@@ -134,8 +134,8 @@ public class AmqpSupportTest {
         Exception result = AmqpSupport.createRedirectException(mockProvider, error, message, condition);
 
         assertNotNull(result);
-        assertFalse(result instanceof ProviderRedirectedException);
-        assertTrue(result instanceof IOException);
+        assertFalse(result instanceof ProviderConnectionRedirectedException);
+        assertTrue(result instanceof ProviderException);
     }
 
     @Test
@@ -160,7 +160,7 @@ public class AmqpSupportTest {
         Exception result = AmqpSupport.createRedirectException(mockProvider, error, message, condition);
 
         assertNotNull(result);
-        assertFalse(result instanceof ProviderRedirectedException);
-        assertTrue(result instanceof IOException);
+        assertFalse(result instanceof ProviderConnectionRedirectedException);
+        assertTrue(result instanceof ProviderException);
     }
 }
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/failover/FailoverIntegrationTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/failover/FailoverIntegrationTest.java
index 25d6db4..f79f16c 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/failover/FailoverIntegrationTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/failover/FailoverIntegrationTest.java
@@ -16,7 +16,9 @@
  */
 package org.apache.qpid.jms.provider.failover;
 
+import static org.apache.qpid.jms.provider.amqp.AmqpSupport.DELAYED_DELIVERY;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.arrayContaining;
 import static org.hamcrest.Matchers.both;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.greaterThanOrEqualTo;
@@ -84,6 +86,7 @@ import org.apache.qpid.jms.test.testpeer.describedtypes.Released;
 import org.apache.qpid.jms.test.testpeer.describedtypes.TransactionalState;
 import org.apache.qpid.jms.test.testpeer.describedtypes.sections.AmqpValueDescribedType;
 import org.apache.qpid.jms.test.testpeer.matchers.SourceMatcher;
+import org.apache.qpid.jms.test.testpeer.matchers.TargetMatcher;
 import org.apache.qpid.jms.test.testpeer.matchers.TransactionalStateMatcher;
 import org.apache.qpid.jms.test.testpeer.matchers.sections.MessageAnnotationsSectionMatcher;
 import org.apache.qpid.jms.test.testpeer.matchers.sections.MessageHeaderSectionMatcher;
@@ -98,6 +101,7 @@ import org.apache.qpid.proton.amqp.DescribedType;
 import org.apache.qpid.proton.amqp.Symbol;
 import org.apache.qpid.proton.amqp.UnsignedByte;
 import org.apache.qpid.proton.amqp.UnsignedInteger;
+import org.hamcrest.Matcher;
 import org.hamcrest.MatcherAssert;
 import org.hamcrest.Matchers;
 import org.junit.Test;
@@ -423,7 +427,12 @@ public class FailoverIntegrationTest extends QpidJmsTestCase {
                     }
                 }
             });
-            connection.start();
+
+            try {
+                connection.start();
+            } catch (Exception ex) {
+                fail("Should not have thrown an Exception: " + ex);
+            }
 
             Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
             Queue queue = session.createQueue("myQueue");
@@ -596,7 +605,6 @@ public class FailoverIntegrationTest extends QpidJmsTestCase {
 
     @Test(timeout = 20000)
     public void testFailoverHandlesConnectErrorNotFound() throws Exception {
-
         try (TestAmqpPeer originalPeer = new TestAmqpPeer();
              TestAmqpPeer finalPeer = new TestAmqpPeer();) {
 
@@ -1556,12 +1564,12 @@ public class FailoverIntegrationTest extends QpidJmsTestCase {
             Topic dest = session.createTopic(topicName);
 
             //Expect a link to a topic node, which we will then refuse
-            SourceMatcher targetMatcher = new SourceMatcher();
-            targetMatcher.withAddress(equalTo(topicName));
-            targetMatcher.withDynamic(equalTo(false));
-            targetMatcher.withDurable(equalTo(TerminusDurability.NONE));
+            SourceMatcher sourceMatcher = new SourceMatcher();
+            sourceMatcher.withAddress(equalTo(topicName));
+            sourceMatcher.withDynamic(equalTo(false));
+            sourceMatcher.withDurable(equalTo(TerminusDurability.NONE));
 
-            testPeer.expectReceiverAttach(notNullValue(), targetMatcher, true, deferAttachResponseWrite);
+            testPeer.expectReceiverAttach(notNullValue(), sourceMatcher, true, deferAttachResponseWrite);
             //Expect the detach response to the test peer closing the consumer link after refusal.
             testPeer.expectDetach(true, false, false);
 
@@ -1581,6 +1589,57 @@ public class FailoverIntegrationTest extends QpidJmsTestCase {
         }
     }
 
+    @Test(timeout = 20000)
+    public void testCreateProducerFailsWhenLinkRefusedAndAttachResponseWriteIsNotDeferred() throws Exception {
+        doCreateProducerFailsWhenLinkRefusedTestImpl(false);
+    }
+
+    @Test(timeout = 20000)
+    public void testCreateProducerFailsWhenLinkRefusedAndAttachResponseWriteIsDeferred() throws Exception {
+        doCreateProducerFailsWhenLinkRefusedTestImpl(true);
+    }
+
+    private void doCreateProducerFailsWhenLinkRefusedTestImpl(boolean deferAttachResponseWrite) throws Exception {
+        try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+            testPeer.expectSaslAnonymous();
+            testPeer.expectOpen();
+            testPeer.expectBegin();
+
+            Connection connection = establishAnonymousConnecton(testPeer);
+            connection.start();
+
+            testPeer.expectBegin();
+            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+            String topicName = "myTopic";
+            Topic dest = session.createTopic(topicName);
+
+            // Expect a link to a topic node, which we will then refuse
+            TargetMatcher targetMatcher = new TargetMatcher();
+            targetMatcher.withAddress(equalTo(topicName));
+            targetMatcher.withDynamic(equalTo(false));
+            targetMatcher.withDurable(equalTo(TerminusDurability.NONE));
+
+            testPeer.expectSenderAttach(notNullValue(), targetMatcher, true, deferAttachResponseWrite);
+            // Expect the detach response to the test peer closing the producer link after refusal.
+            testPeer.expectDetach(true, false, false);
+
+            try {
+                // Create a producer, expect it to throw exception due to the link-refusal
+                session.createProducer(dest);
+                fail("Producer creation should have failed when link was refused");
+            } catch(InvalidDestinationException ide) {
+                LOG.info("Test caught expected error: {}", ide.getMessage());
+            }
+
+            // Shut it down
+            testPeer.expectClose();
+            connection.close();
+
+            testPeer.waitForAllHandlersToComplete(1000);
+        }
+    }
+
     @Test(timeout=20000)
     public void testTxRecreatedAfterConnectionFailsOver() throws Exception {
         try (TestAmqpPeer originalPeer = new TestAmqpPeer();
@@ -3538,6 +3597,287 @@ public class FailoverIntegrationTest extends QpidJmsTestCase {
         }
     }
 
+    @Test(timeout = 20000)
+    public void testPassthroughCreateTemporaryQueueFailsWhenLinkRefusedAndAttachResponseWriteIsNotDeferred() throws Exception {
+        doCreateTemporaryDestinationFailsWhenLinkRefusedTestImpl(false, false);
+    }
+
+    @Test(timeout = 20000)
+    public void testPassthroughCreateTemporaryQueueFailsWhenLinkRefusedAndAttachResponseWriteIsDeferred() throws Exception {
+        doCreateTemporaryDestinationFailsWhenLinkRefusedTestImpl(false, true);
+    }
+
+    @Test(timeout = 20000)
+    public void testPassthroughCreateTemporaryTopicFailsWhenLinkRefusedAndAttachResponseWriteIsNotDeferred() throws Exception {
+        doCreateTemporaryDestinationFailsWhenLinkRefusedTestImpl(true, false);
+    }
+
+    @Test(timeout = 20000)
+    public void testPassthroughCreateTemporaryTopicFailsWhenLinkRefusedAndAttachResponseWriteIsDeferred() throws Exception {
+        doCreateTemporaryDestinationFailsWhenLinkRefusedTestImpl(true, true);
+    }
+
+    private void doCreateTemporaryDestinationFailsWhenLinkRefusedTestImpl(boolean topic, boolean deferAttachResponseWrite) throws Exception {
+        try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+            JmsConnection connection = establishAnonymousConnecton(testPeer);
+
+            testPeer.expectSaslAnonymous();
+            testPeer.expectOpen();
+            testPeer.expectBegin();
+            testPeer.expectBegin();
+
+            connection.start();
+
+            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+            try {
+                if (topic) {
+                    testPeer.expectAndRefuseTempTopicCreationAttach(AmqpError.UNAUTHORIZED_ACCESS, "Not Authorized to create temp topics.", false);
+                    //Expect the detach response to the test peer after refusal.
+                    testPeer.expectDetach(true, false, false);
+
+                    session.createTemporaryTopic();
+                } else {
+                    testPeer.expectAndRefuseTempQueueCreationAttach(AmqpError.UNAUTHORIZED_ACCESS, "Not Authorized to create temp queues.", false);
+                    //Expect the detach response to the test peer after refusal.
+                    testPeer.expectDetach(true, false, false);
+
+                    session.createTemporaryQueue();
+                }
+                fail("Should have thrown security exception");
+            } catch (JMSSecurityException jmsse) {
+            }
+
+            testPeer.expectClose();
+            connection.close();
+
+            testPeer.waitForAllHandlersToComplete(1000);
+        }
+    }
+
+    @Test(timeout = 20000)
+    public void testPassthroughRemotelyCloseProducer() throws Exception {
+        final String BREAD_CRUMB = "ErrorMessageBreadCrumb";
+
+        try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+            final CountDownLatch producerClosed = new CountDownLatch(1);
+            JmsConnection connection = establishAnonymousConnecton(testPeer);
+            connection.addConnectionListener(new JmsDefaultConnectionListener() {
+                @Override
+                public void onProducerClosed(MessageProducer producer, Throwable exception) {
+                    producerClosed.countDown();
+                }
+            });
+
+            testPeer.expectSaslAnonymous();
+            testPeer.expectOpen();
+            testPeer.expectBegin();
+            testPeer.expectBegin();
+
+            connection.start();
+
+            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+            // Create a producer, then remotely end it afterwards.
+            testPeer.expectSenderAttach();
+            testPeer.remotelyDetachLastOpenedLinkOnLastOpenedSession(true, true, AmqpError.RESOURCE_DELETED, BREAD_CRUMB);
+
+            Queue queue = session.createQueue("myQueue");
+            final MessageProducer producer = session.createProducer(queue);
+
+            // Verify the producer gets marked closed
+            testPeer.waitForAllHandlersToComplete(1000);
+            assertTrue("producer never closed.", Wait.waitFor(new Wait.Condition() {
+                @Override
+                public boolean isSatisfied() throws Exception {
+                    try {
+                        producer.getDestination();
+                    } catch (Exception ex) {
+                        if (ex instanceof IllegalStateException && ex.getCause() != null) {
+                            String message = ex.getCause().getMessage();
+                            if (message.contains(AmqpError.RESOURCE_DELETED.toString()) &&
+                                message.contains(BREAD_CRUMB)) {
+                                return true;
+                            }
+                        }
+
+                        LOG.debug("Caught unexpected exception: {}", ex);
+                    }
+
+                    return false;
+                }
+            }, 10000, 10));
+
+            assertTrue("Producer closed callback didn't trigger", producerClosed.await(10, TimeUnit.SECONDS));
+
+            // Try closing it explicitly, should effectively no-op in client.
+            // The test peer will throw during close if it sends anything.
+            producer.close();
+
+            testPeer.expectClose();
+            connection.close();
+
+            testPeer.waitForAllHandlersToComplete(1000);
+        }
+    }
+
+    @Test(timeout = 20000)
+    public void testPassthroughOfSendFailsWhenDelayedDeliveryIsNotSupported() throws Exception {
+        try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+            // DO NOT add capability to indicate server support for DELAYED-DELIVERY
+            JmsConnection connection = establishAnonymousConnecton(testPeer);
+
+            testPeer.expectSaslAnonymous();
+            testPeer.expectOpen();
+            testPeer.expectBegin();
+            testPeer.expectBegin();
+
+            connection.start();
+
+            Matcher<Symbol[]> desiredCapabilitiesMatcher = arrayContaining(new Symbol[] { DELAYED_DELIVERY });
+            Symbol[] offeredCapabilities = null;
+            testPeer.expectSenderAttach(notNullValue(), notNullValue(), false, false, false, false, 0, 1, null, null, desiredCapabilitiesMatcher, offeredCapabilities);
+
+            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+            String topicName = "myTopic";
+            Topic dest = session.createTopic(topicName);
+
+            MessageProducer producer = session.createProducer(dest);
+            producer.setDeliveryDelay(5000);
+
+            // Producer should fail to send when message has delivery delay since remote
+            // did not report that it supports that option.
+            Message message = session.createMessage();
+            try {
+                producer.send(message);
+                fail("Send should fail");
+            } catch (JMSException jmsEx) {
+                LOG.debug("Caught expected error from failed send.");
+            }
+
+            testPeer.expectClose();
+            connection.close();
+
+            testPeer.waitForAllHandlersToComplete(1000);
+        }
+    }
+
+    @Test(timeout = 20000)
+    public void testPassthroughOfSendTimesOutWhenNoDispostionArrives() throws Exception {
+        try(TestAmqpPeer testPeer = new TestAmqpPeer();) {
+            JmsConnection connection = establishAnonymousConnecton(testPeer);
+
+            testPeer.expectSaslAnonymous();
+            testPeer.expectOpen();
+            testPeer.expectBegin();
+            testPeer.expectBegin();
+
+            connection.setSendTimeout(500);
+            connection.start();
+
+            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+            String queueName = "myQueue";
+            Queue queue = session.createQueue(queueName);
+
+            Message message = session.createTextMessage("text");
+            TransferPayloadCompositeMatcher messageMatcher = new TransferPayloadCompositeMatcher();
+
+            // Expect the producer to attach and grant it some credit, it should send
+            // a transfer which we will not send any response for which should cause the
+            // send operation to time out.
+            testPeer.expectSenderAttach();
+            testPeer.expectTransferButDoNotRespond(messageMatcher);
+            testPeer.expectClose();
+
+            MessageProducer producer = session.createProducer(queue);
+
+            try {
+                producer.send(message);
+                fail("Send should time out.");
+            } catch (JmsSendTimedOutException jmsEx) {
+                LOG.info("Caught expected error: {}", jmsEx.getMessage());
+            } catch (Throwable error) {
+                fail("Send should time out, but got: " + error.getMessage());
+            }
+
+            connection.close();
+
+            testPeer.waitForAllHandlersToComplete(1000);
+        }
+    }
+
+    @Test(timeout=20000)
+    public void testPassthroughOfRollbackErrorCoordinatorClosedOnCommit() throws Exception {
+        try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+            JmsConnection connection = establishAnonymousConnecton(testPeer);
+
+            testPeer.expectSaslAnonymous();
+            testPeer.expectOpen();
+            testPeer.expectBegin();
+            testPeer.expectBegin();
+            testPeer.expectCoordinatorAttach();
+
+            connection.start();
+
+            Binary txnId1 = new Binary(new byte[]{ (byte) 5, (byte) 6, (byte) 7, (byte) 8});
+            Binary txnId2 = new Binary(new byte[]{ (byte) 1, (byte) 2, (byte) 3, (byte) 4});
+
+            testPeer.expectDeclare(txnId1);
+            testPeer.remotelyCloseLastCoordinatorLinkOnDischarge(txnId1, false, true, txnId2);
+            testPeer.expectCoordinatorAttach();
+            testPeer.expectDeclare(txnId2);
+            testPeer.expectDischarge(txnId2, true);
+
+            Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
+
+            try {
+                session.commit();
+                fail("Transaction should have rolled back");
+            } catch (TransactionRolledBackException ex) {
+                LOG.info("Caught expected TransactionRolledBackException");
+            }
+
+            testPeer.expectClose();
+            connection.close();
+
+            testPeer.waitForAllHandlersToComplete(1000);
+        }
+    }
+
+    @Test(timeout=20000)
+    public void testPassthroughOfSessionCreateFailsOnDeclareTimeout() throws Exception {
+        try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+            JmsConnection connection = establishAnonymousConnecton(testPeer);
+
+            testPeer.expectSaslAnonymous();
+            testPeer.expectOpen();
+            testPeer.expectBegin();
+            testPeer.expectBegin();
+            testPeer.expectCoordinatorAttach();
+            testPeer.expectDeclareButDoNotRespond();
+            // Expect the AMQP session to be closed due to the JMS session creation failure.
+            testPeer.expectEnd();
+
+            connection.setRequestTimeout(500);
+            connection.start();
+
+            try {
+                connection.createSession(true, Session.SESSION_TRANSACTED);
+                fail("Should have timed out waiting for declare.");
+            } catch (JmsOperationTimedOutException jmsEx) {
+            } catch (Throwable error) {
+                fail("Should have caught an timed out exception:");
+                LOG.error("Caught -> ", error);
+            }
+
+            testPeer.expectClose();
+            connection.close();
+
+            testPeer.waitForAllHandlersToComplete(1000);
+        }
+    }
+
     private JmsConnection establishAnonymousConnecton(TestAmqpPeer... peers) throws JMSException {
         return establishAnonymousConnecton(null, null, peers);
     }
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/failover/FailoverProviderClosedTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/failover/FailoverProviderClosedTest.java
index c5d4197..fd4be3e 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/failover/FailoverProviderClosedTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/failover/FailoverProviderClosedTest.java
@@ -16,7 +16,6 @@
  */
 package org.apache.qpid.jms.provider.failover;
 
-import java.io.IOException;
 import java.net.URI;
 import java.util.Collections;
 
@@ -28,6 +27,7 @@ import org.apache.qpid.jms.meta.JmsSessionInfo;
 import org.apache.qpid.jms.meta.JmsTransactionId;
 import org.apache.qpid.jms.meta.JmsTransactionInfo;
 import org.apache.qpid.jms.provider.ProviderConstants.ACK_TYPE;
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.jms.provider.ProviderFuture;
 import org.apache.qpid.jms.provider.ProviderFutureFactory;
 import org.junit.Before;
@@ -63,59 +63,59 @@ public class FailoverProviderClosedTest extends FailoverProviderTestSupport {
         provider.close();
     }
 
-    @Test(timeout=30000, expected=IOException.class)
+    @Test(timeout=30000, expected=ProviderException.class)
     public void testConnect() throws Exception {
         provider.connect(connection);
     }
 
-    @Test(timeout=30000, expected=IOException.class)
+    @Test(timeout=30000, expected=ProviderException.class)
     public void testStart() throws Exception {
         provider.start();
     }
 
-    @Test(timeout=30000, expected=IOException.class)
+    @Test(timeout=30000, expected=ProviderException.class)
     public void testCreateResource() throws Exception {
         ProviderFuture request = futuresFactory.createFuture();
         provider.create(connection, request);
     }
 
-    @Test(timeout=30000, expected=IOException.class)
+    @Test(timeout=30000, expected=ProviderException.class)
     public void testStartResource() throws Exception {
         ProviderFuture request = futuresFactory.createFuture();
         provider.start(session, request);
     }
 
-    @Test(timeout=30000, expected=IOException.class)
+    @Test(timeout=30000, expected=ProviderException.class)
     public void testStopResource() throws Exception {
         ProviderFuture request = futuresFactory.createFuture();
         provider.stop(session, request);
     }
 
-    @Test(timeout=30000, expected=IOException.class)
+    @Test(timeout=30000, expected=ProviderException.class)
     public void testDestroyResource() throws Exception {
         ProviderFuture request = futuresFactory.createFuture();
         provider.destroy(session, request);
     }
 
-    @Test(timeout=30000, expected=IOException.class)
+    @Test(timeout=30000, expected=ProviderException.class)
     public void testSend() throws Exception {
         ProviderFuture request = futuresFactory.createFuture();
         provider.send(new JmsOutboundMessageDispatch(), request);
     }
 
-    @Test(timeout=30000, expected=IOException.class)
+    @Test(timeout=30000, expected=ProviderException.class)
     public void testSessionAcknowledge() throws Exception {
         ProviderFuture request = futuresFactory.createFuture();
         provider.acknowledge(session.getId(), ACK_TYPE.ACCEPTED, request);
     }
 
-    @Test(timeout=30000, expected=IOException.class)
+    @Test(timeout=30000, expected=ProviderException.class)
     public void testAcknowledgeMessage() throws Exception {
         ProviderFuture request = futuresFactory.createFuture();
         provider.acknowledge(new JmsInboundMessageDispatch(1), ACK_TYPE.ACCEPTED, request);
     }
 
-    @Test(timeout=30000, expected=IOException.class)
+    @Test(timeout=30000, expected=ProviderException.class)
     public void testCommit() throws Exception {
         ProviderFuture request = futuresFactory.createFuture();
         JmsTransactionId txId = new JmsTransactionId(connection.getId(), 1);
@@ -123,7 +123,7 @@ public class FailoverProviderClosedTest extends FailoverProviderTestSupport {
         provider.commit(txInfo, null, request);
     }
 
-    @Test(timeout=30000, expected=IOException.class)
+    @Test(timeout=30000, expected=ProviderException.class)
     public void testRollback() throws Exception {
         ProviderFuture request = futuresFactory.createFuture();
         JmsTransactionId txId = new JmsTransactionId(connection.getId(), 1);
@@ -131,19 +131,19 @@ public class FailoverProviderClosedTest extends FailoverProviderTestSupport {
         provider.rollback(txInfo, null, request);
     }
 
-    @Test(timeout=30000, expected=IOException.class)
+    @Test(timeout=30000, expected=ProviderException.class)
     public void testRecover() throws Exception {
         ProviderFuture request = futuresFactory.createFuture();
         provider.recover(session.getId(), request);
     }
 
-    @Test(timeout=30000, expected=IOException.class)
+    @Test(timeout=30000, expected=ProviderException.class)
     public void testUnsubscribe() throws Exception {
         ProviderFuture request = futuresFactory.createFuture();
         provider.unsubscribe("subscription-name", request);
     }
 
-    @Test(timeout=30000, expected=IOException.class)
+    @Test(timeout=30000, expected=ProviderException.class)
     public void testMessagePull() throws Exception {
         ProviderFuture request = futuresFactory.createFuture();
         provider.pull(consumer.getId(), 1, request);
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/failover/FailoverProviderOfflineBehaviorTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/failover/FailoverProviderOfflineBehaviorTest.java
index 1ee03ec..6220b2a 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/failover/FailoverProviderOfflineBehaviorTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/failover/FailoverProviderOfflineBehaviorTest.java
@@ -18,7 +18,6 @@ package org.apache.qpid.jms.provider.failover;
 
 import static org.junit.Assert.fail;
 
-import java.io.IOException;
 import java.net.URI;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -34,6 +33,7 @@ import org.apache.qpid.jms.JmsConnectionFactory;
 import org.apache.qpid.jms.JmsDefaultConnectionListener;
 import org.apache.qpid.jms.meta.JmsResource;
 import org.apache.qpid.jms.meta.JmsSessionInfo;
+import org.apache.qpid.jms.provider.exceptions.ProviderIOException;
 import org.apache.qpid.jms.provider.mock.ResourceLifecycleFilter;
 import org.junit.After;
 import org.junit.Before;
@@ -128,7 +128,7 @@ public class FailoverProviderOfflineBehaviorTest extends FailoverProviderTestSup
             public void onLifecycleEvent(JmsResource resource) throws Exception {
                 if (resource instanceof JmsSessionInfo) {
                     mockPeer.shutdownQuietly();
-                    throw new IOException();
+                    throw new ProviderIOException("Failure closing session");
                 }
             }
         });
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/failover/FailoverProviderTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/failover/FailoverProviderTest.java
index 9c624d4..d9188f1 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/failover/FailoverProviderTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/failover/FailoverProviderTest.java
@@ -23,7 +23,6 @@ import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import java.io.IOException;
 import java.net.URI;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -418,7 +417,7 @@ public class FailoverProviderTest extends FailoverProviderTestSupport {
     }
 
     @Test(timeout=10000)
-    public void testTimeoutsSetFromConnectionInfo() throws IOException, JMSException {
+    public void testTimeoutsSetFromConnectionInfo() throws Exception {
         final long CONNECT_TIMEOUT = TimeUnit.SECONDS.toMillis(4);
         final long CLOSE_TIMEOUT = TimeUnit.SECONDS.toMillis(5);
         final long SEND_TIMEOUT = TimeUnit.SECONDS.toMillis(6);
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/mock/MockProvider.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/mock/MockProvider.java
index 565d1f0..0909778 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/mock/MockProvider.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/mock/MockProvider.java
@@ -16,7 +16,6 @@
  */
 package org.apache.qpid.jms.provider.mock;
 
-import java.io.IOException;
 import java.net.URI;
 import java.util.Collections;
 import java.util.List;
@@ -27,8 +26,6 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import javax.jms.JMSException;
-
 import org.apache.qpid.jms.message.JmsInboundMessageDispatch;
 import org.apache.qpid.jms.message.JmsMessageFactory;
 import org.apache.qpid.jms.message.JmsOutboundMessageDispatch;
@@ -40,13 +37,16 @@ import org.apache.qpid.jms.meta.JmsSessionId;
 import org.apache.qpid.jms.meta.JmsTransactionInfo;
 import org.apache.qpid.jms.provider.AsyncResult;
 import org.apache.qpid.jms.provider.Provider;
-import org.apache.qpid.jms.provider.ProviderClosedException;
 import org.apache.qpid.jms.provider.ProviderConstants.ACK_TYPE;
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.jms.provider.ProviderFuture;
 import org.apache.qpid.jms.provider.ProviderFutureFactory;
 import org.apache.qpid.jms.provider.ProviderListener;
 import org.apache.qpid.jms.provider.ProviderSynchronization;
 import org.apache.qpid.jms.provider.amqp.AmqpProvider;
+import org.apache.qpid.jms.provider.exceptions.ProviderClosedException;
+import org.apache.qpid.jms.provider.exceptions.ProviderExceptionSupport;
+import org.apache.qpid.jms.provider.exceptions.ProviderIOException;
 import org.apache.qpid.jms.util.ThreadPoolUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -101,13 +101,13 @@ public class MockProvider implements Provider {
     }
 
     @Override
-    public void connect(JmsConnectionInfo connectionInfo) throws IOException {
+    public void connect(JmsConnectionInfo connectionInfo) throws ProviderException {
         checkClosed();
 
         stats.recordConnectAttempt();
 
         if (configuration.isFailOnConnect()) {
-            throw new IOException("Failed to connect to: " + remoteURI);
+            throw new ProviderIOException("Failed to connect to: " + remoteURI);
         }
 
         if (context != null) {
@@ -116,7 +116,7 @@ public class MockProvider implements Provider {
     }
 
     @Override
-    public void start() throws IOException, IllegalStateException {
+    public void start() throws ProviderException, IllegalStateException {
         checkClosed();
 
         if (listener == null) {
@@ -124,7 +124,7 @@ public class MockProvider implements Provider {
         }
 
         if (configuration.isFailOnStart()) {
-            throw new IOException();
+            throw new ProviderException("Error");
         }
     }
 
@@ -145,13 +145,13 @@ public class MockProvider implements Provider {
                         }
 
                         if (configuration.isFailOnClose()) {
-                            request.onFailure(new RuntimeException());
+                            request.onFailure(new ProviderIOException("Failed on close"));
                         } else {
                             request.onSuccess();
                         }
-                    } catch (Exception e) {
+                    } catch (Throwable e) {
                         LOG.debug("Caught exception while closing the MockProvider");
-                        request.onFailure(e);
+                        request.onFailure(ProviderExceptionSupport.createOrPassthroughFatal(e));
                     }
                 }
             });
@@ -162,7 +162,7 @@ public class MockProvider implements Provider {
                 } else {
                     request.sync(closeTimeout, TimeUnit.MILLISECONDS);
                 }
-            } catch (IOException e) {
+            } catch (Exception e) {
                 LOG.warn("Error caught while closing Provider: ", e.getMessage());
             } finally {
                 ThreadPoolUtils.shutdownGraceful(serializer);
@@ -181,7 +181,7 @@ public class MockProvider implements Provider {
     }
 
     @Override
-    public void create(final JmsResource resource, final AsyncResult request) throws IOException, JMSException {
+    public void create(final JmsResource resource, final AsyncResult request) throws ProviderException {
         checkClosed();
         serializer.execute(new Runnable() {
 
@@ -202,15 +202,15 @@ public class MockProvider implements Provider {
                     }
 
                     request.onSuccess();
-                } catch (Exception error) {
-                    request.onFailure(error);
+                } catch (Throwable error) {
+                    request.onFailure(ProviderExceptionSupport.createNonFatalOrPassthrough(error));
                 }
             }
         });
     }
 
     @Override
-    public void start(final JmsResource resource, final AsyncResult request) throws IOException, JMSException {
+    public void start(final JmsResource resource, final AsyncResult request) throws ProviderException {
         checkClosed();
         serializer.execute(new Runnable() {
 
@@ -226,14 +226,14 @@ public class MockProvider implements Provider {
 
                     request.onSuccess();
                 } catch (Exception error) {
-                    request.onFailure(error);
+                    request.onFailure(ProviderExceptionSupport.createNonFatalOrPassthrough(error));
                 }
             }
         });
     }
 
     @Override
-    public void stop(final JmsResource resource, final AsyncResult request) throws IOException, JMSException {
+    public void stop(final JmsResource resource, final AsyncResult request) throws ProviderException {
         checkClosed();
         serializer.execute(new Runnable() {
 
@@ -248,15 +248,15 @@ public class MockProvider implements Provider {
 
                     stats.recordStopResourceCall(resource);
                     request.onSuccess();
-                } catch (Exception error) {
-                    request.onFailure(error);
+                } catch (Throwable error) {
+                    request.onFailure(ProviderExceptionSupport.createNonFatalOrPassthrough(error));
                 }
             }
         });
     }
 
     @Override
-    public void destroy(final JmsResource resource, final AsyncResult request) throws IOException, JMSException {
+    public void destroy(final JmsResource resource, final AsyncResult request) throws ProviderException {
         checkClosed();
         serializer.execute(new Runnable() {
 
@@ -271,15 +271,15 @@ public class MockProvider implements Provider {
                     }
 
                     request.onSuccess();
-                } catch (Exception error) {
-                    request.onFailure(error);
+                } catch (Throwable error) {
+                    request.onFailure(ProviderExceptionSupport.createNonFatalOrPassthrough(error));
                 }
             }
         });
     }
 
     @Override
-    public void send(final JmsOutboundMessageDispatch envelope, final AsyncResult request) throws IOException, JMSException {
+    public void send(final JmsOutboundMessageDispatch envelope, final AsyncResult request) throws ProviderException {
         checkClosed();
         serializer.execute(new Runnable() {
 
@@ -306,15 +306,15 @@ public class MockProvider implements Provider {
                             }
                         }
                     }
-                } catch (Exception error) {
-                    request.onFailure(error);
+                } catch (Throwable error) {
+                    request.onFailure(ProviderExceptionSupport.createNonFatalOrPassthrough(error));
                 }
             }
         });
     }
 
     @Override
-    public void acknowledge(final JmsSessionId sessionId, final ACK_TYPE ackType, final AsyncResult request) throws IOException, JMSException {
+    public void acknowledge(final JmsSessionId sessionId, final ACK_TYPE ackType, final AsyncResult request) throws ProviderException {
         checkClosed();
         serializer.execute(new Runnable() {
 
@@ -324,15 +324,15 @@ public class MockProvider implements Provider {
                     checkClosed();
                     stats.recoordSessionAcknowledgeCall();
                     request.onSuccess();
-                } catch (Exception error) {
-                    request.onFailure(error);
+                } catch (Throwable error) {
+                    request.onFailure(ProviderExceptionSupport.createNonFatalOrPassthrough(error));
                 }
             }
         });
     }
 
     @Override
-    public void acknowledge(final JmsInboundMessageDispatch envelope, final ACK_TYPE ackType, final AsyncResult request) throws IOException, JMSException {
+    public void acknowledge(final JmsInboundMessageDispatch envelope, final ACK_TYPE ackType, final AsyncResult request) throws ProviderException {
         checkClosed();
         serializer.execute(new Runnable() {
 
@@ -342,15 +342,15 @@ public class MockProvider implements Provider {
                     checkClosed();
                     stats.recoordAcknowledgeCall();
                     request.onSuccess();
-                } catch (Exception error) {
-                    request.onFailure(error);
+                } catch (Throwable error) {
+                    request.onFailure(ProviderExceptionSupport.createNonFatalOrPassthrough(error));
                 }
             }
         });
     }
 
     @Override
-    public void commit(final JmsTransactionInfo transactionInfo, final JmsTransactionInfo nextTransactionInfo, final AsyncResult request) throws IOException, JMSException {
+    public void commit(final JmsTransactionInfo transactionInfo, final JmsTransactionInfo nextTransactionInfo, final AsyncResult request) throws ProviderException {
         checkClosed();
         serializer.execute(new Runnable() {
 
@@ -360,15 +360,15 @@ public class MockProvider implements Provider {
                     checkClosed();
                     stats.recordCommitCall();
                     request.onSuccess();
-                } catch (Exception error) {
-                    request.onFailure(error);
+                } catch (Throwable error) {
+                    request.onFailure(ProviderExceptionSupport.createNonFatalOrPassthrough(error));
                 }
             }
         });
     }
 
     @Override
-    public void rollback(final JmsTransactionInfo transactionInfo, final JmsTransactionInfo nextTransactionInfo, final AsyncResult request) throws IOException, JMSException {
+    public void rollback(final JmsTransactionInfo transactionInfo, final JmsTransactionInfo nextTransactionInfo, final AsyncResult request) throws ProviderException {
         checkClosed();
         serializer.execute(new Runnable() {
 
@@ -378,15 +378,15 @@ public class MockProvider implements Provider {
                     checkClosed();
                     stats.recordRollbackCall();
                     request.onSuccess();
-                } catch (Exception error) {
-                    request.onFailure(error);
+                } catch (Throwable error) {
+                    request.onFailure(ProviderExceptionSupport.createNonFatalOrPassthrough(error));
                 }
             }
         });
     }
 
     @Override
-    public void recover(final JmsSessionId sessionId, final AsyncResult request) throws IOException {
+    public void recover(final JmsSessionId sessionId, final AsyncResult request) throws ProviderException {
         checkClosed();
         serializer.execute(new Runnable() {
 
@@ -396,15 +396,15 @@ public class MockProvider implements Provider {
                     checkClosed();
                     stats.recordRecoverCall();
                     request.onSuccess();
-                } catch (Exception error) {
-                    request.onFailure(error);
+                } catch (Throwable error) {
+                    request.onFailure(ProviderExceptionSupport.createNonFatalOrPassthrough(error));
                 }
             }
         });
     }
 
     @Override
-    public void unsubscribe(final String subscription, final AsyncResult request) throws IOException, JMSException {
+    public void unsubscribe(final String subscription, final AsyncResult request) throws ProviderException {
         checkClosed();
         serializer.execute(new Runnable() {
 
@@ -414,15 +414,15 @@ public class MockProvider implements Provider {
                     checkClosed();
                     stats.recordUnsubscribeCall();
                     request.onSuccess();
-                } catch (Exception error) {
-                    request.onFailure(error);
+                } catch (Throwable error) {
+                    request.onFailure(ProviderExceptionSupport.createNonFatalOrPassthrough(error));
                 }
             }
         });
     }
 
     @Override
-    public void pull(final JmsConsumerId consumerId, final long timeout, final AsyncResult request) throws IOException {
+    public void pull(final JmsConsumerId consumerId, final long timeout, final AsyncResult request) throws ProviderException {
         checkClosed();
         serializer.execute(new Runnable() {
 
@@ -432,8 +432,8 @@ public class MockProvider implements Provider {
                     checkClosed();
                     stats.recordPullCall();
                     request.onSuccess();
-                } catch (Exception error) {
-                    request.onFailure(error);
+                } catch (Throwable error) {
+                    request.onFailure(ProviderExceptionSupport.createNonFatalOrPassthrough(error));
                 }
             }
         });
@@ -447,7 +447,7 @@ public class MockProvider implements Provider {
             @Override
             public void run() {
                 if (!closed.get()) {
-                    listener.onConnectionFailure(new IOException("Connection lost"));
+                    listener.onConnectionFailure(new ProviderIOException("Connection lost"));
                 }
             }
         });
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/mock/MockRemotePeer.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/mock/MockRemotePeer.java
index f9ca714..552cee1 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/mock/MockRemotePeer.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/mock/MockRemotePeer.java
@@ -16,7 +16,6 @@
  */
 package org.apache.qpid.jms.provider.mock;
 
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
@@ -30,6 +29,7 @@ import javax.jms.Message;
 
 import org.apache.qpid.jms.message.JmsOutboundMessageDispatch;
 import org.apache.qpid.jms.meta.JmsResource;
+import org.apache.qpid.jms.provider.ProviderException;
 
 /**
  * Context shared between all MockProvider instances.
@@ -54,9 +54,9 @@ public class MockRemotePeer {
     private final Map<Destination, List<PendingCompletion>> pendingCompletions =
         new ConcurrentHashMap<Destination, List<PendingCompletion>>();
 
-    public void connect(MockProvider provider) throws IOException {
+    public void connect(MockProvider provider) throws ProviderException {
         if (offline) {
-            throw new IOException();
+            throw new ProviderException("Provider is offline");
         }
 
         if (provider != null) {
@@ -195,7 +195,7 @@ public class MockRemotePeer {
         }
     }
 
-    public void failAllPendingSends(Destination destination, Exception error) {
+    public void failAllPendingSends(Destination destination, ProviderException error) {
         if (pendingCompletions.containsKey(destination)) {
 
             for (List<PendingCompletion> pendingSends : pendingCompletions.values()) {
@@ -232,7 +232,7 @@ public class MockRemotePeer {
         }
     }
 
-    public void failPendingSend(Message message, Exception error) throws JMSException {
+    public void failPendingSend(Message message, ProviderException error) throws JMSException {
         List<PendingCompletion> pendingSends = pendingCompletions.get(message.getJMSDestination());
         Iterator<PendingCompletion> iterator = pendingSends.iterator();
         while (iterator.hasNext()) {
@@ -244,7 +244,7 @@ public class MockRemotePeer {
         }
     }
 
-    public void failPendingSend(JmsOutboundMessageDispatch envelope, Exception error) throws JMSException {
+    public void failPendingSend(JmsOutboundMessageDispatch envelope, ProviderException error) throws JMSException {
         List<PendingCompletion> pendingSends = pendingCompletions.get(envelope.getDestination());
         Iterator<PendingCompletion> iterator = pendingSends.iterator();
         while (iterator.hasNext()) {
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/session/JmsSessionFailedTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/session/JmsSessionFailedTest.java
index a903a81..c8d20fa 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/session/JmsSessionFailedTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/session/JmsSessionFailedTest.java
@@ -18,7 +18,6 @@ package org.apache.qpid.jms.session;
 
 import static org.junit.Assert.assertTrue;
 
-import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 
 import javax.jms.ExceptionListener;
@@ -27,6 +26,7 @@ import javax.jms.Queue;
 import javax.jms.Session;
 
 import org.apache.qpid.jms.JmsConnection;
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.jms.test.Wait;
 
 
@@ -50,7 +50,7 @@ public class JmsSessionFailedTest extends JmsSessionClosedTest {
         sender = session.createProducer(destination);
         receiver = session.createConsumer(destination);
         connection.start();
-        providerListener.onConnectionFailure(new IOException());
+        providerListener.onConnectionFailure(new ProviderException("Something went wrong"));
 
         final JmsConnection jmsConnection = connection;
         assertTrue(Wait.waitFor(new Wait.Condition() {
diff --git a/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryAgent.java b/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryAgent.java
index f0c406f..b6d1823 100644
--- a/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryAgent.java
+++ b/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryAgent.java
@@ -16,9 +16,10 @@
  */
 package org.apache.qpid.jms.provider.discovery;
 
-import java.io.IOException;
 import java.util.concurrent.ScheduledExecutorService;
 
+import org.apache.qpid.jms.provider.ProviderException;
+
 /**
  * Interface for all agents used to detect instances of remote peers on the network.
  */
@@ -54,10 +55,10 @@ public interface DiscoveryAgent {
     /**
      * Starts the agent after which new remote peers can start to be found.
      *
-     * @throws IOException if an IO error occurs while starting the agent.
+     * @throws ProviderException if an IO error occurs while starting the agent.
      * @throws IllegalStateException if the agent is not properly configured.
      */
-    void start() throws IOException, IllegalStateException;
+    void start() throws ProviderException, IllegalStateException;
 
     /**
      * Stops the agent after which no new remote peers will be found.  This
diff --git a/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryProvider.java b/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryProvider.java
index 687215a..81013b0 100644
--- a/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryProvider.java
+++ b/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/DiscoveryProvider.java
@@ -16,7 +16,6 @@
  */
 package org.apache.qpid.jms.provider.discovery;
 
-import java.io.IOException;
 import java.net.URI;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -24,6 +23,7 @@ import java.util.List;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.jms.provider.ProviderWrapper;
 import org.apache.qpid.jms.provider.failover.FailoverProvider;
 import org.apache.qpid.jms.util.QpidJMSThreadFactory;
@@ -62,7 +62,7 @@ public class DiscoveryProvider extends ProviderWrapper<FailoverProvider> impleme
     }
 
     @Override
-    public void start() throws IOException, IllegalStateException {
+    public void start() throws ProviderException, IllegalStateException {
         if (discoveryAgents.isEmpty()) {
             throw new IllegalStateException("No DiscoveryAgent configured.");
         }
diff --git a/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/file/FileWatcherDiscoveryAgent.java b/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/file/FileWatcherDiscoveryAgent.java
index 4672ff5..62755b1 100644
--- a/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/file/FileWatcherDiscoveryAgent.java
+++ b/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/file/FileWatcherDiscoveryAgent.java
@@ -34,10 +34,11 @@ import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-import org.apache.qpid.jms.util.URISupport;
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.jms.provider.discovery.DiscoveryAgent;
 import org.apache.qpid.jms.provider.discovery.DiscoveryListener;
 import org.apache.qpid.jms.util.ThreadPoolUtils;
+import org.apache.qpid.jms.util.URISupport;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -85,7 +86,7 @@ public class FileWatcherDiscoveryAgent implements DiscoveryAgent {
     }
 
     @Override
-    public void start() throws IOException, IllegalStateException {
+    public void start() throws ProviderException, IllegalStateException {
         if (listener == null) {
             throw new IllegalStateException("No DiscoveryListener configured.");
         }
diff --git a/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/multicast/MulticastDiscoveryAgent.java b/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/multicast/MulticastDiscoveryAgent.java
index 460eb3b..4390718 100644
--- a/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/multicast/MulticastDiscoveryAgent.java
+++ b/qpid-jms-discovery/src/main/java/org/apache/qpid/jms/provider/discovery/multicast/MulticastDiscoveryAgent.java
@@ -37,9 +37,11 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+import org.apache.qpid.jms.provider.ProviderException;
 import org.apache.qpid.jms.provider.discovery.DiscoveryAgent;
 import org.apache.qpid.jms.provider.discovery.DiscoveryListener;
 import org.apache.qpid.jms.provider.discovery.multicast.DiscoveryEvent.EventType;
+import org.apache.qpid.jms.provider.exceptions.ProviderExceptionSupport;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -107,7 +109,7 @@ public class MulticastDiscoveryAgent implements DiscoveryAgent, Runnable {
     }
 
     @Override
-    public void start() throws IOException, IllegalStateException {
+    public void start() throws ProviderException, IllegalStateException {
         if (listener == null) {
             throw new IllegalStateException("No DiscoveryListener configured.");
         }
@@ -115,7 +117,7 @@ public class MulticastDiscoveryAgent implements DiscoveryAgent, Runnable {
         if (started.compareAndSet(false, true)) {
 
             if (group == null || group.length() == 0) {
-                throw new IOException("You must specify a group to discover");
+                throw new ProviderException("You must specify a group to discover");
             }
 
             if (discoveryURI == null) {
@@ -150,28 +152,32 @@ public class MulticastDiscoveryAgent implements DiscoveryAgent, Runnable {
             LOG.trace("mcast - network interface = {}", mcNetworkInterface);
             LOG.trace("mcast - join network interface = {}", mcJoinNetworkInterface);
 
-            this.inetAddress = InetAddress.getByName(myHost);
-            this.sockAddress = new InetSocketAddress(this.inetAddress, myPort);
-            mcast = new MulticastSocket(myPort);
-            mcast.setLoopbackMode(loopBackMode);
-            mcast.setTimeToLive(getTimeToLive());
-            if (mcJoinNetworkInterface != null) {
-                mcast.joinGroup(sockAddress, NetworkInterface.getByName(mcJoinNetworkInterface));
-            } else {
-                if (mcNetworkInterface != null) {
-                    mcast.setNetworkInterface(NetworkInterface.getByName(mcNetworkInterface));
+            try {
+                this.inetAddress = InetAddress.getByName(myHost);
+                this.sockAddress = new InetSocketAddress(this.inetAddress, myPort);
+                mcast = new MulticastSocket(myPort);
+                mcast.setLoopbackMode(loopBackMode);
+                mcast.setTimeToLive(getTimeToLive());
+                if (mcJoinNetworkInterface != null) {
+                    mcast.joinGroup(sockAddress, NetworkInterface.getByName(mcJoinNetworkInterface));
                 } else {
-                    trySetNetworkInterface(mcast);
+                    if (mcNetworkInterface != null) {
+                        mcast.setNetworkInterface(NetworkInterface.getByName(mcNetworkInterface));
+                    } else {
+                        trySetNetworkInterface(mcast);
+                    }
+                    mcast.joinGroup(inetAddress);
+                }
+                mcast.setSoTimeout((int) keepAliveInterval);
+                if (mcInterface != null) {
+                    mcast.setInterface(InetAddress.getByName(mcInterface));
                 }
-                mcast.joinGroup(inetAddress);
-            }
-            mcast.setSoTimeout((int) keepAliveInterval);
-            if (mcInterface != null) {
-                mcast.setInterface(InetAddress.getByName(mcInterface));
-            }
 
-            if (mcNetworkInterface != null) {
-                mcast.setNetworkInterface(NetworkInterface.getByName(mcNetworkInterface));
+                if (mcNetworkInterface != null) {
+                    mcast.setNetworkInterface(NetworkInterface.getByName(mcNetworkInterface));
+                }
+            } catch (IOException e) {
+                throw ProviderExceptionSupport.createOrPassthroughFatal(e);
             }
 
             runner = new Thread(this);
diff --git a/qpid-jms-interop-tests/qpid-jms-activemq-tests/src/test/java/org/apache/qpid/jms/failover/JmsTxProducerFailoverTest.java b/qpid-jms-interop-tests/qpid-jms-activemq-tests/src/test/java/org/apache/qpid/jms/failover/JmsTxProducerFailoverTest.java
index d5aaf80..d4ef70f 100644
--- a/qpid-jms-interop-tests/qpid-jms-activemq-tests/src/test/java/org/apache/qpid/jms/failover/JmsTxProducerFailoverTest.java
+++ b/qpid-jms-interop-tests/qpid-jms-activemq-tests/src/test/java/org/apache/qpid/jms/failover/JmsTxProducerFailoverTest.java
@@ -55,6 +55,7 @@ public class JmsTxProducerFailoverTest extends AmqpTestSupport {
      * before then should allow Commit to work as expected.
      */
     @Test(timeout=60000)
+    @Repeat(repetitions = 1)
     public void testTxProducerSendAfterFailoverCommits() throws Exception {
         URI brokerURI = new URI(getAmqpFailoverURI());
 
@@ -112,6 +113,7 @@ public class JmsTxProducerFailoverTest extends AmqpTestSupport {
      * will fail and the message are not present on the broker.
      */
     @Test(timeout=60000)
+    @Repeat(repetitions = 1)
     public void testTxProducerSendsThenFailoverCommitFails() throws Exception {
         URI brokerURI = new URI(getAmqpFailoverURI());
 
@@ -151,6 +153,7 @@ public class JmsTxProducerFailoverTest extends AmqpTestSupport {
     }
 
     @Test(timeout=60000)
+    @Repeat(repetitions = 1)
     public void testTxProducerRollbackAfterFailoverGetsNoErrors() throws Exception {
         URI brokerURI = new URI(getAmqpFailoverURI());
 
@@ -186,10 +189,13 @@ public class JmsTxProducerFailoverTest extends AmqpTestSupport {
             session.rollback();
             LOG.info("Transacted rollback after failover ok");
         } catch (JMSException ex) {
+            LOG.warn("Error on rollback not expected: ", ex);
             fail("Session rollback should not have failed: " + ex.getMessage());
         }
 
         assertEquals(0, proxy.getQueueSize());
+
+        LOG.info("Test {} compelted without error as expected:", getTestName());
     }
 
     /*


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org