Announcement Announcement Module
Collapse
No announcement yet.
JMS with JndiTemplate in HA-JNDI environment Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • JMS with JndiTemplate in HA-JNDI environment

    Hi there.

    My environment: Spring app in JBoss cluster with JMS bound in HA-JNDI. So JMS destinations are available under the same JNDI name but on different machines.

    My actually problem is: When the Spring context is loaded, there is no way, for the JndiTemplate to switch to another host.

    When I'am starting the cluster environment, the first node is setting up the JMS destinations. When the Spring application context starts up, the JndiTemplate will be created like this:

    Code:
    <bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
      <property name="environment">
        <props>
          <prop key="java.naming.factory.initial">org.jnp.interfaces.NamingContextFactory</prop>
          <prop key="java.naming.factory.url.pkgs">org.jnp.interfaces:org.jboss.naming</prop>
          <prop key="java.naming.provider.url">jnp://${jms.connection.hosts}</prop>
        </props>
      </property>
    </bean>
    ${jms.connection.hosts} = list of hosts: apps1:1199,apps2:1299

    Code:
    <bean id="jmsTopicDestination" class="org.springframework.jndi.JndiObjectFactoryBean">
      <property name="jndiTemplate" ref="jndiTemplate"/>
      <property name="jndiName" value="${jms.destination.jndiName}"/>
    </bean>
    Till now everything works fine. When i'am going to stop the JBoss instance where the destinations are bound (apps1), the other JBoss instance (apps2) is taking over the JMS destinations. This is called High Availability JMS. So hence the Spring JndiTemplate is not up to date. The instanced bean allways try to connect to the faild host apps1. For Sure, when i rebuild the ApplicationContext, everything works well, but this is not the proper way.

    Is there a solution for this problem?

  • #2
    You should set the "cache" property to false on the your JndiObjectFactoryBean to make sure the destination gets looked up every time.

    Comment


    • #3
      Thanks a lot for your advice. I think that is the right way. I changed my configuration to ...

      Code:
      <bean id="jmsTopicDestination" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiTemplate" ref="jndiTemplate"/>
        <property name="jndiName" value="${jms.destination.jndiName}"/>
        <property name="cache" value="false"/>
        <property name="proxyInterface" value="javax.jms.Topic"/>
      </bean>
      But now i got a strange failure when I am starting up my context ....

      Code:
      ERROR 14:14:33.691 Setup of JMS message listener invoker failed - trying to recover
      java.lang.ClassCastException: $Proxy67
              at org.jboss.mq.SpySession.createSubscriber(SpySession.java:709)
              at org.jboss.mq.SpySession.createConsumer(SpySession.java:617)
              at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.createConsumer(AbstractPollingMessageListenerContainer.java:433)
              at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.createListenerConsumer(AbstractPollingMessageListenerContainer.java:216)
              at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.initResourcesIfNecessary(DefaultMessageListenerContainer.java:886)
              at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener(DefaultMessageListenerContainer.java:869)
              at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run(DefaultMessageListenerContainer.java:810)
              at java.lang.Thread.run(Thread.java:595)
      I am sure to point to the right interface with javax.jms.Topic, because i use topics. I found a thread in Jboss forum pointing to a problem in Spring?!

      I had a look in the org.jboss.mq.SpySession class but this seems okay.

      Can you give me another hint here?

      Comment


      • #4
        I looked at the source for SpySession.java and noted the Topic object is being cast to a SpyTopic. When you set cache to false, Spring generates a proxy with the proxyInterface you specified, which can't be cast to a SpyTopic.

        Try changing:
        Code:
        <property name="proxyInterface" value="javax.jms.Topic"/>
        to
        Code:
        ]<property name="proxyInterface" value="org.jboss.mq.SpyTopic"/>
        Unfortunately SpyTopic isn't an interface so you will have to include cglib in the path, so Spring can create a proxy of a concrete class. This isn't guarenteed to work since cglib based proxies tend to have more restrictions than interface based proxies. But its worth a shot.

        Comment


        • #5
          Hi,
          I tried to do it that way, but SpyTopic didn't work with proxy generation using cglib. So I did it another way:

          + no bean definition with id="jmsTopicDestination" anymore

          + the beans jmsTemplate and jmsContainer don't need the jmsTopicDestination anymore

          + I declared a resolver bean:
          Code:
          <bean id="jmsResolver" class="org.springframework.jms.support.destination.JndiDestinationResolver">
            <property name="jndiTemplate" ref="jndiTemplate"/>
            <property name="resourceRef" value="false"/>
            <property name="cache" value="false"/>
          </bean>
          + and give jmsTemplate and jmsContainer a reference to the resolver ...
          Code:
          <property name="destinationResolver" ref="jmsResolver"/>
          Now it's working. But I think when I'am going to drive loadtests I have to write my own JMSTemplate for better resource handling...

          Comment


          • #6
            You might consider implementing a wrapper around ConnectionFactory/Connection/Session/etc to use proxies to manage the JMS resources better, rather than rewriting JmsTemplate.

            JmsTemplate merely uses the same factory approach that JdbcTemplate uses, and merely expects a good caching factory like Jdbc provides. If your JMS provider doesn't good caching, rather than writing around it with custom caching client code, just wrap the deficient factory code.

            Comment

            Working...
            X