This blog is about Integration Gateway (IGW) in SAP Mobile Platform 3.0 (SMP).
It is the continuation of a tutorial in which we create an OData service that consists of 2 artifacts, a data provider bundle and the OData service itself.
In the first part we’ve created the OSGi bundle that encapsulates all the logic that deals with data, and we’ve deployed it to SMP.
In the present second part, we’re going to create the OData service which uses the API exposed by the bundle.
The source code can be found attached to this blog post
Note: Again, this blog doesn’t represent the official documentation. It is just my personal way of personal wish of modularization
1. Prepare SMP server
2. Prepare Eclipse
3. Create the OSGi bundle
3.1. Create Plug-In project
3.2. Implement DataProvider class
3.3. Implement Activator class
3.4. Deploy the bundle to SMP
3.5. Verify the deployed bundle
Part II
4. Create the OData service
4.1. Implement the QUERY operation
4.2. Adding $skip and $top capability
4.3. Implement the READ operation
5. Test the OData service
6. Debug the Java code
6.1. Start SMP server in debug mode
6.2. Connect from Eclipse to SMP
6.3. Debug the Java code in Eclipse
7. Summary
8. Links
9. Test the API automatically
9.1. Create test fragment
9.2. Create JUnit tests
9.3. Run the tests
9.4. Summary
In the previous step, we’ve created an OSGi bundle that allows access to some sample data.
Now we want to expose that data as OData service.
We’ll be using SAP Mobile Platform Toolkit in Eclipse to create an OData service based on Integration Gateway and on the "Custom Code" data source
Create OData Implementation Project
In Eclipse, choose from the menu
File->New->Project->Other->SAP Mobile Platform -> OData Implementation Project
Provide a project name, e.g. ExampleProject and choose to create a blank model with an arbitrary name, like examplemodel
The OData model should look like this:
Bind data source
In the present tutorial we’re focusing on how to move large part of implementation code into a separate bundle, in order to keep the custom script as slim as possible. So it doesn’t matter which data source we use.
To keep the example quick and simple, we choose the "Custom Code" data source and bind the entity set to it.
Define dependency
Before we implement the script, we want to declare that our OData service bundle depends on the DataProvider bundle.
Background:
As you’ve already understood, the SMP server is based on OSGi. This means that the OData services that we create with the SMP toolkit, at the end of the day are OSGi bundles running in SMP server as well.
During design time, we’re editing a project of type SMP OData Implementation.
When executing the context menu action “Generate&Deploy…”, an archive is generated and deployed to SMP. On SMP, it gets converted to an OSGi bundle and registered in the OSGi runtime.
This can be verified on the OSGi console, just like we’ve done before.
In order to add the dependency to our DataProvider bundle, open the MANIFEST.MF file at ExampleProject/META-INF
Press "Add" and select the bundle com.example.dataprovider
Save the editor.
Now let’s implement the script.
Open the file ProductSet_SCRIPT.js and delete the generated code and start implementing the getFeed() method.
Before we can call the DataProvider class, we need to import the corresponding package:
importPackage(com.example.dataprovider);
Then we only need to call our DataProvider class to obtain the list with products:
message.setBody(new DataProvider(message).getAllProducts());
This sample code shows: We have implemented an OData service in only ONE line of code.
(because all the implementation is located in a separate bundle)
In our example, the Javascript code for the complete script file looks as short as shown below:
function getFeed(message){
importPackage(com.example.dataprovider); // our DataProvider
importPackage(com.sap.gateway.ip.core.customdev);
message.setBody(new DataProvider(message).getAllProducts());
return message;
}
function getEntry(message) {
return message;
}
function createEntry(message) {
return message;
}
function updateEntry(message) {
return message;
}
function deleteEntry(message) {
return message;
}
After Generate&Deploy, we can invoke our OData service at
https://localhost:8083/gateway/odata/SAP/EXAMPLESERVICE;v=1/ProductSet
The result is as expected:
The separation of implementation logic (Java) from scripting (Javascript or Groovy) starts getting interesting when adding more complexity to your OData service.
Once your OData service supports many system query options like top, skip, filter, orderby and all the operations and also navigation, etc, you’ll appreciate to be able to organize your code in a Java bundle where you can have different packages and layers, etc.
In our example, we want to support 2 system query options: $skip and $top
First step is to delete the line that we’ve written in the previous section, because we have to change it a bit.
In the getFeed() method, we first fetch all products.
Then we check if the request URL contains the query option.
If not, nothing is done and the list which contains all products is returned.
If the query option is available in the request URL, then the query option is applied, which means that the list of all products is reduced, depending on the query option.
All this logic is contained in the DataProvider bundle.
From our OData service script, we only call the corresponding methods.
Error handling
To showcase some error handling we’re doing the following:
In our DataProvider, we throw an exception, if the value of $skip or $top is higher than the total amount of existing entities.
However, as service implementor, we decide that our OData service should be more tolerant. If the user of our service specifies a value that is higher than the total amount, we catch the exception and only write an entry to the log.
Like that, the service doesn’t break, it just ignores the silly query option. :grin:
function getFeed(message){
importPackage(java.util);
importPackage(com.example.dataprovider); // our DataProvider
importPackage(com.sap.gateway.ip.core.customdev); // required for message
importPackage(com.sap.gateway.ip.core.customdev.logging); // required for LogMessage
// instantiate our DataProvider object
var dataProvider = new DataProvider(message);
// call DataProvider to get all the data
var productList = dataProvider.getAllProducts();
// delegate the system query options logic to DataProvider
try{
productList = dataProvider.handleSkip(productList);
productList = dataProvider.handleTop(productList);
}catch(e){
// here, we would ideally set the status code to 400
log.logErrors(LogMessage.TechnicalError, "An error occurred while applying system query options." + e.message);
}
// done
message.setBody(productList);
return message;
}
Let’s spend one more little chapter on coding.
When we refer to the READ operation, we mean a GET request to a single entity,
e.g.
https://localhost:8083/gateway/odata/SAP/EXAMPLESERVICE/ProductSet('3')
In our script, the getEntry() method is invoked.
The task that we have to do here is:
As we know, one single product is requested, which is identified by its ID in the request URL.
Remember, the ID property is marked as key.
So, in our code we have to analyze the URL in order to obtain the value of the "ID" property.
Then we search the list of all products until we find the right one.
Again, all the logic is handled in the DataProvider bundle, the code in the script is actually done in one line:
function getEntry(message) {
importPackage(java.util);
importPackage(com.sap.gateway.ip.core.customdev); // required for message
importPackage(com.example.dataprovider); // our DataProvider
// this call returns a Map for the product requested by the ID in the URL
var productMap = new DataProvider(message).getProduct();
message.setBody(productMap);
return message;
}
One word about error handling
In case that we don’t find the requested product, the DataProvider obviously has to return null.
In the OData service we set null as response body of the message object, which is properly interpreted by the IGW framework.
As such, we don’t need to do anything and the correct response is given by our OData service at runtime:
After Generate&Deploy, we can invoke our OData service as follows
https://localhost:8083/gateway/odata/SAP/EXAMPLESERVICE;v=1/ProductSet?$skip=2
and
https://localhost:8083/gateway/odata/SAP/EXAMPLESERVICE;v=1/ProductSet?$top=1
If we specify an invalid number like this:
https://localhost:8083/gateway/odata/SAP/EXAMPLESERVICE;v=1/ProductSet?$top=6
The result can be seen in the log, the error that we’re logging in our OData service script
If we specify an invalid number like this:
https://localhost:8083/gateway/odata/SAP/EXAMPLESERVICE;v=1/ProductSet?$top=-1
then the validation of the OData lib (Olingo) handles it and gives the proper error message in the browser
The next test is to combine both query options:
https://localhost:8083/gateway/odata/SAP/EXAMPLESERVICE;v=1/ProductSet?$skip=2&$top=1
The expected result:
We skip the products 1 and 2, and from the remaining, we take only the first.
Therefore, the service is expected to return only the Product3
Changing the order gives the same result
https://localhost:8083/gateway/odata/SAP/EXAMPLESERVICE;v=1/ProductSet?$top=1&$skip=2
This is intended as it adheres to the OData specification.
Next step: Debug the Java code running on the SMP server
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
26 | |
14 | |
13 | |
13 | |
12 | |
8 | |
8 | |
7 | |
6 | |
5 |