Skip to main content

Develop your App for the Kyma Environment with Java SDK

Kyma is an application runtime that provides you a flexible and easy way to connect, extend, and customize your applications in the cloud-native world of Kubernetes. Find more details in the official documentation.

Document is outdated!

While the Kyma runtime is improving over time, recent changes unfortunately rendered parts of this document outdated. We are currently re-evaluating and updating the information provided. Please follow the official documentation.

Workaround for On-Premise Connectivity

Recent changes of the Kyma runtime environment prevent the SAP Cloud SDK's On-Premise connectivity from working as intended. While we are working on a permanent solution, please use the following workaround:

  1. Add following class to your project:

    public class OnPremiseHeaderProvider implements DestinationHeaderProvider {

    @Override
    @Nonnull
    public List<Header> getHeaders(
    @Nonnull final DestinationRequestContext requestContext
    ) {
    final Option<ProxyType> proxyType = requestContext
    .getDestination()
    .get("ProxyType", ProxyType.class);
    if (proxyType.getOrElse(ProxyType.INTERNET) != ProxyType.ON_PREMISE) {
    return Collections.emptyList();
    }

    final Option<AuthenticationType> authentication = requestContext
    .getDestination()
    .get("authentication", AuthenticationType.class);
    if (
    authentication.getOrElse(AuthenticationType.NO_AUTHENTICATION) !=
    AuthenticationType.PRINCIPAL_PROPAGATION
    ) {
    return Collections.emptyList();
    }

    final Try<String> maybeBearerToken = AuthTokenAccessor
    .tryGetCurrentToken()
    .map(token -> String.format("Bearer %s", token.getJwt().getToken()));

    if (maybeBearerToken.isEmpty()) {
    return Collections.emptyList();
    }

    return Collections.singletonList(
    new Header("SAP-Connectivity-Authentication", maybeBearerToken.get())
    );
    }
    }

  2. Create following file in your application resources:

    src/main/resources/META-INF/com.sap.cloud.sdk.cloudplatform.connectivity.DestinationHeaderProvider
  3. Fill the file with the fully qualified name of the newly created class. For example, the content should look like this:

    name.of.your.package.OnPremiseHeaderProvider

SAP Cloud SDK Features Supported on Kyma by SAP

Find below the list of features we currently support: Legend: ✅ - supported, ❗- partially supported, ❌ - not supported

  • ✅ Consume SAP BTP services like Destination, Connectivity, IAS, XSUAA, and others
  • ✅ Multitenancy
  • ✅ Resilience & Caching
  • ✅ Connect to and consume services from SAP S/4HANA Cloud
  • ✅ Connect to and consume services from SAP S/4HANA On-Premise
  • ✅ Seamless use of typed clients provided by the SAP Cloud SDK

Getting Started with the SAP Cloud SDK on Kyma

This detailed guide will help get your SAP Cloud SDK Java application up and running in Kyma. You can also use this guide to migrate your existing application to Kyma.

Prerequisites

To follow this guide you will need:

Tip

When deploying applications to Kyma, you are not limited to Spring Boot applications. In fact, you may deploy any application that can be executed within a Docker container. The examples in this document, however, do use a Spring Boot app.

Check out the details below in case you are uncertain about any of the prerequisites.

Kyma Environment

This guide assumes you already have a Global Account, a subaccount on SAP BTP platform and have Kubernetes CLI installed on your local machine. If not, please refer to these guides on getting a Global Account and creating a subaccount

In your subaccount in SAP BTP cockpit, create a Kyma Environment instance. Make sure that you select an appropriate plan and also assign the necessary roles to users.

Kyma Setup Time

Creating a Kyma environment instance can take up to 30 minutes.

Once your Kyma environment is created, you can download a Kubeconfig file directly from the SAP BTP cockpit.

Kubeconfig file download

Docker

This guide assumes you have Docker installed on your local machine.

Furthermore, you need a Docker repository where you can store images. The repository needs to be publicly accessible in order for the cluster to access and download the Docker image we are going to create.

