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:
committed by
Artem Bilan
parent
4577200e66
commit
7082b9e4cb
@@ -103,7 +103,7 @@ subprojects { subproject ->
|
||||
springDataRedisVersion = '1.2.1.RELEASE'
|
||||
springGemfireVersion = '1.3.1.RELEASE'
|
||||
springSecurityVersion = '3.1.3.RELEASE'
|
||||
springSocialTwitterVersion = '1.1.0.M4'
|
||||
springSocialTwitterVersion = '1.1.0.RC1'
|
||||
springRetryVersion = '1.0.3.RELEASE'
|
||||
springVersion = project.hasProperty('springVersion') ? project.springVersion : '4.0.3.RELEASE'
|
||||
springWsVersion = '2.1.1.RELEASE'
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beansProjectDescription>
|
||||
<version>1</version>
|
||||
<pluginVersion><![CDATA[2.5.0.201010221000-RELEASE]]></pluginVersion>
|
||||
<configSuffixes>
|
||||
<configSuffix><![CDATA[xml]]></configSuffix>
|
||||
</configSuffixes>
|
||||
<enableImports><![CDATA[false]]></enableImports>
|
||||
<configs>
|
||||
</configs>
|
||||
<configSets>
|
||||
</configSets>
|
||||
</beansProjectDescription>
|
||||
@@ -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.
|
||||
@@ -27,13 +27,12 @@ import org.springframework.integration.twitter.inbound.DirectMessageReceivingMes
|
||||
import org.springframework.integration.twitter.inbound.MentionsReceivingMessageSource;
|
||||
import org.springframework.integration.twitter.inbound.SearchReceivingMessageSource;
|
||||
import org.springframework.integration.twitter.inbound.TimelineReceivingMessageSource;
|
||||
import org.springframework.social.twitter.api.impl.TwitterTemplate;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Parser for inbound Twitter Channel Adapters.
|
||||
*
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
* @since 2.0
|
||||
*/
|
||||
public class TwitterInboundChannelAdapterParser extends AbstractPollingInboundChannelAdapterParser {
|
||||
@@ -42,17 +41,11 @@ public class TwitterInboundChannelAdapterParser extends AbstractPollingInboundCh
|
||||
protected BeanMetadataElement parseSource(Element element, ParserContext parserContext) {
|
||||
Class<?> clazz = determineClass(element, parserContext);
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(clazz);
|
||||
String templateBeanName = element.getAttribute("twitter-template");
|
||||
if (StringUtils.hasText(templateBeanName)) {
|
||||
builder.addConstructorArgReference(templateBeanName);
|
||||
}
|
||||
else {
|
||||
BeanDefinitionBuilder templateBuilder = BeanDefinitionBuilder.genericBeanDefinition(TwitterTemplate.class);
|
||||
builder.addConstructorArgValue(templateBuilder.getBeanDefinition());
|
||||
}
|
||||
builder.addConstructorArgReference(element.getAttribute("twitter-template"));
|
||||
builder.addConstructorArgValue(element.getAttribute(ID_ATTRIBUTE));
|
||||
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "query");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "page-size");
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "metadata-store");
|
||||
return builder.getBeanDefinition();
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -20,13 +20,15 @@ import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHa
|
||||
|
||||
/**
|
||||
* Namespace handler for the Twitter adapters.
|
||||
*
|
||||
*
|
||||
* @author Josh Long
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
* @since 2.0
|
||||
*/
|
||||
public class TwitterNamespaceHandler extends AbstractIntegrationNamespaceHandler {
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
// inbound
|
||||
registerBeanDefinitionParser("inbound-channel-adapter", new TwitterInboundChannelAdapterParser());
|
||||
@@ -37,6 +39,7 @@ public class TwitterNamespaceHandler extends AbstractIntegrationNamespaceHandler
|
||||
// outbound
|
||||
registerBeanDefinitionParser("outbound-channel-adapter", new TwitterOutboundChannelAdapterParser());
|
||||
registerBeanDefinitionParser("dm-outbound-channel-adapter", new TwitterOutboundChannelAdapterParser());
|
||||
registerBeanDefinitionParser("search-outbound-gateway", new TwitterSearchOutboundGatewayParser());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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 org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.config.ExpressionFactoryBean;
|
||||
import org.springframework.integration.config.xml.AbstractConsumerEndpointParser;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.springframework.integration.twitter.outbound.TwitterSearchOutboundGateway;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Parser for {@code <int-twitter:search-outbound-gateway/>}.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 4.0
|
||||
*
|
||||
*/
|
||||
public class TwitterSearchOutboundGatewayParser extends AbstractConsumerEndpointParser {
|
||||
|
||||
@Override
|
||||
protected String getInputChannelAttributeName() {
|
||||
return "request-channel";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(TwitterSearchOutboundGateway.class);
|
||||
builder.addConstructorArgReference(element.getAttribute("twitter-template"));
|
||||
String searchArgsExpression = element.getAttribute("search-args-expression");
|
||||
if (StringUtils.hasText(searchArgsExpression)) {
|
||||
BeanDefinition expressionDef = new RootBeanDefinition(ExpressionFactoryBean.class);
|
||||
expressionDef.getConstructorArgumentValues().addGenericArgumentValue(searchArgsExpression);
|
||||
builder.addPropertyValue("searchArgsExpression", expressionDef);
|
||||
}
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "reply-channel", "outputChannel");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "reply-timeout", "sendTimeout");
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
@@ -21,12 +21,17 @@ package org.springframework.integration.twitter.core;
|
||||
*
|
||||
* @author Josh Long
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
* @since 2.0
|
||||
*/
|
||||
public abstract class TwitterHeaders {
|
||||
public final class TwitterHeaders {
|
||||
|
||||
private static final String PREFIX = "twitter_";
|
||||
|
||||
public static final String DM_TARGET_USER_ID = PREFIX + "dmTargetUserId";
|
||||
|
||||
public static final String SEARCH_METADATA = PREFIX + "searchMetadata";
|
||||
|
||||
private TwitterHeaders() {}
|
||||
|
||||
}
|
||||
|
||||
@@ -58,6 +58,8 @@ import org.springframework.util.StringUtils;
|
||||
@SuppressWarnings("rawtypes")
|
||||
abstract class AbstractTwitterMessageSource<T> extends IntegrationObjectSupport implements MessageSource {
|
||||
|
||||
private static final int DEFAULT_PAGE_SIZE = 20;
|
||||
|
||||
private final Twitter twitter;
|
||||
|
||||
private final TweetComparator tweetComparator = new TweetComparator();
|
||||
@@ -76,6 +78,8 @@ abstract class AbstractTwitterMessageSource<T> extends IntegrationObjectSupport
|
||||
|
||||
private volatile long lastProcessedId = -1;
|
||||
|
||||
private volatile int pageSize = DEFAULT_PAGE_SIZE;
|
||||
|
||||
|
||||
public AbstractTwitterMessageSource(Twitter twitter, String metadataKey) {
|
||||
Assert.notNull(twitter, "twitter must not be null");
|
||||
@@ -104,6 +108,18 @@ abstract class AbstractTwitterMessageSource<T> extends IntegrationObjectSupport
|
||||
return this.twitter;
|
||||
}
|
||||
|
||||
protected int getPageSize() {
|
||||
return this.pageSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the limit for the number of results returned on each poll; default 20.
|
||||
* @param pageSize The pageSize.
|
||||
*/
|
||||
public void setPageSize(int pageSize) {
|
||||
this.pageSize = pageSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() throws Exception {
|
||||
super.onInit();
|
||||
|
||||
@@ -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.
|
||||
@@ -27,6 +27,7 @@ import org.springframework.social.twitter.api.Twitter;
|
||||
* @author Josh Long
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Mark Fisher
|
||||
* @author Gary Russell
|
||||
* @since 2.0
|
||||
*/
|
||||
public class DirectMessageReceivingMessageSource extends AbstractTwitterMessageSource<DirectMessage> {
|
||||
@@ -42,7 +43,7 @@ public class DirectMessageReceivingMessageSource extends AbstractTwitterMessageS
|
||||
|
||||
@Override
|
||||
protected List<DirectMessage> pollForTweets(long sinceId) {
|
||||
return this.getTwitter().directMessageOperations().getDirectMessagesReceived(1, 20, sinceId, 0);
|
||||
return this.getTwitter().directMessageOperations().getDirectMessagesReceived(1, this.getPageSize(), sinceId, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -26,6 +26,7 @@ import org.springframework.social.twitter.api.Twitter;
|
||||
*
|
||||
* @author Josh Long
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MentionsReceivingMessageSource extends AbstractTwitterMessageSource<Tweet> {
|
||||
@@ -41,7 +42,7 @@ public class MentionsReceivingMessageSource extends AbstractTwitterMessageSource
|
||||
|
||||
@Override
|
||||
protected List<Tweet> pollForTweets(long sinceId) {
|
||||
return this.getTwitter().timelineOperations().getMentions(20, sinceId, 0);
|
||||
return this.getTwitter().timelineOperations().getMentions(this.getPageSize(), sinceId, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,16 +19,17 @@ package org.springframework.integration.twitter.inbound;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
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.social.twitter.api.SearchParameters;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Mark Fisher
|
||||
* @author Gunnar Hillert
|
||||
* @author Gary Russell
|
||||
* @since 2.0
|
||||
*/
|
||||
public class SearchReceivingMessageSource extends AbstractTwitterMessageSource<Tweet> {
|
||||
@@ -46,13 +47,13 @@ public class SearchReceivingMessageSource extends AbstractTwitterMessageSource<T
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComponentType() {
|
||||
public String getComponentType() {
|
||||
return "twitter:search-inbound-channel-adapter";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Tweet> pollForTweets(long sinceId) {
|
||||
SearchParameters searchParameters = new SearchParameters(query).count(20).sinceId(sinceId);
|
||||
SearchParameters searchParameters = new SearchParameters(query).count(this.getPageSize()).sinceId(sinceId);
|
||||
SearchResults results = this.getTwitter().searchOperations().search(searchParameters);
|
||||
return (results != null) ? results.getTweets() : Collections.<Tweet>emptyList();
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -27,6 +27,7 @@ import org.springframework.social.twitter.api.Twitter;
|
||||
*
|
||||
* @author Josh Long
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
* @since 2.0
|
||||
*/
|
||||
public class TimelineReceivingMessageSource extends AbstractTwitterMessageSource<Tweet> {
|
||||
@@ -42,7 +43,7 @@ public class TimelineReceivingMessageSource extends AbstractTwitterMessageSource
|
||||
|
||||
@Override
|
||||
protected List<Tweet> pollForTweets(long sinceId) {
|
||||
return this.getTwitter().timelineOperations().getHomeTimeline(20, sinceId, 0);
|
||||
return this.getTwitter().timelineOperations().getHomeTimeline(this.getPageSize(), sinceId, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
* <p> When using a {@code SearchParameters} directly, it is not necessary
|
||||
* to include the package: {@code "new SearchParameters("#foo").count(20)").
|
||||
* <p> 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<Tweet> tweets = (results.getTweets() != null ? results.getTweets() : Collections.<Tweet>emptyList());
|
||||
return this.getMessageBuilderFactory().withPayload(tweets)
|
||||
.setHeader(TwitterHeaders.SEARCH_METADATA, results.getSearchMetadata());
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -96,7 +96,9 @@
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="outbound-twitter-type"/>
|
||||
<xsd:extension base="outbound-twitter-type">
|
||||
<xsd:attributeGroup ref="integration:channelAdapterAttributes"/>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
@@ -110,7 +112,94 @@
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="outbound-twitter-type"/>
|
||||
<xsd:extension base="outbound-twitter-type">
|
||||
<xsd:attributeGroup ref="integration:channelAdapterAttributes"/>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="search-outbound-gateway">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Builds an outbound gateway used to issue Twitter searches.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="outbound-twitter-type">
|
||||
<xsd:attribute name="id" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
The bean id of this gateway; the MessageHandler is also registered with this id
|
||||
plus a suffix '.handler'.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="search-args-expression" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
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".
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="request-channel" use="required" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type
|
||||
type="org.springframework.messaging.MessageChannel" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
<xsd:documentation>
|
||||
Identifies the request channel attached to this gateway.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="reply-channel" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type
|
||||
type="org.springframework.messaging.MessageChannel" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
<xsd:documentation>
|
||||
Identifies the reply channel attached to this
|
||||
gateway.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="reply-timeout" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Allows you to specify how long this gateway will wait for
|
||||
the reply message to be sent successfully to the reply channel
|
||||
before throwing an exception. This attribute only applies when the
|
||||
channel might block, for example when using a bounded queue channel that
|
||||
is currently full.
|
||||
|
||||
Also, keep in mind that when sending to a DirectChannel, the
|
||||
invocation will occur in the sender's thread. Therefore,
|
||||
the failing of the send operation may be caused by other
|
||||
components further downstream.
|
||||
|
||||
The "reply-timeout" attribute maps to the "sendTimeout" property of the
|
||||
underlying 'MessagingTemplate' instance (org.springframework.integration.core.MessagingTemplate).
|
||||
|
||||
The attribute will default, if not specified, to '-1', meaning that
|
||||
by default, the Gateway will wait indefinitely. The value is
|
||||
specified in milliseconds.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attributeGroup ref="integration:smartLifeCycleAttributeGroup"/>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
@@ -145,7 +234,7 @@
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attributeGroup ref="integration:smartLifeCycleAttributeGroup"/>
|
||||
<xsd:attribute name="twitter-template" type="xsd:string">
|
||||
<xsd:attribute name="twitter-template" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
@@ -172,32 +261,39 @@
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="page-size" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Limits the number of tweets retrieved on each poll; default: 20.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="outbound-twitter-type">
|
||||
<xsd:all>
|
||||
<xsd:choice minOccurs="0" maxOccurs="2">
|
||||
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
|
||||
</xsd:all>
|
||||
<xsd:attributeGroup ref="integration:channelAdapterAttributes"/>
|
||||
<xsd:attribute name="twitter-template" use="required" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.social.twitter.api.Twitter"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
<xsd:documentation>
|
||||
Reference to a TwitterTemplate bean provided by the Spring Social project.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="order">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Specifies the order for invocation when this endpoint is connected as a
|
||||
subscriber to a SubscribableChannel.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
|
||||
</xsd:choice>
|
||||
<xsd:attribute name="twitter-template" use="required" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.social.twitter.api.Twitter"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
<xsd:documentation>
|
||||
Reference to a TwitterTemplate bean provided by the Spring Social project.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="order">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Specifies the order for invocation when this endpoint is connected as a
|
||||
subscriber to a SubscribableChannel.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:schema>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,11 +2,12 @@
|
||||
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="twitter"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
|
||||
<title>Twitter Adapter</title>
|
||||
<title>Twitter Support</title>
|
||||
<para>
|
||||
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 <emphasis>version 4.0</emphasis>, a search outbound
|
||||
gateway is provided to perform dynamic searches.
|
||||
</para>
|
||||
|
||||
<section id="twitter-intro">
|
||||
@@ -21,6 +22,8 @@
|
||||
Versions of Spring Integration prior to 2.1 were dependent upon the <ulink url="http://twitter4j.org">Twitter4J API</ulink>,
|
||||
but with the release of <ulink url="http://projects.spring.io/spring-social">Spring Social 1.0 GA</ulink>,
|
||||
Spring Integration, as of version 2.1, now builds directly upon Spring Social's Twitter support, instead of Twitter4J.
|
||||
All Twitter endpoints require the configuration of a <classname>TwitterTemplate</classname> because even
|
||||
search operations require an authenticated template.
|
||||
</important>
|
||||
</para>
|
||||
|
||||
@@ -37,7 +40,7 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/twitter
|
||||
<title>Twitter OAuth Configuration</title>
|
||||
|
||||
<para>
|
||||
The Twitter API allows for both authenticated and anonymous operations. For authenticated operations Twitter uses OAuth
|
||||
For authenticated operations, Twitter uses OAuth
|
||||
- an authentication protocol that allows users to approve an application to act on their behalf without
|
||||
sharing their password. More information can be found at <ulink url="http://oauth.net">http://oauth.net</ulink> or
|
||||
in this article <ulink url="http://hueniverse.com/oauth">http://hueniverse.com/oauth</ulink> from Hueniverse.
|
||||
@@ -156,6 +159,10 @@ twitter.oauth.accessTokenSecret=AbRxUAvyNCtqQtxFK8w5ZMtMj20KFhB6o]]></programlis
|
||||
<code>id</code> attribute of the Twitter Inbound Channel Adapter component plus the <code>profileId</code>
|
||||
of the Twitter user.
|
||||
</note>
|
||||
<para>
|
||||
Prior to <emphasis>version 4.0</emphasis>, the page size was hard-coded to 20. This is now configurable
|
||||
using the <code>page-size</code> attribute (defaults to 20).
|
||||
</para>
|
||||
|
||||
<section id="inbound-twitter-update">
|
||||
<title>Inbound Message Channel Adapter</title>
|
||||
@@ -291,4 +298,89 @@ twitter.oauth.accessTokenSecret=AbRxUAvyNCtqQtxFK8w5ZMtMj20KFhB6o]]></programlis
|
||||
</important>
|
||||
</para>
|
||||
</section>
|
||||
<section id="twitter-sog">
|
||||
<title>Twitter Search Outbound Gateway</title>
|
||||
<para>
|
||||
In Spring Integration, an outbound gateway is used for two-way request/response communication with
|
||||
an external service. The Twitter Search Outbound Gateway allows you to issue dynamic twitter
|
||||
searches. The reply message payload is a collection of <classname>Tweet</classname> objects.
|
||||
If the search returns no results, the payload is an empty collection. You can limit the number
|
||||
of tweets and you can page through a larger set of tweets by making multiple calls. To facilitate this, search
|
||||
reply messages contain a header <code>twitter_searchMetadata</code> with its value being
|
||||
a <classname>SearchMetadata</classname> object. For more information
|
||||
on the <classname>Tweet</classname>, <classname>SearchParameters</classname> and
|
||||
<classname>SearchMetadata</classname> classes, refer to the <ulink
|
||||
url="http://projects.spring.io/spring-social-twitter/">Spring Social Twitter</ulink>
|
||||
documentation.
|
||||
</para>
|
||||
<para>
|
||||
<emphasis role="bold">Configuring the Outbound Gateway</emphasis>
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[<int-twitter:search-outbound-gateway id="twitter"
|
||||
request-channel="in" ]]><co id="tsog010" /><![CDATA[
|
||||
twitter-template="twitterTemplate" ]]><co id="tsog020" /><![CDATA[
|
||||
search-args-expression="payload" ]]><co id="tsog030" /><![CDATA[
|
||||
reply-channel="out" ]]><co id="tsog040" /><![CDATA[
|
||||
reply-timeout="123" ]]><co id="tsog050" /><![CDATA[
|
||||
order="1" ]]><co id="tsog060" /><![CDATA[
|
||||
auto-startup="false" ]]><co id="tsog070" /><![CDATA[
|
||||
phase="100" ]]><co id="tsog080" /><![CDATA[ />
|
||||
]]></programlisting>
|
||||
<calloutlist>
|
||||
<callout arearefs="tsog010">
|
||||
<para>The channel used to send search requests to this gateway.</para>
|
||||
</callout>
|
||||
<callout arearefs="tsog020">
|
||||
<para>A reference to a <classname>TwitterTemplate</classname> with authentication configuration.</para>
|
||||
</callout>
|
||||
<callout arearefs="tsog030">
|
||||
<para>
|
||||
A SpEL expression that evaluates to argument(s) for the search. Default:
|
||||
<emphasis role="bold">"payload"</emphasis> - in which case the payload can be a <classname>String</classname>
|
||||
(e.g "#springintegration") and the gateway limits the query to 20 tweets, or the payload can be a
|
||||
<classname>SearchParameters</classname> object.
|
||||
</para>
|
||||
<para>
|
||||
The expression can also be specified as a <ulink url=
|
||||
"http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html#expressions-inline-lists"
|
||||
>SpEL List</ulink>. The first element (String) is the query, the remaining elements (Numbers)
|
||||
are <code>pageSize, sinceId, maxId</code> respectively - refer to the Spring Social Twitter
|
||||
documentation for more information about these parameters.
|
||||
When specifying a <classname>SearchParameters</classname> object directly in the SpEL
|
||||
expression, you do not have to fully qualify the class name. Some examples:
|
||||
<programlisting language="xml">"new SearchParameters(payload).count(5).sinceId(headers.sinceId)"
|
||||
"{payload, 30}"
|
||||
"{payload, headers.pageSize, headers.sinceId, headers.maxId}"</programlisting>
|
||||
</para>
|
||||
</callout>
|
||||
<callout arearefs="tsog040">
|
||||
<para>
|
||||
The channel to which to send the reply; if omitted, the <code>replyChannel</code> header
|
||||
is used.
|
||||
</para>
|
||||
</callout>
|
||||
<callout arearefs="tsog050">
|
||||
<para>
|
||||
The timeout when sending the reply message to the reply channel; only applies if the reply
|
||||
channel can block, for example a bounded queue channel that is full.
|
||||
</para>
|
||||
</callout>
|
||||
<callout arearefs="tsog060">
|
||||
<para>
|
||||
When subscribed to a publish/subscribe channel, the order in which this endpoint will
|
||||
be invoked.
|
||||
</para>
|
||||
</callout>
|
||||
<callout arearefs="tsog070">
|
||||
<para>
|
||||
<interfacename>SmartLifecycle</interfacename> method.
|
||||
</para>
|
||||
</callout>
|
||||
<callout arearefs="tsog080">
|
||||
<para>
|
||||
<interfacename>SmartLifecycle</interfacename> method.
|
||||
</para>
|
||||
</callout>
|
||||
</calloutlist>
|
||||
</section>
|
||||
</chapter>
|
||||
|
||||
@@ -163,6 +163,15 @@
|
||||
For more information, see <xref linkend="annotations"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="4.0-twitter-sog">
|
||||
<title>Twitter Search Outbound Gateway</title>
|
||||
<para>
|
||||
A new twitter endpoint <classname><int-twitter-search-outbound-gateway/></classname>
|
||||
has been added. Unlike the search inbound adapter which polls using the same search
|
||||
query each time, the outbound gateway allows on-demand customized queries.
|
||||
For more information, see <xref linkend="twitter-sog"/>.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section id="4.0-general">
|
||||
|
||||
Reference in New Issue
Block a user