|
#1
|
|||
|
|||
|
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> |
|
#2
|
|||
|
|||
|
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>
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) |
|
#3
|
|||
|
|||
|
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
Blog Use the [ code ] tags, young padawan |
|
#4
|
|||
|
|||
|
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. |
|
#5
|
|||
|
|||
|
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
Blog Use the [ code ] tags, young padawan |
|
#6
|
|||
|
|||
|
Thanks so much for your help.
Quote:
- 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>
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) 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. |
|
#7
|
|||
|
|||
|
|
|
#8
|
|||
|
|||
|
Quote:
I extend UserCredentialsConnectionFactoryAdapter ? and then ? Thx |
![]() |
| Thread Tools | |
| Display Modes | |
|
|