views:

212

answers:

2

I have a cluster of servers running a spring application. Some of the spring components need to be configured differently depending on the role their server is playing (primary, secondary, etc). I don't want to maintain separate spring config files for each role, rather I want to dynamically detect this when the application is started. Its almost like I want conditional bean instantiation (which doesn't exist in spring).

Q: What is the best way to achieve this type of configuration?

Example: Only the primary node in a cluster should create durable subscription to a JMS broker (which requires a globally unique JMS clientID). I can detect if the current host has this role by looking up the running server's hostname in a database and start this container manually (if my node happens to be primary); however, I don't want every node in the cluster to create a durable subscription (by instantiating this bean).

<bean id="auditrecordListenerContainer"
    class="org.springframework.jms.listener.DefaultMessageListenerContainer">
    <property name="concurrentConsumers" value="1" />
    <property name="clientID" value="${server-hostname}" />
    <property name="durable" value="true" />

    <!-- only started on the primary node: via application listener -->
    <property name="autoStartup" value="false" />
</bean>

Note, however there is no ${server-hostname} property in the spring container (at least that I know of)

+1  A: 

You can implement a conditional instantiation logic as a FactoryBean

axtavt
Usually FactoryBeans are used to implement custom namespaces (like spring security), I don't want to write a framework; I want to create an application using stock components.
Justin
Sort of right, in a general way, but accepted answer explains it better.
Justin
+1  A: 

If your code already conditionally starts the appropriate services based on object properties, you can use utility methods in the following manner:

<!-- Factory methods to determine properties -->
<bean id="hostname" class="MyUtil" factory-method="determineHostName"/>
<bean id="isHost" class="MyUtil" factory-method="isHost"/>


<bean id="auditrecordListenerContainer"
  class="org.springframework.jms.listener.DefaultMessageListenerContainer">
  <property name="concurrentConsumers" value="1" />
  <property name="durable" value="true" />

  <!-- Reference properties here -->
  <property name="hostname" ref="hostname" /> 
  <property name="autoStartup" ref="isHost" />
</bean>

To use a property of a singleton bean instead, use a PropertyPathFactoryBean:

<bean id="config" class="MyConfig"/>
<util:property-path id="hostname" path="config.hostname"/>
<util:property-path id="isHost" path="config.host"/>
flicken
Is it possible to use a single bean instance to provide the values?
Justin
Yes, you can use a singleton bean: added example with PropertyPathFactoryBean.
flicken