Disclaimer: This blog post is only applicable for the SAP Cloud SDK version of at most 2.19.2. We plan to continuously migrate these blog posts into our List of Tutorials. Feel free to check out our updated Tutorials on the SAP Cloud SDK.

cf version without an error.
Its services are declared as either ODATA, SOAP or REST. If you are interested in OData and SOAP, please take a look at what the SAP Cloud SDK can provide as additional functionality.
On this API reference, you can find a documentation of multiple interactive endpoints. By exploring the colored fields, you can test the API and investigate request parameters and expected response bodies. With this Swagger UI -like interface you can test API calls against a sandbox system.
Here you can find meta information about an API, as well as available URLs of the API. They heavily depend on your target landscape. Also details about authentication and links to in-depth documentation are referenced on this page.mvn archetype:generate -DarchetypeGroupId=com.sap.cloud.s4hana.archetypes -DarchetypeArtifactId=scp-cf-spring -DarchetypeVersion=RELEASE
RELEASE reference at the end of the command to your preferred version. This tutorial uses org.example.rest as base package../application/src/main/resources/api.yaml
./application/pom.xml file and add the following <plugin> next to the other declared plugins.<plugins>
...
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>3.3.4</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/src/main/resources/api.yaml</inputSpec>
<generatorName>java</generatorName>
<configurationFile>${project.basedir}/src/main/resources/api-options.json</configurationFile>
<generateApiTests>false</generateApiTests>
<generateModelTests>false</generateModelTests>
<generateApiDocumentation>false</generateApiDocumentation>
<generateModelDocumentation>false</generateModelDocumentation>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
pom.xml file. Again, please feel encouraged to replace the version with the latest version from the Maven central repository.<dependencies>
...
<!-- due to annotations in the generated code -->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.5.22</version>
</dependency>
</dependencies>/src/main/resources/api-options.json. In order to improve the readability of the code generator setup, we delegate some settings to a separate JSON file. Create the api-options.json with the following content, and put it next to api.yaml:{
"java8" : true,
"dateLibrary" : "java8",
"modelPackage" : "org.example.rest.monitoring.model",
"apiPackage" : "org.example.rest.monitoring.api",
"invokerPackage" : "org.example.rest.monitoring.invoker",
"serializableModel" : true,
"withXml" : false,
"booleanGetterPrefix" : "is",
"useRuntimeException" : false,
"hideGenerationTimestamp" : true,
"library" : "resttemplate",
"sourceFolder" : "/src/main/java"
}"library" : "resttemplate" intact, to ensure support with SAP Cloud SDK.
mvn generate-sources
target directory of the application module:./application/target/generated-sources/openapi/src/main/java/
operationId elements in the api.yaml. We advise using an automatic approach to handle the issue, please see the Appendix in the adjacent post Access any REST service with SAP Cloud SDK.
api contains a dynamic set of classes, depending on the OpenAPI interface. Each OpenAPI endpoint is mapped to a class, which can be instantiated. Given the interface, each API class features the respective operations as methods. The return type of these methods are defined in the model package.invoker holds static classes for API querying, e.g. ApiClient and helper classes.model incorporates classes, which are mapped to entities defined by the API. They enable the type-safe usage of API responses.MonitoringDestination.java to serve as placeholder for the destination identifier.package org.example.rest;
import com.sap.cloud.sdk.cloudplatform.connectivity.DestinationDeclarator;
public class MonitoringDestination extends DestinationDeclarator {
public final static String DESTINATION_NAME = "MonitoringEndpoint";
public MonitoringDestination() {
super(DESTINATION_NAME);
}
}
ConfigurationMonitoring.java to serve beans with request scope, to ensure tenant and user separation. With the MonitoringDestinationreference we can resolve service paths and the HttpClient, which automatically resolves authorization headers for us.package org.example.rest;
import com.sap.cloud.sdk.cloudplatform.connectivity.Destination;
import com.sap.cloud.sdk.cloudplatform.connectivity.DestinationAccessor;
import com.sap.cloud.sdk.cloudplatform.connectivity.HttpClientAccessor;
import org.apache.http.client.HttpClient;
import org.example.rest.monitoring.invoker.ApiClient;
import org.springframework.context.annotation.*;
import org.springframework.http.client.BufferingClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.WebApplicationContext;
import java.net.URI;
import java.net.URISyntaxException;
@Configuration
public class ConfigurationMonitoring {
@Bean
@Primary
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public ApiClient createApiClient() throws URISyntaxException
{
// resolve destination
final Destination destination = DestinationAccessor.getDestination(MonitoringDestination.DESTINATION_NAME);
// instantiate RestTemplate and ApiClient
final RestTemplate restTemplate = createRestTemplate();
final ApiClient apiClient = new ApiClient(restTemplate);
// set root of API Client base path
final URI uri = destination.getUri();
final URI path = new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), uri.getPath(), null, null);
apiClient.setBasePath(path.toString());
return apiClient;
}
@Bean
@Primary
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public RestTemplate createRestTemplate()
{
// create new HttpClient for destination
final HttpClient httpClient = HttpClientAccessor.getHttpClient(MonitoringDestination.DESTINATION_NAME);
// instantiate template with prepared HttpClient, featuring repeated response reading
final RestTemplate restTemplate = new RestTemplate();
final HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();
httpRequestFactory.setHttpClient(httpClient);
restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(httpRequestFactory));
return restTemplate;
}
}
@Primary we signal Spring to select this instantiation method with priority over the methods provided by default from the generated OpenAPI code.models/MonitorResponse.java to hold the values prepared by the controller.package org.example.rest.models;
import com.fasterxml.jackson.annotation.JsonProperty;
import javax.annotation.Nonnull;
import org.example.rest.monitoring.model.AccountsAppsMetricsresponse;
import java.util.List;
public class MonitorResponse {
@JsonProperty("monitor")
private final List<AccountsAppsMetricsresponse> monitor;
public MonitorResponse( @Nonnull final List<AccountsAppsMetricsresponse> monitor ) {
this.monitor = monitor;
}
}
controllers/MonitorController.java to listen on requests to our application. To keep the example simple, we are going to simply wrap the API response into our own model class MonitorResponse. You can later manipulate the result list and or use a different response model for further data processing.package org.example.rest.controllers;
import javax.annotation.Nonnull;
import org.example.rest.models.MonitorResponse;
import org.example.rest.monitoring.api.JavaApplicationMetricsApi;
import org.example.rest.monitoring.invoker.ApiClient;
import org.example.rest.monitoring.model.AccountsAppsMetricsresponse;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.net.URISyntaxException;
import java.util.List;
@RestController
@RequestMapping("/monitor")
public class MonitorController {
private final ApiClient apiClient;
public MonitorController( @Nonnull final ApiClient apiClient ) {
this.apiClient = apiClient;
}
@RequestMapping(method = RequestMethod.GET)
public ResponseEntity<MonitorResponse> getMonitor(@RequestParam final String account, @RequestParam final String app) throws URISyntaxException {
final List<AccountsAppsMetricsresponse> result = new JavaApplicationMetricsApi(apiClient).getAccountsSubaccountNameAppsAppNameMetrics(account, app);
return ResponseEntity.ok(new MonitorResponse(result));
}
}
GET /monitor request. Upon execution an instance of ApiClient is injected as part of the class constructor. Spring will resolve the bean from our previously introduced ConfigurationMonitoring configuration.
integration-tests module../integration-tests/src/test/resources/mocked_monitoring_response.json
[
{
"account": "d012345trial",
"application": "sampleapp",
"state": "Ok",
"processes": [
{
"process": "0123456789abcdef",
"state": "Ok",
"metrics": [
{
"name": "Used Disc Space",
"state": "Ok",
"value": 57,
"unit": "%",
"warningThreshold": 90,
"errorThreshold": 95,
"timestamp": 1551950283000,
"output": "DISK OK - free space: / 3041 MB (39% inode=79%); /var 1459 MB (76% inode=98%); /tmp 1844 MB (96% inode=99%);",
"metricType": "rate",
"min": 0,
"max": 8063
},
{
"name": "Requests per Minute",
"state": "Ok",
"value": 0,
"unit": "requests",
"warningThreshold": 0,
"errorThreshold": 0,
"timestamp": 1551950284000,
"output": "JMX OK - RequestsCountMin = 0 ",
"metricType": "performance",
"min": 0,
"max": 0
},
{
"name": "CPU Load",
"state": "Ok",
"value": 10,
"unit": "%",
"warningThreshold": 80,
"errorThreshold": 90,
"timestamp": 1551950283000,
"output": "OK CPUValue: 10 (W> 80, C> 90) ",
"metricType": "performance",
"min": 0,
"max": 0
}
]
}
]
}
]
./integration-tests/src/test/java/[...]/MonitoringControllerLocalTest.java
package org.example.rest;
import com.github.tomakehurst.wiremock.junit.WireMockRule;
import com.sap.cloud.sdk.cloudplatform.servlet.RequestContextExecutor;
import com.sap.cloud.sdk.testutil.MockUtil;
import org.apache.commons.io.Charsets;
import org.apache.commons.io.IOUtils;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import java.io.IOException;
import java.net.URI;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static java.lang.Thread.currentThread;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith( SpringRunner.class )
@WebMvcTest
public class MonitoringControllerLocalTest
{
private static final String TEST_API_BASE_PATH = "/monitoring/v2";
private static final String TEST_SCP_ACCOUNT_ID = "d012345trial";
private static final String TEST_SCP_APPLICATION = "sampleapp";
private static final MockUtil mockUtil = new MockUtil();
@Rule
public final WireMockRule wireMockServer = new WireMockRule(wireMockConfig().dynamicPort());
@Autowired
private MockMvc mvc;
private static String TEST_API_RESPONSE;
@BeforeClass
public static void beforeClass() throws IOException {
mockUtil.mockDefaults();
// load expected response from mocked API service
TEST_API_RESPONSE = IOUtils.toString(currentThread().getContextClassLoader().getResourceAsStream("mocked_monitoring_response.json"), Charsets.UTF_8);
}
@Before
public void mockServerResponses() {
stubFor(
get(urlPathMatching(TEST_API_BASE_PATH + "/accounts/(\\w+)/apps/(\\w+)/metrics"))
.willReturn(okJson(TEST_API_RESPONSE)));
}
@Test
public void testMonitor() throws Exception
{
final URI apiUrl = new URI(wireMockServer.baseUrl() + "/" + TEST_API_BASE_PATH);
mockUtil.mockDestination(MonitoringDestination.DESTINATION_NAME, apiUrl, null);
new RequestContextExecutor().execute(() -> {
mvc.perform(MockMvcRequestBuilders.get("/monitor").param("account", TEST_SCP_ACCOUNT_ID).param("app", TEST_SCP_APPLICATION))
.andExpect(status().isOk())
.andExpect(content().json("{\"monitor\":"+TEST_API_RESPONSE+"}"));
});
}
}
.andExpect(...) statements to improve test assertions.SpringRunner and annotated with @WebMvcTestwe can use the autowired MockMvc instance to directly call our controller.GET /monitoring/v2/accounts/d012345trial/apps/sample-application/metrics

