spring beans , avoid redundancy

hi guys

In my spring beans xml i have 3 queue beans defined like this

	<bean id="queue1" class="org.springframework.jndi.JndiObjectFactoryBean">
		<property name="jndiTemplate">
			<ref bean="jndiTemplate" />
		</property>
		<property name="jndiName" value="INT.QUEUE1"/>			 
	</bean>

	<bean id="queue2" class="org.springframework.jndi.JndiObjectFactoryBean">
		<property name="jndiTemplate">
			<ref bean="jndiTemplate" />
		</property>
		<property name="jndiName" value="INT.QUEUE2"/>			 
	</bean>

	<bean id="queue3" class="org.springframework.jndi.JndiObjectFactoryBean">
		<property name="jndiTemplate">
			<ref bean="jndiTemplate" />
		</property>
		<property name="jndiName" value="INT.QUEUE3"/>			 
	</bean> 

Open in new window


These Queues are used by Listeners listening for messages

<bean id="listener1"
		class="org.springframework.jms.listener.DefaultMessageListenerContainer">		 
		<property name="destination" ref="queue1" />
		 
	</bean>


	<bean id="listener2"
		class="org.springframework.jms.listener.DefaultMessageListenerContainer">		 
		<property name="destination" ref="queue2" />
		 
	</bean>


	<bean id="listener3"
		class="org.springframework.jms.listener.DefaultMessageListenerContainer">		 
		<property name="destination" ref="queue3" />		 
	</bean> 

Open in new window

So tomorrow if i have to add another listener i have to add another bean (listener4).

Is there a better way to do this?
I want to avoid adding beans.

Thanks
royjaydAsked:
Who is Participating?
 
mccarlConnect With a Mentor IT Business Systems Analyst / Software DeveloperCommented:
Based on that number I would spawn up that many DefaultMessageListererContainer beans on the fly.
Yes, that's fine but how do you then configure the queues and the message listener class, that need to be configured against each DMLC.

To start working on such a requirment i would like to know if I can create beans in the jvm at runtime.
Yes, it's called the "new" keyword! :)  In the simplest of cases, Spring is basically just some code that creates "new" objects and calls ".setXXXX()" methods on those objects. Admittedly, in the case of more complex classes, such as DefaultMessageListenerContainer, where special interfaces are implemented, Spring does a little bit more to initialise and start/stop the DMLC. You can do this yourself, but perhaps Murali suggestion of StaticApplicationContext is more appropriate as you can manipulate the bean definitions programmatically but leave the lifecycle management up to Spring.

Ok, this is a bit of a mock up that I have done. Hopefully, it gets you on the right track. It is still based on a normal Spring xml that you can say is a "template" for your beans. Yes, you could do away with this as well and do it ALL in code, but I think that this way gives you at least some of the benefits of Spring. (In the below, JndiTemplate config would need to be added as well, or inherited from a parent context)
<?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:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <bean id="connectionFactory" class="org.apache.activemq.spring.ActiveMQConnectionFactory" p:brokerURL="tcp://localhost:61616" />

    <bean id="abstractQueue" class="org.springframework.jndi.JndiObjectFactoryBean" abstract="true">
        <property name="jndiTemplate">
            <ref bean="jndiTemplate" />
        </property>
    </bean>

    <bean id="abstractMessagelistener" abstract="true">
    </bean>

    <bean id="abstractListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer" abstract="true">        
        <property name="connectionFactory" ref="connectionFactory" />
        <property name="concurrentConsumers" value="5" />
        <property name="maxConcurrentConsumers" value="15" />
    </bean>
    
    <bean class="testSpringBeanFactory.DMLCBeanDefinitionRegistryPostProcessor" />

</beans>

Open in new window

You could also do away with the "abstractMessageListener" but it is there in case there is any common properties you want to set on all MessageListeners.

