Activemq cluster of embedded brokers and failover consumers - spring

Environment
There is an existing software with propietary way of clustering, which should be moved to use JMS
The customer do not want to pay for setting up and mainaining a
messaging system, so it can be used ONLY if I can embedd the whole
messaging into the existing virtual machines
Broker instances and consumers should be in the same JVM. Consumers
should be able to connect to remote broker in failover situations, since all consumers no matter in which JVM will they run, should have ONE input queue.
it would be nice if the consumers would use direct method calls to
communicate with local broker
Demo Project
I have created a really simple demo (eclipse) project with ActiveMQ + Maven + Spring (the whole project is at http://www.woofiles.com/dl-279452-fOcsWkcm-activemq.zip). If you try it, change the dataDirectory of activemq, since it is a wired absolute path till now.
I try to start up a broker from spring, and also a set of consumers. See Spring config below:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:amq="http://activemq.apache.org/schema/core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://activemq.apache.org/schema/core
http://activemq.apache.org/schema/core/activemq-core.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.0.xsd">
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_FALLBACK" />
</bean>
<bean id="embeddedBroker" class="org.apache.activemq.broker.BrokerService"
destroy-method="stop" init-method="start" >
<property name="brokerName" value="conversion" />
<property name="dataDirectory"
value="c:\eclipseWithMaven\activemq\working\conversion" />
<property name="schedulerSupport" value="false" />
<property name="transportConnectorURIs">
<list>
<value>tcp://127.0.0.1:600${idOfClusterNode}</value>
</list>
</property>
<property name="managementContext">
<bean class="org.apache.activemq.broker.jmx.ManagementContext">
<property name="connectorPort" value="201${idOfClusterNode}" />
</bean>
</property>
</bean>
<!-- depends-on see why at http://activemq.apache.org/vm-transport-reference.html -->
<!--depends-on="embeddedBroker" -->
<bean id="jmsFactory" class="org.apache.activemq.ActiveMQConnectionFactory" depends-on="embeddedBroker">
<property name="brokerURL">
<value>failover:(vm:/conversion,tcp://127.0.0.1:6001,tcp://127.0.0.1:6002)</value>
</property>
</bean>
<bean id="cachedConnectionFactory"
class="org.springframework.jms.connection.CachingConnectionFactory"
p:targetConnectionFactory-ref="jmsFactory" p:sessionCacheSize="10" />
<bean id="container"
class="org.springframework.jms.listener.SimpleMessageListenerContainer">
<property name="concurrentConsumers" value="10" />
<property name="connectionFactory" ref="cachedConnectionFactory" />
<property name="messageListener" ref="conversion" />
<property name="destination" ref="conversionInputQueue" />
</bean>
<bean id="conversionInputQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="conversionInputQueue" />
</bean>
<bean id="conversion" class="activemq.Conversion"
p:clusterId="${idOfClusterNode}" />
</beans>
I simply try to start up one or two instances of the activemq.ConversionDemo class with different parameters used by spring & log4j config. The environment entries of the run config looks like this:
Instance 1 : -DidOfClusterNode=1 -DidOfOtherClusterNode=2 -DlogFile=conversion1.log
Instance 2: -DidOfClusterNode=2 -DidOfOtherClusterNode=1
-DlogFile=conversion2.log
If I start up one instance, it is fine. If two is running the following problems occoured:
The second broker will not start up at all. It says it does not have the lock. Its fine, but I supposed, it just starts up a thread asynchronously, and will give back the control to spring. But it seems, that it wont let spring to continue.
SimpleMessageListenerContainer also seems like holding the control
over spring till all consumers are started.
What I want
I want to meet the requirements above
I think I have to start up both brokers and consumers asynchronously,
which I cannot really do in spring with this config
It would be nice to have real load balancing between brokers. It seems, that ActiveMQ is prepared only for failover.
If my needs cannot be satisfied by ActiveMQ, please recommend
other free solution.
If you need further info, just let me know.
EDIT
I think ActiveMQ supports my needs, I just need to understand "network of brokers". So I guess I have to have two filestore, and a network from my two brokers.

if you point 2 brokers at the same file store, then the first one will get the lock and the 2nd will block until the lock is released...this is a shared file system master/slave setup
if you want an active/active setup, then you'll need to use separate file stores and wire them together as a network of brokers

Related

Spring multiple beans with same id in a factory pattern?

I have a two beans with the same id as thats the Neo4j Class it maps to internally so i can't have the id changed for both. Now for a clustered environment I need this bean:
<bean id="graphDatabaseService" factory-bean="graphDbBuilderFinal"
factory-method="newGraphDatabase" destroy-method="shutdown" />
And for a non-clustered one I need this :
<bean id="graphDatabaseService" class="org.springframework.data.neo4j.support.GraphDatabaseServiceFactoryBean"
destroy-method="shutdown" scope="singleton">
<constructor-arg value="${neo4j.database.path}" />
</bean>
Right now I comment either one of them based on environment as not all environments will have cluster setup. Is there a way where as based on environment value(a property may be) it picks one bean among these.
This is how it is used in java class.
#Autowired
private GraphDatabaseService graphDB;
Thanks,
You can use spring profile feature (since spring 3.1.X) see link
e.g
<beans profile="cluster">
<bean id="graphDatabaseService" factory-bean="graphDbBuilderFinal"
factory-method="newGraphDatabase" destroy-method="shutdown" />
</beans>
<beans profile="no_cluster">
<bean id="graphDatabaseService" class="org.springframework.data.neo4j.support.GraphDatabaseServiceFactoryBean"
destroy-method="shutdown" scope="singleton">
<constructor-arg value="${neo4j.database.path}" />
</bean>
</beans>
and active the profile in your application in this way
-Dspring.profiles.active="cluster"
Will be loaded only the beans without profile and all those with the profile activated.
I hope this solution can help to solve your problem.
You dont need to define with different Id´s just in different xml configuration files. Like
cluster.xml --->
<bean id="graphDatabaseService" factory-bean="graphDbBuilderFinal"
factory-method="newGraphDatabase" destroy-method="shutdown" />
no_cluster.xml
<bean id="graphDatabaseService" class="org.springframework.data.neo4j.support.GraphDatabaseServiceFactoryBean"
destroy-method="shutdown" scope="singleton">
And then use Spring profile to load one file or another depending of the profile
<beans profile="cluster">
<import resource="spring/cluster.xml" />
</beans>
<beans profile="no_cluster">
<import resource="spring/no_cluster.xml" />
</beans>

Spring + JMS + ActiveMQ without dependency on ActiveMQ

I'm trying to build a MQ producer that sends a message to an ActiveMQ queue. The thing is that I don't want the implementation to be specific for ActiveMQ. I actually am doing the ActiveMQ sending in dev environment, and in production the application server will use something else. I'm planning to use Maven to create 2 profiles that will filter resources based on the given profile.
I started playing with JNDI, but I'm stuck... I tried a lot of options, but none are viable.
For now, my spring config xml is like this:
<jee:jndi-lookup id="mqConnectionFactory" jndi-name="java:comp/env/jms/mqConnectionFactory" />
<bean id="jmsJndiTemplate" class="org.springframework.jndi.JndiTemplate">
<property name="environment">
<props>
<prop key="java.naming.factory.initial">org.apache.activemq.jndi.ActiveMQInitialContextFactory</prop>
<prop key="java.naming.provider.url">vm://localhost</prop>
</props>
</property>
</bean>
<bean id="ismeJmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="defaultDestination" ref="ismeJmsDestination"/>
<property name="connectionFactory" ref="mqConnectionFactory"/>
</bean>
<bean id="ismeJmsDestination" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiTemplate" ref="jmsJndiTemplate"/>
<property name="jndiName" value="dynamicQueues/FOO.BAR"/>
</bean>
mqConnectionFactory cannot be found because I didn't add it in JNDI. I don't know how to add it actually, as this is not a webapp, so no context.xml.
Can someone help me in the right direction?
Whatever broker implementation you chose, you need to have the corresponding client library in your classpath. The JMS API is just an API, you need an actual implementation to connect to the broker of your choice.
Say you want to connect to ActiveMQ in development and to whatever JMS implementation in production. You can isolate those two config in separate profiles and make the activeMQ dependency an optional dependency in Maven.

Synchronous Spring ActiveMQ receiver

My app is a Spring based application. I'm using activemq as a broker. I manage in my app two different queues receiving messages.
For each queue, my app's aim is to listen to messages sent on the broker, then proceed the message (read it, performs database actions from informations coming to this message etc.), and then treat the next message (so the treatment is synchronous, I want to proceed messages in the order of their arrival).
My actual design is this one :
I create a thread
<bean id="pollThread" class="my.app.receiver" init-method="start" destroy-method="interrupt">
</bean>
run() method of the thread call in a while(true) loop a method which does :
This thread create a connection to active mq and block with receive
After receiving, i close the connection and treat the message (database stuff and so)
Treatment done, end of method
and then the treatment restart (listening with receive, treatment etc.)
My question is : is there any manner to design it better ? My only mandatory process is to proceed message in the order of arrival and do the treatment before handling the next message.
I've read a lot of thing about JMSTemplate and so on but i'm lost whith all the information.
Actually my best guess is too create a PooledConnectionFactory (because we will use only ActiveMQ and CachingConnectionFactory seems to have limitations that matters in our architecture) and to limit it to one concurrentConsumer. Then to use the MessageListener interface to proceed my message.
Thanks
You can use the Message Driven POJO infrastructure provided by the Spring framework. It will do the polling on the destination and recover in case of failures of the broker, etc.
You can change your code (say YourMessageListener) to implement MessageListener. Then the following config can get you started
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="messageListener" class="org.foo.YourMessageListener"/>
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://your-server:61616"/>
</bean>
<bean id="destination" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="queue/yourQueue"/>
</bean>
<bean id="jmsContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="destination"/>
<property name="concurrency" value="1"/>
<property name="messageListener" ref="messageListener"/>
</bean>
</beans>
The important setting is the concurrency element which limits the number of threads that are can simultaneously handle messages on that destination. By setting it to one, only one thread consumes a message at a time.
See the documentation for more details

spring transactional cpool. Which one do I use?

I originally set up spring with xapool, but it turns out that's a dead project and seems to have lots of problems.
I switched to c3p0, but now I learn that the #Transactional annotations don't actually create transactions when used with c3p0. If I do the following it will insert the row into Foo even through an exception was thrown inside the method:
#Service
public class FooTst
{
#PersistenceContext(unitName="accessControlDb") private EntityManager em;
#Transactional
public void insertFoo() {
em.createNativeQuery("INSERT INTO Foo (id) VALUES (:id)")
.setParameter("id", System.currentTimeMillis() % Integer.MAX_VALUE )
.executeUpdate();
throw new RuntimeException("Foo");
}
}
This is strange because if I comment out the #Transactional annotation it will actually fail and complain about having a transaction set to rollback only:
java.lang.IllegalStateException: Cannot get Transaction for setRollbackOnly
at org.objectweb.jotm.Current.setRollbackOnly(Current.java:568)
at org.hibernate.ejb.AbstractEntityManagerImpl.markAsRollback(AbstractEntityManagerImpl.java:421)
at org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:576)
at org.hibernate.ejb.QueryImpl.executeUpdate(QueryImpl.java:48)
at com.ipass.rbac.svc.FooTst.insertFoo(FooTst.java:21)
at com.ipass.rbac.svc.SingleTst.testHasPriv(SingleTst.java:78)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.test.context.junit4.SpringTestMethod.invoke(SpringTestMethod.java:160)
at org.springframework.test.context.junit4.SpringMethodRoadie.runTestMethod(SpringMethodRoadie.java:233)
at org.springframework.test.context.junit4.SpringMethodRoadie$RunBeforesThenTestThenAfters.run(SpringMethodRoadie.java:333)
at org.springframework.test.context.junit4.SpringMethodRoadie.runWithRepetitions(SpringMethodRoadie.java:217)
at org.springframework.test.context.junit4.SpringMethodRoadie.runTest(SpringMethodRoadie.java:197)
at org.springframework.test.context.junit4.SpringMethodRoadie.run(SpringMethodRoadie.java:143)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.invokeTestMethod(SpringJUnit4ClassRunner.java:160)
at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51)
at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44)
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)
at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:97)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:45)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
So, clearly it notices the #Transactional annotation. But, it doesn't actually set autocommit to off at the start of the method.
Here is how I have transactional stuff setup up in the applicationContext.xml. Is this correct? If not, what is this supposed to be?
<bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean"/>
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" ref="jotm"/>
<property name="userTransaction" ref="jotm"/>
<property name="allowCustomIsolationLevels" value="true"/>
</bean>
<tx:annotation-driven transaction-manager="txManager" proxy-target-class="false"/>
After a bunch of searching I found a connection pool called Bitronix, but their spring setup page describes stuff about JMS which doesn't even make any sense. What does JMS have to do with setting up a connection pool?
So I'm stuck. What am I actually supposed to do? I don't understand why the connection pool needs to support transactions. All connections support turning autocommit on and off, so I have no idea what the problem is here.
It took a lot of searching and experimentation, but I finally got things working. Here are my results:
enhydra xapool is a terrible connection pool. I won't enumerate the problems it caused because it doesn't matter. The latest version of that pool hasn't been updated since Dec 2006. It's a dead project.
I put c3p0 into my application context and got it working fairly easily. But, for some reason it just doesn't seem to support rollback even inside a single method. If I mark a method as #Transactional then do an insert into a table and then throw a RuntimeException (one that's definitely not declared in the throws list of the method because there is no throws list on the method) it will still keep the insert into that table. It will not roll back.
I was going to try Apache DBCP, but my searching turned up lots of complaints about it, so I didn't bother.
I tried Bitronix and had plenty of trouble getting it to work properly under Tomcat, but once I figured out the magic configuration it works beautifully. What follows is all the things you need to do to set it up properly.
I dabbled briefly with the Atomkos connection pool. It looks like it should be good, but I got Bitronix working first, so I didn't try using it much.
The configuration below works in standalone unit tests and under Tomcat. That was the major problem I had. Most of the examples I found about how to set up Spring with Bitronix assume that I'm using JBoss or some other full container.
The first bit of configuration is the part that sets up the Bitronix transaction manager.
<!-- Bitronix transaction manager -->
<bean id="btmConfig" factory-method="getConfiguration" class="bitronix.tm.TransactionManagerServices">
<property name="disableJmx" value="true" />
</bean>
<bean id="btmManager" factory-method="getTransactionManager" class="bitronix.tm.TransactionManagerServices" depends-on="btmConfig" destroy-method="shutdown"/>
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" ref="btmManager" />
<property name="userTransaction" ref="btmManager" />
<property name="allowCustomIsolationLevels" value="true" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
The major difference between that code and the examples I found is the "disableJmx" property. It throws exceptions at runtime if you don't use JMX but leave it enabled.
The next bit of configuration is the connection pool data source. Note that the connection pool classname is not the normal oracle class "oracle.jdbc.driver.OracleDriver". It's an XA data source. I don't know what the equivalent class would be in other databases.
<bean id="dataSource" class="bitronix.tm.resource.jdbc.PoolingDataSource" init-method="init" destroy-method="close">
<property name="uniqueName" value="dataSource-BTM" />
<property name="minPoolSize" value="1" />
<property name="maxPoolSize" value="4" />
<property name="testQuery" value="SELECT 1 FROM dual" />
<property name="driverProperties"><props>
<prop key="URL">${jdbc.url}</prop>
<prop key="user">${jdbc.username}</prop>
<prop key="password">${jdbc.password}</prop>
</props></property>
<property name="className" value="oracle.jdbc.xa.client.OracleXADataSource" />
<property name="allowLocalTransactions" value="true" />
</bean>
Note also that the uniqueName needs to be different than any other data sources you have configured.
The testQuery of course needs to be specific to the database that you are using. The driver properties are specific to the database class that I'm using. OracleXADataSource for some silly reason has different setter names for OracleDriver for the same value.
The allowLocalTransactions had to be set to true for me. I found recommendations NOT to set it to true online. But, that seems to be impossible. It just won't work if it's set to false. I am not sufficiently knowledgeable about these things to know why that is.
Lastly we need to configure the entity manager factory.
<util:map id="jpa_property_map">
<entry key="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.BTMTransactionManagerLookup"/>
<entry key="hibernate.current_session_context_class" value="jta"/>
</util:map>
<bean id="dataSource-emf" name="accessControlDb" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="persistenceXmlLocation" value="classpath*:META-INF/foo-persistence.xml" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true"/>
<property name="databasePlatform" value="org.hibernate.dialect.Oracle10gDialect"/>
</bean>
</property>
<property name="jpaPropertyMap" ref="jpa_property_map"/>
<property name="jpaDialect"><bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/></property>
</bean>
Note the dataSource property refers to the id of the dataSource I declared. The persistenceXmlLocation refers to a persistence xml file that exists in the classpath somewhere. The classpath*: indicates it may be in any jar. Without the * it won't find it if it's in a jar for some reason.
I found util:map to be a handy way to put the jpaPropertyMap values in one place so that I don't need to repeat them when I use multiple entity manager factories in one application context.
Note that the util:map above won't work unless you include the proper settings in the outer beans element. Here is the header of the xml file that I use:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
Lastly, in order for Bitronix (or apparently any cpool which supports two phase commit) to work with Oracle you need to run the following grants as user SYS. (See http://publib.boulder.ibm.com/infocenter/wasinfo/v6r0/index.jsp?topic=/com.ibm.websphere.express.doc/info/exp/ae/rtrb_dsaccess2.html and http://docs.codehaus.org/display/BTM/FAQ and http://docs.codehaus.org/display/BTM/JdbcXaSupportEvaluation#JdbcXaSupportEvaluation-Oracle)
grant select on pending_trans$ to <someUsername>;
grant select on dba_2pc_pending to <someUsername>;
grant select on dba_pending_transactions to <someUsername>;
grant execute on dbms_system to <someUsername>;
Those grants need to be run for any user that a connection pool is set up for regardless of whether you actually do any modifying of anything. It apparently looks for those tables when a connection is established.
A few other misc issues:
You can't query tables which are remote synonyms in Oracle while inside a Spring #Transactional block while using Bitronix (you'll get an ORA-24777). Use materialized views or a separate EntityManager which directly points at the other DB instead.
For some reason the btmConfig in the applicationContext.xml has problems setting config values. Instead create a bitronix-default-config.properties file. The config values you can use are found at http://docs.codehaus.org/display/BTM/Configuration13 . Some other config info for that file is at http://docs.codehaus.org/display/BTM/JdbcConfiguration13 but I haven't used it.
Bitronix uses some local files to store transactional stuff. I don't know why, but I do know that if you have multiple webapps with local connection pools you will have problems because they will both try to access the same files. To fix this specify different values for bitronix.tm.journal.disk.logPart1Filename and bitronix.tm.journal.disk.logPart2Filename in the bitronix-default-config.properties for each app.
Bitronix javadocs are at http://www.bitronix.be/uploads/api/index.html .
That's pretty much it. It's very fiddly to get it to work, but it's working now and I'm happy. I hope that all this helps others who are going through the same troubles I did to get this all to work.
When I do connection pooling I tend to use the one provided by the app server I'm deploying on. It's just a JNDI name to Spring at that point.
Since I don't want to worry about an app server when I'm testing, I use a DriverManagerDataSource and its associated transaction manager when I'm unit testing. I'm not as concerned about pooling or performance when testing. I do want the tests to run efficiently, but pooling isn't a deal breaker in that case.

host/role dependent spring configuration

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)
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"/>
You can implement a conditional instantiation logic
as a FactoryBean

Resources