You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@camel.apache.org by bwtaylor <br...@yahoo.com> on 2008/06/02 07:30:15 UTC

Controlling the ApplicationContext used by a RouteBuilder

I'm embedding camel in ActiveMQ. I start activemq, which reads
$ACTIVEMQ_HOME/conf/activemq.xml . I want to use the java DSL for my routes,
using the <package> tag in activemq.xml to allow them to be discovered from
the classpath. The RouteBuilders defined there use a Registry based on the
ApplicationContext defined by the activemq.xml file, whereas I want them to
see a registry built out of an ApplicationContext that already exists,
defined by my code and spring xml files in my jars. 

It looks like the activemq.xml parsing of the camelContext element is
handled by org.apache.camel.spring.handler.CamelNamespaceHandler which uses
a org.apache.camel.spring.CamelContextFactoryBean. This is
ApplicationContextAware and uses the injected context as the basis for a
SpringCamelContext. This is used in installRoutes(), which calls addRoutes()
on the discovered RouteBuilders, whereupon each RouteBuilder's context is
set to the SpringCamelContext from CamelContextFactoryBean.

So it seems what I need to do is to tell the CamelContextFactoryBean to use
the ApplicationContext I want. An equivalent way is to start activemq and
have the application context it constructs from the activemq.xml use mine as
a parent. It looks like I could do that by overriding
createApplicationContext() in XBeanBrokerFactory to call
ResourceXmlApplicationContext(resource, parent) and having my subclass
override the handling of xbean: uri's. Is this overkill? 



-- 
View this message in context: http://www.nabble.com/Controlling-the-ApplicationContext-used-by-a-RouteBuilder-tp17594236s22882p17594236.html
Sent from the Camel - Users mailing list archive at Nabble.com.


Re: Controlling the ApplicationContext used by a RouteBuilder

Posted by bwtaylor <br...@yahoo.com>.
I had to fix one little issue, by calling setParentBeanFactory also, as
shown... and logged a message.

   private void attachParent() {
        if (applicationContext != null && parentApplicationContext !=null) {        
            applicationContext.setParent(parentApplicationContext);
           
applicationContext.getBeanFactory().setParentBeanFactory(parentApplicationContext);
            log.info("attached parentApplicationContext
"+parentApplicationContext.getDisplayName()+" to
"+applicationContext.getDisplayName());
        }
    }

-- 
View this message in context: http://www.nabble.com/Controlling-the-ApplicationContext-used-by-a-RouteBuilder-tp17594236s22882p17727409.html
Sent from the Camel - Users mailing list archive at Nabble.com.


Re: Controlling the ApplicationContext used by a RouteBuilder

Posted by bwtaylor <br...@yahoo.com>.
I feel silly. I just came up with a way to do what I want in pure spring. The
solution I was proposing with  CamelContextFactoryBean, doesn't actually use
any aspect of the Camel code. You can attach an AC to a new parent in
standalone code, like so:

-------------------------------------
package utility.spring;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;

public class ParentInjector implements ApplicationContextAware {
    
    ConfigurableApplicationContext applicationContext;
    ApplicationContext parentApplicationContext;

    public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
        if  (  (ConfigurableApplicationContext.class).isAssignableFrom(
applicationContext.getClass() ) ) {
            this.applicationContext =
(ConfigurableApplicationContext)applicationContext; 
            attachParent();
        } else {
            throw new BeanNotOfRequiredTypeException("<ApplicationContext>",
ConfigurableApplicationContext.class, applicationContext.getClass());
        }
    }
    
    public void setParentApplicationContext(ApplicationContext
parentApplicationContext) throws BeansException {
        this.parentApplicationContext = parentApplicationContext;        
        attachParent();
    }

    private void attachParent() {
        if (applicationContext != null && parentApplicationContext !=null)            
            applicationContext.setParent(parentApplicationContext);
        }

}
-------------------------------------

Then in ANY xml based config file, you can add a bean like so

-------------------------------------
    <bean class="utility.spring.ParentInjector">
      <property name="parentApplicationContext">
        <bean class="utility.spring.MyFavSpringLocatorOrFactory"
