1 2 3 16 Previous Next

SAP Mobile Platform Developer Center

226 Posts

File this under "Stuff that should have always worked this way by default"...

 

The Android Development Toolkit (ADT) comes with two important applets - the SDK Manager, and the Android Virtual Device Manager.  The SDK Manager is the tool that downloads and installs all the Android APIs, build tools, etc.  The Virtual Device Manager allows you to create and customize the Android emulators.  You can setup as many different device emulators as you desire, each with different form factors and Android OS levels

 

Two very important tools, but neither is available directly from Eclipse (which is where I spend most of my time when developing SMP apps for Kapsel or native Android).  On Windows, they're executables located in the android-sdk\tools\lib folder.  So, you could create shortcuts out on the desktop, or pin them to the tart menu, but that means leaving Eclipse to launch them.  Too much trouble for me...  It's even more complex on my Macbook!  You have to open a Terminal window, navigate to the /tools folder, and run android avd or android sdk.  I mean, I went to the trouble of installing the ADT plugins into Eclipse - the least they could do is give me an easier way to run these critical apps!

 

 

Well, I found a way to add them to the Window menu in Eclipse, and it was so easy it made me wonder why this isn't the default behavior...  (The screen shots here are for the Mac version of Eclipse Kepler, but this is exactly the same for Windows and Linux).

 

 

Go to Window > Customize Perspective...

 

Screen Shot 2014-10-18 at 8.13.05 PM.png

 

Select the Command Groups Availability button, and check ON the Android SDK and AVD Manager option.

 

Screen Shot 2014-10-18 at 8.13.32 PM.png

 

Now, when you return to the Window menu, you'll see the two options for Android SDK Manager and Android Virtual Device Manager right under the Navigation... option. 

 

Screen Shot 2014-10-18 at 8.14.05 PM.png

In this blog I would like to introduce you to the custom scripting capabilities that were introduced with the Integration Gateway in SAP Mobile Platform 3.0 SP04. If you are interested in seeing which other new features were introduced, I can highly recommend you to read my colleague Mustafa's blog: What's New in Integration Gateway in SAP Mobile Platform 3.0 SP04.

 

A little while ago I wrote a blog/tutorial about creating an OData service based on SAP Gateway, SOAP, JDBC and JPA data sources. As you could see in that blog, there was only a mapping capability available for SOAP data sources. For ODC, JDBC and JPA data sources this meant that the names of the entity sets and their properties had to correspond exactly to the fields of the data sources. Sometimes this resulted in rather ugly looking entity sets, as you had to keep the case lettering identical to the source and keep the technical field names of the source. You can see it in this example:

mapping before.png

However, as of the latest release of SAP Mobile Platform, it is possible to create powerful enhancements using the JavaScript and Groovy scripting languages. After assigning a data source to an entity set, there some user exits are provided where you can modify the requests before they are sent to the data source, and modify the responses as they are returned from the data source. Some examples of what you can do with this are delta token handling, tombstone support, SOAP authentication, or data source mapping.


In the following, I'd like to show you how you can map an entity set to a JDBC data source using JavaScript. Taking the example from the image above, the result will look as follows, after having performed a data mapping:

mapping sp4.png

Setting up the SAP Mobile Platform Tools

Along with the new service pack, a new set of OData modeling tools called SAP Mobile Platform Tools were released. You can find more information about this in my colleague Amit's blog: SAP Gateway Developer Tools – Bridging the Past, Present and Future.


The SAP Mobile Platform tools are based on Eclipse Kepler and support the new features of the SAP Mobile Platform 3.0 SP04. But of course they are downwards compatible and you may use them to work on your SP03 projects.


So, if you haven't done so already, please download Eclipse Kepler and install the SAP Mobile Platform Tools by following these instructions.

 

Implementing the Data Mapping

1. Create a new project

Do a right click in the Project Explorer view and select: New > Other > SAP Mobile Platform > SAP Mobile Platform OData Implementation Project

17-10-2014 15-56-18.png

 

Provide a project name and select the target runtime SAP Mobile Platform 3.0 SP4.

17-10-2014 15-59-02.png

Provide a model name, select Blank OData Model and finish the wizard.

17-10-2014 16-05-36.png

 

2. Modeling the entity set and assigning the data source

Please model your entity set as follows:

17-10-2014 16-27-10.png

Perform a right click on the JDBCmapping.odatasrv file and select Select Data Source. In the following wizard, assign the Data Source JDBC and finish.

17-10-2014 16-33-22.png

3. Defining the Custom Code

Expand the nodes under the odatasrv-file, do a right click on Data Source JDBC and select Define Custom Code.

17-10-2014 16-30-42.png

You may keep the suggested file name, and select the script type JavaScript.

17-10-2014 16-35-44.png

 

Now Eclipse will open a JavaScript file with some sample coding. The script contains four JavaScript functions; processRequestData, processRequestSQL, processResponseData, processResponseResult:

17-10-2014 16-38-54.png

4. Implementing the mapping

In order to do the mapping, we will need to implement the processRequestData function. Luckily, it comes with sample coding to perform such a mapping. The coding should look like this:

function processRequestData(message) {
  //Import statements
  importPackage(com.sap.gateway.ip.core.customdev.util);
  importPackage(java.util);
  importPackage(org.apache.olingo.odata2.api.edm);
  importPackage(com.sap.gateway.core.ip.component.commons);
  importPackage(org.apache.olingo.odata2.api.uri);
  importPackage (com.sap.gateway.ip.core.customdev.logging);
  importPackage(com.sap.gateway.ip.core.customdev.api);
  //Create the table map and populate with EntitySet name and DB Table name
  var tableMap = new HashMap();
  tableMap.put("LocationsSet","COMPANYLOCATIONS");
  //Create the entity map and the two property maps
  var entitiesMap = new HashMap();
  var companyLocationsMap = new HashMap();
  //Property map
  companyLocationsMap.put("LocationID","LOC_ID");
  companyLocationsMap.put("LocationName","DESCRIPTION");
  //Entities map populated with property maps against relevant EntitySets
  entitiesMap.put("LocationsSet", companyLocationsMap);
  //Set the header in the message object with the two maps created
  message.setHeader(ODataCamelExchangeHeaders.JDBC_TABLE_MAPPING.toString(), tableMap);
  message.setHeader(ODataCamelExchangeHeaders.JDBC_PROP_MAPPING.toString(), entitiesMap);
  //Logger this will log error to the console - useful for finding errors
  log.logErrors(LogMessage.TechnicalError, message.getBody());
  return message;
}


