Solved

spring beans , avoid redundancy

Posted on 2013-12-10
13
382 Views
Last Modified: 2014-02-04
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
0
Comment
Question by:royjayd
  • 6
  • 5
  • 2
13 Comments
 
LVL 35

Expert Comment

by:mccarl
ID: 39710422
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
 

Author Comment

by:royjayd
ID: 39710435
>> 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
 
LVL 35

Expert Comment

by:mccarl
ID: 39710545
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
 

Author Comment

by:royjayd
ID: 39712955
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
 
LVL 35

Expert Comment

by:mccarl
ID: 39712977
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
 
LVL 35

Expert Comment

by:mccarl
ID: 39712981
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
Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

 
LVL 13

Assisted Solution

by:Murali Murugesan
Murali Murugesan earned 100 total points
ID: 39713066
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
 

Author Comment

by:royjayd
ID: 39713131
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
 
LVL 13

Expert Comment

by:Murali Murugesan
ID: 39713157
I believe StaticApplicationContext would be suitable for this.

Refer: http://java.dzone.com/articles/spring-static-application
0
 
LVL 35

Accepted Solution

by:
mccarl earned 400 total points
ID: 39713261
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
 

Author Comment

by:royjayd
ID: 39769992
Thanks , very good ideas. Was able to impliment Mccarl's sugession.
0
 
LVL 35

Expert Comment

by:mccarl
ID: 39770034
Not a problem, glad to help! :)
0
 

Author Comment

by:royjayd
ID: 39833998
hi mccarl..any sugessions would greatly help..

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

Thanks.
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Suggested Solutions

Introduction This article is the first of three articles that explain why and how the Experts Exchange QA Team does test automation for our web site. This article explains our test automation goals. Then rationale is given for the tools we use to a…
Basic understanding on "OO- Object Orientation" is needed for designing a logical solution to solve a problem. Basic OOAD is a prerequisite for a coder to ensure that they follow the basic design of OO. This would help developers to understand the b…
This tutorial covers a practical example of lazy loading technique and early loading technique in a Singleton Design Pattern.
This tutorial covers a step-by-step guide to install VisualVM launcher in eclipse.

706 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

20 Experts available now in Live!

Get 1:1 Help Now