Java用マイクロサービスSDK

概要

このセクションでは、Java用のマークロサービスSDKを使用して、Things Cloudの上にマイクロサービスを開発およびデプロイする方法を説明します。また、Javaを使用してマイクロサービスを開発するための基礎を習得できるHello worldチュートリアルも紹介します。最初のマイクロサービスをThings Cloudに正常にデプロイした後、マイクロサービスの開発のセクションへ進めば、SDKの他の機能についてさらに学習できます。

備考
任意のIDEおよびビルドツールでThings Cloud用のマイクロサービスを開発できますが、このセクションではMavenとEclipseのトラブルシュートに焦点を当てています。

これらは、SDKの基礎となる技術を紹介する便利なリファレンスです。

重要
古いバージョンのJRE とJDKでは最新のセキュリティパッチが更新されておらず、商用環境での使用を推奨しないため、開発環境にはJava D開発キットのバージョン11以上がインストールされている必要があります。古いバージョンのJREおよびJDKは最新のセキュリティパッチで更新されておらず、運用での使用は推奨されません。

問題が発生したりサポートが必要な場合は、Cumulocity Tech Communityを参照してください。ここには、役に立つ質問と回答がたくさん記載されています。

Hello world チュートリアル

ここでは、Java用のMicroservice SDKを使用して、Things Cloudにデプロイできる最初のマイクロサービスを作成する方法を学びます。

マイクロサービスへのリクエストは、基本認証またはOAuthを使用して認証できます。詳細については、認証と認可を参照してください。

前提条件

まず、Things Cloudの認証情報と専用のテナントが必要です。まだアカウントをお持ちでない場合は、Things Cloudのアカウントを作成してください。この段階で、テナント専用のURLアドレスが提供されます。

推奨されるJavaバージョンがインストールされており、Maven 3以上を確認してください。これは、Mavenウェブサイトからダウンロードできます。

$ mvn -v
Apache Maven 3.8.5
Maven home: /Library/Maven/apache-maven-3.8.5
Java version: 17.0.6, vendor: Oracle Corporation
Java home (runtime): /Library/Java/JavaVirtualMachines/jdk-17.0.6.jdk/Contents/Home
OS name: "mac os x", version: "10.14.6", arch: "x86_64", family: "mac"

また、Dockerのインストールが必要です。まだ持っていない場合は、Dockerウェブサイトにアクセスして、ダウンロードしてインストールしてください。

Things Cloudのマイクロサービスは、Linux/Amd64プラットフォーム用のDockerコンテナです。他のアーキテクチャは現在サポートされていません。DockerエンジンはAPIバージョン1.38以上を提供する必要があります。これは、Dockerバージョン18.06以降に該当します。Dockerのインストールを確認するには、以下のコマンドを使用します。

$ docker version
Client: Docker Engine - Community
 Version:           20.10.14
 API version:       1.41
 Go version:        go1.16.15
 Git commit:        a224086
 Built:             Thu Mar 24 01:47:57 2022
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.14
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.16.15
  Git commit:       87a90dc
  Built:            Thu Mar 24 01:45:46 2022
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.5.11
  GitCommit:        3df54a852345ae127d1fa3092b95168e4a88e2f8
 runc:
  Version:          1.0.3
  GitCommit:        v1.0.3-0-gf46b6ba
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

「Hello world」マイクロサービスの開発

この例のソースコードは、当社のGitHubリポジトリからダウンロードして任意のIDEを使用して構築および実行するか、或いはコードと必要な作業/構成を段階的に理解できるように次の手順に従ってください。

重要
このマイクロサービスの例は、macOS、Ubuntu、Windows 10の環境でJava 17、Maven 3.8.5、Docker 20.10.14、最新バージョンのIntelliJ IDEAを使用してテストされています。他のツールやJavaバージョンでは異なる構成が必要な場合があります。

Mavenプロジェクトの作成

Maven Archetype Pluginを使用して、既存のMavenテンプレートからJavaプロジェクトを作成します。groupIdとしてc8y.example、artifactIdとしてhello-microservice-javaを使用し、マイクロサービスマニフェストSemVer形式に従ってバージョンを設定してください。

$ mvn archetype:generate -DgroupId=c8y.example -DartifactId=hello-microservice-java -Dversion=1.0.0-SNAPSHOT -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

これにより、プロジェクトのスケルトン構造を持つhello-microservice-javaという名のフォルダが現在のディレクトリに作成されます。

プロパティを指定

pom.xml ファイルはhello-microservice-javaフォルダー内にあります。バージョン17を使用して、Javaコンパイラの-source-target を設定 するために <properties>要素を追加して、このファイルを編集します。この例では、Spring Bootを使用して、Spring Frameworkを使用したアプリケーションを素早く構築し、作成します。したがって、使用するバージョンを次のように、<properties>要素に指定します。

<properties>
    <java.version>17</java.version>
    <maven.compiler.source>${java.version}</maven.compiler.source>
    <maven.compiler.target>${java.version}</maven.compiler.target>
    <spring-boot-dependencies.version>2.5.14</spring-boot-dependencies.version>
</properties>

マイクロサービスライブラリの追加

使用するThings Cloudのマイクロサービスライブラリーのバージョンを指定する必要があります。これはプラットフォーム上で確認が可能です。右上のテナントユーザーをクリックし、ポップアップメニューでバックエンドバージョンを確認してください。

または、以下のGETリクエストを/tenant/system/options/system/versionに送信すると、バックエンドバージョンを取得することもできます。

レスポンスは次のようになります。

{
    "category": "system",
    "value": "1016.0.117",
    "key": "version"
}

Things Cloud OpenAPI仕様のテナントも参照してください。

上記で指定した <properties>要素に、テナントのバックエンドバージョンを持つ子要素 <c8y.version>を追加します。また、マイクロサービスアプリケーションに名前を付けるために <microservice.name> 子要素も追加します。

    <c8y.version>1016.0.117</c8y.version>
    <microservice.name>hello-microservice-java</microservice.name>
重要
マイクロサービスアプリケーションに名前を付ける際は、小文字、数字、およびダッシュのみを使用してください。名前の最大長は23文字です。

リポジトリと依存関係の追加

pom.xml ファイルには、クライアントライブラリを格納するThings Cloud Mavenリポジトリを指す <repository><pluginRepository>要素が必要です。

<repositories>
    <repository>
        <id>cumulocity</id>
        <layout>default</layout>
        <url>https://download.cumulocity.com/maven/repository</url>
    </repository>
</repositories>
<pluginRepositories>
    <pluginRepository>
        <id>public</id>
        <url>https://download.cumulocity.com/maven/repository</url>
    </pluginRepository>
</pluginRepositories>

また、JavaマイクロサービスSDKライブラリの依存要素を<dependencies>ノード内に追加します。

<dependencies>
    ...
    <dependency>
        <groupId>com.nsn.cumulocity.clients-java</groupId>
        <artifactId>microservice-autoconfigure</artifactId>
        <version>${c8y.version}</version>
    </dependency>
</dependencies>

