log
*/
- protected static Logger log = Red5LoggerFactory.getLogger(ContextLoader.class);
+ protected static Logger log = LoggerFactory.getLogger(ContextLoader.class);
/**
* Spring Application context
diff --git a/common/src/main/java/org/red5/server/LoaderBase.java b/common/src/main/java/org/red5/server/LoaderBase.java
index 68d925352..f9e010abd 100644
--- a/common/src/main/java/org/red5/server/LoaderBase.java
+++ b/common/src/main/java/org/red5/server/LoaderBase.java
@@ -11,10 +11,11 @@
import java.util.HashMap;
import java.util.Map;
-import org.red5.logging.Red5LoggerFactory;
import org.red5.server.api.IApplicationContext;
import org.red5.server.api.IApplicationLoader;
+import org.red5.server.net.sse.ISSEService;
import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
@@ -28,7 +29,7 @@
*/
public abstract class LoaderBase implements ApplicationContextAware {
- private static Logger log = Red5LoggerFactory.getLogger(LoaderBase.class);
+ private static Logger log = LoggerFactory.getLogger(LoaderBase.class);
/**
* We store the application context so we can access it later.
@@ -50,6 +51,20 @@ public abstract class LoaderBase implements ApplicationContextAware {
*/
protected String webappFolder = null;
+ /**
+ * Singleton instance.
+ */
+ protected static LoaderBase instance;
+
+ /**
+ * Getter for the singleton instance.
+ *
+ * @return LoaderBase instance
+ */
+ public static LoaderBase getInstance() {
+ return instance;
+ }
+
/**
* Getter for the application loader.
*
@@ -174,4 +189,13 @@ public void removeContext(String path) {
throw new UnsupportedOperationException();
}
+ /**
+ * Returns the SSE service if available.
+ *
+ * @return the SSE service
+ */
+ public ISSEService getSSEService() {
+ throw new UnsupportedOperationException("Unimplemented method 'getSSEService'");
+ }
+
}
diff --git a/common/src/main/java/org/red5/server/api/IApplicationLoader.java b/common/src/main/java/org/red5/server/api/IApplicationLoader.java
index 2dc5136fa..ba05b0965 100644
--- a/common/src/main/java/org/red5/server/api/IApplicationLoader.java
+++ b/common/src/main/java/org/red5/server/api/IApplicationLoader.java
@@ -37,4 +37,5 @@ public interface IApplicationLoader {
* @return application context
*/
public ApplicationContext getRootContext();
+
}
diff --git a/common/src/main/java/org/red5/server/net/servlet/ServletUtils.java b/common/src/main/java/org/red5/server/net/servlet/ServletUtils.java
index e625543df..8339c515a 100644
--- a/common/src/main/java/org/red5/server/net/servlet/ServletUtils.java
+++ b/common/src/main/java/org/red5/server/net/servlet/ServletUtils.java
@@ -15,10 +15,10 @@
import java.util.Collections;
import java.util.List;
-import jakarta.servlet.http.HttpServletRequest;
-
-import org.red5.logging.Red5LoggerFactory;
import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import jakarta.servlet.http.HttpServletRequest;
/**
* ServletUtils class.
@@ -27,7 +27,7 @@ */ public class ServletUtils { - private static Logger log = Red5LoggerFactory.getLogger(ServletUtils.class); + private static Logger log = LoggerFactory.getLogger(ServletUtils.class); /** * Default value is 2048. diff --git a/common/src/main/java/org/red5/server/net/sse/ISSEService.java b/common/src/main/java/org/red5/server/net/sse/ISSEService.java new file mode 100644 index 000000000..2f0457e1d --- /dev/null +++ b/common/src/main/java/org/red5/server/net/sse/ISSEService.java @@ -0,0 +1,144 @@ +/* + * RED5 Open Source Flash Server - https://github.com/Red5/ Copyright 2006-2023 by respective authors (see below). All rights reserved. Licensed under the Apache License, Version + * 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless + * required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions and limitations under the License. + */ + +package org.red5.server.net.sse; + +import java.util.Collection; + +import org.red5.server.api.scope.IScope; +import org.red5.server.api.scope.IScopeService; + +/** + * Interface for Server-Sent Events service operations. + * This interface provides methods for managing SSE connections and broadcasting events. + * + * @author Paul Gregoire (mondain@gmail.com) + */ +public interface ISSEService extends IScopeService { + + /** ConstantBEAN_NAME="sseService"
*/
+ public static String BEAN_NAME = "sseService";
+
+ /**
+ * Broadcasts a message to all connected SSE clients.
+ *
+ * @param message The message to broadcast
+ * @return The number of successful sends
+ */
+ int broadcastMessage(String message);
+
+ /**
+ * Broadcasts an event to all connected SSE clients.
+ *
+ * @param event The event type
+ * @param message The message to broadcast
+ * @return The number of successful sends
+ */
+ int broadcastEvent(String event, String message);
+
+ /**
+ * Broadcasts an SSE event to all connected clients.
+ *
+ * @param sseEvent The SSE event to broadcast
+ * @return The number of successful sends
+ */
+ int broadcastEvent(SSEEvent sseEvent);
+
+ /**
+ * Broadcasts a message to all clients in a specific scope.
+ *
+ * @param scope The scope to broadcast to
+ * @param message The message to broadcast
+ * @return The number of successful sends
+ */
+ int broadcastToScope(IScope scope, String message);
+
+ /**
+ * Broadcasts an event to all clients in a specific scope.
+ *
+ * @param scope The scope to broadcast to
+ * @param event The event type
+ * @param message The message to broadcast
+ * @return The number of successful sends
+ */
+ int broadcastEventToScope(IScope scope, String event, String message);
+
+ /**
+ * Broadcasts an SSE event to all clients in a specific scope.
+ *
+ * @param scope The scope to broadcast to
+ * @param sseEvent The SSE event to broadcast
+ * @return The number of successful sends
+ */
+ int broadcastEventToScope(IScope scope, SSEEvent sseEvent);
+
+ /**
+ * Sends a message to a specific connection.
+ *
+ * @param connectionId The connection ID
+ * @param message The message to send
+ * @return true if the message was sent successfully
+ */
+ boolean sendToConnection(String connectionId, String message);
+
+ /**
+ * Sends an event to a specific connection.
+ *
+ * @param connectionId The connection ID
+ * @param event The event type
+ * @param message The message to send
+ * @return true if the event was sent successfully
+ */
+ boolean sendEventToConnection(String connectionId, String event, String message);
+
+ /**
+ * Sends an SSE event to a specific connection.
+ *
+ * @param connectionId The connection ID
+ * @param sseEvent The SSE event to send
+ * @return true if the event was sent successfully
+ */
+ boolean sendEventToConnection(String connectionId, SSEEvent sseEvent);
+
+ /**
+ * Gets all active SSE connections.
+ *
+ * @return Collection of all active connections
+ */
+ CollectiongetScopeService.
+ *getScopeService
* * @param scope a {@link org.red5.server.api.scope.IScope} object * @param intf a {@link java.lang.Class} object @@ -276,7 +276,7 @@ public static Object getScopeService(IScope scope, Class> intf, Class> defau } /** - *getScopeService.
+ *getScopeService
* * @param scope a {@link org.red5.server.api.scope.IScope} object * @param intf a {@link java.lang.Class} object diff --git a/server/pom.xml b/server/pom.xml index 230dad912..dfa5d416d 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -139,7 +139,6 @@- * Web scope is special scope that is aware of servlet context and represents scope of a Red5 application within a servlet container (or application server) such as Tomcat, Jetty or JBoss. - *
- *- * Web scope is aware of virtual hosts configuration for Red5 application and is the first scope that instantiated after Red5 application gets started. - *
- *
- * Then it loads virtual hosts configuration, adds mappings of paths to global scope that is injected thru Spring IoC context file and runs initialization process.
+ * Web scope is special scope that is aware of servlet context and represents scope of a Red5 application within a
+ * servlet container (or application server) such as Tomcat, Jetty or JBoss.
+ *
+ * Web scope is aware of virtual hosts configuration for Red5 application and is the first scope that instantiated
+ * after Red5 application gets started.
+ *
+ * Then it loads virtual hosts configuration, adds mappings of paths to global scope that is injected thru Spring IoC
+ * context file and runs initialization process.
*
ProviderService class.
@@ -40,7 +40,7 @@ */ public class ProviderService implements IProviderService { - private static final Logger log = Red5LoggerFactory.getLogger(ProviderService.class); + private static final Logger log = LoggerFactory.getLogger(ProviderService.class); // whether or not to support FCS/FMS/AMS live-wait (default to off) private boolean liveWaitSupport; diff --git a/server/src/main/java/org/red5/server/tomcat/TomcatApplicationContext.java b/server/src/main/java/org/red5/server/tomcat/TomcatApplicationContext.java index 102db3b88..f972445ea 100644 --- a/server/src/main/java/org/red5/server/tomcat/TomcatApplicationContext.java +++ b/server/src/main/java/org/red5/server/tomcat/TomcatApplicationContext.java @@ -7,17 +7,17 @@ package org.red5.server.tomcat; -import jakarta.servlet.ServletContext; - import org.apache.catalina.Context; import org.apache.catalina.LifecycleState; import org.apache.catalina.core.StandardContext; -import org.red5.logging.Red5LoggerFactory; import org.red5.server.api.IApplicationContext; import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.web.context.ConfigurableWebApplicationContext; import org.springframework.web.context.WebApplicationContext; +import jakarta.servlet.ServletContext; + /** * Class that wraps a Tomcat webapp context. * @@ -28,7 +28,7 @@ public class TomcatApplicationContext implements IApplicationContext { /** Constantlog
*/
- protected static Logger log = Red5LoggerFactory.getLogger(TomcatApplicationContext.class);
+ protected static Logger log = LoggerFactory.getLogger(TomcatApplicationContext.class);
/** Store a reference to the Tomcat webapp context. */
private Context context;
diff --git a/server/src/main/java/org/red5/server/tomcat/TomcatConnector.java b/server/src/main/java/org/red5/server/tomcat/TomcatConnector.java
index 09b9f0137..97b36f434 100644
--- a/server/src/main/java/org/red5/server/tomcat/TomcatConnector.java
+++ b/server/src/main/java/org/red5/server/tomcat/TomcatConnector.java
@@ -19,8 +19,8 @@
import org.apache.tomcat.util.IntrospectionUtils;
import org.apache.tomcat.util.net.SSLHostConfig;
import org.apache.tomcat.util.net.SSLHostConfigCertificate;
-import org.red5.logging.Red5LoggerFactory;
import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Model object to contain a connector, socket address, and connection properties for a Tomcat connection.
@@ -29,7 +29,7 @@
*/
public class TomcatConnector {
- private static Logger log = Red5LoggerFactory.getLogger(TomcatConnector.class);
+ private static Logger log = LoggerFactory.getLogger(TomcatConnector.class);
private Connector connector;
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 369697317..dd6066aef 100644
--- a/server/src/main/java/org/red5/server/tomcat/TomcatLoader.java
+++ b/server/src/main/java/org/red5/server/tomcat/TomcatLoader.java
@@ -48,6 +48,7 @@
import org.red5.server.api.Red5;
import org.red5.server.jmx.mxbeans.ContextLoaderMXBean;
import org.red5.server.jmx.mxbeans.LoaderMXBean;
+import org.red5.server.net.sse.ISSEService;
import org.red5.server.plugin.PluginRegistry;
import org.red5.server.security.IRed5Realm;
import org.red5.server.util.FileUtil;
@@ -175,6 +176,11 @@ public boolean accept(File dir, String name) {
*/
protected boolean websocketEnabled = true;
+ /**
+ * SSE feature
+ */
+ protected boolean sseEnabled = true;
+
/**
* HTTPS/WSS feature
*/
@@ -190,6 +196,11 @@ public boolean accept(File dir, String name) {
*/
private WarDeployer deployer;
+ /**
+ * SSE service.
+ */
+ private ISSEService sseService;
+
{
// allow setting to true if we're running in Red5 Pro
if (!awaitPlugins) {
@@ -206,6 +217,8 @@ public boolean accept(File dir, String name) {
/** {@inheritDoc} */
@Override
public void afterPropertiesSet() throws Exception {
+ // set ourself as the instance
+ instance = this;
// if we are not awaiting plugins, start immediately
if (awaitPlugins) {
log.info("Awaiting plugin loading");
@@ -326,6 +339,9 @@ public void start() throws ServletException {
if (websocketEnabled) {
checkWebsocketPlugin();
}
+ if (sseEnabled) {
+ checkSSEService();
+ }
// get a reference to the current threads classloader
final ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
// root location for servlet container
@@ -356,10 +372,8 @@ public void start() throws ServletException {
// Use default webapps directory
webappFolder = FileUtil.formatPath(serverRoot, "/webapps");
}
-
// To negotiate for web-socket compression or not.
UpgradeUtil.wsAllowCompression = Boolean.valueOf(System.getProperty("ws.allow.compression", "true"));
-
System.setProperty("red5.webapp.root", webappFolder);
log.info("Application root: {}", webappFolder);
// Root applications directory
@@ -594,6 +608,9 @@ public void run() {
log.debug("Tomcat load completed");
}
+ /**
+ * Check for websocket plugin and load it if available.
+ */
private void checkWebsocketPlugin() {
// if websockets are enabled, ensure the websocket plugin is loaded
if (PluginRegistry.getPlugin(WebSocketPlugin.NAME) == null) {
@@ -615,6 +632,21 @@ private void checkWebsocketPlugin() {
}
}
+ /**
+ * Check for SSE service and load it if available.
+ */
+ private void checkSSEService() {
+ // get common context
+ ApplicationContext common = (ApplicationContext) applicationContext.getBean("red5.common");
+ // if SSE is enabled, ensure the SSE service is loaded
+ if (common.containsBean("sseService")) {
+ log.debug("SSE service was found");
+ sseService = common.getBean("sseService", ISSEService.class);
+ } else {
+ log.debug("SSE service was not found");
+ }
+ }
+
/**
* {@inheritDoc}
*
@@ -887,6 +919,32 @@ public void setWebsocketEnabled(boolean websocketEnabled) {
this.websocketEnabled = websocketEnabled;
}
+ /**
+ * Returns enabled state of SSE support.
+ *
+ * @return true if enabled and false otherwise
+ */
+ public boolean isSseEnabled() {
+ return sseEnabled;
+ }
+
+ /**
+ * Set SSE feature enabled / disabled.
+ *
+ * @param sseEnabled a boolean
+ */
+ public void setSseEnabled(boolean sseEnabled) {
+ this.sseEnabled = sseEnabled;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ISSEService getSSEService() {
+ return sseService;
+ }
+
/**
* Returns enabled state of secure support.
*
diff --git a/server/src/main/server/conf/jee-container.xml b/server/src/main/server/conf/jee-container.xml
index c141b55b4..b49fb60b4 100644
--- a/server/src/main/server/conf/jee-container.xml
+++ b/server/src/main/server/conf/jee-container.xml
@@ -18,6 +18,7 @@
-->