INT-1939 Twitter Search Outbound Gateway

JIRA: https://jira.spring.io/browse/INT-1939

Outbound gateway to allow on-demand variable searches.

Also add the `page-size` attribute to inbound adapters (previously
hard-coded to 20).

Also the inbound adapters now `require` a `TwitterTemplate` because even
search requires authentication.

Polishing - PR Comments

- Bump to spring-social-twitter 1.0.0.RC1
- Remove `requires-reply`
- Polishing
- Add integration test

INT-1939 Doc and Rework

Now supports up to 4 search args (as well as a
SearchParameters).

INT-1939: Polishing
This commit is contained in:
Gary Russell
2014-04-11 16:12:38 +03:00
committed by Artem Bilan
parent 4577200e66
commit 7082b9e4cb
28 changed files with 1106 additions and 175 deletions

View File

@@ -23,22 +23,25 @@
<channel id="inbound_mentions"/>
<twitter:mentions-inbound-channel-adapter id="mentionAdapter"
twitter-template="twitter"
channel="inbound_mentions"
<twitter:mentions-inbound-channel-adapter id="mentionAdapter"
twitter-template="twitter"
channel="inbound_mentions"
page-size="23"
auto-startup="false">
<poller fixed-rate="5000" max-messages-per-poll="3"/>
</twitter:mentions-inbound-channel-adapter>
<twitter:dm-inbound-channel-adapter id="dmAdapter"
twitter-template="twitter"
channel="inbound_mentions"
<twitter:dm-inbound-channel-adapter id="dmAdapter"
twitter-template="twitter"
channel="inbound_mentions"
page-size="45"
auto-startup="false">
<poller fixed-rate="5000" max-messages-per-poll="3"/>
</twitter:dm-inbound-channel-adapter>
<twitter:inbound-channel-adapter id="updateAdapter"
twitter-template="twitter"
channel="inbound_mentions"
<twitter:inbound-channel-adapter id="updateAdapter"
twitter-template="twitter"
channel="inbound_mentions"
page-size="67"
auto-startup="false">
<poller fixed-rate="5000" max-messages-per-poll="3"/>
</twitter:inbound-channel-adapter>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@@ -16,11 +16,12 @@
package org.springframework.integration.twitter.config;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
import org.springframework.integration.test.util.TestUtils;
@@ -32,30 +33,37 @@ import org.springframework.integration.twitter.inbound.TimelineReceivingMessageS
/**
* @author Oleg Zhurakousky
* @author Gunnar Hillert
* @author Gary Russell
*/
public class TestReceivingMessageSourceParserTests {
@Test
public void testReceivingAdapterConfigurationAutoStartup(){
ApplicationContext ac = new ClassPathXmlApplicationContext("TestReceivingMessageSourceParser-context.xml", this.getClass());
ConfigurableApplicationContext ac = new ClassPathXmlApplicationContext(
"TestReceivingMessageSourceParser-context.xml", this.getClass());
SourcePollingChannelAdapter spca = ac.getBean("mentionAdapter", SourcePollingChannelAdapter.class);
MentionsReceivingMessageSource ms = TestUtils.getPropertyValue(spca, "source", MentionsReceivingMessageSource.class);
assertEquals(Integer.valueOf(23), TestUtils.getPropertyValue(ms, "pageSize", Integer.class));
assertNotNull(ms);
spca = ac.getBean("dmAdapter", SourcePollingChannelAdapter.class);
DirectMessageReceivingMessageSource dms = TestUtils.getPropertyValue(spca, "source", DirectMessageReceivingMessageSource.class);
assertNotNull(dms);
assertEquals(Integer.valueOf(45), TestUtils.getPropertyValue(dms, "pageSize", Integer.class));
spca = ac.getBean("updateAdapter", SourcePollingChannelAdapter.class);
spca = ac.getBean("updateAdapter", SourcePollingChannelAdapter.class);
TimelineReceivingMessageSource tms = TestUtils.getPropertyValue(spca, "source", TimelineReceivingMessageSource.class);
assertEquals(Integer.valueOf(67), TestUtils.getPropertyValue(tms, "pageSize", Integer.class));
assertNotNull(tms);
ac.close();
}
@Test
public void testThatMessageSourcesAreRegisteredAsBeans(){
ApplicationContext ac = new ClassPathXmlApplicationContext("TestReceivingMessageSourceParser-context.xml", this.getClass());
ConfigurableApplicationContext ac = new ClassPathXmlApplicationContext(
"TestReceivingMessageSourceParser-context.xml", this.getClass());
MentionsReceivingMessageSource ms = ac.getBean("mentionAdapter.source", MentionsReceivingMessageSource.class);
assertNotNull(ms);
@@ -65,6 +73,7 @@ public class TestReceivingMessageSourceParserTests {
TimelineReceivingMessageSource tms = ac.getBean("updateAdapter.source", TimelineReceivingMessageSource.class);
assertNotNull(tms);
ac.close();
}
}

View File

@@ -1,44 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
xmlns="http://www.springframework.org/schema/integration"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:tool="http://www.springframework.org/schema/tool"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:twitter="http://www.springframework.org/schema/integration/twitter"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
xmlns="http://www.springframework.org/schema/integration"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:tool="http://www.springframework.org/schema/tool"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:twitter="http://www.springframework.org/schema/integration/twitter"
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.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd
http://www.springframework.org/schema/integration/twitter http://www.springframework.org/schema/integration/twitter/spring-integration-twitter.xsd">
<channel id="searchChannel"/>
<twitter:search-inbound-channel-adapter id="searchAdapter"
channel="searchChannel"
query="#springintegration"
auto-startup="false">
<poller fixed-rate="5000" max-messages-per-poll="3"/>
</twitter:search-inbound-channel-adapter>
<beans:bean id="twitter" class="org.springframework.integration.twitter.config.TestReceivingMessageSourceParserTests.TwitterTemplateFactoryBean"/>
<twitter:search-inbound-channel-adapter id="searchAdapterWithTemplate"
channel="searchChannel"
twitter-template="twitter"
query="#springintegration"
auto-startup="false">
<poller fixed-rate="5000" max-messages-per-poll="3"/>
</twitter:search-inbound-channel-adapter>
<beans:bean id="twitter" class="org.mockito.Mockito" factory-method="mock">
<beans:constructor-arg value="org.springframework.social.twitter.api.Twitter" />
</beans:bean>
<twitter:search-inbound-channel-adapter id="searchAdapterWithTemplate"
channel="searchChannel"
twitter-template="twitter"
page-size="23"
query="#springintegration"
auto-startup="false">
<poller fixed-rate="5000" max-messages-per-poll="3"/>
</twitter:search-inbound-channel-adapter>
</beans:beans>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2014 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.
@@ -16,30 +16,34 @@
package org.springframework.integration.twitter.config;
import org.junit.Ignore;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.integration.twitter.inbound.SearchReceivingMessageSource;
import org.springframework.social.twitter.api.Twitter;
import static org.junit.Assert.assertNotNull;
/**
* @author Oleg Zhurakousky
* @author Gary Russell
*/
public class TestSearchReceivingMessageSourceParserTests {
@Test
@Ignore // because userOpoeration.getProfile() throws exception where it doesn't have to since its a search
public void testSearchReceivingDefaultTemplate(){
ApplicationContext ac = new ClassPathXmlApplicationContext("TestSearchReceivingMessageSourceParser-context.xml", this.getClass());
SourcePollingChannelAdapter spca = ac.getBean("searchAdapter", SourcePollingChannelAdapter.class);
ConfigurableApplicationContext ac = new ClassPathXmlApplicationContext(
"TestSearchReceivingMessageSourceParser-context.xml", this.getClass());
SourcePollingChannelAdapter spca = ac.getBean("searchAdapterWithTemplate", SourcePollingChannelAdapter.class);
SearchReceivingMessageSource ms = (SearchReceivingMessageSource) TestUtils.getPropertyValue(spca, "source");
assertEquals(Integer.valueOf(23), TestUtils.getPropertyValue(ms, "pageSize", Integer.class));
Twitter template = (Twitter) TestUtils.getPropertyValue(ms, "twitter");
assertNotNull(template);
ac.close();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 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.
@@ -22,22 +22,24 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.Test;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandler;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.integration.twitter.outbound.DirectMessageSendingMessageHandler;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.support.GenericMessage;
/**
* @author Oleg Zhurakousky
* @author Gary Russell
* @author Artem Bilan
* @author Gary Russell
* @since 2.0
*/
public class TestSendingMessageHandlerParserTests {
@@ -46,7 +48,8 @@ public class TestSendingMessageHandlerParserTests {
@Test
public void testSendingMessageHandlerSuccessfulBootstrap(){
ApplicationContext ac = new ClassPathXmlApplicationContext("TestSendingMessageHandlerParser-context.xml", this.getClass());
ConfigurableApplicationContext ac = new ClassPathXmlApplicationContext(
"TestSendingMessageHandlerParser-context.xml", this.getClass());
EventDrivenConsumer dmAdapter = ac.getBean("dmAdapter", EventDrivenConsumer.class);
MessageHandler handler = TestUtils.getPropertyValue(dmAdapter, "handler", MessageHandler.class);
assertEquals(DirectMessageSendingMessageHandler.class, handler.getClass());
@@ -59,6 +62,7 @@ public class TestSendingMessageHandlerParserTests {
assertNotSame(handler, handler2);
handler2.handleMessage(new GenericMessage<String>("foo"));
assertEquals(2, adviceCalled);
ac.close();
}
@Test

View File

@@ -0,0 +1,39 @@
<?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:int="http://www.springframework.org/schema/integration"
xmlns:int-twitter="http://www.springframework.org/schema/integration/twitter"
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.xsd
http://www.springframework.org/schema/integration/twitter http://www.springframework.org/schema/integration/twitter/spring-integration-twitter.xsd">
<bean id="tt" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="org.springframework.social.twitter.api.Twitter" />
</bean>
<int:channel id="in" />
<int-twitter:search-outbound-gateway id="defaultTSOG" twitter-template="tt" request-channel="in" />
<int-twitter:search-outbound-gateway id="allAttsTSOG"
request-channel="in"
twitter-template="tt"
search-args-expression="'foo'"
reply-channel="out"
order="23"
reply-timeout="123"
auto-startup="false"
phase="100" />
<int-twitter:search-outbound-gateway id="polledAndAdvisedTSOG" twitter-template="tt" request-channel="out">
<int-twitter:request-handler-advice-chain>
<bean class="org.springframework.integration.handler.advice.RequestHandlerRetryAdvice" />
</int-twitter:request-handler-advice-chain>
<int:poller fixed-rate="1000" />
</int-twitter:search-outbound-gateway>
<int:channel id="out">
<int:queue />
</int:channel>
</beans>

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2014 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.twitter.config;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.integration.endpoint.PollingConsumer;
import org.springframework.integration.handler.advice.RequestHandlerRetryAdvice;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.integration.twitter.outbound.TwitterSearchOutboundGateway;
import org.springframework.social.twitter.api.Twitter;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Gary Russell
* @since 4.0
*
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext
public class TwitterSearchOutboundGatewayParserTests {
@Autowired
@Qualifier("defaultTSOG.handler")
private TwitterSearchOutboundGateway defaultTSOG;
@Autowired
@Qualifier("allAttsTSOG.handler")
private TwitterSearchOutboundGateway allAttsTSOG;
@Autowired
private PollingConsumer polledAndAdvisedTSOG;
@Autowired
private Twitter twitter;
@Test
public void testDefault() {
assertSame(twitter, TestUtils.getPropertyValue(defaultTSOG, "twitter"));
}
@Test
public void testAllAtts() {
assertSame(twitter, TestUtils.getPropertyValue(allAttsTSOG, "twitter"));
assertEquals("'foo'", TestUtils.getPropertyValue(allAttsTSOG, "searchArgsExpression.expression"));
}
@Test
public void testAdvised() {
assertSame(twitter, TestUtils.getPropertyValue(polledAndAdvisedTSOG, "handler.twitter"));
assertThat(TestUtils.getPropertyValue(polledAndAdvisedTSOG, "handler.adviceChain", ArrayList.class).get(0),
Matchers.instanceOf(RequestHandlerRetryAdvice.class));
}
}

View File

@@ -1,60 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
xmlns="http://www.springframework.org/schema/integration"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:tool="http://www.springframework.org/schema/tool"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:twitter="http://www.springframework.org/schema/integration/twitter"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
xmlns="http://www.springframework.org/schema/integration"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:tool="http://www.springframework.org/schema/tool"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:twitter="http://www.springframework.org/schema/integration/twitter"
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.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd
http://www.springframework.org/schema/integration/twitter http://www.springframework.org/schema/integration/twitter/spring-integration-twitter.xsd">
<message-history/>
<message-history/>
<context:property-placeholder location="classpath:sample.properties"/>
<channel id="inbound_dm"/>
<channel id="inbound_mentions"/>
<channel id="inbound_updates"/>
<channel id="inbound_search"/>
<context:property-placeholder location="classpath:sample.properties"/>
<channel id="inbound_dm"/>
<channel id="inbound_mentions"/>
<channel id="inbound_updates"/>
<channel id="inbound_search"/>
<beans:bean id="twitterTemplate" class="org.springframework.social.twitter.api.impl.TwitterTemplate">
<beans:constructor-arg value="${z_oleg.oauth.consumerKey}"/>
<beans:constructor-arg value="${z_oleg.oauth.consumerSecret}"/>
<beans:constructor-arg value="${z_oleg.oauth.accessToken}"/>
<beans:constructor-arg value="${z_oleg.oauth.accessTokenSecret}"/>
<beans:constructor-arg value="${twitter.oauth.consumerKey}"/>
<beans:constructor-arg value="${twitter.oauth.consumerSecret}"/>
<beans:constructor-arg value="${twitter.oauth.accessToken}"/>
<beans:constructor-arg value="${twitter.oauth.accessTokenSecret}"/>
</beans:bean>
<!-- <twitter:mentions-inbound-channel-adapter twitter-template="twitterTemplate" channel="inbound_mentions"> -->
<!-- <poller fixed-rate="5000" max-messages-per-poll="-1"/> -->
<!-- </twitter:mentions-inbound-channel-adapter> -->
<!-- <service-activator input-channel="inbound_mentions" ref="twitterAnnouncer" method="mention"/> -->
<!-- <twitter:mentions-inbound-channel-adapter twitter-template="twitterTemplate" channel="inbound_mentions"> -->
<!-- <poller fixed-rate="5000" max-messages-per-poll="-1"/> -->
<!-- </twitter:mentions-inbound-channel-adapter> -->
<!-- <twitter:dm-inbound-channel-adapter twitter-template="twitterTemplate" channel="inbound_dm"> -->
<!-- <poller fixed-rate="5000" max-messages-per-poll="-1"/> -->
<!-- </twitter:dm-inbound-channel-adapter> -->
<!-- <service-activator input-channel="inbound_dm" ref="twitterAnnouncer" method="dm"/> -->
<!-- <twitter:search-inbound-channel-adapter id="searchAdapter" twitter-template="twitterTemplate" channel="inbound_search" query="#springintegration"> -->
<!-- <poller fixed-rate="5000" max-messages-per-poll="5"/> -->
<!-- </twitter:search-inbound-channel-adapter> -->
<!-- <service-activator input-channel="inbound_search" ref="twitterAnnouncer" method="search"/> -->
<!-- <service-activator input-channel="inbound_mentions" ref="twitterAnnouncer" method="mention"/> -->
<twitter:inbound-channel-adapter id="twitterInbound" twitter-template="twitterTemplate" channel="inbound_updates">
<poller fixed-rate="1000" max-messages-per-poll="3"/>
</twitter:inbound-channel-adapter>
<service-activator input-channel="inbound_updates" ref="twitterAnnouncer" method="updates"/>
<!-- <twitter:dm-inbound-channel-adapter twitter-template="twitterTemplate" channel="inbound_dm"> -->
<!-- <poller fixed-rate="5000" max-messages-per-poll="-1"/> -->
<!-- </twitter:dm-inbound-channel-adapter> -->
<!-- <service-activator input-channel="inbound_dm" ref="twitterAnnouncer" method="dm"/> -->
<!-- <twitter:search-inbound-channel-adapter id="searchAdapter" twitter-template="twitterTemplate" channel="inbound_search" query="#springintegration"> -->
<!-- <poller fixed-rate="5000" max-messages-per-poll="5"/> -->
<!-- </twitter:search-inbound-channel-adapter> -->
<!-- <service-activator input-channel="inbound_search" ref="twitterAnnouncer" method="search"/> -->
<twitter:inbound-channel-adapter id="twitterInbound" twitter-template="twitterTemplate" channel="inbound_updates">
<poller fixed-rate="1000" max-messages-per-poll="3"/>
</twitter:inbound-channel-adapter>
<service-activator input-channel="inbound_updates" ref="twitterAnnouncer" method="updates"/>
<beans:bean id="twitterAnnouncer" class="org.springframework.integration.twitter.ignored.TwitterAnnouncer"/>
</beans:beans>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2014 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.
@@ -20,10 +20,13 @@ import java.util.concurrent.TimeUnit;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author Oleg Zhurakousky
* @author Gary Russell
*
*/
public class TestReceivingUsingNamespace {
@@ -31,13 +34,13 @@ public class TestReceivingUsingNamespace {
@Test
@Ignore
/*
* In order to run this test you need to provide values to the twitter.properties file
* In order to run this test you need to provide oauth properties in sample.properties on the classpath.
*/
public void testUpdatesWithRealTwitter() throws Exception{
CountDownLatch latch = new CountDownLatch(1);
new ClassPathXmlApplicationContext("TestReceivingUsingNamespace-context.xml", this.getClass());
System.out.println("done");
ConfigurableApplicationContext ctx =
new ClassPathXmlApplicationContext("TestReceivingUsingNamespace-context.xml", this.getClass());
latch.await(10000, TimeUnit.SECONDS);
ctx.close();
}
}

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
xmlns="http://www.springframework.org/schema/integration"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:tool="http://www.springframework.org/schema/tool"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:twitter="http://www.springframework.org/schema/integration/twitter"
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.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd
http://www.springframework.org/schema/integration/twitter http://www.springframework.org/schema/integration/twitter/spring-integration-twitter.xsd">
<message-history/>
<context:property-placeholder location="classpath:sample.properties"/>
<beans:bean id="twitterTemplate" class="org.springframework.social.twitter.api.impl.TwitterTemplate">
<beans:constructor-arg value="${twitter.oauth.consumerKey}"/>
<beans:constructor-arg value="${twitter.oauth.consumerSecret}"/>
<beans:constructor-arg value="${twitter.oauth.accessToken}"/>
<beans:constructor-arg value="${twitter.oauth.accessTokenSecret}"/>
</beans:bean>
<channel id="search" />
<twitter:search-outbound-gateway request-channel="search" twitter-template="twitterTemplate"
reply-channel="inbound" />
<service-activator input-channel="inbound" ref="twitterAnnouncer" method="searchResult"/>
<beans:bean id="twitterAnnouncer" class="org.springframework.integration.twitter.ignored.TwitterAnnouncer" />
</beans:beans>

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2014 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.twitter.ignored;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.social.twitter.api.SearchParameters;
/**
* @author Gary Russell
*
* @since 4.0
*
*/
public class TestSearchOutboundGateway {
@Test
@Ignore
/*
* In order to run this test you need to provide oauth properties in sample.properties on the classpath.
*/
public void testSearch() throws Exception{
ConfigurableApplicationContext ctx =
new ClassPathXmlApplicationContext("TestSearchOutboundGateway-context.xml", this.getClass());
MessageChannel search = ctx.getBean("search", MessageChannel.class);
search.send(new GenericMessage<String>("#springintegration"));
Thread.sleep(10000);
search.send(new GenericMessage<SearchParameters>(new SearchParameters("#springintegration").count(5)));
Thread.sleep(10000);
search.send(new GenericMessage<SearchParameters>(new SearchParameters("#jjjjunk").count(5)));
Thread.sleep(10000);
ctx.close();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2014 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.
@@ -16,12 +16,20 @@
package org.springframework.integration.twitter.ignored;
import org.springframework.messaging.Message;
import java.util.Collection;
import org.springframework.integration.history.MessageHistory;
import org.springframework.messaging.Message;
import org.springframework.social.twitter.api.DirectMessage;
import org.springframework.social.twitter.api.Tweet;
import org.springframework.stereotype.Component;
/**
* @author Oleg Zhurakousky
* @author Mark Fisher
* @author Gary Russell
*
*/
@Component
public class TwitterAnnouncer {
@@ -43,6 +51,16 @@ public class TwitterAnnouncer {
+ s.getFromUser() + "-" + s.getText() + " from " + s.getSource());
}
public void searchResult(Collection<Tweet> tweets) {
if (tweets.size() == 0) {
System.out.println("No results");
}
for (Tweet s : tweets) {
System.out.println("Search result: "
+ s.getFromUser() + "-" + s.getText() + " from " + s.getSource());
}
}
public void updates(Tweet t) {
System.out.println("Received timeline update: " + t.getText() + " from " + t.getSource());
}

View File

@@ -0,0 +1,266 @@
/*
* Copyright 2014 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.twitter.outbound;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Matchers;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.twitter.core.TwitterHeaders;
import org.springframework.integration.twitter.outbound.TwitterSearchOutboundGatewayTests.TwitterConfig;
import org.springframework.messaging.Message;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.social.twitter.api.SearchMetadata;
import org.springframework.social.twitter.api.SearchOperations;
import org.springframework.social.twitter.api.SearchParameters;
import org.springframework.social.twitter.api.SearchResults;
import org.springframework.social.twitter.api.Tweet;
import org.springframework.social.twitter.api.Twitter;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Gary Russell
* @since 4.0
*
*/
@ContextConfiguration(classes=TwitterConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD)
public class TwitterSearchOutboundGatewayTests {
@Autowired
private SearchOperations searchOps;
@Autowired
private TwitterSearchOutboundGateway gateway;
@Autowired
private PollableChannel outputChannel;
@Test
public void testStringQuery() {
Tweet tweet = new Tweet(1L, "foo", new Date(), "bar", "baz", 0L, 0L, "qux", "fiz");
SearchMetadata searchMetadata = mock(SearchMetadata.class);
final SearchResults searchResults = new SearchResults(Collections.singletonList(tweet), searchMetadata);
doAnswer(new Answer<SearchResults>() {
@Override
public SearchResults answer(InvocationOnMock invocation) throws Throwable {
SearchParameters searchParameters = (SearchParameters) invocation.getArguments()[0];
assertEquals("foo", searchParameters.getQuery());
assertEquals(Integer.valueOf(20), searchParameters.getCount());
return searchResults;
}
}).when(this.searchOps).search(Matchers.any(SearchParameters.class));
this.gateway.handleMessage(new GenericMessage<String>("foo"));
Message<?> reply = this.outputChannel.receive(0);
assertNotNull(reply);
@SuppressWarnings("unchecked")
List<Tweet> tweets = (List<Tweet>) reply.getPayload();
assertEquals(1, tweets.size());
assertSame(tweet, tweets.get(0));
assertSame(searchMetadata, reply.getHeaders().get(TwitterHeaders.SEARCH_METADATA));
}
@Test
public void testStringQueryCustomLimit() {
this.gateway.setSearchArgsExpression(new SpelExpressionParser()
.parseExpression("{payload, 30}"));
Tweet tweet = new Tweet(1L, "foo", new Date(), "bar", "baz", 0L, 0L, "qux", "fiz");
SearchMetadata searchMetadata = mock(SearchMetadata.class);
final SearchResults searchResults = new SearchResults(Collections.singletonList(tweet), searchMetadata);
doAnswer(new Answer<SearchResults>() {
@Override
public SearchResults answer(InvocationOnMock invocation) throws Throwable {
SearchParameters searchParameters = (SearchParameters) invocation.getArguments()[0];
assertEquals("foo", searchParameters.getQuery());
assertEquals(Integer.valueOf(30), searchParameters.getCount());
return searchResults;
}
}).when(this.searchOps).search(Matchers.any(SearchParameters.class));
this.gateway.handleMessage(new GenericMessage<String>("foo"));
Message<?> reply = this.outputChannel.receive(0);
assertNotNull(reply);
@SuppressWarnings("unchecked")
List<Tweet> tweets = (List<Tweet>) reply.getPayload();
assertEquals(1, tweets.size());
assertSame(tweet, tweets.get(0));
assertSame(searchMetadata, reply.getHeaders().get(TwitterHeaders.SEARCH_METADATA));
}
@Test
public void testStringQueryCustomExpression() {
this.gateway.setSearchArgsExpression(new SpelExpressionParser()
.parseExpression("{'bar', 1, 2, 3}"));
Tweet tweet = new Tweet(1L, "foo", new Date(), "bar", "baz", 0L, 0L, "qux", "fiz");
SearchMetadata searchMetadata = mock(SearchMetadata.class);
final SearchResults searchResults = new SearchResults(Collections.singletonList(tweet), searchMetadata);
doAnswer(new Answer<SearchResults>() {
@Override
public SearchResults answer(InvocationOnMock invocation) throws Throwable {
SearchParameters searchParameters = (SearchParameters) invocation.getArguments()[0];
assertEquals("bar", searchParameters.getQuery());
assertEquals(Integer.valueOf(1), searchParameters.getCount());
assertEquals(Long.valueOf(2), searchParameters.getSinceId());
assertEquals(Long.valueOf(3), searchParameters.getMaxId());
return searchResults;
}
}).when(this.searchOps).search(Matchers.any(SearchParameters.class));
this.gateway.handleMessage(new GenericMessage<String>("foo"));
Message<?> reply = this.outputChannel.receive(0);
assertNotNull(reply);
@SuppressWarnings("unchecked")
List<Tweet> tweets = (List<Tweet>) reply.getPayload();
assertEquals(1, tweets.size());
assertSame(tweet, tweets.get(0));
assertSame(searchMetadata, reply.getHeaders().get(TwitterHeaders.SEARCH_METADATA));
}
@Test
public void testSearchParamsQuery() {
Tweet tweet = new Tweet(1L, "foo", new Date(), "bar", "baz", 0L, 0L, "qux", "fiz");
SearchMetadata searchMetadata = mock(SearchMetadata.class);
final SearchResults searchResults = new SearchResults(Collections.singletonList(tweet), searchMetadata);
final SearchParameters parameters = new SearchParameters("bar");
doAnswer(new Answer<SearchResults>() {
@Override
public SearchResults answer(InvocationOnMock invocation) throws Throwable {
SearchParameters searchParameters = (SearchParameters) invocation.getArguments()[0];
assertSame(parameters, searchParameters);
return searchResults;
}
}).when(this.searchOps).search(Matchers.any(SearchParameters.class));
this.gateway.handleMessage(new GenericMessage<SearchParameters>(parameters));
Message<?> reply = this.outputChannel.receive(0);
assertNotNull(reply);
@SuppressWarnings("unchecked")
List<Tweet> tweets = (List<Tweet>) reply.getPayload();
assertEquals(1, tweets.size());
assertSame(tweet, tweets.get(0));
assertSame(searchMetadata, reply.getHeaders().get(TwitterHeaders.SEARCH_METADATA));
}
@Test
public void testSearchParamsQueryCustomExpression() {
this.gateway.setSearchArgsExpression(new SpelExpressionParser()
.parseExpression("new SearchParameters('foo' + payload).count(5).sinceId(11)"));
Tweet tweet = new Tweet(1L, "foo", new Date(), "bar", "baz", 0L, 0L, "qux", "fiz");
SearchMetadata searchMetadata = mock(SearchMetadata.class);
final SearchResults searchResults = new SearchResults(Collections.singletonList(tweet), searchMetadata);
doAnswer(new Answer<SearchResults>() {
@Override
public SearchResults answer(InvocationOnMock invocation) throws Throwable {
SearchParameters searchParameters = (SearchParameters) invocation.getArguments()[0];
assertEquals("foobar", searchParameters.getQuery());
assertEquals(Integer.valueOf(5), searchParameters.getCount());
assertEquals(Long.valueOf(11), searchParameters.getSinceId());
return searchResults;
}
}).when(this.searchOps).search(Matchers.any(SearchParameters.class));
this.gateway.handleMessage(new GenericMessage<String>("bar"));
Message<?> reply = this.outputChannel.receive(0);
assertNotNull(reply);
@SuppressWarnings("unchecked")
List<Tweet> tweets = (List<Tweet>) reply.getPayload();
assertEquals(1, tweets.size());
assertSame(tweet, tweets.get(0));
assertSame(searchMetadata, reply.getHeaders().get(TwitterHeaders.SEARCH_METADATA));
}
@Test
public void testEmptyResult() {
SearchMetadata searchMetadata = mock(SearchMetadata.class);
List<Tweet> empty = new ArrayList<Tweet>(0);
final SearchResults searchResults = new SearchResults(empty, searchMetadata);
doAnswer(new Answer<SearchResults>() {
@Override
public SearchResults answer(InvocationOnMock invocation) throws Throwable {
SearchParameters searchParameters = (SearchParameters) invocation.getArguments()[0];
assertEquals("foo", searchParameters.getQuery());
assertEquals(Integer.valueOf(20), searchParameters.getCount());
return searchResults;
}
}).when(this.searchOps).search(Matchers.any(SearchParameters.class));
this.gateway.handleMessage(new GenericMessage<String>("foo"));
Message<?> reply = this.outputChannel.receive(0);
assertNotNull(reply);
@SuppressWarnings("unchecked")
List<Tweet> tweets = (List<Tweet>) reply.getPayload();
assertEquals(0, tweets.size());
assertSame(searchMetadata, reply.getHeaders().get(TwitterHeaders.SEARCH_METADATA));
}
@Configuration
@EnableIntegration
public static class TwitterConfig {
@Bean
public TwitterSearchOutboundGateway gateway() {
TwitterSearchOutboundGateway gateway = new TwitterSearchOutboundGateway(twitter());
gateway.setOutputChannel(outputChannel());
return gateway;
}
@Bean
public PollableChannel outputChannel() {
return new QueueChannel();
}
@Bean
public Twitter twitter() {
Twitter twitter = mock(Twitter.class);
when(twitter.searchOperations()).thenReturn(searchOps());
return twitter;
}
@Bean
public SearchOperations searchOps() {
return mock(SearchOperations.class);
}
}
}