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: 
mibo1
Explorer

How to consume an OData Service of SAP NetWeaver Gateway with Apache Olingo


This article shows how to consume (Read) an OData Service with support of the Apache Olingolibrary.


As OData Service a gateway demo service is used which defines a data model containingBusinessPartners and their related Products they sell. For this sample we assume an use case that a client (e.g. UI) is interested in all offered Products and in addition the related BusinessPartner which supplies the Product.


To implement and demonstrate the described use case following steps have to be proceeded:


  • Fulfil prerequisites to access the gateway OData demo service
  • Read a single Product and Read all available Products
  • Navigation from a Product to the related BusinessPartner
  • Inclusion of information of related Entries to reduce requests
  • Selection of wanted information from Entries to reduce transfered data


Prerequisites

The gateway demo service is named ZGWSAMPLE_SRV and the functionality the service provides is described here. To get access to the gateway demo service it is necessary to sign up as described.


Read

Apache Olingo provides client functionality in form of create Requests (serialization) and interpret Responses (deserialization) based on the OData format. It does not provide functionality for sending and receiving requests so an application is free to decide whatever HTTP client library it wants to use. The methods to access provided serialization and deserialization functionality are all declared at the EntityProvider class. For basic consumption (Read) of an OData Service following methods are necessary:


  • EntityProvider.readMetadata(...) to read the metadata of the service.
  • EntityProvider.readEntry(...) to read an single entry.
  • EntityProvider.readFeed(...) to read a collection of entities.


Read Prerequisites: EDM/$metadata


Because Apache Olingo requires the metadata for serialization and deserialization of an entity the first step is to read the whole Entity Data Model (EDM) of an OData Service.



public Edm readEdm(String serviceUrl) throws IOException, ODataException {
  InputStream content = execute(serviceUrl + "/" + METADATA, APPLICATION_XML, HTTP_METHOD_GET);
  return EntityProvider.readMetadata(content, false);
}










To read the Entity Data Model (EDM) a HTTP GET on the corresponding $metadata URI of the OData Service via the execute(...) method. The resulting content is passed into theEntityProvider.readMetadata(InputStream content, boolean validate) method which de-serialize the EDMX into an EDM object. This EDM object than can be used for necessary deserialization in combination of read operations.


Read Entity: Product


To read a single Product of the service a single request to the ProductsCollection URI with set ID is done.


public ODataEntry readEntry(Edm edm, String serviceUri, String contentType, String entitySetName, String keyValue)
  throws IOException, ODataException {
// working with the default entity container
EdmEntityContainer entityContainer = edm.getDefaultEntityContainer();
// create absolute uri based on service uri, entity set name and key property value
String absolutUri = createUri(serviceUri, entitySetName, keyValue);
InputStream content = execute(absolutUri, contentType, HTTP_METHOD_GET);
return EntityProvider.readEntry(contentType,
    entityContainer.getEntitySet(entitySetName),
    content,
    EntityProviderReadProperties.init().build());
  }









The execute(...) method executes the request against the absolute URI and returns the response content as InputStream which then is deserialized by the EntityProvider.readEntry(...) into anODataEntry object. This contains all properties (data) of the entry in form of a Map, additional entry metadata as EntryMetadata object (which contains etag, id, association uris and uri information), the ExpandSelectTreeNode, information whether the entry contains inline entries and if the entry is a media resource additional the MediaMetadata.


Read Entities: ProductCollection


To read all Products of the service a single request to the ProductsCollection URI is done.



public ODataFeed readFeed(Edm edm, String serviceUri, String contentType, String entitySetName)
    throws IOException, ODataException {
  EdmEntityContainer entityContainer = edm.getDefaultEntityContainer();
  String absolutUri = createUri(serviceUri, entitySetName, null);
  InputStream content = (InputStream) connect(absolutUri, contentType, HTTP_METHOD_GET).getContent();
  return EntityProvider.readFeed(contentType,
      entityContainer.getEntitySet(entitySetName),
      content,
      EntityProviderReadProperties.init().build());
}









