Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
Fukuhara
Advisor
Advisor
Hi All,

I am writing this blog to describe steps to build Java application calling function modules on SAP Cloud Platform CF with SAP Cloud SDK.

Function modules are on on-premise environment and called by JCo via SAP Cloud Connector.

There are so many useful information here.(2020/8/21 Added)

Architecture


System architecture is as below image, which is on the SAP Help portal.



It takes time to test this for me, since I was unfamiliar with SAP Cloud Platform services like XSUAA and App Router.  To understand SAP Cloud Platform services, I posted an article about App Router, XSUAA and Java Application in Japanese.

Environment


I used both Windows and Ubuntu PC, just because my development environment is separated into Java and others.  It is not mandatory to use both windows and ubuntu.

Local PC(Java development on Windows)



  • OS: Windows10 64-bit

  • openJDK: 1.8.0_242

  • Chocolatey: 0.10.15

  • maven: 3.6.3

  • IDE: IntelliJ IDEA Community Edition 2019.3.5

  • CF cli: 6.51.0+2acd15650.2020-04-07

  • SAP Cloud SDK for Java: 3.19.1


Local PC(App router development on Ubuntu)



  • OS: Ubuntu18.04.01 LTS

  • nvm: 0.35.3

  • Node.js: 12.16.2

  • npm: 6.14.5

  • SAP Cloud SDK for JavaScript:1.19.0

  • SAP Cloud SDK cli: 0.1.8

  • CF cli: 6.51.0+2acd15650.2020-04-07


Cloud Foundry



  • Java Buildpack version: sap java build pack 1.25.0

  • CF (Europe – Frankfurt)


Netweaver ABAP



  • NetWeaver ABAP 7.53 SP0


Cloud Connector



  • SAP Cloud Connector 2.11.2


Seeing following stackoverflow question, invoking function modules/ BAPIs from localhost is not supported.
What you wanna do is technically not supported: You want to invoke a BAPI from your localhost via the transport protocol RFC using the JCo library (used behind the scenes via the SAP Cloud SDK). The usage of JCo requires to run your app on SAP Cloud Platform.

https://stackoverflow.com/questions/59819607/target-host-is-not-specified-exception-while-calling-ba...

I'm not sure if invoking them is possible with Neo SDK.
when deploying locally you must add a dependency to Neo SDK (which also contains the JCO libraries).

https://answers.sap.com/questions/13012430/classnotfoundexception-when-calling-remote-functio.html?c...

Steps


1. Setup Cloud Connector


Access SAP Cloud Connector and setup connection from SAP Cloud Platform to ABAP Server.  The steps are same as the one of  blog post "Configure Cloud Connector for RFC connection from cloud to on-premise".

2. Create Destination


Create Destination service by CF cli command “cf create-service”.  “lite” is service plan name.  The steps are same as the one of  blog post "Configure Cloud Connector for RFC connection from cloud to on-premise".

3. Create Routes for Java and App Router


I did this step on Ubuntu PC.

Firstly create routes for Java application and App Router by cf cli create-route command.

Please replace from <space name> to your space name.  Name <host name of XXX> as you like.
cf create-route <space name> cfapps.eu10.hana.ondemand.com --hostname <host name of App Router>
cf create-route <space name> cfapps.eu10.hana.ondemand.com --hostname <host name of Java App>

4. Create Java Application


I did this step on Windows PC.

With maven, create a Java Application project.
mvn archetype:generate "-DarchetypeGroupId=com.sap.cloud.sdk.archetypes" "-DarchetypeArtifactId=scp-cf-tomee" "-DarchetypeVersion=RELEASE"

I set following information during prompt.

  • groupId: com.sap.cloud.sdk

  • artifactId: test-rfc

  • version: 1.0-SNAPSHOT

  • package: com.sap.cloud.sdk


Change "manifest.yml" file.

  • Add the created route in step 3 to "routers" -> "route"

  • Change values of "random-route"

  • Add the created destination to "services"


---
applications:

