updating tests.
This commit is contained in:
@@ -13,14 +13,12 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.twitter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang.exception.ExceptionUtils;
|
||||
|
||||
import org.springframework.context.Lifecycle;
|
||||
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.MessageChannel;
|
||||
import org.springframework.integration.core.MessagingTemplate;
|
||||
@@ -29,190 +27,190 @@ import org.springframework.integration.history.HistoryWritingMessagePostProcesso
|
||||
import org.springframework.integration.history.TrackableComponent;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.twitter.oauth.OAuthConfiguration;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import twitter4j.RateLimitStatus;
|
||||
import twitter4j.ResponseList;
|
||||
import twitter4j.Twitter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* There are a lot of operations that are common to receiving the various types of messages when using the
|
||||
* There are a lot of operations that are common to receiving the various types of messages when using the
|
||||
* Twitter API, and this class abstracts most of them for you. Implementers must take note of
|
||||
* {@link org.springframework.integration.twitter.AbstractInboundTwitterEndpointSupport#runAsAPIRateLimitsPermit(org.springframework.integration.twitter.AbstractInboundTwitterEndpointSupport.ApiCallback)}
|
||||
* which will invoke the instance of {@link AbstractInboundTwitterEndpointSupport.ApiCallback} when the
|
||||
* rate-limit API deems that its OK to do so. This class handles keeping tabs on that and on spacing out requests
|
||||
* which will invoke the instance of {@link AbstractInboundTwitterEndpointSupport.ApiCallback} when the
|
||||
* rate-limit API deems that its OK to do so. This class handles keeping tabs on that and on spacing out requests
|
||||
* as required.
|
||||
*
|
||||
* <p/>
|
||||
* Simialarly, this class handles keeping track on the latest inbound message its received and avoiding, where
|
||||
* possible, redelivery of common messages. This functionality is enabled using the
|
||||
* Simialarly, this class handles keeping track on the latest inbound message its received and avoiding, where
|
||||
* possible, redelivery of common messages. This functionality is enabled using the
|
||||
* {@link org.springframework.integration.context.metadata.MetadataStore} implementation
|
||||
*
|
||||
* @author Josh Long
|
||||
* @since 2.0
|
||||
*/
|
||||
public abstract class AbstractInboundTwitterEndpointSupport<T> extends AbstractEndpoint implements Lifecycle, TrackableComponent {
|
||||
protected volatile OAuthConfiguration configuration;
|
||||
protected final MessagingTemplate messagingTemplate = new MessagingTemplate();
|
||||
private volatile MessageChannel requestChannel;
|
||||
protected volatile long markerId = -1;
|
||||
protected Twitter twitter;
|
||||
private final Object markerGuard = new Object();
|
||||
private final Object apiPermitGuard = new Object();
|
||||
private final HistoryWritingMessagePostProcessor historyWritingPostProcessor =
|
||||
new HistoryWritingMessagePostProcessor();
|
||||
|
||||
protected volatile OAuthConfiguration configuration;
|
||||
public void setConfiguration(OAuthConfiguration configuration) {
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
protected final MessagingTemplate messagingTemplate = new MessagingTemplate();
|
||||
abstract protected void markLastStatusId(T statusId);
|
||||
|
||||
private volatile MessageChannel requestChannel;
|
||||
abstract protected List<T> sort(List<T> rl);
|
||||
|
||||
protected volatile long markerId = -1;
|
||||
protected void forwardAll(List<T> tResponses) {
|
||||
List<T> stats = new ArrayList<T>();
|
||||
|
||||
protected Twitter twitter;
|
||||
for (T t : tResponses)
|
||||
stats.add(t);
|
||||
|
||||
private final Object markerGuard = new Object();
|
||||
for (T twitterResponse : sort(stats))
|
||||
forward(twitterResponse);
|
||||
}
|
||||
|
||||
private final Object apiPermitGuard = new Object();
|
||||
|
||||
private final HistoryWritingMessagePostProcessor historyWritingPostProcessor = new HistoryWritingMessagePostProcessor();
|
||||
public long getMarkerId() {
|
||||
return markerId;
|
||||
}
|
||||
|
||||
public void setConfiguration(OAuthConfiguration configuration) {
|
||||
this.configuration = configuration;
|
||||
}
|
||||
public String getComponentType() {
|
||||
return "twitter:inbound-dm-channel-adapter";
|
||||
}
|
||||
|
||||
public void setRequestChannel(MessageChannel requestChannel) {
|
||||
this.messagingTemplate.setDefaultChannel(requestChannel);
|
||||
this.requestChannel = requestChannel;
|
||||
}
|
||||
|
||||
abstract protected void markLastStatusId(T statusId);
|
||||
@Override
|
||||
protected void doStart() {
|
||||
try {
|
||||
this.historyWritingPostProcessor.setTrackableComponent(this);
|
||||
refresh();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
abstract protected List<T> sort( List<T> rl);
|
||||
protected void forward(T status) {
|
||||
synchronized (this.markerGuard) {
|
||||
Message<T> twtMsg = MessageBuilder.withPayload(status).build();
|
||||
messagingTemplate.convertAndSend(requestChannel, twtMsg,
|
||||
this.historyWritingPostProcessor);
|
||||
markLastStatusId(status);
|
||||
}
|
||||
}
|
||||
|
||||
protected void forwardAll( List<T> tResponses) {
|
||||
List<T> stats = new ArrayList<T>();
|
||||
abstract protected void refresh() throws Exception;
|
||||
|
||||
for (T t : tResponses)
|
||||
stats.add(t);
|
||||
protected void forwardAll(ResponseList<T> tResponses) {
|
||||
List<T> stats = new ArrayList<T>();
|
||||
|
||||
for (T twitterResponse : sort(stats))
|
||||
forward(twitterResponse);
|
||||
}
|
||||
for (T t : tResponses)
|
||||
stats.add(t);
|
||||
|
||||
public long getMarkerId() {
|
||||
return markerId;
|
||||
}
|
||||
|
||||
public String getComponentType() {
|
||||
return "twitter:inbound-dm-channel-adapter";
|
||||
}
|
||||
|
||||
public void setRequestChannel(MessageChannel requestChannel) {
|
||||
this.messagingTemplate.setDefaultChannel(requestChannel);
|
||||
this.requestChannel = requestChannel;
|
||||
}
|
||||
for (T twitterResponse : sort(stats))
|
||||
forward(twitterResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() {
|
||||
try {
|
||||
this.historyWritingPostProcessor.setTrackableComponent(this);
|
||||
refresh();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void runAsAPIRateLimitsPermit(ApiCallback cb)
|
||||
throws Exception {
|
||||
synchronized (this.apiPermitGuard) {
|
||||
while (waitUntilPullAvailable()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("have room to make an API request now");
|
||||
}
|
||||
|
||||
protected void forward(T status) {
|
||||
synchronized (this.markerGuard) {
|
||||
Message<T> twtMsg = MessageBuilder.withPayload(status).build();
|
||||
messagingTemplate.convertAndSend(requestChannel, twtMsg, this.historyWritingPostProcessor);
|
||||
markLastStatusId(status);
|
||||
}
|
||||
}
|
||||
|
||||
//abstract protected List<T> sort(List<T> rl);
|
||||
|
||||
//abstract protected void markLastStatusId(T statusId);
|
||||
|
||||
abstract protected void refresh() throws Exception;
|
||||
cb.run(this, twitter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void forwardAll(ResponseList<T> tResponses) {
|
||||
List<T> stats = new ArrayList<T>();
|
||||
protected boolean handleReceivingRateLimitStatus(
|
||||
RateLimitStatus rateLimitStatus) {
|
||||
try {
|
||||
int secondsUntilReset = rateLimitStatus.getSecondsUntilReset();
|
||||
int remainingHits = rateLimitStatus.getRemainingHits();
|
||||
|
||||
for (T t : tResponses)
|
||||
stats.add(t);
|
||||
if (remainingHits == 0) {
|
||||
logger.debug(
|
||||
"rate status limit service returned 0 for the remaining hits value");
|
||||
|
||||
for (T twitterResponse : sort(stats))
|
||||
forward(twitterResponse);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void runAsAPIRateLimitsPermit(ApiCallback cb)
|
||||
throws Exception {
|
||||
synchronized (this.apiPermitGuard) {
|
||||
while (waitUntilPullAvailable()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("have room to make an API request now");
|
||||
}
|
||||
if (secondsUntilReset == 0) {
|
||||
logger.debug(
|
||||
"rate status limit service returned 0 for the seconds until reset period value");
|
||||
|
||||
cb.run(this, twitter);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean handleReceivingRateLimitStatus(RateLimitStatus rateLimitStatus) {
|
||||
try {
|
||||
int secondsUntilReset = rateLimitStatus.getSecondsUntilReset();
|
||||
int remainingHits = rateLimitStatus.getRemainingHits();
|
||||
int secondsUntilWeCanPullAgain = secondsUntilReset / remainingHits;
|
||||
long msUntilWeCanPullAgain = secondsUntilWeCanPullAgain * 1000;
|
||||
|
||||
if (remainingHits == 0) {
|
||||
logger.debug("rate status limit service returned 0 for the remaining hits value");
|
||||
logger.debug("need to Thread.sleep() " +
|
||||
secondsUntilWeCanPullAgain +
|
||||
" seconds until the next timeline pull. Have " + remainingHits +
|
||||
" remaining pull this rate period. The period ends in " +
|
||||
secondsUntilReset);
|
||||
|
||||
return false;
|
||||
}
|
||||
Thread.sleep(msUntilWeCanPullAgain);
|
||||
} catch (Throwable throwable) {
|
||||
logger.debug("encountered an error when" +
|
||||
" trying to refresh the timeline: " +
|
||||
ExceptionUtils.getFullStackTrace(throwable));
|
||||
}
|
||||
|
||||
if (secondsUntilReset == 0) {
|
||||
logger.debug("rate status limit service returned 0 for the seconds until reset period value");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
protected boolean waitUntilPullAvailable() throws Exception {
|
||||
return this.handleReceivingRateLimitStatus(this.twitter.getRateLimitStatus());
|
||||
}
|
||||
|
||||
int secondsUntilWeCanPullAgain = secondsUntilReset / remainingHits;
|
||||
long msUntilWeCanPullAgain = secondsUntilWeCanPullAgain * 1000;
|
||||
protected boolean hasMarkedStatus() {
|
||||
return markerId > -1;
|
||||
}
|
||||
|
||||
logger.debug("need to Thread.sleep() " + secondsUntilWeCanPullAgain +
|
||||
" seconds until the next timeline pull. Have " + remainingHits +
|
||||
" remaining pull this rate period. The period ends in " + secondsUntilReset);
|
||||
@Override
|
||||
protected void onInit() throws Exception {
|
||||
messagingTemplate.afterPropertiesSet();
|
||||
Assert.notNull(this.configuration, "'configuration' can't be null");
|
||||
this.twitter = this.configuration.getTwitter();
|
||||
Assert.notNull(this.twitter, "'twitter' instance can't be null");
|
||||
}
|
||||
|
||||
Thread.sleep(msUntilWeCanPullAgain);
|
||||
} catch (Throwable throwable) {
|
||||
logger.debug("encountered an error when" + " trying to refresh the timeline: " + ExceptionUtils.getFullStackTrace(throwable));
|
||||
}
|
||||
@Override
|
||||
protected void doStop() {
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
public void setShouldTrack(boolean shouldTrack) {
|
||||
this.historyWritingPostProcessor.setShouldTrack(shouldTrack);
|
||||
}
|
||||
|
||||
protected boolean waitUntilPullAvailable() throws Exception {
|
||||
return this.handleReceivingRateLimitStatus(this.twitter.getRateLimitStatus());
|
||||
}
|
||||
|
||||
protected boolean hasMarkedStatus() {
|
||||
return markerId > -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() throws Exception {
|
||||
messagingTemplate.afterPropertiesSet();
|
||||
Assert.notNull(this.configuration, "'configuration' can't be null");
|
||||
this.twitter = this.configuration.getTwitter();
|
||||
Assert.notNull(this.twitter, "'twitter' instance can't be null");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for clients to run logic when the API rate limiting lets us
|
||||
* <p/>
|
||||
* Simply register your callback using #runAsAPIRateLimitsPermit
|
||||
*
|
||||
* @param <C>
|
||||
*/
|
||||
public static interface ApiCallback<C> {
|
||||
void run(C t, Twitter twitter) throws Exception;
|
||||
}
|
||||
|
||||
public void setShouldTrack(boolean shouldTrack) {
|
||||
this.historyWritingPostProcessor.setShouldTrack(shouldTrack);
|
||||
}
|
||||
/**
|
||||
* Hook for clients to run logic when the API rate limiting lets us
|
||||
* <p/>
|
||||
* Simply register your callback using #runAsAPIRateLimitsPermit
|
||||
*
|
||||
* @param <C>
|
||||
*/
|
||||
public static interface ApiCallback<C> {
|
||||
void run(C t, Twitter twitter) throws Exception;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import java.util.List;
|
||||
*/
|
||||
abstract public class AbstractInboundTwitterStatusEndpointSupport
|
||||
extends AbstractInboundTwitterEndpointSupport<Status> {
|
||||
|
||||
private Comparator<Status> statusComparator = new Comparator<Status>() {
|
||||
public int compare(Status status, Status status1) {
|
||||
return status.getCreatedAt().compareTo(status1.getCreatedAt());
|
||||
|
||||
@@ -35,6 +35,7 @@ import java.util.List;
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class InboundDirectMessageStatusEndpoint extends AbstractInboundTwitterEndpointSupport<DirectMessage> {
|
||||
|
||||
private Comparator<DirectMessage> dmComparator = new Comparator<DirectMessage>() {
|
||||
public int compare(DirectMessage directMessage, DirectMessage directMessage1) {
|
||||
return directMessage.getCreatedAt().compareTo(directMessage1.getCreatedAt());
|
||||
|
||||
@@ -39,14 +39,12 @@
|
||||
base-package="org.springframework.integration.twitter"/>
|
||||
|
||||
<context:property-placeholder
|
||||
location="classpath:twitter.properties"
|
||||
location="file://${user.home}/Desktop/twitter.properties"
|
||||
ignore-unresolvable="true"/>
|
||||
|
||||
<channel id="inbound_dm"/>
|
||||
|
||||
<twitter:inbound-dm-channel-adapter id="twitterInbound"
|
||||
channel="inbound_dm"
|
||||
twitter-connection="tc"/>
|
||||
<channel id="inbound_mentions"/>
|
||||
<channel id="inbound_updates"/>
|
||||
|
||||
<twitter:twitter-connection id="tc"
|
||||
access-token="${twitter.oauth.accessToken}"
|
||||
@@ -54,10 +52,16 @@
|
||||
consumer-key="${twitter.oauth.consumerKey}"
|
||||
consumer-secret="${twitter.oauth.consumerSecret}"/>
|
||||
|
||||
<service-activator id="twitterService"
|
||||
input-channel="inbound_dm"
|
||||
ref="twitterAnnouncer"
|
||||
method="dm"/>
|
||||
<twitter:inbound-update-channel-adapter twitter-connection="tc" channel="inbound_updates"/>
|
||||
<service-activator input-channel="inbound_updates" ref="twitterAnnouncer" method="updates"/>
|
||||
|
||||
<twitter:inbound-mention-channel-adapter twitter-connection="tc" channel="inbound_mentions"/>
|
||||
<service-activator input-channel="inbound_mentions" ref="twitterAnnouncer" method="mention"/>
|
||||
|
||||
<twitter:inbound-dm-channel-adapter twitter-connection="tc" channel="inbound_dm" />
|
||||
<service-activator input-channel="inbound_dm" ref="twitterAnnouncer" method="dm"/>
|
||||
|
||||
|
||||
|
||||
|
||||
</beans:beans>
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.springframework.integration.twitter;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
@@ -23,15 +22,17 @@ import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
|
||||
|
||||
|
||||
/**
|
||||
* provides a simple example of a command-line client to receive DMs, messages, timeline updates.
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
@ContextConfiguration
|
||||
public class TestRecievingUsingNamespace extends AbstractJUnit4SpringContextTests {
|
||||
@Autowired
|
||||
private TwitterAnnouncer twitterAnnouncer;
|
||||
|
||||
@Autowired private TwitterAnnouncer twitterAnnouncer;
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
//@Ignore
|
||||
public void testIt() throws Throwable {
|
||||
long ctr = 0;
|
||||
long s = 1000;
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.springframework.integration.Message;
|
||||
import org.springframework.integration.MessageChannel;
|
||||
import org.springframework.integration.core.MessagingTemplate;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.twitter.model.Twitter4jGeoLocationImpl;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
|
||||
|
||||
@@ -32,7 +33,7 @@ import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
|
||||
@ContextConfiguration
|
||||
public class TestSendingUpdatesUsingNamespace extends AbstractJUnit4SpringContextTests {
|
||||
|
||||
private StatusUpdateSupport statusUpdateSupport = new StatusUpdateSupport();
|
||||
|
||||
|
||||
private MessagingTemplate messagingTemplate = new MessagingTemplate();
|
||||
|
||||
@@ -44,7 +45,7 @@ public class TestSendingUpdatesUsingNamespace extends AbstractJUnit4SpringContex
|
||||
MessageBuilder<String> mb = MessageBuilder.withPayload("test message 1")
|
||||
.setHeader(TwitterHeaders.TWITTER_IN_REPLY_TO_STATUS_ID, 21927437001L)
|
||||
.setHeader(TwitterHeaders.TWITTER_GEOLOCATION,
|
||||
this.statusUpdateSupport.fromLatitudeLongitudePair(-76.226823, 23.642465)) // antarctica
|
||||
new Twitter4jGeoLocationImpl(-76.226823, 23.642465)) // antarctica
|
||||
.setHeader(TwitterHeaders.TWITTER_DISPLAY_COORDINATES, true);
|
||||
Message<String> m = mb.build();
|
||||
this.messagingTemplate.send(this.channel, m);
|
||||
|
||||
@@ -4,12 +4,6 @@ import org.springframework.integration.twitter.model.DirectMessage;
|
||||
import org.springframework.integration.twitter.model.Status;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
import twitter4j.DirectMessage;
|
||||
import twitter4j.Status;
|
||||
>>>>>>> 64fec64d4095a6e793739607816d63d600e1ed0c
|
||||
|
||||
|
||||
@Component
|
||||
public class TwitterAnnouncer {
|
||||
@@ -22,7 +16,7 @@ public class TwitterAnnouncer {
|
||||
System.out.println("A tweet mentioning (or replying) to " + "you was received having text " + s.getText() + " from " + s.getSource());
|
||||
}
|
||||
|
||||
public void friendsTimelineUpdated(Status t) {
|
||||
System.out.println("Received " + t.getText() + " from " + t.getSource());
|
||||
public void updates(Status t) {
|
||||
System.out.println("Received timeline update: " + t.getText() + " from " + t.getSource());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user