This blog post is following up on the SAP Cloud SDK for Java (version 4.0.0 just released) introduction tutorial on SAP Developers Community, Create a Simple Cloud Foundry App Using SAP Cloud SDK , which provides a hands-on to create a very simple servlet retrieving Business Partner information from an OData service system, and then deploy the application onto Business Technology Platform, Cloud Foundry environment. The purpose is to provide a procedure-concentrated guide and expand the details on each step, especially the steps to connect to the OData service, as a compliment to the original tutorial.
Although there are multiple alternatives to implement the original tutorial, here in this blog post we will only show the path that I took. Specifications:
- backend OData system: SAP S/4HANA Cloud
- deploy environment: SAP BTP, Cloud Foundry
- local machine: macOS (Terminal)
- IDE: Eclipse (with maven plugin)
References
Original tutorial (in java) https://developers.sap.com/group.s4sdk-cloud-foundry.html
SAP Cloud SDK document https://sap.github.io/cloud-sdk/docs/java/getting-started
M2Eclipse http://www.eclipse.org/m2e/
Environment Setup
➡️ Install JAVA 8
First you need to check if java is installed.
java --version
I got the result:
openjdk 18 2022-03-22
OpenJDK Runtime Environment SapMachine (build 18+36-sapmachine)
OpenJDK 64-Bit Server VM SapMachine (build 18+36-sapmachine, mixed mode)
There might be multiple versions installed, and this version is not supporting the project. So I still need to install the correct version.
If your operating system is macOS or Linux, you can use Homebrew to install java, first update brew,
brew update
brew tap AdoptOpenJDK/openjdk
and then install Java 8
brew install adoptopenjdk8 --cask
➡️ Install Maven
Check if maven is installed.
mvn --version
Result,
Apache Maven 3.8.5 (3599d3414f046de2324203b78ddcf9b5e4388aa0)
Maven home: /Users/xyz/Workspace/apache-maven-3.8.5
Java version: 18, vendor: SAP SE, runtime: /Library/Java/JavaVirtualMachines/sapmachine-jdk-18.jdk/Contents/Home
Default locale: en_CN, platform encoding: UTF-8
OS name: "mac os x", version: "12.4", arch: "aarch64", family: "mac"
If not, first download the maven latest release and then install it.
In my case maven is installed but the default java version is not correct. This is because the Java home is still the old one.
echo $JAVA_HOME
get result:
/Library/Java/JavaVirtualMachines/sapmachine-jdk-18.jdk/Contents/Home
So after finding the java home, set new java home. In my case:
export JAVA_HOME=/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home
Now both the java version and maven default java version are correct.
java -version
# result
openjdk version "1.8.0_292"
OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_292-b10)
OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.292-b10, mixed mode)
mvn --version
# result
Apache Maven 3.8.5 (3599d3414f046de2324203b78ddcf9b5e4388aa0)
Maven home: /Users/xyz/Workspace/apache-maven-3.8.5
Java version: 1.8.0_292, vendor: AdoptOpenJDK, runtime: /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre
Default locale: en_CN, platform encoding: UTF-8
OS name: "mac os x", version: "10.16", arch: "x86_64", family: "mac"
If you want to make the change permanent, write it in /usr/local/etc/mavenrc or /etc/mavenrc
➡️ Install eclipse and eclipse maven plugin
Install eclipse by following the steps on this page.
In my case eclipse is installed, so open the eclipse, go to the Eclipse Marketplace and search for the maven plugin: M2Eclipse – Eclipse IDE integration for Maven. For terminal you can use the Terminal desktop app or install a terminal plug-in in eclipse.
➡️ Install cf cli
Install the command-line interface for Cloud Foundry, see here.
In my case it is installed, check version,
cf -v
get result
cf version 8.1.0+034d929d7.2022-01-03
Initiate the project
➡️ Generate project from archetype
Use maven to generate a Cloud SDK project, from the “hello world” archetype,
mvn archetype:generate -DarchetypeGroupId=com.sap.cloud.sdk.archetypes -DarchetypeArtifactId=scp-cf-tomee -DarchetypeVersion=RELEASE
and then fill in the requested info in the interactive session, e.g. name – firstapp. If you want to use the default setting then just hit return.
The archetype is a TomEE-based project, scp-cf-tomee, latest version 4.0.0.
Import the newly created project into eclipse.
➡️ Build the project
Go into the project root path and build the project,
mvn clean package
with this command an *.war file will be generated.
You can run the project locally, with TomEE container as its target runtime,
mvn tomee:run -pl application
and then visit http://localhost:8080/hello
➡️ Deploy to cf
It can run on both neo and cloud foundry. First login to cf with the user credentials and API endpoint URL, in my case I found https://api.cf.sap.hana.ondemand.com in my Subaccount.
cf login -a https://api.cf.sap.hana.ondemand.com -u sindy.zhan@sap.com -p XYZ
Then specify the ORG and SPACE, and deploy the app,
cf push
You can check the deployed app and its route, which you use to access the app.
cf apps
# result
Getting apps in org XYZ / space XYZ as sindy.zhan@sap.com...
name requested state processes routes
firstapp started web:1/1 firstapp-fearless-chipmunk-vf.cfapps.sap.hana.ondemand.com
In my case, I can visit the app via https://firstapp-fearless-chipmunk-vf.cfapps.sap.hana.ondemand.com
Click HelloWorldServlet, or add a path /hello at the end of the URL to see the “hello world” greeting.
Retrieve data from the backend
The above steps bring us a demo hello world module, displaying static text on the webpage. Now we will create a new module to connect to the backend system.
➡️ Add business partner servlet
Create BusinessPartnerServlet, to retrieve all persons (a specific kind of business partner) with their name and a few additional properties. You can copy and paste the code from the original tutorial, which needs some changes according to your scenario, or you can follow this blog post and copy and paste the code below.
package com.sap.cloud.sdk.tutorial;
import com.google.gson.Gson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import com.sap.cloud.sdk.cloudplatform.connectivity.DestinationAccessor;
import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataException;
import com.sap.cloud.sdk.datamodel.odata.helper.Order;
import com.sap.cloud.sdk.s4hana.connectivity.DefaultErpHttpDestination;
import com.sap.cloud.sdk.s4hana.connectivity.ErpHttpDestination;
import com.sap.cloud.sdk.s4hana.datamodel.odata.namespaces.businesspartner.BusinessPartner;
import com.sap.cloud.sdk.s4hana.datamodel.odata.services.DefaultBusinessPartnerService;
@WebServlet("/businesspartners")
public class BusinessPartnerServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final Logger logger = LoggerFactory.getLogger(BusinessPartnerServlet.class);
private static final String CATEGORY_PERSON = "1";
private final ErpHttpDestination destination = DestinationAccessor.getDestination("MyErpSystem").asHttp().decorate(DefaultErpHttpDestination::new);
@Override
protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
throws ServletException, IOException {
try {
final List<BusinessPartner> businessPartners =
new DefaultBusinessPartnerService()
.getAllBusinessPartner()
.select(BusinessPartner.BUSINESS_PARTNER,
BusinessPartner.LAST_NAME,
BusinessPartner.FIRST_NAME,
BusinessPartner.IS_MALE,
BusinessPartner.IS_FEMALE,
BusinessPartner.CREATION_DATE)
.filter(BusinessPartner.BUSINESS_PARTNER_CATEGORY.eq(CATEGORY_PERSON))
.orderBy(BusinessPartner.LAST_NAME, Order.ASC)
.top(200)
.executeRequest(destination);
logger.info(String.format("Found %d business partner(s).", businessPartners.size()));
response.setContentType("application/json");
response.getWriter().write(new Gson().toJson(businessPartners));
} catch (final ODataException e) {
logger.error(e.getMessage(), e);
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
response.getWriter().write(e.getMessage());
}
}
}
Connect to OData service
➡️ Choose OData service
Here the OData service is SAP S/4HANA cloud with Business Partner OData V2 service.
➡️ Expose APIs on SAP S/4HANA cloud
To expose APIs on SAP S/4HANA cloud, then you should create communication users, and create destination
0. search for communication scenario
On SAP API Business Hub, we find the information for Business Partner (A2X): https://api.sap.com/api/API_BUSINESS_PARTNER/overview
You can click on the business document to see APIs and their sample playloads.
Here we will use a sample URL read Business Partner master data:
GET | /sap/opu/odata/SAP/API_BUSINESS_PARTNER/A_BusinessPartner(BusinessPartner=’900023122′) |
You can also find the Communication Scenario we need: Business Partner, Customer and Supplier Integration(SAP_COM_0008)
And then In S/4HANA Cloud, Fiori Launchpad, we complete the rest of the steps to expose APIs.
1. create communication user
Search for communication user > open “Maintain Communication Users” tile > click New > fill in the user name,
Do remember to note down the generated password for later use.
Then click Create.
2. create communication system
Navigate to Communication Systems > New > input System ID and System Name.
In the Technical Data > General, fill in Host Name. In my case, the host name to my S/4HANA Cloud system is my301481.s4hana.ondemand.com, so I set my host name to be my301481-api.s4hana.ondemand.com
Choose the Communication User we just created for New Inbound Communication User
And for the Outbound Communication, choose the default customer client certificate
Save the changes.
3. create communication arrangement
Search for and navigate to Communication Arrangements > create New > fill in the Communication Scenario that we need SAP_COM_0008.
Select Communication System we just created, and the associated Communication User automatically comes in.
In the Inbound Services section, we can find the Service URL for Business Partner (A2X) OData V2
Business Partner (A2X) | OData V2 | https://my301481-api.s4hana.ondemand.com/sap/opu/odata/sap/API_BUSINESS_PARTNER |
In the Outbound Services section, dis-select the outbound services that are active by default.
Save it.
~. test it on postman (or other tools)
This is an optional step.
If we want to read the list of business partners, create request:
GET https://my301481-api.s4hana.ondemand.com/sap/opu/odata/sap/API_BUSINESS_PARTNER/A_BusinessPartner
And fill in the authorization: Authorization > Type > choose Basic Auth > fill in communication user as Username and Password
From the response body we can find a Business Partner ID, and use it to read the corresponding business partner details. In my case I check the Business Partner with the ID 1000154,
GET https://my301481-api.s4hana.ondemand.com/sap/opu/odata/sap/API_BUSINESS_PARTNER/A_BusinessPartner('1000154')
➡️ Configurate destination on BTP
If you want to deploy the app to Cloud Foundry, go to your BTP Cockpit > Subaccount > Connectivity > Destinations.
Create a new destination with the name of MyErpSystem (which is hardcoded in BusinessPartnerServlet.java), URL to be https:// = host name, and choose BasicAuthentication, with the Communication User credentials to be User and Password.
After creation, you can test the connection. Every HTTP status code below 500 is treated as successful.
Deploy to the cloud
Now go back from BTP Cockpit to local Terminal, and do the deployment.
➡️ Create two service instance
Create service instance for the destination service and xsuaa. You can do it in cf cli or in Cockpit, here we provide the cf cli commands:
cf create-service destination lite my-destination
cf create-service xsuaa application my-xsuaa
➡️ Bind the app with two newly created service instances
To bind the two newly created service instances to your application when it is deployed, adapt the manifest.yml file by adding the two instances to the services section at the end.
In this case, the two services are commented in manifest.yml so now uncomment them.
➡️ Deploy to SAP BTP Cloud Foundry
Now deploy the application again.
mvn clean install
cf push
Use the same URL we can visit the application, but this time a new servlet can be accessed via path /businesspartners
The rest of the tutorial is to add a local integration test in the integration-test module, which doesn’t affect the above steps so I will not add them to this blog post. If necessary, I will complete them later.