1. はじめに
V5 Java APIクライアントを使用して、最初のコールを送信するまでの手順を案内します。
クイックスタート (60秒)
必要な3つの項目
| 項目 | 値 | 場所 |
|---|---|---|
| サーバーアドレス | http://<server-ip>:80 (TLSは https://<server-ip>:443) | REST API エンドポイント。ウェブコンソールと同じアドレスです |
| アカウントID | 例: api | APIコール用ユーザーアカウント |
| トークン | UUID形式 | コンソール セキュリティ管理 → API認証トークン(/token/index) でそのアカウントとして発行 → セキュリティ設定 · セキュリティ管理 |
これら3つの値があれば、以下のブロックひとつで最初のコールが可能です。
import java.util.List;
import plantpulse.api.v5.APIClient_V5;
import plantpulse.api.v5.dto.response.SiteResponseV5;
try (APIClient_V5 client = new APIClient_V5(
"HTTP", "100.68.69.41", 80, "api", "<issued-token>", false)) {
client.connect(); // 인증
System.out.println(client.system().ping().isPing()); // → true
for (SiteResponseV5 s : client.site().list()) // 사이트 목록
System.out.println(s.getSite_id() + " — " + s.getSite_name());
}
このブロックが true とサイト一覧を出力すれば準備完了です。以下1.1〜1.7で各ステップを詳しく説明します。
1.1 依存関係の追加
JARファイルをクラスパスに追加してください。
# 빌드 (Gradle + Java 21)
cd plantpulse-api
./gradlew build
成果物は target/ に生成されます。
| ファイル | 用途 |
|---|---|
target/plantpulse-api.jar | 既存プロジェクトに依存関係と共に統合する場合 |
1.2 クライアントの生成
APIClient_V5 コンストラクタにオプションを直接渡します。
import plantpulse.api.v5.APIClient_V5;
APIClient_V5 client = new APIClient_V5(
"HTTPS", // 프로토콜: "HTTP" 또는 "HTTPS"
"100.68.69.41", // host
443, // port (REST API: 80=HTTP, 443=HTTPS)
"api", // user_id
"19ba7a54-1132-49f2-967e-3c6f5cd989a0", // token (api_key 발급용)
false); // debug
client.connect(); // 인증 + HTTP 클라이언트 초기화
// ... API 호출 ...
client.close(); // 종료
コンストラクタパラメータ
| パラメータ | タイプ | 説明 |
|---|---|---|
protocol | String | "HTTP" または "HTTPS" |
host | String | サーバーホストまたはIP |
port | int | サーバーポート |
user_id | String | 認証アカウントID |
token | String | 発行済みトークン (UUID形式) |
debug | boolean | HTTPリクエスト・レスポンス デバッグログ |
認証フロー:
connect()呼び出し時に内部でuser_id+tokenによりapi_keyを発行し、以降のすべてのコールでそのキーを自動的にヘッダーに含めます。ユーザーが直接扱う必要はありません。
1.3 最初のAPIコール — ping & サイト一覧
import java.util.List;
import plantpulse.api.v5.APIClient_V5;
import plantpulse.api.v5.dto.response.SiteResponseV5;
import plantpulse.api.v5.dto.response.SystemPingResponseV5;
public class FirstCall {
public static void main(String[] args) throws Exception {
try (APIClient_V5 client = new APIClient_V5(
"HTTP", "100.68.69.41", 80, "api",
"19ba7a54-1132-49f2-967e-3c6f5cd989a0", false)) {
client.connect();
// 1) 서버 헬스체크
SystemPingResponseV5 ping = client.system().ping();
System.out.println("Server alive: " + ping.isPing()
+ " (v" + ping.getVersion() + ")");
// 2) 사이트 목록 (typed DTO 리스트)
List<SiteResponseV5> sites = client.site().list();
for (SiteResponseV5 s : sites) {
System.out.println(s.getSite_id() + " — " + s.getSite_name());
}
}
}
}
実行結果:
Server alive: true (v5.0)
SITE_000001 — 대전 1공장
SITE_000002 — 천안 2공장
1.4 try-with-resources パターン
APIClient_V5は AutoCloseableを実装しています。try-with-resources の使用を推奨します。
try (APIClient_V5 client = new APIClient_V5(proto, host, port, user, token, false)) {
client.connect();
// ... API 호출 ...
}
// close() 자동 호출 — HTTP 커넥션 정리
1.5 13種類のサービス一覧
connect() 直後に以下のサービスメソッドですべてのドメインにアクセスできます (lazy初期化)。
client.system() // 헬스체크
client.site() // 사이트(공장)
client.customer() // 고객
client.employee() // 직원
client.product() // 제품
client.asset() // 자산 계층 (Area → Line → Equipment)
client.opc() // OPC 데이터 채널
client.tag() // 태그(데이터 포인트)
client.alarm() // 알람
client.order() // 작업지시
client.calendar() // 일정
client.path() // 자산 경로
client.flow() // Flow / Flow Node
1.6 V5 メソッドシグネチャ標準パターン
すべてのドメインサービスは一貫したメソッドパターンに従います。ドメイン名が異なるだけで、使い方は同一です。
| パターン | 戻り値タイプ | 失敗時の戻り値 | 説明 |
|---|---|---|---|
create(req) | *ResponseV5 | null | 登録 |
update(id, req) | *ResponseV5 | null | 全体修正 |
delete(id) | boolean | false | 削除 |
get(id) | *ResponseV5 | null | 単件照会 |
list() | List<*ResponseV5> | 空リスト | 全体一覧 |
exists(id) | boolean | false | 存在確認 |
count() | long | 0 | カウント |
⚠️ 例外はスローされません。 通信エラーやサーバーのERRORレスポンスは、上表の安全なデフォルト値として返されます。エラー原因はログまたは
BaseServiceV5.getErrorCode(env)で確認してください。詳細は レスポンス形式およびエラー処理 を参照。
1.7 デバッグモード
コンストラクタの最後の引数を true にすると、HTTPリクエスト・レスポンスがコンソールに出力されます。
APIClient_V5 client = new APIClient_V5(
"HTTP", "100.68.69.41", 80, "api", TOKEN, true); // debug=true
出力例:
[V5] GET https://server/api/v5/site headers={api_key=ab****cd}
[V5] response: 200 {"_status":"OK","data":[{"site_id":"SITE_000001",...}]}
1.8 認証失敗時 — 503は「キーの間違い」ではない
connect() は POST /api/v5/auth でトークンを api_key と交換します。成功判定はHTTP 200ではありません — meta.status が "OK" であり、かつ data.api_key が空ではないことが成功条件です。
第二条件があるのは、トークン検証は通過したがキー発行に失敗する経路が実際に存在するためです(その際、サーバーは500を返します)。
curl -s -X POST "http://<server>/api/v5/auth" \
-H "Content-Type: application/json; charset=UTF-8" \
-d '{"token":"<issued-token>"}'
# 200: {"data":{"api_key":"..."},"meta":{"status":"OK"},"errors":null}
| レスポンス | コード | 意味 | 修正箇所 |
|---|---|---|---|
| 400 | E1001 | token がないか空です | リクエストボディ |
| 422 | E1002 | ボディがJSONではありません | リクエストボディ |
| 401 | E1100 | トークンが正しくありません | 資格情報 |
| 401 | E1102 | トークン有効期限切れ | 再発行で復旧します |
| 401 | E1103 | トークン回収済み | 同じトークンは復旧しません — 新しく取得してください |
| 429 | E1101 | 同じIPでの失敗が蓄積しました | Retry-After 分待機後、値を修正して再試行 |
| 500 | E5000 | トークンは正しいが api_key 発行に失敗 | サーバー側 — 資格情報ではありません |
| 503 | E5500 | トークンストレージに到達できず«判定自体がありません» | サーバー側 — 資格情報ではありません |
サーバーはトークンストレージ(PostgreSQL)に到達できなかったとき、401を出しません。 401は「あなたの資格情報が間違っている」という主張であり、そのときサーバーはトークンが正しいかどうか分かっていないためです。 呼び出し側がこの二つを区別できなくなることが問題となります。
実際にそのような事例がありました — 2026-09-07、夜間e2eが8日間にわたり「22/22 失敗, E1100 Token authentication failed」を報告しましたが、プラットフォームログの真の原因は Connection to plantpulse-datalake:5432 refused でした。PostgreSQLの障害が8日間、資格情報エラーの顔をしていました。 現在はストレージ例外(SQLException · ソケット系)を原因チェーンまで辿って確認し、503 E5500 で回答します。
同様の理由で、503は失敗回数としてカウントしません。 カウントしていた場合、DB障害が正常な呼び出し側を429でロックしてしまい、障害の上にロックが重なることになります。
Javaクライアントはこの区別を例外メッセージとしてそのまま返します。メッセージを見てどこを修正するか決定してください。
| メッセージ | 意味 |
|---|---|
V5 connect: /api/v5/auth call failed — … | リクエストがサーバーに到達していません (アドレス・ポート・ファイアウォール) |
V5 connect: auth endpoint unavailable (http=503) — server reached no verdict about the token | サーバーが判定を出せませんでした — トークンを疑わないでください |
V5 connect: authentication failed — … | 401/400系 — 資格情報またはリクエストを修正してください |
V5 connect: api_key issuance failed — response=… | meta.status はOKだがキーが空です — サーバー側 |
同じIPでの認証失敗が engine.api.v5.auth.bruteforce.ip_limit (デフォルト 30) 回蓄積されると、engine.api.v5.auth.bruteforce.window.minutes (デフォルト 15) 分間429でロックされます。
engine.api.v5.auth.bruteforce.enabled=false でオフにできますが、オフにするとトークン代入攻撃に対して無防備になります。ボディがJSONでないリクエストは資格情報試行ではないためカウントしません。
次のステップ
- ドメインIDルール — 必ず次に読んでください。 IDプレフィックスの検証が強制されます。
- レスポンス形式およびエラー処理 — エラーコードマッピングと安全な処理パターン
- Siteサービス — 最初のマスタデータ生成