JMS Message Driven Bean Working Synchronization

We are just starting to build our JMS architecture and have the following basic setup:

  • GLassfish v2.1
  • MDB listens to topic through TopicConnectionFactory (all on local server)

The MDB now creates a worker thread when a new message arrives, and although we are dealing with message delivery, we need a synchronization mechanism to have the threads check a certain condition before processing the request at the same time.

Is there a way for these streams to exchange data? Or are there any other mechanisms (besides database tables / rows) that we can use to synchronize?

Thanks in advance.


To clarify, I am not creating my own streams. As everyone has correctly pointed out, the container does this for me. Let me help explain my dilemma with an example.

-Message A arrives at t = 0, which "creates" data id 1

-Message B comes at t = 0.1, which "updates" data id 1

Now, assuming the container spawns 2 workers to process A and B, and it takes much longer to create the "data" than the update, the update will be processed earlier and will have no effect.

To be clearer

-During post B processing, I would look for data id 1 at t = 1 (didn't find it and thus ended up doing nothing).

-This identifier 1 will be created when processing message A at t = 2.

+1


a source to share


6 answers


Pedant warning! I am the kind of guy who reads the actual technology specs.

Reading the EJB Specification 3.0, Section 21.1.2 (Programming Limitations) disallows the use of streams in your code. Here is the language and rationale ...

An enterprise bean shouldn't try to control threads. An enterprise bean should not try to start, stop, pause, or resume a thread, or change the priority or name of a thread. An enterprise bean should not attempt to manage thread groups.

These functions are reserved for the EJB container. Allowing an enterprise bean to manage threads will decrease the ability of containers to properly manage the runtime.



So, if you do what you say, the EJB police will come to your door in the middle of the night and take you away. Or your app might not work properly and the salesperson will laugh when you complain. Or nothing bad will happen.

But as duffymo says, why do that? If you want the scalability offered by multiple threads, can you customize that for your MDB? The EJB point is designed to handle this kind of thing for you.

+6


a source


I don't understand why the MDB should spawn a worker thread. There is a thread pool associated with message listeners in JMS. This is the thread that needs to do the work.

The EJB spec says there is no thread in your beans. The container processes the threads. This also includes MDB.



The listener must process the message that it will remove from the queue. The required data must be in the message. What do you need to exchange?

I believe your approach is contrary to EJB guidelines.

+4


a source


The real problem is that the Application Server usually spawns the workers mentioned separately. While JMS makes sure messages are consumed in the same order as they are produced within at least one vendor, the MDB specification explicitly states that the order is not saved (due to the workers mentioned). See Section 5.4.11 JSR-000220 Enterprise JavaBeans 3.0 Final Release (ejbcore) .

There is no portable and 100% reliable way to get around this. Due to the nature of the working conditions of the race, conditions are introduced that cannot be controlled.

Fortunately, most application servers have ways, although proprietary and incompatible, to tune the number of workers.

For JBoss and ActiveMQ, this works:

@PoolClass(value = org.jboss.ejb3.StrictMaxPool.class, maxSize = 1)
@MessageDriven(activationConfig = {
    @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"),
    @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
    @ActivationConfigProperty(propertyName = "maxSessions", propertyValue = "1"),
    @ActivationConfigProperty(propertyName = "destination", propertyValue = "TEST.FOO")
})
@ResourceAdapter("activemq-ra.rar")
public class NewMessageBean implements MessageListener { ... }

      

In this case, "maxSessions" is the number of workers. It might be different with other JMS providers, but it should point in the right direction.

+3


a source


As mentioned above, the JMS framework handles issues like thread dispatching. Anything you do in this case will not only yield to the default, it will probably severely limit your JMS functionality.

More complex JMS handlers are designed to work across multiple nodes (= servers), so any shared memory solution would limit you to one JVM per node, which would be a shame since JMS is a big advantage of scalability.

Perhaps the JMSy solution would be for the cookie queue to have one dummy cookie to keep things synchronized. When it is time for the process to perform the contested activity, it "receives pending" in one message from the queue's cookies, when the disputed activity is completed, it puts the cookies back in the queue. The magic of JMS will handle almost all locks, waits, and error recovery.

+2


a source


As mentioned by several people, you shouldn't create your own streams in MDB (or any other type of EJB or servlet for that matter).

Many EJB containers won't actually let you create and run threads. There is one safe way to do this though, using the WorkManager from the commonj spec, although I see no reason for this in this particular case, since the MDB is already running in its own "workflow".

See the thread spawning information here for more information on why you shouldn't spawn threads in a Java EE server and how to do it safely when you need to.

0


a source


It seems like the question changed completely in nature after everyone had already answered it. I will be a very belated answer for posterity; this assumes that there is some type of post id that can be used to order. You say, "We have a message delivery order," but you don't know exactly how this is achieved.

... Since you figured out that you don't start your own threads, you basically have a race condition between "creating" the @ID = 1 data and then "updating" it. I am assuming you are blocking @ID = 1 data during creation and / or update. So there are two possibilities:

  • the message "create data" appears: (1) lock ID = 1, (2) create data. (3) release the lock. (4) apply a pending "update".
  • "update" appears before "create": (1) lock id = 1, (2) creates missing data (ie, does "upsert": insert data even if there is none). (3) release the lock. (4) ignore the pending "create data" message (the "create" message has a lower sequence number than "update",
    • If the sender of the message is allowed to send updates and insert at the same time (no request / response), then they really should have some sort of sequence number. If so, when the "insert" arrives, its sequence number will be less than the current value on that line, so it can be ignored. Or, if the "create" message type is different from the "update" message type, then "create" can always be ignored if the data already exists.

I think you have a question how to sync data. Essentially, threads share objects and create a mutex (mutual exclusion) to allow one thread to access data, causing the other thread to block. This can be done simply through Java's low-level synchronization tools ("synchronized" keyword) or built-in classes that help with this (http://java.sun.com/docs/books/tutorial/essential/concurrency/highlevel.html .

0


a source







All Articles