1 2 3 22 Previous Next

SMP Developer Center

319 Posts

I have written a number of blogs on Mobile Applications with Xamarin Forms and Explained in detail the architecture I was using.

Some of these are listed

http://scn.sap.com/community/ developer-center/mobility- platform/blog/2014/09/12/ order-to-cash-mobile- application-using-xamarin- forms-c

http://scn.sap.com/community/ developer-center/mobility- platform/blog/2014/07/31/c- cross-platform-mobile- application-using-xamarin-form

http://scn.sap.com/community/ developer-center/mobility- platform/blog/2014/08/16/sap- hr-mobile-application-to-post- expense--using-c-and-xamarin

http://scn.sap.com/community/ developer-center/mobility- platform/blog/2015/01/06/ iphone-andorid-app-using- xamarinc-to-save-signature-in- sap


The above applications were developed using Xamarin Forms which generate native code and work on the three platforms(Andorid,IOS,WP).  They are very fast

compared to any of HTML5 applications.  The new versions of Xamarin has many more cool features which will make makes Cross platform applications even more appealing.  However  some clients insist on Android Native or IOS Native apps - Because of this I developed a mobile version of MMBE.

1) SAP RFC Call Development  - As described in my earlier blogs I have developed SICF Node and implemented the REST handler which calls a Custom RFC that calls  '/SPE/MATERIAL_STOCK_READ' to get the stock information


image7.png

 

2) There are number of tutorials, github samples and you tube videos that  people can use to learn - The Xamarin Web site a lof of documentation and free trail download to try out these as well.

A) Initial Screen - This is from Main.axml file which has the layout  - At the load of the Application I store All Plant and Storage Location information in SQL Lite

database so that on subsequent runs we dont have to call SAP.

image1.png

B) When you Click on Select a dialog Fragment  to give the selection Screen  - This selection screen the following

1)  Material - Either key in or Scan the Barcode - For Barcode scanning I using a Xamarin Compoment ZXING.  We have Plants and Storage Location  in Local DB - I create a dropdown

image4.png

 

image3.png

C) When you Click on Stock Button The Main screen will List all the results of the FM

image5.png

D) Select on Line to get the details

image6.png

To Download and try this Go to Google Play and in the Search Term Enter my name "Ramesh Vodela" and choose SAP Plant Stock(MMBE) App.

Have you read Martin's recent blog on the new features of HANA Cloud Platform mobile services? In that list Staging is one of the notable feature. Staging helps the developers to give their application to the users for testing, after the completion of the tests it could be moved immediately to the end users from admin cockpit itself.

 

In this blog I am explaining how to put your application in a test state and then move it to the production.

Configure Application in Cockpit

  • Open HCPms admin cockpit, then click on Applications.
  • Click on Configure and provide below details.

    stage1.png

    stage2.png

    Note: Backend URL - http://services.odata.org/V2/OData/OData.svc

  • Click on Save.

Create a Kapsel Application

The following steps will create a simple Kapsel app with an AppUpdate plugin.

  • Cordova command to create project
      cordova -d create C:\Kapsel_Projects\StagingDemo com.sap.staging StagingDemo "{\"plugin_search_path\":\"C:/SAP/MobileSDK3/KapselSDK/plugins/\"}"
      cd C:\Kapsel_Projects\StagingDemo
      cordova -d platform add android
               
      cordova -d create ~/Documents/Kapsel_Projects/StagingDemo com.sap.staging StagingDemo "{\"plugin_search_path\":\"/Users/i12345/SAP/MobileSDK3/KapselSDK/plugins/\"}"
      cd ~/Documents/Kapsel_Projects/StagingDemo
      cordova -d platform add ios

   

  • Replace index.html (C:\Kapsel_Projects\StageDemo\www\index.html) with this sample code. This is an extended code from these kapsel guides.
  • Add AppUpdate plugin and logon plugin
      cordova -d plugin add com.sap.mp.cordova.plugins.appupdate
      cordova -d plugin add com.sap.mp.cordova.plugins.logon
  • Run the following command to build project.
      cordova build
  • Run app in emulator using the below command.
      cordova emulate android

 

      cordova emulate ios
  • Login to the application. The credentials doesn't matter since we configured the app with No Authentication. The application will show a list of products.

    stage0.png

  • From HCPms Admin Home page click on Registrations and Users > Turn on "Is Tester" button. Hence this user gets the applications which are under stage state.

    stage3.png

 

Enable Staging

In this step we are going to upload a new version of the Kapsel application to HCPms for testing (Stage state).

  • When we run the app for the second time to identify the a change, update index.html file as given below. This will get the list of Suppliers instead of Products.

stage9.png

  • Build the app
      cordova build

 

  • The Kapsel command line provides a way to create a zip file that could to uploaded to HCPms. Open a command prompt and change path to
      C:\SAP\MobileSDK3\KapselSDK\cli

 

      ~/SAP/MobileSDK3/KapselSDK/cli

 

  • Run command
      npm install -g

 

        For Mac:

      sudo npm -g install

 

  • Open command prompt and change the path to your Kapsel project (C:\Kapsel_Projects\StagingDemo) and execute the command
      kapsel package

    stage5.png

    Now you could find a zipped file created inside your project folder.

    stage10.png

  • Next step is to upload the zipped file to HCPms. To upload the file open HCPms home page then click on Applications > Choose your application > Configure > App specific settings. Then click on Upload Kapsel and browse the zipped file.

    stage6.png

  • Choose the application uploaded, then click on Stage. It moves the application to stage state. Which will be accessible to the users who are testers.

    stage11.png

Test App on Emulator or Device

Now, when we close and reopen the app the AppUpdate plugin identifies that there is an updated app in the server. It gives a pop up for relaunching the app to download the changed files from server.

  • Choose Relaunch Now. You could see that the list is changed from Products to Suppliers.

    stage7.pngstage12.png

 

After testing the application, from admin cockpit choose the application under Stage state, then click on Deploy, which will make the application available for users in production.

 

CC:SAP for Mobile

 

Regards,Midhun

SAP Technology RIG

SAP Mobile Platform SDK 3.0 SP08 - What is new ?

 

We are happy to announce the release of the latest version of the SDK for SMP and HCP Mobile Services, SMP SDK SP08.  The last version of the SDK already supports a wide range of capabilities including SAML Support, Usage collection and Offline APIs across iOS, Android (ARM), and Windows devices.  SP08 introduces a set of new features for native and hybrid development around developer experience, security & offline.

 

Let us start with a high level recap of the mobile SDK.

 

sdk.png

 

The figure above shows the different programming models that are available in the SDK (native, hybrid and agentry metadata) as well as different consumption paths (cloud, on premise) for platform services.

 

With every service pack release we focus on creating value in our SDK offering for the development community by addressing themes that are spread over the programming models and consumption paths. The focus themes in SDK SP08 were the following.

 

Native SDK

 

Developer Experience improvements

 

We took a close look at the experience of our developers and we are introducing a set of best practices in managing the SDK experience. The following are the specific improvements that will be available in SP08

  • Cocoa Pod support (IOS)– Cocoa Pods is the dependency manager for Swift and Objective-C Cocoa projects. It has thousands of libraries and can help you scale your projects elegantly. A pod spec file will be delivered as part of the sdk installer.

 

By running a pod install command, the xcode project with all the sdk dependencies could be easily set up.

 

  • Maven Support (IOS, Android) - Maven addresses two aspects of building software: First, it describes how software is built, and second, it describes its dependencies. Maven makes it easy to identify when a project has a dependency on code outside its source control folders. Developers and managers alike would prefer small, componentized builds because they allow code changes to be very quickly tested and integrated.

 

After installing new deploy.sh script is available under the appropriate OS folder. Through the execution of the script, all artifacts are deployed into the local Maven repository and using a pom.xml, it can be added to the xcode project

 

  • Gradle Support (Android) - We intend to provide more support for Gradle and android studio release-by-release. With SP08 we would start with a getting started guide for android studio based projects and the mobile sdk. We simplify the dependency management for android projects using gradle files that are added to the installation. For more details please refer to the help documentation and the following links.

 

 

Features & improvements

 

Media Support

  • The Online and Offline OData Stores now supports media resource handling, for example creating and downloading of image files. In previous versions of the SDK, the stores supported OData related operations (CRUD) only for OData entities, feeds, properties, links, but not for downloading (GET) of resources such as media files.


Appsettings enhancements

The new ApplicationSettings property of the LogonCore instance replaces the ConnectionData property on the RegistrationContext to access server application settings, allowing a common solution across multiple platforms and providing the following benefit:

  • Type safety of the stored settings.
  • Validity of downloaded application settings.
  • The ability of the application developer to modify settings those are writable with validation.
  • Enforcement of consistent state of application setting values on the client and server.
  • Prevention of an application developer from (intentionally or unintentionally) creating an inconsistent state by overwriting application setting values locally.

 

Enhanced SAML Support (iOS, Android)

Previous versions of Logon Manager supported HTTP-POST binding for getting redirected to the Identity Provider. The HTTP-REDIRECT binding is now also supported on iOS and Android. For more details on Http conversation manager and SAML redirects please refer to the following link http://scn.sap.com/community/developer-center/mobility-platform/blog/2014/11/14/request-response-challenge-filters-in-httpconversationmanager on the SAP Community Network Web site.

 

Hybrid SDK

 

Features & improvements

 

  • The Hybrid SDK (Kapsel) plugins now support a Voice Recording service that allows a user to record audio information using the new native audio recorder application.
  • The X.509 certificate provider interface now supports a new certificate provider library, which provides a new certificate provider interface to communicate with third-party certificate provider implementations.
  • The Offline OData plugin, which provides offline OData support to Cordova applications, now provides support for offline media elements.
  • The Online plugin has been enhanced to provide busy indicator support when a page starts loading from the server.
  • The Application Preferences plugin, Usage plugin, and Toolbar plugin are now supported for use in applications on the Windows 8.1 and Windows Phone 8.1 platforms, in addition to Android and iOS.

 

 

Agentry

 

Agentry Toolkit (64-bit only)

The Agentry Toolkit, which provides the components to create Agentry applications, is available in 64-bit format. The 32-bit version is no longer supported, or available for download. Links to the 32-bit download site have been removed from the documentation.

 

Agentry Client Changes

The screen-background feature is now available for the Agentry WPF client. (It was previously available only for Agentry Android, iOS, and legacy clients). This feature enables you to change the Screen Background Image, making it easier to distinguish between read-only, editable, and disabled fields on both transaction and non-transaction screens.

 

Password Validation Audits for Agentry Applications

Password validation audits are supported for Agentry applications using SQL and Java back-end connections. The developer must enable password validation in the application definition. A new script file provides a template for handling password validation audit records. When the user performs a password-related action, an encrypted password validation audit record is created on the client device. The encrypted audit record is sent to the server. Once the server receives the record, the server sends confirmation to the client, and the client deletes the encrypted audit record.

 

Agentry Publication Changes

Clusters were introduced in 3.0 SP03, which changed how application and server configuration is managed in SAP Mobile Platform. Configuration values are now stored in the SAP Mobile Platform data store, and available to all server nodes. For Agentry, this means the Agentry.ini functionality is no longer needed in production.

When you publish an Agentry application in the cockpit, configuration values are added to the data store, and distributed to all server nodes. When you upload an updated version of an Agentry application, the configuration values are updated in the data store, and distributed to all server nodes.

