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