In case you don't have such a repository yet we recommend either:

Access to images in a repository may be limited to authenticated and/or authorized users, depending on your configuration.

Make sure you are logged in to your repository on your local machine by running:

docker login (your-repo) --username=(your-username)

And check your configuration which is usually located under (your-home-directory)/.docker/config.json.

tip

In case AuthN/AuthZ is required to download images make sure you have a secret configured in your cluster

kubectl create secret docker-registry (name-of-the-secret) --docker-username=(username) --docker-password=(API-token) --docker-server=(your-repo)
Application using the SAP Cloud SDK

If you don't have an application already you can comfortably create one from our archetypes.

Containerize the Application

To run on Kyma the application needs to be shipped in a container. For this guide we will be using Docker.

Create a Dockerfile in the project root directory:

FROM openjdk:8-jdk-slim
ARG JAR_FILE=application/target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

If needed, update the JAR_FILE to point to your JAR file.

tip

You can find more information on how to containerize Spring Boot applications in this guide (in particular, check the Containerize It section).

Compile and push the image by running:

docker build -t <your-repo>/<your-image-name> .
docker push <your-repo>/<your-image-name>

# alternatively, you may add a tag to your image
docker build -t <your-repo>/<your-image-name>:<your-tag> .
docker push <your-repo>/<your-image-name>:<your-tag>
Tip

In case you are facing authorization issues when pushing to your repository refer to the dedicated section under Prerequisites.

Create a Kyma Deployment

  1. Create a new YAML file:

    deployment.yml
     apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: my-app-deployment
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: my-app
    template:
    metadata:
    labels:
    app: my-app
    spec:
    containers:
    # Configure the docker image you just pushed to your repository here
    - image: <name-of-the-image>
    name: my-app
    imagePullPolicy: Always
    resources:
    requests:
    memory: '1.5Gi'
    cpu: '500m'
    limits:
    memory: '2Gi'
    cpu: '1000m'
    # Volume mounts needed for injecting SAP BTP service credentials
    volumeMounts:
    imagePullSecrets:
    # In case your repository requires a login, reference your secret here
    - name: <your-secret-for-docker-login>
    volumes:
    ----
    apiVersion: v1
    kind: Service
    metadata:
    labels:
    app: my-app
    name: my-app
    spec:
    type: NodePort
    ports:
    - port: 8080
    targetPort: 8080
    protocol: TCP
    selector:
    app: my-app
  2. Install the configuration via kubectl apply -f path/to/deployment/file/deployment.yml.

  3. Monitor the status of the deployment by running: kubectl get deployment my-app-deployment.

Eventually, you should see an output similar to:

kubectl get deployment my-app-deployment

NAME READY UP-TO-DATE AVAILABLE AGE
my-app-deployment 1/1 1 1 15s
tip

In case something went wrong use kubectl describe together with deployment or pod to get more information about the status of your application.

Access Your Application

To access your application without further configuration you need to find the name of the application pod by listing out all available pods:

kubectl get pods

Now use the name of the application pod and forward the application port to your local machine:

kubectl port-forward [pod-name] [local-port]:[application-port]

In case you started with an SAP Cloud SDK Archetype, you can test the hello endpoint:

curl localhost:[local-port]/hello
Example

If your application is running on port 8080 (default configuration) in it's pod, you can use the following command to gain access to your app:

kubectl port-forward my-app-pod-XYZ :8080

Please note that we skipped the [local-port] parameter in the command above. This will let kubectl choose a random available port on your local machine every time you run the command.

If everything worked as expected, you should see an output similar to the following:

Forwarding from 127.0.0.1:50309 -> 8080
Forwarding from [::1]:50309 -> 8080

In this example, kubectl chose the local port 50309. Hence, you should be able to successfully run the following curl command:

curl localhost:50309/hello
SSH Access to your Pods

If port-forwarding is not enough for you, you can also get SSH access to your application pods:

kubectl exec --stdin --tty [pod-name] -- /bin/sh

Bind SAP BTP Services to the Application

