camel-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "James Strachan" <>
Subject Re: Exception based routing
Date Fri, 10 Aug 2007 13:08:19 GMT
On 8/9/07, Neil Thorne <> wrote:
> Awesome stuff James.

Thanks for all the great feedback! :)

> 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.

I can imagine others having that kinda scenario too. e.g. doing
standard things with XML parsing errors/validation errors on messages

> 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?

So the nice thing is, stuff thats registered outside of a specific
route, is shared by all of the routes defined by that RouteBuilder.

So if you do all your interceptors/error handlers up front, before you
define any routes - the error handlers / interceptors are shared on
all routes.

If you want to customize this; you can overload the error handler
later on for other routes. Or its probably easier to just write
another RouteBuilder maybe?

> 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.

BTW have you seen the registry of type converters?

which is great at converting the body or a header into a well defined
type you expect. It works great for different Classes (e.g. if you had
a POJO representation of a business object, then wanted to turn it
into a String or DOM or whatever). It doesn't work too well on more
general-formatting/translation issues though. e.g. what if you have a
number of different ways of marshalling a POJO to a stream; the
existing type converter mechanism isn't quite ideal for that.

So maybe we need a way to just define a registry of transformers
maybe? Which could have a route to define exactly how they work (so
they are kinda their own route?).

> 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.

Interesting idea. BTW what does ".andBack()" mean? Also "canonical"
and "component" are just named formats right? So you could configure
some route/translation that goes from A, B or C to X etc.

> 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)));

Interesting; its been one of those things on my list of things to
ponder about, how to register custom translators easily etc.

> 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.

Yes! Nice work. Still mulling over the translator stuff; I'll post
more when I've had time to digest more fully


View raw message