This is all you need to do to perform the mapping. Now you only need to deploy your project to the SAP Mobile Platform server, assign the JDBC destination to your entity set, and then you are done.

 

5. Testing the service

When you call your service, you will find that the mapping is in place, and it is returning the data from your database:

17-10-2014 17-07-16.png

 

Thanks for reading, I hope you will find this helpful.

 

Kind regards

Björn

With our latest release of the SAP Mobile Platform Server we have done some important enhancements to the tools used to monitor system health and analyze server performance.  The new enhancements give administrators the visibility and controls they need to efficiently manage their Mobile Platform deployment.

In this blog I give you a quick update in the following areas:

  • Logging
  • Solution Manager integration
  • End-to-End change analysis
  • Technical availability monitoring

 

in addition to point you to where you can get more information if you like what you just read.

Logging

The first source of information for administrators is the server logs for monitoring SAP Mobile Platform (SMP) system health. Troubleshooting can be viewed on the SMP server Admin Console and from here the administrator can change the Log level to the needed depth at runtime in addition to  view the logs.

 

Screen Shot 2014-09-10 at 15.36.27.png

 

 

 

 

Solution Manager integration

Gives you a second level of information layer to access supportability features such as change analysis, workload analysis, system availability and end to end trace.

 

Perform end-to-end workload analysis for native and hybrid apps:

Solution Manager is integrated with Wily Introscope to extend the workload analysis feature for the larger enterprise system. The Introscope agent on SAP Mobile Platform Server captures the workload metrics which then can be reviewed using Solution Manager or the Introscope Enterprise Manager dashboard.

 

 

 

Screen Shot 2014-09-10 at 15.23.22.png

Perform end-to-end change analysis:

Solution Manager periodically retrieves server and application-level configurations from SAP Mobile Platform. In the Solution Manager dashboard any configuration changes that have been made over a specific period of time be reviewed and discovered.

 

 

 

1blog.png

 

 

Technical availability monitoring:

Solution Manager provides a central point of access for monitoring a system's technical availability for the SAP Mobile Platform. In the Solution Manager dashboard you can evaluate the SAP Mobile Platform's overall system status in the mobile system landscape

 

 

Screen Shot 2014-09-10 at 15.33.21.png

 

 

 

End to End Trace:

Finally the Solution Manager also provides the capability to capture the analytics for each system in a mobile transaction and consolidate it to further analysis

 

If this topic interests you and you want to get more information on how to run your mobile solutions I recommend you to join us at TechEd this year. We have several Lectures and Hands-On sessions, which will give you more details on this topic.

 

 

Also check out our latest blogs covering more news about this new SAP Mobile Platform release:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

10.png


Hi everyone,

 

Working with the error archive.


