Quantcast
Channel: SCN : Blog List - SMP Developer Center
Viewing all 370 articles
Browse latest View live

New features for Windows in SAP Mobile Platform SDK - Part 2 (Push notification)

$
0
0

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 = awaitPushNotificationChannelManager.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


New features for Windows in SAP Mobile Platform SDK - Part 3 (Batch processing)

$
0
0

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 = newODataRequestChangeSet();

 

 

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 = newODataRequestParametersSingle("Suppliers", RequestMode.Create, entity);

this.ChangeSet.Add(item);

 

Adding UPDATE operation


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

this.ChangeSet.Add(item);

 

Adding DELETE operation


var item = newODataRequestParametersSingle("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 = newODataRequestParametersSingle(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 = newODataRequestParametersBatch ();

 

 

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 = newODataRequestParametersSingle(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)((awaitthis.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 isIODataResponseChangeSet)

{

   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…

 

elseif (item isIODataResponseSingle)

{

   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. 

New features for Windows in SAP Mobile Platform SDK - Part 4 (Usage Collection)

$
0
0

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. 

 

privatevoid 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.

 

privatestaticstring uploadUrl = string.Empty;

publicstaticstring 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. 

 

privatestatic SAP.Net.Http.HttpClient httpClient = new SAP.Net.Http.HttpClient();

publicstatic 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. 

 

privateasyncvoid 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

   awaitUsage.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

awaitUsage.TimeEndAsync("myTimer", newDictionary<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

awaitUsage.LogAsync("logKey", newDictionary<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. 

New features for Windows in SAP Mobile Platform SDK - Part 5 (Technical Cache)

$
0
0

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 = newODataStore(URL, newODataStore.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 = newODataEntitySet();

 

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 = newODataEntitySet();

 

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.

New features for Windows in SAP Mobile Platform SDK - Part 6 (Offline support)

$
0
0

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.

 

 

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 !

SAP Mobile Platform SDK 3.0 SP08 - What is new ?

$
0
0

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.

How to Stage Application in HCPms

$
0
0

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 UploadKapsel 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

Xamarin Andorid Native Application for Plant Stock-Transaction MMBE(mobile Version)

$
0
0

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.


How to uninstall SMP Server manually

$
0
0

There might be some situations where it is practically impossible to uninstall the SMP Server through standard means (Control Panel -> Uninstall a Program). The situations might be:

 

  • SMP Installation get corrupted
  • You might have accidentally deleted C:\SMP\MobilePlatform3 folder.

 

Following are the simple steps to manually uninstall the SMP Server:

 

  • Delete the folder C:\SAP\MobilePlatform3
  • Delete the SMP folder C:\Program Files\Common Files\InstallShield\Universal\SMP
  • Delete the SMP registry
    • Open registry – regedit
    • Delete HKEY_LOCAL_MACHINE\SOFTWARE\SAP\MobilePlatform
  • Restart the machine.

 

Reference: http://scn.sap.com/docs/DOC-39460

SAP Discovery Services for Application Security

$
0
0

Mobile apps for anything and everything is today’s reality. Thousands of new apps hit the market on a daily basis. Are they all secure? Not necessarily; however, most users assume that the developers have taken care of the security aspect.

Does SAP provide security to the applications developed using SAP HANA Cloud Platform Mobile Services? Absolutely. It’s our top priority! Whether on-premise or cloud, we help you improve application security.


How to Secure your Apps?

Use SAP Mobile Place, to secure apps developed using SAP HANA Cloud Platform Mobile Services.

SAP Discovery Services provides application specific configuration information to the user, and helps to secure your app  in three simple steps:

  1. Configuration: Admin creates configurations for the mobile application and pushes them over a secure connection to SAP Mobile Place.
  2. Identification: When a user starts the application for the first time, SAP Mobile Place identifies the user by the email address. On successful identification, a signed app configuration information is returned to the mobile app.
  3. Verification: The mobile app verifies the configuration provided by SAP Mobile Place and applies the same to the application, thus securing it.


Here is how it happens:

Blog.PNG


It’s time to make your apps less vulnerable by developing them using SAP HANA Cloud Platform Mobile Services and securing them using SAP Mobile Place.

In addition to the security aspect, Discovery Service enhances the user onboarding process by providing the initial configuration details.


Additional Information:

For more information about SAP Discovery Services, see the SAP HANA Cloud Platform Mobile Services product documentation:

https://help.hana.ondemand.com/hana_cloud_platform_mobile_services/frameset.htm?063c131a022e4432ae4f21022c90a178.html

SAP Work Manager 6.2.0: How To Add Pictures to a Local Notification (using BDS)

$
0
0

Hi there,

 

I decided to create the post because it's the second time I have to enhance Work Manager 6.x with this functionality that in my opinion should come out-of-the-box, I hope SAP will add it in the future.

 

It will also be useful for inexperienced developers since there are not many articles about how to develop in Agentry, and this task involves changes to all the different components.

 

The Problem:

 

In Work Manager 6.2 the option to attach pictures to a notification is disabled when the object is local, meaning that it doesn't exist yet in SAP and therefore it only has a temporary local ID.

 

This is very inconvenient for field technicians working offline. If they need to create a notification they can't add pictures to it before they synchronize the data to SAP and that is only possible when they are online. Therefore I assume that a lot of customers had to do the same enhancement.

 

The below explanation is highly technical and it requires a knowledge of Agentry Framework development, including UI, Java interfacing and Back End processing:

 

Development Overview:

 

  • The goal is to activate the add pictures button for local notifications. But unlike the standard action, for local notifications the transaction step won't have an update step sending the picture directly to the back end. First it will just be added to the DocumentLinks collection, and it will be posted only after its linked notification has been created in SAP during the transmit action,
  • From the back end point of view, when the notification is created, the link between the mobile app object local ID and the  ID given by SAP to the notification is stored in a custom table. Then, when the pictures are posted to SAP, the local notification ID will be replaced by the SAP one in an enhancement in order to attach the pictures to the correct notification.
  • Prerequisite. Work Manager should be configured to use BDS for Notification attachments.
  • Observations: For Work Manager 6.1 the idea can be reused but the DocumentLink actions and objects have some differences, therefore it must be adapted. I haven't investigated Work Manager 6.2.1 (released on May 2015) but I also saw some differences that may affect my changes.

 

 

Agentry UI changes:

 

The action to add picture to a notification has been replaced for a new action that calls the original one in case of a non-local notification or a new one (without update step) for local notifications.

 

The non-local notifications will be processed as in the standard solution, but the local ones, will first be first posted in SAP, and later on if they have documents attached, a new action during the transmit will be executed to add them once the real SAP notification ID is known.

 

Rules:

  • Created rule Z_EnableLocalNotificationPictureAttachment. This rule determines if the action step DocumentLinkPictureAddForNonLocalNotification for action Z_DocumentLinkPictureAddForNotification has to be called.
  • Created rule Z_EnableNonLocalNotificationPictureAttachment. This rule determines if the action step DocumentLinkPictureAddForLocalNotification for action Z_DocumentLinkPictureAddForNotification has to be called.
  • Modified rule ExecuteDocumentLinksClearLoopForNotifications to not clear document links from local notifications during the transmit action.
  • Modified rule EnableNotificationAttachment in order to show the attachments tab for local notifications as well as the non-locals.

 

Transactions:

  • Created transaction Z_DocumentLinkPictureAddForLocalNotification copy of the original DocumentLinkPictureAddForNotification but removing the update step, since we need to force the notification creation first in order to have the SAP notification ID.
  • Created transaction Z_DocumentLinkPostForLocalNotification. It is an edit without screen transaction with the update step AttachmentCaptureSteplet that will create the attachment in SAP.

 

Actions:

  • Created action Z_DocumentLinkPictureAddForLocalNotification, it will add a picture to the MainObject DocumentLinks collection but without an update step, like the standard solution does for non-local. It also adds a DocumentMetaData object to the Notification collection property.
  • Created action Z_DocumentLinkPictureAddForNotification. It calls the standard action DocumentLinkPictureAddForNotification for non-local notifications and the new Z_DocumentLinkPictureAddForLocalNotification for the local ones.
  • Created action Z_DocumentLinkPostForLocalNotification. It calls the transaction Z_DocumentLinkPostForLocalNotification in order to create the picture in SAP as BDS.
  • Modified action NotificationPostCurrent adding a new subaction step Z_DocumentLinksPostForLocalNotifications. This subaction loops over all the Main Object DocumentsLinks property items and for each one linked to the local notification being posted, calls the action Z_DocumentLinksPostForLocalNotification to create the picture in SAP.

 

 

Screens:

  • Modified NotificationAttachmentTileList_Detail_iPad from NotificationTileView screen set.

  Action from button "Upload picture" modified to use the new Z_DocumentLinkPictureAddForNotification.

 

This is how the new action to add pictures looks like:

 

 

 

And the new action to add pictures executed after posting the local notification.

 

 

 

Java changes:

 

  • Created class com.syclo.sap.workmanager.customer.bapi.NotificationPostBAPI.java:
    • Overridden method setHeaderParameters(Logger) in order to send the notification ID when posting a local notification. The reason is to have the ID available in SAP, it will be stored in a custom cross reference table and then the pictures attached to the local notification will be correctly added to the right one in SAP.
@Override    protected void setHeaderParameters(Logger log) throws Exception {        super.setHeaderParameters(log);        //Set the notification ID for local notificatons, it will be used to create an        //entry to the custom cross reference table to be able to add pictures knowing only the local ID        if (_notification.getIsLocal()) {            setValue(_imports, log, "IV_NOTIF_NO", _notification.getNotifNum());        }    }

 

 

Configuration Portal Adjustments:

 

  • Changed global parameter to assign the customer NotificationPostBAPI class
    • Group: BAPI_CLASS
    • Name: NotificationPostBAPI
    • Value: com.syclo.sap.workmanager.customer.bapi.NotificationPostBAPI

 

  • Created global parameter to assign a BAPI wrapper to the new customer NotificationPostBAPI class
    • Group: BAPI_WRAPPER
    • Name: com.syclo.sap.workmanager.customer.bapi.NotificationPostBAPI
    • Value: /SMERP/PM_DONOTIFICATION_CRT

 

 

ABAP Changes:

 

To be able to add attachments to a notification whose ID in the Agentry client is LOCAL_X, the following changes have been implemented in SAP in order to know the notification ID assigned by the backend after its creation and be able to add the corresponding attachment to it.

 

  • Created table ZAGT_CUST_XREF. This table will store the links between the Agentry client local notification ID and the value assigned by SAP after creation. It is a copy of the standard /SYCLO/MDW03 but reusing the original table was not an option because deprecated Java code was triggered once entries were added to this table and it was not possible (or easy) to control the flow of data. The table has a generic name since it may be used for other Agentry applications or objects.

 

  • BADI implementation in order to maintain the new cross reference table ZAGT_CUST_XREF every time a notification is created from the device and clear the entries after the fetch.

 

  • Standard Handler Class: /SMERP/CL_PM_NOTIFICATION_DO
  • Enhancement Spot name: /SMERP/MDO_PM_NOTIFICATION
  • Implemented/Modified methods:

 

  • /SMERP/IF_PM_NOTIF_BADI~CREATE_BEGIN
    • BADI Action and implementation description: Added the changes in order to read the local notification ID before calling the notification creation BAPI.
    • Implementation description: If the notification is local (creation), read the IV_NOTIF_NO field and clear it after saving the local ID from Agentry client to a BADI class attribute.

 

*    Read the client local notification ID into the BADI attribute      ASSIGN ('CS_MDO_INPUT_VALS-IV_NOTIF_NO->*') TO <lv_notif_no>.      IF <lv_notif_no> IS ASSIGNED AND <lv_notif_no> CS 'LOCAL_'.        me->notif_no = <lv_notif_no>.        CLEAR <lv_notif_no>.      ENDIF.

 

 

  • /SMERP/IF_PM_NOTIF_BADI~CREATE_END
    • BADI Action and implementation description: Read the new ID after creating the notification in SAP and store the cross reference between the local Agentry client ID and the new one in SAP.
    • Implementation description: Store the link between IDs in the new custom table ZAGT_CUST_XREF.

 

*    Get the local client and the SAP notifications ID values and create an entry in the xref table      ASSIGN ('CS_MDO_OUTPUT-ES_NOTIF_HEADER->*') TO <ls_bapi_notif_header>.      IF <ls_bapi_notif_header> IS ASSIGNED AND <ls_bapi_notif_header>-notif_no IS NOT INITIAL.        CALL METHOD cl_system_uuid=>if_system_uuid_static~create_uuid_c32          RECEIVING            uuid = ls_cust_xref-reference_guid.        ls_cust_xref-user_guid = <ls_bapi_input>-user_guid.        ls_cust_xref-object_type = me->wm_obj_type_notif.        ls_cust_xref-source_obj_key = me->notif_no.        ls_cust_xref-target_obj_key = <ls_bapi_notif_header>-notif_no.        ls_cust_xref-effective_ts = 0.        ls_cust_xref-changed_by = sy-uname.        /syclo/cl_core_bapi_tools=>get_system_time( IMPORTING ev_system_ts = ls_cust_xref-changed_ts ).        MODIFY zagt_cust_xref FROM ls_cust_xref.      ENDIF.

 

 

  • /SMERP/IF_PM_NOTIF_BADI~GET_END
    • BADI Action and implementation description: Remove all the cross reference custom table notification entries for the user executing the notification fetch.
    • Implementation description: Delete entries from table ZAGT_CUST_XREF for the object type notification and the user executing the fetch.
*    Delete user's notification cross reference IDs      DELETE FROM zagt_cust_xref        WHERE user_guid = <ls_bapi_input>-user_guid          AND object_type = me->wm_obj_type_notif.

 

 

Implicit enhancement point added in order to replace the local notification ID for the real one created in SAP when posting a BDS picture. Implementation details:

 

  • Standard Handler Class: /SMERP/CL_CORE_KWDOCUMENT_DO
  • Enhanced Method: BDS_DOCUMENT_CREATE
  • Implemented Points START:
    • Implementation description: Replace notification local notification ID by the SAP ID (instid) when the BDS document being created is a notification and the ID sent from Agentry starts by "LOCAL_", meaning that the attachment was added to a local notification before knowing the real ID.

 

ASSIGN <ls_mob_in_params>-value->* TO <lv_target>.      IF <lv_target> IS ASSIGNED.        ASSIGN <lv_target> TO <ls_business_obj>.
 *      Replace the local notification ID for the SAP ID        IF <ls_business_obj>-typeid = lc_type_maint_notif AND <ls_business_obj>-instid CP 'LOCAL_*'.          lv_source_obj_key = <ls_business_obj>-instid.          SELECT SINGLE target_obj_key FROM zagt_cust_xref INTO lv_target_obj_key            WHERE user_guid = me->active_user_guid              AND object_type = lc_wm_obj_type_notif              AND source_obj_key = lv_source_obj_key.          IF sy-subrc = 0.            lv_qmnum = lv_target_obj_key.            CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'              EXPORTING                input  = lv_qmnum              IMPORTING                output = lv_qmnum.            <ls_business_obj>-instid = lv_qmnum.          ENDIF.        ENDIF.      ENDIF.

Implementing a Skip Token OData Query Option ($skiptoken) for SOAP data using Integration Gateway

$
0
0

Introduction: What is $skiptoken?

 

 

When working with OData services, a developer might use the $top and $skip commands so that the client application can control paging.

 

However, some client applications might be coded inefficiently or even with malicious intent, so you might like to enforce Server Side Paging supported by the OData protocol. This means that the server does not return all results at once, but only a subset or “page” of all results.

 

If you choose to implement this, the client usually needs to know how many results there as a whole. To find this out, you can append the $inlinecount option to your query, like this:

 

http://<server>/ODataService/EntitySet?$inlinecount=allpages

 

The results will include a total count ‘inline’, and a url generated to get the next page of results.

 

The generated url includes a $skiptoken, that is the equivalent of a cursor or bookmark, that instructs the server where to resume:

 

http://<server>/ODataService/EntitySet?$skiptoken=4

 

 

Note: This feature is available for SP07 onwards.

 

 

Implementing a custom JavaScript to support Server Side Paging for your SOAP data source

 

 

In order to support the scenario above, $top and $skip are most likely used along with $skiptoken. Hence the operation should handle $top, $skip and $skiptoken OData operation together.

 

 

Steps in the SAP Mobile Platform Tools

 

 

  1. Create an OData Service Implementation Project
  2. Create an OData Model that includes a “Products” entity set for getProduct operation, which can have properties ProductID,ProductName, ProductDescription,,SupplierId,isDeleted

1st.png

 

3.  Right click on odatasrv file on the lefthand side of your Service Implementation Project and choose “Select data source”.

4. Select your entity set and choose the query CRUD operation. Select the data source SOAP Service.

2nd.jpg

5.Specify your required wsdl file and choose the required query operation from the list of soap operations and click on finish

 

3rd.jpg


6. Right click on Query and select Define Custom Code

4th.jpg

7. Select the script type as either JavaScript or Groovy script. In this example we chose to implement the custom code using JavaScript.

 

 

6th.jpg

8. The skip token value has to be fetched from the OData request URI and this value must be sent to the web service operation as an input parameter. A hashmap must be created in the processRequestData function with the input parameters (skipTokenID, top, skip) that will be passed in the URI. These parameters must then be set to message body as follows:

 

 

function
processRequestData(message) {

 

     
importPackage(com.sap.gateway.ip.core.customdev.api);
     
importPackage(java.util);
    
importPackage(com.sap.gateway.core.ip.component.commons);

importPackage(com.sap.gateway.ip.core.customdev.logging);    
importPackage(com.sap.gateway.ip.core.customdev.util);

 

     var uriInfo = message.getHeaders().get(ODataExchangeHeaderProperty.UriInfo.toString());

      var top = "";

      var skip = "";

      var productId = "";

      if (uriInfo.getTop() !=null)

             top = uriInfo.getTop();

      if (uriInfo.getSkip()!= null)

                skip = uriInfo.getSkip();

        if (uriInfo.getSkipToken() != null)

                productId = uriInfo.getSkipToken();

  

      var parentMap = new LinkedHashMap();

 

     // Add the Top and Skip productId and skipToken values to map

           parentMap.put("ProductId", productId);

           parentMap.put("top", top);

           parentMap.put("skip", "");

           parentMap.put("skiptoken", "2");

    // Logger

           log.logErrors(LogMessage.TechnicalError, "Values set correctly");

    // Set the message body back

           message.setBody(parentMap);

     return message;

}

 

 

 

 

9. In our sample implementation, the OData request would look as follows:

 

 

https://localhost:8083/gateway/odata/SAP/SKIPTOKEN;v=1/ProductSet?$skipToken=101

 

 

The resulting outgoing SOAP request body should then look as follows:

 

 
<soapenv:Body>

 

  <soapenv:Body>

     <web:getProducts>

     <web:ProductId>101</web:ProductId>

     <web:top></web:top>

     <web:skip></web:skip>

            <web:skiptoken>2</web:skiptoken>

           </web:getProducts>

  </soapenv:Body>

 

 

 

 

 

In this example, the $skipToken value equals 101 which is the ProductID of the last product. $skipToken value = 2 means, you will get only 2 entries at a time. This returns the next two entries. In this operation, moreEntries gives the information, if there are some more entries still there or not.

 

 

 
<soapenv:Body>

<getProductsResponse xmlns="http://webservice.SalesOrder.test.org">
<getProductsReturn>
<moreEntries>true</moreEntries>
<prod>            
<PRODUCTID>103</PRODUCTID>            
<ProductDesc>5L Olive Oil Can</ProductDesc>             
<SupplierId>10</SupplierId>

</prod>
<prod>
<PRODUCTID>104</PRODUCTID>
<ProductDesc>FreshAir Gum</ProductDesc>

<SupplierId>11</SupplierId>         
</prod>

</getProductsReturn>   
</getProductsResponse>

</soapenv:Body>

 

 

 

10. Once the web service response is returned, the response includes the entries and information whether there exist some more entries. These entries have to be added in the result set in processResponseData function. Also, we need to set the next the $skipToken value using the
following code:

     message.setHeader(ODataExchangeHeaderProperty.SkipToken.toString(), token);

 

11. Right click on Query and select Define Response Mapping

10th.jpg

12. Do the response mappings from your entity set to your WSDL file as required.

13. Right Click on your Service Implementation Project and select “Generate and Deploy Integration Content”. This will deploy the bundle to your
SMP server.

 

Now fire an OData Request https://localhost:8083/gateway/odata/SAP/SKIPTOKEN;v=1/ProductSet    on the browser and response will give the list of entries with link tag having a value similar to this: ?$skiptoken='MYID'

 

 

The support of skipToken and its implementation depends on web service, so you will have to adapt this example for your specific service.

SMP Tools on Eclipse - The "Luna" Cometh...

$
0
0

It’s that time of the year again… old making way for the new

 

As you probably remember, June is the time-frame when a newer version of Eclipse is added to the fold and the older one is dropped from SAP support. Keeping the same tradition, Eclipse Kepler will be out of support from SAP NetWeaver Cloud by end of June 2015. This also means that Eclipse Luna will be added to the list of supported Eclipse versions for our SAP Development Tools for Eclipse.

 

So what are the implications for SAP Mobile Platform Tools based on Eclipse Kepler?

 

We plan to release the next version of our tools compatible with Eclipse Luna in early July 2015. This will ensure business continuity for developers without having to wait long after Kepler support has ended. We have come a long way since releasing the 1st version of SAP Mobile Platform Tools in July 2014.

 

In 2015 alone we have added the following features to this toolset:

 

  • Jan 2015
    • Support for REST “Read” operation. “Query” was already supported
  • Apr 2015
    • Support for REST “Create”, “Update” and “Delete”. With this we now cover the entire CRUDQ stack
    • Release of Ogee as open source component to Apache foundation and consuming
    • Debugging support for scripts
  • July 2015

 

So please stay tuned for this release onSAP Development Tools for Eclipse page, and as always keep your feedback flowing

 

Thank you!

 

Amit Nigam

Product Management - SAP Gateway

Xamarin Native IPhone and Apple Watch for Plant Stock - Part1

$
0
0

Very Recently I had written a Plant StockNative application using Xamarin for Andorid

http://scn.sap.com/community/developer-center/mobility-platform/blog/2015/05/22/xamarin-andorid-native-application-for-plant-stock-transaction-mmbemobile-version

With all the buzz on apple Watch I wanted to develop Apple Watch application for SAP Plant Stock - I wanted to use the same Backend REST services that I had developed for my earlier android application.

When you want to develop an Apple Watch application ( as of version 1.0 of Apple Watch you have to have an iPhone app and watch app.  The Watch app

communicates with Phone app to get the data which is displayed on the watch.  So I decided to write an IPhone app using Xamarin and then extend this to Apple Watch in (Part 2 http://scn.sap.com/community/developer-center/mobility-platform/blog/2015/06/12/xamarin-native-iphone-and-apple-watch-for-plant-stock--part2).  If only wanted to develop an Watch app alone my Phone app need have any Great UI and handle interactions - As I wanted to have a

fully working iPhone App as well I created a new IPhone Project using Xamarin Studio.

image1.png

I choose a single View Application and double click on Main.storyboard file to open the Xamarin designer and added the tableView controller and couple of view controller, navigation controller and tool bar button -  For detailed description of app development check out on  Xamarin.com, youtube and github.

I have added nugets for SQLite, Newtonsoft json, ZXING bar code component from Xamarin Components - Added references to System.Net.Http.

image2.png

 

When the Appstarts in Appdelegate.cs file I have set up SQLite database that w will save Plant Stock so that you don't have to call SAP for the Same Selection Criteria.    The View Controller that is connected to the navigation controller is displayed on the phone

image3.png

In the Select Screen for the Plant and Location I have two Picker controls that are populated by an REST Call to SAP - For Material and Batch I have to textfields. For the Scan Barcode button I have code that calls ZXING component to scan using Iphone Camera - When you click on the get button -

a REST call is made to SAP to get Stock Records which are populated into an SQLite Database and is displayed on the tableview Controller.  For the table view Controller  I have custom cell with four lables.

1) in the table View Controller - you have to set TableView.Source to a class that implements UITableViewSource Interface methods.

2) When you click on Select the Select View Controller is called - This has two pickers - For the pickers to display the dropdown you have implement UIPickerModel for Plant and Loctions and then assign to the respective picker model.

 

 

3) When the Get Button is clicked make a REST Call to SAP to get the stock records and then call PopViewConrller to get back to the TableView Contoller as the seque that is called when you click the button performs a push.

In the next part I will extend this to work on Apple Watch

Xamarin Native IPhone and Apple Watch for Plant Stock - Part2

$
0
0

In Part1

http://scn.sap.com/community/developer-center/mobility-platform/blog/2015/06/12/xamarin-native-iphone-and-apple-watch-for-plant-stock--part1

I had developed Xamarin IOS native app for Plant Stock.  Here we will extend the app to develop  Apple Watch app to provide a similar functionality.

In the Phone app I could enter or scan the Material Code - There is no input Text Box where for you can  enter on the watch - There are table controls that you can scroll and Pick.  For this Watch App I ddi not want any material to be used and further I don't want the user to select Plant and Batch - The list of Materials that Watch app user can Choose are determined in SAP - I develop a REST call that will return a List of Materials that a watch app user can choose.  This can be changed by SM30 in a Ztable on SAP side.

Here are the steps to  create a watch app project using Xamarin

1) Open the Project Created in Part1 in Xamarin Studio

2) Right Click on the solution and choose Add new project and choose IOS - Extension and WatchKitApp

W1.png

When you do this you have 3 projects - In my case I started with project PlantstockIOS in part1 when I added the wachkit app project I have two more projects

A) PlatnstockIOS ( IPhone app created in Part1)

B) PlantstockIOSWatchExtensionApp - The runs on iPhone - When you want to test you have set this as start up project

C)PlantStockIOSWatchKitapp   runs on Watch - This has interface.Storyboard file -

The prjoject C references Project B and B in turn references Project A.  Double click on info.plist file and make sure that  Bundel ids are correct otherwise

the complier will error out.

3)  Select Watchkit  App project (Project C) and double click on interface.StoryBoard file - This opens up Xamarin Watch kit designer.

W2.png

 

 

 

 

4) Add 3 interfaces controls

     a)  Starting interface Controller - Add tablecontrol -give it a name, There is a RowController - Assign it class name and id - Drag a label and give it a name

         This will display the list of materials that the user is allowed to select to fetch the stock records from SAP

    b)  The Second will also be very similar - this will display records giving break down by Plant, Location and Batch - You can Click on this to display a details record

   c)  This interface control had  5 labels that will display Material ,Plant, Location, ,Batch and Stock - If the stock is less than 10 the color will be displayed as red otherwise green

