2

Normally we are deploying our camel/blueprint based services once. Each service has its own property placeholder and the camel-context is bound to it:

<cm:property-placeholder id="service-name.placeholder" persistent-id="service-name.blueprint">
    <cm:default-properties>
        ...
    </cm:default-properties>
</cm:property-placeholder>
...
<camelContext id="service-name-service-camel" xmlns="http://camel.apache.org/schema/blueprint"
    useMDCLogging="true">
    <propertyPlaceholder id="properties" location="blueprint:service-name.placeholder" />
    <routeBuilder ref="mainRoute"/>
</camelContext>

Now we created a service that we want deploy multiple times. Each instance should use its own set of property-values. The only way i see is to set the property placeholder name on compile time (maven filter) but this will result in different artifacts - bad.

Is there a way to set the property placeholder to be used on runtime or start time?

dermoritz
  • 12,519
  • 25
  • 97
  • 185

1 Answers1

2

You can do it with ManagedServiceFactory and a few lines of code.

Define a bean for the Factory, and inject the BundleContext. Chose a Pid to later identify and configure this Factory:

<bean id="myServiceFactory" class="org.my.MyServiceFactory" init-method="init" destroy-method="destroy">
    <property name="bundleContext" ref="blueprintBundleContext"/>
    <property name="configurationPid" value="org.my.pid"/>
</bean>

Implement the service Factory (not working code, just to give you an idea):

public class MyServiceFactory implements ManagedServiceFactory {

    private BundleContext bundleContext;

    private String configurationPid;

    public void setConfigurationPid(String configurationPid) {
       this.configurationPid = configurationPid;
    }

    public void setBundleContext(BundleContext bundleContext) {
       this.bundleContext = bundleContext;
    }

    public void init() {
        // your setup goes here
    }

    public void destroy() {
        // your shutdown logic goes here
    }

    @Override
    public String getName() {
        return configurationPid;
    }

    @Override
    public void updated(String pid, Dictionary dict) throws ConfigurationException { 
        // Instantiate each service with its own properties
        MyServiceImpl service = new MyServiceImpl(dict);
        Dictionary servProps = new Properties();
        servProps.put("custom.service.property", "an id or someting")
        bundleContext.registerService(MyServiceImpl.class.getName(), service, servProps);
        // save your servicereferences to unregister, eg in a map
        // you can customize your service by giving some property to later retrieve it
    }

    @Override
    public void deleted(String pid) {
        // get the ServiceReference from some map
        servicereference.unregister();
    }

}

The ManagedServiceFactory has one method init() to setup all required resources, a destroy() method to clean up (for example by unregistering all the services).

A new service instance is created for each config file in etc/org.my.pid-*.cfg, for example to create 3 instances of a service:

etc/org.my.pid-serviceinstance1.cfg
etc/org.my.pid-serviceinstance2.cfg
etc/org.my.pid-whatever.cfg

To get a particular instance of the service, register them with some custom property (like custom.service.property in my example). Then in a consumer bundle ask for an instance of MyService with custom.service.property = serviceinstance2 and you're done.

You can even create new CamelContexts this way. There is a complete tutorial on PacktPub website.

edit: When you write a new file in etc/org.my.pid-* the updated() method is called and a new service deployed. When a file is removed, the deleted() method is called and you must destroy and unregister the service. Of course, you can add/delete/modify files with JBoss/Karaf running :-) without stopping the main bundle.

Alessandro Da Rugna
  • 4,571
  • 20
  • 40
  • 64
  • thanks for this detailed answer. But one question remains: How to setup the camel context? Is it enough to have one in my blueprint xml? Blueprint should instantiate the camelcontext (along with all other beans) multiple times - once per property file, right? – dermoritz Apr 05 '17 at 08:18
  • @dermoritz In the example on PacktPub they set up a single CamelContext and instantiate routes dynamically inside it using java [RouteBuilder](https://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/builder/RouteBuilder.html). Each route will have its own property values. So yes, one CamelContext is enough. Blueprint instantiates the CamelContext, your ManagedServiceFactory instantiates the routes based on config files. – Alessandro Da Rugna Apr 05 '17 at 09:08
  • @dermoritz Take a look at the example they provide: https://github.com/jgoodyear/ApacheKarafCookbook/tree/master/chapter2/chapter2-recipe6 – Alessandro Da Rugna Apr 05 '17 at 09:12
  • thanks - i accepted. the book and the linked example could are really helpful. thanks for kicking me in right direction (it opened a new room for me) – dermoritz Apr 05 '17 at 09:52