Skip to content

Commit d9d6dd9

Browse files
feat: WebSocket 0.1
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Michael Vorburger <[email protected]>
1 parent e503970 commit d9d6dd9

File tree

11 files changed

+291
-1
lines changed

11 files changed

+291
-1
lines changed

MODULE.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ maven.install(
111111
"com.github.vorburger.adk-java:google-adk-dev:4dd09e7a3c008b0da11dbf25e1fed0efa9c5f2d7",
112112
"org.commonmark:commonmark:0.26.0",
113113
"io.modelcontextprotocol.sdk:mcp:0.15.0",
114+
"org.java-websocket:Java-WebSocket:1.6.0",
114115
],
115116

116117
# NB: Never de-activate duplicate_version_warning = "error"!

MODULE.bom.handlebars.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,9 @@ bom:
148148
# https://github.com/modelcontextprotocol/java-sdk
149149
- io.modelcontextprotocol.sdk:mcp:0.15.0
150150

151+
# https://github.com/TooTallNate/Java-WebSocket
152+
- org.java-websocket:Java-WebSocket:1.6.0
153+
151154
bomx:
152155
- "com.github.vorburger.adk-java:google-adk-langchain4j:{{version.adk}}":
153156
exclusions:

flake.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
# bazelisk, and then rm this, and .bazelversion
4242
bazelisk
4343
shellcheck
44+
twilio-cli
4445
nixpkgs-fmt
4546
unzip
4647
nodejs
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
#
3+
# Copyright 2025 The Enola <https://enola.dev> Authors
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# https://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
load("@rules_java//java:defs.bzl", "java_library")
18+
load("//tools/bazel:junit.bzl", "junit_tests")
19+
20+
java_library(
21+
name = "relay",
22+
srcs = glob(
23+
["*.java"],
24+
exclude = [
25+
"*Test.java",
26+
],
27+
),
28+
visibility = ["//:__subpackages__"],
29+
deps = [
30+
"@maven//:org_slf4j_slf4j_api",
31+
],
32+
)
33+
34+
junit_tests(
35+
name = "tests",
36+
srcs = glob(
37+
["*Test.java"],
38+
),
39+
deps = [
40+
":relay",
41+
],
42+
)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* Copyright 2025 The Enola <https://enola.dev> Authors
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* https://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package dev.enola.audio.voice.twilio.relay;
19+
20+
public class ConversationRelay {}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* Copyright 2025 The Enola <https://enola.dev> Authors
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* https://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package dev.enola.audio.voice.twilio.relay;
19+
20+
import org.junit.Test;
21+
22+
public class ConversationRelayTest {
23+
24+
@Test
25+
public void first() {}
26+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
#
3+
# Copyright 2025 The Enola <https://enola.dev> Authors
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# https://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
load("@rules_java//java:java_binary.bzl", "java_binary")
18+
19+
java_binary(
20+
name = "websocket",
21+
srcs = glob(
22+
["*.java"],
23+
exclude = [
24+
"*Test.java",
25+
],
26+
),
27+
main_class = "dev.enola.audio.voice.twilio.relay.websocket.Main",
28+
runtime_deps = [
29+
"//java/dev/enola/audio/voice/twilio/relay",
30+
],
31+
deps = [
32+
"//java/dev/enola/common/logging",
33+
"@maven//:org_java_websocket_Java_WebSocket",
34+
"@maven//:org_jspecify_jspecify",
35+
"@maven//:org_slf4j_slf4j_api",
36+
],
37+
)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* Copyright 2025 The Enola <https://enola.dev> Authors
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* https://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package dev.enola.audio.voice.twilio.relay.websocket;
19+
20+
import org.java_websocket.WebSocket;
21+
import org.slf4j.Logger;
22+
import org.slf4j.LoggerFactory;
23+
24+
import java.net.InetSocketAddress;
25+
26+
class ConversationRelayServer extends LoggingWebSocketServer {
27+
28+
private static final Logger logger = LoggerFactory.getLogger(ConversationRelayServer.class);
29+
30+
public ConversationRelayServer(InetSocketAddress address) {
31+
super(address);
32+
}
33+
34+
@Override
35+
public void onMessage(WebSocket conn, String message) {
36+
// TODO Change info to debug - and actually parse the JSON, into ConversationRelay
37+
logger.info("WebSocket message received: message={}", message);
38+
}
39+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* Copyright 2025 The Enola <https://enola.dev> Authors
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* https://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package dev.enola.audio.voice.twilio.relay.websocket;
19+
20+
import org.java_websocket.WebSocket;
21+
import org.java_websocket.handshake.ClientHandshake;
22+
import org.java_websocket.server.WebSocketServer;
23+
import org.jspecify.annotations.Nullable;
24+
import org.slf4j.Logger;
25+
import org.slf4j.LoggerFactory;
26+
27+
import java.net.InetSocketAddress;
28+
29+
public abstract class LoggingWebSocketServer extends WebSocketServer {
30+
31+
private static final Logger logger = LoggerFactory.getLogger(LoggingWebSocketServer.class);
32+
33+
protected LoggingWebSocketServer(InetSocketAddress address) {
34+
super(address);
35+
}
36+
37+
@Override
38+
public void onStart() {
39+
logger.info("WebSocket server started on {}", getAddress());
40+
}
41+
42+
@Override
43+
public void onOpen(WebSocket conn, ClientHandshake handshake) {
44+
logger.info(
45+
"WebSocket connection opened: remote={}, local={}",
46+
conn.getRemoteSocketAddress(),
47+
conn.getLocalSocketAddress());
48+
}
49+
50+
@Override
51+
public void onClose(WebSocket conn, int code, String reason, boolean remote) {
52+
logger.info(
53+
"WebSocket connection closed: remote={}, local={}, code={}, reason={}, remote={}",
54+
conn.getRemoteSocketAddress(),
55+
conn.getLocalSocketAddress(),
56+
code,
57+
reason,
58+
remote);
59+
}
60+
61+
@Override
62+
public void onError(@Nullable WebSocket conn, Exception ex) {
63+
if (conn != null) {
64+
logger.error(
65+
"WebSocket error on connection: remote={}, local={}",
66+
conn.getRemoteSocketAddress(),
67+
conn.getLocalSocketAddress(),
68+
ex);
69+
} else {
70+
logger.error("WebSocket server error", ex);
71+
}
72+
}
73+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* Copyright 2025 The Enola <https://enola.dev> Authors
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* https://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package dev.enola.audio.voice.twilio.relay.websocket;
19+
20+
import dev.enola.common.logging.JavaUtilLogging;
21+
22+
import org.java_websocket.server.WebSocketServer;
23+
24+
import java.net.InetSocketAddress;
25+
import java.util.logging.Level;
26+
27+
public class Main {
28+
public static void main(String[] args) {
29+
String host = args.length > 0 ? args[0] : "localhost";
30+
int port = args.length > 1 ? Integer.parseInt(args[1]) : 8888;
31+
32+
JavaUtilLogging.configure(Level.ALL);
33+
WebSocketServer server = new ConversationRelayServer(new InetSocketAddress(host, port));
34+
Runtime.getRuntime()
35+
.addShutdownHook(
36+
new Thread(
37+
() -> {
38+
try {
39+
server.stop(1000); // 1 second timeout
40+
} catch (InterruptedException e) {
41+
System.err.println(
42+
"Failed to stop the WebSocketServer gracefully.");
43+
Thread.currentThread().interrupt();
44+
}
45+
}));
46+
server.run();
47+
}
48+
}

0 commit comments

Comments
 (0)