Merge pull request #68 from olegz/INT-1818

using parameterized types
  migrated Twitter support to use Spring Social
This commit is contained in:
Mark Fisher
2011-09-15 14:26:35 -04:00
32 changed files with 346 additions and 1543 deletions

View File

@@ -457,7 +457,8 @@ project('spring-integration-twitter') {
dependencies {
compile project(":spring-integration-core")
compile "org.springframework:spring-context-support:$springVersion"
compile "org.twitter4j:twitter4j-core:2.1.12"
compile "org.springframework.social:spring-social-twitter:1.0.0.RELEASE"
compile "org.springframework.security:spring-security-crypto:3.1.0.RC3"
compile("javax.activation:activation:$javaxActivationVersion") { optional = true }
testCompile project(":spring-integration-test")
}

View File

@@ -18,9 +18,9 @@
</para>
<para>
<important>
Current Twitter support is based on the <link linkend="http://twitter4j.org/en/index.html">Twitter4J API</link>.
However, future versions will be changed to use the <link linkend="https://jira.springframework.org/browse/SOCIAL">Spring Social</link>
project as it is nearing its first release at the time of writing.
Previous versions of Spring Integration were dependent upon the <link linkend="http://twitter4j.org/en/index.html">Twitter4J API</link>,
but with the release of <link linkend="http://www.springsource.org/spring-social">Spring Social 1.0 GA</link>,
Spring Integration, as of version 2.1, now builds directly upon Spring Social's Twitter support, instead of Twitter4J.
</important>
</para>
@@ -29,7 +29,7 @@
the following within your XML header.
<programlisting language="xml"><![CDATA[xmlns:int-twitter="http://www.springframework.org/schema/integration/twitter"
xsi:schemaLocation="http://www.springframework.org/schema/integration/twitter
http://www.springframework.org/schema/integration/twitter/spring-integration-twitter-2.0.xsd"]]></programlisting>
http://www.springframework.org/schema/integration/twitter/spring-integration-twitter-2.1.xsd"]]></programlisting>
</para>
</section>
@@ -72,15 +72,15 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/twitter
<title>Twitter Template</title>
<para>
Spring Integration uses the same familiar template pattern to interact with Twitter. Since current Twitter support
is based on the Twitter4J API we provide a simple Twiter4JTemplate.
For anonymous operations (e.g., search), you don't have to define <classname>Twitter4JTemplate</classname> explicitly, since a default
instance will be created and injected into the endpoint. However, for authenticated operation
(update status, send direct message, etc.), you must configure <classname>Twitter4JTemplate</classname> as a bean and
inject it explicitly into the endpoint, because the authentication configuration is required.
Below is a sample configuration of Twitter4JTemplate:
<programlisting language="xml"><![CDATA[<bean id="twitterTemplate" class="org.springframework.integration.twitter.core.Twitter4jTemplate">
As mentioned above, Spring Integration relies upon Spring Social, and that library provides an implementation of the template
pattern, <classname>org.springframework.social.twitter.api.impl.TwitterTemplate</classname> to interact with Twitter.
For anonymous operations (e.g., search), you don't have to define an instance of <classname>TwitterTemplate</classname> explicitly,
since a default instance will be created and injected into the endpoint. However, for authenticated operations
(update status, send direct message, etc.), you must configure a <classname>TwitterTemplate</classname> as a bean and
inject it explicitly into the endpoint, because the authentication configuration is required.
Below is a sample configuration of TwitterTemplate:
<programlisting language="xml"><![CDATA[<bean id="twitterTemplate" class="org.springframework.social.twitter.api.impl.TwitterTemplate">
<constructor-arg value="4XzBPacJQxyBzzzH"/>
<constructor-arg value="AbRxUAvyCtqQtvxFK8w5ZMtMj20KFhB6o"/>
<constructor-arg value="21691649-4YZY5iJEOfz2A9qCFd9SjBRGb3HLmIm4HNE"/>
@@ -108,7 +108,7 @@ twitter.oauth.accessTokenSecret=AbRxUAvyNCtqQtxFK8w5ZMtMj20KFhB6o]]></programlis
<programlisting language="java"><![CDATA[<context:property-placeholder
location="classpath:oauth.properties"/>
<bean id="twitterTemplate" class="org.springframework.integration.twitter.core.Twitter4jTemplate">
<bean id="twitterTemplate" class="org.springframework.social.twitter.api.impl.TwitterTemplate">
<constructor-arg value="${twitter.oauth.consumerKey}"/>
<constructor-arg value="${twitter.oauth.consumerSecret}"/>
<constructor-arg value="${twitter.oauth.accessToken}"/>
@@ -219,7 +219,7 @@ received.
of Spring Social.
</para>
<para>
To get the text from the <classname>org.springframework.integration.twitter.core.Tweet</classname>
To get the text from the <classname>org.springframework.social.twitter.api.Tweet</classname>
simply invoke the <code>getText()</code> method.
</para>
</section>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors
* Copyright 2002-2011 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.
@@ -23,6 +23,11 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.integration.twitter.inbound.DirectMessageReceivingMessageSource;
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;
/**
@@ -33,20 +38,16 @@ import org.springframework.util.StringUtils;
*/
public class TwitterInboundChannelAdapterParser extends AbstractPollingInboundChannelAdapterParser {
private static final String BASE_PACKAGE = "org.springframework.integration.twitter";
@Override
protected BeanMetadataElement parseSource(Element element, ParserContext parserContext) {
String className = determineClassName(element, parserContext);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(className);
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(
BASE_PACKAGE + ".core.Twitter4jTemplate");
BeanDefinitionBuilder templateBuilder = BeanDefinitionBuilder.genericBeanDefinition(TwitterTemplate.class);
builder.addConstructorArgValue(templateBuilder.getBeanDefinition());
}
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "query");
@@ -54,25 +55,25 @@ public class TwitterInboundChannelAdapterParser extends AbstractPollingInboundCh
}
private static String determineClassName(Element element, ParserContext parserContext) {
String className = null;
private static Class<?> determineClass(Element element, ParserContext parserContext) {
Class<?> clazz = null;
String elementName = element.getLocalName().trim();
if ("inbound-channel-adapter".equals(elementName)) {
className = BASE_PACKAGE + ".inbound.TimelineReceivingMessageSource";
clazz = TimelineReceivingMessageSource.class;
}
else if ("dm-inbound-channel-adapter".equals(elementName)) {
className = BASE_PACKAGE + ".inbound.DirectMessageReceivingMessageSource";
clazz = DirectMessageReceivingMessageSource.class;
}
else if ("mentions-inbound-channel-adapter".equals(elementName)) {
className = BASE_PACKAGE + ".inbound.MentionsReceivingMessageSource";
clazz = MentionsReceivingMessageSource.class;
}
else if ("search-inbound-channel-adapter".equals(elementName)){
className = BASE_PACKAGE + ".inbound.SearchReceivingMessageSource";
clazz = SearchReceivingMessageSource.class;
}
else {
parserContext.getReaderContext().error("element '" + elementName + "' is not supported by this parser.", element);
}
return className;
return clazz;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors
* Copyright 2002-2011 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors
* Copyright 2002-2011 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,6 +22,8 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser;
import org.springframework.integration.twitter.outbound.DirectMessageSendingMessageHandler;
import org.springframework.integration.twitter.outbound.StatusUpdatingMessageHandler;
/**
* Parser for all outbound Twitter adapters
@@ -32,31 +34,28 @@ import org.springframework.integration.config.xml.AbstractOutboundChannelAdapter
*/
public class TwitterOutboundChannelAdapterParser extends AbstractOutboundChannelAdapterParser {
private static final String BASE_PACKAGE = "org.springframework.integration.twitter";
@Override
protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) {
String className = determineClassName(element, parserContext);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(className);
Class<?> clazz = determineClass(element, parserContext);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(clazz);
builder.addConstructorArgReference(element.getAttribute("twitter-template"));
return builder.getBeanDefinition();
}
private static String determineClassName(Element element, ParserContext parserContext) {
String className = null;
private static Class<?> determineClass(Element element, ParserContext parserContext) {
Class<?> clazz = null;
String elementName = element.getLocalName().trim();
if ("outbound-channel-adapter".equals(elementName)) {
className = BASE_PACKAGE + ".outbound.StatusUpdatingMessageHandler";
clazz = StatusUpdatingMessageHandler.class;
}
else if ("dm-outbound-channel-adapter".equals(elementName)) {
className = BASE_PACKAGE + ".outbound.DirectMessageSendingMessageHandler";
clazz = DirectMessageSendingMessageHandler.class;
}
else {
parserContext.getReaderContext().error("element '" + elementName + "' is not supported by this parser.", element);
}
return className;
return clazz;
}
}

View File

@@ -1,64 +0,0 @@
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.twitter.core;
import java.util.List;
/**
* Represents the results of a Twitter search, including matching {@link Tweet}s
* and any metadata associated with that search.
*
* @author Craig Walls
*/
public class SearchResults {
private List<Tweet> tweets;
private long maxId;
private long sinceId;
public SearchResults(List<Tweet> tweets, long maxId, long sinceId) {
this.tweets = tweets;
this.maxId = maxId;
this.sinceId = sinceId;
}
/**
* Returns the list of matching {@link Tweet}s
*/
public List<Tweet> getTweets() {
return tweets;
}
/**
* Returns the maximum {@link Tweet} ID in the search results
*/
public long getMaxId() {
return maxId;
}
/**
* Returns the {@link Tweet} ID after which all of the matching {@link Tweet}s were created
*/
public long getSinceId() {
return sinceId;
}
}

View File

@@ -1,120 +0,0 @@
/*
* Copyright 2002-2010 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.twitter.core;
import java.util.Date;
/**
* Represents a Twitter status update (e.g., a "tweet").
*
* @author Craig Walls
* @author Oleg Zhurakousky
*/
public class Tweet {
private long id;
private String text;
private Date createdAt;
private String fromUser;
private String profileImageUrl;
private Long toUserId;
private long fromUserId;
private String languageCode;
private String source;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public String getFromUser() {
return fromUser;
}
public void setFromUser(String fromUser) {
this.fromUser = fromUser;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getProfileImageUrl() {
return profileImageUrl;
}
public void setProfileImageUrl(String profileImageUrl) {
this.profileImageUrl = profileImageUrl;
}
public Long getToUserId() {
return toUserId;
}
public void setToUserId(Long toUserId) {
this.toUserId = toUserId;
}
public long getFromUserId() {
return fromUserId;
}
public void setFromUserId(long fromUserId) {
this.fromUserId = fromUserId;
}
public String getLanguageCode() {
return languageCode;
}
public void setLanguageCode(String languageCode) {
this.languageCode = languageCode;
}
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
}

View File

@@ -1,287 +0,0 @@
/*
* Copyright 2002-2011 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.core;
import java.util.LinkedList;
import java.util.List;
import org.springframework.util.Assert;
import twitter4j.DirectMessage;
import twitter4j.Paging;
import twitter4j.Query;
import twitter4j.QueryResult;
import twitter4j.ResponseList;
import twitter4j.Status;
import twitter4j.StatusUpdate;
import twitter4j.Twitter;
import twitter4j.TwitterFactory;
import twitter4j.http.AccessToken;
/**
* Implementation of {@link TwitterOperations} that delegates to Twitter4J.
*
* @author Oleg Zhurakousky
* @since 2.0
*/
public class Twitter4jTemplate implements TwitterOperations {
private final Twitter twitter;
/**
* Used to construct this template to perform Twitter API calls that do not require authorization.
* (e.g., search)
*/
public Twitter4jTemplate() {
this.twitter = new TwitterFactory().getInstance();
}
/**
* Used to construct this template with OAuth authentication/authorization to perform Twitter API calls
* that do require authorization (e.g., send/receive DirectMessage)
*
* @param consumerKey
* @param consumerSecret
* @param accessToken
* @param accessTokenSecret
*/
@SuppressWarnings("deprecation")
public Twitter4jTemplate(String consumerKey, String consumerSecret, String accessToken, String accessTokenSecret) {
Assert.hasText(consumerKey, "'consumerKey' must be provided");
Assert.hasText(consumerSecret, "'consumerSecret' must be provided");
Assert.hasText(accessToken, "'accessToken' must be provided");
Assert.hasText(accessTokenSecret, "'accessTokenSecret' must be provided");
AccessToken token = new AccessToken(accessToken, accessTokenSecret);
this.twitter = new TwitterFactory().getOAuthAuthorizedInstance(consumerKey, consumerSecret, token);
/*
* We are aware of the fact that the above method is deprecated and the code should really look
* like the one below, but we are keeping the deprecated call to address backwards compatibility.
* In future versions we won't be relying on Twitter4J in favor of SpringSocial API.
*/
// Properties properties = new Properties();
// properties.put("oauth.consumerKey", consumerKey);
// properties.put("oauth.consumerSecret", consumerSecret);
// Configuration configuration = new PropertyConfiguration(properties);
// this.twitter = new TwitterFactory(configuration).getInstance(token);
}
public String getProfileId() {
try {
if (twitter.isOAuthEnabled()) {
return twitter.getScreenName();
}
else {
return "twitter-anonymous";
}
}
catch (Exception e) {
throw new TwitterOperationException("Failed to obtain Profile ID. ", e);
}
}
public List<Tweet> getDirectMessages() {
try {
ResponseList<DirectMessage> directMessages = twitter.getDirectMessages();
return this.buildTweetsFromTwitterResponses(directMessages);
}
catch (Exception e) {
throw new TwitterOperationException("Failed to receive Direct Messages. ", e);
}
}
public List<Tweet> getDirectMessages(long sinceId) {
try {
ResponseList<DirectMessage> directMessages = twitter.getDirectMessages(new Paging(sinceId));
return this.buildTweetsFromTwitterResponses(directMessages);
}
catch (Exception e) {
throw new TwitterOperationException("Failed to receive Direct Messages since the last message with ID: "
+ sinceId + ".", e);
}
}
public List<Tweet> getMentions() {
try {
ResponseList<Status> mentions = twitter.getMentions();
return this.buildTweetsFromTwitterResponses(mentions);
}
catch (Exception e) {
throw new TwitterOperationException("Failed to receive Mention statuses. ", e);
}
}
public List<Tweet> getMentions(long sinceId) {
try {
ResponseList<Status> mentions = twitter.getMentions(new Paging(sinceId));
return this.buildTweetsFromTwitterResponses(mentions);
}
catch (Exception e) {
throw new TwitterOperationException("Failed to receive Mention statuses since the last status with ID: "
+ sinceId + ".", e);
}
}
public List<Tweet> getTimeline() {
try {
ResponseList<Status> timelines = twitter.getHomeTimeline();
return this.buildTweetsFromTwitterResponses(timelines);
}
catch (Exception e) {
throw new TwitterOperationException("Failed to receive Timeline statuses. ", e);
}
}
public List<Tweet> getTimeline(long sinceId) {
try {
ResponseList<Status> timelines = twitter.getHomeTimeline(new Paging(sinceId));
return this.buildTweetsFromTwitterResponses(timelines);
}
catch (Exception e) {
throw new TwitterOperationException("Failed to receive Timeline statuses since the last status with ID: "
+ sinceId + ".", e);
}
}
public void sendDirectMessage(String userName, String text) {
Assert.hasText(userName, "'userName' is required");
Assert.hasText(text, "'text' is required");
try {
twitter.sendDirectMessage(userName, text);
}
catch (Exception e) {
throw new TwitterOperationException("Failed to send Direct Message to user: " + userName + ".", e);
}
}
public void sendDirectMessage(int userId, String text) {
Assert.state(userId > 0, "'userId' is required");
Assert.hasText(text, "'text' is required");
try {
twitter.sendDirectMessage(userId, text);
}
catch (Exception e) {
throw new TwitterOperationException("Failed to send Direct Message to user with id: " + userId + ".", e);
}
}
public void updateStatus(String statusTweet) {
Assert.hasText(statusTweet, "'statusTweet' must not be null");
try {
StatusUpdate status = new StatusUpdate(statusTweet);
twitter.updateStatus(status);
}
catch (Exception e) {
throw new TwitterOperationException("Failed to send Status update. ", e);
}
}
public SearchResults search(String query) {
Assert.hasText(query, "'query' must not be null");
Query q = new Query(query);
return this.search(q);
}
public SearchResults search(String query, long sinceId) {
Assert.hasText(query, "'query' must not be null");
Query q = new Query(query);
q.setSinceId(sinceId);
return this.search(q);
}
public Twitter getUnderlyingTwitter() {
return this.twitter;
}
private SearchResults search(Query query) {
try {
QueryResult result = twitter.search(query);
if (result != null) {
List<twitter4j.Tweet> t4jTweets = result.getTweets();
List<Tweet> tweets = this.buildTweetsFromTwitterResponses(t4jTweets);
SearchResults results = new SearchResults(tweets, result.getMaxId(), result.getSinceId());
return results;
}
}
catch (Exception e) {
throw new TwitterOperationException("failed to perform Twitter search", e);
}
return null;
}
private List<Tweet> buildTweetsFromTwitterResponses(List<?> responses) {
List<Tweet> tweets = new LinkedList<Tweet>();
if (responses != null) {
for (Object response : responses) {
if (response instanceof Status) {
tweets.add(this.buildTweetFromStatus((Status) response));
}
else if (response instanceof DirectMessage) {
tweets.add(this.buildTweetFromDm((DirectMessage) response));
}
else if (response instanceof twitter4j.Tweet) {
tweets.add(this.buildTweetFromTwitter4jTweet((twitter4j.Tweet) response));
}
else {
throw new TwitterOperationException("Unsupported response type: " + response.getClass());
}
}
}
return tweets;
}
private Tweet buildTweetFromDm(DirectMessage dm) {
Tweet tweet = new Tweet();
tweet.setCreatedAt(dm.getCreatedAt());
tweet.setFromUser(dm.getSenderScreenName());
tweet.setFromUserId(dm.getSenderId());
tweet.setId(dm.getId());
tweet.setText(dm.getText());
tweet.setToUserId((long)dm.getRecipientId());
return tweet;
}
private Tweet buildTweetFromStatus(Status status) {
Tweet tweet = new Tweet();
tweet.setCreatedAt(status.getCreatedAt());
if (status.getUser() != null){
tweet.setFromUser(status.getUser().getScreenName());
tweet.setFromUserId(status.getUser().getId());
}
tweet.setId(status.getId());
tweet.setSource(status.getSource());
tweet.setText(status.getText());
return tweet;
}
private Tweet buildTweetFromTwitter4jTweet(twitter4j.Tweet t4jTweet) {
Tweet tweet = new Tweet();
tweet.setCreatedAt(t4jTweet.getCreatedAt());
tweet.setFromUser(t4jTweet.getFromUser());
tweet.setFromUserId(t4jTweet.getFromUserId());
tweet.setId(t4jTweet.getId());
tweet.setLanguageCode(t4jTweet.getIsoLanguageCode());
tweet.setProfileImageUrl(t4jTweet.getProfileImageUrl());
tweet.setSource(t4jTweet.getSource());
tweet.setText(t4jTweet.getText());
return tweet;
}
}

View File

@@ -1,91 +0,0 @@
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.twitter.core;
import org.springframework.util.StringUtils;
import twitter4j.TwitterException;
/**
* @author Oleg Zhurakousky
* @since 2.0
*/
@SuppressWarnings("serial")
public class TwitterOperationException extends RuntimeException {
private int twitterStatusCode = -1;
public TwitterOperationException() {
this(null, null);
}
/**
* @param description
*/
public TwitterOperationException(String description) {
this(description, null);
}
/**
* @param throwable
*/
public TwitterOperationException(Throwable throwable) {
this(null, throwable);
}
/**
* @param description
* @param throwable
*/
public TwitterOperationException(String description, Throwable throwable) {
super(formatDescription(description, throwable), throwable);
if (throwable instanceof TwitterException){
this.twitterStatusCode = ((TwitterException)throwable).getStatusCode();
}
}
public int getTwitterStatusCode() {
return twitterStatusCode;
}
private static String formatDescription(String description, Throwable throwable) {
StringBuffer buffer = new StringBuffer();
if (StringUtils.hasText(description)){
buffer.append(description + " ");
}
if (throwable != null && throwable instanceof TwitterException){
TwitterException te = (TwitterException) throwable;
String message = te.getMessage();
if (StringUtils.hasText(message)) {
buffer.append("Detailed Error: ");
if (message.contains("{")){
buffer.append(message.substring(0, message.indexOf("{")));
}
else {
buffer.append(message);
}
}
buffer.append("For more information about this exception please visit the following Twitter website: " +
"http://apiwiki.twitter.com/w/page/22554652/HTTP-Response-Codes-and-Errors");
}
return buffer.toString();
}
}

View File

@@ -1,91 +0,0 @@
/*
* Copyright 2002-2010 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.twitter.core;
import java.util.List;
import twitter4j.Twitter;
/**
* @author Craig Walls
* @author Oleg Zhurakousky
* @since 2.0
*/
public interface TwitterOperations {
/**
* Retrieves the user's Twitter screen name.
*
* @return the user's screen name at Twitter
*/
String getProfileId();
/**
* Updates the user's status.
*
* @param status
* The status message
*
*/
void updateStatus(String status);
/**
* Searches Twitter, returning the first page of {@link Tweet}s
*
* @param query
* The search query string
* @return a {@link SearchResults} containing {@link Tweet}s
*
*/
SearchResults search(String query);
/**
* Searches Twitter, returning a specific page out of the complete set of
* results.
*
* @param query
* The search query string
* @param sinceId
* The minimum {@link Tweet} ID to return in the results
*
* @return a {@link SearchResults} containing {@link Tweet}s
*
*/
SearchResults search(String query, long sinceId);
List<Tweet> getDirectMessages();
List<Tweet> getDirectMessages(long sinceId);
List<Tweet> getMentions();
List<Tweet> getMentions(long sinceId);
List<Tweet> getTimeline();
List<Tweet> getTimeline(long sinceId);
void sendDirectMessage(String userName, String text);
void sendDirectMessage(int userId, String text);
/**
* Temporary method. Should be removed one migrated to Spring Social
*/
Twitter getUnderlyingTwitter();
}

View File

@@ -1,5 +1,4 @@
/*
* Copyright 2002-2010 the original author or authors.
/* Copyright 2002-2011 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.
@@ -18,6 +17,7 @@ package org.springframework.integration.twitter.inbound;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
@@ -31,8 +31,10 @@ import org.springframework.integration.core.MessageSource;
import org.springframework.integration.store.MetadataStore;
import org.springframework.integration.store.SimpleMetadataStore;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.twitter.core.Tweet;
import org.springframework.integration.twitter.core.TwitterOperations;
import org.springframework.social.twitter.api.DirectMessage;
import org.springframework.social.twitter.api.Tweet;
import org.springframework.social.twitter.api.Twitter;
import org.springframework.social.twitter.api.UserOperations;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
@@ -58,7 +60,7 @@ abstract class AbstractTwitterMessageSource<T> extends IntegrationObjectSupport
private volatile String metadataKey;
private final Queue<Tweet> tweets = new LinkedBlockingQueue<Tweet>();
private final Queue<T> tweets = new LinkedBlockingQueue<T>();
private volatile int prefetchThreshold = 0;
@@ -66,21 +68,21 @@ abstract class AbstractTwitterMessageSource<T> extends IntegrationObjectSupport
private volatile long lastProcessedId = -1;
private final TwitterOperations twitterOperations;
private final Twitter twitter;
private final TweetComparator tweetComparator = new TweetComparator();
private final Object lastEnqueuedIdMonitor = new Object();
public AbstractTwitterMessageSource(TwitterOperations twitterOperations) {
Assert.notNull(twitterOperations, "twitterOperations must not be null");
this.twitterOperations = twitterOperations;
public AbstractTwitterMessageSource(Twitter twitter) {
Assert.notNull(twitter, "twitter must not be null");
this.twitter = twitter;
}
protected TwitterOperations getTwitterOperations() {
return this.twitterOperations;
protected Twitter getTwitter() {
return this.twitter;
}
@Override
@@ -106,21 +108,26 @@ abstract class AbstractTwitterMessageSource<T> extends IntegrationObjectSupport
else if (logger.isWarnEnabled()) {
logger.warn(this.getClass().getSimpleName() + " has no name. MetadataStore key might not be unique.");
}
String profileId = this.twitterOperations.getProfileId();
if (profileId != null) {
metadataKeyBuilder.append(profileId);
}
this.metadataKey = metadataKeyBuilder.toString();
String lastId = this.metadataStore.get(this.metadataKey);
// initialize the last status ID from the metadataStore
if (StringUtils.hasText(lastId)) {
this.lastProcessedId = Long.parseLong(lastId);
this.lastEnqueuedId = this.lastProcessedId;
UserOperations userOperations = this.twitter.userOperations();
if (userOperations != null){
String profileId = String.valueOf(userOperations.getProfileId());
if (profileId != null) {
metadataKeyBuilder.append(profileId);
}
this.metadataKey = metadataKeyBuilder.toString();
String lastId = this.metadataStore.get(this.metadataKey);
// initialize the last status ID from the metadataStore
if (StringUtils.hasText(lastId)) {
this.lastProcessedId = Long.parseLong(lastId);
this.lastEnqueuedId = this.lastProcessedId;
}
}
}
public Message<?> receive() {
Tweet tweet = this.tweets.poll();
T tweet = this.tweets.poll();
if (tweet == null) {
long currentTime = System.currentTimeMillis();
long elapsedTime = currentTime - this.lastPollForTweet;
@@ -133,23 +140,23 @@ abstract class AbstractTwitterMessageSource<T> extends IntegrationObjectSupport
this.lastPollForTweet = currentTime;
}
if (tweet != null) {
this.lastProcessedId = tweet.getId();
this.lastProcessedId = this.getIdForTweet(tweet);
this.metadataStore.put(this.metadataKey, String.valueOf(this.lastProcessedId));
return MessageBuilder.withPayload(tweet).build();
}
return null;
}
private void enqueueAll(List<Tweet> tweets) {
private void enqueueAll(List<T> tweets) {
Collections.sort(tweets, this.tweetComparator);
for (Tweet tweet : tweets) {
for (T tweet : tweets) {
enqueue(tweet);
}
}
private void enqueue(Tweet tweet) {
private void enqueue(T tweet) {
synchronized (this.lastEnqueuedIdMonitor) {
long id = tweet.getId();
long id = this.getIdForTweet(tweet);
if (id > this.lastEnqueuedId) {
this.tweets.add(tweet);
this.lastEnqueuedId = id;
@@ -160,7 +167,7 @@ abstract class AbstractTwitterMessageSource<T> extends IntegrationObjectSupport
private void refreshTweetQueueIfNecessary() {
try {
if (tweets.size() <= prefetchThreshold) {
List<Tweet> tweets = pollForTweets(lastEnqueuedId);
List<T> tweets = pollForTweets(lastEnqueuedId);
if (!CollectionUtils.isEmpty(tweets)) {
enqueueAll(tweets);
}
@@ -178,13 +185,47 @@ abstract class AbstractTwitterMessageSource<T> extends IntegrationObjectSupport
* Subclasses must implement this to return tweets.
* The 'sinceId' value will be negative if no last id is known.
*/
protected abstract List<Tweet> pollForTweets(long sinceId);
protected abstract List<T> pollForTweets(long sinceId);
private static class TweetComparator implements Comparator<Tweet> {
private long getIdForTweet(T twitterMessage) {
if (twitterMessage instanceof Tweet) {
return ((Tweet) twitterMessage).getId();
}
else if (twitterMessage instanceof DirectMessage) {
return ((DirectMessage) twitterMessage).getId();
}
else {
throw new IllegalArgumentException("Unsupported Twitter object: " + twitterMessage);
}
}
public int compare(Tweet tweet1, Tweet tweet2) {
return tweet1.getCreatedAt().compareTo(tweet2.getCreatedAt());
private class TweetComparator implements Comparator<T> {
public int compare(T tweet1, T tweet2) {
// hopefully temporary logic. Will suggest that SpringSocial use a common base class for DM and Tweet
if (tweet1 instanceof Tweet && tweet2 instanceof Tweet) {
Tweet t1 = (Tweet) tweet1;
Tweet t2 = (Tweet) tweet2;
Date t1CreatedAt = t1.getCreatedAt();
Date t2CreatedAt = t2.getCreatedAt();
Assert.notNull(t1CreatedAt, "Tweet is missing 'createdAt' date. Cannot compare.");
Assert.notNull(t2CreatedAt, "Tweet is missing 'createdAt' date. Cannot compare.");
return t1CreatedAt.compareTo(t2CreatedAt);
}
else if (tweet1 instanceof DirectMessage && tweet2 instanceof DirectMessage) {
DirectMessage d1 = (DirectMessage) tweet1;
DirectMessage d2 = (DirectMessage) tweet2;
Date d1CreatedAt = d1.getCreatedAt();
Date d2CreatedAt = d2.getCreatedAt();
Assert.notNull(d1CreatedAt, "DirectMessage is missing 'createdAt' date. Cannot compare.");
Assert.notNull(d2CreatedAt, "DirectMessage is missing 'createdAt' date. Cannot compare.");
return d1CreatedAt.compareTo(d2CreatedAt);
}
else {
throw new IllegalArgumentException("Uncomparable Twitter objects: " + tweet1 + " and " + tweet2);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 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.
@@ -18,8 +18,8 @@ package org.springframework.integration.twitter.inbound;
import java.util.List;
import org.springframework.integration.twitter.core.Tweet;
import org.springframework.integration.twitter.core.TwitterOperations;
import org.springframework.social.twitter.api.DirectMessage;
import org.springframework.social.twitter.api.Twitter;
/**
* This class handles support for receiving DMs (direct messages) using Twitter.
@@ -29,9 +29,9 @@ import org.springframework.integration.twitter.core.TwitterOperations;
* @author Mark Fisher
* @since 2.0
*/
public class DirectMessageReceivingMessageSource extends AbstractTwitterMessageSource<Tweet> {
public class DirectMessageReceivingMessageSource extends AbstractTwitterMessageSource<DirectMessage> {
public DirectMessageReceivingMessageSource(TwitterOperations twitter) {
public DirectMessageReceivingMessageSource(Twitter twitter) {
super(twitter);
}
@@ -42,10 +42,8 @@ public class DirectMessageReceivingMessageSource extends AbstractTwitterMessageS
}
@Override
protected List<Tweet> pollForTweets(long sinceId) {
return (sinceId > 0)
? this.getTwitterOperations().getDirectMessages(sinceId)
: this.getTwitterOperations().getDirectMessages();
protected List<DirectMessage> pollForTweets(long sinceId) {
return this.getTwitter().directMessageOperations().getDirectMessagesReceived(1, 20, sinceId, 0);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 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.
@@ -18,8 +18,8 @@ package org.springframework.integration.twitter.inbound;
import java.util.List;
import org.springframework.integration.twitter.core.Tweet;
import org.springframework.integration.twitter.core.TwitterOperations;
import org.springframework.social.twitter.api.Tweet;
import org.springframework.social.twitter.api.Twitter;
/**
* Handles forwarding all new {@link twitter4j.Status} that are 'replies' or 'mentions' to some other tweet.
@@ -30,19 +30,19 @@ import org.springframework.integration.twitter.core.TwitterOperations;
*/
public class MentionsReceivingMessageSource extends AbstractTwitterMessageSource<Tweet> {
public MentionsReceivingMessageSource(TwitterOperations twitter){
public MentionsReceivingMessageSource(Twitter twitter) {
super(twitter);
}
@Override
public String getComponentType() {
return "twitter:mention-inbound-channel-adapter";
return "twitter:mentions-inbound-channel-adapter";
}
@Override
protected List<Tweet> pollForTweets(long sinceId) {
return (sinceId > 0) ? this.getTwitterOperations().getMentions(sinceId) : this.getTwitterOperations().getMentions();
return this.getTwitter().timelineOperations().getMentions(1, 20, sinceId, 0);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 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.inbound;
import java.util.Collections;
import java.util.List;
import org.springframework.integration.twitter.core.SearchResults;
import org.springframework.integration.twitter.core.Tweet;
import org.springframework.integration.twitter.core.TwitterOperations;
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;
/**
@@ -33,7 +34,7 @@ public class SearchReceivingMessageSource extends AbstractTwitterMessageSource<T
private volatile String query;
public SearchReceivingMessageSource(TwitterOperations twitter) {
public SearchReceivingMessageSource(Twitter twitter) {
super(twitter);
}
@@ -50,10 +51,8 @@ public class SearchReceivingMessageSource extends AbstractTwitterMessageSource<T
@Override
protected List<Tweet> pollForTweets(long sinceId) {
SearchResults results = (sinceId > 0)
? this.getTwitterOperations().search(query, sinceId)
: this.getTwitterOperations().search(query);
return (results != null) ? results.getTweets() : null;
SearchResults results = this.getTwitter().searchOperations().search(query, 1, 20, sinceId, 0);
return (results != null) ? results.getTweets() : Collections.<Tweet>emptyList();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 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.
@@ -18,8 +18,8 @@ package org.springframework.integration.twitter.inbound;
import java.util.List;
import org.springframework.integration.twitter.core.Tweet;
import org.springframework.integration.twitter.core.TwitterOperations;
import org.springframework.social.twitter.api.Tweet;
import org.springframework.social.twitter.api.Twitter;
/**
* This {@link org.springframework.integration.core.MessageSource} lets Spring Integration consume
@@ -31,7 +31,7 @@ import org.springframework.integration.twitter.core.TwitterOperations;
*/
public class TimelineReceivingMessageSource extends AbstractTwitterMessageSource<Tweet> {
public TimelineReceivingMessageSource(TwitterOperations twitter){
public TimelineReceivingMessageSource(Twitter twitter) {
super(twitter);
}
@@ -43,7 +43,7 @@ public class TimelineReceivingMessageSource extends AbstractTwitterMessageSource
@Override
protected List<Tweet> pollForTweets(long sinceId) {
return(sinceId > 0) ? this.getTwitterOperations().getTimeline(sinceId) : this.getTwitterOperations().getTimeline();
return this.getTwitter().timelineOperations().getHomeTimeline(1, 20, sinceId, 0);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors
* Copyright 2002-2011 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.
@@ -19,7 +19,7 @@ package org.springframework.integration.twitter.outbound;
import org.springframework.integration.Message;
import org.springframework.integration.handler.AbstractMessageHandler;
import org.springframework.integration.twitter.core.TwitterHeaders;
import org.springframework.integration.twitter.core.TwitterOperations;
import org.springframework.social.twitter.api.Twitter;
import org.springframework.util.Assert;
/**
@@ -32,12 +32,12 @@ import org.springframework.util.Assert;
*/
public class DirectMessageSendingMessageHandler extends AbstractMessageHandler {
private final TwitterOperations twitterOperations;
private final Twitter twitter;
public DirectMessageSendingMessageHandler(TwitterOperations twitterOperations) {
Assert.notNull(twitterOperations, "twitterOperations must not be null");
this.twitterOperations = twitterOperations;
public DirectMessageSendingMessageHandler(Twitter twitter) {
Assert.notNull(twitter, "twitter must not be null");
this.twitter = twitter;
}
@@ -46,15 +46,15 @@ public class DirectMessageSendingMessageHandler extends AbstractMessageHandler {
Assert.isTrue(message.getPayload() instanceof String, "Only payload of type String is supported. " +
"Consider adding a transformer to the message flow in front of this adapter.");
Object toUser = message.getHeaders().get(TwitterHeaders.DM_TARGET_USER_ID);
Assert.isTrue(toUser instanceof String || toUser instanceof Integer,
Assert.isTrue(toUser instanceof String || toUser instanceof Number,
"the header '" + TwitterHeaders.DM_TARGET_USER_ID +
"' must contain either a String (a screenname) or an int (a user ID)");
"' must contain either a String (a screenname) or an number (a user ID)");
String payload = (String) message.getPayload();
if (toUser instanceof Integer) {
this.twitterOperations.sendDirectMessage((Integer) toUser, payload);
if (toUser instanceof Number) {
this.twitter.directMessageOperations().sendDirectMessage(((Number) toUser).longValue(), payload);
}
else if (toUser instanceof String) {
this.twitterOperations.sendDirectMessage((String) toUser, payload);
this.twitter.directMessageOperations().sendDirectMessage((String) toUser, payload);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors
* Copyright 2002-2011 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.
@@ -19,8 +19,8 @@ package org.springframework.integration.twitter.outbound;
import org.springframework.integration.Message;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.handler.AbstractMessageHandler;
import org.springframework.integration.twitter.core.Tweet;
import org.springframework.integration.twitter.core.TwitterOperations;
import org.springframework.social.twitter.api.Tweet;
import org.springframework.social.twitter.api.Twitter;
import org.springframework.util.Assert;
/**
@@ -32,12 +32,12 @@ import org.springframework.util.Assert;
*/
public class StatusUpdatingMessageHandler extends AbstractMessageHandler {
private final TwitterOperations twitterOperations;
private final Twitter twitter;
public StatusUpdatingMessageHandler(TwitterOperations twitterOperations) {
Assert.notNull(twitterOperations, "twitterOperations must not be null");
this.twitterOperations = twitterOperations;
public StatusUpdatingMessageHandler(Twitter twitter) {
Assert.notNull(twitter, "twitter must not be null");
this.twitter = twitter;
}
@@ -54,7 +54,7 @@ public class StatusUpdatingMessageHandler extends AbstractMessageHandler {
else {
throw new MessageHandlingException(message, "Unsupported payload type '" + payload.getClass().getName() + "'");
}
this.twitterOperations.updateStatus(statusText);
this.twitter.timelineOperations().updateStatus(statusText);
}
}

View File

@@ -17,7 +17,9 @@
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.0.xsd http://www.springframework.org/schema/integration/twitter http://www.springframework.org/schema/integration/twitter/spring-integration-twitter.xsd">
<beans:bean id="twitter" class="org.springframework.integration.twitter.config.TestReceivingMessageSourceParserTests.TwitterTemplateFactoryBean"/>
<beans:bean id="twitter" class="org.mockito.Mockito" factory-method="mock">
<beans:constructor-arg value="org.springframework.social.twitter.api.Twitter"/>
</beans:bean>
<channel id="inbound_mentions"/>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 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,53 +16,41 @@
package org.springframework.integration.twitter.config;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
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.DirectMessageReceivingMessageSource;
import org.springframework.integration.twitter.inbound.MentionsReceivingMessageSource;
import org.springframework.integration.twitter.inbound.TimelineReceivingMessageSource;
import static org.junit.Assert.assertNotNull;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.integration.twitter.core.TwitterOperations;
/**
* @author Oleg Zhurakousky
*/
public class TestReceivingMessageSourceParserTests {
@org.junit.Test public void test() { }
// NO LONGER RELEVANT...
// @Test
// public void testRecievingAdapterConfigurationAutoStartup(){
// ApplicationContext ac = new ClassPathXmlApplicationContext("TestReceivingMessageSourceParser-context.xml", this.getClass());
// SourcePollingChannelAdapter spca = ac.getBean("mentionAdapter", SourcePollingChannelAdapter.class);
// SmartLifecycle ms = TestUtils.getPropertyValue(spca, "source", SmartLifecycle.class);
// assertFalse(ms.isAutoStartup());
//
// spca = ac.getBean("dmAdapter", SourcePollingChannelAdapter.class);
// ms = TestUtils.getPropertyValue(spca, "source", SmartLifecycle.class);
// assertFalse(ms.isAutoStartup());
//
// spca = ac.getBean("updateAdapter", SourcePollingChannelAdapter.class);
// ms = TestUtils.getPropertyValue(spca, "source", SmartLifecycle.class);
// assertFalse(ms.isAutoStartup());
// }
@Test
public void testReceivingAdapterConfigurationAutoStartup(){
ApplicationContext ac = new ClassPathXmlApplicationContext("TestReceivingMessageSourceParser-context.xml", this.getClass());
SourcePollingChannelAdapter spca = ac.getBean("mentionAdapter", SourcePollingChannelAdapter.class);
MentionsReceivingMessageSource ms = TestUtils.getPropertyValue(spca, "source", MentionsReceivingMessageSource.class);
assertNotNull(ms);
public static class TwitterTemplateFactoryBean implements FactoryBean<TwitterOperations>{
spca = ac.getBean("dmAdapter", SourcePollingChannelAdapter.class);
DirectMessageReceivingMessageSource dms = TestUtils.getPropertyValue(spca, "source", DirectMessageReceivingMessageSource.class);
assertNotNull(dms);
public TwitterOperations getObject() throws Exception {
TwitterOperations oper = mock(TwitterOperations.class);
when(oper.getProfileId()).thenReturn("kermit");
return oper;
}
public Class<?> getObjectType() {
return TwitterOperations.class;
}
public boolean isSingleton() {
return true;
}
spca = ac.getBean("updateAdapter", SourcePollingChannelAdapter.class);
spca = ac.getBean("updateAdapter", SourcePollingChannelAdapter.class);
TimelineReceivingMessageSource tms = TestUtils.getPropertyValue(spca, "source", TimelineReceivingMessageSource.class);
assertNotNull(tms);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 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,18 +16,17 @@
package org.springframework.integration.twitter.config;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.integration.twitter.core.Twitter4jTemplate;
import org.springframework.integration.twitter.core.TwitterOperations;
import org.springframework.integration.twitter.inbound.SearchReceivingMessageSource;
import org.springframework.social.twitter.api.Twitter;
import static org.junit.Assert.assertNotNull;
/**
* @author Oleg Zhurakousky
@@ -35,23 +34,12 @@ import org.springframework.integration.twitter.inbound.SearchReceivingMessageSou
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);
SearchReceivingMessageSource ms = (SearchReceivingMessageSource) TestUtils.getPropertyValue(spca, "source");
//assertFalse(ms.isAutoStartup());
Twitter4jTemplate template = (Twitter4jTemplate) TestUtils.getPropertyValue(ms, "twitterOperations");
assertFalse(template.getUnderlyingTwitter().isOAuthEnabled()); // verify anonymous Twitter
Twitter template = (Twitter) TestUtils.getPropertyValue(ms, "twitter");
assertNotNull(template);
}
@Test
public void testSearchReceivingCustomTemplate(){
ApplicationContext ac = new ClassPathXmlApplicationContext("TestSearchReceivingMessageSourceParser-context.xml", this.getClass());
SourcePollingChannelAdapter spca = ac.getBean("searchAdapterWithTemplate", SourcePollingChannelAdapter.class);
SearchReceivingMessageSource ms = (SearchReceivingMessageSource) TestUtils.getPropertyValue(spca, "source");
//assertFalse(ms.isAutoStartup());
TwitterOperations template = (TwitterOperations) TestUtils.getPropertyValue(ms, "twitterOperations");
assertEquals(ac.getBean("twitter"), template);
}
}

View File

@@ -18,7 +18,7 @@
http://www.springframework.org/schema/integration/twitter http://www.springframework.org/schema/integration/twitter/spring-integration-twitter-2.0.xsd">
<beans:bean id="twitter" class="org.springframework.integration.twitter.core.Twitter4jTemplate"/>
<beans:bean id="twitter" class="org.springframework.social.twitter.api.impl.TwitterTemplate"/>
<channel id="inbound_mentions"/>

View File

@@ -1,138 +0,0 @@
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.twitter.core;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.integration.test.util.TestUtils;
import twitter4j.Paging;
import twitter4j.Query;
import twitter4j.QueryResult;
import twitter4j.StatusUpdate;
import twitter4j.Twitter;
import twitter4j.http.AccessToken;
import twitter4j.http.Authorization;
import twitter4j.http.OAuthAuthorization;
/**
* Validates that all calls are delegated properly top Twitter
*
* @author Oleg Zhurakousky
*
*/
public class Twitter4jTemplateTests {
Twitter4jTemplate template;
Twitter twitter;
@Before
public void prepare() throws Exception{
template = new Twitter4jTemplate();
Field twitterField = Twitter4jTemplate.class.getDeclaredField("twitter");
twitterField.setAccessible(true);
twitter = mock(Twitter.class);
twitterField.set(template, twitter);
}
@Test
public void testOauthConstructor() throws Exception{
template = new Twitter4jTemplate("a", "b", "1234-c", "d");
Twitter twitter = (Twitter) TestUtils.getPropertyValue(template, "twitter");
Authorization auth = twitter.getAuthorization();
assertTrue(twitter.getAuthorization() instanceof OAuthAuthorization);
AccessToken accessToken = ((OAuthAuthorization)auth).getOAuthAccessToken();
assertEquals("1234-c", accessToken.getToken());
assertEquals("d", accessToken.getTokenSecret());
}
@Test
public void testProfileId() throws Exception{
when(twitter.getScreenName()).thenReturn("kermit");
when(twitter.isOAuthEnabled()).thenReturn(true);
assertEquals("kermit", template.getProfileId());
}
@Test
public void testGetDirectMessages() throws Exception{
template.getDirectMessages();
template.getDirectMessages(123);
verify(twitter, times(1)).getDirectMessages();
verify(twitter, times(1)).getDirectMessages(Mockito.any(Paging.class));
}
@Test
public void testGetMentions() throws Exception{
template.getMentions();
template.getMentions(123);
verify(twitter, times(1)).getMentions();
verify(twitter, times(1)).getMentions(Mockito.any(Paging.class));
}
@Test
public void testGetFriendsTimeline() throws Exception{
template.getTimeline();
template.getTimeline(123);
verify(twitter, times(1)).getHomeTimeline();
verify(twitter, times(1)).getHomeTimeline(Mockito.any(Paging.class));
}
@Test
public void testSendDirectMessage() throws Exception{
template.sendDirectMessage("kermit", "hello");
template.sendDirectMessage(1, "hello");
verify(twitter, times(1)).sendDirectMessage("kermit", "hello");
verify(twitter, times(1)).sendDirectMessage(1, "hello");
}
@Test
public void testUpdateStatus() throws Exception{
template.updateStatus("writing twitter test");
verify(twitter, times(1)).updateStatus(Mockito.any(StatusUpdate.class));
}
@Test
public void testSearch() throws Exception{
// set up test
QueryResult result = mock(QueryResult.class);
List<twitter4j.Tweet> t4jTweets = new ArrayList<twitter4j.Tweet>();
t4jTweets.add(mock(twitter4j.Tweet.class));
t4jTweets.add(mock(twitter4j.Tweet.class));
t4jTweets.add(mock(twitter4j.Tweet.class));
when(result.getTweets()).thenReturn(t4jTweets);
when(twitter.search(Mockito.any(Query.class))).thenReturn(result);
// end setup test
SearchResults results = template.search("#s2gx");
List<Tweet> tweets = results.getTweets();
assertNotNull(tweets);
assertEquals(3, tweets.size());
}
}

View File

@@ -1,78 +0,0 @@
/*
* Copyright 2002-2010 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.twitter.core;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
import org.junit.Test;
import org.springframework.util.StringUtils;
import twitter4j.TwitterException;
/**
* @author Oleg Zhurakousky
*
*/
public class TwitterOperationExceptionTests {
@Test
public void test401(){
// will result in exception since a,b,c,d are invalid credentials
Twitter4jTemplate template = new Twitter4jTemplate("a", "b", "1234-c", "d");
try {
template.getProfileId();
fail();
} catch (Exception e) {
assertTrue(e instanceof TwitterOperationException);
assertEquals(401, ((TwitterOperationException)e).getTwitterStatusCode());
}
}
@Test
public void testWithNull(){
try {
throw new TwitterOperationException();
} catch (Exception e) {
TwitterOperationException tex = (TwitterOperationException) e;
assertFalse(StringUtils.hasText(tex.getMessage()));
assertEquals(-1, tex.getTwitterStatusCode());
}
}
@Test
public void testWithTwitterException(){
try {
throw new TwitterOperationException(new TwitterException("foo"));
} catch (Exception e) {
TwitterOperationException tex = (TwitterOperationException) e;
assertTrue(StringUtils.hasText(tex.getMessage()));
assertTrue(tex.getMessage().contains("foo"));
assertEquals(-1, tex.getTwitterStatusCode());
}
}
@Test
public void testWithDescription(){
try {
throw new TwitterOperationException("foo");
} catch (Exception e) {
TwitterOperationException tex = (TwitterOperationException) e;
assertTrue(StringUtils.hasText(tex.getMessage()));
assertTrue(tex.getMessage().contains("foo"));
assertEquals(-1, tex.getTwitterStatusCode());
}
}
}

View File

@@ -18,41 +18,40 @@
<message-history/>
<context:property-placeholder
location="classpath:twitter.receiver.properties"
ignore-unresolvable="true"/>
<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.integration.twitter.core.Twitter4jTemplate">
<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 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:bean>
<twitter:mentions-inbound-channel-adapter twitter-template="twitterTemplate" channel="inbound_mentions">
<poller fixed-rate="5000" max-messages-per-poll="3"/>
</twitter:mentions-inbound-channel-adapter>
<service-activator input-channel="inbound_mentions" ref="twitterAnnouncer" method="mention"/>
<!-- <twitter:dm-inbound-channel-adapter twitter-template="twitterTemplate" channel="inbound_dm">-->
<!-- <poller fixed-rate="5000" max-messages-per-poll="3"/>-->
<!-- </twitter:dm-inbound-channel-adapter>-->
<!-- <service-activator input-channel="inbound_dm" ref="twitterAnnouncer" method="dm"/>-->
<!-- <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:search-inbound-channel-adapter id="searchAdapter" channel="inbound_search" query="#springintegration">-->
<!-- <poller fixed-rate="5000" max-messages-per-poll="5"/>-->
<!-- </twitter:search-inbound-channel-adapter>-->
<!-- <service-activator input-channel="inbound_search" ref="twitterAnnouncer" method="search"/>-->
<!-- <service-activator input-channel="inbound_mentions" ref="twitterAnnouncer" method="mention"/> -->
<!-- <twitter:inbound-channel-adapter id="twitterInbound" twitter-template="twitterTemplate" channel="inbound_updates">-->
<!-- <poller fixed-rate="1000" max-messages-per-poll="3"/>-->
<!-- </twitter:inbound-channel-adapter>-->
<!-- <service-activator input-channel="inbound_updates" ref="twitterAnnouncer" method="updates"/>-->
<!-- <twitter:dm-inbound-channel-adapter twitter-template="twitterTemplate" channel="inbound_dm"> -->
<!-- <poller fixed-rate="5000" max-messages-per-poll="-1"/> -->
<!-- </twitter:dm-inbound-channel-adapter> -->
<!-- <service-activator input-channel="inbound_dm" ref="twitterAnnouncer" method="dm"/> -->
<!-- <twitter:search-inbound-channel-adapter id="searchAdapter" twitter-template="twitterTemplate" channel="inbound_search" query="#springintegration"> -->
<!-- <poller fixed-rate="5000" max-messages-per-poll="5"/> -->
<!-- </twitter:search-inbound-channel-adapter> -->
<!-- <service-activator input-channel="inbound_search" ref="twitterAnnouncer" method="search"/> -->
<twitter:inbound-channel-adapter id="twitterInbound" twitter-template="twitterTemplate" channel="inbound_updates">
<poller fixed-rate="1000" max-messages-per-poll="3"/>
</twitter:inbound-channel-adapter>
<service-activator input-channel="inbound_updates" ref="twitterAnnouncer" method="updates"/>
<beans:bean id="twitterAnnouncer" class="org.springframework.integration.twitter.ignored.TwitterAnnouncer"/>
</beans:beans>

View File

@@ -21,7 +21,7 @@
location="classpath:twitter.sender.properties"
ignore-unresolvable="true"/>
<beans:bean id="twitterTemplate" class="org.springframework.integration.twitter.core.Twitter4jTemplate">
<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}"/>

View File

@@ -21,7 +21,7 @@
location="classpath:twitter.receiver.properties"
ignore-unresolvable="true"/>
<beans:bean id="twitterTemplate" class="org.springframework.integration.twitter.core.Twitter4jTemplate">
<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}"/>

View File

@@ -18,15 +18,16 @@ package org.springframework.integration.twitter.ignored;
import org.springframework.integration.Message;
import org.springframework.integration.history.MessageHistory;
import org.springframework.integration.twitter.core.Tweet;
import org.springframework.social.twitter.api.DirectMessage;
import org.springframework.social.twitter.api.Tweet;
import org.springframework.stereotype.Component;
@Component
public class TwitterAnnouncer {
public void dm(Tweet directMessage) {
public void dm(DirectMessage directMessage) {
System.out.println("A direct message has been received from " +
directMessage.getFromUser() + " with text " + directMessage.getText());
directMessage.getSender().getScreenName() + " with text " + directMessage.getText());
}
public void search(Message<?> search) {

View File

@@ -16,197 +16,42 @@
package org.springframework.integration.twitter.inbound;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Properties;
import java.io.File;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Date;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.integration.Message;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.integration.store.PropertiesPersistingMetadataStore;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.integration.twitter.core.Tweet;
import org.springframework.integration.twitter.core.Twitter4jTemplate;
import org.springframework.integration.twitter.core.TwitterOperations;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import twitter4j.DirectMessage;
import twitter4j.Paging;
import twitter4j.RateLimitStatus;
import twitter4j.ResponseList;
import twitter4j.Twitter;
import org.springframework.social.twitter.api.DirectMessage;
import org.springframework.social.twitter.api.impl.TwitterTemplate;
/**
* @author Oleg Zhurakousky
*/
public class DirectMessageReceivingMessageSourceTests {
private DirectMessage firstMessage;
private DirectMessage secondMessage;
private DirectMessage thirdMessage;
private DirectMessage fourthMessage;
private TwitterOperations twitter;
Twitter tw;
@Before
public void prepare() throws Exception{
twitter = new Twitter4jTemplate();
firstMessage = mock(DirectMessage.class);
when(firstMessage.getCreatedAt()).thenReturn(new Date(5555555555L));
when(firstMessage.getId()).thenReturn( (long) 200);
secondMessage = mock(DirectMessage.class);
when(secondMessage.getCreatedAt()).thenReturn(new Date(2222222222L));
when(secondMessage.getId()).thenReturn( (long) 2000);
thirdMessage = mock(DirectMessage.class);
when(thirdMessage.getCreatedAt()).thenReturn(new Date(66666666666L));
when(thirdMessage.getId()).thenReturn( (long) 3000);
fourthMessage = mock(DirectMessage.class);
when(fourthMessage.getCreatedAt()).thenReturn(new Date(77777777777L));
when(fourthMessage.getId()).thenReturn( (long) 4000);
tw = mock(Twitter.class);
Field twField = Twitter4jTemplate.class.getDeclaredField("twitter");
twField.setAccessible(true);
twField.set(twitter, tw);
when(tw.getScreenName()).thenReturn("kermit");
twitter = spy(twitter);
}
@Test
public void testSuccessfullInitialization() throws Exception{
when(tw.isOAuthEnabled()).thenReturn(true);
DirectMessageReceivingMessageSource source = new DirectMessageReceivingMessageSource(twitter);
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.afterPropertiesSet();
source.setBeanName("twitterEndpoint");
source.afterPropertiesSet();
assertEquals("twitter:dm-inbound-channel-adapter.twitterEndpoint.kermit", TestUtils.getPropertyValue(source, "metadataKey"));
}
@SuppressWarnings({ "unchecked" })
@Test
public void testSuccessfullInitializationWithMessages() throws Exception{
this.setUpMockScenarioForMessagePolling();
DirectMessageReceivingMessageSource source = new DirectMessageReceivingMessageSource(twitter);
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.afterPropertiesSet();
source.setBeanName("twitterEndpoint");
source.afterPropertiesSet();
Message<Tweet> msg = (Message<Tweet>) source.receive();
assertNotNull(msg);
Tweet message = msg.getPayload();
assertEquals(2000, message.getId());
Thread.sleep(1000);
verify(twitter, times(1)).getDirectMessages();
}
/**
* This test will validate that last status is initialized from the metadatastore
* @throws Exception
*/
@SuppressWarnings("rawtypes")
@Test
public void testSuccessfullInitializationWithMessagesWithPersistentMetadata() throws Exception{
String fileName = System.getProperty("java.io.tmpdir") + "/spring-integration/metadata-store.properties";
File file = new File(fileName);
if (file.exists()){
file.delete();
}
this.setUpMockScenarioForMessagePolling();
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
PropertiesPersistingMetadataStore store = new PropertiesPersistingMetadataStore();
store.afterPropertiesSet();
bf.registerSingleton(IntegrationContextUtils.METADATA_STORE_BEAN_NAME, store);
DirectMessageReceivingMessageSource source = new DirectMessageReceivingMessageSource(twitter);
source.setBeanFactory(bf);
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.afterPropertiesSet();
source.setBeanName("twitterEndpoint");
source.afterPropertiesSet();
Message msg = source.receive();
assertNotNull(msg);
Tweet tweet = (Tweet) msg.getPayload();
// Resuming
this.prepare();
this.setUpMockScenarioForMessagePolling();
store.destroy();
bf = new DefaultListableBeanFactory();
store = new PropertiesPersistingMetadataStore();
store.afterPropertiesSet();
bf.registerSingleton(IntegrationContextUtils.METADATA_STORE_BEAN_NAME, store);
source = new DirectMessageReceivingMessageSource(twitter);
source.setBeanFactory(bf);
scheduler = new ThreadPoolTaskScheduler();
scheduler.afterPropertiesSet();
source.setBeanName("twitterEndpoint");
source.afterPropertiesSet();
msg = source.receive();
tweet = (Tweet) msg.getPayload();
assertEquals(3000, tweet.getId());
msg = source.receive();
tweet = (Tweet) msg.getPayload();
assertEquals(4000, tweet.getId());
file.delete();
}
@SuppressWarnings("unchecked")
private void setUpMockScenarioForMessagePolling() throws Exception{
RateLimitStatus rateLimitStatus = mock(RateLimitStatus.class);
when(tw.isOAuthEnabled()).thenReturn(true);
when(tw.getRateLimitStatus()).thenReturn(rateLimitStatus);
when(rateLimitStatus.getSecondsUntilReset()).thenReturn(1000);
when(rateLimitStatus.getRemainingHits()).thenReturn(1000);
SampleResoponceList testMessages = new SampleResoponceList();
testMessages.add(firstMessage);
testMessages.add(secondMessage);
when(tw.getDirectMessages()).thenReturn(testMessages);
testMessages = new SampleResoponceList();
testMessages.add(thirdMessage);
testMessages.add(fourthMessage);
when(tw.getDirectMessages(Mockito.any(Paging.class))).thenReturn(testMessages);
}
@SuppressWarnings({ "rawtypes", "serial" })
public static class SampleResoponceList extends ArrayList implements ResponseList {
public RateLimitStatus getRateLimitStatus() {
return mock(RateLimitStatus.class);
@Test @Ignore
public void demoReceiveDm() throws Exception{
PropertiesFactoryBean pf = new PropertiesFactoryBean();
pf.setLocation(new ClassPathResource("sample.properties"));
pf.afterPropertiesSet();
Properties prop = pf.getObject();
System.out.println(prop);
TwitterTemplate template = new TwitterTemplate(prop.getProperty("z_oleg.oauth.consumerKey"),
prop.getProperty("z_oleg.oauth.consumerSecret"),
prop.getProperty("z_oleg.oauth.accessToken"),
prop.getProperty("z_oleg.oauth.accessTokenSecret"));
DirectMessageReceivingMessageSource tSource = new DirectMessageReceivingMessageSource(template);
tSource.afterPropertiesSet();
for (int i = 0; i < 50; i++) {
Message<DirectMessage> message = (Message<DirectMessage>) tSource.receive();
if (message != null){
DirectMessage tweet = message.getPayload();
System.out.println(tweet.getSender().getScreenName() + " - " + tweet.getText() + " - " + tweet.getCreatedAt());
}
}
public RateLimitStatus getFeatureSpecificRateLimitStatus() {
return mock(RateLimitStatus.class);
}
}
}

View File

@@ -16,17 +16,15 @@
package org.springframework.integration.twitter.inbound;
import java.util.Properties;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.integration.Message;
import org.springframework.integration.MessagingException;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
import org.springframework.integration.twitter.core.Tweet;
import org.springframework.integration.twitter.core.Twitter4jTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.social.twitter.api.Tweet;
import org.springframework.social.twitter.api.impl.TwitterTemplate;
/**
@@ -34,37 +32,28 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
*/
public class SearchReceivingMessageSourceTests {
/**
* THis test is a sample test and wil require connecting to a real Twitter
* however no OAuth is required sincxe uts a search, so simply uncomment and run
* @throws Exception
*/
@Test
@Ignore
public void testSearchReceiving() throws Exception{
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.afterPropertiesSet();
bf.registerSingleton("taskScheduler", scheduler);
SearchReceivingMessageSource ms = new SearchReceivingMessageSource(new Twitter4jTemplate());
DirectChannel channel = new DirectChannel();
channel.subscribe(new MessageHandler() {
public void handleMessage(Message<?> message) throws MessagingException {
System.out.println("Message: " + ((Tweet)message.getPayload()).getCreatedAt() + " - " + ((Tweet)message.getPayload()).getText());
@SuppressWarnings("unchecked")
@Test @Ignore
public void demoReceiveSearchResults() throws Exception{
PropertiesFactoryBean pf = new PropertiesFactoryBean();
pf.setLocation(new ClassPathResource("sample.properties"));
pf.afterPropertiesSet();
Properties prop = pf.getObject();
System.out.println(prop);
TwitterTemplate template = new TwitterTemplate(prop.getProperty("z_oleg.oauth.consumerKey"),
prop.getProperty("z_oleg.oauth.consumerSecret"),
prop.getProperty("z_oleg.oauth.accessToken"),
prop.getProperty("z_oleg.oauth.accessTokenSecret"));
SearchReceivingMessageSource tSource = new SearchReceivingMessageSource(template);
tSource.setQuery("#springsocial");
tSource.afterPropertiesSet();
for (int i = 0; i < 50; i++) {
Message<Tweet> message = (Message<Tweet>) tSource.receive();
if (message != null){
Tweet tweet = message.getPayload();
System.out.println(tweet.getFromUser() + " - " + tweet.getText() + " - " + tweet.getCreatedAt());
}
});
SourcePollingChannelAdapter adapter = new SourcePollingChannelAdapter();
adapter.setSource(ms);
adapter.setBeanFactory(bf);
adapter.setOutputChannel(channel);
adapter.afterPropertiesSet();
adapter.start();
ms.setBeanFactory(bf);
ms.setQuery("#springintegration");
//ms.setTaskScheduler(scheduler);
ms.afterPropertiesSet();
//ms.start();
System.in.read();
}
}
}

View File

@@ -16,197 +16,43 @@
package org.springframework.integration.twitter.inbound;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Properties;
import java.io.File;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Date;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.integration.Message;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.integration.store.PropertiesPersistingMetadataStore;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.integration.twitter.core.Tweet;
import org.springframework.integration.twitter.core.Twitter4jTemplate;
import org.springframework.integration.twitter.core.TwitterOperations;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.social.twitter.api.Tweet;
import org.springframework.social.twitter.api.impl.TwitterTemplate;
import twitter4j.Paging;
import twitter4j.RateLimitStatus;
import twitter4j.ResponseList;
import twitter4j.Status;
import twitter4j.Twitter;
/**
* @author Oleg Zhurakousky
*/
public class TimelineReceivingMessageSourceTests {
private Status firstMessage;
private Status secondMessage;
private Status thirdMessage;
private Status fourthMessage;
private TwitterOperations twitter;
Twitter tw;
@Before
public void prepare() throws Exception{
twitter = new Twitter4jTemplate();
firstMessage = mock(Status.class);
when(firstMessage.getCreatedAt()).thenReturn(new Date(5555555555L));
when(firstMessage.getId()).thenReturn( (long) 200);
secondMessage = mock(Status.class);
when(secondMessage.getCreatedAt()).thenReturn(new Date(2222222222L));
when(secondMessage.getId()).thenReturn((long) 2000);
thirdMessage = mock(Status.class);
when(thirdMessage.getCreatedAt()).thenReturn(new Date(66666666666L));
when(thirdMessage.getId()).thenReturn((long) 3000);
fourthMessage = mock(Status.class);
when(fourthMessage.getCreatedAt()).thenReturn(new Date(77777777777L));
when(fourthMessage.getId()).thenReturn( (long)4000);
tw = mock(Twitter.class);
Field twField = Twitter4jTemplate.class.getDeclaredField("twitter");
twField.setAccessible(true);
twField.set(twitter, tw);
when(tw.getScreenName()).thenReturn("kermit");
twitter = spy(twitter);
}
@Test
public void testSuccessfulInitialization() throws Exception{
when(tw.isOAuthEnabled()).thenReturn(true);
TimelineReceivingMessageSource source = new TimelineReceivingMessageSource(twitter);
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.afterPropertiesSet();
source.setBeanName("twitterEndpoint");
source.afterPropertiesSet();
assertEquals("twitter:inbound-channel-adapter.twitterEndpoint.kermit", TestUtils.getPropertyValue(source, "metadataKey"));
}
@SuppressWarnings("rawtypes")
@Test
public void testSuccessfulInitializationWithMessages() throws Exception{
this.setUpMockScenarioForMessagePolling();
TimelineReceivingMessageSource source = new TimelineReceivingMessageSource(twitter);
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.afterPropertiesSet();
source.setBeanName("twitterEndpoint");
source.afterPropertiesSet();
Message msg = source.receive();
assertNotNull(msg);
Tweet message = (Tweet) msg.getPayload();
assertEquals(2000, message.getId());
verify(twitter, times(1)).getTimeline();
}
/**
* This test will validate that last status is initilaized from the metadatastore
* @throws Exception
*/
@SuppressWarnings("rawtypes")
@Test
public void testSuccessfulInitializationWithMessagesWithPersistentMetadata() throws Exception{
String fileName = System.getProperty("java.io.tmpdir") + "/spring-integration/metadata-store.properties";
File file = new File(fileName);
if (file.exists()){
file.delete();
}
this.setUpMockScenarioForMessagePolling();
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
PropertiesPersistingMetadataStore store = new PropertiesPersistingMetadataStore();
store.afterPropertiesSet();
bf.registerSingleton(IntegrationContextUtils.METADATA_STORE_BEAN_NAME, store);
TimelineReceivingMessageSource source = new TimelineReceivingMessageSource(twitter);
source.setBeanFactory(bf);
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.afterPropertiesSet();
source.setBeanName("twitterEndpoint");
source.afterPropertiesSet();
Message message = source.receive();
Tweet tweet = (Tweet) message.getPayload();
assertEquals(2000, tweet.getId());
// Resuming
this.prepare();
this.setUpMockScenarioForMessagePolling();
store.destroy();
bf = new DefaultListableBeanFactory();
store = new PropertiesPersistingMetadataStore();
store.afterPropertiesSet();
bf.registerSingleton(IntegrationContextUtils.METADATA_STORE_BEAN_NAME, store);
source = new TimelineReceivingMessageSource(twitter);
source.setBeanFactory(bf);
scheduler = new ThreadPoolTaskScheduler();
scheduler.afterPropertiesSet();
source.setBeanName("twitterEndpoint");
source.afterPropertiesSet();
message = source.receive();
tweet = (Tweet) message.getPayload();
assertEquals(3000, tweet.getId());
message = source.receive();
tweet = (Tweet) message.getPayload();
assertEquals(4000, tweet.getId());
file.delete();
}
@SuppressWarnings("unchecked")
private void setUpMockScenarioForMessagePolling() throws Exception{
RateLimitStatus rateLimitStatus = mock(RateLimitStatus.class);
when(tw.getRateLimitStatus()).thenReturn(rateLimitStatus);
when(rateLimitStatus.getSecondsUntilReset()).thenReturn(1000);
when(rateLimitStatus.getRemainingHits()).thenReturn(1000);
SampleResoponceList testMessages = new SampleResoponceList();
testMessages.add(firstMessage);
testMessages.add(secondMessage);
when(tw.getHomeTimeline()).thenReturn(testMessages);
testMessages = new SampleResoponceList();
testMessages.add(thirdMessage);
testMessages.add(fourthMessage);
when(tw.getHomeTimeline(Mockito.any(Paging.class))).thenReturn(testMessages);
}
@SuppressWarnings({ "rawtypes", "serial" })
public static class SampleResoponceList extends ArrayList implements ResponseList {
public RateLimitStatus getRateLimitStatus() {
return mock(RateLimitStatus.class);
@Test @Ignore
public void demoReceiveTimeline() throws Exception{
PropertiesFactoryBean pf = new PropertiesFactoryBean();
pf.setLocation(new ClassPathResource("sample.properties"));
pf.afterPropertiesSet();
Properties prop = pf.getObject();
System.out.println(prop);
TwitterTemplate template = new TwitterTemplate(prop.getProperty("z_oleg.oauth.consumerKey"),
prop.getProperty("z_oleg.oauth.consumerSecret"),
prop.getProperty("z_oleg.oauth.accessToken"),
prop.getProperty("z_oleg.oauth.accessTokenSecret"));
TimelineReceivingMessageSource tSource = new TimelineReceivingMessageSource(template);
tSource.afterPropertiesSet();
for (int i = 0; i < 50; i++) {
Message<Tweet> message = (Message<Tweet>) tSource.receive();
if (message != null){
Tweet tweet = message.getPayload();
System.out.println(tweet.getFromUser() + " - " + tweet.getText() + " - " + tweet.getCreatedAt());
}
}
public RateLimitStatus getFeatureSpecificRateLimitStatus() {
return mock(RateLimitStatus.class);
}
}
}

View File

@@ -16,16 +16,16 @@
package org.springframework.integration.twitter.outbound;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.util.Properties;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.integration.Message;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.twitter.core.TwitterHeaders;
import org.springframework.integration.twitter.core.TwitterOperations;
import org.springframework.social.twitter.api.impl.TwitterTemplate;
/**
* @author Oleg Zhurakousky
@@ -33,20 +33,22 @@ import org.springframework.integration.twitter.core.TwitterOperations;
*/
public class DirectMessageSendingMessageHandlerTests {
private TwitterOperations twitter = mock(TwitterOperations.class);
@Test
@Test @Ignore
public void validateSendDirectMessage() throws Exception{
Message<?> message1 = MessageBuilder.withPayload("hello")
.setHeader(TwitterHeaders.DM_TARGET_USER_ID, "foo").build();
DirectMessageSendingMessageHandler handler = new DirectMessageSendingMessageHandler(twitter);
PropertiesFactoryBean pf = new PropertiesFactoryBean();
pf.setLocation(new ClassPathResource("sample.properties"));
pf.afterPropertiesSet();
Properties prop = pf.getObject();
System.out.println(prop);
TwitterTemplate template = new TwitterTemplate(prop.getProperty("spring_eip.oauth.consumerKey"),
prop.getProperty("spring_eip.oauth.consumerSecret"),
prop.getProperty("spring_eip.oauth.accessToken"),
prop.getProperty("spring_eip.oauth.accessTokenSecret"));
Message<?> message1 = MessageBuilder.withPayload("Polsihing SI Twitter migration")
.setHeader(TwitterHeaders.DM_TARGET_USER_ID, "z_oleg").build();
DirectMessageSendingMessageHandler handler = new DirectMessageSendingMessageHandler(template);
handler.afterPropertiesSet();
handler.handleMessage(message1);
verify(twitter, times(1)).sendDirectMessage("foo", "hello");
Message<?> message2 = MessageBuilder.withPayload("hello")
.setHeader(TwitterHeaders.DM_TARGET_USER_ID, 123).build();;
handler.handleMessage(message2);
verify(twitter, times(1)).sendDirectMessage(123, "hello");
}
}

View File

@@ -16,64 +16,37 @@
package org.springframework.integration.twitter.outbound;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.util.Properties;
import java.lang.reflect.Field;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.integration.Message;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.twitter.core.Tweet;
import org.springframework.integration.twitter.core.Twitter4jTemplate;
import org.springframework.integration.twitter.core.TwitterOperations;
import twitter4j.StatusUpdate;
import twitter4j.Twitter;
import org.springframework.social.twitter.api.impl.TwitterTemplate;
/**
* @author Oleg Zhurakousky
* @since 2.0
*/
public class StatusUpdatingMessageHandlerTests {
TwitterOperations twitterOperations;
Twitter twitter;
@Before
public void prepare() throws Exception{
twitterOperations = spy(new Twitter4jTemplate());
Field twitterField = Twitter4jTemplate.class.getDeclaredField("twitter");
twitterField.setAccessible(true);
twitter = mock(Twitter.class);
twitterField.set(twitterOperations, twitter);
}
@Test
@SuppressWarnings({ "unchecked", "rawtypes" })
public void testSendingStatusUpdate() throws Exception{
StatusUpdatingMessageHandler handler = new StatusUpdatingMessageHandler(twitterOperations);
Tweet tweet = new Tweet();
tweet.setText("writing twitter tests");
handler.handleMessage(new GenericMessage(tweet));
verify(twitterOperations, times(1)).updateStatus(Mockito.any(String.class));
verify(twitter, times(1)).updateStatus(Mockito.any(StatusUpdate.class));
}
@Test
public void testSendingStatusUpdateWithStringPayload() throws Exception{
StatusUpdatingMessageHandler handler = new StatusUpdatingMessageHandler(twitterOperations);
Message<?> message = MessageBuilder.withPayload("writing twitter tests").build();
handler.handleMessage(message);
verify(twitterOperations, times(1)).updateStatus(Mockito.any(String.class));
verify(twitter, times(1)).updateStatus(Mockito.any(StatusUpdate.class));
@Test @Ignore
public void demoSendStatusMessage() throws Exception{
PropertiesFactoryBean pf = new PropertiesFactoryBean();
pf.setLocation(new ClassPathResource("sample.properties"));
pf.afterPropertiesSet();
Properties prop = pf.getObject();
TwitterTemplate template = new TwitterTemplate(prop.getProperty("z_oleg.oauth.consumerKey"),
prop.getProperty("z_oleg.oauth.consumerSecret"),
prop.getProperty("z_oleg.oauth.accessToken"),
prop.getProperty("z_oleg.oauth.accessTokenSecret"));
Message<?> message1 = MessageBuilder.withPayload("Ppolishing #springintegration migration to Spring Social. test").build();
StatusUpdatingMessageHandler handler = new StatusUpdatingMessageHandler(template);
handler.afterPropertiesSet();
handler.handleMessage(message1);
}
}