Package com.ibm.portal.resolver.atom

See:
          
Description

Interface Summary
AtomContentHandler http://tools.ietf.org/html/rfc4287 RFC 4287
AtomDateParser Parses ATOM dates according to RFC 4287:
AtomPublishingContentHandler http://tools.ietf.org/html/rfc5023 (October 2007) The Atom Publishing Protocol (introspection document)
AtomPublishingXMLReader  
AtomThreadingContentHandler http://www.ietf.org/rfc/rfc4685.txt ATOM threading extension (RFC 4685)
AtomThreadingXMLReader  
AtomXMLReader Data source for an XML stream in ATOM format (http://tools.ietf.org/html/rfc4287).
AtomXMLReaderFactory Helper factory that generates artifacts to convert between ATOM specific interfaces and SAX specific interfaces
Constants Declaration of the constants that define the ATOM namespace
DefaultAtomContentHandler Extension of the AtomContentHandler interface that provides convenience methods to more easily generate ATOM events.
DefaultAtomPublishingContentHandler Extension of the AtomPublishingContentHandler interface that provides convenience methods to more easily generate ATOM publishing events.
DefaultAtomThreadingContentHandler Extension of the AtomThreadingContentHandler interface that provides convenience methods to more easily generate ATOM threading events.
ResettableAtomContentHandler Extension of AtomContentHandler that allows to builds processing chains of these handlers.
ResettableAtomPublishingContentHandler Extension of AtomPublishingContentHandler that allows to builds processing chains of these handlers.
ResettableAtomThreadingContentHandler Extension of AtomThreadingContentHandler that allows to builds processing chains of these handlers.
 

Package com.ibm.portal.resolver.atom Description

ATOM API

From Portalpedia

This article describes an API to facilitate the generation and parsing of ATOM feeds in JAVA. The goal of the API is to be easy to use, efficient and to cooperate well with existing XML generation and parsing mechanisms on the JAVA platform.

Table of contents

Design Principles

The ATOM API fully integrates with the JAXP API and is based on the SAX interfaces that are part of this API. The advantages of this design decision are

  • the SAX API is very performant, because it avoids the generation of intermediary object both at feed generation and feed consumption time. Even large feeds can therefore handled in a scalable way
  • by reusing the JAXP mechanism ATOM feeds can be serialized, deserialized, transformed (via XSLT) using existing and efficient mechanisms. The ATOM API only adds semantic on top of existing interfaces without breaking interoperability. This extra semantic makes it simple for clients to generate or parse ATOM feeds but at the same time maintain full flexibility.

AtomXMLReader

The AtomXMLReader interface represents the source of SAX events and extends the defaultl XMLReader interface. Generators of feeds need to implement this interface, see the example section how this can be easily done. The actual events can be delivered in two ways

  • directly as SAX events on the ContentHandler that is attached to the reader. This is the default case if interoparability with JAXP is required (e.g. if the feed needs to be serialized into XML markup).
  • as callbacks to the AtomContentHandler interfaces. This is useful if there exists code that needs to further interpret the ATOM feeds.

AtomContentHandler

As an extension of ContentHandler the AtomContentHandler interface adds method calls for each XML element that is defined in the ATOM RFC. So instead of constructing low level SAX events client can call these human readable methods to generate ATOM events.

The same interface is also used during ATOM parsing. The ATOM parser will make callbacks to the methods on the AtomContentHandler interface for all elements of the parsed feed that are in the ATOM namespace. All other events are transmitted as SAX events.

The methods on the AtomContentHandler are meant to be complete in the sense that they cover all elements that are declared in the ATOM namespace by RFC 4287. All methods define the attributes that the RFC mentions explicitly as possible attributes as top level parameters. All common or custom extensions are transported via the Attributes interface. The top level parameters carry a prefix that indicates if they are optional (o) or (r) required attributes. Optional attributes may be set to null, required attributes must not be null.

DefaultAtomContentHandler

This is an extension of the AtomContentHandler interface that adds another layer of convenience for generators of ATOM feeds. In contrast to the AtomContentHandler interface however it is not used for parsing. This makes life easier for the consumer of the feed, because only the methods on AtomContentHandler need to be implemented.

The use of the methods on DefaultAtomContentHandler is optional, the complete functionality can be achieved by calling methods on AtomContentHandler directly. The purpose of the convenience layer is to simplify the common use cases. In particular all convenience methods assume that no custom attributes to the ATOM format are required, so they leave of the Attributes parameter. There exist the following categories of convenience methods

atomXXX
these methods write the start and the end tag in one single call, without an enclosed text() element
startXXX
these methods simplify the generation of start tags by omitting the Attributes parameter
misc
there exist convenience methods to write text() elements in different formats.

AtomXMLReaderFactory

The AtomXMLReaderFactory is the main entry point for the API. It allows for the construction of implementations of the ATOM API interfaces that can be used by feed producers and feed consumers to build their environment.

Access to the service

The ATOM XML factory defines the Eclipse extension point AtomXMLReaderFactory.EXTENSION_POINT_ID. Clients can either look for any available extension of this extension point or directly look for the extension AtomXMLReaderFactory.DEFAULT_EXTENSION_ID.

Example how to access an implementation

final IExtensionPoint extPoint = extReg
        .getExtensionPoint(AtomXMLReaderFactory.EXTENSION_POINT_ID);
final IExtension ext = extPoint
        .getExtension(AtomXMLReaderFactory.DEFAULT_EXTENSION_ID);
final IConfigurationElement[] cfgElems = ext.getConfigurationElements();
final AtomXMLReaderFactory atomFct = (AtomXMLReaderFactory) cfgElems[0]
        .createExecutableExtension(AtomXMLReaderFactory.ATTR_CLASS);

Example how to access the registry

InitialContext initialcontext = new InitialContext();
IExtensionRegistry extReg = (IExtensionRegistry) initialcontext
                .lookup("services/extensionregistry/global");

Examples

These examples demonstrate how feeds can be produced, consumed and how both sides can be plugged together, e.g. to serialize or transform a feed.

Producers

In order to generate an ATOM feed we need to create a stream of SAX events that corresponds to the ATOM specification. In the JAXP API the generator of SAX events is abstracted by the XMLReader interface. Our feed producer therefore needs to implement this interface and can do so conveniently by extending the XMLFilterImpl class that is part of the JAXP framework. As a minimum the implementor needs to override the parse method and needs to generate SAX events on the attached content handler. XMLFilterImpl implements the ContentHandler interface and dispatches automatically to the attached content handler, so we can use this as the content handler.

Simple Feed Producer

Instead of directly generating SAX events we use the DefaultAtomContentHandler that allows to make human readable method calls to produce the events instead of generating the events directly. The implementation of DefaultAtomContentHandler translates these method calls into SAX events on a content handler that is attached to the DefaultAtomContentHandler. As discussed before, the target content handler is the this object, so as a first parsing step we attach this content handler to the DefaultAtomContentHandler. All subsequent calls on DefaultAtomContentHandler will then finally be translated into SAX events on the content handler that is attached to the XMLReader.

package com.ibm.wps.resolver.test;

import java.io.IOException;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.XMLFilterImpl;

import com.ibm.portal.resolver.atom.AtomXMLReaderFactory;
import com.ibm.portal.resolver.atom.DefaultAtomContentHandler;

/**
 * This example demonstrates how to produce a simple ATOM feed.
 * 
 * @author cleue
 *
 */
public class SimpleAtomFeed extends XMLFilterImpl {
        
        /**
         * handler that converts convenient method calls into ATOM events
         */
        protected final DefaultAtomContentHandler handler;

        public SimpleAtomFeed(final AtomXMLReaderFactory aFct) throws SAXException {
                handler = aFct.createAtomContentHandler();
        }
        
        /* (non-Javadoc)
         * @see org.xml.sax.XMLReader#parse(org.xml.sax.InputSource)
         */
        public void parse(final InputSource input) throws SAXException, IOException {
                // reroute the events via our own content handler
                handler.setContentHandler(this);
                
                // produce the events
                handler.startDocument();
                handler.startFeed();
                handler.atomTitle("Mein erster Feed");
                handler.atomAuthor("Carsten Leue");
                handler.atomUpdated(System.currentTimeMillis());
                handler.atomId("urn:test");
                
                handler.startEntry();
                handler.atomTitle("yet another title");
                handler.atomLink("http://www.ibm.com", null, null, null, null, null);
                handler.atomId("urn:test:entry");
                handler.atomUpdated(System.currentTimeMillis());
                handler.atomSummary("some text");               
                handler.endEntry();
                
                handler.endFeed();
                handler.endDocument();
        }
        
}

Sophisticated Feed Producer

In a more sophisticated example we show how standard JAXP mechanisms can be reused to merge existing XML documents into the ATOM feed. This can be very useful if such documents (or more generally any JAXP Source object) become the content of an atom:content element. The example shows this for the special case of a SAXSource object. For a generic source object, an identity transform needs to be applied to transform the generic source onto a SAXResult attached to the inner handler in the example.

For our example we reuse the system parser object in form of an XMLReader to parse an XML document. The parsing result is a series of SAX events that the parser throws onto a ContentHandler. The DefaultAtomContentHandler provides access to a special ContentHandler implementation that merges all events thrown onto it into the target XML stream. This merging process is very efficient, because it basically only consists of routing all events except the start/endDocument events to the target stream.

Note that the same parser object can be reused to embed an arbitrary number of inner content elements.

package com.ibm.wps.resolver.test;

import java.io.IOException;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.helpers.XMLFilterImpl;
import org.xml.sax.helpers.XMLReaderFactory;

import com.ibm.portal.resolver.atom.AtomContentHandler;
import com.ibm.portal.resolver.atom.AtomXMLReader;
import com.ibm.portal.resolver.atom.AtomXMLReaderFactory;
import com.ibm.portal.resolver.atom.DefaultAtomContentHandler;

/**
 * <p>
 * Somewhat more spohisticated feed that demonstrates how custom attributes are
 * serialized and how content from an external source can be embedded into the stream.
 * </p>
 * 
 * <p>
 * Notice that the feed implements the AtomXMLReader interface, implementing the #setAtomContentHandler(AtomContentHandler)
 * method. This allows the feed to be caugth on the AtomContentHandler interface
 * instead of the ContentHandler interface.
 * </p>

 * 
 * @author cleue
 *
 */
public class ExtensiveAtomFeed extends XMLFilterImpl implements AtomXMLReader {

        /**
         * handler that converts convenient method calls into ATOM events
         */
        protected final DefaultAtomContentHandler handler;
        
        /**
         * holder for custom attributes on the ATOM entries
         */
        protected final AttributesImpl attrs = new AttributesImpl();

        /**
         * parser used to embed XHTML content from an external source in the feed
         */
        protected final XMLReader parser;

        public ExtensiveAtomFeed(final AtomXMLReaderFactory aFct) throws SAXException {
                parser = XMLReaderFactory.createXMLReader();
                handler = aFct.createAtomContentHandler();
        }

        /* (non-Javadoc)
         * @see org.xml.sax.XMLReader#parse(org.xml.sax.InputSource)
         */
        public void parse(final InputSource input) throws SAXException, IOException {
                // reroute the event
                handler.setContentHandler(this);
                
                handler.startDocument();
                handler.startFeed();
                handler.atomTitle("dive into mark");
                handler.startSubtitle("html");
                handler.text("A <em>lot</em> of effort went into making this effortless");
                handler.endSubtitle();
                handler.atomUpdated(System.currentTimeMillis());
                handler.atomId("tag:example.org,2003:3");
                handler.atomLink("http://example.org", "alternate", "text/html", "en",
                                null, null);
                handler.atomLink("http://example.org/feed.atom", "self",
                                "application/atom+xml", null, null, null);
                handler.atomRights("Rights");
                handler.atomGenerator("http://www.example.com/", "1.0", "Example Toolkit");
                handler.startEntry();
                handler.atomTitle("Atom draft-07 snapshot");
                handler.atomLink("http://example.org/2005/04/02/atom", "alternate",
                                "text/html", null, null, null);
                handler.atomLink("http://example.org/audio/ph34r_my_podcast.mp3",
                                "enclosure", "audio/mpeg", null, null, "1337");
                handler.atomId("tag:example.org,2003:3.2397");
                handler.atomUpdated(System.currentTimeMillis());
                handler.atomPublished(System.currentTimeMillis());
                handler.startAuthor();
                handler.atomName("Mark Pilgrim");
                handler.atomURI("http://example.org/");
                handler.atomEMail("f8dy@example.com");
                handler.endAuthor();
                handler.atomContributor("Sam Ruby");
                handler.atomContributor("Joe Gregorio");
        
                attrs.addAttribute("", "xml:lang", "xml:lang", "NMTOKEN", "en");
                attrs.addAttribute("", "xml:base", "xml:base", "NMTOKEN",
                                "http://diveintomark.org/");
                handler.startContent("xhtml", null, attrs);
                // now fetch this content from some external source
                parser.setContentHandler(handler.getInnerHandler());
                parser.parse(new InputSource(getClass().getResourceAsStream(
                        "content.xhtml")));
                handler.endContent();
                handler.endEntry();
                handler.endFeed();
                handler.endDocument();
        }

        /* (non-Javadoc)
         * @see com.ibm.portal.resolver.atom.AtomXMLReader#setAtomContentHandler(com.ibm.portal.resolver.atom.AtomContentHandler)
         */
        public void setAtomContentHandler(final AtomContentHandler aHandler) {
                // replace the atom handler on the delegate
                handler.setAtomContentHandler(aHandler);
        }

}

Consumers

ATOM parsing is done by the AtomXMLReader that is instantiated via the AtomXMLReaderFactory. This reader produced method callbacks on the AtomContentHandler interface for each XML element in the ATOM namespace. Clients of the parser that want to interpret the feed need to implement AtomContentHandler and further interpret the method calls.

In many cases clients will only be interested in a small subset of all ATOM events. For this purpose they can subclass EmptyAtomContentHandler and only override the methods they are interested in.

public class TestAtomHandler extends EmptyAtomContentHandler {

        public void endEntry() throws SAXException {
                System.out.println("endEntry"); }

        public void startEntry(Attributes attrs) throws SAXException {
                System.out.println("startEntry");
        }

        public void startLink(String rHref, String oRel, String oType, String oHrefLang, String oTitle, String oLength, Attributes attrs) throws SAXException {
                System.out.println("startLink href = " + rHref);
        }

        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
                System.out.println("startElement uri = " + uri);
        }

}

Glueing it together

Feed producers and consumers can be glued together in many different ways, only restricted by the JAXP APIs. These possibilities include the serialization of feeds into XML markup, transformation into different formats or ATOM flavors using XSLT, merging or chaining feeds, etc. The following examples illustrate the main usecases.

Serialization of a feed into XML markup

In JAXP the serialization of XML into markup is done by applying the identity transformation to a Source object, routing the events onto a javax.xml.transform.stream.StreamResult object. The same mechanism is used to serialize ATOM feeds.

Note that the InputSource that is passed as a parameter to SAXSource can be used to carry parametrization on to the parse method of the feed provider.

Ir the transformer used in the example was not the identity transform but a more sophisticated XSLT, then the same code could be used to transform the ATOM feed on-the-fly before serializing it.

final Transformer trfrm = TransformerFactory.newInstance().newTransformer();
                
Source src = new SAXSource(new SimpleAtomFeed(xmlFct), new InputSource());
Result res = new StreamResult(System.out);
                
trfrm.transform(src, res);

Interpretation of an XML markup stream as ATOM feed

The interpretation of a feed is done by an implementation of AtomContentHandler. In this example this is the TestAtomHandler class. The ATOM API framework takes care of the production of method callbacks onto this handler that then can be interpreted easily.

final AtomXMLReader rdr = xmlFct.createXMLReader();
final AtomContentHandler handler = new TestAtomHandler();
                
final InputSource in = new InputSource(getClass().getResourceAsStream("sampleFeed.xml"));
rdr.setAtomContentHandler(handler);
rdr.parse(in);

or

final Transformer trfrm = TransformerFactory.newInstance().newTransformer();
final AtomContentHandler handler = new TestAtomHandler();

final Source src = new StreamSource(getClass().getResourceAsStream("sampleFeed.xml"));
final Result res = new SAXResult(xmlFct.createContentHandler(handler));
                
trfrm.transform(src, res);