Broadcast on Broadcast off
The Documentation for Project Zero has moved. Please update your bookmarks to: http://www.projectzero.org/documentation/
Table of
Contents...
Hide
 

Books demo

This sample demonstrates Atom support with the zero.atom extension and ibm_atom Dojo extension. zero.atom is useful for explicit Atom support; Zero also provides implicit Atom support as part of the Zero Resource Model. ZRM is still evolving, but we're looking to make it an easy choice for rapid model-based development of resource handlers.

For those that prefer explicit Atom support, the "back-end" of the Books Demo demonstrates use of the Atom renderer and Abdera APIs with a relational database from a Groovy-based resource handler. The "front-end" of the application uses a Dojo extension to enable simple create/read/update/delete operations of records via Atom and Atom Publishing Protocol.

The Books Demo is available from the download page under the Project Zero Examples Plugin for Eclipse. Instructions for installing Books Demo and other samples are available in the Getting Started section of the Core Developer's Guide. Additional post-install steps are documented in the README.TXT file in the Books Demo folder.

Leveraging Zero conventions

This application uses zero.data to access the relational database. The simplest form of the zero.data APIs returns records as =Map=s, where the keys are the column names. Given that we want to serialize the results with the Atom renderer, we'll save ourselves some work by setting up the table schema with column names that match the convention of the Atom renderer. (Obviously this is most suitable for "new" applications, although mapping from existing tables is possible.)

Table schema

The table schema is defined in createBookTable.sql:

CREATE TABLE books (
  ID int NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
  TITLE varchar(56) NOT NULL,
  AUTHORNAME varchar(56) NOT NULL,
  UPDATED date NOT NULL,
  CONTENTTYPE varchar(256) NOT NULL,
  CONTENT varchar(256) NOT NULL,
  CATEGORYTERM varchar(256) ,  
  CATEGORYLABEL varchar(56) ,
  FOREIGN1 varchar(256),  
  FOREIGN2 varchar(56),
  PRIMARY KEY  (ID)
);

Most of the columns map directly into Atom elements (e.g. TITLE, AUTHORNAME); FOREIGN1 and FOREIGN2 are extensions employed here for additional metadata related to the books.

The application is configured to use embedded Derby, as shown in config/derby-embedded.config:

/config/db/books_db = {
    "class" : "org.apache.derby.jdbc.EmbeddedDataSource",
    "databaseName" : "db/books_db",
    "connectionAttributes" : "create=true"
}

Simple resource list/retrieve handlers

Given the table schema, the simplest implementation of a resource handler for serving the collection as Atom feeds (list) and entries (retrieve) would be:

def onList() {
    def dataManager = zero.data.groovy.Manager.create('books_db');
    def rs = dataManager.queryArray('SELECT * FROM books ORDER BY UPDATED DESC')
    
    request.view='atom'
    request.atom.output = rs 
    render() 
}

def onRetrieve() {
    def dataManager = zero.data.groovy.Manager.create('books_db')
    def id = request.params.booksId[]
    def rs = dataManager.queryFirst("SELECT * FROM books WHERE id=$id" )

    request.view='atom'
    request.atom.output = rs
    render()
}

Note that we're able to render directly the data results (again, because of our careful selection of column names).

app/resources/books.groovy contains an enhanced implementation, including error checking and support for queries within the list operation.

Create handler

Create and update handlers are a little more verbose with zero.atom, since you'll be explicitly mapping the posted entry to a SQL insert/update. For example, a simplified excerpt from app/resources/books.groovy:

def onCreate() {
    def entry = org.apache.abdera.Abdera.getNewParser().parse(request.input[]).getRoot()
    def updated = new java.sql.Timestamp(System.currentTimeMillis())
    
    def dataManager = zero.data.groovy.Manager.create('books_db')   
        
    id = dataManager.insert('INSERT INTO books (TITLE, AUTHORNAME, CONTENT, CONTENTTYPE, UPDATED, CATEGORYTERM, CATEGORYLABEL, FOREIGN1, FOREIGN2) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)', 
                ['id'],
                [entry.getTitle(), 
                entry.getAuthor().getName(),        
                entry.getContent().toString(), 
                entry.getContentType().toString(), 
                updated, 
                entry.getCategories().get(0).getTerm(),
                entry.getCategories().get(0).getLabel(),
                zero.atom.AtomUtils.getExtension(entry, 'FOREIGN1'), 
                zero.atom.AtomUtils.getExtension(entry, 'FOREIGN2')])
 
                
        resourceUri = getRequestedUri() + '/' + id
        request.headers.out.Location = resourceUri
        
        entry.setId(resourceUri)
        link = entry.addLink(resourceUri)
        link.setAttributeValue('rel', 'edit')

        request.status = 201
        request.view = 'atom'
        request.atom.output = entry
        render()
    }
}

zero.atom.AtomUtils.getExtension() is a convenience API for extracting default extensions set by the Atom renderer. This simplifies the case where you want to use extensions, but you're not concerned with the namespace and prefix details.

Note that the auto-incremented id is used to construct the member URI, which is set into the Location response header. When using zero.atom, you'll be required to manage low-level aspects, like the Location header, of proper HTTP responses.

r7 - 08 Feb 2008 - 22:48:37 - paynel
Syndicate this site RSS ATOM
Copyright 2007 © IBM Corporation | Privacy | Terms of Use | About this site