The execute(...) method executes the request against the absolute URI and returns the response content as InputStream which then is deserialized by the EntitProvider.readFeed(...) into anODataFeed object. This contains all entities provided by the OData Service as ODataEntry in a list as well as FeedMetadata like inline count and next link.

All used URIs


Navigate

The demo service defines an association between Products and their releated BusinessPartners as navigation property at a Product with the name Supplier. By following that association theBusinessPartner can be retrieved. This can be done by calling the absolute URI to a Supplier of a single Product which can be get by using the getAssociationUris(...) method of theEntryMetadata object. In this example the metadata object method is called with parameterSupplier which then returns with a list which contains the absolute URI (e.g. https://sapes1.sapdevcenter.com/sap/opu/odata/sap/ZGWSAMPLE_SRV/ProductCollection('HT-1000')/Supplie...) which can be called to get the Supplier for the Product.

For this example we prefer to already include the Supplier information for a Product into the response of our request. Therefore a read with expand system query option is the way to go (in next section).

All used URIs


Read more with Expand

To read an Entry or Feed which already includes properties of a related Entry (or Entries) the$expand system query option can be used. The $expand clause is a list off all associations to be retrieved within this request. For the example the $expand parameter is set to the navigation property Supplier to be retrieved. This results in the following relative URI:"./ProductCollection('HT-1000')/?$expand=Supplier".


The client has only to append the $expand for this case and can then again parse the result viaEntityProvider.readEntry(...). The difference now is that containsInlineEntry() is true and the Supplier properties are available as ODataEntry within the properties (i.e. Map) of the Product. To visualize the Map looks like:


Product (as ODataEntry)
\- *additional information like EntryMetadata*
\- *all simple properties*
\- Supplier (as ODataEntry)
    \- *additional information like EntryMetadata*
    \- *all simple properties*









The above shown readEntry(...) method could be used with minor adaption of URI creation, which now has to include the name of the expanded navigation property.


String absolutUri = createUri(serviceUri, entitySetName, keyValue, expand);









All used URIs

Read more with Expand on a Feed


As mentioned in the section above the $expand can also be used to read a Feed so that then as example each Product already includes the related Supplier. Also EntityProvider.readFeed(...)can be used for deserialization and the only adaption in compare to normal read Feed use case is to append the $expand system query option. For the sample use case the relative URI is"../ProductCollection/?$expand=Supplier".

All used URIs


Read less with Select

Each response usually contains all properties for an entry but this is not necessary for each client use case. Hence, for a use case in that a client is only interested in some properties it is possible to define the system query option $select to specify which properties of an Entry should be sent in the response payload. As example we only want the Name of each Product and the CompanyName of the related Supplier. Then we can use the $expand to include the Supplier in the response and define the $select system query option with Name,Supplier/CompanyName which results in the relative URI "../ProductCollection/?$select=Name,Supplier/CompanyName&$expand=Supplier".


Again the EntityProvider.readFeed(...) method is used for deserialization and the only adaption in comparison to the read Feed with expand use case is appending the $select system query option in creation of the URI.


String absolutUri = createUri(serviceUri, entitySetName, keyValue, expand, select);









All used URIs


Conclusion

In the sections above it was shown that with the support of Apache Olingo it is relatively easy for a client to consume an OData Service. While the library takes care of serialization and deserialization of the OData formatted entities the client can focus on processing the data.

Runnable sample client

A sample client exists for a quick Hands-On covering the here shown read use cases and in addition containing the boiler plate code which is necessary for the URL connection handling (e.g. Proxy/Authentication support).


To run this sample follow these steps:


  • Prerequisite: An installed JVM (Version 1.6 or above) and Maven (Version 3 or above).
  • Download it here.
  • Extract it into some folder
  • Configure proxy and/or authentication in client.properties in folder src/main/resources(with credentials provided via signing up)
  • Execute mvn compile exec:java to run Maven which compiles and executes the sample project.
10 Comments