Professional Documents
Culture Documents
Table of Contents
1. Introduction to OpenMobster - Mobile Cloud Platform .......................................................... 1 Data Synchronization .................................................................................................. 1 Real-Time Push Notifications ....................................................................................... 1 Mobile RPC (Remote Procedure Call) ............................................................................ 1 Management Console .................................................................................................. 1 2. Mobile Programmer's Dilemma ......................................................................................... 2 ............................................................................................................................... 2 WebApp ("Browser based") Development ....................................................................... 2 Advantages ........................................................................................................ 2 Disadvantages .................................................................................................... 2 App ("Native") Development ........................................................................................ 3 Advantages ........................................................................................................ 3 Disadvantages .................................................................................................... 3 Popular Mobile Platforms (in no particular order of preference or market share) ..................... 3 Blackberry ......................................................................................................... 3 Google Android ................................................................................................. 3 iPhone .............................................................................................................. 3 Symbian ............................................................................................................ 4 Windows Mobile ................................................................................................ 4 ............................................................................................................................... 4 Back to the Future ...................................................................................................... 4 3. Programming Concepts .................................................................................................... 5 Cloud Server ............................................................................................................. 5 Channel ............................................................................................................ 5 MobileServiceBean ............................................................................................. 5 Mobile App Frameworks ............................................................................................. 5 Mobile Data Framework ...................................................................................... 5 Mobile MVC Framework ..................................................................................... 6 Mobile Cloud .................................................................................................... 6 4. Architecture ................................................................................................................... 7 OpenMobster Architecture ........................................................................................... 7 Mobile Cloud Stack .................................................................................................... 8 Sync ................................................................................................................. 8 Push ................................................................................................................. 8 OfflineApp ........................................................................................................ 8 Mobile RPC ...................................................................................................... 9 Network ............................................................................................................ 9 Database ........................................................................................................... 9 Inter-App Bus .................................................................................................... 9 Cloud Server Stack ................................................................................................... 10 5. Get Started: Hello Mobster ............................................................................................. 12 .............................................................................................................................. 12 System Requirements ........................................................................................ 12 Create your first Mobile Cloud App ............................................................................. 12 Developing the Channel ............................................................................................. 12 Step 1: Write the HelloSyncBean ......................................................................... 12 Step 2: Write the HelloSyncChannel .................................................................... 13 Step 3: Configuration ........................................................................................ 15 Developing the Android App ...................................................................................... 16 Step 1: Write the HomeScreen ............................................................................ 16 Step 2: Configuration ........................................................................................ 16
iii
OpenMobster - Mobile Cloud Platform Running the App ...................................................................................................... 17 JBoss Deployment .................................................................................................... 18 6. Production Mode Installation ........................................................................................... 19 Cloud-Side: Installation .............................................................................................. 19 Install Cloud Server .......................................................................................... 19 Device-Side: Installation ............................................................................................ 19 CloudManager App Installation ........................................................................... 19 7. Sync App Development .................................................................................................. 20 .............................................................................................................................. 20 Tutorial ................................................................................................................... 20 Cloud-Side: Channel Development ....................................................................... 20 App/Device-Side: MobileBean component ............................................................. 24 8. MobileBean .................................................................................................................. 26 MobileBean ............................................................................................................. 26 Cloud-Side ............................................................................................................... 26 Specification .................................................................................................... 26 Device-Side ............................................................................................................. 26 Accessing a Simple Property .............................................................................. 26 Accessing a Nested Property ............................................................................... 26 Accessing an Indexed Property (One-Dimensional Array or a java.util.List) ................. 27 Iterating through an Indexed Property (One-Dimensional Array or a java.util.List) ......... 27 9. Push Programming ........................................................................................................ 28 Sending a Push Notification ........................................................................................ 28 Push Setup on an Android App ................................................................................... 28 Push Setup on an iOS App ......................................................................................... 29 Apple Provisioning ........................................................................................... 29 OpenMobster Provisioning ................................................................................. 29 10. iOS + OpenMobster integration ...................................................................................... 32 Introduction ............................................................................................................. 32 Prepare the mobilecloudlib static library ....................................................................... 32 Start a View-based App ............................................................................................. 32 Create a Group called OpenMobster ............................................................................. 32 Add the libraries and Frameworks ............................................................................... 32 Add OpenMobster bootstrap code ................................................................................ 33 The bootstrapping functions ................................................................................ 33 Integrating the bootstrapping function with the App Delegate .................................... 34 Integrating the CloudManager ..................................................................................... 36 Integrate the CloudManager button on the View ..................................................... 36 Implement the action behind the button ................................................................ 37 Sample App ............................................................................................................. 37 11. iOS + OpenMobster Sample App ................................................................................... 38 Introduction ............................................................................................................. 38 Prepare the mobilecloudlib static library ....................................................................... 38 Run the Cloud Server ................................................................................................ 38 Run the SampleApp .................................................................................................. 38 12. PhoneGap: Offline Web Apps using the Sync Plugin ......................................................... 39 Introduction ............................................................................................................. 39 Offline App Usage .................................................................................................... 39 Running the Cloud Server .................................................................................. 39 Cloud Activation .............................................................................................. 39 Installing the Offline App .................................................................................. 39 Dissecting the JQuery Offline App .............................................................................. 40 Load Synchronized Beans .................................................................................. 40 Add a New Bean to the Sync Channel .................................................................. 41
iv
OpenMobster - Mobile Cloud Platform Update an existing Bean in the Sync Channel ........................................................ 43 Delete a Bean from the Sync Channel .................................................................. 44 Dissecting the Cloud ................................................................................................. 45 The MobileBean ............................................................................................... 45 The Channel .................................................................................................... 47 PhoneGap + iOS + OpenMobster integration .................................................................... 49 Introduction ............................................................................................................. 49 Prepare the mobilecloudlib static library ....................................................................... 49 Start a Cordova-based App ......................................................................................... 49 Copy JSON components to the App ............................................................................. 49 Create a Group called OpenMobster ............................................................................. 49 Add the libraries and Frameworks ............................................................................... 49 Add OpenMobster bootstrap code ................................................................................ 50 The bootstrapping functions ................................................................................ 50 Integrating the bootstrapping function with the App Delegate .................................... 51 PhoneGapSync App .................................................................................................. 53 PhoneGap: Sync Plugin Reference .................................................................................. 54 Introduction ............................................................................................................. 54 ReadAll - window.plugins.sync.readall ......................................................................... 54 Get a Property Value - window.plugins.sync.value .......................................................... 55 Execute Query Match All - window.plugins.sync.queryByMatchAll ................................... 55 Execute Query Match Atleast One - window.plugins.sync.queryByMatchOne ....................... 56 Execute Query Do Not Match All - window.plugins.sync.queryByNotMatchAll .................... 57 Execute Query Do Not Match Even One - window.plugins.sync.queryByNotMatchOne .......... 57 Execute Query Match Contains All - window.plugins.sync.queryByContainsAll ................... 58 Execute Query Match Contains Atleast One - window.plugins.sync.queryByContainsOne....... 59 Add a New Bean into the Sync Channel - window.plugins.sync.addNewBean ....................... 60 Update a Bean in the Sync Channel - window.plugins.sync.updateBean .............................. 61 Delete a Bean from the Sync Channel - window.plugins.sync.deleteBean ............................. 62 Commit the operations with the Sync Channel - window.plugins.sync.commit ...................... 63 Get the Length of an Array/List property - window.plugins.sync.arrayLength ....................... 63 Insert an object into an Array/List property - window.plugins.sync.insertIntoArray ................ 64 Clear the entries inside an Array/List property - window.plugins.sync.clearArray .................. 65 Location Aware Apps ................................................................................................... 66 Location Aware Apps ................................................................................................ 66 LocationServiceBean ................................................................................................. 66 The App Side Logic .................................................................................................. 67 Processing the Response .................................................................................... 69 Mobile RPC (Remote Procedure Call) Development .......................................................... 70 Cloud-Side: MobileServiceBean implementation ............................................................. 70 Cloud-Side: Configuration .......................................................................................... 71 Cloud-Side: Packaging and Deployment ........................................................................ 71 Putting it altogether ................................................................................................... 71 App/Device-Side: Invoking the MobileServiceBean ........................................................ 71 Clustering ................................................................................................................... 72 Clustering the Cloud Server ........................................................................................ 72 High Availability .............................................................................................. 72 Load Balancing ................................................................................................ 72 Setup ...................................................................................................................... 72 Configuration ................................................................................................... 72 Starting the Cluster ........................................................................................... 73 Management Console ................................................................................................... 74 .............................................................................................................................. 74 GUI Functionality ..................................................................................................... 74
13.
14.
15.
16.
17.
18.
OpenMobster - Mobile Cloud Platform Create Account ................................................................................................. 74 Devices ........................................................................................................... 74 Administrators .................................................................................................. 74 Push Setup ...................................................................................................... 74 19. Mobile MVC Framework .............................................................................................. 75 .............................................................................................................................. 75 Components ..................................................................................................... 75 Services .......................................................................................................... 80 Tutorial ........................................................................................................... 80
vi
Data Synchronization
Cloud data is made available to an App's local storage. It is available via a simple API. This allows the App to function seamlessly in both online as well as offline modes. The data is automatically synchronized with the Cloud service based on local state changes. These state changes are auto detected and synchronized with the Cloud. It does not require any special device-side sync-related programming on the part of the developer.
Management Console
A Management Console is provided to administrate the Cloud Server. It provides security, and account provisioning features. Over time the console will carry device management features like remote wipe, remote tracking, remote lock-down, etc.
Advantages
No need to maintain a different codebase for each native mobile platform. Write Once, Run Anywhere flexibility for the developer. No App approval process needed. Uses the same serverside infrastructure as traditional web applications.
Disadvantages
Not a very good user experience. The network latency on a desktop does not map equally to a mobile device. Users need information quick. Even a delay of a few milliseconds can result in frustration. Same pause on a desktop goes completely ignored. Web apps don't have the native look and feel which makes the user experience un-intuitive. Plus, each browser renders its content differently depending on the device, which can further add to frustration. Does not allow access to all the low level services of the mobile platform that can deliver true innovation. Accessing remote data on the go is not the only real advantage of this new computer. Its ability to feed context sensitive information back into the system unlocks the potential for a whole new world of computing. Some access via javascript and/or browser plugin is available, but it can never replicate the natural feel of accessing the low level platform API.
Too much dependence on a network connection. No offline access to critical data in the event of a network outage.
Advantages
Best user experience with respect to response times and intuitive user interaction Accessiblity to low level hardware, and sensors to truly take advantage of a mobile computer that is aware of its surroundings Offline access to critical data even in the event of network failure Provide intuitive push based notifications as the state of an App changes remotely
Disadvantages
Managing an App codebase across multiple programming languages and platform APIs No write once, run everywhere convenience for programmers Some platforms may require a slightly tedious approval process before installation on an actual device
Blackberry
Programming Language: Java Operating System: RIMOS (Proprietary/Open API)
Google Android
Programming Language: Java Operating System: Android (Open Source, License: Apache 2.0)
iPhone
Programming Language: Objective-C Operating System: Mac OSX (Proprietary/Open API)
Symbian
Programming Language: C++ Operating System: Symbian (Open Source, License: Eclipse Public License 1.0)
Windows Mobile
Programming Language: C#, .Net Programming Languages Operating System: Windows Mobile (Proprietary/Open API) In other words, whether to use the native approach or the web based approach is purely a business decision. For simple apps that only need to access data via a dumb terminal, native approach is overkill. For complex apps that fully utilize the power of the underlying platform, a native app is the best route. The OpenMobster Mobile Cloud Platform aims to deliver the low level infrastructure that provides services needed to build easy to use, innovative native apps. It takes away the hardwork of writing middleware infrastructure, so that the app developer can focus their development effort on implementing their business requirements. Here are some of the ways, OpenMobster tries to alleviate some of the disadvantages of native app development The pluggable nature of the Mobile MVC framework makes it easier to port an app across multiple platforms. Most of the GUI level plumbing is provided by the Mobile Cloud runtime. Only thing that requires porting are the Screen and Command components that are implemented using the native programming language/API. Having a consistent messaging spec across all the device level components. This makes the API features lot more consistent and only thing that varies across platform code is the syntax of the programming languge. Provide the same exact framework API in case where the programming languages are the same. For instance, Blackberry OS and Google Android are based on the same 'Java' programming language. In that case the API of the various frameworks have the same exact syntax. This cuts down the porting effort dramatically. Only code that would require porting is the code that uses the low level platform API. The code cannot be platform independent, but it can be language-portable.
Cloud Server
A Cloud Server is the server-side component of the infrastructure that is located in the 'Cloud'. The system provides mobile-oriented features like data synchronization, real-time push, and mobile rpc. From an architecture standpoint it sits between the mobile device and the actual cloud data services being mobilized. The Cloud Server provides a Java based Developer API to expose your data services. Here are its programming concepts:
Channel
A Channel serves as a gateway for integrating on-device model/data objects with the server-side backend storage systems such as relational databases, content repositories, or Enterprise systems like CRMs, ERPs etc. It provides a simple CRUD (Create, Read, Update, and Delete) interface to expose the backend data. The Channel is specifically designed such that the Developer does not have to worry about any low-level state management, synchronization, or other mobile-oriented issues. The idea is to keep a Channel a purely data-oriented component.
MobileServiceBean
A MobileServiceBean exposes some coarse grained business process to the on-device Mobile App. It provides a very simple request/response based synchronous invocation mechanism. It frees up the developer from all low-level (Remote Procedure Call) concerns like making network connections, security, marshalling/unmarshalling payloads etc. Note: This component is quite simple at the time of the milestone M1 release. Eventually it will provide more robust REST-based functionality. In any case, the Developer will still be shielded from the low-level programming details regardless of what higher-level services will be supported.
MobileBean
MobileBean is a managed Mobile Component which carries the state of the domain object that it represents on the Cloud. It is propagated from the Cloud Server to the mobile device via its corresponding "Channel" on the server. The Mobile Data Framework shields the App developer from state management issues like, offline access, receiving push notifications related to state changes on the server, synchronizing locally modified beans back with the server, sync concepts like two-way sync, one-way sync etc. The native runtime smartly tracks the changes to the local state of the MobileBean and decides which type of sync is needed.
Programming Concepts
MobileService
MobileService facilitates making RPC (Remote Procedure Call) invocations from the device to the server side 'MobileServiceBean' components. It presents a simple API to the developer and shields them from low-level networking details, http libraries, REST invocations etc.
Screen
Screen is an abstraction for an instance of the App screen that must be made visible to the user at a particular moment in time The low level Navigation Manager keeps track of the various screens of an App and provides services such as navigating to a specified screen, going back to the previous screen, and going to the home screen. Besides the actual implementation of a "Screen" all services related to a "Screen" are portable across mobile platforms.
Command
Command is an abstraction for an instance of a GUI Event Handler which receives various callbacks based on the screen's lifecycle A command typically puts a business process into motion via accessing various other services like the Mobile Cloud Framework components and/or native platform services. The Mobile MVC Framework is extensible to support various GUI frameworks. This does open the door for integrating cross platform GUI frameworks like standard widgets, HTML5 based GUI, etc.
Mobile Cloud
Mobile Cloud is an on-device native system service. It hosts the runtime that is used by the above mentioned App Frameworks. On platforms that support inter-application communication such as Android, there is a single instance of a Mobile Cloud which is shared by all the Apps installed on the device. This helps make better use of device resources like storage, network management, push sockets, background services, etc. On platforms that do not support inter-application communication, an instance of the Mobile Cloud runtime must be installed by bundling it with each App. The Mobile Cloud also comes with a Cloud Manager App. The Cloud Manager App provides configuration functions such as secure Device Activation, Push Management, Channel Management, Discovering/Installing new Apps, etc.
Chapter 4. Architecture
openmobster at gmail.com <openmobster@gmail.com>
OpenMobster Architecture
Architecture
This is a software stack that is installed on the mobile device. It provides the following services to Mobile Apps: Sync, Push, OfflineApp, Mobile RPC, Network, Database, Inter-App Bus.
Sync
Sync service auto-synchronizes all state changes to App/Moblet Data back with the Cloud Server. It supports various synchronization modes such as two way sync, one way server sync, one way device sync, slow sync, and boot sync.
Push
Push service manages state updates being sent as notifications from the Cloud Server. This improves the mobile user's experience as they do not have to pro-actively check for new information. When relevant information becomes available on the server, the user is automatically notified via system notifications like a beep, vibration, etc. Clarification: The Push service is a real time comet based service. The notifications are received within the context of the App and not as SMS alerts or some other non-intuitive experience. The experience is just like the Blackberry email experience. The Cloud Server does not require any special infrastructure like the Blackberry Enterprise Server to make this happen.
OfflineApp
OfflineApp service provided is designed to be an App Developer's best friend. Its carries the management capabilities to create smart coordination between low-level services like Sync and Push. Because of the
Architecture
OfflineApp service, the programmer never has to write any code to actually perform any synchronization. Synchronization is something that is managed by the OfflineApp service and it decides which mode of synchronization is the best for the current runtime state of the App. The App developer is never exposed to low level synchronization details like two way sync, one way device sync, etc. It coordinates managing the Push service. It carries the smartness to track the type of data being pushed along with which installed App on the device needs the notification. The App developer does not have to write any special code to receive notifications. The moment the data channel for the App is established, all synchronizations and push notifications are automatically handled by the OfflineApp service.
Mobile RPC
Mobile RPC facilitates making synchronous RPC (Remote Procedure Call) invocations from the device to the server side 'MobileServiceBean' components.
Network
Network service manages establishing a network connection with the Cloud Server. It manages the communication channel needed to receive Push notifications from the server. It carries the smartness to track coverage and establishes proper connections automatically. This is a very low-level service and an App developer never has to deal with using it directly. The App developer is shielded from any low level connection establishment, security, protocol details, etc by using the higher level Mobile Data Framework components.
Database
Database service manages local data storage details for Apps. Depending on the platform in question it uses the corresponding storage facilities. It is designed to coordinate storage among the suite of Apps/ Moblets installed on the device. It provides thread-safe concurrent access to the Apps. Just like the Network service, its a low-level service used by the Mobile Data Framework components.
Inter-App Bus
Inter-App Bus service provides low-level coordination/communication between the suite of Apps/Moblets installed on the device.
Architecture
This is a software stack that is installed on the server-side. It provides the following services to Mobile Apps: Sync, Push, Secure Socket-Based Data Service, Mobile RPC, Security, Management Console
Sync
Sync service synchronizes device side App state changes with the backend services where the data actually originates. It provides a plugin framework to mobilize the backend data. It uses the concept of a data "Channel" which mobilizes the data in the form of "MobileBean" instances.
Push
Push service monitors data "Channels" for updates. The moment updates are detected, corresponding Comet-based notifications are sent back to the device. If the device is out of coverage or disconnected for some reason, it waits in a queue, and delivers the push the moment the device connects back to the network. Clarification: The push service does not depend on any special infrastructure like a Blackberry Enterprise Server to achieve its functionality. Its a pure Comet-based approach via a socket channel with the device.
10
Architecture
Mobile RPC
Mobile RPC service on the server-side provides a Remote Procedure Call framework for invoking coarse grained business services of an App. The components are plugged in as MobileService Beans and the device-side Mobile RPC service invokes them via a simple synchronous request/response based approach.
Security
Security component provides authentication and authorization services to make sure mobile devices connecting to the Cloud Server are in fact allowed to access the system. Every device must be first securely provisioned with the system before it can be used. After the device is registered, it is challenged for proper credentials when the device itself needs to be activated. Once the device is activated, all Cloud requests are properly authenticated/authorized going forward.
Management Console
Every instance of a Cloud Server ships with a Command Line application called the Management Console. The console provides user and device provisioning functionalities. In the future, this same component will have more device management features like remote data wipe, remote locking, remote tracking, etc.
11
System Requirements
Cloud Server
Java SE JDK v6.0
appcreator.bat
This will generate a Maven-based skeleton for the Mobile App. Each generated project has the following maven modules: cloud: Contains the src for the Cloud-side Channel component. Java code is located under src/main/ java, and configuration is located under src/main/resources. app-android: Contains the src for the Android App. Java code is located under src/main/java, Configuration is located under src/main/resources/moblet-app. Besides the OpenMobster component setup, the Android SDK specific setup is located under AndroidManifest.xml, and res directory moblet: This is an assembly component that packages this App into a jar file ready for deployment in a JBoss AS based OpenMobster instance.
package com.hello.sync;
12
import org.openmobster.cloud.api.sync.MobileBean; import org.openmobster.cloud.api.sync.MobileBeanId; public class HelloSyncBean implements MobileBean { private static final long serialVersionUID = 1L; @MobileBeanId private String oid; private String message; public HelloSyncBean() { } public String getOid() { return oid; } public void setOid(String oid) { this.oid = oid; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
13
package com.hello.sync; import java.util.Date; import java.util.List; import java.util.ArrayList; import org.openmobster.cloud.api.sync.Channel; import org.openmobster.cloud.api.sync.ChannelInfo; import org.openmobster.cloud.api.sync.MobileBean; import org.openmobster.core.security.device.Device; @ChannelInfo(uri="hellosync", mobileBeanClass="com.hello.sync.HelloSyncBean") public class HelloSyncChannel implements Channel
Annotate the channel class with org.openmobster.cloud.api.sync.ChannelInfo annotation. uri: Unique value for registering the channel with the Channel Framework. mobileBeanClass: Class of the MobileBean that will be managed via this channel.
This method only returns the essential beans needed to make the App functional. It can be thought of as providing enough information for booting up the App. The rest of the beans are synchronized silently in the background without requiring any manual intervention or docking the device to a desktop, etc. This allows instant usage of the App without having to wait a few hours for all the required data to be loaded.
14
public String[] scanForNew(Device device, Date lastScanTimestamp) { //In this example, it pushes something every scan..Just for push demo return new String[]{"push:1", "push:2"}; }
This method checks with the backend service if a new MobileBean instance has been created on the backend connected to by the channel. Based on that it would send the just new bean ids back, or return null, if nothing new is available. If something new is available, this information is automatically synced and notified on the user's mobile device. If not, nothing happens on the device side. device: provides the context around which device should this call apply to. Backend information is linked via the user identity associated with the device lastScanTimestamp: provides when the last scan occurred. This way developer knows to only include beans that may have been created in the backend since this period in time.
This method provides a fully loaded MobileBean instance associated with the supplied id.
Step 3: Configuration
Register this channel using the cloud/src/resources/META-INF/openmobster-config.xml file.
<?xml version="1.0" encoding="UTF-8"?> <deployment xmlns="urn:jboss:bean-deployer:2.0"> <bean name="hellosync" class="com.hello.sync.HelloSyncChannel"> <depends>services://MobileObjectMonitor</depends> <depends>services://MobileServiceMonitor</depends> </bean> </deployment>
15
//Show the List of the "HelloSyncBeans" automatically synced and stored on the dev //As a developer you only deal with the MobileBean component... //No low-level sync stuff to worry about if(MobileBean.isBooted("hellosync")) { MobileBean[] helloBeans = MobileBean.readAll("hellosync"); //Preparing the ui with data stored in the beans..in the message field String[] ui = new String[helloBeans.length]; for(int i=0,size=ui.length;i<size;i++) { ui[i] = helloBeans[i].getValue("message"); } //Showing the data in the list listApp.setListAdapter(new ArrayAdapter(listApp, android.R.layout.simple_list_item_1, ui)); } ............ }
Step 2: Configuration
Configure the Sync and Push systems using app-android/src/resources/openmobster-app.xml <app-conf> <encryption>false</encryption>
16
<push> <launch-activity-class>org.openmobster.core.mobileCloud.android_native.framework. <icon-name>push</icon-name> </push> <!-- Registers the Cloud channels used by the App --> <channels> <channel name='hellosync'/> </channels> </app-conf> Configure the OpenMobster MVC Framework used by this App using app-android/src/resources/ moblet-app/moblet-app.xml <moblet-app> <!-- Registers the home/main screen of the app --> <bootstrap> <screen>com.hello.sync.app.HomeScreen</screen> </bootstrap>
<!-- Registers App Commands with the OpenMobster Command Framework. App Command are fired in response to events generated by user interactions --> <commands> <command id='/hellosync/reset'>com.hello.sync.app.ResetChannel</command> </commands> </moblet-app> Android platform specific configuration can be found under: android-app/AndroidManifest.xml, androidapp/res/layout/home.xml, and android-app/res/strings.xml
17
mvn -Papp-hot-deploy install Launch the HelloSync App. At first launch you will be guided through an Activation process of the App with the Cloud Server. This is a security measure to make sure only securely authenticated users are allowed to access the data in the Cloud. You can download the entire HelloSync App here: HelloSyncApp [http://openmobster.googlecode.com/ svn/samples/hellosync-2.4.zip]
JBoss Deployment
Once the App and its corresponding Cloud artifacts are developed and tested end-to-end, you can aggregate all these artifacts into a single jar ready for deployment into a JBoss AS based Cloud Server. For instructions on setting up your JBoss instance please see JBoss Setup. This single artifact when deployed into the JBoss AS based Cloud Server performs all necessary registrations with the system and is ready for deploying the app onto a real device via the Internet. Go to the moblet module: cd moblet Generate the app and deploy into the JBoss AS instance: mvn -Pjboss-install install Note: Before executing this deployment command you will need to make sure that the jboss.home property is properly set to your location of the JBoss 5.1.0.GA AS. You can change this value in the moblet/ pom.xml file
18
Cloud-Side: Installation
Install Cloud Server
Step 1: Download and install JBoss-5.1.0.GA from here [http://www.jboss.org/jbossas/downloads/] Step 2: Copy openmobster to the JBoss AS server directory. openmobster is a pre-configured/optimized instance for the OpenMobster Cloud Server Step 3: In the case of using MySQL5 as the database, modify openmobster-ds.xml according to your own MySql5 instance Step 4: Start the JBoss AS instance with the OpenMobster binary installed using: run -c openmobster -b "a real IP address" Note: A real IP address like "192.168.0.1" or something like that is needed for the mobile device to be able to connect to the server. Even in the case of a simulation environment, the device's browser does not connect to a loopback address like "localhost" or "127.0.0.1" Step 5: Verify the Cloud Server installation by typing in: http://{cloudserverIp}:{port}/o
Device-Side: Installation
Starting with OpenMobster 2.4, there is no requirement to install the CloudManager App on the device. All you need to do is install your App and it will automatically take care of the Activation process. The CloudManager App does still serve a purpose. The main purpose is to activate device management features such as: Remote Wipe and Remote Lock. You also get access to the Corporate App Store functionality which makes it easy to download/install apps from the OpenMobster App Store.
19
Tutorial
Cloud-Side: Channel Development
A Channel serves as a gateway for integrating on-device model/data objects with the server-side backend storage systems such as relational databases, content repositories, or Enterprise systems like CRMs, ERPs etc. It provides a simple CRUD (Create, Read, Update, and Delete) interface to expose the backend data. The Channel is specifically designed such that the Developer does not have to worry about any low-level state management, and synchronization issues.
Step 1:
Define a simple MobileBean to represent a data entity being mobilized. This MobileBean should adhere to the MobileBean specification covered here: Specification
import java.util.List; import org.openmobster.cloud.api.sync.MobileBean; import org.openmobster.cloud.api.sync.MobileBeanId; public class DemoBean implements MobileBean { @MobileBeanId private String beanId;
20
private List<String> demoList; //used to demonstrate mobilizing an indexed propert public DemoBean() { } public String getBeanId() { return beanId; } public void setBeanId(String beanId) { this.beanId = beanId; } public String getDemoString() { return demoString; } public void setDemoString(String demoString) { this.demoString = demoString; } public String[] getDemoArray() { return demoArray; } public void setDemoArray(String[] demoArray) { this.demoArray = demoArray; } public List<String> getDemoList() { return demoList; } public void setDemoList(List<String> demoList) { this.demoList = demoList; } }
Step 2:
Provide a Channel implementation that exposes this MobileBean via a CRUD interface.
@ChannelInfo(uri="/offlineapp/demochannel",
21
bootup
This method provides a subset of the "MobileBean" instances associated with the said device. They provide just enough information for an App to be functional. This helps with avoiding very long synchronization sessions. The other beans are loaded on-demand from there on
public List<? extends MobileBean> bootup() { List<MobileBean> list = new ArrayList<MobileBean>(); //Just get only the top 5 beans to bootup the service on device side //This decision is App-specific for(int i=0; i<5; i++) { DemoBean bean = this.demoRepository.getData().get(""+i); list.add(bean); } return list; }
readAll
This method provides all the "MobileBean" instances associated with the said device.
public List<? extends MobileBean> readAll() { List<MobileBean> list = new ArrayList<MobileBean>(); //Get All the Beans associated with this Channel for this Device Set<String> beanIds = this.demoRepository.getData().keySet(); for(String beanId: beanIds) { list.add(this.demoRepository.getData().get(beanId)); } return list; }
read
This method loads the particular "MobileBean" instance in question.
22
create
Creates a new instance of a "MobileBean" within the backend data service. This happens when a new instance is created on the device and synchronized back with the Cloud. It returns the unique id generated by the server and associated with this bean.
public String create(MobileBean mobileBean) { DemoBean newBean = (DemoBean)mobileBean; //Generate a new unique bean Id. This bean was created on the Device and is being //synchronized with the backend cloud service String newBeanId = String.valueOf(this.getDemoRepository().getData().size()); newBean.setBeanId(newBeanId); this.demoRepository.getData().put(newBeanId, newBean); return newBeanId; }
update
Synchronizes the updated state of a bean from the device with the state on the Cloud.
delete
Deletes a bean instance that is user confirmed to be deleted from the device.
Step 3:
Provide the META-INF/openmobster-config.xml that will deploy the "Channel" into the Cloud Server.
23
Step 4:
Package the the above classes and the corresponding META-INF/openmobster-config.xml into a simple jar file.
Step 5:
Deploy this jar file into the "deploy" directory of your JBoss AS instance
MobileBean[] demoBeans = MobileBean.readAll("/offlineapp/demochannel"); actions = new Vector(); int size = demoBeans.length; for(int i=0; i<size; i++) { if(!demoBeans[i].isProxy()) { actions.addElement(demoBeans[i].getValue("demoString")); } else { actions.addElement(demoBeans[i].getId()+": proxyState"); } }
24
getValue: Reads the value associated with a field/property of an instance of a bean. A property expression is provided to access this information.
demoBeans[i].getValue("demoString");
This particular method call reads the "demoString" property of the corresponding Cloud-Side MobileBean instance covered earlier. setValue: Updates the field/property of an instance of a bean. A property expression and its value are provided.
For a more detailed coverage of the MobileBean usage, please see some example code in the binary distribution located at: Samples/offlineapp/app-android
25
Chapter 8. MobileBean
openmobster at gmail.com <openmobster@gmail.com>
MobileBean
MobileBean is a managed Mobile Component which carries the state of the domain object that it represents on the server. It is propagated from the Cloud Server to the mobile device via its corresponding Channel on the server. The Mobile Data Framework shields the App developer from state management issues like, offline access, receiving push notifications related to state changes on the server, synchronizing locally modified beans back with the server, etc. The concept of a MobileBean applies to both sides of the world, Cloud-Side as well as Device-Side.
Cloud-Side
On the Cloud-Side the MobileBean is a simple Java Object that implements the org.openmobster.cloud.api.sync.MobileBean interface. The MobileBean is processed by its corresponding Channel. Through the channel instances of these beans are serialized into wire format and propagated to their respective devices. In order to be successfully serialized/deserialized, they should follow the proper specification.
Specification
The system successfully processes the following properties of a bean: Simple Property, Nested Property, One-Dimensional Array property, and Parameterized java.util.List properties of Concrete Types. Array PropertiesMUST NOT contain Null elements. MUST contain an empty constructor MUST contain provide public get and set methods for each one of its properties
Device-Side
On the Device-Side the MobileBeans from a channel are made accessible via the Mobile Data Framework. The generic org.openmobster.android.api.sync.MobileBean component is used to extract the state associated with each instance. It provides various state-oriented operations. The individual properties of a bean are accessed using simple and intuitive expressions.
26
MobileBean
MobileBean.getValue("myAddress.myStreet");
MobileBean.getValue("users[2].myName");
In the background, the state of all device-side MobileBean instances is tracked by the OfflineApp service. Any state updates are automatically synchronized back with its Cloud-Side channel using the appropriate synchronization mode. As far as the App developer is concerned, they just update this state locally and go about their business.
27
/**
* A device agnostic Push method. Push is associated with the user and not * * @param identity user that must receive this message * @param appId unique application id this push is associated with * @param message message to be sent * @param title title of the message * @param details any other details associated with the message */ public void push(String identity, String appId, String message, String titl
First you setup the user initiated push notification receiver. In the action value you have to make sure its the same as the name of the unique package that identifies this application. Next, you will setup sync initiated push notifications. There is no extra configuration to keep in mind here.
28
Push Programming
openmobster-app.xml
<push>
Here, launch-activity-class indicates the activity that must be launched when the user clicks on the notification from the notification bar icon-name points to a drawable image that should be displayed as an icon with the notification.
Apple Provisioning
Step 1: Obtain the Application Certificate
In order to push via the APN service, the provider side (OpenMobster?->APN connection) requires a certificate for each App registered for Push Notifications. The best instructions for doing the proper provisioning and obtaining a certificate is explained at : http://mobiforge.com/developing/story/ programming-apple-push-notification-services.
OpenMobster Provisioning
Step 1: Register the App and the Device Token On the OpenMobster? side, Apps that want Push notifications must be registered with the OpenMobster? system. The Device Token is also needed to be registered as it is a requirement for the Apple Push Notification Service. This registration is as follows:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWith // Override point for customization after application launch.
29
Push Programming
// Add the view controller's view to the window and display. [self.window addSubview:viewController.view]; [self.window makeKeyAndVisible]; //Bootstrap the Cloud services [self startCloudService]; //This registers the App for Push Notifications [[UIApplication sharedApplication] registerForRemoteNotificationTypes: (UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)]; return YES; }
NSString *deviceTokenStr = [NSString stringWithFormat:@"%@",deviceToken]; deviceTokenStr = [StringUtil replaceAll:deviceTokenStr :@"<" :@""]; deviceTokenStr = [StringUtil replaceAll:deviceTokenStr :@">" :@""]; NSLog(@"DeviceToken: %@",deviceTokenStr); @try { SubmitDeviceToken *submit = [SubmitDeviceToken withInit]; [submit submit:deviceTokenStr]; } @catch (SystemException * syse) { UIAlertView *dialog = [[UIAlertView alloc]
initWithTitle:@"Token message:@"Device Token delegate:nil cancelButtonTitle:@"OK dialog = [dialog autorelease]; [dialog show]; } }
These two operations registers the Application for Push notifications both on the device and on the OpenMobster? Push Service.
30
Push Programming
Step 2: Upload the certificate .p12 file Login to the Management Console: http://cloud-server-address/console Select Push Setup Find the App associated with this certificate Upload the certificate and supply its password If successfull, the icon next to the App will turn green Step 3: Send a Test Push Click on the App Click the 'Test Push' button Select the 'Device' where it should be sent You should receive a Push alert on your phone
31
Introduction
As of version 2.2-M1, iOS is fully supported by OpenMobster. Here are some tips related to iOS and OpenMobster integration
32
-(void)startCloudService { @try { CloudService *cloudService = [CloudService getInstance]; [cloudService startup]; } @catch (NSException * e) { //something caused the kernel to crash //stop the kernel [self stopCloudService]; } }
-(void)stopCloudService { @try { CloudService *cloudService = [CloudService getInstance]; [cloudService shutdown]; } @catch (NSException *e) { } }
-(void)startActivation
33
{ @try { CloudService *cloudService = [CloudService getInstance]; [cloudService forceActivation:self.window.rootViewController]; } @catch (NSException * e) { //something caused the kernel to crash //stop the kernel [self stopCloudService]; } }
Do a Sync at Startup
-(void)sync { CommandContext *commandContext = [CommandContext withInit:self.viewController]; BackgroundSyncCommand *syncCommand = [BackgroundSyncCommand withInit]; [commandContext setTarget:syncCommand]; CommandService *service = [CommandService getInstance]; [service execute:commandContext]; }
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSD { //OpenMobster bootstrapping [self startCloudService]; [self sync]; self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]
// Override point for customization after application launch. if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { self.viewController = [[[ViewController alloc] initWithNibName:@"ViewContro
34
} else { }
//setup the NavigationController self.navigationController = [[UINavigationController alloc] initWithRootViewCon //Add the CloudManager button to the navbar UIBarButtonItem *button = [[UIBarButtonItem alloc] initWithTitle:@"Cloud Manager"
//Add the Create button to the nav bar UIBarButtonItem *create = [[UIBarButtonItem alloc] initWithTitle:@"Create" styl
//OpenMobster bootstrapping [self startActivation]; //Register the App for Push notifications [[UIApplication sharedApplication] registerForRemoteNotificationTypes: (UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)];
return YES; }
-(void)applicationWillEnterForeground:(UIApplication *)application
- (void)applicationWillEnterForeground:(UIApplication *)application { /* Called as part of the transition from the background to the inactive state; he */ //OpenMobster bootstrapping [self sync];
35
-(void)applicationWillTerminate:(UIApplication *)application
- (void)applicationWillTerminate:(UIApplication *)application { /* Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. */ //OpenMobster bootstrapping [self stopCloudService]; }
//Add the CloudManager button to the navbar UIBarButtonItem *button = [[UIBarButtonItem alloc] initWithTitle:@"Cloud Manager" s
36
Sample App
In the OpenMobster distribution, you can find an iOS/OpenMobster Sync App under iphone/SampleApp. On the Cloud Side, the App to run is located under iphone/showcase/cloud. You run the Cloud Server using the command
37
Introduction
This chapter covers the steps involved in running the iOS + OpenMobster based Sample App
Cloud IP: The IP address of the Cloud Server Port: The port that the Cloud Server is running on (1502, by default) Email Address: Your email address to uniquely identify you with the Cloud Password: Your password to authenticate with the Cloud
38
Chapter 12. PhoneGap: Offline Web Apps using the Sync Plugin
openmobster at gmail.com <openmobster@gmail.com>
Introduction
PhoneGap [http://www.phonegap.com] is an HTML5 app platform that allows you to author native applications with web technologies and get access to APIs and app stores. PhoneGap leverages web technologies developers already know best... HTML and JavaScript. Starting with OpenMobster 2.2-M8, you can write offline web apps with synchronization of data using the OpenMobster Sync Plugin for PhoneGap. The Sync Plugin exposes the native Sync service to the JavaScript layer using the PhoneGap bridge technology. The rest of this chapter will discuss how to use the Sync Plugin using a JQuery based sample offline application.
This should make the OpenMobster Cloud Server up and running for the Offline App.
Cloud Activation
For security reasons, before apps can use the OpenMobster Cloud, the device must be registered with the cloud. This is done using a CloudManager App that comes with the OpenMobster distribution. You can locate this App in the distribution under Android/core-runtime/CloudManager.apk. You can install this App on the Android device or emulator using the following command:
Once installed you can use the Activate function to register with the Cloud.
39
PhoneGap: Offline Web Apps using the Sync Plugin adb install -r JQueryOfflineApp.apk
//create a list item corresponding to the ticket in question html += '<li><a href="#read_ticket?oid='+encodedOid+'" data-rel="dialog" }, function(error) { } ); } }, function(error) { alert('Sync Plugin:'+error); } );
This function reads the oids of the beans and then iterates through each bean and extracts the title property.
window.plugins.sync.readall(channel,
40
PhoneGap: Offline Web Apps using the Sync Plugin function(oids) { if(oids == '0') { return; } oids = JSON.parse(oids);
Invokes the readall function and reads the oids of all the beans stored in the sync channel. If the function is successful it returns an array of oids in JSON format. oids are then parse into a JavaScript object using the JSON.parse function.
//read the value of the 'title' property of the synchronized bean window.plugins.sync.value(channel,oid,'title', function(value) { var encodedOid = encodeURIComponent(oid);
//create a list item corresponding to the ticket in question html += '<li><a href="#read_ticket?oid='+encodedOid+'" data-rel="dialog" }, function(error) { } );
window.plugins.sync.value reads the value of the specified title property. It takes the channel name and the oid of the bean as arguments to locate the bean whose property is to be read.
41
PhoneGap: Offline Web Apps using the Sync Plugin }, function(error) { }); window.plugins.sync.updateBean(channel,tempoid,'specialist',specialist, function(success) { }, function(error) { }); window.plugins.sync.updateBean(channel,tempoid,'comments',comments, function(success) { }, function(error) { }); }, function(error) { alert("Sync Plugin:"+error); }); //Commit here window.plugins.sync.commit(function(success) { alert("Ticket was successfully added"); }, function(error){ alert("Ticket Add Error:"+error); });
The above code creates a new bean in the Sync Channel. Once the bean is created, its properties are updated and committed to the Sync Engine for synchronization
window.plugins.sync.addNewBean(channel, function(tempoid) {
window.plugins.sync.addNewBean creates a new bean into the Sync Channel. The method returns a temporary oid used to refer to this newly added bean.
window.plugins.sync.updateBean(channel,tempoid,'title',title, function(success)
42
PhoneGap: Offline Web Apps using the Sync Plugin { }, function(error) { }); window.plugins.sync.updateBean(channel,tempoid,'customer',customer, function(success) { }, function(error) { });
window.plugins.sync.updateBean updates the specified property on the bean referred to by its oid. In this case it modifies the title property on the newly added bean referred to by tempoid.
//Commit here window.plugins.sync.commit(function(success) { alert("Ticket was successfully added"); }, function(error){ alert("Ticket Add Error:"+error); });
window.plugins.sync.commit commits the beans into the Sync Channel for synchronization
43
}, function(error) { }); //update the 'specialist' property on the ticket bean window.plugins.sync.updateBean(channel,oid,'specialist',specialist, function(success) { }, function(error) { }); //update the 'comments' property on the ticket bean window.plugins.sync.updateBean(channel,oid,'comments',comments, function(success) { }, function(error) { }); //commit window.plugins.sync.commit(function(success) { alert("The Ticket was successfully saved"); }, function(error){ alert('Ticket Update Failed: '+error); });
This is very similar to the add new bean explanation above. It updates each property of the bean and then calls commit to get the bean synchronized with the Cloud.
44
PhoneGap: Offline Web Apps using the Sync Plugin { alert("The Ticket was successfully deleted"); }, function(error){alert("Ticket Delete Failed: "+error);}); }, function(error) { alert("Ticket Delete Failed: "+error); } ); $.mobile.changePage('#tickets','slide',true,false); }
window.plugins.sync.deleteBean deletes the bean referred to by the oid on the specified channel. window.plugins.sync.commit commits this change into the Sync Channel and prepares for synchronization with the Cloud.
Creates a MobileBean by implementing the MobileBean interface. This is the component which will be injected into the Sync Channel. It will then be accesssed on the device via the Sync Plugin API. The MobileBeanId annotation specified that the oid field will serve as the unique object identifier for these beans. Full Source of the MobileBean implementation
public class JQueryBean implements MobileBean,Serializable { @MobileBeanId private String oid; private private private private String String String String title; customer; specialist; comments;
public JQueryBean() {
45
PhoneGap: Offline Web Apps using the Sync Plugin } public String getOid() { return oid; } public void setOid(String oid) { this.oid = oid; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getCustomer() { return customer; } public void setCustomer(String customer) { this.customer = customer; } public String getSpecialist() { return specialist; } public void setSpecialist(String specialist) { this.specialist = specialist; } public String getComments() { return comments; } public void setComments(String comments) { this.comments = comments; } }
46
The Channel
The Channel is the component that exposes the MobileBeans to the Sync Engine via a CRUD (Create, Read, Update, Delete) interface.
The ChannelInfo.uri specifies the name of the Sync Channel and ChannelInfo.mobileBeanClass specifies the class of the MobileBean instance that the channel will be dealing with. The MobileBean instances used by the Channel implementation must be instances of this specified class. If this rule is not followed there will be unexpected errors during the synchronization process.
@Override public List<? extends MobileBean> readAll() { Collection<Object> all = this.objectStore.readAll(); List<JQueryBean> beans = new ArrayList<JQueryBean>(); if(all != null && !all.isEmpty()) { for(Object bean:all) { beans.add((JQueryBean)bean); } } return beans; } @Override public List<? extends MobileBean> bootup() { return this.readAll(); } @Override public MobileBean read(String id) { return (JQueryBean)this.objectStore.read(id); }
47
@Override public String create(MobileBean mobileBean) { JQueryBean toCreate = (JQueryBean)mobileBean; return this.objectStore.save(toCreate.getOid(), toCreate); }
@Override public void update(MobileBean mobileBean) { JQueryBean toUpdate = (JQueryBean)mobileBean; this.objectStore.save(toUpdate.getOid(), toUpdate); }
48
Introduction
PhoneGap [http://www.phonegap.com] is an HTML5 app platform that allows you to author native applications with web technologies and get access to APIs and app stores. PhoneGap leverages web technologies developers already know best... HTML and JavaScript. Starting with OpenMobster 2.2-M8, you can write offline web apps with synchronization of data using the OpenMobster Sync Plugin for PhoneGap. The Sync Plugin exposes the native Sync service to the JavaScript layer using the PhoneGap bridge technology. The rest of this chapter will discuss how to setup an iOS app for development using the SyncPlugin for iOS.
PhoneGap + iOS + OpenMobster integration libmobilecloudlib.a - OpenMobster static library CoreData.framework CFNetwork.framework CoreGraphics.framework UIKit.framework
-(void)startCloudService { @try { CloudService *cloudService = [CloudService getInstance]; [cloudService startup]; } @catch (NSException * e) { //something caused the kernel to crash //stop the kernel [self stopCloudService]; } }
-(void)stopCloudService { @try { CloudService *cloudService = [CloudService getInstance]; [cloudService shutdown]; } @catch (NSException *e) { }
50
-(void)startActivation { @try { CloudService *cloudService = [CloudService getInstance]; [cloudService forceActivation:self.viewController]; } @catch (NSException * e) { //something caused the kernel to crash //stop the kernel [self stopCloudService]; } }
Do a Sync at Startup
-(void)sync { CommandContext *commandContext = [CommandContext withInit:self.viewController]; BackgroundSyncCommand *syncCommand = [BackgroundSyncCommand withInit]; [commandContext setTarget:syncCommand]; CommandService *service = [CommandService getInstance]; [service execute:commandContext]; }
51
PhoneGap + iOS + OpenMobster integration [self sync]; NSURL* url = [launchOptions objectForKey:UIApplicationLaunchOptionsURLKey]; if (url && [url isKindOfClass:[NSURL class]]) { self.invokeString = [url absoluteString]; NSLog(@"PhoneGapSyncApp launchOptions = %@", url); } CGRect screenBounds = [[UIScreen mainScreen] bounds]; self.window = [[[UIWindow alloc] initWithFrame:screenBounds] autorelease]; self.window.autoresizesSubviews = YES; CGRect viewBounds = [[UIScreen mainScreen] applicationFrame]; self.viewController = [[[MainViewController alloc] init] autorelease]; self.viewController.useSplashScreen = YES; self.viewController.wwwFolderName = @"www"; self.viewController.startPage = @"index.html"; self.viewController.view.frame = viewBounds; // over-ride delegates self.viewController.webView.delegate = self; self.viewController.commandDelegate = self;
// check whether the current orientation is supported: if it is, keep it, rathe BOOL forceStartupRotation = YES; UIDeviceOrientation curDevOrientation = [[UIDevice currentDevice] orientation];
if (UIDeviceOrientationUnknown == curDevOrientation) { // UIDevice isn't firing orientation notifications yet go look at the stat curDevOrientation = (UIDeviceOrientation)[[UIApplication sharedApplication] } if (UIDeviceOrientationIsValidInterfaceOrientation(curDevOrientation)) { for (NSNumber *orient in self.viewController.supportedOrientations) { if ([orient intValue] == curDevOrientation) { forceStartupRotation = NO; break; } } }
if (forceStartupRotation) { NSLog(@"supportedOrientations: %@", self.viewController.supportedOrientatio // The first item in the supportedOrientations array is the start orientati UIInterfaceOrientation newOrient = [[self.viewController.supportedOrientati NSLog(@"AppDelegate forcing status bar to: %d from: %d", newOrient, curDevO [[UIApplication sharedApplication] setStatusBarOrientation:newOrient]; }
52
PhoneGap + iOS + OpenMobster integration //OpenMobster bootstrapping [self startActivation]; return YES; }
-(void)applicationWillEnterForeground:(UIApplication *)application
-(void)applicationWillEnterForeground:(UIApplication *)application { /* Called as part of transition from the background to the inactive state: here */ [self sync]; }
-(void)applicationWillTerminate:(UIApplication *)application
-(void)applicationWillTerminate:(UIApplication *)application { /* Called when the application is about to terminate. See also applicationDidEnterBackground:. */ [self stopCloudService]; }
PhoneGapSync App
In the OpenMobster distribution, you can find an iOS/PhoneGap Sync App under iphone/ PhoneGapSyncApp. On the Cloud Side, the App to run is located under iphone/showcase/cloud. You run the Cloud Server using the command
53
Introduction
PhoneGap [http://www.phonegap.com] is an HTML5 app platform that allows you to author native applications with web technologies and get access to APIs and app stores. PhoneGap leverages web technologies developers already know best... HTML and JavaScript. Starting with OpenMobster 2.2-M8, you can write offline web apps with synchronization of data using the OpenMobster Sync Plugin for PhoneGap. The Sync Plugin exposes the native Sync service to the JavaScript layer using the PhoneGap bridge technology. The rest of this chapter will provide a reference guide to the Sync Plugin and how to use it
ReadAll - window.plugins.sync.readall
Reads all the beans stored in the sync channel Returns a JSON array of oids associated with the beans Parameters: channel: The Sync Channel name where the beans are stored successCallback:function that will be invoked if this call is successful errorCallback:function that will be invoked if this call fails Usage Example:
window.plugins.sync.readall(channel, function(jsonArray) { if(jsonArray == '0') { return; } var oids = JSON.parse(jsonArray); }, function(error) { alert(error); } );
54
55
window.plugins.sync.queryByMatchAll(channel,{'title':'titleValue','comments': function(matchedOids) { if(matchedOids == '0') { alert('No Records Found!!!'); return; } var oids = JSON.parse(matchedOids); }, function(error) { } );
window.plugins.sync.queryByMatchOne(channel,{'title':'titleValue','comments': function(matchedOids) { if(matchedOids == '0') { alert('No Records Found!!!'); return; } var oids = JSON.parse(matchedOids); },
56
function(error) { } );
window.plugins.sync.queryByNotMatchAll(channel,{'title':'titleValue','comment function(matchedOids) { if(matchedOids == '0') { alert('No Records Found!!!'); return; } var oids = JSON.parse(matchedOids); }, function(error) { } );
57
Returns a JSON Array of oids of beans that do not match the criteria Parameters: channel: The Sync Channel name where the beans are stored criteria: JSON name/value pairs to match against successCallback:function that will be invoked if this call is successful errorCallback:function that will be invoked if this call fails Usage Example:
window.plugins.sync.queryByNotMatchOne(channel,{'title':'titleValue','comment function(matchedOids) { if(matchedOids == '0') { alert('No Records Found!!!'); return; } var oids = JSON.parse(matchedOids); }, function(error) { } );
58
window.plugins.sync.queryByContainsAll(channel,{'title':'title','comments':'c function(matchedOids) { if(matchedOids == '0') { alert('No Records Found!!!'); return; } var oids = JSON.parse(matchedOids); }, function(error) { } );
window.plugins.sync.queryByContainsOne(channel,{'title':'title','comments':'c function(matchedOids) { if(matchedOids == '0') { alert('No Records Found!!!'); return; } var oids = JSON.parse(matchedOids);
59
}, function(error) { } );
window.plugins.sync.addNewBean(channel, function(tempOid) { console.log('Temporary Oid: '+tempOid); //set the title window.plugins.sync.updateBean(channel,tempOid,'title','/test/newbean', function(success) { }, function(error){ alert('Setting the Title: '+error); } );
60
//insert into an object array window.plugins.sync.insertIntoArray(channel,tempOid,'messages',{'from':'from@tes function(success) { }, function(error) { alert('InsertIntoObjectArray: '+error); } ); }, function(error) { alert(error); }); //Commit here window.plugins.sync.commit(function(success) { alert('Commit successful'); }, function(error){});
61
function(value) { console.log(value); }, function(error) { alert('Reading the Title:'+error); } ); }, function(error){ alert('Updating Title: '+error); } ); //Commit here window.plugins.sync.commit(function(success) { alert('Commit successful'); }, function(error){});
62
63
property: The property of the bean successCallback:function that will be invoked if this call is successful errorCallback:function that will be invoked if this call fails Usage Example:
64
} );
65
LocationServiceBean
On the Cloud-side, LocationServiceBean components are encapsulated by Location Data carried inside an object called the LocationContext. Invocation of these components involves two paramaters. One is the LocationContext and the other is a Request object which carries the business data associated with the invocation. The following is a RestaurantBean which provides coupon data associated with restaurants that are close to a certain user provided location.
import import import import import import import import import import
java.util.HashMap; java.util.List; java.util.Map; java.util.Random; org.openmobster.cloud.api.location.LocationContext; org.openmobster.cloud.api.location.LocationServiceBean; org.openmobster.cloud.api.location.BeanURI; org.openmobster.cloud.api.location.Request; org.openmobster.cloud.api.location.Response; org.openmobster.cloud.api.location.Place;
@Override public Response invoke(LocationContext locationContext, Request request) { Response response = new Response();
66
//Get coupons associated with each place List<Place> nearbyPlaces = locationContext.getNearbyPlaces(); if(nearbyPlaces != null && !nearbyPlaces.isEmpty()) { Map<String,String> coupons = new HashMap<String,String>(); for(Place place:nearbyPlaces) { String placeId = place.getId(); //In a real implementation, you can lookup the coupon in the database based on int couponIndex = (this.random.nextInt())%7; couponIndex = Math.abs(couponIndex); String coupon = coupondb[couponIndex]; coupons.put(placeId, coupon); } response.setMapAttribute("coupons", coupons); } return response; }
It takes a list of nearby restaurants from the LocationContext and then associates coupons from a database with each. This business data married with location data is then sent back as a Response object.
String street = (String)commandContext.getAttribute("street"); String city = (String)commandContext.getAttribute("city"); String zip = (String)commandContext.getAttribute("zip"); //Construct a request for the RestaurantBean Request request = new Request("restaurants"); LocationContext locationContext = new LocationContext(); locationContext.setRequest(request); //Add the Address around which the restaurants must be looked up Address address = new Address(); address.setStreet(street); address.setCity(city); address.setZipCode(zip); locationContext.setAddress(address); //Narrow search to restaurants List<String> placeTypes = new ArrayList<String>(); placeTypes.add("food");
67
//Make the invocation to the Cloud to make a Location Aware search LocationContext responseContext = LocationService.invoke(request, locationContex commandContext.setAttribute("locationContext", responseContext);
//Construct a request for the RestaurantBean Request request = new Request("restaurants"); LocationContext locationContext = new LocationContext(); locationContext.setRequest(request);
A Request object is created and its given the name of the component to invoke which is restaurants in this case. A LocationContext is also initialized which will carry the location data associated with the invocation
//Add the Address around which the restaurants must be looked up Address address = new Address(); address.setStreet(street); address.setCity(city); address.setZipCode(zip); locationContext.setAddress(address);
//Narrow search to restaurants List<String> placeTypes = new ArrayList<String>(); placeTypes.add("food"); locationContext.setPlaceTypes(placeTypes); //Set the search radius locationContext.setRadius(1000); //1000 meters
Narrow the search to only restaurants over a 1000 meter search radius.
68
Invoke the RestaurantBean providing the location data in the LocationContext and business data inside the Request object.
//Add restaurant markers and corresponding coupon information MyItemizedOverlay restaurantMarkers = new MyItemizedOverlay(marker,map); for(Place restaurant:restaurants) { double latitude = Double.parseDouble(restaurant.getLatitude()); double longitude = Double.parseDouble(restaurant.getLongitude()); GeoPoint point = new GeoPoint((int)(latitude * 1E6), (int)(longitude * 1E6)); OverlayItem item = new OverlayItem(point,restaurant.getName(),coupons.get(resta restaurantMarkers.addOverlay(item); }
The coupons being business data are read from the Response object as a Map<String,String>, while the nearby restaurants being location data are read from the LocationContext.
69
@ServiceInfo(uri="/demo/mobile-rpc") public class DemoMobileBeanService implements MobileServiceBean { private static Logger log = Logger.getLogger(DemoMobileBeanService.class); public DemoMobileBeanService() { } public Response invoke(Request request) { log.info("-------------------------------------------------"); log.info(this.getClass().getName()+" successfully invoked..."); Response response = new Response(); String[] names = request.getNames(); for(String name: names) { String value = request.getAttribute(name); log.info("Name="+name+", Value="+value); response.setAttribute(name, "response://"+value); } log.info("-------------------------------------------------"); return response; }
70
Cloud-Side: Configuration
Provide the META-INF/openmobster-config.xml that will deploy the "MobileServiceBean" instance into the Cloud Server.
Putting it altogether
Entire MobileServiceBean Example is located at: src/dev-tools/sampleApps/rpcdemo/cloud and AppCreator/sampleApps/rpcdemo/cloud
Request request = new Request("/demo/mobile-rpc"); request.setAttribute("param1", "paramValue1"); request.setAttribute("param2", "paramValue2"); MobileService service = new MobileService(); Response response = service.invoke(request);
71
High Availability
The high availability cluster consists of multiple JBoss nodes of the OpenMobster Cloud Server. Out of these nodes there is a single node that serves as the Master node. All the incoming traffic is directed to this Master node. The Master node is not a single point of failure because if the Master node goes down, one of the other nodes immediately becomes a Master node. This process keeps going till all the nodes are used up. This is how you can get a highly available cluster running since at any given time there is always one master node processing requests from the mobile devices.
Load Balancing
At this point in time the Master node processes all the incoming requests. It does not delegate any requests to its other nodes to balance the load. This feature will be supported in a future release. This is a challenge because it needs to replicate local state among the cluster members. At this point, the Sync service does not support this replication except data sharing via the shared database. This is not enough and will require some re-architecting to make the service truly stateless. From here on out, all new services developed will support load balancing to get the best out of a clustered setup.
Setup
This will cover the steps for setting up an OpenMobster Cloud in a clustered environment
Configuration
In your JBoss server open the following file: deploy/openmobster.last/clustering-2.4-SNAPSHOT.jar/ META-INF/openmobster-config.xml. Make sure the file looks as the following to activate the node as a Cluster node
<deployment xmlns="urn:jboss:bean-deployer:2.0"> <bean name="clustering://ClusterService" class="org.openmobster.core.cluster.Clu <!-- Make this value true to activate this node as a Cluster node --> <property name="active">true</property> </bean> </deployment>
72
Clustering
73
GUI Functionality
Create Account
This lets you create an 'Admin' account. This creation has to be approved by another administrator before you can login.
Devices
This lets you manage your devices such activation, deactivation, etc. As the software matures it may add more system level functions like remote wipe, remote lock down, etc.
Activate/De-Activate
Activates or Deactivates the 'Device' account. If de-activated, none of the Cloud services will be available to the device. They will be available as soon as the account is activated.
Re-assign
De-activates the 'Device' account and makes it available to be assigned to another user. This is so that when a user leaves, his device can be re-assigned to another user.
Administrators
Used for managing the 'Admin' accounts.
Activate/De-Activate
Activates and De-Activates the 'Admin' account.
Push Setup
This is used to configure the Apple Push Notification service. As part of the system, a security certificate has to be assigned to an application before messages can be pushed to it. This is where your certificate can be uploaded and assigned to an App. Once this is successful, you can send a test push to your iPhone.
74
Components
Screen
Screen is an abstraction for an instance of the App screen that must be made visible to the user at a particular moment in time. The low level Navigation service keeps track of the various screens of an App and provides services such as navigating to a specified screen, going back to the previous screen, and going to the home screen. Besides the actual implementation of a "Screen" all services related to a "Screen" are portable across mobile platforms
public abstract class Screen { private String id; public String getId() { return this.id; } void setId(String id) { this.id = id; } public abstract void render(); public abstract Object getContentPane();
75
Command
Command is an abstraction for an instance of a GUI Event Handler which receives various callbacks based on the screen's lifecycle. A command typically puts a business process into motion via accessing various other services like the Mobile Data Framework components, and/or native platform services. Command instances are managed by the built-in EventBus of the MVC framework. A Command has the following life cycle which is managed by the event bus. doAction: This method is invoked when the actual business logic associated with the command should be executed. doViewBefore: This method is invoked prior to doAction. Its executed within an active Event Dispatch Thread and allows making any visual GUI changes to the screen. Some examples would be putting up a simple alert dialog, may be draw a status bar, etc doViewAfter: This method is invoked after doAction is executed. This is also executed within the context of an Event Dispatch Thread, and allows for making visual GUI changes to the screen. doViewError: This method is invoked if an App-level exception is encountered during the execution of this command. Being executed within the context of an Event Dispatch thread, it provides the opportunity to make appropriate GUI changes to robustly handle the error condition. The MVC Framework provides two builtin Commands. It provides standard behavior associated with these commands. This frees the App Developer to focus on the App behavior.
LocalCommand
This tells the system that the business logic executes quickly and will not freeze the UI. In the next iteration of the system, this component will probably be renamed to: FastCommand
public class DemoLocalCommand implements LocalCommand { public void doViewBefore(CommandContext commandContext) { Toast.makeText((Activity)commandContext.getAppContext(), "LocalCommand about to execute........", Toast.LENGTH_SHORT).show(); } public void doAction(CommandContext commandContext) { try { System.out.println("-------------------------------------------------------"); System.out.println("Demo Local Command successfully executed..............."); System.out.println("-------------------------------------------------------"); } catch(Exception e) {
76
throw new RuntimeException(e.toString()); } } public void doViewAfter(CommandContext commandContext) { Services.getInstance().getNavigationContext().navigate("local"); } public void doViewError(CommandContext commandContext) { ViewHelper.getOkModal((Activity)commandContext.getAppContext(), "Error", "DemoLocalCommand had an error!!").show(); } }
AsyncCommand
This tells the system that the associated business logic executes in the background. This approach is synonymous to the Ajax [http://en.wikipedia.org/wiki/Ajax_%28programming%29] approach in the Web Development realm. This is used to make the mobile application more responsive and at the same time accomplish some critical task of interacting with the remote cloud. The difference between this and the RemoteCommand is that, this command immediately frees up the UI for other interactions. The RemoteCommand freezes the UI but shows an in progress dialog. Once the action associated with this command finishes execution outside the UI thread, it re-establises the view cycle. Any UI changes associated with the execution of this command are then displayed to the user's screen. Below are some of many mobile scenarios where an AsyncCommand can come in handy: Show validation errors inline with user input Update the current screen with data fetched from the Cloud without showing a busy dialog or freezing the UI public final class DemoAsyncCommand implements AsyncCommand { public void doViewBefore(CommandContext commandContext) { Toast.makeText((Activity)commandContext.getAppContext(), "AsyncCommand about to execute........", Toast.LENGTH_SHORT).show(); } public void doAction(CommandContext commandContext) { try { //Simulate network latency Thread.currentThread().sleep(10000); System.out.println("-------------------------------------------------------"); System.out.println("Demo Async Command successfully executed..............."); System.out.println("-------------------------------------------------------"); } catch(Exception e)
77
{ throw new RuntimeException(e.toString()); } } public void doViewAfter(CommandContext commandContext) { ViewHelper.getOkModal((Activity)commandContext.getAppContext(), "Success", "Async Command success...").show();
//An Async Command should not navigate away from the screen that launch it...it c //Services.getInstance().getNavigationContext().navigate("async"); } public void doViewError(CommandContext commandContext) { ViewHelper.getOkModal((Activity)commandContext.getAppContext(), "Error", "DemoAsyncCommand had an error!!").show(); } }
RemoteCommand
This tells the system that the associated business logic executes slowly and must execute in the background. Usually a RemoteCommand is used when making network calls for data located in the cloud, or may be other scenarios where there is some form of waiting involved. The system wants to provide the appropriate user experience/feedback so that the user does not think the device is frozen. It will put up appropriate status indicators to keep the UI fluid. In the next iteration of the system, this component will probably be renamed to: BusyCommand
public final class DemoRemoteCommand implements RemoteCommand { public void doViewBefore(CommandContext commandContext) { Toast.makeText((Activity)commandContext.getAppContext(), "RemoteCommand about to execute........", Toast.LENGTH_SHORT).show(); } public void doAction(CommandContext commandContext) { try { //Simulate network latency Thread.currentThread().sleep(10000); System.out.println("-------------------------------------------------------"); System.out.println("Demo Remote Command successfully executed..............."); System.out.println("-------------------------------------------------------"); } catch(Exception e) {
78
throw new RuntimeException(e.toString()); } } public void doViewAfter(CommandContext commandContext) { Services.getInstance().getNavigationContext().navigate("remote"); } public void doViewError(CommandContext commandContext) { ViewHelper.getOkModal((Activity)commandContext.getAppContext(), "Error", "DemoRemoteCommand had an error!!").show(); } }
PushCommand
A PushCommand allows the developer to handle Cloud Push at the App level. When the Mobile Cloud runtime synchronizes the App state with the Cloud in the background, it decides how to route the push notifications to the respective Apps on the device. An App's PushCommand (if specified) is invoked so that the App can take necessary action such as showing a dialog box, showing some global screen, etc. Note: PushCommand should only be used to perform an App-specific action. The Mobile Cloud runtime automatically takes care of system level notifications like blinking LED, updating the app's icon with an alert, etc.
public final class PushHandler implements PushCommand { public void doViewBefore(CommandContext commandContext) { } public void doAction(CommandContext commandContext) { try { MobilePush push = commandContext.getPush(); System.out.println("Handling Push----------------------------------------"); System.out.println("Push Updates: "+push.getNumberOfUpdates()); MobileBeanMetaData[] updates = push.getPushData(); if(updates != null) { for(MobileBeanMetaData update:updates) { System.out.println("Bean: "+update.getId()); } } System.out.println("----------------------------------------"); } catch(Exception e) {
79
public void doViewAfter(CommandContext commandContext) { MobilePush push = commandContext.getPush(); Context context = Registry.getActiveInstance().getContext(); Toast.makeText(context, push.getNumberOfUpdates()+" Updates successfully received Toast.LENGTH_SHORT).show(); } public void doViewError(CommandContext commandContext) { Context context = Registry.getActiveInstance().getContext(); Toast.makeText(context, this.getClass().getName()+" had an error!!", Toast.LENGTH_SHORT).show(); } }
Services
EventBus
The EventBus shields the App Developer from learning the low-level GUI Event Management details. Each mobile platform has its own methodology for handling GUI events. Typically this revolves around using the Event Dispatch Thread most efficiently and providing a fluid user experience. Users are far more sensitive to GUI pauses on a mobile device compared to their traditional desktop. The EventBus frees up the App Developer to develop high-level App specific components like Screen and Commands, and lets the EventBus worry about the low-level details.
Navigation
The Navigation service abstracts low-level details about navigating through the various App screens that will be presented to the user.
Internationalization
The Internationalization service abstracts low-level details about localizing an App. It provides a platform independent way to package the resource bundles, and a standard API to access the information. The API is language-portable.
Tutorial
Simple Home Screen
Create a simple Home Screen component. This will be the first screen that will be displayed upon launching an App.
import java.lang.reflect.Field;
80
import import import import import import import import import import import import import import import import import import import
org.openmobster.core.mobileCloud.android.configuration.Configuration; org.openmobster.core.mobileCloud.android.errors.ErrorHandler; org.openmobster.core.mobileCloud.android.errors.SystemException; org.openmobster.core.mobileCloud.android.service.Registry; org.openmobster.core.mobileCloud.android_native.framework.ViewHelper; org.openmobster.core.mobileCloud.android_native.framework.events.ListItemCli org.openmobster.core.mobileCloud.android_native.framework.events.ListItemCli org.openmobster.core.mobileCloud.api.model.MobileBean; org.openmobster.core.mobileCloud.api.ui.framework.Services; org.openmobster.core.mobileCloud.api.ui.framework.command.CommandContext; org.openmobster.core.mobileCloud.api.ui.framework.navigation.NavigationConte org.openmobster.core.mobileCloud.api.ui.framework.navigation.Screen; org.openmobster.core.mobileCloud.api.ui.framework.resources.AppResources; android.app.Activity; android.app.ListActivity; android.view.Menu; android.view.MenuItem; android.view.MenuItem.OnMenuItemClickListener; android.widget.ArrayAdapter;
/** * @author openmobster@gmail.com */ public class HomeScreen extends Screen { private Integer screenId; @Override public void render() { try { final Activity currentActivity = (Activity)Registry.getActiveInstance(). getContext(); String layoutClass = currentActivity.getPackageName()+".R$layout"; String home = "home"; Class clazz = Class.forName(layoutClass); Field field = clazz.getField(home);
this.screenId = field.getInt(clazz); } catch(Exception e) { SystemException se = new SystemException(this.getClass().getName(), "render", ne "Message:"+e.getMessage(), "Exception:"+e.toString() }); ErrorHandler.getInstance().handle(se); throw se; } }
81
@Override public Object getContentPane() { return this.screenId; } @Override public void postRender() { ListActivity listApp = (ListActivity)Registry.getActiveInstance(). getContext(); AppResources res = Services.getInstance().getResources(); Configuration configuration = Configuration.getInstance(listApp);
if(!configuration.isActive()) { ViewHelper.getOkModalWithCloseApp(listApp, "App Error", res.localize("inactive_m show(); return; } //Show the List of the "Demo Beans" stored on the device if(MobileBean.isBooted("offlineapp_demochannel")) { MobileBean[] demoBeans = MobileBean.readAll("offlineapp_demochannel"); String[] ui = new String[demoBeans.length]; for(int i=0,size=ui.length;i<size;i++) { ui[i] = demoBeans[i].getValue("demoString"); } listApp.setListAdapter(new ArrayAdapter(listApp, android.R.layout.simple_list_item_1, ui)); //List Listener ListItemClickListener clickListener = new ClickListener(demoBeans); NavigationContext.getInstance().addClickListener(clickListener); } //Setup the App Menu this.setMenuItems(); } private void setMenuItems() { Menu menu = (Menu)NavigationContext.getInstance(). getAttribute("options-menu"); if(menu != null) { MenuItem resetChannel = menu.add(Menu.NONE, Menu.NONE, 0, "Reset Channel");
82
resetChannel.setOnMenuItemClickListener(new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem clickedItem) { //UserInteraction/Event Processing...this is where the Commands can be execute CommandContext commandContext = new CommandContext(); commandContext.setTarget("/offlineapp/reset"); Services.getInstance().getCommandService().execute(commandContext); return true; } });
MenuItem pushTrigger = menu.add(Menu.NONE, Menu.NONE, 1, "Push Trigger"); pushTrigger.setOnMenuItemClickListener(new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem clickedItem) { //UserInteraction/Event Processing...this is where the Commands can be execute CommandContext commandContext = new CommandContext(); commandContext.setTarget("/offlineapp/pushtrigger"); Services.getInstance().getCommandService().execute(commandContext); return true; } });
MenuItem rpc = menu.add(Menu.NONE, Menu.NONE, 0, "Make RPC Invocation"); rpc.setOnMenuItemClickListener(new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem clickedItem) { //UserInteraction/Event Processing...this is where the Commands can be execute CommandContext commandContext = new CommandContext(); commandContext.setTarget("/offlineapp/rpc"); Services.getInstance().getCommandService().execute(commandContext); return true; } }); } } private static class ClickListener implements ListItemClickListener { private MobileBean[] activeBeans; private ClickListener(MobileBean[] activeBeans) { this.activeBeans = activeBeans; } public void onClick(ListItemClickEvent clickEvent) { int selectedIndex = clickEvent.getPosition(); MobileBean selectedBean = activeBeans[selectedIndex];
83
Configuration
The Moblet code is packaged in a simple jar file. The configuration is located at: /moblet-app/mobletapp.xml
<!-In Android's case this is not needed for this App...The Android core + OpenM I stand corrected...Android is actually superior to BlackBerry Platform (atl <push> <command>org.openmobster.core.examples.offline.command.PushHandler</command> </push> -->
84