ジョブのスケジュールとブロードキャスト (Java)
Azure IoT Hub を使用して、数百万のデバイスを更新するジョブのスケジュールと追跡を行います。 ジョブを使用して、次の操作を行います。
- 必要なプロパティを更新する
- タグを更新する
- ダイレクト メソッドを呼び出す
ジョブはこれらのアクションの 1 つをラップし、デバイスのセットに対して実行状況を追跡します。 ジョブが実行されるデバイスのセットはデバイス ツイン クエリで定義されます。 たとえば、バックエンド アプリは、ジョブを使用して 10,000 台のデバイスでダイレクト メソッドを呼び出し、デバイスを再起動できます。 デバイス ツイン クエリでデバイスのセットを指定し、ジョブが将来実行されるようにスケジュールを設定します。 各デバイスが reboot ダイレクト メソッドを受信し、実行するたびに、ジョブは進行状況を追跡します。
これらの各機能の詳細については、次の記事をご覧ください。
デバイス ツインとプロパティ: デバイス ツインの概要および IoT Hub のデバイス ツインの理解と使用
ダイレクト メソッド: IoT Hub 開発者ガイド - ダイレクト メソッド
Note
この記事で説明されている機能は、Standard レベルの IoT Hub でのみ使用できます。 Basic および Standard または Free レベルの IoT Hub の詳細については、「ソリューションに適した IoT Hub のレベルを選択する」を参照してください。
この記事では、2 つの Java アプリを作成する方法について説明します。
バックエンド アプリから呼び出すことができる lockDoor というダイレクト メソッドを実装するデバイス アプリ simulated-device。
2 つのジョブを作成するバックエンド アプリ schedule-jobs。 1 つのジョブが lockDoor ダイレクト メソッドを呼び出し、別のジョブが必要なプロパティの更新を複数のデバイスに送信します。
注意
デバイスとバックエンド アプリの両方の構築に使用できる SDK ツールに関する詳細については、「Azure IoT Hub SDK」を参照してください。
前提条件
Azure サブスクリプション内の IoT ハブ。 ハブがまだない場合は、「IoT ハブの作成」の手順に従うことができます。
お使いの IoT ハブに登録されているデバイス。 IoT ハブにデバイスがない場合は、「デバイスを登録する」の手順に従います。
Java SE Development Kit 8。 JDK 8 のダウンロードを利用するには、「長期サポート」の「Java 8」を選択します。
ポート 8883 がファイアウォールで開放されていることを確認してください。 この記事のデバイス サンプルでは、ポート 8883 を介して通信する MQTT プロトコルを使用しています。 このポートは、企業や教育用のネットワーク環境によってはブロックされている場合があります。 この問題の詳細と対処方法については、「IoT Hub への接続 (MQTT)」を参照してください。
注意
わかりやすくするために、この記事では再試行ポリシーは実装しません。 運用環境のコードでは、「一時的な障害の処理」の記事で推奨されているように、再試行ポリシー (指数関数的バックオフなど) を実装することをお勧めします。
IoT ハブ接続文字列を取得する
この記事では、デバイス上で直接メソッドを呼び出すようにジョブをスケジュールし、デバイス ツインを更新するようにジョブをスケジュールし、各ジョブの進行状況を監視するバックエンド サービスを作成します。 これらの操作を実行するには、サービスにレジストリ読み取りおよびレジストリ書き込みのアクセス許可が必要です。 既定では、すべての IoT ハブは、これらのアクセス許可を付与する registryReadWrite という名前の共有アクセス ポリシーがある状態で作成されます。
registryReadWrite ポリシーの IoT Hub 接続文字列を取得するには、次の手順を実行します。
Azure portal で、 [リソース グループ] を選択します。 ハブが配置されているリソース グループを選択し、リソースの一覧からハブを選択します。
ハブの左側のウィンドウで、 [共有アクセス ポリシー] を選択します。
ポリシーの一覧から、 [registryReadWrite] ポリシーを選択します。
[プライマリ接続文字列] をコピーし、値を保存します。
IoT Hub の共有アクセス ポリシーとアクセス許可の詳細については、「アクセス制御とアクセス許可」を参照してください。
重要
この記事では、Shared Access Signature を使用してサービスに接続する手順について説明しています。 この認証方法はテストと評価には便利ですが、サービスに対する認証方法としては、Microsoft Entra ID またはマネージド ID を使用する方が安全です。 詳細については、「セキュリティのベスト プラクティス」の「クラウドのセキュリティ」 を参照してください。>
デバイス アプリを作成する
このセクションでは、ジョブを使用する Java コンソール アプリを作成して、次の操作を行います。
複数のデバイスで lockDoor ダイレクト メソッドを呼び出す。
必要なプロパティを複数のデバイスに送信する。
アプリを作成するには:
開発用コンピューターで、iot-java-schedule-jobs という名前の空のフォルダーを作成します。
コマンド プロンプトで次のコマンドを使用して、iot-java-schedule-jobs フォルダー内に schedule-jobs という名前の Maven プロジェクトを作成します。 これは、1 つの長いコマンドであることに注意してください。
mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=schedule-jobs -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
コマンド プロンプトで、schedule-jobs フォルダーに移動します。
テキスト エディターを使用して、schedule-jobs フォルダー内の pom.xml ファイルを開き、次の依存関係を dependencies ノードに追加します。 この依存関係により、アプリで iot-service-client パッケージを使用して IoT Hub と通信できるようになります。
<dependency> <groupId>com.microsoft.azure.sdk.iot</groupId> <artifactId>iot-service-client</artifactId> <version>1.17.1</version> <type>jar</type> </dependency>
注意
Maven 検索を使用して、iot-service-client の最新バージョンを確認できます。
dependencies ノードの後に、次の build ノードを追加します。 この構成では、Java 1.8 を使用してアプリをビルドするように Maven に指示しています。
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.3</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build>
pom.xml ファイルを保存して閉じます。
テキスト エディターを使用して、schedule-jobs\src\main\java\com\mycompany\app\App.java ファイルを開きます。
ファイルに次の import ステートメントを追加します。
import com.microsoft.azure.sdk.iot.service.devicetwin.DeviceTwinDevice; import com.microsoft.azure.sdk.iot.service.devicetwin.Pair; import com.microsoft.azure.sdk.iot.service.devicetwin.Query; import com.microsoft.azure.sdk.iot.service.devicetwin.SqlQuery; import com.microsoft.azure.sdk.iot.service.jobs.JobClient; import com.microsoft.azure.sdk.iot.service.jobs.JobResult; import com.microsoft.azure.sdk.iot.service.jobs.JobStatus; import java.util.Date; import java.time.Instant; import java.util.HashSet; import java.util.Set; import java.util.UUID;
次のクラスレベル変数を App クラスに追加します。
{youriothubconnectionstring}
は、先ほど「IoT ハブ接続文字列を取得する」でコピーしておいた IoT ハブ接続文字列に置き換えてください。public static final String iotHubConnectionString = "{youriothubconnectionstring}"; public static final String deviceId = "myDeviceId"; // How long the job is permitted to run without // completing its work on the set of devices private static final long maxExecutionTimeInSeconds = 30;
デバイス ツインで次のメソッドを App クラスに追加して、必要なプロパティ Building および Floor を更新するようにジョブのスケジュールを設定します。
private static JobResult scheduleJobSetDesiredProperties(JobClient jobClient, String jobId) { DeviceTwinDevice twin = new DeviceTwinDevice(deviceId); Set<Pair> desiredProperties = new HashSet<Pair>(); desiredProperties.add(new Pair("Building", 43)); desiredProperties.add(new Pair("Floor", 3)); twin.setDesiredProperties(desiredProperties); // Optimistic concurrency control twin.setETag("*"); // Schedule the update twin job to run now // against a single device System.out.println("Schedule job " + jobId + " for device " + deviceId); try { JobResult jobResult = jobClient.scheduleUpdateTwin(jobId, "deviceId='" + deviceId + "'", twin, new Date(), maxExecutionTimeInSeconds); return jobResult; } catch (Exception e) { System.out.println("Exception scheduling desired properties job: " + jobId); System.out.println(e.getMessage()); return null; } }
lockDoor メソッドを呼び出すようにジョブのスケジュールを設定するには、次のメソッドを App クラスに追加します。
private static JobResult scheduleJobCallDirectMethod(JobClient jobClient, String jobId) { // Schedule a job now to call the lockDoor direct method // against a single device. Response and connection // timeouts are set to 5 seconds. System.out.println("Schedule job " + jobId + " for device " + deviceId); try { JobResult jobResult = jobClient.scheduleDeviceMethod(jobId, "deviceId='" + deviceId + "'", "lockDoor", 5L, 5L, null, new Date(), maxExecutionTimeInSeconds); return jobResult; } catch (Exception e) { System.out.println("Exception scheduling direct method job: " + jobId); System.out.println(e.getMessage()); return null; } };
ジョブを監視するには、次のメソッドを App クラスに追加します。
private static void monitorJob(JobClient jobClient, String jobId) { try { JobResult jobResult = jobClient.getJob(jobId); if(jobResult == null) { System.out.println("No JobResult for: " + jobId); return; } // Check the job result until it's completed while(jobResult.getJobStatus() != JobStatus.completed) { Thread.sleep(100); jobResult = jobClient.getJob(jobId); System.out.println("Status " + jobResult.getJobStatus() + " for job " + jobId); } System.out.println("Final status " + jobResult.getJobStatus() + " for job " + jobId); } catch (Exception e) { System.out.println("Exception monitoring job: " + jobId); System.out.println(e.getMessage()); return; } }
実行したジョブの詳細に関するクエリを実行するには、次のメソッドを追加します。
private static void queryDeviceJobs(JobClient jobClient, String start) throws Exception { System.out.println("\nQuery device jobs since " + start); // Create a jobs query using the time the jobs started Query deviceJobQuery = jobClient .queryDeviceJob(SqlQuery.createSqlQuery("*", SqlQuery.FromType.JOBS, "devices.jobs.startTimeUtc > '" + start + "'", null).getQuery()); // Iterate over the list of jobs and print the details while (jobClient.hasNextJob(deviceJobQuery)) { System.out.println(jobClient.getNextJob(deviceJobQuery)); } }
main メソッドのシグネチャを、次の
throws
句を含むように更新します。public static void main( String[] args ) throws Exception
2 つのジョブを順番に実行して監視するには、main メソッドのコードを、次のコードに置き換えます。
// Record the start time String start = Instant.now().toString(); // Create JobClient JobClient jobClient = JobClient.createFromConnectionString(iotHubConnectionString); System.out.println("JobClient created with success"); // Schedule twin job desired properties // Maximum concurrent jobs is 1 for Free and S1 tiers String desiredPropertiesJobId = "DPCMD" + UUID.randomUUID(); scheduleJobSetDesiredProperties(jobClient, desiredPropertiesJobId); monitorJob(jobClient, desiredPropertiesJobId); // Schedule twin job direct method String directMethodJobId = "DMCMD" + UUID.randomUUID(); scheduleJobCallDirectMethod(jobClient, directMethodJobId); monitorJob(jobClient, directMethodJobId); // Run a query to show the job detail queryDeviceJobs(jobClient, start); System.out.println("Shutting down schedule-jobs app");
schedule-jobs\src\main\java\com\mycompany\app\App.java ファイルを保存して閉じます。
schedule-jobs アプリをビルドし、エラーを修正します。 コマンド プロンプトで schedule-jobs フォルダーに移動し、次のコマンドを実行します。
mvn clean package -DskipTests
デバイス アプリの作成
このセクションでは、IoT Hub から送信された必要なプロパティを処理する Java コンソール アプリを作成し、ダイレクト メソッド呼び出しを実装します。
重要
この記事では、Shared Access Signature (対称キー認証とも呼ばれます) を使用してデバイスを接続する手順について説明します。 この認証方法はテストと評価には便利ですが、X.509 証明書を使用してデバイスを認証する方が安全なアプローチです。 詳細については、「セキュリティのベスト プラクティス」>「接続のセキュリティ」をご覧ください。
コマンド プロンプトで次のコマンドを実行して、iot-java-schedule-jobs フォルダー内に simulated-device という名前の Maven プロジェクトを作成します。 これは、1 つの長いコマンドであることに注意してください。
mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=simulated-device -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
コマンド プロンプトで、simulated-device フォルダーに移動します。
テキスト エディターを使用して、simulated-device フォルダー内の pom.xml ファイルを開き、次の依存関係を dependencies ノードに追加します。 この依存関係により、アプリで iot-device-client パッケージを使用して IoT Hub と通信できるようになります。
<dependency> <groupId>com.microsoft.azure.sdk.iot</groupId> <artifactId>iot-device-client</artifactId> <version>1.17.5</version> </dependency>
注意
Maven 検索を使用して、iot-device-client の最新バージョンを確認できます。
dependencies ノードに、次の依存関係を追加します。 この依存関係によって、Apache SLF4J ログ記録ファサード用の NOP が構成され、ログ記録を実装するためにデバイス クライアント SDK によって使用されます。 この構成は省略可能ですが、省略した場合、アプリの実行時にコンソールに警告が表示される可能性があります。 デバイス クライアント SDK でのログ記録の詳細については、Samples for the Azure IoT device SDK for Java readme ファイルに含まれているログ記録を参照してください。
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-nop</artifactId> <version>1.7.28</version> </dependency>
dependencies ノードの後に、次の build ノードを追加します。 この構成では、Java 1.8 を使用してアプリをビルドするように Maven に指示しています。
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.3</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build>
pom.xml ファイルを保存して閉じます。
テキスト エディターを使用して、simulated-device\src\main\java\com\mycompany\app\App.java ファイルを開きます。
ファイルに次の import ステートメントを追加します。
import com.microsoft.azure.sdk.iot.device.*; import com.microsoft.azure.sdk.iot.device.DeviceTwin.*; import java.io.IOException; import java.net.URISyntaxException; import java.util.Scanner;
次のクラスレベル変数を App クラスに追加します。
{yourdeviceconnectionstring}
を、IoT Hub にデバイスを登録したときに表示されたデバイス接続文字列に置き換えます。private static String connString = "{yourdeviceconnectionstring}"; private static IotHubClientProtocol protocol = IotHubClientProtocol.MQTT; private static final int METHOD_SUCCESS = 200; private static final int METHOD_NOT_DEFINED = 404;
このサンプル アプリでは、DeviceClient オブジェクトをインスタンス化するときに protocol 変数が使用されます。
デバイス ツイン通知をコンソールに出力するには、次の入れ子になったクラスを App クラスに追加します。
// Handler for device twin operation notifications from IoT Hub protected static class DeviceTwinStatusCallBack implements IotHubEventCallback { public void execute(IotHubStatusCode status, Object context) { System.out.println("IoT Hub responded to device twin operation with status " + status.name()); } }
ダイレクト メソッド通知をコンソールに出力するには、次の入れ子になったクラスを App クラスに追加します。
// Handler for direct method notifications from IoT Hub protected static class DirectMethodStatusCallback implements IotHubEventCallback { public void execute(IotHubStatusCode status, Object context) { System.out.println("IoT Hub responded to direct method operation with status " + status.name()); } }
IoT Hub からのダイレクト メソッド呼び出しを処理するには、次の入れ子になったクラスを App クラスに追加します。
// Handler for direct method calls from IoT Hub protected static class DirectMethodCallback implements DeviceMethodCallback { @Override public DeviceMethodData call(String methodName, Object methodData, Object context) { DeviceMethodData deviceMethodData; switch (methodName) { case "lockDoor": { System.out.println("Executing direct method: " + methodName); deviceMethodData = new DeviceMethodData(METHOD_SUCCESS, "Executed direct method " + methodName); break; } default: { deviceMethodData = new DeviceMethodData(METHOD_NOT_DEFINED, "Not defined direct method " + methodName); } } // Notify IoT Hub of result return deviceMethodData; } }
main メソッドのシグネチャを、次の
throws
句を含むように更新します。public static void main( String[] args ) throws IOException, URISyntaxException
main メソッドのコードを次のコードに置き換えます。
- IoT Hub と通信するデバイス クライアントを作成します。
- デバイス ツインのプロパティを格納する Device オブジェクトを作成します。
// Create a device client DeviceClient client = new DeviceClient(connString, protocol); // An object to manage device twin desired and reported properties Device dataCollector = new Device() { @Override public void PropertyCall(String propertyKey, Object propertyValue, Object context) { System.out.println("Received desired property change: " + propertyKey + " " + propertyValue); } };
デバイス クライアント サービスを開始するには、次のコードを main メソッドに追加します。
try { // Open the DeviceClient // Start the device twin services // Subscribe to direct method calls client.open(); client.startDeviceTwin(new DeviceTwinStatusCallBack(), null, dataCollector, null); client.subscribeToDeviceMethod(new DirectMethodCallback(), null, new DirectMethodStatusCallback(), null); } catch (Exception e) { System.out.println("Exception, shutting down \n" + " Cause: " + e.getCause() + " \n" + e.getMessage()); dataCollector.clean(); client.closeNow(); System.out.println("Shutting down..."); }
ユーザーが Enter キーを押してからシャット ダウンするには、次のコードを main メソッドの末尾に追加します。
// Close the app System.out.println("Press any key to exit..."); Scanner scanner = new Scanner(System.in); scanner.nextLine(); dataCollector.clean(); client.closeNow(); scanner.close();
simulated-device\src\main\java\com\mycompany\app\App.java ファイルを保存して閉じます。
simulated-device アプリをビルドし、エラーを修正します。 コマンド プロンプトで simulated-device フォルダーに移動し、次のコマンドを実行します。
mvn clean package -DskipTests
アプリの実行
これで、コンソール アプリを実行する準備が整いました。
simulated-device フォルダーのコマンド プロンプトで、次のコマンドを実行して、必要なプロパティの変更とダイレクト メソッド呼び出しをリッスンするデバイス アプリを開始します。
mvn exec:java -Dexec.mainClass="com.mycompany.app.App"
schedule-jobs
フォルダーのコマンド プロンプトで、次のコマンドによって schedule-jobs サービス アプリを実行し、2 つのジョブを実行します。 最初のジョブで必要なプロパティの値が設定され、2 つ目のジョブでダイレクト メソッドが呼び出されます。mvn exec:java -Dexec.mainClass="com.mycompany.app.App"
デバイス アプリは必要なプロパティの変更とダイレクト メソッド呼び出しを処理します。
次の手順
この記事では、ダイレクト メソッドを実行してデバイス ツインのプロパティを更新するようにジョブをスケジュールしました。
IoT Hub とデバイス管理パターンの調査を続けるには、「Raspberry Pi 3 B+ 参照イメージを使用した Device Update for Azure IoT Hub のチュートリアル」でイメージを更新してください。