You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by or...@apache.org on 2019/09/29 22:53:45 UTC

[qpid-broker-j] 01/02: QPID-8306: [Broker-J] Add operation to update port TLS support

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

orudyy pushed a commit to branch 7.1.x
in repository https://gitbox.apache.org/repos/asf/qpid-broker-j.git

commit 46c90b50f50adea6d5d037e5cb9e8b16832cf5e6
Author: Alex Rudyy <or...@apache.org>
AuthorDate: Tue Apr 30 13:26:24 2019 +0100

    QPID-8306: [Broker-J] Add operation to update port TLS support
    
    This closes #31
    
    (cherry picked from commit ce1cb00c795a70ed33f8717250f0ab61838d23f5)
---
 .../java/org/apache/qpid/server/model/Port.java    |  13 +
 .../qpid/server/model/port/AbstractPort.java       |  18 +
 .../apache/qpid/server/model/port/AmqpPort.java    |   4 -
 .../qpid/server/model/port/AmqpPortImpl.java       |  16 +-
 .../qpid/server/model/port/HttpPortImpl.java       |  37 +-
 .../apache/qpid/server/model/port/PortManager.java |   6 +
 .../qpid/server/transport/AcceptingTransport.java  |   2 +
 .../transport/NonBlockingNetworkTransport.java     |   7 +-
 .../qpid/server/transport/TCPandSSLTransport.java  |  35 +-
 .../server/management/plugin/HttpManagement.java   | 114 ++++--
 .../main/java/resources/js/qpid/management/Port.js |  28 ++
 .../src/main/java/resources/showPort.html          |   1 +
 .../transport/websocket/WebSocketProvider.java     |  81 +++--
 .../qpid/tests/http/endtoend/port/PortTest.java    | 400 +++++++++++++++++++++
 .../apache/qpid/systests/ConnectionBuilder.java    |   2 +
 .../systests/QpidJmsClient0xConnectionBuilder.java |   8 +-
 .../systests/QpidJmsClientConnectionBuilder.java   |  14 +-
 17 files changed, 705 insertions(+), 81 deletions(-)

diff --git a/broker-core/src/main/java/org/apache/qpid/server/model/Port.java b/broker-core/src/main/java/org/apache/qpid/server/model/Port.java
index 3850afd..510d4d5 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/model/Port.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/model/Port.java
@@ -24,6 +24,8 @@ import java.util.Collection;
 import java.util.List;
 import java.util.Set;
 
+import javax.net.ssl.SSLContext;
+
 import com.google.common.util.concurrent.ListenableFuture;
 
 import org.apache.qpid.server.configuration.CommonProperties;
@@ -105,6 +107,8 @@ public interface Port<X extends Port<X>> extends ConfiguredObject<X>
                                     + "hostname.  If null or * then bind to all interfaces.")
     String getBindingAddress();
 
+    SSLContext getSSLContext();
+
     @ManagedAttribute
     boolean getNeedClientAuth();
 
@@ -133,5 +137,14 @@ public interface Port<X extends Port<X>> extends ConfiguredObject<X>
 
     SubjectCreator getSubjectCreator(final boolean secure, String host);
 
+    @DerivedAttribute(description = "Indicates whether TLS transport support is created.")
+    boolean isTlsSupported();
 
+    @ManagedOperation(description =
+            "Updates port TLS support without affecting existing connections."
+            + " The TLS changes are applied to new connections only."
+            + " Returns true if TLS support is successfully updated.",
+            nonModifying = true,
+            changesConfiguredObjectState = false)
+    boolean updateTLS();
 }
diff --git a/broker-core/src/main/java/org/apache/qpid/server/model/port/AbstractPort.java b/broker-core/src/main/java/org/apache/qpid/server/model/port/AbstractPort.java
index 45efb41..a5fb3d2 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/model/port/AbstractPort.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/model/port/AbstractPort.java
@@ -504,4 +504,22 @@ public abstract class AbstractPort<X extends AbstractPort<X>> extends AbstractCo
         return getCategoryClass().getSimpleName() + "[id=" + getId() + ", name=" + getName() + ", type=" + getType() +  ", port=" + getPort() + "]";
     }
 
+    @Override
+    public boolean isTlsSupported()
+    {
+        return getSSLContext() != null;
+    }
+
+    @Override
+    public boolean updateTLS()
+    {
+        if (isTlsSupported())
+        {
+            return updateSSLContext();
+        }
+        return false;
+    }
+
+    protected abstract boolean updateSSLContext();
+
 }
diff --git a/broker-core/src/main/java/org/apache/qpid/server/model/port/AmqpPort.java b/broker-core/src/main/java/org/apache/qpid/server/model/port/AmqpPort.java
index c1144d1..f88b99a 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/model/port/AmqpPort.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/model/port/AmqpPort.java
@@ -24,8 +24,6 @@ import java.net.SocketAddress;
 import java.util.List;
 import java.util.Set;
 
-import javax.net.ssl.SSLContext;
-
 import org.apache.qpid.server.model.DerivedAttribute;
 import org.apache.qpid.server.model.ManagedAttribute;
 import org.apache.qpid.server.model.ManagedContextDefault;
@@ -132,8 +130,6 @@ public interface AmqpPort<X extends AmqpPort<X>> extends Port<X>
             description = "The connection property enrichers to apply to connections created on this port.")
     String DEFAULT_CONNECTION_PROTOCOL_ENRICHERS = "[ \"STANDARD\" ] ";
 
-    SSLContext getSSLContext();
-
     @ManagedAttribute( defaultValue = AmqpPort.DEFAULT_AMQP_TCP_NO_DELAY )
     boolean isTcpNoDelay();
 
diff --git a/broker-core/src/main/java/org/apache/qpid/server/model/port/AmqpPortImpl.java b/broker-core/src/main/java/org/apache/qpid/server/model/port/AmqpPortImpl.java
index 4e4fc64..88fc19c 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/model/port/AmqpPortImpl.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/model/port/AmqpPortImpl.java
@@ -98,8 +98,8 @@ public class AmqpPortImpl extends AbstractPort<AmqpPortImpl> implements AmqpPort
     private final Container<?> _container;
     private final AtomicBoolean _closingOrDeleting = new AtomicBoolean();
 