5) When ever you give a name to the controls that you added in storyboard the corresponding Classes are created in the Extension project and you have write the code in the extension project.

6) Some of Events that required coding in the Watch kit interface to make the application happen

     a)  async overrid void WillActivate() - In this I call SAP to get Materialist    - await getSAPMaterial() and load the rows in the first Controller

     b)  overrid void DidSelectRow

            get the row that the user clicked on  - the row index is parameter to this call

     c)  override NSObject GetcontextForSeque

          get the selectedRow (index is parameter for this call) and pass the selected material to the next call

     d) Second Controller -

             override Awake(Context)

                Get the Selected Parameter from the context  material =  context.ToString();

      e) async overide void willActivate()

        await getstock(material) - Call SAP to get the stock records

        bind the records to the table rows

     f)override NSobject GetContextForSegue - Get the selected record to be passed on the next controller

7)  Third Controller Detail Record - Again get the different fields check qty for > 10 and display green other wise red. All this is done in the WillActivate method of this controller

W2.png

 

 

The Current Version of the apple Watch requires phone app (Extension app) - The recently accounced 2.0 does not require phone app - Xamarin has announced the release of preview version which I have not yet explored.   As Apple Watch is becoming popular I feel that for C# developer developing SAP Based Apple Watch should be interesting .


