 | Level: Intermediate James Snell (jasnell@us.ibm.com), Software Engineer, IBM
12 Dec 2006
Earlier articles in this series provided an overview of the Atom Publishing Protocol and described the various ways it is being utilized in real world applications. This article begins to demonstrate how you can start to implement Atom-enabled applications using a new open-source project, called Abdera, currently under incubation at the Apache Software Foundation.
This discussion assumes that you have read the Atom Format specification and that you are familiar with syndication in general (see Resources for links). All of the examples are provided in Java code and a sample Eclipse project containing all of the code samples is provided for download.
Getting started
Before you begin, ensure that you have the current
version of Apache Abdera installed. The source is available in the Apache
Subversion repository at hhttp://svn.apache.org/repos/asf/incubator/abdera/java/branches/0.2.1-incubating/.
To retrieve the source, first make sure you have the subversion client and
issue the command:
> svn co http://svn.apache.org/repos/asf/incubator/abdera/java/branches/0.2.1-incubating/
|
Once you download the source image, you can build Abdera using
Ant version 1.6.5 or higher.
> cd trunk
> ant -f build/build.xml dist |
After the build completes, the compiled jars and dependencies are
located in a newly created "dist" directory. To run the examples, you
need to have the following jars in your classpath. Not all of the examples
require all of the jars in the dependencies list:
Table 1. Jars required to run the examples
| Abdera (dist) | Dependencies (dist/lib) |
|---|
- abdera.client.0.2.0-incubating.jar
- abdera.core.0.2.0-incubating.jar
- abdera.parser.0.2.0-incubating.jar
- abdera.protocol.0.2.0-incubating.jar
|
- axiom-api-1.0.jar
- axiom-impl-1.0.jar
- commons-codec-1.3.jar
- commons-httpclient-3.0.1.jar
- commons-logging-1.0.4.jar
- geronimo-activation_1.0.2_spec-1.1.jar
- log4j-1.2.12.jar
- stax-1.1.2-dev.jar
- stax-api-1.0.jar
- jaxen-1.1-beta-7.jar
|
Getting started
The Abdera project consists of a collection of individual modules, each
of which is listed alphabetically in the following table. Of these, the core,
dependencies and parser modules are the most important and most frequently used:
Table 2. The Apache Abdera Project Modules
| Module | Description | Dependencies |
|---|
| build | Ant build for the entire project | Apache Ant 1.6.5+ | | client | Atom Publishing Protocol Client | core, parser, protocol, commons-codec-1.3.jar, commons-httpclient-3.0.1.jar | | core | Feed Object Model interfaces | Java Activation Framework | | dependencies | Common dependencies for all modules |
| | extensions | Feed syntax and protocol extensions | core, protocol, json-1.0.jar | | parser | StAX and Axiom-based default Feed Object Model (FOM) implementation | core, axiom-api-1.0.jar, axiom-impl-1.0.jar, stax-1.1.2-dev.jar, stax-api-1.0.jar, jaxen-1.1-beta-7.jar, commons-logging-1.0.4.jar, log4j-1.2.12.jar, wstx-asl-2.0.5.jar | | protocol | Common Atom Publishing Protocol code | core, parser | | security | XML Digital Signature and Encryption support | core, parser, xmlsec-1.3.0.jar, Bouncy Castle JCE implementation | | server | Atom Publishing Protocol server implementation | core, parser, protocol, Servlet API |
The core module, whose packages are listed in the following table, defines what Abdera
calls the "Feed Object Model", a set of interfaces used for parsing,
creating and manipulating Atom documents that is modeled after the Atom
Syndication Format specification:
Table 3. The packages of the Abdera "core" module
| Package | Description |
|---|
| org.apache.abdera | Main package containing a single "Abdera" object | | org.apache.abdera.factory | Defines the Factory interface that creates new Feed Object Model object instances | | org.apache.abdera.filter | Defines interfaces to filter Atom documents during the parse | | org.apache.abdera.model | Defines the primary interfaces to work with Atom Feed and Entry documents. The model also includes support to work with Atom Publishing Protocol Service and Category documents. | | org.apache.abdera.parser | Defines the Parser interface that creates new Feed Object Model object instances from XML documents | | org.apache.abdera.util | Provides a variety of utility classes targeted primarily at developers looking to extend or replace Abdera's default parser and factory implementations | | org.apache.abdera.writer | Defines the Writer interfaces used to serialize Feed Object Model object instances | | org.apache.abdera.xpath | Defines the interfaces for using XPath to navigate the Feed Object Model |
After you download and build the Abdera source, take a moment, if necessary, to familiarize yourself with the API by browsing through the Javadoc documentation that the build generates (located in the $ABDERA_HOME/dist/docs directory created).
Creating entries and feeds
The two primary functions of Abdera's Feed Object Model are to make it
easy to both produce and consume Atom Feed and Entry documents.
To create an Atom document starts by acquiring an instance of
org.apache.abdera.factory.Factory and setting the properties of the feed
or entry. Listing 1 illustrates the creation of a simple Atom Entry Document:
Listing 1. Creating a simple Atom Entry Document
Abdera abdera = new Abdera();
Factory factory = abdera.getFactory();
Entry entry = factory.newEntry();
entry.setId("http://example.org/foo/entry");
entry.setUpdated(new java.util.Date());
entry.setTitle("This is an entry");
entry.setContentAsXhtml("This <b>is</b> <i>markup</i>");
entry.addAuthor("James");
entry.addLink("http://www.example.org");
entry.writeTo(System.out);
|
This example illustrates all of the key characteristics of the Feed Object Model.
- First, you create an instance of org.apache.abdera.Abdera, which
provides a top-level entry point to initialize and access various
key subcomponents like the Abdera Parser and Factory.
- Second, you acquire an instance of org.apache.abdera.factory.Factory.
This is the interface that bootstraps the creation of all Feed Object
Model objects.
- Third, you create a new instance of org.apache.abdera.model.Entry
using the Factory. The two most frequently used methods on the Factory
interface are the newEntry() and newFeed() methods.
- Finally, you serialize the Atom document out to an OutputStream using
the default Abdera Writer.
The output from Listing 1 is shown in Listing 2. Note that the
XML namespace declarations, date formats, and proper serialization of
atom:content elements is handled automatically by the Feed Object Model
implementation. However, the serialized output is not that easy to read
due to the lack of line breaks and indentation:
Listing 2. The created Atom Entry Document
<entry xmlns="http://www.w3.org/2005/Atom"><id>http://example.org/foo/entry</id><updated>
2006-09-04T19:27:01.068Z</updated><title type="text">This is an entry</title><content
type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml">This <b>is</b>
<i>markup</i></div></content>
<author><name>James</name></author><link href="http://www.example.org"/></entry>
|
Serializing without whitespace and line breaks is an optimization
designed to make the transfer of the documents more efficient. As an
alternative, you can use a prettyxml writer to insert line
breaks and indents:
Listing 3. Serializing with the prettyxml writer
Writer writer = abdera.getWriterFactory().getWriter("prettyxml");
writer.writeTo(entry, System.out);
|
The result of which is output that is a bit easier on the eyes:
Listing 4. Pretty XML output
<?xml version='1.0' encoding='UTF-8'?>
<entry xmlns="http://www.w3.org/2005/Atom">
<id>http://example.org/foo/entry</id>
<updated>2006-09-04T19:28:58.237Z</updated>
<title type="text">This is an entry</title>
<content type="xhtml">
<div xmlns="http://www.w3.org/1999/xhtml">This <b>is</b> <i>markup</i></div>
</content>
<author>
<name>James</name>
</author>
<link href="http://www.example.org"/>
</entry>
|
Be careful using the prettyxml writer as it can cause a
number of unintended side-effects. For instance, using the writer to
serialize an Atom Feed or Entry that contains an XML Digital Signature might
cause the signature to become invalid.
Listing 5 illustrates the creation of an Atom Feed Document.
As one might expect, the process is identical to creating an Atom Entry Document:
Listing 5. Creating a simple Atom Feed Document
Abdera abdera = new Abdera();
Factory factory = abdera.getFactory();
Feed feed = factory.newFeed();
feed.setId("http://example.org/foo");
feed.setUpdated(new java.util.Date());
feed.setTitle("This is a feed");
feed.addLink("http://www.example.org");
feed.addLink("http://www.example.org/foo", "self");
Entry entry = factory.newEntry();
entry.setId("http://example.org/foo/entry");
entry.setUpdated(new java.util.Date());
entry.setTitle("This is an entry");
entry.setContentAsXhtml("This <b>is</b> <i>markup</i>");
entry.addAuthor("James");
entry.addLink("http://www.example.org");
feed.addEntry(entry);
Writer writer = abdera.getWriterFactory().getWriter("prettyxml");
writer.writeTo(feed, System.out);
|
Listing 6. The created Atom Feed Document
<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns="http://www.w3.org/2005/Atom">
<id>http://example.org/foo</id>
<updated>2006-09-04T19:31:45.286Z</updated>
<title type="text">This is a feed</title>
<link href="http://www.example.org"/>
<link href="http://www.example.org/foo" rel="self"/>
<entry>
<id>http://example.org/foo/entry</id>
<updated>2006-09-04T19:31:44.695Z</updated>
<title type="text">This is an entry</title>
<content type="xhtml">
<div xmlns="http://www.w3.org/1999/xhtml">This <b>is</b>
<i>markup</i></div>
</content>
<author>
<name>James</name>
</author>
<link href="http://www.example.org"/>
</entry>
</feed>
|
Abdera's Feed Object Model interfaces follow the Atom Syndication Format
schema very closely, making it natural for developers who are familiar with the
RFC 4287 specification to produce valid Atom documents. However, it is
important to point out that the implementation does not perform any
validation of the input. For instance, RFC 4287 requires that all
atom:entry and atom:feed elements contain exactly one atom:id element
whose value is a normalized IRI. Abdera, however, does not throw an
exception if a developer tries to create and serialize an entry or feed
that does not contain an atom:id or that contains multiple atom:ids.
Developers are responsible for ensuring that the documents they produce
are valid. With the FeedValidator, you can check the validity of Atom documents.
Adding extensions to documents
The Feed Object Model includes the ability to work with extensions to
the Atom format without requiring developers to first write modules that
implement those extensions. For example, Listing 7 illustrates how
to add an element "foo" in the "tag:example.org,2006:/namespaces" namespace
to an entry. Listing 8 illustrates the serialized result:
Listing 7. Adding an extension element
QName extensionQName =
new QName("tag:example.org,2006:/namespaces", "foo", "f");
Element element = entry.addExtension(extensionQName);
element.setAttributeValue("foo", "bar");
element.setText("fooBar");
|
Listing 8. An Entry Document with an extension element
<?xml version='1.0' encoding='UTF-8'?>
<entry xmlns="http://www.w3.org/2005/Atom">
<id>http://example.org/foo/entry</id>
<updated>2006-09-04T19:34:10.322Z</updated>
<title type="text">This is an entry</title>
<content type="xhtml">
<div xmlns="http://www.w3.org/1999/xhtml">This is markup</div>
</content>
<author>
<name>James</name>
</author>
<link href="http://www.example.org"/>
<f:foo xmlns:f="tag:example.org,2006:/namespaces" foo="bar">fooBar</f:foo>
</entry>
|
Listing 9 shows a more practical example by adding elements from the
OpenSearch version 1.1 specification to a feed:
Listing 9. Adding OpenSearch version 1.1 elements to an Atom Feed
final String OSURI = "http://a9.com/-/spec/opensearch/1.1/";
final QName TOTALRESULTS = new QName(OSURI, "totalResults");
final QName STARTINDEX = new QName(OSURI, "startIndex");
final QName ITEMSPERPAGE = new QName(OSURI, "itemsPerPage");
final QName QUERY = new QName(OSURI, "Query");
Feed feed = factory.newFeed();
feed.addExtension(TOTALRESULTS).setText("4230000");
feed.addExtension(STARTINDEX).setText("21");
feed.addExtension(ITEMSPERPAGE).setText("10");
Element query = feed.addExtension(QUERY);
query.setAttributeValue("role", "request");
query.setAttributeValue("searchTerms", "New York History");
query.setAttributeValue("startPage", "1");
|
Flexible content options
A key differentiator of the Atom format relative to other syndication
formats is its support for a broad and flexible range of content types.
Atom entries can contain plain text, escaped HTML, well-formed XHTML,
XML, any arbitrary character-based data, and Base64-encoded content.
It is also possible for an Atom entry to reference content by URI.
The Feed Object Model provides a simple means to work with each of
these options. Listings 10 and 11 illustrate how to set the content of an
entry as plain text:
Listing 10. Setting content as plain text
Entry entry = factory.newEntry();
entry.setContent("This <b>is</b> not <i>markup</i>");
|
Listing 11. A plain text content element
<content type="text">This <b>is</b>not <i>markup</i></content>
|
When working with plain text content (Listing 10) and escaped HTML
(Listing 12), it is important to note that the serialized content elements
will be nearly identical (Listings 11 and 13). It is very important that
processors of the generated feeds pay proper attention to the value of the
content type attribute. When displaying the content in a feed reader,
the plain text value in Listing 11 must be rendered as the unformatted
literal string "This <b>is</b> not <i>markup</i>", while the
escaped HTML in Listing 13 is rendered with formatting: "This is
markup".
Listing 12. Setting content as escaped HTML
Entry entry = factory.newEntry();
entry.setContentAsHtml("This <b>is</b> <i>markup</i>");
|
Listing 13. An HTML content element
<content type="html">This <b>is</b>
<i>markup</i></content>
|
The preferred alternative to escaped HTML is for feed publishers to use
well-formed XHTML fragments in their entries using the Entry interface's
setContentAsXhtml method as shown in Listing 14:
Listing 14. Setting content as XHTML
Entry entry = factory.newEntry();
entry.setContentAsXhtml("This <b>is</b> <i>markup</i>");
|
Listing 15. An XHTML content element
<content type="xhtml">
<div xmlns="http://www.w3.org/1999/xhtml">
This <b>is</b> <i>markup</i>
</div>
</content>
|
Abdera parses the XHTML input passed in to the setContextAsXHTML
method to ensure that it is well-formed, but it does not check to make sure
it is valid XHTML. Similarly, it is possible to set the content of the
entry to any arbitrary, well-formed XML as shown in Listings 16 and 17:
Listing 16. Setting content as XML
Entry entry = factory.newEntry();
entry.setContent("<a><b><c><d>foo</d></c></b></a>", Content.Type.XML);
|
Listing 17. An XML content element
<content type="application/xml">
<a>
<b>
<c>
<d>foo</d>
</c>
</b>
</a>
</content>
|
At times, simply marking character-based content as
type="text" or type="html" is not enough. For instance, it is possible for
an Atom entry to contain an entire HTML document. When type="html", however,
the escaped markup contained in the content element must be appropriate for
containing within an HTML DIV element. To include the entire document, the
type attribute must specify the HTML media-type, as shown in Listings 18 and 19:
Listing 18. Using text-based media content
Entry entry = factory.newEntry();
entry.setContent(
"<html>
<head>
<title>Foo</title>
</head>
<body>This is new</body></html>",
"text/html");
|
Listing 19: A text-based media content element
<content type="text/html">
<html>
<head>
<title>Foo</title>
</head>
<body>This is new</body> </html>
</content>
|
If a feed publisher wishes to syndicate non-character based content,
it must Base64-encode the content and use the content type attribute to specify
the media type of the encoded binary data. To achieve this in Abdera, use the Java Activation Framework DataHandler interface. When a DataHandler
is passed in as the content of an entry, Abdera automatically
Base64-encodes the content:
Listing 20. Setting Base64-encoded binary content
Entry entry = factory.newEntry();
URL url = new URL("file:/home/jasnell/mozilla-firefox.png");
URLDataSource ds = new URLDataSource(url);
DataHandler dh = new DataHandler(ds);
entry.setContent(dh);
|
Listing 21. A content element with Base64-encoded data
<content type="image/png">iVBORw0KGgoAAAANSUhEUgAAA...</content>
|
Because of the natural inefficiencies of Base64 encoding and because
there are times when including content directly with the Atom entry is
not appropriate (for example, when syndicating streaming audio or video), it is
possible to reference content by URI as seen in Listings 22 and 23:
Listing 22. Setting the src attribute on the content element
Entry entry = factory.newEntry();
URI uri = new URI("http://example.org");
entry.setContent(uri.toString(), "application/xhtml+xml");
|
Listing 23. A content element using the src attribute
<content type="application/xhtml+xml" src="http://example.org" />
|
Parsing Feed and Entry Documents
Abdera's second primary function is parsing Atom documents. This begins
by acquiring an instance of a Parser and passing it an InputStream or
Reader for the document to be parsed:
Listing 24. Parsing an Atom Feed Document
Abdera abdera = new Abdera();
Parser parser = abdera.getParser();
URL url = Listing7.class.getResource("/example.xml");
Document<Feed> feed_doc = parser.parse(url.openStream());
Feed feed = feed_doc.getRoot();
|
Because Abdera's default parser implementation uses a pull-oriented model
to parse XML, calling the parse method on the Parser object does not
fully consume the InputStream or Reader passed in to the method. The
stream is consumed incrementally as the information in the document
is requested. As one might expect, calling methods such as writeTo, clone, and so forth, causes the stream to be fully consumed. This might not be true of
non-default implementations of the Parser interface.
Once the Document is parsed, developers can access the content of
the feed using either the methods of the Feed Object Model API or by using XPath:
Listing 25. Navigating the Feed Object Model
System.out.println(feed.getBaseUri());
System.out.println(feed.getLanguage());
System.out.println(feed.getId());
System.out.println(feed.getTitle());
System.out.println(feed.getTitleType());
System.out.println(feed.getUpdated());
System.out.println(feed.getAlternateLinkResolvedHref());
System.out.println(feed.getSelfLinkResolvedHref());
List<Entry> entries = feed.getEntries();
for (Entry entry : entries) {
System.out.println(entry.getId());
System.out.println(entry.getTitle());
System.out.println(entry.getTitleType());
System.out.println(entry.getUpdated());
System.out.println(entry.getAlternateLinkResolvedHref());
System.out.println(entry.getEnclosureLinkResolvedHref());
System.out.println(entry.getContent());
System.out.println(entry.getContentType());
Element element = entry.getExtension(
new QName("tag:example.org,2006:/namespaces", "foo"));
System.out.println(element.getText());
}
|
As an alternative to walking through and calling the various get methods
on the feed and entry objects, developers can use XPath statements to navigate
the parsed document as illustrated in Listing 26:
Listing 26. Navigating the Feed Object Model with XPath
Abdera abdera = new Abdera();
XPath xpath = abdera.getXPath();
System.out.println(xpath.valueOf("/a:feed/a:id", feed));
System.out.println(xpath.valueOf("/a:feed/@xml:base", feed));
System.out.println(xpath.valueOf("/a:feed/@xml:lang", feed));
System.out.println(xpath.valueOf("/a:feed/a:title", feed));
System.out.println(xpath.valueOf("/a:feed/a:title/@type", feed));
System.out.println(xpath.valueOf("/a:feed/a:updated", feed));
System.out.println(xpath.valueOf("/a:feed/a:link[not(@rel)]/@href", feed));
System.out.println(xpath.valueOf("/a:feed/a:link[@rel='self']/@href", feed));
|
In addition to the ability to select individual nodes, groups of nodes,
text, boolean, and numeric values, standard XPath features, such as functions
and axes, are also supported:
Listing 27. Using standard XPath functions
System.out.println(xpath.valueOf("count(//a:entry)", feed) + " entry");
System.out.println(xpath.valueOf("name(//a:entry/ancestor::*)", feed));
|
Selecting nodes using XPath returns Feed Object Model objects that
can themselves be evaluated using XPath as illustrated in Listing 28.
Note that instead of passing the feed in as the second argument of the
valueOf function, as in the previous examples, the Div selected from the
selectSingleNode method is passed in. The XPath is evaluated relative
to whatever Feed Object Model element is passed in to the method:
Listing 28. Evaluating XPath statements against individual FOM objects
Div div = (Div) xpath.selectSingleNode(
"//a:entry/a:content/x:div", feed, namespaces);
System.out.println(xpath.valueOf("namespace-uri()", div));
System.out.println(xpath.valueOf("x:p", div, namespaces));
|
Using XPath to navigate Abdera documents provides a number of advantages,
especially to developers who work with extensions to the core format. For
instance, with the xpath.booleanValueOf method, you can quickly determine
whether or not a given feed or entry contains a particular extension element
and it is generally more efficient than walking through the structure of the
document using the various get methods and iterators.
Another important feature of the Feed Object Model is the ability to
apply XSLT transforms to parsed documents and elements using the standard
Java Transformation APIs. As with the XPath support, it is possible to
apply XSLT transforms to any part of a parsed document:
Listing 29. Transforming Abdera documents with XSLT
Abdera abdera = new Abdera();
Parser parser = abdera.getParser();
URL url = Listing8.class.getResource("/example.xml");
URL xslt = Listing8.class.getResource("/example.xslt");
Document<Feed> feed_doc = parser.parse(url.openStream());
Document<Element> xslt_doc = parser.parse(xslt.openStream());
Source feed_source = new AbderaSource(feed_doc);
Source xslt_source = new AbderaSource(xslt_doc);
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer(xslt_source);
Result result = new StreamResult(System.out);
transformer.transform(feed_source, result);
|
The results of the transformation can be sent to any Result implementation
supported by the Java Transformer API. In the example above, the results are simply
going to stdout. It is possible to send the results directly
to the Abdera parser to produce another document.
Atom Publishing support
When the Abdera project was launched, the stated goal of the project was
to provide functionally complete implementations of both the Atom Syndication
Format and the Atom Publishing Protocol. Thus far, this series has provided a fair
amount of coverage of the support for the Syndication Format and has illustrated
how to produce and consume feeds. The next installment
will cover the Atom Publishing Protocol client and server support that is
still currently being implemented. Listing 30 illustrates a simple example
of Abdera's APP client:
Listing 30. A simple Atom Publishing Protocol client
Abdera abdera = new Abdera();
Client client = new CommonsClient(abdera);
RequestOptions options = client.getDefaultRequestOptions();
options.setIfModifiedSince(new java.util.Date());
options.setNoCache(true);
ClientResponse response = client.get("http://example.org/entries/1", options);
System.out.println(response.getStatus());
System.out.println(response.getStatusText());
System.out.println(response.getEntityTag());
System.out.println(response.getLastModified());
System.out.println(response.getContentType());
response.release();
|
Abdera and thread safety
Before wrapping up, you should know that most of the core Abdera
components, such as the Abdera, Factory, and Parser objects, are thread-safe
and stateless, making it possible to share instances efficiently across
multiple threads. However, the Feed Object Model (FOM) objects they create are not
thread-safe. For a variety of reasons, none of the FOM objects are
synchronized. If you intend to allow multiple threads to access or
modify Entry and Feed objects, you need to provide your own synchronization:
Listing 31. Non-synchronized concurrent modification to FOM Objects leads to unpredictable results
final Abdera abdera = new Abdera();
final Factory factory = abdera.getFactory();
final Entry entry = factory.newEntry();
final Thread[] threads = new Thread[100];
for (int n = 0; n < threads.length; n++) {
final int i = n;
threads[n] = new Thread(
new Runnable() {
public void run() {
try {
int s = (new java.util.Random(
System.currentTimeMillis())).nextInt(10);
Thread.sleep(s);
} catch (InterruptedException e) {}
try {
IRI id = factory.newID();
id.setValue("urn:thread:" + i);
entry.setIdElement(id);
System.out.println(i + "\t" + entry); // unpredictable results
} catch (Exception e) {
e.printStackTrace();
}
}
}
);
}
for (Thread t : threads) t.start();
|
Listing 32. A sample of the invalid output from Listing 31
85 urn:thread:85
86
urn:thread:
99
91 urn:thread:91
org.apache.axiom.om.OMException
at org.apache.axiom.om.impl.llom.OMNodeImpl.insertSiblingBefore(OMNodeImpl.java:232)
at org.apache.abdera.parser.stax.FOMElement._setChild(Unknown Source)
at org.apache.abdera.parser.stax.FOMEntry.setIdElement(Unknown Source)
at abdera.examples.atom.Listing11$1.run(Listing11.java:29)
at java.lang.Thread.run(Thread.java:788)
87 urn:thread:87
88 urn:thread:88
|
Wrapping the entry modifications in a synchronization block is enough to
resolve the issues. It is, however, recommended that FOM objects only ever
be created and used by a single thread.
Looking forward
In this article, you looked at the core features of the Apache Abdera project
including its Feed Object Model, XPath and XSLT support, extension handling
and incremental parsing model. The next installment of this series introduces and demonstrates the support for the Atom Publishing Protocol currently being developed.
Download | Description | Name | Size | Download method |
|---|
| Sample Eclipse Project with the example code | x-atompp3-code.zip | 39KB | HTTP |
|---|
Resources Learn
Get products and technologies
-
IBM trial software: Build your next development project with trial software available for download directly from developerWorks.
Discuss
About the author  | 
|  | James Snell is a member of IBM's WebAhead development lab focusing on the prototype development of pre-emerging software technologies and standards for IBM's own use. His research and development interests cover a broad range of current technology trends including Atom, AJAX, REST, Open Source, personal publishing systems, semantic Web and situational applications. He is an active committer to the Apache Abdera project, currently in incubation and tasked with the creation of a high-performance, functionally complete implementation of the Atom Syndication Format and Atom Publishing Protocol standards. |
Rate this page
|  |