Skip to Content
Technical Articles
Author's profile photo Sourav Das

Local Development in SAP Cloud Foundry: A Guide for Software Developers


All of us want to locally develop our Cloud Foundry application, debug locally at high speed, and make repetitive code changes to check application behaviors. However, we couldn’t do it efficiently due to various roadblocks. The solution?

This blog will help you efficiently develop your application from your local machine and overcome all those roadblocks.

This post will cover the following topics:

  • Downloading the Cloud Foundry environment to locally run applications.
  • Building & starting a Java Spring Boot Application within a Tomcat Server.
  • Launching Approuter to send authenticated requests to the Java Backend.
  • Setting up FLP Launchpad to access multiple UI5 applications.

Let’s get started.

Setting Up Your Local Development Environment

We need to clone a Git repository named ‘cloud-foundry-local-setup‘ which includes scripts for downloading the CF environment.

This repository also provides additional scripts for starting the Java and Approuter servers.

Clone Repo

git clone

The folder structure of the repo.

- account-info.json           # contains the cf account information, e.g. org, space, API-endpoint
- accounts-env                # contains the downloaded CF environments
- app-router                  # contains local app router configurations
- apps.json                   # contains a list of applications present in the cf environments
- deps                        # static dependency for local app setup, e.g. Local Tomcat
- env.json                    # contains local app setup settings, e.g. cf account, Shell
- package.json                # contains an npm dependency script to run different apps & app router
- scripts                     # contains local setup scripts

Install dependency

Run below command on the root folder.

npm install

While npm is getting installed, Let’s update the account details.

Update Account Details

To update account information, open the ‘account-info.json’ file and modify the ‘default-account’ details.

Additionally, in the ‘env.json’ file, the ‘activeAccount’ property sets the default active account. Change the ‘activeAccount’ property to set a different account as active.


    "default-account": {
        "org": "sampleorg", //update your cf org
        "space": "samplespace", // update your cf space
        "apiEndpoint": "", // API endpoint of cf account
        "subdomain": "sampleorg" //account subdomain, use consumer subdomain for consumer account login


    "activeAccount": "default-account",
    "shell": {
        "useCustom": false

Cloud Foundry Login

Once, you’ve updated the account details, run the following command to log into the cf account, This command will sign you into the default account using the information provided in the ‘account-info.json’ file.

Dependency: CF CLI should be installed.

npm run cf-login

After executing the above command, the console will prompt you for a passcode. Open the link provided next to the prompt to obtain the passcode and log in to your CF account.

> cloud-foundry-local-setup % npm run cf-login

> cf-login
> cd scripts && node cf-login.js

cf login -o mytrial -s dev --sso -a
API endpoint:

Temporary Authentication Code ( Get one at ): 

Targeted org mytrial

Targeted space dev

API endpoint: (API version: 3.140.0)
Org:            mytrial
Space:          dev
i353584@C02Z10RELVDQ cloud-foundry-local-setup % 

Pulling Cloud Foundry Environment

We need to add the applications for which environment needs to be pulled, Specify the app names & properties in the ‘apps.json’ file.

Note: If you don’t have a ready application(s), a set of sample applications has been provided in the ‘deps’ folder. Build and deploy the ‘sample-java’ and ‘sample-ar’ MTARs.

Update apps.json with applications

Two apps are already added, for the sample applications.

  • app-router
  • java application

You can add your own application using the sample application example provided below. No changes are required to start the sample application locally.

update java-app

	"sample-java": {
		"cfAppName": "sample-java", // your cf application name
		"folder": "./deps/sample-java", // local dir of the spring boot app
		"path": "sample-module/target", // path to war file
		"artifactName": "sample-java.war", // war file name
		"build:cmd": "mvn clean install", // build command
		"start:cmd": "-Denvironment=cf,cloud -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=8000,suspend=n", // add profiles for tomcat start
		"env": {}

update app router details.

	"app-router": {
		"cfAppName": "sample-ar", // only update the cf app-router name
		"folder": "app-router", //don't modify this, 
		"env": {


Ensure that you have updated the ‘apps.json’ file with your CF applications. For each application, make sure the ‘cfAppName’ property is correctly specified. This property is used to query the CF landscape for the application environment.

Once you’ve completed these steps, execute the following command to pull the CF environment to your local machine.

npm run pull-env

This command will execute a script that downloads the application environment from the ‘activeAccount’ specified in ‘env.json’.

Once you’ve run the command, you should see the following message for each of the configured applications:

> cloud-foundry-local-setup % npm run pull-env

> pull-env
> cd scripts && node pull-env.js

==> getting cf oauth-token
bearer ********
==> Org mytrial
==> Space dev
==> fetching cf org mytrial --guid
==> fetching cf space dev --guid
==> fetching cf app sample-ar --guid
==> downloading cf env sample-ar
==> persisting sample-ar environment.
==> fetching cf app sample-java --guid
==> downloading cf env sample-java
==> persisting sample-java environment.
==> updating account-info file.

After executing the command, you will notice an account-env folder has appeared which will contain the vcap.json & vcap-application.json under ‘org/space’ folder.

Starting the Java App

Once the environment is successfully pulled, proceed to start the Java application using a Tomcat server.

Update the ‘apps.json’ file with the appropriate build and start parameters for your application. A ‘sample-java’ app has already been added from the ‘deps/sample-java’ folder as an example.

	"sample-java": {
		"cfAppName": "sample-java", // your cf application name
		"folder": "./deps/sample-java", // local dir of the spring boot app
		"path": "sample-module/target", // path to war file
		"artifactName": "sample-java.war", // war file name
		"build:cmd": "mvn clean install", // build command
		"start:cmd": "-Denvironment=cf,cloud -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=8000,suspend=n", // add profiles for tomcat start
		"env": {} // add environment variables here

After updating the Java app details, we need to modify the start command in the ‘package.json’ file.

"start:sample-java": "cd scripts && node tomcat-adapter.js sample-java" 

Add a new script option by duplicating the ‘sample-java’ script and replacing the ‘sample-java’ application name with the name of the application you are using.

As the tomcat adapter is used, the spring boot java application will start inside a tomcat server.

For sample application.

npm run start:sample-java

For your app.

npm run start:<your_app_name>

If everything is done correctly the application should start easily.

> cloud-foundry-local-setup % npm run start:sample-java

> start:sample-java
> cd scripts && node tomcat-adapter.js sample-java

==> Maven Home  /Users/i353584/.m2
==> App Name: sample-java
==> App Properties {
  cfAppName: 'sample-java',
  folder: './deps/sample-java',
  path: 'sample-module/target',
  artifactName: 'sample-java.war',
  'build:cmd': 'mvn clean install',
  'start:cmd': '-Denvironment=cf,cloud -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=8000,suspend=n',
  env: {}
==> Building App -  mvn clean install
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO] hello-spring-cloud                                                 [pom]
[INFO] sample-module                                                      [war]
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for hello-spring-cloud 0.0.1:
[INFO] hello-spring-cloud ................................. SUCCESS [  3.549 s]
[INFO] sample-module ...................................... SUCCESS [  4.866 s]
[INFO] ------------------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  9.036 s
[INFO] Finished at: 2023-08-23T00:16:50+05:30
[INFO] ------------------------------------------------------------------------
==> setting vcap variables for mytrial - dev

==> using war file from  ./deps/sample-java/sample-module/target/sample-java.war

Listening for transport dt_socket at address: 8000

SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 :: Spring Boot ::                (v2.7.9)

2023-08-22 18:46:58,653 [localhost-startStop-1] INFO []:: Starting ServletInitializer v0.0.1 using Java 11.0.9 on ***** with PID 4726 (/Users/i353584/Documents/cloud-foundry-local-setup/deps/apache-tomcat-8.5.78/webapps/sample-java/WEB-INF/classes started by i353584 in /Users/i353584/Documents/cloud-foundry-local-setup/deps/apache-tomcat-8.5.78/bin) 
2023-08-22 18:46:58,658 [localhost-startStop-1] INFO []:: The following 2 profiles are active: "provider", "cloud" 
2023-08-22 18:47:00,584 [localhost-startStop-1] INFO  o.s.b.w.s.c.ServletWebServerApplicationContext []:: Root WebApplicationContext: initialization completed in 1824 ms 
2023-08-22 18:47:01,530 [localhost-startStop-1] INFO  o.s.s.web.DefaultSecurityFilterChain []:: Will secure any request with [,,,,,,,,,,,,] 
2023-08-22 18:47:01,554 [localhost-startStop-1] WARN  c.s.c.s.x.a.XsuaaAutoConfiguration []:: In productive environment provide a well configured client secret based RestOperations bean 
2023-08-22 18:47:01,707 [localhost-startStop-1] INFO  c.s.s.ApplicationStartOperationsHandler []:: Initial Start 
2023-08-22 18:47:02,224 [localhost-startStop-1] INFO []:: Started ServletInitializer in 4.913 seconds (JVM running for 10.893) 
23-Aug-2023 00:17:02.261 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployWAR Deployment of web application archive [/Users/i353584/Documents/cloud-foundry-local-setup/deps/apache-tomcat-8.5.78/webapps/sample-java.war] has finished in [9,331] ms
23-Aug-2023 00:17:02.264 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-nio-8080"]
23-Aug-2023 00:17:02.279 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 9441 ms

The logs above indicate that the Tomcat Server is running on port 8080, with a debug port at 8000, and a shutdown port at 8005.

Your application will be accessible at http://localhost:8080/<application-name>.

Be aware that some ports might be blocked by other applications on your system. If you encounter this issue, follow the instructions below to change the ports:

  • 8080 & 8005 ports can be changed from ‘deps/apache-tomcat-8.5.78/conf/server.xml’ file.
  • 8000 port can be changed from the start command in the ‘apps.json’ file

Starting App Router

As your backend application requires bearer tokens for each API request, it’s necessary to start the app router. The app router authorizes all requests made to the application.

In the apps.json you had to update the cfAppName of the app-router, consequently the app-router environment should be downloaded on your local machine.

Let’s examine the app-router configuration. If you open the ‘app-router’ folder, you’ll find the following contents:

- destinations.json     # contains destinations to different back-end services.
- xs-app.json           # contains routes, If route matches, forward to respective destination.
- pull-token.js         # script to fetch bearer token of the user.


When you open the destinations.json file you will see a entry name “sample-java” with URL pointing to the backend tomcat service, duplicate the configuration if required for your own tomcat application.

		"name": "sample-java", // destination name
		"url": "http://localhost:8080/sample-java", // url pointing to backing service
		"forwardAuthToken": true, // send bearer token to backend.
		"timeout": 6000000


In this file, you will come across a route with the source set as ‘/sap/opu/rest/java’. It is linked to the ‘sample-java’ destination, which signifies that any attempts to access this URL will route requests to the backend application. Additionally, the ‘authenticationType’ is set to ‘xsuaa’, ensuring that a bearer token is attached to every request. Duplicate these configuration if required for your application.

	"source": "^/sap/opu/rest/java/(.*)$",
	"target": "$1",
	"authenticationType": "xsuaa",
	"destination": "sample-java"

When you are ready, start the app-router by the following command.

npm run start:app-router

When you run the command, it will prompt you for the username (it’s preferable to use a technical user), password, and subdomain details to fetch the bearer token. Once you’ve provided all the necessary information, the command will initiate the app-router on port 9200 and launch the FLP Sandbox.

Prefer using a technical user for the app-router login, add the technical user in your space and org and provide access to it, then use it to login here.

> cloud-foundry-local-setup % npm run start:app-router

> start:app-router
> cd app-router && node index.js -p 9200

#2.0#2023 08 22 22:12:43:517#+05:30#WARNING#/LoggingLibrary################PLAIN##Dynamic log level switching not available#
[session-management] setting up session with identityzone :  mytrial
prompt: Consumer Subdomain:  (mytrial) 
prompt: Email or UserId:  TestTechincalUser
prompt: password:  
[session-management] session created.
#2.0#2023 08 22 22:13:03:679#+05:30#INFO#/approuter#####llmjbkow##########llmjbkow#PLAIN##Application router version 10.15.4#
#2.0#2023 08 22 22:13:03:685#+05:30#INFO#/Configuration#####llmjbkp1##########llmjbkp1#PLAIN##No COOKIES environment variable#
#2.0#2023 08 22 22:13:03:691#+05:30#WARNING#/Configuration#####llmjbkp7##########llmjbkp7#PLAIN## Scopes defined in routes will be ignored.#
#2.0#2023 08 22 22:13:04:043#+05:30#INFO#/approuter#####llmjbkow##########llmjbkow#PLAIN##Application router is listening on port: 9200#

FLP Sandbox


FLP Sandbox

With the App-router running, you can now access your backing service and observe that a bearer token is consistently included with every request.

If you’re using the sample back-end application, try calling http://localhost:9200/sap/opu/rest/java/hello to receive a successful response. Note that this endpoint is inaccessible without a bearer token.

Additionally, you have the option to explore the Sample Fiori application integrated with the Northwind OData service.

Refresh token

The app-router will also persist the refresh token so that when you restart the app-router, it will use the refresh token to obtain the bearer token.

FLP tile & accessing UI application

Once the app-router is up and running, the FLP will be launched, and you’ll notice a pre-configured tile already visible. If you’d like to add your own tile to the FLP, follow these steps:

  • Locate the HTML page at ‘app-router/webapp/flpsandbox.html.’ This page serves as the FLP interface.
  • Within this HTML page, all tiles are configured. Open the page to see the configuration. Look for the ‘sap-ushell-config’ section, where the sample app is configured under the ‘application’ section.
    window["sap-ushell-config"] = {
        defaultRenderer: "fiori2",
        bootstrapPlugins: {
            "KeyUserPlugin": {
                "component": "sap.ushell.plugins.rta"
            "PersonalizePlugin": {
                "component": "sap.ushell.plugins.rta-personalize"
        applications: {
            "sample-app": {
                title: "Sample App", // tile name 
                description: "Sample App for Training",
                additionalInformation: "", // app component id
                applicationType: "URL",
                url: "/resources/com/sap/sample-ui/", // route in xsapp.json to fetch the ui files
                navigationMode: "embedded"

This tile configuration is linked to the ‘xsapp.json’ route ‘/resource/com/sap/sample-ui/’. Within the app-router, this route directs to a local directory where the UI application is located.

xsapp config

	"source": "^/resources/com/sap/sample-ui/(.*)$",
	"target": "$1",
	"cacheControl": "no-cache",
	"localDir": "../deps/sample-ui/sample-ui-html5/webapp"

As you can observe, the ‘localDir’ parameter points to the sample UI application located in the ‘deps’ folder. Ensure that the path includes the ‘webapp’ folder.

In the sample application, the ‘manifest.json’ file should be available under the ‘webapp’ folder. Additionally, make sure to enable cross navigation. While the local FLP functions without cross navigation, the cloud FLP won’t operate properly without it.

manifest.json cross navigation

	"crossNavigation": {
		"inbounds": {
			"intent1": {
				"signature": {
					"parameters": {},
					"additionalParameters": "allowed"
				"semanticObject": "Sample",
				"action": "display"


Assigned Tags

      Be the first to leave a comment
      You must be Logged on to comment or reply to a post.