diff --git a/client/pom.xml b/client/pom.xml
index 2def3a12bb55290f1e64ad27dbbf05688158b5b9..1f2ad3bc97a37a0d09429b9b352451f9174bc376 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <groupId>org.red5</groupId>
         <artifactId>red5-parent</artifactId>
-        <version>1.3.30</version>
+        <version>1.3.31</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>red5-client</artifactId>
diff --git a/client/src/main/java/org/red5/client/Red5Client.java b/client/src/main/java/org/red5/client/Red5Client.java
index 2c9f618019c0ddbc8e58af1cbf3f71244f8de42f..9855929045bae866697822d602a484f2d9856f59 100644
--- a/client/src/main/java/org/red5/client/Red5Client.java
+++ b/client/src/main/java/org/red5/client/Red5Client.java
@@ -18,7 +18,7 @@ public final class Red5Client {
     /**
      * Current server version with revision
      */
-    public static final String VERSION = "Red5 Client 1.3.30";
+    public static final String VERSION = "Red5 Client 1.3.31";
 
     /**
      * Create a new Red5Client object using the connection local to the current thread A bit of magic that lets you access the red5 scope
diff --git a/client/src/main/java/org/red5/client/net/rtmp/BaseRTMPClientHandler.java b/client/src/main/java/org/red5/client/net/rtmp/BaseRTMPClientHandler.java
index a3e7867f0c98e55ec7709786cd8372a1076c5777..bcad639ed7acedcdc573e938c9c1eaf9f16c4547 100644
--- a/client/src/main/java/org/red5/client/net/rtmp/BaseRTMPClientHandler.java
+++ b/client/src/main/java/org/red5/client/net/rtmp/BaseRTMPClientHandler.java
@@ -541,7 +541,7 @@ public abstract class BaseRTMPClientHandler extends BaseRTMPHandler implements I
      */
     @Override
     public void disconnect() {
-        log.debug("disconnect: {}", conn);
+        log.debug("disconnect - connection: {}", conn);
         if (conn != null) {
             streamDataList.clear();
             conn.close();
diff --git a/common/pom.xml b/common/pom.xml
index 1ea3a1e97da8551b3c5940dbc05e9ef656cfbd06..3e8a8962f1c7c189882a21acce16f450a3371e79 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <groupId>org.red5</groupId>
         <artifactId>red5-parent</artifactId>
-        <version>1.3.30</version>
+        <version>1.3.31</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>red5-server-common</artifactId>
@@ -113,7 +113,7 @@
         <dependency>
             <groupId>net.engio</groupId>
             <artifactId>mbassador</artifactId>
-            <version>1.3.30</version>
+            <version>1.3.31</version>
         </dependency> -->
         <dependency>
             <groupId>junit</groupId>
diff --git a/common/src/main/java/org/red5/server/api/Red5.java b/common/src/main/java/org/red5/server/api/Red5.java
index a7da45f8a7c84b226f92a40b154553599ac70097..b9db2c79744593cdf5ac924ec04e96c588c04a3f 100644
--- a/common/src/main/java/org/red5/server/api/Red5.java
+++ b/common/src/main/java/org/red5/server/api/Red5.java
@@ -57,12 +57,12 @@ public final class Red5 {
     /**
      * Server version with revision
      */
-    public static final String VERSION = "Red5 Server 1.3.30";
+    public static final String VERSION = "Red5 Server 1.3.31";
 
     /**
      * Server version for fmsVer requests
      */
-    public static final String FMS_VERSION = "RED5/1,3,30,0";
+    public static final String FMS_VERSION = "RED5/1,3,31,0";
 
     /**
      * Server capabilities
diff --git a/io/pom.xml b/io/pom.xml
index 85f9c267d55ab29394fb81088ed4094d848984a8..005d9bdff768a88002a073314947155e428a3024 100644
--- a/io/pom.xml
+++ b/io/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <groupId>org.red5</groupId>
         <artifactId>red5-parent</artifactId>
-        <version>1.3.30</version>
+        <version>1.3.31</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>red5-io</artifactId>
diff --git a/pom.xml b/pom.xml
index ad51276ad2646833c2e8364e4bb9a30988978d18..16b3ea9188999e9e2e514b238fff6e5cd8c2ff17 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,7 +24,7 @@
     <name>Red5</name>
     <description>The Red5 server</description>
     <groupId>org.red5</groupId>
-    <version>1.3.30</version>
+    <version>1.3.31</version>
     <url>https://github.com/Red5/red5-server</url>
     <inceptionYear>2005</inceptionYear>
     <organization>
@@ -105,7 +105,7 @@
         <bc.version>1.62</bc.version>
         <mina.version>2.0.23</mina.version>
         <!-- Can no longer open-end spring, 6.0 forces jdk 17 -->
-        <spring.version>5.3.32</spring.version>
+        <spring.version>5.3.33</spring.version>
         <tomcat.version>8.5.95</tomcat.version>
         <junit.version>[4.13.1,)</junit.version>
         <isoparser.version>1.9.39</isoparser.version>
@@ -514,7 +514,7 @@
                     <plugin>
                         <groupId>org.sonatype.plugins</groupId>
                         <artifactId>nexus-staging-maven-plugin</artifactId>
-                        <version>1.6.8</version>
+                        <version>1.6.13</version>
                         <extensions>true</extensions>
                         <configuration>
                             <serverId>ossrh</serverId>
@@ -524,7 +524,7 @@
                     </plugin>
                     <plugin>
                         <artifactId>maven-gpg-plugin</artifactId>
-                        <version>1.6</version>
+                        <version>3.2.1</version>
                         <executions>
                             <execution>
                                 <id>sign-artifacts</id>
diff --git a/server/pom.xml b/server/pom.xml
index 77edd3e8d224127e7ec7b5421fafa8227b172300..2fd86ca87a3d4332af841d493225355401dc3ba6 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <groupId>org.red5</groupId>
         <artifactId>red5-parent</artifactId>
-        <version>1.3.30</version>
+        <version>1.3.31</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>red5-server</artifactId>
diff --git a/server/src/main/java/org/red5/net/websocket/WebSocketPlugin.java b/server/src/main/java/org/red5/net/websocket/WebSocketPlugin.java
index 95d2b90dc530927d1ad34d3610e414fcf44080d9..771d47d27d314d00261969ed81f86c8207968eba 100644
--- a/server/src/main/java/org/red5/net/websocket/WebSocketPlugin.java
+++ b/server/src/main/java/org/red5/net/websocket/WebSocketPlugin.java
@@ -82,7 +82,6 @@ public class WebSocketPlugin extends Red5Plugin {
     /** {@inheritDoc} */
     @Override
     public void doStart() throws Exception {
-        super.doStart();
         log.trace("WebSocketPlugin start");
         // add scope listener to allow creation of websocket scopes
         scopeListener = new ScopeListenerAdapter() {
@@ -136,7 +135,6 @@ public class WebSocketPlugin extends Red5Plugin {
         });
         managerMap.clear();
         executor.shutdownNow();
-        super.doStop();
     }
 
     /**
diff --git a/server/src/main/java/org/red5/server/plugin/PluginRegistry.java b/server/src/main/java/org/red5/server/plugin/PluginRegistry.java
index f565cab042b337791eb039ae7448cfc05b3e064c..1bd5ee0e53606cef1511bd24ef2e50932b1c15e3 100644
--- a/server/src/main/java/org/red5/server/plugin/PluginRegistry.java
+++ b/server/src/main/java/org/red5/server/plugin/PluginRegistry.java
@@ -10,9 +10,6 @@ package org.red5.server.plugin;
 import java.util.Map.Entry;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 import org.red5.logging.Red5LoggerFactory;
 import org.red5.server.api.plugin.IRed5Plugin;
@@ -30,16 +27,27 @@ public class PluginRegistry {
     // keeps track of plug-ins, keyed by plug-in name
     private static volatile ConcurrentMap<String, IRed5Plugin> plugins = new ConcurrentHashMap<>(3, 0.9f, 1);
 
-    // locks for guarding plug-ins
-    private final static ReadWriteLock pluginLock = new ReentrantReadWriteLock();
-
-    private final static Lock pluginReadLock;
-
-    private final static Lock pluginWriteLock;
+    /**
+     * Returns true if the plug-in is registered.
+     *
+     * @param plugin
+     *            plugin
+     * @return true if the plug-in is registered
+     */
+    public static boolean isRegistered(IRed5Plugin plugin) {
+        String pluginName = plugin.getName();
+        return plugins.containsKey(pluginName);
+    }
 
-    static {
-        pluginReadLock = pluginLock.readLock();
-        pluginWriteLock = pluginLock.writeLock();
+    /**
+    * Returns true if the plug-in is registered.
+    *
+    * @param pluginName
+    *            plugin name
+    * @return true if the plug-in is registered
+    */
+    public static boolean isRegistered(String pluginName) {
+        return plugins.containsKey(pluginName);
     }
 
     /**
@@ -51,27 +59,21 @@ public class PluginRegistry {
     public static void register(IRed5Plugin plugin) {
         log.debug("Register plugin: {}", plugin);
         String pluginName = plugin.getName();
-        //get a write lock
-        pluginWriteLock.lock();
-        try {
-            if (plugins.containsKey(pluginName)) {
-                //get old plugin
-                IRed5Plugin oldPlugin = plugins.get(pluginName);
-                //if they are not the same shutdown the older one
-                if (!plugin.equals(oldPlugin)) {
-                    try {
-                        oldPlugin.doStop();
-                    } catch (Exception e) {
-                        log.warn("Exception caused when stopping old plugin", e);
-                    }
-                    //replace old one
-                    plugins.replace(pluginName, plugin);
+        if (plugins.containsKey(pluginName)) {
+            //get old plugin
+            IRed5Plugin oldPlugin = plugins.get(pluginName);
+            //if they are not the same shutdown the older one
+            if (!plugin.equals(oldPlugin)) {
+                try {
+                    oldPlugin.doStop();
+                } catch (Exception e) {
+                    log.warn("Exception caused when stopping old plugin", e);
                 }
-            } else {
-                plugins.put(pluginName, plugin);
+                //replace old one
+                plugins.replace(pluginName, plugin);
             }
-        } finally {
-            pluginWriteLock.unlock();
+        } else {
+            plugins.put(pluginName, plugin);
         }
     }
 
@@ -83,30 +85,24 @@ public class PluginRegistry {
      */
     public static void unregister(IRed5Plugin plugin) {
         log.debug("Unregister plugin: {}", plugin);
-        //get a write lock
-        pluginWriteLock.lock();
-        try {
-            if (plugins.containsValue(plugin)) {
-                boolean removed = false;
-                for (Entry<String, IRed5Plugin> f : plugins.entrySet()) {
-                    if (plugin.equals(f.getValue())) {
-                        log.debug("Removing {}", plugin);
-                        plugins.remove(f.getKey());
-                        removed = true;
-                        break;
-                    } else {
-                        log.debug("Not equal - {} {}", plugin, f.getValue());
-                    }
-                }
-                if (!removed) {
-                    log.debug("Last try to remove the plugin");
-                    plugins.remove(plugin.getName());
+        if (plugins.containsValue(plugin)) {
+            boolean removed = false;
+            for (Entry<String, IRed5Plugin> f : plugins.entrySet()) {
+                if (plugin.equals(f.getValue())) {
+                    log.debug("Removing {}", plugin);
+                    plugins.remove(f.getKey());
+                    removed = true;
+                    break;
+                } else {
+                    log.debug("Not equal - {} {}", plugin, f.getValue());
                 }
-            } else {
-                log.warn("Plugin is not registered {}", plugin);
             }
-        } finally {
-            pluginWriteLock.unlock();
+            if (!removed) {
+                log.debug("Last try to remove the plugin");
+                plugins.remove(plugin.getName());
+            }
+        } else {
+            log.warn("Plugin is not registered {}", plugin);
         }
     }
 
@@ -118,13 +114,7 @@ public class PluginRegistry {
      * @return requested plug-in matching the name given or null if not found
      */
     public static IRed5Plugin getPlugin(String pluginName) {
-        IRed5Plugin plugin = null;
-        pluginReadLock.lock();
-        try {
-            plugin = plugins.get(pluginName);
-        } finally {
-            pluginReadLock.unlock();
-        }
+        IRed5Plugin plugin = plugins.get(pluginName);
         return plugin;
     }
 
@@ -136,23 +126,18 @@ public class PluginRegistry {
      */
     public static void shutdown() throws Exception {
         log.info("Destroying and cleaning up {} plugins", plugins.size());
-        //loop through the plugins and stop them
-        pluginReadLock.lock();
-        try {
-            for (Entry<String, IRed5Plugin> pluginEntry : plugins.entrySet()) {
-                IRed5Plugin plugin = pluginEntry.getValue();
-                try {
-                    plugin.doStop();
-                } catch (Exception ex) {
-                    if (plugin != null) {
-                        log.warn("Plugin stop failed for: {}", plugin.getName(), ex);
-                    } else {
-                        log.warn("Plugin stop failed", ex);
-                    }
+        // loop through the plugins and stop them
+        for (Entry<String, IRed5Plugin> pluginEntry : plugins.entrySet()) {
+            IRed5Plugin plugin = pluginEntry.getValue();
+            try {
+                plugin.doStop();
+            } catch (Exception ex) {
+                if (plugin != null) {
+                    log.warn("Plugin stop failed for: {}", plugin.getName(), ex);
+                } else {
+                    log.warn("Plugin stop failed", ex);
                 }
             }
-        } finally {
-            pluginReadLock.unlock();
         }
         plugins.clear();
     }
diff --git a/server/src/main/java/org/red5/server/service/ShutdownServer.java b/server/src/main/java/org/red5/server/service/ShutdownServer.java
index 66bfd4d23786dca53f663df3b9b35bf6bd4de0d8..6f7e1f03ac331f969a3cb7cbfea070bbeacfaf1b 100644
--- a/server/src/main/java/org/red5/server/service/ShutdownServer.java
+++ b/server/src/main/java/org/red5/server/service/ShutdownServer.java
@@ -89,8 +89,12 @@ public class ShutdownServer implements ApplicationContextAware, InitializingBean
     // whether the server is shutdown
     private AtomicBoolean shutdown = new AtomicBoolean(false);
 
-    // single thread executor for the internal startup / server
-    private ExecutorService executor = Executors.newSingleThreadExecutor();
+    // thread executor for the internal startup and shutdown
+    private ExecutorService executor = Executors.newFixedThreadPool(2, r -> {
+        Thread t = new Thread(r, "ShutdownServer-" + r.hashCode());
+        t.setDaemon(true);
+        return t;
+    });
 
     // reference to the runnable
     private Future<?> future;
@@ -110,10 +114,8 @@ public class ShutdownServer implements ApplicationContextAware, InitializingBean
         } catch (Exception e) {
         }
         // start blocks, so it must be on its own thread
-        future = executor.submit(new Runnable() {
-            public void run() {
-                start();
-            }
+        future = executor.submit(() -> {
+            start();
         });
     }
 
@@ -196,30 +198,30 @@ public class ShutdownServer implements ApplicationContextAware, InitializingBean
 
     private void shutdownOrderly() {
         // shutdown internal listener
-        shutdown.compareAndSet(false, true);
-        // shutdown the plug-in launcher
-        try {
-            log.debug("Attempting to shutdown plugin registry");
-            PluginRegistry.shutdown();
-        } catch (Exception e) {
-            log.warn("Exception shutting down plugin registry", e);
-        }
-        // shutdown the context loader
-        if (contextLoader != null) {
-            log.debug("Attempting to shutdown context loader");
-            contextLoader.shutdown();
-            contextLoader = null;
-        }
-        // shutdown the jee server
-        if (jeeServer != null) {
-            // destroy is a DisposibleBean method not LoaderBase
-            // jeeServer.destroy();
-            jeeServer = null;
-        }
-        // attempt to kill the contexts
-        final CountDownLatch latch = new CountDownLatch(3);
-        new Thread(new Runnable() {
-            public void run() {
+        if (shutdown.compareAndSet(false, true)) {
+            log.info("Shutdown server shutdown");
+            // shutdown the plug-in launcher
+            try {
+                log.debug("Attempting to shutdown plugin registry");
+                PluginRegistry.shutdown();
+            } catch (Exception e) {
+                log.warn("Exception shutting down plugin registry", e);
+            }
+            // shutdown the context loader
+            if (contextLoader != null) {
+                log.debug("Attempting to shutdown context loader");
+                contextLoader.shutdown();
+                contextLoader = null;
+            }
+            // shutdown the jee server
+            if (jeeServer != null) {
+                // destroy is a DisposibleBean method not LoaderBase
+                // jeeServer.destroy();
+                jeeServer = null;
+            }
+            // attempt to kill the contexts
+            final CountDownLatch latch = new CountDownLatch(3);
+            executor.submit(() -> {
                 try {
                     log.debug("Attempting to close core context");
                     ((ConfigurableApplicationContext) coreContext).close();
@@ -227,10 +229,8 @@ public class ShutdownServer implements ApplicationContextAware, InitializingBean
                 } catch (Exception e) {
                     e.printStackTrace();
                 }
-            }
-        }).start();
-        new Thread(new Runnable() {
-            public void run() {
+            });
+            executor.submit(() -> {
                 try {
                     log.debug("Attempting to close common context");
                     ((ConfigurableApplicationContext) commonContext).close();
@@ -238,10 +238,8 @@ public class ShutdownServer implements ApplicationContextAware, InitializingBean
                 } catch (Exception e) {
                     e.printStackTrace();
                 }
-            }
-        }).start();
-        new Thread(new Runnable() {
-            public void run() {
+            });
+            executor.submit(() -> {
                 try {
                     log.debug("Attempting to close app context");
                     ((ConfigurableApplicationContext) applicationContext).close();
@@ -249,19 +247,22 @@ public class ShutdownServer implements ApplicationContextAware, InitializingBean
                 } catch (Exception e) {
                     e.printStackTrace();
                 }
+            });
+            try {
+                if (latch.await(shutdownDelay, TimeUnit.SECONDS)) {
+                    log.info("Application contexts are closed");
+                } else {
+                    log.info("One or more contexts didn't close in the allotted time");
+                }
+            } catch (InterruptedException e) {
+                log.error("Exception attempting to close app contexts", e);
+            } finally {
+                // shutdown the executor
+                executor.shutdown();
             }
-        }).start();
-        try {
-            if (latch.await(shutdownDelay, TimeUnit.SECONDS)) {
-                log.info("Application contexts are closed");
-            } else {
-                log.info("One or more contexts didn't close in the allotted time");
-            }
-        } catch (InterruptedException e) {
-            log.error("Exception attempting to close app contexts", e);
+            // exit
+            System.exit(0);
         }
-        // exit
-        System.exit(0);
     }
 
     public void setPort(int port) {
diff --git a/server/src/main/java/org/red5/server/tomcat/TomcatLoader.java b/server/src/main/java/org/red5/server/tomcat/TomcatLoader.java
index fb76908cf25b20e22da8edb10c102060bd425493..b573a6391c01458be60289fcdae92c9870a5ec86 100644
--- a/server/src/main/java/org/red5/server/tomcat/TomcatLoader.java
+++ b/server/src/main/java/org/red5/server/tomcat/TomcatLoader.java
@@ -181,9 +181,10 @@ public class TomcatLoader extends LoaderBase implements InitializingBean, Dispos
             WebSocketPlugin plugin = new WebSocketPlugin();
             plugin.setApplicationContext(applicationContext);
             plugin.setServer(server);
-            // start it up and register it
-            plugin.doStart();
+            // register it
             PluginRegistry.register(plugin);
+            // start it
+            plugin.doStart();
         }
         start();
     }
@@ -866,14 +867,17 @@ public class TomcatLoader extends LoaderBase implements InitializingBean, Dispos
         } else {
             log.error("Error getting Spring bean factory for shutdown");
         }
-        try {
+        // no need to stop the websocket plugin if it is not registered
+        if (PluginRegistry.isRegistered(WebSocketPlugin.NAME)) {
             // stop websocket
-            WebSocketPlugin plugin = (WebSocketPlugin) PluginRegistry.getPlugin(WebSocketPlugin.NAME);
-            if (plugin != null) {
-                plugin.doStop();
+            try {
+                WebSocketPlugin plugin = (WebSocketPlugin) PluginRegistry.getPlugin(WebSocketPlugin.NAME);
+                if (plugin != null) {
+                    plugin.doStop();
+                }
+            } catch (Exception e) {
+                log.warn("WebSocket plugin stop, failed", e);
             }
-        } catch (Exception e) {
-            log.warn("WebSocket plugin stop, failed", e);
         }
         try {
             // stop tomcat
diff --git a/service/pom.xml b/service/pom.xml
index 9cbef4099db8f56a9ecd94b1ddf20a263142dc50..bb77afb1b0348b3019a0fb61411a657f88b416be 100644
--- a/service/pom.xml
+++ b/service/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <groupId>org.red5</groupId>
         <artifactId>red5-parent</artifactId>
-        <version>1.3.30</version>
+        <version>1.3.31</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>red5-service</artifactId>
diff --git a/servlet/pom.xml b/servlet/pom.xml
index d6868eda33591263e9dbf6113b482fb671a764db..4a8dbc5c05871d35f1156d2ce46bea1080521ae2 100644
--- a/servlet/pom.xml
+++ b/servlet/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <groupId>org.red5</groupId>
         <artifactId>red5-parent</artifactId>
-        <version>1.3.30</version>
+        <version>1.3.31</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>red5-servlet</artifactId>