As discussed in the previous blog, we need to manually execute delete operations against the error archive entries to purge them. In theory it is pretty simple, you know how to delete an entity from an OData collection (covered in the blog #04).


But it actually turns out that we need to come up with a way to associate and identify each request and its error in the error archive.

tag1.png

There could be different approaches how to do it - and one possible tip is to use the simple technique called "custom tag". By assigning each request a unique custom tag, it will appear in both the request object in the callback and the property of the error archive entity.

tag2.png

How do we add a custom tag? In the blog #04, we learned how to do the CUD operation by means of schedule<CUD>Entity methods.


For example, the Create operation is invoked with this method.

01  [store scheduleCreateEntity:entity collectionPath:@"CollectionName" delegate:self options:nil];

Alternatively, if we want to add a custom tag, we can invoke the Create operation with scheduleRequest: method.

01  SODataRequestParamSingleDefault *requestParam
02     = [[SODataRequestParamSingleDefault alloc] initWithMode:SODataRequestModeCreate
03                                                resourcePath:@"CollectionName"];
04  requestParam.payload = entity;
05  requestParam.customTag = @"myCustomTag";
06  [store scheduleRequest:requestParam delegate:self];

From the functional perspective, both do the same job - but the scheduleCreateEntity: does the better abstraction, in fact it calls very similar logic behind the scenes. In the bottom of this blog you can refer the CRUD operations code with scheduleRequest:.


Hold on, now I have a second thought with the line #05. It is a good idea to give each request some kind of unique custom tag, it can be useful for debugging.

01  requestParam.customTag = [NSString stringWithFormat:@"tag%@", [[NSUUID UUID] UUIDString]];

Now that a custom tag has been assigned to a CUD operation, how do we make use of it in the offlineStoreRequestFailed:?

 

01  - (void) offlineStoreRequestFailed:(SODataOfflineStore*) store
02                             request:(id<SODataRequestExecution>) requestExecution
03                               error:(NSError*) error
04  {
05    ... 
06    id<SODataRequestParamSingle> request = (id<SODataRequestParamSingle>)requestParam;
07    NSString *resourcePath = [NSString stringWithFormat:@"ErrorArchive?$filter=CustomTag eq '%@'", requestParam.customTag];
08    [offlineStore scheduleReadEntityWithResourcePath:resourcePath delegate:self options:nil];
09  }

First of all, your custom tag can be referred in the request object. In #08, you see how the requestParam.customTag is obtained. It should have the assigned tag value like "tag25716A15-667A-4468-AC88-B56598D42E0D". And it build an OData query string like "ErrorArchive?$filter=CustomTag eq tag25716A15-667A-4468-AC88-B56598D42E0D", which let you identify the associated error item out of the error archive. With the line #08, it triggers OData query against the error archive, which locally exists with the offline store.


It will invoke requestServerResponse: method, and as it was queried, it returns the entity set - simply delete the associated error entity out of the error archive.

01  - (void) requestServerResponse:(id<SODataRequestExecution>)requestExecution
02  {
03      id<SODataRequestParam> requestParam = requestExecution.request;    
04      if ([requestParam conformsToProtocol:@protocol(SODataRequestParamSingle)]) {
05          id<SODataRequestParamSingle> request = (id<SODataRequestParamSingle>)requestParam;
06          if (request.mode == SODataRequestModeRead) {
07              id<SODataResponseSingle> responseSingle = (id<SODataResponseSingle>)requestExecution.response;
08              if ([responseSingle.payload conformsToProtocol:@protocol(SODataEntitySet)]) { 
09                  id<SODataEntitySet> errors = (id<SODataEntitySet>)responseSingle.payload;
10                  for (id<SODataEntity> entity in errors.entities) {
11                      [offlineStore scheduleDeleteEntity:entity delegate:self options:nil];
12                      // purging an error item is complete
13                  }                
14              } 
15      ...

That's all about how to handle the situation in which the OData Producer fails one of the queued requests during a flush. Hope you get some solid idea how all gizmos are working in the offline scenario and how to work with them.


This is a kind of closing of the offline scenario - but not the end of the show. I'll add some more things, which should be helpful for the actual implementation (that's a whole idea of these blogs). Tiny but important bits and pieces. Thanks for keeping company so far.



See you in the next blog,

Ken


 

Appendix:

 

Create operation (= HTTP POST)

01  [store scheduleCreateEntity:entity collectionPath:@"CollectionName" delegate:self options:nil];

is equivalent to

01  SODataRequestParamSingleDefault *requestParam
02     = [[SODataRequestParamSingleDefault alloc] initWithMode:SODataRequestModeCreate
03                                                resourcePath:@"CollectionName"];
04  requestParam.payload = entity;
07  [store scheduleRequest:requestParam delegate:self];

Read operation (= HTTP GET)

01  [store scheduleReadEntityWithResourcePath:entity.resourcePath delegate:self options:nil];

is equivalent to

01  SODataRequestParamSingleDefault *requestParam
02     = [[SODataRequestParamSingleDefault alloc] initWithMode:SODataRequestModeRead
03                                                resourcePath:entity.resourcePath];
04  [store scheduleRequest:requestParam delegate:self];

Update operation (= HTTP PUT)

01  [store scheduleUpdateEntity:entity delegate:self options:nil];

is equivalent to

01  SODataRequestParamSingleDefault *requestParam
02     = [[SODataRequestParamSingleDefault alloc] initWithMode:SODataRequestModeUpdate
03                                                resourcePath:entity.editResourcePath];
04  requestParam.payload = entity;
07  [store scheduleRequest:requestParam delegate:self];

Delete operation (= HTTP DELETE)

01  [store scheduleDeleteEntity:entity delegate:self options:nil];

is equivalent to

01  SODataRequestParamSingleDefault *requestParam
02     = [[SODataRequestParamSingleDefault alloc] initWithMode:SODataRequestModeDelete
03                                                resourcePath:entity.editResourcePath];
04  [store scheduleRequest:requestParam delegate:self];

09.png

This blog content is joint work with Thasneem Yasmin Hameed from the SAP Development team in Dublin, California - special thanks to Thasneem!


Hi everyone!


Let's start with the last question.


- What if the OData Producer fails one of the queued requests during a flush?


In an online scenario, the result of any request is immediately propagated to the client. For example, a create request call will immediately return a 201 if it is successful or an error message if the entry was not created successfully at the OData producer end. Upon receiving the result, the client can manage subsequent requests based on the outcome.


But this doesn't happen in an offline scenario where there is no connectivity. For example, if an application makes a create request in offline mode, the corresponding data is only available in the local database on the device. The application could continue to do an update on the same data while still being offline.


Another scenario might be where an entry may have been deleted on the OData producer, but an offline application, which is unaware of this change, may still schedule an update on it. 

 

A typical scenario for an offline app is to schedule a series of requests when offline. When the device becomes online and a flush call is initiated, all of these requests are sent to the server. For each request queued in the flush, the outcome must be propagated to the application.

queue.png

The SDK provides the SODataOfflineStoreRequestErrorDelegate protocol for this purpose. This delegate has one method


  • offlineStoreRequestFailed:request:error: During a flush, called once for each request that fails against the backend OData Producer.


For example if 10 requests are being queued in a flush call and result in 3 requests failing at the backend, then the offlineStoreRequestFailed:request:error: will be called 3 times, each time with the error message specific to the failed request. In the scenario mentioned above where there is an offline create and then an update on the same entry in offline mode, then during a flush call, if the create has fails at the server, the offlineStoreRequestFailed:request:error: will be called first for the create request failure and subsequently for the update request failure, which of course will fail (since there is no such entry as the create had failed).

errors.png

Note: The call to this delegate method cannot stop or modify the flush operation. So, if a series of 10 requests are sent in a flush call, then the flush will execute all the 10 requests in the backend regardless of whether some of the requests fail in the process (thereby invoking the delegate method for each failure.) For apps that expect to have large numbers of requests in each flush we strongly recommend that the apps be written to try to remove the possibility of such errors. For example, a create can be made not to fail if the application is written to do some validation so invalid property values are never sent to the server in the first place.


An offline application can also view the offline request errors by means of the Error Archive, which is a collection of offline request errors stored on the local device. When an offline request fails during a flush call, the error is immediately added to the error archive. The application can retrieve the error archive from the offline store with a simple read request with the resource path set to "ErrorArchive".

error_archive.png

The ErrorArchive collection can be queried the same way as any other OData collection, so you can use $filter, $top etc. query options to modify the search. There is no limit to the number of errors that will be stored in the archive, and it is up to the application to manually execute delete operations against the archive entries to purge them.


So let's try to code. Don't forget to declare and set the SODataOfflineStoreRequestErrorDelegate.

01  [offlineStore setRequestErrorDelegate:self];

And the method will be called back. Most likely you want to do two things in the pseudo code below.

01  - (void) offlineStoreRequestFailed:(SODataOfflineStore*) store
02                             request:(id<SODataRequestExecution>) requestExecution
03                               error:(NSError*) error
04  {
05    // A. Notify the user a CUD operation was failed
06    // B. Purge an item from the error archive
07  }

Implementing A. is nothing special, after examining the requestExecution and error objects, a dry (boring) popup with an error message might be just fine, but as you know iOS devices provide fancy options to notify users with cool user experience, it is all up to our imagination with the brand-new technology :-)


Here comes another good question with B. - how to purge them?


We'll discuss in the next blog!



See you in the next blog,

Ken

Dear All,

 

Just to share my experience with SAP Mobile Platform 3.0, as we all know that from SMP 3.0 onwards there is not standard configuration for the Database file like in the previous versions wherein the setup used to create a Database file, be it a development system or a production system. From SMP 3.0 onwards, we have two versions the development system and the production system, both differing in the installation process.

For the development system the installation is straight forward as we can install it over the Derby Database, but for Production system we need to proceed with a custom Database, though i have tried the production installation on Derby Database and it worked but its been advised from SAP not to run Production systems on Derby Databases. So now the only option left with us is to carry on with the Custom Database which would be an additional pain for the Basis team to install a Database Server first and then install the SAP Mobile Platform server. The supported database as of now are

  • Oracle DB
  • Adaptive Server Enterprise
  • IBM DB2

 

Once this is installed, SAP provides a pre coded script that we just need to run in order to create users, password and the database file (phewww that saves some amount of effort) but again sometimes the scripts runs fine but we are not able to connect the database to SAP Mobile Platform. This is actually where the actual pain starts because the entire installation went through properly still it does not work. When we try to view data by running an OData service on the Gateway Management Cockpit we receive an error "Could not send a message" this is a place where we need to worry coz this is the point when SMP is not able to communicate with the custom Database.

 

So i am writing this blog post to just verify that everything went through properly before actually investing our time in development prior to verification.

 

First step is to login to the SAP Gateway Cockpit from the url http://smpserveraddress:smpport/gateway/cockpit

 

By default you will see a service by the name ESPM Service.

 

This guy is our helper who will tell us are we on the path or off it.

 

So we start taking help with the helper.

 

Step 1 : Go to the Destinations tab and click on Create a new Destination


Screen Shot 2014-10-06 at 4.23.37 PM.png

 

A Window will pop up. We will create a JPA2 Destination

Enter the below details

 

Screen Shot 2014-10-06 at 4.23.45 PM.png

 

 

 

Destination Type : JPA

Persistence Unit : com.sap.espm.model

Authentication Type : Basic Authentication

Username : the same you used in the DDL file for running the script. If you didn't change the script it will be gomobile

Password : the same you used in the DDL file for running the script. If you didn't change the script it will be secret.


Step 2 : Adding the Destination to the ESPM Service


Navigate back to the services tab and click on the ESPM Service.


It will open up the detail window of the ESPM Service.

At the bottom you will see a small rectangular section mentioning DESTINATIONS.


Screen Shot 2014-10-06 at 4.24.06 PM.png

Click on Add Destination and a small window will pop up fetching the destination which you recently created in step 1, in the drop down. Select your destination and click on Save.

 

Step 3 : Checking if the SMP Server is able to communicate with the Custom Database.

 

Go back to the Services tab and against the ESPM Service on the right you have a hyperlink which states Open Service Document which will open up the list of services for the ESPM Service.

 

Screen Shot 2014-10-06 at 4.24.22 PM.png

Now to check if the server is communicating with the Database.

Pick up any of the methods of the choice and run it in the url

 

For eg If i am wanting to see Sales Order Items data from the sample database the url will be something like this https://smpserverip:gatewayport(8080/8081)/gateway/odata/sap/ESPMService;v=1/SalesOrderItems

 

Click enter to see the response. If all works good and SMP is able to communicate to the custom DB you will see a response like below

 

Screen Shot 2014-10-06 at 4.24.39 PM.png

 

If you see a message like below or any other message , it means the SAP Mobile Platform is nt able to communicate with the Custom Database and you need to re install SAP Mobile Platform Server and reconfigure the Custom Database and re run the custom script.

 

Screen Shot 2014-10-06 at 4.55.33 PM.png

 

Hope this helps,

 

Thanks,

Rakshit Doshi

Hi everyone, time to code.


In order to refresh the offline store...

refresh.png

This single line of code does the job.

01  [offlineStore scheduleRefreshWithDelegate:self];

Just like the other delegates so far, we need a new delegate named SODataOfflineStoreRefreshDelegate. In this example it is declared in the same class, so simply set self. This delegate has two mandatory callback methods:

  • offlineStoreRefreshSucceeded: Called when a refresh has finished successfully.
  • offlineStoreRefreshFailed: Called when a refresh has failed.

And two optional callback methods:

  • offlineStoreRefreshStarted: Called when a refresh has started.
  • offlineStoreRefreshFinished: Called when a refresh has finished.

 

That's all for the refresh. How about the flush?

flush.png

01  [offlineStore scheduleFlushQueuedRequestsWithDelegate:self];

Yes we have a delegate for this too. It is SODataOfflineStoreFlushDelegate (yes, your wild guess is correct :-) ). It has two mandatory callback methods:

  • offlineStoreFlushSucceeded: Called when a flush has finished successfully.
  • offlineStoreFlushFailed: Called when a flush has failed.

And two optional callback methods:

  • offlineStoreFlushStarted: Called when a flush has started.
  • offlineStoreFlushFinished: Called when a flush has finished.

 

...Hope you're pleasantly surprised with its simplicity.

 

However...


Yes, I hear you. The question is "What if the OData Producer fails one of the queued requests during a flush?"


We'll discuss about it in the next one, stay tuned.



See you in the next blog,

Ken

07.png

Hi everyone, let's continue.


In order to use SODataOfflineStore, we need to understand how the offline store works.


During initialization, the offline store communicates with the server to collect the data from that OData Endpoint and stores it locally on the device. In the last blog, we learned how to do it with the defining request in the code snippet.


A formal description about the defining request is:


quote_start.pngA defining request is an OData read request that targets the OData Endpoint associated with the offline store and retrieves a subset of the OData Endpoint data. Before a store is opened, it is initialized with a set of defining requests. The set of data that is made available while disconnected on the device corresponds to the union of data received by executing all of the defining requests. During initialization, the offline store communicates with the server to collect the data from that OData Endpoint and stores it locally on the device.quote_end.png


Once you obtained an offline store successfully, you can do the Read operation just like you do with an online store. One thing we have to keep in mind is the fact that the offline store is the locally stored data. This means if you try to read the OData collection, which is not defined during the initialization (via the defining request), you will get an empty result.


Okie, so what we have learned is once the creation of an offline store is done, we can read the store. But we can expect that over time, the locally stored data may grow out of date compared to the OData Endpoint.


It's time to learn another new cool thing - MobiLink, introduced with SMP3 SP4.


SODataOfflineStore is running with MobiLink behind the scenes. MobiLink is responsible to synchronize the local database in the mobile device and the database in the SMP server.

mobilink.png

When the developer works with the SODataOfflineStore, it refers the local data in an offline store. And MobiLink handles the data communication in the most efficient way. If the OData service supports Delta Query mechanism, the transferred data can be optimized too.

 

It has two key tasks:

  • Refresh
  • Flush

 

Refresh is to asynchronously communicate with the server to download any new changes to the OData content. During a refresh, the offline store communicates with the server to re-collect the data from the OData Endpoint and reconciles it with the locally stored data.refresh.png

An offline store enables the application to modify data (that is, CUD operation) while disconnected. As a result, modification requests made to the offline store are stored locally in a request queue until connectivity can be re-established. When connectivity is available, the application must flush the request queue in order to propagate the stored requests to the OData Producer. During a flush, the offline store communicates with the server and attempts to execute all of the stored requests against the OData Producer.

flush.png

...that's all about MobiLink 101 with SMP3 SP4. Hope you have gained a quick idea how SODataOfflineStore works - so what we haven't done yet is how we implement it with the API. We'll talk about it in the next blog!



See you in the next blog,

Ken

06.png

Ladies and Gentlemen,


Time to start the offline scenario. Before jumping in the new API, let's recall the formal description about what the ODataStore is.

 

quote_start.pngThe ODataStore is covering exactly one OData service (service document and metadata). It shall be possible for an application to create multiple ODataStores (also in combination on- and offline) at the same time, which shall be completely independent from the API perspective and have own life cycles.quote_end.png


For online we use SODataOnlineStore. For offline, we need to understand another store object called SODataOfflineStore.

 

Both stores declare SODataStoreAsync protocol, this means SODataOfflineStore can use the same CRUD methods with SODataOnlineStore.


Let's have a look at how we create SODataOfflineStore:

01  SODataOfflineStore *offlineStore = [[SODataOfflineStore alloc] init];
02  SODataOfflineStoreOptions *options = [[SODataOfflineStoreOptions alloc] init];
03  options.host = serverHost;
04  options.port = serverPort; 
05  options.serviceRoot = [NSString stringWithFormat:@"/%@", applicationId];
06  options.conversationManager = myConversationManager;
07  options.storeEncryptionKey = @"MyEncryptionKey";
08  options.definingRequests[@"req1"] = @"/CarrierCollection";
09  ..
10  [offlineStore setOfflineStoreDelegate:self];
11  [offlineStore openStoreWithOptions:options error:&error];

Once you get familiar with SODataOnlineStore, it should be pretty easy. The difference is we supply option values via ODataOfflineStoreOptions, by which we set the values like SMP server host name or HttpConversationManager.


#07 is the optional parameter, which encrypts the store. If you don't set it explicitly, the store won't get encrypted. You only need to provide it to the store options. After that, the library takes care of encrypting/decrypting all of the content.


You have noticed a new line ".definingRequests" in #08. The defining requests define the entire set of data that will be available to read out of the store (we'll discuss the detail in the next blog). As it is NSMutableDictionary, you can add as many defining requests as you like. The value is OData URLs, relative to the service root.

 

For ODataOfflineStore, we use another delegate protocol named SODataOfflineStoreDelegate. For this example the delegate is declared in its own class, so simply set self. Finally we can try to open the offline store by openStoreWithOptions: which triggers following callback methods:

  • offlineStoreStateChanged:state:  Called when the store state changes.
  • offlineStoreOpenFailed:error: Called if the store fails to open.

And two optional callback methods:

  • offlineStoreOpenFinished: Called when the store finishes opening (for both success and failed).
  • offlineStoreNotification:notification:  (Bonus explanation in the bottom part of this blog) Called once for each notification that is generated during the open.


The offlineStoreStateChanged:state: callback method keeps us informed what the current status is.

01  - (void) offlineStoreStateChanged:(SODataOfflineStore *)store state:(SODataOfflineStoreState)newState
02  {
03    switch (newState) {
04      case SODataOfflineStoreOpening:  
05      break;
06 
07      case SODataOfflineStoreInitializing:
08      break;
09     
10      case SODataOfflineStorePopulating:
11      break;
12     
13      case SODataOfflineStoreDownloading:
14      break;
15     
16      case SODataOfflineStoreOpen:
17      // the offline store has opened...read the OData
18      break;
19     
20      case SODataOfflineStoreClosed:
21      break;
22    }
23  }

Once it reaches the SODataOfflineStoreOpen state, the offline store is ready.


Tip: Make sure to call GlobalInit and GlobalFini at the beginning and ending of the app code to use SODataOfflineStore.

01  - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
02  {   
03    [SODataOfflineStore GlobalInit];
04     ...
05  }
01  - (void)applicationWillTerminate:(UIApplication *)application
02  {
03    [SODataOfflineStore GlobalFini];
04  }

Congratulations, now you have successfully obtained the offline store. As explained, this store can use the same CRUD methods you had learned in the blog #03 and #04. The difference is one important fact - this store has the "offline" state. What does that mean to the API? We'll discuss it in the next blog.

 

 

See you in the next blog,

Ken


PS. Additional explanation of offlineStoreNotification:notification: method -


Currently, there are only two possible notifications and they indicate that either a refresh or a flush (both will be covered in the next blog) was in progress when the store was previously closed and that the store may be able to continue these operations where it left off if the application calls the refresh/flush operation again.  The advantage to doing this is to save on bandwidth and processing since you are picking up where you left off from last time instead of starting the entire process from a scratch.


To date, more than three thousand users are subscribed to the SAP Mobile Platform cloud version trial account over the last 12+ months. Many developers are attracted to the simplicity of a cloud based mobile platform to securely connect to on-premise back-end business systems and leverage a reliable push abstraction layer for their mobile business apps.

With the release of the SAP HANA Cloud Platform mobile services, SAP is further consolidating mobile technology and making it available as a Service.

 

SAP HANA Cloud Platform mobile services (HCPms) will replace the well-known SAP Mobile Platform, enterprise edition, cloud version (commonly referred to as SMP Cloud) as SAP’s Platform as a Service (MaaS) offering. At first glance this sounds like a rebranding, but in fact this release is much more than that.

pic1.png

The new name really makes more sense. First it clearly states that our product is running on top of the SAP HANA Cloud Platform, SAPs in-memory, feature-rich and easy-to-use development environment in the cloud. Secondly the name tells us that the mobile services are on par with other services like database, security, integration or app services. This is an important distinction as over the last year, I’ve had many discussions with people who thought that SMP Cloud was just an SMP 3.0 installed on virtual machines somewhere in the cloud. HCPms is a real Mobile-as-a-Service offering therefore you don’t have to worry about upgrades, backup, patching, scalability and so forth. SAP will take care of that.

 

 

Given that SMP 3.0 and HCPms are not the same offering – a logical question is “What do the two have in common?”. In developing HCPms, we have merged the two source-code baselines so that the bulk of the code is common between the two products and therefore expose the same behavior. A key benefit of this approach is that it allows us to develop features, deploy it to HCPms and you can benefit from it and with the next update of SMP 3.0 on-premise customers will have the same feature available as well. All this glued together with a single mobile SDK means that an app written for HCPms can also run against SMP 3.0 - no code change required.

 

 

There are a few differences behind the scenes, such as HCPms’ use of SAP HANA Cloud Integration services instead of Integration Gateway and the use of HANA Cloud Platform security infrastructure.

 

I hope this sounds good to you and I have more great new features to share for HCPms so read on!

 

 

Full SAP Mobile SDK Support for cross-platfrom app development (a.k.a. Kapsel)

 

HCPms supports the full set of Apache Cordova Plugins that we ship with our Mobile SDK SP05. From AppUpdate (allowing content updates in Cordova project at runtime, controlled by the server) to Offline Plugin, which allows to run full offline OData applications in a hybrid manner.

 

pic2.png

With SDK SP05 we shipped the first set of plugins for Windows 8.1 – all compatible with HCPms. If you want to read more about the new features of the SDK, you can do it here SAP Mobile SDK SP05 - What is new ?

Full OData Offline Services support (same as SMP 3.0)

One of the most anticipated features this year is definitely the Offline OData Service. This combination of easy to use API and a feature-rich, proven sync technology allows developer to create offline mobile Apps without having a PhD in “Offline Technology“. Developers are now able to create sophisticated offline applications the same way they would develop online applications – there is no development paradigm chance. If you want to find out more check the maybe you want to check this Getting Started with Kapsel - Part 15 -- Offline OData (New in SP05).

 

Enhanced Push Services

The new enhanced Push API allows back-end systems to publish business events to HCPms with ease and flexibility. The back-end just needs to know, which user and what app needs to be notified with what kind of message. This eliminates the need for the back-end to know anything about the device.


In addition to that the new API enables bulk push messages. All in all, this is what you need for your notification scenarios – regardless of target device: we support notification for Android, Apple, Blackberry and Windows.


The picture below is an example for the Push API. Given information is: a message to be shown on the device, a target application and a list of users to be notified. This information will be send to the Push API resulting in three notifications: two for User A (she owns two devices) and one for User B. The other users will not receive a notification since they are not in the recipients list and do not have the app.


pic3.png

Modern and new admin UX

A picture is worth a thousand words:

pic4.png

The new fresh design is responsive and can be accessed via phones or tables and of course from you laptop in your office. If the appearance of the Admin UI reminds you of SAP Fiori, there’s a good reason for that. The new Admin UI was built using the same open source UI5 framework as SAP Fiori. Needless to say that it’s easy to use and probably you’ll never visit the documentation we put together to explain you all the details.


Final Observation

There is more to tell about the new features in this release, but I doubt that you would have the time to read through every aspect of HCPms – it would fill a book on it’s own. Nevertheless, I hope I have raised your curiosity and share my excitement about this new release.

 

 

In closing, I would like to share one more thing with you: HCPms supports full-cloud scenarios, where the back-end is already available in the cloud as well as hybrid-cloud scenarios where the back-end is located on premise. The latter is made possible by the SAP HANA Cloud Connector, which establishes a secure connection and allows whitelisting of resources to be accessed by HCPms. I think the following picture explains it better than a description.

pic5.png

I hope that you now have a good understanding of what SAP HANA Cloud Platform mobile services is all about and are starting to think of how you would utilize it. We are working hard on providing a trial infrastructure as soon as possible, and expect to have it online in the first quarter of 2015.


Stay tuned and have fun!

 

Martin Grasshoff

Product Management, SAP HANA Cloud Platform mobile services



For the ones who want to learn more I have some links:

05.png

Hi everyone.

 

Onboarding. User on-boarding is the process of registering a user and giving them appropriate access to data and applications. SMP offers the proven onboarding scheme together with the API.

 

OData SDK bundles the Mobile Application Framework(MAF) UI. With the MAF UI, you can use the out-of-the-box onboarding features with very minimal development effort.

maf01.pngmaf02.png

Let's have a look at the typical onboarding implementation with MAF UI.

01  MAFLogonUIViewManager *logonUIViewManager = [[MAFLogonUIViewManager alloc] init];
02  [logonUIViewManager.logonManager setApplicationId:appID];
03  [logonUIViewManager.logonManager setLogonDelegate:self];
04  [logonUIViewManager.logonManager logon]; // Shows MAF UI 

The appID in #02 is the application id, which is configured in the SMP server. #03 is for the delegate named MAFLogonNGDelegate. #04 renders the MAF UI for end user. Once the end user entered all the onboarding info (SMP server name, credentials, etc.), the logonFinishedWithError: method is called back - this method is defined in the MAFLogonNGDelegate. Once the method is called back, make sure we configure the HttpConversationManager in the logonFishedWithError:.

01  -(void) logonFinishedWithError:(NSError*)anError
02  {
03    ...
04    HttpConversationManager *httpConvManager = [[HttpConversationManager alloc] init];
05    [[logonUIViewManager.logonManager logonConfigurator] configureManager:httpConvManager];
06    // onboarding completed
07  }

Remember the HttpConversationManager in the blog#02? Once completing the steps above, we can happily use the HttpConversationManager instance to create the ODataStore instance.


One remark is the logonUIViewManager needs the topmost ViewController so it can display the logon screens as modal screens on top of it. This code demonstrates how to set the current ViewController as the parentViewController of the logonUIViewManager.


Note: You have to make sure the parentViewController is set before the logon method gets called.

01  - (void)viewWillAppear:(BOOL)animated
02  {
03        ..
04    logonUIViewManager.parentViewController = self;
05        ..
06  }

Once the onboarding is completed, we can access the user specific onboarding data. This code fetches the OData application endpoint URL that routes the SMP server as a content proxy.

01  MAFLogonRegistrationData *regData = [logonUIViewManager.logonManager registrationDataWithError:&error];
02  NSString *endpointUrl = regData.applicationEndpointURL;

That's all for the onboarding step with MAF UI. Without onboarding, you can't obtain the ODataStore instance, so it is a must knowledge for you.


Next one is offline API :-)