New Integration Gateway Features in SMP 3.0 SP07

$
0
0

Some of you may not aware the latest SP07 of SAP Mobile Platform (SMP) 3.0 provides a few long-awaited important OData features with Integration Gateway. In this blog, I will summarize the key features just to give you a quick overview.

IGW.jpg

 

  • Full REST support. With SP07, SMP now supports full CRUD on REST data source. This is on top of the Read and Query support in SP06. Adding to the list in SP07 are Create, Update and Delete. To learn more about implementation of REST data source with Integration Gateway, please check out the blog series created by our Gateway colleague Carlos Roggan.

     REST.jpg

 

  • Deep Insert for JDBC data source. With $expand in OData, you can query related objects such as supplier for a specific product. Vice versa, with deep insert, you can insert related objects in DB table with just one POST. The relationship can be multiple levels deep. For example, Category (10) - Product (Laptop) - Supplier (ABC Vendor) - Location (San Francisco) ... All can be inserted in just one shot! And this is out-of-the-box, no scripting is required. Awesome!

 

  • $expand and $filter on complex data model. This supported feature will give you the ability to query data in complex data model with navigation property. Consider a complex data model as shown below in the picture. Here NAV stands for navigation property, PK stands for primary key, and FK stands for foreign key. You could select all entries from Table1 where FK3 of Table3 eq to 10. The OData request will be as /Table1?$filter=NAV1/NAV2/FK3 eq 10.

     Complex.jpg

  • $skiptoken. A long-awaited OData feature for Integration Gateway, $skiptoken allows server size paging and it is much faster than $skip client side paging. $skiptoken is supported via custom scripting for different data sources.

 

