Hi all,
Long time listener, first time caller...
Currently I have a similar issue on a project I am working on. Taking some
hints from this discussion I am wondering if it is possible, and makes sense,
to extend a Camel Context to provide the IN, OUT and ERROR Endpoints.
Then you could nest this context inside a top level context and use nested
context ID as it's namespace reference.
{bean definition}
blackboxContext
in
out
error
routerContext
from:seda
to:blackboxContext
from:blackboxContext
to:mock
This isolates the namespaces and would allow abstraction of the desired
capabilities.
Thoughts?
Thanks,
Scott
On Oct 27, 2010, at 4:28 AM, James Strachan <[email protected]> wrote:
> Great discussion Kristof! More inline...
>
> On 26 October 2010 22:12, kristofsajdak <[email protected]> wrote:
>> jstrachan wrote:
>>>
>>> Or to say that a slightly different way; if you just want a
>>> parameterised RouteBuilder, well thats just a bean; no need for
>>> anything new, Camel can do that nicely today
>>>
>>
>> I considered using a parameterized RouteBuilder, however it failed to
>> address some specific
>> needs.
>>
>> When using a solution based on parameterized RouteBuilders it requires you
>> to reference all
>> RouteBuilder used in the CamelContext.
>>
>> This becomes complex when nesting route abstractions within route
>> abstractions several levels
>> deep. When using a top level RouteBuilder like
>> generateAndSignPdfRouteBuilder you are required
>> to also declare the signPdfRouteBuilder reference which exposes the
>> direct:signPdf endpoint.
>>
>> E.g.
>>
>> <camelContext ...
>> <routeBuilder ref="generateAndSignPdfRouteBuilder"/>
>> <routeBuilder ref="signPdfRouteBuilder"/>
>> ....
>>
>> public class GenerateAndSignPdfRouteBuilder extends RouteBuilder {
>>
>> @Override
>> public void configure() throws Exception {
>> from("direct:generateAndSignPdf")
>> ...
>> to("direct:signPdf");
>> }
>>
>> }
>>
>> public class SignPdfRouteBuilder extends RouteBuilder {
>>
>> @Override
>> public void configure() throws Exception {
>> from("direct:signPdf")
>> ...
>> }
>>
>> }
>>
>> In other words, you would have to know how GenerateAndSignPdfRouteBuilder is
>> implemented
>> in order to use it.
>
> Agreed. If you kinda squint a bit and think of the route builders as
> 'objects' and you want to compose 2 different routes together with
> shared dependencies its a bit like 'constructors' and 'data hiding'.
>
> We have protocol A and B and we need to route the output of A to the
> input of B; so rather than B needing to know the implementation detail
> of A; we add a constructor parameter for B's input...
>
> class A extends ProtocolBuilder {...}
>
> class B extends ProtocolBuilder {
> public B(String inputUri) { ... }
> }
>
> // lets use Java code to simulate DI...
> ProtocolBuilder a = new A();
> ProtocolBuilder b = new B(b.getOutputUri());
>
>
> we've now wired A and B together without B knowing anything about the
> implementation detail of A.
>
> Its quite common for lots of protocols to be 'unix pipe-ish' with an
> input and output; so we could have helper classes where there's
> standard input/output endpoints like this to create a pipeline...
>
> CompositeProtocol composite = Protocols.pipeline(A.class, B.class, C.class);
>
> where A:out -> B:in and B:out -> C:in. Indeed that could return a new
> protocol; where composite:in maps to A:in and composite:out maps to
> C:out
>
> But then this is sounding like just treating A, B and C as endpoints :)
>
>
>> The Route Component solution (II) doesn't require the RouteBuilders used to
>> be referenced
>> in the camelContext. It instantiates an endpoint which adds the Route
>> information to the
>> camelcontext at runtime whenever a producer or consumer is started.
>>
>> Would this concept be available in the ProtocolBuilder construct as well ?
>> I do see the added value of using a ProtocolBuilder and declaring it as a
>> bean in spring
>> but wouldn't you need to use some kind of generic component in the middle to
>> instantiate
>> and add those builders on demand ?
>>
>> E.g.
>>
>> <route>
>> <from uri="route:standardActions://out"/>
>> <to uri="route:generateAndSignPdf://in"/>
>> <to uri="mock:result"/>
>> </route>
>>
>> Another reason why I didn't consider the Parameterized RouteBuilder is the
>> risk of name clashes
>> . When nesting components several levels deep and/or reusing the same
>> RouteBuilder beans multiple times
>> yet with different properties, this could lead to impredictable behaviour
>> when direct endpoints and routeIds are not unique within the CamelContext.
>
> Thats a great point! Just like with OO, we probably need some kind of
> data hiding; or in this case its 'endpoint hiding'. So maybe using
> nested CamelContexts is a good thing.
>
> Another random idea; imagine if we had a ProtocolBuilder which created
> a child CamelContext - so all "direct:foo" and "seda:bar" endpoints
> were totally private. Then imagine, as a simple implementation option
> - we map all URIs within the ProtocolBuilder's URI space to the
> direct: endpoint within the nested CamelContext.
>
> So rather than having to add the "alias()" method I mentioned in the
> other thread - we could just use URIs inside the route for the
> 'input/output/error' URIs...
> http://camel.465427.n5.nabble.com/implementing-Protocols-or-a-way-to-make-it-easier-to-black-box-routes-and-compose-them-with-other-ros-td3218777.html#a3218777
>
> e.g. imagine...
>
> class A extends ProtocolBuilder {
> public void configure() {
> from("direct:in").
> beanRef("foo").
> to("direct:out");
> }
> }
>
> class B extends ProtocolBuilder {
> public void configure() {
> from("direct:in").
> unmarshal().jaxb().
> to("direct:out");
> }
> }
>
> // now lets wire A and B
> // kinda like "Foo | A | B | Bar" on a unix command line
>
> class Foo extends RouteBuilder {
> public void configure() {
> from("activemq:Foo").to("a:in");
> from("a:out").to("b:in");
> from("b:out").to("activemq:Bar");
> }
> }
>
>
> i.e. so A and B have no knowledge of each other (kinda like unix
> pipes), you can compose them together in any way you like (Foo) - and
> bind them to any middleware.
>
> In this example the 'local endpoints' (using direct:) are totally
> private to each ProtocolBuilder so there's no name clashes as we're
> using nested CamelContext's.
>
> Not totally sure if we should make "direct" into some magical
> component here; or if we should just introduce some kind of "local:"
> endpoint to use inside a ProtocolBuilder to emphasise a local,
> internal endpoint which is private to the ProtocolBuilder? e.g.
> replace "direct:in" to "local:in".
>
> The question of local v global endpoint URIs is interesting; I wonder
> how many kinds of endpoints can even support the concept of local.
> Using nested CamelContexts we'd have local seda/vm/direct I think;
> though given their similarity, maybe a new 'local' endpoint is simpler
> as it clearly describes what really is local versus what really is an
> external/global endpoint?
>
> Note that there's nothing to stop these protocols using
> external/global endpoints too; am sure they will. Its more where we
> parameterise things together we might want to use local endpoints
> which we can then route into / out of.
>
>
>
>> jstrachan wrote:
>>>
>>> ii) a parameterized RouteBuilder which is dependency injected using a
>>> URI (which is kinda like Kristof's example code
>>>
>>
>> Yeah, maybe the implementation of the RouteEndpoint class is somewhat
>> clumsy, but what I am trying to
>> do here is to pass header values to the nested route.
>>
>> Normally you would go about this using the setHeader statement, however this
>> is quite verbose when
>> using xml dsl.
>>
>> <setHeader headerName="signatureReason">
>> <constant>JustForFun</constant>
>> </setHeader>
>> <setHeader headerName="signatureVisible">
>> <constant>true</constant>
>> </setHeader>
>> <setHeader headerName="xslUri">
>>
>> <constant>com/xti/poc/camel/route/sample1/krusty.xsl</constant>
>> </setHeader>
>>
>> That's why I figured i'd use the uri parameters to have some kind of
>> shorthand notation for assigning
>> header values.
>>
>> ?signatureReason=JustForFun&signatureVisible=true&xslUri=com/xti/poc/camel/route/sample1/krusty.xsl
>
> Ah sorry - I misunderstood. (Bad James, should have read your code
> more closely).
>
> Thats a pretty neat idea. Given that the 'local' endpoints I mention
> above would be pretty simple things; we could maybe do something
> similar in the ProtocolBuilder idea?
>
>
>
>> Maybe the implementation would be cleaner if properties are used only for
>> dependency injection.
>> The shorthand notation could define an interceptor on the from of the target
>> RouteBuilder. The interceptor could then populate the headers
>> when the exchange comes in.
>
> Or the setting of headers could be done inside the ProtocolBuilder's
> configure() method? So its hidden inside the implementation detail of
> the ProtocolBuilder, then folks wiring A and B together don't need to
> worry about that kinda thing?
>
> --
> James
> -------
> FuseSource
> Email: [email protected]
> Web: http://fusesource.com
> Twitter: jstrachan
> Blog: http://macstrac.blogspot.com/
>
> Open Source Integration