See you in the next blog,

Ken

All.

 

I have done some findings in the area of JVM memory analysis and adjustment that I wanted to share.

 

Background

 

In the Agentry Server prior to SMP one had the option to adjust the JVM heap memory size of each Agentry Server by adjusting the corresponding parameters of the Agentry.ini file.

 

The parameters were:

[Java-1]

initialHeapSize=

maxHeapSize=

 

Now on SMP 3.0 the Agentry Server(s) are using the same runtime as the SMP, and you can no longer set memory allocation in the Agentry.ini (as it no longer exist). And you cannot set it in the Management Cockpit either, eventhough you have the [Java-1] section in your Backend configuration part of your Application definition.

 

So how do you analyse and adjust the JVM heap memory of the SMP 3.0 ?

 

Analyze

 

Please note that the SMP 3.0 is using the SAP Java runtime JVM.

Fortunately a simple Analyzer tool is included in the SMP installation.

The tool is located in <SMP_HOME>\sapjvm_7\bin\jconsole.exe

 

If you start the tool directly, it will display Java processes executing in your user, and you can connect directly. Normally you would have to start the SMP by Go.bat.

If your SMP server is running as a service it will normally be executing under a different user (smpServiceUser), and you will need to start jconsole a little differently.

 

Start the Task Manager, and determine the process ID of the java.exe process hosting your SMP platform.

 

