MessageDrivenBean Workaround?

Discussions

EJB design: MessageDrivenBean Workaround?

  1. MessageDrivenBean Workaround? (10 messages)

    I have been thinking about how to provide MessageDrivenBean (MDB) functionality while waiting for our application server vendor to provide EJB 2.0 support. The MDB interface is more of a push system where the onMessage method of the MDB is invoked when a message arrives. We could live with a more pull-oriented approach (this is how most of our current system work). I believe that a SessionBean can make a connection to a queue session, connect to a queue and block on that queue, In addition, it could make a connection to a queue and send messages to that queue. This is all allowed within the EJB 1.1 specification. What I envision is something like the following:

    1. Define a run method in a SessionBean, e.g.

    public void run(String inQueueInfo, String outQueueInfo) throws JMSException {
     QueueReceiver receiver = null;
     QueueSender sender = null;
     Message inMsg = null;
     Message outMsg = null;
     // Create the queue sender and receiver
     // from the connection info
     while (true) {
      inMsg = receiver.receive();
      // Dispatch the inMsg to another EJB or
      // do some other processing
      // and create an outMsg
      outMsg = dispatch(inMsg);
      if (outMsg != null) sender.send(outMsg);
    }
    }

    This method functions sort of like the onMessage method in a MDB.

    2. Create an EJB Client application (probably multi-threaded) that uses the SessionBean and calls the run method. This method would essentially return while the application server was up and would have to have a long (infinite?) time out period. The client program could create as many of these SessionBeans as needed to provide the required throughput. Multiple queues could also be handled by passing in different connection information to the SessionBeans. The client would have to handle the excerptions generated by JMS and try to re-connect, etc.

    This is what of a kludge but it seemed to me to provide the right functionality without impose any additional performance penalty on the application server than the MDB would. The SessionBean could be written so that the functionality could be easily migrated to an MDB when they become available.

    I have not seen this approach described before and since it is so simple, I must be missing something. Does any one have any comments on this approach? Thanks in advance.

    Threaded Messages (10)

  2. MessageDrivenBean Workaround?[ Go to top ]

    I have implemented something very similar to what you describe:

        - a session bean
        - driven by an ejb client program

        The only major difference I see in my approach is that the client program runs in a loop, and each call to "run" in the session bean will only process 1 message from MQ/JMS.
        This avoids the super long running transaction from the EJB servers point of view, and any associated hogging of resources. This also allows me to enforce the idea that one mq/jms transaction equates to one ejb transaction.
  3. MessageDrivenBean Workaround?[ Go to top ]

    Why not create a class with the onMessage() method which registers for the Queue/Topic and has a reference to a SessionBean? onMessage() on the class simply triggers the onMessage() method in the SB (you shouldn't use it directly because it could throw a RemoteException).
    An even more elaborate approach would be registering some objects ti receive messages and retrieve references to the SBs dynamically. This would have nearly the same effect as MDBs.
    Please let me know what you think, I haven't implemented what I stated above, nor have I really "thought" about whether it will violate anything in J2EE.

    regards

    Messi
  4. MessageDrivenBean Workaround?[ Go to top ]

    I don't think a (1.1) bean can block on a JMS message queue. If it could there would be no need for message-driven beans in EJB 2.0. It may work on a given server, but the spec does not allow a bean to block the thread. (That would prevent the container from properly managing threads and bean instances). For the same reason a bean cannot block on a socket or any other external event...

    -Mark
  5. A 1.1 bean _can_ block on the onMessage. The approach is quite easy: Construct a "dispatcher" class which provides the onMessage() method and set several of them as listener on the queue. Each of them then delegates to a session bean.
    The problem with this approach, i.e. why you need MDBs is: This approach does not scale, if you have five listeners you have to construct them at the beginning, and you'll have to stick with them, there will always be five. In contrast, MDBs are created dynamically and you will have as many as messages are currently processed.

    Messi
  6. Thats not quite the same as the bean *itself* blocking on the JMS APIs. You have constructed a JMS client which calls a bean (as an EJB client), and it just happens to call once per JMS message it gets.

    As to scalability... what if the JMS client spins off a thread for each message it gets, and each thread makes the call to a session bean? The JMS listener can service the queue as fast as it can grab messages and start threads. If you write an onMessage() method on a stateless session bean and the threads pass the JMS message, it should behave very much like an EJB 2.0 MDB.

    The only (?) problem I see is that the message is ACK'ed by the JMS listener, and if the session bean method fails the message will still be consumed. That may or may not be a problem depending on the overall system design.

    -Mark
  7. MessageDrivenBean Workaround?[ Go to top ]

    Opps. The bean itself cannot read the JMS Message object... the listener must extract the data from the Message and pass that onto the bean via the thread. The bean is running in a different thread than the JMS session from which the message is obtained... to be safe it should not access the message itself.

    I just coded up this scenario and it seems to work fine (in a test environment of course...)

    -Mark
  8. Yes, you cannot set the bean directly as listener because how will it de-register? It throws a RemoteException on onMessage()...
    So in general, this is not a good idea.
    Furthermore, I do not think it is a good idea to spawn off threads in the JMS listener. The threading situation is quite unclear in J2EE, and although most AppServers handle it gracefully if you spawn off a thread I in general avoid threads in J2EE.
    However, I discovered that it is not even necessary to spawn off threads (at least in my theory :-) because onMessage of the mediator will be called in the JMS-thread-context. Thus it should be possible to register a listener with several sessions/receivers and the method should be able to take three messages concurrently. This of course means the method must be thread safe without using "synchronized", but this should be possible. Normally if you do async calls the overhead of doing everything method-local will not matter.
    But still, you'll have a fixed number of listeners, and you cannot tell how the server is currently performing (i.e. would it be better to spawn off three or ten "threads"?), the server can. And that is why we are all waiting for MDBs :-)
    I use this approach in a real world application currently, and although it works quite well I have a somewhat bad feeling about it (and actually that is not what you want for an enterprise application :-)

    Messi
  9. I don't think spawning threads in JMS listeners is any problem. The JMS spec is quite clear on the issue of multithreading and addresses it in several places. It is only in *EJBs* that threading is a no-no, and as you point out, some vendors allow it, but it is risky. Each part of J2EE defines its own threading issues... I don't see any problem using it in JMS if you read the spec with respect to what supports threaded access and what does not. I can't see how else to get a scalable solution prior to EJB 2.0.

    -Mark
  10. No, indeed multithreading is a no-no for _anything_ being called from an EJB or running within the server and calling EJBs. I had a quite long discussion with someone about this recently (this was about "synchronized" in EJBs). You just have to see what you are doing (break down, try to substitute etc.) and in most cases you'll see that you may not do it.
    However, there is a way to get a scalable solution: Register a "singleton" (which must be constructed by a single method call, since you may not use "synchronized", not in utility classes or anywhere else) several times with the JMS queue through several sessions.
    Now onMessage must be thread-safe (but not via synchronized!), you could provide a method like:
    public void onMessage(Message msg) {
     MyBean=MyBeanHome.create();
     MyBean.doSomething();
    }
    This is of course not "really" scalable (you are limited to onMessage() being executed as often as you registered the singleton at a maximum);
    But I guess this is the reason why EJB 2.0 introduces MDBs (they must have a reason).

    Messi
  11. No, indeed multithreading is a no-no for _anything_ being called from an EJB or running within the server and calling EJBs.


    Part A is true, an EJB must not call anything that does threading, but part B is not. The fact that a JMS listener is running 'in the server' is not relevant... most JMS implementations run 'out of process' anyway so they are not even in the same JVM as the EJB container. Anyway, an EJB must service any client call it gets (from a JMS listener in the server, or a remote Java application over the network). The EJB does not know or care where the call comes from or what the threading issues are in the caller. The EJB container insulates the bean from the client and is the boundry for the 'no threading' rule.

    We have built an infrastructure which simulates EJB 2.0 message beans by having a listener which routes JMS messages to onMessage() methods of stateless session beans. The listener starts a thread for each message received, and the thread makes the (syncrounous) call to the session bean's onMessage() method. There is no threading issue here from the EJB perspective. Our JMS listener is configured to reading a mapping of JMS topic names to EJB session beans, much like the deployment descripter will do in EJB 2.0.

    The only part of EJB 2.0 we cannot really emulate properly is the transaction context (JMS messages are always ACK'ed and consumed even if the bean's onMessage() method fails or rolls back its transaction). Also, scalability is not as good as an EBJ 2.0 container will be able to do because we still have only one thread per topic and it can loop to read messages and spin off threads only so fast.

    To date we have tested this only on the Inprise server, but I expect no problems with the technique on any EJB 1.1 platform.

    -Mark