- name: test-rfc
memory: 1024M
timeout: 300
routers:
- route: https://<route host name>.<route domain name>
random-route: false
path: application/target/test-rfc-application.war
buildpacks:
- sap_java_buildpack
env:
TARGET_RUNTIME: tomee7
SET_LOGGING_LEVEL: '{ROOT: INFO, com.sap.cloud.sdk: INFO}'
JBP_CONFIG_SAPJVM_MEMORY_SIZES: 'metaspace:128m..'
services:
# - my-application-logs
- <destination name>
# - my-connectivity

 

Thought it is unusual, "Plugins Usage Analytics Maven Plugin" version 3.19.1 doesn't exist on the maven repository.  So I changed the version on "<project root>/application/pom.xml".  In addition, I changed skipUsageAnalytics value.
            <plugin>
<groupId>com.sap.cloud.sdk.plugins</groupId>
<artifactId>usage-analytics-maven-plugin</artifactId>
<!-- changed from 3.19.1 to 3.18.0 -->
<version>3.18.0</version>
<executions>
<execution>
<goals>
<goal>usage-analytics</goal>
</goals>
<configuration>
<!-- changed from false to true -->
<skipUsageAnalytics>true</skipUsageAnalytics>
<generateSalt>true</generateSalt>
<!--
Note: A random salt is auto-generated once the project is built for the first time.
Please keep the generated salt in the POM file, for example, when pushing to git.

To learn more, visit: https://blogs.sap.com/2018/10/23/usage-analytics-s4sdk/
-->
<salt>55578c0d7b7bff985020ad9ce2e033dbb9bd6425acb24eb8d4b40dcc1ba0f9b0</salt>
</configuration>
</execution>
</executions>
</plugin>

After project generation, build and deploy the Java application.
mvn clean package && cf push

Check if the deployment is successful using Curl.  Only this command run on Ubuntu PC.
$ curl https://test-rfc.cfapps.eu10.hana.ondemand.com/hello
Hello World!

5. Create XSUAA service and App Router


I did this step on Ubuntu PC.

5.1. Create App Router Project


I created App Router using SAP Cloud SDK for Javascript cli. During prompt, input Java application name "test-rfc".

It is not mandatory to use the cli.  App Router and relevant codes are very simple.
$ sap-cloud-sdk add-approuter
No 'manifest.yml' found.
Enter project name as maintained in Cloud Foundry: test-rfc
✔ Creating files
Successfully added approuter to your project.