factory-method="getApplicationContext">
           <constructor-arg value="theNameOfMyContext"/> 
        </bean>
      </property>
    </bean>
-------------------------------------

Where utility.spring.MyFavSpringLocatorOrFactory is a class that locates,
builds, borrows, or steals an ApplicationContext object in any fashion it
pleases, xml based or not. 


James.Strachan wrote:
> 
> BTW I'm still not quite sure why you couldn't just have this inside
> activemq.xml
> <include uri="myroutes.xml"/>
> then in myroutes.xml have a <camelContext> and whatever other routes you
> want?
> 

For example, suppose I want to make a message enricher, and my route will be
something like:
from(activemq:inbox).to("enricher").to(activemq:outbox);

Suppose the "enricher" bean is a camel Processor that will delegate to a
bunch of preexisting beans, like my data access layer, defined in some
ApplicationContext that is not 100% XML based. Maybe we used the grails
spring builder or spring's java config for all or part of it. Maybe I have
mostly XML, except that I do a little bit with java code that uses Spring
APIs to manipulate the AC before it's offered up for consumption. Maybe I'm
constructing my AC dynamically from a registry using mule's
GalaxyApplicationContext. 

I could port the "enricher" bean to an XML config, but I'd have to do so for
it and all it's bean dependencies, which might be hard to accomplish. If I
succeed, then I'm double maintaining them, violating DRY. It might not even
be possible to know what the dependencies are: if I use something like a
strategy pattern with <property name="strategy"
value="${strategy.impl.bean.name}" , a plugin system, or a registry
indirection like GalaxyApplicationContext, they could be changing without my
control.
-- 
View this message in context: http://www.nabble.com/Controlling-the-ApplicationContext-used-by-a-RouteBuilder-tp17594236s22882p17705503.html
Sent from the Camel - Users mailing list archive at Nabble.com.


Re: Controlling the ApplicationContext used by a RouteBuilder

Posted by James Strachan <ja...@gmail.com>.
2008/6/4 bwtaylor <br...@yahoo.com>:
>
>
> James.Strachan wrote:
>>
>> You can also add <include/> tags to include other spring.xml files
>> inside the activemq.xml if you want. Plus if you need it you can split
>> routes between different spring XML files. (e.g. have a camelContext
>> in each spring.xml defining some routes).
>>
>
> <include/> tags are the first approach I tried. This is just a mechanism to
> split up activemq.xml for manageability. The problem is that not every
> ApplicationContext is formed by loading from XML files. We use a mix of java
> and xml to create the AC we want to use. See below for more on non-XML AC
> configuration.
>
>
> James.Strachan wrote:
>>
>> Note that the activemq.xml is just a spring AC; you can create them
>> however you wish without the need to create new factory beans or hack
>> XBean or Spring or whatever.
>>
>> e.g. just run the <broker> inside any spring.xml / AC you like.
>>
>
> Sure. But then you don't get the nice activemq admin scripts and startup
> model. We could always start everything ourselves and effectively embed
> activemq in our app rather than the other way around.
>
>
> James.Strachan wrote:
>>
>> FWIW we're generally trying to move away from hacking XBean and
>> relying more on the standard Spring stuff now (and the Spring 2
>> namespace handler stuff) so I'd rather a solution that works with
>> normal Spring
>>
>
> I guess it depends on what you mean by "normal" spring. Parent/child
> relationships between ACs are pretty powerful and I'd call them normal. Lots
> of people are using mechanisms besides XML to configure their ACs now.
> I think you'll see non XML mechanisms for AC configuration becoming much
> more common.
> The grails/groovy spring builder comes to mind, as does the spring java
> config project. Parent/child
> contexts are a natural way to mix AC's together that are defined in
> different ways. In
> some sense, by only allowing XML AC's you are fighting against the full
> power of Spring.
>
> I'm surprised you didn't like adding
> CamelContextFactoryBean.setParentApplicationContext()
> This would let you inject your parent using nothing more than simple XML.
> You
> could just add
>  <property name="parentApplicationContext" ref="myCustomApplicationContext"
> />
> or leave it blank to match current behavior. No xbean hacking is involved.

