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.