initial commit of the finished twitter support.

This commit is contained in:
Josh Long
2010-08-24 10:26:47 +00:00
parent d6176a7f1b
commit 126fa35e2b
34 changed files with 2391 additions and 0 deletions

View File

@@ -30,6 +30,7 @@
<module>spring-integration-xmpp</module>
<module>spring-integration-ftp</module>
<module>spring-integration-sftp</module>
<module>spring-integration-twitter</module>
</modules>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

View File

@@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-parent</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
</parent>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-twitter</artifactId>
<packaging>jar</packaging>
<name>Spring Integration Twitter Support</name>
<dependencies>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.twitter4j</groupId>
<artifactId>twitter4j-core</artifactId>
<!--<version>[2.1,)</version>-->
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>nlog4j</artifactId>
<version>1.2.25</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>${cglib.version}</version>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>${org.easymock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymockclassextension</artifactId>
<version>${org.easymock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${org.springframework.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-stream</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-core</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.4</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.springsource.bundlor</groupId>
<artifactId>com.springsource.bundlor.maven</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,183 @@
/*
* Copyright 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;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.springframework.context.Lifecycle;
import org.springframework.integration.Message;
import org.springframework.integration.core.MessageBuilder;
import org.springframework.integration.core.MessageChannel;
import org.springframework.integration.core.MessagingTemplate;
import org.springframework.integration.endpoint.AbstractEndpoint;
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 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 org.springframework.integration.twitter.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.
*
* 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.MetadataPersister} implementation
*
* @author Josh Long
* @since 2.0
*/
public abstract class AbstractInboundTwitterEndpointSupport<T> extends AbstractEndpoint implements Lifecycle {
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();
@SuppressWarnings("unused")
public void setConfiguration(OAuthConfiguration configuration) {
this.configuration = configuration;
}
abstract protected void markLastStatusId(T statusId);
abstract protected List<T> sort(List<T> rl);
protected void forwardAll(ResponseList<T> tResponses) {
List<T> stats = new ArrayList<T>();
for (T t : tResponses)
stats.add(t);
for (T twitterResponse : sort(stats))
forward(twitterResponse);
}
public long getMarkerId() {
return markerId;
}
@Override
protected void doStart() {
try {
refresh();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected void forward(T status) {
synchronized (this.markerGuard) {
Message<T> twtMsg = MessageBuilder.withPayload(status).build();
messagingTemplate.send(requestChannel, twtMsg);
markLastStatusId(status);
}
}
@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");
}
cb.run(this, twitter);
}
}
}
protected boolean handleReceivingRateLimitStatus(RateLimitStatus rateLimitStatus) {
try {
int secondsUntilReset = rateLimitStatus.getSecondsUntilReset();
int remainingHits = rateLimitStatus.getRemainingHits();
if (remainingHits == 0) {
logger.debug("rate status limit service returned 0 for the remaining hits value");
return false;
}
if (secondsUntilReset == 0) {
logger.debug("rate status limit service returned 0 for the seconds until reset period value");
return false;
}
int secondsUntilWeCanPullAgain = secondsUntilReset / remainingHits;
long msUntilWeCanPullAgain = secondsUntilWeCanPullAgain * 1000;
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);
Thread.sleep(msUntilWeCanPullAgain);
} catch (Throwable throwable) {
logger.debug("encountered an error when" + " trying to refresh the timeline: " + ExceptionUtils.getFullStackTrace(throwable));
}
return true;
}
protected boolean waitUntilPullAvailable() throws Exception {
return this.handleReceivingRateLimitStatus(this.twitter.getRateLimitStatus());
}
protected boolean hasMarkedStatus() {
return markerId > -1;
}
abstract protected void refresh() throws Exception;
@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() {
}
@SuppressWarnings("unused")
public void setRequestChannel(MessageChannel requestChannel) {
this.messagingTemplate.setDefaultChannel(requestChannel);
this.requestChannel = requestChannel;
}
/**
* Hook for clients to run logic when the API rate limiting lets us
*
* Simply register your callback using #runAsAPIRateLimitsPermit
*
* @param <C>
*/
public static interface ApiCallback<C> {
void run(C t, Twitter twitter) throws Exception;
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 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;
import twitter4j.Status;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* Simple base class for the reply and timeline cases (as well as any other {@link twitter4j.Status} implementations of
* {@link twitter4j.TwitterResponse}.
*
* @author Josh Long
*/
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());
}
};
@Override
protected void markLastStatusId(Status statusId) {
this.markerId = statusId.getId();
}
@Override
protected List<Status> sort(List<Status> rl) {
List<Status> statusArrayList = new ArrayList<Status>();
statusArrayList.addAll(rl);
Collections.sort(statusArrayList, statusComparator);
return statusArrayList;
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright 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;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.endpoint.AbstractEndpoint;
import org.springframework.integration.twitter.oauth.OAuthConfiguration;
import org.springframework.util.Assert;
import twitter4j.Twitter;
/**
* The adapters that support 'sending' / 'updating status' messages will do so on top of this implementation for convenience, only.
*
* @author Josh Long
*/
public abstract class AbstractOutboundTwitterEndpointSupport extends AbstractEndpoint implements MessageHandler {
protected volatile OAuthConfiguration configuration;
protected volatile Twitter twitter;
protected volatile StatusUpdateSupport statusUpdateSupport = new StatusUpdateSupport();
@SuppressWarnings("unused")
public void setConfiguration(OAuthConfiguration configuration) {
this.configuration = configuration;
}
@Override
protected void onInit() throws Exception {
Assert.notNull(this.configuration, "'configuration' can't be null");
this.twitter = this.configuration.getTwitter();
Assert.notNull(this.twitter, "'twitter' can't be null");
}
@Override
protected void doStart() {
}
@Override
protected void doStop() {
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 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;
import twitter4j.DirectMessage;
import twitter4j.Paging;
import twitter4j.Twitter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* This class handles support for receiving DMs (direct messages) using Twitter.
*
* @author Josh Long
*/
public class InboundDMStatusEndpoint extends AbstractInboundTwitterEndpointSupport<DirectMessage> {
private Comparator<DirectMessage> dmComparator = new Comparator<DirectMessage>() {
public int compare(DirectMessage directMessage, DirectMessage directMessage1) {
return directMessage.getCreatedAt().compareTo(directMessage1.getCreatedAt());
}
};
@Override
protected void markLastStatusId(DirectMessage dm) {
this.markerId = dm.getId();
}
@Override
protected List<DirectMessage> sort(List<DirectMessage> rl) {
List<DirectMessage> dms = new ArrayList<DirectMessage>();
dms.addAll(rl);
Collections.sort(dms, dmComparator);
return dms;
}
@Override
protected void refresh() throws Exception {
this.runAsAPIRateLimitsPermit(new ApiCallback<InboundDMStatusEndpoint>() {
public void run(InboundDMStatusEndpoint t, Twitter twitter)
throws Exception {
forwardAll((!hasMarkedStatus()) ? t.twitter.getDirectMessages() : t.twitter.getDirectMessages(new Paging(t.getMarkerId())));
}
});
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 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;
import twitter4j.Paging;
import twitter4j.Twitter;
/**
* Handles forwarding all new {@link twitter4j.Status} that are 'replies' or 'mentions' to some other tweet.
*
* @author Josh Long
*/
public class InboundMentionStatusEndpoint extends AbstractInboundTwitterStatusEndpointSupport {
@Override
protected void refresh() throws Exception {
this.runAsAPIRateLimitsPermit(new ApiCallback<InboundMentionStatusEndpoint>() {
public void run(InboundMentionStatusEndpoint ctx, Twitter twitter)
throws Exception {
forwardAll((!hasMarkedStatus()) ? twitter.getMentions() : twitter.getMentions(new Paging(ctx.getMarkerId())));
}
});
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 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;
import twitter4j.Paging;
import twitter4j.Twitter;
/**
* This {@link org.springframework.integration.core.MessageSource} lets Spring Integration consume a given account's timeline
* as messages. It has support for dynamic throttling of API requests.
*
* @author Josh Long
* @since 2.0
*/
public class InboundUpdatedStatusEndpoint extends AbstractInboundTwitterStatusEndpointSupport {
@Override
protected void refresh() throws Exception {
this.runAsAPIRateLimitsPermit(new ApiCallback<InboundUpdatedStatusEndpoint>() {
public void run(InboundUpdatedStatusEndpoint t, Twitter twitter)
throws Exception {
forwardAll((!t.hasMarkedStatus()) ? twitter.getFriendsTimeline() : twitter.getFriendsTimeline(new Paging(t.getMarkerId())));
}
});
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright 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;
import org.springframework.integration.Message;
import org.springframework.integration.MessageDeliveryException;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.MessageRejectedException;
import org.springframework.util.Assert;
import twitter4j.TwitterException;
/**
* Simple adapter to support sending outbound direct messages ("DM"s) using twitter
*
* @author Josh Long
* @see org.springframework.integration.twitter.TwitterHeaders
* @see twitter4j.Twitter
*/
public class OutboundDMStatusMessageHandler extends AbstractOutboundTwitterEndpointSupport {
public void handleMessage(Message<?> message) throws MessageRejectedException, MessageHandlingException, MessageDeliveryException {
try {
String txt = (String) message.getPayload();
Object toUser =
message.getHeaders().containsKey(TwitterHeaders.TWITTER_DM_TARGET_USER_ID)?
message.getHeaders().get(TwitterHeaders.TWITTER_DM_TARGET_USER_ID) :
null;
Assert.notNull(txt, "the message payload must be a String to be used as the direct message body text");
Assert.notNull(toUser, "the header '" + TwitterHeaders.TWITTER_DM_TARGET_USER_ID + "' must be present");
Assert.state(toUser instanceof String || toUser instanceof Integer,
"the header '" + TwitterHeaders.TWITTER_DM_TARGET_USER_ID + "' must be either a String (a screenname) or an int (a user ID)");
if (toUser instanceof Integer) {
this.twitter.sendDirectMessage((Integer) toUser, txt);
} else if (toUser instanceof String) {
this.twitter.sendDirectMessage((String) toUser, txt);
}
} catch (TwitterException e) {
logger.debug(e);
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 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;
import org.springframework.integration.Message;
import org.springframework.integration.MessageDeliveryException;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.MessageRejectedException;
import org.springframework.util.Assert;
import twitter4j.StatusUpdate;
/**
* This class is useful for both sending regular status updates as well as 'replies' or 'mentions'
*
* @author Josh Long
* @since 2.0
*/
public class OutboundUpdatedStatusMessageHandler extends AbstractOutboundTwitterEndpointSupport {
public void handleMessage(Message<?> message) throws MessageRejectedException, MessageHandlingException, MessageDeliveryException {
try {
StatusUpdate statusUpdate = this.statusUpdateSupport.fromMessage(message);
Assert.notNull(statusUpdate, "couldn't send message, unable to build a StatusUpdate instance correctly");
this.twitter.updateStatus(statusUpdate);
} catch (Throwable e) {
this.logger.debug(e);
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright 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;
import org.springframework.integration.Message;
import org.springframework.util.StringUtils;
import twitter4j.GeoLocation;
import twitter4j.StatusUpdate;
/**
* Convenience class that can take a seemingly disparate jumble of headers and a payload and do a best-faith attempt at vending a {@link twitter4j.StatusUpdate} instance
*
* @author Josh Long
* @see twitter4j.StatusUpdate
* @see org.springframework.integration.twitter.TwitterHeaders
* @since 2.0
*/
public class StatusUpdateSupport {
/**
* {@link StatusUpdate} instances are used to drive status updates.
*
* @param message the inbound messages
* @return a {@link StatusUpdate} that's been materialized from the inbound message
* @throws Throwable thrown if something goes wrong
*/
public StatusUpdate fromMessage(Message<?> message)
throws Throwable {
Object payload = message.getPayload();
StatusUpdate statusUpdate = null;
if (payload instanceof String) {
statusUpdate = new StatusUpdate((String) payload);
if (message.getHeaders().containsKey(TwitterHeaders.TWITTER_IN_REPLY_TO_STATUS_ID)) {
Long replyId = (Long) message.getHeaders().get(TwitterHeaders.TWITTER_IN_REPLY_TO_STATUS_ID);
if ((replyId != null) && (replyId > 0)) {
statusUpdate.inReplyToStatusId(replyId);
}
}
if (message.getHeaders().containsKey(TwitterHeaders.TWITTER_PLACE_ID)) {
String placeId = (String) message.getHeaders().get(TwitterHeaders.TWITTER_PLACE_ID);
if (StringUtils.hasText(placeId)) {
statusUpdate.placeId(placeId);
}
}
if (message.getHeaders().containsKey(TwitterHeaders.TWITTER_GEOLOCATION)) {
GeoLocation geoLocation = (GeoLocation) message.getHeaders().get(TwitterHeaders.TWITTER_GEOLOCATION);
if (null != geoLocation) {
statusUpdate.location(geoLocation);
}
}
if (message.getHeaders().containsKey(TwitterHeaders.TWITTER_DISPLAY_COORDINATES)) {
Boolean displayCoords = (Boolean) message.getHeaders().get(TwitterHeaders.TWITTER_DISPLAY_COORDINATES);
if (displayCoords != null) {
statusUpdate.displayCoordinates(displayCoords);
}
}
}
if (payload instanceof StatusUpdate) {
statusUpdate = (StatusUpdate) payload;
}
return statusUpdate;
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 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;
/**
* An enum to allow users to express interest in particular kinds of tweets.
*
* Contains header keys used by the various adapters.
*
* @author Josh Long
* @since 2.0
*/
public class TwitterHeaders {
public static final String TWITTER_IN_REPLY_TO_STATUS_ID = "TWITTER_IN_REPLY_TO_STATUS_ID";
public static final String TWITTER_PLACE_ID = "TWITTER_PLACE_ID";
public static final String TWITTER_GEOLOCATION = "TWITTER_GEOLOCATION";
public static final String TWITTER_DISPLAY_COORDINATES = "TWITTER_DISPLAY_COORDINATES";
public static final String TWITTER_DM_TARGET_USER_ID = "TWITTER_DM_TARGET_USER_ID";
}

View File

@@ -0,0 +1,163 @@
/*
* Copyright 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.config;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.integration.twitter.*;
import org.springframework.integration.twitter.oauth.OAuthConfigurationFactoryBean;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* @author Josh Long
* @since 2.0
*/
@SuppressWarnings("unused")
public class TwitterNamespaceHandler extends org.springframework.beans.factory.xml.NamespaceHandlerSupport {
public static final String DIRECT_MESSAGES = "direct-messages";
public static final String MENTIONS = "mentions";
public static final String FRIENDS = "friends";
public void init() {
// twitter connections
registerBeanDefinitionParser("twitter-connection", new TwitterConnectionParser());
// inbound
registerBeanDefinitionParser("inbound-update-channel-adapter", new TwitterUpdatedStatusInboundEndpointParser());
registerBeanDefinitionParser("inbound-dm-channel-adapter", new TwitterDMInboundEndpointParser());
registerBeanDefinitionParser("inbound-mention-channel-adapter", new TwitterMentionInboundEndpointParser());
// outbound
registerBeanDefinitionParser("outbound-update-channel-adapter", new TwitterUpdatedStatusOutboundEndpointParser());
registerBeanDefinitionParser("outbound-dm-channel-adapter", new TwitterDMOutboundEndpointParser());
}
private static void configureTwitterConnection(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
String ref = element.getAttribute("twitter-connection");
if (org.springframework.util.StringUtils.hasText(ref)) {
builder.addPropertyReference("twitterConnection", ref);
} else {
for (String attribute : new String[] { "consumer-key", "consumer-secret", "access-token", "access-token-secret" }) {
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, attribute);
}
}
}
// twitter:twitter-connection
private static class TwitterConnectionParser extends AbstractSingleBeanDefinitionParser {
@Override
protected Class getBeanClass(Element element) {
return OAuthConfigurationFactoryBean.class;
}
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
configureTwitterConnection(element, parserContext, builder);
}
@Override
protected boolean shouldGenerateIdAsFallback() {
return true;
}
}
// twitter:inbound-mention-channel-adapter
private static class TwitterMentionInboundEndpointParser extends AbstractSingleBeanDefinitionParser {
@Override
protected String getBeanClassName(Element element) {
return InboundMentionStatusEndpoint.class.getName();
}
@Override
protected boolean shouldGenerateIdAsFallback() {
return true;
}
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "channel", "requestChannel");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "twitter-connection", "configuration");
}
}
// twitter:inbound-dm-channel-adapter
private static class TwitterDMInboundEndpointParser extends AbstractSingleBeanDefinitionParser {
@Override
protected String getBeanClassName(Element element) {
return InboundDMStatusEndpoint.class.getName();
}
@Override
protected boolean shouldGenerateIdAsFallback() {
return true;
}
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "channel", "requestChannel");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "twitter-connection", "configuration");
}
}
// twitter:inbound-update-channel-adapter
private static class TwitterUpdatedStatusInboundEndpointParser extends AbstractSingleBeanDefinitionParser {
@Override
protected String getBeanClassName(Element element) {
return InboundUpdatedStatusEndpoint.class.getName();
}
@Override
protected boolean shouldGenerateIdAsFallback() {
return true;
}
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "channel", "requestChannel");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "twitter-connection", "configuration");
}
}
// twitter:outbound-update-channel-adapter
private static class TwitterUpdatedStatusOutboundEndpointParser extends AbstractOutboundChannelAdapterParser {
@Override
protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(OutboundUpdatedStatusMessageHandler.class.getName());
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "twitter-connection", "configuration");
return builder.getBeanDefinition();
}
}
// twitter:outbound-dm-channel-adapter
private static class TwitterDMOutboundEndpointParser extends AbstractOutboundChannelAdapterParser {
@Override
protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(OutboundDMStatusMessageHandler.class.getName());
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "twitter-connection", "configuration");
return builder.getBeanDefinition();
}
}
}

View File

@@ -0,0 +1,213 @@
/*
* Copyright 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.oauth;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import twitter4j.http.AccessToken;
import twitter4j.http.RequestToken;
import java.util.Properties;
/**
* base-class for {@link org.springframework.integration.twitter.oauth.OAuthAccessTokenBasedTwitterFactoryBean}.
* <p/>
* Provides hooks so that subclasses able to reference a concrete implementation of the generic type parameter can call methods for us. Most hooks are to handle the case
* of the first time subscription, where no accessToken is present.
*
* @author Josh Long
* @param <T>
* @see org.springframework.integration.twitter.oauth.OAuthAccessTokenBasedTwitterFactoryBean
* @since 2.0
*/
abstract public class AbstractOAuthAccessTokenBasedFactoryBean<T> implements InitializingBean, FactoryBean<T> {
protected OAuthConfiguration configuration;
protected final Object monitor = new Object();
protected volatile T twitter;
protected volatile AccessTokenInitialRequestProcessListener accessTokenInitialRequestProcessListener;
protected volatile boolean initialized = false;
/**
* Nasty little bit of circular indirection here: the {@link org.springframework.integration.twitter.oauth.OAuthConfiguration} hosts the String values for authentication,
* which we need to build up this instance, but the {@link org.springframework.integration.twitter.oauth.OAuthConfiguration} in turn needs references to the instances provided by
* this {@link org.springframework.beans.factory.FactoryBean}. So, they collaborate and guard each others state. Ultimately, clients should use {@link org.springframework.integration.twitter.oauth.OAuthConfiguration}
* to correctly any implementations of this factory bean as well as the {@link org.springframework.integration.twitter.oauth.OAuthConfiguration} reference itself.
*
* @param configuration the configuration object
*/
protected AbstractOAuthAccessTokenBasedFactoryBean(OAuthConfiguration configuration) {
this.configuration = configuration;
}
/**
* This probably doesn't belong here. It's more for support for running the {@link OAuthAccessTokenBasedTwitterFactoryBean#main(String[])} or
* {@link OAuthAccessTokenBasedTwitterFactoryBean#main(String[])} methods that run the user through a command line tool to approve a user for the first
* time if the user hasn't obtained her {@code accessToken } yet
*
* @param resource the resource where properties file lives
* @return returns a fully configured {@link java.util.Properties} instance
* @throws Exception thrown if anythign goes wrong
*/
@SuppressWarnings("unused")
protected static Properties fromResource(Resource resource)
throws Exception {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(resource);
propertiesFactoryBean.setSingleton(true);
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}
/**
* provides lifecycle for initiation of the reference. By the time this method is left we should have a fully configured twitter connection that can connect and make calls
* @throws Exception
*/
public void afterPropertiesSet() throws Exception {
synchronized (this.monitor) {
if (this.accessTokenInitialRequestProcessListener == null) {
accessTokenInitialRequestProcessListener = new ConsoleBasedAccessTokenInitialRequestProcessListener();
}
Assert.notNull(this.configuration.getConsumerKey(), "'consumerKey' mustn't be null");
Assert.notNull(this.configuration.getConsumerSecret(), "'consumerSecret' mustn't be null");
AccessToken accessTokenObj;
if (StringUtils.hasText(this.configuration.getAccessToken()) && StringUtils.hasText(this.configuration.getAccessTokenSecret())) {
accessTokenObj = new AccessToken(this.configuration.getAccessToken(), this.configuration.getAccessTokenSecret());
} else {
accessTokenObj = initialAuthorizationWizard();
}
establishTwitterObject(accessTokenObj);
Assert.notNull(accessTokenObj, "'accessTokenObj' can't be null");
this.initialized = true;
}
}
@SuppressWarnings("unused")
public void setAccessTokenInitialRequestProcessListener(AccessTokenInitialRequestProcessListener accessTokenInitialRequestProcessListener) {
this.accessTokenInitialRequestProcessListener = accessTokenInitialRequestProcessListener;
}
public abstract void establishTwitterObject(AccessToken accessToken)
throws Exception;
/**
* because we are not able to dereference the {@link twitter4j.Twitter} or {@link twitter4j.AsyncTwitter} instances, we need to ask subclasses to call
* us how to call {@link twitter4j.AsyncTwitter#getOAuthRequestToken()} or {@link twitter4j.Twitter#getOAuthRequestToken()} for us.This method
* will never be evaluated as long as the {@link org.springframework.integration.twitter.oauth.OAuthConfiguration#accessToken}
* and {@link org.springframework.integration.twitter.oauth.OAuthConfiguration#accessTokenSecret} beans are not null.
*
* @return the {@link twitter4j.http.RequestToken} as vended by the service. Ths will contain a verification URl required to obtain an access key and secret.
* @throws Exception thrown if anything should go wrong
*/
public abstract RequestToken getOAuthRequestToken()
throws Exception;
/**
* Only used if the impementation is trying to get an {@link twitter4j.http.AccessToken} for the first time. This method
* will never be evaluated as long as the {@link org.springframework.integration.twitter.oauth.OAuthConfiguration#accessToken}
* and {@link org.springframework.integration.twitter.oauth.OAuthConfiguration#accessTokenSecret} beans are not null.
*
* @param token the initiating {@link twitter4j.http.RequestToken}
* @param pin the string returned from the verification URL
* @return returns the {@link twitter4j.http.AccessToken} fetched from the twitter service.
* @throws Exception thrown if anything should go wrong
*/
public abstract AccessToken getOAuthAccessToken(RequestToken token, String pin)
throws Exception;
/**
* Only used if the impementation is trying to get an {@link twitter4j.http.AccessToken} for the first time. This method
* will never be evaluated as long as the {@link org.springframework.integration.twitter.oauth.OAuthConfiguration#accessToken}
* and {@link org.springframework.integration.twitter.oauth.OAuthConfiguration#accessTokenSecret} beans are not null.
*
* @return returns the {@link twitter4j.http.AccessToken} fetched from the twitter service.
* @throws Exception thrown if anything should go wrong
*/
public abstract AccessToken getOAuthAccessToken() throws Exception;
/**
* @return returns the freshly created {@link twitter4j.http.AccessToken} object from the service
* @throws Exception for just about any deviation from the expected
*/
private AccessToken initialAuthorizationWizard() throws Exception {
Assert.notNull(this.accessTokenInitialRequestProcessListener, "'accessTokenInitialRequestProcessListener' can't be null");
try {
RequestToken requestToken = getOAuthRequestToken();
String pin = this.accessTokenInitialRequestProcessListener.openUrlAndReturnPin(requestToken.getAuthorizationURL());
AccessToken at = StringUtils.hasText(pin) ? getOAuthAccessToken(requestToken, pin) : getOAuthAccessToken();
this.accessTokenInitialRequestProcessListener.persistReturnedAccessToken(at);
return at;
} catch (Throwable th) {
this.accessTokenInitialRequestProcessListener.failure(th);
}
return null;
}
/**
* Responsibility of subclasses to call this because we cant dereference the generic type appropriately. The responsibility is
* to call {@link twitter4j.Twitter#verifyCredentials()} or {@link twitter4j.AsyncTwitter#verifyCredentials()} as appropriate
*
* @throws Exception if there's an inability to authenticate
*/
public abstract void verifyCredentials() throws Exception;
/**
* Rubber meets the road: builds up a reference to the twitter4j.(Async)Twitter instance
*
* @return the instance
* @throws Exception thrown in case some condition isn't met correctly in construction
*/
public T getObject() throws Exception {
if (!initialized) {
afterPropertiesSet();
}
return this.twitter;
}
/**
* this method is delegated to implementations because we can't correctly dereference the generic type's class
*
* @return a class
*/
abstract public Class<?> getObjectType();
/**
* Standard {@link org.springframework.beans.factory.FactoryBean} method. Implementations may override if there's a specific method
*
* @return whether or not this is a singleton
*/
public boolean isSingleton() {
return true;
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 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.oauth;
import twitter4j.http.AccessToken;
/**
* if the factory bean isn't provided a 'accesstoken' and 'accesstokensecret', then it will try to obtain those bits of informatio on the users behalf.
* <p/>
* In doing so it will need input fro the user (automatic or human intervention is required)
*
* @author Josh Long
*/
public interface AccessTokenInitialRequestProcessListener {
String openUrlAndReturnPin(String urlToOpen) throws Exception;
void persistReturnedAccessToken(AccessToken accessToken)
throws Exception;
/**
* Callback in case something goes wrong at any point in the process. Up to client to intervene
* @param t the exception thrown (if any). Could be null if none available
*/
void failure(Throwable t);
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright 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.oauth;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.SystemUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import twitter4j.http.AccessToken;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* Default System.(out|in) based implementation of the {@link org.springframework.integration.twitter.oauth.AccessTokenInitialRequestProcessListener} interface
*
* @author Josh Long
*/
public class ConsoleBasedAccessTokenInitialRequestProcessListener implements AccessTokenInitialRequestProcessListener {
public String openUrlAndReturnPin(String urlToOpen)
throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("Open the following URL and grant access to your account:");
System.out.println(urlToOpen);
System.out.print("Enter the PIN(if aviailable) or just hit enter.[PIN]:");
return StringUtils.trim(br.readLine());
}
public void persistReturnedAccessToken(AccessToken accessToken)
throws Exception {
Map<String, String> output = new HashMap<String, String>();
output.put(OAuthConfigurationFactoryBean.WELL_KNOWN_CONSUMER_ACCESS_TOKEN, accessToken.getToken());
output.put(OAuthConfigurationFactoryBean.WELL_KNOWN_CONSUMER_ACCESS_TOKEN_SECRET, accessToken.getTokenSecret());
File accessTokenCreds = new File(SystemUtils.getJavaIoTmpDir(), "twitter-accesstoken.properties");
FileWriter writer = new FileWriter(accessTokenCreds);
Properties props = new Properties();
props.putAll(output);
props.store(writer, "oauth-access-token");
IOUtils.closeQuietly(writer);
System.out.println("The oauth accesstoken credentials have been written to " + accessTokenCreds.getAbsolutePath());
}
public void failure(Throwable t) {
System.err.println("Exception occurred when trying to retrieve credentials: " + ExceptionUtils.getFullStackTrace(t));
}
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright 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.oauth;
import org.apache.commons.lang.SystemUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.springframework.core.io.FileSystemResource;
import twitter4j.ResponseList;
import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterFactory;
import twitter4j.http.AccessToken;
import twitter4j.http.RequestToken;
import java.io.File;
import java.util.Properties;
public class OAuthAccessTokenBasedTwitterFactoryBean extends AbstractOAuthAccessTokenBasedFactoryBean<Twitter> {
protected OAuthAccessTokenBasedTwitterFactoryBean(OAuthConfiguration configuration) {
super(configuration);
}
@Override
public void establishTwitterObject(AccessToken accessToken)
throws Exception {
this.twitter = new TwitterFactory().getOAuthAuthorizedInstance(this.configuration.getConsumerKey(), this.configuration.getConsumerSecret(), accessToken);
}
@Override
public RequestToken getOAuthRequestToken() throws Exception {
return twitter.getOAuthRequestToken();
}
@Override
public void verifyCredentials() throws Exception {
this.twitter.verifyCredentials();
}
@Override
public AccessToken getOAuthAccessToken(RequestToken token, String pin)
throws Exception {
return this.twitter.getOAuthAccessToken(token, pin);
}
@Override
public AccessToken getOAuthAccessToken() throws Exception {
return this.twitter.getOAuthAccessToken();
}
@Override
public Class<?> getObjectType() {
return Twitter.class;
}
public static void main(String[] args) throws Throwable {
File propsFile = new File(new File(SystemUtils.getUserHome(), "Desktop"), "twitter.properties");
FileSystemResource fileSystemResource = new FileSystemResource(propsFile);
Properties properties = fromResource(fileSystemResource);
OAuthConfigurationFactoryBean oAuthConfigurationFactoryBean = new OAuthConfigurationFactoryBean();
oAuthConfigurationFactoryBean.bootstrapFromProperties(properties);
OAuthConfiguration configuration = oAuthConfigurationFactoryBean.getObject();
Twitter twitter = configuration.getTwitter();
System.out.println("Used the " + OAuthAccessTokenBasedTwitterFactoryBean.class.getName() + " and arrived at " + ToStringBuilder.reflectionToString(twitter));
ResponseList<Status> friendsTimeline = twitter.getFriendsTimeline();
System.out.println("Friends' timeline " + friendsTimeline);
Thread.sleep(10000);
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright 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.oauth;
import twitter4j.Twitter;
/**
* Simple bean we can use to store the configuration and store shared references to both an {@link twitter4j.AsyncTwitter}
* and an {@link twitter4j.Twitter} instance.
* <p/>
* client should store this bean and simply lookup the Twitter configuration from there
*/
public class OAuthConfiguration {
//
// private AsyncTwitter asyncTwitter;
private Twitter twitter;
private volatile String consumerKey;
private volatile String consumerSecret;
private volatile String accessToken;
private volatile String accessTokenSecret;
public OAuthConfiguration(String consumerKey, String consumerSecret, String accessToken, String accessTokenSecret) {
this.consumerKey = consumerKey;
this.consumerSecret = consumerSecret;
this.accessToken = accessToken;
this.accessTokenSecret = accessTokenSecret;
}
/** package friendly */
/* void setAsyncTwitter(AsyncTwitter asyncTwitter) {
this.asyncTwitter = asyncTwitter;
}*/
/** package friendly */
void setTwitter(Twitter twitter) {
this.twitter = twitter;
}
/**
* @return
*/
public Twitter getTwitter() {
return twitter;
}
/*
public AsyncTwitter getAsyncTwitter() {
return asyncTwitter;
}*/
public String getConsumerKey() {
return consumerKey;
}
public void setConsumerKey(String consumerKey) {
this.consumerKey = consumerKey;
}
public String getConsumerSecret() {
return consumerSecret;
}
public void setConsumerSecret(String consumerSecret) {
this.consumerSecret = consumerSecret;
}
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public String getAccessTokenSecret() {
return accessTokenSecret;
}
public void setAccessTokenSecret(String accessTokenSecret) {
this.accessTokenSecret = accessTokenSecret;
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright 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.oauth;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.util.Assert;
import twitter4j.Twitter;
import java.util.Properties;
/**
* Center piece for configuration for all the twitter adapters
*
* @author Josh Long
* @since 2.0
*/
public class OAuthConfigurationFactoryBean implements FactoryBean<OAuthConfiguration> {
public static final String WELL_KNOWN_CONSUMER_KEY = "twitter.oauth.consumerKey";
public static final String WELL_KNOWN_CONSUMER_KEY_SECRET = "twitter.oauth.consumerSecret";
public static final String WELL_KNOWN_CONSUMER_ACCESS_TOKEN = "twitter.oauth.accessToken";
public static final String WELL_KNOWN_CONSUMER_ACCESS_TOKEN_SECRET = "twitter.oauth.accessTokenSecret";
private volatile String consumerKey;
private volatile String consumerSecret;
private volatile String accessToken;
private volatile String accessTokenSecret;
private Twitter twitter;
private volatile OAuthConfiguration oAuthConfiguration;
private final Object guard = new Object();
private Twitter twitter(OAuthConfiguration oAuthConfiguration)
throws Exception {
OAuthAccessTokenBasedTwitterFactoryBean twitterFactoryBean = new OAuthAccessTokenBasedTwitterFactoryBean(oAuthConfiguration);
twitterFactoryBean.afterPropertiesSet();
twitterFactoryBean.verifyCredentials();
return twitterFactoryBean.getObject();
}
protected void bootstrapFromProperties(Properties props)
throws Throwable {
Assert.notNull(props, "'properties' must not be null");
this.setAccessToken(props.getProperty(WELL_KNOWN_CONSUMER_ACCESS_TOKEN));
this.setAccessTokenSecret(props.getProperty(WELL_KNOWN_CONSUMER_ACCESS_TOKEN_SECRET));
this.setConsumerKey(props.getProperty(WELL_KNOWN_CONSUMER_KEY));
this.setConsumerSecret(props.getProperty(WELL_KNOWN_CONSUMER_KEY_SECRET));
}
public OAuthConfiguration getObject() throws Exception {
return build();
}
public Class<?> getObjectType() {
return OAuthConfiguration.class;
}
public boolean isSingleton() {
return true;
}
public void setConsumerKey(String consumerKey) {
this.consumerKey = consumerKey;
}
public void setConsumerSecret(String consumerSecret) {
this.consumerSecret = consumerSecret;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public void setAccessTokenSecret(String accessTokenSecret) {
this.accessTokenSecret = accessTokenSecret;
}
private OAuthConfiguration build() throws Exception {
synchronized (this.guard) {
if (oAuthConfiguration == null) {
oAuthConfiguration = new OAuthConfiguration(this.consumerKey, this.consumerSecret, this.accessToken, this.accessTokenSecret);
twitter = this.twitter(oAuthConfiguration);
oAuthConfiguration.setTwitter(twitter);
}
}
return this.oAuthConfiguration;
}
}

View File

@@ -0,0 +1,2 @@
http\://www.springframework.org/schema/integration/twitter=org.springframework.integration.twitter.config.TwitterNamespaceHandler

View File

@@ -0,0 +1,2 @@
http\://www.springframework.org/schema/integration/twitter/spring-integration-twitter-2.0.xsd=org/springframework/integration/twitter/config/spring-integration-twitter-2.0.xsd
http\://www.springframework.org/schema/integration/twitter/spring-integration-twitter.xsd=org/springframework/integration/twitter/config/spring-integration-twitter-2.0.xsd

View File

@@ -0,0 +1,219 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 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.
-->
<xsd:schema xmlns="http://www.springframework.org/schema/integration/twitter"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:tool="http://www.springframework.org/schema/tool"
xmlns:integration="http://www.springframework.org/schema/integration"
targetNamespace="http://www.springframework.org/schema/integration/twitter"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/beans"/>
<xsd:import namespace="http://www.springframework.org/schema/tool"/>
<xsd:import namespace="http://www.springframework.org/schema/integration"
schemaLocation="http://www.springframework.org/schema/integration/spring-integration-2.0.xsd"/>
<xsd:element name="twitter-connection">
<xsd:annotation>
<xsd:documentation>
Sets up an instance of OAuthConfiguration which all adapters need to function properly
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:attribute name="id" type="xsd:string" use="required"/>
<xsd:attribute name="consumer-key" type="xsd:string" use="required"/>
<xsd:attribute name="consumer-secret" type="xsd:string" use="required"/>
<xsd:attribute name="access-token" type="xsd:string" use="required"/>
<xsd:attribute name="access-token-secret" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
<!--
This defines the beans we have for working with Twitter
-->
<xsd:element name="inbound-mention-channel-adapter">
<xsd:annotation>
<xsd:documentation>
Configures an inbound channel adapter that consumes message (representing mentions of your handle)
from twitter and sends Messages whose payloads are Status objects.
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:attribute name="id" type="xsd:string"/>
<xsd:attribute name="channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute use="required" name="twitter-connection" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.twitter.oauth.OAuthConfiguration"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<!-- <xsd:attribute name="username" type="xsd:string" use="required"/>
<xsd:attribute name="password" type="xsd:string" use="required"/>-->
</xsd:complexType>
</xsd:element>
<xsd:element name="inbound-dm-channel-adapter">
<xsd:annotation>
<xsd:documentation>
Configures an inbound channel adapter that consumes direct messages and forwards them to Spring Integration
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:attribute name="twitter-connection" use="required" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.twitter.oauth.OAuthConfiguration"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="id" type="xsd:string"/>
<xsd:attribute name="channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<!--<xsd:attribute name="username" type="xsd:string" use="required"/>
<xsd:attribute name="password" type="xsd:string" use="required"/>-->
</xsd:complexType>
</xsd:element>
<xsd:element name="inbound-update-channel-adapter">
<xsd:annotation>
<xsd:documentation>
Configures an inbound channel adapter that consumes message (representing your friends' timeline updates)
from twitter and sends Messages whose payloads are Tweet objects.
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:attribute name="id" type="xsd:string"/>
<xsd:attribute name="twitter-connection" use="required" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.twitter.oauth.OAuthConfiguration"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<!--
OUTBOUND
-->
<xsd:element name="outbound-dm-channel-adapter">
<xsd:annotation>
<xsd:documentation>
Configures an inbound channel adapter that consumes message (representing your friends' timeline updates)
from twitter and sends Messages whose payloads are Tweet objects.
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:attribute name="id" type="xsd:string"/>
<xsd:attribute name="twitter-connection" use="required" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.twitter.oauth.OAuthConfiguration"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="outbound-update-channel-adapter">
<xsd:annotation>
<xsd:documentation>
Configures an inbound channel adapter that consumes message (representing your friends' timeline updates)
from twitter and sends Messages whose payloads are Tweet objects.
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:attribute name="id" type="xsd:string"/>
<xsd:attribute name="twitter-connection" use="required" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.twitter.oauth.OAuthConfiguration"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
</xsd:schema>

View File

@@ -0,0 +1,43 @@
package org.springframework.integration.twitter;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.twitter.oauth.OAuthConfiguration;
import org.springframework.test.context.ContextConfiguration;
import twitter4j.Status;
import twitter4j.Twitter;
import java.util.Collection;
import javax.annotation.PostConstruct;
/**
* This class is used to simply demonstrating correctly factory-ing a {@link twitter4j.Twitter} instance
*/
@ContextConfiguration(locations = "twitter_connection_using_ns.xml")
public class SimpleTwitterTestClient {
private Twitter twitter;
@Autowired
private volatile OAuthConfiguration oAuthConfiguration;
@PostConstruct
public void begin() throws Exception {
this.twitter = oAuthConfiguration.getTwitter();
}
@Test
@Ignore
public void testConnectivity() throws Throwable {
Collection<Status> responses;
Assert.assertNotNull(this.twitter);
Assert.assertNotNull(responses = this.twitter.getFriendsTimeline());
Assert.assertTrue(responses.size() > 0);
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 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;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
/**
* @author Josh Long
*/
@ContextConfiguration(locations = {
"/receiving_dms_using_ns.xml"}
)
public class TestRecievingUsingNamespace extends AbstractJUnit4SpringContextTests {
@Autowired
private TwitterAnnouncer twitterAnnouncer;
@Test
@Ignore
public void testIt() throws Throwable {
long ctr = 0;
long s = 1000;
while (ctr < (s * 60 * 3)) {
Thread.sleep(s);
ctr += s;
}
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 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;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.integration.Message;
import org.springframework.integration.core.MessageBuilder;
import org.springframework.integration.core.MessageChannel;
import org.springframework.integration.core.MessagingTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
import org.springframework.util.StringUtils;
import twitter4j.GeoLocation;
/**
* @author Josh Long
*/
@ContextConfiguration(locations = {
"/sending_dms_using_ns.xml"}
)
public class TestSendingDMsUsingNamespace extends AbstractJUnit4SpringContextTests {
private volatile MessagingTemplate messagingTemplate = new MessagingTemplate();
@Value("#{out}")
private MessageChannel channel;
@Test
@Ignore
public void testSendingATweet() throws Throwable {
String dmUsr = System.getProperties().getProperty("twitter.dm.user");
MessageBuilder<String> mb = MessageBuilder.withPayload("'Hello world!', from the Spring Integration outbound Twitter adapter")
.setHeader(TwitterHeaders.TWITTER_GEOLOCATION, new GeoLocation(-76.226823, 23.642465)) // antarctica
.setHeader(TwitterHeaders.TWITTER_DISPLAY_COORDINATES, true);
if (StringUtils.hasText(dmUsr)) {
mb.setHeader(TwitterHeaders.TWITTER_DM_TARGET_USER_ID, dmUsr);
}
Message<String> m = mb.build();
this.messagingTemplate.send(this.channel, m);
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 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;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.integration.Message;
import org.springframework.integration.core.MessageBuilder;
import org.springframework.integration.core.MessageChannel;
import org.springframework.integration.core.MessagingTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
import org.springframework.util.StringUtils;
import twitter4j.GeoLocation;
/**
* @author Josh Long
*/
@ContextConfiguration(locations = {
"/sending_updates_using_ns.xml"}
)
public class TestSendingUpdatesUsingNamespace extends AbstractJUnit4SpringContextTests {
private MessagingTemplate messagingTemplate = new MessagingTemplate();
@Value("#{out}")
private MessageChannel channel;
@Test
@Ignore
public void testSendingATweet() throws Throwable {
MessageBuilder<String> mb = MessageBuilder.withPayload("'Hello world!', from the Spring Integration outbound Twitter adapter")
.setHeader(TwitterHeaders.TWITTER_IN_REPLY_TO_STATUS_ID, 21927437001L)
.setHeader(TwitterHeaders.TWITTER_GEOLOCATION, new GeoLocation(-76.226823, 23.642465)) // antarctica
.setHeader(TwitterHeaders.TWITTER_DISPLAY_COORDINATES, true);
Message<String> m = mb.build();
this.messagingTemplate.send(this.channel, m);
}
}

View File

@@ -0,0 +1,22 @@
package org.springframework.integration.twitter;
import org.springframework.stereotype.Component;
import twitter4j.DirectMessage;
import twitter4j.Status;
@Component
public class TwitterAnnouncer {
public void dm(DirectMessage directMessage) {
System.out.println("A direct message has been received from " + directMessage.getSenderScreenName() + " with text " + directMessage.getText());
}
public void mention(Status s) {
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());
}
}

View File

@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 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.
-->
<beans:beans
xmlns="http://www.springframework.org/schema/integration"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:tool="http://www.springframework.org/schema/tool"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:twitter="http://www.springframework.org/schema/integration/twitter"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-1.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool-3.0.xsd
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">
<context:component-scan
base-package="org.springframework.integration.twitter"/>
<context:property-placeholder
location="file://${user.home}/Desktop/twitter.properties"
ignore-unresolvable="true"/>
<channel id="inbound_dm"/>
<twitter:inbound-dm-channel-adapter
channel="inbound_dm" twitter-connection="tc"
/>
<twitter:twitter-connection
id="tc"
access-token="${twitter.oauth.accessToken}"
access-token-secret="${twitter.oauth.accessTokenSecret}"
consumer-key="${twitter.oauth.consumerKey}"
consumer-secret="${twitter.oauth.consumerSecret}"
/>
<service-activator input-channel="inbound_dm"
ref="twitterAnnouncer"
method="dm"/>
</beans:beans>

View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 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.
-->
<beans:beans
xmlns="http://www.springframework.org/schema/integration"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:tool="http://www.springframework.org/schema/tool"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:twitter="http://www.springframework.org/schema/integration/twitter"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-1.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool-3.0.xsd
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">
<context:component-scan
base-package="org.springframework.integration.twitter"/>
<context:property-placeholder
location="file://${user.home}/Desktop/twitter.properties"
ignore-unresolvable="true"/>
<twitter:twitter-connection
id="tc"
access-token="${twitter.oauth.accessToken}"
access-token-secret="${twitter.oauth.accessTokenSecret}"
consumer-key="${twitter.oauth.consumerKey}"
consumer-secret="${twitter.oauth.consumerSecret}"
/>
<channel id="inbound_tweets"/>
<twitter:inbound-mention-channel-adapter twitter-connection="tc" channel="inbound_tweets" />
<service-activator input-channel="inbound_tweets" ref="twitterAnnouncer" method="mention"/>
</beans:beans>

View File

@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 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.
-->
<beans:beans
xmlns="http://www.springframework.org/schema/integration"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:tool="http://www.springframework.org/schema/tool"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:twitter="http://www.springframework.org/schema/integration/twitter"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-1.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool-3.0.xsd
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">
<context:component-scan
base-package="org.springframework.integration.twitter"/>
<context:property-placeholder
location="file://${user.home}/Desktop/twitter.properties"
ignore-unresolvable="true"/>
<twitter:twitter-connection
id="tc"
access-token="${twitter.oauth.accessToken}"
access-token-secret="${twitter.oauth.accessTokenSecret}"
consumer-key="${twitter.oauth.consumerKey}"
consumer-secret="${twitter.oauth.consumerSecret}"
/>
<channel id="inbound_tweets"/>
<twitter:inbound-update-channel-adapter
twitter-connection="tc"
channel="inbound_tweets"
/>
<service-activator input-channel="inbound_tweets"
ref="twitterAnnouncer"
method="friendsTimelineUpdated"/>
</beans:beans>

View File

@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 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.
-->
<beans:beans
xmlns="http://www.springframework.org/schema/integration"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:tool="http://www.springframework.org/schema/tool"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:twitter="http://www.springframework.org/schema/integration/twitter"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-1.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool-3.0.xsd
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">
<context:component-scan
base-package="org.springframework.integration.twitter"/>
<context:property-placeholder
location="file://${user.home}/Desktop/twitter.properties"
ignore-unresolvable="true"/>
<twitter:twitter-connection
id="tc"
access-token="${twitter.oauth.accessToken}"
access-token-secret="${twitter.oauth.accessTokenSecret}"
consumer-key="${twitter.oauth.consumerKey}"
consumer-secret="${twitter.oauth.consumerSecret}"
/>
<channel id="out"/>
<twitter:outbound-dm-channel-adapter twitter-connection="tc" channel="out"/>
</beans:beans>

View File

@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 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.
-->
<beans:beans
xmlns="http://www.springframework.org/schema/integration"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:tool="http://www.springframework.org/schema/tool"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:twitter="http://www.springframework.org/schema/integration/twitter"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-1.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool-3.0.xsd
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">
<context:component-scan
base-package="org.springframework.integration.twitter"/>
<context:property-placeholder
location="file://${user.home}/Desktop/twitter.properties"
ignore-unresolvable="true"/>
<twitter:twitter-connection
id="tc"
access-token="${twitter.oauth.accessToken}"
access-token-secret="${twitter.oauth.accessTokenSecret}"
consumer-key="${twitter.oauth.consumerKey}"
consumer-secret="${twitter.oauth.consumerSecret}"
/>
<channel id="out"/>
<twitter:outbound-update-channel-adapter twitter-connection="tc" channel="out"/>
</beans:beans>

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 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.
-->
<beans:beans
xmlns="http://www.springframework.org/schema/integration"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:tool="http://www.springframework.org/schema/tool"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:twitter="http://www.springframework.org/schema/integration/twitter"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-1.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool-3.0.xsd
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">
<context:component-scan
base-package="org.springframework.integration.twitter"/>
<context:property-placeholder
location="file://${user.home}/Desktop/twitter.properties"
ignore-unresolvable="true"/>
<twitter:twitter-connection
id="tc"
access-token="${twitter.oauth.accessToken}"
access-token-secret="${twitter.oauth.accessTokenSecret}"
consumer-key="${twitter.oauth.consumerKey}"
consumer-secret="${twitter.oauth.consumerSecret}"
/>
<beans:bean class="org.springframework.integration.twitter.SimpleTwitterTestClient"/>
</beans:beans>

View File

@@ -0,0 +1,17 @@
Bundle-SymbolicName: org.springframework.integration.twitter
Bundle-Name: Spring Integration Twitter Support
Bundle-Vendor: SpringSource
Bundle-ManifestVersion: 2
Import-Template:
org.apache.commons.logging;version="[1.1.1, 2.0.0)",
org.apache.commons.lang.*;version="[2.5.0, 3.0.0)",
org.springframework.integration.*;version="[2.0.0, 2.0.1)",
org.springframework.beans.*;version="[3.0.0, 4.0.0)",
org.springframework.context;version="[3.0.0, 4.0.0)",
org.springframework.core.*;version="[3.0.0, 4.0.0)",
org.springframework.util;version="[3.0.0, 4.0.0)",
org.jivesoftware.*;version="[3.1.0, 4.0.0)",
org.apache.commons.io.*;version="[1.4,3.0)",
twitter4j.*;version="[2.1.0,2.2.0]",
javax.*;version="0",
org.w3c.dom.*;version="0"