Sorry I misunderstood; I thought you meant hacking XBean :)

Oh sure, specifying the parent applicationContext on the camel context
factory bean is fine. I just wanted to check what you wanted the
CamelContextFactoryBean to do with the parent applicationContext its
given?

BTW I'm still not quite sure why you couldn't just have this inside activemq.xml

<include uri="myroutes.xml"/>

then in myroutes.xml have a <camelContext> and whatever other routes you want?

-- 
James
-------
http://macstrac.blogspot.com/

Open Source Integration
http://open.iona.com

Re: Controlling the ApplicationContext used by a RouteBuilder

Posted by bwtaylor <br...@yahoo.com>.

James.Strachan wrote:
> 
> You can also add <include/> tags to include other spring.xml files
> inside the activemq.xml if you want. Plus if you need it you can split
> routes between different spring XML files. (e.g. have a camelContext
> in each spring.xml defining some routes).
> 

<include/> tags are the first approach I tried. This is just a mechanism to
split up activemq.xml for manageability. The problem is that not every 
ApplicationContext is formed by loading from XML files. We use a mix of java
and xml to create the AC we want to use. See below for more on non-XML AC 
configuration.


James.Strachan wrote:
> 
> Note that the activemq.xml is just a spring AC; you can create them
> however you wish without the need to create new factory beans or hack
> XBean or Spring or whatever.
> 
> e.g. just run the <broker> inside any spring.xml / AC you like.
> 

Sure. But then you don't get the nice activemq admin scripts and startup
model. We could always start everything ourselves and effectively embed 
activemq in our app rather than the other way around.


James.Strachan wrote:
> 
> FWIW we're generally trying to move away from hacking XBean and
> relying more on the standard Spring stuff now (and the Spring 2
> namespace handler stuff) so I'd rather a solution that works with
> normal Spring
> 

I guess it depends on what you mean by "normal" spring. Parent/child 
relationships between ACs are pretty powerful and I'd call them normal. Lots
of people are using mechanisms besides XML to configure their ACs now. 
I think you'll see non XML mechanisms for AC configuration becoming much
more common.
The grails/groovy spring builder comes to mind, as does the spring java
config project. Parent/child
contexts are a natural way to mix AC's together that are defined in
different ways. In
some sense, by only allowing XML AC's you are fighting against the full
power of Spring.

I'm surprised you didn't like adding 
CamelContextFactoryBean.setParentApplicationContext() 
This would let you inject your parent using nothing more than simple XML.
You
could just add 
  <property name="parentApplicationContext" ref="myCustomApplicationContext"
/>
or leave it blank to match current behavior. No xbean hacking is involved.


-- 
View this message in context: http://www.nabble.com/Controlling-the-ApplicationContext-used-by-a-RouteBuilder-tp17594236s22882p17656860.html
Sent from the Camel - Users mailing list archive at Nabble.com.


Re: Controlling the ApplicationContext used by a RouteBuilder

Posted by James Strachan <ja...@gmail.com>.
2008/6/3 bwtaylor <br...@yahoo.com>:
>
> I'd still need the activemq.xml to start my ApplicationContext, as I want
> it's lifecycle coupled to that of ActiveMQ and effectively embedded within
> ActiveMQ. I suppose I could do by simply putting a factory bean for it in
> activemq.xml.

You can also add <include/> tags to include other spring.xml files
inside the activemq.xml if you want. Plus if you need it you can split
routes between different spring XML files. (e.g. have a camelContext
in each spring.xml defining some routes).

>I liked the idea of my AC being the parent of the one created
> by ActiveMQ, so that you still could put xml routes in activemq.xml with the
> combined registry.

Note that the activemq.xml is just a spring AC; you can create them
however you wish without the need to create new factory beans or hack
XBean or Spring or whatever.

e.g. just run the <broker> inside any spring.xml / AC you like.

For the dependency stuff, just make sure you use the depends attribute
on the beans that define the JMS ConnectionFactory etc.