In this blog, I will be talking about offline support for Windows.  If you have been following my other blogs (or corresponding blogs for Android and iOS), you are already aware of the concept of a Store.  You are probably aware that there is an Online Store and by extension, an Offline Store.  Although I haven’t talked about an Offline Store explicitly, I have made a few subtle references to its existence in my previous blogs.  An astute reader might have already guessed that an Online Store can be used when the device has network connection.  So what about the Offline Store?  A common mistake most people make, is that they only associate Offline Store with no network connection.  While this is true (Offline Store is used with no network connection), an Offline Store can also be used when there is network connection. In fact, I would recommend using the Offline Store in all cases (if you want offline support) – and not use the Online Store at all.  Of course, this would also depend on the business needs.  But using the Offline Store regardless of network connectivity reduces the complexity of switching back and forth between Online and Offline Stores.  The caveat is that the user might be working with stale data.

 

Comparing Online and Offline Stores

In the table below, I roughly compare the features of an Online Store and an Offline Store.

 

Online Store

Offline Store

Can work with network connection only

Can work regardless of network connectivity

Opening Online Store – straightforward

Opening Offline Store – requires more options

HTTP request – roundtrip to server

HTTP request – reads from local ultralite database

Very basic persistence using Technical Cache

Full-fledged persistence using ultralite database

CRUD operations – accomplished by OData calls

CRUD operations – OData calls are intercepted and converted to SQL calls and executed against ultralite database

No data latency

Data latency exists

No concept of flush

Flush method pushes changes made locally to the backend

No concept of refresh

Refresh method pushes changes made on the backend to the local ultralite database

Data conflicts – very minimal

Data conflicts – a real possibility

HTTP(S) protocol to transfer data between SMP Server and device

Mobilink protocol to transfer data between SMP Server and device

 

 

 

What happens when I open an Offline Store?

The first time an Offline Store is opened, a lot of things happen.  The Offline Store connects to the SMP Server and sends it the defining request.  It is very critical to understand what constitutes a defining request.  A defining request is an URL representing data fetched by an HTTP GET method that needs to be persisted.  An application can have multiple defining requests.

 

Examples of defining requests…  Note that each defining request corresponds to a single GET request.

  • Request 1 = BusinessPartners?$expand=SalesOrders/Items
  • Request 2 = SalesOrders(‘0500000002’)?expand=Items
  • Request 3 = Products?$expand=ProductDetails

 

Request 1

BusinessPartners

  • Rows 1, 2, 3

SalesOrders

  • Rows 1, 2, 3, 4, 5, 6, 7

Items

  • Rows 1, 2, 3, 4, 5, 6, 7, 8, 9

Data persisted on device

 

BusinessPartners

  • Rows 1, 2, 3

SalesOrders

  • Rows 1, 2, 3, 4, 5, 6, 7

Items

  • Rows 1, 2, 3, 4, 5, 6, 7, 8, 9

Products

  • Rows 1, 2, 3, 4

ProductDetails

  • Rows 1, 2, 3, 4

Request 2

SalesOrders

  • Rows 3

Items

  • Rows 4, 5

Request 3

Products

  • Rows 1, 2, 3, 4

ProductDetails

  • Rows 1, 2, 3, 4

 

SMP Server queries the backend using the defining requests.  The union of the data retrieved by all the defining requests is used to populate the database on the SMP Server.  This prepopulated database is then sent to the device.  Note that data is not duplicated on the device – just the union of the data retrieved by all the defining requests is persisted.  For efficiency reasons, it is recommended not to have overlapping defining requests.  However, even if you have overlapping defining requests, data will not be duplicated on the device.

 

Lot less happens during subsequent openings of the Offline Store.  The application does not connect to SMP Server.  Therefore application does not require network connection. Application simply opens the existing database on the device.

 

Store

Action

Offline Store (first time opening)

Needs network connection

Defining request sent to SMP Server

SMP Server queries backend using defining requests

Creates a new database with backend response

Sends newly created database to device

Offline Store (Subsequent openings)

Does not need network connection

Simply opens existing database on device

Does not connect to SMP Server

 

 

How do I create and open an Offline Store?

Creating an Offline Store is straightforward.  The constructor does not take any parameters.

 

this.Store = new SAP.Data.OData.Offline.Store.ODataOfflineStore();

 

Opening the Offline Store takes ODataOfflineStoreOptions as a parameter

 

await this.Store.OpenAsync(options);

 

So, how do I create this “options” parameter?  For this, you need to create the ODataOfflineStoreOptions object with the proper values. Thankfully, most values are intuitive. The following code snippet creates the ODataOfflineStoreOptions and populates it with the proper values.

 

var options = new SAP.Data.OData.Offline.Store.ODataOfflineStoreOptions();

 

var client = new SAP.Net.Http.HttpClient(new System.Net.Http.HttpClientHandler()

   {Credentials = new System.Net.NetworkCredential(“user”, “password”)},

   true); // will be disposed by the store!

client.DefaultRequestHeaders.TryAddWithoutValidation("X-SMP-APPCID", connectionId);

client.DefaultRequestHeaders.TryAddWithoutValidation("X-SUP-APPCID", connectionId);

client.ShouldHandleXcsrfToken = true;

options.ConversationManager = client;

 

options.Host = "10.4.64.212";

options.Port = 8080;

options.ServiceRoot = "com.sap.flight";

options.EnableHttps = false;

options.StoreName = "OfflineStore";

options.StoreEncryptionKey = "SuperSecretEncryptionKey";

options.URLSuffix = "";

options.AddDefiningRequest("TravelagencyDR""TravelagencyCollection"false);

options.EnableRepeatableRequests = false;

 

 

CRUD operations on Offline Store after opening

CRUD operations can be performed on the Offline Store after successfully opening the Offline Store. The syntax for CRUD operations on an Offline Store is identical to the syntax for an Online Store.  The only difference is that the data is retrieved from the locally persisted ultralite database instead of from the backend.

 

CREATE


var execution = store.ScheduleCreateEntity(entity, collectionName);

var response = await execution.Response;

 

READ


var execution = store.ScheduleReadEntitySet(collectionName);

var response = await execution.Response;

 

UPDATE



var execution = store.ScheduleUpdateEntity(copiedEntity);

var response = await execution.Response;

 

DELETE


var execution = store.ScheduleDeleteEntity(entity);

var response = await execution.Response;

 

Understanding Flush and Refresh

Flush and refresh allows the locally persisted data to be synchronized with the backend data.  Both Flush and Refresh methods require network connection.  Flush allows changes made locally on the device to be applied on the backend in an asynchronous fashion.  Refresh on the other hand, allows changes made on the backend to be downloaded to the device.  An important thing to note when calling the Flush method is that all the changes made locally on the device are submitted.   The SDK currently does not support sending part of the changes.  However, Refresh method has the option of downloading part of the backend changes based on defining request.

 

Flush method

Refresh method

Requires network connection

Requires network connection

Changes made locally submitted to backend

Changes made on backend downloaded to device

This call is asynchronous

This call is asynchronous

All changes are submitted – Cannot submit part of the changes

Developer has the option of downloading part of backend changes based on defining request

Call Flush before calling Refresh

Call Flush before calling Refresh

 

FLUSH


await this.Store.ScheduleFlushQueuedRequestsAsync();

 

REFRESH

 

await this.Store.ScheduleRefreshAsync(); OR

await this.Store.ScheduleRefreshAsync(definingrequest);

 

 

Some important considerations

  • Offline support for Windows is only available for Windows Store applications and Windows Phone Store applications.  Offline support is not available for Windows desktop .NET applications.
  • The local database is created in the location given by: Windows.Storage.ApplicationData.Current.LocalFolder.  It is essentially the application data directory.  Unfortunately, there is no tool to access the database tables directly. Even if you could open the database, it would be hard to read the data as there is a lot of metadata needed to map the database tables to the OData model.
  • The Offline Store takes care of getting and setting the XCSRF token in the MobiLink server component.  Nothing additional needs to be done on the client application.
  • Batch processing is supported in the Offline Store as well.
  • In the event, the SMP Server is configured with multiple endpoints, then your application can have multiple Offline Stores (one for each endpoint – make sure you supply a unique store name for each Offline Store).  You can also have multiple Offline Stores open at the same time.
  • You can also have Online Store and Offline Store (for the same endpoint) in the same application.  Both these stores can be open at the same time.  Depending on network availability, the developer can choose which store to use. A lot of times it is easier to only have the Offline Store and use it regardless of network connectivity. Periodically call flush and refresh to sync the data with the backend. 

 

 

Please feel free to post any comments or questions that you might have.  I will try to answer them as soon as I can.  Hopefully, these blogs have given you enough insights into the Windows SDK to start building new mobile applications.  Good luck !

In this blog, I will be talking about Technical Cache.  As more and more devices are increasingly online, it makes more sense to optimize the server communication for Online Stores.  Yes, you guessed it right.  Technical Cache is only applicable for Online Stores.  This feature is not applicable for Offline Stores.  Technical Cache provides a simple mechanism whereby responses from an online OData request is cached for later use.  This allows the user to now read from the cache instead of making another round trip request to the server thereby optimizing bandwidth utilization.  Additionally an application can use the cache content to improve user experience.  You will find in this blog, how a developer can add the necessary implementation to make use of the Technical Cache.

 

What is the difference between Technical Cache and Offline Store?

You might be wondering the difference between a Technical Cache and Offline Store.  In an Offline Store, data is persisted locally on the device as well.  So why do we need a Technical Cache?  A lot of mobile applications do not require offline capabilities.  These devices are mostly online and the application itself is not mission critical.  For applications like these, it makes no sense to add offline capabilities since they are more complex.  However, you may still want some kind of persistence that is easy to implement that allows basic functionality when there is no network coverage. Additionally, the cache content can improve user experience as well.  The Technical Cache provides this solution for online applications to somewhat use a persistent store without all the complexities of an Offline Store.  Note that the Technical Cache provides very basic functionality as compared to the Offline Store.  For example, the Technical Cache can only be used to query the cache if the request URL is (literally) identical.

 

Some keywords and concepts you need to know regarding Technical Cache

When discussing about Technical Cache, you need to understand some keywords and concepts.  I have not yet talked about Offline Stores. I will be talking about Offline Stores in my next blog.  Just know that Technical Cache is not applicable for Offline Stores.  This now leaves us with only the Online Store. When opening an Online Store, you now have the option of enabling the Technical Cache.  For backwards compatibility, the default is disabled.

Assume the Online Store is opened with the Technical Cache enabled.  In this scenario only, the Online Store can switch to passive mode. In all other scenarios, switching to passive mode will result in an exception.

 

Store

Opening method

Technical Cache

Can switch to Passive Mode

Online Store

UseCache = true

Enabled

Yes

Online Store

UseCache = false (OR)

Not Explicitly set

Disabled

No

Offline Store

Not Applicable

Not Applicable

Not Applicable

 

So, what is passive mode? In simple terms, during passive mode the Online Store does not make a request to the backend server.  It simply reads from the cache and presents the response to the caller.

 

Store

Passive Mode

Online Store (cache enabled)

Reads from cache

Online Store (cache disabled)

Not Applicable

 

