The previous chapter covered the SyncML protocol in detail. Funambol uses SyncML as its synchronization protocol to exchange data between the client and server.
This chapter explains how to develop extensions for the Funambol DS Service so that a developer can create integrations with other backend datasources. It will also describe how to build a simple connector, helping us to understand how the Funambol DS Service interacts with server extensions and connectors.
The following sections present several concepts related to how Funambol can be used to build mobile applications. Before digging into the details of Funambol development, it is useful to describe some basic concepts of data synchronization, as it is the foundation for Funambol applications and services. Some of these concepts such as the SyncML protocol were introduced in the previous chapter. In this chapter, those aspects will be described from a more technical perspective.
All mobile devices—handheld computers, mobile phones, pagers, and laptops—need to synchronize their data with the server where the information is stored. This ability to access and update information on the fly is the key to the pervasive nature of mobile computing. Yet, today almost every device uses a different technology for data synchronization.
Data synchronization is helpful for:
Propagating updates between a growing number of applications
Overcoming the limitations of mobile devices and wireless connections
Maximizing the user experience by minimizing data access latency
Keeping scalability of the infrastructure in an environment where the number of devices (clients) and connections tend to increase considerably
Understanding the requirements of mobile applications, providing a user experience that is helpful, and not an obstacle, for mobile tasks
Data synchronization is the process of making two sets of data look identical, as shown in the following figure:
This involves many techniques, which will be discussed in the following sections. The most important are:
ID handling
Change detection
Modification exchange
Conflict detection
Conflict resolution
Slow and fast synchronization
At first glance, ID handling seems like a pretty straightforward process that requires little or no attention. However, ID handling is an important aspect of the synchronization process and is not trivial.
In some cases a piece of data is identifiable by a subset of its content fields. For example, in the case of a contact entry, the concatenation of a first name and last name uniquely selects an entry in the directory. In other cases, the ID is represented by a particular field specifically introduced for that purpose. For example, in a Sales Force Automation mobile application, an order is identified by an order number or ID. The way in which an item ID is generated is not predetermined and it may be application or even device specific.
In an enterprise system, data is stored in a centralized database, which is shared by many users. Each single item is recognized by the system because of a unique global ID. In some cases, two sets of data (the order on a client and the order on a server) represent the same information (the order made by the customer) but they differ. What could be done to reconcile client and server IDs to make the information consistent? Many approaches can be chosen:
Client and server agree on an ID scheme (a convention on how to generate IDs must be defined and used).
Each client generates globally unique IDs (GUIDs) and the server accepts client-generated IDs.
The server generates GUIDs and each client accepts those IDs.
Client and server generate their own IDs and a mapping is kept between the two. Client-side IDs are called Locally Unique Identifiers (LUID) and server-side IDs are called Globally Unique Identifiers (GUID). The mapping between local and global identifiers is referred as LUID-GUID mapping. The SyncML specifications prescribe the use of LUID-GUID mapping technique, which allows maximum freedom to client implementations.
Change detection is the process of identifying the data that was modified after a particular point in time that is, the last synchronization. This is usually achieved by using additional information such as timestamp and state information. For example, a possible database enabled for efficient change detection is shown in the following table:
ID |
First name |
Last name |
Telephone |
State |
Last_update |
---|---|---|---|---|---|
12 |
John |
Doe |
+1 650 5050403 |
N |
2008-04-02 13:22 |
13 |
Mike |
Smith |
+1 469 4322045 |
D |
2008-04-01 17:32 |
14 |
Vincent |
Brown |
+1 329 2662203 |
U |
2008-03-21 17:29 |
However, sometimes legacy databases do not provide the information needed to accomplish efficient change detection. As a result, the matter becomes more complicated and alternative methods must be adopted (based on content comparison, for instance). This is one of the most important aspects to consider when writing a Funambol extension, because the synchronization engine needs to know what's changed from a point in time.
A key component of a data synchronization infrastructure is the way modifications are exchanged between client and server. This involves the definition of a synchronization protocol that client and server use to initiate and execute a synchronization session. In addition to the exchange modification method, a synchronization protocol must also define a set of supported modification commands. The minimal set of modification commands are as follows:
Let's assume that two users synchronize their local contacts database with a central server in the morning before going to the office. After synchronization, the contacts on their smartphones are exactly the same. Let's now assume that they update the telephone number for "John Doe" entry and one of them makes a mistake and enters a different number. What will happen the next morning when they both synchronize again? Which of the two new versions of the "John Doe" record should be taken and stored into the server? This condition is called conflict and the server has the duty of identifying and resolving it.
Funambol detects a conflict by means of a synchronization matrix shown in the following table:
Database A → ↓ Database B |
New |
Deleted |
Updated |
Synchronized/ Unchanged |
Not Existing |
---|---|---|---|---|---|
New |
C |
C |
C |
C |
B |
Deleted |
C |
X |
C |
D |
X |
Updated |
C |
C |
C |
B |
B |
Synchronized/ Unchanged |
C |
D |
A |
= |
B |
Not Existing |
A |
X |
A |
A |
X |
As both users synchronize with the central database, we can consider what happens between the server database and one of the client databases at a time. Let's call Database A, as the client database and Database B, as the server database. The symbols in the synchronization matrix have the following meaning:
X: Do nothing
A: Item A replaces item B
B: Item B replaces item A
C: Conflict
D: Delete the item from the source(s) containing it
Once a conflict arises and is detected, proper action must be taken. Different policies can be applied. Let's see some of them:
User decides: The user is notified of the conflict condition and decides what to do.
Client wins: The server silently replaces conflicting items with the ones sent by the client.
Server wins: The client has to replace conflicting items with the ones from the server.
Last/first in wins: The last/first arrived item wins.
Merge: Try to merge the changes, at least when there is no direct conflict. Consider the case of a vcard, where two concurrent modifications have been applied to two different fields. There is a conflict at the card level, but the two changes can be merged so that both clients can then have a valid version of the card. This is the best example of the case when the change is not directly conflicting.
Do not resolve.
Note that Funambol adopts a special merging policy that guarantees that the user does not lose data. The server always tries to merge if possible. When a conflict cannot be resolved with merging (for example, there are conflicting changes on the same field), the value in the last synchronization wins over the older synchronizations to meet the expectation of the user who is synchronizing. In this way, when the users who applied previous changes receive the new updates all devices will be in sync.
As seen in the previous chapter, there are many modes to carry out the synchronization process. The main distinction is between fast and full synchronization. Fast synchronization involves only the items changed since the last synchronization between two devices. Of course, this is an optimized process that relies on the fact that, the devices were fully synchronized at some point in the past; this way, the state at the beginning of the sync operation is well known and sound. When this condition is not met (for instance, the mobile device has been reset and lost the timestamp of the last synchronization), a full synchronization must be performed. In a full synchronization, the client sends its entire database to the server, which compares it with its local database and returns the modifications that must be applied to be up-to-date again.
Both fast and full synchronization modes can be performed in one of the following manners:
Client-to-server: The server updates its database with client modifications, but sends no server-side modifications
Server-to-client: The client updates its database with server modifications, but sends no client-side modifications
Two-way: The client and server exchange their modifications and both databases are updated accordingly
The Funambol platform can be extended in many areas to integrate Funambol with existing systems and environments. The most common integration use cases and the Funambol modules involved are:
These are illustrated in the following diagram:
Funambol extensions are distributed and deployed as Funambol modules. This section describes the structure of a Funambol module, while the following sections describe each of these listed scenarios.
A Funambol module represents the means by which developers can extend the Funambol server. A module is a packaged set of files containing Java classes, installation scripts, configuration files, initialization SQL scripts, components, and so on, used by the installation procedure to embed extensions into the server core.
For more information on how to install Funambol modules, see the Funambol Installation and Administration Guide.
A Funambol module is a ZIP package named using the following convention:
Here<module-name>
is the name of the module without spaces and using lowercase characters only, and<major/minor-version>
are the major and minor version numbers. A new version of a module with a minor version number change must be backward compatible, while changes in the major version number imply that a migration may be required.
The package must have the structure illustrated in the following diagram:
In the previous diagram, entries ending with a '/' represent directories and filenames in italic are given as examples (in a real package they will be replaced with real filenames).
The module classes are packaged in a main JAR file called<modulename>.jar
.
Configuration files are stored under the package directory config
, creating subdirectories as needed.
Even if it is not mandatory, usually SyncSource instance configuration files are stored under a subtree in the form<module-id>/<connector-id>/<sourcetype-id>
, which is the convention used by the Funambol Administration Tool when creating a new SyncSource instance.
The directory install
contains install.xml
, an Apache Ant script that is called when the module is installed; this is the hook where a module developer can insert module specific installation tasks. Installation specific files can be organized in subdirectories under install
.
If the module requires a custom database schema, the scripts to create, drop, and initialize the database are stored under the sql/<database>
directory, where<database>
is the name of the DBMS as listed in the install.properties
file.
Finally, the exclude
directory is used to store files that will be temporarily used by the installation procedure, but will not be (automatically) copied. These can be used by the module installation script for any purpose.
As mentioned, a module is a container for anything related to one or more server extensions, which are used by the engine to communicate and integrate with external systems. These extensions are usually specific with backend to be integrated. A specific case of such an extension is when the main purpose is to connect to an external datasource and in this case the module is called a connector. In other words, a connector is an extension of the server, intended to support the synchronization of a particular datasource.
To access the datasource, the connector must provide a SyncSource type. A SyncSource type represents the template from which an instance of a SyncSource can be created. For example, the FileSystemSyncSource
type made available by Funambol is the means used by the server to synchronize data stored in the file system. However, it does not represent a particular directory to synchronize. In order to synchronize a specific directory (for instance /data/contacts)
a real SyncSource instance must be created and configured with the desired directory. You can think of a SyncSource type as a class and of a SyncSource as an instance.
An additional (but optional) component that a connector can provide is called listener. This component is in charge of detecting changes in the backend so that the server can trigger a device to synchronize the changes.
Funambol provides a module called foundation that contains basic functionality, libraries, and classes required by all custom modules. This module must not be removed from any Funambol installation.
Modules, connectors, and SyncSource types are registered by filling the following database tables:
The last two tables are used to create the hierarchy module-connector-SyncSource type that can be seen in the Funambol Administration Tool.
Let's consider the registration of the foundation
module. When Funambol is installed, the foundation
module is also installed. It brings a connector called FunambolFoundationConnector
which, in turn, contains the SyncSource types PIM Contact SyncSource, PIM Calendar SyncSource, FileSystem SyncSource
, and SIF SyncSource
as shown in the following screenshot:
This hierarchy is obtained with the following SQL commands:
insert into fnbl_module (id, name, description) values('foundation','foundation','Foundation');
SyncConnector registration:
insert into fnbl_connector(id, name, description) values('foundation','FunambolFoundationConnector', 'Funambol Foundation Connector');
The Foundation Connector belongs to the foundation module:
insert into fnbl_module_connector(module, connector) values('foundation','foundation');
The SyncSource Type registration:
insert into fnbl_sync_source_type(id, description, class, admin_class) values('contact-foundation','PIM Contact SyncSource', 'com.funambol.foundation.engine.source. PIMContactSyncSource', 'com.funambol.foundation.admin. PIMContactSyncSourceConfigPanel'); insert into fnbl_sync_source_type(id, description, class,admin_class) values('calendar-foundation','PIM Calendar SyncSource', 'com.funambol.foundation.engine.source. PIMCalendarSyncSource', 'com.funambol.foundation.admin. PIMCalendarSyncSourceConfigPanel'); insert into fnbl_sync_source_type(id, description, class, admin_class) values('fs-foundation','FileSystem SyncSource', 'com.funambol.foundation.engine.source. FileSystemSyncSource', 'com.funambol.foundation.admin. FileSystemSyncSourceConfigPanel'); insert into fnbl_sync_source_type(id, description, class, admin_class) values('sif-fs-foundation','SIF SyncSource','com.funambol.foundation.engine.source.SIFSyncSource','com.funambol.foundation.admin.SIFSyncSourceConfigPanel');
Finally, the SyncSource type belongs to the Foundation Connector:
insert into fnbl_connector_source_type(connector, sourcetype) values('foundation','contact-foundation'); insert into fnbl_connector_source_type(connector, sourcetype) values('foundation','calendar-foundation'); insert into fnbl_connector_source_type(connector, sourcetype) values('foundation','fs-foundation'); insert into fnbl_connector_source_type(connector, sourcetype) values('foundation','sif-fs-foundation');
Two classes are specified for each SyncSource type registration—The class (for example, com.funambol.foundation.engine.source.FileSystemSyncSource)
that actually implements the SyncSource interface, and the admin_class
that is used to create a new SyncSource instance and to configure it in the Funambol Administration Tool.
In the following section, we will see how these two classes are developed.
This section describes how to create a connector that extends the functionality of the Funambol Data Synchronization Service.
In this section we will use the following terms and concepts:
Module: A module is a container for anything related to one or more server extensions, which are used by the engine to integrate with external systems.
Connector: A connector is a particular type of module, whose primary purpose is to connect to an external datasource. In other words, a connector is an extension of the DS service intended to support the synchronization of a particular datasource.
SyncSource: This is a key component of a connector that defines the way a set of data is made accessible to the Funambol Data Synchronization Service for synchronization. A SyncSource type represents the template from which an instance of a SyncSource can be created.
Synclet: A preprocessing or postprocessing unit that can process a SyncML message before it gets into the synchronization engine or just after it is going out from it.
This chapter will guide the developer through the development, packaging, installation, and testing of a module. The module contains a simple SyncSource and Synclet, which will just produce some logging. In this way, the developer can easily see the flow of calls to the SyncSource API during a synchronization.
After becoming familiar with the tutorial, it is recommended that you inspect some real-world examples of connectors such as the OpenXchange connector (https://funamboloxconnector.forge.funambol.org) or the Exchange connector (https://exchange-connector.forge.funambol.org).
The following connector development quick-start section assumes a working knowledge of Java, Maven, and SQL.
To follow this section you will need:
Funambol Data Synchronization Service installed and running
Funambol Software Development Kit
Apache Maven (see http://maven.apache.org)
Optionally, you may want to download a Maven plugin for your preferred IDE (see http://mevenide.codehaus.org)
To download the Funambol SDK, go to https://www.forge.funambol.org/download and click on Funambol SDK in the Get started with Funambol section.
After downloading the software, install it in a directory of choice. In the following section we will use the below directory conventions:
$FUNAMBOL_HOME:
This is the directory where the bundle has been installed (for example, /opt/Funambol)
$FUNAMBOL_SDK_HOME:
This is the directory where the Funambol SDK has been installed (for example, /opt/Funambol/tools/sdk)
$JAVA_HOME:
This is the directory where Java is installed (for example, /opt/jdk1.5.0_10)
$MAVEN_HOME:
This is the directory where Apache Maven is installed (for example, /opt/apache-maven-2.0.8)
$USER_HOME:
This is the home directory of the operating system you are using (for example, /home/ste, c:Usersste)
Basic knowledge of Apache Maven, its terminology, and principles is assumed, as the following sections use terms from the Maven world without explaining them in detail. For more information refer to http://maven.apache.org/guides/getting-started/index.html.
After the installation, Maven should be configured to point to the Funambol public Maven repository (m2.funambol.org/repositories). To do so, copy the file $FUNAMBOL_SDK_HOME/docs/settings.xml
under $USER_HOME/.m2
.
The following sections are a guide on how to develop the sample module. This can be done using the following steps:
The easiest way to create a connector project is by running the following Maven command:
mvn archetype:generate -DarchetypeGroupId=funambol
connector project, creatingMaven command, using-DarchetypeArtifactId=funambol-module-archetype -DarchetypeVersion=7.1.0
-DgroupId=acme -DartifactId=acmeconnector
-DarchetypeRepository=http://m2.funambol.org/repositories/artifacts
-Dversion=1.0.0
When prompted for an answer, type Y.
This command will download and create a skeleton application ready to be built, which contains:
All of these will be generated in a Maven project located in a directory named acmeconnector
. The content of the directory is illustrated in the following screenshot:
The following table explains the function of each file:
File |
Description |
---|---|
|
AGPL license file |
|
Maven project file for the connector |
|
Sample input synclet configuration |
|
Sample output synclet configuration |
|
Readme for the content of this directory |
|
Module installation file |
|
Sample mergeable SyncSource |
|
Sample synclet |
|
Sample SyncSource |
|
Sample administration panel for both MySyncSource and MyMergeableSyncSource |
|
SQL scrip to create the database tables required by the module |
|
SQL scrip to drop the database tables required by the module |
|
SQL scrip to initialize the database tables required by the module |
As seen earlier, the SyncSource type is the primary component of the connector. The source code created by the archetype is very simple. It only writes some logging information to trace its execution. However in a real project this is where the code necessary to integrate an external datasource would go.
MyMergeableSyncSource
inherits most of its behavior from MySyncSource
. Open this file in an editor or in your IDE and go through it. MySyncSource
defines three properties that can be set via the Funambol Administration Tool. These are myString, myInt
, and myMap. Getter
and setter
methods to get and set those properties are provided. Also, note that the class implements all methods of the SyncSource interface by writing a log entry. Each method also has a description of what it does and what the developer should add.
In addition to the SyncSource types described earlier, the archetype project also contains a sample input and output synclet—MySynclet. Open it in an editor or in your IDE and go through it.
The first thing to note is that it implements both an input and an output synclet, which means that the synclet will be called for both incoming and outgoing messages. The synclet is very simple; it just logs the message in the funambol.myconnector
logger.
The goal of this section is to create a new SyncSource based on MyMergeableSyncSource
and to configure it via the Funambol Administration Tool. This is possible because of the class MySyncSourceAdminPanel
. Open it with an editor or in your IDE and go through it.
MySyncSourceAdminPanel
inherits SourceManagementPanel
, which is a class of the Admin framework. SourceManagementPanel
is a java.swing.JPanel
, therefore it has all the methods of a swing panel. The init()
method creates all widgets that we want to display in the Funambol Administration Tool and adds them to the panel. These widgets are as follows:
Source name
Data types supported (e.g. text/vcard)
Data type versions supported (e.g. 2.1)
Source URI
myString
myInt
myMap entry
In addition, it adds a JButton to the panel to save the values of an existing SyncSource or to add a newly created SyncSource. An important aspect to note is that this is where the panel interacts with the Funambol Administration Tool to save the changes to the server. The following code performs this task:
confirmButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event ) { try { validateValues(); getValues(); if (getState() == STATE_INSERT) { SyncSourceAdminPanel.this.actionPerformed( newActionEvent(MySyncSourceAdminPanel.this, ACTION_EVENT_INSERT, event.getActionCommand())); } else { MySyncSourceAdminPanel.this.actionPerformed( new ActionEvent(MySyncSourceAdminPanel.this, ACTION_EVENT_UPDATE, event.getActionCommand())); } } catch (Exception e) { notifyError(new AdminException(e.getMessage())); } } });
The key is that, when needed, the method actionPerformed()
of the base class is called with a proper event.
Another important method is updateForm()
where the value of the SyncSource instance is displayed in proper fields. Again this method is called by the Funambol Administration Tool when an existing instance must be displayed.
To package the created project into a Funambol module, go in the project directory and type:
mvn package
A typical output will be as follows:
[INFO] Scanning for projects... [INFO] -------------------------------------------------- [INFO] Building acme acmeconnector Module [INFO] task-segment: [package] [INFO] -------------------------------------------------- [INFO] artifact org.apache.maven.plugins:maven-resources-plugin: checking for updates from artifacts [INFO] artifact org.apache.maven.plugins:maven-resources-plugin: checking for updates from snapshots [INFO] artifact org.apache.maven.plugins:maven-compiler-plugin: checking for updates from artifacts [INFO] artifact org.apache.maven.plugins:maven-compiler-plugin: checking for updates from snapshots [INFO] artifact org.apache.maven.plugins:maven-surefire-plugin: checking for updates from artifacts [INFO] artifact org.apache.maven.plugins:maven-surefire-plugin: checking for updates from snapshots [INFO] artifact org.apache.maven.plugins:maven-jar-plugin: checking for updates from artifacts [INFO] artifact org.apache.maven.plugins:maven-jar-plugin: checking for updates from snapshots [INFO] [resources:resources] [INFO] Using default encoding to copy filtered resources. [INFO] [compiler:compile] [INFO] Compiling 4 source files to /Users/ste/Projects/acmeconnector/target/classes [INFO] [resources:testResources] [INFO] Using default encoding to copy filtered resources. [INFO] [compiler:testCompile] [INFO] No sources to compile [INFO] [surefire:test] [INFO] No tests to run. [INFO] [jar:jar] [INFO] Building jar: /Users/ste/Projects/acmeconnector/target/acmeconnector-1.0-SNAPSHOT.jar [INFO] [funambol:s4j] [INFO] Exploding Funambol packaging... [INFO] Assembling Funambol packaging acmeconnector in /Users/ste/Projects/acmeconnector/target/acmeconnector-1.0-SNAPSHOT [INFO] Including license file /Users/ste/Projects/acmeconnector/LICENSE.txt [INFO] [INFO] Including artifacts: [INFO] ------------------- [INFO] x funambol:server-framework:jar:8.0.3-SNAPSHOT:compile [INFO] x funambol:core-framework:jar:6.5.4:compile [INFO] x funambol:ext:jar:6.5.2:compile [INFO] o org.jibx:jibx-run:jar:1.1.2fun:compile [INFO] o xpp3:xpp3:jar:1.1.2a-fun:compile [INFO] o commons-lang:commons-lang:jar:2.3:compile [INFO] o funambol:admin-framework:jar:6.5.2:compile [INFO] [INFO] Excluded artifacts: [INFO] ------------------ [INFO] [INFO] Including jar files... [INFO] basedir: /Users/ste/Projects/acmeconnector [INFO] srcDir: /Users/ste/Projects/acmeconnector/src/main [INFO] sqlDirectory: /Users/ste/Projects/acmeconnector/target/acmeconnector-1.0-SNAPSHOT/sql [INFO] wsddDirectory: /Users/ste/Projects/acmeconnector/target/acmeconnector-1.0-SNAPSHOT/wsdd [INFO] No config files... [INFO] No exclude files... [INFO] Including install files... [INFO] Including sql files... [INFO] No wsdd files... [INFO] [INFO] Generating Funambol packaging /Users/ste/Projects/acmeconnector/target/acmeconnector-1.0-SNAPSHOT.s4j [INFO] Building jar: /Users/ste/Projects/acmeconnector/target/acmeconnector-1.0-SNAPSHOT.s4j [INFO] -------------------------------------------------- [INFO] BUILD SUCCESSFUL [INFO] -------------------------------------------------- [INFO] Total time: 12 seconds [INFO] Finished at: Sat May 17 15:42:08 CEST 2008 [INFO] Final Memory: 9M/16M [INFO] -------------------------------------------------------------
The final message Generating Funambol packaging /Users/ste/Projects/acmeconnector/target/acmeconnector-1.0-SNAPSHOT.s4j
tells where the package is created.
To install the newly created module into the server, copy acmeconnector-1.0-SNAPSHOT.s4j
into $FUNAMBOL_HOME/ds-server/modules
and follow the steps detailed next:
Make sure Funambol is up and running.
Using a text editor, open the $FUNAMBOL_HOME/ds-server/install.properties
file.
Find the line that begins with modules-to-install=
in the Module definitions section. This line specifies, in a comma-separated list, the modules to install during installation.
Add acmeconnector-1.0-SNAPSHOT
to the comma-separated list (note that you do not have to specify the .s4j
file extension).
Save and close install.properties
.
Open a command window and run the server installation script by typing:
on Windows:
cd $FUNAMBOL_HOME/ds-server bininstall-modules
on Unix/Linux:
cd $FUNAMBOL_HOME/ds-server bin/install-modules
Answer all questions about recreating the modules DB schema when prompted. Type Y when asked to install the database for the new module and N for all others.
[echo] Funambol Data Synchronization Server will be installed on the Tomcat 5.5.x application server [echo] Undeploying funambol... [echo] Pre installation for modules foundation- 8.0.1,acmeconnector-1.0-SNAPSHOT [echo] foundation-8.0.1 pre-installation... [echo] foundation-8.0.1 pre-installation successfully completed [echo] acmeconnector-1.0-SNAPSHOT pre-installation... [echo] acmeconnector-1.0-SNAPSHOT pre-installation successfully completed [echo] Copying configuration files [echo] Post installation for modules foundation- 8.0.1,acmeconnector-1.0-SNAPSHOT [echo] has.install: true [echo] Starting custom installation... [echo] Foundation Installation [echo] Foundation installation successfully completed [echo] foundation-8.0.1 installation... [echo] Database installation for module foundation- 8.0.1 on hypersonic (/opt/Funambol/ds-server) [iterate] The Funambol Data Synchronization Server installation program can now create [iterate] the database required by the module foundation-8.0.1 (if any is needed). [iterate] You can skip this step if you have already a valid database created [iterate] or the module does not require a database. [iterate] If you choose 'y' your existing data will be deleted. [iterate] Do you want to recreate the database? [iterate] (y,n) n [...] [echo] foundation-8.0.1 installation successfully completed [echo] has.install: true [echo] Starting custom installation... [echo] acmeconnector installation [echo] acmeconnector installation successfully completed [echo] acmeconnector-1.0-SNAPSHOT installation... [echo] Database installation for module acmeconnector-1.0-SNAPSHOT on hypersonic (/opt/Funambol/ds-server) [iterate] The Funambol Data Synchronization Server installation program can now create [iterate] the database required by the module acmeconnector-1.0-SNAPSHOT (if any is needed). [iterate] You can skip this step if you have already a valid database created [iterate] or the module does not require a database. [iterate] If you choose 'y' your existing data will be deleted. [iterate] Do you want to recreate the database? [iterate] (y,n)y [echo] acmeconnector-1.0-SNAPSHOT installation successfully completed [war] Warning: selected war files include a WEB- INF/web.xml which will be ignored (please use webxml attribute to war task) [echo] Remove output dir BUILD SUCCESSFUL Total time: 12 seconds
Restart the Data Synchronization Service.
The new connector is now installed.
Now that the acmeconnector
is installed, it is displayed in the Funambol Administration Tool, under the Modules section, as shown in the following screenshot:
Once the new SyncSource type is installed on the server, a new instance of the SyncSource can be created and configured so that it is available to the clients. This is done by means of the Funambol Administration Tool. Right-click the MyMergeableSyncSource
node and select New from the context menu that pops up. The form illustrated on the right side of the previous screenshot will appear.
This window is used to specify configuration values. Insert the following values and click Add:
Source URI: acme
Name: Acme
Supported type: text/plain
Supported version: 1.0
MyString: acme connector!
MyInt: 10
MyMap entry: <acme, connector>
To test the Acme connector, a simple command line client will be used. This is distributed in the Funambol SDK under $FUNAMBOL_SDK_HOME/plug-ins/cl
.
Before using it, the command line client must be configured to access the SyncSource created earlier. Edit the file config/spds/sources/briefcase.properties
and set the following values:
name=acme sourceClass=com.funambol.syncclient.spds.source.FileSystemSyncSource sourceDirectory=db/briefcase type=text/plain sync=two-way encode=true sourceURI=acme
The command line tool is a simple client that synchronizes the content of a given directory. This is the one specified by the sourceDirectory
parameter. In the case of Acme connector, however, we are not interested in the data transfer, but more interested in learning the calls executed in the connector SyncSource.
To launch the client, run the command $FUNAMBOL_SDK_HOMEplug-insclinrun.cmd
on Windows (or $FUNAMBOL_SDK_HOME/plug-ins/cl/run.sh
if using Linux).
An output similar to the following will be displayed:
Funambol Command Line Tool ver. 8.0.1 -------------------------- 2008-04-17 16:28:10:969 - # SyncClient API J2SE Log 16:28:10:970 [INFO] - Initializing 16:28:10:973 [INFO] - Sending initialization commands 16:28:11:716 [INFO] - The server alert code for acme is 201 6:28:11:718 [INFO] - Synchronizing acme 16:28:11:745 [INFO] - exchange modifications started 16:28:11:746 [INFO] - Preparing slow sync for acme 16:28:11:747 [INFO] - Detected 0 items 16:28:11:748 [INFO] - Sending modifications 16:28:11:837 [INFO] - Returned 0 new items, 0 updated items, 0 deleted items for acme 16:28:11:838 [INFO] - Mapping started 16:28:11:841 [INFO] - Sending mapping 16:28:11:853 [INFO] - Sending mapping 16:28:11:874 [INFO] - Mapping done 16:28:11:874 [INFO] - Synchronization done
The server log shows the Acme connector at work. After filtering out the lines that are not of interest for our connector, the log entries will be similar to the following text:
[2008-05-17 16:31:56,656] [funambol.myconnector] [INFO] [399F31F06404433DE69A05F71D562ACD] [sc-api-j2se] [guest] [] Initializing acme.MyMergeableSyncSource
connector developmenttesting[2008-05-17 16:31:56,657] [funambol.myconnector] [INFO] [399F31F06404433DE69A05F71D562ACD] [sc-api-j2se] [guest] [] myString: acme connector!
[2008-05-17 16:31:56,657] [funambol.myconnector] [INFO] [399F31F06404433DE69A05F71D562ACD] [sc-api-j2se] [guest] [] myInt: 10
[2008-05-17 16:31:56,657] [funambol.myconnector] [INFO] [399F31F06404433DE69A05F71D562ACD] [sc-api-j2se] [guest] [] myMap: {acme=connector}
[2008-05-17 16:31:56,703] [funambol.myconnector] [INFO] [399F31F06404433DE69A05F71D562ACD] [sc-api-j2se] [guest] [acme] Starting synchronization: com.funambol.framework.engine.source.SyncContext@e85079
[2008-05-17 16:31:56,703] [funambol.myconnector] [INFO] [399F31F06404433DE69A05F71D562ACD] [sc-api-j2se] [guest] [acme] getNewSyncItemKeys()
[2008-05-17 16:31:56,703] [funambol.myconnector] [INFO] [399F31F06404433DE69A05F71D562ACD] [sc-api-j2se] [guest] [acme] getUpdatedSyncItemKeys()
[2008-05-17 16:31:56,703] [funambol.myconnector] [INFO] [399F31F06404433DE69A05F71D562ACD] [sc-api-j2se] [guest] [acme] getDeletedSyncItemKeys()
[2008-05-17 16:31:56,703] [funambol.myconnector] [INFO] [399F31F06404433DE69A05F71D562ACD] [sc-api-j2se] [guest] [acme] Committing synchronization
[2008-05-17 16:31:56,782] [funambol.myconnector] [INFO] [399F31F06404433DE69A05F71D562ACD] [sc-api-j2se] [guest] [acme] Ending synchronization
Note: The first entries of the log show the values inserted earlier in the administration panel, which means that the SyncSource configuration was properly picked up.
Similarly, the server log also shows the effect of MySynclet
. Each SyncML message has been logged; for example the first input message is something like the following text:
[2008-05-17 16:59:28,576] [funambol.myconnector] [INFO] [FF4B335BA02F0581DAFF016319EDD263] [] [] [] [2008-05-17 16:59:28,576] [funambol.myconnector] [INFO] [FF4B335BA02F0581DAFF016319EDD263] [] [] [] Input message[2008-05-17 16:59:28,576] [funambol.myconnector] [INFO] [FF4B335BA02F0581DAFF016319EDD263] [] [] [] [2008-05-17 16:59:28,585] [funambol.myconnector] [INFO] [FF4B335BA02F0581DAFF016319EDD263] [] [] [] <?xml version="1.0" encoding="UTF-8"?> <SyncML><SyncHdr><VerDTD>1.1</VerDTD><VerProto> SyncML/1.1</VerProto><SessionID>12345678</SessionID><MsgID>1</MsgID><Target><LocURI>http://localhost:8080/funambol/ds</LocURI></Target> <Source><LocURI>sc-api-j2se</LocURI></Source> <Cred><Meta><Type>syncml:auth-basic</Type></Meta> <Data>Z3Vlc3Q6Z3Vlc3Q=</Data></Cred><Meta><MaxMsgSize>250000 </MaxMsgSize><MaxObjSize>4000000</MaxObjSize></Meta></SyncHdr> <SyncBody><Alert><CmdID>1</CmdID><Data>200</Data><Item><Target> <LocURI>acme</LocURI></Target><Source><LocURI>acme</LocURI></Source> <Meta><Anchor><Last>1211034716596</Last><Next>1211036368401</Next> </Anchor></Meta></Item></Alert><Final/></SyncBody></SyncML>[2008-05-17 16:59:28,585] [funambol.myconnector] [INFO] [FF4B335BA02F0581DAFF016319EDD263] [] [] [] -------------------------------------------------------------
The last output message is something like the following:
[2008-05-17 16:59:28,876] [funambol.myconnector] [INFO] [FF4B335BA02F0581DAFF016319EDD263] [sc-api-j2se] [guest] [] [2008-05-17 16:59:28,877] [funambol.myconnector] [INFO] [FF4B335BA02F0581DAFF016319EDD263] [sc-api-j2se] [guest] [] Output message[2008-05-17 16:59:28,877] [funambol.myconnector] [INFO] [FF4B335BA02F0581DAFF016319EDD263] [sc-api-j2se] [guest] [] [2008-05-17 16:59:28,877] [funambol.myconnector] [INFO] [FF4B335BA02F0581DAFF016319EDD263] [sc-api-j2se] [guest] [] <?xml version="1.0" encoding="UTF-8"?> <SyncML><SyncHdr><VerDTD>1.1</VerDTD><VerProto>SyncML/1.1</VerProto> <SessionID>12345678</SessionID><MsgID>5</MsgID><Target><LocURI>sc-api-j2se</LocURI></Target><Source><LocURI> http://localhost:8080/funambol/ds</LocURI></Source> <RespURI>http://localhost:8080/funambol/ds;jsessionid=FF4B335BA02F0581DAFF016319EDD263</RespURI></SyncHdr><SyncBody><Status><CmdID>1 </CmdID><MsgRef>5</MsgRef><CmdRef>0</CmdRef><Cmd>SyncHdr</Cmd><TargetRef>http://localhost:8080/funambol/ds</TargetRef><SourceRef>sc-api-j2se</SourceRef><Data>200</Data></Status><Final/></SyncBody></SyncML>[2008-05-17 16:59:28,877] [funambol.myconnector] [INFO] [FF4B335BA02F0581DAFF016319EDD263] [sc-api-j2se] [guest] [] --------------- ----------------------------------------------
If you have reached this point, you have successfully developed and deployed a Funambol connector. Congratulations! You can now further extend the acmeconnector
project or start your own.
When developing a connector, it can be very helpful to to use a debugger to inspect the behavior of the connector to discover and fix bugs. The best way to do this is to use the remote debugging capabilities provided by the Java Virtual Machine (JVM). To do so, follow these steps:
Edit the file $FUNAMBOL_HOME/bin/funambol-server
uncommenting the JPDA_OPTS
line, which you can easily find if you search for "debug". This will start Funambol, enabling it for remote debugging.
Connect to the running JVM. If you use Eclipse, you can create a debug profile to attach to Funambol. Note that the debug port given in the JPDA_OPTS
line is 8787, not eclipse's default port, (8000), so either change the JDPA_OPTS
or the Eclipse port. NetBeans supports remote debugging too. Click on the Debug menu item and then select Attach Debugger.
Now you can start a sync session and stop at breakpoints.
This chapter helps developers who want to extend the Funambol DS Service to integrate with their own backend datasources. This chapter presented some key concepts of the synchronization process, which provided us with the background information needed to understand some of the details of the Funambol API and framework. It also showed that developing a connector can be pretty straightforward. By following the steps presented in this chapter, a new simple connector can be created and deployed. This is only a starting point, but it allows developers to see the overall synchronization flow, from a client making a synchronization request, to the connector code.
3.129.42.134