# Microservice SDK for Java

Source: https://cumulocity.com/docs/microservice-sdk/java/
Sector: Application enablement & solutions
Release: Latest
Description: Develop and deploy microservices in Java, with a hello world tutorial, an example microservice, the client library, the SMS API, and monitoring support.

## Introduction

This section describes how to develop and deploy microservices on top of Cumulocity using the Microservice SDK for Java. It also contains a [Hello world tutorial](https://cumulocity.com/docs/microservice-sdk/java/#java-hello-world-tutorial) that you may follow to get the basics of developing microservices using Java. After you have successfully deployed your first microservice to Cumulocity, you may also continue with the section [Developing microservices](https://cumulocity.com/docs/microservice-sdk/java/#developing-microservice) to learn more about other features and capabilities of the SDK.


> **Info:**
> You can develop microservices for Cumulocity with any IDE and build tool that you prefer, but this section focuses on Maven and some troubleshooting for Eclipse.



These are some useful references to get started with the basic technologies underlying the SDK:

- The client libraries use the Cumulocity REST interfaces as underlying communication protocol as described in the section [Using the REST interface](https://cumulocity.com/docs/microservice-sdk/rest).
- All examples are open source and can be reviewed at the [Cumulocity microservices examples](https://github.com/Cumulocity-IoT//cumulocity-examples/tree/develop/microservices) repository.


> **Important:**
> You must have at least version 11 of the [Java Development Kit](http://www.oracle.com/technetwork/java/javase/downloads/index.html) installed in your development environment as older versions of the JRE and JDK are not updated with the latest security patches and are not recommended for use in production.



If you face any issue or need support, refer to [Cumulocity Tech Community](https://community.cumulocity.com/). You will find plenty of useful information there.

## Hello world tutorial for Java

Here you will learn how to create your first microservice that can be deployed on the [Cumulocity platform](https://cumulocity.com) using the Microservice SDK for Java.

Requests to a microservice can be authenticated using basic authentication or OAuth. Refer to [Authentication and authorization](https://cumulocity.com/docs/microservice-sdk/general-aspects/#authentication-and-authorization) for more details.

### Prerequisites {#prerequisites}

You must have Cumulocity credentials and a dedicated tenant. In case you do not have that yet, create an account on the [Cumulocity platform](https://cumulocity.com), for example by using a free trial. At this step you will be provided with a dedicated URL address for your tenant.

Verify that you have a recommended Java version installed together with Maven 3 or higher. It can be downloaded from the [Maven website](https://maven.apache.org/download.cgi).

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

You will also need a Docker installation, and in case that you don't have it yet, go to the [Docker website](https://www.docker.com/get-started) to download and install it.

Cumulocity microservices are Docker containers for the Linux/Amd64 platform. Other architectures are currently not supported. The Docker engine must provide the API version 1.38 or newer. This is the case for Docker versions 18.06 and later. Use the following command to verify your Docker installation:

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

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

### Developing the "Hello world" microservice {#developing-the-hello-world-microservice}

You can download the source code of this example from our [GitHub](https://github.com/Cumulocity-IoT//cumulocity-examples/tree/develop/hello-world-microservice) repository to build and run it using your favorite IDE, or follow the instructions below to guide you step-by-step for you to have a better understanding of the code and what must be done/configured.


> **Important:**
> This microservice example has been tested under macOS, Ubuntu and Windows 10 with Java 17, Maven 3.8.5, Docker 20.10.14; latest version of IntelliJ IDEA as IDE. Other tools or Java versions may require different configurations.



#### Create a Maven project {#create-a-maven-project}

Use the [Maven Archetype Plugin](https://maven.apache.org/archetype/maven-archetype-plugin/) to create a Java project from an existing Maven template. Use `c8y.example` as your groupId, `hello-microservice-java` as your artifactId, and set the version following the SemVer format as specified in [Microservice manifest](https://cumulocity.com/docs/microservice-sdk/general-aspects/#microservice-manifest).

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

This will create a folder *hello-microservice-java* in the current directory with a skeleton structure for your project.

#### Specify the properties {#specify-the-properties}

You will find the _pom.xml_ file inside the *hello-microservice-java* folder. Edit this file and add a `<properties>` element to set the `-source` and `-target` of the [Java Compiler](https://maven.apache.org/plugins/maven-compiler-plugin/examples/set-compiler-source-and-target.html) using version 17. This example uses [Spring Boot](https://spring.io/projects/spring-boot) to quickly build and create the application using the Spring Framework. Hence, also specify in the `<properties>` element the version to use as follows:

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

#### Add the microservice library {#add-the-microservice-library}

You must specify the version of the Cumulocity's microservice library to be used. This version is based on the platform version ("cumulocity"). To find the platform version, click the user icon at the top right and in the right drawer, under **Platform info**, download the platform details.  

Alternatively, you can retrieve the backend version with a GET request to <kbd><URL>/tenant/system/options/system/version</kbd>.

The response looks like this:

```json
{
    "category": "system",
    "value": "2025.0.17",
    "key": "version"
}
```

See also [Tenants](https://cumulocity.com/api/core//#tag/Tenants) in the Cumulocity OpenAPI Specification.

For yearly releases, you should use the release with matching major and minor versions and the latest maintenance version from the [Cumulocity maven repository](https://download.cumulocity.com/maven/repository/com/nsn/cumulocity/clients-java/microservice-dependencies/).

For the continous deployment version you should use the latest version from the [Cumulocity maven repository](https://download.cumulocity.com/maven/repository/com/nsn/cumulocity/clients-java/microservice-dependencies/).

In the `<properties>` element specified above, add a child element `<c8y.version>` with the backend version of your tenant. Also add a `<microservice.name>` child element to name your microservice application.

```xml
    <c8y.version>2025.0.5</c8y.version>
    <microservice.name>hello-microservice-java</microservice.name>
```


> **Important:**
> When naming your microservice application use only lower-case letters, digits and dashes. The maximum length for the name is 23 characters.



#### Add repositories and dependencies {#add-repositories-and-dependencies}

Your _pom.xml_ file must have `<repository>` and `<pluginRepository>` elements to point to the Cumulocity Maven repository which stores the client libraries.

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

Also add a dependency for the Microservice SDK library inside the `<dependencies>` node.

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

Add a `<dependencyManagement>` element to automatically manage the required artifacts needed for your microservice application.

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

#### Configure the build plugins {#configure-the-build-plugins}

Your microservice application must be packed as a Docker image in a ZIP file including all the required dependencies. To achieve that, include in your _pom.xml_ file build plugins as follows:

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

The name of the generated ZIP file is specified in the image element as `<image>${microservice.name}</image>`. It takes the name from the previously defined property `microservice.name`, which in this case is *hello-microservice-java*.


#### Create a Java application {#create-a-java-application}

Edit the _App.java_ file located in the folder */src/main/java/c8y/example* with the following content:

```java
package c8y.example;

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

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

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

The code uses four annotations; three are part of the Spring Framework and one of the Cumulocity Microservice SDK. The `@RestController` annotation marks the class as a controller where every method returns a domain object instead of a view. The `@RequestMapping` annotation ensures that HTTP requests to the <kbd>/service/&lt;microservice-name>/hello</kbd> endpoint are mapped to the `greeting()` method. `@RequestParam` binds the value of the query string parameter <kbd>name</kbd> into the `you` parameter of the `greeting()` method. Refer to the [Spring Guides](https://spring.io/guides) for more details about building RESTful Web Services using the Spring Framework.

Employing the `@MicroserviceApplication` annotation is a simple way to add the required behavior for Cumulocity microservices including:

* Security
* Subscription
* Health check endpoint at <kbd>/service/&lt;microservice-name>/health</kbd>
* Context
* Settings
* Internal platform API
* Spring Boot application

#### Configure the microservice application {#configure-the-microservice-application}

Create the directory _src/main/resources_ to contain an _application.properties_ file specifiying the name of the microservice application and the server port:

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

Create the directory _src/main/configuration_ to contain a _cumulocity.json_ file. This is the [manifest](https://cumulocity.com/docs/microservice-sdk/general-aspects/#microservice-manifest) file and it is required to deploy the microservice in the Cumulocity platform.

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

#### Build the microservice application {#build-the-microservice-application}

In a terminal, navigate to the folder where your _pom.xml_ is located and execute the following Maven command:

```shell
$ mvn clean install
```

After a successful build, you will find a ZIP file inside the _target_ directory.

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

### Deploying the "Hello world" microservice {#deploying-the-hello-world-microservice}

To deploy your microservice on the Cumulocity platform you need:

* A valid tenant, a user and a password in order to access Cumulocity.
* The ZIP file built with Maven on the previous steps.


> **Important:**
> The **Microservice hosting** feature must be activated on your tenant, otherwise your request will return an error message like "security/Forbidden, access is denied". This feature is not assigned to tenants by default, so trial accounts won't have it. Contact [product support](https://cumulocity.com/docs/additional-resources/contacting-support/) so that we can assist you with the activation. Note that this is a paid feature.



In the Administration application, navigate to **Ecosystem** > **Microservices**, and click **Add microservice**.

Upload the ZIP file for your microservice application and click **Subscribe** to subscribe the microservice to your tenant.

Once the ZIP file has been uploaded successfully, you will see a new microservice application created.

#### Test the deployed microservice {#test-the-deployed-microservice}

Employing your tenant credentials, you can test the microservice on any web browser using the URL as follows:

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

You can also use third-party applications or commands to make a GET request to your microservice endpoint. To do so, you need:

* A valid tenant, a user and a password in order to access Cumulocity.
* A basic authorization header `"Authorization: Basic <Base64(<tenantID>/<username>:<password>)>"`.

For instance, if your tenant ID, username and password are **t0071234**, **testuser** and **secret123** respectively, you can get the Base64 string with the following command:

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

and your authorization header would look like `Authorization: Basic dDAwNzEyMzQvdGVzdHVzZXI6c2VjcmV0MTIz`. Employing the cURL command you can test your microservice as follows:

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

Most tools should already support the Cumulocity Authorization header out of the box. Simply use `<tenantId>/<username>` as username and `<password>` as password. In modern versions, for example, the above cURL command can also look like below, and the header will be generated automatically:

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


### Running the microservice locally {#running-the-microservice-locally}

You can run the Docker container locally in order to test the REST calls from the microservice to Cumulocity.

To run a microservice which uses the Cumulocity API locally, you need:

* A valid tenant, a user and a password in order to access Cumulocity.
* An authorization header as "Basic &lt;Base64(&lt;tenantID>/&lt;username>:&lt;password>)>".

#### Create the application {#create-the-application}

If the application does not exist, create a new application on the Cumulocity platform employing a POST request.

```http
POST <URL>/application/applications

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

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

You must replace the values `<URL>` with the URL of your Cumulocity tenant (domain), `<AUTHORIZATION>` is Basic with a Base64 encoded string, and for `<APPLICATION_NAME>` use the desired name for your microservice application and its `key` name.


> **Important:**
> When naming your microservice application use only lower-case letters, digits and dashes. The maximum length for the name is 23 characters.



The cURL command can be used to create the application with a POST request:

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

In case of errors, such as invalid names, you will get the details printed in the console. When the application is created successfully, you will get a response in JSON format similar to the following example:

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

In the Administration application, navigate to **Ecosystem** > **Microservices**. There you will see the created microservice.

#### Acquire the microservice bootstrap user {#acquire-the-microservice-bootstrap-user}

You will need the bootstrap user credentials in order to run the microservice locally. Get the details of your bootstrap user with a GET request.

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

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


> **Info:**
> Besides the cURL command, you can also employ a graphical interface such as Postman.



The response looks like this:

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

#### Run the Docker container {#run-the-docker-container}

The Docker image was built using your local Docker repository during the [Maven build](https://cumulocity.com/docs/microservice-sdk/java/#build-the-microservice-application). **Note, that by default the image is deleted to keep your registry clean during development**. You can change this by adding the property `microservice.package.deleteImage=false` to the maven command or *pom.xml*.

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

You can list all the Docker images available with the following command:

```shell
$ docker images
```

It yields an output similar to this:

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


Get your IMAGE ID and TAG from the list. While not strictly a means of identifying a container, you can specify a version of an image (TAG) you would like to run the container with. Run the Docker container for the microservice:

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

`-p 8082:80` will expose your port 80 to a port on your host system, for example, 8082.

If your Docker image has run successfully, you shall see the output on the console similar to the one below.

```plain
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v3.3.5)

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

#### Subscribe to the microservice {#subscribe-to-the-microservice}

In the Administration application, navigate to **Ecosystem** > **Microservices**. Locate your microservice application and click it to open its details. On the top right, click **Subscribe**.

At this point, you may open your favorite browser and test your microservice at <http://localhost:8082/hello>. Enter your bootstrap user credentials using &lt;tenant>/&lt;username> and your password.

You may also use the name parameter, for example, <http://localhost:8082/hello?name=Neo>.

### Improving the microservice {#improving-the-microservice}

Now that you have done your first steps, check out the section [Developing microservices](https://cumulocity.com/docs/microservice-sdk/java#developing-microservice) to find out what else can be implemented. Review also the [Java example](https://cumulocity.com/docs/microservice-sdk/java/#create-a-java-application) in this guide to learn using more features of the microservice SDK and REST API by employing third-party services.

## IP-tracker microservice

> **Important:**
> Visit our [Hello world tutorial for Java](https://cumulocity.com/docs/microservice-sdk/java/#java-hello-world-tutorial) and follow the setup steps there before starting the IP-tracker microservice tutorial. The basic configuration steps are not explained here.



### Developing the IP-tracker microservice {#developing-the-ip-tracker-microservice}

This microservice application creates a warning alarm message (for demonstration purposes) and it exposes endpoints to:

- Verify that the microservice is up and running.
- Pass a parameter to the platform and return a formatted string.
- Get some of the environment variables and the microservice service settings.
- Track a user's approximate location and store it in the platform.
- Get the tracked IPs and locations.

It also uses the Cumulocity UI to display the tracked locations on a map.

#### Update the Project Object Model {#update-the-project-object-model}

Assuming that you have the base code presented in our [Hello world tutorial for Java](https://cumulocity.com/docs/microservice-sdk/java/#java-hello-world-tutorial), edit your *pom.xml* file changing the `artifactId` and `microservice.name` of your microservice to `iptracker-microservice`.
Also add a child element `<java.version>` to the `<properties>` element to specify the Java version you want to use.
Your *pom.xml* file should contain a snippet similar to:

```xml
<name>iptracker-microservice</name>
<artifactId>iptracker-microservice</artifactId>
<properties>
    <java.version>17</java.version>
    <maven.compiler.source>${java.version}</maven.compiler.source>
    <maven.compiler.target>${java.version}</maven.compiler.target>
    <spring-boot-dependencies.version>3.3.5</spring-boot-dependencies.version>
    <c8y.version>1016.0.117</c8y.version>
    <microservice.name>iptracker-microservice</microservice.name>
</properties>
```


> **Info:**
> This example was implemented using Java 17 and Spring Boot 2. You may [install the JDK 17](https://www.oracle.com/technetwork/java/javase/downloads/index.html) or adjust this example to the version you already have, for example, JDK 11. Note that since Java 13 some API methods were removed or deprecated, so you may get some warning messages during build time but they won't affect the microservice application.



Finally, add the following dependency:

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

#### Update the application manifest {#update-the-application-manifest}

In your _cumulocity.json_ file:

1. Add the required permissions (using the `requiredRoles` field) to be able to create events and alarms.
2. Add the readiness and liveness probes.
3. Add two keys for the microservice settings: `"ipstack.key"` and `"tracker.id"`.
4. Set the isolation level to `"PER_TENANT"`. This means that there will be a separate instance for each tenant. For more details see the Settings section in [Microservice manifest](https://cumulocity.com/docs/microservice-sdk/general-aspects/#microservice-manifest).

Your manifest file should look similar to this:

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

### Creating a managed object {#creating-a-managed-object}

An alarm must be associated with a source and it requires an ID.
Hence, you must [create a managed object](https://cumulocity.com/api/core/#operation/postManagedObjectCollectionResource) to be your source and use its ID in your microservice application.
The same managed object will track the locations when the microservice gets accessed on a particular endpoint.

First, get your current location (latitude, longitude) using any free service.

Create a managed object as a device named "Microservice tracker" via POST request as follows:

```http
POST <URL>/inventory/managedObjects

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

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

You will get the ID of your managed object in the response.
Assign this ID to the `"tracker.id"` key in your _cumulocity.json_ file.

On the Cumulocity platform, navigate to **Devices** > **All devices** in the Device Management application to verify that your device has been created and its location is displayed on the map.

![Microservice tracking](https://cumulocity.com/docs/images/microservices-sdk/ms-tracking-newdevice.png)

### Getting the client's location {#getting-the-clients-location}

The microservice will get the approximate location based on the client's IP.
To achieve this, it uses the free service [ipstack](https://ipstack.com) and you must get a free API key.
Once you have it, assign it to the `"ipstack.key"` key in your _cumulocity.json_ file.

A GET request to the ipstack API using your key will return a location object. Therefore, you must create a new file named _Location.java_ in the same directory of your _App.java_ with the following content:

```java
package c8y.example;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@JsonIgnoreProperties(ignoreUnknown = true)
public class Location {

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

    public String getLongitude() {
        return longitude;
    }

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

    public String getLatitude() {
        return latitude;
    }

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

    public String getCountry_code() {
        return country_code;
    }

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

    public String getCity() {
        return city;
    }

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

### Updating the application {#updating-the-application}

Modify your _App.java_ file and:

1. Run the microservice as a Spring application.
2. Add a post-construct init method to get a subset of the environment variables and the microservice settings.
3. Add an event listener to the microservice subscription. Each time a tenant subscribes to the microservice, an alarm will be created.
4. Define a method to create LocationUpdate events based on the client's IP.
4. Add the application endpoints.

Your code should look similar to:

```java
package c8y.example;

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

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

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

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

import net.minidev.json.JSONObject;

@MicroserviceApplication
@RestController
public class App {

    @Autowired
    private MicroserviceSettingsService settingsService;

    @Autowired
    private ContextService<MicroserviceCredentials> contextService;

    @Autowired
    private Platform platform;

    private Map<String, String> c8yEnv;

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


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

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

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


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

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

            platform.getAlarmApi().create(alarm);

            return true;
        });
    }


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

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

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

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

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

        return event;
    }


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

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

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

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

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

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

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

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

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

            locations.add(map);
        });

        return locations;
    }
}
```

### Building and deploying the application {#building-and-deploying-the-application}

Use the command `mvn clean install` and follow the same steps of the [Hello world tutorial for Java](https://cumulocity.com/docs/microservice-sdk/java/#java-hello-world-tutorial) to deploy your microservice.
You may also employ the cURL command to deploy the microservice.

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

### Testing the application {#testing-the-application}

You can test any endpoint of your application using the command line or a web browser.
For example, a GET request to <kbd>location/track</kbd> will obtain the client's IP from the request header and use the `createLocationUpdateEvent` method to get the approximate location.
The response will be similar to:

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

Using the endpoint <kbd>location/locations</kbd> will return five stored events by default.
You can use the `max` parameter to specify a higher number.

In the Device Management application, navigate to **Devices** > **All devices** and locate your microservice tracker.
Under **Tracking** you will see a map with the tracked locations.
You can also develop your own web application and customize a "Map" widget.
For details, refer to the [Web developer codex](https://cumulocity.com/codex/).

![Microservice tracking](https://cumulocity.com/docs/images/microservices-sdk/ms-tracking-map.png)

#### Run the Docker container {#run-the-docker-container}

The Docker image is built and added to the local Docker repository during the [Maven build](https://cumulocity.com/docs/microservice-sdk/java/#build-the-microservice-application) if the following property is set `microservice.package.deleteImage=false`.
As you have learned in our [Hello world tutorial for Java](https://cumulocity.com/docs/microservice-sdk/java/#java-hello-world-tutorial), you can [run the Docker container](https://cumulocity.com/docs/microservice-sdk/java/#run-the-docker-container) locally.
Note that in this case the isolation was changed to `PER_TENANT`.
You can also use your Docker image name and tag to run it as follows:

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

If your Docker image has run successfully, you can test the microservice on any web browser.
For instance, using <http://localhost:8082/location/locations> will return all the tracked locations.

### Source code {#source-code}

The code of our [iptracker-microservice](https://github.com/Cumulocity-IoT//cumulocity-examples/tree/develop/microservices/iptracker-microservice) can be found in our public GitHub repositories.

## Developing microservices

The Cumulocity Microservice SDK is an open source toolkit for building microservices using the popular [Spring Boot](https://spring.io/projects/spring-boot) framework and the Cumulocity [platform APIs](https://cumulocity.com/api/core). It accelerates development by offering preconfigured annotations, built-in services, and a Maven plugin for creating Docker containers and Cumulocity applications. The SDK source code is available on GitHub in the
[cumulocity-clients-java](https://github.com/Cumulocity-IoT/cumulocity-clients-java) repository.

In this section, you will learn how to:
* Use SDK annotations to simplify the setup.
* Access platform APIs via dependency injection.
* Authenticate towards the platform APIs.
* Secure access to your microservice APIs.
* Handle subscriptions and multi-tenant access.
* Configure your microservice.
* Upload and run your microservice.
* Manage and monitor your microservice.
* Set up external or legacy deployments.

### Using annotations {#annotations}

As shown in the ["Hello world" tutorial](https://cumulocity.com/docs/microservice-sdk/java/#create-a-java-application), the easiest way to enable default microservice behavior is to annotate your main class with `@MicroserviceApplication`. This composite annotation includes:

| Annotation                             | Description                                                                                      |
| -------------------------------------- | ------------------------------------------------------------------------------------------------ |
| @SpringBootApplication                 | Enables Spring Boot’s auto-configuration                                                         |
| @EnableContextSupport                  | Allows use of @UserScope and @TenantScope for method-level context switching                     |
| @EnableHealthIndicator                 | Exposes a standard health endpoint for platform monitoring                                       |
| @EnableMicroserviceSecurity            | Enables security by verifying users and roles against the platform                               |
| @EnableMicroserviceSubscription        | Manages subscriptions, metadata updates, and listens to tenant subscription changes              |
| @EnableMicroservicePlatformInternalApi | Injects platform API services into the Spring context                                            |
| @EnableTenantOptionSettings            | Allows configuration through tenant options and supports overriding default properties via files |

### Accessing platform APIs {#acessing-platform-api}

The Cumulocity Microservice SDK includes a set of Java APIs that are automatically injected into the Spring context and allow you to operate the REST APIs from Java in a simple manner. The REST APIs correspond to Java APIs as follows:

* Alarm - AlarmApi
* AuditRecord - AuditRecordApi
* Operation - DeviceControlApi
* Event - EventApi
* EventBinary - EventBinaryApi
* ExternalID - IdentityApi
* Binary - BinariesApi
* ManagedObject - InventoryApi
* Measurement - MeasurementApi
* DeviceCredentials - DeviceCredentialsApi
* User - UserApi
* TenantOption - TenantOptionApi
* SystemOption - SystemOptionApi
* Token - TokenApi
* Notification - NotificationSubscriptionApi

Each API supports standard "CRUD" operations (create, read, update, delete). For example, the `AlarmApi` interface provides:

```java
// Create
AlarmRepresentation create(AlarmRepresentation alarm) throws SDKException;
Future createAsync(AlarmRepresentation alarm) throws SDKException;
// Read
AlarmRepresentation getAlarm(GId gid) throws SDKException;
AlarmCollection getAlarms() throws SDKException;
AlarmCollection getAlarmsByFilter(AlarmFilter filter) throws SDKException;
// Update
AlarmRepresentation updateAlarm(AlarmRepresentation alarm) throws SDKException;
// Delete
void deleteAlarmsByFilter(AlarmFilter filter) throws IllegalArgumentException, SDKException;
```

This is an example of retrieving all devices registered in all subscribed tenants:

```java
@Autowired
MicroserviceSubscriptionsService subscriptionsService;

@Autowired
InventoryApi inventoryApi;

public List<ManagedObjectRepresentation> getAllDevicesTenants() {
  List<ManagedObjectRepresentation> managedObjectsList = new ArrayList<>();
  InventoryFilter filter = new InventoryFilter().byFragmentType(IsDevice.class);
  subscriptionsService.runForEachTenant( () -> {
    inventoryApi.getManagedObjectsByFilter(filter).get().allPages().forEach(mor -> {
      managedObjectsList.add(mor);
    });
  });
  return managedObjectsList;
}
```

More details on using the APIs are available in the [Client library](https://cumulocity.com/docs/microservice-sdk/java/#client-library) section.

### Authenticating and authorizing towards the platform {#authenticating-and-authorizing-towards-the-platform}

API requests in Cumulocity microservices can run in two authentication scopes:
 * Tenant scope – uses the service user's credentials.
 * User scope – uses the credentials of the authenticated user who triggered the request.

Each microservice has a service user whose roles are defined in the `cumulocity.json` manifest. 

**API requests must be executed in one of these contexts: tenant scope or user scope**.

To execute API requests in the tenant scope in any thread of the application, the `MicroserviceSubscriptionsService` can be used to wrap the request like in the next example:

```java
@Autowired
MicroserviceSubscriptionsService subscriptionsService;

@Autowired
private EventApi eventApi;

public List<EventRepresentation> getAllEvents() {
  List<EventRepresentation> eventsList = new ArrayList<>();
  subscriptionsService.runForEachTenant( () -> {
    eventApi.getEvents().get().getEvents().forEach(event -> {
      eventsList.add(event);
    });
  });
  return eventsList;
}
```

Tenant scope is the default context in:
* Classes annotated with `@RestController`
* Methods annotated with `@EventListener`

In these cases, the `MicroserviceSubscriptionsService` is not needed.
The API service beans like `eventApi` or `inventoryApi` are executed in the tenant scope by default:

#### @EventListener annotation and scope

Example for `@EventListener` annotation and tenant scope:

```java
@Autowired
InventoryApi tenantInventoryApi;

@EventListener
public void initialize(MicroserviceSubscriptionAddedEvent event) {
   String tenant = event.getCredentials().getTenant();
   log.info("Tenant {} - Microservice subscribed", tenant);
   tenantInventoryApi.getManagedObjects().get().allPages();
}
```

#### @RestController annotation and scope

The next examples show the execution of API calls in tenant scope and user scope for @RestController classes.

Example of an API request in the tenant scope:

```java
@RestController
@RequestMapping("/devices")
public class DeviceController {
    
    @Autowired
    InventoryApi inventoryApi;

    @GetMapping(path = "/devicesTenantScope", produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<List<ManagedObjectRepresentation>> getAllDevicesTenantScope() {
        List<ManagedObjectRepresentation> managedObjectsList = new ArrayList<>();
        InventoryFilter filter = new InventoryFilter().byFragmentType(IsDevice.class);
        inventoryApi.getManagedObjectsByFilter(filter).get().allPages().forEach(mor -> {
            managedObjectsList.add(mor);
        });
        return new ResponseEntity<>(managedObjectsList, HttpStatus.OK);
    }
  
}
```

To execute an API request in the user scope, the API service bean must be injected with a qualifier annotation like `@Qualifier("userInventoryApi")`:

```java
@RestController
@RequestMapping("/devices")
public class DeviceController {

    @Autowired
    @Qualifier("userInventoryApi")
    InventoryApi userInventoryApi;

    @GetMapping(path = "/devicesUserScope", produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<List<ManagedObjectRepresentation>> getAllDevicesUserScope() {
      List<ManagedObjectRepresentation> managedObjectsList = new ArrayList<>();
      InventoryFilter filter = new InventoryFilter().byFragmentType(IsDevice.class);
      userInventoryApi.getManagedObjectsByFilter(filter).get().allPages().forEach(mor -> {
        managedObjectsList.add(mor);
      });
      return new ResponseEntity<>(managedObjectsList, HttpStatus.OK);
    }
  
}
```

#### API service beans

The Microservice SDK provides both tenant-scope and user-scope beans with the following names:

| Bean names in tenant scope                                     | Qualifier for user scope        |
| -------------------------------------------------------------- | ------------------------------- |
| inventoryApi, tenantInventoryApi                               | userInventoryApi                |
| identityApi, tenantIdentityApi                                 | userIdentityApi                 |
| measurementApi, tenantMeasurementApi                           | userMeasurementApi              |
| deviceControlApi, tenantDeviceControlApi                       | userDeviceControlApi            |
| alarmApi, tenantAlarmApi                                       | userAlarmApi                    |
| eventApi, tenantEventApi                                       | userEventApi                    |
| eventBinaryApi, tenantEventBinaryApi                           | userEventBinaryApi              |
| auditRecordApi, tenantAuditRecordApi                           | userAuditRecordApi              |
| deviceCredentialsApi, tenantDeviceCredentialsApi               | userDeviceCredentialsApi        |
| binariesApi, tenantBinariesApi                                 | userBinariesApi                 |
| userApi, tenantUserApi                                         | userUserApi                     |
| tenantOptionApi, tenantTenantOptionApi                         | userTenantOptionApi             |
| systemOptionApi, tenantSystemOptionApi                         | userSystemOptionApi             |
| tokenApi, tenantTokenApi                                       | userTokenApi                    |
| notificationSubscriptionApi, tenantNotificationSubscriptionApi | userNotificationSubscriptionApi |

Various examples demonstrating use cases of the platform API can be found in the GitHub repositories
[Cumulocity microservice templates](https://github.com/Cumulocity-IoT/cumulocity-microservice-templates) and
[Cumulocity examples](https://github.com/Cumulocity-IoT/cumulocity-examples).

### Securing your microservice {#microservice-security}

The `@EnableMicroserviceSecurity` annotation sets up the standard security configuration for microservices.
It enforces basic authentication or other standard authentication mechanisms (refer to [Authentication and authorization](https://cumulocity.com/docs/microservice-sdk/general-aspects/#authentication-and-authorization))
for all endpoints -- except for the health check endpoint configured via `@EnableHealthIndicator`.

You can configure security for your endpoints using standard Spring Security annotations. For example, you can restrict access based on platform roles using `@PreAuthorize("hasRole('ROLE_A')")`.

### Microservice subscription {#microservice-subscription}

The microservice subscription module handles two core functions:

* Registration
* Tenant subscription event listening

The default behavior for the package is self-registration, which means that after you run the application it will try to register and use the generated credentials for the communication with the platform. The self-registration is required to correctly deploy the microservice on the platform.

The other way to register an application to the platform is to do it manually. This can be done by creating a new application on the platform with the same application name and providing the following properties into the microservice:

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

To create an application and acquire credentials, refer to [Creating applications](https://cumulocity.com/docs/microservice-sdk/rest/#creating-applications) and [Acquiring microservice credentials](https://cumulocity.com/docs/microservice-sdk/rest#acquiring-microservice-credentials) in the **Using the REST interface** section.

The subscription package provides means to monitor and it acts upon changes in tenant subscriptions to a microservice. To add a custom behavior, a developer can add an event listener for `MicroserviceSubscriptionAddedEvent` and `MicroserviceSubscriptionRemovedEvent` as the following example:

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

On application startup, the `MicroserviceSubscriptionAddedEvent` is triggered for all subscribed tenants.

### Configuration files {#configuration-files}

The *application.properties* file used by the hosted deployment must be located in *src/main/resources/*.

The following properties are used by a microservice:

#### General properties {#general-properties}

| Property                   | Description                                                                                            |
| -------------------------- | ------------------------------------------------------------------------------------------------------ |
| application.name           | The name of the microservice application.                                                              |
| C8Y.bootstrap.register     | Indicates if a microservice should follow the self-registration process. True by default.              |
| C8Y.baseURL                | Address of the platform. Provided by the deployment process.                                           |
| C8Y.baseURL.mqtt           | Address of the MQTT service. Provided by the platform.                                                 |
| C8Y.bootstrap.tenant       | The tenant ID, owner of the microservice.                                                              |
| C8Y.bootstrap.user         | Username used by a microservice or by the microservice registration process.                           |
| C8Y.bootstrap.password     | Password used by a microservice or by the microservice registration process.                           |
| C8Y.bootstrap.delay        | Subscription refresh delay (milliseconds).                                                             |
| C8Y.bootstrap.initialDelay | Initial subscription delay (milliseconds).                                                             |
| C8Y.microservice.isolation | Microservice isolation. Only PER_TENANT or MULTI_TENANT values are available. MULTI_TENANT by default. |

#### HTTP client configuration properties {#http-client-configuration-properties}

| Property                         | Description                                                    | Default value |
| -------------------------------- | -------------------------------------------------------------- | ------------- |
| C8Y.httpClient.httpReadTimeout   | HTTP read timeout (milliseconds).                              | 180000        |
| C8Y.httpClient.pool.enabled      | HTTP connection pooling enabled.                               | true          |
| C8Y.httpClient.pool.perHost      | Max connections per host if the connection pooling is enabled. | 50            |
| C8Y.httpClient.pool.max          | Max total connections if the connection pooling is enabled.    | 100           |
| C8Y.httpClient.pool.awaitTimeout | Connection manager timeout (milliseconds).                     | 10000         |


> **Info:**
> No changes should be made unless the request/connection timeouts or HTTP client related exceptions are being experienced for the requests to the microservice where the network environment is fully understood.



### Microservice settings {#microservice-settings}

The microservice settings module provides two features:

* Configure a microservice by defining tenant options
* Override existing properties - Tenant options can override default values from properties files

By default the microservice loads the tenant options for the category specified by the microservice context path.
The custom settings category can be specified by the manifest parameter: `settingsCategory`.
Note that the defined tenant option category must be unique within the tenant.
When neither settings category nor context path is provided in the microservice manifest, the application name is used.


> **Info:**
> Once the microservice is deployed it is not possible to change the category during application upgrade.



Options can be configured for the application owner or the subscriber. The subscriber can override the owner's option value only when such option is defined as editable.

Settings are lazy cached for 10 minutes, so when they were accessed previously, the user must wait the remaining time to see the change being applied.
When the access attempt occurs to fetch settings without the tenant context being specified, the application owner is used to complete the request.


> **Info:**
> For security reasons, the functionality is not available when running the microservice in legacy mode, that is, local development or RPM installation.



Tenant option settings can be accessed in two ways:

Using Environment:

```java
@Autowired
private Environment environment;

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

Using settings service:

```java
@Autowired
private MicroserviceSettingsService settingsService;

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

Settings can be encrypted by using the *credentials.* prefix for the tenant option key. They will be decrypted and become available within the microservice environment.

Defining tenant options for a microservice with the same key as it was defined in the configuration files, such as *.properties* or the manifest file, will override the particular property.

For instance, there is a property defined in the _application.properties_ file of the microservice hello-world with context path _helloworld_:

```properties
access.timeout=25
```

Now the microservice owner can override it by defining the following setting in the _cumulocity.json_ manifest file:

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

Because the `access.timeout` setting is defined as editable, the subscriber can override it by creating an own tenant option via REST API:

```http
POST <URL>/tenant/options

BODY:
  {
    "category": "helloworld",
    "key": "access.timeout",
    "value": "40"
  }
```


> **Info:**
> You cannot override a property injected by Spring `@Value("${property.name}")`.



### Logging {#logging}

The standard output should be used for hosted deployments.
For more details on how to use your own log configuration file refer to [Logging](https://cumulocity.com/docs/microservice-sdk/java/#legacy-logging).

### Maven plugin {#maven-plugin}

The package module provides a Maven plugin to prepare a ZIP file required by the microservice deployment. The build requires an executable JAR file. To create one, a developer can use `spring-boot-maven-plugin`. An example with minimum configuration is presented below:

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

#### Package goal {#package-goal}

The package plugin is responsible for the creation of a Docker container, RPM file and for creating a ZIP file that can be deployed on the platform.
It can be configured with the following parameters:  
(If a single xml tag is specified as parameter in the following list, use embracing xml tags like \<tag>...\</tag> to set those parameters. "..." must be replaced by the respective value of the corresponding data type.)

| Parameter<br>short form<br>for pom.xml entries<br> in \<configuration> section |                                      Data type                                       |                                               Parmameter<br>command<br>line name                                                |                                             Default value                                              | Description                                                                                                                                                                                                                                    |
|:------------------------------------------------------------------------------:|:------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------------------------------------:|:------------------------------------------------------------------------------------------------------:|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|                                  \<arguments>                                  |                                    List\<String>                                     |                                            &#8209;Dagent-package.arguments=[...,]...                                            |                                                   ""                                                   | General command line arguments for jar startup.<br>Specify with "," separated arguments.                                                                                                                                                       |
|                                \<containerSkip>                                |                                       Boolean                                        |                                            &#8209;Dskip.agent.package.container=...                                             |                                                 false                                                  | Skip the container packaging                                                                                                                                                                                                                   |
|                                 \<description>                                 |                                        String                                        |                                                 &#8209;Dpackage.description=...                                                 |                                         ${project.description}                                         | Microservice description                                                                                                                                                                                                                       |
|                             \<dockerBuildTimeout>                              |                                         Int                                          |                                       &#8209;Dmicroservice.package.dockerBuildTimeout=...                                       |                                                  360                                                   | Timeout value in seconds for the generation of a docker build                                                                                                                                                                                  |
|                                  \<encoding>                                   |                                        String                                        |                                            &#8209;Dproject.build.sourceEncoding=...                                             |                                                 UTF-8                                                  | Define String encoding                                                                                                                                                                                                                         |
|           \<heap><br>\<min>...\</min><br>\<max>...\<max><br>\</heap>           | min, max : \<Int>m<br>(m:megabytes; for available units refer to Java documentation) | &#8209;&#8209;<br>[(Setting complex<br>command line parameter)](https://cumulocity.com/docs/microservice-sdk/java/#package-goal-command-line-complex-data) |                                        min = 128m<br>max = 384m                                        | \<heap> parameter results to &#8209;Xms\<min> &#8209;Xmx\<max> Java runtime arguments for the microservice startup.                                                                                                                                        |
|                                   \<jvmArgs>                                   |                                    List\<String>                                     |                                             &#8209;Dagent-package.jvmArgs=[...,]...                                             | "&#8209;XX:+UseG1GC<br>&#8209;XX:+UseStringDeduplication<br>&#8209;XX:MinHeapFreeRatio=25<br>&#8209;XX:MaxHeapFreeRatio=75" | Java runtime arguments for the microservice startup. Specify with "," separated arguments. Default values will be overwritten if other options are provided.                                                                                   |
|                                \<manifestFile>                                 |                                        String                                        |                                                    &#8209;DmanifestFile=...                                                     |                        "$\{basedir}/src/main/<br>configuration/cumulocity.json"                        | Path to the microservice manifest file location                                                                                                                                                                                                |
|      \<metaspace><br>\<min>...\</min><br>\<max>...\<max><br>\</metaspace>      |       min, max : \<Int>m<br>(m:megabytes; for available units refer to Java documentation)        |  &#8209;&#8209;<br>[(Setting complex<br>command line parameter)](https://cumulocity.com/docs/microservice-sdk/java/#package-goal-command-line-complex-data)   |                                        min = 64m<br>max = 128m                                         | \<metaspace> parameter is combined<br>with \<perm> parameter values if available which results in &#8209;XX:MetaspaceSize=\<min> &#8209;XX:MaxMetaspaceSize=\<max> Java runtime arguments for the microservice startup.                        |
|                                    \<name>                                     |                                        String                                        |                                                    &#8209;Dpackage.name=...                                                     |                                         ${project.artifactId}                                          | Microservice name                                                                                                                                                                                                                              |
|           \<perm><br>\<min>...\</min><br>\<max>...\<max><br>\</perm>           |       min, max : \<Int>m<br>(m:megabytes; for available units refer to Java documentation)        |  &#8209;&#8209;<br>[(Setting complex<br>command line parameter)](https://cumulocity.com/docs/microservice-sdk/java/#package-goal-command-line-complex-data)   |                                        min = 64m<br>max = 128m                                         | \<perm> parameter is combined with \<metaspace> parameter values for compatibility reasons if available which results in &#8209;XX:MetaspaceSize=\<min> &#8209;XX:MaxMetaspaceSize=\<max> Java runtime arguments for the microservice startup. |
|                                   \<rpmSkip>                                   |                                       Boolean                                        |                                               &#8209;Dskip.agent.package.rpm=...                                                |                                                  true                                                  | Skip the rpm packaging                                                                                                                                                                                                                         |
|                                    \<skip>                                     |                                       Boolean                                        |                                                 &#8209;Dskip.agent.package=...                                                  |                                                 false                                                  | Skip the whole packaging                                                                                                                                                                                                                       |


Example configuration in pom.xml:

```xml
...
<plugin>
  <groupId>com.nsn.cumulocity.clients-java</groupId>
  <artifactId>microservice-package-maven-plugin</artifactId>
  <version>${c8y.version}</version>
  <executions>
    <execution>
      <configuration>
        <name>hello-world</name>
        <encoding>UTF-8</encoding>
        <rpmSkip>true</rpmSkip>
        <containerSkip>false</containerSkip>
        <manifestFile>${basedir}/src/main/microservice/cumulocity.json</manifestFile>
        <heap>
          <min>200m</min>
          <max>600m</max>
        </heap>
        <metaspace>
          <min>200m</min>
          <max>300m</max>
        </metaspace>
      </configuration>
    </execution>
  </executions>
</plugin>
...
```

> **Info:**
> Settings for heap and metaspace must be aligned with your settings of `resources/memory` in the microservice manifest
> file `cumulocity.json`. You have to be sure about the effect those parameters might have. Those parameters are directly
> used for the microservice start without further verification. For example be sure that the heap values meet the
> condition: Xms < Xmx.



##### Setting parameters on command line {#package-goal-command-line}

For information about how and whether it is possible to set parameters on command line refer to column
"Parameter command line name" of table in chapter [Package goal](https://cumulocity.com/docs/microservice-sdk/java/#package-goal).

###### Primitive configuration values {#package-goal-command-line-primitive-data}

Primitive configuration values or lists can be set on the Maven command line directly as usual for Maven command line
properties. Items of lists are specified with `,` as separation character.<br>
<br>
Example:
```sh
-Dskip.agent.package.rpm=true
-Dagent-package.arguments=XX:+PrintCommandLineFlags,-XX:+UseCompressedClassPointers,-XX:+UseCompressedOops
```

###### Complex data types as for heap and metaspace parameter {#package-goal-command-line-complex-data}

Properties must be used if you want to specify data of complex data types on the command line. This is the case for
memory data like heap and metaspace. In this case you have to specify each primitive value separately as pom property
which is then used inside the configuration of the microservice-package-maven-plugin.

Example for the definition of primitive default parameters in pom.xml:
```xml
<project ... >
  ...
  <properties>
    ...
    <custom-property.metaspace.min>200m</custom-property.metaspace.min>
    <custom-property.metaspace.max>300m</custom-property.metaspace.max>
    ...
  </properties>
  ...
</project>
```
These properties can be used inside the configuration section of the microservice-package-maven-plugin 
defined in the pom.xml file of your microservice project:

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

If you have defined the custom properties in your pom.xml file you can specify those parameters on command line:
```
mvn clean install -Dcustom-property.metaspace.min=400m -Dcustom-property.metaspace.max=500m
```

#### Validate REST security goal {#validate-rest-security-goal}

Available since version 2026.29.0, the validate REST security goal scans compiled classes to ensure all REST controller endpoints have proper security annotations. This provides build-time verification that your microservice endpoints are adequately secured.

The goal verifies that all REST endpoints are either secured with one of the recognized security annotations or explicitly marked as unsecured.

##### Configuring the goal {#configuring-the-goal}

Add the following to your microservice *pom.xml* file:

```xml
<plugin>
    <groupId>com.nsn.cumulocity.clients-java</groupId>
    <artifactId>microservice-package-maven-plugin</artifactId>
    <version>${c8y.version}</version>
    <executions>
        <execution>
            <id>validate-rest-security</id>
            <goals>
                <goal>validate-rest-security</goal>
            </goals>
            <phase>prepare-package</phase>
            <configuration>
                <enabled>true</enabled>
                <failOnError>true</failOnError>
            </configuration>
        </execution>
    </executions>
</plugin>
```

##### Configuration parameters {#configuration-parameters}

| Parameter | Default | Description |
|-----------|---------|-------------|
| enabled | false | Enable the validation. Must be set to `true` to activate. |
| failOnError | true | Fail the build if unsecured endpoints are found. Set to `false` to log warnings only. |

##### Securing your endpoints {#securing-your-endpoints}

All endpoints in your @RestController classes must be either secured or explicitly marked as unsecured.

**Securing an endpoint with role-based access:**

```java
@RestController
@RequestMapping("/api")
public class UserController {
    
    @PreAuthorize("hasRole('ADMIN')")
    @GetMapping("/users")
    public List<User> getUsers() {
        return userService.findAll();
    }
}
```

**Marking an endpoint as explicitly unsecured:**

```java
@RestController
@RequestMapping("/api")
public class HealthController {
    
    @UnauthorizedEndpoint("Public health check endpoint")
    @GetMapping("/health")
    public ResponseEntity<String> health() {
        return ResponseEntity.ok("OK");
    }
}
```

The validator recognizes the following security annotations:

* `@org.springframework.security.access.prepost.PreAuthorize`
* `@org.springframework.security.access.annotation.Secured`
* `@jakarta.annotation.security.RolesAllowed`
* `@javax.annotation.security.RolesAllowed`

Use the `@UnauthorizedEndpoint` annotation from the `com.cumulocity.microservice.security.annotation` package to mark endpoints that intentionally do not require authentication. The annotation accepts an optional parameter to document why the endpoint is unsecured.

##### Example build output {#example-build-output}

When validation succeeds, you see a message similar to:

```
[INFO] Starting REST endpoint security validation...
[INFO] Found 5 REST controller classes
[INFO] ✓ All REST endpoints are properly secured!
```

When validation fails with unsecured endpoints, the build stops with error messages:

```
[ERROR] Found 2 unsecured REST endpoints:
[ERROR]   - REST endpoint not secured: com.example.UserController.deleteAll.
[ERROR]     Must be annotated with @PreAuthorize, @Secured, @RolesAllowed, or @UnauthorizedEndpoint.
[ERROR]   - REST endpoint not secured: com.example.ConfigController.reset.
[ERROR]     Must be annotated with @PreAuthorize, @Secured, @RolesAllowed, or @UnauthorizedEndpoint.
```

#### Push goal {#push-goal}

The push plugin is responsible for pushing the Docker image to a registry. The registry can be configured by:

* containerSkip (alias skip.agent.package.container) - Prevents the push to execute. True by default
* registry (alias agent-package.container.registry) - Docker registry address

Example configuration:

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

#### Upload goal {#upload-goal}

The upload goal is responsible for deploying the microservice to a server.
There are three options to configure the server URL and credentials:

* _settings.xml_ - Maven global configuration placed at *~/.m2/settings.xml*
* _pom.xml_ - Maven project configuration file
* Command line

All three ways can be used together, for example, a goal partially can be configured in the _settings.xml_ and partially in the _pom.xml_.
In case of conflicts, the command line configuration has the highest priority and _settings.xml_ configuration the lowest.

To upload a microservice to the server you must configure the following properties:

* url - Mandatory URL that will be used for deployment. Empty by default.
* username - Mandatory tenant ID and username used for authorization. Empty by default.
* password - Mandatory password used for authorization. Empty by default.
* name - Optional name of the uploaded application. By default it is the same as `package.name` property or `artifactId` if `package.name` is not provided.
* skipMicroserviceUpload (alias `skip.microservice.upload`) - Controls if the microservice upload should be skipped. True by default so for the goal to work it must be set to `false`)


#### settings.xml {#settingsxml}

To configure the goal in the _settings.xml_ file, add the server configuration as follows:

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

#### pom.xml {#pomxml}

To configure the plugin in the _pom.xml_ file, add the server configuration as follows:

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

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

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

#### Command line {#command-line}

To pass the configuration only to the particular build, execute the following command:

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


#### Using Maven in debug mode {#using-maven-in-debug-mode}

Running Maven CLI commands in debug mode (for example, `mvn clean install --debug ...`) may
generate a very large volume of HTTP-related log output, such as logs from resource downloads. 
Analyzing this data might quickly become tedious.

To reduce such logging information, HTTP logging can be suppressed with these command line options:
```
-Dorg.slf4j.simpleLogger.log.org.apache.http=off
-Dorg.slf4j.simpleLogger.log.org.apache.http.wire=off
```
Besides `off`, `error` or `warn` might also be appropriate values. The parameters can also be added to the Maven configuration file `${MAVEN_HOME}/conf/logging/simplelogger.properties` 
or to the `MAVEN_OPTS` environment variable. Related documentation can be found in [Maven logging](https://maven.apache.org/maven-logging.html).


### Heap and perm/metadata {#heap-and-permmetadata}

To calculate heap and perm/metadata, it takes the limit defined in the [microservice manifest](https://cumulocity.com/docs/microservice-sdk/general-aspects/#microservice-manifest) (`resources/memory`) and it is converted into Megabytes (MB). For Java applications developed using the Java Microservice SDK the minimal value is 178MB. <br>
10% is reserved for "system", but not less than 50 MB. <br>
10% is taken for metaspace, but not less than 64 MB and not more than 1024MB. <br>
The rest is allocated for heap size.<br>
Refer to [Package goal](https://cumulocity.com/docs/microservice-sdk/java/#package-goal) for information on how to change the heap and metaspace settings.

### Deployment {#deployment}

#### Hosted deployment {#hosted-deployment}


> **Info:**
> For your convenience, Cumulocity provides a [Microservice utility tool](https://cumulocity.com/docs/microservice-sdk/general-aspects/#microservice-utility-tool) for easy packaging, deployment and subscription.



To deploy an application on an environment you need the following:

* URL address of your tenant
* Authorization header as "Basic <Base64(<username>:<password>)>"
* Tenant - tenant ID
* ZIP build from previous steps


##### Step 1 - Create the application {#step-1---create-the-application}

If the application does not exist, create a new application on the platform:

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

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

Example:

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

If the application has been created correctly, you can GET the application ID:

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

Example:

```shell
$ curl -H "Authorization:<AUTHORIZATION>" \
     <URL>/application/applicationsByName/hello-world
```

##### Step 2 - Upload the ZIP file {#step-2---upload-the-zip-file}

```http
POST /application/applications/<APPLICATION_ID>/binaries
Host: ...
Authorization: Basic xxxxxxxxxxxxxxxxxxx
Content-Type: "multipart/form-data"
```

Example:

```shell
$ curl -F "data=@<PATH_TO_ZIP>" \
	     -H "Authorization: <AUTHORIZATION>" \
	     "<URL>/application/applications/<APPLICATION_ID>/binaries"
```

##### Step 3 - Subscribe to the microservice {#step-3---subscribe-to-the-microservice}

```http
POST /tenant/tenants/<TENANT_ID>/applications
Host: ...
Authorization: Basic xxxxxxxxxxxxxxxxxxx
Content-Type: "multipart/form-data"

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

Example:

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

#### Local Docker deployment {#local-docker-deployment}

To deploy the application on a local Docker container, one must inject the environment variables into a container. This is done with the Docker `run -e` command. The full description of available parameters is available in [Environment variables](https://cumulocity.com/docs/microservice-sdk/general-aspects/#environment-variables).

An example execution could be:

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

### Monitoring {#monitoring}

The microservice's health endpoint can be checked to verify if a hosted microservice is running successfully.
This endpoint is enabled by default for all microservices that are developed using the Java Microservice SDK.

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

Example response when the microservice is functional:

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

or in case it is not working:

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

### Legacy Deployment {#legacy-deployment}

#### Properties {#properties}

For external/legacy deployment, the following paths will be searched in order to find a properties file specific for the environment the application is run on:

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

#### Logging {#legacy-logging}

##### Add your own log configuration file

* To customize the logging configuration for your microservice instead of using the default configuration, create a log
configuration file in the _configuration_ folder of your microservice project.
* The file must adhere to the naming convention *\<application-name\>-logging.xml*.
This ensures that your custom log configuration replaces the default configuration file
with the same name in the resulting Docker image.
* Once deployed, the customized log configuration file will be located in the */etc/\<artifactId\>*
directory within the microservice pod.

##### Locations to be searched for log configuration file

For external/legacy deployments, logging into the application implies using [Spring Logging](https://docs.spring.io/spring-boot/docs/current/reference/html/howto-logging.html).
The following locations are searched for the logback configuration file:

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

### Upgrade to Microservice SDK 10.13+ {#upgrade-to-microservice-sdk-1013}

A Spring Boot library was upgraded to 2.5.8, hence upgrading Microservice SDK to 10.13+ may require some additional development.

* The `content(matcher)` method of RestAssured has been replaced with `body(matcher)`, see [RequestSpecification#content()](https://javadoc.io/static/io.rest-assured/rest-assured/3.3.0/io/restassured/specification/RequestSpecification.html#content-byte:A-)
* Spring Boot BOM does not define a version for joda-time, you may need to explicitly define version.

  Maven example:
    ```
    <dependency>
      <groupId>joda-time</groupId>
      <artifactId>joda-time</artifactId>
      <version>2.10.10</version>
    </dependency>
    ```
* Jackson 2.12.x does not provide the Joda Module by default, it might be required to add `jackson-datatype-joda` dependency and define Joda Module:
  `new ObjectMapper().addModule(new JodaModule());` in a custom Microservice code.
* Spring Boot 2.5.8 does not provide the _Bean Validation 2.0_ provider  as a transitive dependency anymore. Developers may have to explicitly define a validation provider, for example `hibernate-validator`, or add the `spring-boot-starter-validation` dependency.

  Maven example:
     ```
     <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-validation</artifactId>
     </dependency>
    ```
* `junit-vintage-engine` was removed from the `spring-boot-starter-test` dependency, if you still use JUnit 4.x you must add the Vintage engine explicitly:
     ```
     <dependency>
       <groupId>org.junit.vintage</groupId>
       <artifactId>junit-vintage-engine</artifactId>
       <scope>test</scope>
     </dependency>
     ```

* The `message` field and binding errors are disabled by default for Spring Boot native error responses. This can be enabled by overriding the `microservice_error_attributes.properties` file.

  Sample content:
   ```
   server.error.include-message=ALWAYS
   server.error.include-binding-errors=ALWAYS
   ```

### Upgrade to Microservice SDK 10.17+ {#upgrade-to-microservice-sdk-1017}

A Spring Boot library was upgraded to 2.7.6, hence upgrading Microservice SDK to 10.17+ may require some additional development.

There was a change in the internal microservice security configuration following
the deprecation of `WebSecurityConfigurerAdapter` by Spring Security. The Microservice SDK now uses a direct
declaration of the `SecurityFilterChain` bean in its internal configuration instead. At the same time, Spring Security
only allows one of these configuration approaches in a single application. This means that if the old,
adapter-based method has been used in your code before, you will have to migrate to the new, direct filters
declaration for applications to start. Refer to the [Spring Security release notes](https://github.com/spring-projects/spring-security/releases/tag/5.8.0) for more details.

## Client library

This section provides an overview on how to access Cumulocity from Java clients, starting from connecting to the platform over accessing data to remote control of devices. It also discusses how to extend the Cumulocity domain model from Java for new devices and other business objects. Finally, this section describes how to configure the logging service in order to control the level of diagnostic messages generated by the client.

The client library is tightly linked to the design of the REST interfaces, which are described in [REST implementation](https://cumulocity.com/api/core/#section/REST-implementation) in the Cumulocity OpenAPI Specification.


### Connecting to the platform {#connecting-to-the-platform}

The root interface for connecting to Cumulocity from Java is called Platform (see Root interface in [REST implementation](https://cumulocity.com/api/core/#section/REST-implementation) in the Cumulocity OpenAPI Specification). It provides access to all other interfaces of the platform, such as the inventory. In its simplest form, it is instantiated as follows:

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

As an example:

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

If you use the Java client for developing an application, you must register an application key (through [Managing applications](https://cumulocity.com/docs/standard-tenant/ecosystem/#managing-applications) in the Cumulocity Administration application, or through the [Application API](https://cumulocity.com/api/core/#tag/Application-API)).

For testing purposes, every tenant is subscribed to the demo application key "uL27no8nhvLlYmW1JIK1CA==". The platform is then initialized like this:

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

The constructor for `PlatformImpl` also allows you to specify the default number of objects returned from the server in one reply with the parameter `pageSize`:

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


### Accessing the inventory {#accessing-the-inventory}

The following code snippet shows how to obtain a handle to the inventory:

```java
InventoryApi inventory = platform.getInventoryApi();
```

Using this handle, you can create, retrieve and update managed objects. For example, if you would like to retrieve all objects that have a geographical position, use the following:

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

Note that it returns a query to get the objects but it does not actually get them. In practice, such a list of objects could be very large. Hence, it is returned in pages from the server. To get all pages and iterate over them, use the following:

```java
for (ManagedObjectRepresentation mo : moc.get().allPages()) {
	System.out.println(mo.getName());
}
```


> **Important:**
> By default, `allPages()` doesn't return all elements at once, rather in batches of 5 elements (paginated). A separate request is made for each subsequent page after the iteration of the previous page is completed. Hence, it is not recommended to change/edit those objects while iterating through them, otherwise the filters may include/exclude different elements. It is better to collect them all and save them in memory, and only then perform edit operations.



To create a new managed object, construct a local representation of the object and send it to the platform. The following code snippet shows how to create a new electricity meter with a relay in it:

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

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

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

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

By invoking the `create()` method, a new managed object is created with an auto-generated unique identifier.

Assume that you would like to store additional custom properties along with the device. This can be done by creating a new fragment in the form of a Java bean. For example, assume that you would like to store tariff information along with your meter. There is a day and a night time tariff, and you must store the hours during which the night time tariff is active:

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

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

    public int getNightTariffEnd() {
        return nightTariffEnd;
    }

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

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

Now, you can add the tariff information to your meter:

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

### Accessing the identity service {#accessing-the-identity-service}

A device typically has a technical identifier that an agent must know to be able to contact the device. Examples are meter numbers, IP addresses and REST URLs. To associate such identifiers with the unique identifier of Cumulocity, agents can use the identity service. Again, to create the association, create an object of type `ExternalIDRepresentation` and send it to the platform.

The code snippet below shows how to register a REST URL for a device. It assumes that `mo` is the managed object from the above example and `deviceUrl` is a string with the REST URL of the device.

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

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

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

Now, if you need the association back, you can just query the identity service as follows:

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

The returned object will contain the unique identifier and a link to the managed object.


### Accessing events and measurements {#accessing-events-and-measurements}

Events and measurements can be accessed in a very similar manner as described above for the inventory. The following example queries the signal strength of the mobile connection of devices in the past two weeks and prints the device ID, the time of the measurement, the received signal strength and the bit error rate.

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

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

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

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

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

### Controlling devices {#controlling-devices}

The `DeviceControlResource` enables you to manipulate devices remotely. It has two sides: You can create operations in applications to be sent to devices, and you can query operations from agents.

In order to control a device it must be in the child devices hierarchy of an agent managed object. The agent managed object represents your agent in the inventory. It is identified by a fragment com\_cumulocity\_model\_Agent. This is how Cumulocity identifies where to send operations to control a particular device.

The following code demonstrates the setup:

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

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

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

For example, assume that you would like to switch off a relay in a meter from an application. Similar to the previous examples, you create the operation to be executed locally, and then send it to the platform:

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

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

Now, if you want to query the pending operations from an agent, the following code must be executed:

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

Again, the returned result may come in several pages due to its potential size.

```java
OperationCollectionRepresentation opCollectionRepresentation;

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

### Realtime features {#realtime-features}

The Java client libraries fully support the real-time APIs of Cumulocity. For example, to get immediately notified when someone sends an operation to your agent, use the following code:

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

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

	public void onNotification(Subscription<GId> sub, OperationRepresentation operation) {
		// Execute the operation
	}
});
```


> **Info:**
> "agentId" is the ID of your agent in the inventory.



To unsubscribe from a subscription, use the following code:

```java
subscription.unsubscribe();
```

If you wish to disconnect, the following code must be used:

```java
subscriber.disconnect();
```

### Subscribing to Notifications 2.0 {#subscribing-to-notifications-20}

The Notifications 2.0 API can be accessed in a very similar manner as described above in [Accessing the inventory](https://cumulocity.com/docs/microservice-sdk/java/#accessing-the-inventory).
See [Notifications 2.0](https://cumulocity.com/api/core/#tag/Notification-2.0-API) for more details about the API.

The following snippet shows how users can create, query and delete notification subscriptions. It also shows how a token string can be obtained.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

There is a sample microservice available in the [cumulocity-examples repository](https://github.com/Cumulocity-IoT//cumulocity-examples/tree/develop/hello-world-notification-microservice) with more details on the API usage.

### Reliability features {#reliability-features}

In particular on mobile devices, Internet connectivity might be unreliable. To support such environments, the Java client libraries support local buffering. This means that you can pass data to the client libraries regardless of an Internet connection being available or not. If a connection is available, the data will be sent immediately. If not, the data will be buffered until the connection is back again. For this, asynchronous variants of the API calls are offered. For example, to send an alarm:

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

The `createAsync` method returns immediately. The `Future` object can be used to determine the result of the request whenever it was actually carried out.

### Logging configuration {#logging-configuration}

Logging in the Java client SDK is handled through [slf4j](http://www.slf4j.org/) with a [logback](http://logback.qos.ch) backend. For a detailed description on how to use and configure logging, see the [logback documentation](http://logback.qos.ch/documentation.html).

Since version 10.11, the default logging level of the SDK is set to "Error" for all components, which means that logging messages are suppressed unless their level is "Error". If everything runs smoothly, there should be no log messages generated by the SDK. By default, log messages are sent to the console only.

The default logging configuration can be changed by providing a new configuration file. Two methods for providing the configuration file are discussed here: via an absolute filename passed using a system property; and via an OSGi fragment. Note that both of these methods override the default behaviour, rather than extending it.

## Services platform and SMS API

This section describes the Cumulocity SMS API and shows how to access it using the Cumulocity Java Client. You will also learn how to send and receive SMS messages via the Java Client API.

### Using the services platform {#using-the-services-platform}

The services platform interface is responsible for connecting to the Java services (SMS) API.

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

The URL pointing to the platform must be of the form *&lt;tenant>.cumulocity.com*, for example *https://demos.cumulocity.com*, which will process all the API requests.


> **Info:**
> You must have appropriate credentials to be able to access the Services API from outside. See the example above.



### Accessing the SMS Messaging API {#accessing-the-sms-messaging-api}

The following code snippet shows how to obtain a handle to the SMS API from Java.

```java
SmsMessagingApi smsMessagingApi = platform.getSmsMessagingApi();
```

Using this handle, you can send and retrieve the SMS messages from Java by calling its functions.

### Assigning required permissions and roles {#assigning-required-roles}

To use the SMS messaging API, the user must have the ADMIN and READ permission for "SMS" (SMS_ADMIN and SMS_READ) for sending and receiving messages respectively.
Refer to [Managing permissions and roles](https://cumulocity.com/docs/standard-tenant/managing-permissions/) for more information.

### Sending a message {#sending-a-message}

To send a SMS message using the API, prepare the message with the SendMessageRequest builder and call the sendMessage function of the API with the prepared message.

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

smsMessagingApi.sendMessage(smsMessage);
```

### Receiving messages {#receiving-messages}

You can use the API as follows to receive all SMS messages. Note that not every SMS provider supports receiving messages.

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

You can use the API as follows to receive a specific SMS message identified by message ID. Note that not every SMS provider supports receiving messages.

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

### SMS management endpoints {#sms-management-endpoints}

The REST API can be used to send and receive SMS messages.

Sending a message:

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

Receiving all messages:

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

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

Receiving a specific message:

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

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

## Monitoring support for microservices

Cumulocity supports the [OpenTelemetry (OTLP) framework](https://opentelemetry.io/) for exporting telemetry data (metrics, logs, and traces) from your microservice to help you analyze your application’s performance and behavior.
The Java Microservice SDK leverages the [OpenTelemetry Java agent](https://opentelemetry.io/docs/zero-code/java/agent/) which provides automatic instrumentation for many popular libraries and frameworks.

### Enabling auto-instrumentation {#enabling-auto-instrumentation}
The instrumentation of the microservice application by the OpenTelemetry Java agent is controlled by the `otel.javaagent.enabled` parameter. Setting this parameter to `true` enables instrumentation.

```json
{
"category": "<application-name>",
"key": "otel.javaagent.enabled",
"value": "true"
}
```

If enabled, the Java agent JAR file is attached to the microservice JVM at startup time.


> **Important:**
> To enable or disable instrumentation, the microservice must be unsubscribed and subscribed again.



Configuring auto-instrumentation for selected libraries or frameworks, or opting for manual instrumentation only, is described
in the [OpenTelemetry instrumention documentation](https://opentelemetry.io/docs/zero-code/java/agent/disable/).

#### Maven configuration
Besides setting up the OTLP configuration in tenant options, the Java agent JAR file must be included in the microservice image at build time. 
By default, this JAR file is not contained in the image. 
To download and copy the Java agent JAR file to the microservice image, the `microservice-package-maven-plugin` must be 
configured with the `otelJavaAgentInclude` element set to `true` in the Maven `pom.xml` file: 

```xml
            <plugin>
                <groupId>com.nsn.cumulocity.clients-java</groupId>
                <artifactId>microservice-package-maven-plugin</artifactId>
                <version>${c8y.version}</version>
                <executions>
                    <execution>
                        <id>package</id>
                        <phase>package</phase>
                        <goals>
                            <goal>package</goal>
                        </goals>
                        <configuration>
                            ...
                            <otelJavaAgentInclude>true</otelJavaAgentInclude>
                            <otelJavaAgentDownloadUrl>https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v2.21.0/opentelemetry-javaagent.jar</otelJavaAgentDownloadUrl>
                            ...
                        </configuration>
                    </execution>
                </executions>
            </plugin>
```

The Java agent JAR file download URL can be set with the parameter `otelJavaAgentDownloadUrl`.


> **Important:**
> If the Java agent JAR file is not contained in the microservice image 
> and `otel.javaagent.enabled` is set to `true`, then the microservice will fail to start. 
> The error message will be like "Error opening ... opentelemetry-javaagent.jar".




### Manual instrumentation {#manual-instrumentation}
In addition to automatic instrumentation, microservices can be manually instrumented.

The Java agent creates the GlobalOpenTelemetry object which can be used as a starting point to create
individual Tracer or Meter objects for [custom instrumentation](https://opentelemetry.io/docs/zero-code/java/agent/api/).

Java code example:
```java
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.metrics.Meter;

Meter meter = GlobalOpenTelemetry.getMeter("application");
```

A basic example for this use case can be found in the [OpenTelemetry GitHub repository](https://github.com/open-telemetry/opentelemetry-java-examples/tree/main/javaagent/src/main/java/io/opentelemetry/example/javagent).

If instrumentation with the Java agent is disabled, complete manual instrumentation without the Java agent can be applied as well.
Detailed examples for various use cases can be found in the [OpenTelemetry GitHub repository](https://github.com/open-telemetry/opentelemetry-java-examples/tree/main).

#### Maven dependencies {#maven-dependencies}
The Maven `pom.xml` file of the microservice application needs to be extended with the required OTLP library dependencies according to the manual instrumentation code.

## Troubleshooting

Some common problems and their solutions have been identified and documented below.

##### SSL or certificate errors {#ssl-or-certificate-errors}

You can use both HTTP and HTTPS from the Java client libraries. To use HTTPS, you may need to import the Cumulocity production certificate into your Java Runtime Environment. Download the certificate with the following command:

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

Import the certificate using the following command:

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

Confirm that you trust this certificate.

Use the following argument to run Java:

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

If you use Eclipse/OSGi, open the **Run Configurations...** dialog in the **Run** menu. Double-click **OSGi Framework**, then open the **Arguments** tab on the right side. In the **VM arguments** text box, add the above parameter.

Since the Java SDK comes with its own set of trusted root certificates, you might still get the error message "java.security.cert.CertificateException: Certificate Not Trusted". In this case, make sure that the GoDaddy Certificate Authority (CACert) is available for your Java environment using the following command:

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

The *gd\_bundle.crt* certificate can be downloaded directly from the [GoDaddy repository](https://certs.godaddy.com/anonymous/repository.pki).

##### When I install the SDK, Eclipse complains about compatibility problems {#when-i-install-the-sdk-eclipse-complains-about-compatibility-problems}

Make sure that you use the **Target Platform** preferences page to install the SDK as described in the instructions. **Install New Software** installs software into your running Eclipse IDE, but you must install the SDK as a separate server software.

##### I get "Expected to find an object at table index" when running a microservice or application {#i-get-expected-to-find-an-object-at-table-index-when-running-a-microservice-or-application}

This error occurs due to a bug in particular Eclipse versions. As a workaround, select **Run** from the main menu and then **Run Configurations ...**. On the left, select the launch configuration that you have been using, for example, **OSGi Framework**. On the right, click the **Arguments** tab. Append a " -clean" to the **Program Arguments** and click **Apply**.

##### The microservice or application won't start {#the-microservice-or-application-wont-start}

Verify that all required plugins are checked in your launch configuration. Go to **Run** > **Run Configurations** and select the **OSGi Framework** launch configuration. Click **Select All** and try running it again.

Check if the required plugins are started. While the application or microservice is running, type "ss" into the console and hit the return key. All listed plugins should be either in the ACTIVE or RESOLVED state.

Check if you are using the correct target platform. Go to the **Target Platform** page in the preferences and check if "Cumulocity runtime" is checked.

##### The microservice application does not compile. I get "Access Restriction" messages {#the-microservice-application-does-not-compile-i-get-access-restriction-messages}

This error may be caused because of a missing package import. Navigate to the **Dependencies** tab of the project manifest file and check if the package of the type that contains the method giving the access restriction is present in the Import-Package section.

You can find the package by opening the declaration of the method (right-click and select **Open Declaration** from the context menu).

##### When starting an application I get "address already in use" messages {#when-starting-an-application-i-get-address-already-in-use-messages}

Check if you are running another instance of the application. Click on the **Display Selected Console** icon in the console toolbar (the terminal icon) to browse through your consoles. Terminate other running instances by clicking the red **Stop** icon in the toolbar.

Under Unix/macOS you can also use the `lsof` command to see which process is using a particular port. For example, to see which process is using TCP port 8080 enter:

```shell
$ lsof -i tcp:8080
```

It will return something like:

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

This means that the process 12985 is using the 8080 port and it can be killed if necessary.

##### When trying to build an application I get a "BeanCreationException: Error creating bean with name methodSecurityInterceptor" error {#when-trying-to-build-an-application-i-get-a-beancreationexception-error-creating-bean-with-name-methodsecurityinterceptor-error}

This is caused mainly by versions incompatibility between the SDK and Spring Boot specified in your _pom.xml_ file. If you want to use a recent version of the SDK, for example, 1016.0.0, the version of Spring Boot must be compatible or equal to version 2.5.4.

##### When using the SDK endpoints /env, /configprops, or /quartz I get return values masked with "******" {#when-using-the-sdk-endpoints-env-configprops-or-quartz-i-get-return-values-masked}

This behavior changed with the update to Spring Boot version 3.2 in the Microservice SDK. To configure or restore the previous behavior, refer to the [Spring Boot documentation](https://docs.spring.io/spring-boot/docs/3.2.2/reference/html/actuator.html#actuator.endpoints.sanitization).

##### Endpoints with a trailing slash like /some/greeting/ are no longer accepted {#endpoints-with-a-trailng-slash-like-some-greeting-are-not-accepted-any-more}

This behavior changed with the update to Spring Boot version 3.2 in the Microservice SDK. To configure the previous behaviour, refer to the [Spring Boot Migration guide](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Migration-Guide#web-application-changes).

##### Missing Docker permissions in Linux {#missing-docker-permissions-in-linux}

When you build a microservice application via `mvn`, you might get this error:

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

This is an issue with Docker in Linux OS.
You can verify that your user is lacking permissions for Docker by running:

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

In order to fix this, do the following:

1. Create the Docker group.

   ```shell
   $ sudo groupadd docker
   ```

2. Add your user to the Docker group.

   ```shell
   $ sudo usermod -aG docker $your_user_name
   ```

3. Log out and log back in, so that your group membership is updated. Alternatively, run

   ```shell
   $ newgrp docker
   ```

4. Try running a Docker command again.

Also refer to [Docker Engine > Installation per distro > Optional post-installation steps](https://docs.docker.com/engine/install/linux-postinstall/) in the Docker documentation.
