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’)/Supplier) 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.
To report this post you need to login first.

10 Comments

You must be Logged on to comment or reply to a post.

  1. Alex Liao

    Hi Michael,

    It’s very useful guide.

    One more question, if we application needs basic authentication (usename+password) to access the gateway service, can the olingo APIs support the authentications?

    BR,

    Alex.

    (0) 
    1. Stefan Sturm

      Hi Alex,

      yes. You have to use a URLConnection for instance a HTTPSUrlConnection and there you use the SetRequestPropterty method to add you basic athentication user and password.

      For the Gateway you also may have to fetch the X-CSRF-Token. 

      BR

      Stefan

      (0) 
      1. Alex Liao

        Hi Stefan,

        Thanks for your kind prompt.

        In case I have an OData service in Gateway, do you mean I can only use HttpUrlConnection to access the OData service? And the Olingo APIs for OData are not avalaible for the authentication?

        BR,
        Alex.

        (0) 
        1. Stefan Sturm

          Hi Alex,

          how to access an ODATA service in a gateway depends on how the service knot is configured in ICF on the Gateway side. So if the knot supports basic authentication you can use a JAVA connection object. The Olingo API is to do CRUID ODATA in an easy way. This has nothing to do how the authentication is done. I do not know that much about the Gateway side and if always Https is required. In our environment all OData services can only be used via https.

          HTH

          Stefan

          (0) 
          1. Alex Liao

            Hi Stefan,

            Actually, we deploy JAVA application in HCP to access gateway odata service via Cloud Connector. With use of HttpUrlConnection, we can add basic authentication into Http Property to enable accessing gateway odata service, it works.  However, if I want to switch the API to Olingo API to consume service, how can I add the basic authentication? I’m blocked here, honestly.

            BR,

            Alex.

            (0) 
            1. Stefan Sturm

              Hi cou have to set this to the connection object. For instance something like this:

              Connection conn;

              conn.setRequestProperty( “Proxy-Authorization”,   “Basic ” +   new sun.misc.BASE64Encoder().encode((proxyUser + “:” + proxyPass).getBytes()) ); conn.connect();

              (0) 
    2. Michael Bolz Post author

      Hi Alex,

      Thanks for your feedback.

      The Olingo library does not provide convenience methods for authentication.

      However, when I read the comments from Stefan I see that you found an answer for your question.

      Therefore Thanks to Stefan  ;o)

      Best Regards, Michael

      (0) 

Leave a Reply