Awesome stuff James.

The idea of aspect oriented routes is very nice.

In our current use cases we can classify the results of a endpoint delivery
into a few types.

Success, exception1, exception2. 

The actions taking place on these exceptions are common.

We're found that we can classify our endpoint components according to a QoS
type of 

For idempotent endpoints 

intercept(error(SystemException.class)).maximumRedeliveries(-1)

for "never retry - we need to manually intervene first"

intercept(error(BusinessException.class)).to("jms:errors")

how can we register these interceptors across multiple routes?

intercept(error(BusinessException.class)).to("jms:business.errors");
intercept(error(SystemException.class)).maximumRedeliveries(-1);
intercept(error(*)).to("jms:uncaught.errors");//not sure how to specify a
catch all here
from("jms:bus").to("jms:dateservice").to("jms:paymentservice").to("jms.responseservice");

Read as:

along the route bus->dateservice->paymentservice->responseservice

if at any time a BusinessException occurs terminate the originating route
flow and redirect to business.errors.
if at any time a SystemException occurs retry until you can proceed
if at any time any other exception occurs redirect to uncaught.errors  

In our use case the payment service is a "retry when its a system exception
type", and a "never retry if it's a BusinessException type".
Both of the other services are idempotent and will always throw
SystemExceptions or potentially uncaught exceptions.

Your new AOP concept should handle this nicely. We've got a clean route, and
the error handling aspect is nicely separated.

Endpoints can be categorized in a few different ways in our app.

* by the exeptions they throw
* by the retry policies (which can differ based on some exception type)
* by the kind of data they expect in and out (Message Translation)

We also do some common message translation too.

You've already talked about some kind of registry. What about a registry of
MessageTranslators registered by to and from types.

Eg. I could have a route defined as

from("jms:bus").translate(from("canonical").into("component").andBack()).
to("jms:dateservice");
from("jms:dateservice").translate(from("canonical").into("component").andBack()).
to("jms:paymentservice");
from("jms:paymentservice").translate(from("canonical").into("component").andBack()).
to("jms:responseservice")

If all of these are the same then I can use AOP style:
//leave out the jms:bus endpoint
intercept(except(endpoints("jms:bus")).translate(from("canonical").into("component").andBack());
from("jms:bus").to("jms:dateservice").to("jms:paymentservice").to("jms.responseservice");

that means all of my endpoints have message translation to and from the
canonical format into component format along the route.

I can then easily change one translation for a new component:

intercept(endpoints("jms:dateservice",
"jms:responseservice")).translate(from("canonical").into("component").andBack());
intercept(endpoints("jms:paymentservice").translate(from("canonical").into("componentNew").andBack());
from("jms:bus").to("jms:dateservice").to("jms:paymentservice").to("jms.responseservice");

We'd then just need to register the translators somewhere

translate(from("component").to("canonical").andBack().with(componentToCanonicalMessageTranslator)));
translate(from("componentNew").to("canonical").andBack().with(componentNewToCanonicalMessageTranslator)));

