Sun GlassFish Mobility Platform 1.1 Developer's Guide for Enterprise Connectors

The MyBusinessObjectsResource Class

The MyBusinessObjectsResource class exposes the following URLs, as the comments for the class indicate:

The @Path annotation at the beginning of the class establishes the path to these URLs:

@Path("businessObjects")
public class MyBusinessObjectsResource {

The class begins with a definition of the file extension to be returned by the lifeCycle method, followed by a context declaration and class constructor.

    // UPDATE CODE: Set extension for these objects
    public static final String EXTENSION = ".obj";
    
    @Context
    private UriInfo context;
    
    public MyBusinessObjectsResource() {
    }

The getBusinessObjects method is mapped to the HTTP GET operation on this resource. The method template looks like this:

    @GET
    @Produces("text/xml")
    public BusinessObjects getBusinessObjects(
            @QueryParam("username") @DefaultValue("username") String user,
            @QueryParam("password") @DefaultValue("password") String password,
            @QueryParam("sessionId") @DefaultValue("") String sessionId)
    {
        try {
            BusinessObjects result = new BusinessObjects();
            List<BusinessObject> resultList = result.getBusinessObject();
            
            // INSERT CODE: populate list of business objects
            
            return result;
        }
        catch (Exception e) {
            throw new RuntimeException(e); 
        }
    }

The getBusinessObjects method returns an instance of a JAXB object, BusinessObjects, serialized as an XML document. Each object is identified by a name and a binary representation that is a contract between the Enterprise Connector and the client. The getBusinessObject method called on the result object is not one of the methods exposed in the resource classes. Instead, it is a JAXB accessor method defined in the package that is imported at the beginning of the resource class file:

import com.sun.mep.connector.jaxrs.getbusinessobjects.*;

Some operations go beyond the simple CRUD operations. The lifeCycle method corresponds to the HTTP POST operation. It has the following signature:

    @POST
    @Produces("text/plain")
    public String lifeCycle(
            @QueryParam("username") @DefaultValue("username") String user,
            @QueryParam("password") @DefaultValue("password") String password,
            @QueryParam("sessionId") @DefaultValue("") String sessionId,
            @QueryParam("operation") String operation)

You can use this method to control the lifecycle of a synchronization session, identified by the sessionId parameter. Specifically, you can use the value of the operation argument to initialize a session by connecting to a database or EIS system, or to terminate the session:

        if (operation.equals("initialize")) {
            // INSERT CODE: session initialization
        }
        else if (operation.equals("terminate")) {
            // INSERT CODE: session termination
        }
        else {
            throw new RuntimeException("Lifecycle operation " + operation + 
                    " not understood");
        }
        return EXTENSION;

Example: The MusicAlbumsResource Container Class

In the MusicDB example, the MusicAlbumsResource class is the container class. After importing needed packages, the resource class begins with a @Path annotation that establishes the URLs:

@Path("albums")
public class MusicAlbumsResource {

The HTTP operations will use the URLs http://server:port/context-root/resources/albums and http://server:port/context-root/resources/albums/{id}. For example, if you specify musicdb-ws as the context root and are running the Enterprise Server on your own system, the URL for retrieving the business objects would be http://localhost:8080/musicdb-ws/resources/albums.

The next few lines define the file extension to be used for these business objects, a JAX-RS context, and a class constructor. They also define a user name prefix specific to this Enterprise Connector, to be used by the lifeCycle method:

    private static final String EXTENSION = ".alb";
    private static final String DB_USER_NAME = "musicdbuser";
    
    @Context
    private UriInfo context;
    
    public MusicAlbumsResource() {
    }

Next, the lifeCycle method provides code to establish or terminate a connection to the database, using the ConnectionPool defined in src/main/java/com/sun/mep/ws/musicdb/util/ConnectionPool.java. Both the createConnection and destroyConnection methods take a session ID argument.

    @POST
    @Produces("text/plain")
    public String lifeCycle(
            @QueryParam("username") @DefaultValue("username") String user,
            @QueryParam("password") @DefaultValue("password") String password,
            @QueryParam("sessionId") @DefaultValue("") String sessionId,
            @QueryParam("operation") String operation) 
    {
        if (operation.equals("initialize")) {
            if (!user.startsWith(DB_USER_NAME)) {
                throw new RuntimeException("A MusicDB user name must start " 
                        + "with the '"+ DB_USER_NAME + "' prefix");
            }
            ConnectionPool.getInstance().createConnection(sessionId);
        }
        else if (operation.equals("terminate")) {
            ConnectionPool.getInstance().destroyConnection(sessionId);
        }
        else {
            throw new RuntimeException("Lifecycle operation " + operation + 
                    " not understood");
        }
        return EXTENSION;
    }

For the MusicDB application, a connection can be established only if the user name begins with musicdbuser. At the end, the method returns the string .alb, the file extension for music albums.

The getBusinessObjects method begins by using another connection to create a JDBC statement:

    @GET
    @Produces("text/xml")
    public BusinessObjects getBusinessObjects(
            @QueryParam("username") @DefaultValue("username") String user,
            @QueryParam("password") @DefaultValue("password") String password,
            @QueryParam("sessionId") @DefaultValue("") String sessionId,
            @QueryParam("useTimestamps") @DefaultValue("false") boolean useTimestamps)
    {
        Statement stmt = null;
        
        try {
            // Create SQL statement from connection
            ConnectionPool pool = ConnectionPool.getInstance();
            stmt = pool.getConnection(sessionId).createStatement();

The useTimestamps argument of the method, which is specific to the MusicDB Enterprise Connector, provides the value of the useTimestamps flag. An administrator can set the value of the useTimestamps property when defining the Enterprise Connector in the Sun GlassFish Mobility Platform Administration Console. If the property is not set, the argument value is set to false.

It then executes the template code that instantiates the list of JAXB objects:

            // Get list of business objects to return
            BusinessObjects result = new BusinessObjects();
            List<BusinessObject> resultList = result.getBusinessObject();

Next, the method performs an SQL query to retrieve from the back-end database all the business objects that belong to the user who is logged in to the Enterprise Connector:

            ResultSet rs = stmt.executeQuery(
                    "SELECT * FROM album WHERE username = '" + user + "'");

The method iterates through the query result set, instantiating each business object (in this case, a MusicAlbum) and populating it with its retrieved properties (including the lastModified property if the useTimestamps flag is set), then serializing it to a JAXB object and adding it to the list of objects. Finally, the method closes the result set and returns the list of JAXB objects.

            while (rs.next()) {
                // Use temporary object for serialization purposes
                MusicAlbum album = new MusicAlbum(user, false);
                album.setName(rs.getString(1));
                album.setArtist(rs.getString(2));
                album.setDatePublished(rs.getString(3).replace("-", ""));
                album.setRating(rs.getInt(4));
                if (useTimestamps) {
                    album.setLastModified(rs.getTimestamp(6).getTime());
                }
                
                // Map MusicAlbum to JAXB bean
                BusinessObject bo = new BusinessObject();
                bo.setName(album.getName());
                bo.setValue(album.serialize());
                if (useTimestamps) {
                    bo.setLastModified(album.getLastModified());
                }
                
                // Add BusinessObject to result list
                resultList.add(bo);
            }
            rs.close();  
            
            return result;
        }
        catch (Exception e) {
            throw new RuntimeException(e); 
        }
        finally {
            if (stmt != null) {
                try { stmt.close(); } catch (Exception e) { /* ignore !*/ }
            }
        }        
    }