Java での Relay ハイブリッド接続 HTTP 要求の概要

このクイックスタートでは、HTTP プロトコルを使ってメッセージを送受信する Java のセンダー アプリケーションとレシーバー アプリケーションを作成します。 これらのアプリケーションには、Azure Relay のハイブリッド接続機能が使用されます。 Azure Relay 全般については、Azure Relay に関するページを参照してください。

このクイック スタートでは、以下の手順を実行します。

  1. Azure Portal を使用した Relay 名前空間の作成
  2. Azure Portal を使用した、その名前空間内のハイブリッド接続の作成
  3. メッセージを受信するサーバー (リスナー) コンソール アプリケーションの作成
  4. メッセージを送信するクライアント (送信側) コンソール アプリケーションの作成
  5. アプリケーションの実行

前提条件

  • Java. JDK 1.8 以降を実行していることを確認します
  • Maven。 Maven がインストールされていることを確認します
  • Azure Relay SDK. Java SDK を確認します
  • Azure サブスクリプション。 お持ちでない場合は、開始する前に無料アカウントを作成してください。

Azure Portal を使用した名前空間の作成

  1. Azure portal にサインインします。

  2. 左側のメニューから、 [すべてのサービス] を選択します。 [統合] を選択し、[リレー] を検索し、[リレー] の上にマウス ポインターを移動して、[作成] を選択します。

    [リレー] -> [作成] ボタンの選択を示すスクリーンショット。

  3. [名前空間の作成] ページで、これらの手順を実行します。

    1. 名前空間を作成する Azure サブスクリプションを選択します。

    2. [リソース グループ] で、名前空間を追加する既存のリソース グループを選択するか、新しいリソース グループを作成します。

    3. リレー名前空間の名前を入力します。

    4. 名前空間をホストするリージョンを選択します。

    5. ページ下部にある [確認と作成] を選択します。

      [名前空間の作成] ページを示すスクリーンショット。

    6. [確認および作成] ページで、 [作成] を選択します。

    7. 数分後に、名前空間の [リレー] ページを確認します。

      リレー名前空間のホーム ページを示すスクリーンショット。

