Skip to content

Commit 042234c

Browse files
declarative websockets: parameter binding
1 parent 6acd841 commit 042234c

5 files changed

Lines changed: 133 additions & 24 deletions

File tree

modules/jooby-apt/src/main/java/io/jooby/internal/apt/MvcParameter.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,4 +183,8 @@ private List<? extends AnnotationMirror> annotationFromAnnotationType(Element el
183183
public boolean isRequireBeanValidation() {
184184
return requireBeanValidation;
185185
}
186+
187+
public VariableElement variableElement() {
188+
return parameter;
189+
}
186190
}

modules/jooby-apt/src/main/java/io/jooby/internal/apt/ws/WsRoute.java

Lines changed: 52 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -58,30 +58,6 @@ public TypeDefinition getReturnType() {
5858
return new TypeDefinition(types, method.getReturnType());
5959
}
6060

61-
private String wsParameterName(MvcParameter parameter) {
62-
String rawParamType = parameter.getType().getRawType().toString();
63-
var name = WsParamTypes.generateArgumentName(rawParamType);
64-
if (name != null) {
65-
return name;
66-
}
67-
68-
getContext()
69-
.error("Unsupported websocket handler parameter type: %s.", rawParamType);
70-
return "null";
71-
}
72-
73-
private String paramList() {
74-
var joiner = new StringJoiner(", ", "(", ")");
75-
for (var param : getParameters(true)) {
76-
joiner.add(wsParameterName(param));
77-
}
78-
return joiner.toString();
79-
}
80-
81-
public String invocation(boolean kt) {
82-
return makeCall(kt, paramList(), false, false);
83-
}
84-
8561
public void appendBody(boolean kt, StringBuilder buffer, String indent) {
8662
buffer.append(statement(indent, var(kt), "c = this.factory.apply(ctx)", semicolon(kt)));
8763

@@ -112,6 +88,54 @@ public void appendBody(boolean kt, StringBuilder buffer, String indent) {
11288
}
11389
}
11490

