Announcement Announcement Module
Collapse
No announcement yet.
MDB vs MDP concurrency Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • MDB vs MDP concurrency

    Hi.

    We are considering refactoring a large set of MDBeans to Spring based MD POJOs.

    I am a bit unuclear on the concurrency semantics of the DefaultMessageListenerContainer.

    The EJB spec says this about MDBs:
    "The container must ensure that only one thread can be executing an instance at any time. "
    So I know that each MDB instance will never be accessed concurrently even though there might be several concurrent MDB consumers of the same destination.

    This is my understanding of the concurrency semantics of DefaultMessageListenerContainer:
    One MDP instance can potentially be accessed by <concurrentConsumers>-number of threads simultaneously.

    Is my understanding correct?

    If so; Does anybody have any suggestion about how we approach the refactoring our MDBs, since many of them are not thread safe, but should be consuming messages in parallel?

    Kind regards
    Christian

  • #2
    Your understanding is correct.

    You need to make your MDPs thread safe. Spring provides tools for doing this differently than EJB/MDB does. For example, in a non-thread-safe EJB/MDB, each bean creates some heavy weight object that gets modified during the course of each method. It nees to do this because creating the heavy weight object is too expensive to create on the fly for each method. EJB/MDB makes it thread-safe by the way it only executes each bean in one thread at a time. To simulate that using Spring in an MDP you would create a proxy for your heavy weight object and wrap a pooling targetsource around it. Take a look at the section in the reference manual on Pooling target sources

    To give you a more concrete example of what I mean:

    MDB:
    Code:
    public class MyMDB extends ... implements ... {
      HeavyWeight heavyWeight = new HeavyWeightImpl();  // HeavyWeight is an  interface
      public void onMessage(Message obj) {
        MyObject myObj = ((ObjectMessage)obj).getObject();
        heavyWeight.reset();
        heavyWeight.doStuff(myObj);
        heavyWeight.doMoreStuff(myObj);
        /* etc */
      }
    }
    MDP:
    Code:
    public class MyMDP { /* Doesn't need to implement anything, including MessageListener */
      public void setHeavyWeight(HeavyWeight heavyWeight) {
        this.heavyWeight = heavyWeight;
      }
    
      // Spring's MDP stuff can unwrap the javax.jms.Message for you and invoke process with the unwrapped mesage.
      public void process(MyObject obj) {
        heavyWeight.reset();
        heavyWeight.doStuff(myObj);
        heavyWeight.doMoreStuff(myObj);
        /* etc */
      }
    }
    To wire it up:
    Code:
    <bean id="heavyWeightTarget" class="com.mycompany.HeavyWeightImpl" 
        scope="prototype">
      ... properties omitted
    </bean>
    
    <bean id="poolTargetSource" class="org.springframework.aop.target.CommonsPoolTargetSource">
      <property name="targetBeanName" value="businessObjectTarget"/>
      <property name="maxSize" value="25"/>
    </bean>
    
    <bean id="heavyWeight" class="org.springframework.aop.framework.ProxyFactoryBean">
      <property name="targetSource" ref="poolTargetSource"/>
    </bean>
    
    <bean id="myMdp" class="com.mycompany.MyMDP">
      <property name="heavyWeight" ref="heavyWeight"/>
    </bean>
    The interesting thing is if you can somehow come up with an implementation of HeavyWeight that is indeed thread-safe and doesn't store state between calls to the object, you can eliminate the proxy and just set a singleton heavyWeight object into your myMdp. The thread-safety considerations are removed from your business object, and into your application context.

    For objects/fields that aren't very expensive, you can move them as local variables into each method invocation.

    Are there other reasons why your MDB code wouldn't be thread safe?

    Comment


    • #3
      Thanks for your fast answer Bill

      To answer your question first:
      We are considering automatically refactoring a very large number of MDBeans, written by a large number of different people over serveral years. As the MDBean concurrency paradigm prevented concurrent access we have to assume that some of the MDBeans are not written to be thread safe. Therefore we have to maintain the same concurrency paradigm for the MDPojos we are refactoring to.

      So; in line with your suggestion it will probably be a good idea for us to serve our MDPs from a pool the size of <DefaultMessageListenerContainer.concurrentConsume rs>.

      However I do not think that your example config will work in the context of the DefaultMessageListenerContainer. The reason for this is that the DefaultMessageListenerContainer will recieve only a single instance of MyMDP which again will have revieced only a single instance of "heavyWeight". All <concurrentConsumers> will still run against the same single instance.

      I think the solution here maybe will be to create an 'onMessage' method interceptor that delegates to a <concurrentConsumers>-sized pool of MDP instances.
      Chr

      Comment


      • #4
        Actually my example isn't correct. The code is fine, the XML file isn't:

        Code:
        <bean id="myMdpTarget" class="com.mycompany.MyMDP" scope="prototype">
          <description>Create a prototype MyMDP with its own copy of HeavyWeightImpl</description>
          <property name="heavyWeight>
            <bean class="com.mycompany.HeavyWeightImpl"/>
          </property>
        
        <bean id="poolTargetSource" class="org.springframework.aop.target.CommonsPoolTargetSource">
          <property name="targetBeanName" value="myMdpTarget"/>
          <property name="maxSize" value="25"/>
        </bean>
        
        <bean id="myMdp" class="org.springframework.aop.framework.ProxyFactoryBean">
          <property name="targetSource" ref="poolTargetSource"/>
        </bean>
        This would create a pooled myMdp that acted like a regular myMdp. Each invocation of each method (which in this case would be process) would get a myMdp from the pool, invoke the method, and return it to the pool. Basically what EJB/MDBs do, but without a lot of the overhead.

        Comment


        • #5
          Thanks Bill.

          That did the trick

          Christian

          Comment

          Working...
          X