Skip to content

Commit 91735c3

Browse files
authored
fix: solve invalid java method name generation and handling multiple handlers (#1745)
Co-authored-by: ssala034 <sh.salat@gmail.com>
1 parent 7e0823a commit 91735c3

File tree

14 files changed

+256
-57
lines changed

14 files changed

+256
-57
lines changed

.changeset/lucky-zebras-wonder.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
"@asyncapi/generator-components": patch
3+
"@asyncapi/generator": patch
4+
---
5+
6+
Fix invalid Java method name generation and properly handle multiple WebSocket handlers
7+
8+
- Generate valid Java method names for WebSocket operation handlers with strange identifiers.
9+
- Produce distinct @OnTextMessage handler methods for each send operation and add safe default routing for unrecognized messages.
10+
- Update onClose/onOpen formatting.

packages/components/src/components/OnClose.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,14 @@ const websocketOnCloseMethod = {
3535
},
3636
java: {
3737
quarkus: (title) => {
38-
const onCloseMethod = `@OnClose
39-
public void onClose(CloseReason reason, WebSocketClientConnection connection) {
40-
int code = reason.getCode();
41-
LOG.info("Websocket disconnected from ${title} with Close code: " + code);
38+
const onCloseMethod = `
39+
@OnClose
40+
public void onClose(CloseReason reason, WebSocketClientConnection connection) {
41+
int code = reason.getCode();
42+
LOG.info("Websocket disconnected from ${title} with Close code: " + code);
4243
}
4344
}`;
44-
return { onCloseMethod, indent: 2 };
45+
return { onCloseMethod, indent: 0 };
4546
}
4647
}
4748
};

packages/components/src/components/OnOpen.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public void onOpen() {
3434
LOG.info("Connected to ${title} server");
3535
LOG.info(broadcastMessage);
3636
}`;
37-
return { onOpenMethod, indent: 2 };
37+
return { onOpenMethod, indent: 2, newLines: 2 };
3838
}
3939
}
4040
};
@@ -60,16 +60,18 @@ const resolveOpenConfig = (language, framework = '') => {
6060
export function OnOpen({ language, framework='', title }) {
6161
let onOpenMethod = '';
6262
let indent = 0;
63+
let newLines = 0;
6364

6465
if (websocketOnOpenMethod[language]) {
6566
const generateOnOpenCode = resolveOpenConfig(language, framework);
6667
const openResult = generateOnOpenCode(title);
6768
onOpenMethod = openResult.onOpenMethod;
6869
indent = openResult.indent ?? 0;
70+
newLines = openResult.newLines ?? 1;
6971
}
7072

7173
return (
72-
<Text indent={indent}>
74+
<Text newLines={newLines} indent={indent}>
7375
{onOpenMethod}
7476
</Text>
7577
);

packages/components/test/components/__snapshots__/OnClose.test.js.snap

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ exports[`Testing of OnClose function render dart OnClose method 1`] = `
99

1010
exports[`Testing of OnClose function render java quarkas OnClose method 1`] = `
1111
"@OnClose
12-
public void onClose(CloseReason reason, WebSocketClientConnection connection) {
13-
int code = reason.getCode();
14-
LOG.info(\\"Websocket disconnected from HoppscotchEchoWebSocketClient with Close code: \\" + code);
15-
}
16-
}"
12+
public void onClose(CloseReason reason, WebSocketClientConnection connection) {
13+
int code = reason.getCode();
14+
LOG.info(\\"Websocket disconnected from HoppscotchEchoWebSocketClient with Close code: \\" + code);
15+
}
16+
}"
1717
`;
1818

1919
exports[`Testing of OnClose function render javascript OnClose method 1`] = `

