Skip to content

Commit b515fb8

Browse files
committed
Documentation: websocket, mvc context/session param
1 parent 5e6e4de commit b515fb8

File tree

9 files changed

+314
-9
lines changed

9 files changed

+314
-9
lines changed

TODO

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,3 @@
22
* tests and coverage
33
* make reset headers on error configurable
44
* netty reports a leak in MVC body
5-
6-
* doc WebSocket
7-
* doc ContextParam/SessionParam

docs/asciidoc/index.adoc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,12 +240,14 @@ include::templates.adoc[]
240240

241241
include::session.adoc[]
242242

243+
include::websocket.adoc[]
244+
245+
include::mvc-api.adoc[]
246+
243247
include::execution-model.adoc[]
244248

245249
include::responses.adoc[]
246250

247-
include::mvc-api.adoc[]
248-
249251
include::error-handler.adoc[]
250252

251253
include::handlers.adoc[]

docs/asciidoc/mvc-api.adoc

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,123 @@ class MyController {
332332

333333
<1> Access to flash named `success`
334334

335+
==== Session
336+
337+
Provisioning of session attribute is available via javadoc:annotations.SessionParam[] annotation:
338+
339+
.Session Attribute
340+
[source, java, role = "primary"]
341+
----
342+
public class MyController {
343+
344+
@GET
345+
public Object provisioning(@SessionParam String userId) { // <1>
346+
...
347+
}
348+
}
349+
----
350+
351+
.Kotlin
352+
[source, kotlin, role = "secondary"]
353+
----
354+
class MyController {
355+
356+
@GET
357+
fun provisioning(@SessionParam userId: String) : Any { // <1>
358+
...
359+
}
360+
}
361+
----
362+
363+
<1> Access to session attribute named `userId`
364+
365+
Provisioning of javadoc:Session[] is available too:
366+
367+
.Session Attribute
368+
[source, java, role = "primary"]
369+
----
370+
public class MyController {
371+
372+
@GET
373+
public Object provisioning(Session session) { // <1>
374+
...
375+
}
376+
}
377+
----
378+
379+
.Kotlin
380+
[source, kotlin, role = "secondary"]
381+
----
382+
class MyController {
383+
384+
@GET
385+
fun provisioning(session: Session) : Any { // <1>
386+
...
387+
}
388+
}
389+
----
390+
391+
<1> If no session exists yet, new session will be created
392+
393+
To avoid this, just use `java.util.Optional<Session>` as type.
394+
395+
==== Context
396+
397+
Provisioning of context attributes is available via javadoc:annotations.ContextParam[] annotation:
398+
399+
.Context Attribute
400+
[source, java, role = "primary"]
401+
----
402+
public class MyController {
403+
404+
@GET
405+
public Object provisioning(@ContextParam String userId) { // <1>
406+
...
407+
}
408+
}
409+
----
410+
411+
.Kotlin
412+
[source, kotlin, role = "secondary"]
413+
----
414+
class MyController {
415+
416+
@GET
417+
fun provisioning(@ContextParam userId: String) : Any { // <1>
418+
...
419+
}
420+
}
421+
----
422+
423+
<1> Access to context attribute named `userId`
424+
425+
Provisioning of all javadoc:Context[getAttributes, text="attributes"] is available too:
426+
427+
.Context Attributes
428+
[source, java, role = "primary"]
429+
----
430+
public class MyController {
431+
432+
@GET
433+
public Object provisioning(@ContextParam Map<String, Object> attributes) { // <1>
434+
...
435+
}
436+
}
437+
----
438+
439+
.Kotlin
440+
[source, kotlin, role = "secondary"]
441+
----
442+
class MyController {
443+
444+
@GET
445+
fun provisioning(@ContextParam attributes: Map<String, Object>) : Any { // <1>
446+
...
447+
}
448+
}
449+
----
450+
451+
<1> All context attributes must be set as arguments. They must be declared as `Map<String, Object>`
335452

336453
=== Registration
337454

docs/asciidoc/websocket.adoc

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
== Web Sockets
2+
3+
Jooby supports https://developer.mozilla.org/es/docs/Web/API/WebSockets_API[WebSockets].
4+
A javadoc:WebSocket[] is registered like any other handler:
5+
6+
.WebSocket
7+
[source,java,role="primary"]
8+
----
9+
{
10+
ws("/ws", (ctx, configurer) -> { // <1>
11+
configurer.onConnect(ws -> {
12+
ws.send("Connected"); // <2>
13+
});
14+
15+
configurer.onMessage((ws, message) -> {
16+
ws.send("Got " + message.value()); // <3>
17+
});
18+
19+
configurer.onClose((ws, statusCode) -> {
20+
// <4>
21+
});
22+
23+
configurer.onError((ws, cause) -> {
24+
// 5
25+
});
26+
});
27+
}
28+
----
29+
30+
.Kotlin
31+
[source,kotlin,role="secondary"]
32+
----
33+
{
34+
ws("/ws") { // <1>
35+
configurer.onConnect { ws ->
36+
ws.send("Connected") // <2>
37+
}
38+
39+
configurer.onMessage { ws, message ->
40+
ws.send("Got " + message.value()) // <3>
41+
}
42+
43+
configurer.onClose { ws, statusCode ->
44+
// <4>
45+
}
46+
47+
configurer.onError { ws, cause ->
48+
// <5>
49+
}
50+
}
51+
}
52+
----
53+
54+
<1> Add a WebSocket handler. Useful to initialize resources
55+
<2> On WebSocket connect/open send a message back to client. Useful to initialize resources
56+
<3> On new message send back to client
57+
<4> WebSocket is about to close, you must free/release any acquire resources
58+
<5> WebSocket found a exception. Useful to log the error and provide an alternative response is
59+
the WebSocket is still open
60+
61+
You are free to access to HTTP context from WebSocket configurer or callback, but it is disallowed
62+
to modify the HTTP context or produces a response from it:
63+
64+
.Context
65+
[source,java,role="primary"]
66+
----
67+
{
68+
ws("/ws/{key}", (ctx, configurer) -> {
69+
String key = ctx.path("key").value(); // <1>
70+
String foo = ctx.session().get("foo").value(); // <2>
71+
...
72+
});
73+
}
74+
----
75+
76+
.Kotlin
77+
[source,kotlin,role="secondary"]
78+
----
79+
{
80+
ws("/ws/{key}") { ctx, configurer ->
81+
val key = ctx.path("key").value() // <1>
82+
val foo = ctx.session().get("foo").value() // <2>
83+
...
84+
}
85+
}
86+
----
87+
88+
<1> Access to path variable: `key`
89+
<2> Access to session variable: `foo`
90+
91+
=== Structured data
92+
93+
Structure data is supported using the Value API and the javadoc:WebSocket[render] method:
94+
95+
.JSON example:
96+
97+
.JSON example
98+
[source,java,role="primary"]
99+
----
100+
import io.jooby.json.JacksonModule;
101+
102+
{
103+
install(new JackonModule()); // <1>
104+
105+
ws("/ws", (ctx, configurer) -> {
106+
configurer.onMessage((ws, message) -> {
107+
MyObject myobject = message.to(MyObject.class); // <2>
108+
ws.render(myobject); // <3>
109+
})
110+
});
111+
}
112+
----
113+
114+
.Kotlin
115+
[source,kotlin,role="secondary"]
116+
----
117+
{
118+
install(JacksonModule()) // <1>
119+
120+
ws("/ws/{key}") { ctx, configurer ->
121+
configurer.onMessage { ws, message ->
122+
val myobject = message.to<MyObject>() // <2>
123+
ws.render(myobject) // <3>
124+
}
125+
}
126+
}
127+
----
128+
129+
<1> Install Jackson module (required for JSON decoding/encoding)
130+
<2> Parse/decode message to `MyObject`
131+
<3> Encode myobject as JSON and send to client
132+
133+
Alternative you explicit tells with decoder/encoder to use using consumes/produces attributes:
134+
135+
.Context
136+
[source,java,role="primary"]
137+
----
138+
import io.jooby.json.JacksonModule;
139+
140+
{
141+
install(new JackonModule()); // <1>
142+
143+
ws("/ws", (ctx, configurer) -> {
144+
configurer.onMessage((ws, message) -> {
145+
MyObject myobject = message.to(MyObject.class); // <2>
146+
ws.render(myobject); // <3>
147+
})
148+
})
149+
.consumes(MediaType.json)
150+
.produces(MediaType.json);
151+
}
152+
----
153+
154+
.Kotlin
155+
[source,kotlin,role="secondary"]
156+
----
157+
{
158+
install(JacksonModule()) // <1>
159+
160+
ws("/ws/{key}") { ctx, configurer ->
161+
configurer.onMessage { ws, message ->
162+
val myobject = message.to<MyObject>() // <2>
163+
ws.render(myobject) // <3>
164+
}
165+
}.consumes(MediaType.json)
166+
.produces(MediaType.json)
167+
}
168+
----
169+
170+
Structure messages depends/requires a javadoc:MessageDecoder[] and jadoc:MessageEncoder[]. In this
171+
example both are provided by the JacksonModule.
172+
173+
=== Connection Timeouts
174+
By default Jooby will timeout idle connections that have no activity after 5 minutes. You can
175+
control this behaviour by setting the `websocket.idleTimeout` property:
176+
177+
.application.conf
178+
[source, properties]
179+
----
180+
websocket.idleTimeout = 1h
181+
----
182+
183+
See https://github.com/lightbend/config/blob/master/HOCON.md#duration-format[duration format]

examples/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
</dependency>
5454
<dependency>
5555
<groupId>io.jooby</groupId>
56-
<artifactId>jooby-jetty</artifactId>
56+
<artifactId>jooby-netty</artifactId>
5757
<version>${jooby.version}</version>
5858
</dependency>
5959
<dependency>

examples/src/main/resources/application.conf

Whitespace-only changes.

jooby/src/main/kotlin/io/jooby/Kooby.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,11 @@ operator fun ValueNode.get(index: Int): ValueNode {
6464
return this.get(index)
6565
}
6666

67-
inline fun <reified T> ValueNode.to(): T? {
67+
inline fun <reified T> Value.to(): T {
6868
return this.to(T::class.java)
6969
}
7070

71-
infix fun <T : Any> ValueNode.to(type: KClass<T>): T? {
71+
infix fun <T : Any> Value.to(type: KClass<T>): T {
7272
return this.to(type.java)
7373
}
7474

jooby/src/test/kotlin/io/jooby/Idioms.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,13 @@ class Idioms : Kooby({
136136
configurer.onConnect { ws ->
137137
ws.send("SS")
138138
}
139+
configurer.onMessage { ws, message ->
140+
val value = message.to<IdiomsPojo> ()
141+
ws.render(value)
142+
}
139143
}
140144
})
141145

142146
class IdiomsController {}
147+
148+
class IdiomsPojo {}

tests/src/test/java/io/jooby/WebSocketTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,6 @@ public void webSocketJson() {
8383
assertEquals("{\"message\":\"Hello JSON!\"}", ws.send("{\"message\" : \"Hello JSON!\"}"));
8484
});
8585
});
86-
8786
}
87+
8888
}

0 commit comments

Comments
 (0)