You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@camel.apache.org by Ron Cecchini <ro...@comcast.net> on 2019/12/12 06:07:55 UTC

Communicating a value between a route and the RouteBuilder class

The use case is simply this: I need to poll and hit an HTTP endpoint with an initial "offset" param of -1.  The response header contains a new offset ("NEXT_OFFSET") to use the next time I hit the endpoint.

Sounds simple enough... Until you start trying to do it... playing around with properties and headers, on and off the exchange, system properties, GlobalOptions, setting the initial value in a "calling" route instead of a class variable, etc, etc, etc.

Finally I found a post with someone else asking the exact same question and there was a response from Claus that mentioned using inline processors and lambdas, which finally pointed me in the direction toward a solution.  I still needed to figure out a few more things to actually do it. 

So, this is what I eventually implemented, and it works fine.  But is it *really* the only way to communicate a value between a route and its RouteBuilder class?  And can my snippet be simplified even more?  In particular, note that I seemed to be required to use an intermediate header value ("offset") to store/retrieve the actual offset to the "offsetCache" class variable.

Thanks.

In my RouteBuilder configure():

AtomicReference<String> offsetCache = new AtomicReference<>("-1");

from("timer:mytimer?period={{polling.interval}}")
    .process(exchange -> exchange.getIn().setHeader("offset", offsetCache.get()))
    .toD("{{url.base}}/${header.offset}")
    // process the response and get the new offset from the header into the class variable
    .setHeader("offset", simple("${in.header.NEXT_OFFSET}"))
    .process(exchange -> offsetCache.set(exchange.getIn().getHeader("offset").toString()))

Re: Communicating a value between a route and the RouteBuilder class

Posted by Mark Nuttall <mk...@gmail.com>.
I'm not seeing all the code of course but remember that spring beans by
default are Singletons. So values set at the class level are not thread
safe.

On Fri, Dec 13, 2019, 1:05 AM Ron Cecchini <ro...@comcast.net> wrote:

> > On December 12, 2019 at 11:16 AM Claus Ibsen <cl...@gmail.com>
> wrote:
> >
> > You can also name your RouteBuilder class with a bean id, if you use
> > spring / spring-boot or cdi etc. And then refer to this bean ids,
> > where you can have methods that you can refer to in the simple bean
> > function.  Then you dont need to add new classes.
>
> Whaaaat...?  You mean my MyRouteBuilder class can have *more* than just
> the single overridden configure() method...!?
>
> *slaps forehead*  Man, talk about not seeing what was right in front of
> me...
>
> So, I am using Spring Boot, and after I took your elegant advice and made
> the AtomicReference a class variable, and created a getter/setter for it, I
> was able to eliminate the process() and setHeader() calls and go from this:
>
> from("timer:mytimer?period={{polling.interval}}")
>      .process(exchange -> exchange.getIn().setHeader("offset",
> offsetCache.get()))
>      .toD("{{url.base}}/${header.offset}")
>      // process the response and get the new offset from the header into
> the class variable
>      .setHeader("offset", simple("${in.header.NEXT_OFFSET}"))
>      .process(exchange ->
> offsetCache.set(exchange.getIn().getHeader("offset").toString()))
>
> to this sweet thing:
>
> from("timer:mytimer?period={{polling.interval}}")
>      .toD("{{url.base}}/${bean:MyRouteBuilder.getOffset}
>      // process the response and get the new offset from the header into
> the class variable
>      .bean("MyRouteBuilder", "setOffset(${in.header.NEXT_OFFSET})")
>
> Thanks as always - and on to the next thing. (figuring out a unified
> logging for routes and classes...)
>

Re: Communicating a value between a route and the RouteBuilder class

Posted by Ron Cecchini <ro...@comcast.net>.
> On December 12, 2019 at 11:16 AM Claus Ibsen <cl...@gmail.com> wrote:
> 
> You can also name your RouteBuilder class with a bean id, if you use
> spring / spring-boot or cdi etc. And then refer to this bean ids,
> where you can have methods that you can refer to in the simple bean
> function.  Then you dont need to add new classes.

Whaaaat...?  You mean my MyRouteBuilder class can have *more* than just the single overridden configure() method...!?

*slaps forehead*  Man, talk about not seeing what was right in front of me...

So, I am using Spring Boot, and after I took your elegant advice and made the AtomicReference a class variable, and created a getter/setter for it, I was able to eliminate the process() and setHeader() calls and go from this:

from("timer:mytimer?period={{polling.interval}}")
     .process(exchange -> exchange.getIn().setHeader("offset", offsetCache.get()))
     .toD("{{url.base}}/${header.offset}")
     // process the response and get the new offset from the header into the class variable
     .setHeader("offset", simple("${in.header.NEXT_OFFSET}"))
     .process(exchange -> offsetCache.set(exchange.getIn().getHeader("offset").toString()))

to this sweet thing:

from("timer:mytimer?period={{polling.interval}}")
     .toD("{{url.base}}/${bean:MyRouteBuilder.getOffset}
     // process the response and get the new offset from the header into the class variable
     .bean("MyRouteBuilder", "setOffset(${in.header.NEXT_OFFSET})")

Thanks as always - and on to the next thing. (figuring out a unified logging for routes and classes...)

Re: Communicating a value between a route and the RouteBuilder class

Posted by Claus Ibsen <cl...@gmail.com>.
You can also name your RouteBuilder class with a bean id, if you use
spring / spring-boot or cdi etc. And then refer to this bean ids,
where you can have methods that you can refer to in the simple bean
function.
Then you dont need to add new classes.

On Thu, Dec 12, 2019 at 4:03 PM Ron Cecchini <ro...@comcast.net> wrote:
>
>
> Thanks. Yeah, that would definitely make the route cleaner and would have been easier to implement...
>
> I guess I should have mentioned that I’ve been playing a game with myself and have been refusing to add any new classes/beans unless I utterly had to.  I’m sort of “obsessed” with refactoring every bit of logic and whatnot into this sleek, stripped down (one RouteBuilder) pipeline I’ve created.
>
> I think a “helper ” or “utility” bean of sorts might end up being a good idea though...
>
> Thanks again.
>
> > On December 12, 2019 at 7:35 AM Claus Ibsen <cl...@gmail.com> wrote:
> >
> >
> > You can use bean function in the simple language ${bean:xxx} to refer
> > to a bean by its id, then you can store your offset cache with some
> > bean id.
> >
> > On Thu, Dec 12, 2019 at 7:08 AM Ron Cecchini <ro...@comcast.net> wrote:
> > >
> > > The use case is simply this: I need to poll and hit an HTTP endpoint with an initial "offset" param of -1.  The response header contains a new offset ("NEXT_OFFSET") to use the next time I hit the endpoint.
> > >
> > > Sounds simple enough... Until you start trying to do it... playing around with properties and headers, on and off the exchange, system properties, GlobalOptions, setting the initial value in a "calling" route instead of a class variable, etc, etc, etc.
> > >
> > > Finally I found a post with someone else asking the exact same question and there was a response from Claus that mentioned using inline processors and lambdas, which finally pointed me in the direction toward a solution.  I still needed to figure out a few more things to actually do it.
> > >
> > > So, this is what I eventually implemented, and it works fine.  But is it *really* the only way to communicate a value between a route and its RouteBuilder class?  And can my snippet be simplified even more?  In particular, note that I seemed to be required to use an intermediate header value ("offset") to store/retrieve the actual offset to the "offsetCache" class variable.
> > >
> > > Thanks.
> > >
> > > In my RouteBuilder configure():
> > >
> > > AtomicReference<String> offsetCache = new AtomicReference<>("-1");
> > >
> > > from("timer:mytimer?period={{polling.interval}}")
> > >     .process(exchange -> exchange.getIn().setHeader("offset", offsetCache.get()))
> > >     .toD("{{url.base}}/${header.offset}")
> > >     // process the response and get the new offset from the header into the class variable
> > >     .setHeader("offset", simple("${in.header.NEXT_OFFSET}"))
> > >     .process(exchange -> offsetCache.set(exchange.getIn().getHeader("offset").toString()))
> >
> >
> >
> > --
> > Claus Ibsen
> > -----------------
> > http://davsclaus.com @davsclaus
> > Camel in Action 2: https://www.manning.com/ibsen2



-- 
Claus Ibsen
-----------------
http://davsclaus.com @davsclaus
Camel in Action 2: https://www.manning.com/ibsen2

Re: Communicating a value between a route and the RouteBuilder class

Posted by Ron Cecchini <ro...@comcast.net>.
Thanks. Yeah, that would definitely make the route cleaner and would have been easier to implement...
 
I guess I should have mentioned that I’ve been playing a game with myself and have been refusing to add any new classes/beans unless I utterly had to.  I’m sort of “obsessed” with refactoring every bit of logic and whatnot into this sleek, stripped down (one RouteBuilder) pipeline I’ve created. 
 
I think a “helper ” or “utility” bean of sorts might end up being a good idea though...
 
Thanks again. 

> On December 12, 2019 at 7:35 AM Claus Ibsen <cl...@gmail.com> wrote:
> 
> 
> You can use bean function in the simple language ${bean:xxx} to refer
> to a bean by its id, then you can store your offset cache with some
> bean id.
> 
> On Thu, Dec 12, 2019 at 7:08 AM Ron Cecchini <ro...@comcast.net> wrote:
> >
> > The use case is simply this: I need to poll and hit an HTTP endpoint with an initial "offset" param of -1.  The response header contains a new offset ("NEXT_OFFSET") to use the next time I hit the endpoint.
> >
> > Sounds simple enough... Until you start trying to do it... playing around with properties and headers, on and off the exchange, system properties, GlobalOptions, setting the initial value in a "calling" route instead of a class variable, etc, etc, etc.
> >
> > Finally I found a post with someone else asking the exact same question and there was a response from Claus that mentioned using inline processors and lambdas, which finally pointed me in the direction toward a solution.  I still needed to figure out a few more things to actually do it.
> >
> > So, this is what I eventually implemented, and it works fine.  But is it *really* the only way to communicate a value between a route and its RouteBuilder class?  And can my snippet be simplified even more?  In particular, note that I seemed to be required to use an intermediate header value ("offset") to store/retrieve the actual offset to the "offsetCache" class variable.
> >
> > Thanks.
> >
> > In my RouteBuilder configure():
> >
> > AtomicReference<String> offsetCache = new AtomicReference<>("-1");
> >
> > from("timer:mytimer?period={{polling.interval}}")
> >     .process(exchange -> exchange.getIn().setHeader("offset", offsetCache.get()))
> >     .toD("{{url.base}}/${header.offset}")
> >     // process the response and get the new offset from the header into the class variable
> >     .setHeader("offset", simple("${in.header.NEXT_OFFSET}"))
> >     .process(exchange -> offsetCache.set(exchange.getIn().getHeader("offset").toString()))
> 
> 
> 
> -- 
> Claus Ibsen
> -----------------
> http://davsclaus.com @davsclaus
> Camel in Action 2: https://www.manning.com/ibsen2

Re: Communicating a value between a route and the RouteBuilder class

Posted by Claus Ibsen <cl...@gmail.com>.
You can use bean function in the simple language ${bean:xxx} to refer
to a bean by its id, then you can store your offset cache with some
bean id.

On Thu, Dec 12, 2019 at 7:08 AM Ron Cecchini <ro...@comcast.net> wrote:
>
> The use case is simply this: I need to poll and hit an HTTP endpoint with an initial "offset" param of -1.  The response header contains a new offset ("NEXT_OFFSET") to use the next time I hit the endpoint.
>
> Sounds simple enough... Until you start trying to do it... playing around with properties and headers, on and off the exchange, system properties, GlobalOptions, setting the initial value in a "calling" route instead of a class variable, etc, etc, etc.
>
> Finally I found a post with someone else asking the exact same question and there was a response from Claus that mentioned using inline processors and lambdas, which finally pointed me in the direction toward a solution.  I still needed to figure out a few more things to actually do it.
>
> So, this is what I eventually implemented, and it works fine.  But is it *really* the only way to communicate a value between a route and its RouteBuilder class?  And can my snippet be simplified even more?  In particular, note that I seemed to be required to use an intermediate header value ("offset") to store/retrieve the actual offset to the "offsetCache" class variable.
>
> Thanks.
>
> In my RouteBuilder configure():
>
> AtomicReference<String> offsetCache = new AtomicReference<>("-1");
>
> from("timer:mytimer?period={{polling.interval}}")
>     .process(exchange -> exchange.getIn().setHeader("offset", offsetCache.get()))
>     .toD("{{url.base}}/${header.offset}")
>     // process the response and get the new offset from the header into the class variable
>     .setHeader("offset", simple("${in.header.NEXT_OFFSET}"))
>     .process(exchange -> offsetCache.set(exchange.getIn().getHeader("offset").toString()))



-- 
Claus Ibsen
-----------------
http://davsclaus.com @davsclaus
Camel in Action 2: https://www.manning.com/ibsen2