Skip to Content
Technical Articles

Step by Step with the SAP Cloud Platform SDK for Android — Part 5 — Online OData

Previous (Logging)   Home   Next (Offline OData)

OData stands for Open Data Protocol and is a common way of accessing data from an SAP or other backend using RESTful APIs. The SAP Cloud Platform SDK for Android can be used to generate proxy classes from a backend that provides an OData interface.

The following are some additional sources of documentation on OData and generating and using proxy classes to access OData.
OData Overview
OData Asynchronous APIs
OData Version 2.0

The following instructions demonstrate how to make a data request. Proxy classes that represent the entity types in the of the sample OData service such as Product or SalesOrder will be used.
Generating Proxy Classes
Using the Proxy Classes
Using the Async OData API

Generating Proxy Classes

  1. The async OData API uses Lambda expressions which require setting the source and target compatibility to 1.8. The Project Structure dialog can be opened via File, Project Structure.
  2. Modify the app’s build.gradle to include the OData library.
    implementation 'com.sap.cloud.android:odata:2.1.0'

  3. The metadata document for the service can be viewed with the below URL. Note replace the user id (p1743065160) with your user id.
    https://hcpms-p1743065160trial.hanatrial.ondemand.com/mobileservices/origin/hcpms/ESPM.svc/v2/$metadata
    Right click the res folder and create a directory named xml. Create a file named metadata.xml.
    Copy the content from the url and paste into res/xml/metadata.xml.
    If the metadata.xml does not display well, right click on the file in Android Studio and choose Reformat Code.
  4. Change directories to the root of the project.
    cd %USERPROFILE%\AndroidStudioProjects\StepbyStep
    or on a Mac
    cd $HOME/AndroidStudioProjects/StepbyStep
    
  5. Generate the proxy OData classes by executing the following command in a terminal. The proxy classes will be generated into the project at StepbyStep\app\src\main\java\com\sap\stepbystep.
    C:\SAP\AndroidSDK\tools\proxygenerator\bin\proxygenerator -na -m app\src\main\res\xml\metadata.xml -p com.sap.stepbystep.ESPM -d app\src\main\java
    or on a Mac
    ~/SAP/AndroidSDK/tools/proxygenerator/bin/proxygenerator -na -m app/src/main/res/xml/metadata.xml -p com.sap.stepbystep.ESPM -d app/src/main/java
    

    Note, the proxygenerator requires a JAVA_HOME environment variable or the java command to be found in your path. The Java JDK can be downloaded from Java SE Downloads.

    set JAVA_HOME=C:\Program Files\Java\jdk-11.0.1
  6. Notice that the proxy classes now appear in the project.

Using the Proxy Classes

  1. Add the following variables to MainActivity.
    private ESPMContainer myServiceContainer;
    private OnlineODataProvider myDataProvider;
  2. In the onRegister method, in the updateUICallback, in the response.isSuccessful block, add the below code.
    myDataProvider = new OnlineODataProvider("ESPMContainer", serviceURL + "/" + connectionID , myOkHttpClient);
    myServiceContainer = new ESPMContainer(myDataProvider);
  3. Add the following class in MainActivity (ie, before the closing }). This class when called will make a request to the OData service to return products using AsyncTask as network tasks cannot be performed on the main thread.
    private class ODataQueryTask extends AsyncTask<Void, Void, List<Product>> {
        @Override
        protected List doInBackground(Void... voids) {
            if (myServiceContainer != null) {
                try {
                    return myServiceContainer.getProducts();
                }
                catch (DataServiceException dse) {
                    Log.d(myTag, "Exception " + dse.getMessage());
                }
                catch (Exception e){
                    Log.d(myTag, "Exception " + e.getCause().getMessage());
                }
                return Arrays.asList(new Product[0]);
            }
            else {
                Log.d(myTag, "service container is null");
                return Arrays.asList(new Product[0]);
            }
        }
    
        @Override
        protected void onPostExecute(List<Product> products) {
            //Can update the UI thread here
            toastAMessage(products.size() + " products returned");
            for (Product product : products) {
                Log.d(myTag, product.getName());
            }
        }
    }
    
  4. Add the following code to the onOnlineOData method.
    new ODataQueryTask().execute();
        
  5. Deploy and run the project and examine the Logcat after pressing the Online OData button.

Using the Async OData API