TaskManager.jpg

In my case the SMP java.exe is executing under process ID 1632.

 

Now open a command prompt and navigate to the folder where jconsole is located.

 

Command.jpg

Issue the command "jconsole <PID>", where <PID> is the process ID of the SMP java process.

Warning.png

You will get this warning, but just click "Insecure connection".

You are located directly on the host so I don't see any problem in this.

 

 

jconsole1.jpg

You will now get an overview, and you can navigate to multiple tabs for various information.

 

jconsole2.jpg

On the "VM Summary" tab, you can find info of the current and maximum heap size, as well as all kinds of other info regarding the VM.

 

 

Adjusting memory

 

If your server is running out of heap memory (java.lang.OutOfMemoryError), you may want to increase the heap memory allocation on the SMP.

 

Startup parameters for the JVM is set in the file: <SMP_HOME>\Server\props.ini

 

Default memory parameters are:

-Xms1024m                          Initial Heap size

-Xmx2048m                          Max Heap size

-XX:PermSize=256M             Initial memory allocation for permanent objects

-XX:MaxPermSize=512M       Max memory allocation for permanent objects

 

 

By adjusting the value of Xmx you can change the max allowed memory allocation for the JVM.

It is good practice to set Xms to 50% of the Xmx value (as far as I know).

 

