INT-786, added persistence to the feed adapter, added more tests, polished more code

This commit is contained in:
Oleg Zhurakousky
2010-10-17 19:02:39 -04:00
parent 05de3b44c4
commit 31b3a8c0e6
13 changed files with 269 additions and 167 deletions

View File

@@ -1,32 +0,0 @@
package org.springframework.integration.feed;
import java.util.Properties;
import org.springframework.integration.Message;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.history.MessageHistory;
import org.springframework.stereotype.Component;
import com.sun.syndication.feed.synd.SyndEntry;
@Component
public class FeedDeliveryEventServiceActivator {
@ServiceActivator
public void activate(Message<SyndEntry> message) throws Exception {
MessageHistory history = MessageHistory.read(message);
for (Properties properties : history) {
System.out.println(properties);
}
SyndEntry syndEntry = message.getPayload();
System.out.println( "Publishing new SyndEntry " + syndEntry.getUri() +":"+
syndEntry.getPublishedDate().toString()+ ":"+ syndEntry.getPublishedDate().getTime());
// System.out.println( syndEntry.toString());
// System.out.println("Delivery! " + ToStringBuilder.reflectionToString(evtMsg));
}
}

View File

@@ -21,10 +21,13 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.when;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.springframework.integration.Message;
@@ -36,6 +39,13 @@ import com.sun.syndication.feed.synd.SyndFeed;
*
*/
public class FeedEntryReaderMessageSourceTests {
@Before
public void prepare(){
File persisterFile = new File(System.getProperty("user.home") + "/temp/spring-integration", "feedReader.last.entry");
if (persisterFile.exists()){
persisterFile.delete();
}
}
@Test(expected=IllegalArgumentException.class)
public void testFailureWhenNotInitialized(){
@@ -49,6 +59,7 @@ public class FeedEntryReaderMessageSourceTests {
SyndFeed feed = mock(SyndFeed.class);
when(feedReaderSource.receiveSyndFeed()).thenReturn(feed);
FeedEntryReaderMessageSource feedEntrySource = new FeedEntryReaderMessageSource(feedReaderSource);
feedEntrySource.setPersistentIdentifier("feedReader");
feedEntrySource.afterPropertiesSet();
assertNull(feedEntrySource.receive());
}
@@ -68,6 +79,7 @@ public class FeedEntryReaderMessageSourceTests {
when(feedReaderSource.receiveSyndFeed()).thenReturn(feed);
FeedEntryReaderMessageSource feedEntrySource = new FeedEntryReaderMessageSource(feedReaderSource);
feedEntrySource.setPersistentIdentifier("feedReader");
feedEntrySource.afterPropertiesSet();
Message<SyndEntry> entryMessage = feedEntrySource.receive();
assertEquals(entry2, entryMessage.getPayload());
@@ -77,4 +89,79 @@ public class FeedEntryReaderMessageSourceTests {
entryMessage = feedEntrySource.receive();
assertNull(entryMessage);
}
// will test, that last feed entry is remembered between the sessions
// and no duplicate entries are retrieved
@Test
public void testReceieveFeedWithRealEntriesAndRepeatWithPersistentIdentifier() throws Exception{
FeedReaderMessageSource feedReaderSource =
new FeedReaderMessageSource(new URL("file:src/test/java/org/springframework/integration/feed/sample.rss"));
FeedEntryReaderMessageSource feedEntrySource = new FeedEntryReaderMessageSource(feedReaderSource);
feedEntrySource.setPersistentIdentifier("feedReader");
feedEntrySource.afterPropertiesSet();
SyndEntry entry1 = feedEntrySource.receive().getPayload();
SyndEntry entry2 = feedEntrySource.receive().getPayload();
SyndEntry entry3 = feedEntrySource.receive().getPayload();
assertNull(feedEntrySource.receive()); // only 3 entries in the test feed
assertEquals("Spring Integration download", entry1.getTitle().trim());
assertEquals(1266088337000L, entry1.getPublishedDate().getTime());
assertEquals("Check out Spring Integration forums", entry2.getTitle().trim());
assertEquals(1268469501000L, entry2.getPublishedDate().getTime());
assertEquals("Spring Integration adapters", entry3.getTitle().trim());
assertEquals(1272044098000L, entry3.getPublishedDate().getTime());
// now test that what's been read is no longer retrieved
feedEntrySource = new FeedEntryReaderMessageSource(feedReaderSource);
feedEntrySource.setPersistentIdentifier("feedReader");
feedEntrySource.afterPropertiesSet();
assertNull(feedEntrySource.receive());
assertNull(feedEntrySource.receive());
assertNull(feedEntrySource.receive());
}
// will test, that last feed entry is NOT remembered between the sessions, since
// persister is not used due to the lack of persistentIdentifier (id attribute in xml)
// and the same entries are retrieved again
@Test
public void testReceieveFeedWithRealEntriesAndRepeatNoPersistentIdentifier() throws Exception{
FeedReaderMessageSource feedReaderSource =
new FeedReaderMessageSource(new URL("file:src/test/java/org/springframework/integration/feed/sample.rss"));
FeedEntryReaderMessageSource feedEntrySource = new FeedEntryReaderMessageSource(feedReaderSource);
feedEntrySource.afterPropertiesSet();
SyndEntry entry1 = feedEntrySource.receive().getPayload();
SyndEntry entry2 = feedEntrySource.receive().getPayload();
SyndEntry entry3 = feedEntrySource.receive().getPayload();
assertNull(feedEntrySource.receive()); // only 3 entries in the test feed
assertEquals("Spring Integration download", entry1.getTitle().trim());
assertEquals(1266088337000L, entry1.getPublishedDate().getTime());
assertEquals("Check out Spring Integration forums", entry2.getTitle().trim());
assertEquals(1268469501000L, entry2.getPublishedDate().getTime());
assertEquals("Spring Integration adapters", entry3.getTitle().trim());
assertEquals(1272044098000L, entry3.getPublishedDate().getTime());
// UNLIKE the previous test
// now test that what's been read is read AGAIN
feedEntrySource = new FeedEntryReaderMessageSource(feedReaderSource);
feedEntrySource.afterPropertiesSet();
entry1 = feedEntrySource.receive().getPayload();
entry2 = feedEntrySource.receive().getPayload();
entry3 = feedEntrySource.receive().getPayload();
assertNull(feedEntrySource.receive()); // only 3 entries in the test feed
assertEquals("Spring Integration download", entry1.getTitle().trim());
assertEquals(1266088337000L, entry1.getPublishedDate().getTime());
assertEquals("Check out Spring Integration forums", entry2.getTitle().trim());
assertEquals(1268469501000L, entry2.getPublishedDate().getTime());
assertEquals("Spring Integration adapters", entry3.getTitle().trim());
assertEquals(1272044098000L, entry3.getPublishedDate().getTime());
}
}

View File

@@ -1,37 +0,0 @@
<?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:feed="http://www.springframework.org/schema/integration/feed"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.0.xsd
http://www.springframework.org/schema/integration/feed http://www.springframework.org/schema/integration/feed/spring-integration-feed.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<int:message-history/>
<bean id="activator" class="org.springframework.integration.feed.FeedDeliveryEventServiceActivator"/>
<!--
this will keep state in /tmp/feedDemo.properties and not deliver anything until the feed has an updated pub date
to see the feed again, rm /tmp/feedDemo.properties
-->
<!-- <bean id="metadataPersister" class="org.springframework.integration.context.metadata.PropertiesBasedMetadataPersister">-->
<!-- <property name="uniqueName" value="feedDemo"/>-->
<!-- </bean>-->
<!--http://twitter.com/statuses/public_timeline.atom
-->
<feed:inbound-channel-adapter id="feedAdapter" channel="feedChanges" feedUrl="http://feeds.bbci.co.uk/news/rss.xml" >
<int:poller fixed-rate="10000" max-messages-per-poll="-1"/>
</feed:inbound-channel-adapter>
<int:channel id="feedChanges"/>
<int:service-activator input-channel="feedChanges" ref="activator" />
</beans>

View File

@@ -1,33 +0,0 @@
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.feed;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class TestFeedEventDelivery {
@Test
@Ignore
public void testDeliveryOfFeed() throws Exception {
Thread.sleep(1000 * 60);
}
}

View File

@@ -6,10 +6,14 @@
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.0.xsd
http://www.springframework.org/schema/integration/feed http://www.springframework.org/schema/integration/feed/spring-integration-feed-2.0.xsd">
<int-feed:inbound-channel-adapter id="feedAdapter" channel="feedChannel"
<int-feed:inbound-channel-adapter id="feedAdapter" channel="feedChannel" auto-startup="false"
feedUrl="file:src/test/java/org/springframework/integration/feed/config/sample.rss">
<int:poller fixed-rate="10000" max-messages-per-poll="100" />
</int-feed:inbound-channel-adapter>
<int:channel id="feedChannel" />
<int:channel id="feedChannel">
<int:queue/>
</int:channel>
</beans>

View File

@@ -6,7 +6,7 @@
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.0.xsd
http://www.springframework.org/schema/integration/feed http://www.springframework.org/schema/integration/feed/spring-integration-feed-2.0.xsd">
<int-feed:inbound-channel-adapter id="feedAdapter" channel="feedChannel"
<int-feed:inbound-channel-adapter id="feedAdapter" channel="feedChannel" auto-startup="false"
feedUrl="http://feeds.bbci.co.uk/news/rss.xml">
<int:poller fixed-rate="10000" max-messages-per-poll="100" />
</int-feed:inbound-channel-adapter>

View File

@@ -15,15 +15,20 @@
*/
package org.springframework.integration.feed.config;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.io.File;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.context.ApplicationContext;
@@ -36,21 +41,30 @@ import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
import org.springframework.integration.feed.FeedEntryReaderMessageSource;
import org.springframework.integration.feed.FeedReaderMessageSource;
import org.springframework.integration.feed.FileUrlFeedFetcher;
import org.springframework.integration.history.MessageHistory;
import org.springframework.integration.test.util.TestUtils;
import com.sun.syndication.feed.synd.SyndEntry;
import com.sun.syndication.fetcher.impl.AbstractFeedFetcher;
import com.sun.syndication.fetcher.impl.HttpURLFeedFetcher;
/**
* @author Oleg Zhurakousky
*
*/
public class FeedMessageSourceBeanDefinitionParserTests {
private static CountDownLatch latch;
@Before
public void prepare(){
File persisterFile = new File(System.getProperty("user.home") + "/temp/spring-integration", "feedAdapter.last.entry");
if (persisterFile.exists()){
persisterFile.delete();
}
}
@Test
public void validateSuccessfullConfiguration(){
ApplicationContext context =
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("FeedMessageSourceBeanDefinitionParserTests-file-context.xml", this.getClass());
SourcePollingChannelAdapter adapter = context.getBean("feedAdapter", SourcePollingChannelAdapter.class);
FeedEntryReaderMessageSource source = (FeedEntryReaderMessageSource) TestUtils.getPropertyValue(adapter, "source");
@@ -65,26 +79,55 @@ public class FeedMessageSourceBeanDefinitionParserTests {
feedReaderMessageSource = (FeedReaderMessageSource) TestUtils.getPropertyValue(source, "feedReaderMessageSource");
fetcher = (AbstractFeedFetcher) TestUtils.getPropertyValue(feedReaderMessageSource, "fetcher");
assertTrue(fetcher instanceof HttpURLFeedFetcher);
context.destroy();
}
@Test
public void validateSuccessfullNewsRetrievalFile() throws Exception{
public void validateSuccessfullNewsRetrievalWithFileUrlAndMessageHistory() throws Exception{
File persisterFile = new File(System.getProperty("user.home") + "/temp/spring-integration", "feedAdapterUsage.last.entry");
if (persisterFile.exists()){
persisterFile.delete();
}
//Test file samples.rss has 3 news items
final CountDownLatch latch = new CountDownLatch(3);
MessageHandler handler = spy(new MessageHandler() {
public void handleMessage(Message<?> message) throws MessagingException {
latch.countDown();
}
});
ApplicationContext context =
new ClassPathXmlApplicationContext("FeedMessageSourceBeanDefinitionParserTests-file-context.xml", this.getClass());
DirectChannel feedChannel = context.getBean("feedChannel", DirectChannel.class);
feedChannel.subscribe(handler);
latch = spy(new CountDownLatch(3));
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("FeedMessageSourceBeanDefinitionParserTests-file-usage-context.xml", this.getClass());
latch.await(5, TimeUnit.SECONDS);
verify(handler, times(3)).handleMessage(Mockito.any(Message.class));
verify(latch, times(3)).countDown();
context.destroy();
// since we are not deleting the persister file
// in this iteration no new feeds will be received and the latch will timeout
latch = spy(new CountDownLatch(3));
context =
new ClassPathXmlApplicationContext("FeedMessageSourceBeanDefinitionParserTests-file-usage-context.xml", this.getClass());
latch.await(5, TimeUnit.SECONDS);
verify(latch, times(0)).countDown();
context.destroy();
}
@Test
public void validateSuccessfullNewsRetrievalHttp() throws Exception{
public void validateSuccessfullNewsRetrievalWithFileUrlNoPersistentIdentifier() throws Exception{
//Test file samples.rss has 3 news items
latch = spy(new CountDownLatch(3));
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("FeedMessageSourceBeanDefinitionParserTests-file-usage-noid-context.xml", this.getClass());
latch.await(5, TimeUnit.SECONDS);
verify(latch, times(3)).countDown();
context.destroy();
// since we are not deleting the persister file
// in this iteration no new feeds will be received and the latch will timeout
latch = spy(new CountDownLatch(3));
context =
new ClassPathXmlApplicationContext("FeedMessageSourceBeanDefinitionParserTests-file-usage-noid-context.xml", this.getClass());
latch.await(5, TimeUnit.SECONDS);
verify(latch, times(3)).countDown();
context.destroy();
}
@Test
@Ignore // goes against the real feed
public void validateSuccessfullNewsRetrievalWithHttpUrl() throws Exception{
final CountDownLatch latch = new CountDownLatch(3);
MessageHandler handler = spy(new MessageHandler() {
public void handleMessage(Message<?> message) throws MessagingException {
@@ -98,4 +141,29 @@ public class FeedMessageSourceBeanDefinitionParserTests {
latch.await(5, TimeUnit.SECONDS);
verify(handler, atLeast(3)).handleMessage(Mockito.any(Message.class));
}
public static class SampleService{
public void receiveFeedEntry(Message<?> message){
MessageHistory history = MessageHistory.read(message);
assertTrue(history.size() == 3);
Properties historyItem = history.get(0);
assertEquals("feedAdapterUsage", historyItem.get("name"));
assertEquals("feed:inbound-channel-adapter", historyItem.get("type"));
historyItem = history.get(1);
assertEquals("feedChannelUsage", historyItem.get("name"));
assertEquals("channel", historyItem.get("type"));
historyItem = history.get(2);
assertEquals("sampleActivator", historyItem.get("name"));
assertEquals("service-activator", historyItem.get("type"));
latch.countDown();
}
}
public static class SampleServiceNoHistory{
public void receiveFeedEntry(SyndEntry entry){
latch.countDown();
}
}
}

View File

@@ -1,52 +1,52 @@
<rss version="2.0">
<channel>
<title>ASP @ BellaOnline</title>
<link>http://www.bellaonline.com/Site/asp</link>
<title>Spring Integration</title>
<link>http://www.springsource.org/spring-integration</link>
<description>
Learn to program in ASP, and enhance your ASP skills to add great new functionality to your website!
Spring Integration is a really cool framework
</description>
<language>en-us</language>
<copyright>Copyright 2001-2005 BellaOnline.com
<copyright>Copyright 2004-2010 SpringSource/VMWare
All Rights Reserved.</copyright>
<lastBuildDate>Tue, 12 Apr 2005 14:21:32 EST</lastBuildDate>
<lastBuildDate>Tue, 12 Apr 2010 18:21:32 EST</lastBuildDate>
<ttl>240</ttl>
<image>
<url>http://www.bellaonline.com/images/bella.gif</url>
<title>ASP @ BellaOnline</title>
<link>http://asp.bellaonline.com</link>
<url>http://www.springsource.org/sites/all/themes/dotorg09/images/dotorg09_logo.png</url>
<title>Spring Integration</title>
<link>http://www.springsource.org/spring-integration</link>
</image>
<item>
<title>
Using ASP to Code an RSS Feed
Spring Integration adapters
</title>
<link>http://www.bellaonline.com/articles/art30646.asp</link>
<link>http://www.springsource.org/extensions/se-sia</link>
<description>
RSS feeds let you easily syndicate your content to an end user or another website. ASP can help you easily create your own RSS feed for your website.
Spring Integration adapters are realy cool
</description>
<pubDate>Tue, 12 Apr 2005 13:59:56 EST</pubDate>
<pubDate>Tue, 23 Apr 2010 12:34:58 EST</pubDate>
</item>
<item>
<title>
RecordCount and Count
Spring Integration download
</title>
<link>http://www.bellaonline.com/articles/art30403.asp</link>
<link>http://www.springsource.com/products/spring-community-download</link>
<description>
If you're trying to figure out how many records are in a given SQL result set, you can use either the RecordCount or Count command. Both work in different ways.
Download Spring Integration
</description>
<pubDate>Sun, 3 Apr 2005 17:12:17 EST</pubDate>
<pubDate>Sun, 13 Feb 2010 14:12:17 EST</pubDate>
</item>
<item>
<title>
Bubble Sort Code Technique
Check out Spring Integration forums
</title>
<link>http://www.bellaonline.com/articles/art29843.asp</link>
<link>http://forum.springsource.org/forumdisplay.php?f=42</link>
<description>
If you are sorting content into an order, one of the most simple techniques that exists is the bubble sort technique.
Spring Integration forums are awesome
</description>
<pubDate>Wed, 16 Mar 2005 00:38:21 EST</pubDate>
<pubDate>Wed, 13 Mar 2010 03:38:21 EST</pubDate>
</item>
</channel>