<dependencyManagement>要素を追加して、マイクロサービスアプリケーションに必要なアーティファクトを自動的に管理します。

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.nsn.cumulocity.clients-java</groupId>
            <artifactId>microservice-dependencies</artifactId>
            <version>${c8y.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

ビルドプラグインの構成

マイクロサービスアプリケーションは、必要なすべての依存関係を含むZIPファイルにDockerイメージとして梱包する必要があります。これを実現するには、次のように pom.xml ファイルビルドプラグインに含めます。

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>${spring-boot-dependencies.version}</version>
            <configuration>
                <mainClass>c8y.example.App</mainClass>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <groupId>com.nsn.cumulocity.clients-java</groupId>
            <artifactId>microservice-package-maven-plugin</artifactId>
            <version>${c8y.version}</version>
            <executions>
                <execution>
                    <id>package</id>
                    <phase>package</phase>
                    <goals>
                        <goal>package</goal>
                    </goals>
                    <configuration>
                        <name>${microservice.name}</name>
                        <image>${microservice.name}</image>
                        <encoding>UTF-8</encoding>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

生成されるZIPファイルの名前は、image要素で <image>${microservice.name}</image>と指定します。 先に定義したプロパティmicroservice.nameから名前を取り、この場合はhello-microservice-javaとなります。

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

/src/main/java/c8y/exampleフォルダー内の App.java ファイルを以下の内容で編集します。

package c8y.example;

import com.cumulocity.microservice.autoconfigure.MicroserviceApplication;
import org.springframework.boot.SpringApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@MicroserviceApplication
@RestController
public class App {
    public static void main (String[] args) {
        SpringApplication.run(App.class, args);
    }

    @RequestMapping("hello")
    public String greeting (@RequestParam(value = "name", defaultValue = "World") String you) {
        return "Hello " + you + "!";
    }
}

このコードは4つのアノテーションを使用しています。3つはSpring Frameworkの一部であり、1つはThings CloudマイクロサービスSDKのものです。@RestControllerアノテーションは、このクラスをすべてのメソッドがビューの代わりにドメインオブジェクトを返すコントローラーとしてマークします。@RequestMappingアノテーションは、/service/<microservice-name>/helloエンドポイントへのHTTPリクエストがgreeting()メソッドにマップされることを保証します。 @RequestParamは、クエリ文字列パラメーターnameの値をgreeting()メソッドのyouパラメーターにバインドします。Spring Frameworkを使用してRESTful Web サービスを構築する詳細については、Spring Guidesを参照してください。

@MicroserviceApplicationアノテーションを使用することで、Things Cloudのマイクロサービスに必要な動作を簡単に追加できます。これには以下が含まれます。

  • セキュリティ
  • サブスクリプション
  • /service/<microservice-name>/healthでのヘルスチェックエンドポイント
  • コンテキスト
  • 設定
  • 内部プラットフォームAPI
  • Spring Bootアプリケーション

マイクロサービスの構成設定

_src/main/resources_ディレクトリを作成し、マイクロサービスアプリケーションの名前とサーバーポートを指定する application.properties ファイルを作成します。

application.name=my-first-microservice
server.port=80

次の内容を含んだ _cumulocity.json_ファイルを_src/main/configuration_ディレクトリに追加します。これはマニフェストファイルであり、Things Cloudプラットフォームにマイクロサービスをデプロイするために必要なファイルです。

{
  "apiVersion": "2",
  "version": "@project.version@",
  "provider": {
    "name": "Things Cloud"
  },
  "isolation": "MULTI_TENANT",
  "replicas": 2,
  "livenessProbe": {
    "httpGet": {
      "path": "/health"
    },
    "initialDelaySeconds": 60
  },
  "readinessProbe": {
    "httpGet": {
      "path": "/health"
    },
    "initialDelaySeconds": 60
  },
  "requiredRoles": [ ]
}

マイクロサービスアプリケーションの構築

ターミナルで、pom.xml があるフォルダーに移動し、以下のMavenコマンドを実行します。

$ mvn clean install

ビルドが成功すると、target ディレクトリ内に ZIP ファイルがビルドされます。

$ ls target | grep zip
hello-microservice-java-1.0.0-SNAPSHOT.zip

“Hello world"マイクロサービスの展開

Things Cloudプラットフォームにマイクロサービスをデプロイするには、以下が必要です。

  • Things Cloudにアクセスするための有効なテナント、ユーザー、およびパスワード。
  • 前のステップでMavenでビルドしたZIPファイル。
重要
マイクロサービスのホスト機能が、テナントで有効にする必要があります。そうでない場合、リクエストは「security/Forbidden、アクセスが拒否されました」というエラーメッセージを返します。この機能はデフォルトでテナントに割り当てられていないため、トライアルアカウントには含まれていません。有効化に関してのサポートが必要な場合は、製品サポートにお問い合わせください。これは有料機能であることにご注意ください。。

管理アプリケーションで、エコシステム > マイクロサービスへ移動し、マイクロサービスの追加をクリックします。

マイクロサービスアプリケーションのZIPファイルをアップロードし、サブスクライブをクリックして、マイクロサービスをテナントにサブスクライブします。

ZIPファイルが正常にアップロードされると、新しいマイクロサービスアプリケーションが作成され、表示されます。

展開されたマイクロサービスの検証

テナントの資格情報を使用すれば、次のURLを使用して任意のWebブラウザーでマイクロサービスを検証できます。

https://<yourTenantDomain>/service/hello-microservice-java/health

サードパーティのアプリケーションやコマンドを使用して、マイクロサービスエンドポイントへのGETリクエストを行うこともできます。そのためには、以下が必要です。

  • Things Cloudにアクセスするための有効なテナント、ユーザー、およびパスワード。
  • 基本Authorizationヘッダー "Authorization: Basic <Base64(<tenantID>/<username>:<password>)>"

たとえば、テナントID、ユーザー名、パスワードがそれぞれt0071234testusersecret123の場合、次のコマンドでBase64文字列を取得できます。

$ echo -n t0071234/testuser:secret123 | base64
dDAwNzEyMzQvdGVzdHVzZXI6c2VjcmV0MTIz

Authorizationヘッダーは次のようになります:Authorization: Basic dDAwNzEyMzQvdGVzdHVzZXI6c2VjcmV0MTIz。cURLコマンドを使用して次のようにマイクロサービスの検証を行うことができます。

$ curl -H "Authorization: <AUTHORIZATION>" https://<yourTenantDomain>/service/hello-microservice-java/hello?name=Skywalker

ほとんどのツールは、Things CloudのAuthorizationヘッダーをデフォルトでサポートしています。単純に、<tenantId>/<username>をユーザー名として、<password>をパスワードとして使用してください。例えば、最新のバージョンでは、以下のようにcURLコマンドを使用することもでき、ヘッダーは自動的に生成されます。

$ curl --user "<TENANTID>/<USERNAME>:<PASSWORD>" https://<yourTenantDomain>/service/hello-microservice-java/hello?name=Skywalker

マイクロサービスをローカルで実行

Dockerコンテナをローカルで実行して、マイクロサービスからThings CloudへのRESTコールを検証することができます。

Things Cloud APIをローカルで使用するマイクロサービスを実行するには、次のものが必要です。

  • Things Cloudにアクセスするための有効なテナント、ユーザー、およびパスワード。
  • Authorizationヘッダーを “Basic <Base64(<tenantID>/<username>:<password>)>“として指定します。

アプリケーションの作成

アプリケーションが存在しない場合は、Things Cloudプラットフォームで新規アプリケーションをPOSTリクエストを使用して作成します。

POST <URL>/application/applications

HEADERS:
  "Authorization": "<AUTHORIZATION>"
  "Content-Type": "application/vnd.com.nsn.cumulocity.application+json"
  "Accept": "application/vnd.com.nsn.cumulocity.application+json"

BODY:
{
  "name": "<APPLICATION_NAME>",
  "type": "MICROSERVICE",
  "key": "<APPLICATION_NAME>-key"
}

上記では、<URL>の値をThings CloudテナントのURLに置き換える必要があり、<AUTHORIZATION>はBase64でエンコードされた文字列で、<APPLICATION_NAME>は独自のマイクロサービスアプリケーションの名前とkeyの名前を使用します。

重要
マイクロサービスアプリケーションに名前を付けるときは、小文字、数字、ダッシュのみを使用してください。名前は最長で23文字です。

cURLコマンドを使用して、POSTリクエストでアプリケーションを作成できます。

$ curl -X POST -s \
  -d '{"name":"local-microservice-java","type":"MICROSERVICE","key":"my-hello-world-ms-key"}' \
  -H "Authorization: <AUTHORIZATION>" \
  -H "Content-Type: application/vnd.com.nsn.cumulocity.application+json" \
  -H "Accept: application/vnd.com.nsn.cumulocity.application+json" \
  "<URL>/application/applications"

無効な名前などエラーが発生した場合、コンソールに詳細が表示されます。アプリケーションが正常に作成されると、次のようなJSON形式のレスポンスが得られます。

{
    "availability": "PRIVATE",
    "contextPath": "local-microservice-java",
    "id": "<APPLICATION_ID>",
    "key": "my-hello-world-ms-key",
    "manifest": {
        "noAppSwitcher": true,
        "settingsCategory": null
    },
    "name": "local-microservice-java",
    "owner": {
        "self": "...",
        "tenant": {
            "id": "<TENANT_ID>"
        }
    },
    "requiredRoles": [],
    "roles": [],
    "self": "<URL>/application/applications/<APPLICATION_ID>",
    "type": "MICROSERVICE"
}

管理アプリケーションで、エコシステム > マイクロサービスに移動します。ここに、新しく作成されたマイクロサービスが表示されます。

マイクロサービスのブートストラップユーザーを取得

ローカルでマイクロサービスを実行するには、ブートストラップユーザーの資格情報が必要です。GETリクエストを使用してブートストラップユーザーの詳細を取得します。

GET <URL>/application/applications/<APPLICATION_ID>/bootstrapUser

HEADERS:
  "Authorization": <AUTHORIZATION>
  "Content-Type": application/vnd.com.nsn.cumulocity.user+json
備考
cURLコマンドの他に、Postmanなどのグラフィカルインターフェイスを使用することもできます。

レスポンスは次のようになります。

{
    "password": "<BOOTSTRAP_USER_PASSWORD>",
    "name": "<BOOTSTRAP_USER_NAME>",
    "tenant": "<BOOTSTRAP_USER_TENANT>"
}

Dockerコンテナを実行

Dockerイメージは、Mavenビルドの際にローカルDockerリポジトリを使用してビルドされました。デフォルトでは、開発中にリポジトリをクリーンに保つためにイメージは削除されます。これを変更するには、Mavenコマンドまたはpom.xmlmicroservice.package.deleteImage=falseプロパティを追加します。

$ mvn clean install -Dmicroservice.package.deleteImage=false

以下のコマンドで利用可能なすべてのDockerイメージのリストを表示できます。

$ docker images

出力は次のようになります。

REPOSITORY                TAG                 IMAGE ID            CREATED             SIZE
hello-microservice-java   1.0.0-SNAPSHOT      3e5e7aeea7bc        52 minutes ago      143MB

一覧からIMAGE IDとTAGを取得してください。コンテナを識別する確実な手段ではありませんが、実行するイメージのバージョン(TAG)を指定できます。マイクロサービスのDockerコンテナを実行します。

$ docker run -p 8082:80 -e C8Y_BOOTSTRAP_TENANT=<BOOTSTRAP_USER_TENANT> \
  -e C8Y_BOOTSTRAP_USER=<BOOTSTRAP_USER_NAME> \
  -e C8Y_BOOTSTRAP_PASSWORD=<BOOTSTRAP_USER_PASSWORD> \
  -e C8Y_MICROSERVICE_ISOLATION=MULTI_TENANT \
  -i -t -e C8Y_BASEURL=<URL> <IMAGE_ID>

-p 8082:80は、ポート80をホストシステム上のポート(例:8082)に公開します。

Dockerイメージが正常に実行されると、コンソールに次のような出力が表示されます。

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.5.14)