intercept(endpoints("jms:dateservice",
"jms:responseservice")).translate(from("canonical").into("component").andBack());
intercept(endpoints("jms:paymentservice").translate(from("canonical").into("componentNew").andBack());

from("jms:bus").to("jms:dateservice").to("jms:paymentservice").to("jms.responseservice");

the translator could be given access to the appropriate part of the exchange
body by the framework.

hopefully some of this makes sense.

thanks,

Neil


James.Strachan wrote:
> 
> On 8/9/07, James Strachan <[EMAIL PROTECTED]> wrote:
>> On 8/9/07, James Strachan <[EMAIL PROTECTED]> wrote:
>> > On 8/9/07, Nick Outram <[EMAIL PROTECTED]> wrote:
>> > >
>> > >
>> > > James.Strachan wrote:
>> > > >
>> > > >
>> > > > Am just wondering the semantics of onPattern() really; as when I
>> first
>> > > > read your suggestion, in my mind I was thinking that onPattern()
>> would
>> > > > setup an interceptor. So maybe onPattern() needs some kind of
>> > > > proceed() call in part of its route to indicate if/when it carries
>> on
>> > > > processing?
>> > > >
>> > > > e.g. how about this (making a slight change to the language)...
>> > > >
>> > > >
>> onIntercept(anyPredicateGoesHere).to("seda:a").filter(somethingElse).proceed();
>> > > >
>> > > > from("seda:foo").to("seda:b");
>> > > >
>> > > > i.e. in the above we intercept the route, if the message matches
>> > > > anyPredicateGoesHere, then its sent to "seda:a" and if
>> somethingElse
>> > > > matches, then it carries on to "seda:b". So it could go to
>> "seda:a",
>> > > > "seda:b" or both - depending on the evaluation of the two
>> predicates.
>> > > >
>> > > > If you really just want it to go to "seda:a" or "seda:b" then you'd
>> just
>> > > > do...
>> > > >
>> > > > onIntercept(anyPredicateGoesHere).to("seda:a");
>> > > >
>> > > > from("seda:foo").to("seda:b");
>> > > >
>> > > > In both cases really, we're just putting Message Filter / Content
>> > > > Based Router inside an interceptor; rather than in-place inside the
>> > > > route (and allowing the default behaviour of interceptors to be
>> > > > inherited on all child nodes of the DSL, so the interceptor would
>> be
>> > > > applied around all Processors in the chain).
>> > > >
>> > > > How's that sound?
>> > > >
>> > > >
>> > > >
>> > >
>> > >
>> > > onIntercept().choice()
>> > >                 .when(anyPredicateGoesHere).to("seda:a")
>> > >                 .when(somethingElse).to("seda:b", "seda:c")
>> > >                 .when(somethingElseAgain).to("seda:d", proceed());
>> > >
>> > > from("seda:foo").process(new Enricher1()). process(new
>> > > Enricher2()).to("seda:z");
>> > >
>> > > Reads:
>> > > After from("seda:foo") or process(new Enricher1()) or process(new
>> > > Enricher2()) if the message matches anyPredicateGoesHere then send to
>> > > seda:a, if the message matches somethinElse then send it to seda:b
>> and
>> > > seda:c, if the message matches somethingElseAgain send it to seda:d
>> and
>> > > proceed on the current route, otherwise in all other cases just
>> proceed; you
>> > > could include an explicit otherwise clause but as this catches
>> everything it
>> > > would make your route pretty redundant.
>> > >
>> > > Also:
>> > >
>> > >
>> onIntercept().filter(xpath("/[EMAIL PROTECTED]'James']")).to("mock:result",
>> > > proceed());
>> > >
>> > > … after any processing on the route if the xpath matches then send
>> the
>> > > message to mock:result and proceed on the original route…
>> > >
>> > > and even:
>> > >
>> > >
>> onIntercept().aggregator(header("JMSDestination")).to("activemq:someSlowTopicForGuis");
>> > >
>> > >
>> > > Basically the onIntercept() applies the following message routing
>> rules to
>> > > all processes on routes it is registered with.
>> > >
>> > > If you really wanted to have fun you could have:
>> > >
>> > >
>> onIntercept(interceptCriteria).filter(xpath("/[EMAIL 
>> PROTECTED]'James']")).to("mock:result",
>> > > proceed());
>> > >
>> > > Which would only apply the intercept to processors matching the
>> > > interceptCriteria…
>> > >
>> > > Thoughts? Am I raving ;-)
>> >
>> > Love it! :) Thanks for this awesome feedback!
>> >
>> > I'll see if I can have a go at implementing this later today unless
>> > anyone beats me to it...
>>
>> OK I've a first cut of the interceptor route definition. BTW I raised
>> a JIRA to track this...
>> https://issues.apache.org/activemq/browse/CAMEL-92
>>
>> When actually implementing it, I found the method 'intercept()' to fit
>> better with the existing code; so went with that to start with - is
>> that OK with you?
>>
>> Now in a route builder if you do
>>
>>   intercept().
>>
>> you are now defining an interceptor route, you must call proceed() to
>> continue processing the underlying route. e.g.
>>
>>   intercept().to("foo")
>>
>> would kinda replace the routing rules you have - so won't be too useful
>> :)
>>
>> also if you do
>>
>>   intercept(Predicate)
>>
>> the same rules apply - though if the predicate is false then there's a
>> kinda explicit call to proceed() for you. If the predicate is true,
>> its up to you to decide when/if to proceed() etc.
>>
>> Probably the easiest way to check I've implemented what you were
>> thinking is to try the test cases; they are all here beginning with
>> "Intercept*.java"
>> https://svn.apache.org/repos/asf/activemq/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/
>>
>> i.e.
>> https://svn.apache.org/repos/asf/activemq/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/InterceptRouteTest.java
>> https://svn.apache.org/repos/asf/activemq/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/InterceptWithPredicateAndProceedRouteTest.java
>> https://svn.apache.org/repos/asf/activemq/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/InterceptWithPredicateRouteTest.java
>> https://svn.apache.org/repos/asf/activemq/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/InterceptWithoutProceedRouteTest.java
>>
>>
>> Does this seem OK to you? Will start on the onError() now...
> 
> OK the onError() is done now too. Again when I actually came to code
> it, I found it was a tad better to call it exception().
> 
> So you can now do
> 
> exception(FooException.class).to("seda:whatnot");
> 
> from("foo").process(something).to("seda:bar");
> 
> if the FooException is thrown then its sent to the "seda:whatnot"
> queue; otherwise the default error handling takes place.
> 
> You can see this DSL in action using a variation of the ValidationTest...
> https://svn.apache.org/repos/asf/activemq/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/ValidationWithExceptionTest.java
> 
> I'll ponder how easy its gonna be to use the DSL to customize
> redelivery policy stuff on a per type basis too...
> 
> -- 
> James
> -------
> http://macstrac.blogspot.com/
> 
> 

-- 
View this message in context: 
http://www.nabble.com/Exception-based-routing-tf4226796s22882.html#a12082262
Sent from the Camel - Users mailing list archive at Nabble.com.

Reply via email to