-    private AcceptingTransport _transport;
-    private SSLContext _sslContext;
+    private volatile AcceptingTransport _transport;
+    private volatile SSLContext _sslContext;
     private volatile int _connectionWarnCount;
     private volatile long _protocolHandshakeTimeout;
     private volatile int _boundPort = -1;
@@ -275,6 +275,18 @@ public class AmqpPortImpl extends AbstractPort<AmqpPortImpl> implements AmqpPort
     }
 
     @Override
+    protected boolean updateSSLContext()
+    {
+        final Set<Transport> transports = getTransports();
+        if (transports.contains(Transport.SSL) || transports.contains(Transport.WSS))
+        {
+            _sslContext = createSslContext();
+            return _transport.updatesSSLContext();
+        }
+        return false;
+    }
+
+    @Override
     protected ListenableFuture<Void> beforeClose()
     {
         _closingOrDeleting.set(true);
diff --git a/broker-core/src/main/java/org/apache/qpid/server/model/port/HttpPortImpl.java b/broker-core/src/main/java/org/apache/qpid/server/model/port/HttpPortImpl.java
index 21b4c26..a3d714d 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/model/port/HttpPortImpl.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/model/port/HttpPortImpl.java
@@ -23,12 +23,15 @@ package org.apache.qpid.server.model.port;
 import java.util.Map;
 import java.util.Set;
 
+import javax.net.ssl.SSLContext;
+
 import org.apache.qpid.server.configuration.IllegalConfigurationException;
 import org.apache.qpid.server.model.ConfiguredObject;
 import org.apache.qpid.server.model.Container;
 import org.apache.qpid.server.model.ManagedAttributeField;
 import org.apache.qpid.server.model.ManagedObjectFactoryConstructor;
 import org.apache.qpid.server.model.State;
+import org.apache.qpid.server.model.Transport;
 
 public class HttpPortImpl extends AbstractPort<HttpPortImpl> implements HttpPort<HttpPortImpl>
 {
@@ -66,7 +69,8 @@ public class HttpPortImpl extends AbstractPort<HttpPortImpl> implements HttpPort
     @Override
     public int getBoundPort()
     {
-        return _portManager == null ? -1 : _portManager.getBoundPort(this);
+        final PortManager portManager = getPortManager();
+        return portManager == null ? -1 : portManager.getBoundPort(this);
     }
 
     @Override
@@ -108,13 +112,15 @@ public class HttpPortImpl extends AbstractPort<HttpPortImpl> implements HttpPort
     @Override
     public int getNumberOfAcceptors()
     {
-        return _portManager == null ? 0 : _portManager.getNumberOfAcceptors(this) ;
+        final PortManager portManager = getPortManager();
+        return portManager == null ? 0 : portManager.getNumberOfAcceptors(this) ;
     }
 
     @Override
     public int getNumberOfSelectors()
     {
-        return _portManager == null ? 0 : _portManager.getNumberOfSelectors(this) ;
+        final PortManager portManager = getPortManager();
+        return portManager == null ? 0 : portManager.getNumberOfSelectors(this) ;
     }
 
     @Override
@@ -145,7 +151,7 @@ public class HttpPortImpl extends AbstractPort<HttpPortImpl> implements HttpPort
     @Override
     protected State onActivate()
     {
-        if(_portManager != null)
+        if(getPortManager() != null)
         {
             return super.onActivate();
         }
@@ -156,6 +162,29 @@ public class HttpPortImpl extends AbstractPort<HttpPortImpl> implements HttpPort
     }
 
     @Override
+    public SSLContext getSSLContext()
+    {
+        final PortManager portManager = getPortManager();
+        return portManager == null ? null : portManager.getSSLContext(this);
+    }
+
+    @Override
+    protected boolean updateSSLContext()
+    {
+        if (getTransports().contains(Transport.SSL))
+        {
+            final PortManager portManager = getPortManager();
+            return portManager != null && portManager.updateSSLContext(this);
+        }
+        return false;
+    }
+
+    private PortManager getPortManager()
+    {
+        return _portManager;
+    }
+
+    @Override
     public void onValidate()
     {
         super.onValidate();
diff --git a/broker-core/src/main/java/org/apache/qpid/server/model/port/PortManager.java b/broker-core/src/main/java/org/apache/qpid/server/model/port/PortManager.java
index b535a8b..3a06462 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/model/port/PortManager.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/model/port/PortManager.java
@@ -20,6 +20,8 @@
  */
 package org.apache.qpid.server.model.port;
 
+import javax.net.ssl.SSLContext;
+
 public interface PortManager
 {
     int getBoundPort(HttpPort httpPort);
@@ -27,4 +29,8 @@ public interface PortManager
     int getNumberOfAcceptors(HttpPort httpPort);
 
     int getNumberOfSelectors(HttpPort httpPort);
+
+    SSLContext getSSLContext(HttpPort httpPort);
+
+    boolean updateSSLContext(HttpPort httpPort);
 }
diff --git a/broker-core/src/main/java/org/apache/qpid/server/transport/AcceptingTransport.java b/broker-core/src/main/java/org/apache/qpid/server/transport/AcceptingTransport.java
index e8b7fda..bfefac1 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/transport/AcceptingTransport.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/transport/AcceptingTransport.java
@@ -27,4 +27,6 @@ public interface AcceptingTransport
     void close();
 
     int getAcceptingPort();
+
+    boolean updatesSSLContext();
 }
diff --git a/broker-core/src/main/java/org/apache/qpid/server/transport/NonBlockingNetworkTransport.java b/broker-core/src/main/java/org/apache/qpid/server/transport/NonBlockingNetworkTransport.java
index 02767f4..76f5977 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/transport/NonBlockingNetworkTransport.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/transport/NonBlockingNetworkTransport.java
@@ -42,7 +42,7 @@ public class NonBlockingNetworkTransport
 
     private static final Logger LOGGER = LoggerFactory.getLogger(NonBlockingNetworkTransport.class);
 
-    private final Set<TransportEncryption> _encryptionSet;
+    private volatile Set<TransportEncryption> _encryptionSet;
     private final MultiVersionProtocolEngineFactory _factory;
     private final ServerSocketChannel _serverSocket;
     private final NetworkConnectionScheduler _scheduler;
@@ -205,4 +205,9 @@ public class NonBlockingNetworkTransport
             }
         }
     }