91+
public String invocation(boolean kt) {
92+
return makeCall(kt, paramList(kt), false, false);
93+
}
94+
95+
private String paramList(boolean kt) {
96+
var joiner = new StringJoiner(", ", "(", ")");
97+
for (var param : getParameters(true)) {
98+
joiner.add(websocketArgumentExpression(param, kt));
99+
}
100+
return joiner.toString();
101+
}
102+
103+
private String websocketArgumentExpression(MvcParameter parameter, boolean kt) {
104+
String rawParamType = parameter.getType().getRawType().toString();
105+
if (wsLifecycle == WsLifecycle.MESSAGE) {
106+
if (WsParamTypes.getAllowedTypes(WsLifecycle.MESSAGE).contains(rawParamType)) {
107+
return WsParamTypes.generateArgumentName(rawParamType);
108+
}
109+
110+
var mvcBodyExpr =
111+
ParameterGenerator.BodyParam.toSourceCode(
112+
kt,
113+
this,
114+
null,
115+
parameter.getType(),
116+
parameter.variableElement(),
117+
parameter.getName(),
118+
parameter.isNullable(kt));
119+
return mvcExprToWsExpr(mvcBodyExpr);
120+
}
121+
122+
String expr = WsParamTypes.generateArgumentName(rawParamType);
123+
if (expr != null) {
124+
return expr;
125+
}
126+
127+
getContext()
128+
.error("Unsupported websocket handler parameter type: %s.", rawParamType);
129+
return "null";
130+
}
131+
132+
private static String mvcExprToWsExpr(String mvcBodyExpr) {
133+
String s = mvcBodyExpr;
134+
s = s.replace("ctx.body().", "message.");
135+
s = s.replace("ctx.body(", "message.to(");
136+
return s;
137+
}
138+
115139
private void validateLifecycleParameters(MvcContext context, ExecutableElement method) {
116140
var env = context.getProcessingEnvironment();
117141
var types = env.getTypeUtils();
@@ -131,6 +155,10 @@ private void validateLifecycleParameters(MvcContext context, ExecutableElement m
131155
continue;
132156
}
133157

158+
if (wsLifecycle == WsLifecycle.MESSAGE) {
159+
continue;
160+
}
161+
134162
context.error(
135163
"Illegal parameter type %s on websocket %s method %s#%s",
136164
raw,
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package tests.ws;
7+
8+
import io.jooby.Context;
9+
import io.jooby.WebSocket;
10+
import io.jooby.annotation.ws.OnMessage;
11+
12+
import java.util.Map;
13+
14+
public class WebsocketBeanMessage {
15+
16+
public record Incoming(String text) {
17+
}
18+
19+
@OnMessage
20+
public Map<String, String> onMessage(WebSocket ws, Context ctx, Incoming msg) {
21+
return Map.of("echo", msg.text());
22+
}
23+
}

modules/jooby-apt/src/test/java/tests/ws/WebsocketGeneratorTest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,20 @@ public void chatWebsocketMatchesGeneratedSource() throws Exception {
2626
);
2727
}
2828

29+
@Test
30+
public void beanMessageWebsocketMatchesGeneratedSource() throws Exception {
31+
var expected = new String(
32+
getClass()
33+
.getResourceAsStream("/tests/ws/WebsocketBeanMessageWs_expected.java")
34+
.readAllBytes()
35+
);
36+
37+
new ProcessorRunner(new WebsocketBeanMessage())
38+
.withWsCode(source -> assertThat(normalize(source))
39+
.isEqualTo(normalize(expected))
40+
);
41+
}
42+
2943
private static String normalize(String source) {
3044
return source.replace("\r\n", "\n").replace('\r', '\n')
3145
.stripTrailing();
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package tests.ws;
2+
3+
@io.jooby.annotation.Generated(WebsocketBeanMessage.class)
4+
public class WebsocketBeanMessageWs_ implements io.jooby.Extension {
5+
protected java.util.function.Function<io.jooby.Context, WebsocketBeanMessage> factory;
6+
7+
public WebsocketBeanMessageWs_() {
8+
this(io.jooby.SneakyThrows.singleton(WebsocketBeanMessage::new));
9+
}
10+
11+
public WebsocketBeanMessageWs_(WebsocketBeanMessage instance) {
12+
setup(ctx -> instance);
13+
}
14+
15+
public WebsocketBeanMessageWs_(io.jooby.SneakyThrows.Supplier<WebsocketBeanMessage> provider) {
16+
setup(ctx -> provider.get());
17+
}
18+
19+
public WebsocketBeanMessageWs_(io.jooby.SneakyThrows.Function<Class<WebsocketBeanMessage>, WebsocketBeanMessage> provider) {
20+
setup(ctx -> provider.apply(WebsocketBeanMessage.class));
21+
}
22+
23+
private void setup(java.util.function.Function<io.jooby.Context, WebsocketBeanMessage> factory) {
24+
this.factory = factory;
25+
}
26+
27+
public void install(io.jooby.Jooby app) throws Exception {
28+
app.ws("/", this::wsInit);
29+
}
30+
31+
private void wsInit(io.jooby.Context ctx, io.jooby.WebSocketConfigurer configurer) {
32+
/** See {@link WebsocketBeanMessage#onMessage(io.jooby.WebSocket, io.jooby.Context, tests.ws.WebsocketBeanMessage.Incoming)} */
33+
configurer.onMessage((ws, message) -> {
34+
var c = this.factory.apply(ctx);
35+
var __wsReturn = c.onMessage(ws, ctx, message.to(tests.ws.WebsocketBeanMessage.Incoming.class));
36+
ws.render(__wsReturn);
37+
});
38+
39+
}
40+
}

0 commit comments

Comments
 (0)