Introduction
Recently I have worked with Magnolia CMS. This is a CMS written in Java that really shines when it comes to providing a rich user experience that even non-technical people can understand (demo). Furthermore, it:
is tested opensource, based on open standards (eg. JCR)
- has an active community
- provides optional professional support through an enterprise license
- has a plethora of extension modules (plugins) readily available (eg. MVC integration, public user registration)
With the shameless plug out of the way, this blog post focuses on how to use a recent feature of the RSS aggregator module:
Easy RSS syndication of Magnolia content
The RSS aggregator module is one of the modules that I have used when working with Magnolia. It allows to show an aggregate list of recent blog posts, gathered from different blogs. After having used the module a little, I encountered the use case to syndicate content from the CMS as RSS.
The goal of this post is to show you what it takes to be able to syndicate any Magnolia content as RSS, using the RSS aggregator module.
Behind the scenes
Before we dive into the code, let’s consider for a moment what actually happens when someone requests our RSS syndication url. How does the RSS aggregator module get from an incoming request to an output RSS feed?
The RSS aggregator module registers a FeedSyndicationServlet that is mapped to handle requests on”/rss/*”.Given this mapping, a request for a RSS feed syndicated through the RSS aggregator module could look like this:
“http://server/rss/?generatorName=newsRssGenerator”
The FeedSyndicationServlet will attempt to resolve an appropriate FeedGenerator through its FeedGeneratorResolver dependency, based on given request parameters.
The FeedGeneratorResolver determines what FeedGenerator implementation to use for generating the output feed, by using the value of the “generatorName” parameter as a logical name that maps to an implementation class. This mapping is configured in the Content Repository under “config:/modules/rssaggregator/config/feedGenerators”, as is shown in the image on the right.
In this case, for the FeedGeneratorResolver will return an instance of “custom.generator.NewsRssGenerator“. The servlet now uses this FeedGenerator to generate a Feed, and writes the content of the Feed to the response.
Now you know what core concepts are involved to syndicate a RSS feed, let’s implement our own FeedGenerator!
Setting the stage
We’ll need the following prerequisites in order to be able to leverage the new RSS syndication infrastructure in the RSS aggregator module, being:
We should use *-bundle.zip (or .tag.gz) of the RSS aggregator module link above, as it contains the dependencies required by the RSS aggregator module. Note that version 1.0-m2 mistakenly bundles “xercesImpl-2.4.0.jar”, which should be removed. Update: This unintended packaging has been fixed in the latest revision of the sourcecode in version control (see Resources).
Besides a running CMS with the above installed, we will need to use some of the infrastructure provided by the RSS aggregator module. Assuming you are using maven, add a dependency to the RSS module with the required repository, so you’ll have the libraries required in order to build our own FeedGenerator. With this in place, we can start rolling our own RSS syndication of our content.
<repositories>
<repository>
<id>magnolia</id>
<name>Magnolia Repository</name>
<url>http://svn.magnolia-cms.com/maven/m2</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>info.magnolia</groupId>
<artifactId>magnolia-core</artifactId>
<version>${magnolia.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>info.magnolia</groupId>
<artifactId>magnolia-module-rssaggregator</artifactId>
<version>1.0-m2</version>
<scope>provided</scope>
</dependency>
</dependencies>
Let’s write some code
Let’s assume that we run a Magnolia CMS site that has a newspage which contains newsitems. These newsitems we would like to syndicate as an RSS channel. How do we go about this? Open your favorite IDE …
The first thing we need to do, is implement a FeedGenerator that retrieves the news from the CMS and generate a Feed from it. The FeedGenerator is a simple interface that looks like this:
public interface FeedGenerator {
Feed generate() throws FeedGenerationException;
}
The Feed returned by the “generate()” call looks like this:
public class Feed {
private final String xml;
private final String characterEncoding;
public Feed(String xml, String characterEncoding) {
this.xml = xml;
this.characterEncoding = characterEncoding;
}
// getters follow...
}
Our FeedGenerator just needs to generate a simple encoded xml string and wrap it in a Feed in order to work. However, we still need to retrieve the news nodes from the CMS and convert this to xml; Luckily there are some convenience classes that makes that trivial.
By extending the abstract base class AbstractSyndFeedGenerator, we leverage the open source ROME library for generating an RSS for us. All we need to do is implement two template methods.
public class NewsSyndFeedGenerator extends AbstractSyndFeedGenerator {
@Override
public void setFeedInfo(SyndFeed feed) {
feed.setTitle("Latest news");
feed.setDescription("Only the latest news!");
feed.setLink("http://mywebsite.com/rss/?generatorName=NewsRssGenerator");
}
@Override
public List<SyndEntry> loadFeedEntries() {
// retrieve news items from Content Repository
// convert news items to SyndEntry list and return these
}
}
The “setFeedInfo(SyndFeed)” method takes a SyndFeed as its argument. A SyndFeed is ROME’s representation of a syndication feed. A new instance of such a SyndFeed is provided as a parameter by the AbstractSyndFeedGenerator, implementors only need to set the properties above.
The “loadFeedEntries()” method is less trivial. It requires retrieving the news items from the Content Repository, and map these to SyndEntry instances. In order to do this in a convenient way, MagnoliaTemplate was introduced. Honouring the Spring JDBC template pattern, this class allows to query the Content Repository and map the resulting Content nodes using a ContentMapper in a one liner.
First we’ll need to know how to query for the news items, in this example the news items reside under “/home/newsitems” in the “website” repository and are of type CONTENTNODE. A good way to try out and tune your query to retrieve the proper content, is to use the JCR Queries tool, provided in Magnolia CMS, see the image below.

In addition to being able to query the news items, we also need the properties of the news items, so we can map them appropriately. Keep in mind that any rich text content in the news items might contain links to uploaded images or other media. A useful utility class to resolve these kind of links is LinkUtil, provided by the Magnolia core.
You can have a look at the image below to get an impression of the news item content type we are about to map to RSS in this example.

Given the above layout of news items in our Content Repository, the implementation of the “loadFeedEntries()” then looks like this:
@Override
public List<SyndEntry> loadFeedEntries() {
String query = "/jcr:root/home/newsitems/*";
return magnoliaTemplate.xpathQueryForList("website", query, ItemType.CONTENTNODE, new NewsMapper());
}
The “xpathQueryForList()” method on the MagnoliaTemplate, takes four arguments:
the repository to query, the query to execute, the type of node to query for, and a ContentMapper.
The ContentMapper is a simple interface that enables mapping each of the result nodes of the query execution, to an object instance:
public interface ContentMapper<T> {
/**
* Map a content node that defines a type T to an instance of type T.
*
* @param content the content node to map
* @return the result object of type T
* @throws RepositoryException when problems occur accessing the underlying Repository
*/
T map(Content content) throws RepositoryException;
}
At this point you must be curious how the implementation of the our NewsMapper looks like. So without further ado, here it is:
class NewsMapper implements ContentMapper<SyndEntry> {
@Override
public SyndEntry map(Content content) throws RepositoryException {
SyndEntry entry = new SyndEntryImpl();
entry.setTitle(content.getNodeData("title").getString());
if (content.getNodeData("date").getDate() != null) {
entry.setPublishedDate(content.getNodeData("date").getDate().getTime());
}
SyndContent description = new SyndContentImpl();
description.setType("text/html");
String text = content.getNodeData("text").getString();
// Replace internal links that use the special magnolia link format (looks like ${link: {uuid: ... etc)
// (see info.magnolia.cms.link.UUIDLink for an example of the special format that this next line handles)
String textWithResolvedLinks = new LinkResolverImpl().convertToExternalLinks(text);
description.setValue(textWithResolvedLinks);
entry.setDescription(description);
return entry;
}
}
Aside from the tests you have naturally written while implementing the code above, you should now be able to verify your FeedGenerator implementation works by finishing off the exercise by following the three steps:
- Make sure your FeedGenerator implementation is deployed to Magnolia
- Configure this FeedGenerator implementation in the Repository so that it maps to a “generatorName“, remember?
- Request the feed from your Magnolia instance
Summary
In this post you have seen that the Magnolia CMS RSS aggregator module can now syndicate content from your CMS as RSS with minimal effort: Implement a FeedGenerator, wire it with a generatorName in the Content Repository, have your feed requested and read too!
Lastly, know that you can always find the complete sample code for this post in the resources section below.
Resources