You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@camel.apache.org by Martin Gilday <ma...@imap.cc> on 2008/03/27 11:44:45 UTC

Request Reponse with Camel

Hi,
I am currently trying to find the best way to implement request response
with ActiveMQ.  I am considering Lingo as well as Camel.  This page on
the ActiveMQ site [1] suggests that using Camel as a Spring Remoting
provider is the best solution.  However the page it links to [2] is
seemingly only a partial listing of the test case from camel-spring.  I
have attempted to follow the test case, however this resulted in errors
once I changed the example from direct to JMS.  This lead me to finding
this posting [3] on the mailing list describing the same fault.  It
seems very strange to me that the recommended solution from the ActiveMQ
site does not actually work.  Is using Camel for JMS request response
actually the current recommended practice, or is the suggestion
incorrect?

Thanks,
Martin

[1]http://activemq.apache.org/how-should-i-implement-request-response-with-jms.html
[2]http://activemq.apache.org/camel/spring-remoting.html
[3]http://www.nabble.com/spring-remoting-over-jms-td14640388s22882.html

Re: Request Reponse with Camel

Posted by Martin Gilday <ma...@imap.cc>.
Sorry for bumping my own topic, but does anyone have any ideas of how to
implement this in Camel?


----- Original message -----
From: "Martin Gilday" <ma...@imap.cc>
To: camel-user@activemq.apache.org
Date: Tue, 01 Apr 2008 10:57:59 +0100
Subject: Re: Request Reponse with Camel

I've drawn up a rough diagram to try and clear up what I am attempting
to describe. 
http://public.mgilday.imap.cc/routes.png

Here source 2 is a client (at the moment a Struts 2 action) which
performs some rudimentary operations through a Spring based service to
persist incoming data.  It then places a message which is destined for
trade.queue.  This is then consumed by a Spring service bean which
returns details of the trade.  This result should be returned to the
original client (the Struts 2 action).  However a based on the header or
content of the result it should also be placed on other JMS queues. 
This is the snippet I have in my concept so far:

from("test-jms:queue:test.queue")
.beanRef("tradingService", "trade")
.choice()
.when(header("source").isEqualTo("source2"))
.to("test-jms:queue:some.other.queue4")
.end();

The problem is that the client does not receive a response.  It seems to
wait until the message placed on some.other.queue4 is consumed.  Then
lots of "Future timed out" exceptions are thrown.

In your previous reply you mention placing onto a named queue/setting
the replyTo header rather than a temp queue.  If I did this I would have
to start correlating messages back to the correct client, which I don't
think I could do if I was going through a Spring remoting proxy.

I have found that my own headers set on the original message are not
maintained along the whole route.  Am I responsible for passing them
along the route myself, or can I say that a header going in to
TradingService should also be an outheader without copying it manually?

Thanks,
Martin

Re: Request Reponse with Camel

Posted by Martin Gilday <ma...@imap.cc>.
I've drawn up a rough diagram to try and clear up what I am attempting
to describe. 
http://public.mgilday.imap.cc/routes.png

Here source 2 is a client (at the moment a Struts 2 action) which
performs some rudimentary operations through a Spring based service to
persist incoming data.  It then places a message which is destined for
trade.queue.  This is then consumed by a Spring service bean which
returns details of the trade.  This result should be returned to the
original client (the Struts 2 action).  However a based on the header or
content of the result it should also be placed on other JMS queues. 
This is the snippet I have in my concept so far:

from("test-jms:queue:test.queue")
.beanRef("tradingService", "trade")
.choice()
.when(header("source").isEqualTo("source2"))
.to("test-jms:queue:some.other.queue4")
.end();

The problem is that the client does not receive a response.  It seems to
wait until the message placed on some.other.queue4 is consumed.  Then
lots of "Future timed out" exceptions are thrown.

In your previous reply you mention placing onto a named queue/setting
the replyTo header rather than a temp queue.  If I did this I would have
to start correlating messages back to the correct client, which I don't
think I could do if I was going through a Spring remoting proxy.

I have found that my own headers set on the original message are not
maintained along the whole route.  Am I responsible for passing them
along the route myself, or can I say that a header going in to
TradingService should also be an outheader without copying it manually?

Thanks,
Martin

Re: Request Reponse with Camel