A restart of the SMP server is needed for the changes to take effect.

 

 

I hope this is helpfull.

 

 

Søren Hansen

Now that we have the SODataEntitySet instance, we pick up each entity out of the entityset.

01  id<SODataEntity> entity = [entityset.entities objectAtIndex:row];

Obtaining the value out of the entity is demonstrated here:

01  NSString *propValue = (NSString *)[(id<SODataProperty>)entity.properties[propName] value];

Question - how do we pick up the value out of the complex type? - Here's the answer.

01  NSDictionary *details = (NSDictionary *)[(id<SODataProperty>)entity.properties[detailsName] value];
02  NSString *propValue = (NSString *) [(id<SODataProperty>)details[propName] value];

So the complex type gets returned as the NSDictionary object.

 

Okie, so far we have done pretty much about Read operation. Let's move on to the CUD scenario.

 

First thing we need to understand is how we construct an entity. Here's the code snippet:

01  NSMutableArray *properties = [NSMutableArray array];
02  id<SODataProperty> prop = [[SODataPropertyDefault alloc] initWithName:@"myKey1"];
03  prop.value = @"My prop value";
04  [properties addObject:prop];
05  prop = [[SODataPropertyDefault alloc] initWithName:@"myKey2"];
06  prop.value = @"Another prop value";
07  [properties addObject:prop];
08  // goes on and on..
09  SODataEntityDefault *entity = [[SODataEntityDefault alloc] initWithType:@"MyEntity"];
10  for (id<SODataProperty> prop in properties) {
11    [entity.properties setObject:prop forKey:prop.name];
12  }

