Introduction
PhoneGap 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.
Features
- two way synchronization between the local sqlite database and the corporate backend in the Cloud
- Offline mode operation. All data changes are tracked and auto synchronized with the Cloud once the connection returns
- Use of Push notifications to push data changes from the Cloud to the device
- Support for replicating changes to multiple devices like iCloud does
- A Java based Channel Framework for integrating your backend with the Cloud
System Requirements
Running the Cloud Server
cd PhoneGap/plugin-jquery-cloud
mvn -PrunCloud integration-test
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:
adb install -r CloudManager.apk
Once installed you can use the Activate function to register with the Cloud.
Installing the Offline App
adb install -r JQueryOfflineApp.apk
Dissecting the JQuery Offline App
Load Synchronized Beans
//read the oids of the tickets stored in the sync channel
window.plugins.sync.readall(channel,
function(oids)
{
if(oids =='0')
{
return;
}
oids = JSON.parse(oids);
var length = oids.length;
for(var i=0; i<length; i++)
{
var oid = oids[i];
//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">'+value+'</a></li>';
},
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,
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">'+value+'</a></li>';
},
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.
Add a New Bean to the Sync Channel
window.plugins.sync.addNewBean(channel,
function(tempoid)
{
window.plugins.sync.updateBean(channel,tempoid,'title',title,
function(success)
{
},
function(error)
{
});
window.plugins.sync.updateBean(channel,tempoid,'customer',customer,
function(success)
{
},
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)
{
},
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
Updating an existing Bean in the Sync Channel
var oid = $('#update_ticket_oid').val();
//update the 'title' property on the ticket bean
window.plugins.sync.updateBean(channel,oid,'title',title,
function(success)
{
},
function(error)
{
});
//update the 'customer' property on the ticket bean
window.plugins.sync.updateBean(channel,oid,'customer',customer,
function(success)
{
},
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.
Delete a Bean from the Sync Channel
function deleteTicket()
{
var oid = $('#read_ticket_oid').val();
//delete this bean
window.plugins.sync.deleteBean(channel,oid,
function(success)
{
//commit
window.plugins.sync.commit(function(success)
{
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.
Dissecting the Cloud
The MobileBean
publicclassJQueryBeanimplementsMobileBean,Serializable
{
@MobileBeanId
privateString oid;
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 for the MobileBean implementation:
publicclassJQueryBeanimplementsMobileBean,Serializable
{
@MobileBeanId
privateString oid;
privateString title;
privateString customer;
privateString specialist;
privateString comments;
publicJQueryBean()
{
}
publicString getOid()
{
return oid;
}
publicvoid setOid(String oid)
{
this.oid = oid;
}
publicString getTitle()
{
return title;
}
publicvoid setTitle(String title)
{
this.title = title;
}
publicString getCustomer()
{
return customer;
}
publicvoid setCustomer(String customer)
{
this.customer = customer;
}
publicString getSpecialist()
{
return specialist;
}
publicvoid setSpecialist(String specialist)
{
this.specialist = specialist;
}
publicString getComments()
{
return comments;
}
publicvoid setComments(String comments)
{
this.comments = comments;
}
}
The Channel
The Channel is the component that exposes the MobileBeans to the Sync Engine via a CRUD (Create, Read, Update, Delete) interface.
@ChannelInfo(uri="plugin_jquery_channel", mobileBeanClass="org.openmobster.core.phonegap.plugin.jquery.cloud.JQueryBean")
publicclassPluginJQueryChannelimplementsChannel
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.
Read the MobileBeans
@Override
publicList<?extendsMobileBean> readAll()
{
Collection<Object> all =this.objectStore.readAll();
List<JQueryBean> beans =newArrayList<JQueryBean>();
if(all !=null&&!all.isEmpty())
{
for(Object bean:all)
{
beans.add((JQueryBean)bean);
}
}
return beans;
}
@Override
publicList<?extendsMobileBean> bootup()
{
returnthis.readAll();
}
@Override
publicMobileBean read(String id)
{
return(JQueryBean)this.objectStore.read(id);
}
Create the MobileBean
@Override
publicString create(MobileBean mobileBean)
{
JQueryBean toCreate =(JQueryBean)mobileBean;
returnthis.objectStore.save(toCreate.getOid(), toCreate);
}
Update the MobileBean
@Override
publicvoid update(MobileBean mobileBean)
{
JQueryBean toUpdate =(JQueryBean)mobileBean;
this.objectStore.save(toUpdate.getOid(), toUpdate);
}
Delete the MobileBean
@Override
publicvoiddelete(MobileBean mobileBean)
{
JQueryBean toDelete =(JQueryBean)mobileBean;
this.objectStore.delete(toDelete.getOid());
}
Conclusion
This tutorial should give you a good idea on how to develop Offline Sync Apps using PhoneGap and the OpenMobster Sync Plugin. For more info about the Plugin please take a look at the API Reference document