Posted by James Strachan <ja...@gmail.com>.
On 31/03/2008, Martin Gilday <ma...@imap.cc> wrote:
> > So the missing bit of functionality is to be able to mark operations
>  > on your bean as being asynchronous; so that the client side doesn't
>  > block waiting for the results.
>
>
> No I'm not sure that's what I mean.  I want the route to place onto the
>  2nd (or nth) JMS queue after a bean invocation, but not have the route
>  wait for the consumer of that JMS queue to return.

So the bean invocation is async (it doesn't wait to consume the
response). Though rather than using a temporary queue, you wanna
specify the reply-to destination right?

i.e. its an async invocation; but the JMSReplyTo destination is specified.


> So the placing on
>  the "to" is async not the consuming, or is this the same thing? Does
>  that seem like a reasonable thing to be trying to do?
>
>  So it would be something like:
>
>  from("test-jms:queue:test.queue")
>
> .beanRef("tradingService", "trade")
>  .choice().when(header("distribute").isEqualTo(true)).to("test-jms:queue:data.distribution.queue",
>  ExchangePattern.InOnly)  //PATTERN SPECIFIED HERE
>  .end();
>
>  from("test-jms:queue:data.distribution.queue")
>  .beanRef("distributionService", "distribute");

Oh hang on a minute - I think we were just talking at cross purposes :)

I was talking about the Spring Remoting - the client side invoking a
bean causing JMS operations to occur. I was talking about the client
invoking the bean blocking waiting to consume the JMS message
containing the response.

The route you've described is the server side; consuming a message,
invoking a bean and sending the response to whatever queue you want.
If so thats fine.

If an inbound JMS message has a JMSReplyTo header specified, then
it'll automatically forward to whereever the caller requested.

i.e. if someone sends a message with a JMSReplyTo header specified to
the 2nd queue, it'll just do the right thing if you do

from("jms:someQueue").bean("someBean", "method");

If you want to override/customize the JMSReplyTo header you could
explicitly specify the header in a transform before invoking the bean.
 Or you could disable the header and then just have an explicit to()
after the bean invocation. You're call really.


>  As this hasn't already been required by Camel developers I'm thinking
>  that maybe I am going about this the wrong way?  I think what I am after
>  is just a routing slip,

BTW we've a Routing Slip now...
http://activemq.apache.org/camel/routing-slip.html

> but some parts of the route are just drops onto
>  queues which are processed async without a reply.  My use case is that a
>  queue consumer process trades and gives back a response (a buyer id and
>  price).  Depending on the type of trade there may be a need to trasmit
>  data to an external client.  As this is a long running process it should
>  not block, as the original caller is waiting for the trade result.
>  Ideally "tradingService" should not be aware of the different types of
>  trade outcomes, which is why I am attempting to use a choice based on
>  headers sent in.
>
>  I have also found that when using InOut and having both bean signatures
>  as 'void' then BOTH the client and server fail with:
>  2008-03-31 16:47:32,421 DEBUG
>  [org.apache.camel.component.jms.JmsProducer] Future timed out
>  I can see both bean consumers for each JMS queue are executing through
>  logging.  The timeout makes sense as there is no Out to be sent back.
>  This seems like expected behavious (altough should this log statement's
>  level be debug?), so it would be nice if Camel could detect that you
>  have specified InOut on a route that cannot return.

Even for a void return, it could return an exception though. i.e. not
returning anything is different to throwing a runtime exception.

>  Thank you for assisting me whilst I learn Camel.

You're most welcome! Sorry this stuff isn't as clear as it should be
from the docs

-- 
James
-------
http://macstrac.blogspot.com/

Open Source Integration
http://open.iona.com

Re: Request Reponse with Camel

Posted by Martin Gilday <ma...@imap.cc>.
> So the missing bit of functionality is to be able to mark operations
> on your bean as being asynchronous; so that the client side doesn't
> block waiting for the results.

No I'm not sure that's what I mean.  I want the route to place onto the
2nd (or nth) JMS queue after a bean invocation, but not have the route
wait for the consumer of that JMS queue to return.  So the placing on
the "to" is async not the consuming, or is this the same thing? Does
that seem like a reasonable thing to be trying to do?

So it would be something like:

from("test-jms:queue:test.queue")
.beanRef("tradingService", "trade")
.choice().when(header("distribute").isEqualTo(true)).to("test-jms:queue:data.distribution.queue",
ExchangePattern.InOnly)  //PATTERN SPECIFIED HERE 
.end();

from("test-jms:queue:data.distribution.queue")
.beanRef("distributionService", "distribute");