管理資格情報を取得する

  1. [リレー] ページで、左側のメニューの [共有アクセス ポリシー] を選択します。 `

  2. [共有アクセス ポリシー] ページで、 [RootManageSharedAccessKey] を選択します。

  3. [SAS ポリシー:RootManageSharedAccessKey] の下で、 [プライマリ接続文字列] の横の [コピー] ボタンを選択します。 これで、後で使用できるように接続文字列がクリップボードにコピーされます。 この値をメモ帳などに一時的に貼り付けます。

  4. 前の手順を繰り返し、 [主キー] の値をコピーして、後で使用するために一時的な場所に貼り付けます。

    リレー名前空間の接続情報を示すスクリーンショット。

Azure Portal を使用したハイブリッド接続の作成

名前空間の [リレー] ページで、次の手順に従ってハイブリッド接続を作成します。

  1. 左側のメニューの [エンティティ] の下で、[ハイブリッド接続] を選択し、[+ ハイブリッド接続] を選択します。

    [ハイブリッド接続] ページを示すスクリーンショット。

  2. [ハイブリッド接続の作成] ページで、ハイブリッド接続の名前を入力し、[作成] を選択します。

    [ハイブリッド接続の作成] ページを示すスクリーンショット。

サーバー アプリケーション (リスナー) の作成

Relay からのメッセージをリッスンして受信するため、Java コンソール アプリケーションを作成します。

Java アプリケーションの作成

リレーの作成時に [クライアント認証が必要] オプションを無効にした場合は、ブラウザーを問わずハイブリッド接続 URL に要求を送信できます。 保護されているエンドポイントにアクセスするには、ここに示したように ServiceBusAuthorization ヘッダーでトークンを作成して渡す必要があります。

ここで見ていただく簡単な Maven プロジェクト構造と Java クラスは、Azure Relay ライブラリを利用して、クライアントの認可を行うハイブリッド接続 URL に要求を送信します。

Relay パッケージを追加する

maven アプリケーション パッケージの pom.xml ファイルを、Azure Relay パッケージを含むように変更します。

<dependency>
    <groupId>com.microsoft.azure</groupId>
    <artifactId>azure-relay</artifactId>
    <version>0.0.6</version>
</dependency>

mvn プロジェクトで mvn dependency:copy-dependencies -DoutputDirectory=lib を実行して、プロジェクトの lib ディレクトリに依存関係の jar ファイルを追加します。 これにより、azure-relay mvn パッケージのすべての依存関係がインポートされます。 このパッケージは、Relay の Uniform Resource Identifier (URI) とトークンを構築するための関数を提供します。

メッセージを送信するコードを記述する

  1. 依存関係の jar ファイルを、Listener.java ファイルの ClassPath に追加します。

    javac -cp lib/* src/main/java/com/example/listener/Listener.Java
    
  2. 依存関係を Listener.java クラスにインポートします。

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.URI;
    import java.net.URISyntaxException;
    import java.util.Scanner;
    import com.microsoft.azure.relay.HybridConnectionListener;
    import com.microsoft.azure.relay.RelayConnectionStringBuilder;
    import com.microsoft.azure.relay.RelayedHttpListenerResponse;
    import com.microsoft.azure.relay.TokenProvider;
    
  3. ハイブリッド接続に関する詳細を createConnectionString Java 関数に渡す次の constants を、Listener.java ファイルの先頭に追加します。

    public static String createConnectionString(){
        StringBuilder connectionString = new StringBuilder();
        connectionString.append("Endpoint=sb://");
        connectionString.append("{namespace}");
        connectionString.append(".servicebus.windows.net/;SharedAccessKeyName=");
        connectionString.append("{keyrule}");
        connectionString.append(";SharedAccessKey=");
        connectionString.append("{key}");
        connectionString.append(";EntityPath=");
        connectionString.append("{path}");
        return connectionString.toString();
    }
    

    中かっこ内のプレースホルダーを、ハイブリッド接続の作成時に取得した値に置き換えます。

    • namespace - Relay 名前空間。 必ず完全修飾名前空間名を使用してください (たとえば、{namespace}.servicebus.windows.net)。
    • path - ハイブリッド接続の名前。
    • keyrule - 共有アクセス ポリシー キーの名前 (既定では RootManageSharedAccessKey)。
    • nst key - 前に保存した名前空間の主キー。
  4. 次のコードを Listener.java ファイルに追加します。 main 関数は次のようなコードになります。

    public static void main( String[] args ) throws URISyntaxException
    {
        String CONNECTION_STRING_ENV_VARIABLE_NAME = createConnectionString();
        RelayConnectionStringBuilder connectionParams = new RelayConnectionStringBuilder(CONNECTION_STRING_ENV_VARIABLE_NAME);
        TokenProvider tokenProvider = TokenProvider.createSharedAccessSignatureTokenProvider(
                    connectionParams.getSharedAccessKeyName(),
                    connectionParams.getSharedAccessKey());
        HybridConnectionListener listener = new HybridConnectionListener(new URI(connectionParams.getEndpoint().toString() + connectionParams.getEntityPath()), tokenProvider);
    
        // The "context" object encapsulates both the incoming request and the outgoing response
        listener.setRequestHandler((context) -> {
            String receivedText = "";
            if (context.getRequest().getInputStream() != null) {
                try (BufferedReader reader = new BufferedReader(new InputStreamReader(context.getRequest().getInputStream(), "UTF8"))) {
                    StringBuilder builder = new StringBuilder();
                    String inputLine;
                    while ((inputLine = reader.readLine()) != null) {
                        builder.append(inputLine);
                    }
                    receivedText = builder.toString();
                } catch (IOException e) {
                    System.out.println(e.getMessage());
                }
            }
            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
            response.close();
        });
    
        listener.openAsync().join();
    
        Scanner in = new Scanner(System.in);
        System.out.println("Press ENTER to terminate this program.");
        in.nextLine();
    
        listener.close();
        in.close();
    }
    
    

    Listener.java ファイルは次のようになります。

    package com.example.listener;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.URI;
    import java.net.URISyntaxException;
    import java.util.Scanner;
    import com.microsoft.azure.relay.HybridConnectionListener;
    import com.microsoft.azure.relay.RelayConnectionStringBuilder;
    import com.microsoft.azure.relay.RelayedHttpListenerResponse;
    import com.microsoft.azure.relay.TokenProvider;
    
     public class Listener
     {
        public static String createConnectionString(){
            StringBuilder connectionString = new StringBuilder();
            connectionString.append("Endpoint=sb://");
            connectionString.append("{namespace}");
            connectionString.append(".servicebus.windows.net/;SharedAccessKeyName=");
            connectionString.append("{keyrule}");
            connectionString.append(";SharedAccessKey=");
            connectionString.append("{key}");
            connectionString.append(";EntityPath=");
            connectionString.append("{path}");
            return connectionString.toString();
        }
    
        public static void main( String[] args ) throws URISyntaxException
        {
            String CONNECTION_STRING_ENV_VARIABLE_NAME = createConnectionString();
            RelayConnectionStringBuilder connectionParams = new RelayConnectionStringBuilder(CONNECTION_STRING_ENV_VARIABLE_NAME);
            TokenProvider tokenProvider = TokenProvider.createSharedAccessSignatureTokenProvider(
                        connectionParams.getSharedAccessKeyName(),
                        connectionParams.getSharedAccessKey());
            HybridConnectionListener listener = new HybridConnectionListener(new URI(connectionParams.getEndpoint().toString() + connectionParams.getEntityPath()), tokenProvider);
    
            // The "context" object encapsulates both the incoming request and the outgoing response
            listener.setRequestHandler((context) -> {
                String receivedText = "";
                if (context.getRequest().getInputStream() != null) {
                    try (BufferedReader reader = new BufferedReader(new InputStreamReader(context.getRequest().getInputStream(), "UTF8"))) {
                        StringBuilder builder = new StringBuilder();
                        String inputLine;
                        while ((inputLine = reader.readLine()) != null) {
                            builder.append(inputLine);
                        }
                        receivedText = builder.toString();
                    } catch (IOException e) {
                        System.out.println(e.getMessage());
                    }
                }
                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
                response.close();
            });
    
            listener.openAsync().join();
    
            Scanner in = new Scanner(System.in);
            System.out.println("Press ENTER to terminate this program.");
            in.nextLine();
    
            listener.close();
            in.close();
        }
    }
    

クライアント アプリケーション (センダー) の作成

Relay にメッセージを送信するには、任意の HTTP クライアントを使うことも、Java コンソール アプリケーションを作成することもできます。

Java アプリケーションの作成

リレーの作成時に [クライアント認証が必要] オプションを無効にした場合は、ブラウザーを問わずハイブリッド接続 URL に要求を送信できます。 保護されているエンドポイントにアクセスするには、ここに示したように ServiceBusAuthorization ヘッダーでトークンを作成して渡す必要があります。

ここで見ていただく簡単な Maven プロジェクト構造と Java クラスは、Azure Relay ライブラリを利用して、クライアントの認可を行うハイブリッド接続 URL に要求を送信します。

Relay パッケージを追加する

maven アプリケーション パッケージの pom.xml ファイルを、Azure Relay パッケージを含むように変更します。

<dependency>
	<groupId>com.microsoft.azure</groupId>
	<artifactId>azure-relay</artifactId>
	<version>0.0.6</version>
</dependency>

mvn プロジェクトで mvn dependency:copy-dependencies -DoutputDirectory=lib を実行して、プロジェクトの lib ディレクトリに依存関係の jar ファイルを追加します。 これにより、azure-relay mvn パッケージのすべての依存関係もインポートされます。 このパッケージは、Relay の Uniform Resource Identifier (URI) とトークンを構築するための関数を提供します。

メッセージを送信するコードを記述する

  1. 依存関係の jar ファイルを、Sender.java ファイルの ClassPath に追加します。

    javac -cp lib/* src/main/java/com/example/sender/Sender.Java
    
  2. 依存関係を Sender.java クラスにインポートします。

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.net.HttpURLConnection;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.time.Duration;
    import java.util.Scanner;
    import com.microsoft.azure.relay.RelayConnectionStringBuilder;
    import com.microsoft.azure.relay.TokenProvider;
    
  3. ハイブリッド接続に関する詳細を createConnectionString Java 関数に渡す次の constants を、Sender.java ファイルの先頭に追加します。

    public static String createConnectionString(){
        StringBuilder connectionString = new StringBuilder();
        connectionString.append("Endpoint=sb://");
        connectionString.append("{namespace}");
        connectionString.append(".servicebus.windows.net/;SharedAccessKeyName=");
        connectionString.append("{keyrule}");
        connectionString.append(";SharedAccessKey=");
        connectionString.append("{key}");
        connectionString.append(";EntityPath=");
        connectionString.append("{path}");
        return connectionString.toString();
    }
    

    中かっこ内のプレースホルダーを、ハイブリッド接続の作成時に取得した値に置き換えます。

    • namespace - Relay 名前空間。 必ず完全修飾名前空間名を使用してください (たとえば、{namespace}.servicebus.windows.net)。
    • path - ハイブリッド接続の名前。
    • keyrule - 共有アクセス ポリシー キーの名前 (既定では RootManageSharedAccessKey)。
    • nst key - 前に保存した名前空間の主キー。
  4. 次のコードを Sender.java ファイルに追加します。 main 関数は次のようなコードになります。

    public static void main(String[] args) throws IOException {
        String CONNECTION_STRING_ENV_VARIABLE_NAME = createConnectionString();
        if (CONNECTION_STRING_ENV_VARIABLE_NAME == null || CONNECTION_STRING_ENV_VARIABLE_NAME.isEmpty()){
            System.err.println("Connection string is null or empty. Please check your createConnectionString method.");
            return;
        }
        RelayConnectionStringBuilder connectionParams = new RelayConnectionStringBuilder(CONNECTION_STRING_ENV_VARIABLE_NAME);
        TokenProvider tokenProvider = TokenProvider.createSharedAccessSignatureTokenProvider(
                connectionParams.getSharedAccessKeyName(), 
                connectionParams.getSharedAccessKey());
        URL url = buildHttpConnectionURL(connectionParams.getEndpoint().toString(), connectionParams.getEntityPath());
        String tokenString = tokenProvider.getTokenAsync(url.toString(), Duration.ofHours(1)).join().getToken();
        Scanner in = new Scanner(System.in);
        while (true) {
            System.out.println("Press ENTER to terminate this program.");
            String message = in.nextLine();
            int value = System.in.read();
            if (value == '\n' || value == '\r') {
                System.out.println("Terminating the program...");
                break;}
            // Starting a HTTP connection to the listener
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            // Sending an HTTP request to the listener
            // To send a message body, use POST
            conn.setRequestMethod((message == null || message.length() == 0) ? "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();
            // Reading the HTTP response
            String inputLine;
            BufferedReader reader = null;
            StringBuilder responseBuilder = new StringBuilder();
            try {
                InputStream inputStream = conn.getInputStream();
                reader = new BufferedReader(new InputStreamReader(inputStream));
                System.out.println("status code: " + conn.getResponseCode());
                while ((inputLine = reader.readLine()) != null) {
                    responseBuilder.append(inputLine);
                }
                System.out.println("received back " + responseBuilder.toString());
            } catch (IOException e) {
                System.out.println("The listener is offline or could not be reached.");
                break;
            } finally {
                if (reader != null) {
                    reader.close();
                }
            }
        }
        in.close();
    }
    
    static URL buildHttpConnectionURL(String endpoint, String entity) throws MalformedURLException {
        StringBuilder urlBuilder = new StringBuilder(endpoint + entity);
    
        // For HTTP connections, the scheme must be https://
        int schemeIndex = urlBuilder.indexOf("://");
        if (schemeIndex < 0) {
            throw new IllegalArgumentException("Invalid scheme from the given endpoint.");
        }
        urlBuilder.replace(0, schemeIndex, "https");
        return new URL(urlBuilder.toString());
    }
    

    Sender.java ファイルは次のようになります。

    package com.example.sender;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.net.HttpURLConnection;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.time.Duration;
    import java.util.Scanner;
    import com.microsoft.azure.relay.RelayConnectionStringBuilder;
    import com.microsoft.azure.relay.TokenProvider;
    
    public class Sender
    {
        public static String createConnectionString(){
            StringBuilder connectionString = new StringBuilder();
            connectionString.append("Endpoint=sb://");
            connectionString.append("{namespace}");
            connectionString.append(".servicebus.windows.net/;SharedAccessKeyName=");
            connectionString.append("{keyrule}");
            connectionString.append(";SharedAccessKey=");
            connectionString.append("{key}");
            connectionString.append(";EntityPath=");
            connectionString.append("{path}");
            return connectionString.toString();
            }
        public static void main(String[] args) throws IOException {
            String CONNECTION_STRING_ENV_VARIABLE_NAME = createConnectionString();
            if (CONNECTION_STRING_ENV_VARIABLE_NAME == null || CONNECTION_STRING_ENV_VARIABLE_NAME.isEmpty()){
                System.err.println("Connection string is null or empty. Please check your createConnectionString method.");
                return;
            }
            RelayConnectionStringBuilder connectionParams = new RelayConnectionStringBuilder(CONNECTION_STRING_ENV_VARIABLE_NAME);
            TokenProvider tokenProvider = TokenProvider.createSharedAccessSignatureTokenProvider(
                    connectionParams.getSharedAccessKeyName(), 
                    connectionParams.getSharedAccessKey());
            URL url = buildHttpConnectionURL(connectionParams.getEndpoint().toString(), connectionParams.getEntityPath());
            String tokenString = tokenProvider.getTokenAsync(url.toString(), Duration.ofHours(1)).join().getToken();
            Scanner in = new Scanner(System.in);
            while (true) {
                System.out.println("Press ENTER to terminate this program.");
                String message = in.nextLine();
                int value = System.in.read();
                if (value == '\n' || value == '\r') {
                    System.out.println("Terminating the program...");
                    break;}
                // Starting a HTTP connection to the listener
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                // Sending an HTTP request to the listener
                // To send a message body, use POST
                conn.setRequestMethod((message == null || message.length() == 0) ? "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();
                // Reading the HTTP response
                String inputLine;
                BufferedReader reader = null;
                StringBuilder responseBuilder = new StringBuilder();
                try {
                    InputStream inputStream = conn.getInputStream();
                    reader = new BufferedReader(new InputStreamReader(inputStream));
                    System.out.println("status code: " + conn.getResponseCode());
                    while ((inputLine = reader.readLine()) != null) {
                        responseBuilder.append(inputLine);
                    }
                    System.out.println("received back " + responseBuilder.toString());
                } catch (IOException e) {
                    System.out.println("The listener is offline or could not be reached.");
                    break;
                } finally {
                    if (reader != null) {
                        reader.close();
                    }
                }
            }
            in.close();
        }
    
        static URL buildHttpConnectionURL(String endpoint, String entity) throws MalformedURLException {
            StringBuilder urlBuilder = new StringBuilder(endpoint + entity);
    
            // For HTTP connections, the scheme must be https://
            int schemeIndex = urlBuilder.indexOf("://");
            if (schemeIndex < 0) {
                throw new IllegalArgumentException("Invalid scheme from the given endpoint.");
            }
            urlBuilder.replace(0, schemeIndex, "https");
            return new URL(urlBuilder.toString());
        }
    }
    

アプリケーションの実行

  1. Java コマンド プロンプトまたはアプリケーションで「java -cp <jar_dependency_path> com.example.listener.Listener.java」と入力して、サーバー アプリケーションを実行します。
  2. Java コマンド プロンプトまたはアプリケーションで「java -cp <jar_dependency_path> com.example.sender.Sender.java」と入力してクライアント アプリケーションを実行し、何かテキストを入力します。
  3. サーバー アプリケーション コンソールに、クライアント アプリケーションで入力したテキストが出力されることを確認します。

これで、Java を使ってエンド ツー エンドのハイブリッド接続アプリケーションを作成できました。

次のステップ

このクイックスタートでは、HTTP を使ってメッセージの送信と受信を行う Java のクライアントとサーバー アプリケーションを作成しました。 Azure Relay のハイブリッド接続機能は、WebSocket を使用したメッセージの送受信もサポートしています。 Azure Relay のハイブリッド接続で WebSocket を使用する方法については、WebSocket のクイック スタートを参照してください。

このクイックスタートでは、Java を使ってクライアントとサーバーのアプリケーションを作成しました。 .NET Framework を使用してクライアント アプリケーションとサーバー アプリケーションを作成する方法については、.NET WebSocket のクイック スタートまたは .NET HTTP のクイック スタートを参照してください。