| |||||||||
PREV PACKAGE NEXT PACKAGE | FRAMES NO FRAMES |
See:
Description
Interface Summary | |
---|---|
AtomContentHandler | http://tools.ietf.org/html/rfc4287 RFC 4287 |
AtomThreadingContentHandler | http://www.ietf.org/rfc/rfc4685.txt ATOM threading extension (RFC 4685) |
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 |
DefaultAtomContentHandler | Extension of the AtomContentHandler interface that provides convenience methods to more easily generate ATOM 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. |
ResettableAtomThreadingContentHandler | Extension of AtomThreadingContentHandler that allows to builds processing chains of these handlers. |
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 |
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 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
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
.
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
text()
elementtext()
elements in different formats.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.
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
.
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);
InitialContext initialcontext = new InitialContext(); IExtensionRegistry extReg = (IExtensionRegistry) initialcontext .lookup("services/extensionregistry/global");
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.
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.
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(); } }
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("Copyright (c) 2003, Mark Pilgrim"); 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); } }
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); } }
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.
In JAXP the serialization of XML into markup is done by applying the identity transformation to a Source object, routing the events onto a 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);
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);
| |||||||||
PREV PACKAGE NEXT PACKAGE | FRAMES NO FRAMES |