Community   SpringSource   Projects    Downloads    Documentation    Forums    Training   Exchange   Blogs

Go Back   Spring Community Forums > Core Spring Projects > Remoting and JMS

Reply
 
Thread Tools Display Modes
  #1  
Old Nov 11th, 2008, 01:26 PM
honeybunny honeybunny is offline
Senior Member
 
Join Date: May 2008
Posts: 152
Default excessive JNDI lookups on weblogic

I am trying to use JMS from spring on weblogic 10 (from the app server). I configured a security policy on the weblogic hosted JMS server. At first this prevented me from connecting, I believe this is due to issues described in SPR-2941 and here SPR-4720. I implemented the suggested approach of a connection factory decorator, which I used in my distributed clients as well as on the app server and it works, however, on my app server I see the decorator looking up the JNDI connection factory over and over.

The lookup is performed roughly once a second (which is the default recieveTimeout on DefaultMessageListenerContainer). I am not sure I can make the timeout longer because of the constraint on the transaction manager (the recieveTimeout must be shorter than the transaction manager timeout).

I have a few basic questions:
1) If the credentials on weblogic are bound to a thread and populated during JNDI lookup, what about using a thread bound cache. Perhaps the JNDIObjectFactory bean or the connection factory decorator could use a LRUMap<Thread, ConnectionFactory> (keeping like the last 10 entries) For long lived threads (such as the one in the DefaultMessageListenerContainer) this would prevent the lookup each time.

2) For application server threads (like the ones handling user http requests or web service requests) is there anything I can do? Won't the jndi lookup happen once per request? (gasp). In weblogic's jms-servlet example they do the lookup every time. Are jndi lookups super fast on weblogic or something?

It seems that I could have my servlet trigger some long lived "sender thread" then wait for the jms message to actually be sent, to avoid the jndi lookup but that seems like it would overly complicate things.

3) Am I doing this right? Is there some magic way to make JMS work on weblogic that I am unaware of?

I also tried this snippet from the SPR, but it did not seem to work on the client (and I don't fully understand it). Should I expect that this would work on the server?
Code:
<bean id="..." class="org.springframework.jndi.JndiObjectFactoryBean">
  <property ...>
  <property name="exposeAccessContext" value="true"/>
</bean>
Reply With Quote
  #2  
Old Nov 13th, 2008, 11:06 AM
honeybunny honeybunny is offline
Senior Member
 
Join Date: May 2008
Posts: 152
Default Error while using exposeAccessContext

I tried using exposeAccessContext on weblogic 10, and got the following error when spring beans is initializing. Here is the beans.xml snippet:
Code:
	<bean id="connectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
		<property name="jndiName" value="jms/${connectionFatory.jndi-name}"/>		
		<property name="exposeAccessContext" value="true" />
	</bean>
Has anyone run into something like this or got exposeAccessContext to work?

Code:
Caused by: java.lang.IllegalAccessError: tried to access class weblogic/jms/client/Reconnectable from class weblogic/jms/client/$Proxy162
	at java.lang.reflect.Proxy.defineClass0(Ljava.lang.ClassLoader;Ljava.lang.String;[BII)Ljava.lang.Class;(Native Method)
	at java.lang.reflect.Proxy.getProxyClass(Proxy.java:504)
	at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:581)
	at org.springframework.aop.framework.JdkDynamicAopProxy.getProxy(JdkDynamicAopProxy.java:117)
	at org.springframework.aop.framework.ProxyFactory.getProxy(ProxyFactory.java:110)
	at org.springframework.jndi.JndiObjectFactoryBean$JndiObjectProxyFactory.createJndiObjectProxy(JndiObjectFactoryBean.java:303)
	at org.springframework.jndi.JndiObjectFactoryBean$JndiObjectProxyFactory.access$000(JndiObjectFactoryBean.java:273)
	at org.springframework.jndi.JndiObjectFactoryBean.afterPropertiesSet(JndiObjectFactoryBean.java:176)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1369)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1335)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:473)
Reply With Quote
  #3  
Old Nov 14th, 2008, 12:54 AM
Marten Deinum Marten Deinum is offline
Senior Member
 
Join Date: Jun 2006
Location: The Netherlands
Posts: 9,521
Default

By default tje JndiObjectFactoryBean caches the result of the lookup and so should only do the lookup once. Did you override caching and/or lookupOnStartup?
__________________
Marten Deinum
  • Senior Java Consultant
  • SpringSource Certified Trainer