If there is passive mode, is there an active mode also?  Yes. In simple terms, during active mode the Online Store makes a request to the backend server.  However, there is a small difference between how an Online Store with cache disabled behaves compared to an Online Store with cache enabled. An Online Store with cache disabled simply makes a request to the backend server.  However, an Online Store with cache enabled in addition to making a request to backend server, also reads from the cache.  It returns both the responses to the caller.  The cache is then updated with the response from the server.

 

Store

Active Mode

Online Store (cache enabled)

Sends roundtrip request to backend

Also reads from cache

Updates cache with latest server response

Online Store (cache disabled)

Sends roundtrip request to backend

 

How to open Online Store with Technical Cache enabled?

Opening an Online Store with Technical Cache enabled is fairly straightforward.  The following code snippet enables the Technical Cache in an Online Store.  In addition, values for CacheSize in KB and CacheCleanupPercent can be specified.  CacheSize specifies the maximum size of the cache, while CacheCleanupPercent specifies the percentage of cache content to be cleared when CacheSize is exceeded.

 

this.Store = new ODataStore(URL, new ODataStore.StoreOptions

{

   UseCache = true,

   EncryptionKey = "encryptionKey",

   RequestFormat = ODataStore.EntityFormat.Json

});

 

 

Making an HTTP request in active mode

Assume we have an Online Store opened with Technical Cache enabled.  The next step is to make an HTTP request to retrieve data from the backend. Making an HTTP request sends a round trip request to backend and also reads from the cache.  The first time an HTTP request is made to a resource, the cache is not available.  A null value indicates a non-existing cache content.

 

var execution = Store.ScheduleReadEntitySet(collectionName);

 

// Read from backend

var response = await execution.Response;

   this.EntitySet = (ODataEntitySet)((IODataResponseSingle)response).Payload;

 

// Read from cache

var cacheResponse = await execution.CacheResponse;

if (cacheResponse != null)

   this.CachedEntitySet = (ODataEntitySet)((IODataResponseSingle)cacheResponse).Payload;

else

   // null indicates non-existing cache – possibly first time

   this.CachedEntitySet = new ODataEntitySet();

 

After the first request, the cache is now populated with the server response.  Making an HTTP request results in making a roundtrip to the backend and reading from the cache.  If data has not changed in the backend since the last time the cache was updated, the server response and the cache response would be identical.

 

Note:  The HTTP request should be literally identical for the cache to work.

 

For example, the initial HTTP request is “Products?$top=5”.

  • The server response returns the top 5 rows.
  • The cache response is null (since it is non-existent).
  • The cache is then populated with the server response (in this case, with the top 5 rows)

Your subsequent HTTP request is “Products?$top=5” (Assume nothing has changed on the backend)

  • The server response returns the top 5 rows again.
  • The cache response now also returns top 5 rows (These rows were cached from the previous request).
  • The cache is now updated with server response (Nothing has changed on backend, so no updates necessary.  If data has changed on the backend, then the cache is now updated with the new values).

You now make another HTTP request.  This time it is “Products?$top=4”.  It is not literally identical.

  • The server response returns the top 4 rows.
  • The cache response is null (since it is non-existent).
  • The cache is then populated with the server response (and associated with this request).
  • The cache now contains values for 2 requests (“Products?$top=5” and “Products?$top=4”)

 

Making an HTTP request in passive mode

Assume that network connection is lost and you cannot make a roundtrip to the backend.  So you only want to read from the cache.  The first step is putting the Online Store in passive mode.  This is accomplished by the following snippet of code.

 

this.Store.IsPassive = true;

 

When the Online Store is in passive mode, request to the backend server is not made.  It only reads from the cache.

 

var execution = Store.ScheduleReadEntitySet(collectionName);

 

// Read from cache only – Online Store is in passive mode !

var cacheResponse = await execution.CacheResponse;

if (cacheResponse != null)

   this.CachedEntitySet = (ODataEntitySet)((IODataResponseSingle)cacheResponse).Payload;

else

   // null indicates non-existing cache – possibly first time

   this.CachedEntitySet = new ODataEntitySet();

 

The cache content is now returned to the caller.  Reading from the cache is much faster than making a roundtrip to the backend.  So even when network connection is present, you can switch the Online Store to passive mode and read from the cache (as long as the application does not require the latest data).

 

Can Technical Cache work with delta tokens?

Technical Cache can also work with delta tokens.  The SMP SDK is smart enough to update the cache based on the delta feed from the server response.  This is incredibly cool, because now you can utilize the power of delta tokens and also the handy feature of Technical Cache to optimize your online application.

 

 

Technical Cache is a pretty handy feature to improve performance and provide very basic offline functionality to an online application.  In the next blog, I will be talking about features of an offline application and how to implement an offline application.

In this blog, I will be talking about the Usage Collection library.  The primary objective of this library of course, is to collect application usage statistics.  The SAP Mobile Platform SDK (hereinafter referred to as “SMP SDK” or “SDK”) automatically logs some predefined events and metrics.  In addition, you can also log custom defined timers, events and measurements.  Overall, this library gives IT administrators the ability to view and analyze application usage statistics.  Usage Collection works only with HANA Cloud Platform Mobile Services (hereinafter referred to as “HCPms”) and not the On-Premise SMP Server. 

 

Lifecycle of Windows Store application

The Usage Collection library is equipped to send certain metrics to HCPms when the application starts.  It is also necessary to stop the Usage Collection library from collecting metrics when the application is closed.  For this reason, it is very important to understand the lifecycle of a Windows Store application.

usage1.png

 

NotRunning

An application can be in the NotRunning state if

  1. Application has never been launched
  2. Application was running, but then crashed
  3. Application was suspended and later terminated by system (due to memory resources)

Running

An application can be in the Running state if

  1. Once the application completes activation (UI ready to be displayed to user)

 

Suspended

An application can be in the suspended state if

  1. User moves app to background (if user doesn’t switch back soon, Windows suspends the app)
  2. Device enters low power state

If application does not return from suspended state within 5 seconds, Windows could terminate the app anytime for low resources. 

 

Understanding user session

In the context of the mobile application built using SMP SDK, the user session can be roughly characterized as the time interval between the application in a running state and the application in a suspended state.  Running state (in the context of Usage Collection) is defined as the application in a running state and the user logged in (For example, the application is not in the running state if the logon screen is being displayed). 

The idea is for the developer to let the application know using the Usage Collection library that the application is about to start and about to be suspended.  This can be accomplished using the ApplicationWillEnterForeground and ApplicationWillEnterBackground asynchronous methods of the Usage Collection class.  A user session id is generated and appended to each record that is recorded during that particular session.  When the session is started by calling ApplicationWillEnterForeground, all previously collected usage statistics are automatically sent to HCPms.  In the event usage statistics could not be sent for some unforeseen reason when a session starts, then they are resent in subsequent session restarts along with additional usage statistics collected up until that point. 

 

How to initialize the Usage Collection library and start the user session?

Initializing the Usage Collection library takes 2 parameters and is done asynchronously.   Once the Usage Collection library is initialized, you also want to call ApplicationWillEnterForeground to signal that the user session has started.  This can be called when the user logs in successfully in the LogonCompleted event handler. 

 

await SAP.Usage.Usage.InitUsageAsync(Globals.UploadUrl, Globals.HttpClient);

 

// notify the Usage that the app goes to foreground

SAP.Usage.Usage.ApplicationWillEnterForeground();

 

Note that the application can also resume from a suspended mode.  If the datavault timeout has not expired, then the application will not display the logon screen and hence LogonCompleted event handler will not be fired. So it is also necessary to call the ApplicationWillEnterForeground method when the application resumes from a suspended state.  This can be done in the OnResuming event handler. 

 

private void OnResuming(object sender, object e)

{

   // notify the Usage that the app goes to foreground

   Usage.ApplicationWillEnterForeground();

}

 

The InitUsageAsync method takes 2 parameters.  The first parameter is the URL where you want to upload the usage statistics.  Note that Usage Collection only works with HCPms. Please refer to the HCPms documentation for URL to upload the usage statistics.

 

private static string uploadUrl = string.Empty;

public static string UploadUrl

{

   get

   {

      return "URL to upload.  Refer to HCPms documentation";

   }

}

 

The second parameter is the SAP.Net.Http.HttpClient.  The SAP.Net.Http.HttpClient is SAP’s implementation of a class for sending HTTP requests and receiving HTTP responses from a resource identified by a URI. In addition, the SAP.Net.Http.HttpClient class provides several additional functionalities over the base System.Net.Http.HttpClient class. 

 

private static SAP.Net.Http.HttpClient httpClient = new SAP.Net.Http.HttpClient();

public static SAP.Net.Http.HttpClient HttpClient

{

   get

   {

      var applicationSettings = LogonCore.ApplicationSettings;

        

      SAP.Logon.Core.Settings.IReadOnlyProperty baseUrlObject = null;

      applicationSettings.TryGetValue("BaseUrl", out baseUrlObject);

      Uri baseUrl = (Uri)baseUrlObject;

 

      SAP.Logon.Core.Settings.IReadOnlyProperty cookieObject = null;

      applicationSettings.TryGetValue("Cookies", out cookieObject);

      string cookieString = cookieObject == null ? null : cookieObject.ToString();

 

      if ((cookieString != null) && (baseUrl != null))

httpClient.SetInitialCookies(cookieString, baseUrl);

               

httpClient.DefaultRequestHeaders.TryAddWithoutValidation("X-SMP-APPCID", applicationSettings.ApplicationConnectionId);

 

      return httpClient;

   }

}

 

 

How can I end the user session?

The user session ends when the application enters the suspended state.  In Windows, the OnSuspending event is fired when application execution is being suspended.  This is a good place to end the user session.  This is done by calling ApplicationWillEnterBackgroundAsync asynchronously. 

 

private async void OnSuspending(object sender, SuspendingEventArgs e)

{

   var deferral = e.SuspendingOperation.GetDeferral();

   //TODO: Save application state and stop any background activity

   deferral.Complete();

   // notify the Usage that the app goes to background

   await Usage.ApplicationWillEnterBackgroundAsync();

}

 

 

Collecting Device specific info

Device specific information can be collected using the Usage Collection library.  This can be accomplished by querying the DeviceInformation property of the Usage class.  

 

Usage.DeviceInformation.Application

Usage.DeviceInformation.AppVersion

Usage.DeviceInformation.Model

Usage.DeviceInformation.Platform

Usage.DeviceInformation.SystemVersion

UsageReachability.CurrentReachabilityStatus

 

 

Timing events and logging key events

Events can be timed using the Usage Collection library.  The timer object allows you to measure the duration of an application operation. The duration is logged with the timestamp of the event and a key value that can be used for filtering analytics.

The following code snippet shows how to use the timer object. 

 

Usage.TimeStart ("myTimer");

var downloadedFile = await GetHugeFileAsync();

 

// stops the timer. The last 2 parameters are optional

await Usage.TimeEndAsync("myTimer", new Dictionary<string, string>() { { "customKey", "customVal" } }, "customType");

 

There is yet another way to use the timer object. 

 

var timerInstance = Usage.MakeTimer("myTimer");

timerInstance.StartTimer();

var downloadedFile = await GetHugeFileAsync();

 

timerInstance.StopTimer();

 

Logging key events can be done asynchronously by calling the LogAsync method. 