Services. In category DevOps make sure Monitoring is active.
Security > OAuth. In the content frame, find the Token Endpoint, note it down - you will need this in the next step.
Platform API.


xsuaaand destination. For the sake of this guide, let's assume the xsuaa service instance is called "myxsuaa" and the destination service instance is called "mydestination".In case you are missing a service instance, go to Service Marketplace and setup it up. For xsuaa, the recommended service plan is application. For destination it is lite.MonitoringEndpointJust like described in your Java application, as field MonitoringDestination.DESTINATION_NAMEHTTPhttps://api.[domain].ondemand.com/monitoring/v2/Enter the correct sub domain, depending on your landscape. You can find a list of supported URLs on the Service API page.InternetOAuth2ClientCredentials
mvn clean install
manifest.ymlservices:
- myxsuaa
- mydestinationcf tool, please find the starter tutorial for applications on Cloud Foundry with the SAP Cloud SDK.cf push

urls above.
Enter the following path./monitor?account=D123456&app=sampleapp
account enter the (sub) account Id for which the monitored application is running. Enter the application name for app.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
| User | Count |
|---|---|
| 4246 | |
| 3357 | |
| 2603 | |
| 2153 | |
| 1983 | |
| 1255 | |
| 1164 | |
| 1122 | |
| 1100 |