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: 
CarlosRoggan
Product and Topic Expert
Product and Topic Expert
OK, the first blog of this series was meant for those of you who don’t like reading – for this one you’ll need much more patience, as it will contain many many words….

Take a breath (and a coffee) and read on…

 
Who are we and why?

We are nice people
We are lazy people
We are encouraged people
We are Java-loving people
We are service-oriented people
We want to create OData services with Java
We want to benefit from the Cloud
We want to relax

 

Intro


Once upon a time…. The Olingo library was created, allowing to create OData services in Java

Nowadays… creating OData services is fast and easy (and explained...), using the SAP Cloud Platform SDK for service development (I will just call it “The SDK”)
Why?

Because it is shorter
No: Why is it fast and easy?
(and explained?)

Because: Less code (and nice tutorials explaining this few code)
Yes, the SDK removes the need of the famous boilerplate code.
It is a framework on top of Olingo (THE OData library for Java), trying to handle as much as possible, allowing the developer to focus on one main task: providing data.
The framework also provides generic functionality, such that we as developers don’t need to take care (e.g. $top, $select, $expand, etc)

More info can be found in this introduction blog and in SAP Help Portal

 

Learnings


The friendly reader of this blog will learn how to use the SAP Cloud Platform SDK for service development ("The SDK" how I call it),
And how to implement a very very basic READ operation for a first OData service

 

Project


Everything starts with the beginning – the creation of a project.
The SDK provides a maven archetype which makes it easier to create a (standard) web application project  with support for OData provisioning based on the SDK.
Therefore, the following description will be using Eclipse and Maven.

See here for prerequisites.

Follow this blog for a detailed description on how to create a project.

After we've created our project, the next step is to define our model.

 

Model


Why "Model" ?

Yes, we follow the model first approach.
We like to talk about cool things like data model. What we mean is just: data.
We mean data which is structured and has relationships.

For example:
We store data about people. People have in common: person with name and age and address and job etc.
Job has its own structure with name, category etc
If we look at a person, then we find its information about name etc, but the info about job we retrieve by navigating to the data for the respective job.
Does this help?

OK, the above example is a small data model and can be nicely defined with OData.
The data model is described with edmx

With other words:
We define an entity data model (EDM) and persist it in XML (edmx)
For that, we create a file with file extension .xml (it is created in src/main/resources/edmx, see here)

Once we've created the xml file in the edmx folder, we can start to define the model in the appropriate format.
What is our intention?

For the first very simple OData service, we don’t want to lose time with complexity (as you can see, I’m already spending many words for the simple case…), so we choose a very simple model.

It has only one entity, nothing else.
And even this entity is so small that it contains only 2 properties.

You can think of it as one table which has 2 columns.
How does our mode look like?

I thought of choosing a very simple and human example: People

As such, our model contains one entity called Person
The person needs a unique id, such that it can be identified.
This id is a number, so we choose the data type integer.
In OData it is the primitive type Edm.Int32

The person is a human, so it has a human name
The name is human readable, so the data type is string
In terms of OData, it is Edm.String

Furthermore, in OData there’s a difference between the definition of a type (EntityType) - which is our Person - and accessing the instances, i.e., the data itself, at runtime. For that we need to specify a so-called  EntitySet. We call it People and it refers to the EntityType Person.
Why “People”?

Because data is provided as “set” or “list” of instances, hence the name of the entity set should be plural.

Oh – and one more thing:
The entity set must be wrapped by an entity container

Ups – even one last thing:
All of it is contained in a “Schema”

 

And here is how it looks like, when defined in edmx:
<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
<edmx:DataServices>
<Schema Namespace="demo" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityType Name="Person">
<Key>
<PropertyRef Name="UniqueId" />
</Key>
<Property Name="UniqueId" Type="Edm.Int32" />
<Property Name="Name" Type="Edm.String" />
</EntityType>
<EntityContainer Name="container" >
<EntitySet Name="People" EntityType="demo.Person" />
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>

 

Now that you have understood this quite readable xml snippet, you can copy all of it and paste it into the DemoService.xml file which you have created in eclipse.

Don’t forget to save the file before closing.

Ups - Have you closed it?

My suggestion: better reopen it, as you’ll have to refer to it soon…

 

Code code code


Now we’re coming to our favorite part:
Writing code
Writing easy code
Writing easy code using convenient framework
Writing easy code using convenient framework and be superfast and enterprise ready and happy  and and and

Ok, I’m stopping with marketing  bullsh***
What is it about the code...?

1. First of all: No code
This “no code” statement is about the model. The model has been defined in xml
The framework will parse it and convert it into code.
I’m mentioning it, because if you’ve ever created a service based on Olingo, you must have written many many lines of code to define the model.

2. Secondly: the data
One thing is to define the OData model, i.e., the structure of the data that we want to provide with our service.
Obviously, more important is the actual data that we want to provide.
To follow our example: one thing is to have an address book which allows to enter people in a defined structured way. But more important is to have friends who we want to store in the address book and foes and other contacts.

The SDK aims to take over as much work from the developer as possible. But at the same time, let him have as much freedom and flexibility as possible.
This has been achieved with the SAP Cloud Platform SDK for service development.

Oh, sorry… now I promise that this has been the last marketing bu***

 
How does it work?

We don’t need to register anything anywhere, we don’t need to adhere to any convention rules, the only thing that is required: use a Java annotation.
anno-what?

You’ll see.

 

Now it is time to create a Java class. It may have any user-defined name, but it must be located in the generated empty package. See here for details.

In the new and freshly opened Java editor, create a method.
What method?

Just like for the Java class, again, you’re absolutely free to give any arbitrary name for this method.

This method is called by the framework whenever the user of our future OData service invokes a  URL which points to a single entity.
Such request is usually referred to as READ request.
We'll come to that at the end of this blog.

So, the user fires an HTTP request and then the framework identifies which kind of request was executed by the user.
If it is a READ request, then the FWK will call a method which as a @Read annotation.
That's why we have to add the @Read annotation to our method:

 
@Read(...)
public ... anyMethodName(...) {
...
}

 

But this is not enough, there must be some more information:
The service name and the entity set name
Since there can be many models and many entity sets (within a project), the framework needs to know, for which service and which resource our method is meant to be called.

So, when we write the code, it is meant for one specific entity type. However, there’s still some more information which we will need when writing advanced services, so the framework gives us an instance containing context information, in case we need it: the Request object

 
@Read(serviceName="DemoService", entity="People")
public ReadResponse getPerson(ReadRequest request) {
...
}

 
OK, enough introduction.

No, not enough.

 

In our example, our OData service is called by an external user who expects exactly one “Person” to be returned by our service.
So we have to provide that “Person” instance in our code.
It needs to have a structure just like we defined it in our model.
I mean it has an id and a name.
In Java, such structured data can be easily defined as a java.util.Map

The Map instance represents the entity type
The content of the map represents the properties. One Map-entry corresponds to one property.
Each entry of the map has
one key, which represents the property name
one value, which represents the property value

In our example, we need to create one Map with 2 entries, for the 2 properties.
The keys have to match exactly the text as defined in the xml.

That’s why I recommended to have the xml file open… 😉
Map<String, Object> map = new HashMap<String, Object>();
map.put("UniqueId", 1);
map.put("Name", "Kitty");

 
Enough intro ?

Sorry, one last point.

 

The method has a return value.
Of course, that’s how we provide the data to the framework.
The return value carries the instance of the Map.
In addition, our OData service will provide the external user with an HTTP response, which we can
configure.
In our example, we decide that the HTTP call is successful, and as such, the response body will contain one Person’s data.

About the data:
We want to keep our sample code very very simple, so our way to provide the data is to create some dummy data on the fly.
Of course, in a real scenario, the data would be fetched from a database or any other storage.
...?

OK, enough intro.

Here's the complete method to be copied into your editor:
(see below for the full source code)

 
@Read(serviceName="DemoService", entity="People")
public ReadResponse getPerson(ReadRequest request) {

Map<String, Object> map = new HashMap<String, Object>();
map.put("UniqueId", 1);
map.put("Name", "Kitty");

return ReadResponse.setSuccess().setData(map).response();
}

 

Repeated explanation:

The user calls a URL like <service1>/entityset1(id1)
The FWK searches for a method with @Read annotation and attributes service1 and entityset1
The FWK invokes the method and passes an instance of ReadRequest carrying the info about id
The service developer collects the requested data for id1
The service developer, if necessary, converts the data into instance of java.util.Map
The service developer returns the data, if everything is fine

 

Build


After the coding is done, we have to build the Java project.
No surprise that Maven is used to build our project.

You can open a command prompt, navigate to your project folder and execute
mvn package

or for subsequent builds
mvn clean package

 

(of course you can also run mvn clean install)

 



 

Alternatively, you can open a command prompt directly in eclipse itself

 



and execute the build from there

 



 

Alternatively2, you can execute a maven build in Eclipse using the Maven integration

 



 

Then enter the goals: clean package
and press Apply to save the settings

 



 

After pressing Apply, under the hood a so-called “Launch Configuration” has been created.
You may verify it by clicking on “Run” from the main menu bar, then Run Configurations or “Debug Configurations”



 

Then you’ll see that eclipse has generated an entry under “Maven Build”, pointing to your “DemoService” project.

 



 

The advantage of this approach is that you can always re-launch the last launch operation by pressing Ctrl+F12 (or just F12 for debug)
(however, this has to be configured in Window->Preferences->Run/Debug->Launching and enable “Always launch the previously launched application”)

 

Now that we've learned 3 ways of building our project, it is enough.

