pollForTweets(long sinceId) {
- return this.getTwitter().timelineOperations().getHomeTimeline(20, sinceId, 0);
+ return this.getTwitter().timelineOperations().getHomeTimeline(this.getPageSize(), sinceId, 0);
}
}
diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/outbound/TwitterSearchOutboundGateway.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/outbound/TwitterSearchOutboundGateway.java
new file mode 100644
index 0000000000..ae7918c1e2
--- /dev/null
+++ b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/outbound/TwitterSearchOutboundGateway.java
@@ -0,0 +1,151 @@
+/*
+ * 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 java.util.Collections;
+import java.util.List;
+
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.Expression;
+import org.springframework.expression.TypeLocator;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.StandardTypeLocator;
+import org.springframework.integration.expression.IntegrationEvaluationContextAware;
+import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
+import org.springframework.integration.twitter.core.TwitterHeaders;
+import org.springframework.messaging.Message;
+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.util.Assert;
+
+/**
+ * The {@link AbstractReplyProducingMessageHandler} implementation to perform request/reply
+ * Twitter search with {@link SearchParameters} as the result of {@link #searchArgsExpression}
+ * expression evaluation.
+ *
+ * @author Gary Russell
+ * @since 4.0
+ *
+ */
+public class TwitterSearchOutboundGateway extends AbstractReplyProducingMessageHandler
+ implements IntegrationEvaluationContextAware {
+
+ private static final int DEFAULT_PAGE_SIZE = 20;
+
+ private final Twitter twitter;
+
+ private volatile Expression searchArgsExpression = new SpelExpressionParser().parseExpression("payload");
+
+ private volatile EvaluationContext evaluationContext;
+
+ public TwitterSearchOutboundGateway(Twitter twitter) {
+ Assert.notNull(twitter, "'twitter' must not be null");
+ this.twitter = twitter;
+ }
+
+ @Override
+ public void setIntegrationEvaluationContext(EvaluationContext evaluationContext) {
+ TypeLocator typeLocator = evaluationContext.getTypeLocator();
+ if (typeLocator instanceof StandardTypeLocator) {
+ /*
+ * Register the twitter api package so they don't need a FQCN for SearchParameters.
+ */
+ ((StandardTypeLocator) typeLocator).registerImport("org.springframework.social.twitter.api");
+ }
+ this.evaluationContext = evaluationContext;
+ }
+
+ /**
+ * An expression that is used to build the search; must resolve to a
+ * {@code SearchParameters} object, or a
+ * {@link String}, in which case the default page size of 20 is applied,
+ * or a list of up to 4 arguments, such as
+ * {@code "{payload, headers.pageSize, headers.sinceId, headers.maxId}"}.
+ * The first (required) argument must resolve to a String (query), the
+ * optional arguments must resolve to an Number and represent the
+ * page size, sinceId, and maxId respectively. Refer to the 'Spring
+ * Social Twitter' documentation for more details.
+ * When using a {@code SearchParameters} directly, it is not necessary
+ * to include the package: {@code "new SearchParameters("#foo").count(20)").
+ *
Default: {@code "payload"}.
+ * @param searchArgsExpression The expression.
+ */
+ public void setSearchArgsExpression(Expression searchArgsExpression) {
+ Assert.notNull(searchArgsExpression, "'searchArgsExpression' must not be null");
+ this.searchArgsExpression = searchArgsExpression;
+ }
+
+ @Override
+ public String getComponentType() {
+ return "twitter:search-outbound-gateway";
+ }
+
+ protected Twitter getTwitter() {
+ return twitter;
+ }
+
+ @Override
+ protected Object handleRequestMessage(Message> requestMessage) {
+ Object args = this.searchArgsExpression.getValue(this.evaluationContext, requestMessage);
+ Assert.notNull(args, "The twitter search expression cannot evaluate to 'null'.");
+ SearchParameters searchParameters;
+ if (args instanceof SearchParameters) {
+ searchParameters = (SearchParameters) args;
+ }
+ else if (args instanceof String) {
+ searchParameters = new SearchParameters((String) args).count(DEFAULT_PAGE_SIZE);
+ }
+ else if (args instanceof List) {
+ List> list = (List>) args;
+ Assert.isTrue(list.size() > 0 && list.size() < 5, "Between 1 and 4 search arguments are required");
+ Assert.isInstanceOf(String.class, list.get(0), "The first search argument (query) must be a String");
+ searchParameters = new SearchParameters((String) list.get(0));
+ if (list.size() > 1) {
+ Assert.isInstanceOf(Number.class, list.get(1),
+ "The second search argument (pageSize) must be a Number");
+ searchParameters.count(((Number) list.get(1)).intValue());
+ if (list.size() > 2) {
+ Assert.isInstanceOf(Number.class, list.get(2),
+ "The third search argument (sinceId) must be a Number");
+ searchParameters.sinceId(((Number) list.get(2)).longValue());
+ }
+ if (list.size() > 3) {
+ Assert.isInstanceOf(Number.class, list.get(3),
+ "The fourth search argument (maxId) must be a Number");
+ searchParameters.maxId(((Number) list.get(3)).longValue());
+ }
+ }
+ }
+ else {
+ throw new IllegalArgumentException(
+ "Search Expression must evaluate to a 'SearchParameters', 'String' or 'List'.");
+ }
+ SearchResults results = this.getTwitter().searchOperations().search(searchParameters);
+ if (results != null) {
+ List tweets = (results.getTweets() != null ? results.getTweets() : Collections.emptyList());
+ return this.getMessageBuilderFactory().withPayload(tweets)
+ .setHeader(TwitterHeaders.SEARCH_METADATA, results.getSearchMetadata());
+ }
+ else {
+ return null;
+ }
+
+ }
+
+}
diff --git a/spring-integration-twitter/src/main/resources/org/springframework/integration/twitter/config/spring-integration-twitter-4.0.xsd b/spring-integration-twitter/src/main/resources/org/springframework/integration/twitter/config/spring-integration-twitter-4.0.xsd
index b2b9856daf..5fc97d0120 100644
--- a/spring-integration-twitter/src/main/resources/org/springframework/integration/twitter/config/spring-integration-twitter-4.0.xsd
+++ b/spring-integration-twitter/src/main/resources/org/springframework/integration/twitter/config/spring-integration-twitter-4.0.xsd
@@ -96,7 +96,9 @@
-
+
+
+
@@ -110,7 +112,94 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The bean id of this gateway; the MessageHandler is also registered with this id
+ plus a suffix '.handler'.
+
+
+
+
+
+
+ A SpEL expression that evaluates to search arguments; the evaluation result type can be
+ an 'org.springframework.social.twitter.api.SearchParameters', a 'String', in
+ which case the default page size of 20 is used, or the expression can evaluate to
+ a list of search
+ arguments, for example: "{payload, headers.pageSize, headers.sinceId, headers.maxId}".
+ Default: "payload".
+
+
+
+
+
+
+
+
+
+
+
+ Identifies the request channel attached to this gateway.
+
+
+
+
+
+
+
+
+
+
+
+ Identifies the reply channel attached to this
+ gateway.
+
+
+
+
+
+
+
+
+
+
@@ -145,7 +234,7 @@
-
+
@@ -172,32 +261,39 @@
+
+
+
+ Limits the number of tweets retrieved on each poll; default: 20.
+
+
+
-
+
-
-
-
-
-
-
-
-
-
-
- Reference to a TwitterTemplate bean provided by the Spring Social project.
-
-
-
-
-
-
- Specifies the order for invocation when this endpoint is connected as a
- subscriber to a SubscribableChannel.
-
-
-
+
+
+
+
+
+
+
+
+
+
+ Reference to a TwitterTemplate bean provided by the Spring Social project.
+
+
+
+
+
+
+ Specifies the order for invocation when this endpoint is connected as a
+ subscriber to a SubscribableChannel.
+
+
+
diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestReceivingMessageSourceParser-context.xml b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestReceivingMessageSourceParser-context.xml
index fdb1b43838..14212c9e08 100644
--- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestReceivingMessageSourceParser-context.xml
+++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestReceivingMessageSourceParser-context.xml
@@ -23,22 +23,25 @@
-
-
-
diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestReceivingMessageSourceParserTests.java b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestReceivingMessageSourceParserTests.java
index ec97511362..54de8046a9 100644
--- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestReceivingMessageSourceParserTests.java
+++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestReceivingMessageSourceParserTests.java
@@ -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();
}
}
diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSearchReceivingMessageSourceParser-context.xml b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSearchReceivingMessageSourceParser-context.xml
index 534c7a7a7f..9c79a93d05 100644
--- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSearchReceivingMessageSourceParser-context.xml
+++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSearchReceivingMessageSourceParser-context.xml
@@ -1,44 +1,39 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSearchReceivingMessageSourceParserTests.java b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSearchReceivingMessageSourceParserTests.java
index 6e7d3ef38a..6213856251 100644
--- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSearchReceivingMessageSourceParserTests.java
+++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSearchReceivingMessageSourceParserTests.java
@@ -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();
}
}
diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingMessageHandlerParserTests.java b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingMessageHandlerParserTests.java
index a65c371840..45d4a5ad14 100644
--- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingMessageHandlerParserTests.java
+++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingMessageHandlerParserTests.java
@@ -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("foo"));
assertEquals(2, adviceCalled);
+ ac.close();
}
@Test
diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TwitterSearchOutboundGatewayParserTests-context.xml b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TwitterSearchOutboundGatewayParserTests-context.xml
new file mode 100644
index 0000000000..6c4d15a455
--- /dev/null
+++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TwitterSearchOutboundGatewayParserTests-context.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TwitterSearchOutboundGatewayParserTests.java b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TwitterSearchOutboundGatewayParserTests.java
new file mode 100644
index 0000000000..a4eaf1c28c
--- /dev/null
+++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TwitterSearchOutboundGatewayParserTests.java
@@ -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));
+ }
+
+}
diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestReceivingUsingNamespace-context.xml b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestReceivingUsingNamespace-context.xml
index 4fafcdd8d6..a415a48789 100644
--- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestReceivingUsingNamespace-context.xml
+++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestReceivingUsingNamespace-context.xml
@@ -1,60 +1,60 @@
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestReceivingUsingNamespace.java b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestReceivingUsingNamespace.java
index cfb80691a1..b5e5b5a535 100644
--- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestReceivingUsingNamespace.java
+++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestReceivingUsingNamespace.java
@@ -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();
}
}
diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSearchOutboundGateway-context.xml b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSearchOutboundGateway-context.xml
new file mode 100644
index 0000000000..40e3678d84
--- /dev/null
+++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSearchOutboundGateway-context.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSearchOutboundGateway.java b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSearchOutboundGateway.java
new file mode 100644
index 0000000000..fea4a0be51
--- /dev/null
+++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSearchOutboundGateway.java
@@ -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("#springintegration"));
+ Thread.sleep(10000);
+ search.send(new GenericMessage(new SearchParameters("#springintegration").count(5)));
+ Thread.sleep(10000);
+ search.send(new GenericMessage(new SearchParameters("#jjjjunk").count(5)));
+ Thread.sleep(10000);
+ ctx.close();
+ }
+}
diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TwitterAnnouncer.java b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TwitterAnnouncer.java
index e005d72d8a..a808938898 100644
--- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TwitterAnnouncer.java
+++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TwitterAnnouncer.java
@@ -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 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());
}
diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/outbound/TwitterSearchOutboundGatewayTests.java b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/outbound/TwitterSearchOutboundGatewayTests.java
new file mode 100644
index 0000000000..b83bfd2e8e
--- /dev/null
+++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/outbound/TwitterSearchOutboundGatewayTests.java
@@ -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() {
+
+ @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("foo"));
+ Message> reply = this.outputChannel.receive(0);
+ assertNotNull(reply);
+ @SuppressWarnings("unchecked")
+ List tweets = (List) 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() {
+
+ @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("foo"));
+ Message> reply = this.outputChannel.receive(0);
+ assertNotNull(reply);
+ @SuppressWarnings("unchecked")
+ List tweets = (List) 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() {
+
+ @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("foo"));
+ Message> reply = this.outputChannel.receive(0);
+ assertNotNull(reply);
+ @SuppressWarnings("unchecked")
+ List tweets = (List) 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() {
+
+ @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(parameters));
+ Message> reply = this.outputChannel.receive(0);
+ assertNotNull(reply);
+ @SuppressWarnings("unchecked")
+ List tweets = (List) 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() {
+
+ @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("bar"));
+ Message> reply = this.outputChannel.receive(0);
+ assertNotNull(reply);
+ @SuppressWarnings("unchecked")
+ List tweets = (List) 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 empty = new ArrayList(0);
+ final SearchResults searchResults = new SearchResults(empty, searchMetadata);
+ doAnswer(new Answer() {
+
+ @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("foo"));
+ Message> reply = this.outputChannel.receive(0);
+ assertNotNull(reply);
+ @SuppressWarnings("unchecked")
+ List tweets = (List) 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);
+ }
+
+ }
+
+}
diff --git a/src/reference/docbook/twitter.xml b/src/reference/docbook/twitter.xml
index 0555ce44e5..7e2fe2c3ee 100644
--- a/src/reference/docbook/twitter.xml
+++ b/src/reference/docbook/twitter.xml
@@ -2,11 +2,12 @@
- Twitter Adapter
+ Twitter Support
Spring Integration provides support for interacting with Twitter. With the Twitter adapters you can both
receive and send Twitter messages. You can also perform a Twitter search based on a schedule and publish
- the search results within Messages.
+ the search results within Messages. Since version 4.0, a search outbound
+ gateway is provided to perform dynamic searches.
+
diff --git a/src/reference/docbook/whats-new.xml b/src/reference/docbook/whats-new.xml
index 6ba9b4b459..9c19451999 100644
--- a/src/reference/docbook/whats-new.xml
+++ b/src/reference/docbook/whats-new.xml
@@ -163,6 +163,15 @@
For more information, see .
+