Receiving Camel Twitter Consumer Endpoint Data - java

I have created a Route that looks roughly like this:
#Override
public void configure() throws Exception {
from("direct:twitter")
.from("twitter://timeline/user?type=direct&user=" + this.uri)
.choice()
.when(body().isInstanceOf(Status.class))
.process(new MyTwitterProcessor())
.convertBodyTo(MyClass.class)
.to("log:do something")
.endChoice()
.to("log:mi amigo");
}
Calling this route directly from producerTemplate.requestBody("direct:twitter", object), I expected to receive a list of MyClass.class instances. Instead, it is returning the object I sent in the requestBody method call.
Based on log statements "log:do something" I can see that Status objects are being returned- the request and response from twitter are clearly occuring.
I would like to understand why my route configuration is returning the object I send it, rather than the Status object results from twitter. I have written two other routes for Facebook posts and an RSS feed. They follow a similar pattern and return the response objects, rather than the request I sent.
I would also like to know what I can do to change the behavior so that producerTemplate.requestBody(...) returns a list of twitter messages.
Thank you for your time.

Use the pollEnrich component to obtain additional data:
from("direct:twitter")
.pollEnrich("twitter://timeline/user?type=direct&user=" + this.uri)
.choice()
...
Alternatively, you may just use following route that is automatically started:
from("twitter://timeline/user?type=direct&user=" + this.uri)
.choice()
...
Note, that the twitter component creates one route exchange per returned object and not a list.

Related

Spring Cloud AWS SQSListener Interceptor for Converter

Please take into consideration that the following example is from a SNS Topic with a SQS subscription, not a simple sqs message.
I'm currently using an SQSListener which receives a message from an SNS Topic with success:
#SqsListener("\${my-test-queue}")
fun featurePlacingListener(#NotificationMessage myJob: MyJob, #Headers headers: MessageHeaders) {
// Some code here
}
I would like to have an interceptor somehow so when there's a deserilization error I can log and send a notification.
I know this can be done If I forget using the #NotificationMessage, grab the Message part and deserialize manually but I would like to keep this clean.
Spring AOP doesn't work because the converter does the job before.
Something similar to a Controller advice or a nice AOP interceptor would be great
org.springframework.messaging.converter.MessageConversionException: Could not read JSON: Cannot construct instance of `com.example.MyJob`, problem: Parameter specified as non-null is null: method com.example.MyJob.<init>, parameter myProperty
at org.springframework.messaging.converter.MappingJackson2MessageConverter.convertFromInternal
I just want to be able to detect when this happens.

Unable to create MockEndpoint for camel-timer

I am using Camel Timer component to read blobs from Azure storage container. A route is created which will poll for blobs every 10secs and is processed by the CloudBlobProcessor.
from("timer://testRoute?fixedRate=true&period=10s")
.to("azure-blob://storageAccountName/storageContainerName?credentials=#credentials")
.to(CloudBlobProcessor)
.to("mock:result");
I want to write a testcase by creating a mock endpoint something like this
MockEndpoint timerMockEndpoint = context.getEndpoint("timer://testRoute?fixedRate=true&period=10s", MockEndpoint.class);
But, I receive a below exception while creating the above mock endpoint.
java.lang.IllegalArgumentException: The endpoint is not of type:
class org.apache.camel.component.mock.MockEndpoint but is: org.apache.camel.component.timer.TimerEndpoint
Below is the code where I am trying to skip sending to the original endpoint
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
return new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
interceptSendToEndpoint("timer://testRoute?fixedRate=true&period=10s").skipSendToOriginalEndpoint()
.log("Original Batch Endpoint skipped")
.to("azure-blob://*")
.to(CloudBlobProcessor).to("mock:result");
from("timer://testRoute?fixedRate=true&period=10s").to("mock:result");
}
};
}
What I understand, we're trying to solve two different problems here:
MockEndpoint != TimerEndpoint
Interceptions
Answer to the first one is simple: MockEndpoints follow syntax mock:name. TimerEndpoint is a different endpoint and a totally different object. I don't know what you're aiming to do with the MockEndpoint here, but we just can't technically have a TimerEndpoint object as a MockEndpoint object. Why? Because that's how object oriented programming and Java work.
Let's take a look on the second problem. I've less than a year experience with Camel and I've only used interception once last year, but I hope I can guide you to some helpful direction.
The point of interception is to say "don't do that, do this instead". In this use case, it seems that we're only trying to skip sending a request to azure-blob endpoint. I'd try intercepting azure-blob://storageAccountName/storageContainerName?credentials=#credentials.
So instead of your interception, I'd try writing an interception like this:
interceptSendToEndpoint("azure-blob://storageAccountName/storageContainerName?credentials=#credentials")
.skipSendToOriginalEndpoint()
.log("Intercepted!");
In this case, instead of sending the request to azure-blob we intercept that request. We're telling Camel to skip the send to original endpoint, which means nothing will be sent to azure-blob://storageAccountName/storageContainerName?credentials=#credentials. Instead, we'll log "Intercepted!".

GetEntity vs ReadEntity in Response (Javax.ws.rs)

