This is a significant update to the build system, including the changes listed below. README.md has been updated with instructions on the most important day-to-day commands. - Eliminate buildSrc submodule In favor of using the new bundlor and docbook-reference plugins. The net effect is a large reduction in number of lines of build code. Common docbook resources, stylesheets, etc are stored directly in the docbook plugin. This means that --recursive is no longer required when cloning and there will never be a need to use `git submodule` commands. README files have been updated to reflect. Use of the new bundlor plugin also means the removal of template.mf files from the source tree in favor of an inline approach. See build.gradle for details. Bundlor 'import templates' are built up programmatically and kept physically close to gradle dependency declarations, leading to more convenience when changing these values and hopefully fewer errors / version inconsistencies over time. Certain tests depended on the presence of template.mf files, all of which have recently been removed from the source tree in favor of the new bundlor plugin which allows for inlining bundlor configuration within the Gradle build script. These tests now create temp files using the java.io.File API instead. - Upgrade to Gradle 1.0-milestone-6 The m6 release is significantly faster when resolving dependencies and has a number of valuable new features over the earlier m3 version. Review the release notes for Gradle 1.0-milestone-6 online for full details. - Switch to repo.springsource.org repository Previously the project build declared as many repositories as necessary to resolve all project dependencies. Now depending on a single 'virtual repository' defined within the SpringSource Artifactory instance at http://repo.springsource.org. Currently, the virtual repository in use is 'libs-milestone', which allows for the resolution of all "milestone-or-better" versions of all S2 and third-party dependencies. Should snapshot dependencies become required, this value may be changed from 'libs-milestone' to 'libs-snapshot'. To build only against GA releases, change the value to 'libs-release'. - New build plan(s) Spring Integration build plans have been updated to use the Artifactory Bamboo plugin and publish to repo.springsource.org. Build plans have names like 2.1.x to reflect the version under development, not necessarily the name of the branch, as this may change over time and across major releases. - Improve release process As mentioned above, Spring Integration will now use the Artifactory Bamboo plugin to publish releases and also use Artifactory's support for pushing builds directly into Maven Central via oss.sonatype.org. Generate poms that contain all necessary fields for onboarding at Maven central (scm, developers, organization, licenses, etc). Generate -source and -javadoc poms to comply with Maven Central onboarding rules (and for general good practice anyway). Generation of PGP signatures, sha1 and md5 checksums are all handled automatically by Artifactory. These are also requirements for automated entry into Maven Central. - Remove source-level pom generation Automatic generation of Maven poms suitable for use in building Spring Integration is no longer supported. Generation and publication of poms for the purpose of dependency management remains supported. Sonar support has to date depended on these poms, but will be switched over to use the Gradle Sonar plugin shortly. - Eliminate docs subproject Move docs/src to the root of the project and eliminate docs as a formal subproject. This simplifies the build in a number of ways, including removing the need for distinguishing between 'subprojects' and 'javaprojects' as well as allowing users to build both 'api' and 'reference' docs without qualifying with a ':docs' prefix. Also rename the src/info directory to src/dist to better reflect that these files are packaged with the distribution. For example, the readme.txt there is really the distribution readme, distinct from the README.md at the root of the project which is for building from source, etc.
293 lines
17 KiB
XML
293 lines
17 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="twitter"
|
|
xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
|
|
<title>Twitter Adapter</title>
|
|
<para>
|
|
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.
|
|
</para>
|
|
|
|
<section id="twitter-intro">
|
|
<title>Introduction</title>
|
|
<para>
|
|
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.
|
|
</para>
|
|
<para>
|
|
<important>
|
|
Previous versions of Spring Integration were dependent upon the <link linkend="http://twitter4j.org/en/index.html">Twitter4J API</link>,
|
|
but with the release of <link linkend="http://www.springsource.org/spring-social">Spring Social 1.0 GA</link>,
|
|
Spring Integration, as of version 2.1, now builds directly upon Spring Social's Twitter support, instead of Twitter4J.
|
|
</important>
|
|
</para>
|
|
|
|
<para>
|
|
Spring Integration provides a convenient namespace configuration to define Twitter artifacts. You can enable it by adding
|
|
the following within your XML header.
|
|
<programlisting language="xml"><![CDATA[xmlns:int-twitter="http://www.springframework.org/schema/integration/twitter"
|
|
xsi:schemaLocation="http://www.springframework.org/schema/integration/twitter
|
|
http://www.springframework.org/schema/integration/twitter/spring-integration-twitter.xsd"]]></programlisting>
|
|
</para>
|
|
</section>
|
|
|
|
<section id="twitter-oauth">
|
|
<title>Twitter OAuth Configuration</title>
|
|
|
|
<para>
|
|
The Twitter API allows for both authenticated and anonymous operations. 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 <link linkend="http://oauth.net/">http://oauth.net/</link> or
|
|
in this article <link linkend="http://hueniverse.com/oauth/">http://hueniverse.com/oauth/</link> from Hueniverse.
|
|
Please also see <link linkend="http://dev.twitter.com/pages/oauth_faq">OAuth FAQ</link> for more information about OAuth and Twitter.
|
|
</para>
|
|
<para>
|
|
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:
|
|
</para>
|
|
<para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>Go to <link linkend="http://dev.twitter.com/">http://dev.twitter.com/</link></para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>Click on the <code>Register an app</code> link and fill out all required fields on the form provided;
|
|
set <code>Application Type</code> to <code>Client</code> and depending on the nature of your application select
|
|
<code>Default Access Type</code> as <emphasis>Read & Write</emphasis> or <emphasis>Read-only</emphasis>
|
|
and Submit the form. If everything is successful you'll be presented with the <code>Consumer Key</code>
|
|
and <code>Consumer Secret</code>. Copy both values in a safe place.</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>On the same page you should see a <code>My Access Token</code> button on the side bar (right).
|
|
Click on it and you'll be presented with two more values: <code>Access Token</code> and <code>Access Token Secret</code>.
|
|
Copy these values in a safe place as well.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</para>
|
|
|
|
</section>
|
|
<section>
|
|
<title>Twitter Template</title>
|
|
|
|
<para>
|
|
As mentioned above, Spring Integration relies upon Spring Social, and that library provides an implementation of the template
|
|
pattern, <classname>org.springframework.social.twitter.api.impl.TwitterTemplate</classname> to interact with Twitter.
|
|
For anonymous operations (e.g., search), you don't have to define an instance of <classname>TwitterTemplate</classname> explicitly,
|
|
since a default instance will be created and injected into the endpoint. However, for authenticated operations
|
|
(update status, send direct message, etc.), you must configure a <classname>TwitterTemplate</classname> as a bean and
|
|
inject it explicitly into the endpoint, because the authentication configuration is required.
|
|
Below is a sample configuration of TwitterTemplate:
|
|
|
|
<programlisting language="xml"><![CDATA[<bean id="twitterTemplate" class="org.springframework.social.twitter.api.impl.TwitterTemplate">
|
|
<constructor-arg value="4XzBPacJQxyBzzzH"/>
|
|
<constructor-arg value="AbRxUAvyCtqQtvxFK8w5ZMtMj20KFhB6o"/>
|
|
<constructor-arg value="21691649-4YZY5iJEOfz2A9qCFd9SjBRGb3HLmIm4HNE"/>
|
|
<constructor-arg value="AbRxUAvyNCtqQtxFK8w5ZMtMj20KFhB6o"/>
|
|
</bean>]]></programlisting>
|
|
<note>The values above are not real.</note>
|
|
|
|
As you can see from the configuration above, all we need to do is to provide
|
|
OAuth <code>attributes</code> as constructor arguments. The values would be those you obtained in the previous step.
|
|
The order of constructor arguments is: 1) <code>consumerKey</code>, 2) <code>consumerSecret</code>,
|
|
3) <code>accessToken</code>, and 4) <code>accessTokenSecret</code>.
|
|
</para>
|
|
|
|
<para>
|
|
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):
|
|
|
|
<programlisting language="java"><![CDATA[twitter.oauth.consumerKey=4XzBPacJQxyBzzzH
|
|
twitter.oauth.consumerSecret=AbRxUAvyCtqQtvxFK8w5ZMtMj20KFhB6o
|
|
twitter.oauth.accessToken=21691649-4YZY5iJEOfz2A9qCFd9SjBRGb3HLmIm4HNE
|
|
twitter.oauth.accessTokenSecret=AbRxUAvyNCtqQtxFK8w5ZMtMj20KFhB6o]]></programlisting>
|
|
|
|
Then, you can configure a <code>property-placeholder</code> to point to the above property file:
|
|
|
|
<programlisting language="java"><![CDATA[<context:property-placeholder
|
|
location="classpath:oauth.properties"/>
|
|
|
|
<bean id="twitterTemplate" class="org.springframework.social.twitter.api.impl.TwitterTemplate">
|
|
<constructor-arg value="${twitter.oauth.consumerKey}"/>
|
|
<constructor-arg value="${twitter.oauth.consumerSecret}"/>
|
|
<constructor-arg value="${twitter.oauth.accessToken}"/>
|
|
<constructor-arg value="${twitter.oauth.accessTokenSecret}"/>
|
|
</bean>]]></programlisting>
|
|
</para>
|
|
</section>
|
|
|
|
<section id="twitter-inbound">
|
|
<title>Twitter Inbound Adapters</title>
|
|
<para>
|
|
Twitter inbound adapters allow you to receive Twitter Messages. There are several types of
|
|
<link linkend="http://support.twitter.com/groups/31-twitter-basics/topics/109-tweets-messages/articles/119138-types-of-tweets-and-where-they-appear">twitter messages, or tweets</link>
|
|
</para>
|
|
<para>
|
|
The current release of Spring Integration provides support for receiving tweets as <emphasis>Timeline Updates</emphasis>,
|
|
<emphasis>Direct Messages</emphasis>, <emphasis>Mention Messages</emphasis> as well as Search Results.
|
|
</para>
|
|
<para>
|
|
Every Inbound Twitter Channel Adapter is a <emphasis>Polling Consumer</emphasis> which means you have to provide a poller
|
|
configuration. However, there is one important thing you must understand about Twitter since its inner-workings are slightly
|
|
different than other polling consumers. Twitter defines a concept of Rate Limiting. You can read more about
|
|
it here: <link linkend="http://dev.twitter.com/pages/rate-limiting">Rate Limiting</link>. In a nutshell, Rate Limiting
|
|
is the way Twitter manages how often an application can poll for updates. You should consider this when setting your
|
|
poller intervals, but we are also doing a few things to limit excessively aggressive polling within our adapters.
|
|
</para>
|
|
<para>
|
|
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 timestamp will be stored in an instance of the <classname>org.springframework.integration.store.MetadataStore</classname> which is a
|
|
strategy interface designed for storing various types of metadata (e.g., last retrieved tweet in this case). That strategy helps components such as
|
|
these Twitter adapters avoid duplicates. By default, Spring Integration will look for a bean of type
|
|
<classname>org.springframework.integration.store.MetadataStore</classname> in the ApplicationContext.
|
|
If one is found then it will be used, otherwise it will create a new instance of <classname>SimpleMetadataStore</classname>
|
|
which is a simple in-memory implementation that will only persist metadata within the lifecycle of the currently running application context.
|
|
That means upon restart you may end up with duplicate entries. If you need to persist metadata between Application Context
|
|
restarts, you may use the <classname>PropertiesPersistingMetadataStore</classname> (which is backed by a properties file, and a persister
|
|
strategy), or you may create your own custom implementation of the <classname>MetadataStore</classname> interface (e.g., JdbcMetadatStore)
|
|
and configure it as a bean named 'metadataStore' within the Application Context.
|
|
<programlisting language="java"><![CDATA[<bean id="metadataStore" class="org.springframework.integration.store.PropertiesPersistingMetadataStore"/>
|
|
]]></programlisting>
|
|
The Poller that is configured as part of any Inbound Twitter Adapter (see below) will simply poll from this MetadataStore to determine the latest tweet
|
|
received.
|
|
</para>
|
|
<section id="inbound-twitter-update">
|
|
<title>Inbound Message Channel Adapter</title>
|
|
<para>
|
|
This adapter allows you to receive updates from everyone you follow. It's essentially the "Timeline Update" adapter.
|
|
<programlisting language="xml"><![CDATA[<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>]]></programlisting>
|
|
</para>
|
|
</section>
|
|
|
|
<section id="inbound-twitter-direct">
|
|
<title>Direct Inbound Message Channel Adapter</title>
|
|
<para>
|
|
This adapter allows you to receive Direct Messages that were sent to you from other Twitter users.
|
|
<programlisting language="xml"><![CDATA[<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>]]></programlisting>
|
|
</para>
|
|
</section>
|
|
|
|
<section id="inbound-twitter-mention">
|
|
<title>Mentions Inbound Message Channel Adapter</title>
|
|
<para>
|
|
This adapter allows you to receive Twitter Messages that Mention you via @user syntax.
|
|
<programlisting language="xml"><![CDATA[<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>]]></programlisting>
|
|
</para>
|
|
</section>
|
|
|
|
<section id="inbound-twitter-search">
|
|
<title>Search Inbound Message Channel Adapter</title>
|
|
<para>
|
|
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.
|
|
<programlisting language="xml"><![CDATA[<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>]]></programlisting>
|
|
|
|
</para>
|
|
<para>
|
|
Here is a link that will help you learn more about Twitter queries: http://search.twitter.com/operators
|
|
</para>
|
|
</section>
|
|
<para>
|
|
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 <code>twitter-template</code>. Once received each Twitter Message would be
|
|
encapsulated in a Spring Integration Message and sent to the channel specified by the <code>channel</code> attribute.
|
|
Currently the Payload type of any Message is <classname>org.springframework.integration.twitter.core.Tweet</classname>
|
|
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.
|
|
</para>
|
|
<para>
|
|
To get the text from the <classname>org.springframework.social.twitter.api.Tweet</classname>
|
|
simply invoke the <code>getText()</code> method.
|
|
</para>
|
|
</section>
|
|
|
|
<section id="twitter-outbound">
|
|
<title>Twitter Outbound Adapter</title>
|
|
<para>
|
|
Twitter outbound channel adapters allow you to send Twitter Messages, or tweets.
|
|
</para>
|
|
<para>
|
|
The current release of Spring Integration supports sending <emphasis>Status Update Messages</emphasis> and <emphasis>Direct Messages</emphasis>.
|
|
Twitter outbound channel adapters will take the Message payload and send it as a Twitter message. Currently the only supported payload type is
|
|
<classname>String</classname>, so consider adding a <emphasis>transformer</emphasis> if the payload of the incoming message is not a String.
|
|
</para>
|
|
|
|
<section id="outbound-twitter-update">
|
|
<title>Twitter Outbound Update Channel Adapter</title>
|
|
<para>
|
|
This adapter allows you to send regular status updates by simply sending a Message to the channel
|
|
identified by the <code>channel</code> attribute.
|
|
<programlisting language="xml"><![CDATA[<int-twitter:outbound-channel-adapter
|
|
twitter-template="twitterTemplate"
|
|
channel="twitterChannel"/>]]></programlisting>
|
|
The only extra configuration that is required for this adapter is the <code>twitter-template</code> reference.
|
|
</para>
|
|
</section>
|
|
|
|
<section id="outbound-twitter-direct">
|
|
<title>Twitter Outbound Direct Message Channel Adapter</title>
|
|
<para>
|
|
This adapter allows you to send Direct Twitter Messages (i.e., @user) by simply sending a Message to the channel
|
|
identified by the <code>channel</code> attribute.
|
|
<programlisting language="xml"><![CDATA[<int-twitter:dm-outbound-channel-adapter
|
|
twitter-template="twitterTemplate"
|
|
channel="twitterChannel"/>]]></programlisting>
|
|
The only extra configuration that is required for this adapter is the <code>twitter-template</code> reference.
|
|
</para>
|
|
<para>
|
|
When it comes to Twitter Direct Messages, you must specify who you are sending the message to - the <emphasis>target userid</emphasis>.
|
|
The Twitter Outbound Direct Message Channel Adapter will look for a target userid in the Message headers under the name
|
|
<code>twitter_dmTargetUserId</code> which is also identified by the following constant: <classname>TwitterHeaders.DM_TARGET_USER_ID</classname>.
|
|
So when creating a Message all you need to do is add a value for that header.
|
|
|
|
<programlisting language="java"><![CDATA[Message message = MessageBuilder.withPayload("hello")
|
|
.setHeader(TwitterHeaders.DM_TARGET_USER_ID, "z_oleg").build();]]></programlisting>
|
|
</para>
|
|
<para>
|
|
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>.
|
|
<programlisting language="xml"><![CDATA[<int:header-enricher input-channel="in" output-channel="out">
|
|
<int:header name="twitter_dmTargetUserId" value="z_oleg"/>
|
|
</int:header-enricher>]]></programlisting>
|
|
</para>
|
|
|
|
<para>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>.
|
|
<programlisting language="xml"><![CDATA[<int:header-enricher input-channel="in" output-channel="out">
|
|
<int:header name="twitter_dmTargetUserId" expression="@twitterIdService.lookup(headers.username)"/>
|
|
</int:header-enricher>]]></programlisting>
|
|
</para>
|
|
</section>
|
|
|
|
<para>
|
|
<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.
|
|
</important>
|
|
</para>
|
|
</section>
|
|
</chapter>
|