Generated files might need customization. Documentation available here:
- xs-security.json (for help check https://help.sap.com/viewer/4505d0bdaf4948449b7f7379d24d0f0d/2.0.02/en-US/e6fc90df44464a29952e1c2c36...
- xs-app.json (for help check https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/c103fb414988447ead2023f7680...
- mainfest.yml (for help check https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/ba527058dc4d423a9e0a69ecc67...

5.2. Change "xs-security.json"


Change "tenant-mode" value on "aprouter/xs-security.json" file.  I have never used "shared".  Please refer to tutorial and help document for further detail.
{
"xsappname": "test-rfc",
"tenant-mode": "dedicated"
}

5.3. Create XSUAA service


Create XSUAA service using xs-security.json by cf cli command.
cf create-service xsuaa application <xsuaa name> -c xs-security.json

 

bind XSUAA service and the Java application.
cf bind-service test-rfc <xsuaa name>

5.4. Create App Router


Change manifest.yml file.
applications:
- name: test-rfc-approuter
routes:
- route: >-
https://<App Router Route host name><App Router Route domain>
path: .
memory: 128M
buildpacks:
- nodejs_buildpack
env:
# TENANT_HOST_PATTERN: >-
# "test-rfc-(.*).cfapps.sap.hana.ondemand.com"
destinations: >-
[{"name":"test-rfc","url":"https://<Java Route host name>.<Java Route domain>","forwardAuthToken":true}]
services:
- <xsuaa name>

Deploy the App Router.
cf push

Now go to App router route url via browser and redirect to XSUAA service.  After authentication, "Hello World!" is displayed on browser.

7. Create Connectivity


I did this step on Ubuntu PC.

Create Connectivity service.  The Connectivity service is necessary to connect from cloud to on-premise environment according to Help document.
cf create-service connectivity lite <connectivity name>

8. Update Java Application


I did this step on Windows PC.

Change "<project root>/manifest.yml" to bind created services.  Make sure that "env" is also updated.
---
applications:

- name: test-rfc
memory: 1024M
timeout: 300
random-route: false
routers:
- route: https://test-rfc-java.cfapps.eu10.hana.ondemand.com
path: application/target/test-rfc-application.war
buildpacks:
- sap_java_buildpack
env:
TARGET_RUNTIME: tomee7
SET_LOGGING_LEVEL: '{ROOT: INFO, com.sap.cloud.sdk: INFO}'
JBP_CONFIG_SAPJVM_MEMORY_SIZES: 'metaspace:128m..'
xsuaa_connectivity_instance_name: "<xsuaa name>"
services:
- <destination name>
- <xsuaa name>
- <connectivity name>

Create "<project root>/application/src/main/java/com/sap/cloud/sdk/RfcServlet.java".  Please replace from <destination name> to your destination name.  The servlet receive "name" url parameter, which is function module name.  If there is no "name" parameter, "RFC PING" is called.

I haven't implemented security in this Java App, so the app can be called without App Router though it fails.  I posted about the security implementation in another article, though it is in Japanese.
package com.sap.cloud.sdk;


import com.google.gson.Gson;
import com.sap.cloud.sdk.cloudplatform.connectivity.Destination;
import com.sap.cloud.sdk.cloudplatform.connectivity.DestinationAccessor;
import com.sap.cloud.sdk.s4hana.connectivity.exception.RequestExecutionException;
import com.sap.cloud.sdk.s4hana.connectivity.rfc.RfmRequest;
import com.sap.cloud.sdk.s4hana.connectivity.rfc.RfmRequestResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/rfc")
public class RfcServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final Logger logger = LoggerFactory.getLogger(RfcServlet.class);
private static final Destination destinationRfc =
DestinationAccessor.getDestination("<destination name>");

@Override
protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
throws IOException {

logger.info("Start get method: " + request.getRequestURI());
String parameter = request.getParameter("name");
logger.info("Get parameter 'name': " + parameter);
if (parameter == null) {
parameter = "RFCPING";
}
Iterable names = destinationRfc.getPropertyNames();
logger.info(new Gson().toJson(names));

try {
final RfmRequestResult rfmTest = new RfmRequest(parameter, false) //false is for non-commit
.execute(destinationRfc);
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(new Gson().toJson(rfmTest));
} catch (RequestExecutionException e) {
e.printStackTrace();
}
}
}

Finally, build and deploy the Java application.
# from application directory
mvn clean package && cd .. && cf push

 

Result


When I open the Java Application via App router, then result is like this.

https://<App Router route host name>.<App Router route domain>/rfc?name=RFC_TEST_EXTERNAL


There are many words "Error" on the screen. but actually it is not errors.  It just displays error mapping information.

If you access the app without App router, Internal Server Error occurs.
3 Comments
Andy1
Product and Topic Expert
Product and Topic Expert
Dear Yohei Fukuhara,

Thanks a lot for your effort and sharing, it is very helpful.

Best regards,

Tikeer
Animatron56
Participant
0 Kudos
Hello fukuhara ,

thank you for the blog, very nice!

Currently I have the same use case, where I need to call a BAPI / RFC in an onpremise system to adapt a users master data (like first name and SNC).

I tried the approach via a java app to access the BAPI BAPI_USER_UPDATE using your tutorial. I know that the cloud sdk collegues have deprecated the BAPIRequeset and RFMRequest, but I still wanted to try this approach because then I would not have to implement an ODATA service for every system I have.

However, when trying out your approach, I always get the error:

jakarta.servlet.ServletException: Handler dispatch failed: java.lang.NoClassDefFoundError: com/sap/conn/jco/JCoException

Therefore I was wondering if this approach is still possible ? In my opinion it should still be possible since the capability for the BAPI Request is still available in version 5 of the cloud sdk.

Do you have further informations about this or know how I can fix this issue?

Thank you very much !

 
Fukuhara
Advisor
Advisor
0 Kudos
Hi,

 

I'm sorry that I don't have any information.

Though I haven't check the availability, I guess it is available.

 

Regards,

Yohei