updating tests.

This commit is contained in:
Josh Long
2010-10-23 13:18:01 -07:00
parent 21b7058959
commit 13966b4f75
7 changed files with 164 additions and 164 deletions

View File

@@ -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;
}
}

View File

@@ -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());

View File

@@ -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());

View File

@@ -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>

View File

@@ -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;

View File

@@ -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);

View File

@@ -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());
}
}