// adds a log entry into the report. The last 2 parameters are optional

await Usage.LogAsync("logKey", new Dictionary<string, string>() { { "testKey", "testValue" } }, "testType");

 

All of these usage statistics collected will be automatically sent to HCPms without any further coding from the developer.  An example of a sample record generated by the Usage Collection library that will be posted to HCPms is as follows…

 

{

       "type":"timer",

       "key":"DownloadTimer",

       "info":{

              "start":"2014-10-27T09:05:39.215Z",

              "duration":"10.6092"},

       "appSessionId":"",

       "userSessionId":"4dbcb6fa-7608-4430-bade-128f41a2f614",

       "timestamp":"2014-10-27T09:05:49.825Z"

}

 

 

  This concludes my blog on Usage Collection library.  See you all at the next blog. 

In this blog, I will be talking about batch processing which allows grouping multiple operations into a single HTTP request payload.  If network bandwidth is a concern, then batch processing allows you to send a single HTTP request to the backend server thereby avoiding multiple round trips.  A request within a ChangeSet can be referenced by subsequent requests within the same ChangeSet by referring to the Content-ID. This allows developers to implement deep inserts between entity sets that have parent child relationships.  The other advantage using batch processing is that you can provide transactional support within a ChangeSet. Transactional support however requires additional modifications on the backend and therefore not discussed in this blog. 

 

What can a batch request contain?

The body of the batch request must be made up of an ordered series of Query operations and / or ChangeSets. Based on this, it is important to note that the sequence in which you add the Query operations and / or ChangeSets to the body of the batch request matters.  Now, let us look into what constitutes a Query operation (in the context of a batch request).  Query operations can consist of READ operations to read 1 or more entities. Query operations can also contain Function invocations.   Ok, now that we understand what a Query operation is, let us look into what constitutes a ChangeSet.  ChangeSet can consist of an unordered group of 1 or more CREATE, UPDATE or DELETE operation.   You can also reference requests in a ChangeSet.  One quick note is that the sequence of the CREATE, UPDATE or DELETE operation inside a ChangeSet does not matter, unless you reference a request in a ChangeSet. 

 

batch1.png

 

How to create a ChangeSet and add operations to it?

Creating a ChangeSet is fairly easy in Windows SMP SDK.  There are no parameters for the constructor. 

 

this.ChangeSet = new ODataRequestChangeSet();

 

 

Adding operations to the ChangeSet is also fairly easy.  Note that you can only add CREATE, UPDATE or DELETE operations to a ChangeSet.   

 

Adding CREATE operation


var item = new ODataRequestParametersSingle("Suppliers", RequestMode.Create, entity);

this.ChangeSet.Add(item);

 

Adding UPDATE operation


var item = new ODataRequestParametersSingle("Suppliers(" + id + ")", RequestMode.Update, entity);

this.ChangeSet.Add(item);

 

Adding DELETE operation


var item = new ODataRequestParametersSingle("Suppliers(" + id + ")", RequestMode.Delete);

this.ChangeSet.Add(item);

 

ChangeSet can have 1 or more CREATE, UPDATE or DELETE operations.  The order in which you add the operations does not matter.

 

How to create a Query operation?

A Query operation is basically a READ operation or a Function invocation.  Both of them can be created in a similar fashion. 

 

var item = new ODataRequestParametersSingle(collectionName + "(" + readId + ")", RequestMode.Read);

 

 

How to create a batch request and add ChangeSets and Query operations to it?

Creating a batch request is fairly straight forward.  There are no parameters for the constructor. 

 

this.BatchRequest = new ODataRequestParametersBatch ();

 

 

Adding a ChangeSet to a batch request

This assumes that you have already added 1 or more operations to this ChangeSet.

 

if (this.ChangeSet.Count > 0)

{

   this.BatchRequest.Add(ChangeSet);

}

 

Adding a Query operation to a batch request


var item = new ODataRequestParametersSingle(collectionName + "(" + readId + ")", RequestMode.Read);

this.BatchRequest.Add(item);

 

The sequence in which the ChangeSets and Query operations are added to a batch request is important.

 

How to execute a batch request?

Once all the ChangeSets and Query operations have been added to the batch request, the batch request can be submitted as a single HTTP POST request.  This is done asynchronously and the corresponding response can be parsed. This assumes that you already have an Online Store opened.  You simply call the ScheduleRequest method of the Online Store and pass the batch request as a parameter. 

 

this.ResponseList = ((IReadOnlyCollection<IODataResponseBatchItem>)((IODataResponseBatch)((await this.Store.ScheduleRequest(this.BatchRequest).Response))).Responses);

           

 

How to parse the response?

The response from the server will be in the same exact order as the batch request.  For example, if a batch request contained the following…

 

Batch request

  1. ChangeSetA
  2. Query operation
  3. Query operation
  4. ChangeSetB
  5. Query operation
  6. ChangeSetC
  7. ChangeSetD

 

Then the response will also be in the same order…

  1. ChangeSetA
  2. Query operation
  3. Query operation
  4. ChangeSetB
  5. Query operation
  6. ChangeSetC
  7. ChangeSetD

 

However, the operations within a ChangeSet may not be in the same order. 

Simply iterate through the response collection.  For each item, check if it is a ChangeSet or a Query operation. 

 

If it's a ChangeSet


A ChangeSet has 1 or more operations inside of it.  So iterate through the ChangeSet to find the response for each operation.

 

if (item is IODataResponseChangeSet)

{

   foreach (var element in ((ODataResponseChangeSet)item).Responses)

   {

      BatchRequestItems.Add("ODataResponseChangeSet Status Code: " + element.Headers["http.code"].ElementAt(0));

   }

}

 

 

The batch response is parsed.  The first check is to determine if the item in the response list is a ChangeSet.  If it is a ChangeSet, then the results for the various operations inside a ChangeSet are enumerated and the HTTP status code is displayed to the end user.

 

If it's a Query operation


If it’s a Query operation, simply check the status of the response code…

 

else if (item is IODataResponseSingle)

{

   var response = (ODataResponseSingle)item;

   this.EntityList.Add((SAP.Data.OData.Online.ODataEntity)(response).Payload);

                   

   BatchRequestItems.Add("ODataResponseSingle Status Code: " + response.Headers["http.code"].ElementAt(0));

}

 

The batch response is parsed.  The second check is to determine if the item in the response list is a Query operation. If it is a Query operation, then the payload of the READ operation is added to the EntityList.  Also, the HTTP status code is displayed to the end user.

 

Ok, that concludes the blog on batch processing.  See you at the next blog. 

In this blog, I will be talking about how you can enable push notification on Windows Store applications built using the SAP Mobile Platform SDK (hereinafter referred to as “SMP SDK” or “SDK”).  Push notifications are ubiquitous in the mobile world.  Users are accustomed to getting the latest information immediately on their mobile device.  The SAP Mobile Platform runtime (hereinafter referred to as “SMP Server”) uses Windows Push Notification Services (hereinafter referred to as “WNS”) to send toast, tile, badge and raw updates to the mobile application in a dependable manner.


How it works?

There are basically 4 players in the push notification process.

  1. Windows Store application – This is the application that the end user interacts with
  2. Notification Client Platform (NCP) – Part of the Windows 8.1 OS.  It interacts with WNS and passes notifications to the client application
  3. Windows Notification Service (WNS) – Responsible for sending notifications to devices
  4. Cloud Service – SAP Mobile Platform performs this role – responsible for creating the push notification

push1.png

 

The workflow involves 6 steps.

  1. Client application requests Channel URI from the Notification Client Platform
  2. Notification Client Platform talks with WNS to obtain Channel URI
  3. Notification Client Platform returns Channel URI to client application
  4. Client application sends Channel URI to SAP Mobile Platform
  5. Something changes in the backend database.  SAP Mobile Platform sends the notification to WNS and Channel URI to WNS
  6. WNS sends the notification to Notification Client Platform which handles tile or toast updates



Step by step instructions on Push Notification workflow

As mentioned earlier, the Push Notification workflow has 6 important steps.  Let’s look at each step one by one.

 

Step 1

Client application requests Channel URI from Notification Client Platform


The Channel URI can be requested asynchronously from the Notification Client Platform by calling the CreatePushNotificationChannelForApplicationAsync method.  This can be done immediately after the on-boarding process.  It is recommended to make this call in the LogonCompleted event handler method.

 

// Request a push notification channel.

var CurrentChannel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();

 

Note:  The Channel URI can expire in 30 days.  So periodically renew the Channel URI as push notifications to expired Channel URI’s will be dropped. In the sample application, we request the Channel URI every time user logs in successfully and persist it in local storage.  If the newly requested Channel URI is different from the Channel URI in local storage, then we know that the Channel URI has changed.  We then send this changed Channel URI to the SMP Server.

 

Step 2

Notification Client Platform talks with WNS to obtain Channel URI


The Windows 8.1 operating system must be able to reach the WNS.  In the Package.appxmanifest file, make sure that the application has Internet capabilities.  This is enabled by default.

push2.png

 

Step 3

Notification Client Platform returns Channel URI to client application


No further action required from the developer.

 

Step 4

Client application sends Channel URI to SAP Mobile Platform


The client application must submit an HTTP POST request to the SAP Mobile Platform and send the newly acquired Channel URI as part of the message body.

 

URL: http://{SMPHost:Port}/odata/applications/{latest|v1/}{appid}/Connections('{appcid}')

 

Method : PUT

 

HTTP Headers :

  "Content-Type" = "application/atom+xml"

  "X-HTTP-METHOD" = "MERGE"

 

Body :

<entry xmlns="http://www.w3.org/2005/Atom" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices">

  <content type="application/xml">

    <m:properties>

<d:WnsChannelURI>{WNS Channel URI}</d:WnsChannelURI>

    </m:properties>

  </content>

</entry>

 

This can be accomplished asynchronously by

 

var writableSettings = logonCore.ApplicationSettings.GetWritableSettings();

writableSettings["WnsChannelURI"].Value = "YOUR_CHANNEL_URI_HERE_AS_A_STRING";

await logonCore.ApplicationSettings.UpdateSettingsAsync(writableSettings);

 

Step 5

SAP Mobile Platform sends notification and Channel URI to WNS


SAP Mobile Platform takes care of sending the notification and Channel URI to WNS behind the scenes as long as it’s properly configured.  However, the developer can trigger this action by sending an HTTP POST message. This is typically done when some value changes in the backend database and the end user needs to be notified of the change.

 

URL: http://{SMPHost:Port}/restnotification/registration/{applicationRegistrationId}

 

Method : POST

 

Body :

{"alert":"New Approval Request has been submitted"}

 

See appendix on how to configure the SAP Mobile Platform for Windows Push Notification.

push3.png

 

Step 6

WNS sends notification to Notification Client Platform which handles toast and tile updates


The client application must be configured to receive toast notifications.  Also, for tile updates the client application must be pinned to the Start screen using the Medium tile size.

 

push4.png

 

See you all in the next blog where I will be talking about batch processing.

 

Appendix

 

Configuring SAP Mobile Platform for push notification

push5.png

 

  • Choose Windows Store

push6.png

  • From the dashboard menu in the left pane, click on the option ‘Submit an app’

push7.png

  • Give the app an unique name

push8.png

push9.png

  • Click on Services to enable push notification

push10.png

  • Click on the link 'Live Services Site'

push11.png

  • Make note of Package SID and client secret.  You will enter these values in the SAP Mobile Platform application configuration.

push12.png

push13.png

 

Associating app with the Store

 

  • From within Visual Studio, right click on the project and select Store -> Associate App with the Store

push14.png

  • Login to your Windows developer account if prompted
  • Select your app name and click Next

push15.png

  • Click Associate to associate your app with the Store

push16.png

It’s been a while since my last blog on SAP Mobile Platform 3.0 SDK (hereinafter referred to as “SMP SDK” or “SDK”).  A lot of new features have been added to the Windows SMP SDK in a short period of time. Chief among them is offline support for Windows.  I will be writing a series of 6 blogs talking about the various new features that have been included in the Windows SMP SDK.  Sample applications that illustrate the various new features will also be included along with the blogs…

 

This series of 6 blogs will cover the following topics.

 

On-boarding process

A key requirement in the use of SAP Mobile Platform runtime (hereinafter referred to as “SMP Server”) is registering the device with the SMP Server.  This process of registering a device with the SMP Server is known as on-boarding the device.  Without the on-boarding process, the backend data source is agnostic of devices that are connected to it.  So important features like push notification, usage collection, device logging etc. cannot be implemented.  With SMP Server, it is mandatory for new devices to register with the SMP Server before any CRUD operations can be performed.  SMP Server therefore can tie every CRUD operation to a single device. From an administrative standpoint, this is extremely valuable.  Key features like push notification, usage collection, device logging etc. can now be implemented.

 

So, how do I on-board a new device?

On-boarding a new device is accomplished by sending an HTTP POST request with the device information.

 

 

URL: http://<hostname>:<port>/odata/applications/v1/<application id>/Connections

Method: POST

 

Body:

 

<?xml version='1.0' encoding='UTF-8'?>

<entry xml:base="http://127.0.0.1:8080/odata/applications/v1/com.sap.windows/" xmlns='http://www.w3.org/2005/Atom' xmlns:m='http://schemas.microsoft.com/ado/2007/08/dataservices/metadata' xmlns:d='http://schemas.microsoft.com/ado/2007/08/dataservices'>

  <category term='applications.Connection' scheme='http://schemas.microsoft.com/ado/2007/08/dataservices/scheme'/>

  <content type='application/xml'>

    <m:properties />

  </content>

</entry>

 

 

In the first installment of Windows SMP SDK, the on-boarding process required the developer to create their own UI, understand the process flow and implement the logic behind the process flow.  While this is not hard, it can get quite time consuming (especially the UI part of the process flow).  With that in mind, the SAP developers created the LogonFlow control.  The LogonFlow control is a new user control (a user control is essentially a component with visual representation) that greatly simplifies the on-boarding process.  In the simplest of use cases, the developer can drag the LogonFlow control from the Toolbox in Visual Studio and assign a few required default values.  And on-boarding is complete!!

 

 

Adding the LogonFlow user control to the Toolbox

The LogonFlow control is packaged as a NuGet package and shipped with the SMP SDK.  Install the LogonFlow control NuGet package as a reference in your application from within Visual Studio.  NuGet Package Manager installs all dependent packages for you automatically.  In addition, the proper package for your platform is installed.

 

  • Rebuild the solution.
  • Right click on the Toolbox (View -> Toolbox if it’s not displayed) and select ‘Choose Items…’.

Logon1.png

  • Click Browse to select the library from the bin\debug folder.  Note that you must have already added reference to the NuGet packages.

Logon2.png

  • The LogonFlow control should now show up in your Toolbox.

Logon3.png

  • Drag and drop the LogonFlow control into your .xaml file.  The properties for the user control can be from the properties page just like any other user control.

Logon4.png

 

 

Using the LogonFlow control

In order to use the LogonFlow control, set the Name and ApplicationId property of the LogonFlow control.

Logon5.png

In the code behind .xaml.cs file, assign a few required default values and you are done.

logonFlow.DefaultValues = new SAP.Logon.Core.RegistrationContext()

{

   ApplicationId = "com.sap.windows.flight",

   ServerHost = "54.80.15.207",

   IsHttps = false,

   ServerPort = 8080,

   CommunicatorId = "REST"

};

Further customizations…

The LogonFlow control can be highly customized.  Let us look at some of the ways we can customize the LogonFlow control.

 

Adding a background image – This can be done by selecting the Background property either using the properties page or setting the value directly in the .xaml page. 

Logon6.png

 

 

 

<FlowEngine:LogonUI Name="logonFlow" ApplicationId="com.sap.windows.flight" Background="{StaticResource BackgroundImage}"></FlowEngine:LogonUI>

 

Adding a demo mode


<FlowEngine:LogonUI Name="logonFlow" ApplicationId="com.sap.windows.flight" HasDemoMode="True"></FlowEngine:LogonUI>

 

Option for end user to edit ApplicationId


<FlowEngine:LogonUI Name="logonFlow" ApplicationId="com.sap.windows.flight" CanEditApplicationId="True"></FlowEngine:LogonUI>

 

Enable Mobile Place


<FlowEngine:LogonUI Name="logonFlow" ApplicationId="flightbasic" MobilePlaceEnabled="True" ApplicationVersion="1.0" Email="demo@sap.com">

</FlowEngine:LogonUI>

 

You can also set the values directly in the Properties page as well…

Logon7.png

 

In addition, the properties of the LogonFlow control can be modified in the code behind file.

 

logonFlow.CanEditApplicationId = true;

 

Adding a splash screen

<FlowEngine:LogonUI ApplicationId="com.sap.windows.flight" Name="logonFlow"

                           Background="{StaticResource BackgroundImage}">

   <FlowEngine:LogonUI.Splash>

      <DataTemplate>

         <Grid Background="{StaticResource SplashBackgroundBrush}">

            <Viewbox StretchDirection="DownOnly" Margin="12">

               <StackPanel VerticalAlignment="Center">

                  <Image Source="/Assets/SAP_Logo.png" Stretch="None" HorizontalAlignment="Center"/>

                  <StackPanel HorizontalAlignment="Center">

                     <TextBlock Text="SAP Sample Application Using FlowEngine" HorizontalAlignment="Right" Foreground="#666666" FontSize="24 Margin="0,24,0,12"/>

                     <TextBlock Text="© 2014 SAP SE. All rights reserved." HorizontalAlignment="Right" Foreground="#666666" FontSize="12" />

                    </StackPanel>

               </StackPanel>

            </Viewbox>

         </Grid>

      </DataTemplate>

   </FlowEngine:LogonUI.Splash>

</FlowEngine:LogonUI>

 

 

LogonFlow control events

The LogonFlow control has 2 events that a developer can handle.  One of them is LogonCompleted.  The other is DemoModeSelected (applicable only if HasDemoModeProperty is set to true).

 

The event handler for the LogonCompleted event has 2 parameters.  The first parameter is the sender object.  In this case, it is the LogonFlow control itself.  The second parameter is the LogonCompletedEventArgs object.  The LogonCompletedEventArgs object has a LogonCore property.  Typically in the event handler you will save the LogonCore object for later user and navigate to the main page of the application.

 

logonFlow.LogonCompleted += (sender, e) =>

{

   Globals.LogonCore = e.LogonCore;

   this.Frame.Navigate(typeof(MainPage));

};

 

LogonFlow control screen flow

 

Logon8.png

 

As you can see, the LogonFlow control greatly simplifies the on-boarding process and at the same time introduces quite a few functionality without the developer having to spend any effort.

 

See you all in the next blog where I will be talking about push notification.

Developers can use MAF Logon component to implement quickly the logon UI logic needed to onboard users with SMP 3.0. Although this component is very functional and easy to implement, companies would rarely use its default settings in mobile clients to go live. In this blog will explore some of the settings that can be modified in the MAF Logon component.

 

Prerequisites

The use of this component requires some dependencies that are described in this document How To... Setup MAF resources in Android Studio  Follow along and you will find that in this document we got an instance of the LogonUIFacade class and initialize it in the onCreate method of the LogonActivity as shown in the following code snippet


Code Snippet - initialize MAF Logon Component (with default settings)

 

   @Override

       protected void onCreate(Bundle savedInstanceState) {

              super.onCreate(savedInstanceState);

              // set context reference

        Context mContext = this;

              // get an instance of the LogonUIFacade

        LogonUIFacade mLogonUIFacade = LogonUIFacade.getInstance();

              //Initialize the Logon UI Facade

              mLogonUIFacade.init(this, mContext, <name of your application id>);

              // ask LogonUIFacede to present the logon screen

              // set the resulting view as the content view for this activity

        setContentView(mLogonUIFacade.logon());

       }

 

As a result, the mobile client will display the default screen flow of the MAF Logon component

customMAFOriginal.jpg

 

Now with few more lines of codes in the onCreate method, you can customize the MAF logon component to simplify the registration process

 

Step 1-2: Hide MobilePlace Window and then initialize MAF Logon component

In order to hide the MobilePlace window we would need to get the logon core shared preferences settings and modify the mobile place shared property before we get an instance of the LogonUIFacade class.

 

Code Snippet - Hide MobilePlace window and initialize LogonUIFacade class


//STEP1: Hide MobilePlace window

SharedPreferences prefs = getSharedPreferences(LogonCore.PREFERENCE_FILE_NAME, Context.MODE_PRIVATE);

SharedPreferences.Editor pEditor = prefs.edit();

pEditor.putBoolean(LogonCore.SharedPreferenceKeys.PREFERENCE_ID_MOBILEPLACE.toString(), false);

pEditor.commit();

 

//STEP2: Get an instance of the LogonUIFacade and initialize it as in the original code

LogonUIFacade mLogonUIFacade = LogonUIFacade.getInstance();

mLogonUIFacade.init(this, this, "<your application id>");


 

Step 3: Set Default Values in login fields

Mobile clients require the SMP server URL and port in order to onboard the user with SMP 3.0. However, in most cases companies would like to set these fields with default values and hide them from the final user.  Developers can set default values with the setDefaultValue methods of the LogonUIFacade class as shown following code snippet.

 

Code Snippet - Set default values


//STEP3: Set Default values

mLogonUIFacade.setDefaultValue(LogonCore.SharedPreferenceKeys.PREFERENCE_ID_SUPSERVERURL, "<your smp server host>");

mLogonUIFacade.setDefaultValue(LogonCore.SharedPreferenceKeys.PREFERENCE_ID_SUPSERVERPORT, "<your smp server port>");

//type "true" if the mobile client is using HTTPS, type "false" otherwise

mLogonUIFacade.setDefaultValue(SharedPreferenceKeys.PREFERENCE_ID_HTTPSSTATUS, "<true|false>");


 

Step 4: Hide fields in the login details

Generally, users only know their username and passwords. Developers may want to hide the rest of the fields to simplify the registration process. By default, all fields are displayed, hence you only need to specify those fields that are going to be hidden as shown below.

 

Code Snippet - Hide fields


// STEP4: Hide Login details

mLogonUIFacade.isFieldHidden(LogonCore.SharedPreferenceKeys.PREFERENCE_ID_SUPSERVERURL, true);

mLogonUIFacade.isFieldHidden(LogonCore.SharedPreferenceKeys.PREFERENCE_ID_SUPSERVERPORT, true);

