Skip to content

Commit 164c27a

Browse files
authored
Merge pull request #99 from corndeladmin/java
adds ws notes
2 parents 2509d17 + 6408a27 commit 164c27a

File tree

4 files changed

+334
-0
lines changed

4 files changed

+334
-0
lines changed

.vitepress/sidebars/javalin.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,22 @@ export const javalin = [
5454
{ text: 'CSS and assets', link: '/javalin/css-and-assets' },
5555
{ text: 'User input', link: '/javalin/user-input' }
5656
]
57+
},
58+
{
59+
text: 'Web sockets',
60+
items: [
61+
{
62+
text: 'Creating a web socket server',
63+
link: '/javalin/web-socket-server'
64+
},
65+
{
66+
text: 'Syncing clients',
67+
link: '/javalin/syncing-clients'
68+
},
69+
{
70+
text: 'Creating a web socket client',
71+
link: '/javalin/web-socket-client'
72+
}
73+
]
5774
}
5875
]

src/javalin/syncing-clients.md

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# Syncing clients
2+
3+
## Setting up the server
4+
5+
As a quick reminder, let's see again the basic server set up:
6+
7+
```java
8+
import io.javalin.Javalin;
9+
10+
public class WebSocketSync {
11+
public static void main(String[] args) {
12+
Javalin app = Javalin.create().start(5001);
13+
14+
app.ws("/websocket", ws -> {
15+
ws.onConnect(ctx -> {
16+
// Handle new connections
17+
System.out.println("New connection: " + ctx.getSessionId());
18+
});
19+
});
20+
}
21+
}
22+
```
23+
24+
## Tracking sockets
25+
26+
To update all clients, we need to keep track of the WebSocket sessions.
27+
Fortunately, Javalin provides a `WsContext` object for each connection, which we
28+
can store in a `List` or `Set` for easy tracking.
29+
30+
```java
31+
import io.javalin.Javalin;
32+
import io.javalin.websocket.WsContext;
33+
34+
import java.util.Collections;
35+
import java.util.Set;
36+
import java.util.concurrent.ConcurrentHashMap;
37+
38+
public class WebSocketSync {
39+
private static final Set<WsContext> connectedClients =
40+
Collections.newSetFromMap(new ConcurrentHashMap<>());
41+
42+
public static void main(String[] args) {
43+
Javalin app = Javalin.create().start(5001);
44+
45+
app.ws("/websocket", ws -> {
46+
ws.onConnect(ctx -> {
47+
connectedClients.add(ctx);
48+
System.out.println("Connected clients: " + connectedClients.size());
49+
});
50+
51+
ws.onClose(ctx -> {
52+
connectedClients.remove(ctx);
53+
System.out.println("Connection closed. Remaining clients: " + connectedClients.size());
54+
});
55+
});
56+
}
57+
}
58+
```
59+
60+
- The `connectedClients` set holds references to all currently active WebSocket
61+
sessions.
62+
63+
- When a client connects, it is added to the `connectedClients` set.
64+
65+
- When a client disconnects, it is removed from the set.
66+
67+
## Forwarding messages
68+
69+
To broadcast messages received from one client to all other connected clients,
70+
we iterate over the `connectedClients` set and call the `send` method for each
71+
session.
72+
73+
```java
74+
import io.javalin.Javalin;
75+
import io.javalin.websocket.WsContext;
76+
77+
import java.util.Collections;
78+
import java.util.Set;
79+
import java.util.concurrent.ConcurrentHashMap;
80+
81+
public class WebSocketSync {
82+
private static final Set<WsContext> connectedClients =
83+
Collections.newSetFromMap(new ConcurrentHashMap<>());
84+
85+
public static void main(String[] args) {
86+
Javalin app = Javalin.create().start(5001);
87+
88+
app.ws("/websocket", ws -> {
89+
ws.onConnect(ctx -> connectedClients.add(ctx));
90+
91+
ws.onClose(ctx -> connectedClients.remove(ctx));
92+
93+
ws.onMessage((ctx, message) -> {
94+
for (WsContext client : connectedClients) {
95+
if (client != ctx) { // Avoid echoing the message back to the sender
96+
client.send(message);
97+
}
98+
}
99+
});
100+
});
101+
}
102+
}
103+
104+
```
105+
106+
- The `onMessage` handler receives messages from a client.
107+
108+
- The message is forwarded to all other connected clients using the `send`
109+
method.
110+
111+
And that's it! Now all connected users will receive any message sent by any
112+
other user.

