Skip to content
Search

    Deploy as Multitenant SaaS Application

    This guide explains how to deploy CAP multitenant SaaS applications to SAP BTP Cloud Foundry environment or Kyma environment, and how SaaS Customers would subscribe to them.

    Intro & Overview

    Software-as-a-Service (SaaS) solutions are deployed once by a SaaS provider, and then used by multiple SaaS customers subscribing to the software.

    SaaS applications need to register with the SaaS Provisioning Service to handle subscribe and unsubscribe events. In contrast to single-tenant deployments, databases or other tenant-specific resources aren’t created and bootstrapped upon deployment, but upon subscription per tenant.

    CAP includes the MTX services, which provide out-of-the-box handlers for subscribe/unsubscribe events, with automatic creation of SAP HANA database containers or SAP Event Mesh instances.

    If everything is set up, the following graphic shows what’s happening when a user subscribes to a SaaS application:

    saas-overview.drawio

    1. The SaaS Provisioning Service sends a subscribe event to the CAP application.
    2. The CAP application delegates the request to the MTX services.
    3. The MTX services use Service Manager to create the database tenant.
    4. The CAP Application connects to this tenant at runtime using Service Manager.

    Prepare for Production

    The detailed procedure is described in the Deploy to Cloud Foundry guide.

    git clone https://github.com/sap-samples/cloud-cap-samples samples
    cd samples/bookshop
    

    To start with this guide, you fulfill all Prerequisites and went through Prepare for Production, or instead run this command to fast-forward:

    cds add hana,xsuaa,approuter,mta --for production
    

    Add MTX

    To enable MTX services for your project, run the following command:

    cds add mtx --for production
    

    This modifies the modules and resources in mta.yaml for multitenant deployment.

    Learn more about MTX services.

    Build & Deploy

    Cloud Foundry Environment

    You know the build and deploy steps from the Deploy to Cloud Foundry guide.

    mbt build -t gen --mtar mta.tar
    

    Learn more about Build & Assemble

    cf deploy gen/mta.tar
    

    Learn more about Deploy to Cloud Foundry.

    Kyma

    You know the build and deploy steps from the Deploy to Kyma guide.

    1. To add the Helm Chart to your project, run the following command:

       cds add helm
      
    2. To Build Approuter Image, run the following command:

       pack build bookshop-approuter \
           --path app \
           --buildpack gcr.io/paketo-buildpacks/nodejs \
           --builder paketobuildpacks/builder:base \
           --env BP_NODE_RUN_SCRIPTS=
      
    3. To Build CAP Node.js Image, run the following command:

       pack build bookshop-srv \
           --path gen/srv \
           --buildpack gcr.io/paketo-buildpacks/nodejs \
           --builder paketobuildpacks/builder:base \
           --env BP_NODE_RUN_SCRIPTS=
      

      Learn more about Building & Pushing Images

    4. Follow the steps mentioned in Deploy using CAP Helm Chart and configure your Helm Chart.

    5. Add backend destinations for Approuter using the backendDestinations property:

       backendDestinations:
         srv-api:
           service: srv
         mtx-api:
           service: srv
      
    6. To deploy your Helm Chart, run the following command:

       helm upgrade --install bookshop ./chart
      

    Subscribe

    Create a BTP subaccount to subscribe to your deployed application. This subaccount has to be in the same region, for example, us10.

    Global Account view

    In your subscriber account go to Instances and Subscription and select Create. subscriber account

    Select bookshop and use the only available plan default. Inst

    Learn more about subscribing to a SaaS application using the SAP BTP cockpit. Learn more about subscribing to a SaaS application using the btp CLI.

    Go to Application. subscribed bookshop

    As you can see, your route doesn’t exist yet. You need to create and map it first.

    Leave the window open. You need the information to create the route.

    route doesn't exist

    Cloud Foundry

    Switch to your provider account and go to your space → Routes Route Screen

    Click New Route New Route wizard

    Use the broken route to fill the fields. For the following explanations let’s use this route as example: https://my-account.cfapps.acme.com

    • As Domain, use the part that usually starts with cfapps… and ends with .com

      In our example, the domain is: cfapps.acme.com

    • As Host Name, use the part between https:// and .cfapps

      In our example, host name is: my-account

    • Click Save

    You can now see the route is created but not mapped yet (none). Overview

    Click Map Route Map Route

    Select bookshop, which is the App Router application we just deployed, and save. Overview with new mapped route

    Kyma

    Use the broken route to fill the fields. For the following explanations let’s use this route as example: https://my-account.c-abcdef.kyma.ondemand.com

    • As Host, use the part between https:// and c-abcdef

      In our example, host is: my-account

    Create an API Rule using the following code:

    apiVersion: gateway.kyma-project.io/v1beta1
    kind: APIRule
    metadata:
      name: <your-api-rule-name>
    spec:
      gateway: kyma-gateway.kyma-system.svc.cluster.local
      host: my_account
      service:
        name: <your-release-name>-approuter
        port: 8080
      rules:
      - accessStrategies:
        - handler: allow
        methods:
        - GET
        - POST
        - PUT
        - PATCH
        - DELETE
        - HEAD
        path: /.*
    

    You’ve successfully created the route for your subscribed bookshop multitenant application. Refresh the browser tab of the bookshop application and log in.

    Behind the Scenes

    With adding the MTX services, your project configuration is adapted at all relevant places. Configuration and dependencies are added to your package.json and an xs-security.json containing MTX-specific scopes and roles is created.

    For the MTA deployment service dependencies are added to the mta.yaml file. Each SaaS application will have bindings to at least three SAP BTP service instances.

    Service Description
    Service Manager (service-manager) CAP uses this service for creating a new SAP HANA Deployment Infrastructure (HDI) container for each tenant and for retrieving tenant-specific database connections.
    SaaS Provisioning Service (saas-registry) To make a SaaS application available for subscription to SaaS consumer tenants, the application provider must register the application in the SAP BTP Cloud Foundry environment through the SaaS Provisioning Service.
    User Account and Authentication Service (xsuaa) Binding information contains the OAuth client ID and client credentials. The XSUAA service can be used to validate the JSON Web Token (JWT) from requests and to retrieve the tenant context from the JWT.

    If you’re interested, use version control to spot the exact changes.

    Appendix — Configuration

    SaaS Service Dependencies

    If you require SAP reuse services, that need subscriptions, use cds.mtx.dependencies in your package.json and pass an array of their xsappnames.

    The SaaS Provisioning service (getDependencies handler) needs this information.

    "mtx": {
      "dependencies": ["xsappname-1", "xsappname-2"]
    }
    

    Maven Dependencies

    Multitenancy support is available as optional application feature of the CAP Java SDK. It’s already included when you use the cds-starter-cloudfoundry dependency. Otherwise, you can add the following Maven dependency to apply the feature:

    <dependency>
        <groupId>com.sap.cds</groupId>
        <artifactId>cds-feature-mt</artifactId>
    </dependency>
    

    Having added this dependency to your project, multitenancy support becomes only active when additional services are configured as described in Configuring Required Platform Services and Adding the MTX Sidecar Module.

    Local Development and Testing

    You can run your application locally with the MTX sidecar and SQLite database in multitenant mode. To do that, add the necessary dependencies and configure MTX sidecar to your project to enable multitenancy and set the following configuration in a new spring profile in your application.yaml file:

    spring:
      config.activate.on-profile: local-mtxs
    cds:
      multi-tenancy:
        mtxs:
          enabled: true
        sidecar:
          url: http://localhost:4004
    

    Add mock users with the tenants you want to use.

    Add SQLite JDBC driver to your POM:

    <dependency>
        <groupId>org.xerial</groupId>
        <artifactId>sqlite-jdbc</artifactId>
        <scope>runtime</scope>
    </dependency>
    

    The default project template for CAP does not include it by default. H2 database is not supported in this mode.

    If you use the default-env.json file to use services from Cloud Foundry for local development, set the property cds.environment.local.defaultEnvPath to a directory that doesn’t contain this file. Then, the SQLite database generated by the local sidecar will be used instead. It’s best to do that with the separate Spring profile to keep both options available.

    Start the MTX sidecar as described in the Multitenancy guide and keep it running. For example, use command cds watch mtx/sidecar in the root directory of your project or cds watch in the directory of the sidecar in a separate terminal. Adjust the default port in the application.yaml file, if sidecar is running on different port.

    Use subscription endpoints of CAP Java or endpoints of the sidecar directly to create tenant databases. Your CAP Java application will pick up SQLite files created by the sidecar.

    If you have configured the location of SQLite files in the MTX sidecar and CAP doesn’t pick up the files automatically, use the configuration option cds.multiTenancy.mock.sqliteDirectory.

    Configuring Platform Services

    To enable multitenancy on SAP BTP, three services are involved:

    Only when these services are bound to your application, the multitenancy feature is turned on. You can either create and configure these services manually. See section Developing Multitenant Applications in SAP BTP, Cloud Foundry Environment for more details. The following sections describe how to configure and bind these services by means of an mta.yaml file.

    XSUAA

    A special configuration of the application’s XSUAA service instance is required to enable authorization between the SaaS Provisioning service, CAP Java application, and MTX sidecar.

    The service can be configured in the mta.yaml by adding an xsuaa resource as follows:

    resources:
      [...]
      - name: xsuaa
        type: com.sap.xs.uaa
        parameters:
          service-plan: application
          path: ./xs-security.json
          config:
            xsappname: <appname>
    

    Choose a value for property xsappname that is unique globally.

    Also, you have to create an Application Security Descriptor (xs-security.json) file, which must include the scope mtcallback. The mtcallback scope is required by the onboarding process.

    You can also use a custom scope name by configuring it. Use the following application configuration properties: cds.multitenancy.security.subscription-scope

    An example xs-security.json file looks like this:

    {
      "tenant-mode": "shared",
      "scopes": [
        {
          "name": "$XSAPPNAME.mtcallback",
          "description": "Multi Tenancy Callback Access",
          "grant-as-authority-to-apps": [
            "$XSAPPNAME(application, sap-provisioning, tenant-onboarding)"
          ]
        },
        {
          "name": "$XSAPPNAME.cds.ExtensionDeveloper",
          "description": "Extend CAP applications via extension projects"
        }
      ],
      "role-templates": [
        {
          "name": "ExtensionDeveloper",
          "description": "Extension development",
          "scope-references": [
            "$XSAPPNAME.cds.ExtensionDeveloper"
          ]
        }
      ]
    }
    

    In this example, the grant-as-authority-to-apps section is used to grant the mtcallback scope to the applications sap-provisioning and tenant-onboarding. These are services provided by SAP BTP involved in the onboarding process. The ExtensionDeveloper scope and role can be used to grant SaaS Customers the capabilities to extend the application, if the extensibility feature is enabled.

    Learn more about extending SaaS applications.

    It isn’t necessary to have the security configuration in a separate file. It can also be added to the mta.yaml file directly.

    ❗ Warning The mtcallback scope must not be exposed to any business user, for example, using a role template. Else a malicious user could update or even delete the artifacts of arbitrary tenants.

    Service Manager

    A service instance of the Service Manager (service-manager) is required for CAP Java to create and use database containers per tenant at application runtime. It doesn’t require special parameters and can be added as a resource in mta.yaml as follows:

    resources:
      [...]
      - name: service-manager
        type: org.cloudfoundry.managed-service
        parameters:
          service: service-manager
          service-plan: container
    

    SaaS Provisioning Service

    A service instance of the SaaS Provisioning service (type saas-registry) is required to make your application known to the SAP BTP Provisioning Service and to register the endpoints that should be called when tenants are added or removed. The service can be configured as a resource in mta.yaml as follows. See section Register the Multitenant Application to the SaaS Provisioning Service for more details.

    resources:
      [...]
      - name: saas-registry
        type: org.cloudfoundry.managed-service
        parameters:
          service: saas-registry
          service-plan: application
          config:
            appName: <app display name>
            xsappname: <appname>
            appUrls:
              getDependencies: ~{srv/url}/mt/v1.0/subscriptions/dependencies
              onSubscription: ~{srv/url}/mt/v1.0/subscriptions/tenants/{tenantId}
              onSubscriptionAsync: true
              onUnSubscriptionAsync: true
              onUpdateDependenciesAsync: true
              callbackTimeoutMillis: 3600000
        requires:
          - name: srv
    

    It’s required to configure the parameters:

    • appName: Choose an appropriate application display name.
    • xsappname: Use the value for xsappname you configured at your UAA service instance.
    • appUrls: Configure the callback URLs used by the SaaS Provisioning service to get the dependencies of the application and to trigger a subscription. In the above example, the property ~{srv/url} is used, which is provided by the srv module. See section Configuring the Java Service for more details. If you use different module and property names for your CAP Java backend module, you have to adapt these properties here accordingly.

    Make sure to choose the asynchronous subscription (onSubscriptionAsync: true) and unsubscription mode (onUnSubscriptionAsync: true). Always keep callbackTimeoutMillis in sync with cds.multitenancy.provisioningservice.pollingTimeout (default: 10min).

    Adding the MTX Sidecar

    This section describes how to use the cds-mtxs Node.js module and add the MTX sidecar microservice to the mta.yaml file.

    To define the dependencies and start command, create a subfolder named mtx/sidecar, and within that folder create a file named package.json :

    {
      "cds": {
        "requires": {
          "[production]": {
            "db": "hana-mt",
            "auth": "xsuaa"
          },
          "[development]": {
            "db": "sqlite",
            "auth": "dummy"
          },
          "cds.xt.ModelProviderService": "in-sidecar",
          "cds.xt.DeploymentService": true,
          "cds.xt.SaasProvisioningService": true,
          "cds.xt.ExtensibilityService": true
        }
      },
      "devDependencies": {
        "sqlite3": "^5"
      },
      "engines": {
        "node": "^18"
      },
      "scripts": {
        "start": "cds run",
        "build": "npm install && cds build ../.. --for mtx-sidecar && cd gen && npm install"
      }
    }
    

    Please note, that it’s required to activate the ModelProviderService in the sidecar application so that tenant-specific CDS models are served at runtime. In contrast, ExtensibilityService is only required for projects that allow custom extensions at runtime.

    Activate multitenancy in the main application’s .cdsrc.json. If required, activate extensibility and/or feature toggles as well:

    {
      "requires": {
        ...
        "multitenancy": true,
        "extensibility": true,
        "toggles": true,
        ...
      }
    }
    

    Learn more about MTX services and their configuration.

    Next, add the required dependencies by calling the below command in the mtx/sidecar directory:

    npm add @sap/cds @sap/cds-mtxs @sap/xssec hdb express passport
    

    The cds-mtxs module requires @sap/cds version 6 at least.

    Because the MTX sidecar needs to know the base CDS model and features, you additionally need to configure the build task by means of the .cdsrc.json file in the root folder of your project:

    {
        "requires": {
            "multitenancy": true,
            "extensibility": true,
            "toggles": true
        },
        "build": {
            "target": ".",
            "tasks": [
                {
                    "for": "java"
                },
                {
                    "for": "mtx-sidecar"
                },
                {
                    "for": "hana"
                }
            ]
        },
        "hana": {
            "deploy-format": "hdbtable"
        }
    }
    

    This configuration relates to the default project folder layout with app, db, srv, fts and mtx/sidecar. If you have a different layout, please adapt the configuration accordingly.

    Make sure to run cds build with @sap/cds-dk version 6 at least.

    A detailed description of this configuration file can be found in section Build Configuration.

    Now, add the mtx-sidecar module to your mta.yaml file:

    modules:
      [...]
      - name: mtx-sidecar
        type: nodejs
        path: mtx/sidecar
        build-parameters:
          builder: custom
          build-result: gen
          commands:
            - npm run build
        parameters:
          memory: 256M
          disk-quota: 512M
        requires:
          - name: xsuaa
          - name: service-manager
        provides:
          - name: sidecar
            properties:
              url: ${default-url}
    

    The mtx-sidecar module requires the XSUAA and Service Manager services. Also the sidecar exposes its own URL (note that ${default-url} is a placeholder for the own URL) which is required to configure the service module. The Cloud MTA Build Tool executes the build script which is provided in mtx/sidecar/package.json to build the MTX Sidecar application.

    Configuring the Java Service

    To make use of the previously mentioned services and the MTX sidecar in your CAP Java multitenant application, you need to enhance the srv module in the mta.yaml file:

    modules:
      [...]
      - name: srv
        type: java
        path: srv
        parameters:
          [...]
        requires:
          - name: service-manager
          - name: xsuaa
          - name: saas-registry
          - name: sidecar
            properties:
              CDS_MULTITENANCY_MTXS_ENABLED: true
              CDS_MULTITENANCY_SIDECAR_URL: ~{url}
          - name: app
            properties:
              CDS_MULTITENANCY_APPUI_URL: ~{url}
              CDS_MULTITENANCY_APPUI_TENANTSEPARATOR: "."
        provides:
          - name: srv
            properties:
              url: '${default-url}'
    
    • The environment variable CDS_MULTITENANCY_MTXS_ENABLED sets application property cds.multitenancy.mtxs.enabled and is used to activate streamlined MTX (default is classic MTX).
    • CDS_MULTITENANCY_SIDECAR_URL sets the application property cds.multitenancy.sidecar.url. This URL is required by the CAP Java runtime to connect to the MTX Sidecar application and is derived from the property url of the mtx-sidecar module.
    • Similarly, CDS_MULTITENANCY_APPUI_URL configures the URL that is shown in the SAP BTP Cockpit. Usually it points to the app providing the UI, which is the module app in this example.

    The tenant application requests are separated by the tenant specific app URLs. The tenant specific URL is made up of:

    https://<subaccount subdomain><CDS_MULTITENANCY_APPUI_TENANTSEPARATOR><CDS_MULTITENANCY_APPUI_URL>
    

    Currently only the value "." is allowed for CDS_MULTITENANCY_APPUI_TENANTSEPARATOR. The actual URL shown in the SAP BTP Cockpit is then composed of:

    https://<subaccount subdomain>.<CDS_MULTITENANCY_APPUI_URL>
    

    Deployment to Kyma

    Build

    1. Follow the Build Images Guide to build your CAP Java and Approuter Image.

    2. To build your sidecar image, run the following commands:

       cds build --production
      
       pack build bookshop-sidecar --path mtx/sidecar/gen \
       --buildpack gcr.io/paketo-buildpacks/nodejs \
       --builder paketobuildpacks/builder:base \
       --env BP_NODE_RUN_SCRIPTS=""
      
    3. Follow the Push Images Guide and push images to your registry.

    Deploy

    1. To add the Helm Chart to your project, run the following command:

       cds add helm
      
    2. Follow the steps mentioned in Deploy using CAP Helm Chart and configure your Helm Chart.

    3. To deploy your Helm Chart, run the following command:

       helm upgrade --install bookshop ./chart
      

    Adding Custom Handlers

    The SaaS Provisioning service in SAP BTP sends specific requests to applications when tenants are subscribed or unsubscribed. For these requests, CAP Java internally generates CAP events on the technical service DeploymentService.

    For a general introduction to CAP events, see Event Handlers.

    Register event handlers for the following CAP events to add custom logic for requests sent by the SaaS Provisioning service. Each event passes a special type of EventContext object to the event handler method and provides event-specific information:

    Event Name Event Context Use Case
    DEPENDENCIES DependenciesEventContext Dependencies
    SUBSCRIBE SubscribeEventContext Add a tenant
    UNSUBSCRIBE UnsubscribeEventContext Remove a tenant

    You only need to register event handlers to override the default behavior.

    Default behaviors:

    • A new tenant-specific database container is created through the Service Manager during subscription.
    • The tenant-specific database container is deleted during unsubscription.

    Note, that by default a compatibility mode is enabled to ensure compatibility with the old MtSubscriptionService API. If this mode is enabled, the database container is not deleted during unsubscription by default. Please refer to Multitenancy (Classic) > Unsubscribe Tenant for more information.

    The following sections describe how to register to these events in more detail.

    Subscribe Tenant

    Subscription events are generated when a new tenant is added. By default, subscription creates a new database container for a newly subscribed tenant. This happens during the @On phase of the SUBSCRIBE event. You can add additional @On handlers to perform additional subscription steps. Note, that these @On handlers should not call setCompleted(), as the event processing is auto-completed.

    The following examples show how to register custom handlers for the SUBSCRIBE event:

    @Before
    public void beforeSubscription(SubscribeEventContext context) {
        // Activities before tenant database container is created
    }
    
    @After
    public void afterSubscribe(SubscribeEventContext context) {
        // For example, send notification, ...
    }
    

    Tenant Unsubscription

    By default, the tenant-specific database container is deleted during off-boarding. This happens during the @On phase of the UNSUBSCRIBE event. You can add additional @On handlers to perform additional unsubscription steps. Note, that these @On handlers should not call setCompleted(), as the event processing is auto-completed.

    The following example shows how to add custom logic for the UNSUBSCRIBE event:

    @Before
    public void beforeUnsubscribe(UnsubscribeEventContext context) {
        // Activities before off-boarding
    }
    
    @After
    public void afterUnsubscribe(UnsubscribeEventContext context) {
        // Notify off-boarding finished
    }
    

    If you are accessing the tenant database container during unsubscription, you need to wrap the access into a dedicated ChangeSetContext or transaction. This ensures that the transaction to the tenant database container is committed, before the container is deleted.

    Skipping Deletion of Tenant Containers During Tenant Unsubscription

    By default, tenant-specific resources (for example, database containers) are deleted during removal. However, you can register a customer handler to change this behavior. This is required, for example, in case a tenant is subscribed to your application multiple times and only the last unsubscription should remove its resources.

    @Before
    public void beforeUnsubscribe(UnsubscribeEventContext context) {
        if (keepResources(context.getTenant())) {
          context.setCompleted(); // avoid @On handler phase
        }
    }
    

    Returning Dependencies

    The event DEPENDENCIES fires when the SaaS Provisioning service calls the getDependencies callback. Hence, if your application consumes any reuse services provided by SAP, you must implement the DEPENDENCIES event to return the service dependencies of the application. The event must return a list of all of the dependent services’ xsappname values. CAP automatically adds dependencies of services to the list, for which it provides dedicated integrations. This includes AuditLog and Event Mesh.

    The xsappname of an SAP reuse service that is bound to your application can be found as part of the VCAP_SERVICES JSON structure under the path VCAP_SERVICES.<service>.credentials.xsappname.

    The following example shows this in more detail:

    @Value("${vcap.services.<my-service-instance>.credentials.xsappname}")
    private String xsappname;
    
    @On
    public void onDependencies(DependenciesEventContext context) {
        List<Map<String, Object>> dependencies = new ArrayList<>();
        dependencies.add(SaasRegistryDependency.create(xsappname));
        context.setResult(dependencies);
    }
    

    Returning a Database ID

    When you’ve registered exactly one SAP HANA instance in your SAP BTP space, a new tenant-specific database container is created automatically. However, if you’ve registered more than one SAP HANA instances in your SAP BTP space, you have to pass the target database ID for the new database container in a customer handler, as illustrated in the following example:

    @Before
    public void beforeSubscription(SubscribeEventContext context) {
        context.getOptions().put("provisioningParameters",
            Collections.singletonMap("database_id", "<database ID>"));
    }
    

    Logging Support

    Logging service support gives you the capability to observe properly correlated requests between the different components of your CAP application in Kibana. This is especially useful for multi-tenant aware applications that use the MTX sidecar. Just enable the Cloud Foundry application-logs service for both, the Java service (see details in Observability > Logging) as well as for the MTX sidecar, to get correlated log messages from these components.

    This can easily be seen in Kibana, which is part of the ELK Stack (Elasticsearch/Logstash/Kibana) on Cloud Foundry and available by default with the application-logs service:

    Kibana screenshot

    Subscribe & Unsubscribe

    The CAP Java SDK offers main methods for Subscribe/Unsubscribe in the classes com.sap.cds.framework.spring.utils.Subscribe/Unsubscribe that can be called from the command line. This way, you can run the tenant subscribe/unsubscribe for the specified tenant. This would trigger also your custom handlers, which is useful for the local testing scenarios.

    You can stop the CAP Java back end when you call this method, but the MTX Sidecar application must be running!

    The main method optionally takes tenant ID (string) as the first input argument and tenant options (JSON string) as the second input argument. Alternatively, you can use the environment variables MTCOMMAND_TENANTS and MTCOMMAND_OPTIONS instead of arguments. The command-line arguments have higher priority, so you can use them to override the environment variables.

    The method returns the following exit codes.

    Exit Code Result
    0 Tenant subscribed/unsubsribed successfully.
    3 Failed to subscribe/unsubscribe the tenant. Rerun the procedure to make sure the tenant is subscribed/unsubscribed.

    To run this method locally, use the following command where <jar-file> is the one of your applications:

    java -cp <jar-file> -Dloader.main=com.sap.cds.framework.spring.utils.Subscribe/Unsubscribe org.springframework.boot.loader.PropertiesLauncher <tenant> [<tenant options>]
    

    In the SAP BTP Cloud Foundry environment, it can be tricky to construct such a command. The reason is that the JAR file is extracted by the Java buildpack and the place of the Java executable isn’t easy to determine. Also the place differs for different Java versions. Therefore, we recommend adapting the start command that is generated by the buildpack and run the adapted command:

    sed -i 's/org.springframework.boot.loader.JarLauncher/-Dloader.main=com.sap.cds.framework.spring.utils.Subscribe/Unsubscribe org.springframework.boot.loader.PropertiesLauncher/g' /home/vcap/staging_info.yml && jq -r .start_command /home/vcap/staging_info.yml | sed 's/^/ MTCOMMAND_TENANTS=my-tenant [MTCOMMAND_TENANTS=<tenant options>]/' | bash
    

    If you use Java 8, you need to use the following command:

    sed -i 's/org.springframework.boot.loader.JarLauncher/org.springframework.boot.loader.PropertiesLauncher/g' /home/vcap/staging_info.yml && sed -i 's/-Dsun.net.inetaddr.negative.ttl=0/-Dsun.net.inetaddr.negative.ttl=0 -Dloader.main=com.sap.cds.framework.spring.utils.Subscribe/Unsubscribe/g' /home/vcap/staging_info.yml && jq -r .start_command /home/vcap/staging_info.yml | sed 's/^/ MTCOMMAND_TENANTS=my-tenant [MTCOMMAND_TENANTS=<tenant options>]/' | bash
    

    Database Schema Update

    When shipping a new application version with an updated CDS model, the database schema for each subscribed tenant needs an update. The database schema update needs to be triggered explicitly.

    When the database schema update is triggered, the following CAP events are sent.

    Event Name Event Context
    UPGRADE UpgradeEventContext

    By registering custom handlers for these events, you can add custom logic to influence the deployment and upgrade process of a tenant. By default, the CAP Java SDK notifies the MTX Sidecar to perform any schema upgrade if necessary.

    It’s often desired to update the whole service in a zero downtime manner. This section doesn’t deal with the details about updating a service productively, but describes tool support the CAP Java SDK offers to update tenants.

    The following sections describe how to trigger the update for tenants, including the database schema upgrade.

    Deploy Main Method

    The CAP Java SDK offers a main method in the class com.sap.cds.framework.spring.utils.Deploy that can be called from the command line while the CAP Java application is still stopped. This way, you can run the update for all tenants before you start a new version of the Java application. This prevents new application code to access database artifacts that aren’t yet deployed.

    While the CAP Java backend might be stopped when you call this method, the MTX Sidecar application must be running!

    This synchronization can also be automated, for example using Cloud Foundry Tasks on SAP BTP and Module Hooks in your MTA.

    The main method takes an optional list of tenant IDs as input arguments. If tenant IDs are specified, only these tenants are updated. If no input parameters are specified, all tenants are updated. The method waits until all deployments are finished and then prints the result.

    The method returns the following exit codes

    Exit Code Result
    0 All tenants updated successfully.
    1 Failed to update at least one tenant. Re-run the procedure to make sure that all tenants are updated.

    To run this method locally, use the following command where <jar-file> is the one of your application:

    java -cp <jar-file> -Dloader.main=com.sap.cds.framework.spring.utils.Deploy org.springframework.boot.loader.PropertiesLauncher [<tenant 1>] ... [<tenant n>]
    

    In the SAP BTP, Cloud Foundry environment it can be tricky to construct such a command. The reason is, that the JAR file is extracted by the Java Buildpack and the place of the Java executable isn’t easy to determine. Also the place differs for different Java versions. Therefore, we recommend to adapt the start command that is generated by the buildpack and run the adapted command:

    sed -i 's/org.springframework.boot.loader.JarLauncher/-Dloader.main=com.sap.cds.framework.spring.utils.Deploy org.springframework.boot.loader.PropertiesLauncher/g' /home/vcap/staging_info.yml && jq -r .start_command /home/vcap/staging_info.yml | bash
    

    If you use Java 8, you need to use the following command:

    sed -i 's/org.springframework.boot.loader.JarLauncher/org.springframework.boot.loader.PropertiesLauncher/g' /home/vcap/staging_info.yml && sed -i 's/-Dsun.net.inetaddr.negative.ttl=0/-Dsun.net.inetaddr.negative.ttl=0 -Dloader.main=com.sap.cds.framework.spring.utils.Deploy/g' /home/vcap/staging_info.yml && jq -r .start_command /home/vcap/staging_info.yml | bash
    

    For local development you can create a launch configuration in your IDE. For example in case of VS Code it looks like this:

    {
        "type": "java",
        "name": "MTX Update tenants",
        "request": "launch",
        "mainClass": "com.sap.cds.framework.spring.utils.Deploy",
        "args": "", // optional: specify the tenants to upgrade, defaults to all
        "projectName": "<your project>",
        "vmArgs": "-Dspring.profiles.active=local-mtxs" // or any other profile required for MTX
    }
    

    MtSubscriptionService API Compatibility

    The former MtSubscriptionService API described in Multitenancy (Classic) has been deprecated, but can still be used in a compatibility mode. This compatibility mode is enabled by default and can be disabled by setting application property cds.multitenancy.compatibility.enabled to false.

    In this compatibility mode the event handlers as described in Multitenancy (Classic) are still triggered in addition to the event handlers of the new API. Therefore the UNSUBSCRIBE event no longer deletes the tenant database container by default. Refer to Multitenancy (Classic) > Unsubscribe Tenant for more information.

    As part of the compatibility mode the HTTP-based deploy REST API is also available. However it only uses the deprecated MtSubscriptionService API. It doesn’t trigger the UPGRADE event of the new API.

    Once all event handlers have been migrated to the new API and the compatibility mode is no longer required it is recommended to disable the compatibility mode. The compatibility mode will be removed again in a future major release.

    Configuration Properties

    A number of multitenancy settings can be configured through application configuration properties. See section Application Configuration for more details. They can be found in the following table. The prefix for multitenancy-related settings is cds.multitenancy.

    Name Description Default
    servlet.path Path of the subscription and deployment endpoints /mt/v1.0/subscriptions
    servlet.enabled Flag to deactivate the endpoints true
    datasource.pool Pool to use for the tenant-dependent data source. Possible values are: hikari, tomcat, atomikos hikari
    datasource.<pool>.<property> Pool-specific properties. See the tools documentation for HikariCP, Apache Tomcat, and Atomikos for more details. Default depends on the pool and property
    datasource.combinePools.enabled Only one data source pool per database is created. false
    servicemanager.timeout Timeout for requests to the Service Manager in seconds 3600
    security.subscriptionScope Scope necessary to call the subscription endpoints mtcallback
    sidecar.url URL of the MTX sidecar Not set by default
    app-ui.url URL of the application UI Not set by default
    app-ui.tenantSeparator Separator of the tenant in the URL, for example, “.” or “-“ Not set by default
    mtxs Enables the usage of @sap/cds-mtxs module (MTX streamlined) false
    provisioning.pollingTimeout The maximum waiting time for the provisioning operation to finish 10 minutes
    mock.enabled Option to deactivate local multitenancy mode. true
    mock.sqliteDirectory Relative path to directory where SQLite files for tenants are stored. Not set by default. Only specify if the MTX sidecar stores database files in a non-default location, as CAP discovers database files in common directories of the project.
    deploy.componentScan Specifies the package to be included in component scan of Deploy Main Method with custom handlers. Not set by default
    📓