INT-4453: Move SI Twitter module to Extensions
JIRA: https://jira.spring.io/browse/INT-4453 Since Spring Social project is aiming to its EOL, there is no reason to keep such a dependency in the Core project * Create a `spring-integration-social-twitter` project * Move all the sources and tests from the SI Core to this project * Rename `spring-integration-twitter-5.1.xsd` to `spring-integration-social-twitter-1.0.xsd` do not confuse IDE for auto imports of XSDs * Move `twitter.adoc` to README of this extension project * Replace `RedisMetadaStore` in test-case to a `SimpleMetadataStore` since this integration test does nothing with Redis and we don't have access to the `@RedisAvailable` JUnit rule * Enable all the Checkstyle rules and fix violations
This commit is contained in:
committed by
Gary Russell
parent
2486fdaa41
commit
159c1d5326
12
spring-integration-social-twitter/.travis.yml
Normal file
12
spring-integration-social-twitter/.travis.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
language: java
|
||||
jdk: oraclejdk8
|
||||
sudo: false
|
||||
before_cache:
|
||||
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.gradle/caches/
|
||||
- $HOME/.gradle/wrapper/
|
||||
install: true
|
||||
script:
|
||||
- ./gradlew check --refresh-dependencies --no-daemon
|
||||
312
spring-integration-social-twitter/README.adoc
Normal file
312
spring-integration-social-twitter/README.adoc
Normal file
@@ -0,0 +1,312 @@
|
||||
== Spring Integration Social Twitter Support
|
||||
|
||||
Spring Integration provides support for interacting with Twitter.
|
||||
With the Twitter adapters you can both receive and send Twitter messages.
|
||||
You can also perform a Twitter search based on a schedule and publish the search results within Messages.
|
||||
Also a search outbound gateway is provided to perform dynamic searches.
|
||||
|
||||
=== Introduction
|
||||
|
||||
Twitter is a social networking and micro-blogging service that enables its users to send and read messages known as tweets.
|
||||
Tweets are text-based posts of up to 140 characters displayed on the author's profile page and delivered to the author's subscribers who are known as followers.
|
||||
|
||||
The Spring Integration Social Twitter is based on the http://projects.spring.io/spring-social[Spring Social] project.
|
||||
All Twitter endpoints require the configuration of a `TwitterTemplate` because even search operations require an authenticated template.
|
||||
|
||||
Spring Integration provides a convenient namespace configuration to define Twitter artifacts.
|
||||
You can enable it by adding the following within your XML header.
|
||||
[source,xml]
|
||||
----
|
||||
xmlns:int-twitter="http://www.springframework.org/schema/integration/twitter"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/integration/twitter
|
||||
http://www.springframework.org/schema/integration/twitter/spring-integration-twitter.xsd"
|
||||
----
|
||||
|
||||
=== Twitter OAuth Configuration
|
||||
|
||||
For authenticated operations, Twitter uses OAuth - an authentication protocol that allows users to approve an application to act on their behalf without sharing their password.
|
||||
More information can be found at http://oauth.net[http://oauth.net] or in this article http://hueniverse.com/oauth[http://hueniverse.com/oauth] from Hueniverse.
|
||||
Please also see http://dev.twitter.com/pages/oauth_faq[OAuth FAQ] for more information about OAuth and Twitter.
|
||||
|
||||
In order to use OAuth authentication/authorization with Twitter you must create a new Application on the Twitter Developers site.
|
||||
Follow the directions below to create a new application and obtain consumer keys and an access token:
|
||||
|
||||
* Go to http://dev.twitter.com[http://dev.twitter.com]
|
||||
|
||||
* Click on the `Register an app` link and fill out all required fields on the form provided; set `Application Type` to `Client` and depending on the nature of your application select `Default Access Type` as _Read & Write_ or _Read-only_ and Submit the form.
|
||||
If everything is successful you'll be presented with the `Consumer Key` and `Consumer Secret`.
|
||||
Copy both values in a safe place.
|
||||
|
||||
* On the same page you should see a `My Access Token` button on the side bar (right).
|
||||
Click on it and you'll be presented with two more values: `Access Token` and `Access Token Secret`.
|
||||
Copy these values in a safe place as well.
|
||||
|
||||
=== Twitter Template
|
||||
|
||||
As mentioned above, Spring Integration relies upon Spring Social, and that library provides an implementation of the template pattern, `o.s.social.twitter.api.impl.TwitterTemplate` to interact with Twitter.
|
||||
For anonymous operations (e.g., search), you don't have to define an instance of `TwitterTemplate` explicitly, since a default instance will be created and injected into the endpoint.
|
||||
However, for authenticated operations (update status, send direct message, etc.), you must configure a `TwitterTemplate` as a bean and inject it explicitly into the endpoint, because the authentication configuration is required.
|
||||
Below is a sample configuration of TwitterTemplate:
|
||||
[source,xml]
|
||||
----
|
||||
<bean id="twitterTemplate" class="o.s.social.twitter.api.impl.TwitterTemplate">
|
||||
<constructor-arg value="4XzBPacJQxyBzzzH"/>
|
||||
<constructor-arg value="AbRxUAvyCtqQtvxFK8w5ZMtMj20KFhB6o"/>
|
||||
<constructor-arg value="21691649-4YZY5iJEOfz2A9qCFd9SjBRGb3HLmIm4HNE"/>
|
||||
<constructor-arg value="AbRxUAvyNCtqQtxFK8w5ZMtMj20KFhB6o"/>
|
||||
</bean>
|
||||
----
|
||||
|
||||
NOTE: The values above are not real.
|
||||
|
||||
As you can see from the configuration above, all we need to do is to provide OAuth `attributes` as constructor arguments.
|
||||
The values would be those you obtained in the previous step.
|
||||
The order of constructor arguments is: 1) `consumerKey`, 2) `consumerSecret`, 3) `accessToken`, and 4) `accessTokenSecret`.
|
||||
|
||||
A more practical way to manage OAuth connection attributes would be via Spring's property placeholder support by simply creating a property file (e.g., oauth.properties):
|
||||
|
||||
[source,java]
|
||||
----
|
||||
twitter.oauth.consumerKey=4XzBPacJQxyBzzzH
|
||||
twitter.oauth.consumerSecret=AbRxUAvyCtqQtvxFK8w5ZMtMj20KFhB6o
|
||||
twitter.oauth.accessToken=21691649-4YZY5iJEOfz2A9qCFd9SjBRGb3HLmIm4HNE
|
||||
twitter.oauth.accessTokenSecret=AbRxUAvyNCtqQtxFK8w5ZMtMj20KFhB6o
|
||||
----
|
||||
|
||||
Then, you can configure a `property-placeholder` to point to the above property file:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<context:property-placeholder location="classpath:oauth.properties"/>
|
||||
|
||||
<bean id="twitterTemplate" class="o.s.social.twitter.api.impl.TwitterTemplate">
|
||||
<constructor-arg value="${twitter.oauth.consumerKey}"/>
|
||||
<constructor-arg value="${twitter.oauth.consumerSecret}"/>
|
||||
<constructor-arg value="${twitter.oauth.accessToken}"/>
|
||||
<constructor-arg value="${twitter.oauth.accessTokenSecret}"/>
|
||||
</bean>
|
||||
----
|
||||
|
||||
=== Twitter Inbound Adapters
|
||||
|
||||
Twitter inbound adapters allow you to receive Twitter Messages.
|
||||
There are several types of http://support.twitter.com/articles/119138-types-of-tweets-and-where-they-appear[twitter messages, or tweets].
|
||||
|
||||
Spring Integration provides support for receiving tweets as _Timeline Updates_, _Direct Messages_, _Mention Messages_ as well as Search Results.
|
||||
|
||||
[IMPORTANT]
|
||||
=====
|
||||
Every Inbound Twitter Channel Adapter is a _Polling Consumer_ which means you have to provide a poller configuration.
|
||||
Twitter defines a concept of Rate Limiting.
|
||||
You can read more about it here: https://dev.twitter.com/docs/rate-limiting/1.1[Rate Limiting].
|
||||
In a nutshell, Rate Limiting is a mechanism that Twitter uses to manage how often an application can poll for updates.
|
||||
You should consider this when setting your poller intervals so that the adapter polls in compliance with the Twitter policies.
|
||||
=====
|
||||
|
||||
Another issue that we need to worry about is handling duplicate Tweets.
|
||||
The same adapter (e.g., Search or Timeline Update) while polling on Twitter may receive the same values more than once.
|
||||
For example if you keep searching on Twitter with the same search criteria you'll end up with the same set of tweets unless some other new tweet that matches your search criteria was posted in between your searches.
|
||||
In that situation you'll get all the tweets you had before plus the new one.
|
||||
But what you really want is only the new tweet(s).
|
||||
Spring Integration provides an elegant mechanism for handling these situations.
|
||||
The latest Tweet id will be stored in an instance of the `org.springframework.integration.metadata.MetadataStore` strategy (e.g.
|
||||
last retrieved tweet in this case).
|
||||
For more information see https://docs.spring.io/spring-integration/docs/current/reference/html/system-management-chapter.html#metadata-store[MetadataStore].
|
||||
|
||||
NOTE: The key used to persist the latest _twitter id_ is the value of the (required) `id` attribute of the Twitter Inbound Channel Adapter component plus the `profileId` of the Twitter user.
|
||||
|
||||
==== Inbound Message Channel Adapter
|
||||
|
||||
This adapter allows you to receive updates from everyone you follow.
|
||||
It's essentially the "Timeline Update" adapter.
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<int-twitter:inbound-channel-adapter
|
||||
twitter-template="twitterTemplate"
|
||||
channel="inChannel">
|
||||
<int:poller fixed-rate="5000" max-messages-per-poll="3"/>
|
||||
</int-twitter:inbound-channel-adapter>
|
||||
----
|
||||
|
||||
==== Direct Inbound Message Channel Adapter
|
||||
|
||||
This adapter allows you to receive Direct Messages that were sent to you from other Twitter users.
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<int-twitter:dm-inbound-channel-adapter
|
||||
twitter-template="twiterTemplate"
|
||||
channel="inboundDmChannel">
|
||||
<int-poller fixed-rate="5000" max-messages-per-poll="3"/>
|
||||
</int-twitter:dm-inbound-channel-adapter>
|
||||
----
|
||||
|
||||
==== Mentions Inbound Message Channel Adapter
|
||||
|
||||
This adapter allows you to receive Twitter Messages that Mention you via `@user` syntax.
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<int-twitter:mentions-inbound-channel-adapter
|
||||
twitter-template="twiterTemplate"
|
||||
channel="inboundMentionsChannel">
|
||||
<int:poller fixed-rate="5000" max-messages-per-poll="3"/>
|
||||
</int-twitter:mentions-inbound-channel-adapter>
|
||||
----
|
||||
|
||||
==== Search Inbound Message Channel Adapter
|
||||
|
||||
This adapter allows you to perform searches.
|
||||
As you can see it is not necessary to define twitter-template since a search can be performed anonymously, however you must define a search query.
|
||||
[source,xml]
|
||||
----
|
||||
<int-twitter:search-inbound-channel-adapter
|
||||
query="#springintegration"
|
||||
channel="inboundMentionsChannel">
|
||||
<int:poller fixed-rate="5000" max-messages-per-poll="3"/>
|
||||
</int-twitter:search-inbound-channel-adapter>
|
||||
----
|
||||
|
||||
Refer to https://dev.twitter.com/docs/using-search to learn more about Twitter queries.
|
||||
|
||||
As you can see the configuration of all of these adapters is very similar to other inbound adapters with one exception.
|
||||
Some may need to be injected with the `twitter-template`.
|
||||
Once received each Twitter Message would be encapsulated in a Spring Integration Message and sent to the channel specified by the `channel` attribute.
|
||||
Currently the Payload type of any Message is `org.springframework.integration.twitter.core.Tweet` which is very similar to the object with the same name in Spring Social.
|
||||
As we migrate to Spring Social we'll be depending on their API and some of the artifacts that are currently in use will be obsolete, however we've already made sure that the impact of such migration is minimal by aligning our API with the current state (at the time of writing) of Spring Social.
|
||||
|
||||
To get the text from the `org.springframework.social.twitter.api.Tweet` simply invoke the `getText()` method.
|
||||
|
||||
=== Twitter Outbound Adapter
|
||||
|
||||
Twitter outbound channel adapters allow you to send Twitter Messages, or tweets.
|
||||
|
||||
Spring Integration also supports sending _Status Update Messages_ and _Direct Messages_.
|
||||
Twitter outbound channel adapters will take the Message payload and send it as a Twitter message.
|
||||
Currently the only supported payload type is`String`, so consider adding a _transformer_ if the payload of the incoming message is not a String.
|
||||
|
||||
==== Twitter Outbound Update Channel Adapter
|
||||
|
||||
This adapter allows you to send regular status updates by simply sending a Message to the channel identified by the `channel` attribute.
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<int-twitter:outbound-channel-adapter
|
||||
twitter-template="twitterTemplate"
|
||||
channel="twitterChannel"/>
|
||||
----
|
||||
|
||||
The only extra configuration that is required for this adapter is the `twitter-template` reference.
|
||||
|
||||
The `<int-twitter:outbound-channel-adapter>` supports a `tweet-data-expression` to populate the `TweetData` argument (http://projects.spring.io/spring-social-twitter/[Spring Social Twitter]) using the message as the root object of the expression evaluation context.
|
||||
The result can be a `String`, which will be used for the `TweetData` message; a `Tweet` object, the `text` of which will be used for the `TweetData` message; or an entire `TweetData` object.
|
||||
For convenience, the `TweetData` can be built from the expression directly without needing a fully qualified class name:
|
||||
[source,xml]
|
||||
----
|
||||
<int-twitter:outbound-channel-adapter
|
||||
twitter-template="twitterTemplate"
|
||||
channel="twitterChannel"
|
||||
tweet-data-expression="new TweetData(payload).withMedia(headers.media).displayCoordinates(true)/>
|
||||
----
|
||||
|
||||
This allows, for example, attaching an image to the tweet.
|
||||
|
||||
==== Twitter Outbound Direct Message Channel Adapter
|
||||
|
||||
This adapter allows you to send Direct Twitter Messages (i.e., `@user`) by simply sending a Message to the channel identified by the `channel` attribute.
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<int-twitter:dm-outbound-channel-adapter
|
||||
twitter-template="twitterTemplate"
|
||||
channel="twitterChannel"/>
|
||||
----
|
||||
|
||||
The only extra configuration that is required for this adapter is the `twitter-template` reference.
|
||||
|
||||
When it comes to Twitter Direct Messages, you must specify who you are sending the message to - the _target userId_.
|
||||
The Twitter Outbound Direct Message Channel Adapter will look for a target userId in the Message headers under the name `twitter_dmTargetUserId` which is also identified by the following constant: `TwitterHeaders.DM_TARGET_USER_ID`.
|
||||
So when creating a Message all you need to do is add a value for that header.
|
||||
[source,java]
|
||||
----
|
||||
Message message = MessageBuilder.withPayload("hello")
|
||||
.setHeader(TwitterHeaders.DM_TARGET_USER_ID, "z_oleg").build();
|
||||
----
|
||||
|
||||
The above approach works well if you are creating the Message programmatically.
|
||||
However it's more common to provide the header value within a messaging flow.
|
||||
The value can be provided by an upstream <header-enricher>.
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<int:header-enricher input-channel="in" output-channel="out">
|
||||
<int:header name="twitter_dmTargetUserId" value="z_oleg"/>
|
||||
</int:header-enricher>
|
||||
----
|
||||
|
||||
It's quite common that the value must be determined dynamically.
|
||||
For those cases you can take advantage of SpEL support within the `<header-enricher>`.
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<int:header-enricher input-channel="in" output-channel="out">
|
||||
<int:header name="twitter_dmTargetUserId"
|
||||
expression="@twitterIdService.lookup(headers.username)"/>
|
||||
</int:header-enricher>
|
||||
----
|
||||
|
||||
IMPORTANT: Twitter does not allow you to post duplicate Messages.
|
||||
This is a common problem during testing when the same code works the first time but does not work the second time.
|
||||
So, make sure to change the content of the Message each time.
|
||||
Another thing that works well for testing is to append a timestamp to the end of each message.
|
||||
|
||||
=== Twitter Search Outbound Gateway
|
||||
|
||||
In Spring Integration, an outbound gateway is used for two-way request/response communication with an external service.
|
||||
The Twitter Search Outbound Gateway allows you to issue dynamic twitter searches.
|
||||
The reply message payload is a collection of `Tweet` objects.
|
||||
If the search returns no results, the payload is an empty collection.
|
||||
You can limit the number of tweets and you can page through a larger set of tweets by making multiple calls.
|
||||
To facilitate this, search reply messages contain a header `twitter_searchMetadata` with its value being a `SearchMetadata` object.
|
||||
For more information on the `Tweet`, `SearchParameters` and `SearchMetadata` classes, refer to the http://projects.spring.io/spring-social-twitter/[Spring Social Twitter] documentation.
|
||||
|
||||
*Configuring the Outbound Gateway*
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<int-twitter:search-outbound-gateway id="twitter"
|
||||
request-channel="in" <1>
|
||||
twitter-template="twitterTemplate" <2>
|
||||
search-args-expression="payload" <3>
|
||||
reply-channel="out" <4>
|
||||
reply-timeout="123" <5>
|
||||
order="1" <6>
|
||||
auto-startup="false" <7>
|
||||
phase="100" /> <8>
|
||||
|
||||
----
|
||||
|
||||
<1> The channel used to send search requests to this gateway.
|
||||
|
||||
<2> A reference to a `TwitterTemplate` with authentication configuration.
|
||||
|
||||
<3> A SpEL expression that evaluates to argument(s) for the search.
|
||||
Default: *"payload"* - in which case the payload can be a `String` (e.g "#springintegration") and the gateway limits the query to 20 tweets, or the payload can be a `SearchParameters` object. +
|
||||
The expression can also be specified as a http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html#expressions-inline-lists[SpEL List].
|
||||
The first element (String) is the query, the remaining elements (Numbers) are `pageSize, sinceId, maxId` respectively - refer to the Spring Social Twitter documentation for more information about these parameters.
|
||||
When specifying a `SearchParameters` object directly in the SpEL expression, you do not have to fully qualify the class name.
|
||||
Some examples: +
|
||||
`new SearchParameters(payload).count(5).sinceId(headers.sinceId)` +
|
||||
`{payload, 30}` +
|
||||
`{payload, headers.pageSize, headers.sinceId, headers.maxId}`
|
||||
|
||||
<4> The channel to which to send the reply; if omitted, the `replyChannel` header is used.
|
||||
|
||||
<5> The timeout when sending the reply message to the reply channel; only applies if the reply channel can block, for example a bounded queue channel that is full.
|
||||
|
||||
<6> When subscribed to a publish/subscribe channel, the order in which this endpoint will be invoked.
|
||||
|
||||
<7> `SmartLifecycle` method.
|
||||
|
||||
<8> `SmartLifecycle` method.
|
||||
255
spring-integration-social-twitter/build.gradle
Normal file
255
spring-integration-social-twitter/build.gradle
Normal file
@@ -0,0 +1,255 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'eclipse'
|
||||
id 'idea'
|
||||
id 'jacoco'
|
||||
id 'checkstyle'
|
||||
id 'org.sonarqube' version '2.6.2'
|
||||
}
|
||||
|
||||
description = 'Spring Integration Social Twitter Support'
|
||||
|
||||
apply from: "${rootProject.projectDir}/publish-maven.gradle"
|
||||
|
||||
group = 'org.springframework.integration'
|
||||
|
||||
repositories {
|
||||
if (version.endsWith('BUILD-SNAPSHOT')) {
|
||||
maven { url 'http://repo.spring.io/libs-snapshot' }
|
||||
}
|
||||
maven { url 'http://repo.spring.io/libs-milestone' }
|
||||
}
|
||||
|
||||
compileJava {
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
}
|
||||
|
||||
ext {
|
||||
jackson2Version = '2.9.5'
|
||||
springIntegrationVersion = '5.1.0.BUILD-SNAPSHOT'
|
||||
springSocialTwitterVersion = '1.1.2.RELEASE'
|
||||
springVersion = '5.1.0.BUILD-SNAPSHOT'
|
||||
|
||||
idPrefix = 'twitter'
|
||||
|
||||
linkHomepage = 'https://github.com/spring-projects/spring-integration-extensions'
|
||||
linkCi = 'https://build.spring.io/browse/INTEXT'
|
||||
linkIssue = 'https://jira.spring.io/browse/INTEXT'
|
||||
linkScmUrl = 'https://github.com/spring-projects/spring-integration-extensions'
|
||||
linkScmConnection = 'https://github.com/spring-projects/spring-integration-extensions.git'
|
||||
linkScmDevConnection = 'git@github.com:spring-projects/spring-integration-extensions.git'
|
||||
}
|
||||
|
||||
eclipse.project.natures += 'org.springframework.ide.eclipse.core.springnature'
|
||||
|
||||
sourceSets {
|
||||
test {
|
||||
resources {
|
||||
srcDirs = ['src/test/resources', 'src/test/java']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jacoco {
|
||||
toolVersion = "0.7.9"
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
configFile = file("$rootDir/src/checkstyle/checkstyle.xml")
|
||||
toolVersion = "8.9"
|
||||
}
|
||||
|
||||
// enable all compiler warnings; individual projects may customize further
|
||||
[compileJava, compileTestJava]*.options*.compilerArgs = ['-Xlint:all,-options,-processing']
|
||||
|
||||
dependencies {
|
||||
dependencies {
|
||||
compile "org.springframework.integration:spring-integration-core:$springIntegrationVersion"
|
||||
compile "org.springframework:spring-web:$springVersion"
|
||||
compile("org.springframework.social:spring-social-twitter:$springSocialTwitterVersion") {
|
||||
exclude group: 'org.springframework', module: 'spring-beans'
|
||||
exclude group: 'org.springframework', module: 'spring-context'
|
||||
exclude group: 'org.springframework', module: 'spring-core'
|
||||
exclude group: 'org.springframework', module: 'spring-expression'
|
||||
exclude group: 'org.springframework', module: 'spring-web'
|
||||
exclude group: 'org.springframework', module: 'spring-webmvc'
|
||||
}
|
||||
//compile("javax.activation:activation:$javaxActivationVersion", optional)
|
||||
|
||||
testCompile "org.springframework.integration:spring-integration-test:$springIntegrationVersion"
|
||||
testCompile "com.fasterxml.jackson.core:jackson-databind:$jackson2Version"
|
||||
}
|
||||
}
|
||||
|
||||
test {
|
||||
// suppress all console output during testing unless running `gradle -i`
|
||||
logging.captureStandardOutput(LogLevel.INFO)
|
||||
|
||||
maxHeapSize = "1024m"
|
||||
jacoco {
|
||||
append = false
|
||||
destinationFile = file("$buildDir/jacoco.exec")
|
||||
}
|
||||
}
|
||||
|
||||
jacocoTestReport {
|
||||
reports {
|
||||
xml.enabled false
|
||||
csv.enabled false
|
||||
html.destination file("${buildDir}/reports/jacoco/html")
|
||||
}
|
||||
}
|
||||
|
||||
task sourcesJar(type: Jar) {
|
||||
classifier = 'sources'
|
||||
from sourceSets.main.allJava
|
||||
}
|
||||
|
||||
task javadocJar(type: Jar) {
|
||||
classifier = 'javadoc'
|
||||
from javadoc
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives sourcesJar
|
||||
archives javadocJar
|
||||
}
|
||||
|
||||
sonarqube {
|
||||
properties {
|
||||
property "sonar.jacoco.reportPath", "${buildDir.name}/jacoco.exec"
|
||||
property "sonar.links.homepage", linkHomepage
|
||||
property "sonar.links.ci", linkCi
|
||||
property "sonar.links.issue", linkIssue
|
||||
property "sonar.links.scm", linkScmUrl
|
||||
property "sonar.links.scm_dev", linkScmDevConnection
|
||||
property "sonar.java.coveragePlugin", "jacoco"
|
||||
}
|
||||
}
|
||||
|
||||
task api(type: Javadoc) {
|
||||
group = 'Documentation'
|
||||
description = 'Generates the Javadoc API documentation.'
|
||||
title = "${rootProject.description} ${version} API"
|
||||
options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PROTECTED
|
||||
options.author = true
|
||||
options.header = rootProject.description
|
||||
options.overview = 'src/api/overview.html'
|
||||
options.stylesheetFile = file("src/api/stylesheet.css")
|
||||
|
||||
source = sourceSets.main.allJava
|
||||
classpath = project.sourceSets.main.compileClasspath
|
||||
destinationDir = new File(buildDir, "api")
|
||||
}
|
||||
|
||||
task schemaZip(type: Zip) {
|
||||
group = 'Distribution'
|
||||
classifier = 'schema'
|
||||
description = "Builds -${classifier} archive containing all " +
|
||||
"XSDs for deployment at static.springframework.org/schema."
|
||||
|
||||
duplicatesStrategy = 'exclude'
|
||||
|
||||
def Properties schemas = new Properties();
|
||||
def shortName = idPrefix.replaceFirst("${idPrefix}-", '')
|
||||
|
||||
project.sourceSets.main.resources.find {
|
||||
it.path.endsWith("META-INF${File.separator}spring.schemas")
|
||||
}?.withInputStream { schemas.load(it) }
|
||||
|
||||
for (def key : schemas.keySet()) {
|
||||
File xsdFile = project.sourceSets.main.resources.find {
|
||||
it.path.replaceAll('\\\\', '/').endsWith(schemas.get(key))
|
||||
}
|
||||
assert xsdFile != null
|
||||
into("integration/${shortName}") {
|
||||
from xsdFile.path
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task docsZip(type: Zip) {
|
||||
group = 'Distribution'
|
||||
classifier = 'docs'
|
||||
description = "Builds -${classifier} archive containing api " +
|
||||
"for deployment at static.spring.io/spring-integration/docs."
|
||||
|
||||
from('src/dist') {
|
||||
include 'changelog.txt'
|
||||
}
|
||||
|
||||
from(api) {
|
||||
into 'api'
|
||||
}
|
||||
}
|
||||
|
||||
task distZip(type: Zip, dependsOn: [docsZip, schemaZip]) {
|
||||
group = 'Distribution'
|
||||
classifier = 'dist'
|
||||
description = "Builds -${classifier} archive, containing all jars and docs, " +
|
||||
"suitable for community download page."
|
||||
|
||||
ext.baseDir = "${project.name}-${project.version}";
|
||||
|
||||
from('src/dist') {
|
||||
include 'readme.txt'
|
||||
include 'license.txt'
|
||||
include 'notice.txt'
|
||||
into "${baseDir}"
|
||||
}
|
||||
|
||||
from(zipTree(docsZip.archivePath)) {
|
||||
into "${baseDir}/docs"
|
||||
}
|
||||
|
||||
from(zipTree(schemaZip.archivePath)) {
|
||||
into "${baseDir}/schema"
|
||||
}
|
||||
|
||||
into("${baseDir}/libs") {
|
||||
from project.jar
|
||||
from project.sourcesJar
|
||||
from project.javadocJar
|
||||
}
|
||||
}
|
||||
|
||||
// Create an optional "with dependencies" distribution.
|
||||
// Not published by default; only for use when building from source.
|
||||
task depsZip(type: Zip, dependsOn: distZip) { zipTask ->
|
||||
group = 'Distribution'
|
||||
classifier = 'dist-with-deps'
|
||||
description = "Builds -${classifier} archive, containing everything " +
|
||||
"in the -${distZip.classifier} archive plus all dependencies."
|
||||
|
||||
from zipTree(distZip.archivePath)
|
||||
|
||||
gradle.taskGraph.whenReady { taskGraph ->
|
||||
if (taskGraph.hasTask(":${zipTask.name}")) {
|
||||
def projectName = rootProject.name
|
||||
def artifacts = new HashSet()
|
||||
|
||||
rootProject.configurations.runtime.resolvedConfiguration.resolvedArtifacts.each { artifact ->
|
||||
def dependency = artifact.moduleVersion.id
|
||||
if (!projectName.equals(dependency.name)) {
|
||||
artifacts << artifact.file
|
||||
}
|
||||
}
|
||||
|
||||
zipTask.from(artifacts) {
|
||||
into "${distZip.baseDir}/deps"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives distZip
|
||||
archives docsZip
|
||||
archives schemaZip
|
||||
}
|
||||
|
||||
task dist(dependsOn: assemble) {
|
||||
group = 'Distribution'
|
||||
description = 'Builds -dist, -docs and -schema distribution archives.'
|
||||
}
|
||||
1
spring-integration-social-twitter/gradle.properties
Normal file
1
spring-integration-social-twitter/gradle.properties
Normal file
@@ -0,0 +1 @@
|
||||
version=1.0.0.BUILD-SNAPSHOT
|
||||
BIN
spring-integration-social-twitter/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
spring-integration-social-twitter/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
spring-integration-social-twitter/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
spring-integration-social-twitter/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#Fri Jul 07 17:27:53 EDT 2017
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-bin.zip
|
||||
172
spring-integration-social-twitter/gradlew
vendored
Normal file
172
spring-integration-social-twitter/gradlew
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
84
spring-integration-social-twitter/gradlew.bat
vendored
Normal file
84
spring-integration-social-twitter/gradlew.bat
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
62
spring-integration-social-twitter/publish-maven.gradle
Normal file
62
spring-integration-social-twitter/publish-maven.gradle
Normal file
@@ -0,0 +1,62 @@
|
||||
apply plugin: 'maven'
|
||||
|
||||
ext.optionalDeps = []
|
||||
ext.providedDeps = []
|
||||
|
||||
ext.optional = { optionalDeps << it }
|
||||
ext.provided = { providedDeps << it }
|
||||
|
||||
install {
|
||||
repositories.mavenInstaller {
|
||||
customizePom(pom, project)
|
||||
}
|
||||
}
|
||||
|
||||
def customizePom(pom, gradleProject) {
|
||||
pom.whenConfigured { generatedPom ->
|
||||
// respect 'optional' and 'provided' dependencies
|
||||
gradleProject.optionalDeps.each { dep ->
|
||||
generatedPom.dependencies.find { it.artifactId == dep.name }?.optional = true
|
||||
}
|
||||
gradleProject.providedDeps.each { dep ->
|
||||
generatedPom.dependencies.find { it.artifactId == dep.name }?.scope = 'provided'
|
||||
}
|
||||
|
||||
// eliminate test-scoped dependencies (no need in maven central poms)
|
||||
generatedPom.dependencies.removeAll { dep ->
|
||||
dep.scope == 'test'
|
||||
}
|
||||
|
||||
// add all items necessary for maven central publication
|
||||
generatedPom.project {
|
||||
name = gradleProject.description
|
||||
description = gradleProject.description
|
||||
url = linkHomepage
|
||||
organization {
|
||||
name = 'SpringIO'
|
||||
url = 'http://spring.io'
|
||||
}
|
||||
licenses {
|
||||
license {
|
||||
name 'The Apache Software License, Version 2.0'
|
||||
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
|
||||
distribution 'repo'
|
||||
}
|
||||
}
|
||||
|
||||
scm {
|
||||
url = linkScmUrl
|
||||
connection = 'scm:git:' + linkScmConnection
|
||||
developerConnection = 'scm:git:' + linkScmDevConnection
|
||||
}
|
||||
|
||||
developers {
|
||||
developer {
|
||||
id = 'erenavsarogullari'
|
||||
name = 'Eren Avsarogullari'
|
||||
email = 'erenavsarogullari@gmail.com'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
spring-integration-social-twitter/settings.gradle
Normal file
1
spring-integration-social-twitter/settings.gradle
Normal file
@@ -0,0 +1 @@
|
||||
rootProject.name = 'spring-integration-social-twitter'
|
||||
22
spring-integration-social-twitter/src/api/overview.html
Normal file
22
spring-integration-social-twitter/src/api/overview.html
Normal file
@@ -0,0 +1,22 @@
|
||||
<html>
|
||||
<body>
|
||||
This document is the API specification for Spring Integration Hazelcast Support project
|
||||
<hr/>
|
||||
<div id="overviewBody">
|
||||
<p>
|
||||
For further API reference and developer documentation, see the
|
||||
<a href="http://docs.spring.io/spring-integration/reference/"
|
||||
target="_top">Spring Integration Reference Documentation</a>.
|
||||
That documentation contains more detailed, developer-targeted
|
||||
descriptions, with conceptual overviews, definitions of terms,
|
||||
workarounds, and working code examples.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you are interested in commercial training, consultancy, and
|
||||
support for Spring Integration, please visit <a href="http://spring.io/" target="_top">
|
||||
http://spring.io</a>
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
598
spring-integration-social-twitter/src/api/stylesheet.css
Normal file
598
spring-integration-social-twitter/src/api/stylesheet.css
Normal file
@@ -0,0 +1,598 @@
|
||||
/* Javadoc style sheet */
|
||||
/*
|
||||
Overall document style
|
||||
*/
|
||||
|
||||
@import url('resources/fonts/dejavu.css');
|
||||
|
||||
body {
|
||||
background-color:#ffffff;
|
||||
color:#353833;
|
||||
font-family:'DejaVu Sans', Arial, Helvetica, sans-serif;
|
||||
font-size:14px;
|
||||
margin:0;
|
||||
}
|
||||
a:link, a:visited {
|
||||
text-decoration:none;
|
||||
color:#4A6782;
|
||||
}
|
||||
a:hover, a:focus {
|
||||
text-decoration:none;
|
||||
color:#bb7a2a;
|
||||
}
|
||||
a:active {
|
||||
text-decoration:none;
|
||||
color:#4A6782;
|
||||
}
|
||||
a[name] {
|
||||
color:#353833;
|
||||
}
|
||||
a[name]:hover {
|
||||
text-decoration:none;
|
||||
color:#353833;
|
||||
}
|
||||
pre {
|
||||
font-family:'DejaVu Sans Mono', monospace;
|
||||
font-size:14px;
|
||||
}
|
||||
h1 {
|
||||
font-size:20px;
|
||||
}
|
||||
h2 {
|
||||
font-size:18px;
|
||||
}
|
||||
h3 {
|
||||
font-size:16px;
|
||||
font-style:italic;
|
||||
}
|
||||
h4 {
|
||||
font-size:13px;
|
||||
}
|
||||
h5 {
|
||||
font-size:12px;
|
||||
}
|
||||
h6 {
|
||||
font-size:11px;
|
||||
}
|
||||
ul {
|
||||
list-style-type:disc;
|
||||
}
|
||||
code, tt {
|
||||
font-family:'DejaVu Sans Mono', monospace;
|
||||
font-size:14px;
|
||||
padding-top:4px;
|
||||
margin-top:8px;
|
||||
line-height:1.4em;
|
||||
}
|
||||
dt code {
|
||||
font-family:'DejaVu Sans Mono', monospace;
|
||||
font-size:14px;
|
||||
padding-top:4px;
|
||||
}
|
||||
table tr td dt code {
|
||||
font-family:'DejaVu Sans Mono', monospace;
|
||||
font-size:14px;
|
||||
vertical-align:top;
|
||||
padding-top:4px;
|
||||
}
|
||||
sup {
|
||||
font-size:8px;
|
||||
}
|
||||
/*
|
||||
Document title and Copyright styles
|
||||
*/
|
||||
.clear {
|
||||
clear:both;
|
||||
height:0px;
|
||||
overflow:hidden;
|
||||
}
|
||||
.aboutLanguage {
|
||||
float:right;
|
||||
padding:0px 21px;
|
||||
font-size:11px;
|
||||
z-index:200;
|
||||
margin-top:-9px;
|
||||
}
|
||||
.legalCopy {
|
||||
margin-left:.5em;
|
||||
}
|
||||
.bar a, .bar a:link, .bar a:visited, .bar a:active {
|
||||
color:#FFFFFF;
|
||||
text-decoration:none;
|
||||
}
|
||||
.bar a:hover, .bar a:focus {
|
||||
color:#bb7a2a;
|
||||
}
|
||||
.tab {
|
||||
background-color:#0066FF;
|
||||
color:#ffffff;
|
||||
padding:8px;
|
||||
width:5em;
|
||||
font-weight:bold;
|
||||
}
|
||||
/*
|
||||
Navigation bar styles
|
||||
*/
|
||||
.bar {
|
||||
background-color:#4D7A97;
|
||||
color:#FFFFFF;
|
||||
padding:.8em .5em .4em .8em;
|
||||
height:auto;/*height:1.8em;*/
|
||||
font-size:11px;
|
||||
margin:0;
|
||||
}
|
||||
.topNav {
|
||||
background-color:#4D7A97;
|
||||
color:#FFFFFF;
|
||||
float:left;
|
||||
padding:0;
|
||||
width:100%;
|
||||
clear:right;
|
||||
height:2.8em;
|
||||
padding-top:10px;
|
||||
overflow:hidden;
|
||||
font-size:12px;
|
||||
}
|
||||
.bottomNav {
|
||||
margin-top:10px;
|
||||
background-color:#4D7A97;
|
||||
color:#FFFFFF;
|
||||
float:left;
|
||||
padding:0;
|
||||
width:100%;
|
||||
clear:right;
|
||||
height:2.8em;
|
||||
padding-top:10px;
|
||||
overflow:hidden;
|
||||
font-size:12px;
|
||||
}
|
||||
.subNav {
|
||||
background-color:#dee3e9;
|
||||
float:left;
|
||||
width:100%;
|
||||
overflow:hidden;
|
||||
font-size:12px;
|
||||
}
|
||||
.subNav div {
|
||||
clear:left;
|
||||
float:left;
|
||||
padding:0 0 5px 6px;
|
||||
text-transform:uppercase;
|
||||
}
|
||||
ul.navList, ul.subNavList {
|
||||
float:left;
|
||||
margin:0 25px 0 0;
|
||||
padding:0;
|
||||
}
|
||||
ul.navList li{
|
||||
list-style:none;
|
||||
float:left;
|
||||
padding: 5px 6px;
|
||||
text-transform:uppercase;
|
||||
}
|
||||
ul.subNavList li{
|
||||
list-style:none;
|
||||
float:left;
|
||||
}
|
||||
.topNav a:link, .topNav a:active, .topNav a:visited, .bottomNav a:link, .bottomNav a:active, .bottomNav a:visited {
|
||||
color:#FFFFFF;
|
||||
text-decoration:none;
|
||||
text-transform:uppercase;
|
||||
}
|
||||
.topNav a:hover, .bottomNav a:hover {
|
||||
text-decoration:none;
|
||||
color:#bb7a2a;
|
||||
text-transform:uppercase;
|
||||
}
|
||||
.navBarCell1Rev {
|
||||
background-color:#F8981D;
|
||||
color:#253441;
|
||||
margin: auto 5px;
|
||||
}
|
||||
.skipNav {
|
||||
position:absolute;
|
||||
top:auto;
|
||||
left:-9999px;
|
||||
overflow:hidden;
|
||||
}
|
||||
/*
|
||||
Page header and footer styles
|
||||
*/
|
||||
.header, .footer {
|
||||
clear:both;
|
||||
margin:0 20px;
|
||||
padding:5px 0 0 0;
|
||||
}
|
||||
.indexHeader {
|
||||
margin:10px;
|
||||
position:relative;
|
||||
}
|
||||
.indexHeader span{
|
||||
margin-right:15px;
|
||||
}
|
||||
.indexHeader h1 {
|
||||
font-size:13px;
|
||||
}
|
||||
.title {
|
||||
color:#2c4557;
|
||||
margin:10px 0;
|
||||
}
|
||||
.subTitle {
|
||||
margin:5px 0 0 0;
|
||||
}
|
||||
.header ul {
|
||||
margin:0 0 15px 0;
|
||||
padding:0;
|
||||
}
|
||||
.footer ul {
|
||||
margin:20px 0 5px 0;
|
||||
}
|
||||
.header ul li, .footer ul li {
|
||||
list-style:none;
|
||||
font-size:13px;
|
||||
}
|
||||
/*
|
||||
Heading styles
|
||||
*/
|
||||
div.details ul.blockList ul.blockList ul.blockList li.blockList h4, div.details ul.blockList ul.blockList ul.blockListLast li.blockList h4 {
|
||||
background-color:#dee3e9;
|
||||
border:1px solid #d0d9e0;
|
||||
margin:0 0 6px -8px;
|
||||
padding:7px 5px;
|
||||
}
|
||||
ul.blockList ul.blockList ul.blockList li.blockList h3 {
|
||||
background-color:#dee3e9;
|
||||
border:1px solid #d0d9e0;
|
||||
margin:0 0 6px -8px;
|
||||
padding:7px 5px;
|
||||
}
|
||||
ul.blockList ul.blockList li.blockList h3 {
|
||||
padding:0;
|
||||
margin:15px 0;
|
||||
}
|
||||
ul.blockList li.blockList h2 {
|
||||
padding:0px 0 20px 0;
|
||||
}
|
||||
/*
|
||||
Page layout container styles
|
||||
*/
|
||||
.contentContainer, .sourceContainer, .classUseContainer, .serializedFormContainer, .constantValuesContainer {
|
||||
clear:both;
|
||||
padding:10px 20px;
|
||||
position:relative;
|
||||
}
|
||||
.indexContainer {
|
||||
margin:10px;
|
||||
position:relative;
|
||||
font-size:12px;
|
||||
}
|
||||
.indexContainer h2 {
|
||||
font-size:13px;
|
||||
padding:0 0 3px 0;
|
||||
}
|
||||
.indexContainer ul {
|
||||
margin:0;
|
||||
padding:0;
|
||||
}
|
||||
.indexContainer ul li {
|
||||
list-style:none;
|
||||
padding-top:2px;
|
||||
}
|
||||
.contentContainer .description dl dt, .contentContainer .details dl dt, .serializedFormContainer dl dt {
|
||||
font-size:12px;
|
||||
font-weight:bold;
|
||||
margin:10px 0 0 0;
|
||||
color:#4E4E4E;
|
||||
}
|
||||
.contentContainer .description dl dd, .contentContainer .details dl dd, .serializedFormContainer dl dd {
|
||||
margin:5px 0 10px 0px;
|
||||
font-size:14px;
|
||||
font-family:'DejaVu Sans Mono',monospace;
|
||||
}
|
||||
.serializedFormContainer dl.nameValue dt {
|
||||
margin-left:1px;
|
||||
font-size:1.1em;
|
||||
display:inline;
|
||||
font-weight:bold;
|
||||
}
|
||||
.serializedFormContainer dl.nameValue dd {
|
||||
margin:0 0 0 1px;
|
||||
font-size:1.1em;
|
||||
display:inline;
|
||||
}
|
||||
/*
|
||||
List styles
|
||||
*/
|
||||
ul.horizontal li {
|
||||
display:inline;
|
||||
font-size:0.9em;
|
||||
}
|
||||
ul.inheritance {
|
||||
margin:0;
|
||||
padding:0;
|
||||
}
|
||||
ul.inheritance li {
|
||||
display:inline;
|
||||
list-style:none;
|
||||
}
|
||||
ul.inheritance li ul.inheritance {
|
||||
margin-left:15px;
|
||||
padding-left:15px;
|
||||
padding-top:1px;
|
||||
}
|
||||
ul.blockList, ul.blockListLast {
|
||||
margin:10px 0 10px 0;
|
||||
padding:0;
|
||||
}
|
||||
ul.blockList li.blockList, ul.blockListLast li.blockList {
|
||||
list-style:none;
|
||||
margin-bottom:15px;
|
||||
line-height:1.4;
|
||||
}
|
||||
ul.blockList ul.blockList li.blockList, ul.blockList ul.blockListLast li.blockList {
|
||||
padding:0px 20px 5px 10px;
|
||||
border:1px solid #ededed;
|
||||
background-color:#f8f8f8;
|
||||
}
|
||||
ul.blockList ul.blockList ul.blockList li.blockList, ul.blockList ul.blockList ul.blockListLast li.blockList {
|
||||
padding:0 0 5px 8px;
|
||||
background-color:#ffffff;
|
||||
border:none;
|
||||
}
|
||||
ul.blockList ul.blockList ul.blockList ul.blockList li.blockList {
|
||||
margin-left:0;
|
||||
padding-left:0;
|
||||
padding-bottom:15px;
|
||||
border:none;
|
||||
}
|
||||
ul.blockList ul.blockList ul.blockList ul.blockList li.blockListLast {
|
||||
list-style:none;
|
||||
border-bottom:none;
|
||||
padding-bottom:0;
|
||||
}
|
||||
table tr td dl, table tr td dl dt, table tr td dl dd {
|
||||
margin-top:0;
|
||||
margin-bottom:1px;
|
||||
}
|
||||
/*
|
||||
Table styles
|
||||
*/
|
||||
.overviewSummary, .memberSummary, .typeSummary, .useSummary, .constantsSummary, .deprecatedSummary {
|
||||
width:100%;
|
||||
border-left:1px solid #EEE;
|
||||
border-right:1px solid #EEE;
|
||||
border-bottom:1px solid #EEE;
|
||||
}
|
||||
.overviewSummary, .memberSummary {
|
||||
padding:0px;
|
||||
}
|
||||
.overviewSummary caption, .memberSummary caption, .typeSummary caption,
|
||||
.useSummary caption, .constantsSummary caption, .deprecatedSummary caption {
|
||||
position:relative;
|
||||
text-align:left;
|
||||
background-repeat:no-repeat;
|
||||
color:#253441;
|
||||
font-weight:bold;
|
||||
clear:none;
|
||||
overflow:hidden;
|
||||
padding:0px;
|
||||
padding-top:10px;
|
||||
padding-left:1px;
|
||||
margin:0px;
|
||||
white-space:pre;
|
||||
}
|
||||
.overviewSummary caption a:link, .memberSummary caption a:link, .typeSummary caption a:link,
|
||||
.useSummary caption a:link, .constantsSummary caption a:link, .deprecatedSummary caption a:link,
|
||||
.overviewSummary caption a:hover, .memberSummary caption a:hover, .typeSummary caption a:hover,
|
||||
.useSummary caption a:hover, .constantsSummary caption a:hover, .deprecatedSummary caption a:hover,
|
||||
.overviewSummary caption a:active, .memberSummary caption a:active, .typeSummary caption a:active,
|
||||
.useSummary caption a:active, .constantsSummary caption a:active, .deprecatedSummary caption a:active,
|
||||
.overviewSummary caption a:visited, .memberSummary caption a:visited, .typeSummary caption a:visited,
|
||||
.useSummary caption a:visited, .constantsSummary caption a:visited, .deprecatedSummary caption a:visited {
|
||||
color:#FFFFFF;
|
||||
}
|
||||
.overviewSummary caption span, .memberSummary caption span, .typeSummary caption span,
|
||||
.useSummary caption span, .constantsSummary caption span, .deprecatedSummary caption span {
|
||||
white-space:nowrap;
|
||||
padding-top:5px;
|
||||
padding-left:12px;
|
||||
padding-right:12px;
|
||||
padding-bottom:7px;
|
||||
display:inline-block;
|
||||
float:left;
|
||||
background-color:#F8981D;
|
||||
border: none;
|
||||
height:16px;
|
||||
}
|
||||
.memberSummary caption span.activeTableTab span {
|
||||
white-space:nowrap;
|
||||
padding-top:5px;
|
||||
padding-left:12px;
|
||||
padding-right:12px;
|
||||
margin-right:3px;
|
||||
display:inline-block;
|
||||
float:left;
|
||||
background-color:#F8981D;
|
||||
height:16px;
|
||||
}
|
||||
.memberSummary caption span.tableTab span {
|
||||
white-space:nowrap;
|
||||
padding-top:5px;
|
||||
padding-left:12px;
|
||||
padding-right:12px;
|
||||
margin-right:3px;
|
||||
display:inline-block;
|
||||
float:left;
|
||||
background-color:#4D7A97;
|
||||
height:16px;
|
||||
}
|
||||
.memberSummary caption span.tableTab, .memberSummary caption span.activeTableTab {
|
||||
padding-top:0px;
|
||||
padding-left:0px;
|
||||
padding-right:0px;
|
||||
background-image:none;
|
||||
float:none;
|
||||
display:inline;
|
||||
}
|
||||
.overviewSummary .tabEnd, .memberSummary .tabEnd, .typeSummary .tabEnd,
|
||||
.useSummary .tabEnd, .constantsSummary .tabEnd, .deprecatedSummary .tabEnd {
|
||||
display:none;
|
||||
width:5px;
|
||||
position:relative;
|
||||
float:left;
|
||||
background-color:#F8981D;
|
||||
}
|
||||
.memberSummary .activeTableTab .tabEnd {
|
||||
display:none;
|
||||
width:5px;
|
||||
margin-right:3px;
|
||||
position:relative;
|
||||
float:left;
|
||||
background-color:#F8981D;
|
||||
}
|
||||
.memberSummary .tableTab .tabEnd {
|
||||
display:none;
|
||||
width:5px;
|
||||
margin-right:3px;
|
||||
position:relative;
|
||||
background-color:#4D7A97;
|
||||
float:left;
|
||||
|
||||
}
|
||||
.overviewSummary td, .memberSummary td, .typeSummary td,
|
||||
.useSummary td, .constantsSummary td, .deprecatedSummary td {
|
||||
text-align:left;
|
||||
padding:0px 0px 12px 10px;
|
||||
width:100%;
|
||||
}
|
||||
th.colOne, th.colFirst, th.colLast, .useSummary th, .constantsSummary th,
|
||||
td.colOne, td.colFirst, td.colLast, .useSummary td, .constantsSummary td{
|
||||
vertical-align:top;
|
||||
padding-right:0px;
|
||||
padding-top:8px;
|
||||
padding-bottom:3px;
|
||||
}
|
||||
th.colFirst, th.colLast, th.colOne, .constantsSummary th {
|
||||
background:#dee3e9;
|
||||
text-align:left;
|
||||
padding:8px 3px 3px 7px;
|
||||
}
|
||||
td.colFirst, th.colFirst {
|
||||
white-space:nowrap;
|
||||
font-size:13px;
|
||||
}
|
||||
td.colLast, th.colLast {
|
||||
font-size:13px;
|
||||
}
|
||||
td.colOne, th.colOne {
|
||||
font-size:13px;
|
||||
}
|
||||
.overviewSummary td.colFirst, .overviewSummary th.colFirst,
|
||||
.overviewSummary td.colOne, .overviewSummary th.colOne,
|
||||
.memberSummary td.colFirst, .memberSummary th.colFirst,
|
||||
.memberSummary td.colOne, .memberSummary th.colOne,
|
||||
.typeSummary td.colFirst{
|
||||
width:25%;
|
||||
vertical-align:top;
|
||||
}
|
||||
td.colOne a:link, td.colOne a:active, td.colOne a:visited, td.colOne a:hover, td.colFirst a:link, td.colFirst a:active, td.colFirst a:visited, td.colFirst a:hover, td.colLast a:link, td.colLast a:active, td.colLast a:visited, td.colLast a:hover, .constantValuesContainer td a:link, .constantValuesContainer td a:active, .constantValuesContainer td a:visited, .constantValuesContainer td a:hover {
|
||||
font-weight:bold;
|
||||
}
|
||||
.tableSubHeadingColor {
|
||||
background-color:#EEEEFF;
|
||||
}
|
||||
.altColor {
|
||||
background-color:#FFFFFF;
|
||||
}
|
||||
.rowColor {
|
||||
background-color:#EEEEEF;
|
||||
}
|
||||
/*
|
||||
Content styles
|
||||
*/
|
||||
.description pre {
|
||||
margin-top:0;
|
||||
}
|
||||
.deprecatedContent {
|
||||
margin:0;
|
||||
padding:10px 0;
|
||||
}
|
||||
.docSummary {
|
||||
padding:0;
|
||||
}
|
||||
|
||||
ul.blockList ul.blockList ul.blockList li.blockList h3 {
|
||||
font-style:normal;
|
||||
}
|
||||
|
||||
div.block {
|
||||
font-size:14px;
|
||||
font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif;
|
||||
}
|
||||
|
||||
td.colLast div {
|
||||
padding-top:0px;
|
||||
}
|
||||
|
||||
|
||||
td.colLast a {
|
||||
padding-bottom:3px;
|
||||
}
|
||||
/*
|
||||
Formatting effect styles
|
||||
*/
|
||||
.sourceLineNo {
|
||||
color:green;
|
||||
padding:0 30px 0 0;
|
||||
}
|
||||
h1.hidden {
|
||||
visibility:hidden;
|
||||
overflow:hidden;
|
||||
font-size:10px;
|
||||
}
|
||||
.block {
|
||||
display:block;
|
||||
margin:3px 10px 2px 0px;
|
||||
color:#474747;
|
||||
}
|
||||
.deprecatedLabel, .descfrmTypeLabel, .memberNameLabel, .memberNameLink,
|
||||
.overrideSpecifyLabel, .packageHierarchyLabel, .paramLabel, .returnLabel,
|
||||
.seeLabel, .simpleTagLabel, .throwsLabel, .typeNameLabel, .typeNameLink {
|
||||
font-weight:bold;
|
||||
}
|
||||
.deprecationComment, .emphasizedPhrase, .interfaceName {
|
||||
font-style:italic;
|
||||
}
|
||||
|
||||
div.block div.block span.deprecationComment, div.block div.block span.emphasizedPhrase,
|
||||
div.block div.block span.interfaceName {
|
||||
font-style:normal;
|
||||
}
|
||||
|
||||
div.contentContainer ul.blockList li.blockList h2{
|
||||
padding-bottom:0px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Spring
|
||||
*/
|
||||
|
||||
pre.code {
|
||||
background-color: #F8F8F8;
|
||||
border: 1px solid #CCCCCC;
|
||||
border-radius: 3px 3px 3px 3px;
|
||||
overflow: auto;
|
||||
padding: 10px;
|
||||
margin: 4px 20px 2px 0px;
|
||||
}
|
||||
|
||||
pre.code code, pre.code code * {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
pre.code code, pre.code code * {
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
^\Q/*\E$
|
||||
^\Q * Copyright \E20\d\d(\-20\d\d)?\Q the original author or authors.\E$
|
||||
^\Q *\E$
|
||||
^\Q * Licensed under the Apache License, Version 2.0 (the "License");\E$
|
||||
^\Q * you may not use this file except in compliance with the License.\E$
|
||||
^\Q * You may obtain a copy of the License at\E$
|
||||
^\Q *\E$
|
||||
^\Q * http://www.apache.org/licenses/LICENSE-2.0\E$
|
||||
^\Q *\E$
|
||||
^\Q * Unless required by applicable law or agreed to in writing, software\E$
|
||||
^\Q * distributed under the License is distributed on an "AS IS" BASIS,\E$
|
||||
^\Q * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\E$
|
||||
^\Q * See the License for the specific language governing permissions and\E$
|
||||
^\Q * limitations under the License.\E$
|
||||
^\Q */\E$
|
||||
^$
|
||||
^.*$
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE suppressions PUBLIC
|
||||
"-//Puppy Crawl//DTD Suppressions 1.1//EN"
|
||||
"http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
|
||||
<suppressions>
|
||||
<suppress files="package-info\.java" checks=".*" />
|
||||
<suppress files="[\\/]test[\\/]" checks="AvoidStaticImport" />
|
||||
<suppress files="[\\/]test[\\/]" checks="InnerTypeLast" />
|
||||
<suppress files="[\\/]test[\\/]" checks="Javadoc*" />
|
||||
</suppressions>
|
||||
183
spring-integration-social-twitter/src/checkstyle/checkstyle.xml
Normal file
183
spring-integration-social-twitter/src/checkstyle/checkstyle.xml
Normal file
@@ -0,0 +1,183 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.2//EN" "http://www.puppycrawl.com/dtds/configuration_1_2.dtd">
|
||||
<module name="Checker">
|
||||
|
||||
<module name="SuppressionFilter">
|
||||
<property name="file" value="src/checkstyle/checkstyle-suppressions.xml" />
|
||||
</module>
|
||||
|
||||
<!-- Root Checks -->
|
||||
<module name="RegexpHeader">
|
||||
<property name="headerFile" value="src/checkstyle/checkstyle-header.txt" />
|
||||
<property name="fileExtensions" value="java" />
|
||||
</module>
|
||||
<module name="NewlineAtEndOfFile">
|
||||
<property name="lineSeparator" value="lf"/>
|
||||
</module>
|
||||
|
||||
<!-- TreeWalker Checks -->
|
||||
<module name="TreeWalker">
|
||||
|
||||
<!-- Annotations -->
|
||||
<module name="AnnotationUseStyle">
|
||||
<property name="elementStyle" value="compact" />
|
||||
</module>
|
||||
<module name="MissingOverride" />
|
||||
<module name="PackageAnnotation" />
|
||||
<module name="AnnotationLocation">
|
||||
<property name="allowSamelineSingleParameterlessAnnotation"
|
||||
value="false" />
|
||||
</module>
|
||||
|
||||
<!-- Block Checks -->
|
||||
<module name="EmptyBlock">
|
||||
<property name="option" value="text" />
|
||||
</module>
|
||||
<module name="LeftCurly" />
|
||||
<module name="RightCurly">
|
||||
<property name="option" value="alone" />
|
||||
</module>
|
||||
<module name="NeedBraces" />
|
||||
<module name="AvoidNestedBlocks" />
|
||||
|
||||
<!-- Class Design -->
|
||||
<module name="FinalClass" />
|
||||
<module name="InterfaceIsType" />
|
||||
<module name="HideUtilityClassConstructor" />
|
||||
<module name="MutableException" />
|
||||
<module name="InnerTypeLast" />
|
||||
<module name="OneTopLevelClass" />
|
||||
|
||||
<!-- Coding -->
|
||||
<module name="CovariantEquals" />
|
||||
<module name="EmptyStatement" />
|
||||
<module name="EqualsHashCode" />
|
||||
<module name="InnerAssignment" />
|
||||
<module name="SimplifyBooleanExpression" />
|
||||
<module name="SimplifyBooleanReturn" />
|
||||
<module name="StringLiteralEquality" />
|
||||
<module name="NestedForDepth">
|
||||
<property name="max" value="3" />
|
||||
</module>
|
||||
<module name="NestedIfDepth">
|
||||
<property name="max" value="4" />
|
||||
</module>
|
||||
<module name="NestedTryDepth">
|
||||
<property name="max" value="3" />
|
||||
</module>
|
||||
<module name="MultipleVariableDeclarations" />
|
||||
<module name="RequireThis">
|
||||
<property name="checkMethods" value="false" />
|
||||
</module>
|
||||
<module name="OneStatementPerLine" />
|
||||
|
||||
<!-- Imports -->
|
||||
<module name="AvoidStarImport" />
|
||||
<module name="AvoidStaticImport">
|
||||
<property name="excludes"
|
||||
value="org.assertj.core.api.Assertions.*,
|
||||
org.junit.Assert.*,
|
||||
org.junit.Assume.*,
|
||||
org.junit.internal.matchers.ThrowableMessageMatcher.*,
|
||||
org.hamcrest.CoreMatchers.*,
|
||||
org.hamcrest.Matchers.*,
|
||||
org.mockito.Mockito.*,
|
||||
org.mockito.BDDMockito.*,
|
||||
org.mockito.Matchers.*" />
|
||||
</module>
|
||||
<module name="IllegalImport" />
|
||||
<module name="RedundantImport" />
|
||||
<module name="UnusedImports">
|
||||
<property name="processJavadoc" value="true" />
|
||||
</module>
|
||||
<module name="ImportOrder">
|
||||
<property name="groups" value="java,/^javax?\./,org,org.springframework,*" />
|
||||
<property name="ordered" value="true" />
|
||||
<property name="separated" value="true" />
|
||||
<property name="option" value="top" />
|
||||
<property name="sortStaticImportsAlphabetically" value="true" />
|
||||
</module>
|
||||
|
||||
<!-- Javadoc Comments -->
|
||||
<module name="JavadocType">
|
||||
<property name="scope" value="package"/>
|
||||
<property name="authorFormat" value=".+\s.+"/>
|
||||
</module>
|
||||
<module name="JavadocMethod">
|
||||
<property name="allowMissingJavadoc" value="true" />
|
||||
</module>
|
||||
<module name="JavadocVariable">
|
||||
<property name="scope" value="public"/>
|
||||
</module>
|
||||
<module name="JavadocStyle">
|
||||
<property name="checkEmptyJavadoc" value="true"/>
|
||||
</module>
|
||||
<module name="NonEmptyAtclauseDescription" />
|
||||
<module name="JavadocTagContinuationIndentation">
|
||||
<property name="offset" value="0"/>
|
||||
</module>
|
||||
<module name="AtclauseOrder">
|
||||
<property name="target" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF"/>
|
||||
<property name="tagOrder" value="@param, @author, @since, @see, @version, @serial, @deprecated"/>
|
||||
</module>
|
||||
<module name="AtclauseOrder">
|
||||
<property name="target" value="METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
|
||||
<property name="tagOrder" value="@param, @return, @throws, @since, @deprecated, @see"/>
|
||||
</module>
|
||||
|
||||
<!-- Miscellaneous -->
|
||||
<module name="CommentsIndentation">
|
||||
<property name="tokens" value="BLOCK_COMMENT_BEGIN" />
|
||||
</module>
|
||||
<module name="UpperEll" />
|
||||
<module name="ArrayTypeStyle" />
|
||||
<module name="OuterTypeFilename" />
|
||||
|
||||
<!-- Modifiers -->
|
||||
<module name="RedundantModifier" />
|
||||
|
||||
<!-- Regexp -->
|
||||
<module name="RegexpSinglelineJava">
|
||||
<property name="format" value="^\t* +\t*\S" />
|
||||
<property name="message"
|
||||
value="Line has leading space characters; indentation should be performed with tabs only." />
|
||||
<property name="ignoreComments" value="true" />
|
||||
</module>
|
||||
<module name="RegexpSinglelineJava">
|
||||
<property name="maximum" value="0"/>
|
||||
<property name="format" value="org\.mockito\..*Mockito\.(when|doThrow|doAnswer)" />
|
||||
<property name="message"
|
||||
value="Please use BDDMockito instead of Mockito.(when|doThrow|doAnswer)." />
|
||||
<property name="ignoreComments" value="true" />
|
||||
</module>
|
||||
<!--<module name="RegexpSinglelineJava">
|
||||
<property name="maximum" value="0"/>
|
||||
<property name="format" value="org\.junit\.Assert\.assert" />
|
||||
<property name="message"
|
||||
value="Please use AssertJ imports." />
|
||||
<property name="ignoreComments" value="true" />
|
||||
</module>-->
|
||||
<module name="Regexp">
|
||||
<property name="format" value="[ \t]+$" />
|
||||
<property name="illegalPattern" value="true" />
|
||||
<property name="message" value="Trailing whitespace" />
|
||||
</module>
|
||||
<module name="Regexp">
|
||||
<property name="format" value="System.(out|err).print" />
|
||||
<property name="illegalPattern" value="true" />
|
||||
<property name="message" value="System.out or .err" />
|
||||
</module>
|
||||
<!-- Whitespace -->
|
||||
<module name="GenericWhitespace" />
|
||||
<module name="MethodParamPad" />
|
||||
<module name="NoWhitespaceAfter" >
|
||||
<property name="tokens" value="BNOT, DEC, DOT, INC, LNOT, UNARY_MINUS, UNARY_PLUS, ARRAY_DECLARATOR"/>
|
||||
</module>
|
||||
<module name="NoWhitespaceBefore" />
|
||||
<module name="ParenPad" />
|
||||
<module name="TypecastParenPad" />
|
||||
<module name="WhitespaceAfter" />
|
||||
<module name="WhitespaceAround" />
|
||||
|
||||
</module>
|
||||
</module>
|
||||
201
spring-integration-social-twitter/src/dist/license.txt
vendored
Normal file
201
spring-integration-social-twitter/src/dist/license.txt
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
21
spring-integration-social-twitter/src/dist/notice.txt
vendored
Normal file
21
spring-integration-social-twitter/src/dist/notice.txt
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
========================================================================
|
||||
== NOTICE file corresponding to section 4 d of the Apache License, ==
|
||||
== Version 2.0, in this case for the Spring Integration distribution. ==
|
||||
========================================================================
|
||||
|
||||
This product includes software developed by
|
||||
the Apache Software Foundation (http://www.apache.org).
|
||||
|
||||
The end-user documentation included with a redistribution, if any,
|
||||
must include the following acknowledgement:
|
||||
|
||||
"This product includes software developed by the Spring Framework
|
||||
Project (http://www.spring.io)."
|
||||
|
||||
Alternatively, this acknowledgement may appear in the software itself,
|
||||
if and wherever such third-party acknowledgements normally appear.
|
||||
|
||||
The names "Spring", "Spring Framework", and "Spring Integration" must
|
||||
not be used to endorse or promote products derived from this software
|
||||
without prior written permission. For written permission, please contact
|
||||
enquiries@springsource.com.
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.twitter.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.springframework.integration.twitter.inbound.DirectMessageReceivingMessageSource;
|
||||
import org.springframework.integration.twitter.inbound.MentionsReceivingMessageSource;
|
||||
import org.springframework.integration.twitter.inbound.SearchReceivingMessageSource;
|
||||
import org.springframework.integration.twitter.inbound.TimelineReceivingMessageSource;
|
||||
|
||||
/**
|
||||
* Parser for inbound Twitter Channel Adapters.
|
||||
*
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
*/
|
||||
public class TwitterInboundChannelAdapterParser extends AbstractPollingInboundChannelAdapterParser {
|
||||
|
||||
@Override
|
||||
protected BeanMetadataElement parseSource(Element element, ParserContext parserContext) {
|
||||
Class<?> clazz = determineClass(element, parserContext);
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(clazz);
|
||||
builder.addConstructorArgReference(element.getAttribute("twitter-template"));
|
||||
builder.addConstructorArgValue(element.getAttribute(ID_ATTRIBUTE));
|
||||
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "query");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "page-size");
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "metadata-store");
|
||||
return builder.getBeanDefinition();
|
||||
}
|
||||
|
||||
|
||||
private static Class<?> determineClass(Element element, ParserContext parserContext) {
|
||||
Class<?> clazz = null;
|
||||
String elementName = element.getLocalName().trim();
|
||||
if ("inbound-channel-adapter".equals(elementName)) {
|
||||
clazz = TimelineReceivingMessageSource.class;
|
||||
}
|
||||
else if ("dm-inbound-channel-adapter".equals(elementName)) {
|
||||
clazz = DirectMessageReceivingMessageSource.class;
|
||||
}
|
||||
else if ("mentions-inbound-channel-adapter".equals(elementName)) {
|
||||
clazz = MentionsReceivingMessageSource.class;
|
||||
}
|
||||
else if ("search-inbound-channel-adapter".equals(elementName)) {
|
||||
clazz = SearchReceivingMessageSource.class;
|
||||
}
|
||||
else {
|
||||
parserContext.getReaderContext().error("element '" + elementName + "' is not supported by this parser.", element);
|
||||
}
|
||||
return clazz;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2018 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.integration.config.xml.AbstractIntegrationNamespaceHandler;
|
||||
|
||||
/**
|
||||
* Namespace handler for the Twitter adapters.
|
||||
*
|
||||
* @author Josh Long
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
*/
|
||||
public class TwitterNamespaceHandler extends AbstractIntegrationNamespaceHandler {
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
// inbound
|
||||
registerBeanDefinitionParser("inbound-channel-adapter", new TwitterInboundChannelAdapterParser());
|
||||
registerBeanDefinitionParser("dm-inbound-channel-adapter", new TwitterInboundChannelAdapterParser());
|
||||
registerBeanDefinitionParser("mentions-inbound-channel-adapter", new TwitterInboundChannelAdapterParser());
|
||||
registerBeanDefinitionParser("search-inbound-channel-adapter", new TwitterInboundChannelAdapterParser());
|
||||
|
||||
// outbound
|
||||
registerBeanDefinitionParser("outbound-channel-adapter", new TwitterOutboundChannelAdapterParser());
|
||||
registerBeanDefinitionParser("dm-outbound-channel-adapter", new TwitterOutboundChannelAdapterParser());
|
||||
registerBeanDefinitionParser("search-outbound-gateway", new TwitterSearchOutboundGatewayParser());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.twitter.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.config.ExpressionFactoryBean;
|
||||
import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser;
|
||||
import org.springframework.integration.twitter.outbound.DirectMessageSendingMessageHandler;
|
||||
import org.springframework.integration.twitter.outbound.StatusUpdatingMessageHandler;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Parser for all outbound Twitter adapters.
|
||||
*
|
||||
* @author Josh Long
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
public class TwitterOutboundChannelAdapterParser extends AbstractOutboundChannelAdapterParser {
|
||||
|
||||
@Override
|
||||
protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) {
|
||||
Class<?> clazz = determineClass(element, parserContext);
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(clazz);
|
||||
builder.addConstructorArgReference(element.getAttribute("twitter-template"));
|
||||
String tweetDataExpression = element.getAttribute("tweet-data-expression");
|
||||
if (StringUtils.hasText(tweetDataExpression)) {
|
||||
builder.addPropertyValue("tweetDataExpression",
|
||||
BeanDefinitionBuilder.genericBeanDefinition(ExpressionFactoryBean.class)
|
||||
.addConstructorArgValue(tweetDataExpression)
|
||||
.getBeanDefinition());
|
||||
}
|
||||
return builder.getBeanDefinition();
|
||||
}
|
||||
|
||||
|
||||
private static Class<?> determineClass(Element element, ParserContext parserContext) {
|
||||
Class<?> clazz = null;
|
||||
String elementName = element.getLocalName().trim();
|
||||
if ("outbound-channel-adapter".equals(elementName)) {
|
||||
clazz = StatusUpdatingMessageHandler.class;
|
||||
}
|
||||
else if ("dm-outbound-channel-adapter".equals(elementName)) {
|
||||
clazz = DirectMessageSendingMessageHandler.class;
|
||||
}
|
||||
else {
|
||||
parserContext.getReaderContext().error("element '" + elementName + "' is not supported by this parser.", element);
|
||||
}
|
||||
return clazz;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.twitter.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.config.ExpressionFactoryBean;
|
||||
import org.springframework.integration.config.xml.AbstractConsumerEndpointParser;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.springframework.integration.twitter.outbound.TwitterSearchOutboundGateway;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Parser for {@code <int-twitter:search-outbound-gateway/>}.
|
||||
*
|
||||
* @author Gary Russell
|
||||
*/
|
||||
public class TwitterSearchOutboundGatewayParser extends AbstractConsumerEndpointParser {
|
||||
|
||||
@Override
|
||||
protected String getInputChannelAttributeName() {
|
||||
return "request-channel";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(TwitterSearchOutboundGateway.class);
|
||||
builder.addConstructorArgReference(element.getAttribute("twitter-template"));
|
||||
String searchArgsExpression = element.getAttribute("search-args-expression");
|
||||
if (StringUtils.hasText(searchArgsExpression)) {
|
||||
BeanDefinition expressionDef = new RootBeanDefinition(ExpressionFactoryBean.class);
|
||||
expressionDef.getConstructorArgumentValues().addGenericArgumentValue(searchArgsExpression);
|
||||
builder.addPropertyValue("searchArgsExpression", expressionDef);
|
||||
}
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "reply-channel", "outputChannel");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "reply-timeout", "sendTimeout");
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Contains parser classes for the Twitter namespace support.
|
||||
*/
|
||||
package org.springframework.integration.twitter.config;
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.twitter.core;
|
||||
|
||||
/**
|
||||
* Header keys used by the various Twitter adapters.
|
||||
*
|
||||
* @author Josh Long
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
public final class TwitterHeaders {
|
||||
|
||||
private static final String PREFIX = "twitter_";
|
||||
|
||||
/**
|
||||
* The name of the header for the target user for DM.
|
||||
*/
|
||||
public static final String DM_TARGET_USER_ID = PREFIX + "dmTargetUserId";
|
||||
|
||||
/**
|
||||
* The name of the header for the search metadata.
|
||||
*/
|
||||
public static final String SEARCH_METADATA = PREFIX + "searchMetadata";
|
||||
|
||||
private TwitterHeaders() { }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Provides classes used across all Twitter components.
|
||||
*/
|
||||
package org.springframework.integration.twitter.core;
|
||||
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
* Copyright 2018 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.inbound;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.integration.context.IntegrationContextUtils;
|
||||
import org.springframework.integration.context.IntegrationObjectSupport;
|
||||
import org.springframework.integration.core.MessageSource;
|
||||
import org.springframework.integration.metadata.MetadataStore;
|
||||
import org.springframework.integration.metadata.SimpleMetadataStore;
|
||||
import org.springframework.jmx.export.annotation.ManagedAttribute;
|
||||
import org.springframework.jmx.export.annotation.ManagedOperation;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.social.twitter.api.DirectMessage;
|
||||
import org.springframework.social.twitter.api.Tweet;
|
||||
import org.springframework.social.twitter.api.Twitter;
|
||||
import org.springframework.social.twitter.api.UserOperations;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Abstract class that defines common operations for receiving various types of
|
||||
* messages when using the Twitter API. This class also handles keeping track of
|
||||
* the latest inbound message it has received and avoiding, where possible,
|
||||
* redelivery of duplicate messages. This functionality is enabled using the
|
||||
* {@link org.springframework.integration.metadata.MetadataStore} strategy.
|
||||
*
|
||||
* @param <T> the target twitter object type.
|
||||
*
|
||||
* @author Josh Long
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Mark Fisher
|
||||
* @author Gunnar Hillert
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
abstract class AbstractTwitterMessageSource<T> extends IntegrationObjectSupport implements MessageSource<T>,
|
||||
Lifecycle {
|
||||
|
||||
private static final int DEFAULT_PAGE_SIZE = 20;
|
||||
|
||||
private final Twitter twitter;
|
||||
|
||||
private final TweetComparator tweetComparator = new TweetComparator();
|
||||
|
||||
private final Object lastEnqueuedIdMonitor = new Object();
|
||||
|
||||
private final String metadataKey;
|
||||
|
||||
private final Queue<T> tweets = new LinkedBlockingQueue<T>();
|
||||
|
||||
private volatile MetadataStore metadataStore;
|
||||
|
||||
private volatile int prefetchThreshold = 0;
|
||||
|
||||
private volatile long lastEnqueuedId = -1;
|
||||
|
||||
private volatile long lastProcessedId = -1;
|
||||
|
||||
private volatile int pageSize = DEFAULT_PAGE_SIZE;
|
||||
|
||||
private volatile boolean running;
|
||||
|
||||
protected AbstractTwitterMessageSource(Twitter twitter, String metadataKey) {
|
||||
Assert.notNull(twitter, "twitter must not be null");
|
||||
Assert.notNull(metadataKey, "metadataKey must not be null");
|
||||
this.twitter = twitter;
|
||||
if (this.twitter.isAuthorized()) {
|
||||
UserOperations userOperations = this.twitter.userOperations();
|
||||
metadataKey += "." + userOperations.getProfileId();
|
||||
}
|
||||
this.metadataKey = metadataKey;
|
||||
}
|
||||
|
||||
|
||||
public void setMetadataStore(MetadataStore metadataStore) {
|
||||
this.metadataStore = metadataStore;
|
||||
}
|
||||
|
||||
public void setPrefetchThreshold(int prefetchThreshold) {
|
||||
this.prefetchThreshold = prefetchThreshold;
|
||||
}
|
||||
|
||||
protected Twitter getTwitter() {
|
||||
return this.twitter;
|
||||
}
|
||||
|
||||
protected int getPageSize() {
|
||||
return this.pageSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the limit for the number of results returned on each poll; default 20.
|
||||
* @param pageSize The pageSize.
|
||||
*/
|
||||
public void setPageSize(int pageSize) {
|
||||
this.pageSize = pageSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() throws Exception {
|
||||
super.onInit();
|
||||
if (this.metadataStore == null) {
|
||||
// first try to look for a 'metadataStore' in the context
|
||||
BeanFactory beanFactory = this.getBeanFactory();
|
||||
if (beanFactory != null) {
|
||||
this.metadataStore = IntegrationContextUtils.getMetadataStore(beanFactory);
|
||||
}
|
||||
if (this.metadataStore == null) {
|
||||
this.metadataStore = new SimpleMetadataStore();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void start() {
|
||||
if (!this.running) {
|
||||
String lastId = this.metadataStore.get(this.metadataKey);
|
||||
// initialize the last status ID from the metadataStore
|
||||
if (StringUtils.hasText(lastId)) {
|
||||
this.lastProcessedId = Long.parseLong(lastId);
|
||||
synchronized (this.lastEnqueuedIdMonitor) {
|
||||
this.lastEnqueuedId = this.lastProcessedId;
|
||||
}
|
||||
}
|
||||
this.running = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void stop() {
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message<T> receive() {
|
||||
T tweet = this.tweets.poll();
|
||||
if (tweet == null) {
|
||||
this.refreshTweetQueueIfNecessary();
|
||||
tweet = this.tweets.poll();
|
||||
}
|
||||
|
||||
if (tweet != null) {
|
||||
this.lastProcessedId = this.getIdForTweet(tweet);
|
||||
this.metadataStore.put(this.metadataKey, String.valueOf(this.lastProcessedId));
|
||||
return this.getMessageBuilderFactory().withPayload(tweet).build();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void enqueueAll(List<T> tweets) {
|
||||
tweets.sort(this.tweetComparator);
|
||||
tweets.forEach(this::enqueue);
|
||||
}
|
||||
|
||||
private void enqueue(T tweet) {
|
||||
synchronized (this.lastEnqueuedIdMonitor) {
|
||||
long id = this.getIdForTweet(tweet);
|
||||
if (id > this.lastEnqueuedId) {
|
||||
this.tweets.add(tweet);
|
||||
synchronized (this.lastEnqueuedIdMonitor) {
|
||||
this.lastEnqueuedId = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshTweetQueueIfNecessary() {
|
||||
try {
|
||||
if (this.tweets.size() <= this.prefetchThreshold) {
|
||||
synchronized (this.lastEnqueuedIdMonitor) {
|
||||
List<T> tweets = pollForTweets(this.lastEnqueuedId);
|
||||
if (!CollectionUtils.isEmpty(tweets)) {
|
||||
enqueueAll(tweets);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new MessagingException("failed while polling Twitter", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must implement this to return tweets.
|
||||
* The 'sinceId' value will be negative if no last id is known.
|
||||
*
|
||||
* @param sinceId The id of the last reported tweet.
|
||||
* @return The list of tweets.
|
||||
*/
|
||||
protected abstract List<T> pollForTweets(long sinceId);
|
||||
|
||||
|
||||
private long getIdForTweet(T twitterMessage) {
|
||||
if (twitterMessage instanceof Tweet) {
|
||||
return ((Tweet) twitterMessage).getId();
|
||||
}
|
||||
else if (twitterMessage instanceof DirectMessage) {
|
||||
return ((DirectMessage) twitterMessage).getId();
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unsupported Twitter object: " + twitterMessage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove the metadata key and the corresponding value from the Metadata Store.
|
||||
*/
|
||||
@ManagedOperation(description = "Remove the metadata key and the corresponding value from the Metadata Store.")
|
||||
void resetMetadataStore() {
|
||||
synchronized (this) {
|
||||
this.metadataStore.remove(this.metadataKey);
|
||||
this.lastProcessedId = -1L;
|
||||
synchronized (this.lastEnqueuedIdMonitor) {
|
||||
this.lastEnqueuedId = -1L;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the last processed id.
|
||||
* @return {@code -1} if lastProcessedId is not set, yet.
|
||||
*/
|
||||
@ManagedAttribute
|
||||
public long getLastProcessedId() {
|
||||
return this.lastProcessedId;
|
||||
}
|
||||
|
||||
private class TweetComparator implements Comparator<T> {
|
||||
|
||||
TweetComparator() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(T tweet1, T tweet2) {
|
||||
// hopefully temporary logic. Will suggest that SpringSocial use a common base class for DM and Tweet
|
||||
if (tweet1 instanceof Tweet && tweet2 instanceof Tweet) {
|
||||
Tweet t1 = (Tweet) tweet1;
|
||||
Tweet t2 = (Tweet) tweet2;
|
||||
Date t1CreatedAt = t1.getCreatedAt();
|
||||
Date t2CreatedAt = t2.getCreatedAt();
|
||||
Assert.notNull(t1CreatedAt, "Tweet is missing 'createdAt' date. Cannot compare.");
|
||||
Assert.notNull(t2CreatedAt, "Tweet is missing 'createdAt' date. Cannot compare.");
|
||||
return t1CreatedAt.compareTo(t2CreatedAt);
|
||||
}
|
||||
else if (tweet1 instanceof DirectMessage && tweet2 instanceof DirectMessage) {
|
||||
DirectMessage d1 = (DirectMessage) tweet1;
|
||||
DirectMessage d2 = (DirectMessage) tweet2;
|
||||
Date d1CreatedAt = d1.getCreatedAt();
|
||||
Date d2CreatedAt = d2.getCreatedAt();
|
||||
Assert.notNull(d1CreatedAt, "DirectMessage is missing 'createdAt' date. Cannot compare.");
|
||||
Assert.notNull(d2CreatedAt, "DirectMessage is missing 'createdAt' date. Cannot compare.");
|
||||
return d1CreatedAt.compareTo(d2CreatedAt);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Uncomparable Twitter objects: " + tweet1 + " and " + tweet2);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2018 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.inbound;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.social.twitter.api.DirectMessage;
|
||||
import org.springframework.social.twitter.api.Twitter;
|
||||
|
||||
/**
|
||||
* This class handles support for receiving DMs (direct messages) using Twitter.
|
||||
*
|
||||
* @author Josh Long
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Mark Fisher
|
||||
* @author Gary Russell
|
||||
*/
|
||||
public class DirectMessageReceivingMessageSource extends AbstractTwitterMessageSource<DirectMessage> {
|
||||
|
||||
public DirectMessageReceivingMessageSource(Twitter twitter, String metadataKey) {
|
||||
super(twitter, metadataKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComponentType() {
|
||||
return "twitter:dm-inbound-channel-adapter";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<DirectMessage> pollForTweets(long sinceId) {
|
||||
return this.getTwitter().directMessageOperations().getDirectMessagesReceived(1, this.getPageSize(), sinceId, 0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2018 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.inbound;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.social.twitter.api.Tweet;
|
||||
import org.springframework.social.twitter.api.Twitter;
|
||||
|
||||
/**
|
||||
* Receives Message Tweets.
|
||||
*
|
||||
* @author Josh Long
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
*/
|
||||
public class MentionsReceivingMessageSource extends AbstractTwitterMessageSource<Tweet> {
|
||||
|
||||
public MentionsReceivingMessageSource(Twitter twitter, String metadataKey) {
|
||||
super(twitter, metadataKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComponentType() {
|
||||
return "twitter:mentions-inbound-channel-adapter";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Tweet> pollForTweets(long sinceId) {
|
||||
return this.getTwitter().timelineOperations().getMentions(this.getPageSize(), sinceId, 0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2018 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.inbound;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.social.twitter.api.SearchParameters;
|
||||
import org.springframework.social.twitter.api.SearchResults;
|
||||
import org.springframework.social.twitter.api.Tweet;
|
||||
import org.springframework.social.twitter.api.Twitter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* The {@link AbstractTwitterMessageSource} implementation for search.
|
||||
*
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Mark Fisher
|
||||
* @author Gunnar Hillert
|
||||
* @author Gary Russell
|
||||
*/
|
||||
public class SearchReceivingMessageSource extends AbstractTwitterMessageSource<Tweet> {
|
||||
|
||||
private volatile String query;
|
||||
|
||||
|
||||
public SearchReceivingMessageSource(Twitter twitter, String metadataKey) {
|
||||
super(twitter, metadataKey);
|
||||
}
|
||||
|
||||
public void setQuery(String query) {
|
||||
Assert.hasText(query, "'query' must not be null");
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComponentType() {
|
||||
return "twitter:search-inbound-channel-adapter";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Tweet> pollForTweets(long sinceId) {
|
||||
SearchParameters searchParameters = new SearchParameters(this.query).count(this.getPageSize()).sinceId(sinceId);
|
||||
SearchResults results = this.getTwitter().searchOperations().search(searchParameters);
|
||||
return (results != null) ? results.getTweets() : Collections.<Tweet>emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2018 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.inbound;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.social.twitter.api.Tweet;
|
||||
import org.springframework.social.twitter.api.Twitter;
|
||||
|
||||
/**
|
||||
* This {@link org.springframework.integration.core.MessageSource} lets Spring Integration consume
|
||||
* given account's timeline as messages. It has support for dynamic throttling of API requests.
|
||||
*
|
||||
* @author Josh Long
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
*/
|
||||
public class TimelineReceivingMessageSource extends AbstractTwitterMessageSource<Tweet> {
|
||||
|
||||
public TimelineReceivingMessageSource(Twitter twitter, String metadataKey) {
|
||||
super(twitter, metadataKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComponentType() {
|
||||
return "twitter:inbound-channel-adapter";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Tweet> pollForTweets(long sinceId) {
|
||||
return this.getTwitter().timelineOperations().getHomeTimeline(this.getPageSize(), sinceId, 0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Provides inbound Twitter components.
|
||||
*/
|
||||
package org.springframework.integration.twitter.inbound;
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.twitter.outbound;
|
||||
|
||||
import org.springframework.integration.handler.AbstractMessageHandler;
|
||||
import org.springframework.integration.twitter.core.TwitterHeaders;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.social.twitter.api.Twitter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Simple adapter to support sending outbound direct messages ("DM"s) using Twitter.
|
||||
*
|
||||
* @author Josh Long
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class DirectMessageSendingMessageHandler extends AbstractMessageHandler {
|
||||
|
||||
private final Twitter twitter;
|
||||
|
||||
|
||||
public DirectMessageSendingMessageHandler(Twitter twitter) {
|
||||
Assert.notNull(twitter, "twitter must not be null");
|
||||
this.twitter = twitter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComponentType() {
|
||||
return "twitter:dm-outbound-channel-adapter";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleMessageInternal(Message<?> message) throws Exception {
|
||||
Assert.isTrue(message.getPayload() instanceof String, "Only payload of type String is supported. " +
|
||||
"Consider adding a transformer to the message flow in front of this adapter.");
|
||||
Object toUser = message.getHeaders().get(TwitterHeaders.DM_TARGET_USER_ID);
|
||||
Assert.isTrue(toUser instanceof String || toUser instanceof Number,
|
||||
"the header '" + TwitterHeaders.DM_TARGET_USER_ID +
|
||||
"' must contain either a String (a screenname) or an number (a user ID)");
|
||||
String payload = (String) message.getPayload();
|
||||
if (toUser instanceof Number) {
|
||||
this.twitter.directMessageOperations().sendDirectMessage(((Number) toUser).longValue(), payload);
|
||||
}
|
||||
else if (toUser instanceof String) {
|
||||
this.twitter.directMessageOperations().sendDirectMessage((String) toUser, payload);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.twitter.outbound;
|
||||
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.TypeLocator;
|
||||
import org.springframework.expression.spel.support.StandardTypeLocator;
|
||||
import org.springframework.integration.expression.ExpressionUtils;
|
||||
import org.springframework.integration.handler.AbstractMessageHandler;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHandlingException;
|
||||
import org.springframework.social.twitter.api.Tweet;
|
||||
import org.springframework.social.twitter.api.TweetData;
|
||||
import org.springframework.social.twitter.api.Twitter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* MessageHandler for sending regular status updates as well as 'replies' or 'mentions'.
|
||||
*
|
||||
* @author Josh Long
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
public class StatusUpdatingMessageHandler extends AbstractMessageHandler {
|
||||
|
||||
private final Twitter twitter;
|
||||
|
||||
private volatile Expression tweetDataExpression;
|
||||
|
||||
private EvaluationContext evaluationContext;
|
||||
|
||||
public StatusUpdatingMessageHandler(Twitter twitter) {
|
||||
Assert.notNull(twitter, "twitter must not be null");
|
||||
this.twitter = twitter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComponentType() {
|
||||
return "twitter:outbound-channel-adapter";
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that is used to build the {@link TweetData}; must resolve to a
|
||||
* {@link TweetData} object, or a {@link String}, or a {@link Tweet}.
|
||||
* <p> When using a {@code TweetData} directly in the expression, it is not necessary
|
||||
* to include the package:
|
||||
* {@code "new TweetData("test").withMedia(headers.mediaResource).displayCoordinates(true)")}.
|
||||
* @param tweetDataExpression The expression.
|
||||
* @since 4.0
|
||||
*/
|
||||
public void setTweetDataExpression(Expression tweetDataExpression) {
|
||||
this.tweetDataExpression = tweetDataExpression;
|
||||
}
|
||||
|
||||
public void setIntegrationEvaluationContext(EvaluationContext evaluationContext) {
|
||||
this.evaluationContext = evaluationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() throws Exception {
|
||||
super.onInit();
|
||||
|
||||
if (this.evaluationContext == null) {
|
||||
this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(getBeanFactory());
|
||||
|
||||
TypeLocator typeLocator = this.evaluationContext.getTypeLocator();
|
||||
if (typeLocator instanceof StandardTypeLocator) {
|
||||
/*
|
||||
* Register the twitter api package so they don't need a FQCN for TweetData.
|
||||
*/
|
||||
((StandardTypeLocator) typeLocator).registerImport("org.springframework.social.twitter.api");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleMessageInternal(Message<?> message) throws Exception {
|
||||
Object value;
|
||||
if (this.tweetDataExpression != null) {
|
||||
value = this.tweetDataExpression.getValue(this.evaluationContext, message);
|
||||
}
|
||||
else {
|
||||
value = message.getPayload();
|
||||
}
|
||||
Assert.notNull(value, "The tweetData cannot evaluate to 'null'.");
|
||||
|
||||
TweetData tweetData = null;
|
||||
|
||||
if (value instanceof TweetData) {
|
||||
tweetData = (TweetData) value;
|
||||
}
|
||||
else if (value instanceof Tweet) {
|
||||
tweetData = new TweetData(((Tweet) value).getText());
|
||||
}
|
||||
else if (value instanceof String) {
|
||||
tweetData = new TweetData((String) value);
|
||||
}
|
||||
else {
|
||||
throw new MessageHandlingException(message, "Unsupported tweetData: " + value);
|
||||
}
|
||||
|
||||
this.twitter.timelineOperations().updateStatus(tweetData);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.twitter.outbound;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.TypeLocator;
|
||||
import org.springframework.expression.spel.support.StandardTypeLocator;
|
||||
import org.springframework.integration.expression.ExpressionUtils;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.twitter.core.TwitterHeaders;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.social.twitter.api.SearchParameters;
|
||||
import org.springframework.social.twitter.api.SearchResults;
|
||||
import org.springframework.social.twitter.api.Tweet;
|
||||
import org.springframework.social.twitter.api.Twitter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* The {@link AbstractReplyProducingMessageHandler} implementation to perform request/reply
|
||||
* Twitter search with {@link SearchParameters} as the result of {@link #searchArgsExpression}
|
||||
* expression evaluation.
|
||||
*
|
||||
* @author Gary Russell
|
||||
*/
|
||||
public class TwitterSearchOutboundGateway extends AbstractReplyProducingMessageHandler {
|
||||
|
||||
private static final int DEFAULT_PAGE_SIZE = 20;
|
||||
|
||||
private final Twitter twitter;
|
||||
|
||||
private volatile Expression searchArgsExpression;
|
||||
|
||||
private volatile EvaluationContext evaluationContext;
|
||||
|
||||
public TwitterSearchOutboundGateway(Twitter twitter) {
|
||||
Assert.notNull(twitter, "'twitter' must not be null");
|
||||
this.twitter = twitter;
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that is used to build the search; must resolve to a
|
||||
* {@code SearchParameters} object, or a
|
||||
* {@link String}, in which case the default page size of 20 is applied,
|
||||
* or a list of up to 4 arguments, such as
|
||||
* {@code "{payload, headers.pageSize, headers.sinceId, headers.maxId}"}.
|
||||
* The first (required) argument must resolve to a String (query), the
|
||||
* optional arguments must resolve to an Number and represent the
|
||||
* page size, sinceId, and maxId respectively. Refer to the 'Spring
|
||||
* Social Twitter' documentation for more details.
|
||||
* <p> When using a {@code SearchParameters} directly, it is not necessary
|
||||
* to include the package: {@code "new SearchParameters("#foo").count(20)")}.
|
||||
* <p> Default: {@code "payload"}.
|
||||
* @param searchArgsExpression The expression.
|
||||
*/
|
||||
public void setSearchArgsExpression(Expression searchArgsExpression) {
|
||||
Assert.notNull(searchArgsExpression, "'searchArgsExpression' must not be null");
|
||||
this.searchArgsExpression = searchArgsExpression;
|
||||
}
|
||||
|
||||
public void setIntegrationEvaluationContext(EvaluationContext evaluationContext) {
|
||||
this.evaluationContext = evaluationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComponentType() {
|
||||
return "twitter:search-outbound-gateway";
|
||||
}
|
||||
|
||||
protected Twitter getTwitter() {
|
||||
return this.twitter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInit() {
|
||||
super.doInit();
|
||||
if (this.evaluationContext == null) {
|
||||
this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(getBeanFactory());
|
||||
TypeLocator typeLocator = this.evaluationContext.getTypeLocator();
|
||||
if (typeLocator instanceof StandardTypeLocator) {
|
||||
/*
|
||||
* Register the twitter api package so they don't need a FQCN for SearchParameters.
|
||||
*/
|
||||
((StandardTypeLocator) typeLocator).registerImport("org.springframework.social.twitter.api");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object handleRequestMessage(Message<?> requestMessage) {
|
||||
Object args;
|
||||
if (this.searchArgsExpression != null) {
|
||||
args = this.searchArgsExpression.getValue(this.evaluationContext, requestMessage);
|
||||
}
|
||||
else {
|
||||
args = requestMessage.getPayload();
|
||||
}
|
||||
Assert.notNull(args, "The twitter search expression cannot evaluate to 'null'.");
|
||||
SearchParameters searchParameters;
|
||||
if (args instanceof SearchParameters) {
|
||||
searchParameters = (SearchParameters) args;
|
||||
}
|
||||
else if (args instanceof String) {
|
||||
searchParameters = new SearchParameters((String) args).count(DEFAULT_PAGE_SIZE);
|
||||
}
|
||||
else if (args instanceof List) {
|
||||
List<?> list = (List<?>) args;
|
||||
Assert.isTrue(list.size() > 0 && list.size() < 5, "Between 1 and 4 search arguments are required");
|
||||
Assert.isInstanceOf(String.class, list.get(0), "The first search argument (query) must be a String");
|
||||
searchParameters = new SearchParameters((String) list.get(0));
|
||||
if (list.size() > 1) {
|
||||
Assert.isInstanceOf(Number.class, list.get(1),
|
||||
"The second search argument (pageSize) must be a Number");
|
||||
searchParameters.count(((Number) list.get(1)).intValue());
|
||||
if (list.size() > 2) {
|
||||
Assert.isInstanceOf(Number.class, list.get(2),
|
||||
"The third search argument (sinceId) must be a Number");
|
||||
searchParameters.sinceId(((Number) list.get(2)).longValue());
|
||||
}
|
||||
if (list.size() > 3) {
|
||||
Assert.isInstanceOf(Number.class, list.get(3),
|
||||
"The fourth search argument (maxId) must be a Number");
|
||||
searchParameters.maxId(((Number) list.get(3)).longValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(
|
||||
"Search Expression must evaluate to a 'SearchParameters', 'String' or 'List'.");
|
||||
}
|
||||
SearchResults results = this.getTwitter().searchOperations().search(searchParameters);
|
||||
if (results != null) {
|
||||
List<Tweet> tweets = (results.getTweets() != null ? results.getTweets() : Collections.<Tweet>emptyList());
|
||||
return this.getMessageBuilderFactory().withPayload(tweets)
|
||||
.setHeader(TwitterHeaders.SEARCH_METADATA, results.getSearchMetadata());
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Provides outbound Twitter components.
|
||||
*/
|
||||
package org.springframework.integration.twitter.outbound;
|
||||
@@ -0,0 +1,2 @@
|
||||
http\://www.springframework.org/schema/integration/twitter=org.springframework.integration.twitter.config.TwitterNamespaceHandler
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
http\://www.springframework.org/schema/integration/twitter/spring-integration-social-twitter-1.0.xsd=org/springframework/integration/twitter/config/spring-integration-social-twitter-1.0.xsd
|
||||
http\://www.springframework.org/schema/integration/twitter/spring-integration-social-twitter.xsd=org/springframework/integration/twitter/config/spring-integration-social-twitter-1.0.xsd
|
||||
@@ -0,0 +1,4 @@
|
||||
# Tooling related information for the integration twitter namespace
|
||||
http\://www.springframework.org/schema/integration/twitter@name=integration twitter Namespace
|
||||
http\://www.springframework.org/schema/integration/twitter@prefix=int-twitter
|
||||
http\://www.springframework.org/schema/integration/twitter@icon=org/springframework/integration/twitter/config/spring-integration-twitter.gif
|
||||
@@ -0,0 +1,322 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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-5.1.xsd"/>
|
||||
|
||||
<!--
|
||||
INBOUND
|
||||
-->
|
||||
|
||||
<xsd:element name="inbound-channel-adapter">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines a Polling Channel Adapter for the
|
||||
'org.springframework.integration.twitter.inbound.TimelineReceivingMessageSource' that consumes your
|
||||
friends' timeline updates from Twitter and sends Messages whose payloads are Tweet objects.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType >
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="inbound-twitter-type"/>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="mentions-inbound-channel-adapter">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines a Polling Channel Adapter for the
|
||||
'org.springframework.integration.twitter.inbound.MentionsReceivingMessageSource' that consumes mentions
|
||||
of your handle from Twitter and sends Messages whose payloads are Tweet objects.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="inbound-twitter-type"/>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
|
||||
<xsd:element name="search-inbound-channel-adapter">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines a Polling Channel Adapter for the
|
||||
'org.springframework.integration.twitter.inbound.SearchReceivingMessageSource' that consumes search
|
||||
results for a given query from Twitter and sends Messages whose payloads are Tweet objects.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="inbound-twitter-type">
|
||||
<xsd:attribute name="query" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Twitter search query (e.g, #springintegration).
|
||||
For more info on Twitter queries please refer to this site: http://search.twitter.com/operators)
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="dm-inbound-channel-adapter">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines a Polling Channel Adapter for the
|
||||
'org.springframework.integration.twitter.inbound.DirectMessageReceivingMessageSource' that consumes
|
||||
direct messages from Twitter and sends Messages whose payloads are DirectMessage objects.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="inbound-twitter-type"/>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<!--
|
||||
OUTBOUND
|
||||
-->
|
||||
|
||||
<xsd:element name="dm-outbound-channel-adapter">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Configures a Consumer Endpoint for the
|
||||
'org.springframework.integration.twitter.outbound.DirectMessageSendingMessageHandler'
|
||||
that sends Direct Messages to a Twitter user as
|
||||
specified in the header whose name is defined by the TwitterHeaders.DM_TARGET_USER_ID constant.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="outbound-twitter-type">
|
||||
<xsd:attributeGroup ref="integration:channelAdapterAttributes"/>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
|
||||
<xsd:element name="outbound-channel-adapter">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Configures a Consumer Endpoint for the
|
||||
'org.springframework.integration.twitter.outbound.StatusUpdatingMessageHandler'
|
||||
that posts a status update to the authorized user's timeline.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="outbound-twitter-type">
|
||||
<xsd:attribute name="tweet-data-expression" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
A SpEL expression that evaluates to tweetData; the evaluation result type can be
|
||||
an 'org.springframework.social.twitter.api.TweetData', a 'String' or
|
||||
'org.springframework.social.twitter.api.Tweet'.
|
||||
Default: "payload".
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attributeGroup ref="integration:channelAdapterAttributes"/>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="search-outbound-gateway">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Configures a Consumer Endpoint for the
|
||||
'org.springframework.integration.twitter.outbound.TwitterSearchOutboundGateway'
|
||||
that issues Twitter searches and produces their results.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="outbound-twitter-type">
|
||||
<xsd:attribute name="id" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
The bean id of this gateway; the MessageHandler is also registered with this id
|
||||
plus a suffix '.handler'.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="search-args-expression" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
A SpEL expression that evaluates to search arguments; the evaluation result type can be
|
||||
an 'org.springframework.social.twitter.api.SearchParameters', a 'String', in
|
||||
which case the default page size of 20 is used, or the expression can evaluate to
|
||||
a list of search
|
||||
arguments, for example: "{payload, headers.pageSize, headers.sinceId, headers.maxId}".
|
||||
Default: "payload".
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="request-channel" use="required" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type
|
||||
type="org.springframework.messaging.MessageChannel" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
<xsd:documentation>
|
||||
Identifies the request channel attached to this gateway.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="reply-channel" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type
|
||||
type="org.springframework.messaging.MessageChannel" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
<xsd:documentation>
|
||||
Identifies the reply channel attached to this
|
||||
gateway.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="reply-timeout" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Allows you to specify how long this gateway will wait for
|
||||
the reply message to be sent successfully to the reply channel
|
||||
before throwing an exception. This attribute only applies when the
|
||||
channel might block, for example when using a bounded queue channel that
|
||||
is currently full.
|
||||
|
||||
Also, keep in mind that when sending to a DirectChannel, the
|
||||
invocation will occur in the sender's thread. Therefore,
|
||||
the failing of the send operation may be caused by other
|
||||
components further downstream.
|
||||
|
||||
The "reply-timeout" attribute maps to the "sendTimeout" property of the
|
||||
underlying 'MessagingTemplate' instance (org.springframework.integration.core.MessagingTemplate).
|
||||
|
||||
The attribute will default, if not specified, to '-1', meaning that
|
||||
by default, the Gateway will wait indefinitely. The value is
|
||||
specified in milliseconds.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attributeGroup ref="integration:smartLifeCycleAttributeGroup"/>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<!--
|
||||
BASE TYPES
|
||||
-->
|
||||
|
||||
<xsd:complexType name="inbound-twitter-type">
|
||||
<xsd:sequence>
|
||||
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="id" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
The bean id of this Polling Endpoint; the MessageSource is also registered with this id
|
||||
plus a suffix '.source'; also used as the
|
||||
MetaDataStore key with suffix '.' + the profileId from the authorized Twitter user.
|
||||
</xsd:documentation>
|
||||
</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.messaging.MessageChannel" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
<xsd:documentation>
|
||||
Identifies the channel the attached to this adapter, to which messages will be sent.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attributeGroup ref="integration:smartLifeCycleAttributeGroup"/>
|
||||
<xsd:attribute name="twitter-template" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.social.twitter.api.Twitter"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
<xsd:documentation>
|
||||
Reference to a TwitterTemplate bean provided by the Spring Social project.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="metadata-store" use="optional" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Reference to a MetadataStore instance for storing metadata associated with
|
||||
the retrieved feeds. If the implementation is persistent, it can help to
|
||||
prevent duplicates between restarts. If shared, it can help coordinate multiple
|
||||
instances of an adapter across different processes.
|
||||
</xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.integration.metadata.MetadataStore" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="page-size" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Limits the number of tweets retrieved on each poll; default: 20.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="outbound-twitter-type">
|
||||
<xsd:choice minOccurs="0" maxOccurs="2">
|
||||
<xsd:element name="transactional" type="integration:transactionalType" minOccurs="0" maxOccurs="1" />
|
||||
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType"
|
||||
minOccurs="0" maxOccurs="1" />
|
||||
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
|
||||
</xsd:choice>
|
||||
<xsd:attribute name="twitter-template" use="required" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.social.twitter.api.Twitter"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
<xsd:documentation>
|
||||
Reference to a TwitterTemplate bean provided by the Spring Social project.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="order">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Specifies the order for invocation when this endpoint is connected as a
|
||||
subscriber to a SubscribableChannel.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
</xsd:schema>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 991 B |
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans:beans xmlns="http://www.springframework.org/schema/integration"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:twitter="http://www.springframework.org/schema/integration/twitter"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/integration/twitter http://www.springframework.org/schema/integration/twitter/spring-integration-social-twitter.xsd">
|
||||
|
||||
<beans:bean id="twitter" class="org.springframework.social.twitter.api.impl.TwitterTemplate"/>
|
||||
|
||||
<chain input-channel="inputChannel">
|
||||
<twitter:outbound-channel-adapter twitter-template="twitter">
|
||||
<twitter:request-handler-advice-chain>
|
||||
<beans:bean class="org.springframework.integration.twitter.config.TestSendingMessageHandlerParserTests$FooAdvice" />
|
||||
</twitter:request-handler-advice-chain>
|
||||
</twitter:outbound-channel-adapter>
|
||||
</chain>
|
||||
|
||||
</beans:beans>
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans:beans
|
||||
xmlns="http://www.springframework.org/schema/integration"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:twitter="http://www.springframework.org/schema/integration/twitter"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/integration/twitter http://www.springframework.org/schema/integration/twitter/spring-integration-social-twitter.xsd">
|
||||
|
||||
<beans:bean id="twitter" class="org.mockito.Mockito" factory-method="mock">
|
||||
<beans:constructor-arg value="org.springframework.social.twitter.api.Twitter"/>
|
||||
</beans:bean>
|
||||
|
||||
<channel id="inbound_mentions"/>
|
||||
|
||||
<twitter:mentions-inbound-channel-adapter id="mentionAdapter"
|
||||
twitter-template="twitter"
|
||||
channel="inbound_mentions"
|
||||
page-size="23"
|
||||
auto-startup="false">
|
||||
<poller fixed-rate="5000" max-messages-per-poll="3"/>
|
||||
</twitter:mentions-inbound-channel-adapter>
|
||||
|
||||
<twitter:dm-inbound-channel-adapter id="dmAdapter"
|
||||
twitter-template="twitter"
|
||||
channel="inbound_mentions"
|
||||
page-size="45"
|
||||
auto-startup="false">
|
||||
<poller fixed-rate="5000" max-messages-per-poll="3"/>
|
||||
</twitter:dm-inbound-channel-adapter>
|
||||
<twitter:inbound-channel-adapter id="updateAdapter"
|
||||
twitter-template="twitter"
|
||||
channel="inbound_mentions"
|
||||
page-size="67"
|
||||
auto-startup="false">
|
||||
<poller fixed-rate="5000" max-messages-per-poll="3"/>
|
||||
</twitter:inbound-channel-adapter>
|
||||
|
||||
</beans:beans>
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.twitter.config;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.integration.twitter.inbound.DirectMessageReceivingMessageSource;
|
||||
import org.springframework.integration.twitter.inbound.MentionsReceivingMessageSource;
|
||||
import org.springframework.integration.twitter.inbound.TimelineReceivingMessageSource;
|
||||
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gunnar Hillert
|
||||
* @author Gary Russell
|
||||
* @author Rijnard van Tonder
|
||||
*/
|
||||
public class TestReceivingMessageSourceParserTests {
|
||||
|
||||
@Test
|
||||
public void testReceivingAdapterConfigurationAutoStartup() {
|
||||
ConfigurableApplicationContext ac = new ClassPathXmlApplicationContext(
|
||||
"TestReceivingMessageSourceParser-context.xml", getClass());
|
||||
SourcePollingChannelAdapter spca = ac.getBean("mentionAdapter", SourcePollingChannelAdapter.class);
|
||||
MentionsReceivingMessageSource ms = TestUtils.getPropertyValue(spca, "source",
|
||||
MentionsReceivingMessageSource.class);
|
||||
assertEquals(Integer.valueOf(23), TestUtils.getPropertyValue(ms, "pageSize", Integer.class));
|
||||
assertNotNull(ms);
|
||||
|
||||
spca = ac.getBean("dmAdapter", SourcePollingChannelAdapter.class);
|
||||
DirectMessageReceivingMessageSource dms = TestUtils.getPropertyValue(spca, "source",
|
||||
DirectMessageReceivingMessageSource.class);
|
||||
assertNotNull(dms);
|
||||
assertEquals(Integer.valueOf(45), TestUtils.getPropertyValue(dms, "pageSize", Integer.class));
|
||||
|
||||
spca = ac.getBean("updateAdapter", SourcePollingChannelAdapter.class);
|
||||
|
||||
TimelineReceivingMessageSource tms = TestUtils.getPropertyValue(spca, "source",
|
||||
TimelineReceivingMessageSource.class);
|
||||
assertEquals(Integer.valueOf(67), TestUtils.getPropertyValue(tms, "pageSize", Integer.class));
|
||||
assertNotNull(tms);
|
||||
ac.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatMessageSourcesAreRegisteredAsBeans() {
|
||||
ConfigurableApplicationContext ac = new ClassPathXmlApplicationContext(
|
||||
"TestReceivingMessageSourceParser-context.xml", this.getClass());
|
||||
|
||||
MentionsReceivingMessageSource ms = ac.getBean("mentionAdapter.source", MentionsReceivingMessageSource.class);
|
||||
assertNotNull(ms);
|
||||
|
||||
DirectMessageReceivingMessageSource dms = ac.getBean("dmAdapter.source",
|
||||
DirectMessageReceivingMessageSource.class);
|
||||
assertNotNull(dms);
|
||||
|
||||
TimelineReceivingMessageSource tms = ac.getBean("updateAdapter.source", TimelineReceivingMessageSource.class);
|
||||
assertNotNull(tms);
|
||||
ac.close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans:beans
|
||||
xmlns="http://www.springframework.org/schema/integration"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:twitter="http://www.springframework.org/schema/integration/twitter"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/integration/twitter http://www.springframework.org/schema/integration/twitter/spring-integration-social-twitter.xsd">
|
||||
|
||||
<channel id="searchChannel"/>
|
||||
|
||||
<beans:bean id="twitter" class="org.mockito.Mockito" factory-method="mock">
|
||||
<beans:constructor-arg value="org.springframework.social.twitter.api.Twitter" />
|
||||
</beans:bean>
|
||||
|
||||
|
||||
<twitter:search-inbound-channel-adapter id="searchAdapterWithTemplate"
|
||||
channel="searchChannel"
|
||||
twitter-template="twitter"
|
||||
page-size="23"
|
||||
query="#springintegration"
|
||||
auto-startup="false">
|
||||
<poller fixed-rate="5000" max-messages-per-poll="3"/>
|
||||
</twitter:search-inbound-channel-adapter>
|
||||
|
||||
|
||||
</beans:beans>
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.twitter.config;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.integration.twitter.inbound.SearchReceivingMessageSource;
|
||||
import org.springframework.social.twitter.api.Twitter;
|
||||
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
*/
|
||||
public class TestSearchReceivingMessageSourceParserTests {
|
||||
|
||||
@Test
|
||||
public void testSearchReceivingDefaultTemplate() {
|
||||
ConfigurableApplicationContext ac = new ClassPathXmlApplicationContext(
|
||||
"TestSearchReceivingMessageSourceParser-context.xml", this.getClass());
|
||||
SourcePollingChannelAdapter spca = ac.getBean("searchAdapterWithTemplate", SourcePollingChannelAdapter.class);
|
||||
SearchReceivingMessageSource ms = (SearchReceivingMessageSource) TestUtils.getPropertyValue(spca, "source");
|
||||
assertEquals(Integer.valueOf(23), TestUtils.getPropertyValue(ms, "pageSize", Integer.class));
|
||||
Twitter template = (Twitter) TestUtils.getPropertyValue(ms, "twitter");
|
||||
assertNotNull(template);
|
||||
ac.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans:beans
|
||||
xmlns="http://www.springframework.org/schema/integration"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:twitter="http://www.springframework.org/schema/integration/twitter"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/integration/twitter http://www.springframework.org/schema/integration/twitter/spring-integration-social-twitter.xsd">
|
||||
|
||||
<beans:bean id="twitter" class="org.springframework.social.twitter.api.impl.TwitterTemplate">
|
||||
<beans:constructor-arg name="clientToken" value="myDummyClientToken"/>
|
||||
</beans:bean>
|
||||
|
||||
<channel id="inbound_mentions"/>
|
||||
|
||||
<channel id="inputChannel"/>
|
||||
|
||||
<twitter:dm-outbound-channel-adapter id="dmAdapter" order="23"
|
||||
twitter-template="twitter"
|
||||
channel="inputChannel"/>
|
||||
|
||||
<twitter:outbound-channel-adapter twitter-template="twitter" channel="inputChannel" />
|
||||
|
||||
<twitter:dm-outbound-channel-adapter id="dmAdvised" order="23"
|
||||
twitter-template="twitter"
|
||||
channel="inputChannel">
|
||||
<twitter:request-handler-advice-chain>
|
||||
<beans:bean class="org.springframework.integration.twitter.config.TestSendingMessageHandlerParserTests$FooAdvice" />
|
||||
</twitter:request-handler-advice-chain>
|
||||
</twitter:dm-outbound-channel-adapter>
|
||||
|
||||
<twitter:outbound-channel-adapter id="advised" twitter-template="twitter" channel="inputChannel">
|
||||
<twitter:request-handler-advice-chain>
|
||||
<beans:bean class="org.springframework.integration.twitter.config.TestSendingMessageHandlerParserTests$FooAdvice" />
|
||||
</twitter:request-handler-advice-chain>
|
||||
</twitter:outbound-channel-adapter>
|
||||
|
||||
</beans:beans>
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.twitter.config;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.integration.endpoint.EventDrivenConsumer;
|
||||
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.integration.twitter.outbound.DirectMessageSendingMessageHandler;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
* @author Gary Russell
|
||||
*/
|
||||
public class TestSendingMessageHandlerParserTests {
|
||||
|
||||
private static volatile int adviceCalled;
|
||||
|
||||
@Test
|
||||
public void testSendingMessageHandlerSuccessfulBootstrap() {
|
||||
ConfigurableApplicationContext ac = new ClassPathXmlApplicationContext(
|
||||
"TestSendingMessageHandlerParser-context.xml", this.getClass());
|
||||
EventDrivenConsumer dmAdapter = ac.getBean("dmAdapter", EventDrivenConsumer.class);
|
||||
MessageHandler handler = TestUtils.getPropertyValue(dmAdapter, "handler", MessageHandler.class);
|
||||
assertEquals(DirectMessageSendingMessageHandler.class, handler.getClass());
|
||||
assertEquals(23, TestUtils.getPropertyValue(handler, "order"));
|
||||
dmAdapter = ac.getBean("dmAdvised", EventDrivenConsumer.class);
|
||||
handler = TestUtils.getPropertyValue(dmAdapter, "handler", MessageHandler.class);
|
||||
handler.handleMessage(new GenericMessage<String>("foo"));
|
||||
assertEquals(1, adviceCalled);
|
||||
MessageHandler handler2 = TestUtils.getPropertyValue(ac.getBean("advised"), "handler", MessageHandler.class);
|
||||
assertNotSame(handler, handler2);
|
||||
handler2.handleMessage(new GenericMessage<String>("foo"));
|
||||
assertEquals(2, adviceCalled);
|
||||
ac.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInt2718FailForOutboundAdapterWithRequestHandlerAdviceChainWithinChainConfig() {
|
||||
try {
|
||||
new ClassPathXmlApplicationContext("OutboundAdapterWithRHACWithinChain-fail-context.xml", this.getClass())
|
||||
.close();
|
||||
fail("Expected BeanDefinitionParsingException");
|
||||
}
|
||||
catch (BeansException e) {
|
||||
assertTrue(e instanceof BeanDefinitionParsingException);
|
||||
assertTrue(e.getMessage().contains("'request-handler-advice-chain' isn't allowed " +
|
||||
"for 'twitter:outbound-channel-adapter' within a <chain/>, because its Handler isn't an AbstractReplyProducingMessageHandler"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class FooAdvice extends AbstractRequestHandlerAdvice {
|
||||
|
||||
@Override
|
||||
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
|
||||
adviceCalled++;
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:int-twitter="http://www.springframework.org/schema/integration/twitter"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/integration/twitter http://www.springframework.org/schema/integration/twitter/spring-integration-social-twitter.xsd">
|
||||
|
||||
<bean id="tt" class="org.mockito.Mockito" factory-method="mock">
|
||||
<constructor-arg value="org.springframework.social.twitter.api.Twitter" />
|
||||
</bean>
|
||||
|
||||
<int:channel id="in" />
|
||||
|
||||
<int-twitter:search-outbound-gateway id="defaultTSOG" twitter-template="tt" request-channel="in" />
|
||||
|
||||
<int-twitter:search-outbound-gateway id="allAttsTSOG"
|
||||
request-channel="in"
|
||||
twitter-template="tt"
|
||||
search-args-expression="'foo'"
|
||||
reply-channel="out"
|
||||
order="23"
|
||||
reply-timeout="123"
|
||||
auto-startup="false"
|
||||
phase="100" />
|
||||
|
||||
<int-twitter:search-outbound-gateway id="polledAndAdvisedTSOG" twitter-template="tt" request-channel="out">
|
||||
<int-twitter:request-handler-advice-chain>
|
||||
<bean class="org.springframework.integration.handler.advice.RequestHandlerRetryAdvice" />
|
||||
</int-twitter:request-handler-advice-chain>
|
||||
<int:poller fixed-rate="1000" />
|
||||
</int-twitter:search-outbound-gateway>
|
||||
|
||||
<int:channel id="out">
|
||||
<int:queue />
|
||||
</int:channel>
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.twitter.config;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.integration.endpoint.PollingConsumer;
|
||||
import org.springframework.integration.handler.advice.RequestHandlerRetryAdvice;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.integration.twitter.outbound.TwitterSearchOutboundGateway;
|
||||
import org.springframework.social.twitter.api.Twitter;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@DirtiesContext
|
||||
public class TwitterSearchOutboundGatewayParserTests {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("defaultTSOG.handler")
|
||||
private TwitterSearchOutboundGateway defaultTSOG;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("allAttsTSOG.handler")
|
||||
private TwitterSearchOutboundGateway allAttsTSOG;
|
||||
|
||||
@Autowired
|
||||
private PollingConsumer polledAndAdvisedTSOG;
|
||||
|
||||
@Autowired
|
||||
private Twitter twitter;
|
||||
|
||||
@Test
|
||||
public void testDefault() {
|
||||
assertSame(twitter, TestUtils.getPropertyValue(defaultTSOG, "twitter"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAllAtts() {
|
||||
assertSame(twitter, TestUtils.getPropertyValue(allAttsTSOG, "twitter"));
|
||||
assertEquals("'foo'", TestUtils.getPropertyValue(allAttsTSOG, "searchArgsExpression.expression"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdvised() {
|
||||
assertSame(twitter, TestUtils.getPropertyValue(polledAndAdvisedTSOG, "handler.twitter"));
|
||||
assertThat(TestUtils.getPropertyValue(polledAndAdvisedTSOG, "handler.adviceChain", ArrayList.class).get(0),
|
||||
Matchers.instanceOf(RequestHandlerRetryAdvice.class));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans:beans
|
||||
xmlns="http://www.springframework.org/schema/integration"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:twitter="http://www.springframework.org/schema/integration/twitter"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
|
||||
http://www.springframework.org/schema/integration/twitter http://www.springframework.org/schema/integration/twitter/spring-integration-social-twitter.xsd">
|
||||
|
||||
<message-history/>
|
||||
|
||||
<context:property-placeholder location="classpath:sample.properties"/>
|
||||
|
||||
<channel id="inbound_dm"/>
|
||||
<channel id="inbound_mentions"/>
|
||||
<channel id="inbound_updates"/>
|
||||
<channel id="inbound_search"/>
|
||||
|
||||
<beans:bean id="twitterTemplate" class="org.springframework.social.twitter.api.impl.TwitterTemplate">
|
||||
<beans:constructor-arg value="${twitter.oauth.consumerKey}"/>
|
||||
<beans:constructor-arg value="${twitter.oauth.consumerSecret}"/>
|
||||
<beans:constructor-arg value="${twitter.oauth.accessToken}"/>
|
||||
<beans:constructor-arg value="${twitter.oauth.accessTokenSecret}"/>
|
||||
</beans:bean>
|
||||
|
||||
<!-- <twitter:mentions-inbound-channel-adapter twitter-template="twitterTemplate" channel="inbound_mentions"> -->
|
||||
<!-- <poller fixed-rate="5000" max-messages-per-poll="-1"/> -->
|
||||
<!-- </twitter:mentions-inbound-channel-adapter> -->
|
||||
|
||||
<!-- <service-activator input-channel="inbound_mentions" ref="twitterAnnouncer" method="mention"/> -->
|
||||
|
||||
<!-- <twitter:dm-inbound-channel-adapter twitter-template="twitterTemplate" channel="inbound_dm"> -->
|
||||
<!-- <poller fixed-rate="5000" max-messages-per-poll="-1"/> -->
|
||||
<!-- </twitter:dm-inbound-channel-adapter> -->
|
||||
<!-- <service-activator input-channel="inbound_dm" ref="twitterAnnouncer" method="dm"/> -->
|
||||
|
||||
<!-- <twitter:search-inbound-channel-adapter id="searchAdapter" twitter-template="twitterTemplate" channel="inbound_search" query="#springintegration"> -->
|
||||
<!-- <poller fixed-rate="5000" max-messages-per-poll="5"/> -->
|
||||
<!-- </twitter:search-inbound-channel-adapter> -->
|
||||
<!-- <service-activator input-channel="inbound_search" ref="twitterAnnouncer" method="search"/> -->
|
||||
|
||||
<twitter:inbound-channel-adapter id="twitterInbound" twitter-template="twitterTemplate" channel="inbound_updates">
|
||||
<poller fixed-rate="1000" max-messages-per-poll="3"/>
|
||||
</twitter:inbound-channel-adapter>
|
||||
|
||||
<service-activator input-channel="inbound_updates" ref="twitterAnnouncer" method="updates"/>
|
||||
|
||||
<beans:bean id="twitterAnnouncer" class="org.springframework.integration.twitter.ignored.TwitterAnnouncer"/>
|
||||
</beans:beans>
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.twitter.ignored;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
*
|
||||
*/
|
||||
public class TestReceivingUsingNamespace {
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
/*
|
||||
* In order to run this test you need to provide oauth properties in sample.properties on the classpath.
|
||||
*/
|
||||
public void testUpdatesWithRealTwitter() throws Exception {
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
ConfigurableApplicationContext ctx =
|
||||
new ClassPathXmlApplicationContext("TestReceivingUsingNamespace-context.xml", this.getClass());
|
||||
latch.await(10000, TimeUnit.SECONDS);
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans:beans
|
||||
xmlns="http://www.springframework.org/schema/integration"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:twitter="http://www.springframework.org/schema/integration/twitter"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
|
||||
http://www.springframework.org/schema/integration/twitter http://www.springframework.org/schema/integration/twitter/spring-integration-social-twitter.xsd">
|
||||
|
||||
<message-history/>
|
||||
|
||||
<context:property-placeholder location="classpath:sample.properties"/>
|
||||
|
||||
<beans:bean id="twitterTemplate" class="org.springframework.social.twitter.api.impl.TwitterTemplate">
|
||||
<beans:constructor-arg value="${twitter.oauth.consumerKey}"/>
|
||||
<beans:constructor-arg value="${twitter.oauth.consumerSecret}"/>
|
||||
<beans:constructor-arg value="${twitter.oauth.accessToken}"/>
|
||||
<beans:constructor-arg value="${twitter.oauth.accessTokenSecret}"/>
|
||||
</beans:bean>
|
||||
|
||||
<channel id="search" />
|
||||
|
||||
<twitter:search-outbound-gateway request-channel="search" twitter-template="twitterTemplate"
|
||||
reply-channel="inbound" />
|
||||
|
||||
<service-activator input-channel="inbound" ref="twitterAnnouncer" method="searchResult"/>
|
||||
|
||||
<beans:bean id="twitterAnnouncer" class="org.springframework.integration.twitter.ignored.TwitterAnnouncer" />
|
||||
|
||||
</beans:beans>
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.twitter.ignored;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.social.twitter.api.SearchParameters;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
*/
|
||||
public class TestSearchOutboundGateway {
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
/*
|
||||
* In order to run this test you need to provide oauth properties in sample.properties on the classpath.
|
||||
*/
|
||||
public void testSearch() throws Exception {
|
||||
ConfigurableApplicationContext ctx =
|
||||
new ClassPathXmlApplicationContext("TestSearchOutboundGateway-context.xml", this.getClass());
|
||||
MessageChannel search = ctx.getBean("search", MessageChannel.class);
|
||||
search.send(new GenericMessage<String>("#springintegration"));
|
||||
Thread.sleep(10000);
|
||||
search.send(new GenericMessage<SearchParameters>(new SearchParameters("#springintegration").count(5)));
|
||||
Thread.sleep(10000);
|
||||
search.send(new GenericMessage<SearchParameters>(new SearchParameters("#jjjjunk").count(5)));
|
||||
Thread.sleep(10000);
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans:beans
|
||||
xmlns="http://www.springframework.org/schema/integration"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:twitter="http://www.springframework.org/schema/integration/twitter"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
|
||||
http://www.springframework.org/schema/integration/twitter http://www.springframework.org/schema/integration/twitter/spring-integration-social-twitter.xsd">
|
||||
|
||||
<context:property-placeholder
|
||||
location="classpath:twitter.sender.properties"
|
||||
ignore-unresolvable="true"/>
|
||||
|
||||
<beans:bean id="twitterTemplate" class="org.springframework.social.twitter.api.impl.TwitterTemplate">
|
||||
<beans:constructor-arg value="${twitter.oauth.consumerKey}"/>
|
||||
<beans:constructor-arg value="${twitter.oauth.consumerSecret}"/>
|
||||
<beans:constructor-arg value="${twitter.oauth.accessToken}"/>
|
||||
<beans:constructor-arg value="${twitter.oauth.accessTokenSecret}"/>
|
||||
</beans:bean>
|
||||
|
||||
|
||||
<channel id="inputChannel"/>
|
||||
|
||||
<twitter:dm-outbound-channel-adapter twitter-template="twitterTemplate" channel="inputChannel"/>
|
||||
|
||||
<chain input-channel="dmOutboundWithinChain">
|
||||
<twitter:dm-outbound-channel-adapter twitter-template="twitterTemplate"/>
|
||||
</chain>
|
||||
|
||||
</beans:beans>
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.twitter.ignored;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.twitter.core.TwitterHeaders;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Josh Long
|
||||
* @author Oleg Zhurakouksy
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
@ContextConfiguration
|
||||
public class TestSendingDMsUsingNamespace extends AbstractJUnit4SpringContextTests {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("inputChannel")
|
||||
|
||||
private MessageChannel inputChannel;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("dmOutboundWithinChain")
|
||||
private MessageChannel dmOutboundWithinChain;
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testSendigRealDirectMessage() throws Throwable {
|
||||
String dmUsr = "z_oleg";
|
||||
MessageBuilder<String> mb = MessageBuilder.withPayload("'Hello world!', from the Spring Integration outbound Twitter adapter "
|
||||
+ System.currentTimeMillis());
|
||||
|
||||
if (StringUtils.hasText(dmUsr)) {
|
||||
mb.setHeader(TwitterHeaders.DM_TARGET_USER_ID, dmUsr);
|
||||
}
|
||||
inputChannel.send(mb.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testSendigDirectMessageFromChain() throws Throwable {
|
||||
String dmUsr = "z_oleg";
|
||||
MessageBuilder<String> mb = MessageBuilder.withPayload("Hello world!");
|
||||
|
||||
if (StringUtils.hasText(dmUsr)) {
|
||||
mb.setHeader(TwitterHeaders.DM_TARGET_USER_ID, dmUsr);
|
||||
}
|
||||
dmOutboundWithinChain.send(mb.build());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans:beans
|
||||
xmlns="http://www.springframework.org/schema/integration"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:twitter="http://www.springframework.org/schema/integration/twitter"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
|
||||
http://www.springframework.org/schema/integration/twitter http://www.springframework.org/schema/integration/twitter/spring-integration-social-twitter.xsd">
|
||||
|
||||
<context:property-placeholder
|
||||
location="classpath:twitter.receiver.properties"
|
||||
ignore-unresolvable="true"/>
|
||||
|
||||
<beans:bean id="twitterTemplate" class="org.springframework.social.twitter.api.impl.TwitterTemplate">
|
||||
<beans:constructor-arg value="${twitter.oauth.consumerKey}"/>
|
||||
<beans:constructor-arg value="${twitter.oauth.consumerSecret}"/>
|
||||
<beans:constructor-arg value="${twitter.oauth.accessToken}"/>
|
||||
<beans:constructor-arg value="${twitter.oauth.accessTokenSecret}"/>
|
||||
</beans:bean>
|
||||
|
||||
|
||||
<channel id="out"/>
|
||||
|
||||
<twitter:outbound-channel-adapter twitter-template="twitterTemplate" channel="out"/>
|
||||
|
||||
<chain input-channel="outFromChain">
|
||||
<twitter:outbound-channel-adapter twitter-template="twitterTemplate"/>
|
||||
</chain>
|
||||
|
||||
</beans:beans>
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.twitter.ignored;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.integration.core.MessagingTemplate;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
|
||||
|
||||
/**
|
||||
* @author Josh Long
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
@ContextConfiguration
|
||||
public class TestSendingUpdatesUsingNamespace extends AbstractJUnit4SpringContextTests {
|
||||
|
||||
private MessagingTemplate messagingTemplate = new MessagingTemplate();
|
||||
|
||||
@Value("#{out}")
|
||||
private MessageChannel channel;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("outFromChain")
|
||||
private MessageChannel outFromChain;
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testSendingATweet() throws Throwable {
|
||||
MessageBuilder<String> mb = MessageBuilder.withPayload("Early start today"
|
||||
+ new Date(System.currentTimeMillis()));
|
||||
Message<String> m = mb.build();
|
||||
this.messagingTemplate.send(this.channel, m);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testSendingATweetFromChain() throws Throwable {
|
||||
Message<String> m = MessageBuilder.withPayload("Early start today" + new Date(System.currentTimeMillis())).build();
|
||||
this.outFromChain.send(m);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.twitter.ignored;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.integration.history.MessageHistory;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.social.twitter.api.DirectMessage;
|
||||
import org.springframework.social.twitter.api.Tweet;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Mark Fisher
|
||||
* @author Gary Russell
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class TwitterAnnouncer {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
public void dm(DirectMessage directMessage) {
|
||||
logger.info("A direct message has been received from " +
|
||||
directMessage.getSender().getScreenName() + " with text " + directMessage.getText());
|
||||
}
|
||||
|
||||
public void search(Message<?> search) {
|
||||
MessageHistory history = MessageHistory.read(search);
|
||||
Tweet tweet = (Tweet) search.getPayload();
|
||||
logger.info("A search item was received " +
|
||||
tweet.getCreatedAt() + " with text " + tweet.getText());
|
||||
}
|
||||
|
||||
public void mention(Tweet s) {
|
||||
logger.info("A tweet mentioning (or replying) to you was received having text "
|
||||
+ s.getFromUser() + "-" + s.getText() + " from " + s.getSource());
|
||||
}
|
||||
|
||||
public void searchResult(Collection<Tweet> tweets) {
|
||||
if (tweets.size() == 0) {
|
||||
logger.info("No results");
|
||||
}
|
||||
for (Tweet s : tweets) {
|
||||
logger.info("Search result: "
|
||||
+ s.getFromUser() + "-" + s.getText() + " from " + s.getSource());
|
||||
}
|
||||
}
|
||||
|
||||
public void updates(Tweet t) {
|
||||
logger.info("Received timeline update: " + t.getText() + " from " + t.getSource());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2018 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.inbound;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.config.PropertiesFactoryBean;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.social.twitter.api.DirectMessage;
|
||||
import org.springframework.social.twitter.api.impl.TwitterTemplate;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
*/
|
||||
public class DirectMessageReceivingMessageSourceTests {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
@Ignore
|
||||
public void demoReceiveDm() throws Exception {
|
||||
PropertiesFactoryBean pf = new PropertiesFactoryBean();
|
||||
pf.setLocation(new ClassPathResource("sample.properties"));
|
||||
pf.afterPropertiesSet();
|
||||
Properties prop = pf.getObject();
|
||||
TwitterTemplate template = new TwitterTemplate(prop.getProperty("z_oleg.oauth.consumerKey"),
|
||||
prop.getProperty("z_oleg.oauth.consumerSecret"),
|
||||
prop.getProperty("z_oleg.oauth.accessToken"),
|
||||
prop.getProperty("z_oleg.oauth.accessTokenSecret"));
|
||||
DirectMessageReceivingMessageSource tSource = new DirectMessageReceivingMessageSource(template, "foo");
|
||||
tSource.afterPropertiesSet();
|
||||
for (int i = 0; i < 50; i++) {
|
||||
Message<DirectMessage> message = tSource.receive();
|
||||
if (message != null) {
|
||||
DirectMessage tweet = message.getPayload();
|
||||
logger.info(tweet.getSender().getScreenName() + " - " + tweet.getText() + " - " + tweet.getCreatedAt());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Copyright 2018 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.inbound;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.PropertiesFactoryBean;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.integration.metadata.SimpleMetadataStore;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.social.twitter.api.SearchMetadata;
|
||||
import org.springframework.social.twitter.api.SearchOperations;
|
||||
import org.springframework.social.twitter.api.SearchParameters;
|
||||
import org.springframework.social.twitter.api.SearchResults;
|
||||
import org.springframework.social.twitter.api.Tweet;
|
||||
import org.springframework.social.twitter.api.Twitter;
|
||||
import org.springframework.social.twitter.api.impl.TwitterTemplate;
|
||||
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gunnar Hillert
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
public class SearchReceivingMessageSourceTests {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private static final String SEARCH_QUERY = "#springsource";
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
@Ignore
|
||||
public void demoReceiveSearchResults() throws Exception {
|
||||
PropertiesFactoryBean pf = new PropertiesFactoryBean();
|
||||
pf.setLocation(new ClassPathResource("sample.properties"));
|
||||
pf.afterPropertiesSet();
|
||||
Properties prop = pf.getObject();
|
||||
TwitterTemplate template = new TwitterTemplate(prop.getProperty("z_oleg.oauth.consumerKey"),
|
||||
prop.getProperty("z_oleg.oauth.consumerSecret"),
|
||||
prop.getProperty("z_oleg.oauth.accessToken"),
|
||||
prop.getProperty("z_oleg.oauth.accessTokenSecret"));
|
||||
SearchReceivingMessageSource tSource = new SearchReceivingMessageSource(template, "foo");
|
||||
tSource.setQuery(SEARCH_QUERY);
|
||||
tSource.afterPropertiesSet();
|
||||
for (int i = 0; i < 50; i++) {
|
||||
Message<Tweet> message = tSource.receive();
|
||||
if (message != null) {
|
||||
Tweet tweet = message.getPayload();
|
||||
logger.info(tweet.getFromUser() + " - " + tweet.getText() + " - " + tweet.getCreatedAt());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unit Test ensuring some basic initialization properties being set.
|
||||
*/
|
||||
@Test
|
||||
public void testSearchReceivingMessageSourceInit() {
|
||||
|
||||
final SearchReceivingMessageSource messageSource =
|
||||
new SearchReceivingMessageSource(new TwitterTemplate("test"), "foo");
|
||||
messageSource.setComponentName("twitterSearchMessageSource");
|
||||
|
||||
final Object metadataStore = TestUtils.getPropertyValue(messageSource, "metadataStore");
|
||||
final Object metadataKey = TestUtils.getPropertyValue(messageSource, "metadataKey");
|
||||
|
||||
assertNull(metadataStore);
|
||||
assertNotNull(metadataKey);
|
||||
|
||||
messageSource.setBeanFactory(mock(BeanFactory.class));
|
||||
messageSource.afterPropertiesSet();
|
||||
|
||||
final Object metadataStoreInitialized = TestUtils.getPropertyValue(messageSource, "metadataStore");
|
||||
final Object metadataKeyInitialized = TestUtils.getPropertyValue(messageSource, "metadataKey");
|
||||
|
||||
assertNotNull(metadataStoreInitialized);
|
||||
assertTrue(metadataStoreInitialized instanceof SimpleMetadataStore);
|
||||
assertNotNull(metadataKeyInitialized);
|
||||
assertEquals("foo", metadataKeyInitialized);
|
||||
|
||||
final Twitter twitter = TestUtils.getPropertyValue(messageSource, "twitter", Twitter.class);
|
||||
|
||||
assertFalse(twitter.isAuthorized());
|
||||
assertNotNull(twitter.userOperations());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This test ensures that when polling for a list of Tweets null is never returned.
|
||||
* In case of no polling results, an empty list is returned instead.
|
||||
*/
|
||||
@Test
|
||||
public void testPollForTweetsNullResults() {
|
||||
|
||||
final TwitterTemplate twitterTemplate = mock(TwitterTemplate.class);
|
||||
final SearchOperations so = mock(SearchOperations.class);
|
||||
|
||||
given(twitterTemplate.searchOperations()).willReturn(so);
|
||||
given(twitterTemplate.searchOperations().search(SEARCH_QUERY, 20, 0, 0)).willReturn(null);
|
||||
|
||||
final SearchReceivingMessageSource messageSource = new SearchReceivingMessageSource(twitterTemplate, "foo");
|
||||
messageSource.setQuery(SEARCH_QUERY);
|
||||
|
||||
final String setQuery = TestUtils.getPropertyValue(messageSource, "query", String.class);
|
||||
|
||||
assertEquals(SEARCH_QUERY, setQuery);
|
||||
assertEquals("twitter:search-inbound-channel-adapter", messageSource.getComponentType());
|
||||
|
||||
final List<Tweet> tweets = messageSource.pollForTweets(0);
|
||||
|
||||
assertNotNull(tweets);
|
||||
assertTrue(tweets.isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that a polling operation returns in fact 3 results.
|
||||
*/
|
||||
@Test
|
||||
public void testPollForTweetsThreeResults() {
|
||||
|
||||
final TwitterTemplate twitterTemplate;
|
||||
|
||||
final SearchOperations so = mock(SearchOperations.class);
|
||||
|
||||
final List<Tweet> tweets = new ArrayList<Tweet>();
|
||||
|
||||
tweets.add(mock(Tweet.class));
|
||||
tweets.add(mock(Tweet.class));
|
||||
tweets.add(mock(Tweet.class));
|
||||
|
||||
final SearchResults results = new SearchResults(tweets, new SearchMetadata(111, 111));
|
||||
|
||||
twitterTemplate = mock(TwitterTemplate.class);
|
||||
|
||||
given(twitterTemplate.searchOperations()).willReturn(so);
|
||||
SearchParameters params = new SearchParameters(SEARCH_QUERY).count(20).sinceId(0);
|
||||
given(twitterTemplate.searchOperations().search(params)).willReturn(results);
|
||||
|
||||
final SearchReceivingMessageSource messageSource = new SearchReceivingMessageSource(twitterTemplate, "foo");
|
||||
|
||||
messageSource.setQuery(SEARCH_QUERY);
|
||||
|
||||
final List<Tweet> tweetSearchResults = messageSource.pollForTweets(0);
|
||||
|
||||
assertNotNull(tweetSearchResults);
|
||||
assertEquals(3, tweetSearchResults.size());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:int-twitter="http://www.springframework.org/schema/integration/twitter"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/integration/twitter http://www.springframework.org/schema/integration/twitter/spring-integration-social-twitter.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
|
||||
|
||||
<context:annotation-config/>
|
||||
|
||||
<bean class="org.springframework.integration.twitter.inbound.SearchReceivingMessageSourceWithMetadataTests$SearchReceivingMessageSourceWithMetadataTestsConfig"/>
|
||||
|
||||
<int:channel id="inbound_twitter">
|
||||
<int:queue/>
|
||||
</int:channel>
|
||||
|
||||
<int-twitter:search-inbound-channel-adapter id="twitterSearchAdapter"
|
||||
query="springintegration"
|
||||
twitter-template="twitterTemplate"
|
||||
channel="inbound_twitter"
|
||||
metadata-store="metadataStore"
|
||||
auto-startup="false">
|
||||
<int:poller fixed-delay="100" max-messages-per-poll="3"/>
|
||||
</int-twitter:search-inbound-channel-adapter>
|
||||
|
||||
<bean id="metadataStore" class="org.springframework.integration.metadata.SimpleMetadataStore"/>
|
||||
|
||||
</beans>
|
||||
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Copyright 2018 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.inbound;
|
||||
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
|
||||
import org.springframework.integration.metadata.MetadataStore;
|
||||
import org.springframework.integration.metadata.SimpleMetadataStore;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.social.twitter.api.SearchMetadata;
|
||||
import org.springframework.social.twitter.api.SearchOperations;
|
||||
import org.springframework.social.twitter.api.SearchParameters;
|
||||
import org.springframework.social.twitter.api.SearchResults;
|
||||
import org.springframework.social.twitter.api.Tweet;
|
||||
import org.springframework.social.twitter.api.UserOperations;
|
||||
import org.springframework.social.twitter.api.impl.TwitterTemplate;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* @author Gunnar Hillert
|
||||
* @author Artem Bilan
|
||||
* @author Gary Russell
|
||||
*/
|
||||
@ContextConfiguration("SearchReceivingMessageSourceWithMetadataTests-context.xml")
|
||||
@RunWith(SpringRunner.class)
|
||||
@DirtiesContext
|
||||
public class SearchReceivingMessageSourceWithMetadataTests {
|
||||
|
||||
@Autowired
|
||||
private SourcePollingChannelAdapter twitterSearchAdapter;
|
||||
|
||||
@Autowired
|
||||
private AbstractTwitterMessageSource<?> twitterMessageSource;
|
||||
|
||||
@Autowired
|
||||
private MetadataStore metadataStore;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("inbound_twitter")
|
||||
private PollableChannel tweets;
|
||||
|
||||
@Test
|
||||
public void testPollForTweetsThreeResultsWithHazelcastMetadataStore() {
|
||||
String metadataKey = TestUtils.getPropertyValue(twitterSearchAdapter, "source.metadataKey", String.class);
|
||||
|
||||
// There is need to set a value, not 'remove' and re-init 'twitterMessageSource'
|
||||
this.metadataStore.put(metadataKey, "-1");
|
||||
|
||||
this.twitterMessageSource.afterPropertiesSet();
|
||||
|
||||
MetadataStore metadataStore = TestUtils.getPropertyValue(this.twitterSearchAdapter, "source.metadataStore",
|
||||
MetadataStore.class);
|
||||
|
||||
assertTrue("Expected metadataStore to be an instance of SimpleMetadataStore",
|
||||
metadataStore instanceof SimpleMetadataStore);
|
||||
|
||||
assertSame(this.metadataStore, metadataStore);
|
||||
|
||||
assertEquals("twitterSearchAdapter.74", metadataKey);
|
||||
|
||||
this.twitterSearchAdapter.start();
|
||||
|
||||
Message<?> receive = this.tweets.receive(10000);
|
||||
assertNotNull(receive);
|
||||
|
||||
receive = this.tweets.receive(10000);
|
||||
assertNotNull(receive);
|
||||
|
||||
receive = this.tweets.receive(10000);
|
||||
assertNotNull(receive);
|
||||
|
||||
/* We received 3 messages so far. When invoking receive() again the search
|
||||
* will return again the 3 test Tweets but as we already processed them
|
||||
* no message (null) is returned. */
|
||||
assertNull(this.tweets.receive(0));
|
||||
|
||||
String persistedMetadataStoreValue = this.metadataStore.get(metadataKey);
|
||||
assertNotNull(persistedMetadataStoreValue);
|
||||
assertEquals("3", persistedMetadataStoreValue);
|
||||
|
||||
this.twitterSearchAdapter.stop();
|
||||
|
||||
this.metadataStore.put(metadataKey, "1");
|
||||
|
||||
this.twitterMessageSource.afterPropertiesSet();
|
||||
|
||||
this.twitterSearchAdapter.start();
|
||||
|
||||
receive = this.tweets.receive(10000);
|
||||
assertNotNull(receive);
|
||||
assertThat(receive.getPayload(), instanceOf(Tweet.class));
|
||||
assertEquals(((Tweet) receive.getPayload()).getId(), 2L);
|
||||
|
||||
receive = this.tweets.receive(10000);
|
||||
assertNotNull(receive);
|
||||
assertThat(receive.getPayload(), instanceOf(Tweet.class));
|
||||
assertEquals(((Tweet) receive.getPayload()).getId(), 3L);
|
||||
|
||||
assertNull(this.tweets.receive(0));
|
||||
|
||||
persistedMetadataStoreValue = this.metadataStore.get(metadataKey);
|
||||
assertNotNull(persistedMetadataStoreValue);
|
||||
assertEquals("3", persistedMetadataStoreValue);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class SearchReceivingMessageSourceWithMetadataTestsConfig {
|
||||
|
||||
@Bean(name = "twitterTemplate")
|
||||
public TwitterTemplate twitterTemplate() {
|
||||
TwitterTemplate twitterTemplate = mock(TwitterTemplate.class);
|
||||
|
||||
SearchOperations so = mock(SearchOperations.class);
|
||||
|
||||
Tweet tweet3 = mock(Tweet.class);
|
||||
given(tweet3.getId()).willReturn(3L);
|
||||
given(tweet3.getCreatedAt()).willReturn(new GregorianCalendar(2013, 2, 20).getTime());
|
||||
given(tweet3.toString()).will(invocation -> "Mock for Tweet: " + tweet3.getId());
|
||||
|
||||
Tweet tweet1 = mock(Tweet.class);
|
||||
given(tweet1.getId()).willReturn(1L);
|
||||
given(tweet1.getCreatedAt()).willReturn(new GregorianCalendar(2013, 0, 20).getTime());
|
||||
given(tweet1.toString()).will(invocation -> "Mock for Tweet: " + tweet1.getId());
|
||||
|
||||
final Tweet tweet2 = mock(Tweet.class);
|
||||
given(tweet2.getId()).willReturn(2L);
|
||||
given(tweet2.getCreatedAt()).willReturn(new GregorianCalendar(2013, 1, 20).getTime());
|
||||
given(tweet2.toString()).will(invocation -> "Mock for Tweet: " + tweet2.getId());
|
||||
|
||||
final List<Tweet> tweets = new ArrayList<Tweet>();
|
||||
|
||||
tweets.add(tweet3);
|
||||
tweets.add(tweet1);
|
||||
tweets.add(tweet2);
|
||||
|
||||
final SearchResults results = new SearchResults(tweets, new SearchMetadata(111, 111));
|
||||
|
||||
given(twitterTemplate.searchOperations()).willReturn(so);
|
||||
given(twitterTemplate.searchOperations().search(any(SearchParameters.class))).willReturn(results);
|
||||
|
||||
given(twitterTemplate.isAuthorized()).willReturn(true);
|
||||
|
||||
final UserOperations userOperations = mock(UserOperations.class);
|
||||
given(twitterTemplate.userOperations()).willReturn(userOperations);
|
||||
given(userOperations.getProfileId()).willReturn(74L);
|
||||
|
||||
return twitterTemplate;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2018 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.inbound;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.config.PropertiesFactoryBean;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.social.twitter.api.Tweet;
|
||||
import org.springframework.social.twitter.api.impl.TwitterTemplate;
|
||||
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
*/
|
||||
public class TimelineReceivingMessageSourceTests {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
@Ignore
|
||||
public void demoReceiveTimeline() throws Exception {
|
||||
PropertiesFactoryBean pf = new PropertiesFactoryBean();
|
||||
pf.setLocation(new ClassPathResource("sample.properties"));
|
||||
pf.afterPropertiesSet();
|
||||
Properties prop = pf.getObject();
|
||||
TwitterTemplate template = new TwitterTemplate(prop.getProperty("z_oleg.oauth.consumerKey"),
|
||||
prop.getProperty("z_oleg.oauth.consumerSecret"),
|
||||
prop.getProperty("z_oleg.oauth.accessToken"),
|
||||
prop.getProperty("z_oleg.oauth.accessTokenSecret"));
|
||||
TimelineReceivingMessageSource tSource = new TimelineReceivingMessageSource(template, "foo");
|
||||
tSource.afterPropertiesSet();
|
||||
for (int i = 0; i < 50; i++) {
|
||||
Message<Tweet> message = tSource.receive();
|
||||
if (message != null) {
|
||||
Tweet tweet = message.getPayload();
|
||||
logger.info(tweet.getFromUser() + " - " + tweet.getText() + " - " + tweet.getCreatedAt());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.twitter.outbound;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.config.PropertiesFactoryBean;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.twitter.core.TwitterHeaders;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.social.twitter.api.impl.TwitterTemplate;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Mark Fisher
|
||||
* @author Gary Russell
|
||||
*/
|
||||
public class DirectMessageSendingMessageHandlerTests {
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void validateSendDirectMessage() throws Exception {
|
||||
PropertiesFactoryBean pf = new PropertiesFactoryBean();
|
||||
pf.setLocation(new ClassPathResource("sample.properties"));
|
||||
pf.afterPropertiesSet();
|
||||
Properties prop = pf.getObject();
|
||||
TwitterTemplate template = new TwitterTemplate(prop.getProperty("spring_eip.oauth.consumerKey"),
|
||||
prop.getProperty("spring_eip.oauth.consumerSecret"),
|
||||
prop.getProperty("spring_eip.oauth.accessToken"),
|
||||
prop.getProperty("spring_eip.oauth.accessTokenSecret"));
|
||||
Message<?> message1 = MessageBuilder.withPayload("Polsihing SI Twitter migration")
|
||||
.setHeader(TwitterHeaders.DM_TARGET_USER_ID, "z_oleg").build();
|
||||
DirectMessageSendingMessageHandler handler = new DirectMessageSendingMessageHandler(template);
|
||||
handler.afterPropertiesSet();
|
||||
handler.handleMessage(message1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int-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/twitter http://www.springframework.org/schema/integration/twitter/spring-integration-social-twitter.xsd">
|
||||
|
||||
<bean id="tt" class="org.mockito.Mockito" factory-method="mock">
|
||||
<constructor-arg value="org.springframework.social.twitter.api.Twitter" />
|
||||
</bean>
|
||||
|
||||
<int-twitter:outbound-channel-adapter id="in1" twitter-template="tt" />
|
||||
|
||||
<int-twitter:outbound-channel-adapter
|
||||
id="in2"
|
||||
twitter-template="tt"
|
||||
tweet-data-expression="new TweetData(payload.foo).withMedia(headers.media)"/>
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.twitter.outbound;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.PropertiesFactoryBean;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.social.twitter.api.TimelineOperations;
|
||||
import org.springframework.social.twitter.api.TweetData;
|
||||
import org.springframework.social.twitter.api.Twitter;
|
||||
import org.springframework.social.twitter.api.impl.TwitterTemplate;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@DirtiesContext
|
||||
public class StatusUpdatingMessageHandlerTests {
|
||||
|
||||
@Autowired
|
||||
MessageChannel in1;
|
||||
|
||||
@Autowired
|
||||
MessageChannel in2;
|
||||
|
||||
@Autowired
|
||||
Twitter twitter;
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void demoSendStatusMessage() throws Exception {
|
||||
PropertiesFactoryBean pf = new PropertiesFactoryBean();
|
||||
pf.setLocation(new ClassPathResource("sample.properties"));
|
||||
pf.afterPropertiesSet();
|
||||
Properties prop = pf.getObject();
|
||||
TwitterTemplate template = new TwitterTemplate(prop.getProperty("z_oleg.oauth.consumerKey"),
|
||||
prop.getProperty("z_oleg.oauth.consumerSecret"),
|
||||
prop.getProperty("z_oleg.oauth.accessToken"),
|
||||
prop.getProperty("z_oleg.oauth.accessTokenSecret"));
|
||||
Message<?> message1 = new GenericMessage<>("Polishing #springintegration migration to Spring Social. test");
|
||||
StatusUpdatingMessageHandler handler = new StatusUpdatingMessageHandler(template);
|
||||
handler.afterPropertiesSet();
|
||||
handler.handleMessage(message1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStatusUpdatingMessageHandler() {
|
||||
TimelineOperations timelineOperations = Mockito.mock(TimelineOperations.class);
|
||||
Mockito.when(this.twitter.timelineOperations()).thenReturn(timelineOperations);
|
||||
|
||||
ArgumentCaptor<TweetData> argument = ArgumentCaptor.forClass(TweetData.class);
|
||||
|
||||
this.in1.send(new GenericMessage<String>("foo"));
|
||||
|
||||
Mockito.verify(timelineOperations).updateStatus(argument.capture());
|
||||
assertEquals("foo", argument.getValue().toRequestParameters().getFirst("status"));
|
||||
|
||||
Mockito.reset(timelineOperations);
|
||||
|
||||
ClassPathResource media = new ClassPathResource("log4j.properties");
|
||||
this.in2.send(MessageBuilder.withPayload(Collections.singletonMap("foo", "bar"))
|
||||
.setHeader("media", media)
|
||||
.build());
|
||||
|
||||
Mockito.verify(timelineOperations).updateStatus(argument.capture());
|
||||
TweetData tweetData = argument.getValue();
|
||||
MultiValueMap<String, Object> requestParameters = tweetData.toRequestParameters();
|
||||
assertEquals("bar", requestParameters.getFirst("status"));
|
||||
assertNull(requestParameters.getFirst("media"));
|
||||
MultiValueMap<String, Object> uploadMediaParameters = tweetData.toUploadMediaParameters();
|
||||
assertEquals(media, uploadMediaParameters.getFirst("media"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.twitter.outbound;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.willAnswer;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.config.EnableIntegration;
|
||||
import org.springframework.integration.twitter.core.TwitterHeaders;
|
||||
import org.springframework.integration.twitter.outbound.TwitterSearchOutboundGatewayTests.TwitterConfig;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.social.twitter.api.SearchMetadata;
|
||||
import org.springframework.social.twitter.api.SearchOperations;
|
||||
import org.springframework.social.twitter.api.SearchParameters;
|
||||
import org.springframework.social.twitter.api.SearchResults;
|
||||
import org.springframework.social.twitter.api.Tweet;
|
||||
import org.springframework.social.twitter.api.Twitter;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.annotation.DirtiesContext.ClassMode;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
@ContextConfiguration(classes = TwitterConfig.class)
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
|
||||
public class TwitterSearchOutboundGatewayTests {
|
||||
|
||||
@Autowired
|
||||
private SearchOperations searchOps;
|
||||
|
||||
@Autowired
|
||||
private TwitterSearchOutboundGateway gateway;
|
||||
|
||||
@Autowired
|
||||
private PollableChannel outputChannel;
|
||||
|
||||
@Test
|
||||
public void testStringQuery() {
|
||||
Tweet tweet = mock(Tweet.class);
|
||||
SearchMetadata searchMetadata = mock(SearchMetadata.class);
|
||||
final SearchResults searchResults = new SearchResults(Collections.singletonList(tweet), searchMetadata);
|
||||
willAnswer(invocation -> {
|
||||
SearchParameters searchParameters = invocation.getArgument(0);
|
||||
assertEquals("foo", searchParameters.getQuery());
|
||||
assertEquals(Integer.valueOf(20), searchParameters.getCount());
|
||||
return searchResults;
|
||||
}).given(this.searchOps).search(any(SearchParameters.class));
|
||||
this.gateway.handleMessage(new GenericMessage<>("foo"));
|
||||
Message<?> reply = this.outputChannel.receive(0);
|
||||
assertNotNull(reply);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Tweet> tweets = (List<Tweet>) reply.getPayload();
|
||||
assertEquals(1, tweets.size());
|
||||
assertSame(tweet, tweets.get(0));
|
||||
assertSame(searchMetadata, reply.getHeaders().get(TwitterHeaders.SEARCH_METADATA));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStringQueryCustomLimit() {
|
||||
this.gateway.setSearchArgsExpression(new SpelExpressionParser()
|
||||
.parseExpression("{payload, 30}"));
|
||||
Tweet tweet = mock(Tweet.class);
|
||||
SearchMetadata searchMetadata = mock(SearchMetadata.class);
|
||||
final SearchResults searchResults = new SearchResults(Collections.singletonList(tweet), searchMetadata);
|
||||
willAnswer(invocation -> {
|
||||
SearchParameters searchParameters = invocation.getArgument(0);
|
||||
assertEquals("foo", searchParameters.getQuery());
|
||||
assertEquals(Integer.valueOf(30), searchParameters.getCount());
|
||||
return searchResults;
|
||||
}).given(this.searchOps).search(any(SearchParameters.class));
|
||||
this.gateway.handleMessage(new GenericMessage<String>("foo"));
|
||||
Message<?> reply = this.outputChannel.receive(0);
|
||||
assertNotNull(reply);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Tweet> tweets = (List<Tweet>) reply.getPayload();
|
||||
assertEquals(1, tweets.size());
|
||||
assertSame(tweet, tweets.get(0));
|
||||
assertSame(searchMetadata, reply.getHeaders().get(TwitterHeaders.SEARCH_METADATA));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStringQueryCustomExpression() {
|
||||
this.gateway.setSearchArgsExpression(new SpelExpressionParser()
|
||||
.parseExpression("{'bar', 1, 2, 3}"));
|
||||
Tweet tweet = mock(Tweet.class);
|
||||
SearchMetadata searchMetadata = mock(SearchMetadata.class);
|
||||
final SearchResults searchResults = new SearchResults(Collections.singletonList(tweet), searchMetadata);
|
||||
willAnswer(invocation -> {
|
||||
SearchParameters searchParameters = invocation.getArgument(0);
|
||||
assertEquals("bar", searchParameters.getQuery());
|
||||
assertEquals(Integer.valueOf(1), searchParameters.getCount());
|
||||
assertEquals(Long.valueOf(2), searchParameters.getSinceId());
|
||||
assertEquals(Long.valueOf(3), searchParameters.getMaxId());
|
||||
return searchResults;
|
||||
}).given(this.searchOps).search(any(SearchParameters.class));
|
||||
this.gateway.handleMessage(new GenericMessage<String>("foo"));
|
||||
Message<?> reply = this.outputChannel.receive(0);
|
||||
assertNotNull(reply);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Tweet> tweets = (List<Tweet>) reply.getPayload();
|
||||
assertEquals(1, tweets.size());
|
||||
assertSame(tweet, tweets.get(0));
|
||||
assertSame(searchMetadata, reply.getHeaders().get(TwitterHeaders.SEARCH_METADATA));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchParamsQuery() {
|
||||
Tweet tweet = mock(Tweet.class);
|
||||
SearchMetadata searchMetadata = mock(SearchMetadata.class);
|
||||
final SearchResults searchResults = new SearchResults(Collections.singletonList(tweet), searchMetadata);
|
||||
final SearchParameters parameters = new SearchParameters("bar");
|
||||
willAnswer(invocation -> {
|
||||
SearchParameters searchParameters = invocation.getArgument(0);
|
||||
assertSame(parameters, searchParameters);
|
||||
return searchResults;
|
||||
}).given(this.searchOps).search(any(SearchParameters.class));
|
||||
this.gateway.handleMessage(new GenericMessage<SearchParameters>(parameters));
|
||||
Message<?> reply = this.outputChannel.receive(0);
|
||||
assertNotNull(reply);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Tweet> tweets = (List<Tweet>) reply.getPayload();
|
||||
assertEquals(1, tweets.size());
|
||||
assertSame(tweet, tweets.get(0));
|
||||
assertSame(searchMetadata, reply.getHeaders().get(TwitterHeaders.SEARCH_METADATA));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchParamsQueryCustomExpression() {
|
||||
this.gateway.setSearchArgsExpression(new SpelExpressionParser()
|
||||
.parseExpression("new SearchParameters('foo' + payload).count(5).sinceId(11)"));
|
||||
Tweet tweet = mock(Tweet.class);
|
||||
SearchMetadata searchMetadata = mock(SearchMetadata.class);
|
||||
final SearchResults searchResults = new SearchResults(Collections.singletonList(tweet), searchMetadata);
|
||||
willAnswer(invocation -> {
|
||||
SearchParameters searchParameters = invocation.getArgument(0);
|
||||
assertEquals("foobar", searchParameters.getQuery());
|
||||
assertEquals(Integer.valueOf(5), searchParameters.getCount());
|
||||
assertEquals(Long.valueOf(11), searchParameters.getSinceId());
|
||||
return searchResults;
|
||||
}).given(this.searchOps).search(any(SearchParameters.class));
|
||||
this.gateway.handleMessage(new GenericMessage<String>("bar"));
|
||||
Message<?> reply = this.outputChannel.receive(0);
|
||||
assertNotNull(reply);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Tweet> tweets = (List<Tweet>) reply.getPayload();
|
||||
assertEquals(1, tweets.size());
|
||||
assertSame(tweet, tweets.get(0));
|
||||
assertSame(searchMetadata, reply.getHeaders().get(TwitterHeaders.SEARCH_METADATA));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyResult() {
|
||||
SearchMetadata searchMetadata = mock(SearchMetadata.class);
|
||||
List<Tweet> empty = new ArrayList<Tweet>(0);
|
||||
final SearchResults searchResults = new SearchResults(empty, searchMetadata);
|
||||
willAnswer(invocation -> {
|
||||
SearchParameters searchParameters = invocation.getArgument(0);
|
||||
assertEquals("foo", searchParameters.getQuery());
|
||||
assertEquals(Integer.valueOf(20), searchParameters.getCount());
|
||||
return searchResults;
|
||||
}).given(this.searchOps).search(any(SearchParameters.class));
|
||||
this.gateway.handleMessage(new GenericMessage<String>("foo"));
|
||||
Message<?> reply = this.outputChannel.receive(0);
|
||||
assertNotNull(reply);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Tweet> tweets = (List<Tweet>) reply.getPayload();
|
||||
assertEquals(0, tweets.size());
|
||||
assertSame(searchMetadata, reply.getHeaders().get(TwitterHeaders.SEARCH_METADATA));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableIntegration
|
||||
public static class TwitterConfig {
|
||||
|
||||
@Bean
|
||||
public TwitterSearchOutboundGateway gateway() {
|
||||
TwitterSearchOutboundGateway gateway = new TwitterSearchOutboundGateway(twitter());
|
||||
gateway.setOutputChannel(outputChannel());
|
||||
return gateway;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PollableChannel outputChannel() {
|
||||
return new QueueChannel();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Twitter twitter() {
|
||||
Twitter twitter = mock(Twitter.class);
|
||||
given(twitter.searchOperations()).willReturn(searchOps());
|
||||
return twitter;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SearchOperations searchOps() {
|
||||
return mock(SearchOperations.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="WARN">
|
||||
<Appenders>
|
||||
<Console name="STDOUT" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%d %p [%t] [%c] - %m%n" />
|
||||
</Console>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Logger name="org.springframework.integration" level="warn"/>
|
||||
<Logger name="org.springframework.integration.twitter" level="warn"/>
|
||||
<Root level="warn">
|
||||
<AppenderRef ref="STDOUT" />
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
||||
@@ -0,0 +1,5 @@
|
||||
# oauth setup for prosibook twitter account
|
||||
twitter.oauth.consumerKey=
|
||||
twitter.oauth.consumerSecret=
|
||||
twitter.oauth.accessToken=
|
||||
twitter.oauth.accessTokenSecret=
|
||||
@@ -0,0 +1,5 @@
|
||||
twitter.oauth.consumerKey=
|
||||
twitter.oauth.consumerSecret=
|
||||
twitter.oauth.accessToken=
|
||||
twitter.oauth.accessTokenSecret=
|
||||
twitter.oauth.pin=
|
||||
Reference in New Issue
Block a user