Skip to content

Commit

Permalink
Refactored Tomcat and WarDeployer, removing control from Spring and p…
Browse files Browse the repository at this point in the history
…lacing in the loader.
  • Loading branch information
mondain committed Dec 17, 2024
1 parent 6b3ea15 commit 575fe1b
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 100 deletions.
25 changes: 18 additions & 7 deletions server/src/main/java/org/red5/server/tomcat/TomcatLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@
import javax.management.JMX;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import jakarta.security.auth.message.config.AuthConfigFactory;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;

import org.apache.catalina.Container;
import org.apache.catalina.Context;
Expand Down Expand Up @@ -64,6 +61,10 @@
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.XmlWebApplicationContext;

import jakarta.security.auth.message.config.AuthConfigFactory;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;

/**
* Red5 loader for Tomcat.
*
Expand Down Expand Up @@ -178,6 +179,11 @@ public boolean accept(File dir, String name) {

private static ExecutorService executor;

/**
* War deployer
*/
private WarDeployer deployer;

@Override
public void afterPropertiesSet() throws Exception {
// if we are not awaiting plugins, start immediately
Expand All @@ -189,8 +195,8 @@ public void afterPropertiesSet() throws Exception {
Thread.currentThread().setName("TomcatLoader-delayed-start");
try {
while (!Red5.isPluginsReady()) {
log.debug("Waiting for plugins to load");
Thread.sleep(500L);
log.trace("Waiting for plugins to load");
Thread.sleep(2000L);
}
start();
} catch (ServletException e) {
Expand Down Expand Up @@ -291,7 +297,6 @@ public void removeContext(String path) {
/**
* Initialization.
*/
@SuppressWarnings("null")
public void start() throws ServletException {
log.info("Loading Tomcat");
// if websockets are enabled, ensure the websocket plugin is loaded
Expand Down Expand Up @@ -332,6 +337,8 @@ public void start() throws ServletException {
log.info("Application root: {}", webappFolder);
// Root applications directory
File appDirBase = new File(webappFolder);
// create/start the war deployer, but don't start any expanded apps, yet
deployer = new WarDeployer(appDirBase, true);
// Subdirs of root apps dir
File[] dirs = appDirBase.listFiles(new DirectoryFilter());
// Search for additional context files
Expand Down Expand Up @@ -531,7 +538,7 @@ public void run() {
applicationContext.getBean("rtmpt.server");
log.debug("Finished initializing RTMPT");
} else {
log.info("Dedicated RTMPT server configuration was not specified");
log.debug("Dedicated RTMPT server configuration was not specified");
}
if (applicationContext.containsBean("rtmps.server")) {
log.debug("Initializing RTMPS");
Expand Down Expand Up @@ -901,6 +908,10 @@ protected void unregisterJMX() {
@Override
public void destroy() throws Exception {
log.info("Shutting down Tomcat context");
if (deployer != null) {
deployer.stop();
deployer = null;
}
if (executor != null) {
executor.shutdown();
}
Expand Down
124 changes: 39 additions & 85 deletions server/src/main/java/org/red5/server/tomcat/WarDeployer.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,39 +19,35 @@
import javax.management.JMX;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import jakarta.servlet.ServletException;

import org.red5.server.LoaderBase;
import org.red5.server.jmx.mxbeans.LoaderMXBean;
import org.red5.server.util.FileUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import jakarta.servlet.ServletException;

/**
* This service provides the means to auto-deploy a war.
*
* Note: This class has deprecated use of Spring and has been refactored to be instantiated and controlled via the
* TomcatLoader, it is no longer meant to be used in the jee-container.xml as a bean.
*
* @author Paul Gregoire ([email protected])
*/
public final class WarDeployer implements ApplicationContextAware, InitializingBean, DisposableBean {
public final class WarDeployer implements ApplicationContextAware {

private Logger log = LoggerFactory.getLogger(WarDeployer.class);

//that wars are currently being installed
private static AtomicBoolean deploying = new AtomicBoolean(false);

/**
* Spring Application context
*/
private ApplicationContext applicationContext;

private ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();

private ScheduledFuture<DeployJob> future;
private ScheduledFuture<?> future;

/**
* How often to check for new war files
Expand All @@ -61,48 +57,36 @@ public final class WarDeployer implements ApplicationContextAware, InitializingB
/**
* Deployment directory
*/
private String webappFolder;

/**
* Expand WAR files in the webapps directory prior to start up
*/
private boolean expandWars;
private final File webappsDirectory;

{
log.info("War deployer service created");
}

@SuppressWarnings("unchecked")
@Override
public void afterPropertiesSet() throws Exception {
log.info("Starting WarDeployer");
// create the job and schedule it
future = (ScheduledFuture<DeployJob>) scheduler.scheduleAtFixedRate(new DeployJob(), 60000L, checkInterval, TimeUnit.MILLISECONDS);
// check the deploy from directory
log.debug("Webapps directory: {}", webappFolder);
File dir = new File(webappFolder);
if (!dir.exists()) {
log.warn("Source directory not found");
} else {
if (!dir.isDirectory()) {
throw new Exception("Webapps directory is not a directory");
}
}
dir = null;
@Deprecated(since = "2.0.9", forRemoval = true)
public WarDeployer() {
log.warn("Use via constructor or as a Spring bean is deprecated");
webappsDirectory = new File("webapps");
}

public WarDeployer(File webappsDirectory) {
this(webappsDirectory, false);
}

public WarDeployer(File webappsDirectory, boolean expandWars) {
log.info("Starting WarDeployer - webapps directory: {}", webappsDirectory.getAbsolutePath());
// set the webapp folder
this.webappsDirectory = webappsDirectory;
// expand wars if so requested
if (expandWars) {
log.debug("Deploying wars");
log.debug("Deploying wars, not starting applications");
deploy(false);
}
try {
// check for an embedded jee server
LoaderBase jeeServer = applicationContext.getBean(LoaderBase.class);
// lookup the jee container
if (jeeServer != null) {
log.info("JEE server was found: {}", jeeServer.toString());
}
} catch (Exception e) {
}
// create the job and schedule it
future = (ScheduledFuture<?>) scheduler.scheduleAtFixedRate(() -> {
log.debug("Starting scheduled deployment of wars");
deploy(true);
}, 60000L, checkInterval, TimeUnit.MILLISECONDS);
}

private void deploy(boolean startApplication) {
Expand All @@ -112,10 +96,8 @@ private void deploy(boolean startApplication) {
String application = null;
// file name
String applicationWarName = null;
// look for web application archives
File dir = new File(webappFolder);
// get a list of wars
File[] files = dir.listFiles(new DirectoryFilter());
File[] files = webappsDirectory.listFiles(new DirectoryFilter());
for (File f : files) {
// get the war name
applicationWarName = f.getName();
Expand All @@ -130,10 +112,10 @@ private void deploy(boolean startApplication) {
log.debug("Application name: {}", application);
// setup context
String contextPath = '/' + application;
String contextDir = webappFolder + contextPath;
String contextDir = webappsDirectory.getAbsolutePath() + contextPath;
log.debug("Web context: {} context directory: {}", contextPath, contextDir);
// verify this is a unique app
File appDir = new File(dir, application);
File appDir = new File(webappsDirectory, application);
if (appDir.exists()) {
if (appDir.isDirectory()) {
log.debug("Application directory exists");
Expand All @@ -144,7 +126,7 @@ private void deploy(boolean startApplication) {
} else {
log.debug("Unwaring and starting...");
// un-archive it to app dir
FileUtil.unzip(webappFolder + '/' + applicationWarName, contextDir);
FileUtil.unzip(webappsDirectory.getAbsolutePath() + '/' + applicationWarName, contextDir);
// load and start the context
if (startApplication) {
// get the webapp loader from jmx
Expand All @@ -158,7 +140,7 @@ private void deploy(boolean startApplication) {
}
}
// remove the war file
File warFile = new File(dir, applicationWarName);
File warFile = new File(webappsDirectory, applicationWarName);
if (warFile.delete()) {
log.debug("{} was deleted", warFile.getName());
} else {
Expand All @@ -169,26 +151,18 @@ private void deploy(boolean startApplication) {
}
appDir = null;
}
dir = null;
// reset sentinel
deploying.set(false);
}
}

@Override
public void destroy() throws Exception {
public void stop() throws Exception {
if (future != null) {
future.cancel(true);
}
scheduler.shutdownNow();
}

@SuppressWarnings("null")
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}

public void setCheckInterval(int checkInterval) {
this.checkInterval = checkInterval;
}
Expand All @@ -197,24 +171,6 @@ public int getCheckInterval() {
return checkInterval;
}

public String getWebappFolder() {
return webappFolder;
}

public void setWebappFolder(String webappFolder) {
this.webappFolder = webappFolder;
}

/**
* Whether or not to expand war files prior to start up.
*
* @param expandWars
* to expand or not
*/
public void setExpandWars(boolean expandWars) {
this.expandWars = expandWars;
}

/**
* Returns the LoaderMBean.
*
Expand Down Expand Up @@ -263,13 +219,11 @@ public boolean accept(File dir, String name) {
}
}

private class DeployJob implements Runnable {

public void run() {
log.debug("Starting scheduled deployment of wars");
deploy(true);
}

@Deprecated(since = "2.0.9", forRemoval = true)
@SuppressWarnings("null")
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.warn("This method is deprecated and should not be used; instances are created and controlled internally via TomcatLoader");
}

}
8 changes: 0 additions & 8 deletions server/src/main/server/conf/jee-container.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,6 @@
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd">

<!-- War deployer -->
<bean id="warDeployer" class="org.red5.server.tomcat.WarDeployer">
<property name="checkInterval" value="${war.deploy.server.check.interval}"/>
<property name="webappFolder" value="${red5.root}/webapps"/>
<!-- Expand war files prior to startup of the remaining services -->
<property name="expandWars" value="true" />
</bean>

<!--
The tomcat connectors may be blocking or non-blocking. Select between either option via the protocol property.
Blocking I/O:
Expand Down

0 comments on commit 575fe1b

Please sign in to comment.