>  I already tried it by subclassing
> XBeanBrokerFactory.createApplicationContext(String uri) to call my parent
> locator class followed by new ResourceXmlApplicationContext(resource,
> parent). This solves my problem but isn't generalizable because I have to
> import my specific locator. It's not particularly elegant, but it works.
>
> Interestingly,  ResourceXmlApplicationContext does implement
> ConfigurableApplicationContext which gives the setParent() method, so there
> might be a way to combine our ideas. CamelContextFactoryBean could support
> setParentApplicationContext() injected from the activmq.xml Context and then
> it could use the combined (adopted?) AC if the child can be cast to
> ConfigurableApplicationContext and a parent exists.
>
> Actually, ConfigurableApplicationContext might be better a better interface
> to use generally, since your implementation already conforms to it, and it
> also allows you to add application listeners, to add both flavors of post
> processors,  to refresh, and to add shutdown hooks. Application listeners
> and refresh both seem very valueable. If this interests you, let me know, as
> I'd be willing to send in patches.

FWIW we're generally trying to move away from hacking XBean and
relying more on the standard Spring stuff now (and the Spring 2
namespace handler stuff) so I'd rather a solution that works with
normal Spring

-- 
James
-------
http://macstrac.blogspot.com/

Open Source Integration
http://open.iona.com

Re: Controlling the ApplicationContext used by a RouteBuilder

Posted by bwtaylor <br...@yahoo.com>.
I'd still need the activemq.xml to start my ApplicationContext, as I want
it's lifecycle coupled to that of ActiveMQ and effectively embedded within
ActiveMQ. I suppose I could do by simply putting a factory bean for it in
activemq.xml. I liked the idea of my AC being the parent of the one created
by ActiveMQ, so that you still could put xml routes in activemq.xml with the
combined registry. I already tried it by subclassing
XBeanBrokerFactory.createApplicationContext(String uri) to call my parent
locator class followed by new ResourceXmlApplicationContext(resource,
parent). This solves my problem but isn't generalizable because I have to
import my specific locator. It's not particularly elegant, but it works.

Interestingly,  ResourceXmlApplicationContext does implement
ConfigurableApplicationContext which gives the setParent() method, so there
might be a way to combine our ideas. CamelContextFactoryBean could support
setParentApplicationContext() injected from the activmq.xml Context and then
it could use the combined (adopted?) AC if the child can be cast to
ConfigurableApplicationContext and a parent exists. 

Actually, ConfigurableApplicationContext might be better a better interface
to use generally, since your implementation already conforms to it, and it
also allows you to add application listeners, to add both flavors of post
processors,  to refresh, and to add shutdown hooks. Application listeners
and refresh both seem very valueable. If this interests you, let me know, as
I'd be willing to send in patches.


James.Strachan wrote:
> 
> Why not put the <camelContext ...> <package> elements into your other
> spring XML file? (i.e. remove the package element from the activemq.xml)
> 

-- 
View this message in context: http://www.nabble.com/Controlling-the-ApplicationContext-used-by-a-RouteBuilder-tp17594236s22882p17613713.html
Sent from the Camel - Users mailing list archive at Nabble.com.


Re: Controlling the ApplicationContext used by a RouteBuilder

Posted by James Strachan <ja...@gmail.com>.
2008/6/2 bwtaylor <br...@yahoo.com>:
>
> I'm embedding camel in ActiveMQ. I start activemq, which reads
> $ACTIVEMQ_HOME/conf/activemq.xml . I want to use the java DSL for my routes,
> using the <package> tag in activemq.xml to allow them to be discovered from
> the classpath. The RouteBuilders defined there use a Registry based on the
> ApplicationContext defined by the activemq.xml file, whereas I want them to
> see a registry built out of an ApplicationContext that already exists,
> defined by my code and spring xml files in my jars.

Why not put the <camelContext ...> <package> elements into your other
spring XML file? (i.e. remove the package element from the
activemq.xml)

-- 
James
-------
http://macstrac.blogspot.com/

Open Source Integration
http://open.iona.com