From 59c525a49072b4ab04111365573d389209660baf Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Sun, 17 Oct 2010 10:05:54 -0400 Subject: [PATCH] INT-786, more refactoring and tests, added FileUrlFeedFetcher which will allow feed URLs to be specified as file:// (mainly for testing) --- spring-integration-feed/.project | 6 + .../feed/FeedEntryReaderMessageSource.java | 82 +++++-------- .../feed/FeedReaderMessageSource.java | 63 +++++----- .../integration/feed/FileUrlFeedFetcher.java | 109 ++++++++++++++++++ ...FeedMessageSourceBeanDefinitionParser.java | 12 +- .../config/spring-integration-feed-2.0.xsd | 12 ++ .../src/test/java/log4j.properties | 8 ++ .../FeedEntryReaderMessageSourceTests.java | 80 +++++++++++++ ...BeanDefinitionParserTests-file-context.xml | 15 +++ ...BeanDefinitionParserTests-http-context.xml | 15 +++ ...essageSourceBeanDefinitionParserTests.java | 101 ++++++++++++++++ .../integration/feed/config/sample.rss | 53 +++++++++ 12 files changed, 461 insertions(+), 95 deletions(-) create mode 100644 spring-integration-feed/src/main/java/org/springframework/integration/feed/FileUrlFeedFetcher.java create mode 100644 spring-integration-feed/src/test/java/log4j.properties create mode 100644 spring-integration-feed/src/test/java/org/springframework/integration/feed/FeedEntryReaderMessageSourceTests.java create mode 100644 spring-integration-feed/src/test/java/org/springframework/integration/feed/config/FeedMessageSourceBeanDefinitionParserTests-file-context.xml create mode 100644 spring-integration-feed/src/test/java/org/springframework/integration/feed/config/FeedMessageSourceBeanDefinitionParserTests-http-context.xml create mode 100644 spring-integration-feed/src/test/java/org/springframework/integration/feed/config/FeedMessageSourceBeanDefinitionParserTests.java create mode 100644 spring-integration-feed/src/test/java/org/springframework/integration/feed/config/sample.rss diff --git a/spring-integration-feed/.project b/spring-integration-feed/.project index 52c91409d9..bf75fd2e68 100644 --- a/spring-integration-feed/.project +++ b/spring-integration-feed/.project @@ -20,8 +20,14 @@ + + org.springframework.ide.eclipse.core.springbuilder + + + + org.springframework.ide.eclipse.core.springnature org.maven.ide.eclipse.maven2Nature org.eclipse.jdt.core.javanature org.eclipse.wst.common.project.facet.core.nature diff --git a/spring-integration-feed/src/main/java/org/springframework/integration/feed/FeedEntryReaderMessageSource.java b/spring-integration-feed/src/main/java/org/springframework/integration/feed/FeedEntryReaderMessageSource.java index 0e23846a0f..c8dc154fdb 100644 --- a/spring-integration-feed/src/main/java/org/springframework/integration/feed/FeedEntryReaderMessageSource.java +++ b/spring-integration-feed/src/main/java/org/springframework/integration/feed/FeedEntryReaderMessageSource.java @@ -23,7 +23,6 @@ import java.util.Queue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; -import org.springframework.context.Lifecycle; import org.springframework.integration.Message; import org.springframework.integration.context.IntegrationObjectSupport; import org.springframework.integration.core.MessageSource; @@ -41,20 +40,20 @@ import com.sun.syndication.feed.synd.SyndFeed; * @author Mario Gray * @author Oleg Zhurakousky */ -public class FeedEntryReaderMessageSource extends IntegrationObjectSupport implements MessageSource, Lifecycle { +public class FeedEntryReaderMessageSource extends IntegrationObjectSupport implements MessageSource{ private volatile Map persisterMap = new ConcurrentHashMap(); private volatile Queue entries = new ConcurrentLinkedQueue(); private volatile FeedReaderMessageSource feedReaderMessageSource; private final Object monitor = new Object(); private volatile String feedMetadataIdKey; - private volatile String feedUrl; - private volatile boolean running; + private volatile boolean initialized; private volatile long lastTime = -1; private Comparator syndEntryComparator = new Comparator() { public int compare(SyndEntry syndEntry, SyndEntry syndEntry1) { - long x = sortId(syndEntry) - sortId(syndEntry1); + long x = syndEntry.getPublishedDate().getTime() - + syndEntry1.getPublishedDate().getTime(); if (x < -1) { return -1; } @@ -65,13 +64,10 @@ public class FeedEntryReaderMessageSource extends IntegrationObjectSupport imple } }; - public void setFeedUrl(String feedUrl) { - this.feedUrl = feedUrl; - } - - public String getFeedUrl() { - return feedUrl; - } + public FeedEntryReaderMessageSource(FeedReaderMessageSource feedReaderMessageSource) { + Assert.notNull(feedReaderMessageSource, "'feedReaderMessageSource' must not be null"); + this.feedReaderMessageSource = feedReaderMessageSource; + } /** * Allows you to provide your own implementation of 'persisterMap' instead of relying on * your own which is in-memory. @@ -83,25 +79,21 @@ public class FeedEntryReaderMessageSource extends IntegrationObjectSupport imple this.persisterMap = persisterMap; } - public void setRunning(boolean running) { - this.running = running; - } - - public boolean isRunning() { - return running; - } - - public void stop() { - this.feedReaderMessageSource.stop(); - this.setRunning(false); - } - public String getComponentType(){ return "feed:inbound-channel-adapter"; } + public Message receive() { + Assert.isTrue(this.initialized, "'FeedEntryReaderMessageSource' must be initialized before it can produce Messages"); + SyndEntry se = doReceieve(); + if (se == null) { + return null; + } + return MessageBuilder.withPayload(se).build(); + } + @SuppressWarnings("unchecked") - public SyndEntry receiveSyndEntry() { + private SyndEntry doReceieve() { synchronized (this.monitor) { SyndEntry nextUp = pollAndCache(); @@ -115,58 +107,36 @@ public class FeedEntryReaderMessageSource extends IntegrationObjectSupport imple if (null != feedEntries) { Collections.sort(feedEntries, syndEntryComparator); for (SyndEntry se : feedEntries) { - long sort = this.sortId(se); - if (sort > this.lastTime) - entries.add(se); + long publishedTime = se.getPublishedDate().getTime(); + if (publishedTime > this.lastTime){ + entries.add(se); + } } } } return pollAndCache(); } } - - public Message receive() { - SyndEntry se = receiveSyndEntry(); - if (se == null) { - return null; - } - return MessageBuilder.withPayload(se).build(); - } - - public void start() { - this.feedReaderMessageSource.start(); - this.setRunning(true); - - } - private long sortId(SyndEntry entry) { - return entry.getPublishedDate().getTime(); - } - @Override protected void onInit() throws Exception { - Assert.notNull(this.feedUrl, "the feedUrl can't be null"); - this.feedReaderMessageSource = new FeedReaderMessageSource(); - this.feedReaderMessageSource.setFeedUrl(this.feedUrl); - this.feedReaderMessageSource.setBeanName(this.getComponentName()); - this.feedReaderMessageSource.afterPropertiesSet(); - // setup persistence of metadata - this.feedMetadataIdKey = FeedEntryReaderMessageSource.class.getName() + "#" + feedUrl; + this.feedMetadataIdKey = FeedEntryReaderMessageSource.class.getName() + "#" + feedReaderMessageSource.getFeedUrl(); String lastTime = (String) this.persisterMap.get(this.feedMetadataIdKey); if (lastTime != null && !lastTime.trim().equalsIgnoreCase("")) { this.lastTime = Long.parseLong(lastTime); } + this.initialized = true; } - private SyndEntry pollAndCache() { + private SyndEntry pollAndCache() { SyndEntry next = this.entries.poll(); if (next == null) { return null; } - this.lastTime = sortId(next); + this.lastTime = next.getPublishedDate().getTime(); this.persisterMap.put(this.feedMetadataIdKey, this.lastTime + ""); return next; } diff --git a/spring-integration-feed/src/main/java/org/springframework/integration/feed/FeedReaderMessageSource.java b/spring-integration-feed/src/main/java/org/springframework/integration/feed/FeedReaderMessageSource.java index c1d5fcf935..e2b6f49983 100644 --- a/spring-integration-feed/src/main/java/org/springframework/integration/feed/FeedReaderMessageSource.java +++ b/spring-integration-feed/src/main/java/org/springframework/integration/feed/FeedReaderMessageSource.java @@ -19,7 +19,6 @@ import java.net.URL; import java.util.concurrent.ConcurrentLinkedQueue; import org.springframework.beans.factory.InitializingBean; -import org.springframework.context.Lifecycle; import org.springframework.integration.Message; import org.springframework.integration.MessagingException; import org.springframework.integration.context.IntegrationObjectSupport; @@ -30,6 +29,7 @@ import org.springframework.util.Assert; import com.sun.syndication.feed.synd.SyndFeed; import com.sun.syndication.fetcher.FetcherEvent; import com.sun.syndication.fetcher.FetcherListener; +import com.sun.syndication.fetcher.impl.AbstractFeedFetcher; import com.sun.syndication.fetcher.impl.FeedFetcherCache; import com.sun.syndication.fetcher.impl.HashMapFeedInfoCache; import com.sun.syndication.fetcher.impl.HttpURLFeedFetcher; @@ -43,48 +43,42 @@ import com.sun.syndication.fetcher.impl.HttpURLFeedFetcher; * @author Mario Gray * @author Oleg Zhurakousky */ -class FeedReaderMessageSource extends IntegrationObjectSupport - implements InitializingBean, Lifecycle, MessageSource { +public class FeedReaderMessageSource extends IntegrationObjectSupport + implements InitializingBean, MessageSource { - private volatile boolean running; - private volatile String feedUrl; - private volatile URL feedURLObject; + private final AbstractFeedFetcher fetcher; + private final Object syndFeedMonitor = new Object(); + + private volatile URL feedUrl; private volatile FeedFetcherCache fetcherCache; - private volatile HttpURLFeedFetcher fetcher; - private volatile ConcurrentLinkedQueue syndFeeds; + private volatile ConcurrentLinkedQueue syndFeeds = new ConcurrentLinkedQueue(); private volatile MyFetcherListener myFetcherListener; - private final Object syndFeedMonitor = new Object(); - public FeedReaderMessageSource() { - syndFeeds = new ConcurrentLinkedQueue(); - } - - public void setFeedUrl(final String feedUrl) { + + public FeedReaderMessageSource(URL feedUrl) { this.feedUrl = feedUrl; + if (feedUrl.getProtocol().equals("file")){ + fetcher = new FileUrlFeedFetcher(); + } + else if (feedUrl.getProtocol().equals("http")){ + fetcherCache = HashMapFeedInfoCache.getInstance(); + fetcher = new HttpURLFeedFetcher(fetcherCache); + } + else{ + throw new IllegalArgumentException("Unsupported URL protocol: " + feedUrl.getProtocol()); + } } - public String getFeedUrl() { + public URL getFeedUrl() { return feedUrl; } - - public void start() { - this.running = true; - } - - public void stop() { - this.running = false; - } - - public boolean isRunning() { - return this.running; - } - + public SyndFeed receiveSyndFeed() { SyndFeed returnedSyndFeed = null; try { synchronized (syndFeedMonitor) { - returnedSyndFeed = fetcher.retrieveFeed(this.feedURLObject); + returnedSyndFeed = fetcher.retrieveFeed(this.feedUrl); logger.debug("attempted to retrieve feed '" + this.feedUrl + "'"); if (returnedSyndFeed == null) { @@ -93,7 +87,8 @@ class FeedReaderMessageSource extends IntegrationObjectSupport } } } catch (Exception e) { - throw new MessagingException("Exception thrown when trying to retrive feed at url '" + this.feedURLObject + "'", e); + e.printStackTrace(); + throw new MessagingException("Exception thrown when trying to retrive feed at url '" + this.feedUrl + "'", e); } return returnedSyndFeed; @@ -106,20 +101,16 @@ class FeedReaderMessageSource extends IntegrationObjectSupport return null; } - return MessageBuilder.withPayload(syndFeed).setHeader(FeedConstants.FEED_URL, this.feedURLObject).build(); + return MessageBuilder.withPayload(syndFeed).setHeader(FeedConstants.FEED_URL, this.feedUrl).build(); } @Override protected void onInit() throws Exception { -// myFetcherListener = new MyFetcherListener(); - fetcherCache = HashMapFeedInfoCache.getInstance(); - - fetcher = new HttpURLFeedFetcher(fetcherCache); + fetcher.addFetcherEventListener(myFetcherListener); Assert.notNull(this.feedUrl, "the feedURL can't be null"); - feedURLObject = new URL(this.feedUrl); } class MyFetcherListener implements FetcherListener { diff --git a/spring-integration-feed/src/main/java/org/springframework/integration/feed/FileUrlFeedFetcher.java b/spring-integration-feed/src/main/java/org/springframework/integration/feed/FileUrlFeedFetcher.java new file mode 100644 index 0000000000..1d3e2abcf2 --- /dev/null +++ b/spring-integration-feed/src/main/java/org/springframework/integration/feed/FileUrlFeedFetcher.java @@ -0,0 +1,109 @@ +/* +* Copyright 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 java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.util.zip.GZIPInputStream; + +import com.sun.syndication.feed.synd.SyndFeed; +import com.sun.syndication.fetcher.FetcherEvent; +import com.sun.syndication.fetcher.FetcherException; +import com.sun.syndication.fetcher.impl.AbstractFeedFetcher; +import com.sun.syndication.fetcher.impl.SyndFeedInfo; +import com.sun.syndication.io.FeedException; +import com.sun.syndication.io.SyndFeedInput; +import com.sun.syndication.io.XmlReader; + +/** + * @author Oleg Zhurakousky + * @since 2.0 + */ +public class FileUrlFeedFetcher extends AbstractFeedFetcher { + + /* (non-Javadoc) + * @see com.sun.syndication.fetcher.FeedFetcher#retrieveFeed(java.net.URL) + */ + public SyndFeed retrieveFeed(URL feedUrl) throws IllegalArgumentException, + IOException, FeedException, FetcherException { + if (feedUrl == null) { + throw new IllegalArgumentException("null is not a valid URL"); + } + + URLConnection connection = feedUrl.openConnection(); + + SyndFeedInfo syndFeedInfo = new SyndFeedInfo(); + retrieveAndCacheFeed(feedUrl, syndFeedInfo, connection); + return syndFeedInfo.getSyndFeed(); + } + + protected void retrieveAndCacheFeed(URL feedUrl, SyndFeedInfo syndFeedInfo, URLConnection connection) throws IllegalArgumentException, FeedException, FetcherException, IOException { + resetFeedInfo(feedUrl, syndFeedInfo, connection); + } + + protected void resetFeedInfo(URL orignalUrl, SyndFeedInfo syndFeedInfo, URLConnection connection) throws IllegalArgumentException, IOException, FeedException { + // need to always set the URL because this may have changed due to 3xx redirects + syndFeedInfo.setUrl(connection.getURL()); + + // the ID is a persistant value that should stay the same even if the URL for the + // feed changes (eg, by 3xx redirects) + syndFeedInfo.setId(orignalUrl.toString()); + + // This will be 0 if the server doesn't support or isn't setting the last modified header + syndFeedInfo.setLastModified(new Long(connection.getLastModified())); + + // get the contents + InputStream inputStream = null; + try { + inputStream = connection.getInputStream(); + SyndFeed syndFeed = getSyndFeedFromStream(inputStream, connection); + syndFeedInfo.setSyndFeed(syndFeed); + } finally { + if (inputStream != null) { + inputStream.close(); + } + } + } + private SyndFeed getSyndFeedFromStream(InputStream inputStream, URLConnection connection) throws IOException, IllegalArgumentException, FeedException { + SyndFeed feed = readSyndFeedFromStream(inputStream, connection); + fireEvent(FetcherEvent.EVENT_TYPE_FEED_RETRIEVED, connection, feed); + return feed; + } + private SyndFeed readSyndFeedFromStream(InputStream inputStream, URLConnection connection) throws IOException, IllegalArgumentException, FeedException { + BufferedInputStream is; + if ("gzip".equalsIgnoreCase(connection.getContentEncoding())) { + // handle gzip encoded content + is = new BufferedInputStream(new GZIPInputStream(inputStream)); + } else { + is = new BufferedInputStream(inputStream); + } + + XmlReader reader = null; + if (connection.getHeaderField("Content-Type") != null) { + reader = new XmlReader(is, connection.getHeaderField("Content-Type"), true); + } else { + reader = new XmlReader(is, true); + } + + SyndFeedInput syndFeedInput = new SyndFeedInput(); + syndFeedInput.setPreserveWireFeed(isPreserveWireFeed()); + + return syndFeedInput.build(reader); + } +} diff --git a/spring-integration-feed/src/main/java/org/springframework/integration/feed/config/FeedMessageSourceBeanDefinitionParser.java b/spring-integration-feed/src/main/java/org/springframework/integration/feed/config/FeedMessageSourceBeanDefinitionParser.java index 4f24e10e9d..12ecd2aa20 100644 --- a/spring-integration-feed/src/main/java/org/springframework/integration/feed/config/FeedMessageSourceBeanDefinitionParser.java +++ b/spring-integration-feed/src/main/java/org/springframework/integration/feed/config/FeedMessageSourceBeanDefinitionParser.java @@ -32,9 +32,15 @@ public class FeedMessageSourceBeanDefinitionParser extends AbstractPollingInboun @Override protected String parseSource(final Element element, final ParserContext parserContext) { - BeanDefinitionBuilder feedBuilder = + BeanDefinitionBuilder feedEntryBuilder = BeanDefinitionBuilder.genericBeanDefinition("org.springframework.integration.feed.FeedEntryReaderMessageSource"); - feedBuilder.addPropertyValue("feedUrl", element.getAttribute("feedUrl")); - return BeanDefinitionReaderUtils.registerWithGeneratedName(feedBuilder.getBeanDefinition(), parserContext.getRegistry()); + + BeanDefinitionBuilder feedBuilder = + BeanDefinitionBuilder.genericBeanDefinition("org.springframework.integration.feed.FeedReaderMessageSource"); + feedBuilder.addConstructorArgValue(element.getAttribute("feedUrl")); + + feedEntryBuilder.addConstructorArgValue(feedBuilder.getBeanDefinition()); + + return BeanDefinitionReaderUtils.registerWithGeneratedName(feedEntryBuilder.getBeanDefinition(), parserContext.getRegistry()); } } \ No newline at end of file diff --git a/spring-integration-feed/src/main/resources/org/springframework/integration/feed/config/spring-integration-feed-2.0.xsd b/spring-integration-feed/src/main/resources/org/springframework/integration/feed/config/spring-integration-feed-2.0.xsd index 3f53129fce..71cb01657f 100644 --- a/spring-integration-feed/src/main/resources/org/springframework/integration/feed/config/spring-integration-feed-2.0.xsd +++ b/spring-integration-feed/src/main/resources/org/springframework/integration/feed/config/spring-integration-feed-2.0.xsd @@ -37,6 +37,18 @@ + + + + Allows you to inject Map + + + + + + + + diff --git a/spring-integration-feed/src/test/java/log4j.properties b/spring-integration-feed/src/test/java/log4j.properties new file mode 100644 index 0000000000..0c10e7ac64 --- /dev/null +++ b/spring-integration-feed/src/test/java/log4j.properties @@ -0,0 +1,8 @@ +log4j.rootCategory=WARN, stdout + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%c{1}: %m%n + +log4j.category.org.springframework.integration=WARN +log4j.category.org.springframework.integration.feed=DEBUG diff --git a/spring-integration-feed/src/test/java/org/springframework/integration/feed/FeedEntryReaderMessageSourceTests.java b/spring-integration-feed/src/test/java/org/springframework/integration/feed/FeedEntryReaderMessageSourceTests.java new file mode 100644 index 0000000000..418b29cd2f --- /dev/null +++ b/spring-integration-feed/src/test/java/org/springframework/integration/feed/FeedEntryReaderMessageSourceTests.java @@ -0,0 +1,80 @@ +/* + * 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 static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.junit.Test; +import org.springframework.integration.Message; + +import com.sun.syndication.feed.synd.SyndEntry; +import com.sun.syndication.feed.synd.SyndFeed; + +/** + * @author Oleg Zhurakousky + * + */ +public class FeedEntryReaderMessageSourceTests { + + @Test(expected=IllegalArgumentException.class) + public void testFailureWhenNotInitialized(){ + FeedEntryReaderMessageSource feedEntrySource = new FeedEntryReaderMessageSource(mock(FeedReaderMessageSource.class)); + feedEntrySource.receive(); + } + + @Test + public void testReceieveFeedWithNoEntries(){ + FeedReaderMessageSource feedReaderSource = mock(FeedReaderMessageSource.class); + SyndFeed feed = mock(SyndFeed.class); + when(feedReaderSource.receiveSyndFeed()).thenReturn(feed); + FeedEntryReaderMessageSource feedEntrySource = new FeedEntryReaderMessageSource(feedReaderSource); + feedEntrySource.afterPropertiesSet(); + assertNull(feedEntrySource.receive()); + } + @Test + public void testReceieveFeedWithEntriesSorted(){ + FeedReaderMessageSource feedReaderSource = mock(FeedReaderMessageSource.class); + SyndFeed feed = mock(SyndFeed.class); + SyndEntry entry1 = mock(SyndEntry.class); + SyndEntry entry2 = mock(SyndEntry.class); + when(entry1.getPublishedDate()).thenReturn(new Date(System.currentTimeMillis())); + when(entry2.getPublishedDate()).thenReturn(new Date(System.currentTimeMillis()-10000)); + + List entries = new ArrayList(); + entries.add(entry2); + entries.add(entry1); + when(feed.getEntries()).thenReturn(entries); + when(feedReaderSource.receiveSyndFeed()).thenReturn(feed); + + FeedEntryReaderMessageSource feedEntrySource = new FeedEntryReaderMessageSource(feedReaderSource); + feedEntrySource.afterPropertiesSet(); + Message entryMessage = feedEntrySource.receive(); + assertEquals(entry2, entryMessage.getPayload()); + entryMessage = feedEntrySource.receive(); + assertEquals(entry1, entryMessage.getPayload()); + reset(feed); + entryMessage = feedEntrySource.receive(); + assertNull(entryMessage); + } +} diff --git a/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/FeedMessageSourceBeanDefinitionParserTests-file-context.xml b/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/FeedMessageSourceBeanDefinitionParserTests-file-context.xml new file mode 100644 index 0000000000..17a1c35c40 --- /dev/null +++ b/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/FeedMessageSourceBeanDefinitionParserTests-file-context.xml @@ -0,0 +1,15 @@ + + + + + + + + + \ No newline at end of file diff --git a/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/FeedMessageSourceBeanDefinitionParserTests-http-context.xml b/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/FeedMessageSourceBeanDefinitionParserTests-http-context.xml new file mode 100644 index 0000000000..6756557d68 --- /dev/null +++ b/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/FeedMessageSourceBeanDefinitionParserTests-http-context.xml @@ -0,0 +1,15 @@ + + + + + + + + + \ No newline at end of file diff --git a/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/FeedMessageSourceBeanDefinitionParserTests.java b/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/FeedMessageSourceBeanDefinitionParserTests.java new file mode 100644 index 0000000000..17f2bcab7b --- /dev/null +++ b/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/FeedMessageSourceBeanDefinitionParserTests.java @@ -0,0 +1,101 @@ +/* + * 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.config; + +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.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.Message; +import org.springframework.integration.MessagingException; +import org.springframework.integration.channel.DirectChannel; +import org.springframework.integration.core.MessageHandler; +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.test.util.TestUtils; + +import com.sun.syndication.fetcher.impl.AbstractFeedFetcher; +import com.sun.syndication.fetcher.impl.HttpURLFeedFetcher; + + +/** + * @author Oleg Zhurakousky + * + */ +public class FeedMessageSourceBeanDefinitionParserTests { + + @Test + public void validateSuccessfullConfiguration(){ + ApplicationContext context = + new ClassPathXmlApplicationContext("FeedMessageSourceBeanDefinitionParserTests-file-context.xml", this.getClass()); + SourcePollingChannelAdapter adapter = context.getBean("feedAdapter", SourcePollingChannelAdapter.class); + FeedEntryReaderMessageSource source = (FeedEntryReaderMessageSource) TestUtils.getPropertyValue(adapter, "source"); + FeedReaderMessageSource feedReaderMessageSource = (FeedReaderMessageSource) TestUtils.getPropertyValue(source, "feedReaderMessageSource"); + AbstractFeedFetcher fetcher = (AbstractFeedFetcher) TestUtils.getPropertyValue(feedReaderMessageSource, "fetcher"); + assertTrue(fetcher instanceof FileUrlFeedFetcher); + + context = + new ClassPathXmlApplicationContext("FeedMessageSourceBeanDefinitionParserTests-http-context.xml", this.getClass()); + adapter = context.getBean("feedAdapter", SourcePollingChannelAdapter.class); + source = (FeedEntryReaderMessageSource) TestUtils.getPropertyValue(adapter, "source"); + feedReaderMessageSource = (FeedReaderMessageSource) TestUtils.getPropertyValue(source, "feedReaderMessageSource"); + fetcher = (AbstractFeedFetcher) TestUtils.getPropertyValue(feedReaderMessageSource, "fetcher"); + assertTrue(fetcher instanceof HttpURLFeedFetcher); + } + @Test + public void validateSuccessfullNewsRetrievalFile() throws Exception{ + //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.await(5, TimeUnit.SECONDS); + verify(handler, times(3)).handleMessage(Mockito.any(Message.class)); + } + @Test + public void validateSuccessfullNewsRetrievalHttp() throws Exception{ + //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-http-context.xml", this.getClass()); + DirectChannel feedChannel = context.getBean("feedChannel", DirectChannel.class); + feedChannel.subscribe(handler); + latch.await(5, TimeUnit.SECONDS); + verify(handler, atLeast(3)).handleMessage(Mockito.any(Message.class)); + } +} diff --git a/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/sample.rss b/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/sample.rss new file mode 100644 index 0000000000..cbe572a200 --- /dev/null +++ b/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/sample.rss @@ -0,0 +1,53 @@ + + +ASP @ BellaOnline +http://www.bellaonline.com/Site/asp + +Learn to program in ASP, and enhance your ASP skills to add great new functionality to your website! + +en-us +Copyright 2001-2005 BellaOnline.com +All Rights Reserved. +Tue, 12 Apr 2005 14:21:32 EST +240 + +http://www.bellaonline.com/images/bella.gif +ASP @ BellaOnline +http://asp.bellaonline.com + + + + +Using ASP to Code an RSS Feed + +http://www.bellaonline.com/articles/art30646.asp + +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. + +Tue, 12 Apr 2005 13:59:56 EST + + + + +RecordCount and Count + +http://www.bellaonline.com/articles/art30403.asp + +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. + +Sun, 3 Apr 2005 17:12:17 EST + + + + +Bubble Sort Code Technique + +http://www.bellaonline.com/articles/art29843.asp + +If you are sorting content into an order, one of the most simple techniques that exists is the bubble sort technique. + +Wed, 16 Mar 2005 00:38:21 EST + + + +