That is it for some of the key features of Integration Gateway for SMP 3.0 SP7. If you want to know more about other cool features offered in SP7. Check out the What's New blog from my colleague Sami Lechner. I shall provide another blog very soon on the new features from the upcoming SP8. Stay tuned.

SAP Work Manager 6.2.1: How To Fetch Notification BDS Attachments During Transmit

$
0
0

Hi there,

 

A few weeks ago I created the following post: SAP Work Manager 6.2.0: How To Add Pictures to a Local Notification (using BDS)

 

In this blog, I will describe how to implement the closely related functionality that allows the user to have offline access to all the BDS notification pictures (or documents) after executing the transmit action.

 

Be careful with this because it can cause huge performance issues since we will retrieve the content of all the BDS documents linked to the user notifications. In my scenario, most of the documents, manuals and other work order related material are assigned via DMS and retrieved in a different way out of Agentry. Only the notification pictures are BDS attachments, therefore fetching the content won't cause too much trouble.

 

The Problem:

 

Work Manager 6.2.1 introduces a new functionality to download attachments via synchronous call instead of the push request fetch that is causing a lot of problems to developers implementing the solution. If you are running Work Manager 6.2.0 there is a separate note to implement this: http://service.sap.com/sap/support/notes/2142473

 

But even with this enabled, the documents are only retrieved under user request, so if there is no connectivity it won't be possible to fetch the attachment content. In my scenario, field technicians are working mostly offline and they need to have all the attachments available once the transmit finishes and they leave the facilities with internet access.

 

Development Overview:

 

  • The goal is to fetch the notification attachments during the transmit action. Since there is already a standard BAPI available to fetch the content of a document link, ABAP development won't be necessary.
  • For a better performance and in order to use the Agentry Exchange Mechanism, the document content will be retrieved during the notification fetch process and stored in a session component manager for Document Links. Later on an Agentry fetch will get the documents from the component manager without calling the back end again.
  • Prerequisite. Only valid for Work Manager 6.2.1 or implementing note 2142473 for 6.2.0

 

Agentry UI changes:


The Agentry changes are minimum. Only a new fetch executed every transmit has been created with its corresponding steplet.


  • Created new Java steplet DocumentLinksFetchOnTransmitSteplet calling the adhoc created class com.syclo.sap.workmanager.customer.steplet.DocumentLinksFetchOnTransmitSteplet

 

  • Created new fetch ZDocumentLinksFetchOnTransmit
    • Run on trasmit
    • Add steplet DocumentLinksFetchOnTransmitSteplet as a Server Exchange Step



 


Java changes:

 

There are multiple Java changes divided in 2 separate concepts. The 1st one is to fetch and store the notification document links with the binary content into the DocumentLinks component manager during the notification fetch execution. The 2nd one is the transmit steplet executed during the newly created Agentry Fetch that will retrieve the previously stored Document Links to populate the Agentry collection.

 

1. Store Document Links with content to the component manager. Classes Notification and WorkorderNotification have to be extended

 

 

  • Created static class com.syclo.sap.workmanager.customer.DocumentLinkUtils in order to have a reusable method to fetch the document link content
    • Added constants to access a new configuration portal parameter that enables this functionality
public static final String ATTACHMENT_SYNCH_ENABLE_SECTION = "APPLICATION_CONFIG";    public static final String ATTACHMENT_SYNCH_ENABLE = "ZAttachment.SynchOnTransmit";
    • Added method isFetchOnTransmitEnabled to check if the functionality is ON
public static boolean isFetchOnTransmitEnabled(User user) throws AgentryException{        return user.getPropertyValue(ATTACHMENT_SYNCH_ENABLE_SECTION, ATTACHMENT_SYNCH_ENABLE).equalsIgnoreCase("Y");    }
    • Added method getDocumentLinkContentToComponentManager(DocumentLink, User) to get the binary data of a given Document Link.

 

public static void getDocumentLinkContentToComponentManager(DocumentLink dl, User user){          Logger log = new Logger(user, "getDocumentLinkContentToComponentManager");          try{              DocumentLinkComponentManager documentLinkCompManager = (DocumentLinkComponentManager)user.getComponentManager(DocumentLinkComponentManager.class.getCanonicalName());                  if(dl.isBDS() && dl.getFileMimeType().matches("image/(.*)")){                                        ArrayList<? extends SAPObject> recs = null;                dl.setDocID(dl.getID());                String agentryLastUpdate = user.eval("<<lastUpdate format=\"%m/%d/%Y %H:%M:%S\">>");                GregorianCalendar lastUpdate = ConversionUtility.getTimeStampFromString(agentryLastUpdate, ConversionUtility.TIMESTAMPFORMAT);                          //Call the BAPI to read the binary data for the document                BDSDocumentSynchFetchBAPI bapi = (BDSDocumentSynchFetchBAPI) BAPIFactory.create("BDSDocumentSynchFetchBAPI",                        new Class<?>[] {User.class, GregorianCalendar.class, SAPObject.class},                        new Object[] {user, lastUpdate, dl});                                  bapi.run(dl);                recs = bapi.processResults();                          if(recs.size() > 0){                    //Store the retrieved document to the component manager,                    //DocumentLinksFetchOnTransmitSteplet will access to the data afterwards                    //documentLinkCompManager.addObject(recs.get(0));                                          Object val = recs.get(0);                    if (val instanceof DocumentLink){                        DocumentLink documentWithContent = (DocumentLink)val;                        documentLinkCompManager.addObject(documentWithContent);                        log.info("::Document ID = " + documentWithContent.getDocID());                    }                  }            }        }catch(Exception e){            log = new Logger(                    Server.getSAPServer(),                    "com.syclo.sap.workmanager.customer.DocumentLinkUtils" +                    "::getDocumentLinkContentToComponentManager(dl,user)::Thread " +                    Thread.currentThread().getId() + "::");            log.error(e.getMessage());            e.printStackTrace();        }    }

 

  • Created class com.syclo.sap.workmanager.customer.object.Notification extending from the standard.
    • Override method addDocumentArrayList(ArrayList<DocumentLink> linkedDocuments) in order to retrieve each document link content and store it into the corresponding component manager to previously populate the objects collection in Agentry with an independent fetch.

 

 

@Override    public void addDocumentArrayList(ArrayList<DocumentLink> linkedDocuments){          Logger log = new Logger(_user, "addDocumentArrayList");          try{            if(linkedDocuments.size() > 0 && DocumentLinkUtils.isFetchOnTransmitEnabled(_user)){                Iterator<DocumentLink> it = linkedDocuments.iterator();                          while(it.hasNext()){                    DocumentLink dl = it.next();                    DocumentLinkUtils.getDocumentLinkContentToComponentManager(dl, _user);                }            }        }catch(Exception e){            log = new Logger(                    Server.getSAPServer(),                    "com.syclo.sap.workmanager.customer.object.Notification" +                    "::addDocumentArrayList(linkedDocuments)::Thread " +                    Thread.currentThread().getId() + "::");            log.error(e.getMessage());            e.printStackTrace();        }        super.addDocumentArrayList(linkedDocuments);      }

 

 

  • Created class com.syclo.sap.workmanager.customer.object.WorkorderNotification extending from the standard.
    • Override method addDocumentArrayList(ArrayList<DocumentLink> linkedDocuments) in order to retrieve each document link content and store it into the corresponding component manager to previously populate the objects collection in Agentry with an independent fetch. The code is exactly the same than the used for the Notification object.

 

 

 

  • Created class com.syclo.sap.workmanager.customer.bapi. BDSDocumentSynchFetchBAPI extending the standard
    • Skip reading the document link ID from the fetch properties if the value is not initial. This is necessary since we are not calling the BAPI directly from the Agentry fetch as the original assumes. Now it’s called from the Notification/WorkOrder fetch and the properties are already set in the BAPI class _documentLink attribute.

 

 

@Override    public void init() throws Exception {        //Only call super init() if _documentLink.ID is initial        if(_documentLink.getDocID().equalsIgnoreCase("")){            super.init(); //Gets the ID from the Agentry fetch        }     }

 

2. Steplet executed by the newly created Agentry fetch to populate document links into the Agentry collection.

 

  • Created class com.syclo.sap.workmanager.customer.steplet. ZDocumentLinksFetchOnTransmitSteplet
    • The steplet class will be called by the Agentry fetch ZDocumentLinksFetchOnTransmit  during transmit.
    • The steplet will call a new step handler class called ZDocumentLinksFetchOnTransmitStepHandler

 

@Override    public boolean execute() throws AgentryException {        try {            ZDocumentLinksFetchOnTransmitStepHandler handler = (ZDocumentLinksFetchOnTransmitStepHandler) StepHandlerFactory.create(                    StepHandlerFactory.getClassName("ZDocumentLinksFetchOnTransmitStepHandler"),                    new Class[] { com.syclo.sap.User.class },                    new Object[] { _user });            ArrayList<SAPObject> recs = handler.run();            int sz = recs.size();            if (sz > 0) {                _returnData = createSAPObjectArray(getSAPObject(), recs);            }            return true;        }        catch (Throwable e)        {            throwExceptionToClient(e);            return false;        }    }

 

  • Created class com.syclo.sap.workmanager.customer.stephandler. ZDocumentLinksFetchOnTransmitStepHandler
    • This step handler class retrieves the document links from the corresponding component manager only if the parameter Attachment.Synchronous is enabled

 

public class ZDocumentLinksFetchOnTransmitStepHandler extends StepHandler {    public static final String COMPONENT_MANAGER_CLASS_NAME = DocumentLinkComponentManager.class.getCanonicalName();    protected DocumentLinkComponentManager _compManager;    public ZDocumentLinksFetchOnTransmitStepHandler(User user) throws Exception {        super(user);        _compManager = (DocumentLinkComponentManager)_user.getComponentManager(COMPONENT_MANAGER_CLASS_NAME);    }    /**     * Get the list of Document Links for the user by reading from the component manager     * table associated with the user object. This method does not call any BAPI. This method     * assumes that the user's hashtable has been populated previously from Notification and/or     * Work Order fetch     *     * @return list of DocumentLink objects populated in fetch     * @throws AgentryException     */    public ArrayList<SAPObject> run() throws AgentryException {        String methodLabel = "run";        Logger log = new Logger(_user, methodLabel);        ArrayList<SAPObject> recs = new ArrayList<SAPObject>();        if (DocumentLinkUtils.isFetchOnTransmitEnabled(_user)) {            _user.inFetch(true);            Hashtable<String, DocumentLink> headers = _compManager.getObjects();            Enumeration<?> e = headers.elements();            while (e.hasMoreElements()) {                Object val = e.nextElement();                if (val instanceof DocumentLink) {                    DocumentLink dl = (DocumentLink)val;                    recs.add(dl);                    log.info("::Document ID = " + dl.getDocID());                }            }        }        log.info("::end");        return recs;    }
}

 

 

 

Configuration Portal Adjustments:

 

Finally, some adjustments have to be done in the configuration portal for the newly created classes and parameters

 

  • Created a client global to enable or disable the new functionality
    • Group: APPLICATION_CONFIG
    • Name: ZAttachment.SynchOnTransmit
    • Value: Y

 

  • Change the global parameter to assign the class for Notification object in order to use the new customer class
    • Group: SAPOBJECT
    • Name: Notification
    • Value: com.syclo.sap.workmanager.customer.object.Notification

 

  • Change the global parameter to assign the class for Work Order Notification object in order to use the new customer class
    • Group: SAPOBJECT
    • Name: Notification
    • Value: com.syclo.sap.workmanager.customer.object.WorkorderNotification

 

  • Created global parameter to assign the ZDocumentLinksFetchOnTransmitStepHandler step handler class dynamically.
    • Group: STEPHANDLER
    • Name: ZDocumentLinksFetchOnTransmitStepHandler
    • Value: com.syclo.sap.workmanager.customer.stephandler. ZDocumentLinksFetchOnTransmitStepHandler

 

  • Changed global parameter to assign the customer BDSDocumentSynchFetchBAPI class
    • Group: BAPI_CLASS
    • Name: BDSDocumentSynchFetchBAPI
    • Value: com.syclo.sap.workmanager.customer.bapi. BDSDocumentSynchFetchBAPI

 

  • Created global parameter to assign a BAPI wrapper to the new customer BDSDocumentSynchFetchBAPI class
    • Group: BAPI_WRAPPER
    • Name: com.syclo.sap.workmanager.customer.bapi. BDSDocumentSynchFetchBAPI
    • Value: /SMERP/CORE_DOBDSDOCUMENT_GET

Completion handler APIs using blocks

$
0
0

With SP08 release, the OData SDK introduced block based completion handler APIs. The new APIs are used to open the online store and for scheduling execution requests.

 

Before we go into the specific APIs, let us take a quick look at blocks in iOS. In a nutshell, blocks are segments of code that can be passed to methods, just like you would pass values.Quoting from https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html:

 

“A block represents a distinct unit of work, combining executable code with optional state captured from the surrounding scope.”

 

 

One of the most common uses of blocks are as completion handlers. As specified inhttps://developer.apple.com/library/ios/featuredarticles/Short_Practical_Guide_Blocks/, completion handlers are callbacks that allow a client to perform some action when a framework method or function completes its task.Consider a function that needs to make a callback at a later time. To be able to use a block as a completion handler, the block must be passed as an argument to the function, so that when it finishes execution, the block is simply called. It is essential that the block is the last parameter of the function.

 

For example, here is the declaration of a function:

 

-(void)someFunction:(int)paramOne  andCompletion: (void(^)(NSString* blockParam))completionBlock;

 

Below is the function implementation:

 

-(void)someFunction:(int)paramOne andCompletion: (void(^)(NSString * blockParam))completionBlock{

     NSString *msg = @"Example of using blocks as completion handlers.";

     completionBlock(msg);

}

 

Consider the code below where the above function is invoked:

 

[selfsomeFunction:5andCompletion:^( NSString * blockParam){

 

// Define the block code here, thus only when ‘someFunction’ is invoked, the completion

// handler block is defined.

NSString *finalString = [NSStringstringWithFormat:@"%@Blocks make life easy.",blockParam];

NSLog(@"%@",finalString);

}];

 

 

The final string printed out :

Example of using blocks as completion handlers. Blocks make life easy.

 

With the above basics, lets take a closer look at the block based API for the store opening. The block API for opening the store  is :

 

-(void) openStore:(void (^)(id<SODataStoreAsync> store, NSError* error)) completionBlock;

 

A call to openStore using this API would be as follows:

 

SODataOnlineStore* onlineStore = [[SODataOnlineStorealloc] initWithURL:[NSURL URLWithString:self.endpointUrl] httpConversationManager:self.logonHandler.httpConvManager];

 

[onlineStore openStore:^(id<SODataStoreAsync> store, NSError *error) {

if(!error)

{

     [selfgetODataEntries];

} else {

      NSLog(@"%@",error.description);

}

}];

 

 

If the store was opened via the old delegate based APIs ( as shown below) , we would need to use the SODataOnlineStoreDelegates methods. As you can see the code with blocks is much succinct and readable than using delegates.

 

SODataOnlineStore* onlineStore = [[SODataOnlineStorealloc] initWithURL:[NSURL URLWithString:self.endpointUrl] httpConversationManager:self.logonHandler.httpConvManager];

 

[onlineStore setOnlineStoreDelegate:self];

NSError *error = nil;

BOOL success = [onlineStore openStoreWithError:&error];

 

#pragma mark - SODataOnlineStoreDelegates

-(void)onlineStoreOpenFinished:(SODataOnlineStore *)store{

     [selfgetODataEntries];

}

-(void)onlineStoreOpenFailed:(SODataOnlineStore *)store error:(NSError *)error{

     NSLog(@"%@",error.description);

}

 

The SDK also provides a list of completion block based network request APIs. ( Check the ODataOnlineStore.h header file for the full list.)  These APIs in particular eliminate a lot of application specific code that are required when using delegates. eg. When we use the SODataRequestDelegate methods, in each method such as the requestFinished: method, there is no need to query which kind of request was invoked ( create , update or delete) , the collection being queried etc.

 

Below is an example of an update request with completion handler block APIs and the delegate APIs for comparison. The block based code is much easier than the requestFinished: delegate method where we have to verify if the request mode was indeed update before proceeding to process the result.

 

Block based update request:

 

[onlineStorescheduleUpdateEntity:entity options:nilcompletion:^(id<SODataRequestExecution> requestExecution, NSError *error) {

    if (!error)

            NSLog(@"Update Successful");

}];

 

 

 

Delegate based update request:

 

 

[onlineStorescheduleUpdateEntity:entity delegate:selfoptions:nil];

 

#pragma mark - SODataRequestDelegates

 

- (void) requestFinished:(id<SODataRequestExecution>)requestExecution {

NSError * error = nil;

id<SODataRequestParam> requestParam = requestExecution.request;

id<SODataRequestParamSingle> request = (id<SODataRequestParamSingle>)requestParam;

    

if ([requestParam conformsToProtocol:@protocol(SODataRequestParamSingle)])

{

     if (request.mode == SODataRequestModeUpdate)

     {

      if (!error)

          NSLog(@"Update Successful");

      }

}

}

 

Based on the above examples , using completion-block based APIs has the following advantages:

 

  • There is no need to adopt protocols, or implementing delegate methods. This results in reduced code.

 

  • Since the completion handler block code is always defined when the method is invoked, it results in simpler, more readable code. Additionally, the completion handler can directly access variables existing locally in the scope where it is defined.

 

  • The completion-block based network request APIs in the SDK are easier to implement as they require less context–specific checks as with the delegate methods.

 

One final but very important point - you could choose to use the delegate or the block based APIs in your app, but do not mix the two flavors.

I hope this blog provided a good introduction to the completion-block APIs and help you take advantage of it in your next app.


See you in the next blog!

Iphone App in Swift Xcode 6.3 for SAP Plant Stock

$
0
0

In my first blog on mobile development about a year back

SAP and Xamarin for C# Mobile Cross Platform Development Tutorial

I had described in detail about mobile application architecture.  All my earlier applications were developed  in C# using Xamarin.  When Swift was released I wanted to explore this and write a iphone app with based on Swift and Xcode 6.3. This blog

describes the SAP Plant Stock application that I developed using SWIFT.  The ABAP side REST service has been developed as described in my earlier blog.

When I started my application I wanted to provide all the functionality that I had provided with a similar app that I had developed for Andorid and Iphone using Xamarin(C#) but develop this in SWIFT and Xcode 6.3

Xamarin Native IPhone and Apple Watch for Plant Stock - Part1

I Created a new Single View Application using Xcode  - Deleted the created View Controller - Added a new Table View Controller - Embedding this in Navigation View Controller  (Edit Menu- Embed in Navigation View Controller)

image1.png

Create Swift Classes one based on Table View Controller and other UI View Controller and assoicate these classes with storyboard.  Create a Segue way from the barbutton to the second View Controller which will allow the user to enter the selection - For the Material I have textFiled for the Plant and Location I populate two pickers from the data I get from SAP REST call.

 

To Make REST all to SAP I Created a protocol as follows

protocol SapRestProtocol {

    func didRecieveResponse(results: NSArray)

}

as I wanted to get the master data for Plants and locations at the start I changed the appdelegate file to implement the above protocol to read

class AppDelegate: UIResponder, UIApplicationDelegate, SapRestProtocol {

{

        // Override point for customization after application launch.

        checkandFetch()

        returntrue

  }

}

    func checkandFetch()

    {

       /

 

         var sapapi: SapRest? = SapRest()

         sapapi!.delegate = self

  sapapi!.GetSapMasterData("XXXXX",  RestTag: "/MADATA/MADATA")

}

 

    func didRecieveResponse(results: NSArray) {

      

        if results.count>0 {

     //populate the location and plant arrays which have to populated in the respective pickers

}}


in the SapRest Class I have code as follows

class SapRest: NSObject {

    var data: NSMutableData = NSMutableData()

    var delegate: SapRestProtocol?



func GetSapMasterData(stringPost: String, RestTag:String) {

       

     

        let path =  ""

        let  SapClient = ""

        let userName = "

        let passWord =  ""

        let loginString = NSString(format: "%@:%@", userName, passWord)

        let loginData: NSData = loginString.dataUsingEncoding(NSUTF8StringEncoding)!

        let base64LoginString = loginData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.allZeros)

        let urlPath = path + RestTag + SapClient;

 

            iflet url = NSURL(string: urlPath) {

               

                var request: NSMutableURLRequest = NSMutableURLRequest(URL: url)

                request.HTTPMethod = "POST"

                request.setValue("Basic \(base64LoginString)", forHTTPHeaderField: "Authorization")

                var postData:NSData = stringPost.dataUsingEncoding(NSUTF8StringEncoding)!

                var postLength: NSString = String(postData.length)

                request.HTTPBody = postData

               

                request.setValue(postLength asString, forHTTPHeaderField: "Content-Length")

                request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")

                request.setValue("application/json", forHTTPHeaderField: "Accept")

               

          

                iflet connection = NSURLConnection(request: request,delegate: self,startImmediately: false) {

                   

                 

                    connection.start()

                }

            }

        }

       

    //}


We now have the Plants and Locations in two arrays of strings so that when the user clicks on the Button - we will populate the pickers and transfer to the selection screen.

For the TableView Controller I have code - we have to implement the functions to implement the required functions


class StockListController: UITableViewController {


 

    overridefunc numberOfSectionsInTableView(tableView: UITableView) -> Int {

        return1

    }

 

   

    overridefunc tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

   

        returnitems.count

    }

   

    @IBActionfunc unwindToStockList(segue: UIStoryboardSegue)

    {

        if segue.identifier == "UnWindList" {

            iflet svc = segue.sourceViewControlleras? SelectViewController {

                in_rec = svc.indata

                ifin_rec.MATNR != "" {

                    getData()

                    self.tableView.reloadData()

                }

            }

        }

       

    }

 

   

    overridefunc tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        var cell = tableView.dequeueReusableCellWithIdentifier("StockCell", forIndexPath: indexPath) as? StockCell

       

        if cell == nil {

          

            cell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "StockCell") as? StockCell

           

        }

 

        var celldata = items[indexPath.row];

        // Configure the cell...

        cell!.txtPlant!.text = celldata.WERKS

        cell!.txtLocation!.text = celldata.LGORT

        cell!.txtBatch!.text = celldata.CHARG

        cell!.txtStock!.textString(format: "%d",celldata.LABST)

       

       

 

        return cell!

    }

Notice the UnWind usage - this comes into action when the user selects the parameters and cliks on Get Button - it is at at that time GetData will call SAP and get the stock records based on the Selection Parameters


Now let us look the code where the user is allowed to enter the selection criteria and then click on Get - For the material the user can scan for barcode

import UIKit

import AVFoundation

 

 

class SelectViewController: UIViewController, UITextFieldDelegate, UIPickerViewDelegate, UIPickerViewDataSourceScannerViewControllerDelegate {

    var scannerVC: ScannerViewController!

...

you have to implement the following delegate and datasource methods


func textFieldShouldReturn(textField: UITextField) -> Bool {

       textField.resignFirstResponder()

        returnfalse

    }

   

    func SetDataForPicker( PLANTS:[String], LOCATIONS:[String])->() {

        self.plants = PLANTS

        self.locations = LOCATIONS

    }

    overridefunc prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

        if segue.identifier != "UnWindList" {

          scannerVC = segue.destinationViewControlleras! ScannerViewController

          scannerVC.delegate = self

        }

    }

overridefunc viewWillAppear(animated: Bool) {

     

       self.pickerPlant.reloadAllComponents()

       self.pickerLocation.reloadAllComponents()

        selPlant = "ALL"

        selLocation = ""

    }

   

 

    @IBActionfunc btnScan(sender: AnyObject) {

      

      

    }

   

    //---fired when the barcode is captured---

       

    @IBOutletweakvar pickerPlant: UIPickerView!

  

   

    @IBOutletweakvar pickerLocation: UIPickerView!

   

   

    overridefunc didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

        // Dispose of any resources that can be recreated.

    }

   

    @IBActionfunc btnGetStock(sender: AnyObject) {

        ifselPlant == "ALL" || selPlant == "All" {

            selPlant = ""

        }

        ifselLocation == "ALL" || selLocation == "All" {

            selLocation = ""

        }

        iftxtMaterial.text.isEmpty {

           

        let alert = UIAlertController(title: "No Material",

                message: "Please Enter Material and Click Get Button",

                preferredStyle: .Alert)

           

            // Display the alert

            self.presentViewController(alert,

                animated: true,

                completion: nil)

            return

        }

       // if let parentc = self.parentViewController as? StockListController {

            indata.MATNR = txtMaterial.text

             indata.WERKS = selPlant

             indata.LGORT = selLocation

             indata.CHARG = selbatch

           

     

       

    }

   

 

  

    func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {

       return1

    }

}


We have to implement the required delegates - The picker data is populated when you click on the tool bar button -

overridefunc prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

        // Get the new view controller using [segue destinationViewController].

        // Pass the selected object to the new view controller.

        if segue.identifier == "ToSelect" {

            let vc = segue.destinationViewControlleras! SelectViewController

            var appdeg = UIApplication.sharedApplication().delegateas! AppDelegate

            vc.SetDataForPicker(appdeg.plants,  LOCATIONS: appdeg.locations)

 

        }

        elseif segue.identifier == "StockDetail" {

              let vc = segue.destinationViewControlleras! StockDetailViewController

            iflet currpath =  tableView.indexPathForSelectedRow(){

                vc.datarec = items[currpath.row]

            }

           

        }

    }