packages/templates/clients/websocket/java/quarkus/README.md

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,33 @@ You can use our AsyncAPI's credentials to access different set of events produce
7878
5. Generate the template client with `node .\apps\generator\cli.js <path-to-custom-document> .\packages\templates\clients\websocket\java\quarkus\ -o outputClient --force-write --param server=<custom-server>`
7979
6. Navigate to `outputClient` or any other name you gave the output folder
8080
7. Run `mvn quarkus:dev`
81-
8. See the output in the terminal
81+
8. See the output in the terminal
82+
83+
### Deploying with Kubernetes
84+
85+
Quarkus can automatically generate Kubernetes manifests and container images using the Kubernetes extensions added in your template.
86+
87+
Build your project with:
88+
89+
```bash
90+
mvn clean package
91+
```
92+
93+
This will generate Kubernetes manifests in `target/kubernetes/`. Deploy your app to a cluster with:
94+
95+
```bash
96+
kubectl apply -f target/kubernetes/kubernetes.yml
97+
```
98+
99+
> **Note:**
100+
> The generated deployment uses an image like `username/quarkus-websocket:1.0.0-SNAPSHOT` by default.
101+
> You can customize the image name in `application.properties`:
102+
>
103+
> ```properties
104+
> quarkus.container-image.group=quarkus
105+
> quarkus.container-image.name=demo-app
106+
> quarkus.container-image.tag=1.0
107+
> ```
108+
> The resulting image will be `quarkus/demo-app:1.0`.
109+
110+
For more details, see the [Quarkus Kubernetes guide](https://quarkus.io/guides/deploying-to-kubernetes).

packages/templates/clients/websocket/java/quarkus/components/ClientConnector.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import { Text } from '@asyncapi/generator-react-sdk';
22
import InitConnector from './InitConnector';
33
import { ConnectorFields } from './ConnectorFields';
44

5-
export default function ClientConnector({ clientName, query, pathName }) {
5+
export default function ClientConnector({ clientName, query, pathName, operations }) {
66
const queryParamsArray = query && Array.from(query.entries());
7+
const sendOperations = operations.filterBySend();
78
if (!pathName) {
89
pathName = '/';
910
}
@@ -17,7 +18,7 @@ export default function ClientConnector({ clientName, query, pathName }) {
1718
public class ${clientName}Connector{`}
1819
</Text>
1920
<ConnectorFields clientName={clientName} queryParamsArray={queryParamsArray} />
20-
<InitConnector queryParamsArray={queryParamsArray} pathName={pathName}/>
21+
<InitConnector queryParamsArray={queryParamsArray} pathName={pathName} sendOperations={sendOperations} />
2122
</Text>
2223
);
2324
}

packages/templates/clients/websocket/java/quarkus/components/ClientFields.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export function ClientFields({ queryParams, clientName }) {
55
let queryParamsVariables = '';
66
const queryParamsArray = queryParams && Array.from(queryParams.entries());
77

8-
if (queryParamsArray) {
8+
if (queryParamsArray && queryParamsArray.length > 0) {
99
queryParamsVariables = '\nprivate HashMap<String, String> params;\n';
1010
queryParamsVariables += queryParamsArray.map((param) => {
1111
const paramName = toCamelCase(param[0]);

packages/templates/clients/websocket/java/quarkus/components/InitConnector.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import TimedConnection from './TimedConnection';
33
import URIParams from './URIParams';
44
import { CloseConnection } from '@asyncapi/generator-components';
55

6-
export default function InitConnector({ queryParamsArray, pathName }) {
6+
export default function InitConnector({ queryParamsArray, pathName, sendOperations }) {
77
return (
88
<Text>
99
{`
@@ -14,7 +14,7 @@ export default function InitConnector({ queryParamsArray, pathName }) {
1414
Log.info("Starting WebSocket connection attempt...");`}
1515
{
1616
(!queryParamsArray || queryParamsArray.length === 0) && (
17-
<TimedConnection />
17+
<TimedConnection sendOperations={sendOperations} />
1818
)
1919
}
2020
<URIParams queryParamsArray={queryParamsArray} pathName={pathName} />

packages/templates/clients/websocket/java/quarkus/components/OnTextMessageHandler.js

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,55 @@
11
import { Text } from '@asyncapi/generator-react-sdk';
2+
import { toCamelCase } from '@asyncapi/generator-helpers';
23

34
export default function OnTextMessage({ sendOperations }) {
45
return (
56
<>
67
{
8+
// If there are operations, emit a single @OnTextMessage
9+
(sendOperations && sendOperations.length !== 0) && (
10+
<Text newLines={2} indent={2}>
11+
{`@OnTextMessage
12+
public void handleTextMessage(String message, WebSocketClientConnection connection) {
13+
LOG.info("Handler received text message: " + message);
14+
${sendOperations.map((operation, idx) => {
15+
const methodName = operation.id();
16+
const javaMethodName = toCamelCase(methodName);
17+
const condition = idx === 0 ? ` if (message != null && message.contains("${methodName}")) {` : ` else if (message != null && message.contains("${methodName}")) {`;
18+
return `${condition}
19+
${javaMethodName}(message, connection);
20+
}`;
21+
}).join('\n')}
22+
else {
23+
LOG.warn("Handler received unrecognized message type. Falling back to default handler.");
24+
// Note: By default, we route unrecognized messages to the first operation handler.
25+
// Depending on your business logic, you may want to change this behavior.
26+
${toCamelCase(sendOperations[0].id())}(message, connection);
27+
}
28+
}`}
29+
</Text>
30+
)
31+
}
32+
33+
{
34+
// Generate handlers for each operation
735
(sendOperations && sendOperations.length !== 0) && (
836
sendOperations.map((operation) => {
9-
const methodName = operation.id();
37+
const methodName = operation.id();
38+
const javaMethodName = toCamelCase(methodName);
1039
return (
1140
<Text newLines={2} indent={2}>
12-
{`@OnTextMessage
13-
public void ${methodName}(String message, WebSocketClientConnection connection) {
14-
LOG.info("Received text message: " + message);
41+
{`public void ${javaMethodName}(String message, WebSocketClientConnection connection) {
42+
LOG.info("Processing ${methodName} type message: " + message);
43+
// TODO: implement processing logic for ${methodName}
1544
}`}
1645
</Text>
1746
);
1847
})
19-
)}
48+
)
49+
}
2050