src/javalin/web-socket-client.md

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Creating a web socket client
2+
3+
This is a front-end task, but it's important to complete the picture on working
4+
with web sockets, so we'll demonstrate here how we send and receive messages on
5+
the client.
6+
7+
::: code-group
8+
9+
```html
10+
<!DOCTYPE html>
11+
<html lang="en">
12+
<head>
13+
<meta charset="UTF-8" />
14+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
15+
<link rel="stylesheet" href="style.css" />
16+
<script type="module" src="script.js" defer></script>
17+
<title>Document</title>
18+
</head>
19+
20+
<body>
21+
<h1>Simple chat</h1>
22+
23+
<form id="form" class="form">
24+
<textarea
25+
name="content"
26+
id="content"
27+
placeholder="Write your message"
28+
></textarea>
29+
<input type="submit" value="Send" />
30+
</form>
31+
32+
<h2>Messages</h2>
33+
<ul id="chat"></ul>
34+
</body>
35+
</html>
36+
```
37+
38+
```js
39+
document.addEventListener('DOMContentLoaded', main)
40+
41+
function main() {
42+
// make a client-side socket
43+
const socket = new WebSocket('ws://localhost:5001/websocket')
44+
45+
// get a reference to the elemets we need
46+
const form = document.getElementById('form')
47+
const chat = document.getElementById('chat')
48+
49+
// handle submit events
50+
form.addEventListener('submit', e => {
51+
// stop the page from reloading
52+
e.preventDefault()
53+
54+
// get the message from the textarea
55+
const data = new FormData(form)
56+
57+
// send it to the server
58+
socket.send(data.get('content'))
59+
60+
// clear the form
61+
form.reset()
62+
})
63+
64+
// handle new messages
65+
socket.onmessage = async message => {
66+
// create a new <li> element
67+
const li = document.createElement('li')
68+
69+
// insert the text from the incoming message
70+
li.innerText = await message.data.text()
71+
72+
// add the <li> to the chat area
73+
chat.appendChild(li)
74+
}
75+
}
76+
```
77+
78+
```css
79+
form {
80+
width: 100%;
81+
max-width: 250px;
82+
display: flex;
83+
flex-direction: column;
84+
gap: 1rem;
85+
}
86+
87+
form > * {
88+
display: block;
89+
padding: 0.5rem;
90+
}
91+
92+
li {
93+
margin-bottom: 1rem;
94+
}
95+
```
96+
97+
:::

src/javalin/web-socket-server.md

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# Web socket servers
2+
3+
::: info
4+
5+
Although web sockets aren't specific to Javalin, this part of the docs focuses
6+
heavily on servers, so we felt these notes belonged here.
7+
8+
:::
9+
10+
## Creating a web socket server
11+
12+
To create a WebSocket server in Javalin, we need the Javalin WebSocket plugin,
13+
which is included in the core Javalin library.
14+
15+
Then, create the Javalin app and enable WebSocket functionality:
16+
17+
```java
18+
import io.javalin.Javalin;
19+
20+
public class WebSocketServer {
21+
public static void main(String[] args) {
22+
Javalin app = Javalin.create(config -> {
23+
config.wsFactoryConfig(ws -> {
24+
// WebSocket settings can be configured here
25+
});
26+
}).start(5001);
27+
}
28+
}
29+
```
30+
31+
## Listening for connections
32+
33+
Listening for new WebSocket connections is straightforward. Use the `ws` method
34+
to define WebSocket endpoints.
35+
36+
```java
37+
import io.javalin.Javalin;
38+
import io.javalin.websocket.WsConnectContext;
39+
40+
public class WebSocketServer {
41+
public static void main(String[] args) {
42+
Javalin app = Javalin.create().start(5001);
43+
44+
app.ws("/websocket", ws -> {
45+
ws.onConnect(ctx -> {
46+
System.out.println("New connection: " + ctx.getSessionId());
47+
// do what we want with the connection
48+
});
49+
});
50+
}
51+
}
52+
53+
```
54+
55+
When a client connects to the WebSocket endpoint (`/websocket`), the `onConnect`
56+
handler is triggered, passing a `WsConnectContext` object (`ctx`) representing
57+
the connection.
58+
59+
## Listening for socket events
60+
61+
We can listen for messages from the client using the `onMessage` event and
62+
handle connection closures using the `onClose` event.
63+
64+
```java
65+
import io.javalin.Javalin;
66+
import io.javalin.websocket.WsContext;
67+
68+
public class WebSocketServer {
69+
public static void main(String[] args) {
70+
Javalin app = Javalin.create().start(5001);
71+
72+
app.ws("/websocket", ws -> {
73+
ws.onConnect(ctx -> System.out.println("Connected: " + ctx.getSessionId()));
74+
ws.onMessage((ctx, message) -> handleMessage(ctx, message));
75+
ws.onClose((ctx, status, reason) -> cleanUp(ctx));
76+
});
77+
}
78+
79+
private static void handleMessage(WsContext ctx, String message) {
80+
System.out.println("Received message: " + message);
81+
}
82+
83+
private static void cleanUp(WsContext ctx) {
84+
System.out.println("Connection closed: " + ctx.getSessionId());
85+
}
86+
}
87+
```
88+
89+
- `onMessage`: Triggered when a message is received from the client.
90+
91+
- `onClose`: Triggered when the client closes the connection, allowing us to
92+
perform cleanup.
93+
94+
## Sending a message back
95+
96+
At any point, we can send a message to the client using the `send` method on the
97+
`WsContext`.
98+
99+
```java
100+
app.ws("/websocket", ws -> {
101+
ws.onConnect(ctx -> {
102+
ctx.send("Hello from the server!");
103+
});
104+
});
105+
```
106+
107+
The client will receive the message immediately, provided the connection is
108+
still open.

0 commit comments

Comments
 (0)