Atom application (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.

Installing the books.demo application (Command Line)

Use the following steps to install the books.demo application using the command line:

  1. Install the IBM® WebSphere® sMash command line utility using the instructions on the WebSphere sMash Web site.
  2. Install the employee demo application from the WebSphere sMash repository using the following command:
    zero create books.demo from zero:books.demo
  3. Create the database artifacts for the application from the root directory of the project with the following command:
    zero runsql createBookTable.sql
  4. Start the books.demo application with the start command:
    zero start
  5. After you have followed the previous steps, you can interact with the application in the following ways:
    Dojo-based UI for for atom format
    http://localhost:8080
    Resource URIs
    • http://localhost:8080/resources/books - list all books
    • http://localhost:8080/resources/books/{memberId} - retrieve one book, for example, http://localhost:8080/resources/books/1
    • http://localhost:8080/resources/books?title=The - list of all books with The in the title
    • http://localhost:8080/resources/books?author=o - list of all books with o in the authorname

Details

If you 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.

Leveraging WebSphere sMash conventions

This application uses zero.data to access the relational database. The simplest form of the zero.data APIs returns records as Maps, 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 as shown in the following example:

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 as shown in the following example:

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()
}
You can render the data results directly because of the careful selection of column names.

The app/resources/books.groovy file 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()
    }
}

The zero.atom.AtomUtils.getExtension() API conveniently extracts default extensions set by the Atom renderer. This simplifies the process if you want to use extensions, but you are not concerned with the namespace and prefix details.

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

Version 1.0.0.3.25591