As this hasn't already been required by Camel developers I'm thinking
that maybe I am going about this the wrong way?  I think what I am after
is just a routing slip, but some parts of the route are just drops onto
queues which are processed async without a reply.  My use case is that a
queue consumer process trades and gives back a response (a buyer id and
price).  Depending on the type of trade there may be a need to trasmit
data to an external client.  As this is a long running process it should
not block, as the original caller is waiting for the trade result. 
Ideally "tradingService" should not be aware of the different types of
trade outcomes, which is why I am attempting to use a choice based on
headers sent in.

I have also found that when using InOut and having both bean signatures
as 'void' then BOTH the client and server fail with:
2008-03-31 16:47:32,421 DEBUG
[org.apache.camel.component.jms.JmsProducer] Future timed out
I can see both bean consumers for each JMS queue are executing through
logging.  The timeout makes sense as there is no Out to be sent back. 
This seems like expected behavious (altough should this log statement's
level be debug?), so it would be nice if Camel could detect that you
have specified InOut on a route that cannot return.

Thank you for assisting me whilst I learn Camel.
Martin.

Re: Request Reponse with Camel

Posted by James Strachan <ja...@gmail.com>.
On 31/03/2008, Martin Gilday <ma...@imap.cc> wrote:
> I now have a nice example working with request resonse with messages
>  being received by a Spring bean from a JMS queue, and the result being
>  sent back synchronously back to the client.  After getting past the
>  initial hurdle (I'll avoid saying hump)

:)

> it fits very well with my
>  understanding of the EIP book.

Great - thanks for the heads up.


>  My problem at the moment is trying to understand the meaning of some of
>  the values on camel.ExchangePattern.  Is there any documentation
>  detailing what each means and when it may be used?  For example what is
>  the difference between InOut and OutIn?  I have looked in the source but
>  there is no javadoc for the enum.

Our bad! They basically map to the WSDL message exchange patterns. I
added all of them in case we need them - but really the main ones of
interest are InOnly (i.e. one way messages) and InOut (request/reply).
We could start using more of the others as and when we need them -
maybe on a per component basis. e.g. for REST/HTTP to differentiate
between a GET (OutOnly) / PUT (InOnly) / POST (InOut) maybe?


>  My reason for trying to work out ExchangePattern is to assist in trying
>  to have some parts of my routes async and some sync.  I have been using
>  InOut to make my example described above work synchronously.  But for
>  part of the route I would like to send to certain JMS queues after the
>  Spring bean, but in an async fashion.
>
>  from("test-jms:queue:test.queue")
>  .beanRef("returningBean", "sendBackMessage")
>  .choice().when(header("distribute").isEqualTo(true)).to("test-jms:queue:other.queue")
>  .end();
>
>  //the original message passed in to test.queue has the header
>  'distribute' set
>
>  My problem here is that my orginal client seems to wait for the consumer
>  of other.queue to return.  Is there any way to get around this, or are
>  you tied in to InOut for the whole processing of the route?

So the missing bit of functionality is to be able to mark operations
on your bean as being asynchronous; so that the client side doesn't
block waiting for the results.

e.g. if we had an annotation something like

@Async
public void someMethod(Cheese somePayload) {
 ...
}

when calling someMethod the client side proxy should explicitly use an
InOnly (rather than InOut) as the method is annotated with @Async. If
the InOnly is used as the message exchange pattern then the JMS
endpoint does the right thing (using a fire and forget rather than
blocking for a response).

In those cases where you cannot modify your source code (e.g. you are
reusing some legacy code) we might want to be able to customize the
<proxy> element in Camel to name the async method names.

e.g. if you are using some legacy bean/interface you can't change then
specify the async methods in the XML configuration...

<camel:proxy id="sayProxy" serviceUrl="jms:some.queue"
                    serviceInterface="org.apache.camel.spring.remoting.ISay"
                    asyncMethods="someMethod"/>

-- 
James
-------
http://macstrac.blogspot.com/

Open Source Integration
http://open.iona.com

Re: Request Reponse with Camel

