3.8 Demo application: an Address Book with oine functionality
3.8.2 Implementation steps
This part describes the main and most relevant steps that were undertaken during the implementation of the oine-enabled Address Book. Especially in case of large online applications, it is important to break down the project into more manageable tasks in order to see more immediate progress and reduce development complexity.
Implementing the online web application
First, the application features and the UIare implemented for online use. The communi-cation with the server is directly developped in an asynchronous fashion via XMLHttpRe-quests. The server data layer is represented by the ServerDataLayer JavaScript class. The APImethods are owned at the class level using theprototype object, so that only one copy is shared among all class instances. However, the methods are still instance methods,
3.8. Demo application: an Address Book with oine functionality 58
20 v a r pElm = document . getElementById ( " addressBookMsg " ) ; i f( asyncRequest . r e a d y S t a t e == 4 ) {
Listing 3.6: Implementation of the showAddressBook() method for a server request
since they can be called through an instance object and access instance properties. As an example, Listing 3.6 gives the implementation of the showAddressBook() method that makes an Ajax request to get all contact names from the server in order to display them.
The requested PHP script is given in Listing 3.7. The handleOnClick() event handler is assigned to each address book entry, so that a mouse click on one of the entries will issue another request for the contact's details.
Building the manifest le
Once the online web application is working, the rst step to oine conversion is to prepare the manifest le for the ManagedResourceStore to ensure that the resources are available oine. HTML, CSS and JavaScript les are analyzed for resource references to add to the manifest. Whenever a new le that needs to be available oine is created later, it is added to the manifest le as well, which also involves modifying the manifest version string. The nal resulting application manifest le is given in Listing 3.8. It should not be forgotten that the LocalServer enforces a strict single-origin policy.
<?php
Listing 3.8: The manifest le of the Address Book
Detecting and initializing Gears
Before beginning to write any code, Gears must be initialized, which is done by including the gears_init.js le into the main application le (addressBook.html). Then, before using Gears and calling the APIs, the web application needs rst to detect whether or not the Gears extension is installed on a user's system and also to determine when to display an installation prompt to the user. This is done by seeing if the google.gears object is present and available, which will be the case if Gears is installed and initialized. If Gears is not installed, the Address Book directs the user to a customized installation page that supports a customized message and a returnURLas parameters, like shown in Listing3.9.
This initialization code is contained in theinit()method, called by the body onload()event.
Developers should be aware that a Gears permission prompt for storing data locally will appear automatically whenever the Factory APIs google.gears.factory.create method is called and permission has not been granted. In the Address Book, the permission dialog appears when the main page loads as it has not be programmed to appear based on a user-initiated action.
Decoupling the application from the network
The next step is to ensure that the application can operate without a network connection, which means that the application resources need to be captured locally. Once this is done, it should be possible to take the browser oine and navigate through the oine-enabled static content. The code excerpts listed in Listing3.10 show the main code lines responsible for creating the ManagedResourceStore and capturing the resources. The
3.8. Demo application: an Address Book with oine functionality 60
Listing 3.9: Detecting the presence of the Gears plug-in
Address Book manually calls the checkForUpdate() method only at loading time, in order to capture the set of resources all at once.
Persisting data on the client
Now that the application is decoupled from the network, it is time to move on to persisting application data using the Database feature of Gears. During the initialization of the Address Book, the local database gets instantiated and the dierent tables are created according to the schema previously depicted (see Listing 3.11).
The application is now ready to fetch every contact from the server-side database and store them in the local database when the user goes oine. As depicted more concretely in Figure 3.11, when the user clicks on the Go oine button, thehandleConnChange()event handler gets red and calls thegoOffline()method of theConnectionLayerobject, which issues an asynchronous request to get all contacts from the server. ThestoreContactsInfo()method of the LocalDataLayer object is then called as a callback method. The implementation of the two methods are shown in Listing3.12. Once the data is made available oine, it is a good timing to start implementing the local data layer, represented by the LocalDataLayer
JavaScript class and designed in a similar way as the server data layer. It implements the same CRUD interface but the operations are performed against the local database using the Gears DatabaseAPI. As an example, Listing 3.13 gives the implementation of the showAddressBook() method within the local data layer. It should be compared with Listing 3.6, which shows how this method is implemented in the server data layer for server requests.
Now that the local data layer is implemented, every time the user executes an action that requires a data storage or retrieval request to be issued, the application should check its connection state to decide which data layer to use, since it does not forward all the calls to an intermediate object responsible for making that decision. The application holds a global boolean variable named online to keep track of the connection status. Listing 3.14 gives a typical example of how this verication is performed in the handleOnClick() event
f u n c t i o n ConnectionLayer ( s t o r e ) {
3.8. Demo application: an Address Book with oine functionality 62
handler, which gets red when the user clicks on a contact entry to display more contact details.
Persisting the application state
At this point, the application is fully operational oine, even though the synchronization of the local changes with the server has not been implemented yet. However, when the user moves into the oine mode and closes the application in this state, the in-memory variable that holds the connection status will be reset to its default value and the application will bootstrap online at the next launch. As a result, all local changes will be lost when the application goes oine again. To x this problem, the application must simply persist the connection state into the local database at each connection switch, then retrieve it at every launch and boot accordingly. This explains the use of the local table connectionStatus, created during the application initialization. Listing 3.15 shows how the application gets the connection status at startup.
Implementing the synchronization strategy
Now that the basic oine access has been enabled, the main last step left is the imple-mentation of the synchronization strategy. As previously mentioned, the Address Book implements syncing using a persisted action log that species which actions were taken oine. In other words, each time a contact is added, updated or deleted locally, an object containing the necessary information to replay the operation on the server is created and stored in the local database. This object has at least a property name to specify the type of action that has been carried out and other properties depending on the type of
. . . v a r response = asyncRequest . responseText ;
20 v a r data = response . s p l i t ( ’ < b r / > ’ ) ;
Listing 3.13: Implementation of the showAddressBook() method for a local request
3.8. Demo application: an Address Book with oine functionality 64
f u n c t i o n h a n dl e O nC l ic k ( ) {
2 i f( o n l i n e ) {
s e r v e r D a t a L a y e r . getAddress ( e v a l ( ’ t h i s ’ ) , e v a l ( ’ t h i s . innerHTML ’ ) ) ;
4 }else{
l o c a l D a t a L a y e r . getAddress ( e v a l ( ’ t h i s ’ ) , e v a l ( ’ t h i s . innerHTML ’ ) ) ;
6 }
}
Listing 3.14: Deciding which data layer to use
. . .
2 f u n c t i o n g e t C o n n e c t i o n S t a t u s ( ) { t r y{
4 v a r r e s u l t = t h i s. db . execute ( "SELECT i s O n l i n e F i e l d FROM c o n n e c t i o n S t a t u s WHERE i d = ’ 0 ’ " ) ; }catch( ex ) {
6 s e t E r r o r ( ex ) ; }
8 v a r r e s u l t V a l u e = r e s u l t . f i e l d ( 0 ) ; i f( r e s u l t V a l u e == 0 ) {
10 r e t u r n f a l s e; }else{
12 r e t u r n t r u e; }
14 } . . .
16 v a r o n l i n e =t r u e; . . .
18 f u n c t i o n i n i t ( ) { . . .
20 o n l i n e = g e t C o n n e c t i o n S t a t u s ( ) ; . . .
22 } . . .
Listing 3.15: Retrieving the connection status
Figure 3.11: Sequence diagram of the going oine process
operation, such as the contact data for an update or for the insertion of a new contact.
Listing 3.16 shows how an update action gets logged into the local database.
At sync time, the objects in the action log table are fetched, put into an array and synced one after the other via the sync() method, which examines the type of action the object encapsulates and calls the corresponding operation of the server data layer. As each operation entails an asynchronous request to the server, the application must make sure that each server request is completed, before another action gets synchronized. This is necessary to avoid that requests get executed in non-chronological order, which could cause serious inconsistencies or errors. Thus, instead of simply iterating over the list of logged actions, thesync()method gets called as long as there are local actions to sync and from callback functions of Ajax requests (except for the rst call). The implementation of the sync() method is given in Listing3.17.