diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..8a3d101
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,47 @@
+---
+
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: 'kind/bug'
+assignees: ''
+
+---
+
+**Describe the bug**
+
+
+
+**To Reproduce**
+
+
+
+**Expected behavior**
+
+
+
+**Log/Stacktrace**
+
+
+
+Full Stacktrace
+
+
+```
+
+```
+
+
+
+
+**Environment:**
+- OS:
+- Deployment:
+- Spring-Zeebe Version:
+- SpringBoot Version:
+- Configuration:
diff --git a/.github/ISSUE_TEMPLATE/documentation_issue.md b/.github/ISSUE_TEMPLATE/documentation_issue.md
new file mode 100644
index 0000000..d6d22ce
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/documentation_issue.md
@@ -0,0 +1,13 @@
+---
+
+name: Documentation issue
+about: Changes to the documentation
+title: ''
+labels: 'kind/documentation'
+assignees: ''
+
+---
+
+**Description**
+
+A clear and concise description of what this issue is about.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000..a3fbc02
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,21 @@
+---
+
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: 'kind/feature'
+assignees: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/.github/workflows/gradle-publish.yml b/.github/workflows/gradle-publish.yml
deleted file mode 100644
index bf8cd6d..0000000
--- a/.github/workflows/gradle-publish.yml
+++ /dev/null
@@ -1,46 +0,0 @@
-# This workflow uses actions that are not certified by GitHub.
-# They are provided by a third-party and are governed by
-# separate terms of service, privacy policy, and support
-# documentation.
-# This workflow will build a package using Gradle and then publish it to GitHub packages when a release is created
-# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#Publishing-using-gradle
-
-name: Gradle Package
-
-on:
- release:
- types: [created]
-
-jobs:
- publish:
- runs-on: ubuntu-latest
- permissions:
- contents: read
- packages: write
-
- steps:
- - uses: actions/checkout@v3
- - name: Set up JDK 8
- uses: actions/setup-java@v3
- with:
- java-version: '8'
- distribution: 'adopt'
- server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
- settings-path: ${{ github.workspace }} # location for the settings.xml file
-
- - name: Validate Gradle wrapper
- uses: gradle/wrapper-validation-action@e6e38bacfdf1a337459f332974bb2327a31aaf4b
- - name: Build with Gradle
- uses: gradle/gradle-build-action@0d13054264b0bb894ded474f08ebb30921341cee
- with:
- arguments: build
- - name: Publish with Gradle
- run: ./gradlew -Prelease publishToSonatype closeAndReleaseSonatypeStagingRepository
- env:
- ORG_GRADLE_PROJECT_sonatypeUsername: ${{ secrets.MAVEN_CENTRAL_DEPLOYMENT_USR }}
- ORG_GRADLE_PROJECT_sonatypePassword: ${{ secrets.MAVEN_CENTRAL_DEPLOYMENT_PSW }}
- ORG_GRADLE_PROJECT_signingKeyId: ${{ secrets.MAVEN_CENTRAL_GPG_SIGNING_KEY_ID }}
- ORG_GRADLE_PROJECT_signingKey: ${{ secrets.MAVEN_CENTRAL_GPG_SIGNING_KEY_SEC }}
- ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.MAVEN_CENTRAL_GPG_SIGNING_KEY_PASSPHRASE }}
-
-
diff --git a/.github/workflows/mvn-build.yml b/.github/workflows/mvn-build.yml
new file mode 100644
index 0000000..c907003
--- /dev/null
+++ b/.github/workflows/mvn-build.yml
@@ -0,0 +1,17 @@
+name: Build via Maven and run tests
+
+on: [pull_request]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: Set up Java environment
+ uses: actions/setup-java@v4
+ with:
+ java-version: '21'
+ distribution: 'adopt'
+ cache: maven
+ - name: Build with Maven
+ run: mvn verify -PcheckFormat -B
diff --git a/.github/workflows/mvn-release.yml b/.github/workflows/mvn-release.yml
new file mode 100644
index 0000000..9aac04c
--- /dev/null
+++ b/.github/workflows/mvn-release.yml
@@ -0,0 +1,58 @@
+# If this workflow is triggered by a push to either main or release branches then it
+# deploys a SNAPSHOT
+# If this workflow is triggered by publishing a Release, it
+# deploys a RELEASE with the selected version
+# updates the project version by incrementing the patch version
+# commits the version update change to the repository's branch that triggered the workflow.
+name: Deploy artifacts with Maven
+
+on:
+ push:
+ branches:
+ - main
+ - release/**
+ release:
+ types: [ published ]
+jobs:
+ publish:
+ runs-on: ubuntu-22.04
+ steps:
+
+ - name: Check out code
+ uses: actions/checkout@v4
+
+ - name: Set up Java environment
+ uses: actions/setup-java@v4
+ with:
+ java-version: '21'
+ distribution: 'adopt'
+ cache: maven
+ gpg-private-key: ${{ secrets.MAVEN_CENTRAL_GPG_SIGNING_KEY_SEC }}
+ gpg-passphrase: MAVEN_CENTRAL_GPG_PASSPHRASE
+
+ - name: Deploy SNAPSHOT / Release
+ uses: camunda-community-hub/community-action-maven-release@v1
+ with:
+ release-version: ${{ github.event.release.tag_name }}
+ maven-url: s01.oss.sonatype.org
+ nexus-usr: ${{ secrets.NEXUS_USR }}
+ nexus-psw: ${{ secrets.NEXUS_PSW }}
+ maven-usr: ${{ secrets.MAVEN_CENTRAL_DEPLOYMENT_USR }}
+ maven-psw: ${{ secrets.MAVEN_CENTRAL_DEPLOYMENT_PSW }}
+ maven-additional-options: -U
+ maven-gpg-passphrase: ${{ secrets.MAVEN_CENTRAL_GPG_SIGNING_KEY_PASSPHRASE }}
+ maven-auto-release-after-close: true
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ branch: ${{ github.event.release.target_commitish || github.ref_name }}
+ id: release
+
+ - if: github.event.release
+ name: Attach artifacts to GitHub Release (Release only)
+ uses: actions/upload-release-asset@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ upload_url: ${{ github.event.release.upload_url }}
+ asset_path: ${{ steps.release.outputs.artifacts_archive_path }}
+ asset_name: ${{ steps.release.outputs.artifacts_archive_path }}
+ asset_content_type: application/zip
diff --git a/.gitignore b/.gitignore
index c2065bc..6d9ba77 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
HELP.md
.gradle
build/
+target
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index ad441c4..34de802 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -1,3 +1,3 @@
-# Code of Conduct
+# Code of Conduct
-### View the [Camunda Code of Conduct](https://camunda.com/events/code-conduct/) and find ways to report violations.
\ No newline at end of file
+### View the [Camunda Code of Conduct](https://camunda.com/events/code-conduct/) and find ways to report violations.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 84e495c..ae0f230 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -59,4 +59,4 @@ These are roles that are not code-based, but require some knowledge in that you
- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)
- [GitHub Help](https://help.github.com)
-- [CODE_OF_CONDUCT](https://github.com/camunda-community-hub/community/blob/main/CODE_OF_CONDUCT.MD)
\ No newline at end of file
+- [CODE_OF_CONDUCT](https://github.com/camunda-community-hub/community/blob/main/CODE_OF_CONDUCT.MD)
diff --git a/README.md b/README.md
index 19f61b6..cafc26b 100644
--- a/README.md
+++ b/README.md
@@ -2,49 +2,158 @@
![Compatible with: Camunda Platform 8](https://img.shields.io/badge/Compatible%20with-Camunda%20Platform%208-0072Ce)
[![](https://img.shields.io/badge/Lifecycle-Incubating-blue)](https://github.com/Camunda-Community-Hub/community/blob/main/extension-lifecycle.md#incubating-)
-# DEPRECATED
-
-:information_source: **This project is no more maintained** and you should start using the project provided as part of the [Spring Zeebe project](https://github.com/camunda-community-hub/spring-zeebe). If you want to use it outside of the Spring Zeebe client, you can directly use the [java-operate-client](https://github.com/camunda-community-hub/spring-zeebe/tree/main/camunda-sdk-java/java-client-operate).
+# Camunda Operate Client
This project is designed to simplify communication between a Java backend and the [Operate API of Camunda Platform 8](https://docs.camunda.io/docs/apis-clients/operate-api/).
-## How to use the client
+## How to build the client
-Simply build a CamundaOperateClient that takes an authentication and the Operate URL as parameters.
+### Spring Boot
-```java
-SimpleAuthentication sa = new SimpleAuthentication("demo", "demo", "http://localhost:8081");
-CamundaOperateClient client = new CamundaOperateClient.Builder().operateUrl("http://localhost:8081").authentication(sa).build();
-````
+Add the dependency to your project:
+
+```xml
+
+ io.camunda.spring
+ spring-boot-starter-camunda-operate
+ ${version.operate-client}
+
+```
+
+Configure a Camunda Operate client with simple authentication:
+
+```yaml
+operate:
+ client:
+ profile: simple
+```
+
+To adjust the (meaningful) default properties, you can also override them:
+
+```yaml
+operate:
+ client:
+ profile: simple
+ enabled: true
+ base-url: http://localhost:8081
+ session-timeout: PT10M
+ username: demo
+ password: demo
+```
-## Authentication
-You can use the ***SimpleAuthentication*** to connect to a local Camunda Operate if your setup is "simple": ***without identity and keycloak***.
-To connect to the **SaaS** Operate, you need to use the **SaasAuthentication** rather than the SimpleAuthentication. The SaaSAuthentication requires the ClientId and SecretId
+Configure a Camunda Operate client with identity authentication:
+```yaml
+operate:
+ client:
+ profile: oidc
+ client-id:
+ client-secret:
```
-SaasAuthentication sa = new SaasAuthentication("2~nB1MwkUU45FuXXX", "aBRKtreXQF3uD2MYYY");
-CamundaOperateClient client = new CamundaOperateClient.Builder().authentication(sa)
- .operateUrl("https://bru-2.operate.camunda.io/757dbc30-5127-4bed-XXXX-XXXXXXXXXXXX").build();
+
+To adjust the (meaningful) default properties, you can also override them:
+
+```yaml
+operate:
+ client:
+ profile: oidc
+ enabled: true
+ base-url: http://localhost:8081
+ auth-url: http://localhost:18080/auth/realms/camunda-platform/openid-connect/token
+ audience: operate-api
+ client-id:
+ client-secret:
+```
+
+Configure a Camunda Operate client for Saas:
+
+```yaml
+operate:
+ client:
+ profile: saas
+ region:
+ cluster-id:
+ client-id:
+ client-secret:
+```
+
+```yaml
+operate:
+ client:
+ profile: saas
+ enabled: true
+ base-url: https://${operate.client.region}.operate.camunda.io/${operate.client.cluster-id}
+ auth-url: https://login.cloud.camunda.io/oauth/token
+ audience: operate.camunda.io
+ region:
+ cluster-id:
+ client-id:
+ client-secret:
+```
+
+### Plain Java
+
+Add the dependency to your project:
+
+```xml
+
+ io.camunda.spring
+ java-client-operate
+ ${version.operate-client}
+
```
-You can also specify the OAuth-URL and audience, for example if you connect to a Camunda TEST system:
+Build a Camunda Operate client with simple authentication:
+```java
+// properties you need to provide
+URL operateUrl = URI.create("http://localhost:8081").toURL();
+SimpleCredential credentials = new SimpleCredential("demo", "demo", operateUrl, Duration.ofMinutes(10));
+// bootstrapping
+SimpleAuthentication authentication = new SimpleAuthentication(credentials);
+ObjectMapper objectMapper = new ObjectMapper();
+CamundaOperateClientConfiguration configuration = new CamundaOperateClientConfiguration(authentication, operateUrl, objectMapper, HttpClients.createDefault());
+CamundaOperateClient client = new CamundaOperateClient(configuration);
```
-SaasAuthentication sa = new SaasAuthentication("https://login.cloud.camunda.io/oauth/token", "operate.camunda.io", 2~nB1MwkUU45FuXXX", "aBRKtreXQF3uD2MYYY");
+
+Build a Camunda Operate client with identity authentication:
+
+```java
+// properties you need to provide
+String clientId = "";
+String clientSecret = "";
+String audience = "operate-api";
+URL operateUrl = URI.create("http://localhost:8081").toURL();
+URL authUrl = URI.create("http://localhost:18080/auth/realms/camunda-platform/protocol/openid-connect/token");
+// bootstrapping
+JwtCredential credentials = new JwtCredential(clientId, clientSecret, audience, authUrl);
+ObjectMapper objectMapper = new ObjectMapper();
+JwtAuthentication authentication = new JwtAuthentication(credentials, objectMapper);
+CamundaOperateClientConfiguration configuration = new CamundaOperateClientConfiguration(authentication, operateUrl, objectMapper, HttpClients.createDefault());
+CamundaOperateClient client = new CamundaOperateClient(configuration);
```
-To connect to the **Local** Operate with **Identity & Keycloak**, you need to use the **SelfManagedAuthentication**. The SelfManagedAuthentication requires the clientId and clientSecret. You can also change the Keycloak realm and the keycloakUrl depending on your installation.
+Build a Camunda Operate client for Saas:
```java
-SelfManagedAuthentication sma = new SelfManagedAuthentication().clientId("java").clientSecret("foTPogjlI0hidwbDZcYFWzmU8FOQwLx0").baseUrl("http://localhost:18080").keycloakRealm("camunda-platform");
-CamundaOperateClient client = new CamundaOperateClient.Builder().authentication(sma)
- .operateUrl("http://localhost:8081/").build();
+String region = "";
+String clusterId = "";
+String clientId = "";
+String clientSecret = "";
+// bootstrapping
+URL operateUrl = URI.create("https://"+ region +".operate.camunda.io/" + clusterId).toURL();
+URL authUrl = URI.create("https://login.cloud.camunda.io/oauth/token");
+JwtCredential credentials = new JwtCredential(clientId, clientSecret, "operate.camunda.io", authUrl);
+ObjectMapper objectMapper = new ObjectMapper();
+JwtAuthentication authentication = new JwtAuthentication(credentials, objectMapper);
+CamundaOperateClientConfiguration configuration = new CamundaOperateClientConfiguration(authentication, operateUrl, objectMapper, HttpClients.createDefault());
+CamundaOperateClient client = new CamundaOperateClient(configuration);
```
## Getting and Searching
-When you search objects, you can get results as List or as SearchResult. The SearchResult gives you a sortValues that you can use to paginate your results :
+When you search objects, you can get results as List or as SearchResult. The SearchResult gives you a sortValues that you can use to paginate your results :
```java
SearchQuery query = new SearchQuery.Builder().filter(someFilter).sort(new Sort("name", SortOrder.ASC)).size(20).searchAfter(previousResult.getSortValues()).build();
@@ -75,7 +184,7 @@ SearchQuery instanceQuery = new SearchQuery.Builder().filter(instanceFilter).siz
List list = client.searchProcessInstances(instanceQuery);
SearchResult result = client.searchProcessInstanceResults(instanceQuery);
-
+
//get a process instance by its key
ProcessInstance instance = client.getProcessInstance(instances.get(0).getKey());
```
@@ -89,7 +198,7 @@ FlownodeInstanceFilter flownodeFilter = new FlownodeInstanceFilter.Builder()
SearchQuery flownodeQuery = new SearchQuery.Builder().filter(flownodeFilter).size(20).sort(new Sort("state", SortOrder.ASC)).build();
List flownodes = client.searchFlownodeInstances(flownodeQuery);
-
+
//get a flownode instance by its key
FlownodeInstance flownodes = client.getFlownodeInstance(flownodes.get(0).getKey());
```
@@ -102,50 +211,23 @@ VariableFilter variableFilter = new VariableFilter.Builder().processInstanceKey(
SearchQuery varQuery = new SearchQuery.Builder().filter(variableFilter).size(5).sort(new Sort("name", SortOrder.ASC)).build();
List variables = client.searchVariables(varQuery);
-
+
//get a variable by its key
Variable var = client.getVariable(variables.get(0).getKey());
```
### Incidents
-```java
+```java
//search incidents based on filters
IncidentFilter incidentFilter = new IncidentFilter.Builder().creationTime(new DateFilter(new Date(), DateFilterRange.YEAR)).build();
SearchQuery incidentQuery = new SearchQuery.Builder().filter(incidentFilter).size(20).sort(new Sort("state", SortOrder.ASC)).build();
List incidents = client.searchIncidents(incidentQuery);
-
+
//get a incident by its key
Incident incident = client.getIncident(incidents.get(0).getKey());
```
-
-
-## Use the Beta client
-If you're using an older version of Camunda SaaS or you're having a local setup without Keycloak, you could also query the same APIs as Operate UI. In such a case, you might want to use the Beta client :
-
-```java
-SimpleAuthentication sa = new SimpleAuthentication("demo", "demo", "http://localhost:8081");
-CamundaOperateClient client = new CamundaOperateClient.Builder().beta().operateUrl("http://localhost:8081").authentication(sa).build();
-
-JsonNode json = ((CamundaOperateBetaClient) client).getFlowNodeStates(2L);
-
-AuditTrail auditTrail = ((CamundaOperateBetaClient) client).getAuditTrail(2L);
-```
-
-Obviously, as soon as the exposed APIs will be sufficient, we should get rid of this Beta client.
-
-# use it in your project
-You can import it to your maven or gradle project as a dependency
-
-```xml
-
- io.camunda
- camunda-operate-client-java
- 8.3.0.1
-
-```
-
-# Note
+## Note
A similar library is available for the Tasklist API of Camunda Platform 8 here:
[camunda-tasklist-client-java](https://github.com/camunda-community-hub/camunda-tasklist-client-java)
diff --git a/build.gradle b/build.gradle
deleted file mode 100644
index 5000f69..0000000
--- a/build.gradle
+++ /dev/null
@@ -1,104 +0,0 @@
-plugins {
- id 'maven-publish'
- id 'java-library'
- id 'signing'
- id('io.github.gradle-nexus.publish-plugin') version '1.1.0'
-}
-
-group = 'io.camunda'
-version = '8.3.0.1'
-sourceCompatibility = '8'
-
-repositories {
- mavenCentral()
-}
-
-dependencies {
- def httpClientVersion = "5.1.3"
- def zeebeBpmnVersion = "8.3.0-rc1"
- api "org.apache.httpcomponents.client5:httpclient5:${httpClientVersion}"
- api "org.apache.httpcomponents.client5:httpclient5-fluent:${httpClientVersion}"
-
- api 'com.fasterxml.jackson.core:jackson-databind:2.13.1'
- compileOnly "io.camunda:zeebe-bpmn-model:${zeebeBpmnVersion}"
-
- testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
-}
-
-tasks.named('test') {
- useJUnitPlatform()
-}
-
-task sourceJar(type: Jar) {
- outputs.cacheIf { true }
- from sourceSets.main.allJava
- archiveClassifier = 'sources'
-}
-
-task javadocJar(type: Jar, dependsOn: javadoc) {
- from javadoc.destinationDir
- duplicatesStrategy = DuplicatesStrategy.EXCLUDE
- archiveClassifier = 'javadoc'
-}
-java {
- withSourcesJar()
- withJavadocJar()
-}
-
-nexusPublishing {
- transitionCheckOptions {
- maxRetries.set(100)
- delayBetween.set(Duration.ofSeconds(10))
- }
- repositories {
- sonatype {
- stagingProfileId = "13992b29744076"
- nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/"))
- snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/"))
- }
- }
-}
-
-signing {
- useInMemoryPgpKeys(System.getenv("ORG_GRADLE_PROJECT_signingKey"), System.getenv("ORG_GRADLE_PROJECT_signingPassword"))
- sign(publishing.publications)
-}
-
-publishing {
- publications {
- mavenJava(MavenPublication) {
- from(components.java)
- versionMapping {
- usage('java-api') {
- fromResolutionResult()
- }
- }
- pom {
- groupId = 'io.camunda'
- artifactId = 'camunda-operate-client-java'
- packaging = 'jar'
- name = 'Camunda 8 Operate Java Client'
- description = "Java client for the Operate API of Camunda Platform 8"
- url = "https://github.com/camunda-community-hub/camunda-operate-client-java"
- licenses {
- license {
- name = "The Apache License, Version 2.0"
- url = "http://www.apache.org/licenses/LICENSE-2.0.txt"
- }
- }
- developers {
- developer {
- name = "christophe.dame"
- email = "christophe.dame@camunda.com"
- }
- }
- scm {
- connection = "scm:git:git@github.com:camunda-community-hub/camunda-operate-client-java.git"
- developerConnection = "scm:git:git@github.com:camunda-community-hub/camunda-operate-client-java.git"
- url = "https://github.com/camunda-community-hub/camunda-operate-client-java"
- }
- }
- }
- }
-}
-
diff --git a/examples/operate-example-load-process-instance/README.md b/examples/operate-example-load-process-instance/README.md
new file mode 100644
index 0000000..d6d29f7
--- /dev/null
+++ b/examples/operate-example-load-process-instance/README.md
@@ -0,0 +1,17 @@
+# Example: Load process instances
+
+This example allows you to load process instances plus dependant data from Operate via REST API.
+
+## Configuration
+
+Configure the Operate client as desired. Then, you can run the application and get process instances via
+
+```
+GET /process-instances
+```
+
+Also, you can delete process instances using
+
+```
+DELETE /process-instances/{key}
+```
diff --git a/examples/operate-example-load-process-instance/pom.xml b/examples/operate-example-load-process-instance/pom.xml
new file mode 100644
index 0000000..0b73905
--- /dev/null
+++ b/examples/operate-example-load-process-instance/pom.xml
@@ -0,0 +1,87 @@
+
+
+ 4.0.0
+
+ io.camunda.spring
+ operate-examples-parent
+ 8.6.0-alpha1-SNAPSHOT
+
+
+ operate-example-load-process-instance
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+ provided
+
+
+ org.springframework
+ spring-web
+ provided
+
+
+ org.springframework
+ spring-beans
+ provided
+
+
+ org.springframework
+ spring-context
+ provided
+
+
+ org.springframework.boot
+ spring-boot
+ provided
+
+
+ io.camunda.spring
+ spring-boot-starter-camunda-operate
+
+
+ io.camunda.spring
+ java-client-operate
+ provided
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.springframework.boot
+ spring-boot-test
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ org.springframework.boot:spring-boot-starter-web
+ org.springframework.boot:spring-boot-starter-test
+ io.camunda.spring:spring-boot-starter-camunda-operate
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/operate-example-load-process-instance/src/main/java/io/camunda/operate/example/ExampleApp.java b/examples/operate-example-load-process-instance/src/main/java/io/camunda/operate/example/ExampleApp.java
new file mode 100644
index 0000000..fa0737c
--- /dev/null
+++ b/examples/operate-example-load-process-instance/src/main/java/io/camunda/operate/example/ExampleApp.java
@@ -0,0 +1,11 @@
+package io.camunda.operate.example;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class ExampleApp {
+ public static void main(String[] args) {
+ SpringApplication.run(ExampleApp.class, args);
+ }
+}
diff --git a/examples/operate-example-load-process-instance/src/main/java/io/camunda/operate/example/ProcessInstanceController.java b/examples/operate-example-load-process-instance/src/main/java/io/camunda/operate/example/ProcessInstanceController.java
new file mode 100644
index 0000000..a8b4130
--- /dev/null
+++ b/examples/operate-example-load-process-instance/src/main/java/io/camunda/operate/example/ProcessInstanceController.java
@@ -0,0 +1,67 @@
+package io.camunda.operate.example;
+
+import io.camunda.operate.CamundaOperateClient;
+import io.camunda.operate.exception.OperateException;
+import io.camunda.operate.model.ChangeStatus;
+import io.camunda.operate.model.FlowNodeInstance;
+import io.camunda.operate.model.ProcessInstance;
+import io.camunda.operate.model.Variable;
+import io.camunda.operate.search.FlowNodeInstanceFilter;
+import io.camunda.operate.search.SearchQuery;
+import io.camunda.operate.search.VariableFilter;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/process-instances")
+public class ProcessInstanceController {
+ private final CamundaOperateClient camundaOperateClient;
+
+ @Autowired
+ public ProcessInstanceController(CamundaOperateClient camundaOperateClient) {
+ this.camundaOperateClient = camundaOperateClient;
+ }
+
+ @GetMapping
+ public List getProcessInstances() throws OperateException {
+ return camundaOperateClient.searchProcessInstances(new SearchQuery.Builder().build()).stream()
+ .map(
+ pi -> {
+ try {
+ return new ProcessInstanceWrapper(
+ pi,
+ camundaOperateClient.searchFlowNodeInstances(
+ new SearchQuery.Builder()
+ .filter(
+ FlowNodeInstanceFilter.builder()
+ .processInstanceKey(pi.getKey())
+ .build())
+ .build()),
+ camundaOperateClient.searchVariables(
+ new SearchQuery.Builder()
+ .filter(
+ VariableFilter.builder().processInstanceKey(pi.getKey()).build())
+ .build()));
+ } catch (OperateException e) {
+ throw new RuntimeException(e);
+ }
+ })
+ .toList();
+ }
+
+ @DeleteMapping("/{key}")
+ public ChangeStatus deleteProcessInstance(@PathVariable(name = "key") Long key)
+ throws OperateException {
+ return camundaOperateClient.deleteProcessInstance(key);
+ }
+
+ public record ProcessInstanceWrapper(
+ ProcessInstance processInstance,
+ List flowNodeInstances,
+ List variables) {}
+}
diff --git a/examples/operate-example-load-process-instance/src/main/resources/application.yaml b/examples/operate-example-load-process-instance/src/main/resources/application.yaml
new file mode 100644
index 0000000..89ec0fe
--- /dev/null
+++ b/examples/operate-example-load-process-instance/src/main/resources/application.yaml
@@ -0,0 +1,3 @@
+operate:
+ client:
+ profile: simple
\ No newline at end of file
diff --git a/examples/operate-example-load-process-instance/src/test/java/io/camunda/operate/example/ExampleTest.java b/examples/operate-example-load-process-instance/src/test/java/io/camunda/operate/example/ExampleTest.java
new file mode 100644
index 0000000..11847f4
--- /dev/null
+++ b/examples/operate-example-load-process-instance/src/test/java/io/camunda/operate/example/ExampleTest.java
@@ -0,0 +1,14 @@
+package io.camunda.operate.example;
+
+import io.camunda.operate.CamundaOperateClient;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+public class ExampleTest {
+ @Autowired CamundaOperateClient operateClient;
+
+ @Test
+ void shouldRun() {}
+}
diff --git a/examples/pom.xml b/examples/pom.xml
new file mode 100644
index 0000000..456b210
--- /dev/null
+++ b/examples/pom.xml
@@ -0,0 +1,18 @@
+
+
+ 4.0.0
+
+ io.camunda.spring
+ operate-client-root
+ 8.6.0-alpha1-SNAPSHOT
+
+
+ operate-examples-parent
+ pom
+
+ operate-example-load-process-instance
+
+
+
\ No newline at end of file
diff --git a/extension/java-client-operate/README.md b/extension/java-client-operate/README.md
new file mode 100644
index 0000000..c09ac4e
--- /dev/null
+++ b/extension/java-client-operate/README.md
@@ -0,0 +1,31 @@
+# Java Operate client
+
+The Spring Boot Starter contains the Operate client which becomes injectable as a bean.
+
+```java
+@Autowired CamundaOperateClient operateClient;
+```
+
+### List deployed process definitions
+
+```java
+ ProcessDefinitionFilter processDefinitionFilter = ProcessDefinitionFilter.builder().build();
+ SearchQuery procDefQuery = new SearchQuery.Builder()
+ .filter(processDefinitionFilter)
+ .size(1000)
+ .sort(new Sort("version", SortOrder.DESC))
+ .build();
+ return camundaOperateClient.searchProcessDefinitions(procDefQuery);
+```
+
+### Read process definitions content
+
+```java
+ camundaOperateClient.getProcessDefinitionXml(ProcessDefinitionKey);
+```
+
+### List variables
+
+```java
+ return camundaOperateClient.searchVariables(new SearchQuery.Builder().filter(new VariableFilter()).size(100).build());
+```
diff --git a/extension/java-client-operate/pom.xml b/extension/java-client-operate/pom.xml
new file mode 100644
index 0000000..1cf8461
--- /dev/null
+++ b/extension/java-client-operate/pom.xml
@@ -0,0 +1,87 @@
+
+
+ 4.0.0
+
+ io.camunda.spring
+ operate-client-parent
+ 8.6.0-alpha1-SNAPSHOT
+
+
+ java-client-operate
+
+
+
+ org.slf4j
+ slf4j-api
+ provided
+
+
+ io.camunda
+ zeebe-bpmn-model
+
+
+ org.apache.httpcomponents.client5
+ httpclient5
+
+
+ org.apache.httpcomponents.core5
+ httpcore5
+ provided
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+ provided
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+ provided
+
+
+ io.camunda
+ spring-boot-starter-camunda-sdk
+ provided
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+ org.mockito
+ mockito-junit-jupiter
+ test
+
+
+
+
+ UTF-8
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ io.camunda:spring-boot-starter-camunda-sdk
+
+
+
+
+
+
+
+
diff --git a/extension/java-client-operate/src/main/java/io/camunda/common/auth/Authentication.java b/extension/java-client-operate/src/main/java/io/camunda/common/auth/Authentication.java
new file mode 100644
index 0000000..fbd0520
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/common/auth/Authentication.java
@@ -0,0 +1,10 @@
+package io.camunda.common.auth;
+
+import java.util.Map;
+
+@Deprecated
+public interface Authentication {
+ Map getTokenHeader(Product product);
+
+ void resetToken(Product product);
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/common/auth/JwtConfig.java b/extension/java-client-operate/src/main/java/io/camunda/common/auth/JwtConfig.java
new file mode 100644
index 0000000..2b1c89d
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/common/auth/JwtConfig.java
@@ -0,0 +1,18 @@
+package io.camunda.common.auth;
+
+@Deprecated
+public class JwtConfig {
+ private JwtCredential jwtCredential;
+
+ public void addProduct(Product product, JwtCredential credential) {
+ this.jwtCredential = credential;
+ }
+
+ public JwtCredential getJwtCredential() {
+ return jwtCredential;
+ }
+
+ public JwtCredential getProduct(Product product) {
+ return jwtCredential;
+ }
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/common/auth/JwtCredential.java b/extension/java-client-operate/src/main/java/io/camunda/common/auth/JwtCredential.java
new file mode 100644
index 0000000..d831cc8
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/common/auth/JwtCredential.java
@@ -0,0 +1,21 @@
+package io.camunda.common.auth;
+
+@Deprecated
+public record JwtCredential(String clientId, String clientSecret, String audience, String authUrl) {
+
+ public String getClientId() {
+ return clientId;
+ }
+
+ public String getClientSecret() {
+ return clientSecret;
+ }
+
+ public String getAudience() {
+ return audience;
+ }
+
+ public String getAuthUrl() {
+ return authUrl;
+ }
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/common/auth/SaaSAuthentication.java b/extension/java-client-operate/src/main/java/io/camunda/common/auth/SaaSAuthentication.java
new file mode 100644
index 0000000..0e49667
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/common/auth/SaaSAuthentication.java
@@ -0,0 +1,74 @@
+package io.camunda.common.auth;
+
+import io.camunda.common.json.JsonMapper;
+import io.camunda.operate.auth.JwtAuthentication;
+import io.camunda.operate.auth.JwtCredential;
+import io.camunda.operate.auth.TokenResponse;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.time.LocalDateTime;
+import java.util.Map;
+
+@Deprecated
+public class SaaSAuthentication implements Authentication {
+ private final JwtAuthentication authentication;
+ private TokenContext tokenContext;
+
+ public SaaSAuthentication(JwtAuthentication authentication) {
+ this.authentication = authentication;
+ }
+
+ public SaaSAuthentication(JwtConfig jwtConfig, JsonMapper jsonMapper) {
+ this(jwtAuthentication(jwtConfig, jsonMapper));
+ }
+
+ private static JwtAuthentication jwtAuthentication(JwtConfig jwtConfig, JsonMapper jsonMapper) {
+ io.camunda.common.auth.JwtCredential jwtCredential = jwtConfig.getJwtCredential();
+ JwtCredential credential;
+ try {
+ credential =
+ new JwtCredential(
+ jwtCredential.getClientId(),
+ jwtCredential.getClientSecret(),
+ jwtCredential.getAudience(),
+ URI.create(jwtCredential.getAuthUrl()).toURL());
+ } catch (MalformedURLException e) {
+ throw new RuntimeException("Error while mapping jwt credential", e);
+ }
+ return new JwtAuthentication(
+ credential, (token) -> jsonMapper.fromJson(token, TokenResponse.class));
+ }
+
+ private JwtConfig jwtConfig() {
+ JwtConfig jwtConfig = new JwtConfig();
+ JwtCredential jwtCredential = authentication.getJwtCredential();
+ io.camunda.common.auth.JwtCredential credential =
+ new io.camunda.common.auth.JwtCredential(
+ jwtCredential.clientId(),
+ jwtCredential.clientSecret(),
+ jwtCredential.audience(),
+ jwtCredential.authUrl().toString());
+ jwtConfig.addProduct(Product.ZEEBE, credential);
+ return jwtConfig;
+ }
+
+ @Override
+ public Map getTokenHeader(Product product) {
+ return authentication.getTokenHeader();
+ }
+
+ @Override
+ public void resetToken(Product product) {
+ tokenContext = null;
+ }
+
+ public JwtConfig getJwtConfig() {
+ return jwtConfig();
+ }
+
+ public JwtAuthentication getJwtAuthentication() {
+ return authentication;
+ }
+
+ private record TokenContext(String token, LocalDateTime expiryDate) {}
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/common/auth/SaaSAuthenticationBuilder.java b/extension/java-client-operate/src/main/java/io/camunda/common/auth/SaaSAuthenticationBuilder.java
new file mode 100644
index 0000000..b043383
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/common/auth/SaaSAuthenticationBuilder.java
@@ -0,0 +1,30 @@
+package io.camunda.common.auth;
+
+import io.camunda.common.json.JsonMapper;
+import io.camunda.common.json.SdkObjectMapper;
+
+@Deprecated
+public class SaaSAuthenticationBuilder {
+ private JsonMapper jsonMapper;
+ private JwtConfig jwtConfig;
+
+ public SaaSAuthenticationBuilder withJsonMapper(JsonMapper jsonMapper) {
+ this.jsonMapper = jsonMapper;
+ return this;
+ }
+
+ public SaaSAuthenticationBuilder withJwtConfig(JwtConfig jwtConfig) {
+ this.jwtConfig = jwtConfig;
+ return this;
+ }
+
+ public Authentication build() {
+ if (jsonMapper == null) {
+ jsonMapper = new SdkObjectMapper();
+ }
+ if (jwtConfig == null) {
+ throw new IllegalArgumentException("jwtConfig is required");
+ }
+ return new SaaSAuthentication(jwtConfig, jsonMapper);
+ }
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/CamundaOperateClient.java b/extension/java-client-operate/src/main/java/io/camunda/operate/CamundaOperateClient.java
new file mode 100644
index 0000000..1283133
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/CamundaOperateClient.java
@@ -0,0 +1,229 @@
+package io.camunda.operate;
+
+import static io.camunda.operate.model.TypeReferences.*;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import io.camunda.operate.exception.OperateException;
+import io.camunda.operate.http.DefaultHttpClient;
+import io.camunda.operate.http.HttpClient;
+import io.camunda.operate.model.*;
+import io.camunda.operate.search.SearchQuery;
+import io.camunda.zeebe.model.bpmn.Bpmn;
+import io.camunda.zeebe.model.bpmn.BpmnModelInstance;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class CamundaOperateClient {
+
+ private final HttpClient httpClient;
+
+ private CamundaOperateClient(HttpClient httpClient) {
+ this.httpClient = httpClient;
+ }
+
+ public CamundaOperateClient(CamundaOperateClientConfiguration configuration) {
+ this(buildOperateHttpClient(configuration));
+ }
+
+ @Deprecated
+ public static CamundaOperateClientBuilder builder() {
+ return new CamundaOperateClientBuilder();
+ }
+
+ private static HttpClient buildOperateHttpClient(
+ CamundaOperateClientConfiguration configuration) {
+ // load the config map
+ Map, String> map = new HashMap<>();
+ // process definitions
+ map.put(searchProcessDefinition, "/process-definitions/search");
+ map.put(processDefinition, "/process-definitions/{key}");
+ map.put(processDefinitionXml, "/process-definitions/{key}/xml");
+ // decision definition
+ map.put(searchDecisionDefinition, "/decision-definitions/search");
+ map.put(decisionDefinition, "/decision-definitions/{key}");
+ // decision instance
+ map.put(searchDecisionInstance, "/decision-instances/search");
+ map.put(decisionInstance, "/decision-instances/{id}");
+ // flownode instance
+ map.put(searchFlowNodeInstance, "/flownode-instances/search");
+ map.put(flowNodeInstance, "/flownode-instances/{key}");
+ // variable
+ map.put(searchVariable, "/variables/search");
+ map.put(variable, "/variables/{key}");
+ // process instances
+ map.put(searchProcessInstance, "/process-instances/search");
+ map.put(processInstance, "/process-instances/{key}");
+ map.put(deleteProcessInstance, "/process-instances/{key}");
+ map.put(flownodeStatistics, "/process-instances/{key}/statistics");
+ map.put(sequenceFlows, "/process-instances/{key}/sequence-flows");
+ // decision requirements
+ map.put(searchDecisionRequirements, "/drd/search");
+ map.put(decisionRequirements, "/drd/{key}");
+ map.put(decisionRequirementsXml, "/drd/{key}/xml");
+ // incident
+ map.put(searchIncident, "/incidents/search");
+ map.put(incident, "/incidents/{key}");
+
+ try {
+ return new DefaultHttpClient(
+ URI.create(formatUrl(configuration.baseUrl().toString() + "/v1")).toURL(),
+ configuration.authentication(),
+ configuration.httpClient(),
+ configuration.objectMapper(),
+ map);
+ } catch (MalformedURLException e) {
+ throw new RuntimeException("Error while initializing operate http client", e);
+ }
+ }
+
+ private static String formatUrl(String url) {
+ if (url.endsWith("/")) {
+ return url.substring(0, url.length() - 1);
+ }
+ return url;
+ }
+
+ public ProcessDefinition getProcessDefinition(Long key) throws OperateException {
+ return httpClient.get(processDefinition, key(key));
+ }
+
+ public BpmnModelInstance getProcessDefinitionModel(Long key) throws OperateException {
+ String xml = getProcessDefinitionXml(key);
+ try {
+ InputStream processInputStream = new ByteArrayInputStream(xml.getBytes());
+ return Bpmn.readModelFromStream(processInputStream);
+ } catch (Exception e) {
+ throw new OperateException(e);
+ }
+ }
+
+ public String getProcessDefinitionXml(Long key) throws OperateException {
+ return httpClient.get(processDefinitionXml, key(key));
+ }
+
+ public List searchProcessDefinitions(SearchQuery query)
+ throws OperateException {
+ return searchProcessDefinitionResults(query).getItems();
+ }
+
+ public SearchResult searchProcessDefinitionResults(SearchQuery query)
+ throws OperateException {
+ return httpClient.post(searchProcessDefinition, query);
+ }
+
+ public List searchDecisionDefinitions(SearchQuery query)
+ throws OperateException {
+ return searchDecisionDefinitionResults(query).getItems();
+ }
+
+ public SearchResult searchDecisionDefinitionResults(SearchQuery query)
+ throws OperateException {
+ return httpClient.post(searchDecisionDefinition, query);
+ }
+
+ public List searchDecisionInstances(SearchQuery query) throws OperateException {
+ return searchDecisionInstanceResults(query).getItems();
+ }
+
+ public SearchResult searchDecisionInstanceResults(SearchQuery query)
+ throws OperateException {
+ return httpClient.post(searchDecisionInstance, query);
+ }
+
+ public List searchFlowNodeInstances(SearchQuery query) throws OperateException {
+ return searchFlowNodeInstanceResults(query).getItems();
+ }
+
+ public SearchResult searchFlowNodeInstanceResults(SearchQuery query)
+ throws OperateException {
+ return httpClient.post(searchFlowNodeInstance, query);
+ }
+
+ public List searchVariables(SearchQuery query) throws OperateException {
+ return searchVariableResults(query).getItems();
+ }
+
+ public SearchResult searchVariableResults(SearchQuery query) throws OperateException {
+ return httpClient.post(searchVariable, query);
+ }
+
+ public List searchProcessInstances(SearchQuery query) throws OperateException {
+ return searchProcessInstanceResults(query).getItems();
+ }
+
+ public SearchResult searchProcessInstanceResults(SearchQuery query)
+ throws OperateException {
+ return httpClient.post(searchProcessInstance, query);
+ }
+
+ public List searchDecisionRequirements(SearchQuery query)
+ throws OperateException {
+ return searchDecisionRequirementsResults(query).getItems();
+ }
+
+ public SearchResult searchDecisionRequirementsResults(SearchQuery query)
+ throws OperateException {
+ return httpClient.post(searchDecisionRequirements, query);
+ }
+
+ public List searchIncidents(SearchQuery query) throws OperateException {
+ return searchIncidentResults(query).getItems();
+ }
+
+ public SearchResult searchIncidentResults(SearchQuery query) throws OperateException {
+ return httpClient.post(searchIncident, query);
+ }
+
+ public ProcessInstance getProcessInstance(Long key) throws OperateException {
+ return httpClient.get(processInstance, Map.of("key", String.valueOf(key)));
+ }
+
+ public ChangeStatus deleteProcessInstance(Long key) throws OperateException {
+ return httpClient.delete(deleteProcessInstance, key(key));
+ }
+
+ public List getFlowNodeStatistics(Long key) throws OperateException {
+ return httpClient.get(flownodeStatistics, key(key));
+ }
+
+ public List getSequenceFlows(Long key) throws OperateException {
+ return httpClient.get(sequenceFlows, key(key));
+ }
+
+ public FlowNodeInstance getFlowNodeInstance(Long key) throws OperateException {
+ return httpClient.get(flowNodeInstance, key(key));
+ }
+
+ public Incident getIncident(Long key) throws OperateException {
+ return httpClient.get(incident, key(key));
+ }
+
+ public DecisionDefinition getDecisionDefinition(Long key) throws OperateException {
+ return httpClient.get(decisionDefinition, key(key));
+ }
+
+ public DecisionRequirements getDecisionRequirements(Long key) throws OperateException {
+ return httpClient.get(decisionRequirements, key(key));
+ }
+
+ public String getDecisionRequirementsXml(Long key) throws OperateException {
+ return httpClient.get(decisionRequirementsXml, key(key));
+ }
+
+ public DecisionInstance getDecisionInstance(String id) throws OperateException {
+ return httpClient.get(decisionInstance, Map.of("id", id));
+ }
+
+ public Variable getVariable(Long key) throws OperateException {
+ return httpClient.get(variable, key(key));
+ }
+
+ private Map key(Long key) {
+ return Map.of("key", String.valueOf(key));
+ }
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/CamundaOperateClientBuilder.java b/extension/java-client-operate/src/main/java/io/camunda/operate/CamundaOperateClientBuilder.java
new file mode 100644
index 0000000..b836d07
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/CamundaOperateClientBuilder.java
@@ -0,0 +1,57 @@
+package io.camunda.operate;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.camunda.common.auth.SaaSAuthentication;
+import io.camunda.operate.auth.Authentication;
+import java.net.MalformedURLException;
+import java.net.URI;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+
+@Deprecated
+public class CamundaOperateClientBuilder {
+ private String operateUrl;
+ private io.camunda.common.auth.Authentication authentication;
+
+ public CamundaOperateClientBuilder operateUrl(String operateUrl) {
+ this.operateUrl = operateUrl;
+ return this;
+ }
+
+ public CamundaOperateClientBuilder authentication(
+ io.camunda.common.auth.Authentication authentication) {
+ this.authentication = authentication;
+ return this;
+ }
+
+ public CamundaOperateClientBuilder setup() {
+ // who needs this at all???
+ return this;
+ }
+
+ public CamundaOperateClient build() {
+ try {
+ Authentication auth = mapFromLegacy();
+ CamundaOperateClientConfiguration configuration =
+ new CamundaOperateClientConfiguration(
+ auth,
+ URI.create(operateUrl).toURL(),
+ new ObjectMapper(),
+ HttpClients.createDefault());
+ return new CamundaOperateClient(configuration);
+ } catch (MalformedURLException e) {
+ throw new RuntimeException("Error while creating operate client configuration", e);
+ }
+ }
+
+ private Authentication mapFromLegacy() {
+ if (authentication == null) {
+ throw new IllegalStateException("Authentication not set");
+ }
+ // there is only saas auth which is basically wrapping a jwt auth
+ if (authentication instanceof SaaSAuthentication saaSAuthentication) {
+ return saaSAuthentication.getJwtAuthentication();
+ }
+ throw new IllegalStateException(
+ "Unknown authentication type: " + authentication.getClass().getName());
+ }
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/CamundaOperateClientConfiguration.java b/extension/java-client-operate/src/main/java/io/camunda/operate/CamundaOperateClientConfiguration.java
new file mode 100644
index 0000000..feaf767
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/CamundaOperateClientConfiguration.java
@@ -0,0 +1,12 @@
+package io.camunda.operate;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.camunda.operate.auth.Authentication;
+import java.net.URL;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+
+public record CamundaOperateClientConfiguration(
+ Authentication authentication,
+ URL baseUrl,
+ ObjectMapper objectMapper,
+ CloseableHttpClient httpClient) {}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/auth/Authentication.java b/extension/java-client-operate/src/main/java/io/camunda/operate/auth/Authentication.java
new file mode 100644
index 0000000..c2c4aa6
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/auth/Authentication.java
@@ -0,0 +1,10 @@
+package io.camunda.operate.auth;
+
+import java.util.Map;
+
+public interface Authentication {
+
+ Map getTokenHeader();
+
+ void resetToken();
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/auth/DefaultNoopAuthentication.java b/extension/java-client-operate/src/main/java/io/camunda/operate/auth/DefaultNoopAuthentication.java
new file mode 100644
index 0000000..34260cc
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/auth/DefaultNoopAuthentication.java
@@ -0,0 +1,33 @@
+package io.camunda.operate.auth;
+
+import io.camunda.operate.exception.SdkException;
+import java.lang.invoke.MethodHandles;
+import java.util.Map;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Default implementation for Authentication Typically you will replace this by a proper
+ * authentication by setting the right properties
+ */
+public class DefaultNoopAuthentication implements Authentication {
+
+ private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ private final String errorMessage =
+ "Unable to determine authentication. Please check your configuration";
+
+ public DefaultNoopAuthentication() {
+ LOG.error(errorMessage);
+ }
+
+ @Override
+ public void resetToken() {
+ throw new SdkException(errorMessage);
+ }
+
+ @Override
+ public Map getTokenHeader() {
+ throw new UnsupportedOperationException("Unable to determine authentication");
+ }
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/auth/JwtAuthentication.java b/extension/java-client-operate/src/main/java/io/camunda/operate/auth/JwtAuthentication.java
new file mode 100644
index 0000000..1084e07
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/auth/JwtAuthentication.java
@@ -0,0 +1,84 @@
+package io.camunda.operate.auth;
+
+import java.net.URISyntaxException;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import org.apache.hc.client5.http.classic.methods.HttpPost;
+import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.core5.http.NameValuePair;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.apache.hc.core5.http.message.BasicNameValuePair;
+
+public class JwtAuthentication implements Authentication {
+ private final JwtCredential jwtCredential;
+ private final TokenResponseMapper tokenResponseMapper;
+ private String token;
+ private LocalDateTime timeout;
+
+ public JwtAuthentication(JwtCredential jwtCredential, TokenResponseMapper tokenResponseMapper) {
+ this.jwtCredential = jwtCredential;
+ this.tokenResponseMapper = tokenResponseMapper;
+ }
+
+ @Deprecated
+ public JwtCredential getJwtCredential() {
+ return jwtCredential;
+ }
+
+ @Override
+ public Map getTokenHeader() {
+ if (token == null || timeout == null || timeout.isBefore(LocalDateTime.now())) {
+ TokenResponse response = retrieveToken();
+ token = response.getAccessToken();
+ timeout = LocalDateTime.now().plusSeconds(response.getExpiresIn()).minusSeconds(30);
+ }
+ return Map.of("Authorization", "Bearer " + token);
+ }
+
+ @Override
+ public void resetToken() {
+ this.token = null;
+ this.timeout = null;
+ }
+
+ private TokenResponse retrieveToken() {
+ try (CloseableHttpClient client = HttpClients.createDefault()) {
+ HttpPost request = buildRequest();
+ return client.execute(
+ request,
+ response -> {
+ try {
+ return tokenResponseMapper.readToken(EntityUtils.toString(response.getEntity()));
+ } catch (Exception e) {
+ var errorMessage =
+ String.format(
+ """
+ Token retrieval failed from: %s
+ Response code: %s
+ Audience: %s
+ """,
+ jwtCredential.authUrl(), response.getCode(), jwtCredential.audience());
+ throw new RuntimeException(errorMessage, e);
+ }
+ });
+ } catch (Exception e) {
+ throw new RuntimeException("Authenticating for Operate failed due to " + e.getMessage(), e);
+ }
+ }
+
+ private HttpPost buildRequest() throws URISyntaxException {
+ HttpPost httpPost = new HttpPost(jwtCredential.authUrl().toURI());
+ httpPost.addHeader("Content-Type", "application/json");
+ List formParams = new ArrayList<>();
+ formParams.add(new BasicNameValuePair("grant_type", "client_credentials"));
+ formParams.add(new BasicNameValuePair("client_id", jwtCredential.clientId()));
+ formParams.add(new BasicNameValuePair("client_secret", jwtCredential.clientSecret()));
+ formParams.add(new BasicNameValuePair("audience", jwtCredential.audience()));
+ httpPost.setEntity(new UrlEncodedFormEntity(formParams));
+ return httpPost;
+ }
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/auth/JwtCredential.java b/extension/java-client-operate/src/main/java/io/camunda/operate/auth/JwtCredential.java
new file mode 100644
index 0000000..d094261
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/auth/JwtCredential.java
@@ -0,0 +1,5 @@
+package io.camunda.operate.auth;
+
+import java.net.URL;
+
+public record JwtCredential(String clientId, String clientSecret, String audience, URL authUrl) {}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/auth/SimpleAuthentication.java b/extension/java-client-operate/src/main/java/io/camunda/operate/auth/SimpleAuthentication.java
new file mode 100644
index 0000000..22ae5ad
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/auth/SimpleAuthentication.java
@@ -0,0 +1,98 @@
+package io.camunda.operate.auth;
+
+import java.lang.invoke.MethodHandles;
+import java.time.LocalDateTime;
+import java.util.*;
+import org.apache.hc.client5.http.classic.methods.HttpPost;
+import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.NameValuePair;
+import org.apache.hc.core5.http.message.BasicNameValuePair;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SimpleAuthentication implements Authentication {
+ private static final String CSRF_HEADER = "OPERATE-X-CSRF-TOKEN";
+ private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ private final SimpleCredential simpleCredential;
+ private SimpleAuthToken token;
+ private LocalDateTime sessionTimeout;
+
+ public SimpleAuthentication(SimpleCredential simpleCredential) {
+ this.simpleCredential = simpleCredential;
+ }
+
+ private SimpleAuthToken retrieveToken() {
+ try (CloseableHttpClient client = HttpClients.createDefault()) {
+ HttpPost request = buildRequest(simpleCredential);
+ SimpleAuthToken simpleAuthToken =
+ client.execute(
+ request,
+ response -> {
+ String csrfTokenCandidate = null;
+ Header csrfTokenHeader = response.getHeader(CSRF_HEADER);
+ if (csrfTokenHeader != null) {
+ csrfTokenCandidate = csrfTokenHeader.getValue();
+ }
+ Header[] cookieHeaders = response.getHeaders("Set-Cookie");
+ String sessionCookie = null;
+ String csrfCookie = null;
+ String sessionCookieName = "OPERATE-SESSION";
+ for (Header cookieHeader : cookieHeaders) {
+ if (cookieHeader.getValue().startsWith(sessionCookieName)) {
+ sessionCookie = cookieHeader.getValue();
+ }
+ if (cookieHeader.getValue().startsWith(CSRF_HEADER)) {
+ csrfCookie = cookieHeader.getValue();
+ }
+ }
+ return new SimpleAuthToken(sessionCookie, csrfCookie, csrfTokenCandidate);
+ });
+ if (simpleAuthToken.sessionCookie() == null) {
+ throw new RuntimeException(
+ "Unable to authenticate due to missing Set-Cookie OPERATE-SESSION");
+ }
+ if (simpleAuthToken.csrfToken() == null) {
+ LOG.info("No CSRF token found");
+ }
+ if (simpleAuthToken.csrfCookie() == null) {
+ LOG.info("No CSRF cookie found");
+ }
+ sessionTimeout = LocalDateTime.now().plus(simpleCredential.sessionTimeout());
+ return simpleAuthToken;
+ } catch (Exception e) {
+ throw new RuntimeException("Unable to authenticate", e);
+ }
+ }
+
+ private HttpPost buildRequest(SimpleCredential simpleCredential) {
+ HttpPost httpPost = new HttpPost(simpleCredential.baseUrl().toString() + "/api/login");
+ List params = new ArrayList<>();
+ params.add(new BasicNameValuePair("username", simpleCredential.username()));
+ params.add(new BasicNameValuePair("password", simpleCredential.password()));
+ httpPost.setEntity(new UrlEncodedFormEntity(params));
+ return httpPost;
+ }
+
+ @Override
+ public Map getTokenHeader() {
+ if (token == null || sessionTimeout.isBefore(LocalDateTime.now())) {
+ token = retrieveToken();
+ }
+ return Map.of(
+ "Cookie",
+ token.sessionCookie() + "; " + token.csrfCookie(),
+ CSRF_HEADER,
+ token.csrfToken());
+ }
+
+ @Override
+ public void resetToken() {
+ token = null;
+ }
+
+ private record SimpleAuthToken(String sessionCookie, String csrfCookie, String csrfToken) {}
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/auth/SimpleCredential.java b/extension/java-client-operate/src/main/java/io/camunda/operate/auth/SimpleCredential.java
new file mode 100644
index 0000000..14c5878
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/auth/SimpleCredential.java
@@ -0,0 +1,8 @@
+package io.camunda.operate.auth;
+
+import java.net.URL;
+import java.time.Duration;
+
+/** Contains credential for particular product. Used for Simple authentication. */
+public record SimpleCredential(
+ String username, String password, URL baseUrl, Duration sessionTimeout) {}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/auth/TokenResponse.java b/extension/java-client-operate/src/main/java/io/camunda/operate/auth/TokenResponse.java
new file mode 100644
index 0000000..a9986b9
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/auth/TokenResponse.java
@@ -0,0 +1,51 @@
+package io.camunda.operate.auth;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class TokenResponse {
+
+ @JsonProperty("access_token")
+ private String accessToken;
+
+ private String scope;
+
+ @JsonProperty("expires_in")
+ private Integer expiresIn;
+
+ @JsonProperty("token_type")
+ private String tokenType;
+
+ TokenResponse() {}
+
+ public String getAccessToken() {
+ return accessToken;
+ }
+
+ public void setAccessToken(String accessToken) {
+ this.accessToken = accessToken;
+ }
+
+ public String getScope() {
+ return scope;
+ }
+
+ public void setScope(String scope) {
+ this.scope = scope;
+ }
+
+ public Integer getExpiresIn() {
+ return expiresIn;
+ }
+
+ public void setExpiresIn(Integer expiresIn) {
+ this.expiresIn = expiresIn;
+ }
+
+ public String getTokenType() {
+ return tokenType;
+ }
+
+ public void setTokenType(String tokenType) {
+ this.tokenType = tokenType;
+ }
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/auth/TokenResponseMapper.java b/extension/java-client-operate/src/main/java/io/camunda/operate/auth/TokenResponseMapper.java
new file mode 100644
index 0000000..f2b39a1
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/auth/TokenResponseMapper.java
@@ -0,0 +1,25 @@
+package io.camunda.operate.auth;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public interface TokenResponseMapper {
+ TokenResponse readToken(String token);
+
+ public class JacksonTokenResponseMapper implements TokenResponseMapper {
+ private final ObjectMapper objectMapper;
+
+ public JacksonTokenResponseMapper(ObjectMapper objectMapper) {
+ this.objectMapper = objectMapper;
+ }
+
+ @Override
+ public TokenResponse readToken(String token) {
+ try {
+ return objectMapper.readValue(token, TokenResponse.class);
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException("Error while reading token " + token, e);
+ }
+ }
+ }
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/exception/OperateException.java b/extension/java-client-operate/src/main/java/io/camunda/operate/exception/OperateException.java
new file mode 100644
index 0000000..2116067
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/exception/OperateException.java
@@ -0,0 +1,20 @@
+package io.camunda.operate.exception;
+
+public class OperateException extends Exception {
+
+ public OperateException() {
+ super();
+ }
+
+ public OperateException(Exception e) {
+ super(e);
+ }
+
+ public OperateException(String message) {
+ super(message);
+ }
+
+ public OperateException(String message, Exception e) {
+ super(message, e);
+ }
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/exception/SdkException.java b/extension/java-client-operate/src/main/java/io/camunda/operate/exception/SdkException.java
new file mode 100644
index 0000000..c7ab65a
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/exception/SdkException.java
@@ -0,0 +1,18 @@
+package io.camunda.operate.exception;
+
+public class SdkException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ public SdkException(final Throwable cause) {
+ super(cause);
+ }
+
+ public SdkException(final String message) {
+ super(message);
+ }
+
+ public SdkException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/http/DefaultHttpClient.java b/extension/java-client-operate/src/main/java/io/camunda/operate/http/DefaultHttpClient.java
new file mode 100644
index 0000000..9699dac
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/http/DefaultHttpClient.java
@@ -0,0 +1,133 @@
+package io.camunda.operate.http;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import io.camunda.operate.auth.Authentication;
+import io.camunda.operate.exception.SdkException;
+import java.lang.invoke.MethodHandles;
+import java.net.URL;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+import org.apache.hc.client5.http.classic.methods.HttpDelete;
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.classic.methods.HttpPost;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.io.HttpClientResponseHandler;
+import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.apache.hc.core5.http.message.BasicHeader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** Default Http Client powered by Apache HttpClient */
+public class DefaultHttpClient implements HttpClient {
+
+ private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+ private final Map, String> endpointMap;
+ private final CloseableHttpClient httpClient;
+ private final Authentication authentication;
+ private final ObjectMapper objectMapper;
+ private final String baseUrl;
+
+ public DefaultHttpClient(
+ URL baseUrl,
+ Authentication authentication,
+ CloseableHttpClient httpClient,
+ ObjectMapper objectMapper,
+ Map, String> endpointMap) {
+ this.authentication = authentication;
+ this.httpClient = httpClient;
+ this.objectMapper = objectMapper.copy();
+ this.endpointMap = endpointMap;
+ this.objectMapper
+ .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
+ .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
+ .setSerializationInclusion(JsonInclude.Include.NON_NULL);
+ this.baseUrl = baseUrl.toString();
+ }
+
+ @Override
+ public T get(TypeReference responseType, Map pathParams) {
+ String url = baseUrl + retrievePath(responseType, pathParams);
+ HttpGet httpGet = new HttpGet(url);
+ retrieveToken().forEach(httpGet::addHeader);
+ try {
+ return httpClient.execute(httpGet, handleResponse(responseType));
+ } catch (Exception e) {
+ throw new SdkException(String.format("Failed GET to %s, due to %s", url, e.getMessage()), e);
+ }
+ }
+
+ @Override
+ public T post(TypeReference responseType, U body) {
+
+ String url = baseUrl + retrievePath(responseType, Map.of());
+ HttpPost httpPost = new HttpPost(url);
+ httpPost.addHeader("Content-Type", "application/json");
+ retrieveToken().forEach(httpPost::addHeader);
+ String data;
+ try {
+ data = objectMapper.writeValueAsString(body);
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException("Error while parsing " + body + "of type " + body.getClass(), e);
+ }
+ httpPost.setEntity(new StringEntity(data));
+ try {
+ return httpClient.execute(httpPost, handleResponse(responseType));
+ } catch (Exception e) {
+ throw new SdkException(
+ String.format("Failed POST to %s with body %s, due to %s", url, data, e.getMessage()), e);
+ }
+ }
+
+ @Override
+ public T delete(TypeReference responseType, Map pathParams) {
+ String resourcePath = retrievePath(responseType, pathParams);
+ String url = baseUrl + resourcePath;
+ HttpDelete httpDelete = new HttpDelete(url);
+ retrieveToken().forEach(httpDelete::addHeader);
+ try {
+ return httpClient.execute(httpDelete, handleResponse(responseType));
+ } catch (Exception e) {
+ throw new SdkException(
+ String.format("Failed DELETE to %s, due to %s", url, e.getMessage()), e);
+ }
+ }
+
+ private String retrievePath(TypeReference clazz, Map pathParams) {
+ AtomicReference path = new AtomicReference<>();
+ if (endpointMap.containsKey(clazz)) {
+ path.set(endpointMap.get(clazz));
+ }
+ for (String pathParam : pathParams.keySet()) {
+ String pathParamMarker = "{" + pathParam + "}";
+ if (path.get().contains(pathParamMarker)) {
+ path.set(path.get().replace(pathParamMarker, pathParams.get(pathParam)));
+ }
+ }
+ return path.get();
+ }
+
+ private List extends Header> retrieveToken() {
+ Map header = authentication.getTokenHeader();
+ return header.entrySet().stream().map(e -> new BasicHeader(e.getKey(), e.getValue())).toList();
+ }
+
+ private HttpClientResponseHandler handleResponse(TypeReference responseType) {
+ return new TypeReferenceHttpClientResponseHandler<>(
+ responseType, objectMapper, this::handleErrorResponse);
+ }
+
+ private SdkException handleErrorResponse(Integer code) {
+ if (code == HttpStatus.SC_UNAUTHORIZED || code == HttpStatus.SC_FORBIDDEN) {
+ authentication.resetToken();
+ }
+ return new SdkException("Response not successful: " + code);
+ }
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/http/HttpClient.java b/extension/java-client-operate/src/main/java/io/camunda/operate/http/HttpClient.java
new file mode 100644
index 0000000..c1daec6
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/http/HttpClient.java
@@ -0,0 +1,14 @@
+package io.camunda.operate.http;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import java.util.Map;
+
+/** Interface to enable swappable http client implementations */
+public interface HttpClient {
+
+ T get(TypeReference responseType, Map pathParams);
+
+ T post(TypeReference responseType, U body);
+
+ T delete(TypeReference responseType, Map pathParams);
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/http/Java8Utils.java b/extension/java-client-operate/src/main/java/io/camunda/operate/http/Java8Utils.java
new file mode 100644
index 0000000..074065b
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/http/Java8Utils.java
@@ -0,0 +1,37 @@
+package io.camunda.operate.http;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class Java8Utils {
+
+ private Java8Utils() {}
+
+ public static byte[] readAllBytes(InputStream inputStream) throws IOException {
+ final int bufLen = 4 * 0x400; // 4KB
+ byte[] buf = new byte[bufLen];
+ int readLen;
+ IOException exception = null;
+
+ try {
+ try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
+ while ((readLen = inputStream.read(buf, 0, bufLen)) != -1)
+ outputStream.write(buf, 0, readLen);
+
+ return outputStream.toByteArray();
+ }
+ } catch (IOException e) {
+ exception = e;
+ throw e;
+ } finally {
+ if (exception == null) inputStream.close();
+ else
+ try {
+ inputStream.close();
+ } catch (IOException e) {
+ exception.addSuppressed(e);
+ }
+ }
+ }
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/http/TypeReferenceHttpClientResponseHandler.java b/extension/java-client-operate/src/main/java/io/camunda/operate/http/TypeReferenceHttpClientResponseHandler.java
new file mode 100644
index 0000000..0b14f62
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/http/TypeReferenceHttpClientResponseHandler.java
@@ -0,0 +1,41 @@
+package io.camunda.operate.http;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.function.Function;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.io.HttpClientResponseHandler;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+
+public class TypeReferenceHttpClientResponseHandler implements HttpClientResponseHandler {
+ private final TypeReference typeReference;
+ private final ObjectMapper objectMapper;
+ private final Function errorCodeHandler;
+
+ public TypeReferenceHttpClientResponseHandler(
+ TypeReference typeReference,
+ ObjectMapper objectMapper,
+ Function errorCodeHandler) {
+ this.typeReference = typeReference;
+ this.objectMapper = objectMapper;
+ this.errorCodeHandler = errorCodeHandler;
+ }
+
+ @Override
+ public T handleResponse(ClassicHttpResponse response) throws HttpException, IOException {
+ T resp;
+ if (200 <= response.getCode() && response.getCode() <= 299) {
+ HttpEntity entity = response.getEntity();
+ String tmp = new String(Java8Utils.readAllBytes(entity.getContent()), StandardCharsets.UTF_8);
+ resp = objectMapper.readValue(tmp, typeReference);
+ EntityUtils.consume(entity);
+ return resp;
+ } else {
+ throw errorCodeHandler.apply(response.getCode());
+ }
+ }
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/model/ChangeStatus.java b/extension/java-client-operate/src/main/java/io/camunda/operate/model/ChangeStatus.java
new file mode 100644
index 0000000..53a514c
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/model/ChangeStatus.java
@@ -0,0 +1,23 @@
+package io.camunda.operate.model;
+
+public class ChangeStatus {
+
+ private String message;
+ private Long deleted;
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public Long getDeleted() {
+ return deleted;
+ }
+
+ public void setDeleted(Long deleted) {
+ this.deleted = deleted;
+ }
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/model/DecisionDefinition.java b/extension/java-client-operate/src/main/java/io/camunda/operate/model/DecisionDefinition.java
new file mode 100644
index 0000000..2eeb670
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/model/DecisionDefinition.java
@@ -0,0 +1,95 @@
+package io.camunda.operate.model;
+
+public class DecisionDefinition {
+
+ private String id;
+ private Long key;
+ private String decisionId;
+ private String name;
+ private Long version;
+ private String decisionRequirementsId;
+ private Long decisionRequirementsKey;
+ private String decisionRequirementsName;
+ private Long decisionRequirementsVersion;
+ private String tenantId;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public Long getKey() {
+ return key;
+ }
+
+ public void setKey(Long key) {
+ this.key = key;
+ }
+
+ public String getDecisionId() {
+ return decisionId;
+ }
+
+ public void setDecisionId(String decisionId) {
+ this.decisionId = decisionId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Long getVersion() {
+ return version;
+ }
+
+ public void setVersion(Long version) {
+ this.version = version;
+ }
+
+ public String getDecisionRequirementsId() {
+ return decisionRequirementsId;
+ }
+
+ public void setDecisionRequirementsId(String decisionRequirementsId) {
+ this.decisionRequirementsId = decisionRequirementsId;
+ }
+
+ public Long getDecisionRequirementsKey() {
+ return decisionRequirementsKey;
+ }
+
+ public void setDecisionRequirementsKey(Long decisionRequirementsKey) {
+ this.decisionRequirementsKey = decisionRequirementsKey;
+ }
+
+ public String getDecisionRequirementsName() {
+ return decisionRequirementsName;
+ }
+
+ public void setDecisionRequirementsName(String decisionRequirementsName) {
+ this.decisionRequirementsName = decisionRequirementsName;
+ }
+
+ public Long getDecisionRequirementsVersion() {
+ return decisionRequirementsVersion;
+ }
+
+ public void setDecisionRequirementsVersion(Long decisionRequirementsVersion) {
+ this.decisionRequirementsVersion = decisionRequirementsVersion;
+ }
+
+ public String getTenantId() {
+ return tenantId;
+ }
+
+ public void setTenantId(String tenantId) {
+ this.tenantId = tenantId;
+ }
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/model/DecisionInstance.java b/extension/java-client-operate/src/main/java/io/camunda/operate/model/DecisionInstance.java
new file mode 100644
index 0000000..fa3a895
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/model/DecisionInstance.java
@@ -0,0 +1,151 @@
+package io.camunda.operate.model;
+
+import java.util.List;
+
+public class DecisionInstance {
+
+ private String id;
+ private Long key;
+ private DecisionState state;
+ private String evaluationDate;
+ private String evaluationFailure;
+ private Long processDefinitionKey;
+ private Long processInstanceKey;
+ private String decisionId;
+ private String decisionDefinitionId;
+ private String decisionName;
+ private Long decisionVersion;
+ private DecisionType decisionType;
+ private String result;
+ private List evaluatedInputs;
+ private List evaluatedOutputs;
+ private String tenantId;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public Long getKey() {
+ return key;
+ }
+
+ public void setKey(Long key) {
+ this.key = key;
+ }
+
+ public DecisionState getState() {
+ return state;
+ }
+
+ public void setState(DecisionState state) {
+ this.state = state;
+ }
+
+ public String getEvaluationDate() {
+ return evaluationDate;
+ }
+
+ public void setEvaluationDate(String evaluationDate) {
+ this.evaluationDate = evaluationDate;
+ }
+
+ public String getEvaluationFailure() {
+ return evaluationFailure;
+ }
+
+ public void setEvaluationFailure(String evaluationFailure) {
+ this.evaluationFailure = evaluationFailure;
+ }
+
+ public Long getProcessDefinitionKey() {
+ return processDefinitionKey;
+ }
+
+ public void setProcessDefinitionKey(Long processDefinitionKey) {
+ this.processDefinitionKey = processDefinitionKey;
+ }
+
+ public Long getProcessInstanceKey() {
+ return processInstanceKey;
+ }
+
+ public void setProcessInstanceKey(Long processInstanceKey) {
+ this.processInstanceKey = processInstanceKey;
+ }
+
+ public String getDecisionId() {
+ return decisionId;
+ }
+
+ public void setDecisionId(String decisionId) {
+ this.decisionId = decisionId;
+ }
+
+ public String getDecisionDefinitionId() {
+ return decisionDefinitionId;
+ }
+
+ public void setDecisionDefinitionId(String decisionDefinitionId) {
+ this.decisionDefinitionId = decisionDefinitionId;
+ }
+
+ public String getDecisionName() {
+ return decisionName;
+ }
+
+ public void setDecisionName(String decisionName) {
+ this.decisionName = decisionName;
+ }
+
+ public Long getDecisionVersion() {
+ return decisionVersion;
+ }
+
+ public void setDecisionVersion(Long decisionVersion) {
+ this.decisionVersion = decisionVersion;
+ }
+
+ public DecisionType getDecisionType() {
+ return decisionType;
+ }
+
+ public void setDecisionType(DecisionType decisionType) {
+ this.decisionType = decisionType;
+ }
+
+ public String getResult() {
+ return result;
+ }
+
+ public void setResult(String result) {
+ this.result = result;
+ }
+
+ public List getEvaluatedInputs() {
+ return evaluatedInputs;
+ }
+
+ public void setEvaluatedInputs(List evaluatedInputs) {
+ this.evaluatedInputs = evaluatedInputs;
+ }
+
+ public List getEvaluatedOutputs() {
+ return evaluatedOutputs;
+ }
+
+ public void setEvaluatedOutputs(List evaluatedOutputs) {
+ this.evaluatedOutputs = evaluatedOutputs;
+ }
+
+ public String getTenantId() {
+ return tenantId;
+ }
+
+ public void setTenantId(String tenantId) {
+ this.tenantId = tenantId;
+ }
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/model/DecisionInstanceInput.java b/extension/java-client-operate/src/main/java/io/camunda/operate/model/DecisionInstanceInput.java
new file mode 100644
index 0000000..a5bf5e6
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/model/DecisionInstanceInput.java
@@ -0,0 +1,32 @@
+package io.camunda.operate.model;
+
+public class DecisionInstanceInput {
+
+ private String id;
+ private String name;
+ private String value;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/model/DecisionInstanceOutput.java b/extension/java-client-operate/src/main/java/io/camunda/operate/model/DecisionInstanceOutput.java
new file mode 100644
index 0000000..8939bf7
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/model/DecisionInstanceOutput.java
@@ -0,0 +1,50 @@
+package io.camunda.operate.model;
+
+public class DecisionInstanceOutput {
+
+ private String id;
+ private String name;
+ private String value;
+ private String ruleId;
+ private Long ruleIndex;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public String getRuleId() {
+ return ruleId;
+ }
+
+ public void setRuleId(String ruleId) {
+ this.ruleId = ruleId;
+ }
+
+ public Long getRuleIndex() {
+ return ruleIndex;
+ }
+
+ public void setRuleIndex(Long ruleIndex) {
+ this.ruleIndex = ruleIndex;
+ }
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/model/DecisionRequirements.java b/extension/java-client-operate/src/main/java/io/camunda/operate/model/DecisionRequirements.java
new file mode 100644
index 0000000..b465371
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/model/DecisionRequirements.java
@@ -0,0 +1,68 @@
+package io.camunda.operate.model;
+
+public class DecisionRequirements {
+
+ private String id;
+ private Long key;
+ private String decisionRequirements;
+ private String name;
+ private Long version;
+ private String resourceName;
+ private String tenantId;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public Long getKey() {
+ return key;
+ }
+
+ public void setKey(Long key) {
+ this.key = key;
+ }
+
+ public String getDecisionRequirements() {
+ return decisionRequirements;
+ }
+
+ public void setDecisionRequirements(String decisionRequirements) {
+ this.decisionRequirements = decisionRequirements;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Long getVersion() {
+ return version;
+ }
+
+ public void setVersion(Long version) {
+ this.version = version;
+ }
+
+ public String getResourceName() {
+ return resourceName;
+ }
+
+ public void setResourceName(String resourceName) {
+ this.resourceName = resourceName;
+ }
+
+ public String getTenantId() {
+ return tenantId;
+ }
+
+ public void setTenantId(String tenantId) {
+ this.tenantId = tenantId;
+ }
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/model/DecisionState.java b/extension/java-client-operate/src/main/java/io/camunda/operate/model/DecisionState.java
new file mode 100644
index 0000000..695eb59
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/model/DecisionState.java
@@ -0,0 +1,8 @@
+package io.camunda.operate.model;
+
+public enum DecisionState {
+ FAILED,
+ EVALUATED,
+ UNKNOWN,
+ UNSPECIFIED
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/model/DecisionType.java b/extension/java-client-operate/src/main/java/io/camunda/operate/model/DecisionType.java
new file mode 100644
index 0000000..df755b2
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/model/DecisionType.java
@@ -0,0 +1,8 @@
+package io.camunda.operate.model;
+
+public enum DecisionType {
+ DECISION_TABLE,
+ LITERAL_EXPRESSION,
+ UNSPECIFIED,
+ UNKNOWN
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/model/FlowNodeInstance.java b/extension/java-client-operate/src/main/java/io/camunda/operate/model/FlowNodeInstance.java
new file mode 100644
index 0000000..e3b5f2f
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/model/FlowNodeInstance.java
@@ -0,0 +1,114 @@
+package io.camunda.operate.model;
+
+import java.util.Date;
+
+public class FlowNodeInstance {
+ private Long key;
+ private Long processInstanceKey;
+ private Long processDefinitionKey;
+ private Date startDate;
+ private Date endDate;
+ private String flowNodeId;
+ private String flowNodeName;
+ private Long incidentKey;
+ private String type;
+ private FlowNodeInstanceState state;
+ private Boolean incident;
+ private String tenantId;
+
+ public Long getKey() {
+ return key;
+ }
+
+ public void setKey(Long key) {
+ this.key = key;
+ }
+
+ public Long getProcessInstanceKey() {
+ return processInstanceKey;
+ }
+
+ public void setProcessInstanceKey(Long processInstanceKey) {
+ this.processInstanceKey = processInstanceKey;
+ }
+
+ public Long getProcessDefinitionKey() {
+ return processDefinitionKey;
+ }
+
+ public void setProcessDefinitionKey(Long processDefinitionKey) {
+ this.processDefinitionKey = processDefinitionKey;
+ }
+
+ public Date getStartDate() {
+ return startDate;
+ }
+
+ public void setStartDate(Date startDate) {
+ this.startDate = startDate;
+ }
+
+ public Date getEndDate() {
+ return endDate;
+ }
+
+ public void setEndDate(Date endDate) {
+ this.endDate = endDate;
+ }
+
+ public String getFlowNodeId() {
+ return flowNodeId;
+ }
+
+ public void setFlowNodeId(String flowNodeId) {
+ this.flowNodeId = flowNodeId;
+ }
+
+ public String getFlowNodeName() {
+ return flowNodeName;
+ }
+
+ public void setFlowNodeName(String flowNodeName) {
+ this.flowNodeName = flowNodeName;
+ }
+
+ public Long getIncidentKey() {
+ return incidentKey;
+ }
+
+ public void setIncidentKey(Long incidentKey) {
+ this.incidentKey = incidentKey;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public FlowNodeInstanceState getState() {
+ return state;
+ }
+
+ public void setState(FlowNodeInstanceState state) {
+ this.state = state;
+ }
+
+ public Boolean getIncident() {
+ return incident;
+ }
+
+ public void setIncident(Boolean incident) {
+ this.incident = incident;
+ }
+
+ public String getTenantId() {
+ return tenantId;
+ }
+
+ public void setTenantId(String tenantId) {
+ this.tenantId = tenantId;
+ }
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/model/FlowNodeInstanceState.java b/extension/java-client-operate/src/main/java/io/camunda/operate/model/FlowNodeInstanceState.java
new file mode 100644
index 0000000..e72de8b
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/model/FlowNodeInstanceState.java
@@ -0,0 +1,8 @@
+package io.camunda.operate.model;
+
+public enum FlowNodeInstanceState {
+ ACTIVE,
+ INCIDENT,
+ COMPLETED,
+ TERMINATED;
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/model/FlowNodeStatistics.java b/extension/java-client-operate/src/main/java/io/camunda/operate/model/FlowNodeStatistics.java
new file mode 100644
index 0000000..7a89cfb
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/model/FlowNodeStatistics.java
@@ -0,0 +1,50 @@
+package io.camunda.operate.model;
+
+public class FlowNodeStatistics {
+
+ private String activityId;
+ private Long active;
+ private Long canceled;
+ private Long incidents;
+ private Long completed;
+
+ public String getActivityId() {
+ return activityId;
+ }
+
+ public void setActivityId(String activityId) {
+ this.activityId = activityId;
+ }
+
+ public Long getActive() {
+ return active;
+ }
+
+ public void setActive(Long active) {
+ this.active = active;
+ }
+
+ public Long getCanceled() {
+ return canceled;
+ }
+
+ public void setCanceled(Long canceled) {
+ this.canceled = canceled;
+ }
+
+ public Long getIncidents() {
+ return incidents;
+ }
+
+ public void setIncidents(Long incidents) {
+ this.incidents = incidents;
+ }
+
+ public Long getCompleted() {
+ return completed;
+ }
+
+ public void setCompleted(Long completed) {
+ this.completed = completed;
+ }
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/model/Incident.java b/extension/java-client-operate/src/main/java/io/camunda/operate/model/Incident.java
new file mode 100644
index 0000000..4ab7ee2
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/model/Incident.java
@@ -0,0 +1,78 @@
+package io.camunda.operate.model;
+
+import java.util.Date;
+
+public class Incident {
+ private Long key;
+ private Long processDefinitionKey;
+ private Long processInstanceKey;
+ private String type;
+ private String message;
+ private Date creationTime;
+ private String state;
+ private String tenantId;
+
+ public Long getKey() {
+ return key;
+ }
+
+ public void setKey(Long key) {
+ this.key = key;
+ }
+
+ public Long getProcessDefinitionKey() {
+ return processDefinitionKey;
+ }
+
+ public void setProcessDefinitionKey(Long processDefinitionKey) {
+ this.processDefinitionKey = processDefinitionKey;
+ }
+
+ public Long getProcessInstanceKey() {
+ return processInstanceKey;
+ }
+
+ public void setProcessInstanceKey(Long processInstanceKey) {
+ this.processInstanceKey = processInstanceKey;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public Date getCreationTime() {
+ return creationTime;
+ }
+
+ public void setCreationTime(Date creationTime) {
+ this.creationTime = creationTime;
+ }
+
+ public String getState() {
+ return state;
+ }
+
+ public void setState(String state) {
+ this.state = state;
+ }
+
+ public String getTenantId() {
+ return tenantId;
+ }
+
+ public void setTenantId(String tenantId) {
+ this.tenantId = tenantId;
+ }
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/model/ProcessDefinition.java b/extension/java-client-operate/src/main/java/io/camunda/operate/model/ProcessDefinition.java
new file mode 100644
index 0000000..00f264f
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/model/ProcessDefinition.java
@@ -0,0 +1,49 @@
+package io.camunda.operate.model;
+
+public class ProcessDefinition {
+ private Long key;
+ private String name;
+ private Long version;
+ private String bpmnProcessId;
+ private String tenantId;
+
+ public Long getKey() {
+ return key;
+ }
+
+ public void setKey(Long key) {
+ this.key = key;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Long getVersion() {
+ return version;
+ }
+
+ public void setVersion(Long version) {
+ this.version = version;
+ }
+
+ public String getBpmnProcessId() {
+ return bpmnProcessId;
+ }
+
+ public void setBpmnProcessId(String bpmnProcessId) {
+ this.bpmnProcessId = bpmnProcessId;
+ }
+
+ public String getTenantId() {
+ return tenantId;
+ }
+
+ public void setTenantId(String tenantId) {
+ this.tenantId = tenantId;
+ }
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/model/ProcessInstance.java b/extension/java-client-operate/src/main/java/io/camunda/operate/model/ProcessInstance.java
new file mode 100644
index 0000000..10842f6
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/model/ProcessInstance.java
@@ -0,0 +1,96 @@
+package io.camunda.operate.model;
+
+import java.util.Date;
+
+public class ProcessInstance {
+ private Long key;
+ private Long processVersion;
+ private String bpmnProcessId;
+ private Long parentKey;
+ private Long parentFlowNodeInstanceKey;
+ private Date startDate;
+ private Date endDate;
+ private ProcessInstanceState state;
+ private Long processDefinitionKey;
+ private String tenantId;
+
+ public Long getKey() {
+ return key;
+ }
+
+ public void setKey(Long key) {
+ this.key = key;
+ }
+
+ public Long getProcessVersion() {
+ return processVersion;
+ }
+
+ public void setProcessVersion(Long processVersion) {
+ this.processVersion = processVersion;
+ }
+
+ public String getBpmnProcessId() {
+ return bpmnProcessId;
+ }
+
+ public void setBpmnProcessId(String bpmnProcessId) {
+ this.bpmnProcessId = bpmnProcessId;
+ }
+
+ public Long getParentKey() {
+ return parentKey;
+ }
+
+ public void setParentKey(Long parentKey) {
+ this.parentKey = parentKey;
+ }
+
+ public Long getParentFlowNodeInstanceKey() {
+ return parentFlowNodeInstanceKey;
+ }
+
+ public void setParentFlowNodeInstanceKey(Long parentFlowNodeInstanceKey) {
+ this.parentFlowNodeInstanceKey = parentFlowNodeInstanceKey;
+ }
+
+ public Date getStartDate() {
+ return startDate;
+ }
+
+ public void setStartDate(Date startDate) {
+ this.startDate = startDate;
+ }
+
+ public Date getEndDate() {
+ return endDate;
+ }
+
+ public void setEndDate(Date endDate) {
+ this.endDate = endDate;
+ }
+
+ public ProcessInstanceState getState() {
+ return state;
+ }
+
+ public void setState(ProcessInstanceState state) {
+ this.state = state;
+ }
+
+ public Long getProcessDefinitionKey() {
+ return processDefinitionKey;
+ }
+
+ public void setProcessDefinitionKey(Long processDefinitionKey) {
+ this.processDefinitionKey = processDefinitionKey;
+ }
+
+ public String getTenantId() {
+ return tenantId;
+ }
+
+ public void setTenantId(String tenantId) {
+ this.tenantId = tenantId;
+ }
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/model/ProcessInstanceState.java b/extension/java-client-operate/src/main/java/io/camunda/operate/model/ProcessInstanceState.java
new file mode 100644
index 0000000..c93f267
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/model/ProcessInstanceState.java
@@ -0,0 +1,7 @@
+package io.camunda.operate.model;
+
+public enum ProcessInstanceState {
+ ACTIVE,
+ COMPLETED,
+ CANCELED;
+}
diff --git a/extension/java-client-operate/src/main/java/io/camunda/operate/model/SearchResult.java b/extension/java-client-operate/src/main/java/io/camunda/operate/model/SearchResult.java
new file mode 100644
index 0000000..e809671
--- /dev/null
+++ b/extension/java-client-operate/src/main/java/io/camunda/operate/model/SearchResult.java
@@ -0,0 +1,40 @@
+package io.camunda.operate.model;
+
+import java.util.Iterator;
+import java.util.List;
+
+public class SearchResult implements Iterable {
+ private List items;
+
+ private Integer total;
+
+ private List