In addition I have one more view controller that displays the detailview when you click on TableLine -(segue.identifier == "StockDetail") - Just as I was finishing I upgraded my Iphone to 8.4 - and then I had to upgrade to Xcode 6.4 so that I can test this app on my phone. I have used the same Architecture as my mobile applications - There is lot material on SWIFT/XCODE so I wanted to emphasize on the SAP interfacing part.


image100.jpg




 




SMP 3 Security - Principal Propagation

$
0
0

SMP 3 Security - Principal Propagation

 

Topics

 

Overview

 

Principal Propagation is a Single Sign On possibility. When a user got already authenticated SMP can use Principal Propagation to create a temporary user certificate and then use this user certificate for connecting to the backend system. The backend system would then create a session/context based on the user certificate. SMP needs for this a CA certificate that can be used to create X509 user certificates. It can be configured how the distinguished name of the user certificates should look like. Usually SMP would insert the user name (of the currently active SMP session) as the common name (cn). This temporary created user certificate has a limited lifetime (e.g. 10 minutes) and will be transferred in a header called SSL_CLIENT_CERT to the backend system. The backend system needs to be setup for client certificate authentication, SMP needs to be trusted as well as SMP's CA certificate. Additionally the connection between SMP and the backend should be secured by mutual https connection.

 

The process is described in following picture:

 

SMP3 Security - Principal Propagation.png

 

1) Data Request

A request is sent to SMP, e.g. a GET Request to receive some data from backend system

 

2) SMP Authentication

SMP will authenticate the user by applying the specified Authentication Providers. Inside the Security Profile there also needs to be defined the Principal Propagation Authentication Provider. the Principal Propagation Module cannot be used alone inside the Security Profile. It must be used in combination with another Authentication Provider that is authenticating the user (e.g. HTTP/HTTPS Authentication, LDAP, SAML2, ...)

 

3) Temporary User Certificate Creation

The Principal Propagation Module will then get the username (of the already authenticated user) from the current session and will create a user certificate (by using a specified CA certificate from SMP's keystore)

 

4) Request to Backend System

SMP will establish a mutual https connection to the backend system. That means, that SMP will use a (beforehand defined) client certificate to connect to the backend system. After that SMP attaches the created X509 user certificate as base64 encoded string into the header SSL_CLIENT_CERT and forwards the request to the backend system.

 

5) Backend System

The backend system receives the request and recognizes that a user certificate is given inside the SSL_CLIENT_CERT header. Because SMP got setup as trusted system and also the CA certificate is trusted, the backend system is using this user certificate to create a user session. There has to be a user/certificate mapping in place, so that the user certificate can be mapped to a concrete existing backend user.

 

6) Data Response

Backend system has now a valid session and will return the requested data

 

7) Data Response

SMP will forward the data response to the mobile device.

 

 

 

Preparation

 

In the following I will described the process for Principal Propagation in combination with a SAP Gateway.

 

Make sure that SSL is enabled on the Gateway server. Steps for this are described for example in following post:
Enabling SSL (HTTPS) on SAP Gateway .


Also test before if your Gateway system is prepared for certificate based authentication. Required steps are described here:

Configuring Client Certificate Authentication (mutual https) on SAP Gateway

 

Steps

 

  1. Create (technical) user for SMP on Netweaver Gateway (in my case SMPCLIENT)
  2. Create a user certificate for SMP (that is accepted by Netweaver Gateway)
    Test it by using transaction CERTRULE and importing SMP user's certificate
    image57.png

  3. Create a CA certificate and key which can be used by SMP to create/sign temporary user certificates.
    Check if certificates created by this CA keypair are really trusted (and mapped) in Netweaver Gateway. If not create a new rule in CERTRULE
    (In my case my CA is called PrincipalPropCA)
    image58.png
  4. Import both cert/key pairs (smpClient and PrincipalPropCA)  as .p12 files into SMP’s keystore
    e.g. by using KeyStoreExplorer or since SP08 by using the Certificate tab in Management Cockpit
    SMP3_Certificates_Management_Cockpit.png
    Remember the given alias names (required in later steps)
    You should stop SMP server, then change the keystore and then restart the server again
  5. 5. Import PrincipalPropCA into your Server PSE in STRUST
      image60.png
  6. 6. Add to the Gateway’s profile parameter the following parameters (in RZ10)
    (Server Restart required)
    Without these values SAP Gateway will not accept the user certificates sent inside the SSL_CLIENT_CERT header.

Parameter

Value

Description

icm/HTTPS/trust_client_with_issuer

EMAIL=marvin.hoffmann@sap.com, CN=MyCA, OU=MIT, O=SAP, L=Walldorf, SP=NRW, C=DE

CA cert of SMP client

icm/HTTPS/trust_client_with_subject

CN=smpClient, OU=MIT, O=SAP, L=Walldorf, SP=BW, C=DE

SMP client certificate

Values can be found inside the client certificate that is used by SMP to establish a mutual https channel to Gateway.

Tip: I recommend checking the exact values inside dev_icm log, because the string has to match exactly the sender, so e.g. if a space is missing it is not working. Simply execute it once, and check with ICM Trace set to 2. (see Debugging section)

 

Using SMP’s base Certificate

 

For testing scenarios you can also use smp’s base certificate: smp_crt
It is also tagged as CA certificate. You could use it for both (smp client as well as principal propagation CA).

image62.png

 

But this should be only used for testing/development purpose...

 

 

SMP Configuration

 

Quick Overview

  • Security Profile contains two Authentication Providers
    (Principal Propagation is not authenticating the user)
  • The Subject Pattern in Principal Propagation Auth Provider Settings needs to be setup in the backend system as user mapping (usually Subject DN of X509 certificate)
  • In Backend Configuration add the certificate alias which identifies a certificate (private key required) in SMP‘s keystore that is used by Principal Propagation Auth Provider to create the temporary user certificate
  • In Backend configuration add „X509“ as SSO mechanism
  • After the user got authenticated (by the first auth provider) the Principal Propagation Auth Provider will create a temporary user certificate for this specific user. This temporary user certificate will be attached as Header SSL_CLIENT_CERT to request against the backend
  • The communication between SMP and backend should be encrypted by mutual HTTPS

 

Steps

 

Create a new app which points e.g. to an odata service from netweaver gateway. Choose as SSO mechanism “X.509”. Additionally you have to set the “Certificate alias”. Here you have to specify the alias name of the key pair which will be used to establish the mutual authentication channel between SMP and backend.

image63.png

 

Additional information to SSO Mechanism (from SAP Help): X.509 Certificates

  • Connects to the back end using the configured technical user X.509 certificate. The end-user certificate is passed in the SSL_CLIENT_CERT HTTP header. Configure the back end to allow the technical user to impersonate the end user and execute the request in the context of the end user. The end-user certificate may be generated by the Principal Propagation provider that is configured in the security profile, or it may be supplied by the end user when he or she authenticates to the server over a mutually authenticated HTTPS connection. You can use this mechanism with either the X.509 authentication provider or the Principal Propagation provider that is configured in the security profile.

 

Add a new security profile. Inside the Security Provider use an authentication provider to authenticate the user (in my case SAML2), then add Principal Propagation module as second provider

 

image64.png

image65.png

The Subject Pattern of the user certificate can be chosen. ${name} is the variable which gets replaced during runtime with the current authenticated username. This user certificate will be valid for 10 minutes only. It will be signed by PrincipalPropCA (which is a CA certificate pair that we imported into SMP’s keystore.

 

image66.png

 

Debugging - ICM Logging/Tracing

 

Especially if communication between SMP and Gateway is not working, it makes sense to have a closer look on what exactly has been received on Netweaver side. For that you can increase the log level of ICM and then use the dev_icm log to check what has been received.

 

Open transaction SMICM and choose Goto > Trace Level > Set

 

image82.png

And set it e.g. to "2 Full Trace"

image83.png

After that execute a failing request, then check ICM Trace File:

image84.png

 

E.g. the following error is thrown, if SMP is not setup correctly as trusted system on SAP gateway: Intermediary is NOT trusted / Reject untrusted forwarded certificate

HttpModGetDefRules: intermediary is NOT trusted -> remove SSL header fields

Reject untrusted forwarded certificate (received via HTTPS with untrusted certificate): subject="

HttpModHandler: remove incoming ssl header

image87.png

So you can see here, that the DN of SMP's client certificate is not exactly the same as that one we are trusting (missing spaces...). So we need to correct this in transaction RZ10:

image61.png

 

 

Logo.png

Viewing all 370 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>