camel-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From James Strachan <james.strac...@gmail.com>
Subject Re: Abstracting Routes using Components
Date Wed, 27 Oct 2010 09:28:48 GMT
Great discussion Kristof! More inline...

On 26 October 2010 22:12, kristofsajdak <kristof.sajdak@gmail.com> 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: james@fusesource.com
Web: http://fusesource.com
Twitter: jstrachan
Blog: http://macstrac.blogspot.com/

Open Source Integration

Mime
View raw message