java - Camel: failure to add routes dynamically -


i'm using apache-camel 2.15.2.

i trying add routes camelcontext dynamically, came across problem baffles me.

as far can tell, add routes correct camelcontext, , seems configure() called without throwing exceptions. when try execute main route, run time exception telling me route added dynamically not exist.

here simplified version of code:

public class mainroutebuilder extends routebuilder {     public static camelcontext camelcontext;      public static boolean returnlabel = true;      public static routebuilder nestedroutebuilder;      @override     public void configure() throws exception     {         system.out.println("building main route!");         system.out.println("context: " + getcontext());          camelcontext = getcontext();          from("direct:mainroute")         //3. not want instantiate routecontainer each time call route.         //i want reuse reference instance created outside of configure()...         .to(new routecontainer().getmyroute(2))         ;          returnlabel = false;          //configure direct:myroute.2          includeroutes(nestedroutebuilder);           }  }  public class routecontainer extends routebuilder {     public route route;      routecontainer() {         super(mainroutebuilder.camelcontext);     }      string getmyroute(final int n) {          if (mainroutebuilder.returnlabel && route == null) {             route = new route() {                  @override                 public void configure()                 {                      system.out.println("building nested route!");                     system.out.println("context: " + getcontext());                      from("direct:myroute." + n)                     .transform()                     .simple("number: " + n)                     .to("stream:out")                     .process(new processor() {                         @override                         public void process(exchange exchange) throws exception {                             response response = new response();                             response.setstatus(status.success);                              exchange.getout().setbody(response);                         }                     });                 }                            };         }  //1. works:         mainroutebuilder.nestedroutebuilder = this;  //2. not work: //      routecontainer routecontainer = new routecontainer(); //      routecontainer.route = this.route; //      mainroutebuilder.nestedroutebuilder = routecontainer;          return "direct:myroute." + n;     }       @override     public void configure() throws exception {         if (route != null) {             route.configure();         }     }      public abstract static class route {                 abstract public void configure();     }  } 

requests sent direct:mainroute work. during camel startup see in console:

building main route! context: springcamelcontext(camel-1) spring id org.springframework.web.context.webapplicationcontext:/sample-route building nested route! context: springcamelcontext(camel-1) spring id org.springframework.web.context.webapplicationcontext:/sample-route 

and when send request direct:mainroute, output is:

{"status":"success"} 

however, if comment out (1) above, , uncomment (2), camel starts same output console, when send request direct:mainroute, execution of route fails exception:

org.apache.camel.component.direct.directconsumernotavailableexception: no consumers available on endpoint: endpoint[direct://myroute.2]. 

to clarify: problem because not instantiate routecontainer each time call route, in (3). why instantiate them @ point (2) , plug route instance it...

so mainroutebuilder this:

public class mainroutebuilder extends routebuilder {     public static camelcontext camelcontext;      public static boolean returnlabel = true;      public static routebuilder nestedroutebuilder;      routecontainer routecontainer = new routecontainer();      @override     public void configure() throws exception     {         system.out.println("building main route!");         system.out.println("context: " + getcontext());          camelcontext = getcontext();          from("direct:mainroute")         .to(routecontainer.getmyroute(2))         //i may want call again this:         //.to(routecontainer.getmyroute(3))         ;          returnlabel = false;          //configure direct:myroute.2          includeroutes(nestedroutebuilder);           }  } 

my assumption maybe nested route direct:myroute.2 created in wrong camelcontext, console output tells me not so.

any idea doing wrong here?

route configuration != route execution

it seems confusing route configuration route execution. we've been there ;-)

when configure routebuilder in mainroutebuilder#configure(), method only executed once when camel app bootstraps, in order set routing logic. dsl creates plumbing route (processors, interceptors, etc.) , that's route runtime be.

point bring home: dsl not executed on , on again every exchange.

in other words, camel not point out in (3). doesn't execute new routecontainer().getmyroute(2) every single exchange. think it: bytecode configure() executed when configuring camel, , bytecode instantiates object of class routecontainer , invokes getmyroute argument 2. resulting object fed sendprocessor to() dsl generates.

analysis of code

now, regards why code doesn't yield result expect.

you have problem state-keeping of routecontainer. every time call getmyroute overwrite instance variable route. it's impossible current code call getmyroute several times (with different ns) , call includeroutes once @ end, because generated route added.

i don't masking camel route class class of own, act placeholder, brings different discussion didn't ask for.

simpler solution

instead of routecontainer class, here's routegenerator class creates routes , returns direct: endpoint caller. keeps track of routes in internal set.

public class routegenerator {      private set<routebuilder> routebuilders = new hashset<>();      public string generateroute(final int n) {          routebuilders.add(new routebuilder() {             @override public void configure() throws exception {                 system.out.println("building nested route!");                 system.out.println("context: " + getcontext());                  from("direct:myroute." + n)                     .transform() .simple("number: " + n)                     .to("stream:out")                     .process(new processor() {                         @override                         public void process(exchange exchange) throws exception {                             response response = new response();                             response.setstatus(status.success);                             exchange.getout().setbody(response);                         }                     });             }         });          return "direct:myroute." + n;      }      public set<routebuilder> getroutebuilders() {         return routebuilders;     }  } 

and here mainroutebuilder, instantiates routegenerator once, , can generate many routes wish.

once finish configuring routes, iterate on accumulated routebuilders , include them:

public class mainroutebuilder extends routebuilder {      public static camelcontext camelcontext;      public static routegenerator routegenerator = new routegenerator();      @override     public void configure() throws exception {         system.out.println("building main route!");         system.out.println("context: " + getcontext());          camelcontext = getcontext();          from("direct:mainroute")             .to(routegenerator.generateroute(2));           (routebuilder routebuilder : routegenerator.getroutebuilders()) {             includeroutes(routebuilder);         }     }  } 

edit: why doesn't option (2) work?

after debugging time, realised why you're seeing effect.

extracted the java tutorial:

as instance methods , variables, inner class associated instance of enclosing class , has direct access object's methods , fields.

in (2), create instance of route within scope of initial routecontainer object, acting outer object. route object retains outer routecontainer outer object. from() , subsequent methods therefore being invoked on initial routecontainer (routebuilder) object, not on new routecontainer create later, 1 provide upper routebuilder (which associated camelcontext).

that's why direct:myroute.2 not being added camel context, because it's being created in different route builder.

also note console output of (2):

building main route! context: camelcontext(camel-1) building nested route! context: camelcontext(camel-2) added! 

the second route being added different context camel-2. new context created camel lazily when adds route old routebuilder, hasn't been associated camel context yet.

note the camel context of initial routecontainer (created in instance variable initialization) null, because assign mainroutebuilder.camelcontext property later on.

you can see how 2 different route builders being used adding following println statements:

inside route#configure:

system.out.println("routecontainer route added: " + routecontainer.this.hashcode()); 

inside mainroutebuilder#configure, before includeroutes:

system.out.println("routecontainer loaded camel: " + nestedroutebuilder.hashcode()); 

with (1), hashcode is same. (2), hashcode is different, showing there 2 different routebuilders in play (one contains route, , 1 that's loaded context, not include route).


source: i'm apache camel pmc member , committer.


Comments

Popular posts from this blog

sublimetext3 - what keyboard shortcut is to comment/uncomment for this script tag in sublime -

java - No use of nillable="0" in SOAP Webservice -

ubuntu - Laravel 5.2 quickstart guide gives Not Found Error -