#02, #03, and #04 are creating key/value pair of each property. We'll repeat until we set all the property values for the entity. And #09, #10, 11, and #12 is iterating through to set the SODataEntity properties.


Note about #09 - the snippet uses an entity name as "MyEntity", but in an actual case, you need to qualify the entity type with the full namespace such as "RMTSAMPLEFLIGHT.Booking".

 

Now that you got an idea about how to work with an entity object, let's take a look at the CUD methods. As you have understood the Read operation, they should be very straightforward.


Create operation (= HTTP POST)

01  [onlineStore scheduleCreateEntity:entity collectionPath:@"CollectionName" delegate:self options:nil];

Update operation (= HTTP PUT)

01  [onlineStore scheduleUpdateEntity:entity delegate:self options:nil];

Delete operation (= HTTP DELETE)

01  [onlineStore scheduleDeleteEntity:entity delegate:self options:nil];

One coding remark about Update and Delete. Before invocation of the methods, make sure you set ResourcePath for the entity. The url value would be something like "TravelagencyCollection('00000105')" - something you can identify the entity in OData convention. In most case the setResourcePath: and editResourcePath: should set the same value but it depends on how your OData service is implemented.


Tip: If you already get the entity with the store, the entity should already contain these resourcePaths.

01  [entity setResourcePath:url editResourcePath:url];

And - Read operation (= HTTP GET)

01  // if you want to refresh/reload an entity
02  [onlineStore scheduleReadEntity:entity delegate:self options:nil];
03  // or..specify the resource path
04  [onlineStore scheduleReadEntityWithResourcePath:resourcePath delegate:self options:nil];