The below example makes use of a query to sort the results, uses an async method, and returns the product names that are in the Notebooks category.

  1. Regenerate the proxy OData classes, and this time do not include the -na option (noasync) by executing the following command in a terminal.
    C:\SAP\AndroidSDK\tools\proxygenerator\bin\proxygenerator -m app\src\main\res\xml\metadata.xml -p com.sap.stepbystep.ESPM -d app\src\main\java
    or on a Mac
    ~/SAP/AndroidSDK/tools/proxygenerator/bin/proxygenerator app/src/main/res/xml/metadata.xml -p com.sap.stepbystep.ESPM -d app/src/main/java
        

    Notice that there are now sync and async methods.

  2. Add the below method.
    private void asyncOData() {
        Logger logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("com.sap.cloud.mobile.odata");
        logger.setLevel(Level.ALL);
        myDataProvider.getServiceOptions().setCacheMetadata(false);
        myDataProvider.setTraceRequests(true);
        myDataProvider.setPrettyTracing(true);
        myDataProvider.setTraceWithData(true);
    
        Log.d(myTag, "In aysncOData");
        DataQuery query = new DataQuery()
                .from(ESPMContainerMetadata.EntitySets.products)
                .select(Product.name)
                .where(Product.category.equal("Notebooks"))
                .orderBy(Product.name);
    
        myServiceContainer.getProductsAsync(query, (List<Product> products) -> {
            toastAMessage(products.size() + " products returned");
            for (Product product : products) {
                Log.d(myTag, product.getName());
            }
        }, (RuntimeException re) -> {
            toastAMessage("Online OData request failed: " + re.getMessage());
            Log.d(myTag, "An error occurred during async query:  "  + re.getMessage());
        });
    }
  3. Modify the onOnlineOData method.
    //new ODataQueryTask().execute();
    asyncOData();
    
  4. The following is a subset of the debug/trace output from the above query. Notice that it contains trace information as well as a sorted list of products in the category of Notebooks.
    
    V/com.sap.cloud.mobile.odata.http.HttpRequest: [AsyncTask #2] [ESPMContainer] Request: GET 
    https://hcpms-p1743065160trial.hanatrial.ondemand.com/com.sap.edm.sampleservice.v2/Products?$select=Name
    &$filter=(Category%20eq%20'Notebooks')&$orderby=Name
    V/com.sap.cloud.mobile.odata.http.HttpRequest: [AsyncTask #2] [ESPMContainer] Response: status code = 200, status text = OK, time = 555 ms
    V/com.sap.cloud.mobile.odata.http.HttpRequest: [AsyncTask #2] [ESPMContainer] Response Headers:
    ...
    V/com.sap.cloud.mobile.odata.http.HttpRequest: [AsyncTask #2] [ESPMContainer] Response Content: (pretty printed)
    ...
    D/myDebuggingTag: Astro Laptop 1516
        Benda Laptop 1408
        ITelO FlexTop I4000
    ...
    
  5. A generated project using the Wizard contains a more complete example that demonstrates create, update and delete operations.
  6. Another option to generate the proxy classes is to use the OData Plug-in.

Previous (Logging)   Home   Next (Offline OData)

13 Comments
You must be Logged on to comment or reply to a post.
  • Hi Daniel,

    thanks for great serie. I’d like to ask you where to get the URL for service’s metadata if using custom destination ans service, not the sample backend? In your case it was

    https://hcpms-p1743065160trial.hanatrial.ondemand.com/mobileservices/origin/hcpms/ESPM.svc/v2/$metadata

    If I click on destination link in APIs section of the app on SCP I get “HTTP Status 403 – Neither Application connection id nor Application id is provided.”

    Thanks,

    Zdenek

  • Hi,

     

    How can we generate proxy classes using plugin. I’ve tried adding plugins in applevel gradle and module level gradle as per this link

    https://help.sap.com/doc/c2d571df73104f72b9f1b73e06c5609a/Latest/en-US/docs/user-guide/getting-started/gradle_overview.html#using-the-odata-plug-in

    But I’ve failed to generate it. But I’ve done it with command line tool.

     

    Need little more details about this

    odata {
        verbose true
        services {
            products {
                schemaFile file("src/main/odata/productssvcmetadata.xml")
                packageName "com.example.products"
            serviceClass ProductsService
            }
            orders {
                schemaFile file("src/main/odata/orderssvcmetadata.xml")
                packageName "com.example.orders"
            serviceClass OrdersService
            }
        }
    }

    Edit:---------------------

    After many trials I found the proxy classes generated at this location app\build\generated\source\odata

    Please find the image attached.

    But when I generate through the CMD tool proxyclasses are appeared in app\res\main\java as per our destination. Also could you please brief me about the package (internal)

    Thanks.
  • Hi Daniel Van Leeuwen,

    I’m working on online POST request. I’m using createAsync method for callbacks, where if I’ve passed custom headers, but it was not taking my headers. And I’ve checked with GET request with the same headers, it was working perfectly. But POST was not the same. Finally After many trails I’ve removed all the headers and I’ve passed the registration instance of onlineDataProvider to ESPMContainerService, since then the POST request was executed successfully with no error.

    Problem faced: 

    Every time I open onlineStore to make a POST request it is getting registered with new connectionID / registration ID in HCPms, Need help/suggestion on this.

  • Hello,

    I’m using CreateEntityAsync to upload some data… but I also expect to get response from this process after upload, but I found that Action0 Success Listener has empty parameters, So how can I get response?

    Thanks.

  • Hello,

    I want to wait until the response returns so I do this :

    and it works only with getAsync.

    but when I do the same thing on createEntityAsync/updateEntityAsync/deleteEntityAsync response doesn’t return and system stuck in while loop.

    I read on developers guide that:

    By default, queries are executed in parallel on Async.THREAD_POOL_EXECUTOR while CUD requests are executed serially on AsyncTask.SERIAL_EXECUTOR. This behavior can be changed by passing in your own executor.

    but I don’t know how can I change this behavior?

    Thanks.

    • Instead of putting the thread to sleep, could you call the method or add the code that you want to execute after the create succeeds by calling it or placing it in the create success callback?

      Perhaps one other thing you could try is an AtomicBoolean.

      Regards,

      Dan van Leeuwen