Skip to content

Commit eb75c16

Browse files
committed
Refactor for ordered loading and prevention of tomcat loading before ready
1 parent 82708a5 commit eb75c16

File tree

5 files changed

+97
-22
lines changed

5 files changed

+97
-22
lines changed

common/src/main/java/org/red5/server/api/Red5.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ public final class Red5 {
9696
*/
9797
private static boolean debug = java.lang.management.ManagementFactory.getRuntimeMXBean().getInputArguments().toString().indexOf("jdwp") >= 0;
9898

99+
/**
100+
* Plugins ready flag
101+
*/
102+
private static boolean pluginsReady;
103+
99104
/**
100105
* Create a new Red5 object using given connection.
101106
*
@@ -279,4 +284,15 @@ public static int getTargetChunkSize() {
279284
return targetChunkSize;
280285
}
281286

287+
/**
288+
* Plugins ready flag. This is set to true when the PluginLauncher has completed loading all plugins.
289+
*/
290+
public static void setPluginsReady(boolean pluginsReady) {
291+
Red5.pluginsReady = pluginsReady;
292+
}
293+
294+
public static boolean isPluginsReady() {
295+
return pluginsReady;
296+
}
297+
282298
}

server/src/main/java/org/red5/server/Launcher.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import org.red5.logging.Red5LoggerFactory;
1111
import org.red5.server.api.Red5;
1212
import org.slf4j.Logger;
13+
import org.slf4j.LoggerFactory;
1314
import org.slf4j.bridge.SLF4JBridgeHandler;
1415
import org.springframework.context.support.FileSystemXmlApplicationContext;
1516

@@ -44,7 +45,7 @@ public void launch() throws Exception {
4445
// log stdout and stderr to slf4j
4546
//SysOutOverSLF4J.sendSystemOutAndErrToSLF4J();
4647
// get the first logger
47-
Logger log = Red5LoggerFactory.getLogger(Launcher.class);
48+
Logger log = LoggerFactory.getLogger(Launcher.class);
4849
// version info banner
4950
log.info("{} (https://github.com/Red5)", Red5.getVersion());
5051
if (log.isDebugEnabled()) {

server/src/main/java/org/red5/server/plugin/PluginLauncher.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public void afterPropertiesSet() throws Exception {
4141
// get common context
4242
ApplicationContext common = (ApplicationContext) applicationContext.getBean("red5.common");
4343
Server server = (Server) common.getBean("red5.server");
44-
//server should be up and running at this point so load any plug-ins now
44+
// server should be up and running at this point so load any plug-ins now
4545

4646
// get the plugins dir
4747
File pluginsDir = new File(System.getProperty("red5.root"), "plugins");
@@ -117,14 +117,13 @@ public boolean accept(File dir, String name) {
117117
}
118118
log.info("Loaded plugin: {}", pluginMainClass);
119119
} catch (Throwable t) {
120-
log.warn("Error loading plugin: {}; Method: {}", pluginMainClass, pluginMainMethod);
121-
log.error("", t);
120+
log.warn("Error loading plugin: {}; Method: {}", pluginMainClass, pluginMainMethod, t);
122121
}
123122
}
124123
} else {
125124
log.info("Plugins directory cannot be accessed or doesnt exist");
126125
}
127-
126+
//Red5.setPluginsReady(true);
128127
}
129128

130129
@SuppressWarnings("null")

server/src/main/java/org/red5/server/tomcat/TomcatLoader.java

