How to send websocket message from another custom module

Hi Everyone.

I’m hoping someone can help as it seems such a simple thing to try to do.

I have the example “how to create a websocket server” set up and running from here:

https://www.wowza.com/docs/how-to-create-a-websocket-server

public class HTTPProviderWebSocket extends HTTPProvider2Base
{
	private static final Class<HTTPProviderWebSocket> CLASS = HTTPProviderWebSocket.class;
	private static final String CLASSNAME = "HTTPProviderWebSocket";

	public static final String DATEFORMAT = "EEE, dd MMM yyyy HH:mm:ss";
	public static final int TIMER_INTERVAL = 6000;

	private FastDateFormat fastDateFormat = FastDateFormat.getInstance(DATEFORMAT, SystemUtils.gmtTimeZone, Locale.US);
	private Timer timer = null;
	private Object lock = new Object();

	// WebSocket listener
	class MyWebSocketListener extends WebSocketEventNotifyBase
	{
		@Override
		public void onCreate(IWebSocketSession webSocketSession)
		{
			//WMSLoggerFactory.getLogger(CLASS).info(CLASSNAME+"#MyWebSocketListener.onCreate["+webSocketSession.getSessionId()+"]");
		}

		@Override
		public void onDestroy(IWebSocketSession webSocketSession)
		{
			//WMSLoggerFactory.getLogger(CLASS).info(CLASSNAME+"#MyWebSocketListener.onDestroy["+webSocketSession.getSessionId()+"]");
		}
		
		

		@Override
		public void onMessage(IWebSocketSession webSocketSession, WebSocketMessage message)
		{
			// echo messages we receive back to the browser
			if (message.isText())
			{
				WebSocketMessage messageText = WebSocketMessage.createMessageText(webSocketSession.isMaskOutgoingMessages(), message.getValueString());
				webSocketSession.sendMessage(messageText);

			//	WMSLoggerFactory.getLogger(CLASS).info(CLASSNAME+"#MyWebSocketListener.onMessage["+webSocketSession.getSessionId()+"][text]: "+message.getValueString());
			}
			else if (message.isBinary())
			{
				WebSocketMessage messageBinary = WebSocketMessage.createMessageBinary(webSocketSession.isMaskOutgoingMessages(), message.getBuffer(), message.getOffset(), message.getLen());
				webSocketSession.sendMessage(messageBinary);

			//	WMSLoggerFactory.getLogger(CLASS).info(CLASSNAME+"#MyWebSocketListener.onMessage["+webSocketSession.getSessionId()+"][binary]: #"+BufferUtils.encodeHexString(message.getBuffer(), message.getOffset(), message.getLen()));
			}
		}
		
		
		
	}

	
	
	
	
	class MyTimerTask extends TimerTask
	{
		private IVHost vhost = null;
		private WebSocketContext webSocketContext = null;

		MyTimerTask(IVHost vhost)
		{
			this.vhost = vhost;
			this.webSocketContext = vhost.getWebSocketContext();
		}

		@Override
		public void run()
		{
			String messageStr = Server.getInstance().getVersion()+" date:"+fastDateFormat.format(new Date())+" GMT";

			// broadcast message to all active sessions attached to this HTTPProvider
			WebSocketMessage messageText = WebSocketMessage.createMessageText(webSocketContext.isMaskOutgoingMessages(), messageStr);
			broadcastWebSocketMessage(messageText);

			//WMSLoggerFactory.getLogger(CLASS).info(CLASSNAME+"#MyTimerTask.run: "+messageStr);
		}
		
		
		
	}

	
	
			
	
	
	
	public void onHTTPRequest(IVHost vhost, IHTTPRequest req, IHTTPResponse resp)
	{
		if (!doHTTPAuthentication(vhost, req, resp))
			return;

		synchronized(lock)
		{
			// create timer task on first connection
			if (this.timer == null)
			{
				this.timer = new Timer();
				timer.scheduleAtFixedRate(new MyTimerTask(vhost), TIMER_INTERVAL, TIMER_INTERVAL);
			}
		}

		// is this an upgrade request
		if (req.isUpgradeRequest())
		{
			// it this an websocket upgrade request
			String upgradeType = req.getHeader("upgrade");
			if (upgradeType != null && upgradeType.equalsIgnoreCase(IWebSocketSession.HTTPHEADER_NAME))
			{
				// set response header to accept the upgrade
				resp.setHeader("Upgrade", IWebSocketSession.HTTPHEADER_NAME);
				resp.setHeader("Connection", "Upgrade");

				// set the security hash
				String webSocketKey = req.getHeader(IWebSocketSession.HTTPHEADER_SECKEY);
				if (webSocketKey != null)
				{
					String digestStr = WebSocketUtils.createSecResponse(webSocketKey);
					if (digestStr != null)
						resp.setHeader(IWebSocketSession.HTTPHEADER_SECACCEPT, digestStr);
				}

				// set 101 response code to accept upgrade request
				resp.setResponseCode(101);

				// insert WebSocket listener for this session
				resp.setUpgradeRequestProtocol(IHTTPResponse.UPGRADE_PROTOCOL_WEBSOCKETS, new MyWebSocketListener());
			}
			else
				resp.setResponseCode(404); // return 404 if not websocket upgrade request
		}
		else
			resp.setResponseCode(404); //return 404 if not upgrade request
	}
}

And I can see it has a section to broadcast a message with the server version running on a timer and calling:

WebSocketMessage messageText = WebSocketMessage. *createMessageText* (webSocketContext.isMaskOutgoingMessages(), messageStr);
broadcastWebSocketMessage(messageText);