Conspect ICT diensten
Blog
LinkedIn
Use the [ code ] tags, young padawan
Reply With Quote
  #4  
Old Nov 14th, 2008, 09:00 AM
honeybunny honeybunny is offline
Senior Member
 
Join Date: May 2008
Posts: 152
Default

I think I may have implemented the connection factory decorator wrong, my implementation did a JNDI lookup each time getConnection() is called, then calls getConnection() on the result. This approach did work, I _think_, because each thread that wanted to use the connection factory got the needed credentials stuck into its thread context from the JNDI lookup.

At first I was curious why getConnection() was called over and over (when there were no messages on the topic), and if I could do anything about it. After thinking about it, I am assuming that DefaultMessageListener container thinks that the connection is stale and somehow refreshes it, but this is just a guess.

I implemented a cache so that each thread that called getConnection() only did a JNDI lookup if it had not been done for that thread. This also worked and obviously performs faster.

I am still confused about the original SPR, was the problem that the InitialContext was used by a thread that did not create it, or the ConnectionFactory was used by a thread that didnt look it up?

I still feel like I should not have to do all this stuff, from reading the spring tutorial, using a JCAEndpoint seems like it might work better but I dont have any experience with Resource adapaters and have not yet found a weblogic config example.

Last edited by honeybunny; Nov 14th, 2008 at 09:07 AM.
Reply With Quote
  #5  
Old Nov 17th, 2008, 01:25 AM
Marten Deinum Marten Deinum is offline
Senior Member
 
Join Date: Jun 2006
Location: The Netherlands
Posts: 9,521
Default

What you can do is wrap your JNDI connection Factory inside a UserCredentialsConnectionFactoryAdapter. For each thread call the setCredentialsForCurrentThread method (passing in the username/password) and then everything is going to be done for you, instead of writing your own wrappers etc.
__________________
Marten Deinum
  • Senior Java Consultant
  • SpringSource Certified Trainer
Conspect ICT diensten
Blog
LinkedIn
Use the [ code ] tags, young padawan
Reply With Quote
  #6  
Old Nov 17th, 2008, 04:47 PM
honeybunny honeybunny is offline
Senior Member
 
Join Date: May 2008
Posts: 152
Default

Thanks so much for your help.
Quote:
Originally Posted by Marten Deinum View Post
What you can do is wrap your JNDI connection Factory inside a UserCredentialsConnectionFactoryAdapter. For each thread call the setCredentialsForCurrentThread method (passing in the username/password) and then everything is going to be done for you, instead of writing your own wrappers etc.
Wow, I just looked at the source for UserCredentialsConnectionFactoryAdapter, and the ThreadLocal is so much better than my Map solution. It still did not work however, it seems from your suggestion that I need to write some java code, but I'm still not exactly clear what you were advocating:
- Should I subclass DefaultMessageListenerContainer and call setCredentialsForCurrentThread() from some lifecycle method within it (createConsumer() perhaps)?
- Is there some option I need to set on DefaultMessageListenerContainer to get it to actually perform the lookup?

Here is my spring beans xml, notice I am using DefaultMessageListenerContainer and not creating threads through code, so the spring container is clearly the entity that creates the polling threads.
Code:
<bean id="jndiDestinationResolver" 
	class="org.springframework.jms.support.destination.JndiDestinationResolver">
</bean>

<bean id="connectionFactory" 
	class="org.springframework.jndi.JndiObjectFactoryBean">
	<property name="jndiName" value="jms/${connectionFatory.jndi-name}"/>
</bean>

<bean id="threadBoundConnectionFactory" 
	class="org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter">
	<property name="targetConnectionFactory" ref="connectionFactory" />
	<property name="username" value="MySystemUser" />
	<property name="password" value="password" />
</bean>	

<bean id="listenerContainer"
	class="org.springframework.jms.listener.DefaultMessageListenerContainer">
	<property name="concurrentConsumers" value="1" />
	<property name="connectionFactory" ref="threadBoundConnectionFactory" />
	<property name="destinationResolver" ref="jndiDestinationResolver" />
	<property name="destinationName" value="jms/${reply.queue}" />
	<property name="messageListener" ref="messageListener" />
	<property name="transactionManager" ref="jmsTransactionManager" />
</bean>
Clearly the exception in the logs is from lack of credentials, note that My-JMS-Module is a weblogic JMS module (a new concept new to weblogic 10), the actual web application has a different name.
Code:
2008-11-17 17:22:13,501 ERROR org.springframework.jms.listener.DefaultMessageListenerContainer (DefaultMessageListenerContainer.java:667) - Setup of JMS message listener invoker failed - trying to recover
weblogic.jms.common.JMSSecurityException: Access denied to resource: type=<jms>, application=My-JMS-Module, destinationType=queue, resource=MyApp.ReplyQueue, action=receive
	at weblogic.jms.dispatcher.DispatcherAdapter.convertToJMSExceptionAndThrow(DispatcherAdapter.java:110)
	at weblogic.jms.dispatcher.DispatcherAdapter.dispatchSync(DispatcherAdapter.java:45)
	at weblogic.jms.client.JMSSession.consumerCreate(JMSSession.java:2675)
	at weblogic.jms.client.JMSSession.setupConsumer(JMSSession.java:2448)
	at weblogic.jms.client.JMSSession.createConsumer(JMSSession.java:2395)
	at weblogic.jms.client.JMSSession.createConsumer(JMSSession.java:2375)
	at weblogic.jms.client.WLSessionImpl.createConsumer(WLSessionImpl.java:800)
	at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.createConsumer(AbstractPollingMessageListenerContainer.java:437)
	at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.createListenerConsumer(AbstractPollingMessageListenerContainer.java:216)
	at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.doReceiveAndExecute(AbstractPollingMessageListenerContainer.java:297)
	at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.receiveAndExecute(AbstractPollingMessageListenerContainer.java:234)
	at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener(DefaultMessageListenerContainer.java:871)
	at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run(DefaultMessageListenerContainer.java:811)
	at java.lang.Thread.run()V(Unknown Source)
My only working solution so far is to implement ConnectionFactory. I also implement DestinationResolver but I don't think I have to.
Code:
public class WeblogicJMSHelper implements ConnectionFactory, DestinationResolver, InitializingBean {

	private ConnectionFactory getConnectionFactory() throws JMSException {
		InitialContext ctx = getInitialContext();
		ConnectionFactory connectionFactory = (ConnectionFactory)ctx.lookup(m_connFactoryJNDIName);
		return connectionFactory;
	}
	
	private InitialContext getInitialContext() throws NamingException {
		Hashtable env = new Hashtable();
		env.put(InitialContext.SECURITY_PRINCIPAL, username);
		env.put(InitialContext.SECURITY_CREDENTIALS, new String(password));
		return new InitialContext(env);
	}

	public Connection createConnection() throws JMSException {
		Map<String, Object> previousMap = getThreadBoundMap();
		
		Object cachedConnectionFactory = previousMap.get(m_connFactoryJNDIName);
		if (cachedConnectionFactory != null)
			return ((ConnectionFactory)cachedConnectionFactory).createConnection();
		
		ConnectionFactory connectionFactory = getConnectionFactory();
		previousMap.put(m_connFactoryJNDIName, connectionFactory);
		
		return connectionFactory.createConnection();
	}
}

Last edited by honeybunny; Nov 17th, 2008 at 05:00 PM.
Reply With Quote
  #7  
Old Jan 25th, 2010, 09:12 AM
csergiu77 csergiu77 is offline
Junior Member
 
Join Date: Sep 2008
Posts: 23
Default Same problem here !

http://jira.springframework.org/browse/SPR-5869
Reply With Quote
  #8  
Old Jan 25th, 2010, 09:38 AM
csergiu77 csergiu77 is offline
Junior Member
 
Join Date: Sep 2008
Posts: 23
Question How this can be done ?

Quote:
Originally Posted by Marten Deinum View Post
What you can do is wrap your JNDI connection Factory inside a UserCredentialsConnectionFactoryAdapter. For each thread call the setCredentialsForCurrentThread method (passing in the username/password) and then everything is going to be done for you, instead of writing your own wrappers etc.
How can i do that ?

I extend UserCredentialsConnectionFactoryAdapter ? and then ?

Thx
Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -5. The time now is 02:17 AM.


Contegix provides first-class managed hosting and partial sponsorship of these forums.

Powered by vBulletin® Version 3.8.4
Copyright ©2000 - 2010, Jelsoft Enterprises Ltd.