The SAP Business Technology Platform offers various services that can be used by applications. To access services from a Kyma environment, instances have to be created and bound to the application.

For this guide we'll assume we want to use two services:

  1. Destination Service
  2. XSUAA Service

Bind the Destination Service

  1. Open your Kyma dashboard by clicking the link in your SAP BTP cockpit:

Kyma Console URL

  1. In the Kyma dashboard, select your Kyma namespace (top right hand side of the screen), navigate to Service Management > Catalog, and select the Destination service.

Destination Service Selection

  1. Configure your new service instance by first clicking on the Add + button followed by selecting both a name and a plan.

  2. Finalize the service instance creation by confirming your configuration with a click on the Create button.

Destination Service Creation

Alternative: Using Kubernetes Descriptors
  1. Create a new YAML file :
destination-service.yaml
---
apiVersion: servicecatalog.k8s.io/v1beta1
kind: ServiceInstance
metadata:
name: destination-service
spec:
clusterServiceClassExternalName: destination
clusterServicePlanExternalName: <your-desired-plan>
---
apiVersion: servicecatalog.k8s.io/v1beta1
kind: ServiceBinding
metadata:
name: my-destination-service-binding
spec:
instanceRef:
name: destination-service
secretName: my-dest-secret
  1. Install the configuration via kubectl apply -f destination-service.yml.

  2. Monitor the status via kubectl describe ServiceInstance destination-service. Eventually this should automatically create a Kubernetes secret named my-dest-secret. This secret will contain the actual service binding information.

  3. Mount the my-dest-secret secret into the file system of the application as follows:

    1. Find the empty list of volumes at the end of your deployment.yml. Add a new volume, referencing the secret:

      volumes:
      - name: my-destination-service-binding-volume
      secret:
      secretName: my-dest-secret
    2. Mount this volume into the file system of your application. Add it to the empty list of volumeMounts in the container section of your deployment.yml:

      volumeMounts:
      - name: my-destination-service-binding-volume
      mountPath: '/etc/secrets/sapcp/destination/my-destination-service-binding'
      readOnly: true
  4. Update the configuration via kubectl apply -f deployment.yml.

Bind the XSUAA Service

  1. Open your Kyma dashboard (see previous section) and select the Authorization and Trust Management Service in Service Managament > Catalog:

XSUAA Service Selection

  1. Configure your new service instance by clicking the Add + button and selecting both a name and a plan or your new service instance. Complete the process by clicking the Create button.

XSUAA Service Creation

Alternative: Using Kubernetes Descriptors
  1. Create a new YAML file :
xsuaa-service.yaml
---
apiVersion: servicecatalog.k8s.io/v1beta1
kind: ServiceInstance
metadata:
name: xsuaa-service
spec:
clusterServiceClassExternalName: xsuaa
clusterServicePlanExternalName: <your-desired-plan>
---
apiVersion: servicecatalog.k8s.io/v1beta1
kind: ServiceBinding
metadata:
name: my-xsuaa-service-binding
spec:
instanceRef:
name: xsuaa-service
secretName: my-xsuaa-secret
  1. Repeat the same steps 2-5 from the previous section but make use of my-xsuaa-secret for the steps.

Known XSUAA Service Incompatibility

We have observed that the property plan is missing in the XSUAA secret created in the previous section. This property, however, is required by the SAP Cloud SDK and it's absence causes runtime errors in the application while trying to read this service binding.

To fix this issue you need to edit the secret so that it contains the plan property. The easiest way, when you are already using the CLI, is by using the kubectl edit command:

kubectl edit secrets my-xsuaa-secret

In there, you can now add a plan property with one of the following base64 encoded values:

planbase64 encoded value
applicationYXBwbGljYXRpb24K
brokerYnJva2VyCg==
spacec3BhY2UK
apiaccessYXBpYWNjZXNzCg==

The resulting service binding can now be consumed with the SAP Cloud SDK.

Expose your Application to the Internet