That's pretty much all I want to share about the online scenario. You can be confident you got a solid fundamental about how OData online API works. Next blog will introduce you the onboarding API. I know - everyone is interested in offline API, but let me quickly cover the onboarding first.



See you in the next blog,

Ken

This guide will give a clear understanding on how to create an OData service for a given SAP Gateway data source. Here, i am using publicly available demo gateway service : Flight example

 

Service URL : https://sapes1.sapdevcenter.com/sap/opu/odata/IWFND/RMTSAMPLEFLIGHT/

 

Note: To access this URL, you must have an account on SAP Gateway Demo system. How to get access

 

Now,to make this service available through SMP 3.0 server, i am going to import it in eclipse and then will deploy to SMP server.

 

Tools Used: Eclipse Kepler, SAP Mobile Platform 3.0 SP04 PL01, Advanced REST Client

 

Steps:

 

Prerequisite: Installing "SAP Mobile Platform Tools" in Eclipse Kepler  Reference Document

 

  1. Create a new project in Eclipse Kepler, make sure to select proper "Target Runtime"

 

               1.PNG

 

2. Select Model Content as "OData Service URL"  , Click "Next"

 

               2.PNG

 

3. Provide the service URL https://sapes1.sapdevcenter.com/sap/opu/odata/IWFND/RMTSAMPLEFLIGHT/ and click "Go". Enter correct credentials to connect to sapes1.sapdevcenter.com server

 

               3.PNG

Note: Not able to retrieve the service details? Look at Troubleshooting #1.

 

4. Once done, you can see the automatic creation of OData entities.

 

           

          completeprojc.PNG

 

5. Next task is to assigning data sources to each and every Entitysets.

 

    • Right click> FlightModel.odatasrv >Select Data source
    • Name : RMTSAMPLEFLIGHT
    • Make sure you specify Namespace as "IWBEP" (not SAP as always)

 

                    datasource.png

 

Note: How to identify what is the correct Namespace for a particular SAP gateway service? I generally prefer to a cross-check from step #9.

         odatasrv.png

6. Right click project > Generate and Deploy Integrate Content

 

          deploy.PNG

 

7. Now, i have to register RMTSAMPLEFLIGHT service in Gateway cockpit

 

 

          destinations.PNG

               

KeyValue
Destination NameES1
Destination TypeHTTP
Destination URLhttps://sapes1.sapdevcenter.com:443/sap/iwbep?sap-client=520
Authentication TypeBasic Authentication

User Name

SAP Gateway Demo system usename
PasswordSAP Gateway Demo system password

 

 

8. Select the "ES1" destination, click on "Test Connection". You should see a SUCCESS message.

 

          test.png

Note: Not able to ping successfully? Look at Troubleshooting #2

 

9. Register Back-End service from ES1 sever

 

    • Click on "Register a New Service" under SERVICES
    • Select Destination as "ES1"
    • Type RMTSAMPLEFLIGHT in search
    • Click on "Register"

 

          registerService.PNG

This is how it should look like:

 

     15.png

Note: As you can see the "IWBEP" is the namespace of the service coming directly from server "ES1" and "SAP" is the namespace of the service deployed through Eclipse.

 

10. Add destination "ES1" to the service "SAMPLEFLIGHT" by clicking on it.

 

          addDestination.PNG

 

Note: I didn't find assigning a destination (ES1) to the deployed OData model (SAMPLEFLIGHT) since backend service (RMTSAMPLEFLIGHT) has already been registered in gateway cockpit. But for best practices it is good.

 

You should see that "ES1" destination has been mapped to service "SAMPLEFLIGHT".

 

          servicedoc.png

 

11. Open "Service Document" as highlighted above. (Assuming you have created a security profile named "SAP" in admin cockpit)

 

           https://jk:8084/gateway/odata/SAP/SAMPLEFLIGHT;v=1

          This URL will return all entity sets contained in the service

               servDoc.PNG

12. To test each Entityset details, just add the Entityset name at the end of above URL.

 

e.g.

a. http://smpserverip:8080/gateway/odata/SAP/SAMPLEFLIGHT;v=1/CarrierCollection('AA')

     This URL will return carrier CARRIER NAME, CURRENCY CODE, URL for Carrier ID "AA"

 

          carrierCollection.PNG

b.  http://smpserverip:8080/gateway/odata/SAP/SAMPLEFLIGHT;v=1/TravelagencyCollection

 

       This will return all Travel Agencies details available in ES1 system.

 

c. http://smpserverip:8080/gateway/odata/SAP/SAMPLEFLIGHT;v=1/SubscriptionCollection

d.  http://smpserverip:8080/gateway/odata/SAP/SAMPLEFLIGHT;v=1/FlightCollection

 

Troubleshooting:

 

If you are using any proxy setting to access any external network, you may encounter below issues:

 

1. Connection timed out: connect

 

                         t1.PNG

Resolution: Go to Windows>Preferences>Network Connections. Select Active provider as 'Manual' and provide proxy details for HTTPS. Check Reference Document for more details.

 

2. Connection has Failed: Connection to https://sapes1.sapdevcenter.com:443 refused

 

          connectionFailed.PNG

Resolution:


a. Set HTTP/HTTPS proxy settings in Admin cockpit. > SETTINGS>SYSTEM

 

               proxysettigns.PNG

 

 

 

b. Add the HTTPS certificate to the SMP Keystore. Reference Document


3. Could not retrieve services from the destination ES1


          destinationFetch.PNG


Resolution: 


Set the proxy server details for the destination ES1. Go to path:C:\SAP\MobilePlatform3\Server\config_master\service.destinations\destinations

               Open ES1. And proxy details.

e.g. Proxy=proxyserver:80


save the file and NO need of restarting SMP server.


 

 

4. When you try to access any entityset information, you are encountered with message saying "Error in Getting the service",


          errorInGettingService.PNG


  Check if you have missed registering the service as mentioned in step #9.

 

 

Follow these forums for latest update on SAP Mobile Platform 3.0 : SAP Mobile Platform Developer Center, SAP for Mobile

 

Note: Big thanks to Bjoern Woppmann for helping me in writing this blog.

Actions

Filter Blog

By author:
By date:
By tag: