This article assumes that you are at least marginally familiar with the Atom syndication format, and with HTML concepts such as forms. The examples use the Java version of Apache Abdera, so Java is helpful if you want to use the specific examples, but the concepts apply to implementations in any language, and even manual, non-API assisted applications.
The first thing to do is prepare your system to deal with Atom. To do that, you'll need an application server that supports the Atom Publishing Protocol (APP). Roller is great, and even runs the blogs here at developerWorks, but the installation manual is 27 pages long. Fortunately, Dave Johnson has put all the pieces together in the Blogapps server, available at: https://blogapps.dev.java.net/servlets/ProjectDocumentList?folderID=4598&expandFolder=4598&folderID=0. To install it, simply uncompress it into a directory with no spaces in the directory name. Set the CATALINA_HOME environment variable and start the server (see Listing 1).
Listing 1. Start the server
set CATALINA_HOME=c:\sw\blogapps-server-1.0.4
cd %CATALINA_HOME%\bin
startup
|
Use your own location, of course!
To run the example application, you'll need a servlet-capable server. Blogapps includes Tomcat, so you can use that. If you choose to use a separate instance, you'll need to alter the server.xml file to prevent port conflicts.
The last step is to install the Apache Abdera package (see Resources for download), used to make the APP easier to use. Uncompress it and add all of the jar files in the main and lib directories to the CLASSPATH.
Now you're ready to go.
This article shows you how to create a media collection such as the one shown in Figure 1.
Figure 1. The finished product
To do that, you will create a servlet that uses the Atom Publishing Protocol to add media resources to an Atom collection, which is part of a workspace. To add each resource, you'll use a POST request that sends the file to a URI designated for that collection.
When you do that, the server creates a corresponding media-link entry that refers to it. You can then extract the information about all of those items in an Atom feed, which you can then use to display the information on a Web page.
The Atom protocol also makes use of the other HTTP methods. You'll use GET to query information, DELETE to delete it (of course) and PUT to edit existing information (if possible).
Let's start with the service document.
The service document shows what workspaces and collections are available. For example, if you retrieve the Blogapps service document from: http://localhost:8080/roller/app, you will see the following (see Listing 2).
Listing 2. The service document
<?xml version="1.0" encoding="UTF-8"?>
<app:service xmlns:app="http://purl.org/atom/app#">
<app:workspace>
<atom:title xmlns:atom="http://www.w3.org/2005/atom"
>AdminBlog</atom:title>
<app:collection href=
"http://localhost:8080/roller/app/adminblog/entries">
<atom:title xmlns:atom="http://www.w3.org/2005/atom"
>Weblog Entries</atom:title>
<app:categories app:fixed="yes" app:scheme=
"http://localhost:8080/roller/adminblog/">
<atom:category xmlns:atom="http://www.w3.org/2005/atom"
atom:term="/General" atom:label="General" />
...
</app:categories>
<app:accept>entry</app:accept>
</app:collection>
<app:collection href=
"http://localhost:8080/roller/app/adminblog/resources">
<atom:title xmlns:atom="http://www.w3.org/2005/atom"
>Media Files</atom:title>
<app:accept>image/*</app:accept>
</app:collection>
</app:workspace>
<app:workspace>
<atom:title xmlns:atom=
"http://www.w3.org/2005/atom">main</atom:title>
<app:collection href=
"http://localhost:8080/roller/app/main/entries">
<atom:title xmlns:atom="http://www.w3.org/2005/atom"
>Weblog Entries</atom:title>
<app:categories app:fixed="yes" app:scheme=
"http://localhost:8080/roller/main/">
<atom:category xmlns:atom="http://www.w3.org/2005/atom"
atom:term="/General" atom:label="General" />
...
</app:categories>
<app:accept>entry</app:accept>
</app:collection>
<app:collection href=
"http://localhost:8080/roller/app/main/resources">
<atom:title xmlns:atom="http://www.w3.org/2005/atom"
>Media Files</atom:title>
<app:accept>image/*</app:accept>
</app:collection>
</app:workspace>
</app:service>
|
Notice the document shows two workspaces, Adminblog and main. It's the latter you're concerned with here. It has two collections. The first one, Weblog Entries, is designed for Atom entries, as designated by the accept element. The second is a media collection that accepts images. This is the collection to which you will post.
To start, create a Java class that creates a simple entry and posts it to the server. Creating the entry is just like working with any other object (see Listing 3).
Listing 3. Create an entry
import java.util.Date;
import org.apache.abdera.Abdera;
import org.apache.abdera.factory.Factory;
import org.apache.abdera.model.Document;
import org.apache.abdera.model.Entry;
import org.apache.abdera.protocol.client.Client;
import org.apache.abdera.protocol.client.CommonsClient;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
public class CreateEntry {
public static void main(String[] args) throws Exception {
Abdera abdera = new Abdera();
Factory factory = abdera.getFactory();
Entry entry = factory.newEntry();
entry.setId("urn:sampleEntryIdValue");
entry.setTitle("Pizza, pizza everywhere...");
entry.setUpdated(new Date());
entry.addAuthor("Nick Chase");
entry.setContent("... and not a thing to drink. What kind "+
"of place is this, anyway?");
Document<Entry> doc = entry.getDocument();
doc.writeTo(System.out);
System.out.println();
}
}
|
In Listing 3, you see the creation of the Abdera object, which contains most of the functionality at its core. It also provides a Factory object, which you can use to create a new Entry. The Entry object represents an entry element just as you would see it in an Atom feed. To that end, you can set its various properties, such as the title, updated date, author, and so on.
Once you create the Entry, you can cast it to a Document, which gives you the ability to easily serialize it to the command line.
You should see output such as that shown in Listing 4.
Listing 4. The output
<?xml version='1.0' encoding='UTF-8'?>
<entry xmlns="http://www.w3.org/2005/Atom">
<id>urn:sampleEntryIdValue</id>
<title type="text">Pizza, pizza everywhere...</title>
<updated>2007-03-19T03:59:49.750Z</updated>
<author>
<name>Nick Chase</name>
</author>
<content type="text">... and not a thing to drink. What kind of
place is this, anyway?</content>
</entry>
|
Note that the spacing has been added for readability.
Notice the content element. In an entry, it contains the content such as text or HTML, with a type attribute value of text. That's where you'll ultimately store the media but first look at how to add this entry to the system.
To do that, you need to use a POST request, with the entry as the content of the request (see Listing 5).
Listing 5. Sending the entry
...
System.out.println();
Client client = new CommonsClient(abdera);
UsernamePasswordCredentials credentials =
new UsernamePasswordCredentials("admin", "admin");
client.addCredentials(
"http://localhost:8080/roller/app/main/entries",
null, null, credentials);
Document<Entry> docOut = client.post(
"http://localhost:8080/roller/app/main/entries",
entry).getDocument();
docOut.writeTo(System.out);
}
}
|
First, create a new Client object. This is basically an HTTP client. The Blogapps server protects all of your URLs with a username and password, so you will need to add credentials to the client in order for it to be able to do its work.
The POST request itself includes the URL to which the request is directed, and the entry, which makes up the body of the request. This operation sends back a response, which gives you the opportunity to check the response code to see whether or not it was successful (not shown).
When you execute the POST request, the server sends back information on the created entry, as in Listing 6.
Listing 6. The created entry
<?xml version='1.0' encoding='utf-8'?>
<entry xmlns="http://www.w3.org/2005/Atom">
<title type="text">Pizza, pizza everywhere...</title>
<link rel="alternate"
href="http://localhost:8080/roller/main/entry/pizza_pizza_everywhere" />
<link rel="edit"
href="http://localhost:8080/roller/app/main/entry/46921b221167d69001116859d7ea0097" />
<category term="/General" />
<author>
<name>admin</name>
<email>admin@example.com</email>
</author>
<id>http://localhost:8080/roller/main/entry/pizza_pizza_everywhere
</id>
<updated>2007-03-19T03:59:50Z</updated>
<published>2007-03-19T03:59:50Z</published>
<content type="html">... and not a thing to drink.
What kind of place is this, anyway?</content>
<app:control xmlns:app="http://purl.org/atom/app#">
<app:draft>no</app:draft>
</app:control>
<atom-draft xmlns="http://rollerweblogger.org/namespaces/app">9</atom-draft>
</entry>
|
This document includes two legs. The first, the alternate link, provides the URL by which you can view the entry in the browser. The second, the edit link, provides the URI that represents the data internally. Later, if you were to attempt to edit this entry, this is the URI you would use.
Once you add the entry, to see it in the collection, point the browser to http://localhost:8080/roller/app/main/entries. You should see the new entry, as shown in listing 7.
Listing 7. The new entry in the collection feed
...
<content type="html"><div xmlns="http://www.w3.org/1999/xhtml">
<p><i>The Blogapps server supports the upcoming Atom spec,
so if you install it... </i></p>
</div></content>
<app:control>
<app:draft>no</app:draft>
</app:control>
</entry>
<entry>
<title>Pizza, pizza everywhere...</title>
<link rel="alternate"
href="http://localhost:8080/roller/main/entry/pizza_pizza_everywhere" />
<link rel="edit"
href="http://localhost:8080/roller/app/main/entry/46921b221167d69001116859d7ea0097" />
<category term="/General" />
<author>
<name>admin</name>
<email>admin@example.com</email>
</author>
<id>http://localhost:8080/roller/main/entry/pizza_pizza_everywhere
</id>
<updated>2007-03-19T03:59:50Z</updated>
<published>2007-03-19T03:59:50Z</published>
<content type="html">... and not a thing to drink. What kind of
place is this, anyway?</content>
<app:control>
<app:draft>no</app:draft>
</app:control>
</entry>
<entry>
<title>This is an entry</title>
<link rel="alternate" href=
"http://localhost:8080/roller/main/entry/this_is_an_entry2" />
<link rel="edit" href=
"http://localhost:8080/roller/app/main/entry/402881821161c1fb011162b0ea
3400fd" />
<category term="/General" />
<author>
...
|
Now let's see how the process works for media resources.
The process of creating a media resource is essentially the same as in entry, except that the file itself becomes the content of the POST request (see Listing 8).
Listing 8. Creating a media resource
import java.io.FileInputStream;
import java.util.Date;
import org.apache.abdera.Abdera;
import org.apache.abdera.factory.Factory;
import org.apache.abdera.model.Document;
import org.apache.abdera.model.Entry;
import org.apache.abdera.protocol.client.Client;
import org.apache.abdera.protocol.client.CommonsClient;
import org.apache.abdera.protocol.client.RequestOptions;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
public class CreateResource {
public static void main(String[] args) throws Exception {
Abdera abdera = new Abdera();
Factory factory = abdera.getFactory();
Client client = new CommonsClient(abdera);
FileInputStream inputStream = new FileInputStream(
"c:\\ebay\\robin\\robin4.jpg");
InputStreamRequestEntity file =
new InputStreamRequestEntity(inputStream,
"image/jpg");
RequestOptions options = client.getDefaultRequestOptions();
options.setHeader("Title", "Robin4");
UsernamePasswordCredentials credentials =
new UsernamePasswordCredentials("admin", "admin");
client.addCredentials(
"http://localhost:8080/roller/app/main/resources",
null, null, credentials);
Document<Entry> docOut = client.post(
"http://localhost:8080/roller/app/main/resources",
file).getDocument();
docOut.writeTo(System.out);
}
}
|
In this case, instead of creating an Entry object, you create a FileInputStream out of a physical file on the machine. After you assign it a mime type, it's ready to add. But first, you can assign it a title by sending a header in the options for the request. (Note that it is up to the server to decide how much it will consider your title. For example, Blogapps turns all titles into a filename.)
Finally, you're ready to add the file to the system. Note that you simply replace the entry in the request, adding the file as the body of the POST request instead.
The return document might look a bit familiar (see Listing 9).
Listing 9. The media description entry
<?xml version='1.0' encoding='utf-8'?>
<entry xmlns="http://www.w3.org/2005/Atom">
<title type="text">main-2007031900765.jpg</title>
<link rel="edit" href=
"http://localhost:8080/roller/app/main/resource/main-
2007031900765.jpg.media-link" />
<link rel="edit-media" href=
"http://localhost:8080/roller/app/main/resource/main-2007031900765.jpg"
/>
<id>http://localhost:8080/roller/app/main/resource/main-
2007031900765.jpg</id>
<updated>1970-01-01T00:00:00Z</updated>
<content type="image/jpeg" src=
"http://localhost:8080/roller/resources/main/main-2007031900765.jpg" />
<atom-draft xmlns="http://rollerweblogger.org/namespaces/app">9</atom-draft>
</entry>
|
In this case, the entry shows how to access the image (http://localhost:8080/roller/resources/main/main-2007031900765.jpg). If you point your browser to that URL, you should see the image, as shown in Figure 2. Notice that this also includes not one, but two edit links. The edit link allows you to edit the XML entry and the edit-media link allows you to edit the actual media file.
Figure 2. The uploaded image
You can also see the images entry in the resource collection at http://localhost:8080/roller/app/main/resources (see Listing 10).
Listing 10. The new feed
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" ...>
<title>main</title>
<link rel="alternate" type="text/html"
href="http://localhost:8080/roller/main" />
<id>http://localhost:8080/roller/app/main/entries/0</id>
<updated>2007-03-19T04:46:41Z</updated>
<entry>
<title>main-2007031900765.jpg</title>
<link rel="edit" href=
"http://localhost:8080/roller/app/main/resource/main-
2007031900765.jpg.media-link" />
<link rel="edit-media" href=
"http://localhost:8080/roller/app/main/resource/main-2007031900765.jpg"
/>
<id>http://localhost:8080/roller/app/main/resource/main-
2007031900765.jpg</id>
<updated>2007-03-19T04:31:36Z</updated>
<content type="image/jpeg" src=
"http://localhost:8080/roller/resources/main/main-2007031900765.jpg" />
</entry>
<entry>
<title>robin3.jpg</title>
<link rel="edit" href=
"http://localhost:8080/roller/app/main/resource/robin3.jpg.media-link"
/>
<link rel="edit-media" href=
"http://localhost:8080/roller/app/main/resource/robin3.jpg" />
<id>http://localhost:8080/roller/app/main/resource/robin3.jpg</id>
<updated>2007-03-19T02:20:30Z</updated>
<content type="image/jpeg" src=
"http://localhost:8080/roller/resources/main/robin3.jpg" />
</entry>
<entry>
<title>robin2.jpg</title>
...
</entry>
</feed>
|
This feed is the basis for the servlet.
Displaying the items in the feed
Start by creating a new servlet. To display a list of the items in the collection, parse the feed that represents that collection (see Listing 11).
Listing 11. Parsing the feed
import java.io.FileInputStream;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.util.List;
import org.apache.abdera.*;
import org.apache.abdera.model.*;
import org.apache.abdera.protocol.client.*;
import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
public class FeedList extends javax.servlet.http.HttpServlet
implements javax.servlet.Servlet {
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
java.io.PrintWriter out = response.getWriter();
out.println(
"<html><head><title>Media List</title></head><body>");
try{
Abdera abdera = new Abdera();
Client client = new CommonsClient(abdera);
String uri =
"http://localhost:8080/roller/app/main/resources";
UsernamePasswordCredentials credentials =
new UsernamePasswordCredentials("admin", "admin");
client.addCredentials(uri, null, null, credentials);
Document<Feed> doc = client.execute("get", uri, null,
null).getDocument();
Feed feed = doc.getRoot();
List<Entry> entries = feed.getEntries();
int numEntries = entries.size();
for (int i = 0; i < numEntries; i++){
Entry thisEntry = entries.get(i);
out.println(thisEntry.getTitle());
out.println("<br />");
out.print("<img height='120' src='");
out.println(
thisEntry.getContentElement().getSrc().toString());
out.println("' /><br />");
out.println("<a href='FeedList?action=delete&uri="+
thisEntry.getEditLinkResolvedHref()+"'>Delete</a>");
out.println("<hr />");
}
out.println("<form action='FeedList' method='post'>");
out.println(
"URL for media: <input type='text' name='newMedia' />");
out.println(
"Title for media: <input type='text' name='title' />");
out.println(
"<br /><input type='submit' value='Add New Asset' />");
out.println("</form>");
} catch (Exception e){
e.printStackTrace(response.getWriter());
}
out.println("</body></html>");
}
}
|
In the servlet's doGet() method, which fires a GET request in the browser to the servlet, create the Abdera and client objects as before, add credentials, and so on. In this case, however, you will send a GET request to the server to retrieve the Feed document that corresponds to the collection.
Once you have that Document, you can get the actual Feed object by using the getRoot() method. To retrieve the actual entries from the feed, use the getEntries() method to retrieve a List<Entry>. After you have that, you can loop through its Entry objects one by one, display the appropriate HTML for each one. That HTML includes an img tag that displays the actual image itself at a maximum height of 120 pixels and a link to delete the resource. (The latter uses the getEditLinkResolvedHref() method to retrieve the URL that actually corresponds to the entry internally.)
Notice the form at the bottom of the page. That form enables the user to enter content to add. Because the form specifies the POST method, you'll add that functionality to the doPost() method (see Listing 12).
Listing 12. The doPost() method
...
out.println("</body></html>");
}
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
java.io.PrintWriter out = response.getWriter();
out.println(
"<html><head><title>Media List</title></head><body>");
try {
Abdera abdera = new Abdera();
Client client = new CommonsClient(abdera);
UsernamePasswordCredentials credentials =
new UsernamePasswordCredentials("admin", "admin");
client.addCredentials(
"http://localhost:8080/roller/app/main/resources",
null, null, credentials);
String uri =
"http://localhost:8080/roller/app/main/resources";
Document<Entry> docOut = null;
FileInputStream inputStream = new FileInputStream(
request.getParameter("newMedia").toString());
InputStreamRequestEntity file =
new InputStreamRequestEntity(inputStream,
"image/jpg");
RequestOptions options = client.getDefaultRequestOptions();
options.setHeader("Title", request.getParameter("title"));
docOut = client.post(uri, file, options).getDocument();
doGet(request, response);
} catch (Exception e){
e.printStackTrace();
}
out.println("</body></html>");
}
}
|
Here you added the image just as you did in the standalone class, except that you retrieve parameters from the servlet request to determine the location and title of the file to be added.
Note that you can also set this up to enable the user to upload a file to the server, but because this involves complex manipulation on the part of extracting file uploads in the Java servlet, I will leave this as an exercise for the reader.
You should now see the new entry on the feed, as in Listing 13.
Listing 13. The new entry
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" ... >
<title>main</title>
<link rel="alternate" type="text/html"
href="http://localhost:8080/roller/main" />
<id>http://localhost:8080/roller/app/main/entries/0</id>
<updated>2007-03-19T04:46:41Z</updated>
<entry>
<title>main-2007031900125.jpg</title>
<link rel="edit" href=
"http://localhost:8080/roller/app/main/resource/main-
2007031900125.jpg.media-link" />
<link rel="edit-media" href=
"http://localhost:8080/roller/app/main/resource/main-2007031900125.jpg"
/>
<id>http://localhost:8080/roller/app/main/resource/main-
2007031900125.jpg</id>
<updated>2007-03-19T04:46:41Z</updated>
<content type="image/jpeg" src=
"http://localhost:8080/roller/resources/main/main-2007031900125.jpg" />
</entry>
<entry>
<title>main-200703190078.jpg</title>
<link rel="edit" href=
"http://localhost:8080/roller/app/main/resource/main-
200703190078.jpg.media-link" />
...
|
You'll also see the new image on the actual servlet page.
Deleting an entry is surprisingly easy. Remember, you created a link for each resource. That link causes the browser to make a GET request with two parameters: the action parameter and the edit-URI of the resource. You can use that information to delete the resource (see Listing 14).
Listing 14. Deleting the resource
...
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
java.io.PrintWriter out = response.getWriter();
out.println(
"<html><head><title>Media List</title></head><body>");
String action = null;
if (request.getParameter("action") == null){
action = "list";
} else {
action = request.getParameter("action").toString();
}
try{
Abdera abdera = new Abdera();
Client client = new CommonsClient(abdera);
String uri =
"http://localhost:8080/roller/app/main/resources";
UsernamePasswordCredentials credentials =
new UsernamePasswordCredentials("admin", "admin");
client.addCredentials(uri, null, null, credentials);
if (action.equals("list")){
...
} else if (action.equals("delete")){
client.delete(request.getParameter("uri").toString());
out.println("The item has been deleted. "+
"<a href='FeedList'>Back to the list</a>");
}
} catch (Exception e){
...
|
That's all there is to it! The item is now gone from the collection, and thus the feed.
You might wonder at this point about how to edit an existing item. The current version of Blogapps doesn't support editing a media resource (as it's generally configured) but the process is similar to Listing 15, which shows how to edit an entry.
Listing 15. Editing an entry
...
String editUri = "http://localhost:8080/roller/app/main/entry/40288182115c1af501115c2e50
a80033";
Document<Entry> editDoc = client.get(editUri).getDocument();
Entry thisEntry = editDoc.getRoot();
thisEntry.setTitle("This is a new Title, one that's been edited");
client.put(editUri, thisEntry);
...
|
To edit an entry, you must first retrieve it from the database using the edit URI, which represents the data internally. To do that, use the client (properly configured, of course) to execute a get() request on that URL. This method returns a Response object, from which you can retrieve a Document. When you have the Document, you can retrieve the Entry object using the getRoot() method.
Once you have the Entry object, you can manipulate it just as before. However, in order to replace an existing entry, you use the PUT method rather than the POST method. Using the PUT method, you specify the URL and the data, and the server replaces the existing information rather than creating a new entry.
Remember, each media resource has a media-link entry, which is how you edit that information. To replace the actual resource, simply replace the edited entry with the file, just as you did when you added the resource in the first place (see Listing 16).
Listing 16. Replacing the resource
...
String editUri = "http://localhost:8080/roller/app/main/entry/40288182115c1af501115c2e50
a80033";
FileInputStream inputStream = new FileInputStream(
"c:\\ebay\\redbowl\\redbowl1.jpg");
InputStreamRequestEntity thisFile =
new InputStreamRequestEntity(inputStream,
"image/jpg");
client.put(editUri, thisFile);
...
|
In this case, you don't need to retrieve Entry, because you are replacing only the resource.
The Atom Syndication Format is great for detailing the information in the collection. Combine it with the power of the Atom Publishing Protocol and you have a great way to manage your resources, the images, audio, or even text or xhtml. This article shows you how to use Apache Abdera to easily make use of these capabilities to create a Web-based media resource manager.
| Description | Name | Size | Download method |
|---|---|---|---|
| Sample code | x-atommedia.source.zip | 3KB | HTTP |
Information about download methods
Learn
-
An overview of the Atom 1.0 Syndication Format (James Snell, developerWorks, August 2005): See how this popular Web content syndication format stacks up.
-
Getting to know the Atom Publishing Protocol (James Snell, developerWorks, October - December 2006): Find out about the Atom API in this three-part series:
- Part 1: Create and edit Web resources with the Atom Publishing Protocol: Explore a high-level overview of the protocol and its basic operation and capabilities.
- Part 2: Put the Atom Publishing Protocol (APP) to work: Learn to use APP to interact with a number of real-world deployed applications.
- Part 3: Introducing the Apache Abdera project: Start to implement Atom-enabled apps using a new open-source project, called Abdera, currently under incubation at the Apache Software Foundation.
-
RSS and Atom in Action (Dave Johnson, Manning Publications, July 2006): If you prefer something more physical, take a look at this book about the blog technologies of news feed formats and publishing protocols and how to put these building blocks together. (Blogapps was put together for this book.)
-
Atom 1.0 specification: Read about this XML-based Web content and metadata syndication format.
-
Atom 1.0 compatible software: Visit the Atom Working Group's Wiki for a list of known Atom 1.0 feed consumers.
-
Use the Atom format for syndicating news and more (Uche Ogbuji, developerWorks, May 2004): Read more on this XML-based standard, format, and API for the interchange and cross-reference of Web metadata.
-
developerWorks XML zone: Learn all about XML at the developerWorks XML zone.
-
IBM XML certification: Find out how you can become an IBM-Certified Developer in XML and related technologies.
-
XML technical library: See the developerWorks XML zone for a wide range of technical articles and tips, tutorials, standards, and IBM Redbooks.
-
developerWorks technical events and webcasts: Stay current with technology in these sessions.
Get products and technologies
- The Blogapps server: Download the server in this collection of useful RSS and Atom utilities and examples.
-
Apache Abdera package: Download Apache Abdera and use the Atom Publishing Protocol to create the Web-based media resource manager in this article.
-
Apache Abdera Project Web
site: Explore this tool for building Atom-enabled Java applications.
Discuss
- Participate in the discussion forum.
-
XML zone discussion forums: Participate in any of several XML-centered forums.
Nicholas Chase has been involved in Web site development for companies such as Lucent Technologies, Sun Microsystems, Oracle, and the Tampa Bay Buccaneers. Nick has been a high school physics teacher, a low-level radioactive waste facility manager, an online science fiction magazine editor, a multimedia engineer, an Oracle instructor, and the Chief Technology Officer of an interactive communications company. He is the author of several books, including XML Primer Plus (Sams).



