Skip to main content

Developing MEP Connectors - Part II

Posted by spericas on August 13, 2008 at 7:00 AM PDT

In the first installment of these series we've looked at the architecture of a MEP connector and briefly discuss the main abstractions in the ECBO (Enterprise Connector Business Object) API: BusinessObject and BusinessObject provider. In this second part, we'll discuss an actual implementation of the BusinessObject abstraction used to create a connector that synchronizes data against a relational database.

We will use the MusicDB sample application provided with MEP to demonstrate how to use the ECBO API. The Enterprise Connector in this application acts as the intermediary between a client on a mobile device and a database. In this particular demo, the database will be accessed using the Java Database Connectivity (JDBC) API. The source code for the MusicDB Enterprise Connector is included in the MEP client bundle available for download here. In the directory where you unzipped the client bundle, it is in the subdirectory sjsmep-client-1_0-fcs/samples/ecbo/.

As stated in Part I, the ECBO API provides two essential abstractions: BusinessObject and BusinessObjectProvider. Due to the nature of the synchronization process, every enterprise connector must provide the ability to CRUD (create, retrieve, update and delete) business objects. In addition to providing the data model for the entities being synchronized, the BusinessObject abstraction also provides support for CUD, i.e. each object provides the ability to create, update and delete itself. The R operation, or retrieve, is implemented by the BusinessObjectProvider.

Let us start by describing the MusicAlbum which extends BusinessObject. Every business object in MEP must have a unique name (its identity) and this is stored in the name variable in BusinessObject which can be accessed via getName() and setName(String) in a subclass. The other variables in the subclass complete the model for the business object in question, a music album in our case.

public class MusicAlbum extends BusinessObject {

    private static final String DEFAULT_VALUE = "$$default$$";

    /**
     * Album's artist.
     */
    String artist;

    /**
     * Date when the album was published.
     */
    Calendar datePublished;
   
    /**
     * Album's rating from 1 to 5.
     */
    int rating;

    ...

All business objects must provide the ability to serialize and deserialized to a byte array. The actual format used for serialization is open ended, but must be part of the contract between the connector and the mobile client application. For music albums, serialization is implemented using DataOutputStream and DataInputStream.

    public byte[] serialize() throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        DataOutputStream dOut = new DataOutputStream(out);

        dOut.writeUTF(getName());
        dOut.writeUTF(getArtist() != null ? getArtist() : DEFAULT_VALUE);
        dOut.writeUTF(getDatePublished() != null ? getDatePublished() : DEFAULT_VALUE);
        dOut.writeUTF(Integer.toString(getRating()));
        dOut.flush();
        return out.toByteArray();
    }

    public void deserialize(byte[] array) throws IOException {
        ByteArrayInputStream in = new ByteArrayInputStream(array);
        DataInputStream dIn = new DataInputStream(in);
       
        setName(dIn.readUTF());
        artist = dIn.readUTF();
        if (artist.equals(DEFAULT_VALUE)) {
            artist = null;
        }
        String date = dIn.readUTF();
        if (date.equals(DEFAULT_VALUE)) {
            datePublished = null;
        }
        else {
            setDatePublished(date);
        }
        rating = Integer.parseInt(dIn.readUTF());
    }

As stated above, a business object must also implement CUD operations. Three new classes, namely, MusicAlbumInsertCommand, MusicAlbumUpdateCommand and MusicAlbumDeleteCommand are defined as extensions to InsertCommand, UpdateCommand and DeleteCommand, respectively.

    @Override
    public MusicAlbumInsertCommand getInsertCommand() {
        return new MusicAlbumInsertCommand(this, getSQLConnection(),
                getInsertString());
    }
   
    @Override
    public MusicAlbumUpdateCommand getUpdateCommand() {
        return new MusicAlbumUpdateCommand(this, getSQLConnection(),
                getUpdateString());
    }
   
    @Override
    public MusicAlbumDeleteCommand getDeleteCommand() {
        return new MusicAlbumDeleteCommand(this, getSQLConnection(),
                getDeleteString());
    }

The reader is referred to the client bundle (see folder location above) for the complete sources of all these command classes. The methods above use utility methods to construct SQL statements. The MusicDB album table is comprised of 5 columns: name, artist, datePublished, rating and username. The last column holds the name of the user that created the entry; this last column enables multiple users to share the same physical table. As an example, getDeleteString() is implemented as follows:

    public String getDeleteString() {
        stringBuilder.setLength(0);
        stringBuilder.append("DELETE FROM album WHERE name = '")
                     .append(getName())
                     .append("' AND username = '"
                            + getBusinessObjectProvider().getUsername() + "'");
        return stringBuilder.toString();
    }

The last method that MusicAlbum must override is getExtension(). Business objects are stored as files on a mobile device, and due to compatibility issues among devices, it is easier to store them in the same folder. If multiple MEP applications are installed on a device, then the client MEP library must have a quick and unambiguous way to distinguish these objects. This is currently done by defining a unique file extension which is returned by the getExtension() method.

    @Override
    public String getExtension() {
        return ".alb";
    }

By introducing the MusicAlbum class we have shown how to create a sample MEP business object. This is about half of the work needed to write a MEP connector; the other half would involve the implementation of a MusicAlbumProvider that extends BusinessObjectProvider. This will be the subject of our next blog entry. Stay tuned!

Related Topics >>

Comments

Hello, I am looking at this architecture and I have 2 questions: 1- How/where is the MEP data is saved (actual data for sync, not MEP internal data)? Is it in the database or in JCP implementation or neither? 2- How are the conflicts handled? In the simplistic example above, I can see update/create/delete where each can raise a conflict. How do you handle such thing and how about rejection of the data from the backend? Thank you! Julien.

Julien, 1) Data is always stored in the back-end, the gateway only stores data digests. 2) Conflicts are detected and handled by the gateway. Connectors can optionally implement the method mergeBusinessObjects(serverObject, clientObject) if merging objects is required. Otherwise, it is always server wins.