+
+    void setEncryptionSet(final Set<TransportEncryption> encryptionSet)
+    {
+        _encryptionSet = encryptionSet;
+    }
 }
diff --git a/broker-core/src/main/java/org/apache/qpid/server/transport/TCPandSSLTransport.java b/broker-core/src/main/java/org/apache/qpid/server/transport/TCPandSSLTransport.java
index f52d50d..477d784 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/transport/TCPandSSLTransport.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/transport/TCPandSSLTransport.java
@@ -32,7 +32,7 @@ import org.apache.qpid.server.transport.network.TransportEncryption;
 class TCPandSSLTransport implements AcceptingTransport
 {
     private NonBlockingNetworkTransport _networkTransport;
-    private Set<Transport> _transports;
+    private volatile Set<Transport> _transports;
     private AmqpPort<?> _port;
     private Set<Protocol> _supported;
     private Protocol _defaultSupportedProtocolReply;
@@ -60,15 +60,7 @@ class TCPandSSLTransport implements AcceptingTransport
                         _port,
                         _transports.contains(Transport.TCP) ? Transport.TCP : Transport.SSL);
 
-        EnumSet<TransportEncryption> encryptionSet = EnumSet.noneOf(TransportEncryption.class);
-        if(_transports.contains(Transport.TCP))
-        {
-            encryptionSet.add(TransportEncryption.NONE);
-        }
-        if(_transports.contains(Transport.SSL))
-        {
-            encryptionSet.add(TransportEncryption.TLS);
-        }
+        EnumSet<TransportEncryption> encryptionSet = buildEncryptionSet(_transports);
 
         long threadPoolKeepAliveTimeout = _port.getContextValue(Long.class, AmqpPort.PORT_AMQP_THREAD_POOL_KEEP_ALIVE_TIMEOUT);
 
@@ -80,6 +72,20 @@ class TCPandSSLTransport implements AcceptingTransport
         _networkTransport.start();
     }
 
+    private EnumSet<TransportEncryption> buildEncryptionSet(final Set<Transport> transports)
+    {
+        EnumSet<TransportEncryption> encryptionSet = EnumSet.noneOf(TransportEncryption.class);
+        if(transports.contains(Transport.TCP))
+        {
+            encryptionSet.add(TransportEncryption.NONE);
+        }
+        if(transports.contains(Transport.SSL))
+        {
+            encryptionSet.add(TransportEncryption.TLS);
+        }
+        return encryptionSet;
+    }
+
     @Override
     public int getAcceptingPort()
     {
@@ -88,6 +94,15 @@ class TCPandSSLTransport implements AcceptingTransport
     }
 
     @Override
+    public boolean updatesSSLContext()
+    {
+        Set<Transport> transports = _port.getTransports();
+        _transports = transports;
+        _networkTransport.setEncryptionSet(buildEncryptionSet(transports));
+        return true;
+    }
+
+    @Override
     public void close()
     {
         if (_networkTransport != null)
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java
index f9d7517..b97b6ca 100644
--- a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java
@@ -182,6 +182,7 @@ public class HttpManagement extends AbstractPluginAdapter<HttpManagement> implem
     private boolean _compressResponses;
 
     private final Map<HttpPort<?>, ServerConnector> _portConnectorMap = new ConcurrentHashMap<>();
+    private final Map<HttpPort<?>, SslContextFactory> _sslContextFactoryMap = new ConcurrentHashMap<>();
     private final BrokerChangeListener _brokerChangeListener = new BrokerChangeListener();
 
     private volatile boolean _serveUncompressedDojo;
@@ -460,6 +461,46 @@ public class HttpManagement extends AbstractPluginAdapter<HttpManagement> implem
         }
     }
 
+    @Override
+    public SSLContext getSSLContext(final HttpPort httpPort)
+    {
+        final SslContextFactory sslContextFactory = getSslContextFactory(httpPort);
+        if ( sslContextFactory != null)
+        {
+            return sslContextFactory.getSslContext();
+        }
+        return null;
+    }
+
+    @Override
+    public boolean updateSSLContext(final HttpPort httpPort)
+    {
+        final SslContextFactory sslContextFactory = getSslContextFactory(httpPort);
+        if ( sslContextFactory != null)
+        {
+            try
+            {
+                final SSLContext sslContext = createSslContext(httpPort);
+                sslContextFactory.reload(f -> {
+                    f.setSslContext(sslContext);
+                    f.setNeedClientAuth(httpPort.getNeedClientAuth());
+                    f.setWantClientAuth(httpPort.getWantClientAuth());
+                });
+                return true;
+            }
+            catch (Exception e)
+            {
+                throw new IllegalConfigurationException("Unexpected exception on reload of ssl context factory", e);
+            }
+        }
+        return false;
+    }
+
+    private SslContextFactory getSslContextFactory(final HttpPort httpPort)
+    {
+        return _sslContextFactoryMap.get(httpPort);
+    }
+
     private ServerConnector createConnector(final HttpPort<?> port, final Server server)
     {
         port.setPortManager(this);
@@ -482,13 +523,14 @@ public class HttpManagement extends AbstractPluginAdapter<HttpManagement> implem
 
         ConnectionFactory[] connectionFactories;
         Collection<Transport> transports = port.getTransports();
+        SslContextFactory sslContextFactory = null;
         if (!transports.contains(Transport.SSL))
         {
             connectionFactories = new ConnectionFactory[]{httpConnectionFactory};
         }
         else if (transports.contains(Transport.SSL))
         {
-            SslContextFactory sslContextFactory = getSslContextFactory(port);
+            sslContextFactory = createSslContextFactory(port);
             ConnectionFactory sslConnectionFactory;
             if (port.getTransports().contains(Transport.TCP))
             {
@@ -524,6 +566,7 @@ public class HttpManagement extends AbstractPluginAdapter<HttpManagement> implem
                 }
                 catch (BindException e)
                 {
+                    _sslContextFactoryMap.remove(port);
                     InetSocketAddress addr = getHost() == null ? new InetSocketAddress(getPort())
                             : new InetSocketAddress(getHost(), getPort());
                     throw new PortBindFailureException(addr);
@@ -590,40 +633,16 @@ public class HttpManagement extends AbstractPluginAdapter<HttpManagement> implem
                     acceptors,
                     selectors));
         }
+        if (sslContextFactory != null)
+        {
+            _sslContextFactoryMap.put(port, sslContextFactory);
+        }
 
         return connector;
     }
 
-    private SslContextFactory getSslContextFactory(final HttpPort<?> port)
+    private SslContextFactory createSslContextFactory(final HttpPort<?> port)
     {
-        KeyStore keyStore = port.getKeyStore();
-        if (keyStore == null)
-        {
-            throw new IllegalConfigurationException(
-                    "Key store is not configured. Cannot start management on HTTPS port without keystore");
-        }
-
-        boolean needClientCert = port.getNeedClientAuth() || port.getWantClientAuth();
-        Collection<TrustStore> trustStores = port.getTrustStores();
-
-        if (needClientCert && trustStores.isEmpty())
-        {
-            throw new IllegalConfigurationException(String.format(
-                    "Client certificate authentication is enabled on HTTPS port '%s' but no trust store defined",
-                    this.getName()));
-        }
-
-        SSLContext sslContext = SSLUtil.createSslContext(keyStore, trustStores, port.getName());
-        SSLSessionContext serverSessionContext = sslContext.getServerSessionContext();
-        if (port.getTLSSessionCacheSize() > 0)
-        {
-            serverSessionContext.setSessionCacheSize(port.getTLSSessionCacheSize());
-        }
-        if (port.getTLSSessionTimeout() > 0)
-        {
-            serverSessionContext.setSessionTimeout(port.getTLSSessionTimeout());
-        }
-
         SslContextFactory factory = new SslContextFactory()
         {
             @Override
@@ -645,7 +664,7 @@ public class HttpManagement extends AbstractPluginAdapter<HttpManagement> implem
                                                   port.getTlsProtocolBlackList());
             }
         };
-        factory.setSslContext(sslContext);
+        factory.setSslContext(createSslContext(port));
         if (port.getNeedClientAuth())
         {
             factory.setNeedClientAuth(true);
@@ -657,6 +676,38 @@ public class HttpManagement extends AbstractPluginAdapter<HttpManagement> implem
         return factory;
     }
 