I am writing a client to consume a RestService and I have to read an entity out of the response and I am totally confused which method out of the two (getEntity vs readEntity) should be used.
I have to retrieve the entity whenever I get a WebApplicationException.
So, my code more or less looks like.
catch(WebApplicationException ex) {
// Do something with ex.getResponse
}
From, whatever I have tested,
ex.getResponse().hasEntity() ---> true
ex.getResponse().getEntity() == null ---> true
I don't know how it is working but if the first is true then how second statement could be true.
Surprisingly, readEntity worked fine for me and I was able to read the
entity out from the response.
But, after reading the entity through readEntity,
this call gives false.
ex.getResponse().getEntity() == null --> false
Can someone help me understand what is really happening behind the scenes?
The Response class has two uses: server side and client side. On the server side, it's called the outbound response. On the client, it's inbound response.
Outbound
#GET
public Response get() {
MyModel model = new MyModel();
return Response.ok(model).build();
}
Inbound
Response response = ClientBuilder.newClient().target(url).request().get();
The getEntity() method is meant to be used on the server side because you want to get the entity object. There's not much use for it for us, but Jersey will use it to get the entity object to serialize it before it sends it out.
The readEntity() method is to be used on the client side because you are trying to read the entity stream. If you try to call this on the server side, you will get an error saying that you can't read the stream on an outbound response.
As far as the behavior you're experiencing, I can't really explain why they implemented like this.
This behaviour is documented in the API:
public abstract <T> T readEntity(Class<T> entityType)
Read the message entity input stream as an instance of specified Java type using a MessageBodyReader that supports mapping the message entity stream onto the requested type.
[...]
A message instance returned from this method will be cached for subsequent retrievals via getEntity().
The first call to ex.getResponse().getEntity() is null, because the Entity hasn't been read yet. After calling readEntity(), the parsed entity will be returned as parsed by getEntity().

Spring Integration - Exceptions and retries

My setup:
I have a message daemon using ActiveMQ which will consume JSON messages.
The publisher of JSON messages adds type header with value, for ex, com.example.Foo which is the type of the json message. I use this to transform json to pojo.
Spring config:
Once the message is received, these are the steps it goes through:
1. Transformer: Transforms json to pojo
2. Payload type router: Based on the type of the pojo, routes the pojo to appropriate service activator.
3. Service activator: Process the message.
<int:chain input-channel="transformerChannel">
<int:transformer id="jsonToPojoTransformer" ref="JsonToPojoTransformer" method="transform" />
<int:payload-type-router default-output-channel="defaultChannel">
<int:mapping type="com.example.Foo" channel="fooHandlerChannel"/>
<int:mapping type="com.example.Bar" channel="barHandlerChannel"/>
</int:payload-type-router>
</int:chain>
<int:service-activator input-channel="fooHandlerChannel" ref="fooHandler" method="onMessage"/>
<int:service-activator input-channel="barHandlerChannel" ref="barHandler" method="onMessage"/>
Service activator definition:
public class FooHandler {
public void onMessage(Foo foo) {...}
}
Problem:
I want to know how to access the message headers in the service activator. It seems like the service activator does not have access to the message headers since the transformer is returning a pojo.
Lets say the service activator is unable to call a down stream rest service for whatever reason. I want to skip processing this message now and I want to retry this message later. Or lets say there was an exception in processing this message. I want to retry processing this message after some delay. How do I accomplish this?
--edit--
Removed details to reduce question size as per Artem's comment.
Please, try do not make so long topics here in SO. It's hard to answer particular question if there are to many of them.
Absolutely not clear why you can't get access to header from your service activator method. You can accept the whole Message<>, and call its getHeaders(). You can use#Headersannotation on theMaparg to get headers from the message. You can use#Header` annotation to extract exactly particular header from the message.
Even if your transformer method returns just a POJO that doesn't mean that it isn't wrapped to the Message with the headers from requestMessage. If you need to return specific header alongside with your POJO, you should create Message yourself, using MessageBuilder and don't forget to copy requestMessage headers, just because transformer doesn't copy request headers if the whole message is returned.
You have to support TX on your JMS consumer, so that RuntimeException will lead to the rollback and, therefore, redelivery eventually. And you should ensure that all the flow is performed in the same thread. Otherwise TX is committed and message is acked on the broker. The same happens when you don't have transactions.

asynchronous handleResponse invoked twice for single webmethod request

I have developed a asynchronous JAX-RS web method using Apache CXF API. The webmethod takes a custom type as parameter as in
#POST
#Path("/query")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(value={MediaType.APPLICATION_JSON , "application/CustomType"})
public void getQueryResults(#Suspended final AsyncResponse asyncResponse, CustomType conf)
I had implemented(Service as well as Client side) a Custom MessageBodyReader and MessageBodyWriter to take care of serializing my 'CustomType'.
On the Client side i ergister the imlpemented ones as
Client client = ClientBuilder.newClient().register(CacheConfigReader.class).register(CacheConfigWriter.class);
I make a async request to the service that has
asyncResponse.resume(result); // result is a string
On the client
Future<String> future = asyncInvoker.post(entity, String.class);
My observation is that randomly the response is empty though on the server logs am able to see non-empty result. upon debugging i find that there are two threads that invoke
JaxrsClientCallback . handleResponse()
One of them with the actual result and another empty. Based on what executes first the result is the actual string or empty. The trace of the call contains invocation from phase interceptor chain.
This occurs only when i register the client with Custom reader and writers. When I set the request body with a json only one thread handles the response.
Can someone shed light on why the addition of MessageBodyReaders / Writers causes this issue ?

Categories

Resources