The important point about the above is the very last bean that is configured. It is a PostProcessor which Spring will automatically detect and call certain methods on as the Spring ApplicationContext is being created. This is the point where the magic happens. The code in this bean takes the three "abstract" beans defined above and registers beans that inherit from these three templates.
package testSpringBeanFactory;

import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.GenericBeanDefinition;

public class DMLCBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    private int listenerCounter = 0;
    
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        registerListenerBeans(registry, "test.queue1", "testSpringBeanFactory.MessageListener1");
        registerListenerBeans(registry, "test.queue2", "testSpringBeanFactory.MessageListener1");
        registerListenerBeans(registry, "test.blahX", "testSpringBeanFactory.MessageListenerABC");
        registerListenerBeans(registry, "test.fooY", "testSpringBeanFactory.MessageListener1");
    }

    private void registerListenerBeans(BeanDefinitionRegistry registry, String queueName, String messageListenerClassName) {
        listenerCounter++;
        
        GenericBeanDefinition queueDefinition = new GenericBeanDefinition();
        queueDefinition.setParentName("abstractQueue");
        MutablePropertyValues queuePropertyValues = new MutablePropertyValues();
        queuePropertyValues.add("jndiName", queueName);
        queueDefinition.setPropertyValues(queuePropertyValues);
        registry.registerBeanDefinition("queue" + listenerCounter, queueDefinition);
        
        
        GenericBeanDefinition messageListenerDefinition = new GenericBeanDefinition();
        messageListenerDefinition.setParentName("abstractMessagelistener");
        messageListenerDefinition.setBeanClassName(messageListenerClassName);
        MutablePropertyValues messageListenerPropertyValues = new MutablePropertyValues();
        messageListenerPropertyValues.add("queueName", queueName);
        messageListenerDefinition.setPropertyValues(messageListenerPropertyValues);
        registry.registerBeanDefinition("messageListener" + listenerCounter, messageListenerDefinition);
        
        
        GenericBeanDefinition listenerContainerDefinition = new GenericBeanDefinition();
        listenerContainerDefinition.setParentName("abstractListenerContainer");
        MutablePropertyValues listenerContainerPropertyValues = new MutablePropertyValues();
        listenerContainerPropertyValues.add("destination", queueDefinition);
        listenerContainerPropertyValues.add("messageListener", messageListenerDefinition);
        listenerContainerDefinition.setPropertyValues(listenerContainerPropertyValues);
        registry.registerBeanDefinition("listenerContainer" + listenerCounter, listenerContainerDefinition);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    }
    
}

Open in new window

With the above, lines 15 - 18 create 4 different listener instances. If you wanted a fifth instance, then you would just add another line with different parameters.

So, this is where you could bring in whatever mechanism that you want to configure the listeners, eg. if you have a properties file that lists all the queues that need to be consumed and the MessageListener class that should be used, you can inject the file reference into this PostProcessor and have it parse the properties file and call registerListenerBeans() for each queuename and classname that it finds.
0
 
mccarlIT Business Systems Analyst / Software DeveloperCommented:
One other different way (not necessarily better) is to do this... (note that this replaces BOTH of your code snippets from above)
	<bean id="destinationResolver" 
		class="org.springframework.jms.support.destination.JndiDestinationResolver">
		<property name="jndiTemplate" ref="jndiTemplate" />
	</bean>


	<bean id="abstractListener" abstract="true" 
		class="org.springframework.jms.listener.DefaultMessageListenerContainer">
		<property name="destinationResolver" ref="destinationResolver" />
	</bean>


	<bean id="listener1" parent="abstractListener">		 
		<property name="destinationName" value="INT.QUEUE1" />
	</bean>


	<bean id="listener2" parent="abstractListener">		 
		<property name="destinationName" value="INT.QUEUE2" />
	</bean>


	<bean id="listener3" parent="abstractListener">		 
		<property name="destinationName" value="INT.QUEUE3" />
	</bean>