During the next steps, you will configure and deploy an approuter so that only authenticated users can access your application. For that, you will need to create a simple application that uses the @sap/approuter and starts it for this purpose.

  1. Based on the approuter application you used, containerize your application and push the image to a docker repository of your choice.

  2. Create a new YAML file describing the approuter deployment:

    deployment.yml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: my-approuter-deployment
    labels:
    app: my-approuter
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: my-approuter
    template:
    metadata:
    labels:
    app: my-approuter
    spec:
    imagePullSecrets:
    - name: <your-secret-for-docker-login>
    containers:
    # Configure the docker image of your approuter application here
    - image: <name-of-the-image>
    imagePullPolicy: Always
    name: my-approuter
    env:
    - name: PORT
    value: '5000'
    - name: destinations
    value: >
    [
    {
    "name": "<name-for-the-backend-app>",
    "url": "<URL-to-access-backend-app>",
    "forwardAuthToken": true
    }
    ]
    - name: TENANT_HOST_PATTERN
    value: <URL-pattern-for-outside-cluster-access>
    volumeMounts:
    - name: my-xsuaa-service-binding-volume
    mountPath: '/etc/secrets/sapcp/xsuaa/my-xsuaa-service-binding'
    volumes:
    - name: my-xsuaa-service-binding-volume
    secret:
    secretName: my-xsuaa-secret
    restartPolicy: Always

    ---
    apiVersion: v1
    kind: Service
    metadata:
    labels:
    app: my-approuter
    name: my-approuter
    spec:
    type: NodePort
    ports:
    - port: 5000
    targetPort: 5000
    protocol: TCP
    nodePort: 31002
    selector:
    app: my-approuter
  3. Also adapt the xs-app.json file:

{
"welcomeFile": "/web-pages/index.html",
"routes": [
{
"source": "/<path-for-backend-endpoints>/(.*)",
"target": "$1",
"destination": "<name-for-the-backend-app>",
"identityProvider": "<optional-identity-provider-id>",
"csrfProtection": false
}
]
}
  1. Follow steps similar to steps 2-3 in Create a Kyma Deployment section to deploy the approuter and check if the deployment is successful.

  2. Update the already created XSUAA service instance with few parameters like xsappname and oauth2-configuration. This is required to provide authenticated access to backend application via the approuter.

xsuaa-service.yaml
---
apiVersion: servicecatalog.k8s.io/v1beta1
kind: ServiceInstance
metadata:
name: xsuaa-service
spec:
clusterServiceClassExternalName: xsuaa
clusterServicePlanExternalName: <your-desired-plan>
parameters:
xsappname: <your-backend-app-name>
oauth2-configuration:
allowedproviders:
- <your-idp-key>
redirect-uris:
- <URL-pattern-for-outside-cluster-access>
---
apiVersion: servicecatalog.k8s.io/v1beta1
kind: ServiceBinding
metadata:
name: my-xsuaa-service-binding
spec:
instanceRef:
name: xsuaa-service
secretName: my-xsuaa-secret
  1. Update the configuration via kubectl apply -f xsuaa-service.yaml.

  2. Make the approuter accessible outside the cluster by creating an API rule:

create-api-rule.yml
apiVersion: gateway.kyma-project.io/v1alpha1
kind: APIRule
metadata:
name: <your-rule-name>
spec:
gateway: kyma-gateway.kyma-system.svc.cluster.local
rules:
- accessStrategies:
- config: {}
handler: noop
methods:
- GET
path: /.*
service:
host: <subaccount-prefix>.<URL-pattern-for-outside-cluster-access>
name: my-approuter
port: 5000
  1. Update the configuration via kubectl apply -f create-api-rule.yml.

  2. Finally, access the application by using the host address provided in the API rule created and appending the path-for-backend-endpoints given in xs-app.json along with the an actual end point of your backend application.

tip

You need not necessarily push an Approuter to access your application outside a cluster. You could also go ahead and expose the service for your application directly by creating an APIRule. But, this would mean that access to your application would not be authenticated first.

You can also refer to this additional material available for getting started with SAP Cloud SDK on Kyma: