twitter hosebird client maven usage - maven

hi i'm a noob in this but i want to learn how to use the hosebird client, i downloaded it but from the readme don't understand how to use that. I installed eclipse Java EE and maven in my pc but from the README file in hbc don't see how to connect that to my eclipse. can anyone help me with a list of the thing that i have to do?
this is the readme, i have never used maven before.
thanks
## Getting Started
The Hosebird client is broken down into two modules: hbc-core and hbc-twitter4j. The hbc-core module uses a message queue, which the consumer can poll for the raw String messages, while the hbc-twitter4j module uses the [twitter4j](http://twitter4j.org) listeners and data model on top of the message queue to provide a parsing layer.
The latest hbc artifacts are published to maven central. Bringing hbc into your project should be as simple as adding the following to your maven pom.xml file:
```xml
<dependencies>
<dependency>
<groupId>com.twitter</groupId>
<artifactId>hbc-core</artifactId> <!-- or hbc-twitter4j -->
<version>2.2.0</version> <!-- or whatever the latest version is -->
</dependency>
</dependencies>
```
### Quickstart
Declaring the connection information:
```java
/** Set up your blocking queues: Be sure to size these properly based on expected TPS of your stream */
BlockingQueue<String> msgQueue = new LinkedBlockingQueue<String>(100000);
BlockingQueue<Event> eventQueue = new LinkedBlockingQueue<Event>(1000);
/** Declare the host you want to connect to, the endpoint, and authentication (basic auth or oauth) */
Hosts hosebirdHosts = new HttpHosts(Constants.STREAM_HOST);
StatusesFilterEndpoint hosebirdEndpoint = new StatusesFilterEndpoint();
// Optional: set up some followings and track terms
List<Long> followings = Lists.newArrayList(1234L, 566788L);
List<String> terms = Lists.newArrayList("twitter", "api");
hosebirdEndpoint.followings(followings);
hosebirdEndpoint.trackTerms(terms);
// These secrets should be read from a config file
Authentication hosebirdAuth = new OAuth1("consumerKey", "consumerSecret", "token", "secret");
```
Creating a client:
```java
ClientBuilder builder = new ClientBuilder()
.name("Hosebird-Client-01") // optional: mainly for the logs
.hosts(hosebirdHosts)
.authentication(hosebirdAuth)
.endpoint(hosebirdEndpoint)
.processor(new StringDelimitedProcessor(msgQueue))
.eventMessageQueue(eventQueue); // optional: use this if you want to process client events
Client hosebirdClient = builder.build();
// Attempts to establish a connection.
hosebirdClient.connect();
```
Now, msgQueue and eventQueue will now start being filled with messages/events. Read from these queues however you like.
```java
// on a different thread, or multiple different threads....
while (!hosebirdClient.isDone()) {
String msg = msgQueue.take();
something(msg);
profit();
}
```
You can close a connection with
```java
hosebirdClient.shutdown();
```
### Quick Start Example
To run the sample stream example:
```
mvn install && mvn exec:java -pl hbc-example -Dconsumer.key=XYZ -Dconsumer.secret=SECRET -Daccess.token=ABC -Daccess.token.secret=ABCSECRET
```
You can find these values on http://dev.twitter.com and navigating to one of your applications then to the API Keys tab.
The API key and secrets values on that page correspond to hbc's `-Dconsumer.*` properties.
Alternatively you can set those properties in hbc-examples/pom.xml
## The Details
### Authentication:
Declaring OAuth1 credentials in the client (preferred):
```java
new OAuth1("consumerKey", "consumerSecret", "token", "tokenSecret")
```
Declaring basic auth credentials in the client:
```java
new BasicAuth("username", "password")
```
Be sure not to pass your tokens/passwords as strings directly into the initializers. They should be read from a configuration file that isn't checked in with your code or something similar. Safety first.
### Specifying an endpoint
Declare a StreamingEndpoint to connect to. These classes reside in the package com.twitter.hbc.core.endpoint, and correspond to all of our endpoints. By default, the HTTP parameter "delimited=length" is set for all of our StreamingEndpoints for compatibility with our processor (next section). If you are using our StringDelimitedProcessor this parameter must be set. For a list of available public endpoints and the http parameters we support, see [Twitter's Streaming API docs](https://dev.twitter.com/docs/streaming-apis/streams/public).
#### Filter streams:
```java
StatusesFilterEndpoint endpoint = new StatusesFilterEndpoint();
// Optional: set up some followings and track terms
List<Long> followings = Lists.newArrayList(1234L, 566788L);
List<String> terms = Lists.newArrayList("twitter", "api");
endpoint.followings(followings);
endpoint.trackTerms(terms);
```
#### Firehose streams:
```java
StreamingEndpoint endpoint = new StatusesFirehoseEndpoint();
// Optional: set up the partitions you want to connect to
List<Integer> partitions = Lists.newArrayList(0,1,2,3);
endpoint.partitions(partitions);
// By default, delimited=length is already set for use by our StringDelimitedProcessor
// Do this to unset it (Be sure you really want to do this)
// endpoint.delimited(false);
```
#### Setting up a Processor:
The hosebird client uses the notion of a "processor" which processes the stream and put individual messages into the provided BlockingQueue. We provide a StringDelimitedProcessor class which should be used in conjunction with the StreamingEndpoints provided. The processor takes as its parameter a BlockingQueue, which the client will put String messages into as it streams them.
Setting up a StringDelimitedProcessor is as easy as:
```java
new StringDelimitedProcessor(msgQueue);
```
### Control streams for Sitestream connections
Hosebird provides [control stream support for sitestreams](https://dev.twitter.com/docs/streaming-apis/streams/site/control).
To make control stream calls with the hosebird client, first create a client. When calling connect() to create a connection to a stream with control stream support, the first message you receive will be the streamId. You'll want to hold on to that when processing the messages if you plan on using control streams, so after calling connect(), be sure to keep track of the streamId of this connection. Note that due to reconnections, the streamId could change, so always use the latest one. If you're using our twitter4j layer, keeping track of the control messages/streamIds will be taken care of for you.
```java
SitestreamController controlStreams = client.getSitestreamController();
// When making a connection to the stream with control stream support one of the response messages will include the streamId.
// You'll want to hold on to that when processing the messages if you plan on using control streams
// add userId to our control stream
controlStreams.addUser(streamId, userId);
// remove userId to our control stream
controlStreams.removeUser(streamId, userId);
```
### The hbc-twitter4j module
The hbc-twitter4j module uses the twitter4j listeners and models. To use it, create a normal Client object like before using the ClientBuilder, then depending on which type of stream you are reading from, create an appropriate Twitter4jClient. The Twitter4jClient wraps around the Client it is passed, and calls the callback methods in the twitter4j listeners whenever it retrieves a message from the message queue. The actual work of polling from the message queue, parsing, and executing the callback method is done by forking threads from an executor service that the client is passed.
If connecting to a status stream (filter, firehose, sample), use Twitter4jStatusClient:
```java
// client is our Client object
// msgQueue is our BlockingQueue<String> of messages that the handlers will receive from
// listeners is a List<StatusListener> of the t4j StatusListeners
// executorService
Twitter4jClient t4jClient = new Twitter4jStatusClient(client, msgQueue, listeners, executorService);
t4jClient.connect();
// Call this once for every thread you want to spin off for processing the raw messages.
// This should be called at least once.
t4jClient.process(); // required to start processing the messages
t4jClient.process(); // optional: another Runnable is submitted to the executorService to process the msgQueue
t4jClient.process(); // optional
```
If connecting to a userstream, use Twitter4jUserstreamClient. If making a sitestream connection, use Twitter4jSitestreamClient.
#### Using Handlers, a Twitter4j listener add-on
All Twitter4jClients support Handlers, which extend their respective Twitter4j listeners: StatusStreamHandler extends StatusesListener, UserstreamHandler extends UserstreamListener, SitestreamHandler extends SitestreamHandler. These handlers have extra callback menthods that may be helpful for parsing messages that the Twitter4j listeners do not yet support
```java
UserstreamListener listener = new UserstreamHandler() {
/**
* <UserstreamListener methods here>
*/
#Override
public void onDisconnectMessage(DisconnectMessage disconnectMessage) {
// this method is called when a disconnect message is received
}
#Override
public void onUnfollow(User source, User target) {
// do something
}
#Override
public void onRetweet(User source, User target, Status retweetedStatus) {
// do your thing
}
#Override
public void onUnknownMessageType(String msg) {
// msg is any message that isn't handled by any of our other callbacks
}
}
listeners.append(listener);
Twitter4jClient t4jClient = new Twitter4jUserstreamClient(client, msgQueue, listeners, executorService);
```
## Building / Testing
To build locally (you must use java 1.7 for compiling, though we produce 1.6 compatible classes):
```
mvn compile
```
To run tests:
```
mvn test
```

You can install the Eclipse Maven plugin then compile from Eclipse, or you can do something simple and straightforward like me. Start a command prompt then from the folder of unzipped hbc-master, e.g. c:\downloads\hbc-master, run the following command:
c:\downloads\hbc-master>c:\downloads\apache-maven-3.2.3-bin\apache-maven
-3.2.3\bin\mvn compile
This will compile all the HBC classes. Then you can put all the generated class files including hbc-master\hbc-core\target\classes\ and hbc-master\hbc-twitter4j\target\classes\ into one folder then use the Jar command to zip them together as one Jar file that you can add to your Eclipse project, e.g.
c:\downloads\hbc-master\mybuild>jar cvf Twitter-api.jar com twitter4j
This will provide all the classes that allow you to run the simple examples like example\FilterStreamExample. However, for more complex examples like EnterpriseStreamExample and Twitter4jSampleStreamExample, you will need more to grab more libraries including twitter4j-core, twitter4j-stream, and guava.jar (Google collections).
Hope this helps.

Related

Is there a possibility to set spring integration mail inbound adapter flags after handling the message?

Intro:
We're currently using the spring mail integration to receive and send emails which works without flaws if there's no exception such as a connection error to the exchange server or the database.
These mails come in as Messages and are passed to a handler method which will parse the MimeMessage to a custom mail data object. JPA saves those entities as the last step to our database.
Question/Problem:
There's a problem if the database is down or the mail can't be processed for any other reason, as the IntegrationFlow will still mark it as /SEEN once the message gets passed to the handler.
Setting this flag to false won't fix our problem, because we want Spring to set the /SEEN flag if the mail is processed and saved correctly
shouldMarkMessagesAsRead(false)
Searching for:
Would there be a possibility to set flags AFTER successfully saving the mail to the database?
We'd like to process the failed email again after the cause for the responsible error is fixed, which won't work as long Spring marks them as /SEEN no matter the result.
Reference:
The messages comes in and gets passed to the handler which will parse the mail and execute the CRUD-Repository save(mailDAO) method. The handleMimeMessage() is more or less just a mapper.
#Bean
fun imapIdleFlow(imapProperties: ImapProperties): IntegrationFlow {
imapProperties.username.let { URLEncoder.encode(it, charset) }
return IntegrationFlows
.from(
Mail.imapIdleAdapter(
ImapMailReceiver("imap://${imapProperties.username}:${imapProperties.password}#${imapProperties.host}/Inbox")
.apply {
setSimpleContent(true)
setJavaMailProperties(imapProperties.properties.toProperties())
})
.autoStartup(true)
.shouldReconnectAutomatically(true)
)
.handle(this::handleMimeMessage)
.get()
}
Is it even possible to mark the messages in the same flow afterward as you need to access the exchange a second time or would I need a second flow to get and flag the same mail?
I think it is possible with something like transaction synchronization: https://docs.spring.io/spring-integration/reference/html/mail.html#mail-tx-sync
So, you set transactional(TransactionManager transactionManager) on that Mail.imapIdleAdapter to the JpaTransactionManager to start transaction from this IMAP Idle channel adapter and propagate it to your handleMimeMessage() where you do those JPA saves.
Plus you add:
/**
* Configure a {#link TransactionSynchronizationFactory}. Usually used to synchronize
* message deletion with some external transaction manager.
* #param transactionSynchronizationFactory the transactionSynchronizationFactory.
* #return the spec.
*/
public ImapIdleChannelAdapterSpec transactionSynchronizationFactory(
TransactionSynchronizationFactory transactionSynchronizationFactory) {
To react for commit and rollback of the mentioned transaction.
The DefaultTransactionSynchronizationFactory with some TransactionSynchronizationProcessor impl can give you a desired behavior, where you take a Message and its payload from the provided IntegrationResourceHolder and perform something like message.setFlag(Flag.SEEN, true); on the MimeMessage.
You may consider to use the mentioned in docs an ExpressionEvaluatingTransactionSynchronizationProcessor.
To avoid folder reopening, you may consider to use a public ImapIdleChannelAdapterSpec autoCloseFolder(boolean autoCloseFolder) { with a false option. You need to consider to close it in that TX sync impl or some other way.

Spring AMQP Get Existing Queue Names and Argument

We have a queue in production where message TTL was set via application. Now we want to change message TTL and attach policy via rabbit CTL than setting within application. Snippet:
Map<String, Object> args = new HashMap<>();
args.put("x-message-ttl", 86400000);
for (String queueName : queueNames) {
Queue queue = new Queue(queueName, true, false, false, args);
admin.declareQueue(queue);
...
}
To achieve this in running application we want way to validate if Queue already exists do nothing otherwise create new Queue without args. It is not possible to leverage local cache as multiple publisher/subscriber nodes can restart under unplanned outage scenario. With above would be able to change TTL during Rabbit upgrade/Migration
Can you help if there is an API to fetch all existing queues and its argument properties?
Note: Overriding x-message-ttl with different value throws error.
RabbitMQ has a REST API and a java client for it.
You can use that to get information about existing elements such as queues.

Testing MassTransit endpoint configuration in Autofac module

I have some endpoint configuration code in an Autofac module that's registering consumers based on conventions that I'd like to unit test. I'm not trying to verify any behaviour of any consumers I just want to check that my setup code is doing what I need it to do. I'm using InMemoryTestHarness but consuming doesn't seem to be working and I'm not sure about the correlation between configuring the bus and registering consumer test harnesses.
To allow the host to be swapped between Rabbit for prod and in memory for tests I have this in my module:
Func<Action<IReceiveConfigurator>, IBusControl> BusFactory = receiveConfig => Bus.Factory.CreateUsingRabbitMq(cfg =>
{
cfg.Host(rabbitMqUrl, hostCfg =>
{
hostCfg.Username(rabbitMqUsername);
hostCfg.Password(rabbitMqPassword);
});
receiveConfig(cfg);
});
For the actual consumer registration in my module I have:
// code to scan assembly and build a list of queue definitions with consumers
...
// consumer registration
builder.AddMassTransit(x =>
{
foreach(var consumerType in consumerTypes)
x.AddConsumer(consumerType);
x.AddBus(context => BusFactory(cfg =>
{
foreach(var queueDef in queueDefs)
cfg.ReceiveEndpoint(queueDef.QueueName, e =>
{
foreach(var consumerDef in queueDef.ConsumerDefs)
e.ConfigureConsumer(context, consumerDef.ConsumerType);
});
});
});
For the unit test setup I am doing:
harness = new InMemoryTestHarness();
var module = new MassTransitModule(typeof(TestMessageConsumer).Assembly)
{
BusFactory = (receiveConfig) =>
{
harness.OnConfigureBus += cfg => receiveConfig(cfg);
Task.WaitAll(harness.Start());
return harness.BusControl;
}
};
var builder = new ContainerBuilder();
builder.RegisterModule(module);
container = builder.Build();
// ensure bus initialisation runs
container.Resolve<IBusControl>();
I've verified in the unit test that Autofac can resolve IBus, IBusControl and concrete consumer classes, as well as, given a message type T an IConsumer<T>.
In my tests, if I do:
await harness.InputQueueSendEndpoint.Send(new TestMessage());
harness.Consumed.Select<TestMessage>().Any().ShouldBeTrue();
then first the test waits on the harness.Consumed line for 30 seconds then the test fails (Any() returns false). I get the same behaviour if I register a consumer harness - plus I'm worried that registering a consumer harness doesn't actually verify my registration.
Have I misunderstood something with the test harness? How would I verify that my consumer config is correct? Is the harness.Consume line taking 30 seconds an indication that I've completely misused the test harness? So many questions...
Thanks,
Daniel
EDIT
Based on the comment from Chris Patterson I've updated my registration to use the MassTransit Autofac integration methods (code updated above) but still getting the same problem.
The test harness creates its own bus instance, and the Consumer, Saga, etc. methods add additional harnesses to that same test harness. If you're resolving a bus from the container as part of your test, you're stuck using that bus. The one in the harness is of no use to you, as are the methods in that harness.
You should separate the testing of your consumers from testing the container registration. And while you're at it, why not use the built-in container support for configuring endpoints, etc. instead of writing it yourself? I believe there is an extension method for .AddMassTransit to AddConsumersFromContainer where you specify the container. This makes it usable with previously loaded modules that added consumers to the container, where the bus is in its own module.

How to better correlate Spring Integration TCP Inbound and Outbound Adapters within the same application?

I currently have a Spring Integration application which is utilizing a number of TCP inbound and outbound adapter combinations for message handling. All of these adapter combinations utilize the same single MessageEndpoint for request processing and the same single MessagingGateway for response sending.
The MessageEndpoint’s final output channel is a DirectChannel that is also the DefaultRequestChannel of the MessageGateway. This DirectChannel utilizes the default RoundRobinLoadBalancingStrategy which is doing a Round Robin search for the correct Outbound Adapter to send the given response through. Of course, this round robin search does not always find the appropriate Outbound Adapter on first search and when it doesn’t it logs accordingly. Not only is this producing a large amount of unwanted logging but it also raises some performance concerns as I anticipate several hundred inbound/outbound adapter combinations existing at any given time.
I am wondering if there is a way in which I can more closely correlate the inbound and outbound adapters in a way that there is no need for the round robin processing and each response can be sent directly to the corresponding outbound adapter? Ideally, I would like this to be implemented in a way that the use of a single MessageEndpoint and single MessageGateway can be maintained.
Note: Please limit solutions to those which use the Inbound/Outbound Adapter combinations. The use of TcpInbound/TcpOutboundGateways is not possible for my implementation as I need to send multiple responses to a single request and, to my knowledge, this can only be done with the use of inbound/outbound adapters.
To add some clarity, below is a condensed version of the current implementation described. I have tried to clear out any unrelated code just to make things easier to read...
// Inbound/Outbound Adapter creation (part of a service that is used to dynamically create varying number of inbound/outbound adapter combinations)
public void configureAdapterCombination(int port) {
TcpNioServerConnectionFactory connectionFactory = new TcpNioServerConnectionFactory(port);
// Connection Factory registered with Application Context bean factory (removed for readability)...
TcpReceivingChannelAdapter inboundAdapter = new TcpReceivingChannelAdapter();
inboundAdapter.setConnectionFactory(connectionFactory);
inboundAdapter.setOutputChannel(context.getBean("sendFirstResponse", DirectChannel.class));
// Inbound Adapter registered with Application Context bean factory (removed for readability)...
TcpSendingMessageHandler outboundAdapter = new TcpSendingMessageHandler();
outboundAdapter.setConnectionFactory(connectionFactory);
// Outbound Adapter registered with Application Context bean factory (removed for readability)...
context.getBean("outboundResponse", DirectChannel.class).subscribe(outboundAdapter);
}
// Message Endpoint for processing requests
#MessageEndpoint
public class RequestProcessor {
#Autowired
private OutboundResponseGateway outboundResponseGateway;
// Direct Channel which is using Round Robin lookup
#Bean
public DirectChannel outboundResponse() {
return new DirectChannel();
}
// Removed additional, unrelated, endpoints for readability...
#ServiceActivator(inputChannel="sendFirstResponse", outputChannel="sendSecondResponse")
public Message<String> sendFirstResponse(Message<String> message) {
// Unrelated message processing/response generation excluded...
outboundResponseGateway.sendOutboundResponse("First Response", message.getHeaders().get(IpHeaders.CONNECTION_ID, String.class));
return message;
}
// Service Activator that puts second response on the request channel of the Message Gateway
#ServiceActivator(inputChannel = "sendSecondResponse", outputChannel="outboundResponse")
public Message<String> processQuery(Message<String> message) {
// Unrelated message processing/response generation excluded...
return MessageBuilder.withPayload("Second Response").copyHeaders(message.getHeaders()).build();
}
}
// Messaging Gateway for sending responses
#MessagingGateway(defaultRequestChannel="outboundResponse")
public interface OutboundResponseGateway {
public void sendOutboundResponse(#Payload String payload, #Header(IpHeaders.CONNECTION_ID) String connectionId);
}
SOLUTION:
#Artem's suggestions in the comments/answers below seem to do the trick. Just wanted to make a quick note about how I was able to add a replyChannel to each Outbound Adapter on creation.
What I did was create two maps that are being maintained by the application. The first map is populated whenever a new Inbound/Outbound adapter combination is created and it is a mapping of ConnectionFactory name to replyChannel name. The second map is a map of ConnectionId to replyChannel name and this is populated on any new TcpConnectionOpenEvent via an EventListener.
Note that every TcpConnectionOpenEvent will have a ConnectionFactoryName and ConnectionId property defined based on where/how the connection is established.
From there, whenever a new request is received I use theses maps and the 'ip_connectionId' header on the Message to add a replyChannel header to the Message. The first response is sent by manually grabbing the corresponding replyChannel (based on the value of the replyChannel header) from the application's context and sending the response on that channel. The second response is sent via Spring Integration using the replyChannel header on the message as Artem describes in his responses.
This solution was implemented as a quick proof of concept and is just something that worked for my current implementation. Including this to hopefully jumpstart other viewer's own implementations/solutions.
Well, I see now your point about round-robin. You create many similar TCP channel adapters against the same channels. In this case it is indeed hard to distinguish one flow from another because you have a little control over those channels and their subscribers.
On of the solution would be grate with Spring Integration Java DSL and its dynamic flows: https://docs.spring.io/spring-integration/reference/html/dsl.html#java-dsl-runtime-flows
So, you would concentrate only on the flows and won't worry about runtime registration. But since you are not there and you deal just with plain Java & Annotations configuration, it is much harder for you to achieve a goal. But still...
You may be know that there is something like replyChannel header. It is taken into an account when we don't have a outputChannel configured. This way you would be able to have an isolated channel for each flow and the configuration would be really the same for all the flows.
So,
I would create a new channel for each configureAdapterCombination() call.
Propagate this one into that method for replyChannel.subscribe(outboundAdapter);
Use this channel in the beginning of your particular flow to populate it into a replyChannel header.
This way your processQuery() service-activator should go without an outputChannel. It is going to be selected from the replyChannel header for a proper outbound channel adapter correlation.
You don't need a #MessagingGateway for such a scenario since we don't have a fixed defaultRequestChannel any more. In the sendFirstResponse() service method you just take a replyChannel header and send a newly created message manually. Technically it is exactly the same what you try to do with a mentioned #MessagingGateway.
For Java DSL variant I would go with a filter on the PublishSubscribeChannel to discard those messages which don't belong to the current flow. Anyway it is a different story.
Try to figure out how you can have a reply channel per flow when you configure particular configureAdapterCombination().

Netty: How to add websocket handshake and framing while still supporting native socket

To me it looks like there is no out of the box support with mixed websocket/native socket for Netty 4. I'm using custom binary protocol on my server and it is supposed to support both native and websocket on the same port. Here is what I'm trying in my ServerInitializer:
#Override
public void initChannel(SocketChannel ch) {
System.out.println("channel initialized");
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new HttpServerCodec());
pipeline.addLast(new HttpObjectAggregator(65536));
// client decoders cannot be singleton....
pipeline.addLast(new WebSocketDecoder(), new ClientCommandDecoder());
pipeline.addLast(this.webSocketEncoder, this.serverCommandEncoder);
pipeline.addLast(this.roomHandler);
}
The WebSocketDecoder is taken from the examples, however it seems to use a handshaker which handles only FullHttpRequests which makes use of HttpObjectAggregator mandatory.
However both HttpServerCodec and HttpObjectAggregator don't seem to pass the input data by if it is not HTTP requests. So here is what I wonder:
Can I write custom implementations of given classes and override logic in order to pass the input data if it is not web socket but native
Or can I somehow detect if input data is from websocket and swerve to two different flows (one with HTTP support, other without)
You will need to adjust the pipeline on the fly depending on your input.
Please check our PortUnification example...

Resources