After successful build, the expected DemoService.war file has been generated in the “target” folder of your project.
This file is what will be deployed onto the Java server in the cloud.

 

Deploy


Here we have to be careful...otherwise, this chapter might explode and become a never-ending story…
We cannot expect a full blown introduction in SAP Cloud Platform and Cloud Foundry and CLI here.
Sorry…
So please take care to get started into SAP Cloud Platform and Cloud Foundry environment.
You may refer to my “Prerequisites” blog.

For now, please let me be short and describe the straight-forward way, using the browser (instead of the CLI)

 

Logon to your cloud account

Assuming that you’ve gone through the Prerequisites blog, you have your account and your Cloud Foundry Space.
And you’ve logged in
And you’ve opened the “applications” overview screen.
And you can see a screen like this:



 

And you can see the “Deploy Application” button.
And you’re able to press it.
And you’re able to enter the path to your DemoService.war file located on your local file system:



And you can press “Deploy”
And you have some patience to wait for the deployment to be finished (don’t worry, it doesn’t take too long….)
And you’ve so much patience that you’ve seen the nice green “Started” button



And you’ve been happy and immediately clicked on the hyperlink, which is your application
And… now you may go ahead to the next section

 

Run


After clicking on the hyperlink which represents your Cloud Foundry application, you’re taken to the application details screen.
There you can see the application route:



 

A route is the URL to call the application.
An application can have zero or multiple routes, depending on the use case.

Click on the link, it will open in a separate browser window

 



 

However, this is not the URL of our OData service, it is just the web application.
So please do me the favor to append the following segments:
/odata/v4/

 

The result is not yet the URL of our service, but here the SDK is able to react and display a list of the detected OData services.

In our case it is just one:

 



 

 

So, finally... FINALLY !!! ...  you can click on that link

https://<yourApp>.cfapps.sap.hana.ondemand.com/odata/v4/DemoService/

and enjoy viewing your first OData service, created with THE SDK

This fantastic moment deserves a big screenshot:

 



 

What you’re viewing here is the so-called “Service document”, it displays information about the defined entity sets.
In our case it is just one, according to our small model.

Now I’d like to ask you to append the entity set name to the URL, and also the unique identifier of the entity which we’re providing in our service implementation.

So, the URL to invoke in the browser should look like this:
https://<yourApp>.cfapps.sap.hana.ondemand.com/odata/v4/DemoService/People(1)

and the result:



 

What we see is no surprise, it is the (little bit silly) data which we created in our service implementation.

But don’t allow anybody to spoil the party….

 

The joy is all yours…!


 

You’ve created your first OData service and it has been easy and you’ve enabled your users to access your data, as you intended and now…. NOW… now all the world is yours and you can provide whatever data you wish…!

YOU and THE SDK….


Together you can change the world….!


😉


 

 

Anyways…. The first thing you should do is…. To relax and to continue with the next blogs in this series…;-)

 

 

 

Summary


In this blog we've taken our time to slowly go through the process of creating an OData V4 service based on the SAP Cloud Platform SDK for service development.
We've defined a very very small OData model (really small) and created a very small implementation for the service.
We've focused on a very small aspect of providing data. One piece of data is provided as a HashMap, that's it.

 

Outlook


However, there are more operations supported by REST and HTTP and there are many more concepts specified by OData.
We'll cover much of it in the next blogs of this series.

Stay tuned and don't get tired .... and if you get tired, then please relax and come back...!...;-)

 

Links


Overview of blog series and link collection

 

Appendix: Source code


For writing our sample OData service, we just had to touch 2 files.

In brief:
Generate the project via archetype
Create DemoService.xml files
Create ServiceImplementation.java file

 

Below you find the content of these 2 files


Model file: DemoService.xml


 
<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
<edmx:DataServices>
<Schema Namespace="demo" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityType Name="Person">
<Key>
<PropertyRef Name="UniqueId" />
</Key>
<Property Name="UniqueId" Type="Edm.Int32" />
<Property Name="Name" Type="Edm.String" />
</EntityType>
<EntityContainer Name="container" >
<EntitySet Name="People" EntityType="demo.Person" />
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>

 

 

Java Class: ServiceImplementation.java


 

 
package com.example.odata.DemoService;

import java.util.HashMap;
import java.util.Map;

import com.sap.cloud.sdk.service.prov.api.operations.Read;
import com.sap.cloud.sdk.service.prov.api.request.ReadRequest;
import com.sap.cloud.sdk.service.prov.api.response.ReadResponse;

public class ServiceImplementation {

@Read(serviceName="DemoService", entity="People")
public ReadResponse getPerson(ReadRequest request) {

Map<String, Object> map = new HashMap<String, Object>();
map.put("UniqueId", 1);
map.put("Name", "Kitty");

return ReadResponse.setSuccess().setData(map).response();
}

}

 

 
29 Comments