Extend the abstract WebSocketApplication class, found in the com.prosyst.mbs.services.websockets.application package
Provide a defaultSubProtocol, alias and name, which are Strings. The alias must be the same as the URI used when registering the service
You can add multiple subProtocols if you need your application to support more than one in the supportedSubProtocols Set (by using supportedSubProtocols.add("someSubProtocol") in the constructor)
Attach one or more WebSocketListener instance(s) in the manageWebSocketListeners(WebSocketListenerManager webSocket)
Recommended: Create your own WebSocketListener implementation made specifically for listening (extend the WebSocketListenerAdapter so that you can use only the methods required by your application), this way each webSocket connection has its own listener
Attach the WebSocketApplication as a listener, so that you can use it as a global point of listening, useful only in rare cases, generally you would want to have a different listener for every WebSocket instance
When using the SocketSender it has its own WebSocketListener, which is added automatically.
The main purpose of the manageWebSocketListeners(WebSocketListenerManager webSocket) method is to attach any listeners before the webSocket handshake negotiation is done, this way you will not miss any messages, like you could if you attach the listener in any of the other methods (shown below in Working with WebSocketListeners)
@Override connected() (not needed when working with SocketSender)
DemoWebSocketApplication.java:
// Demo class which extends the abstract class WebSocketApplication public class DemoWebSocketApplication extends WebSocketApplication {
public static final String ALIAS = "/demo/example";
private static final String DEFAULT_SUB_PROTOCOL = "demo_defaultSubProtocol"; private static final String NAME = "demo_name";
public DemoApplication() { super(DEFAULT_SUB_PROTOCOL, ALIAS, NAME); //you can add multiple subProtocols //supportedSubProtocols.add("someSubProtocol" ); }
//Methods below are optional if you are working with SocketSender //Attach WebSocketListener(s) to your WebSocketApplication @Override public void manageWebSocketListeners(WebSocketListenerManager webSocket) { //Option a. - we use our own DemoWebSocketListener class which extends the WebSocketListenerAdapter //and overrides the methods we require webSocket.addListener( new DemoWebSocketListener());
//Option b. - the WebSocketApplication itself is a WebSocketListener //so we can attach it and use it as a global point of listening //for some minor action webSocket.addListener( this); }
//In connected() you obtain the webSocket so that you can work with it @Override public void connected(WebSocket webSocket) { //doSomething } }
Registering the OSGi Service
In the start method instantiate the DemoWebSocketApplication
Put the key WebSocketApplication.URI with value DemoWebSocketApplication.ALIAS in the properties dictionary
Register the service with those properties
DemoActivator.java:
// Demo Activator public class DemoActivator implements BundleActivator {
public void stop(BundleContext context) throws Exception { if (demoServiceRegistration != null) { demoServiceRegistration.unregister(); demoServiceRegistration = null; } } }
Using the Bosch IoT Gateway Software WebSockets
Use the default Bosch IoT Gateway Software WebSocket implementation
If you want to listen for messages sent by the connected webSockets you will have to @Override manageWebSocketListeners(WebSocketListenerManager webSocket) in your WebSocketApplication and add your listener here which can be:
The WebSocketApplication itself, this way it will be used as a global listener, useful if you have one general event for which you don't want to create an individual WebSocketListener for every webSocket connection (for example if you drop the connection immediately after the first message)
A WebSocketListener implementation made specifically for listening (we extend the WebSocketListenerAdapter so that we can use only the methods required by our application), if we want to extract the listening logic, this way each webSocket connection has its own listener
Now that we have a listener attached, we can use the methods for listening that our application requires (for example only onMessage(WebSocket webSocket, String text))
In your WebSocketApplication you can @Override connected(), so that you can obtain the webSocket as soon as it's available, or you if add WebSocketApplication as WebSocketListener you can do that in any of the on.. methods (onMessage(WebSocket webSocket, String text) for example), if you need to hold the webSocket
DemoWebSocketApplication.java:
// Demo class which extends the abstract class WebSocketApplication and uses the SimpleWebSocket public class DemoWebSocketApplication extends WebSocketApplication {
public final static String ALIAS = "/demo/example";
private final String defaultSubProtocol = "demo_defaultSubProtocol"; private final String name = "demo_name";
public DemoApplication() { super(defaultSubProtocol, ALIAS, name); }
@Override public void manageWebSocketListeners(WebSocketListenerManager webSocket) { //Option 1: the WebSocketApplication itself is a WebSocketListener //so we can attach it and use it as a global point of listening webSocket.addListener( this);
//Option 2: we use our own DemoWebSocketListener class which extends the WebSocketListenerAdapter //and overrides the methods we require webSocket.addListener( new DemoWebSocketListener()); }
//override connected() if you want to obtain the webSocket as soon as it's available @Override public void connected(WebSocket webSocket) { //can save this webSocket in some collection or just send message or whatever you want }
//override any method which your application requires if it has been added as a WebSocketListener @Override public void onMessage(WebSocket webSocket, String text) { //doSomething, or obtain the webSocket if you need it and haven't obtained it inconnected } . . . }
Your WebSocketApplication is working!
Use your own WebSocket implementation to extend SimpleWebSocket
Use your own WebSocket implementation which extends the SimpleWebSocket, same approach as option b)
Create your own WebSocket by extending the SimpleWebSocket or WebSocket directly (the second approach is very dangerous, thus not recommended)
@Override connected() in your WebSocket implementation if you need to send messages immediately after the connection has been established.
Add the additional logic which your WebSocket implementation requires
In your WebSocketApplication override the doCreateWebSocket(final WebSocketHandler handler, String individualUri, String sessionId, WebSocketId id, String applicationUri, String subProtocol) method and return a new instance of your WebSocket implementation
Override manageWebSocketListeners(WebSocketListenerManager webSocket) in your WebSocketApplication and attach a WebSocketListener (same options are available as shown in b.). Optionally In your WebSocketApplication you can override the connected() method, so that you can obtain the webSocket as soon as it's available
DemoWebSocket.java:
//Demo of a custom WebSocket implementation //You can add fields, constructors, methods and any custom logic, that your application requires public class DemoWebSocket extends SimpleWebSocket {
super(webSocketHandler, individualUri, sessionId, id, applicationUri, subProtocol); } @Override public void connected() { //send messages immediately after establishing the connection if it is required }
. . . }
DemoWebSocketApplication.java:
// Demo class which extends the abstract class WebSocketApplication and uses a different WebSocket implementation public class DemoWebSocketApplication extends WebSocketApplication {
public final static String ALIAS = "/demo/example";
private final String defaultSubProtocol = "demo_defaultSubProtocol"; private final String name = "demo_name";
public DemoApplication() { super(defaultSubProtocol, ALIAS, name); }
return new DemoWebSocket(handler, individualUri, sessionId, id, applicationUri, subProtocol); }
@Override public void manageWebSocketListeners(WebSocketListenerManager webSocket) { //Use any of the three options mentioned in b. to attach a WebSocketListener webSocket.addListener(...); }
//override connected() if you want to obtain the webSocket as soon as it's available @Override public void connected(WebSocket webSocket) { //can save this webSocket in some collection or just send message or whatever you want }
//override any method which your application requires if it has been added as a WebSocketListener @Override public void onMessage(WebSocket webSocket, String text) { //doSomething, or obtain the webSocket if you have not done so in connected } . . . }
Use SocketSender
Use SocketSender:
Create a class which extends the abstract SocketSender<V extends WebSocketApplication>
Overrides the createSocketSender() method in the DemoWebSocketApplication
Return a new instance of your SocketSender class in the createSocketSender() factory method (this way each SocketSender will have its own WebSocket)
In your SocketSender class override the connected(), if you want to write a response to the clients first, then do it in the connected() method body, if not, leave it empty
The SocketSender is responsible for sending messages (binary or text) or receiving messages via its on... methods (onMessage(String text), etc)
The SocketSender has all the listening methods (on... methods, same as the listener but without the WebSocket parameter). You can use only the methods your application requires (for example only onMessage(String text))
Optional: You can attach additional WebSocketListener instances in the manageWebSocketListeners(WebSocketListenerManager webSocket) if required.
Easier to use compared to the other options but comes with higher performance overhead.
DemoSocketSender.java:
// DemoSocketSender class to demonstrate the SocketSender public class DemoSocketSender extends SocketSender<DemoWebSocketApplication> {
public final static String ALIAS = "/demo/example";
public DemoSocketSender(DemoWebSocketApplication demoWebSocketApplication) { super(demoWebSocketApplication); }
@Override public void connected() { //send something here if your application requires it sendText( "Welcome to the DemoWebSocketApplication!"); }
//override any method which your application requires @Override public void onMessage( String text) { //doSomething } . . . }
DemoWebSocketApplication.java:
// Demo class which extends the abstract class WebSocketApplication and uses a SocketSender public class DemoWebSocketApplication extends WebSocketApplication {
public final static String ALIAS = "/demo/example";
private final String defaultSubProtocol = "demo_defaultSubProtocol"; private final String name = "demo_name";
public DemoApplication() { super(defaultSubProtocol, ALIAS, name); }
@Override public SocketSender createSocketSender() { return new DemoSocketSender( this); } }
Additional Information
Using the WebSocketListeners
A WebSocketListener can be attached to a WebSocket at any given moment as mentioned above - in connected(WebSocket webSocket) method or in any of the on... methods you can call webSocket.add(listener)
The SocketSender has an additional constructor SocketSender(V webSocketApplication, WebSocket webSocket) which allows you to create a SocketSender for a WebSocket at any point in time. In this approach you have to do the following:
Make sure your WebSocket is connected, if it is not the SocketSender constructor will throw an IllegalArgumentException
Create the SocketSender with the WebSocket as a constructor parameter
Attach the WebSocketListener of the SocketSender to your WebSocket
You can do this in any of the on... methods or in connected() or even after that, if you have have obtained the WebSocket instance(s) already.
DemoWebSocketApplication.java:
//Creating a SocketSender with webSocket example public class DemoWebSocketApplication extends WebSocketApplication { . . .
@Override public void onMessage(WebSocket webSocket, String text) { DemoSocketSender demoSocketSender = new DemoSocketSender( this, webSocket); WebSocketListener webSocketListener = demoSocketSender.getWebSocketListener();
webSocket.add(webSocketListener);
//doSomethingElse }
. . . }
Using the FrameTransmitter
The FrameTransmitter is used to optimize the way a single frame is sent to multiple clients.
It is available via the WebSocketApplication.getFrameTransmitter method or it's up for direct use in your WebSocketApplication as it is a protected field in the base class
It has all the transmit... methods which work on Sets of classes which extended the WebSocketSender which are the SocketSender and WebSocket objects (default or custom implementations). Those methods generally look like this: transmitMessage(Set<? extends WebSocketSender> clients, byte[] message) - the first parameter is the Set and the second is the message - binary or text, continuationStart or fragment
It works the same way as the send... methods on the SocketSender and WebSocket however it optimizes the process by creating a frame from the message (textFrame from sendText(String text) and so on) and sending that frame to the Set of WebSocketSender objects
The transmit methods attempt to send the frame to every (WebSocketSender) object of the Set and shows a warning log message if the sending was unsuccessful
You can also do that manually by creating frames (TextFrame from text, BinaryFrame from data[], etc, see package com.prosyst.mbs.services.websockets.frames for more info on each type of frame) and then using the send... method on each WebSocket or SocketSender individually if you desire, but the FrameTransmitter is there to make this easier
For full methods description see the com.prosyst.mbs.services.websockets.FrameTransmitter javadoc.
Closing a WebSocket
A WebSocket can be closed with a Code (a utility class found in the package com.prosyst.mbs.services.websockets, check the WebSocket Protocol RFC-6455) and a Reason (which is a text message), however you don't have to provide either
When a WebSocket is closed via one of its close methods:
webSocket.close() - in this case the WebSocket closes with Code with value 1000 which corresponds to Normal Close (from the WebSocket Protocol RFC-6455) and no reason
webSocket.close(Code code) - in this case the WebSocket closes with the provided code and no reason
webSocket.close(Code code, String reason) - in this case the WebSocket closes with the provided code and reason
When closing the WebSocket, if an exception or an error occurs, the terminateConnectionSilently() method is invoked on the WebSocket and it closes the WebSocket without throwing additional exceptions (thus "silently") and releases all the resources the WebSocket instance held
When a WebSocket closes an onClose(Code code, String reason) method is invoked on your listeners
you should release all the resources it holds, to prevent cause memory leaks and additional problems
the onClose(WebSocket webSocket, Code code, String reason) method is invoked on the all WebSocketListener objects attached to this WebSocket instance
the onClose(Code code, String reason) method is invoked on you SocketSender. Unlike the WebSocketListener the SocketSender holds a WebSocked instance, so it is not provided as a parameter on its onClose method
All the parameters provided in all variants of the onClose methods are:
webSocket - the WebSocket instance which is closing
code - the Code with which the WebSocket has been closed. The code also indicates who closed the WebSocket instance, via the isRemote() method which returns a boolean value : if it returns true then the WebSocket instance has been closed by the client, if it returns false then the WebSocket instance has been closed by the Bosch IoT Gateway Software WebSocket Server or the corresponding WebSocketApplication
reason - the reason why the webSocket has been closed (can be null)
If an exception occurs during the normal workflow of the WebSocket instance then the onError(Exception exc, boolean fatal) method is invoked and it attempts to notify all of your listeners via their own onError(...) methods
the onError(WebSocket webSocket, Exception exc, boolean fatal) method is invoked on all WebSocketListener objects attached to this WebSocket instance
the onError(Exception exc, boolean fatal) method is invoked on you SocketSender. Unlike the WebSocketListener the SocketSender holds a WebSocked instance, so it is not provided as a parameter on its onError method
All the parameters provided in all variants of the onError methods are:
webSocket - the WebSocket instance on which an exception has occurred
exc - the Exception which has occurred
fatal - indicates if the Exception was fatal or not, by default all Exceptions are fatal. If the exception is fatal then the connection will be terminated. If the exception is non-fatal then the connection will not be terminated
Ping Pong Mechanism
Our WebSockets Server implements a default server initiated Ping-Pong mechanism. The server sends a Ping frame to all connected WebSocket instances at a configurable predefined time. If a Pong frame is not received from a given WebSocket instance within the same time interval, it is closed.
The System Ping is not useful for some WebSocketApplication services. It can be disabled a custom pinging implementation can be used. To disable it the notUsingSystemPing() must be overridden in your WebSocketApplication
public class DemoWebSocketApplication extends WebSocketApplication { . . .
/* return true if you wish to disable System Ping by default this method returns false , which indicates that you are going to use the System Ping */ @Override public boolean notUsingSystemPing() { return true; } . . . }
When the client sends the specific keep alive (Heartbeat) message (which starts with &#!^@#$) the server ignores it and will not notify the WebSockets Listeners, because this message is used only to verify if the connection is still open.
Handling WebSocket Exceptions
In the package com.prosyst.mbs.services.websockets.exceptions you will find the ProtocolErrorException, unlike default exceptions the ProtocolErrorException is thrown with reason and code.
The idea of this exception is mainly to be thrown in most cases in your WebSocketApplication and to be handled at the root level of your exception handling logic. The reason for that is because the ProtocolErrorException holds more specific information:
getCode() returns the Code of the exception thrown
getReason() returns the message(reason) of the exception thrown
Additionally you can use getCloseFrame() which returns the CloseFrame object which holds both the code and reason (if a reason is available)
Monitoring the Bosch IoT Gateway Software WebSockets Implementation
You can obtain a large variety of information about the current state of the Bosch IoT Gateway Software WebSockets Server, registered WebSocketApplication services, created and recently closed WebSocket instances, running StandAlone HTTP Servers, etc. To do so, track the WebSocketsServerInfo service (for more details see the com.prosyst.mbs.services.websockets.info package).
WebSocket information is found in the WebSocketInfo class, available through the corresponding getter methods of the class (WebSocketInfo)
WebSocketId class which itself holds a large amount of information on the WebSocket, available through the corresponding getter methods of the class (WebSocketId)
id - the ID of the WebSocket
webSocketApplicationName - the name of the WebSocketApplication that the WebSocket is associated with
timeOfCreation - the time of creation of the WebSocket
timeSinceCreated - the time since the creation of the WebSocket
remoteAddress - the remote address of the WebSocket
timeOfClosing - the time of closing of the WebSocket
host - the host that the WebSocket is bound to
userAgent - the software the client used to connect to the WebSocketApplication
sessionId - the id of the session
applicationUri - the URI of the WebSocketApplication that the webSocket is associated with
individualUri - the individual URI of the that the WebSocket
subProtocol - the subProtocol that the WebSocket used to connect to the WebSocketApplication
hasSslSession - true if SSL is enabled and holds the javax.net.ssl.SSLSession object
serverId - provides the ID of the server that was used to establish the WebSocket connection to the client
WebSocketApplication information found in the WebSocketApplicationInfo class, available through the corresponding getter methods of the class (WebSocketApplicationInfo)
name - the name of the WebSocketApplication
alias - the alias of the WebSocketApplication which is the same as applicationUri
defaultSubProtocol - the defaultSubProtocol of the WebSocketApplication
notUsingSystemPing - information of the WebSocketApplication is using the SystemPing or not
webSocketsCount - the amount of webSockets currently associated with the WebSocketApplication
removedWebSocketsCount - the amount of webSockets which have been removed from the WebSocketApplication
maxAllowedWebSocketConnetions - the maximum allowed webSocket connections of the WebSocketApplication
supportedSubProtocols - the set of the supported subProtocols of the WebSocketApplication
allActiveWebSocketsInfos - returns a list of the WebSocketInfo classes of each webSocket (the contents of this class have been mentioned above)
scope - provide a way for restricting the WebSocketApplication visibility to a predetermined running HTTP server or servers (Bosch IoT Gateway Software HTTP or Standalone Servers)
Standalone Server information is found in the StandaloneServerInfo class, available through the corresponding getter methods of the class (StandaloneServerInfo)
running(isActive) - indicates if the Standalone Server is currently active
port - the port on which the Standalone Server has been registered
host - the host of the Standalone Server
soTimeout - the SO_TIMEOUT, set by the HTTP Standalone Server to every newly created java.net.Socket class
pid - the PID of the Standalone Server
isSecure - returns if the Standalone Server is secure or not
sslConnectionFactoryClassName - the name of the SSLServerSocketFactory service used by the Standalone Server. If not null, the HTTP Standalone Server is secure and vice versa.
scope - provide a way for restricting the WebSocketApplication visibility to a predetermined running HTTP server or servers (Bosch IoT Gateway Software HTTP or Standalone Servers)
filter - the LDAP filter used by the Bosch IoT Gateway Software WebSockets Server to track the SSLServerSocketFactory services.
restart - the default value is false (the SHS will not stop). If set to true the SHS will stop and try to start again until a SSLServerSocketFactory OSGi service is found.
stopConnectedWebSockets - Is dependent on the restart property. If the restart property is set to false thenstopConnectedWebSockets MUST be set to false. If restart is set to true and stopConnectedWebSockets is set to false the current WebSocket connections will not be closed. If both restart and stopConnectedWebSockets are set to true the current WebSocket connections will be closed.