When using Spring MVC, we are not constrained using the View implementations that Spring provides. Here I am going to develop my own view which sends the response as an RSS feed. This implementation is in the spirit of Spring’s own AbstractExcelView and AbstractPdfView that we saw in the previous sections.
The AbstractExcelView and AbstractPdfView both extend AbstractView, so that is what I am going to do to write a new abstract class called AbstractRSSView. Spring’s AbstractView makes you implement renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response), this is where we actually implement the view and write out the response.
There is a simple library to generate RSS feeds called RSSLibJ. That is what I will use to write our custom view. In the same spirit of the abstract pdf and excel views, I will force the user of my AbstractRSSView to implement void buildRSSFeed(Map model, Channel channel, HttpServletRequest request, HttpServletResponse response). Channel is the primary class that RSSLibJ provides, and in AbstractRSSView, I will create an instance of that class that can be worked with in the class that extends AbstractRSSView.
Here is the code for AbstractRSSView:
public abstract class AbstractRSSView extends AbstractView
{
public static final String FEED_TYPE_KEY = "FEED_TYPE";
public static final String RSS_FEED_TYPE = "rss";
public static final String RDF_FEED_TYPE = "rdf";
protected String baseUrl;
protected String description;
protected String link;
protected String title;
protected String imageLabel;
protected String imagePath;
public void setBaseUrl(String baseUrl)
{
this.baseUrl = baseUrl;
}
public void setDescription(String description)
{
this.description = description;
}
public void setLink(String link)
{
this.link = link;
}
public void setTitle(String title)
{
this.title = title;
}
public void setImageLabel(String imageLabel)
{
this.imageLabel = imageLabel;
}
public void setImagePath(String imagePath)
{
this.imagePath = imagePath;
}
public AbstractRSSView()
{
setContentType("text/xml");
}
/**
* Renders the view given the specified model.
*/
protected final void renderMergedOutputModel(Map model,
HttpServletRequest request,
HttpServletResponse response)
throws Exception
{
Channel channel = new Channel();
channel.setDescription(description);
channel.setLink(link);
channel.setTitle(title);
channel.setImage(link, title, imagePath);
buildRSSFeed(model, channel, request, response);
response.setContentType(getContentType());
String feedFormat =
(model.containsKey(FEED_TYPE_KEY)?
(String)model.get(FEED_TYPE_KEY):
RSS_FEED_TYPE);
response.getWriter().write(channel.getFeed(feedFormat));
}
/**
* Subclasses must implement this method to create an RSS Feed
* given the model.
* @param model the model Map
* @param channel the RSS Channel
* @param request in case we need locale etc.
* Shouldn't look at attributes.
* @param response in case we need to set cookies.
* Shouldn't write to it.
*/
protected abstract void buildRSSFeed(Map model,
Channel channel,
HttpServletRequest request,
HttpServletResponse response)
throws Exception;
}
Here is an example of using this AbstractRSSView to make a concrete view to show a list of Widgets in an RSS feed:
public class WidgetListRSSView extends AbstractRSSView
{
protected void buildRSSFeed(Map model,
Channel channel,
HttpServletRequest request,
HttpServletResponse response)
throws Exception
{
Collection widgets = (Collection)model.get("widgetList");
Iterator widgetIterator = widgets.iterator();
while(widgetIterator.hasNext())
{
Widget widget = (Widget)widgetIterator.next();
String url = baseUrl + "?id="+ widget.getId();
//channel.addItem( URL, DESCRIPTION, TITLE);
channel.addItem( url,
"A widget named "+
widget.getName() +
", size " +
widget.getSize(),
widget.getName() + "("+widget.getSize()+")");
}
model.put(AbstractRSSView.FEED_TYPE_KEY,
AbstractRSSView.RDF_FEED_TYPE);
}
}
In this example, I will configure the actual view instance in our springmvc-servlet.xml. Here you can set up the core properties such as the title of your feed, the base url, and an image for your feed. Here is the config:
<bean id="widgetListRSSView"
class="com.zabada.springrecipes.rss.WidgetListRSSView" >
<property name="baseUrl">
<value>
http://localhost:8080/zabada/springmvc/widgetList
</value>
</property>
<property name="title"><value>My Widgets</value></property>
<property name="description">
<value>
List of Widgets from the Zabada Spring Recipes Site.
</value>
</property>
<property name="link">
<value>
http://localhost:8080/zabada/springmvc/widgetList
</value>
</property>
<property name="imagePath">
<value>PATH-TO-IMAGE</value>
</property>
</bean>
That’s it. The controller code to use this view can be identical to the rest of our Widget list controllers, just make sure to return “widgetListRSSView” as the view.
Resources
- RSSLibJ Home
- Spring AbstractView Javadoc
- Check out the Spring source code for AbstractPdfView and AbstractExcelView view for more ideas on writing custom views.
Next: Using Spring Beans in Struts Actions