A simple WebSocket between Java and React (with hooks)
Tags: java, react, websocket, hooks
Published: 2020-11-19 10:17:52

# Async comunication: the phantom menace

When you are working as a Software Engineer or developer, it's only a matter of time since you encounter a great enemy: asynchronous behaviour! You can find it in communication between client and server, or maybe it's included in the programming language or in the framework that you are using.

## Websockets: a new hope

During my current project we (me and my team) faced the following problem: after a certain action of the user, the application should prevent every other action and wait for an **OK**/**KO** from the server. It was unknown how much time can take the server to send a response, but the requirement was clear: no matter how much time, the user **must** wait until the response or call the customer service if he thinks it's taking to long.
After a small discussion we decided to try to implement a websocket to let the client wait for a message (even forever if necessary).

### What is a Websocket?
I don't want to bother you with informations that you can retrieve by yourself from more authoritative sources. In simple words Websocket is a protocol that allow a full-duplex communication over TCP, allowing both client and server to send/receive message from each other and to manage events based on the message received.

Now, the Frontend is purely React + TypeScript, while the Backend is written in Java in an OSGi framework, so it is not possible to use *simple* solutions like [socket.io](https://socket.io/) that allows deleoper to use the same tech on both FE and BE.

## Let's code together - Frontend

Since I was responsible for the Frontend part, I will first describe my React code. Another requirement to keep in mind was that the Websocket is opened at the start of the application.
So I decided to use a [*ref* hook](https://reactjs.org/docs/hooks-reference.html#useref) to manage the WebSocket Object and check if it is closed or open and a boolean `shouldKeepWSAlive` to enable/disable a function that keep alive the connection while waiting for the response:
```javascript
  const connection = useRef<WebSocket>();
  const shouldKeepWSAlive = useRef<boolean>(false);
```
After that, we need to what is the event that start the websocket.
Now I had this *variable* called *isWaitingSomething* that is responsible for blocking the application as said before so I decided to use a *useEffect* hook to manage the opening of the Websocket ([what is a useEffect?](https://reactjs.org/docs/hooks-effect.html))

```javascript
useEffect(() => {
    if (!(connection && 
        connection.current && 
        connection.current.readyState === 1))
    {
      connection.current = new WebSocket("ws://path-to-websocket");
      connection.current.onopen = () => {
          //do something, maybe just log that the websocket is open;
      }
      connection.current.onclose = () => {
          //do something, maybe just log that the websocket is closed;
      };
      connection.current.onmessage = (e) => {
        aFunction();
      };
    }
  }, [dependencies]);
```
Just a little explanation:
<ul>
<li>the if statement on the top helps me to check if connection is already open;</li>
<li>if the connection is not open, the code block inside the if open a new connection;</li>
<li>onopen and onclose are default events that are triggered when the connection is started and closed;</li>
<li>onmessage is the important part: it is the event that is triggered when a message is received on the frontend;</li>
<li>aFunction() is my custom function that do the logic that I want;</li>
<li>with the correct dependencies the Websocket is opened when the application start;</li>
<li>since even the Websocket do timeout, you may need to reopen it.</li>
</ul>

However, if the server takes to much time to send a message, while waiting the Websocket can timeout and close, so I've added a simple `keepAlive()` function in this way:

```javascript
  const keepAlive = useCallback(() => {
    if (shouldKeepWSAlive.current) {
      if (connection.current !== undefined && 
          connection.current !== null &&
          connection.current.readyState === 1) 
      {
        connection.current.send("");
      }
      setTimeout(() => {
        keepAlive();
      }, 20000);
    }
  }, []);

  useEffect(() => {
    if (isWaitingVendi) {
      shouldKeepWSAlive.current = true;
      keepAlive();
    } else {
      shouldKeepWSAlive.current = false;
    }
  }, [isWaitingVendi, keepAlive]);
```

After that my Websockt was working and performing well.

## Let's code together - Backend

In this section I will briefly describe the Java part of the Websocket.
The BE was managed by another team member, so I will not insert detailed explanations, but I've asked him to write a dedicated post.

We are developing in **OSGi framework** and we use **Jetty**. The list of required imports is quite long (I have hidden some...):
```java
import com.google.gson.Gson;
import it.hiddenstuff.common.topic.TopicConstants;
import org.eclipse.jetty.websocket.api.RemoteEndpoint;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin;
import org.osgi.service.event.EventConstants;
import org.osgi.service.event.EventHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Hashtable;
```

and here you have the declaration of the Class with correct annotations and the constructor:
```java
@WebSocket
public class SellWebSocket implements EventHandler {

	public SellWebSocket() {
		BundleContext bundleContext = FrameworkUtil.
            getBundle(SellWebSocket.class).getBundleContext();
		Hashtable<String, String> stringStringHashMap = new Hashtable<>();
		stringStringHashMap.
            put( EventConstants.EVENT_TOPIC , TopicConstants.TOPIC_END_SELL);
		bundleContext.
            registerService(EventHandler.class , this ,  stringStringHashMap);
	}
}
```

Then you need to add some declarations to manage the session, the logs and the endpoints:
```java
    private Session session;
	private RemoteEndpoint remote;
	private Logger log = LoggerFactory.getLogger(getClass());

	public Session getSession() {
		return session;
	}

	public void setSession(Session session) {
		this.session = session;
	}

	public RemoteEndpoint getRemote() {
		return remote;
	}
```

As for the Frontend, you need to *listen* to the events (*open*, *close*, *send*, *receive*):
```java
    @OnWebSocketConnect
	public void onConnect(Session session) {
		setSession(session);
		this.remote = session.getRemote();
	}

    @OnWebSocketClose
	public void onClose(int statusCode, String reason) {
		this.session = null;
	}

    @OnWebSocketMessage
	public void onText(String message) {
		if (session == null) {
			log.debug("null session");
			// no connection, do nothing.
			// this is possible due to async behavior
			return;
		}
        //do something
	}

    /**
	 * Called by the {@link EventAdmin} service to notify the listener of an
	 * event.
	 *
	 * @param event The event that occurred.
	 */
	@Override
	public void handleEvent(Event event) {
		//do what you need to do
	}

```


## Conclusions
Before that, I was not quite familiar with all this stuff, and googleing made me a little crazy since it was hard to find a solution with all the right characteristics: sometimes you find *socket.io*, sometimes you find old React classes while you need Hooks, and some problems like that.
Anyway I managed to pack all together coming out with this solution. Since I don't pretend to be an expert, feel free to comment and add usefull suggestions.
If instead you think this article is usefull, I'd very happy to know :grinning: