Developing S/4HANA side-by-side extensions with Java on SAP BTP

Introduction

SAP S/4HANA is an enterprise resource planning software providing end-to-end functionalities to a great number of companies. The core of system was developed to provide 60% of functionalities and in line with a principle ‘Keep the core clean’, all the custom and unique extensions have to build outside of it. Therefore, SAP has developed two types of extensibility:

  • In-app – implemented by means of key user tools inside SAP S/4HANA itself
  • Side-by-side – implemented on SAP BTP (formerly known as SAP Cloud Platform) with programming languages such as Java or Node.JS

In this blog, we are going to explore the latter option of extensibility with a focus on Java programming language.

OData protocol

Since the OData protocol is not broadly used in Java application development, we will take a brief look at this technology.

Open Data Protocol was developed by Microsoft and allows creation and consumption of RESTful APIs in simple and standard way.  It is build on technologies such as HTTP, ATOM/XML and JSON and is widely used in SAP environment. Currently, two versions of OData are available: v2 and v4.

The main element of OData is a metadata document (in XML format), which defines the data model of an application. Basic metadata document is shown below.

 
  
<edmx:Edmx Version="1.0"  xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx" xmlns:sap="http://www.sap.com/Protocols/SAPData">
    <edmx:DataServices xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" m:DataServiceVersion="2.0">
        <Schema xmlns="http://schemas.microsoft.com/ado/2008/09/edm" Namespace="com.axxiome">
            <EntityType Name="Employee">
                <Key>
                   <PropertyRef Name="id"/>
                </Key>
                <Property Name="id" Type="Edm.Int32" Nullable="false"/>
                <Property Name="firstname" Type="Edm.String" Nullable="false"/>
                <Property Name="lastname" Type="Edm.String" Nullable="false"/>
                <Property Name="homeaddress" Type="Edm.String" Nullable="false"/>
                <Property Name="salary" Type="Edm.Int32" Nullable="false"/>
            </EntityType>
            <EntityContainer Name="container" m:IsDefaultEntityContainer="true">
                <EntitySet Name="employee" EntityType="com.axxiome.Employee"/>
            </EntityContainer>
        </Schema>
    </edmx:DataServices>
</edmx:Edmx>

Main method types that allow to perform operations on server resources are:

  • QUERY – fetch all records (equivalent to HTTP GET)
  • READ – fetch a single record (equivalent to HTTP GET)
  • CREATE – create a new record (equivalent to HTTP POST)
  • UPDATE – edit an existing record (equivalent to HTTP PUT)
  • DELETE – delete one record (equivalent to HTTP DELETE)

URL of the OData service is made up of 3 parts: service root URI, resource part and query string options.

 
  
https://odata.service.root.com/api/odata/v2/Company.svc/Employees?$filter=startswith(LastName, Now)eq true 
  

OData protocol provides a bunch of query string options:

  • $orderby – orders the collection of result by a specified property
  • $top – forms a subset of result, by selecting only the first X items of collection
  • $skip – forms a subset of result, by skipping the first X items of collection
  • $filter – forms a subset of result, by selecting the entries that fulfill the predicate expression
  • $expand – allows to fetch the related entries
  • $select – allows to choose only specified properties of the collection

Side-by-side extensibility

S/4HANA extensibility in a cloud-native way on SAP BTP could be leveraged in certain cases. Table below contains a decision matrix presenting scenarios for each extensibility type.

Table 1 Extensibility decision matrix

Microservices is the most preferable choice when it comes to architecture of developed extensions. It means that the system will consist of small, autonomous, independently deployable services compared to a large, monolithic application with a lot of tight coupling. In this approach, applications are developed as a distributed system, where microservices are build around certain characteristics such as business domains etc. In that case, components run independently and are able to communicate with each other by means of HTTP or AMQP. During the spikes in demand, only one microservice must be scaled, not the entire monolith.

Figure 1 Breaking down monolith to microservices

Figure below shows an example architecture of side-by-side extension deployed to SAP BTP. It comprises the following components:

  • Java app – main backend service created in CAP (Cloud Application Programming Model) or typical Spring Boot application. Responsible for executing application business logic, performing operations on database or requests to SAP S/4HANA
  • SAPUI5 app – frontend application build around one of the supported templates. Custom application or Fiori elements app can be leveraged. Requests to Java app are performed with the OData protocol, however the plain REST is also supported
  • Identity and authentication service – service managing application authorizations and connections to identity providers
  • Connectivity service – service enabling establishing connections between cloud applications and on-premise systems
  • Database (HANA / Postgres) – data store
  • Cloud connector independent technical component that is installed on a machine inside the on-premise network, it establishes a secured tunned between the cloud and on-premise network, acts as a reverse proxy to the cloud application
  • SAP S/4HANA ERP system (on-premise or cloud) acting as a backend system to Java application, it exposes OData service (standard or custom) that the Java app can communicate with (performing all enabled operations) by means of SAP Cloud SDK, messaging systems (Event Mesh that is available on SAP BTP) or other solutions

Direct access to the Java application from frontend is not recommended from security point of view, therefore SAP advises to introduce Application Router in-between. It is a simple NodeJS application that dispatches requests to backend microservices. For the sake of clarity, application router has been omitted in the diagram.

Figure 2 Architecture of side-by-side extension

Technology

In this section, we will discover frameworks provided by SAP enabling development of side-by-side extensions. After long parts of text, we will finally take a look on some code!

There are two development technologies available: CAP (Cloud Application Programming Model) and SAP Cloud SDK.

Please note that I’ll give just a brief overview of those technologies, therefore when you wish to get started with an actual development, you will need to refer to official documentation and tutorials.

Cloud Application Programming Model

CAP allows us to build an application exposing OData API and utilizing SAP HANA database (although other databases such as Postgres are also supported). Creating a simple controller and data model doesn’t require a lot of development as the framework does it for us. However, it could hard for a Java developer that never dealt with SAP to quickly get the hang of this technology.

Development of every application begins with defining the data model. This can be done by creating cds files. In our example, we will be developing schema consisting of two entities Employees and Managers. There is a many to one association between them.

Code below shows a content of file schema.cds defining two entities in database

 
  
namespace com.axxiome;

using { cuid }      from '@sap/cds/common';

entity Employees {
   key id : Integer;
   firstname : String(100) not null;
   lastname  : String(100) not null;
   salary    : Integer;
   Manager   : Association to one Managers;
}

entity Managers {
   key id : Integer;
   firstname : String(100);
   lastname  : String(100);
}
  

The next step will be exposing the data as services. Except for Employees and Managers, we are going to expose a service with information about employees earning more than 4000. We used a dedicated annotation @readonly meaning that only QUERY/READ requests are enabled.

 
  
using { com.axxiome as db } from '../db/schema';

service CompanyService {
   entity Employees as projection on db.Employees;
   entity Managers as projection on db.Managers;
   @readonly entity WealthyEmployees as select from db.Employees where salary > 4000;
}
  

We have done very little programming so far, but the outcome is enormous. When we run the application, we can see that we have a fully working CRUD with only half an hour of effort! After accessing localhost:8080, we can see the links navigating to metadata file (which was generated automatically) and data for every service.

Figure 3 CAP starting page with navigation

Currently, we have just showed a basic functionality of CAP. Obviously, it has a lot more to offer! One more example, I would like to present is a custom coding in data access layer. If we wanted to select e.g. only one column from the Employee entity by his/her id, we could write the following code:

 
  
@Autowired
CdsRuntime runtime;

public Result getEmployeeById(Long id) {
    
    ServiceCatalog catalog = runtime.getServiceCatalog();
    ApplicationService companyService = catalog.getService(ApplicationService.class, "CompanyService");
    CqnSelect select = Select.from("CompanyService.Employees").columns("firstname").byId(id);
    
    return companyService.run(select);
}
  

SAP Cloud SDK

Main purpose of SAP Cloud SDK is creating seamless integrations with SAP S/4HANA systems. It allows us to perform CRUD operations on standard and custom OData services. Additionally, we can produce a OData API to expose data to the client.

The primary difference between SAP Cloud SDK and CAP is that the former one fully employs the industry-standard frameworks and libraries: Hibernate, Spring Boot etc. It means that there is no huge effort to grasp it by a Java developer.

Since, the data model of application is created in a standard manner for Spring applications, we will focus on creating the integration with SAP S/4HANA.

DestinationAccessor class lets to fetch the details of S/4HANA system from connectivity service on SAP BTP. We just need to reference a system name (here: “S4HANA”) and return the ErpHttpDestination object.

In our example, we are aiming to call API_OUTBOUND_DELIVERY_SRV_0002, which is a standard service in S/4. It is also possible to communicate with custom OData services, however in that case we’d need a metadata file to generate VDM (Virtual Data Model).

SAP Cloud SDK provides us DefaultOutboundDeliveryV2Service class which we’ll use to create a final method.

In getOutbDeliveryItem(String area) method in code below, we are fetching all records with entry in Controlling Area column starting with string provided by the user. The method selects only one column with Material.

 
  
private DefaultOutboundDeliveryV2Service outBoundDeliveryService;

    ErpHttpDestina-tion destination = DestinationAccessor.getDestination("S4HANA").asHttp().decorate(DefaultErpHttpDestination::new);

    @PostConstruct
    public void postConstruct() {
        outBoundDeliveryService = new DefaultOutboundDeliveryV2Service();
    }

    public List getOutbDeliveryItem(String area) {
        List list = outBoundDeliveryService
            .getAllOutbDeliveryItem()
            .select(OutbDeliveryItem.MATERIAL)
            .filter(OutbDeliveryItem.CONTROLLING_AREA.startsWith(area))
            .executeRequest(destination);
        return list;
    }
  

As mentioned before, we are also going to create an OData API. Firstly, it is required to create the metadata file (unfortunately, it is not auto-generated, unlike in CAP) reflecting the data model. The next step is to code the controller, that will expose data to the client.

The method providing CREATE functionality for Employee service looks as follows:

 
  
public EmployeeService service = ApplicationContextInstanceProvider.getInstanceTyped(EmployeeService.class);

    @Create(serviceName = "CompanySrv", entity = "employee")
    public CreateResponse createEmployee(CreateRequest request) {

        Employee employee = request.getDataAs(Employee.class);
        Employee employeeCreated = service.saveEmployee(employee);
        return CreateResponse.setSuccess().setData(employeeCreated).response();
  
    }
  

We are accessing the class of Service layer to get the actual data. We need to annotate the method with @Create and return CreateResponse object.

As we have seen, creating the controller layer in the application is fairly simple and doesn’t differ significantly from a standard rest controller.

If you wish to use SAP Cloud SDK along with CAP – it is possible too! Refer to official documentation in order to find out details.

Conclusion

Creating side-by-side extensions is a fantastic way to take advantage of state-of-the-art architectural patterns and technologies such as microservices, cloud, modern frameworks and libraries in creating custom functionalities to SAP S/4HANA. At the outset of the project, we need to determine the best possible solution for a scenario, whether it is classic ABAP implementation or Java/Spring Boot app running on SAP BTP. Decision matrix included in this blog may be of help. SAP has evolved great tools in conjunction with an abundance of resources (opensap.com courses, Learning Hub, blogs, tutorials, certification) to accomplish project goals.

Written by
Maciej Dłużeń

No items found.