mLogonUIFacade.isFieldHidden(LogonCore.SharedPreferenceKeys.PREFERENCE_ID_SECCONFIG, true);

mLogonUIFacade.isFieldHidden(LogonCore.SharedPreferenceKeys.PREFERENCE_ID_SUPSERVERFARMID, true);

mLogonUIFacade.isFieldHidden(LogonCore.SharedPreferenceKeys.PREFERENCE_ID_URLSUFFIX, true);

mLogonUIFacade.isFieldHidden(LogonCore.SharedPreferenceKeys.PREFERENCE_ID_MOBILEUSER, true); mLogonUIFacade.isFieldHidden(LogonCore.SharedPreferenceKeys.PREFERENCE_ID_ACTIVATIONCODE, true); mLogonUIFacade.isFieldHidden(LogonCore.SharedPreferenceKeys.PREFERENCE_ID_HTTPSSTATUS, true);

mLogonUIFacade.isFieldHidden(LogonCore.SharedPreferenceKeys.PREFERENCE_ID_GATEWAYCLIENT, true);

mLogonUIFacade.isFieldHidden(LogonCore.SharedPreferenceKeys.PREFERENCE_ID_SUPSERVERDOMAIN, true);

mLogonUIFacade.isFieldHidden(LogonCore.SharedPreferenceKeys.PREFERENCE_ID_PINGPATH, true);

mLogonUIFacade.isFieldHidden(LogonCore.SharedPreferenceKeys.PREFERENCE_ID_GWONLY, true);


 

Step 5: Hide Splash Screen

In order to hide the splash screen, developers need to set the showSplashScreen to false. However, this change restores the default values, so it’s important to call it at the end.

 

Code Snippet - Hide Splash Screen


// ask LogonUIFacede to present the logon screen

// set the resulting view as the content view for this activity

setContentView(mLogonUIFacade.logon());

// Hide splash screen

mLogonUIFacade.showSplashScreen(false);



After these changes, the new mobile client will display the screen flow of the MAF Logon component  as follows

customMAFModified-nosplash.jpg

 

 

 

Hope this helps,

Claudia

Hello SMP Administrators,

 

Do you want to expose database information into OData services? Integration gateway will help you. It will convert the data coming from the database to OData. As SMP is based on OSGi, you can not directly deployed JDBC driver to SMP server, it has to be OSGi enabled jar package.

 

There are two different ways to convert JDBC JAR to OSGi JAR:

 

1.  Eclipse plug-in development

2. Command line approach >> Limited to Oracle, SQL server & DB2 drivers

 

I will explain only 2nd approach:

 

In this example, i want to create an OSGi enabled bundle which includes Microsoft JDBC driver for SQL server.

 

Steps followed:

 

1. Copy driver jar file to a temporary folder (SMP server should be installed on same machine)

2. Navigate to this path: C:\SAP\MobilePlatform3\Server\tools\dbconfig

3. execute below command

        

F:\SAP\MobilePlatform3\Server\tools\dbconfig>create_jdbc_bundle.bat -sqlserver C:\temp\sqljdbc4.jar

   

      sqlserver.PNG


You will see a new OSGi enabled jar file has been created in the mentioned folder.

 

          sql2.png

 

  4. Copy this new jar file and paste in PICKUP folder and make sure that you see an "OK" message under .state folder

 

     pickup.PNG

                   

As mentioned already,  there are only 3 supported drivers: SQLServer, Oracle & DB2.


-sqlserver

-oracle

-db2


So for other databases like SAP HANA, SAP ASE, SAP SQL Anywhere, HSQLDB, MYSQL you can not use above command line method to generate OSGi enabled jar file. You have to go with Eclipse plug-in development

 

Inputs taken from SAP Note 2146914 and big thanks to Andreas Wegmann for providing extra information.

CC: SAP for Mobile

Regards,

JK

Hello SMP Administrators,

 

Let me start with an example. There are 3 users: A, B &C . SAP Mobile Platform administrator wants to grant admin role to user A, read-only access role (ie.Helpdesk) to user B and notification role to user C. He find this task very easy & simple. He logins to admin cockpit, adds two authentication providers System Login (Admin only) in admin security profile, provides Administrator and Helpdesk roles for A & B respectively. For User C, he adds a new System Login authentication provider but in Notification security profile. His job is done. User A got admin access to Admin cockpit, User B read access and User C can get notification via SMP.

     Untitled.png

Now, SMP Administrator has asked to grant admin roles to 3 more users, read-only access role to 10 more users and notification role to 20 more users.

But this time SMP administrator doesn't want to repeat what he has done before. He doesn't want to do it manually and wants to do it in a general way so that if in future, he gets any more requests for granting roles, he doesn't have to do much on SMP. He came to know that there is LDAP setup installed at organization. Could be like below:

 

     ldapstru.PNG

 

He thought of implementing LDAP/AD authentication provider in SMP security profile and mapping to the LDAP groups to which a user belongs.

 

    

 

Implementing LDAP/AD authentication provider in SMP Admin security profile:

 

  1. Login to Admin Cockpit > Settings > Security profiles > Admin (Cannot be deleted) > Edit
  2. Add a new authentication provider "Directory Serive (LDAP/AD)"

(below settings are as per above LDAP setup..there could be changes as per yours)

     ldap.png

 

Creating Users & groups in LDAP

 

1. I have added user a, b & c and created 3 different groups smp_admin_grp, smp_helpdesk_grp & smp_notification_grp.

2. Assigned a , b & c as uniqueMember in respective groups.

 

ldaprole.png

Modifying admin-role-mapping xml file

 

 

All predefined logical roles are there in SMP admin-role-mapping.xml file. Now we need to map newly created physical roles to predefined logical roles. Open admin-role-mapping.xml file can be found under C:\SAP\MobilePlatform3\Server\configuration\com.sap.mobile.platform.server.security\CSI .

 

Alert : Take a backup of same file before modifying it.

Map physical roles to predefined logical roles (as highlighted in bold)

<?xml version="1.0" encoding="UTF-8"?>

<rm:Mappings xmlns:rm="http://www.sybase.com/csi/3.1/mapping">

    <DefaultMapping>

        <LogicalName>Administrator</LogicalName>

        <MappedName>Administrator</MappedName>

        <MappedName>smp_admin_grp</MappedName>

    </DefaultMapping>

 

       <!-- Avatar Deployer Role Mappings -->

       <DefaultMapping>

             <LogicalName>NodeManager.deploycontent</LogicalName>

             <MappedName>Administrator</MappedName>

       </DefaultMapping>

       <DefaultMapping>

             <LogicalName>GenerationAndBuild.generationandbuildcontent</LogicalName>

             <MappedName>Administrator</MappedName>

       </DefaultMapping>

 

       <DefaultMapping>

             <LogicalName>IntegrationOperationServer.read</LogicalName>

             <MappedName>Administrator</MappedName>

             </DefaultMapping>

    <DefaultMapping>

        <LogicalName>Developer</LogicalName>

        <MappedName>Developer</MappedName>

    </DefaultMapping>

 

    <DefaultMapping>

        <LogicalName>Helpdesk</LogicalName>

        <MappedName>Helpdesk</MappedName>

        <MappedName>smp_helpdesk_grp</MappedName>

       </DefaultMapping>

 

    <DefaultMapping>

        <LogicalName>Notification User</LogicalName>

        <MappedName>Notification User</MappedName>

    </DefaultMapping>

 

    <DefaultMapping>

        <LogicalName>Impersonator</LogicalName>

        <MappedName>Impersonator</MappedName>

    </DefaultMapping>

</rm:Mappings>

 

 

Note: For Notification user role, you have to add a new authentication provider in Notification (cannot be deleted) security profile > Add> Directory service (LDAP/AD)

 

               ldapnotificaiton.png

 

     Once done, open Notification-role-mapping.xml file (C:\SAP\MobilePlatform3\Server\configuration\com.sap.mobile.platform.server.security\CSI) and map notification physical role to logical role as highlighted below.

 

<?xml version="1.0" encoding="UTF-8"?>

<rm:Mappings xmlns:rm="http://www.sybase.com/csi/3.1/mapping">

    <DefaultMapping>

        <LogicalName>Administrator</LogicalName>

        <MappedName>Administrator</MappedName>

    </DefaultMapping>

 

    <DefaultMapping>

        <LogicalName>Developer</LogicalName>

        <MappedName>Developer</MappedName>

    </DefaultMapping>

 

    <DefaultMapping>

        <LogicalName>Helpdesk</LogicalName>

        <MappedName>Helpdesk</MappedName>

    </DefaultMapping>

 

    <DefaultMapping>

        <LogicalName>Notification User</LogicalName>

        <MappedName>Notification User</MappedName>

        <MappedName>smp_notification_grp</MappedName>

    </DefaultMapping>

 

    <DefaultMapping>

        <LogicalName>Impersonator</LogicalName>

        <MappedName>Impersonator</MappedName>

    </DefaultMapping>

</rm:Mappings>

      

 

 

Few things to know

 

  1. By default, each logical role name is mapped to a physical role of the same name.

 

     <DefaultMapping>

     <LogicalName>Administrator</LogicalName>

     <MappedName>Administrator</MappedName>

     </DefaultMapping>

 

   2. By default, Admin security profile assigns smpAdmin user to the Administrator role. (Admin Cockpit credentials)

   3. As per documentation,

SMP includes <MappedName>Administrator</MappedName> in admin-role-mapping.xml, if you do not have a physical role/group called Administrator, delete this mapping from file to avoid unnecessary authorization checks and improve performance.

 

I agree with this point. But imagine if i remove this default mapping and if LDAP server is down, i will not even able to login with smpAdmin. In my opinion, let it be there, no need to remove.

4. You can configure security profiles in Management cockpit but role-mapping configuration has to be done manually by editing .xml file.

5. In SMP cluster, you can configure security profile from any active node, once you are done with changes (either in Management cockpit or editing .xml file or both), CSI pushes this changes to the shared database, which then propagates the changes to the cluster nodes.

6. Helpdesk role is usually granted to analyze root causes of issues/problems. Cannot perform any administrator related tasks.

7. There are also predefined Integration Gateway roles and these roles are mapped to Administrator logical role. But there is no read-only access role defined for Gateway management cockpit.

 

<!-- Avatar Deployer Role Mappings -->

       <DefaultMapping>

             <LogicalName>NodeManager.deploycontent</LogicalName>

           <MappedName>Administrator</MappedName>

    <MappedName>smp_admin_grp</MappedName>

       </DefaultMapping>

  

       <DefaultMapping>

             <LogicalName>GenerationAndBuild.generationandbuildcontent</LogicalName> in case of generate & deploy integration content to SMP

           <MappedName>Administrator</MappedName>

   <MappedName>smp_admin_grp</MappedName>

       </DefaultMapping>

 

 

<DefaultMapping>

             <LogicalName>IntegrationOperationServer.read</LogicalName> >>> needed in case of connecting to SMP server from eclipse kepler

          <MappedName>Administrator</MappedName>

   <MappedName>smp_admin_grp</MappedName>

</DefaultMapping>

 

8. The Developer role appears in the role-mapping.xml file, but is not implemented in SAP Mobile Platform.

 

I hope you find above information useful. Feel free to comment in case of any clarification and feedback.

 

CC: SAP for Mobile

Regards,

JK