And I can also see it has an example of some code that will respond to an incoming message with an outgoing message with the same text.

What I’m very much failing to understand how to do - is to use this websocket server to send useful messages from within other custom modules.

So for example I have a ModuleListenWebRTCSession class that contains an onAppStart function and an onRTPSessionCreate function

public class ModuleListenWebRTCSession extends ModuleBase {
	
	private static String[] securityPlayIPBlackListArray;
	
	public void onAppStart(IApplicationInstance appInstance) { 
		String securityPlayIPBlackListString = appInstance.getProperties().getPropertyStr("securityPlayIPBlackList","");
		if (!securityPlayIPBlackListString.isEmpty()) {
				securityPlayIPBlackListArray = securityPlayIPBlackListString.trim().split("\\s*,\\s*");
		}
		else {
			securityPlayIPBlackListArray = null;
		}	
	}
	
	public void onRTPSessionCreate(RTPSession rtpSession) {
		
		 if (rtpSession.isWebRTC() && rtpSession.getRTSPStream().getModeName() == "PLAY") {
			 Map<String, Object> userData = (Map<String, Object>)rtpSession.getWebRTCSession().getCommandRequest().getJSONEntries().get("userData");
			 String ip = rtpSession.getIp();
			 List<String> securityPlayIPBlackList = Arrays.asList(securityPlayIPBlackListArray);
			 
			 if(securityPlayIPBlackList.contains(ip)){
				 getLogger().info("IP blocked: "+ip);  
				 rtpSession.rejectSession();
		        }		 
		 }	
	}
}

All I want to do is send “Hello World” to the clients connected to the websocket HTTP provider from within either of the onAppStart or onRTPSessionCreate functions as an example.

If anyone has any suggestions I would really appreciate it.

Thanks in advance

Pip

You can use websocket or even other technologies. Take a look as pub sub , message brokers type cloud services that can help you communicate between server and client as independent entities. I personally prefer this to forcing the media server with additional responsibility of handing normal client communication (not that it shouldn’t be done).

If you do go the websocket way…then just build it as a independent class and create a instance of it in the application with a static method for sending messages. Then access this static method from any module within wowza and pass in the message to send.

Hope it is clear.

Hi Connessione.

Thanks for responding. I’m normally a full stack web developer so just getting started on Java in order to get on top of some issues my client is having with webRTC on wowza having some stability issues.

I already have separate a websockets based messenger service within my clients website, so I’m familiar with building that end of things. In this case I’m hoping to have the media server relaying messages regarding the state of the server, Apps and Streams etc to the webserver (I’ve already got those two hooked up and listening to each other via the “how to create a websocket server” example). Then the webserver rather than the media server will relay on the messages to users of the site as needed.

It’s really your second paragraph that you make sound so easy that I need help with. I’m guessing it is, if you’ve had more than two weeks of learning Java by trying to get the Wowza examples working.

So - Build it as an independent class? Totally not in either the HTTPProviderWebSocket class nor my ModuleListenWebRTCSession class - or do you mean one of these class within a class things that I haven’t got my head around yet? Does it need to extend something?

Create an instance of it in the application with a static method for sending messages…
Got to be honest I’m not yet entirely sure how to create an instance of something.

Then access this static method from any module… And again not 100% sure…

If you can help to fill in the vast gaps in my knowledge I would be enormously grateful.

Thanks in advance

Pip

If at first you don’t succeed try try again.

Just in case this is useful to anyone else… To add the ability to broadcast messages from other custom modules via the WebSocket provider example from here: https://www.wowza.com/docs/how-to-create-a-websocket-server.
(You’ll have to excuse my probable lack of proper Java terminology)

I’ve added a new global variable at the top of the HTTPProviderWebSocket class:

public static List<IWebSocketSession> websocketlist = null;

I’ve then added:

websocketlist = getWebSocketSessions();

to each of the onCreate and onDestroy functions within the MyWebSocketListener to give:

class MyWebSocketListener extends WebSocketEventNotifyBase
	{
		@Override
		public void onCreate(IWebSocketSession webSocketSession)
		{
			websocketlist = getWebSocketSessions();
		}

		@Override
		public void onDestroy(IWebSocketSession webSocketSession)
		{
			websocketlist = getWebSocketSessions();
		}...... etc

I’ve then created a new function myBroadcastWebSocketMessage:

public static void myBroadcastWebSocketMessage(String messageStr) {
		WebSocketMessage messageText = WebSocketMessage.createMessageText(false, messageStr);
		
		if (websocketlist != null && !websocketlist.isEmpty()) {
			Iterator<IWebSocketSession> iterator = websocketlist.iterator();
			while (true) {
				if (iterator.hasNext()) {
					IWebSocketSession iWebSocketSession = iterator.next();
					if (!iWebSocketSession.isActive())
						continue;
					iWebSocketSession.sendMessage(messageText);
					continue;
				}
				else { break; }
					
				}
			}
		}

And then within my other custom module - in my case within a onRTPSessionCreate function:

You can send a message to all your websockets clients with:

String messageStr = "Hello World";
HTTPProviderWebSocket.myBroadcastWebSocketMessage(messageStr);

The idea is then to build this out to use the websocket channel to communicate various states on the server in real time back to my webserver (which will be the websocket client via a node process). i.e. App Start, Published Connection / Disconnection etc.

Hopefully helpful for someone else.

Thanks

Pip

Cool good to know you got it working the way you wanted, it :slightly_smiling_face: