Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .vitepress/sidebars/javalin.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,22 @@ export const javalin = [
{ text: 'CSS and assets', link: '/javalin/css-and-assets' },
{ text: 'User input', link: '/javalin/user-input' }
]
},
{
text: 'Web sockets',
items: [
{
text: 'Creating a web socket server',
link: '/javalin/web-socket-server'
},
{
text: 'Syncing clients',
link: '/javalin/syncing-clients'
},
{
text: 'Creating a web socket client',
link: '/javalin/web-socket-client'
}
]
}
]
112 changes: 112 additions & 0 deletions src/javalin/syncing-clients.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# Syncing clients

## Setting up the server

As a quick reminder, let's see again the basic server set up:

```java
import io.javalin.Javalin;

public class WebSocketSync {
public static void main(String[] args) {
Javalin app = Javalin.create().start(5001);

app.ws("/websocket", ws -> {
ws.onConnect(ctx -> {
// Handle new connections
System.out.println("New connection: " + ctx.getSessionId());
});
});
}
}
```

## Tracking sockets

To update all clients, we need to keep track of the WebSocket sessions.
Fortunately, Javalin provides a `WsContext` object for each connection, which we
can store in a `List` or `Set` for easy tracking.

```java
import io.javalin.Javalin;
import io.javalin.websocket.WsContext;

import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class WebSocketSync {
private static final Set<WsContext> connectedClients =
Collections.newSetFromMap(new ConcurrentHashMap<>());

public static void main(String[] args) {
Javalin app = Javalin.create().start(5001);

app.ws("/websocket", ws -> {
ws.onConnect(ctx -> {
connectedClients.add(ctx);
System.out.println("Connected clients: " + connectedClients.size());
});

ws.onClose(ctx -> {
connectedClients.remove(ctx);
System.out.println("Connection closed. Remaining clients: " + connectedClients.size());
});
});
}
}
```

- The `connectedClients` set holds references to all currently active WebSocket
sessions.

- When a client connects, it is added to the `connectedClients` set.

- When a client disconnects, it is removed from the set.

## Forwarding messages

To broadcast messages received from one client to all other connected clients,
we iterate over the `connectedClients` set and call the `send` method for each
session.

```java
import io.javalin.Javalin;
import io.javalin.websocket.WsContext;

import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class WebSocketSync {
private static final Set<WsContext> connectedClients =
Collections.newSetFromMap(new ConcurrentHashMap<>());

public static void main(String[] args) {
Javalin app = Javalin.create().start(5001);

app.ws("/websocket", ws -> {
ws.onConnect(ctx -> connectedClients.add(ctx));

ws.onClose(ctx -> connectedClients.remove(ctx));

ws.onMessage((ctx, message) -> {
for (WsContext client : connectedClients) {
if (client != ctx) { // Avoid echoing the message back to the sender
client.send(message);
}
}
});
});
}
}

```

- The `onMessage` handler receives messages from a client.

- The message is forwarded to all other connected clients using the `send`
method.

And that's it! Now all connected users will receive any message sent by any
other user.
97 changes: 97 additions & 0 deletions src/javalin/web-socket-client.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Creating a web socket client

This is a front-end task, but it's important to complete the picture on working
with web sockets, so we'll demonstrate here how we send and receive messages on
the client.

::: code-group

```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="style.css" />
<script type="module" src="script.js" defer></script>
<title>Document</title>
</head>

<body>
<h1>Simple chat</h1>

<form id="form" class="form">
<textarea
name="content"
id="content"
placeholder="Write your message"
></textarea>
<input type="submit" value="Send" />
</form>

<h2>Messages</h2>
<ul id="chat"></ul>
</body>
</html>
```

```js
document.addEventListener('DOMContentLoaded', main)

function main() {
// make a client-side socket
const socket = new WebSocket('ws://localhost:5001/websocket')

// get a reference to the elemets we need
const form = document.getElementById('form')
const chat = document.getElementById('chat')

// handle submit events
form.addEventListener('submit', e => {
// stop the page from reloading
e.preventDefault()

// get the message from the textarea
const data = new FormData(form)

// send it to the server
socket.send(data.get('content'))

// clear the form
form.reset()
})

// handle new messages
socket.onmessage = async message => {
// create a new <li> element
const li = document.createElement('li')

// insert the text from the incoming message
li.innerText = await message.data.text()

// add the <li> to the chat area
chat.appendChild(li)
}
}
```

```css
form {
width: 100%;
max-width: 250px;
display: flex;
flex-direction: column;
gap: 1rem;
}

form > * {
display: block;
padding: 0.5rem;
}

li {
margin-bottom: 1rem;
}
```

:::
108 changes: 108 additions & 0 deletions src/javalin/web-socket-server.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Web socket servers

::: info

Although web sockets aren't specific to Javalin, this part of the docs focuses
heavily on servers, so we felt these notes belonged here.

:::

## Creating a web socket server

To create a WebSocket server in Javalin, we need the Javalin WebSocket plugin,
which is included in the core Javalin library.

Then, create the Javalin app and enable WebSocket functionality:

```java
import io.javalin.Javalin;

public class WebSocketServer {
public static void main(String[] args) {
Javalin app = Javalin.create(config -> {
config.wsFactoryConfig(ws -> {
// WebSocket settings can be configured here
});
}).start(5001);
}
}
```

## Listening for connections

Listening for new WebSocket connections is straightforward. Use the `ws` method
to define WebSocket endpoints.

```java
import io.javalin.Javalin;
import io.javalin.websocket.WsConnectContext;

public class WebSocketServer {
public static void main(String[] args) {
Javalin app = Javalin.create().start(5001);

app.ws("/websocket", ws -> {
ws.onConnect(ctx -> {
System.out.println("New connection: " + ctx.getSessionId());
// do what we want with the connection
});
});
}
}

```

When a client connects to the WebSocket endpoint (`/websocket`), the `onConnect`
handler is triggered, passing a `WsConnectContext` object (`ctx`) representing
the connection.

## Listening for socket events

We can listen for messages from the client using the `onMessage` event and
handle connection closures using the `onClose` event.

```java
import io.javalin.Javalin;
import io.javalin.websocket.WsContext;

public class WebSocketServer {
public static void main(String[] args) {
Javalin app = Javalin.create().start(5001);

app.ws("/websocket", ws -> {
ws.onConnect(ctx -> System.out.println("Connected: " + ctx.getSessionId()));
ws.onMessage((ctx, message) -> handleMessage(ctx, message));
ws.onClose((ctx, status, reason) -> cleanUp(ctx));
});
}

private static void handleMessage(WsContext ctx, String message) {
System.out.println("Received message: " + message);
}

private static void cleanUp(WsContext ctx) {
System.out.println("Connection closed: " + ctx.getSessionId());
}
}
```

- `onMessage`: Triggered when a message is received from the client.

- `onClose`: Triggered when the client closes the connection, allowing us to
perform cleanup.

## Sending a message back

At any point, we can send a message to the client using the `send` method on the
`WsContext`.

```java
app.ws("/websocket", ws -> {
ws.onConnect(ctx -> {
ctx.send("Hello from the server!");
});
});
```

The client will receive the message immediately, provided the connection is
still open.
Loading