romelogo-small.jpg

For my current customer I had to create an rss feed. In the java domain you immediately grab Rome to do the job. There was a catch. My customer wants (with good reason) to have feeds validated by the w3c feed validator. This turned out to be a slightly more complicated job. Luckily Rome has good support for extensions, so at least it was possible.

In this blog post I describe the challenges I had creating the validated feed. If you want more in depth information please check my post on my employers blog : serving a heavy load rss feed with spring 3 and ehcache.

Rome extension

At the moment I am missing the atom namespace in the resulting xml.

<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">

I also miss the actual atom link:

<atom:link href="http://dallas.example.com/rss.xml" rel="self" type="application/rss+xml" />

Now we must tell Rome how to generate this content. Rome supports a plugin structure. Detailed information can be found on this page:

http://wiki.java.net/bin/view/Javawsxml/Rome05TutorialSampleModule

An extension consists of a module containing the data to be used by the extension, a generator if the extension needs to add items to the generated feed and a parser if the extension is about reading feeds. In my case I only need the module component and the generator. Finally you need to tell Rome about the extension, this is done in a configuration file that needs to be available on the classpath.

Module

The only data I need is the link for the atom element, the href. The module itself consists of an interface and an implementation. The interface defines the getters and setters. The implementation has a few additional methods. The following code block shows them both.

public interface AtomNSModule extends Module {
    public static final String URI = "http://www.w3.org/2005/Atom";
    String getLink();
    void setLink(String href);
}

public class AtomNSModuleImpl extends ModuleImpl implements AtomNSModule {
    private String link;

    public AtomNSModuleImpl() {
        super(AtomNSModule.class, URI);
    }

    public String getLink() {
        return link;
    }

    public void setLink(String link) {
        this.link = link;
    }

    public Class getInterface() {
        return AtomNSModule.class;
    }

    public void copyFrom(Object obj) {
        AtomNSModule module = (AtomNSModule) obj;
        module.setLink(this.link);
    }
}

The code is not to hard to understand. Concentrate on the copyForm method. Here we put the data from our model into the object that is used by the framework to extract data. This is why you need the interface as well.

Generator

Here we add the elements to the generated xml. JDom is used to generate xml from an object tree of Elements. The following code block shows the complete generator.

public class AtomNSModuleGenerator implements ModuleGenerator {
    private static final Namespace ATOM_NS = Namespace.getNamespace("atom", AtomNSModule.URI);

    private static final Set NAMESPACES;

    static {
        Set nss = new HashSet();
        nss.add(ATOM_NS);
        NAMESPACES = Collections.unmodifiableSet(nss);
    }

    public String getNamespaceUri() {
        return AtomNSModule.URI;
    }

    public Set getNamespaces() {
        return NAMESPACES;
    }

    public void generate(Module module, Element element) {
        AtomNSModule atomNSModule = (AtomNSModule) module;
        Element root = element;
        while (root.getParent() != null && root.getParent() instanceof Element) {
            root = (Element) element.getParent();
        }
        root.addNamespaceDeclaration(ATOM_NS);

        Element atomLink = new Element("link", ATOM_NS);
        atomLink.setAttribute("href", atomNSModule.getLink());
        atomLink.setAttribute("rel", "self");
        atomLink.setAttribute("type", "application/rss+xml");

        element.addContent(0, atomLink);
    }
}

Focus on the generate method. In this method we add the namespace declaration to the root element and we create a new element that is added to the provided element. The provided element is the channel element. The result of the feed now looks like this.

<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <atom:link href="http://localhost:8080/rss/nieuws.rss" rel="self" type="application/rss+xml" />
	...
  </channel>
</rss>

Configuration

The last part is telling Rome to use the new component. This is done by putting a rome.properties file on the classpath with the following contents.

rss_2.0.feed.ModuleGenerator.classes=nl.gridshore.rss.romeextension.AtomNSModuleGenerator

It is important to specify the rss_2.0 because multiple generators are available and this is the one that is used.

Encoding – response type

Another problem I had had to do with encoding. You can set the type of encoding in the generated xml, but the response of the spring controller/view combination does not follow this. Therefore you have to set the encoding of the response object explicitly. In the view component I added the following lines of code:

    @Override
    protected void prepareResponse(HttpServletRequest request, HttpServletResponse response) {
        super.prepareResponse(request, response);
        response.setCharacterEncoding(CharacterEncodingConstants.UTF8);
    }

I hope this can help others to create better feeds as well, suggestions for improvements are of course welcome.

Creating a w3c validated rss feed using Rome and spring 3
Tagged on:                     