2022-10-21 15:53:07.510  INFO 7 --- [main] c8y.example.App                          : Starting App on dff01acae6d8 with PID 7 (/data/hello-microservice-java.jar started by root in /)
...
2022-10-21 15:53:17.583  INFO 7 --- [main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 80 (http)
2022-10-21 15:53:17.598  INFO 7 --- [main] c8y.example.App                          : Started App in 11.32 seconds (JVM running for 12.192)

マイクロサービスの登録

管理アプリケーションで、エコシステム > マイクロサービスに移動します。マイクロサービスアプリケーションを見つけて、それをクリックして詳細を開きます。右上にある登録をクリックします。

この時点で、お気に入りのブラウザを開いて、http://localhost:8082/helloでマイクロサービスを検証できます。ブートストラップユーザーの資格情報を使用して、/ とあなたのパスワードを入力します。

次のようなnameパラメータを使用することもできます:http://localhost:8082/hello?name=Neo

マイクロサービスの改善

上記のステップを終えたので、次はマイクロサービスの開発他に何が実装できるか確認してみましょう。さらに、このガイドのJavaの例も確認して、マイクロサービスSDKやREST APIの機能をより多く活用して、サードパーティサービスを利用する方法を学びましょう。

IP-tracker マイクロサービス

重要
ここでは基本的な設定手順が説明されていないため、IP-trackerマイクロサービスチュートリアルへ進む前に、Java用のHello worldチュートリアルを確認し、そこに書かれている設定手順に従ってください。

IP-trackerマイクロサービスの開発

このマイクロサービスアプリケーションは、警告アラームメッセージを作成し(デモ目的)、以下のエンドポイントを公開します。

  • マイクロサービスが稼働していることを確認する。
  • プラットフォームにパラメータを渡し、フォーマットされた文字列を返す。
  • 一部の環境変数とマイクロサービスの設定を取得する。
  • ユーザーの近似位置を追跡し、プラットフォームに保存する。
  • 追跡されたIPと位置を取得する。

また、Things Cloud UIを使用して、地図上に追跡された位置を表示します。

プロジェクトオブジェクトモデルの更新

Hello worldチュートリアルに記載の基本コードを持っていることを前提とし、マイクロサービスのartifactIdmicroservice.nameiptracker-microserviceへ変更し、pom.xmlファイルを編集します。 また、子要素<java.version><properties>要素に追加して、使用するJavaバージョンを指定します。 pom.xmlファイルには、次のようなコードが含まれている必要があります。

<name>iptracker-microservice</name>
<artifactId>iptracker-microservice</artifactId>
<properties>
    <java.version>17</java.version>
    <maven.compiler.source>${java.version}</maven.compiler.source>
    <maven.compiler.target>${java.version}</maven.compiler.target>
    <spring-boot-dependencies.version>2.5.14</spring-boot-dependencies.version>
    <c8y.version>1016.0.117</c8y.version>
    <microservice.name>iptracker-microservice</microservice.name>
</properties>
備考
この例は、Java 17およびSpring Boot 2を使用して実装されました。インストールするか、この例をすでに持っているバージョン(例:JDK 11)に調整してご利用ください。Java 13以降、一部のAPIメソッドが削除または非推奨になったため、ビルド時に警告メッセージが表示される場合がありますが、マイクロサービスアプリケーションには影響しません。

最後に、次の依存関係を追加します。

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <scope>compile</scope>
</dependency>

アプリケーションマニフェストの更新

cumulocity.json ファイルに対し、次のことを行ってください

  1. イベントとアラームを作成できるように必要なロールを追加します。
  2. readinessプローブとlivenessプローブを追加します。
  3. 次の二つのマイクロサービス設定用キー "ipstack.key""tracker.id" を追加します。
  4. 分離レベルを "PER_TENANT" に設定します。これは、各テナントに対して別々のインスタンスが存在することを意味します。詳細については、マイクロサービスマニフェストの設定セクションを参照してください。

マニフェストファイルは次のようになります。

{
    "apiVersion": "2",
    "version": "@project.version@",
    "provider": {
        "name": "Things Cloud"
    },
    "isolation": "PER_TENANT",
    "settings": [
        {
            "key": "ipstack.key",
            "defaultValue": "<your-ipstack-key>"
        },
        {
            "key": "tracker.id",
            "defaultValue": "<your-tracker-id>"
        }
    ],
    "livenessProbe": {
        "httpGet": {
            "path": "/health"
        },
        "initialDelaySeconds": 60,
        "periodSeconds": 10
    },
    "readinessProbe": {
        "httpGet": {
            "path": "/health",
            "port": 80
        },
        "initialDelaySeconds": 20,
        "periodSeconds": 10
    },
    "requiredRoles": [
        "ROLE_EVENT_READ",
        "ROLE_EVENT_ADMIN",
        "ROLE_ALARM_READ",
        "ROLE_ALARM_ADMIN"
    ],
    "roles": []
}

マネージドオブジェクトの作成

アラームはソースに関連付ける必要があり、IDが必要となります。 したがって、ソースとなるマネージドオブジェクトを作成する必要があり、マイクロサービスアプリケーションでそのIDを使用します。 特定のエンドポイントでマイクロサービスにアクセスすると、このマネージドオブジェクトが場所を追跡します。

まず、任意の無料サービスを利用して現在地(緯度、経度)を取得します。

次のようにPOSTリクエストを使用して、「Microservice tracker」という名前のデバイスとしてマネージドオブジェクトを作成します。

POST <URL>/inventory/managedObjects

HEADERS:
  Content-Type: application/vnd.com.nsn.cumulocity.managedobject+json; charset=UTF-8; ver=0.9
  Accept: application/vnd.com.nsn.cumulocity.managedobject+json; charset=UTF-8; ver=0.9
  Authorization: <AUTHORIZATION>

BODY:
  {
    "c8y_IsDevice": {},
    "c8y_Position": {
      "lat": <LATITUDE>,
      "lng": <LONGITUDE>
    },
    "name": "Microservice tracker"
  }

レスポンスで管理オブジェクトのIDを取得します。このIDを cumulocity.json ファイルの "tracker.id" キーに割り当てます。

Things Cloudプラットフォームで、デバイス管理アプリケーションのデバイス > すべてのデバイスに移動し、デバイスが作成され、位置が地図に表示されていることを確認します。

マイクロサービス追跡

クライアントの位置の取得

マイクロサービスは、クライアントのIPに基づいておおよその位置を取得することができます。これを実現するには、無料サービスipstackを使用し、無料のAPIキーを取得する必要があります。取得したら、そのキーを cumulocity.json ファイルの "ipstack.key" キーに割り当ててください。

IPstack APIにキーを使用してGETリクエストを送信すると、位置オブジェクトが返されます。そのため、以下の内容であなたの App.java と同じディレクトリに次のコンテンツを含む新規ファイル Location.java を作成する必要があります。

package c8y.example;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@JsonIgnoreProperties(ignoreUnknown = true)
public class Location {

    private String city;
    private String country_code;
    private String latitude;
    private String longitude;

    public String getLongitude() {
        return longitude;
    }

    public void setLongitude(String longitude) {
        this.longitude = longitude;
    }

    public String getLatitude() {
        return latitude;
    }

    public void setLatitude(String latitude) {
        this.latitude = latitude;
    }

    public String getCountry_code() {
        return country_code;
    }

    public void setCountry_code(String country_code) {
        this.country_code = country_code;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }
}

アプリケーションの更新

App.java を変更し、次のこと行ってください。

  1. マイクロサービスをSpringアプリケーションとして実行します。
  2. post-construct init メソッドを追加して、環境変数とマイクロサービス設定のサブセットを取得します。
  3. マイクロサービスのサブスクリプションにイベントリスナーを追加します。各テナントがマイクロサービスにサブスクライブするたびにアラームが作成されます。
  4. クライアントのIPに基づいてLocationUpdateイベントを作成するメソッドを定義します。
  5. アプリケーションのエンドポイントを追加します。

コードは以下のようになります。

package c8y.example;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;

import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.context.event.EventListener;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import com.cumulocity.microservice.autoconfigure.MicroserviceApplication;
import com.cumulocity.microservice.context.ContextService;
import com.cumulocity.microservice.context.credentials.MicroserviceCredentials;
import com.cumulocity.microservice.settings.service.MicroserviceSettingsService;
import com.cumulocity.microservice.subscription.model.MicroserviceSubscriptionAddedEvent;
import com.cumulocity.model.idtype.GId;
import com.cumulocity.rest.representation.alarm.AlarmRepresentation;
import com.cumulocity.rest.representation.event.EventRepresentation;
import com.cumulocity.rest.representation.inventory.ManagedObjectRepresentation;
import com.cumulocity.sdk.client.Platform;
import com.cumulocity.sdk.client.event.EventFilter;

import net.minidev.json.JSONObject;

@MicroserviceApplication
@RestController
public class App {

    @Autowired
    private MicroserviceSettingsService settingsService;

    @Autowired
    private ContextService<MicroserviceCredentials> contextService;

    @Autowired
    private Platform platform;

    private Map<String, String> c8yEnv;

    public static void main (String[] args) {
        SpringApplication.run(App.class, args);
    }


    /**
    * Get some of the environment variables of the container and load the
    * microservice settings
    */
    @PostConstruct
    private void init () {
        // Environment variables
        var env = System.getenv();

        c8yEnv = new HashMap<>();
        c8yEnv.put("app.name", env.get("APPLICATION_NAME"));
        c8yEnv.put("url", env.get("C8Y_BASEURL"));
        c8yEnv.put("jdk", env.get("JAVA_VERSION"));
        c8yEnv.put("tenant", env.get("C8Y_TENANT"));
        c8yEnv.put("user", env.get("C8Y_USER"));
        c8yEnv.put("password", env.get("C8Y_PASSWORD"));
        c8yEnv.put("isolation", env.get("C8Y_MICROSERVICE_ISOLATION"));
        c8yEnv.put("memory.limit", env.get("MEMORY_LIMIT"));

        // Required ID and key
        c8yEnv.put("tracker.id", settingsService.get("tracker.id"));
        c8yEnv.put("ipstack.key", settingsService.get("ipstack.key"));
    }


    /**
    * Create a warning alarm on microservice subscription
    */
    @EventListener(MicroserviceSubscriptionAddedEvent.class)
    public void createAlarm (MicroserviceSubscriptionAddedEvent event) {
        contextService.callWithinContext(event.getCredentials(), () -> {
            var source = new ManagedObjectRepresentation();
            source.setId(GId.asGId(c8yEnv.get("tracker.id")));

            var alarm = new AlarmRepresentation();
            alarm.setSource(source);
            alarm.setSeverity("WARNING");
            alarm.setStatus("ACTIVE");
            alarm.setDateTime(DateTime.now());
            alarm.setType("c8y_Application__Microservice_subscribed");
            alarm.setText("The microservice " + c8yEnv.get("app.name") + " has been subscribed to tenant "
            + c8yEnv.get("tenant"));

            platform.getAlarmApi().create(alarm);

            return true;
        });
    }


    /**
    * Create a LocationUpdate event based on the client's IP
    *
    * @param String The public IP of the client
    * @return The created event
    */
    public EventRepresentation createLocationUpdateEvent (String ip) {
        // Get location details from ipstack
        var rest = new RestTemplate();
        var apiURL = "http://api.ipstack.com/" + ip + "?access_key=" + c8yEnv.get("ipstack.key");
        var location = rest.getForObject(apiURL, Location.class);

        // Prepare a LocationUpdate event using Things Cloud's API
        var c8y_Position = new JSONObject();
        c8y_Position.put("lat", location.getLatitude());
        c8y_Position.put("lng", location.getLongitude());

        var source = new ManagedObjectRepresentation();
        source.setId(GId.asGId(c8yEnv.get("tracker.id")));

        var event = new EventRepresentation();
        event.setSource(source);
        event.setType("c8y_LocationUpdate");
        event.setDateTime(DateTime.now());
        event.setText("Accessed from " + ip + " (" + (location.getCity() != null ? location.getCity() + ", " : "")
        + location.getCountry_code() + ")");
        event.setProperty("c8y_Position", c8y_Position);
        event.setProperty("ip", ip);

        // Create the event in the platform
        platform.getEventApi().create(event);

        return event;
    }


    /* * * * * * * * * * Application endpoints * * * * * * * * * */

    // Check the microservice status/health (implemented by default)
    // GET /health

    // Greeting endpoints
    @RequestMapping("hello")
    public String greeting (@RequestParam(value = "name", defaultValue = "World") String you) {
        return "Hello " + you + "!";
    }

    @RequestMapping("/")
    public String root () {
        return greeting("World");
    }

    // Return the environment values
    @RequestMapping("environment")
    public Map<String, String> environment () {
        return c8yEnv;
    }

    // Track client's approximate location
    @RequestMapping(value = "location/track", produces="application/json")
    public String trackLocation (HttpServletRequest request) {
        // Get the public IP address and create the event
        return createLocationUpdateEvent(request.getHeader("x-real-ip")).toJSON();
    }

    // Get the tracked IPs and locations
    @RequestMapping("location/locations")
    public ArrayList<Object> getLocations (@RequestParam(value = "max", defaultValue = "5") int max) {
        var filter = new EventFilter().byType("c8y_LocationUpdate");
        var locations = new ArrayList<Object>();
        var eventCollection = platform.getEventApi().getEventsByFilter(filter).get(max);

        eventCollection.getEvents().forEach((event) -> {
            var map = new HashMap<String, Object>();

            map.put("ip", event.getProperty("ip"));
            map.put("coordinates", event.getProperty("c8y_Position"));
            map.put("when", event.getCreationDateTime().toString("yyyy-MM-dd hh:mm:ss"));

            locations.add(map);
        });

        return locations;
    }
}

アプリケーションの構築と展開

コマンド mvn clean install を使用し、Hello worldチュートリアルと同様の手順に従ってマイクロサービスをデプロイします。 cURLコマンドを使用してマイクロサービスをデプロイすることもできます。

$ curl -F "data=@target/iptracker-microservice-1.0.0-SNAPSHOT.zip" \
     -H "Authorization: <AUTHORIZATION>" \
     "<URL>/application/applications/<APPLICATION_ID>/binaries"

アプリケーションの検証

コマンドラインまたはWebブラウザーを使用して、アプリケーションのエンドポイントを検証できます。たとえば、location/trackに対するGETリクエストは、リクエストヘッダーからクライアントのIPを取得し、createLocationUpdateEventメソッドを使用して近似位置を取得します。レスポンスは以下のようになります。

{
  time: "2019-06-03T08:44:21.730Z",
    source: {
      id: "..."
    },
    text: "Accessed from ... (Sofia, BG)",
    type: "c8y_LocationUpdate",
    c8y_Position: {
      lng: "23.3175",
      lat: "42.683"
    },
    ip: "..."
}

エンドポイント location/locations を使用すると、デフォルトで5つの保存されたイベントが返されます。 maxパラメータを使用して、より多くの数を指定できます。

デバイス管理アプリケーションで、デバイス > すべてのデバイスに移動し、マイクロサービストラッカーを見つけます。 追跡の下に、追跡された位置の地図が表示されます。 独自のWebアプリケーションを開発して「マップ」ウィジェットをカスタマイズすることもできます。 詳細はWeb SDKドキュメントを参照してください。

マイクロサービス追跡

Dockerコンテナを実行

プロパティの設定が microservice.package.deleteImage=falseの場合、Dockerイメージは、Mavenビルド中に構築され、ローカルのDockerリポジトリに追加されています。 Hello worldチュートリアルで記載されているように、ローカルででDockerコンテナを実行できます。この場合、分離がPER_TENANTに変更されていることに注意してください。 Dockerイメージの名前とタグを使用して、次のように実行することもできます。

$ docker run -p 8082:80 -e C8Y_BOOTSTRAP_TENANT=<BOOTSTRAP_USER_TENANT> -e C8Y_BOOTSTRAP_USER=<BOOTSTRAP_USER_NAME> -e C8Y_BOOTSTRAP_PASSWORD=<BOOTSTRAP_USER_PASSWORD> -e C8Y_MICROSERVICE_ISOLATION=PER_TENANT -i -t -e C8Y_BASEURL=<URL> iptracker-microservice:latest

Dockerイメージが正常に実行された場合は、任意のWebブラウザーでマイクロサービスを検証できます。 たとえば、http://localhost:8082/location/locationsを使用すると、追跡されたすべての位置が返されます。

ソースコード

iptracker-microserviceコードは、GitHubの公開リポジトリで見つけることができます。

マイクロサービスの開発

ここでは、アノテーション、サービス、設定ファイル、ロギング、Mavenビルドプラグインなど、さまざまなマイクロサービスSDK機能について説明します。

プラットフォーム上のデプロイメントには次の2つのタイプがあります。

  • ホスティングデプロイメント - マイクロサービスのデフォルトであり、典型的なユースケースに推奨されるものです。
  • 外部/レガシーデプロイメント - プラットフォームとエージェントのカスタムインストールが必要です。

開発と検証のために、マイクロサービスをローカルのDockerコンテナにデプロイすることもできます。

注釈

アプリケーションに必要な動作を追加する最も簡単な方法は、メインクラスに@MicroserviceApplicationを注釈することです。これは、以下のアノテーションで構成される集約的なアノテーションです。

注釈 説明
@SpringBootApplication Spring Boot自動設定パッケージに含まれます
@EnableContextSupport メソッド呼び出しに@UserScopeまたは@TenantScopeスコープを使用する必要があります
@EnableHealthIndicator マイクロサービスの可用性を監視するためにプラットフォームが使用する標準的なヘルスエンドポイントを提供します
@EnableMicroserviceSecurity 標準のセキュリティを提供し、プラットフォームに対してユーザーとロールを確認します。
@EnableMicroserviceSubscription プラットフォームへのマイクロサービスの登録、メタデータの更新、テナントの登録変更イベントの受信を担当します
@EnableMicroservicePlatformInternalApi マイクロサービスが使用するSpringコンテキストにプラットフォームAPIサービスを挿入します
@EnableTenantOptionSettings テナントオプション内でマイクロサービス構成設定を提供し、ファイルのデフォルトプロパティを上書きできるようにします

コンテキストのサポート

コンテキストサポートは@EnableContextSupportアノテーションによってカバーされています。これは、ユーザー管理に関連する@TenantScope@UserScopeのいずれかを選択できるようにします。詳細は一般的な側面で説明されています。

各マイクロサービスにはプラットフォームと対話するために使用されるサービスユーザーがあります。このユーザーに関連付けられたロールはマニフェストに指定されています。テナントスコープ内では、このサービスユーザーの認証情報がプラットフォームとの通信に使用され、ユーザースコープ内ではマイクロサービスにリクエストを送信する認証されたユーザーの認証情報が使用されます。

コンテキストの設定

ContextServiceを通じて使用する認証情報を明示的に設定できます。リクエストを送信するユーザーの認証情報を使用するには、ContextService<UserCredentials>を使用します。それに応じて、サービスユーザーの認証情報にはContextService<MicroserviceCredentials>を使用できます。 ContextServiceの使用方法の例は以下に示されています。

@Autowired
private ContextService<UserCredentials> contextService;
@Autowired
private EventApi eventApi;

public PagedEventCollectionRepresentation get10Events () {
  return contextService.runWithinContext(contextService.getContext(), () -> eventApi.getEvents().get(10));
}

この最初の例では、認証されたユーザーの認証情報を使用してイベントが取得されます。

2番目の例では、サービスユーザーの認証情報を使用してイベントが取得されます。

@Autowired
private ContextService<MicroserviceCredentials> contextService;
@Autowired
private EventApi eventApi;

public PagedEventCollectionRepresentation get10Events () {
  return contextService.runWithinContext(contextService.getContext(), () -> eventApi.getEvents().get(10));
}

テナントスコープ

テナントスコープはサービスユーザーの認証情報の使用に関連付けられ、@TenantScopeでアノテートされています。 テナントスコープでtenantEventApiという名前のbeanを作成するには、以下のコード例のように@TenantScopeアノテーションを使用します。

@Autowired
private Platform platform;

@TenantScope
@Bean(name = "tenantEventApi")
public EventApi eventApi (Platform platform) throws SDKException {
  return platform.getEventApi();
}  

デフォルトでは、Microservice SDKが提供するプラットフォームAPI関連のbeanはテナントスコープで作成され、プラットフォームとの通信にサービスユーザーを使用します。

@TenantScope@UserScopeの両方で定義済みのbeanがあります。 テナントスコープ内でのbeanの名前は、プレフィックス"tenant"とそれぞれのAPIの名前で構成されます。したがって、テナントスコープでEvent APIを使用するには、以下の例のように@Qualifier(“tenantEventApi”)を指定できます。テナントスコープは作成されたbeanのデフォルトコンテキストであるため、アノテーションを省略することも可能です。したがって、以下の2つのコードは同等であり、どちらもサービスユーザーの認証情報がプラットフォームとの通信に使用されることを示唆しています。

@Autowired
@Qualifier("tenantEventApi")
private EventApi eventApi;
@Autowired
private EventApi eventApi;

どちらの場合も、テナントスコープ内のbeanは自動的にワイヤリングされます。

ユーザースコープ

特定の状況では、マイクロサービスはサービスユーザーの認証情報ではなく、リクエストを送信するユーザーの認証情報を使用します。

ユーザースコープ内にbeanを作成するには、@UserScopeを指定します。

@Autowired
private Platform platform;

@UserScope
@Bean(name = "userEventApi")
public EventApi eventApi (Platform platform) throws SDKException {
  return platform.getEventApi();
}  

テナントスコープのケースと同様に、ユーザースコープには定義済みのbeanがあります。これらのbeanの名前は、プレフィックス"user"とAPIの名前で構成されます。@UserScopeのbeanを自動ワイヤリングする例は以下に示されています。

@Autowired
@Qualifier("userEventApi")
private EventApi eventApi;

ユーザースコープ内では、作成されたbeanはプラットフォームとの通信に、デフォルトのサービスユーザーの代わりにリクエストを送信する認証されたユーザーの認証情報を使用します。

マイクロサービスのセキュリティ

@EnableMicroserviceSecurityアノテーションは、マイクロサービスの標準セキュリティ設定を構成します。すべてのエンドポイントに基本認証が必要で、(@EnableHealthIndicatorを使用して構成されたヘルスチェックエンドポイントを除く)。開発者は、標準のSpringセキュリティアノテーションを使用してエンドポイントを保護できます。たとえば、@PreAuthorize("hasRole('ROLE_A')")を使用すると、ユーザーの権限がプラットフォームに保存されているユーザーのロールに対して検証されます。

マイクロサービスサブスクリプション

マイクロサービスサブスクリプションモジュールは、2つの主な機能を担っています。

  • 登録
  • テナントサブスクリプションイベントをリッスンする

パッケージのデフォルトの動作は自己登録で、これはアプリケーションを実行すると、プラットフォームとの通信に生成された認証情報を使用して登録を試みることを意味します。自己登録は、マイクロサービスをプラットフォームに正しくデプロイするために必要です。

アプリケーションをプラットフォームに登録するもう1つの方法は、手動で登録することです。同じアプリケーション名を持つ新規アプリケーションをプラットフォーム上に作成し、次のプロパティーをマイクロサービスに提供してください。

application.name=<application_name>
C8Y.bootstrap.register=false
C8Y.bootstrap.tenant=<tenant>
C8Y.bootstrap.user=<username>
C8Y.bootstrap.password=<password>

アプリケーションを作成し、認証情報を取得する方法については、RESTインターフェースの使用セクションのアプリケーションの作成マイクロサービスの認証情報の取得を参照してください。

サブスクリプションパッケージは、マイクロサービスのテナントサブスクリプションの変更を監視し、対処する手段を提供します。カスタム動作を追加するために、開発者は以下の例のように、MicroserviceSubscriptionAddedEventおよびMicroserviceSubscriptionRemovedEventに対してイベントリスナーを追加できます。

@EventListener
public void onAdded (MicroserviceSubscriptionAddedEvent event {
    log.info("subscription added for tenant: " + event.getCredentials().getTenant());
});

アプリケーションの起動時に、すべてのサブスクライブされたテナントに対してMicroserviceSubscriptionAddedEventがトリガーされます。

ヒープとパーマ/メタデータ

ヒープとパーマ/メタデータを計算するには、マイクロサービスマニフェストで定義された制限を参考にし、メガバイト(MB)に変換されます。Java Microservice SDKを使用して開発されたJavaアプリケーションの最小値は178MBです。
10%は「システム」用に予約されていますが、50MB未満であってはなりません。
Metaspaceには10%が使用されますが、64MB以上1024MB以下です。
残りはヒープサイズに割り当てられます。 ヒープやメタスペースの設定を変更する方法についてはPackage goalを参照してください。

プラットフォームAPI

このパッケージは、Springコンテキストに構築され、注入された多数のサービスで構成されています。開発者は、プラットフォームに対して基本操作を実行するためにそれらを使用できます。Beanはファイルから読み取られたプロパティに基づいて構築されます。ホスティングデプロイメントでは、ほとんどのプロパティがプラットフォームによって提供されます。

APIは以下のサービスを提供します。

  • アラーム - AlarmApi
  • 監査レコード - AuditRecordApi
  • オペレーション - DeviceControlApi
  • イベント - EventApi
  • 外部ID - IdentityApi
  • バイナリ - BinariesApi
  • マネージドオブジェクト - InventoryApi
  • メジャーメント - MeasurementApi

APIは基本的なCRUDメソッドを提供します。以下は、アラームインターフェースの例です。

// メソッド
AlarmRepresentation create(final AlarmRepresentation alarm)
Future createAsync(final AlarmRepresentation alarm)

AlarmRepresentation getAlarm(final GId gid)
AlarmCollection getAlarms()
AlarmCollection getAlarmsByFilter(final AlarmFilter filter)

AlarmRepresentation update(final AlarmRepresentation alarm)

使用例:

@Autowired
private AlarmApi alarms;

public AlarmRepresentation addHelloAlarm (){
    AlarmRepresentation alarm = new AlarmRepresentation();
    alarm.setSeverity("CRITICAL");
    alarm.setStatus("Hello");

    return alarms.create(alarm);
}

構成設定ファイル

ホスティングデプロイメントで使用されるapplication.propertiesファイルは、*src/main/resources/*に配置する必要があります。

マイクロサービスで使用されるプロパティは以下のとおりです。

一般的なプロパティ

プロパティ 説明
application.name マイクロサービスアプリケーションの名称。
C8Y.bootstrap.register マイクロサービスが自己登録処理を行う必要があるかどうかを示します。デフォルト値はTrue
C8Y.baseURL プラットフォームのアドレス。デプロイプロセスによって提供されます。
C8Y.baseURL.mqtt MQTTサービスのアドレス。プラットフォームによって提供されます。
C8Y.bootstrap.tenant マイクロサービスの所有者であるテナントID。
C8Y.bootstrap.user マイクロサービスまたはマイクロサービス登録処理で使用されるユーザー名。
C8Y.bootstrap.password マイクロサービスまたはマイクロサービス登録処理で使用されるパスワード。
C8Y.bootstrap.delay 登録更新の遅延(ミリ秒)。
C8Y.bootstrap.initialDelay 初期登録の遅延(ミリ秒)。
C8Y.microservice.isolation マイクロサービスの分離。PER_TENANTまたはMULTI_TENANTの値のみが利用可能。デフォルトはMULTI_TENANTです。

HTTPクライアント設定プロパティ

プロパティ 説明 デフォルト値
C8Y.httpClient.httpReadTimeout HTTP読み込みタイムアウト(ミリ秒)。 180000
C8Y.httpClient.pool.enabled HTTP接続プーリングが有効になっています。 true
C8Y.httpClient.pool.perHost 接続プーリングが有効な場合のホストごとの最大接続数。 50
C8Y.httpClient.pool.max 接続プーリングが有効な場合の合計最大接続数。 100
C8Y.httpClient.pool.awaitTimeout 接続マネージャのタイムアウト(ミリ秒)。 10000
備考
ネットワーク環境が完全に理解されているマイクロサービスへのリクエストで、リクエスト/接続のタイムアウトやHTTPクライアントに関連する例外が発生していない限り、変更は行わないでください。

マイクロサービスの設定

マイクロサービス設定モジュールは、2つの機能を提供します。

  • テナントオプションを定義することでマイクロサービスを構成設定します
  • 既存のプロパティを上書きする - テナントオプションはプロパティファイルの既定値を上書きできます

マイクロサービスは、デフォルトでは、マイクロサービスコンテキストパスによって指定されたカテゴリのテナントオプションを読み込みます。 カスタム設定カテゴリは、マニフェストパラメータsettingsCategoryで指定できます。 マイクロサービスマニフェストに設定カテゴリもコンテキストパスも指定されていない場合、アプリケーション名が使用されます。

備考
マイクロサービスがデプロイされると、アプリケーションのアップグレード中にカテゴリを変更することはできません。

オプションはアプリケーションの所有者または加入者のために構成設定できます。加入者は、そのオプションが編集可能と定義されている場合のみ、所有者のオプション値を上書きできます。

設定は10分間遅延キャッシュされるため、以前にアクセスされた場合、ユーザーは変更が適用されるまでの残りの時間を待つ必要があります。 テナントコンテキストを指定せずに設定を取得しようとアクセス試行すると、リクエストを完了させるために、アプリケーション所有者が使用されます。

備考
セキュリティ上の理由から、レガシーモード (ローカル開発やRPMインストールなど) で実行している場合、この機能は使用できません。

テナントオプション設定には2つの方法でアクセスできます。

環境の使用:

@Autowired
private Environment environment;  

public int getAccessTimeout() {
    return environment.getProperty("access.timeout", Integer.class, 30);
}

設定サービスの使用:

@Autowired
private MicroserviceSettingsService settingsService;

public String getAccessTimeout() {
    return settingsService.get("access.timeout");
}

テナントオプションキーの設定は、*credentials.*プレフィックスを使用して暗号化できます。暗号化されたものは復号化され、マイクロサービス環境内で利用可能になります。

.propertiesやマニフェストファイルなどの構成ファイルで定義されたものと同じキーを使用してマイクロサービスのテナントオプションを定義すると、特定のプロパティが上書きされます。

例として、helloworld というコンテキストパスで定義されているプロパティがhello-worldマイクロサービスの application.properties ファイルにあります。

access.timeout=25

今、マイクロサービスの所有者は、cumulocity.json マニフェストファイルに次の設定を定義すれば、それを上書きすることができます。

"settings": [{
    "key": "access.timeout",
    "defaultValue": "35",
    "editable": true
}]

access.timeoutの設定が編集可能として定義されているので、登録者は、REST APIを経由で独自のテナントオプションを作成すれば、これを上書きすることができます。

POST <URL>/tenant/options

BODY:
  {
    "category": "helloworld",
    "key": "access.timeout",
    "value": "40"
  }
備考
Springの@Value("${property.name}")によって挿入されたプロパティは上書きできません。

ロギング

ホスティングデプロイメントには標準出力を使用する必要があります。

Mavenプラグイン

パッケージモジュールは、マイクロサービスの展開に必要なZIPファイルを準備するためのMavenプラグインを提供します。ビルドには実行可能なJARファイルが必要です。開発者はspring-boot-maven-pluginを使ってそれを作成することができます。最小の構成設定の例を次に示します。

<project>
  ...
  <build>
    ...
    <plugins>
    ...
      <plugin>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-maven-plugin</artifactId>
          <executions>
              <execution>
                  <goals>
                      <goal>repackage</goal>
                  </goals>
              </execution>
          </executions>
          <configuration>
              <mainClass>${main.class}</mainClass>
          </configuration>
      </plugin>
      <plugin>
          <groupId>com.nsn.cumulocity.clients-java</groupId>
          <artifactId>microservice-package-maven-plugin</artifactId>
          <version>${c8y.version}</version>
          <executions>
              <execution>
                  <id>package</id>
                  <phase>package</phase>
                  <goals>
                    <goal>package</goal>
                  </goals>
                  <configuration>
                    <name>hello-world</name>
                    <encoding>UTF-8</encoding>
                    <rpmSkip>true</rpmSkip>
                    <containerSkip>false</containerSkip>
                  </configuration>
              </execution>
              <execution>
                  <id>microservice-package</id>
                  <phase>package</phase>
                  <goals>
                    <goal>package</goal>
                  </goals>
                  <configuration>
                    <name>hello-world</name>
                    <image>hello-world</image>
                    <encoding>UTF-8</encoding>
                    <skip>false</skip>
                  </configuration>
              </execution>
          </executions>
      </plugin>
      ...
    </plugins>
    ...
  </build>
  ...
</project>

Package goal

パッケージプラグインは、Dockerコンテナ、RPMファイル、およびプラットフォーム上でデプロイ可能なZIPファイルの作成を担当します。 次のパラメータで構成できます。
(以下のリストで単一のxmlタグがパラメータとして指定されている場合は、<tag>…</tag> のように外側のxmlタグで囲んで設定してください。"…“は該当するデータ型の値に置き換えてください。)

パラメータ
pom.xmlの<configuration>セクションでの短縮表記
データ型 コマンドライン名 デフォルト値 説明
<arguments> List<String> -Dagent-package.
arguments=[…,]…
"” jar起動時の一般コマンドライン引数。",“区切りで指定。
<containerSkip> Boolean -Dskip.agent.package.
container=…
false コンテナパッケージングをスキップします
<description> String -Dpackage.description=… ${project.description} マイクロサービスの説明
<dockerBuildTimeout> Int -Dmicroservice.package.
dockerBuildTimeout=…
360 docker build生成のタイムアウト秒数
<encoding> String -Dproject.build.
sourceEncoding=…
UTF-8 文字エンコーディング
<heap>
<min>…</min>
<max>…<max>
</heap>
min, max : <Int>m
(m:メガバイト;利用可能な単位はJavaドキュメント参照)
min = 128m
max = 384m
<heap>パラメータはJava起動時の -Xms<min> -Xmx<max> 引数に反映されます
<jvmArgs> List<String> -Dagent-package.
jvmArgs=[…,]…
“-XX:+UseG1GC
-XX:+UseStringDeduplication
-XX:MinHeapFreeRatio=25
-XX:MaxHeapFreeRatio=75”
Java起動引数。”,“区切りで指定。他のオプションが指定された場合はデフォルト値は上書きされます
<manifestFile> String -DmanifestFile=… ${basedir}/src/main/
configuration/cumulocity.json
マイクロサービスマニフェストファイルのパス
<metaspace>
<min>…</min>
<max>…<max>
</metaspace>
min, max : <Int>m
(m:メガバイト;利用可能な単位はJavaドキュメント参照)
min = 64m
max = 128m
<metaspace>パラメータは<perm>値と合わせて -XX:MetaspaceSize=<min> -XX:MaxMetaspaceSize=<max>としてJava起動引数に反映されます
<name> String -Dpackage.name=… ${project.artifactId} マイクロサービス名
<perm>
<min>…</min>
<max>…<max>
</perm>
min, max : <Int>m
(m:メガバイト;利用可能な単位はJavaドキュメント参照)
min = 64m
max = 128m
<perm>パラメータは互換のため<metaspace>値と合わせて -XX:MetaspaceSize=<min> -XX:MaxMetaspaceSize=<max>としてJava起動引数に反映されます
<rpmSkip> Boolean -Dskip.agent.package.rpm=… true rpmパッケージ作成をスキップします
<skip> Boolean -Dskip.agent.package=… false 全パッケージ処理のスキップ

pom.xmlでの構成例:

...
<plugin>
  <groupId>com.nsn.cumulocity.clients-java</groupId>
  <artifactId>microservice-package-maven-plugin</artifactId>
  <version>${c8y.version}</version>
  <executions>
    <execution>
      <configuration>
        <name>hello-world</name>
        <encoding>UTF-8</encoding>
        <rpmSkip>true</rpmSkip>
        <containerSkip>false</containerSkip>
        <manifestFile>${basedir}/src/main/microservice/cumulocity.json</manifestFile>
        <heap>
          <min>200m</min>
          <max>600m</max>
        </heap>
        <metaspace>
          <min>200m</min>
          <max>300m</max>
        </metaspace>
      </configuration>
    </execution>
  </executions>
</plugin>
...
コマンドラインでのパラメータ設定
プリミティブな構成値

プリミティブな構成値やリストは、プロパティを使わずにMavenコマンドラインで直接設定できます。リストは通常 , 区切りです。 例:

-Dskip.agent.package.rpm=true
-Dagent-package.arguments=XX:+PrintCommandLineFlags,-XX:+UseCompressedClassPointers,-XX:+UseCompressedOops
ヒープやメタスペースなど複雑なデータ型の場合

複雑なデータ型をコマンドラインで指定する場合はプロパティを利用します。ヒープやメタスペース等のメモリ指定の場合、各プリミティブ値をpomプロパティで定義し、microservice-package-maven-pluginの設定内で使用します。

pom.xmlでのプリミティブなデフォルトプロパティ定義例:

<project>
  ...
  <properties>
    ...
    <custom-property.metaspace.min>200m</custom-property.metaspace.min>
    <custom-property.metaspace.max>300m</custom-property.metaspace.max>
    ...
  </properties>
  ...
</project>

これらのプロパティはpom.xml内のmicroservice-package-maven-plugin設定セクションで利用できます:

<project>
  ...
  <build>
    ...
    <plugins>
      ...
      <plugin>
        <groupId>com.nsn.cumulocity.clients-java</groupId>
        <artifactId>microservice-package-maven-plugin</artifactId>
        <version></version>
        <executions>
          <execution>
            <id>package</id>
            <phase>package</phase>
            <goals>
              <goal>package</goal>
            </goals>
            <configuration>
              <name>${microservice.name}</name>
              <image>${microservice.name}</image>
              <encoding>UTF-8</encoding>
              ...
              <metaspace>
                <min>${custom-property.metaspace.min}</min>
                <max>${custom-property.metaspace.max}</max>
              </metaspace>
              ...
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

pom.xmlでカスタムプロパティを定義していれば、コマンドラインで以下のように指定できます:

mvn clean install -Dcustom-property.metaspace.min=400m -Dcustom-property.metaspace.max=500m

Push goal

PushプラグインはDockerイメージをレジストリにプッシュします。レジストリは次の方法で設定できます。

  • containerSkip(エイリアスskip.agent.package.container)- プッシュの実行を防ぎます。デフォルトはtrueです。
  • registry(エイリアスagent-package.container.registry)- Dockerレジストリアドレス

構成設定例:

<configuration>
    <registry>http://{yourregistry.com}</registry>
    <containerSkip>false</containerSkip>
</configuration>

Upload goal

Upload goalは、マイクロサービスをサーバーにデプロイする役割を担います。 サーバーのURLと認証情報を構成するための3つのオプションがあります。

  • settings.xml - Mavenグローバル構成が*~/.m2/settings.xml*に配置されます。
  • pom.xml - Mavenプロジェクト構成設定ファイル
  • コマンドライン

この3つの方法は一緒に使用することができ、たとえば、ゴールを部分的に settings.xml で構成し、部分的に pom.xml で構成することができます。 競合が発生した場合、コマンドライン構成が最優先され、settings.xml 構成が最も優先度が低くなります。

サーバーにマイクロサービスをアップロードするには、次のプロパティを設定する必要があります。

  • url - デプロイに使用される必須のURL。デフォルトでは空です。
  • username - 認証に使用される必須のテナントIDおよびユーザー名。デフォルトでは空です。
  • password - 認証に使用される必須のパスワード。デフォルトでは空です。
  • name - アップロードされたアプリケーションのオプション名。デフォルトでは、package.nameプロパティまたはpackage.nameが提供されていない場合はartifactIdと同じになります。
  • skipMicroserviceUpload(エイリアスskip.microservice.upload)- マイクロサービスのアップロードがスキップされるかどうかを制御します。デフォルトはtrueであるため、ゴールを機能させるためにはfalseに設定する必要があります。

settings.xml

settings.xml ファイルでゴールを構成するには、次のようにサーバー構成を追加します。

<server>
    <id>microservice</id>
    <username>demos/username</username>
    <password>******</password>
    <configuration>
        <url>https://demos.cumulocity.com</url>
    </configuration>
</server>

pom.xml

pom.xml ファイルでプラグインを構成するには、次のようにサーバー構成を追加します。

<plugin>
    <groupId>com.nsn.cumulocity.clients-java</groupId>
    <artifactId>microservice-package-maven-plugin</artifactId>
    <configuration>
        <application>
            <name>helloworld</name>
        </application>

         <!-- please note that the credentials are optional if they are already configured in settings.xml -->
        <credentials>
            <url>https://demos.cumulocity.com</url>
            <username>demos/username</username>
            <password>******</password>
        </credentials>

        <skipMicroserviceUpload>false</skipMicroserviceUpload>
    </configuration>
</plugin>

コマンドライン

特定のビルドにのみ構成を渡すには、次のコマンドを実行します。

$ mvn microservice:upload -Dupload.application.name=helloworld -Dupload.url=https://demos.cumulocity.com -Dupload.username=demos/username -Dupload.password=****** -Dskip.microservice.upload=false

展開

ホスト型展開

備考
ご便宜のため、Things Cloudは、簡単なパッケージング、デプロイメント、およびサブスクリプションのためのマイクロサービスユーティリティツールを提供しています。

環境にアプリケーションをデプロイするには、以下が必要です。

  • テナントのURLアドレス
  • 次のAuthorizationヘッダー:“Basic <Base64(:)>”
  • テナント - テナントID
  • 前の手順で作成したZIPファイル
ステップ1 - アプリケーションを作成

アプリケーションが存在しない場合は、プラットフォーム上に新しいアプリケーションを作成します。

POST /application/applications
Host: ...
Authorization: Basic xxxxxxxxxxxxxxxxxxx
Content-Type: "application/json"

BODY:
  {
		"name": "<APPLICATION_NAME>",
		"type": "MICROSERVICE",
		"key": "<APPLICATION_NAME>-microservice-key"
  }

例:

$ curl -X POST -s \
      -d '{"name":"hello-microservice-1","type":"MICROSERVICE","key":"hello-microservice-1-key"}' \
      -H "Authorization: <AUTHORIZATION>" \
      -H "Content-type: application/json" \
      "<URL>/application/applications"

アプリケーションが正しく作成された場合、アプリケーションIDを取得できます。

GET /application/applicationsByName/<APPLICATION_NAME>
Host: ...
Authorization: Basic xxxxxxxxxxxxxxxxxxx
Accept: "application/json"

例:

$ curl -H "Authorization:<AUTHORIZATION>" \
     <URL>/application/applicationsByName/hello-world
ステップ2 - ZIPファイルのアップロード
POST /application/applications/<APPLICATION_ID>/binaries
Host: ...
Authorization: Basic xxxxxxxxxxxxxxxxxxx
Content-Type: "multipart/form-data"

例:

$ curl -F "data=@<PATH_TO_ZIP>" \
	     -H "Authorization: <AUTHORIZATION>" \
	     "<URL>/application/applications/<APPLICATION_ID>/binaries"
ステップ3 - マイクロサービスを登録
POST /tenant/tenants/<TENANT_ID>/applications
Host: ...
Authorization: Basic xxxxxxxxxxxxxxxxxxx
Content-Type: "multipart/form-data"

BODY:
  {
    "application": {
        "id": "<APPLICATION_ID>"
    }
  }

例:

$ curl -X POST -d '{"application":{"id": "<APPLICATION_ID>"}}'  \
       -H "Authorization: <AUTHORIZATION>" \
       -H "Content-type: application/json" \
       "<URL>/tenant/tenants/<TENANT_ID>/applications"

ローカルDocker展開

アプリケーションをローカルDockerコンテナにデプロイするには、環境変数をコンテナに挿入する必要があります。これには、Dockerのrun -eコマンドで行います。使用可能なパラメータの説明については環境変数を参照してください。

実行例は次のようになります。

$ docker run -e "C8Y_BASEURL=<C8Y_BASEURL>" -e "C8Y_BASEURL_MQTT=<C8Y_BASEURL_MQTT>" <IMAGE_NAME>

監視

ホストされたマイクロサービスが正常に実行されているかどうかを確認するために、マイクロサービスのヘルスエンドポイントを確認することができる。 このエンドポイントは、Java Microservice SDKを使用して開発されたすべてのマイクロサービスに対してデフォルトで有効になっています。

GET <URL>/service/<APPLICATION_NAME>/health

マイクロサービスが機能している場合の応答例:

HTTP/1.1 200
{
  "status": "UP"
}

またはそれが機能していない場合:

HTTP/1.1 503
{
  "status": "DOWN"
}

レガシー展開

プロパティ

外部/レガシー展開では、アプリケーションが実行されている環境用の特定プロパティファイルを検索するために、次のパスが検索されます。

  • {UPPERCASE(application_name)}_CONF_DIR/.{application_name}
  • {UPPERCASE(application_name)}_CONF_DIR/{application_name}
  • {user/home}/.{application_name}
  • {user/home}/{application_name}
  • {CONF_DIR}/.{application_name}
  • {CONF_DIR}/{application_name}
  • /etc/{application_name}

ロギング

外部/レガシー展開では、アプリケーションにロギングするということは、スプリングロギングを使用することを意味します。 Logbackの設定ファイルを検索する場所は次の通りです。

  • {UPPERCASE(application_name)}_CONF_DIR/.{application_name}/logging.xml
  • {UPPERCASE(application_name)}_CONF_DIR/{application_name}/logging.xml
  • {user/home}/.{application_name}/logging.xml
  • {user/home}/{application_name}/logging.xml
  • {CONF_DIR}/.{application_name}/logging.xml
  • {CONF_DIR}/{application_name}/logging.xml
  • /etc/{application_name}/logging.xml

マイクロサービスSDK 10.13+へのアップグレード

Spring Bootライブラリが2.5.8にアップグレードされたため、Microservice SDKを10.13+にアップグレードするには、追加の開発が必要になる場合があります。

  • RestAssuredのcontent(matcher)メソッドはbody(matcher)に置き換えられました。詳細はRequestSpecification#content()を参照してください。

  • Spring Boot BOMはjoda-timeのバージョンを定義していないので、明示的にバージョンを定義する必要があるかもしれません。

    Mavenの例:

    <dependency>
      <groupId>joda-time</groupId>
      <artifactId>joda-time</artifactId>
      <version>2.10.10</version>
    </dependency>
    
  • Jackson 2.12.xはデフォルトでJoda Moduleを提供しないため、jackson-datatype-joda依存関係を追加し、Joda Moduleを定義する必要があります。new ObjectMapper().addModule(new JodaModule());をカスタムマイクロサービスコード内に追加します。

  • Spring Boot 2.5.8では、_Bean Validation 2.0_プロバイダを推移的依存要素として提供しなくなりました。開発者は、例えばhibernate-validatorのような検証プロバイダを明示的に定義するか、spring-boot-starter-validation依存要素を追加する必要があるかもしれません。

    Mavenの例:

    <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
    
  • junit-vintage-enginespring-boot-starter-test依存要素から削除されました。まだJUnit 4.xを使用する場合は、明示的に Vintage エンジンを追加する必要があります。

    <dependency>
      <groupId>org.junit.vintage</groupId>
      <artifactId>junit-vintage-engine</artifactId>
      <scope>test</scope>
    </dependency>
    
  • Spring Boot ネイティブのエラー応答では、message欄とバインディングエラーはデフォルトで無効になっています。microservice_error_attributes.properties ファイルをオーバーライドすることで有効にできます。

    コンテンツ例:

    server.error.include-message=ALWAYS
    server.error.include-binding-errors=ALWAYS
    

Microservice SDKを10.17以上へのアップグレード

Spring Bootライブラリが2.7.6にアップグレードされたため、Microservice SDKを10.17以上にアップグレードするには、追加の開発が必要になる場合があります。

WebSecurityConfigurerAdapterがSpring Securityによって非推奨になったため、内部のマイクロサービスセキュリティ設定に変更が加えられました。Microservice SDKでは、内部の設定で SecurityFilterChain ビーンの直接宣言が使用されるようになりました。同時に、Spring Securityは単一のアプリケーションでこれらの設定アプローチを1つしか許可しません。つまり、以前にコードで古いアダプターベースの方法が使用されていた場合、アプリケーションを開始するために新しい、直接のフィルター宣言に移行する必要があります。詳細については、Spring Securityのドキュメントを参照してください。

クライアントライブラリ

このセクションでは、JavaクライアントからThings Cloudにアクセスする方法についての概要を提供します。プラットフォームへの接続からデータのアクセス、デバイスのリモート制御までを取り扱います。また、新しいデバイスやその他のビジネスオブジェクトのためにJavaからThings Cloudドメインモデルを拡張する方法についても説明します。最後に、クライアントによって生成される診断メッセージのレベルを制御するためのログサービスの設定方法についても説明します。

クライアントライブラリはRESTインターフェースの設計に密接にリンクしており、これらはThings Cloud OpenAPI仕様のREST実装に説明されています。

プラットフォームへの接続

JavaからThings Cloudに接続するためのルートインターフェースはPlatformと呼ばれています(Things Cloud OpenAPI仕様内のREST実装におけるルートインターフェースを参照)。これにより、インベントリなどのプラットフォームの他のすべてのインターフェースにアクセスできます。最も簡単な形式では、次のようにインスタンス化されます。

Platform platform = new PlatformImpl("<URL>", CumulocityBasicCredentials.from("<TENANT_ID>/<USERNAME>:<PASSWORD>"));

例:

Platform platform = new PlatformImpl("https://demos.cumulocity.com", CumulocityBasicCredentials.from("mytenant/myuser:mypassword"));

アプリケーションを開発するためにJavaクライアントを使用する場合は、(Things Cloud管理アプリケーション内のアプリケーションの管理を通じて、またはアプリケーションAPIを通じて)アプリケーションキーを登録する必要があります。

テスト目的で、すべてのテナントはデモアプリケーションキー「uL27no8nhvLlYmW1JIK1CA==」に登録されています。プラットフォームは次のように初期化されます。

new PlatformImpl("<URL>", "<TENANT_ID>", "<USERNAME>", "<PASSWORD>", "<APPLICATION_KEY>");

PlatformImplのコンストラクタでは、pageSizeパラメータを使用して、1回の応答でサーバーから返されるオブジェクトのデフォルト数を指定することもできます。

new PlatformImpl("<URL>", "<TENANT_ID>", "<USERNAME>", "<PASSWORD>", "<APPLICATION_KEY>", <PAGE_SIZE>);

インベントリへのアクセス

以下のコードスニペットは、インベントリへのハンドルを取得する方法を示しています。

InventoryApi inventory = platform.getInventoryApi();

このハンドルを使用して、管理オブジェクトを作成、取得、および更新することができます。たとえば、地理的位置を持つすべてのオブジェクトを取得したい場合は、次のようにします。

InventoryFilter inventoryFilter = new InventoryFilter();
inventoryFilter.byFragmentType(Position.class);
ManagedObjectCollection moc = inventory.getManagedObjectsByFilter(inventoryFilter);

これはオブジェクトを取得するためのクエリを返しますが、実際にはオブジェクトを取得しません。実際には、オブジェクトのリストは非常に大きい可能性があります。そのため、サーバーからページ単位で返されます。すべてのページを取得して反復処理するには、次のようにします。

for (ManagedObjectRepresentation mo : moc.get().allPages()) {
	System.out.println(mo.getName());
}
重要
デフォルトでは、allPages()はすべての要素を一度に返すのではなく、5つの要素のバッチ(ページネーション)で返します。前のページの反復が完了した後、それぞれの後続ページのために別々のリクエストが行われます。そのため、反復中にこれらのオブジェクトを変更/編集することはお勧めしません。そうすると、フィルターが異なる要素を含めたり除外したりする可能性があります。すべての要素を収集してメモリに保存し、その後に編集操作を実行する方が良いでしょう。

新しい管理オブジェクトを作成するためには、オブジェクトのローカル表現を構築し、それをプラットフォームに送信します。以下のコードスニペットは、リレーを含む新しい電気メーターを作成する方法を示しています。

ManagedObjectRepresentation mo = new ManagedObjectRepresentation();
mo.setName("MyMeter-1");

Relay relay = new Relay();
mo.set(relay);

SinglePhaseElectricitySensor meter = new SinglePhaseElectricitySensor();
mo.set(meter);

// Set additional properties, for example, tariff tables
mo = inventory.create(mo);
System.out.println(mo.getId());

create()メソッドを呼び出すことで、自動生成された一意の識別子を持つ新しい管理オブジェクトが作成されます。

デバイスとともに追加のカスタムプロパティを保存したいと仮定します。これは、Java Beanの形で新しいフラグメントを作成することで行えます。たとえば、メーターとともに料金情報を保存したいと仮定します。昼間と夜間の料金があり、夜間料金が有効な時間を保存する必要があります。

public class Tariff {
    public int getNightTariffStart() {
        return nightTariffStart;
    }

    public void setNightTariffStart(int nightTariffStart) {
        this.nightTariffStart = nightTariffStart;
    }

    public int getNightTariffEnd() {
        return nightTariffEnd;
    }

    public void setNightTariffEnd(int nightTariffEnd) {
        this.nightTariffEnd = nightTariffEnd;
    }

    private int nightTariffStart = 22;
    private int nightTariffEnd = 6;
}

これで、メーターに料金情報を追加することができます。

Tariff tariff = new Tariff();
mo.set(tariff);

アイデンティティサービスへのアクセス

デバイスには通常、エージェントがデバイスと連絡を取るために知る必要がある技術的な識別子があります。例として、メーター番号、IPアドレス、RESTのURLなどがあります。このような識別子をThings Cloudの一意の識別子と関連付けるために、エージェントはアイデンティティサービスを使用できます。関連付けを作成するには、ExternalIDRepresentation型のオブジェクトを作成してプラットフォームに送信します。

以下のコードスニペットは、デバイスのREST URLを登録する方法を示しています。moは上記の例からの管理オブジェクトであり、deviceUrlはデバイスのREST URLを含む文字列です。

final String ASSET_TYPE = "com_cumulocity_idtype_AssetTag";
final String deviceUrl = "SAMPLE-A-239239232";

ExternalIDRepresentation externalIDGid = new ExternalIDRepresentation();
externalIDGid.setType(ASSET_TYPE);
externalIDGid.setExternalId(deviceUrl);
externalIDGid.setManagedObject(mo);

IdentityApi identityApi= platform.getIdentityApi();
identityApi.create(externalIDGid);

関連付けを取得する必要がある場合は、次のようにしてアイデンティティサービスにクエリを実行するだけです。

ID id = new ID();
id.setType(ASSET_TYPE);
id.setValue(deviceUrl);
externalIDGid = identityApi.getExternalId(id);

返されたオブジェクトには、一意の識別子と管理オブジェクトへのリンクが含まれます。

イベントおよびメジャーメントへのアクセス

イベントとメジャーメントは、上記のインベントリの説明と非常に類似した方法でアクセスできます。以下の例では、過去2週間のデバイスのモバイル接続のシグナル強度をクエリし、デバイスID、計測時刻、受信シグナル強度、ビットエラー率を表示します。

MeasurementApi measurementApi = platform.getMeasurementApi();
MeasurementFilter measurementFilter = new MeasurementFilter();

Calendar cal = Calendar.getInstance();
Date toDate = cal.getTime();
cal.add(Calendar.DATE, -14);

Date fromDate = cal.getTime();
measurementFilter.byDate(fromDate, toDate);
measurementFilter.byFragmentType(SignalStrength.class);

MeasurementCollection mc = measurementApi.getMeasurementsByFilter(measurementFilter);
MeasurementCollectionRepresentation measurements = mc.get();

for (; measurements != null; measurements = mc.getNextPage(measurements)) {
	for (MeasurementRepresentation measurement : measurements.getMeasurements()) {
		SignalStrength signal = measurement.get(SignalStrength.class);
		System.out.println(measurement.getSource().getId() + " " + measurement.getTime() + " " + signal.getRssiValue() + " " + signal.getBerValue());
	}
}

デバイスの制御

DeviceControlResourceを使用すると、リモートでデバイスを操作できます。これには2つの側面があります。アプリケーションでデバイスに送信する操作を作成できますし、エージェントから操作をクエリできます。

デバイスを制御するには、そのデバイスがエージェント管理オブジェクトの子デバイス階層に含まれている必要があります。エージェント管理オブジェクトは、インベントリ内のエージェントを表します。これは、フラグメントcom_cumulocity_model_Agentによって識別されます。このようにしてThings Cloudは、特定のデバイスを制御するために操作を送信する場所を特定します。

以下のコードは、設定を示します:

ManagedObjectRepresentation agent = new ManagedObjectRepresentation();
agent.set(new com.cumulocity.model.Agent()); // agents must include this fragment

// ... create agent in inventory
ManagedObjectRepresentation device;

// ... create device in inventory
ManagedObjectReferenceRepresentation child2Ref = new ManagedObjectReferenceRepresentation();
child2Ref.setManagedObject(device);
inventory.getManagedObject(agent.getId()). addChildDevice(child2Ref);

たとえば、アプリケーションからメーター内のリレーをオフにしたいと仮定します。以前の例と同様に、ローカルで実行される操作を作成し、それをプラットフォームに送信します。

DeviceControlApi control = platform.getDeviceControlApi();
OperationRepresentation operation = new OperationRepresentation();

operation.setDeviceId(mo.getId());
relay.setRelayState(RelayState.OPEN);
operation.set(relay);
control.create(operation);

エージェントから保留中の操作をクエリしたい場合、次のコードを実行する必要があります。

OperationFilter operationFilter = new OperationFilter();
operationFilter.byAgent(mo.getId().getValue());
operationFilter.byStatus(OperationStatus.PENDING);
OperationCollection oc = control.getOperationsByFilter(operationFilter);

同様に、返される結果はその潜在的なサイズのために複数のページに分割される場合があります。

OperationCollectionRepresentation opCollectionRepresentation;

for (opCollectionRepresentation = oc.get(); opCollectionRepresentation != null; opCollectionRepresentation = oc.getNextPage(opCollectionRepresentation)) {
	for (OperationRepresentation op : opCollectionRepresentation.getOperations()) {
		System.out.println(op.getStatus());
	}
}

リアルタイム機能

Javaクライアントライブラリは、Things CloudのリアルタイムAPIを完全にサポートしています。たとえば、誰かがエージェントにオペレーションを送信したときにすぐに通知を受け取るには、次のコードを使用します。

Subscriber<GId, OperationRepresentation> subscriber = deviceControl.getNotificationsSubscriber();
Subscription<> subscription = subscriber.subscribe(agentId, new SubscriptionListener<GId, OperationRepresentation> {

    public void onError(Subscription<GId> sub, Throwable e) {
		logger.error("OperationDispatcher error!", e);
	}

	public void onNotification(Subscription<GId> sub, OperationRepresentation operation) {
		// Execute the operation
	}
});
備考
“agentId"はインベントリ内のエージェントのIDです。

サブスクリプションから解除するには、次のコードを使用します。

subscription.unsubscribe();

切断する場合は、次のコードを使用する必要があります。

subscriber.disconnect();

Notifications 2.0へのサブスクリプション

Notifications 2.0 APIは、上記のインベントリへのアクセスで説明した方法と非常に似た方法でアクセスできます。APIの詳細については、Notifications 2.0を参照してください。

以下のスニペットは、ユーザーが通知サブスクリプションを作成、クエリ、および削除する方法を示しています。また、トークン文字列を取得する方法も示しています。

// Obtain a handle to the Subscription and Token APIs:
private final NotificationSubscriptionApi notificationSubscriptionApi = platform.getNotificationSubscriptionApi();
private final TokenApi tokenApi = platform.getTokenApi();

// Create subscription filter
final NotificationSubscriptionFilterRepresentation filterRepresentation = new NotificationSubscriptionFilterRepresentation();
filterRepresentation.setApis(List.of("measurements"));
filterRepresentation.setTypeFilter("c8y_Speed");

// Construct subscription for managed object context
final NotificationSubscriptionRepresentation subscriptionRepresentation1 = new NotificationSubscriptionRepresentation();
subscriptionRepresentation1.setContext("mo");
subscriptionRepresentation1.setSubscription("testSubscription1");
subscriptionRepresentation1.setSource(mo);
subscriptionRepresentation1.setSubscriptionFilter(filterRepresentation);
subscriptionRepresentation1.setFragmentsToCopy(List.of("c8y_SpeedMeasurement", "c8y_MaxSpeedMeasurement"));

// Create subscription for managed object context
subscriptionApi.subscribe(subscriptionRepresentation1);

// Construct subscription for tenant context
final NotificationSubscriptionRepresentation subscriptionRepresentation2 = new NotificationSubscriptionRepresentation();
subscriptionRepresentation2.setContext("tenant");
subscriptionRepresentation2.setSubscription("testSubscription2");

// Create subscription for tenant context
subscriptionApi.subscribe(subscriptionRepresentation2);

// Obtain access token
final NotificationTokenRequestRepresentation tokenRequestRepresentation = new NotificationTokenRequestRepresentation(
        properties.getSubscriber(), // The subscriber name with which the client wishes to be identified.
        "testSubscription1",        // The subscription name. This value should be the same as with which the subscription was created. The access token will be only valid for the subscription specified here.
        1440,                       // The token expiration duration in minutes.
        false);

// The obtained token is required for establishing a WebSocket connection. Refer to [Notifications 2.0](https://developer.ntt.com/iot/docs/api/core/#tag/Notification-2.0-API) for more details.
final String token = tokenApi.create(tokenRequestRepresentation).getTokenString();

// Query all subscriptions
final NotificationSubscriptionCollection notificationSubscriptionCollection = subscriptionApi.getSubscriptions();
final List<NotificationSubscriptionRepresentation> subscriptions = notificationSubscriptionCollection.get().getSubscriptions();

for (NotificationSubscriptionRepresentation subscriptionRepresentation : subscriptions) {
    System.out.println(subscriptionRepresentation);
}

// Query subscriptions by filter
final NotificationSubscriptionCollection filteredNotificationSubscriptionCollection = subscriptionApi
        .getSubscriptionsByFilter(new NotificationSubscriptionFilter().byContext("mo"));
final List<NotificationSubscriptionRepresentation> filteredSubscriptions = filteredNotificationSubscriptionCollection.get().getSubscriptions();

for (NotificationSubscriptionRepresentation subscriptionRepresentation : filteredSubscriptions) {
    System.out.println(subscriptionRepresentation);
}

// Delete all tenant subscriptions
subscriptionApi.deleteTenantSubscriptions();

// Delete by source
subscriptionApi.deleteBySource(mo.getId().getValue());

cumulocity-examplesリポジトリでは、マイクロサービスのサンプルとAPIの使用方法の詳細が提供されています

信頼性機能

特にモバイルデバイスでは、インターネット接続が不安定な場合があります。このような環境をサポートするために、Javaクライアントライブラリはローカルバッファリングをサポートしています。これは、インターネット接続が利用可能かどうかに関係なく、クライアントライブラリにデータを渡すことができることを意味します。接続が利用可能な場合は、データは即座に送信されます。そうでない場合は、接続が復旧するまでデータがバッファリングされます。これには、非同期バージョンのAPI呼び出しが提供されています。例えば、アラームを送信するには次のようにします。

AlarmApi alarmApi = platform.getAlarmApi();
Future future = alarmApi.createAsync(anAlarm);

createAsyncメソッドは即座に返されます。Futureオブジェクトを使用すると、リクエストが実際に実行されるたびにその結果を判断できます。

ログの設定

JavaクライアントSDKのログは、logbackバックエンドとしたslf4jによって処理されます。ログの使用方法と設定についての詳細な説明については、logbackのドキュメントを参照してください。

バージョン10.11以降、SDKのデフォルトのログレベルはすべてのコンポーネントに対して「エラー」に設定されています。これは、ログメッセージが「エラー」レベルでない限り、抑制されることを意味します。すべてがスムーズに動作する場合、SDKによって生成されるログメッセージはありません。デフォルトでは、ログメッセージはコンソールにのみ送信されます。

デフォルトのログ構成は、新しい構成ファイルを提供することで変更できます。構成ファイルを提供する方法については、次の2つの方法がここで説明されています:システムプロパティを使用して渡された絶対ファイル名を介する方法と、OSGiフラグメントを介する方法です。両方のメソッドは、デフォルトの動作を拡張するのではなく上書きされますので、ご注意ください。

サービスプラットフォームとSMS API

このセクションでは、Things Cloud SMS APIについて説明し、Things Cloud Javaクライアントを使用してアクセスする方法を示します。また、JavaクライアントAPIを介してSMSメッセージを送受信する方法も学びます。

サービスプラットフォームの使用

サービスプラットフォームインターフェイスが、Javaサービス(SMS)APIへの接続をします。

ServicesPlatform platform = new ServicesPlatformImpl("<URL>", new CumulocityCredentials("<tenant>", "<user>", "<password>", "<application key>"));

プラットフォームを指すURLは全てのAPIリクエストを処理し、<tenant>.je1.thingscloud.ntt.comの形式でなければなりません。例えば、https://demos.je1.thingscloud.ntt.comのような形式で、すべてのAPIリクエストを処理します。

備考
外部からサービスAPIにアクセスするには、適切な認証情報が必要です。上記の例を参照してください。

SMSメッセージAPIへのアクセス

次の抜粋したコードは、JavaからSMS APIへのハンドルを取得する方法を示しています。

SmsMessagingApi smsMessagingApi = platform.getSmsMessagingApi();

このハンドルを使用すれば、関数を呼び出してJavaからSMSメッセージを送受信できます。

必要なロールの割り当て

SMSメッセージングAPIを使用するには、ユーザーはメッセージを送信および受信するために、それぞれSMS_ADMINとSMS_READの必要なロールを持っている必要があります。詳細については、権限の管理を参照してください。

メッセージの送信

APIを使用してSMSメッセージを送信するには、SendMessageRequestビルダーでメッセージを作成し、メッセージでAPIのsendMessage関数を呼び出します。

SendMessageRequest smsMessage = SendMessageRequest.builder()
        .withSender(Address.phoneNumber("<phone number>"))
        .withReceiver(Address.phoneNumber("<phone number>"))
        .withMessage("<message text>")
        .build();

smsMessagingApi.sendMessage(smsMessage);

メッセージの受信

APIを使用してSMSメッセージを送信するには、SendMessageRequest ビルダーを使用してメッセージを作成し、このメッセージを使用して API の sendMessage 関数を呼び出します。

smsMessagingApi.getAllMessages(Address.phoneNumber("<phone number>"));

以下のようにAPIを使用して、メッセージIDで識別される特定のSMSメッセージを受信できます。すべてのSMSプロバイダーがメッセージの受信をサポートしているわけではないことに注意してください。

smsMessagingApi.getMessage(Address.phoneNumber("<phone number>"), "<message id>");

SMS管理エンドポイント

REST APIを使用してSMSメッセージを送受信できます。

メッセージの送信:

POST /service/messaging/smsmessaging/outbound/tel:<sender phone number>/requests
Host: ...
Authorization: Basic ...
Content-Type: application/json
{
    "outboundSMSMessageRequest": {
        "address": ["tel:<phone number>"],
        "senderAddress": "tel:<phone number>",
        "outboundSMSTextMessage": {
  	       "message": "<message text>"
        },
        "receiptRequest": {
  	       "notifyUrl": "<notify url>",
  	        "callbackData": "<callback data>"
        },
        "senderName": "<sender name>"
    }
}

すべてのメッセージを受信:

GET /service/messaging/smsmessaging/inbound/registrations/tel:<receiver phone number>/messages
Host: ...
Authorization: Basic ...

HTTP/1.1 200 OK
{
     "inboundSMSMessageList": [
        {
            "inboundSMSMessage": {
            "dateTime": "<date>",
            "destinationAddress": "<destination address>",
            "messageId": "<message id>",
            "message": "<message>",
            "resourceURL": "<resource url>",
            "senderAddress": "<sender address>"
        }
     ]
}

特定のメッセージを受信:

GET /service/messaging/smsmessaging/inbound/registrations/tel:<receiver phone number>/messages/<message id>
Host: ...
Authorization: Basic ...

HTTP/1.1 200 OK
{
    "inboundSMSMessage": {
        "dateTime": "<date>",
        "destinationAddress": "<destination address>",
        "messageId": "<message id>",
        "message": "<message>",
        "resourceURL": "<resource url>",
        "senderAddress": "<sender address>"
    }
}

トラブルシュート

いくつかの、起こりやすい問題とその解決策を、以下に記載しました。

SSL と 証明書エラー

JavaクライアントライブラリからHTTPとHTTPSの両方を使用できます。HTTPSを使用するには、Things Cloud 商用証明書をJavaランタイム環境にインポートしなければならない場合があります。次のコマンドを使用して証明書をダウンロードします。

$ echo | openssl s_client -servername *.cumulocity.com -connect *.cumulocity.com:443 |sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > cumulocity.com.crt

次のコマンドを使用して証明書をインポートします。

$ $JAVA_HOME/bin/keytool -import -alias cumulocity -file cumulocity.com.crt -storepass changeit

この証明書を承認してください。

Javaを実行するには、次の引数を使用します。

-Djavax.net.ssl.trustStore=<home directory>/.keystore

Eclipse/OSGiを使用している場合は、実行メニューの構成設定の実行…を開きます。OSGi Frameworkをダブルクリックし、右側の引数タブを開きます。VM引数テキストボックスに上記のパラメータを追加します。

Java SDKには独自の信頼されたルート証明書が付属しているため、依然として「java.security.cert.CertificateException: Certificate Not Trusted」というエラーメッセージが表示されることがあります。この場合、次のコマンドを使用してGoDaddy認証機関(CACert)がJava環境で使用可能であることを確認してください。

$ keytool -import -v -trustcacerts -alias root -file gd_bundle.crt -keystore $JAVA_HOME/lib/security/cacerts

gd_bundle.crt証明書はGoDaddyリポジトリから直接ダウンロードできます。

SDKをインストールすると、Eclipseで互換性の問題が発生します

SDKをインストールするには、手順に従って対象プラットフォームの設定ページを使用してください。新しいソフトウェアのインストールは、実行中のEclipse IDEにソフトウェアをインストールしますが、SDKは別のサーバーソフトウェアとしてインストールする必要があります。

マイクロサービスまたはアプリケーションを実行すると、“Expected to find an object at table index"と表示されます

このエラーは特定のEclipseのバージョンにおけるバグによって発生します。回避策として、メインメニューから実行を選択し、構成設定の実行… を選択します。左側で、使用している起動構成(たとえば、OSGiフレームワーク)を選択します。右側で、引数タブをクリックします。プログラム引数に「 -clean」を追加し、適用をクリックします。

マイクロサービスまたはアプリケーションが起動しません

起動構成で必要なプラグインがすべてチェックされていることを確認してください。実行 > 実行構成に移動し、OSGiフレームワークの起動構成を選択します。すべて選択をクリックし、再度実行を試みてください。

必要なプラグインが起動されているか確認します。アプリケーションまたはマイクロサービスが実行中のときに、コンソールに「ss」と入力してリターンキーを押します。リストされたすべてのプラグインは、ACTIVEまたはRESOLVEDの状態であるべきです。

正しいターゲットプラットフォームを使用しているか確認します。設定の対象プラットフォームページに移動し、“Things Cloud runtime"がチェックされているか確認してください。

マイクロサービスアプリケーションがコンパイルされません。“Access Restriction"というアクセス制限メッセージが表示されます

このエラーは、パッケージのインポートが不足しているために発生することがあります。プロジェクトマニフェストファイルの依存関係タブに移動し、アクセス制限を与えているメソッドを含むタイプのパッケージがImport-Packageセクションに存在するか確認します。

メソッドの宣言を開くことでパッケージを見つけることができます(右クリックし、コンテキストメニューからDeclarationを開くを選択)。

アプリケーションを起動すると、“address already in use”(このアドレスは使用中です)と表示されます

別のインスタンスのアプリケーションが実行中であるか確認します。コンソールツールバーの選択したコンソールを表示アイコン(ターミナルアイコン)をクリックして、コンソールをブラウズします。赤い停止アイコンをクリックして、他の実行中のインスタンスを終了します。

Unix/macOSでは、lsofコマンドを使用して特定のポートを使用しているプロセスを確認することもできます。たとえば、どのプロセスがTCPポート8080を使用しているかを確認するには、次のように入力します。

$ lsof -i tcp:8080

次のような結果が返されます。

COMMAND    PID  USER  FD   TYPE      DEVICE  SIZE/OFF  NODE  NAME
java     12985   neo  45u  IPv6  0x077c76d0       0t0   TCP  *:8080 (LISTEN)

ここでは、プロセス12985が8080ポートを使用しており、必要に応じて強制終了できることを意味します。

アプリケーションをビルドしようとすると、“BeanCreationException: Error creating bean with name methodSecurityInterceptor"エラーが表示されます。

これは主に、pom.xml ファイルで指定されたSDKとSpring Bootのバージョンの非互換性が原因です。たとえば、SDK 1016.0.0のような、最新バージョンを使用する場合、Spring Bootは、バージョン2.5.4と互換性があるか同じである必要があります。

LinuxでDocker権限がありません。

mvnを介してマイクロサービスアプリケーションをビルドすると、次のエラーが発生する可能性があります。

[ERROR] Failed to execute goal com.nsn.cumulocity.clients-java:microservice-package-maven-plugin:1004.6.12:package (package) on project hello-microservice-java: Execution package of goal com.nsn.cumulocity.clients-java:microservice-package-maven-plugin:1004.6.12:package failed: org.apache.maven.plugin.MojoExecutionException: Exception caught: java.util.concurrent.ExecutionException: com.spotify.docker.client.shaded.javax.ws.rs.ProcessingException: java.io.IOException: Permission denied -> [Help 1]

これはLinux OSにおけるDockerの問題です。次のコマンドを実行して、ユーザーがDockerに対する権限を欠いているか確認できます。

$ docker ps
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.40/containers/json: dial unix /var/run/docker.sock: connect: permission denied

これを修正するためには、次の手順を実行してください。

  1. Dockerグループを作成します。

    $ sudo groupadd docker
    
  2. ユーザーをDockerグループに追加します。

    $ sudo usermod -aG docker $your_user_name
    
  3. 一度ログアウトしてから再度ログインすると、グループメンバーシップが更新されます。 または、次を実行してください。

    $ newgrp docker
    
  4. 再度Dockerコマンドを実行してみてください。

DockerのドキュメントのDockerエンジン > ディストリビューションごとのインストール > オプションのインストール後の手順も参照してください。