2151
{
22-
// need a default handler
52+
// No operations provided need a default handler
2353
(!sendOperations || sendOperations.length === 0) && (
2454
<Text newLines={2} indent={2}>
2555
{`@OnTextMessage

packages/templates/clients/websocket/java/quarkus/components/TimedConnection.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { Text } from '@asyncapi/generator-react-sdk';
22

3-
export default function TimedConnection() {
3+
export default function TimedConnection({ sendOperations }) {
44
const initialDelayMs = 2000;
55
const messageCount = 5;
66
const messageIntervalMs = 5000;
77
const finalDelayMs = 10000;
8+
const firstOperationId = sendOperations && sendOperations.length > 0 ? sendOperations[0].id() : null;
89
return (
910
<Text newLines={2}>
1011
{`
@@ -15,7 +16,8 @@ export default function TimedConnection() {
1516
1617
// Send ${messageCount} messages
1718
for (int i = 1; i <= ${messageCount}; i++) {
18-
String msg = "Message #" + i + " from Quarkus";
19+
// Send a message to any available operation by including its operation ID (i.e. "${firstOperationId}")
20+
String msg = ${firstOperationId ? `"Message #" + i + " from Quarkus for ${firstOperationId}"` : '"Message #" + i + " from Quarkus"'};
1921
connection.sendTextAndAwait(msg);
2022
Log.info("Sent: " + msg);
2123
Thread.sleep(${messageIntervalMs}); // Wait 5 seconds between messages

0 commit comments

Comments
 (0)