14 thoughts on “Creating a w3c validated rss feed using Rome and spring 3

  • July 30, 2011 at 11:13 pm
    Permalink

    Thanks for this great post. I followed your description and now my tool produces valid RSS feeds. Have a look at http://www.feederator.org. It cost me some time to figure out that I had to add the new module myself:
    List modules = feed.getModules();
    AtomNSModuleImpl module = new AtomNSModuleImpl();
    module.setLink(req.getRequestURL().toString());
    modules.add(module);
    feed.setModules(modules);

    Was that the intention or did I do something wrong and this step wouldn’t have been necessary?

    Dani

    • August 1, 2011 at 10:31 am
      Permalink

      Yes you are right, you need to do this.

  • August 18, 2010 at 10:57 pm
    Permalink

    I am using Rome 1.0 to generate an Atom 1.0 feed. I found I did not need to extend Rome to get a valid feed, here is the appropriate Rome API usage:

    SyndFeed techFeed = new SyndFeedImpl();

    feed.setUri(myFeedURI);
    feed.setLink(myFeedLink);

    SyndLink linkRelSelf = new SyndLinkImpl();
    linkRelSelf.setHref(feedURL);
    linkRelSelf.setRel(“self”);
    linkRelSelf.setType(“application/rss+xml”);
    feed.getLinks().add(linkRelSelf);

    feed.setTitle(“your title”);
    feed.setDescription(“your description”);
    // use greatest entry date
    feed.setPublishedDate(new Date());

    … add entries …

  • June 11, 2010 at 6:29 pm
    Permalink

    Hi Jettro, thanks for the post! I’m trying to do almost exactly the same thing – add additional namespace(s) to the feed. I’ve copied your code from above and modified the URI link for a different namespace. But I must be missing some important step as when I run the code, I don’t get anything new in the output.

    My rome.properties file looks like:
    atom_1.0.feed.ModuleGenerator.classes=com.foo.Rome.aggregator.AtomNSModuleGenerator

    and is located in:
    /source/trunk/com.foo.Rome.aggregator/rome.properties (I also tried putting a copy in the folder with my jar files)

    I’ve added the following to my main java file as I’d see other samples which did so:

    feed.getModules().add(new AtomNSModuleImpl());

    This does break at the AtomNSModuleImpl constructor, but I get no other breakpoints hit in any methods in the Impl or Generator classes.

    Not surprisingly, when I generate the output, I don’t see the updated namespace.

    Is there an additional call needed in the main java file? Is something wrong with the rome.properties contents/location such that it isn’t finding/calling the Generator class? Getting the Generator called seems to be the missing link.

    I’m a java/unix newbie after doing Win32 for years so I am probably doing something horribly stupid, but I haven’t spotted it yet despite reading all the samples I can find. ๐Ÿ™‚ But I’ll keep banging my head against the wall cause I’m too ignorant to stop!

    Any thoughts?

    Cheers!
    Bill

    • June 11, 2010 at 8:06 pm
      Permalink

      I do not have the source code with me. Will respond after the week-end.

    • June 11, 2010 at 8:08 pm
      Permalink

      Thanks much! Enjoy the weekend.

      • June 11, 2010 at 10:56 pm
        Permalink

        Prop file is finally getting read. Breaking in generate() now and see additional namespaces being added to root.additionalNamespaces[] via addNamespaceDeclaration(). There are 6 in that array before I add mine. None of the additional namespaces appear in the XML output. Getting closer!

        • July 2, 2010 at 2:38 pm
          Permalink

          Hi. I am trying to create custom modules in order to add some elements to the ROME generated feeds and feed item, and I am having the problem of ROME not finding the rome.properties file, although I have tried to place it in several paths. Any information that helped you get over this step that you can share, Bill?

          • July 12, 2010 at 3:50 pm
            Permalink

            Hi Andreea, sorry for the delay, just back from vacation. I put it in /src/java directory. I’m using NetBeans which seems to find it in that location. I also have a log4j properties file there. I should have noticed that and put the Rome file there in the first place! ๐Ÿ™‚

  • April 22, 2010 at 11:34 am
    Permalink

    Hi there,

    I am looking for information about how to set up a syndication feed with authentication, that is, each suscriber would have to enter his credentials. I haven’t been able to find any useful information, can you point me to any tutorial regarding this matter?

    Thanks in advance,

    รlvaro

    • April 27, 2010 at 9:34 am
      Permalink

      Sorry I do not have experience with secured rss feeds. Depending on the type of authentication it might nog be to hard. Basic authentication should be doable, but I have no examples.

      • April 30, 2010 at 9:57 am
        Permalink

        Thanks anyway, regards

  • February 17, 2010 at 11:52 am
    Permalink

    Useful port. Thanks

Comments are closed.