Open in new window

And then if you need to add listeners, you just need to duplicate 3 lines of code.

Otherwise, if the above is not what you are after you will need to explain more about what you want...
So tomorrow if i have to add another listener
If you add another listener, then you must need to do "something" to your application in order for it to know about it...
I want to avoid adding beans
Ok, so if you don't want to add beans, what other ways do you envisage your application "finding out" that you want to add a listener? Are you talking about wanting to configure it in a property file? Maybe configure it in a database somewhere? Or even, do you want it to automatically "see" that you have added a queue to the JMS server and start consuming from it?

(BTW, I am not advocating any of the above methods, I am just trying to extract out of you what it is you are truly looking for)
0
 
royjaydAuthor Commented:
>> Are you talking about wanting to configure it in a property file?
Yeah, that's the direction I was thinking about.

We have a project properties file, I was thinking to add a property like
Number_of_listeners= n

Thanks for your suggestions.
0
Never miss a deadline with monday.com

The revolutionary project management tool is here!   Plan visually with a single glance and make sure your projects get done.

 
mccarlIT Business Systems Analyst / Software DeveloperCommented:
There is not going to be anything "built-in" (in either Spring or Java) to do this for you. So you will have to "roll your own" somewhat.

Just wondering though, are the queues that you are consuming from named exactly like that, and if say you had Number_of_listeners=20 then the queues would be named INT.QUEUE1 through to INT.QUEUE20 ?  Also, its not obvious from your initial xml configuration but do each of the listeners have a different "messageListener" set for them, or are they all processed by the same listener?

Also, if you can give me a bit more mackground on exactly what you are trying to do, I can possibly give you advice on whether this is really the best way to go about it, ie. there may be a more appropriate way to handle what you are doing, different even from what you gave in the original question?
0
 
royjaydAuthor Commented:
hi

>>There is not going to be anything "built-in" (in either Spring or Java) to do this for you. So you will have to "roll your own" somewhat.

yep, totally agree.

>>Just wondering though, are the queues that you are consuming from named exactly like that, and if say you had Number_of_listeners=20 then the queues would be named INT.QUEUE1 through to INT.QUEUE20 ?

Actually, no
The queue names are totally independent of each other and follow no sequence.


>>Also, its not obvious from your initial xml configuration but do each of the listeners have a different "messageListener" set for them, or are they all processed by the same listener?
They actuallly have their own messageListener
Overall it looks something like this:

 
<bean id="queue1" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiTemplate">
			<ref bean="jndiTemplate" />
		</property>
		<property name="jndiName" value="INT.QUEUE1"/>			 
	</bean>
	<bean id="queue2" class="org.springframework.jndi.JndiObjectFactoryBean">
		<property name="jndiTemplate">
			<ref bean="jndiTemplate" />
		</property>
		<property name="jndiName" value="INT.QUEUE2"/>			 
	</bean>
	<bean id="queue3" class="org.springframework.jndi.JndiObjectFactoryBean">
		<property name="jndiTemplate">
			<ref bean="jndiTemplate" />
		</property>
		<property name="jndiName" value="INT.COMXQ3"/>			 
	</bean> 
	
	
 
	<bean id="messagelistener1" class="com.comxcustom.CustomMessageListener1">		 
	<property name="queueName" value="INT.QUEUE1" />
	</bean>	
	<bean id="messagelistener2" class="com.custom.CustomMessageListener2">		 
	<property name="queueName" value="INT.QUEUE2" />
	</bean>	
	<bean id="messagelistener3" class="com.custom.CustomMessageListener3">		 
	<property name="queueName" value="INT.COMXQ3" />
	</bean>

	<!-- DefaultMessageListenerContainer definitions -->

	
<bean id="listener1"
		class="org.springframework.jms.listener.DefaultMessageListenerContainer">		 
		<property name="destination" ref="queue1" />
		 <property name="connectionFactory" ref="cachedConnectionFactory" />
		<property name="messageListener" ref="messagelistener1" />
		<property name="destination" ref="queue1" />
		<property name="concurrentConsumers" value="5" />
		<property name="maxConcurrentConsumers" value="15" />
	</bean>
	<bean id="listener2"
		class="org.springframework.jms.listener.DefaultMessageListenerContainer">		 
		<property name="destination" ref="queue2" />
		 <property name="connectionFactory" ref="cachedConnectionFactory" />
		<property name="messageListener" ref="messagelistener2" />
		<property name="destination" ref="queue2" />
		<property name="concurrentConsumers" value="5" />
		<property name="maxConcurrentConsumers" value="15" />
	</bean>	
	<bean id="listener3"
		class="org.springframework.jms.listener.DefaultMessageListenerContainer">		 
		<property name="destination" ref="queue3" />
		 <property name="connectionFactory" ref="cachedConnectionFactory" />
		<property name="messageListener" ref="messagelistener3" />
		<property name="destination" ref="queue3" />
		<property name="concurrentConsumers" value="5" />
		<property name="maxConcurrentConsumers" value="15" />
	</bean>

Open in new window

     

CustomMessageListener1,CustomMessageListener2,CustomMessageListener3 all impliment javax.jms.MessageListener and override the public void onMessage(Message message) method.
      
I feel there is a lot of redundancy (copy-pasting) going and the coding logic can be made more simpler and elegant.
      
Your thoughts will be appreciated?
      
Thanks.
0
 
mccarlIT Business Systems Analyst / Software DeveloperCommented:
Actually, no
The queue names are totally independent of each other and follow no sequence
Well then just a simple "Number_of_listeners=n" type of property is obviously not going to work. If that is all the configuration that you have, how is your code supposed to know what the queues are called? And I guess the same for the MessageListener classes?

So I think that cleaning up the xml using Spring's "abstract" bean and the JndiDestinationResolver as I showed above it the best that you will get. You can also move the definitions of those message listeners to be "inline" with the DefaultMessageListenerContainer definitions too which should help.
0
 
mccarlIT Business Systems Analyst / Software DeveloperCommented:
I was also going to add.... For all the benefits that Spring gives you, yes I have also found that the Ctrl+C Ctrl+V gets a bit more of a workout when build my .xml config files. It's just one of those things! :)
0
 
Murali MurugesanConnect With a Mentor Full stack Java developerCommented:
To add on,

How about extending DefaultMessageListenerContainer to provide a custom behavior to receive required params in a overloaded constructor ?

something like [pseudo code]

<bean id="abstractListener" abstract="true" 
		class="mypackage.CustomDefaultMessageListenerContainer">
                 <list>
                           <!-- add your parameters, can add propertyplace holder as deemed fit -->          
                </list>
	</bean>

Open in new window


[BTW: I haven't looked at the details of this DefaultMessageListenerContainer.java, but this would be the approach I would prefer to research on :)]

Murali
0
 
royjaydAuthor Commented:
Thanks
On a high level I want to be able to define number of listeners in a property file.
Based on that number I would spawn up that many DefaultMessageListererContainer beans
on the fly.
To start working on such a requirment i would like to know if I can create beans in the jvm at runtime.
0
 
Murali MurugesanFull stack Java developerCommented:
I believe StaticApplicationContext would be suitable for this.

Refer: http://java.dzone.com/articles/spring-static-application
0
 
royjaydAuthor Commented:
Thanks , very good ideas. Was able to impliment Mccarl's sugession.
0
 
mccarlIT Business Systems Analyst / Software DeveloperCommented:
Not a problem, glad to help! :)
0
 
royjaydAuthor Commented:
hi mccarl..any sugessions would greatly help..

http://www.experts-exchange.com/Programming/Languages/Java/Q_28356518.html

Thanks.
0
All Courses

From novice to tech pro — start learning today.