This  blog is about Integration Gateway in SAP Mobile Platform 3.0 (SMP).

The OData specification describes the concept of navigation, in order to easily navigate from one resource to another one.


REST services can also support a kind of “navigation”, where the result set of one resource depends on another resource.

If we use such a REST service as data source in our Integration Gateway project, we can add the navigation capability to our OData service.

 

In this tutorial, I’d like to show an example for an easy-to-implement bidirectional navigation.

This tutorial is based on SMP SP07.

 

Update:

The sample REST service is now made available for public usage, so I've updated the tutorial to use it.

Also, the code is attached to this blog.

 

 

Prerequisites

 

I expect that you've gone through my previous tutorials , explaining REST data source, QUERY  and READ  operation.

Please check the Links section for more info.

 

Furthermore, you need:

 

  • Eclipse with SAP Mobile Platform Tools installed
  • SMP SP07
  • Basic knowledge about OData provisioning using the Integration Gateway component of SMP

 

 

 

Preparation

 

 

REST Service

 

For this tutorial, we need a REST service with at least 2 resources that supports that kind of “navigation”.

I’m using a service that is public available, you only need to sign up, afterwards you can access it with your SCN  user and password.

Please see the following document for details:

Getting started with the SAP Netweaver Gateway Service Consumption System

 

Finally, you should be able to access it via the following URL:

https://sapes1.sapdevcenter.com/sap/opu/rest/address/companies

 

 

Let’s have a look at how the backend-REST-service is used.

 

First, we invoke the URL for the “companies”:

 

https://scn.sap.com/https://sapes1.sapdevcenter.com/sap/opu/rest/address/companies

 

The result is a list of “companies”:

 

 

We can now choose one single “company”:


https://sapes1.sapdevcenter.com/sap/opu/rest/address/companies/2


The “company” is uniquely identified by the ID field, which is in the URL.

 

 

 

Now, this REST-service allows to add a segment to the URL, in order to retrieve the “contacts” that are responsible for this “company”:

 

https://sapes1.sapdevcenter.com/sap/opu/rest/address/companies/2/contacts

 

The result is a list of “contacts”, since there can be more than one “contact” for a “company”.

This is a one-to-many relationship.

In the details of a “contact”, we can see the ID of the “company” for which he is responsible.

 

 

 

After navigating from a “company” to its “contacts”, let’s now check the other way ‘round:

Open one single “contact”

 

https://sapes1.sapdevcenter.com/sap/opu/rest/address/contacts/46

 

 

 

Go to the “company” for which this “contact” is responsible:

 

https://sapes1.sapdevcenter.com/sap/opu/rest/addresshttps://sapes1.sapdevcenter.com/sap/opu/rest/address/contacts/46/company

 

Here we have a many-to-one relation, so the result is a single entry.

This company is the same that we’ve seen above.

 

 

 

 

Eclipse Project

 

 

We need a SAP Mobile Platform OData Implementation Project that already has the QUERY and READ operations implemented for both resources of the backend-REST-service.

And it should be up and running on our SMP server.

 

 

 

OData Model

 

The 2 EntityTypes are modeled according to the 2 resources of the backend REST service.

Additionally, we have an association between them.

Note that is has to be a bidirectional association, because we want to navigate from the "Company" to the "Contacts" and as well from a "Contact" to its "Company".


 

 

 

 

As we’ve seen above, a company can have many contacts, but a contact can only belong to one company.

So we specify a one-to-many relation.


 

 

 

 

And we create a Referential Constraint, which maps the property ID (of the Company) to the property COMPANY_ID (of the Contact)

 

 

 

 

 

That’s it for the modelling.

 

Next, we create the bindings and the Custom Code for QUERY and READ operations for both EntitySets.

Since this is explained in my previous blogs, we can skip all explanations about it.

 

Here are the relative URLs to be used in the Eclipse wizard:

 

QUERY companies/sap/opu/rest/address/companies
READ company/sap/opu/rest/address/companies/{ID}
QUERY contacts/sap/opu/rest/address/contacts
READ contact/sap/opu/rest/address/contacts/{ID}

 

 

 


Custom Code

 

 

After testing the QUERY and READ operations of our OData service at runtime, we can start taking care about the navigation.

 

Background

 

We invoke the URL for a company, e.g. https://localhost:8083/gateway/odata/<ns>/<service>/Companies('2')

In the response we can see that the Integration Gateway framework has generated a link element:

 

 

 

 

What happens, if we invoke that link?

https://localhost:8083/gateway/odata/<ns>/<service>/Companies('2')/ContactSet

 

Let’s try it.

 

 

Implementing the navigation for one-to-many relation

 

First we have to understand, that our script for the contact-QUERY is invoked.

The last segment of the URL is the ‘ContactSet’, which means that a collection of contacts is requested.

Therefore, when navigating from company to contacts, the contact-QUERY script is invoked (the default name Contacts_REST_Query.groovy)

Note that this is the case because we have a 1-to-many association between Company and Contact

 

First, the processRequestData() method is invoked.

And here we have to do the actual work.

The Integration Gateway framework doesn’t know how the backend REST service is designed.

Integration Gateway only knows that the user requested “give me the contacts which are relevant for the-company-with-ID-2”

But it doesn’t know how to get exactly this amount of data.

 

Therefore, we have to tell, how this is done, because we do know it: the URI has to look as follows:

companies/<valueOfTheID>/contacts

And we tell it in the processRequestData() method.

 

Summarizing:

Our task is to modify the backend-REST-service-URI that is called by Integration Gateway.



Implementing the processRequestData() method

 

We’re already familiar with such tasks.

We hock into the HTTP request, before it is fired agains the backend-REST-service.

In the processRequestData() method, we have to obtain the URI and modify it.

 

Note:

Our contact-QUERY script is invoked in both cases, with navigation and without navigation.

E.g.

https://localhost:8083/gateway/odata/<yourNamespace>/<yourService>/Contacts

https://localhost:8083/gateway/odata/<yourNamespace>/<yourService>/Companies('2')/ContactSet

 

Both URLs lead to our script being invoked.

So we have to consider it in our implementation.

 


Steps that have to be performed:

 

1) Obtain the UriInfo object

We need it in order to get the information about the navigation, which is in the URL.

 

     a) We need to know from where the navigation comes.

         In our example, we have only 2 EntityTypes, so the navigation starts always from a “Company”, but for other services it would be different.

         This is realized with the method UriInfo.getStartEntitySet()

 

     b) We need to know the ID of the “Company”, because depending on it, the amount of “Contacts” is different

        This information is contained in the UriInfo.getKeyPredicates()

 

2) Obtain the relative URI

We need it, in order to modify it.

 

This is how the URL that is invoked by the user of our OData service:

https://localhost:8083/gateway/odata/<yourNamespace>/<yourService>/Companies('2')/ContactSet

 

and this is how it has to look like when the backend-REST-service is called:

https://<host>:<port>/rest/address/companies/2/contacts

 

3) Set the relative URI

After correcting the relative URI, it has to be given back to Integration Gateway.

This is done by setting the header in the message object

 

And here’s the code snippet for our example.

 

 

def Message processRequestData(message) {

 

    UriInfo uriInfo = (UriInfo)message.getHeaders().get("UriInfo");

 

       // check if navigation is in place

    List<NavigationSegment> navSegments = uriInfo.getNavigationSegments();

       if(navSegments.size() < 1){

             // no navigation, just normal getEntitySet operation

             // this is the case for the call to

        //<...>localhost:8083/gateway/<...>/Contacts

             return message; // do nothing

    }else if (navSegments.size() > 1){

               /* this would be the case for e.g.

         * <...>/<srv>/Companies('2')/ContactSet('46')/Company/ContactSet

         * here the number of navSegments would be: 3

         * */

        log.logErrors(LogMessage.TechnicalError, "Not supported");

              return message; // ignore it for today

     }

 

 

       /* in our example, for the URL 

    * <...>localhost:8083/gateway/<...>/Companies('2')/ContactSet

    * the number of navSegments is: 1

    * */

       // handle the navigation

    String startEntitySetName = uriInfo.getStartEntitySet().getName();

 

       // we know that our EntityType has only one key field

    KeyPredicate keyPredicate = uriInfo.getKeyPredicates().get(0);

    String keyLiteral = keyPredicate.getLiteral();

 

       /* in our example, for the URL 

    * <...>localhost:8083/gateway/<...>/Companies('2')/ContactSet

    * the value of keyLiteral will be: 2

    * */

 

       // this is not really required for our service that has only 2 EntityTypes:

       if(startEntitySetName.equals("Companies")){ 

    

       String relativeUri = (String)message.getHeaders().get("RelativeUri");

             /* in our example, the value is: 

        * /rest/addressbook/contacts

       */

 

             // here we're doing the actual work: modify the URI

       String targetRESTuri = relativeUri.replace(

                                 "contacts", "companies/" + keyLiteral + "/contacts");

    

             // finally, set the manipulated REST-service-URI for the navigation

       ((Message)message).setHeader("RelativeUri", targetRESTuri);

            /* in our example, the final URI has to look like this:

       *  /rest/addressbook/companies/2/contacts

       */

    }

 

       return message;

}

 

 

 

Implementing the processResponseData() method

 

In terms of navigation, nothing has to be done here.

This method is already implemented for the QUERY operation, we don’t need to change or add anything.

 

 

 

Result

 

After deploy and configure the service, the navigation can be tried via a URL like this:

https://localhost:8083/gateway/odata/<yourNamespace>/<yourService>/Companies('2')/ContactSet

The result should be the same like in the backend:

https://<host>:<port>/rest/address/companies/2/contacts

 

 

Implementing the navigation for many-to-one relation

 

The first navigation example was for the 1-to-many relationship.

 

My sample backend-REST-service also supports to ask a single “contact” for the “company” that he is responsible for.

This is a many-to-1 relationship.

In our OData model, we’ve described this with a bidirectional association.

The OData modeler tool has generated a NavigationProperty called “Company” in the EntityType “Contact”.

Which means that we can invoke a URL like this:

https://localhost:8083/gateway/odata/<ns>/<service>/Contacts('46')/Company

 

Which corresponds to a backend-REST-service-URL like this:

https://<host>:<port>/rest/address/contacts/46/company

 

The implementation is very similar, the difference is:

Since here we have a many-to-1 relation, the script that is called is the Company-READ.

 

Again the response doesn’t need to be changed, it is just the READ implementation that is used to represent the target entry.

As for the request, the implementation is very similar like above

 

 

 

def Message processRequestData(message) {

 

  UriInfo uriInfo = (UriInfo)message.getHeaders().get("UriInfo");

   List<NavigationSegment> navSegments = uriInfo.getNavigationSegments();

   if(navSegments.size() == 1){

    String startEntitySetName = uriInfo.getStartEntitySet().getName();

    String keyLiteral  = uriInfo.getKeyPredicates().get(0).getLiteral()

    

       if(startEntitySetName.equals("Contacts")){

         // in our example: /rest/addressbook/companies/46

      String relativeUri = (String)message.getHeaders().get("RelativeUri");

           

         // modify the URI

         // in our example, we need: /rest/addressbook/contacts/46/company

     String targetRESTuri = relativeUri.replace("companies", "contacts") + "/company";

 

         // finally, set the manipulated REST-service-URI for the navigation

      ((Message)message).setHeader("RelativeUri", targetRESTuri);

    }

  }

 

  return message;

}






Summary

 

In this tutorial, we’ve learned how to realize navigation between 2 entities, based on REST data source.

It has been easy to realize, because the used backend-REST-service supported a kind of navigation that was easy to adapt to the OData way.

For other REST services it might be more tricky.

 

 

Links

 

Installing SMP Toolkit:

http://scn.sap.com/community/developer-center/mobility-platform/blog/2014/08/22/how-to-install-sap-mobile-platform-tools-for-integration-gateway-in-eclipse-kepler

 

Tutorial for OData provisioning in SMP:

http://scn.sap.com/community/developer-center/mobility-platform/blog/2014/06/10/creating-an-odata-service-based-on-sap-gateway-soap-jdbc-and-jpa-data-sources-ba

 

Preparing Eclipse for Groovy scripting: http://scn.sap.com/docs/DOC-61719

 

Introduction in REST datasource part 1: Understanding the return structure in xml

http://scn.sap.com/community/developer-center/mobility-platform/blog/2015/02/10/understanding-rest-data-source-in-integration-gateway-1-query-very-simplified

 

Introduction in REST data source part 2: Understanding the return structure in json
http://scn.sap.com/community/developer-center/mobility-platform/blog/2015/02/11/integration-gateway-understanding-rest-data-source-2-query--json-very-simplified

 

Introduction in REST data source part 3: Implementing the QUERY operation

http://scn.sap.com/community/developer-center/mobility-platform/blog/2015/02/12/integration-gateway-understanding-rest-data-source-3-query--xml-standard

 

Introduction in REST data source part 7: Implementing the READ operation

http://scn.sap.com/community/developer-center/mobility-platform/blog/2015/03/06/integration-gateway-understanding-rest-data-source-7-read--xml

 

Overview of all REST blogs

http://scn.sap.com/community/developer-center/mobility-platform/blog/2015/04/08/integration-gateway-rest-data-source-overview-of-blogs

 

 

The official documentation: http://help.sap.com/mobile-platform/

You might have faced authentication errors many times while accessing an application id through SAP Mobile Platform and just wondered if there is some tool to troubleshoot the error apart from checking the security log. Yes, there is a way you can debug authentication errors against an authentication provider. Here, i will be using a tool called CSI (Common security infrastructure) tool.

 

Note: This guide is mainly for System Administrators and can be tested on same machine where SMP server is installed. I have tested it on Windows.

 

(I followed this guide but unfortunately it didn't work as per steps mentioned)

 

You can find 3 different security profiles (admin, default and Notification) present under SETTINGS > SECURITY PROFILES in Admin cockpit and for all these profiles you can see two different .xml file.

e.g. For Admin, admin.xml and admin-role-mapping.xml

      For default, default.xml and role-mapping.xml

      For Notification, Notification-role-mapping.xml, Notification-role-mapping.xml

 

Example 1:

 

Lets go with Admin profile first, we got a default username as smpAdmin & password as s3pAdmin (might be different in your case). Lets assume you are facing authentication error while accessing an application through the Admin profile. This CSI tool will help you in debugging the error:

 

Steps to be followed:

 

1. Create a temporary folder somewhere on server machine

2. copy below files into same folder

  • csi-tool.jar file from C:\SAP\MobilePlatform3\Server\tools\csi
  • csibootstrap.properties and csikeystore.jceks files from C:\SAP\MobilePlatform3\Server\configuration\com.sap.mobile.platform.server.security
  • admin.xml and admin-role-mapping.xml from C:\SAP\MobilePlatform3\Server\configuration\com.sap.mobile.platform.server.security\CSI

 

It should like this:

     1.PNG

 

4. Open admin.xml file, modified value for RoleMapFile as mentioned below

 

     2.PNG

 

5. Open a command prompt > Navigate to temporary folder and run this

 

java -Dcom.sybase.security.BootstrapConfigurationFile="C:\Users\Jitendra\Desktop\CSItest\csibootstrap.properties" -cp csi-tool.jar;C:\SAP\MobilePlatform3\Server\plugins\* -Djava.util.logging.config.file=logging.properties com.sybase.security.tools.CSILauncher csi.diag.authenticate --USERNAME "smpAdmin" --PASSWORD "s3pAdmin" --CONFIG_FILE C:\Users\Jitendra\Desktop\CSItest\admin.xml

You can a success message about true authentication.

3.PNG

Let me try passing wrong password

 

     4_error.PNG

 

You can try the same for other security profile(s) as well following steps 1-5

 

Example 2:

 

I have created a new security profile named as SAP_SSO2. You can see that there are 2 different xml files created for same profile: ie. SAP_SSO2.xml and SAP_SSO2-role-mapping.xml under C:\SAP\MobilePlatform3\Server\configuration\com.sap.mobile.platform.server.security\CSI

 

     ldap1.PNG

 

I have copied the same files into that temporary folder and modified the SAP_SSO2.xml file as per step 4.

 

5.PNG

 

java -Dcom.sybase.security.BootstrapConfigurationFile="C:\Users\Jitendra\Desktop\CSItest\csibootstrap.properties" -cp csi-tool.jar;C:\SAP\MobilePlatform3\Server\plugins\* -Djava.util.logging.config.file=logging.properties com.sybase.security.tools.CSILauncher csi.diag.authenticate --USERNAME "p1176845" --PASSWORD "******" --CONFIG_FILE C:\Users\Jitendra\Desktop\CSItest\SAP_SSO2.xml

 

(I have entered wrong password)

 

 

          sso2_incorrect.png

It is throwing '401 unauthorized' error as expected.

 

Note: This method is also quite valid for a security profile having more than one authentication providers.

 

Enable security logs for more troubleshooting. Set log level to DEBUG (Admin cockpit > Logs > Settings >Security)

 

Regards,

JK

This  blog is about Integration Gateway in SAP Mobile Platform 3.0 (SMP).

It is a follow-up of the previous blog, where I explained how to implement the CREATE operation for your OData service, based on a REST data source.

Since SMP SP07, DELETE operation is supported by Integration Gateway.

 

If you’re new to the topic, check the Links section below where you can find the tutorials which get you up to speed.

 

Update:

The sample REST service is now made available for public usage, so I've updated the tutorial to use it.

Also, the code is attached to this blog.

 

 

Prerequisites

 

I expect that you've gone through my previous tutorials, explaining REST data source – QUERY and READ and CREATE operation – based on XML payload.

Please check the Links section for the relevant blogs.

 

Furthermore, you need:

 

  • Eclipse with SAP Mobile Platform Tools installed
  • SMP SP07
  • Basic knowledge about OData provisioning using the Integration Gateway component of SMP

 

 

 

Preparation


REST Service

For this tutorial, we need a REST service that supports writing scenario.

I’m using a service that is public available, you only need to sign up, afterwards you can access it with your SCN  user and password.

Please see the following document for details:

Getting started with the SAP Netweaver Gateway Service Consumption System

 

Finally, you should be able to access it via the following URL:

https://sapes1.sapdevcenter.com/sap/opu/rest/address/companies

 

 

Destination

 

In your SMP, you need to create an HTTP destination to the following URL:

 

https://sapes1.sapdevcenter.com

 

 

 

Furthermore, you need to download the certificate and import it into your SMP keyStore.

 

Note:

For this Destination, it isn’t possible to do a “Test Connection”, as the server doesn’t send a valid response.

As a workaround, you can proceed as follows:

Create a second destination, which is only used to test if the target host can be reached.

This second destination points to a URL that actually can send a valid response.

For example, enter the following URL as destination URL:

 

https://sapes1.sapdevcenter.com/sap/opu/rest/address/companies

 

 

 

So, you use this second destination to do the “Test Connection”.

If this succeeds, then the first destination should be fine as well.

This first destination will be used to configure our OData service, that we create in this tutorial.

 

If you get an error message on connection test, you might consider the following:

Note that you might need to enter proxy settings in your SMP:

https://localhost:8083/Admin/ -> Settings-> System

Note that you might need to restart the SMP server after changing the proxy settings.

 

 

 

 

OData Model

 

The OData model for this tutorial is the same like in the previous tutorial:

 

 

 

You can simply continue with the project that was created for the previous tutorial.

 

 

Bind data source

 

The Relative URI is the same like for the READ operation

 

/sap/opu/rest/address/companies/{ID}

 

 

Please check my previous tutorials for explanation about how to compose the Relative URI

 

 

 

 

 

 

Custom Code

 

Generate the Custom Code for Groovy.

 

For the DELETE operation, we don’t have to implement anything.

Why?

In the processRequestData() method, there’s no special requirement regarding request header or request body

In the processResponseData() method, no response body will be returned, according to OData specification.

 

 

Note:

If your backend requires additional handling, then of course you have to provide the implementation here

For example, my backend REST service requires the x-requested-with header to be set, while sending the request.

Therefore, I’m adding the following line to the processRequestData() method

 

 

 

def Message processRequestData(message) {

    message.setHeader("x-requested-with", "XMLHTTPRequest");

       return message;

}

 

 

 

Result

 

In order to test the DELETE  operation, proceed as described in the previous tutorial.

The response status code should be 204 No Content and the response body should be empty, as can be seen in the screenshot below.

 

 

 

Summary

 

In this tutorial, we’ve learned how to implement the DELETE operation in a Groovy script.

We’ve seen that it’s easy, as no implementation is required to fulfill the task

 

 

 

Links

 

Installing SMP Toolkit:

http://scn.sap.com/community/developer-center/mobility-platform/blog/2014/08/22/how-to-install-sap-mobile-platform-tools-for-integration-gateway-in-eclipse-kepler

 

Tutorial for OData provisioning in SMP:

http://scn.sap.com/community/developer-center/mobility-platform/blog/2014/06/10/creating-an-odata-service-based-on-sap-gateway-soap-jdbc-and-jpa-data-sources-ba

 

Preparing Eclipse for Groovy scripting: http://scn.sap.com/docs/DOC-61719

 

Introduction in REST datasource part 1: Understanding the return structure in xml

http://scn.sap.com/community/developer-center/mobility-platform/blog/2015/02/10/understanding-rest-data-source-in-integration-gateway-1-query-very-simplified

 

Introduction in REST data source part 2: Understanding the return structure in json

http://scn.sap.com/community/developer-center/mobility-platform/blog/2015/02/11/integration-gateway-understanding-rest-data-source-2-query--json-very-simplified

 

Introduction in REST data source part 3: Implementing the QUERY operation

http://scn.sap.com/community/developer-center/mobility-platform/blog/2015/02/12/integration-gateway-understanding-rest-data-source-3-query--xml-standard

 

Introduction in REST data source part 7: Implementing the READ operation

http://scn.sap.com/community/developer-center/mobility-platform/blog/2015/03/06/integration-gateway-understanding-rest-data-source-7-read--xml

 

Introduction in REST data source part 9: Implementing the CREATE operation

http://scn.sap.com/community/developer-center/mobility-platform/blog/2015/04/21/integration-gateway-understanding-rest-data-source-9-create-operation

 

Overview of all REST blogs

http://scn.sap.com/community/developer-center/mobility-platform/blog/2015/04/08/integration-gateway-rest-data-source-overview-of-blogs

 

The official documentation: http://help.sap.com/mobile-platform/

Actions

Filter Blog

By author:
By date:
By tag: