diff --git a/.gitignore b/.gitignore
index f1e3d20..525471d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -250,3 +250,58 @@ paket-files/
# JetBrains Rider
.idea/
*.sln.iml
+
+# Java
+*.class
+
+#External libs
+extlib/
+
+# Auth files
+*.auth
+*.azureauth
+
+# Local checkstyle
+*.checkstyle
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.ear
+
+# Azure Tooling #
+node_modules
+packages
+
+# Eclipse #
+*.pydevproject
+.project
+.metadata
+bin/**
+tmp/**
+tmp/**/*
+*.tmp
+*.bak
+*.swp
+*~.nib
+local.properties
+.classpath
+.settings/
+.loadpath
+
+# Other Tooling #
+.classpath
+.project
+target
+.idea
+*.iml
+
+# Mac OS #
+.DS_Store
+.DS_Store?
+
+# Windows #
+Thumbs.db
\ No newline at end of file
diff --git a/samples/hybrid-connections/java/simple-http-demo/.classpath b/samples/hybrid-connections/java/simple-http-demo/.classpath
new file mode 100644
index 0000000..f914ad4
--- /dev/null
+++ b/samples/hybrid-connections/java/simple-http-demo/.classpath
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/samples/hybrid-connections/java/simple-http-demo/src/samples/HttpListener.java b/samples/hybrid-connections/java/simple-http-demo/src/samples/HttpListener.java
new file mode 100644
index 0000000..6cdbeec
--- /dev/null
+++ b/samples/hybrid-connections/java/simple-http-demo/src/samples/HttpListener.java
@@ -0,0 +1,56 @@
+package samples;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.ByteBuffer;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.concurrent.CompletableFuture;
+
+import com.microsoft.azure.relay.HybridConnectionListener;
+import com.microsoft.azure.relay.HybridConnectionUtil;
+import com.microsoft.azure.relay.RelayedHttpListenerResponse;
+import com.microsoft.azure.relay.TokenProvider;
+
+public class HttpListener {
+ static final String CONNECTION_STRING_ENV_VARIABLE_NAME = "RELAY_CONNECTION_STRING";
+ static final Map connectionParams = HybridConnectionUtil.parseConnectionString(System.getenv(CONNECTION_STRING_ENV_VARIABLE_NAME));
+ static final String RELAY_NAMESPACE = connectionParams.get("Endpoint");
+ static final String ENTITY_PATH = connectionParams.get("EntityPath");
+ static final String KEY_NAME = connectionParams.get("SharedAccessKeyName");
+ static final String KEY = connectionParams.get("SharedAccessKey");
+
+ public static void main(String[] args) throws URISyntaxException {
+ TokenProvider tokenProvider = TokenProvider.createSharedAccessSignatureTokenProvider(KEY_NAME, KEY);
+ HybridConnectionListener listener = new HybridConnectionListener(new URI(RELAY_NAMESPACE + ENTITY_PATH), tokenProvider);
+
+ listener.setRequestHandler((context) -> {
+ ByteBuffer inputStream = context.getRequest().getInputStream();
+ String receivedText = (inputStream != null) ? new String(inputStream.array()) : "";
+ System.out.println("requestHandler received " + receivedText);
+
+ RelayedHttpListenerResponse response = context.getResponse();
+ response.setStatusCode(202);
+ response.setStatusDescription("OK");
+
+ try {
+ response.getOutputStream().write(("Echo: " + receivedText).getBytes());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ // The context MUST be closed for the message to be sent
+ context.getResponse().close();
+ });
+
+ listener.openAsync().join();
+
+ Scanner in = new Scanner(System.in);
+ System.out.println("Press ENTER to terminate this program.");
+ in.nextLine();
+
+ listener.closeAsync().join();
+ in.close();
+ }
+}
diff --git a/samples/hybrid-connections/java/simple-http-demo/src/samples/HttpSender.java b/samples/hybrid-connections/java/simple-http-demo/src/samples/HttpSender.java
new file mode 100644
index 0000000..983429f
--- /dev/null
+++ b/samples/hybrid-connections/java/simple-http-demo/src/samples/HttpSender.java
@@ -0,0 +1,63 @@
+package samples;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.time.Duration;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.concurrent.ExecutionException;
+
+import com.microsoft.azure.relay.HybridConnectionUtil;
+import com.microsoft.azure.relay.StringUtil;
+import com.microsoft.azure.relay.TokenProvider;
+
+public class HttpSender {
+ static final String CONNECTION_STRING_ENV_VARIABLE_NAME = "RELAY_CONNECTION_STRING";
+ static final Map connectionParams = HybridConnectionUtil.parseConnectionString(System.getenv(CONNECTION_STRING_ENV_VARIABLE_NAME));
+ static final String RELAY_NAMESPACE = connectionParams.get("Endpoint");
+ static final String ENTITY_PATH = connectionParams.get("EntityPath");
+ static final String KEY_NAME = connectionParams.get("SharedAccessKeyName");
+ static final String KEY = connectionParams.get("SharedAccessKey");
+
+ public static void main(String[] args) throws IOException, InterruptedException, ExecutionException {
+ TokenProvider tokenProvider = TokenProvider.createSharedAccessSignatureTokenProvider(KEY_NAME, KEY);
+ String urlString = HybridConnectionUtil.getURLString(RELAY_NAMESPACE, ENTITY_PATH);
+ String tokenString = tokenProvider.getTokenAsync(urlString, Duration.ofHours(1)).join().getToken();
+ Scanner in = new Scanner(System.in);
+
+ while (true) {
+ System.out.println("Please enter the message you want to send over http, \"quit\" or \"q\" to terminate:");
+ String message = in.nextLine();
+ if (message.equalsIgnoreCase("quit") || message.equalsIgnoreCase("q")) break;
+
+ HttpURLConnection conn = (HttpURLConnection)new URL(urlString).openConnection();
+ // To send a message body, use POST
+ conn.setRequestMethod(StringUtil.isNullOrEmpty(message) ? "GET" : "POST");
+ conn.setRequestProperty("ServiceBusAuthorization", tokenString);
+ conn.setDoOutput(true);
+
+ OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream());
+ out.write(message, 0, message.length());
+ out.flush();
+ out.close();
+
+ String inputLine;
+ StringBuilder responseBuilder = new StringBuilder();
+ BufferedReader inStream = new BufferedReader(new InputStreamReader(conn.getInputStream()));
+
+ System.out.println("status code: " + conn.getResponseCode());
+ while ((inputLine = inStream.readLine()) != null) {
+ responseBuilder.append(inputLine);
+ }
+
+ inStream.close();
+ System.out.println("received back " + responseBuilder.toString());
+ }
+
+ in.close();
+ }
+}
diff --git a/samples/hybrid-connections/java/simple-websocket-demo/.classpath b/samples/hybrid-connections/java/simple-websocket-demo/.classpath
new file mode 100644
index 0000000..f914ad4
--- /dev/null
+++ b/samples/hybrid-connections/java/simple-websocket-demo/.classpath
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/samples/hybrid-connections/java/simple-websocket-demo/src/samples/WebsocketListener.java b/samples/hybrid-connections/java/simple-websocket-demo/src/samples/WebsocketListener.java
new file mode 100644
index 0000000..347b7b6
--- /dev/null
+++ b/samples/hybrid-connections/java/simple-websocket-demo/src/samples/WebsocketListener.java
@@ -0,0 +1,60 @@
+package samples;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.ByteBuffer;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.concurrent.CompletableFuture;
+
+import com.microsoft.azure.relay.ClientWebSocket;
+import com.microsoft.azure.relay.HybridConnectionListener;
+import com.microsoft.azure.relay.HybridConnectionUtil;
+import com.microsoft.azure.relay.TokenProvider;
+
+public class WebsocketListener {
+ static boolean quit = false;
+
+ static final String CONNECTION_STRING_ENV_VARIABLE_NAME = "RELAY_CONNECTION_STRING";
+ static final Map connectionParams = HybridConnectionUtil.parseConnectionString(System.getenv(CONNECTION_STRING_ENV_VARIABLE_NAME));
+ static final String RELAY_NAMESPACE = connectionParams.get("Endpoint");
+ static final String ENTITY_PATH = connectionParams.get("EntityPath");
+ static final String KEY_NAME = connectionParams.get("SharedAccessKeyName");
+ static final String KEY = connectionParams.get("SharedAccessKey");
+
+ public static void main(String[] args) throws URISyntaxException {
+ TokenProvider tokenProvider = TokenProvider.createSharedAccessSignatureTokenProvider(KEY_NAME, KEY);
+ HybridConnectionListener listener = new HybridConnectionListener(new URI(RELAY_NAMESPACE + ENTITY_PATH), tokenProvider);
+
+ listener.openAsync().join();
+ System.out.println("Listener is online. Press ENTER to terminate this program.");
+
+ CompletableFuture.runAsync(() -> {
+ Scanner in = new Scanner(System.in);
+ in.nextLine();
+
+ listener.closeAsync().join();
+ in.close();
+ });
+
+ while (listener.isOnline()) {
+ ClientWebSocket websocket = listener.acceptConnectionAsync().join();
+
+ // If listener closes, then listener.acceptConnectionAsync() will complete with null after closing down
+ if (websocket != null) {
+ CompletableFuture.runAsync(() -> {
+ System.out.println("New session connected.");
+
+ while (websocket.isOpen()) {
+ ByteBuffer bytesReceived = websocket.receiveMessageAsync().join();
+ String msg = new String(bytesReceived.array());
+
+ System.out.println("Received: " + msg);
+ websocket.sendAsync("Echo: " + msg);
+ }
+ System.out.println("Session disconnected.");
+ });
+ }
+ }
+ }
+}
diff --git a/samples/hybrid-connections/java/simple-websocket-demo/src/samples/WebsocketSender.java b/samples/hybrid-connections/java/simple-websocket-demo/src/samples/WebsocketSender.java
new file mode 100644
index 0000000..5c4e897
--- /dev/null
+++ b/samples/hybrid-connections/java/simple-websocket-demo/src/samples/WebsocketSender.java
@@ -0,0 +1,43 @@
+package samples;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.concurrent.ExecutionException;
+
+import com.microsoft.azure.relay.HybridConnectionClient;
+import com.microsoft.azure.relay.HybridConnectionUtil;
+import com.microsoft.azure.relay.TokenProvider;
+
+public class WebsocketSender {
+ static final String CONNECTION_STRING_ENV_VARIABLE_NAME = "RELAY_CONNECTION_STRING";
+ static final Map connectionParams = HybridConnectionUtil.parseConnectionString(System.getenv(CONNECTION_STRING_ENV_VARIABLE_NAME));
+ static final String RELAY_NAMESPACE = connectionParams.get("Endpoint");
+ static final String ENTITY_PATH = connectionParams.get("EntityPath");
+ static final String KEY_NAME = connectionParams.get("SharedAccessKeyName");
+ static final String KEY = connectionParams.get("SharedAccessKey");
+
+ public static void main(String[] args) throws InterruptedException, ExecutionException, URISyntaxException {
+ TokenProvider tokenProvider = TokenProvider.createSharedAccessSignatureTokenProvider(KEY_NAME, KEY);
+ HybridConnectionClient client = new HybridConnectionClient(new URI(RELAY_NAMESPACE + ENTITY_PATH), tokenProvider);
+
+ Scanner in = new Scanner(System.in);
+
+ client.createConnectionAsync().thenAccept((socket) -> {
+ while (true) {
+ System.out.println("Please enter the text you want to send, or enter \"quit\" or \"q\" to exit");
+ String input = in.nextLine();
+ if (input.equalsIgnoreCase("quit") || input.equalsIgnoreCase("q")) break;
+ socket.sendAsync(input).join();
+
+ socket.receiveMessageAsync().thenAccept((byteBuffer) -> {
+ System.out.println("Received: " + new String(byteBuffer.array()));
+ });
+ }
+
+ client.closeAsync().join();
+ in.close();
+ });
+ }
+}