The view, via Spring 3.0's new AbstractRssFeedView

Now we come to the meat of the subject. Here we're going to implement a Spring Web MVC view by extending Spring 3.0's new AbstractRssFeedView class and by using ROME to model our news feed. Once we have the view in place, we'll be ready to configure the mapping from the logical view name we set in the controller to the view.

Listing 2 shows what's involved in implementing a view for an RSS feed. If you're doing an Atom feed, the approach is entirely analogous, but you would just need to extend AbstractAtomFeedView instead of AbstractRssFeedView.

Listing 2. Implementing a view by extending AbstractRssFeedView
package rssdemo.web;

import java.util.*;
import javax.servlet.http.*;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.web.servlet.view.feed.AbstractRssFeedView;
import com.sun.syndication.feed.rss.Channel;
import com.sun.syndication.feed.rss.Description;
import com.sun.syndication.feed.rss.Item;
import rssdemo.model.NewsItem;

public final class RssNewsFeedView extends AbstractRssFeedView {           // 1
    private String feedTitle;                                              // 2
    private String feedDesc;
    private String feedLink;
    
    @Required
    public void setFeedTitle(String feedTitle) {
        this.feedTitle = feedTitle;
    }
    
    @Required
    public void setFeedDescription(String feedDesc) {
        this.feedDesc = feedDesc;
    }
    
    @Required
    public void setFeedLink(String feedLink) {
        this.feedLink = feedLink;
    }

    @Override
    protected void buildFeedMetadata(
            Map model, Channel feed, HttpServletRequest request) {         // 3
        
        feed.setTitle(feedTitle);
        feed.setDescription(feedDesc);
        feed.setLink(feedLink);
    }
    
    @Override
    protected List<Item> buildFeedItems(
            Map model,
            HttpServletRequest request,
            HttpServletResponse response)
        throws Exception {                                                 // 4
        
        @SuppressWarnings("unchecked")
        List<NewsItem> newsItems =
            (List<NewsItem>) model.get("newsItemList");                    // 5
        
        List<Item> feedItems = new ArrayList<Item>();
        for (NewsItem newsItem : newsItems) {                              // 6
            Item feedItem = new Item();
            feedItem.setTitle(newsItem.getTitle());
            feedItem.setAuthor(newsItem.getAuthor());
            feedItem.setPubDate(newsItem.getDatePublished());
            
            Description desc = new Description();
            desc.setType("text/html");
            desc.setValue(newsItem.getDescription());
            feedItem.setDescription(desc);
            
            feedItem.setLink(newsItem.getLink());
            feedItems.add(feedItem);
        }
        
        return feedItems;
    }
}

So what's going on here? Well, we begin by extending the AbstractRssFeedView class 1. Then we include some injected fields for feed metadata 2. We use these in the optional buildFeedMetadata() method 3. I say "optional" because the AbstractRssFeedView provides a dummy implementation (actually, its superclass AbstractFeedView provides it).

The method we're required to implement is buildFeedItems() 4. Here's where we map our list of domain objects (here, a list of NewsItem objects that are just something we cooked up ourselves) to the ROME API, which provides a structure for modeling feeds. We begin by grabbing the NewsItem list off the model (recall from listing 1 that we placed the list on the model in the controller) 5. Then we iterate over the NewsItem list, mapping each one to a ROME Item 6.

That should be pretty straightforward-looking. And we're almost done. There's just one part that remains, and that's establishing the mapping between the logical view name we return from the controller and the RssNewsFeedView that we just created. We do that in the application context.