Posted by Martin Gilday <ma...@imap.cc>.
I now have a nice example working with request resonse with messages
being received by a Spring bean from a JMS queue, and the result being
sent back synchronously back to the client.  After getting past the
initial hurdle (I'll avoid saying hump) it fits very well with my
understanding of the EIP book.

My problem at the moment is trying to understand the meaning of some of
the values on camel.ExchangePattern.  Is there any documentation
detailing what each means and when it may be used?  For example what is
the difference between InOut and OutIn?  I have looked in the source but
there is no javadoc for the enum.

My reason for trying to work out ExchangePattern is to assist in trying
to have some parts of my routes async and some sync.  I have been using
InOut to make my example described above work synchronously.  But for
part of the route I would like to send to certain JMS queues after the
Spring bean, but in an async fashion.

from("test-jms:queue:test.queue")
.beanRef("returningBean", "sendBackMessage")
.choice().when(header("distribute").isEqualTo(true)).to("test-jms:queue:other.queue")
.end();

//the original message passed in to test.queue has the header
'distribute' set

My problem here is that my orginal client seems to wait for the consumer
of other.queue to return.  Is there any way to get around this, or are
you tied in to InOut for the whole processing of the route?  So here if
'distribute' is set to 'true' then the result of 'returningBean' should
be placed on the other.queue then send the result of 'returningBean'
back to the original clients temporary queue.  The only way I can think
to get around this is to have returningBean place the result on
other.queue itself, but I do not think 'returningBean' should be aware
of this routing.

Thanks,
Martin.

----- Original message -----
From: "James Strachan" <ja...@gmail.com>
To: camel-user@activemq.apache.org
Date: Thu, 27 Mar 2008 16:03:29 +0000
Subject: Re: Request Reponse with Camel

On 27/03/2008, Martin Gilday <ma...@imap.cc> wrote:
> Thanks James.  I did read through the email and have it working with
>  1.3-SNAPSHOT.  It just seemed strange that the suggested method did not
>  work with 1.2.

Yeah - we soooo need 1.3.0 out already :)


>  CamelTemplate with ExchangePattern.InOut.  Do you think this is a good
>  idea?

Sure. So the idea being the spring remoting is to provide a proxy and
a server stub for any Java object / interface so that all the
middleware & messaging APIs are hidden from your code.

Sometimes though folks wanna use an API to send messages (and tinker
with headers and so forth) and look at responses and headers etc.

If thats the case then by all means use CamelTemplate to make
invocations etc. Though ideally we'd just hide this behind smart
proxies using Spring Remoting if possible.

>  I have it working this way as well, but being a Camel newbie I am very
>  confused about what is on the client/server/both.  For example if I was
>  using CamelTemplate to synchronously call a remote queue managed by
>  Camel, what would be in my client's camel context that I give to the
>  CamelTemplate?

It just needs to know how to resolve the remote endpoint. e.g. if the
remote endpoint is JMS, it just needs some JMS component so it can
communicate with the JMS endpoint.


>  Would it just be the JMS component with no routes
>  defined and the AcitveMQ connection factory?

Exactly.

A CamelContext doesn't have to have any explicit routes; its just a
context of components / endpoints / routes where any of those can be
empty.

-- 
James
-------
http://macstrac.blogspot.com/

Open Source Integration
http://open.iona.com

Re: Request Reponse with Camel

Posted by James Strachan <ja...@gmail.com>.
On 27/03/2008, Martin Gilday <ma...@imap.cc> wrote:
> Thanks James.  I did read through the email and have it working with
>  1.3-SNAPSHOT.  It just seemed strange that the suggested method did not
>  work with 1.2.

Yeah - we soooo need 1.3.0 out already :)


>  CamelTemplate with ExchangePattern.InOut.  Do you think this is a good
>  idea?

Sure. So the idea being the spring remoting is to provide a proxy and
a server stub for any Java object / interface so that all the
middleware & messaging APIs are hidden from your code.

Sometimes though folks wanna use an API to send messages (and tinker
with headers and so forth) and look at responses and headers etc.

If thats the case then by all means use CamelTemplate to make
invocations etc. Though ideally we'd just hide this behind smart
proxies using Spring Remoting if possible.

>  I have it working this way as well, but being a Camel newbie I am very
>  confused about what is on the client/server/both.  For example if I was
>  using CamelTemplate to synchronously call a remote queue managed by
>  Camel, what would be in my client's camel context that I give to the
>  CamelTemplate?

It just needs to know how to resolve the remote endpoint. e.g. if the
remote endpoint is JMS, it just needs some JMS component so it can
communicate with the JMS endpoint.


>  Would it just be the JMS component with no routes
>  defined and the AcitveMQ connection factory?

Exactly.

A CamelContext doesn't have to have any explicit routes; its just a
context of components / endpoints / routes where any of those can be
empty.

-- 
James
-------
http://macstrac.blogspot.com/

Open Source Integration
http://open.iona.com

Re: Request Reponse with Camel

Posted by Martin Gilday <ma...@imap.cc>.
Thanks James.  I did read through the email and have it working with
1.3-SNAPSHOT.  It just seemed strange that the suggested method did not
work with 1.2.

I am trying to compare using the spring remoting proxies with using
CamelTemplate with ExchangePattern.InOut.  Do you think this is a good
idea?  

I have it working this way as well, but being a Camel newbie I am very
confused about what is on the client/server/both.  For example if I was
using CamelTemplate to synchronously call a remote queue managed by
Camel, what would be in my client's camel context that I give to the
CamelTemplate?  Would it just be the JMS component with no routes
defined and the AcitveMQ connection factory?

Thanks,
Martin.

----- Original message -----
From: "James Strachan" <ja...@gmail.com>
To: camel-user@activemq.apache.org
Date: Thu, 27 Mar 2008 12:31:29 +0000
Subject: Re: Request Reponse with Camel

On 27/03/2008, Martin Gilday <ma...@imap.cc> wrote:
> Hi,
>  I am currently trying to find the best way to implement request response
>  with ActiveMQ.  I am considering Lingo as well as Camel.

Camel offers much more flexibility for things like smart routing,
transformation & encoding of the messages, setting of headers and so
forth - plus it can work with any Camel transport...
http://activemq.apache.org/camel/components.html

The big downside of Camel right now is it doesn't support Lingo's
asynchronous options yet. We need to enhance the Spring Remoting
implementation in Camel to support this. e.g. marking methods in an
interface as being @Oneway or @Async or something so that the spring
remoting can send an InOnly exchange rather than an InOut.


> This page on
>  the ActiveMQ site [1] suggests that using Camel as a Spring Remoting
>  provider is the best solution.  However the page it links to [2] is
>  seemingly only a partial listing of the test case from camel-spring.  I
>  have attempted to follow the test case, however this resulted in errors
>  once I changed the example from direct to JMS.


There's been a JMS remoting test case in svn for a while - I just
created a simpler one to follow in trunk...

https://svn.apache.org/repos/asf/activemq/camel/trunk/components/camel-jms/src/test/java/org/apache/camel/component/jms/remoting/RemotingTest.java

using this Spring XML
https://svn.apache.org/repos/asf/activemq/camel/trunk/components/camel-jms/src/test/resources/org/apache/camel/component/jms/remoting/RemotingTest-context.xml


> This lead me to finding
>  this posting [3] on the mailing list describing the same fault.

Reading to the end of that thread it seems Carsten got it working in
the end - it turned out to be a configuration issue with ActiveMQ
right?
-- 
James
-------
http://macstrac.blogspot.com/

Open Source Integration
http://open.iona.com

Re: Request Reponse with Camel

Posted by James Strachan <ja...@gmail.com>.
On 27/03/2008, Martin Gilday <ma...@imap.cc> wrote:
> Hi,
>  I am currently trying to find the best way to implement request response
>  with ActiveMQ.  I am considering Lingo as well as Camel.

Camel offers much more flexibility for things like smart routing,
transformation & encoding of the messages, setting of headers and so
forth - plus it can work with any Camel transport...
http://activemq.apache.org/camel/components.html

The big downside of Camel right now is it doesn't support Lingo's
asynchronous options yet. We need to enhance the Spring Remoting
implementation in Camel to support this. e.g. marking methods in an
interface as being @Oneway or @Async or something so that the spring
remoting can send an InOnly exchange rather than an InOut.


> This page on
>  the ActiveMQ site [1] suggests that using Camel as a Spring Remoting
>  provider is the best solution.  However the page it links to [2] is
>  seemingly only a partial listing of the test case from camel-spring.  I
>  have attempted to follow the test case, however this resulted in errors
>  once I changed the example from direct to JMS.


There's been a JMS remoting test case in svn for a while - I just
created a simpler one to follow in trunk...

https://svn.apache.org/repos/asf/activemq/camel/trunk/components/camel-jms/src/test/java/org/apache/camel/component/jms/remoting/RemotingTest.java

using this Spring XML
https://svn.apache.org/repos/asf/activemq/camel/trunk/components/camel-jms/src/test/resources/org/apache/camel/component/jms/remoting/RemotingTest-context.xml


> This lead me to finding
>  this posting [3] on the mailing list describing the same fault.

Reading to the end of that thread it seems Carsten got it working in
the end - it turned out to be a configuration issue with ActiveMQ
right?
-- 
James
-------
http://macstrac.blogspot.com/

Open Source Integration
http://open.iona.com