From 50a2c339ba027566c192b3bbf1c6714ca25df799 Mon Sep 17 00:00:00 2001 From: Paul Gregoire Date: Mon, 18 Mar 2024 12:17:00 -0700 Subject: [PATCH] Update Red5 version to 1.3.31; lifecycle refactor --- client/pom.xml | 2 +- .../main/java/org/red5/client/Red5Client.java | 2 +- .../net/rtmp/BaseRTMPClientHandler.java | 2 +- common/pom.xml | 4 +- .../main/java/org/red5/server/api/Red5.java | 4 +- io/pom.xml | 2 +- pom.xml | 8 +- server/pom.xml | 2 +- .../red5/net/websocket/WebSocketPlugin.java | 2 - .../red5/server/plugin/PluginRegistry.java | 135 ++++++++---------- .../red5/server/service/ShutdownServer.java | 99 ++++++------- .../org/red5/server/tomcat/TomcatLoader.java | 20 +-- service/pom.xml | 2 +- servlet/pom.xml | 2 +- 14 files changed, 137 insertions(+), 149 deletions(-) diff --git a/client/pom.xml b/client/pom.xml index 2def3a12b..1f2ad3bc9 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -3,7 +3,7 @@ org.red5 red5-parent - 1.3.30 + 1.3.31 4.0.0 red5-client diff --git a/client/src/main/java/org/red5/client/Red5Client.java b/client/src/main/java/org/red5/client/Red5Client.java index 2c9f61801..985592904 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 a3e7867f0..bcad639ed 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 void invoke(String method, Object[] params, IPendingServiceCallback callb */ @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 1ea3a1e97..3e8a8962f 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -3,7 +3,7 @@ org.red5 red5-parent - 1.3.30 + 1.3.31 4.0.0 red5-server-common @@ -113,7 +113,7 @@ net.engio mbassador - 1.3.30 + 1.3.31 --> junit 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 a7da45f8a..b9db2c797 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 85f9c267d..005d9bdff 100644 --- a/io/pom.xml +++ b/io/pom.xml @@ -3,7 +3,7 @@ org.red5 red5-parent - 1.3.30 + 1.3.31 4.0.0 red5-io diff --git a/pom.xml b/pom.xml index ad51276ad..16b3ea918 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ Red5 The Red5 server org.red5 - 1.3.30 + 1.3.31 https://github.com/Red5/red5-server 2005 @@ -105,7 +105,7 @@ 1.62 2.0.23 - 5.3.32 + 5.3.33 8.5.95 [4.13.1,) 1.9.39 @@ -514,7 +514,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.8 + 1.6.13 true ossrh @@ -524,7 +524,7 @@ maven-gpg-plugin - 1.6 + 3.2.1 sign-artifacts diff --git a/server/pom.xml b/server/pom.xml index 77edd3e8d..2fd86ca87 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -3,7 +3,7 @@ org.red5 red5-parent - 1.3.30 + 1.3.31 4.0.0 red5-server 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 95d2b90dc..771d47d27 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 WebSocketPlugin() { /** {@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 void doStop() throws Exception { }); 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 f565cab04..1bd5ee0e5 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 @@ 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 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 static void register(IRed5Plugin plugin) { */ 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 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 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 static void unregister(IRed5Plugin plugin) { * @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 static IRed5Plugin getPlugin(String pluginName) { */ 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 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 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 66bfd4d23..6f7e1f03a 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 void afterPropertiesSet() throws Exception { } 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 void start() { 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 void run() { } 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 void run() { } 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 void run() { } 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 fb76908cf..b573a6391 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 void afterPropertiesSet() throws Exception { 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 void destroy() throws Exception { } 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 9cbef4099..bb77afb1b 100644 --- a/service/pom.xml +++ b/service/pom.xml @@ -3,7 +3,7 @@ org.red5 red5-parent - 1.3.30 + 1.3.31 4.0.0 red5-service diff --git a/servlet/pom.xml b/servlet/pom.xml index d6868eda3..4a8dbc5c0 100644 --- a/servlet/pom.xml +++ b/servlet/pom.xml @@ -3,7 +3,7 @@ org.red5 red5-parent - 1.3.30 + 1.3.31 4.0.0 red5-servlet