Lines changed: 75 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,19 @@
4242
import org.apache.catalina.realm.JAASRealm;
4343
import org.apache.catalina.realm.NullRealm;
4444
import org.apache.catalina.realm.RealmBase;
45-
import org.red5.logging.Red5LoggerFactory;
4645
import org.red5.net.websocket.WebSocketPlugin;
4746
import org.red5.server.ContextLoader;
4847
import org.red5.server.LoaderBase;
4948
import org.red5.server.Server;
5049
import org.red5.server.api.IApplicationContext;
50+
import org.red5.server.api.Red5;
5151
import org.red5.server.jmx.mxbeans.ContextLoaderMXBean;
5252
import org.red5.server.jmx.mxbeans.LoaderMXBean;
5353
import org.red5.server.plugin.PluginRegistry;
5454
import org.red5.server.security.IRed5Realm;
5555
import org.red5.server.util.FileUtil;
5656
import org.slf4j.Logger;
57+
import org.slf4j.LoggerFactory;
5758
import org.springframework.beans.factory.DisposableBean;
5859
import org.springframework.beans.factory.InitializingBean;
5960
import org.springframework.context.ApplicationContext;
@@ -115,7 +116,7 @@ public boolean accept(File dir, String name) {
115116
private static final String CONTEXT_CLASS_PARAM = "contextClass";
116117

117118
// Initialize Logging
118-
private static Logger log = Red5LoggerFactory.getLogger(TomcatLoader.class);
119+
private static Logger log = LoggerFactory.getLogger(TomcatLoader.class);
119120

120121
public static final String defaultSpringConfigLocation = "/WEB-INF/red5-*.xml";
121122

@@ -170,23 +171,43 @@ public boolean accept(File dir, String name) {
170171
*/
171172
protected boolean websocketEnabled = true;
172173

174+
/**
175+
* Flag to indicate if we should await plugin loading
176+
*/
177+
protected boolean awaitPlugins = true;
178+
179+
private static ExecutorService executor;
180+
173181
@Override
174182
public void afterPropertiesSet() throws Exception {
175-
// if websockets are enabled, ensure the websocket plugin is loaded
176-
if (websocketEnabled && PluginRegistry.getPlugin(WebSocketPlugin.NAME) == null) {
177-
// get common context
178-
ApplicationContext common = (ApplicationContext) applicationContext.getBean("red5.common");
179-
Server server = (Server) common.getBean("red5.server");
180-
// instance the plugin
181-
WebSocketPlugin plugin = new WebSocketPlugin();
182-
plugin.setApplicationContext(applicationContext);
183-
plugin.setServer(server);
184-
// register it
185-
PluginRegistry.register(plugin);
186-
// start it
187-
plugin.doStart();
183+
// if we are not awaiting plugins, start immediately
184+
if (awaitPlugins) {
185+
log.info("Awaiting plugin loading");
186+
executor = Executors.newSingleThreadExecutor();
187+
executor.submit(() -> {
188+
final String oldName = Thread.currentThread().getName();
189+
Thread.currentThread().setName("TomcatLoader-delayed-start");
190+
try {
191+
while (!Red5.isPluginsReady()) {
192+
log.debug("Waiting for plugins to load");
193+
Thread.sleep(500L);
194+
}
195+
start();
196+
} catch (ServletException e) {
197+
log.error("Error starting Tomcat", e);
198+
} catch (InterruptedException e) {
199+
log.error("Error waiting for plugins", e);
200+
} finally {
201+
Thread.currentThread().setName(oldName);
202+
}
203+
});
204+
} else {
205+
try {
206+
start();
207+
} catch (ServletException e) {
208+
log.error("Error starting Tomcat", e);
209+
}
188210
}
189-
start();
190211
}
191212

192213
/**
@@ -273,7 +294,11 @@ public void removeContext(String path) {
273294
@SuppressWarnings("null")
274295
public void start() throws ServletException {
275296
log.info("Loading Tomcat");
276-
//get a reference to the current threads classloader
297+
// if websockets are enabled, ensure the websocket plugin is loaded
298+
if (websocketEnabled) {
299+
checkWebsocketPlugin();
300+
}
301+
// get a reference to the current threads classloader
277302
final ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
278303
// root location for servlet container
279304
String serverRoot = System.getProperty("red5.root");
@@ -530,6 +555,27 @@ public void run() {
530555
log.debug("Tomcat load completed");
531556
}
532557

558+
private void checkWebsocketPlugin() {
559+
// if websockets are enabled, ensure the websocket plugin is loaded
560+
if (PluginRegistry.getPlugin(WebSocketPlugin.NAME) == null) {
561+
// get common context
562+
ApplicationContext common = (ApplicationContext) applicationContext.getBean("red5.common");
563+
Server server = (Server) common.getBean("red5.server");
564+
// instance the plugin
565+
WebSocketPlugin plugin = new WebSocketPlugin();
566+
plugin.setApplicationContext(applicationContext);
567+
plugin.setServer(server);
568+
// register it
569+
PluginRegistry.register(plugin);
570+
// start it
571+
try {
572+
plugin.doStart();
573+
} catch (Exception e) {
574+
log.warn("WebSocket plugin start, failed", e);
575+
}
576+
}
577+
}
578+
533579
/**
534580
* Starts a web application and its red5 (spring) component. This is basically a stripped down version of start().
535581
*
@@ -803,6 +849,15 @@ public void setWebsocketEnabled(boolean websocketEnabled) {
803849
this.websocketEnabled = websocketEnabled;
804850
}
805851

852+
/**
853+
* Returns await plugin loading state.
854+
*
855+
* @return true if awaiting plugin loading and false otherwise
856+
*/
857+
public void setAwaitPlugins(boolean awaitPlugins) {
858+
this.awaitPlugins = awaitPlugins;
859+
}
860+
806861
/**
807862
* Returns a semi-unique id for this host based on its host values
808863
*
@@ -846,6 +901,9 @@ protected void unregisterJMX() {
846901
@Override
847902
public void destroy() throws Exception {
848903
log.info("Shutting down Tomcat context");
904+
if (executor != null) {
905+
executor.shutdown();
906+
}
849907
// run through the applications and ensure that spring is told to commence shutdown / disposal
850908
AbstractApplicationContext absCtx = (AbstractApplicationContext) LoaderBase.getApplicationContext();
851909
if (absCtx != null) {

server/src/main/server/conf/jee-container.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
-->
2323
<!-- Tomcat without SSL enabled -->
2424
<bean id="tomcat.server" class="org.red5.server.tomcat.TomcatLoader" depends-on="context.loader" lazy-init="true">
25+
<property name="awaitPlugins" value="false" />
2526
<property name="websocketEnabled" value="true" />
2627
<property name="webappFolder" value="${red5.root}/webapps" />
2728
<property name="connectors">

0 commit comments

Comments
 (0)