+    private SSLContext createSslContext(final HttpPort<?> port)
+    {
+        KeyStore keyStore = port.getKeyStore();
+        if (keyStore == null)
+        {
+            throw new IllegalConfigurationException(
+                    "Key store is not configured. Cannot start management on HTTPS port without keystore");
+        }
+
+        final boolean needClientCert = port.getNeedClientAuth() || port.getWantClientAuth();
+        final Collection<TrustStore> trustStores = port.getTrustStores();
+
+        if (needClientCert && trustStores.isEmpty())
+        {
+            throw new IllegalConfigurationException(String.format(
+                    "Client certificate authentication is enabled on HTTPS port '%s' but no trust store defined",
+                    this.getName()));
+        }
+
+        final SSLContext sslContext = SSLUtil.createSslContext(port.getKeyStore(), trustStores, port.getName());
+        final SSLSessionContext serverSessionContext = sslContext.getServerSessionContext();
+        if (port.getTLSSessionCacheSize() > 0)
+        {
+            serverSessionContext.setSessionCacheSize(port.getTLSSessionCacheSize());
+        }
+        if (port.getTLSSessionTimeout() > 0)
+        {
+            serverSessionContext.setSessionTimeout(port.getTLSSessionTimeout());
+        }
+        return sslContext;
+    }
+
     private void addRestServlet(final ServletContextHandler root)
     {
         final Map<String, ManagementControllerFactory> factories = ManagementControllerFactory.loadFactories();
@@ -935,6 +986,7 @@ public class HttpManagement extends AbstractPluginAdapter<HttpManagement> implem
                 Server server = _server;
                 if (server != null)
                 {
+                    _sslContextFactoryMap.remove(port);
                     final ServerConnector connector = _portConnectorMap.remove(port);
                     if (connector != null)
                     {
diff --git a/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Port.js b/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Port.js
index 153c3f7..44c6675 100644
--- a/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Port.js
+++ b/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Port.js
@@ -72,6 +72,11 @@ define(["dojo/dom",
                         that.showEditDialog();
                     });
 
+                    that.updateTLSButton = registry.byNode(query(".updateTLSButton", contentPane.containerNode)[0]);
+                    that.updateTLSButton.on("click", function (evt) {
+                        that.updateTLS();
+                    });
+
                     that.portUpdater.update(function ()
                     {
                         updater.add(that.portUpdater);
@@ -116,6 +121,28 @@ define(["dojo/dom",
                 }, util.xhrErrorHandler);
         };
 
+        Port.prototype.updateTLS = function ()
+        {
+            if (confirm("Are you sure you want to update TLS?"))
+            {
+                this.updateTLSButton.set("disabled", true);
+                var that = this;
+                this.management.update({parent: this.modelObj, type: this.modelObj.type, name: "updateTLS"}, {})
+                    .then(function (data)
+                    {
+                        that.updateTLSButton.set("disabled", false);
+                        if (data)
+                        {
+                            alert("TLS was successfully updated.");
+                        }
+                        else
+                        {
+                            alert("TLS was not updated.");
+                        }
+                    });
+            }
+        };
+
         function PortUpdater(portTab)
         {
             var that = this;
@@ -197,6 +224,7 @@ define(["dojo/dom",
                 : "";
             this.protocolsValue.innerHTML = printArray("protocols", this.portData);
             this.transportsValue.innerHTML = printArray("transports", this.portData);
+            this.tabObject.updateTLSButton.set("disabled", !this.portData.tlsSupported);
             this.bindingAddressValue.innerHTML =
                 this.portData["bindingAddress"] ? entities.encode(String(this.portData["bindingAddress"])) : "";
             this.maxOpenConnectionsValue.innerHTML =
diff --git a/broker-plugins/management-http/src/main/java/resources/showPort.html b/broker-plugins/management-http/src/main/java/resources/showPort.html
index 883f3f9..6543080 100644
--- a/broker-plugins/management-http/src/main/java/resources/showPort.html
+++ b/broker-plugins/management-http/src/main/java/resources/showPort.html
@@ -113,6 +113,7 @@
         <div class="clear"></div>
     </div>
     <div class="dijitDialogPaneActionBar">
+        <button data-dojo-type="dijit.form.Button" class="updateTLSButton" type="button">Update TLS</button>
         <button data-dojo-type="dijit.form.Button" class="editPortButton" type="button">Edit</button>
         <button data-dojo-type="dijit.form.Button" class="deletePortButton" type="button">Delete</button>
     </div>
diff --git a/broker-plugins/websocket/src/main/java/org/apache/qpid/server/transport/websocket/WebSocketProvider.java b/broker-plugins/websocket/src/main/java/org/apache/qpid/server/transport/websocket/WebSocketProvider.java
index 8394ca8..2d4e494 100644
--- a/broker-plugins/websocket/src/main/java/org/apache/qpid/server/transport/websocket/WebSocketProvider.java
+++ b/broker-plugins/websocket/src/main/java/org/apache/qpid/server/transport/websocket/WebSocketProvider.java
@@ -65,6 +65,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import org.apache.qpid.server.bytebuffer.QpidByteBuffer;
+import org.apache.qpid.server.configuration.IllegalConfigurationException;
 import org.apache.qpid.server.model.Broker;
 import org.apache.qpid.server.model.Protocol;
 import org.apache.qpid.server.model.Transport;
@@ -87,7 +88,7 @@ class WebSocketProvider implements AcceptingTransport
     private static final String AMQP_WEBSOCKET_SUBPROTOCOL = "amqp";
 
     private final Transport _transport;
-    private final SSLContext _sslContext;
+    private final SslContextFactory _sslContextFactory;
     private final AmqpPort<?> _port;
     private final Broker<?> _broker;
     private final Set<Protocol> _supported;
@@ -108,7 +109,7 @@ class WebSocketProvider implements AcceptingTransport
                       final Protocol defaultSupportedProtocolReply)
     {
         _transport = transport;
-        _sslContext = sslContext;
+        _sslContextFactory = transport == Transport.WSS ? createSslContextFactory(port) : null;
         _port = port;
         _broker = ((Broker<?>) port.getParent());
         _supported = supported;
@@ -142,29 +143,7 @@ class WebSocketProvider implements AcceptingTransport
         }
         else if (_transport == Transport.WSS)
         {
-            SslContextFactory sslContextFactory = new SslContextFactory()
-                                        {
-                                            @Override
-                                            public void customize(final SSLEngine sslEngine)
-                                            {
-                                                super.customize(sslEngine);
-                                                SSLUtil.updateEnabledCipherSuites(sslEngine, _port.getTlsCipherSuiteWhiteList(), _port.getTlsCipherSuiteBlackList());
-                                                SSLUtil.updateEnabledTlsProtocols(sslEngine, _port.getTlsProtocolWhiteList(), _port.getTlsProtocolBlackList());
-
-                                                if(_port.getTlsCipherSuiteWhiteList() != null
-                                                   && !_port.getTlsCipherSuiteWhiteList().isEmpty())
-                                                {
-                                                    SSLParameters sslParameters = sslEngine.getSSLParameters();
-                                                    sslParameters.setUseCipherSuitesOrder(true);
-                                                    sslEngine.setSSLParameters(sslParameters);
-                                                }
-                                            }
-                                        };
-            sslContextFactory.setSslContext(_sslContext);
-
-            sslContextFactory.setNeedClientAuth(_port.getNeedClientAuth());
-            sslContextFactory.setWantClientAuth(_port.getWantClientAuth());
-            connector = new ServerConnector(_server, sslContextFactory, httpConnectionFactory);
+            connector = new ServerConnector(_server, _sslContextFactory, httpConnectionFactory);
             connector.addBean(new SslHandshakeListener()
             {
                 @Override
@@ -270,6 +249,36 @@ class WebSocketProvider implements AcceptingTransport
 
     }
 
+    private SslContextFactory createSslContextFactory(final AmqpPort<?> port)
+    {
+        SslContextFactory sslContextFactory = new SslContextFactory()
+        {
+            @Override
+            public void customize(final SSLEngine sslEngine)
+            {
+                super.customize(sslEngine);
+                SSLUtil.updateEnabledCipherSuites(sslEngine,
+                                                  port.getTlsCipherSuiteWhiteList(),
+                                                  port.getTlsCipherSuiteBlackList());
+                SSLUtil.updateEnabledTlsProtocols(sslEngine,
+                                                  port.getTlsProtocolWhiteList(),
+                                                  port.getTlsProtocolBlackList());
+
+                if (port.getTlsCipherSuiteWhiteList() != null
+                    && !port.getTlsCipherSuiteWhiteList().isEmpty())
+                {
+                    SSLParameters sslParameters = sslEngine.getSSLParameters();
+                    sslParameters.setUseCipherSuitesOrder(true);
+                    sslEngine.setSSLParameters(sslParameters);
+                }
+            }
+        };
+        sslContextFactory.setSslContext(port.getSSLContext());
+        sslContextFactory.setNeedClientAuth(port.getNeedClientAuth());
+        sslContextFactory.setWantClientAuth(port.getWantClientAuth());
+        return sslContextFactory;
+    }
+
     @Override
     public void close()
     {
@@ -295,6 +304,28 @@ class WebSocketProvider implements AcceptingTransport
                 ((ServerConnector) server.getConnectors()[0]).getLocalPort();
     }
 
+    @Override
+    public boolean updatesSSLContext()
+    {
+        if (_sslContextFactory != null)
+        {
+            try
+            {
+                _sslContextFactory.reload(f -> {
+                    f.setSslContext(_port.getSSLContext());
+                    f.setNeedClientAuth(_port.getNeedClientAuth());
+                    f.setWantClientAuth(_port.getWantClientAuth());
+                });
+                return true;
+            }
+            catch (Exception e)
+            {
+                throw new IllegalConfigurationException("Unexpected exception on reload of ssl context factory", e);
+            }
+        }
+        return false;
+    }
+
     private static class QBBTrackingThreadPool extends QueuedThreadPool
     {
         private final ThreadFactory _threadFactory = QpidByteBuffer.createQpidByteBufferTrackingThreadFactory(r -> QBBTrackingThreadPool.super.newThread(r));
diff --git a/systests/qpid-systests-http-management/src/test/java/org/apache/qpid/tests/http/endtoend/port/PortTest.java b/systests/qpid-systests-http-management/src/test/java/org/apache/qpid/tests/http/endtoend/port/PortTest.java
new file mode 100644
index 0000000..320a73a
--- /dev/null
+++ b/systests/qpid-systests-http-management/src/test/java/org/apache/qpid/tests/http/endtoend/port/PortTest.java
@@ -0,0 +1,400 @@
+package org.apache.qpid.tests.http.endtoend.port;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static javax.servlet.http.HttpServletResponse.SC_CREATED;
+import static javax.servlet.http.HttpServletResponse.SC_OK;
+import static org.apache.qpid.test.utils.TestSSLConstants.JAVA_KEYSTORE_TYPE;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeThat;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.Base64;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jms.Connection;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.naming.NamingException;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.Port;
+import org.apache.qpid.server.model.Protocol;
+import org.apache.qpid.server.model.Transport;
+import org.apache.qpid.server.security.NonJavaKeyStore;
+import org.apache.qpid.server.security.auth.manager.AnonymousAuthenticationManager;
+import org.apache.qpid.server.transport.network.security.ssl.SSLUtil;
+import org.apache.qpid.server.util.DataUrlUtils;
+import org.apache.qpid.systests.ConnectionBuilder;
+import org.apache.qpid.tests.http.HttpTestBase;
+import org.apache.qpid.tests.http.HttpTestHelper;
+
+public class PortTest extends HttpTestBase
+{
+    private static final String PASS = "changeit";
+    private static final String QUEUE_NAME = "testQueue";
+    private static final TypeReference<Boolean> BOOLEAN = new TypeReference<Boolean>()
+    {
+    };
+    private String _portName;
+    private String _authenticationProvider;
+    private String _keyStoreName;
+    private Set<File> _storeFiles;
+    private File _storeFile;
+
+    @Before
+    public void setUp() throws Exception
+    {
+        assumeThat(SSLUtil.canGenerateCerts(), is(true));
+
+        _portName = getTestName();
+        _authenticationProvider = _portName + "AuthenticationProvider";
+        _keyStoreName = _portName + "KeyStore";
+        createAnonymousAuthenticationProvider();
+        final SSLUtil.KeyCertPair keyCertPair = createKeyStore(_keyStoreName);
+        final X509Certificate certificate = keyCertPair.getCertificate();
+
+        _storeFiles = new HashSet<>();
+        _storeFile = createTrustStore(certificate);
+
+        getBrokerAdmin().createQueue(QUEUE_NAME);
+    }
+
+
+    @After
+    public void tearDown()
+    {
+        _storeFiles.forEach(f -> assertTrue(f.delete()));
+    }
+
+    @Test
+    public void testSwapKeyStoreAndUpdateTlsOnAmqpPort() throws Exception
+    {
+        final int port = createPort(Transport.SSL);
+        final Connection connection = createConnection(port, _storeFile.getAbsolutePath());
+        try
+        {
+            final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+            final MessageProducer producer = session.createProducer(session.createQueue(QUEUE_NAME));
+            producer.send(session.createTextMessage("A"));
+
+            final SSLUtil.KeyCertPair keyCertPair = createKeyStoreAndUpdatePortTLS();
+            final File storeFile = createTrustStore(keyCertPair.getCertificate());
+            final Connection connection2 = createConnection(port, storeFile.getAbsolutePath());
+            try
+            {
+                producer.send(session.createTextMessage("B"));
+
+                final Session session2 = connection2.createSession(false, Session.AUTO_ACKNOWLEDGE);
+                final MessageConsumer consumer = session2.createConsumer(session2.createQueue(QUEUE_NAME));
+                connection2.start();
+
+                assertMessage(consumer.receive(getReceiveTimeout()), "A");
+                assertMessage(consumer.receive(getReceiveTimeout()), "B");
+            }
+            finally
+            {
+                connection2.close();
+            }
+        }
+        finally
+        {
+            connection.close();
+        }
+    }
+
+    @Test
+    public void testUpdateKeyStoreAndUpdateTlsOnAmqpPort() throws Exception
+    {
+        final int port = createPort(Transport.SSL);
+        final Connection connection = createConnection(port, _storeFile.getAbsolutePath());
+        try
+        {
+            final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+            final MessageProducer producer = session.createProducer(session.createQueue(QUEUE_NAME));
+            producer.send(session.createTextMessage("A"));
+
+            final SSLUtil.KeyCertPair keyCertPair = updateKeyStoreAndUpdatePortTLS();
+            final File storeFile = createTrustStore(keyCertPair.getCertificate());
+            final Connection connection2 = createConnection(port, storeFile.getAbsolutePath());
+            try
+            {
+                producer.send(session.createTextMessage("B"));
+
+                final Session session2 = connection2.createSession(false, Session.AUTO_ACKNOWLEDGE);
+                final MessageConsumer consumer = session2.createConsumer(session2.createQueue(QUEUE_NAME));
+                connection2.start();
+
+                assertMessage(consumer.receive(getReceiveTimeout()), "A");
+                assertMessage(consumer.receive(getReceiveTimeout()), "B");
+            }
+            finally
+            {
+                connection2.close();
+            }
+        }
+        finally
+        {
+            connection.close();
+        }
+    }
+
+    @Test
+    public void testSwapKeyStoreAndUpdateTlsOnWssPort() throws Exception
+    {
+        assumeThat(getProtocol(), is(equalTo(Protocol.AMQP_1_0)));
+        final int port = createPort(Transport.WSS);
+        final Connection connection = createConnectionBuilder(port, _storeFile.getAbsolutePath())
+                .setTransport("amqpws").build();
+        try
+        {
+            final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+            final MessageProducer producer = session.createProducer(session.createQueue(QUEUE_NAME));
+            producer.send(session.createTextMessage("A"));
+
+            final SSLUtil.KeyCertPair keyCertPair = createKeyStoreAndUpdatePortTLS();
+            final File storeFile = createTrustStore(keyCertPair.getCertificate());
+            final Connection connection2 = createConnectionBuilder(port, storeFile.getAbsolutePath())
+                    .setTransport("amqpws").build();
+            try
+            {
+                producer.send(session.createTextMessage("B"));
+
+                final Session session2 = connection2.createSession(false, Session.AUTO_ACKNOWLEDGE);
+                final MessageConsumer consumer = session2.createConsumer(session2.createQueue(QUEUE_NAME));
+                connection2.start();
+
+                assertMessage(consumer.receive(getReceiveTimeout()), "A");
+                assertMessage(consumer.receive(getReceiveTimeout()), "B");
+            }
+            finally
+            {
+                connection2.close();
+            }
+        }
+        finally
+        {
+            connection.close();
+        }
+    }
+
+    @Test
+    public void testSwapKeyStoreAndUpdateTlsOnHttpPort() throws Exception
+    {
+        final int port = createHttpPort();
+
+        HttpTestHelper helper = new HttpTestHelper(getBrokerAdmin(), null, port);
+        helper.setTls(true);
+        helper.setKeyStore(_storeFile.getAbsolutePath(), PASS);
+
+        final Map<String, Object> attributes = getHelper().getJsonAsMap("port/" + _portName);
+        final Map<String, Object> ownAttributes = helper.getJsonAsMap("port/" + _portName);
+        assertEquals(attributes, ownAttributes);
+
+        final SSLUtil.KeyCertPair keyCertPair = createKeyStoreAndUpdatePortTLS();
+        final File storeFile = createTrustStore(keyCertPair.getCertificate());
+        helper.setKeyStore(storeFile.getAbsolutePath(), PASS);
+
+        final Map<String, Object> attributes2 = getHelper().getJsonAsMap("port/" + _portName);
+        final Map<String, Object> ownAttributes2 = helper.getJsonAsMap("port/" + _portName);
+        assertEquals(attributes2, ownAttributes2);
+    }
+
+    private void createAnonymousAuthenticationProvider() throws IOException
+    {
+        final Map<String, Object> data = Collections.singletonMap(ConfiguredObject.TYPE,
+                                                                  AnonymousAuthenticationManager.PROVIDER_TYPE);
+        getHelper().submitRequest("authenticationprovider/" + _authenticationProvider, "PUT", data, SC_CREATED);
+    }
+
+    private SSLUtil.KeyCertPair createKeyStore(final String keyStoreName) throws Exception
+    {
+        return submitKeyStoreAttributes(keyStoreName, SC_CREATED);
+    }
+
+    private SSLUtil.KeyCertPair submitKeyStoreAttributes(final String keyStoreName, final int status) throws Exception
+    {
+        final SSLUtil.KeyCertPair keyCertPair = generateSelfSignedCertificate();
+
+        final Map<String, Object> attributes = new HashMap<>();
+        attributes.put(NonJavaKeyStore.NAME, keyStoreName);
+        attributes.put(NonJavaKeyStore.PRIVATE_KEY_URL,
+                       DataUrlUtils.getDataUrlForBytes(toPEM(keyCertPair.getPrivateKey()).getBytes(UTF_8)));
+        attributes.put(NonJavaKeyStore.CERTIFICATE_URL,
+                       DataUrlUtils.getDataUrlForBytes(toPEM(keyCertPair.getCertificate()).getBytes(UTF_8)));
+        attributes.put(NonJavaKeyStore.TYPE, "NonJavaKeyStore");
+
+        getHelper().submitRequest("keystore/" + keyStoreName, "PUT", attributes, status);
+        return keyCertPair;
+    }
+
+    private ConnectionBuilder createConnectionBuilder(final int port, final String absolutePath)
+    {
+        return getConnectionBuilder().setPort(port)
+                                     .setTls(true)
+                                     .setVerifyHostName(false)
+                                     .setTrustStoreLocation(absolutePath)
+                                     .setTrustStorePassword(PASS);
+    }
+
+    private Connection createConnection(final int port, final String absolutePath)
+            throws NamingException, JMSException
+    {
+        return createConnectionBuilder(port, absolutePath).build();
+    }
+
+    private int createPort(final Transport transport) throws IOException
+    {
+        return createPort("AMQP",  transport);
+    }
+
+    private int createHttpPort() throws IOException
+    {
+        return createPort("HTTP",  Transport.SSL);
+    }
+
+    private int createPort(final String type, final Transport transport) throws IOException
+    {
+        final Map<String, Object> port = new HashMap<>();
+        port.put(Port.NAME, _portName);
+        port.put(Port.AUTHENTICATION_PROVIDER, _authenticationProvider);
+        port.put(Port.TYPE, type);
+        port.put(Port.PORT, 0);
+        port.put(Port.KEY_STORE, _keyStoreName);
+        port.put(Port.TRANSPORTS, Collections.singleton(transport));
+
+        getHelper().submitRequest("port/" + _portName, "PUT", port, SC_CREATED);
+
+        return getBoundPort();
+    }
+
+    private int getBoundPort() throws IOException
+    {
+        final Map<String, Object> attributes = getHelper().getJsonAsMap("port/" + _portName);
+        assertTrue(attributes.containsKey("boundPort"));
+        assertTrue(attributes.get("boundPort") instanceof Number);
+
+        return ((Number) attributes.get("boundPort")).intValue();
+    }
+
+    private File createTrustStore(final X509Certificate certificate)
+            throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException
+    {
+        final java.security.KeyStore ks = java.security.KeyStore.getInstance(JAVA_KEYSTORE_TYPE);
+        ks.load(null);
+        ks.setCertificateEntry("certificate", certificate);
+        final File storeFile = File.createTempFile(getTestName(), ".pkcs12");
+        try (FileOutputStream fos = new FileOutputStream(storeFile))
+        {
+            ks.store(fos, PASS.toCharArray());
+        }
+        finally
+        {
+            _storeFiles.add(storeFile);
+        }
+        return storeFile;
+    }
+
+    private SSLUtil.KeyCertPair generateSelfSignedCertificate() throws Exception
+    {
+        return SSLUtil.generateSelfSignedCertificate("RSA",
+                                                     "SHA256WithRSA",
+                                                     2048,
+                                                     Instant.now()
+                                                            .minus(1, ChronoUnit.DAYS)
+                                                            .toEpochMilli(),
+                                                     Duration.of(365, ChronoUnit.DAYS)
+                                                             .getSeconds(),
+                                                     "CN=foo",
+                                                     Collections.emptySet(),
+                                                     Collections.emptySet());
+    }
+
+    private String toPEM(final Certificate pub) throws CertificateEncodingException
+    {
+        return toPEM(pub.getEncoded(), "-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----");
+    }
+
+    private String toPEM(final PrivateKey key)
+    {
+        return toPEM(key.getEncoded(), "-----BEGIN PRIVATE KEY-----", "-----END PRIVATE KEY-----");
+    }
+
+    private String toPEM(final byte[] bytes, final String header, final String footer)
+    {
+        StringBuilder pem = new StringBuilder();
+        pem.append(header).append("\n");
+        String base64encoded = Base64.getEncoder().encodeToString(bytes);
+        while (base64encoded.length() > 76)
+        {
+            pem.append(base64encoded, 0, 76).append("\n");
+            base64encoded = base64encoded.substring(76);
+        }
+        pem.append(base64encoded).append("\n");
+        pem.append(footer).append("\n");
+        return pem.toString();
+    }
+
+    private void assertMessage(final Message messageA, final String a) throws JMSException
+    {
+        assertThat(messageA, is(notNullValue()));
+        assertThat(messageA, is(instanceOf(TextMessage.class)));
+        assertThat(((TextMessage) messageA).getText(), is(equalTo(a)));
+    }
+
+    private SSLUtil.KeyCertPair createKeyStoreAndUpdatePortTLS() throws Exception
+    {
+        final SSLUtil.KeyCertPair keyCertPair = createKeyStore(_keyStoreName + "_2");
+        final Map<String, Object> data = Collections.singletonMap(Port.KEY_STORE, _keyStoreName + "_2");
+        getHelper().submitRequest("port/" + _portName, "POST", data, SC_OK);
+        final boolean response = getHelper().postJson("port/" + _portName + "/updateTLS",
+                                                      Collections.emptyMap(),
+                                                      BOOLEAN,
+                                                      SC_OK);
+        assertTrue(response);
+
+        return keyCertPair;
+    }
+
+    private SSLUtil.KeyCertPair updateKeyStoreAndUpdatePortTLS() throws Exception
+    {
+        final SSLUtil.KeyCertPair keyCertPair = submitKeyStoreAttributes(_keyStoreName, SC_OK);
+        final boolean response = getHelper().postJson("port/" + _portName + "/updateTLS",
+                                                      Collections.emptyMap(),
+                                                      BOOLEAN,
+                                                      SC_OK);
+        assertTrue(response);
+
+        return keyCertPair;
+    }
+}
diff --git a/systests/qpid-systests-jms-core/src/main/java/org/apache/qpid/systests/ConnectionBuilder.java b/systests/qpid-systests-jms-core/src/main/java/org/apache/qpid/systests/ConnectionBuilder.java
index 4f113b4..fe64610 100644
--- a/systests/qpid-systests-jms-core/src/main/java/org/apache/qpid/systests/ConnectionBuilder.java
+++ b/systests/qpid-systests-jms-core/src/main/java/org/apache/qpid/systests/ConnectionBuilder.java
@@ -68,4 +68,6 @@ public interface ConnectionBuilder
     Connection build() throws NamingException, JMSException;
     ConnectionFactory buildConnectionFactory() throws NamingException;
     String buildConnectionURL();
+
+    ConnectionBuilder setTransport(String transport);
 }
diff --git a/systests/qpid-systests-jms-core/src/main/java/org/apache/qpid/systests/QpidJmsClient0xConnectionBuilder.java b/systests/qpid-systests-jms-core/src/main/java/org/apache/qpid/systests/QpidJmsClient0xConnectionBuilder.java
index 914cbe8..7935cb3 100644
--- a/systests/qpid-systests-jms-core/src/main/java/org/apache/qpid/systests/QpidJmsClient0xConnectionBuilder.java
+++ b/systests/qpid-systests-jms-core/src/main/java/org/apache/qpid/systests/QpidJmsClient0xConnectionBuilder.java
@@ -72,7 +72,7 @@ public class QpidJmsClient0xConnectionBuilder implements ConnectionBuilder
     public ConnectionBuilder setPort(final int port)
     {
         _port = port;
-        return this;
+        return setSslPort(port);
     }
 
     @Override
@@ -361,6 +361,12 @@ public class QpidJmsClient0xConnectionBuilder implements ConnectionBuilder
         return cUrlBuilder.toString();
     }
 
+    @Override
+    public ConnectionBuilder setTransport(final String transport)
+    {
+        throw new UnsupportedOperationException("Cannot modify transport");
+    }
+
     private String buildTransportQuery()
     {
         final StringBuilder builder = new StringBuilder();
diff --git a/systests/qpid-systests-jms-core/src/main/java/org/apache/qpid/systests/QpidJmsClientConnectionBuilder.java b/systests/qpid-systests-jms-core/src/main/java/org/apache/qpid/systests/QpidJmsClientConnectionBuilder.java
index 6da37ca..c47e81e 100644
--- a/systests/qpid-systests-jms-core/src/main/java/org/apache/qpid/systests/QpidJmsClientConnectionBuilder.java
+++ b/systests/qpid-systests-jms-core/src/main/java/org/apache/qpid/systests/QpidJmsClientConnectionBuilder.java
@@ -51,6 +51,7 @@ public class QpidJmsClientConnectionBuilder implements ConnectionBuilder
     private boolean _enableTls;
     private boolean _enableFailover;
     private final List<Integer> _failoverPorts = new ArrayList<>();
+    private String _transport = "amqp";
 
     QpidJmsClientConnectionBuilder()
     {
@@ -72,7 +73,7 @@ public class QpidJmsClientConnectionBuilder implements ConnectionBuilder
     public ConnectionBuilder setPort(final int port)
     {
         _port = port;
-        return this;
+        return setSslPort(port);
     }
 
     @Override
@@ -351,18 +352,25 @@ public class QpidJmsClientConnectionBuilder implements ConnectionBuilder
         }
         else if (!_enableTls)
         {
-            connectionUrlBuilder.append("amqp://").append(_host).append(":").append(_port);
+            connectionUrlBuilder.append(_transport).append("://").append(_host).append(":").append(_port);
 
             appendOptions(options, connectionUrlBuilder);
         }
         else
         {
-            connectionUrlBuilder.append("amqps://").append(_host).append(":").append(_sslPort);
+            connectionUrlBuilder.append(_transport).append("s").append("://").append(_host).append(":").append(_sslPort);
             appendOptions(options, connectionUrlBuilder);
         }
         return connectionUrlBuilder.toString();
     }
 
+    @Override
+    public ConnectionBuilder setTransport(final String transport)
+    {
+        _transport = transport;
+        return this;
+    }
+
     private void appendOptions(final Map<String, Object> actualOptions, final StringBuilder stem)
     {
         boolean first = true;


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