Spring Integration RSS Feed Reader
Introduction
This article describes the process for implementing an RSS Feed Reader in your application using the Spring Integration framework in combination with Spring 3.
The Spring Integration framework supports Enterprise Integration Patterns which are a common vocabulary and body of knowledge around asynchronous messaging architectures used to build integration solutions. Spring Integration enables lightweight messaging within Spring-based applications and supports integration with external systems via declarative adapters.
Inbound Channel Adapter
RSS feeds are processed using an Inbound Channel Adapter which is bound to a URL. The channel is configured to poll against the URL on a periodic basis (set as milliseconds) and process incoming messages with a payload in the format of a com.sun.syndication.feed.synd.SyndEntry instance. A service activator is configured to bind the channel to a message handler which implements the org.springframework.integration.core.MessageHandler interface to process the incoming message.
Maven project
I have specified Spring Integration and Rome dependencies in the Maven POM file to support processing of RSS feeds. The Rome dependency contains classes and interfaces in the com.sun.syndication package.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>feedchannel</artifactId>
<groupId>net.comdynamics.feedchannel</groupId>
<version>0.1-SNAPSHOT</version>
</parent>
<artifactIdrfeedchannel-core</artifactId>
<packaging>jar</packaging>
<name>rssreader Core</name>
<description>feedchannel - Core Module</description>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>${spring.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-asm</artifactId>
<version>${spring.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.0.4</version>
</dependency>
<dependency>
<groupId>rome</groupId>
<artifactId>rome</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>rome</groupId>
<artifactId>rome-fetcher</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-core</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-feed</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>net.comdynamics.feedchannel.RSSReader</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>net.comdynamics.feedchannel.RSSReader</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.dstovall</groupId>
<artifactId>onejar-maven-plugin</artifactId>
<version>1.4.4</version>
<executions>
<execution>
<configuration>
<!-- Optional -->
<onejarVersion>0.97</onejarVersion>
<!-- Optional, default is false -->
<attachToBuild>true</attachToBuild>
<!-- Optional, default is "onejar" -->
<classifier>onejar</classifier>
</configuration>
<goals>
<goal>one-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<pluginRepositories>
<pluginRepository>
<id>onejar-maven-plugin.googlecode.com</id>
<url>http://onejar-maven-plugin.googlecode.com/svn/mavenrepo</url>
</pluginRepository>
</pluginRepositories>
</project>
The application will be packaged using One-JAR to create a standalone Java application containing dependent libraries in a single executable JAR file.
Spring Application Context
The Spring application context specifies the Inbound Channel Adapter and a URL to subscribe against, the Service Activator, and a Message Handler for processing incoming RSS message payload.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-feed="http://www.springframework.org/schema/integration/feed"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/feed http://www.springframework.org/schema/integration/feed/spring-integration-feed-2.0.xsd">
<int:channel id="feedChannel" />
<int-feed:inbound-channel-adapter id="feedAdapter" channel="feedChannel" url="http://www.formula1.com/rss/news/latest.rss">
<int:poller id="poller" fixed-rate="10000" max-messages-per-poll="100" />
</int-feed:inbound-channel-adapter>
<int:service-activator input-channel="feedChannel" ref="messageHandler"/>
<bean id="messageHandler" class="net.comdynamics.feedchannel.RSSMessageHandler"/>
</beans>
RSS Message Handler
The RSS Message handler is invoked by the poller and the handleMessage is responsible for acting on the incoming message.
package net.comdynamics.feedchannel;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.integration.Message;
import com.sun.syndication.feed.synd.SyndEntryImpl;
import org.springframework.integration.MessagingException;
import org.springframework.integration.core.MessageHandler;
/**
* Handler for processing incoming RSS message payloads.
*
* @author gregsmith
*/
public class RSSMessageHandler implements MessageHandler {
private static final Log log = LogFactory.getLog(RSSMessageHandler.class);
public RSSMessageHandler() {
log.debug("Created RSSMessageHandler");
}
/**
* Handles the message if possible. If the handler cannot deal with the
* message this will result in a <code>MessageRejectedException</code> e.g.
* in case of a Selective Consumer. When a consumer tries to handle a
* message, but fails to do so, a <code>MessageHandlingException</code> is
* thrown. In the last case it is recommended to treat the message as tainted
* and go into an error scenario.
* <p/>
* When the handling results in a failure of another message being sent
* (e.g. a "reply" message), that failure will trigger a
* <code>MessageDeliveryException</code>.
*
* @param message the message to be handled
* @throws org.springframework.integration.MessageRejectedException
* if the handler doesn't accept the message
* @throws org.springframework.integration.MessageHandlingException
* when something fails during the handling
* @throws org.springframework.integration.MessageDeliveryException
* when this handler failed to deliver the
* reply related to the handling of the message
*/
public void handleMessage(Message<?> message) throws MessagingException {
SyndEntryImpl syndFeed = (SyndEntryImpl)message.getPayload();
log.debug("Message: " + syndFeed.getTitle() + " created " + syndFeed.getPublishedDate());
}
}
RSS feed reader bootstrap
This application is invoked from the command line, hence the use of the Spring ClassPathXmlApplicationContext to wire bean dependencies.
package net.comdynamics.feedchannel;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.channel.DirectChannel;
/**
* Bootstrap class for RSSReader
*
* @author gregsmith
*/
public class RSSReader {
private static final Log log = LogFactory.getLog(RSSReader.class);
private RSSMessageHandler rssMessageHandler;
private DirectChannel feedChannel;
public RSSReader() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
new String[] { "applicationContext.xml" });
log.debug("ApplicationContext is: " + context);
feedChannel = (DirectChannel) context.getBean("feedChannel");
rssMessageHandler = (RSSMessageHandler) context.getBean("messageHandler");
feedChannel.setShouldTrack(true);
feedChannel.subscribe(rssMessageHandler);
}
public static void main(String args[]) {
log.debug("RSS reader starting...");
new RSSReader();
log.debug("RSS reader polling!");
}
}
Packaging and execution
The application is built using Maven with the following directive:
mvn package
The packaged JAR file will be created using a suffix of one-jar.jar and can then be launched from the /target directory of the Maven project using a syntax of
java -jar application-name.one-jar.jar
Upon startup, incoming RSS messages will be processed by the message handler. In this example which processes the Formula1.com RSS feed, the title and date of publication for the message is displayed.
Message: Qualifying analysis - can McLaren halt Vettel's winning streak? created Sat Apr 16 23:51:00 NZST 2011 Message: Chinese Grand Prix - selected team & driver quotes created Sun Apr 17 21:39:00 NZST 2011 Message: Race - Hamilton takes sensational Shanghai win created Sun Apr 17 21:41:00 NZST 2011 Message: Sebastian Vettel Q&A - an afternoon full of lessons created Mon Apr 18 01:03:00 NZST 2011 Message: FIA post-race press conference - China created Mon Apr 18 01:10:00 NZST 2011 Message: Montezemolo demands rapid improvement at Ferrari created Mon Apr 18 23:59:00 NZST 2011 Message: China analysis - McLaren strike back in Shanghai created Tue Apr 19 03:41:00 NZST 2011 Message: Shanghai debrief with Williams' Sam Michael created Tue Apr 19 22:29:00 NZST 2011 Message: Massa determined to maintain China momentum created Wed Apr 20 20:46:00 NZST 2011 Message: Lewis Hamilton Q&A: China win just the start of the fight created Thu Apr 21 01:08:00 NZST 2011 Message: Q&A with McLaren's Jenson Button created Thu Apr 21 02:29:00 NZST 2011 Message: Peter Sauber Q&A: We've reached our first target? created Fri Apr 22 00:53:00 NZST 2011 Message: Robert Kubica Q&A: Fans' response has been overwhelming created Fri Apr 22 02:17:00 NZST 2011 Message: Formula One Fantasy - Mercedes' Nico Rosberg created Mon Apr 25 01:14:00 NZST 2011 Message: Kubica discharged from Italian hospital created Mon Apr 25 08:01:00 NZST 2011
Conclusion
Through minimal coding it is possible to subscribe to an RSS feed using the Spring Integration framework. Since processing RSS feeds is a trivial usage example, the reader is encouraged to explore the framework in more detail if there is a need to support asynchronous, message-driven behavior within a Spring-based application.
Comments are closed.



Great tutorial to Spring Integration.
But I get this error on running in STS IDE.
ERROR: org.springframework.integration.handler.LoggingHandler – java.lang.NoClassDefFoundError: org/jdom/input/JDOMParseException at com.sun.syndication.io.
SyndFeedInput.(SyndFeedInput.java:58) at com.sun.syndication.io.SyndFeedInput.(SyndFeedInput.java:48)
I am using Spring STS IDE with Spring 3, ROME1, JDOM1.1.1, JDK6.
Also I build this in as a Spring Template for MVC project with the RSSReader class as Bootstrap class.
I can give more info about my config.xml and Bootstrap.java, but not like to clutter here.
Appreciate any help
SanS
my emal sansun1223 at yahoo dot com
It sounds like a library dependency is missing. Check either your project classpath or build file.