|
Message Driven Beans consume messages from predefined JMS Topics or Queues. Applications that may need to utilize several Message Driven Beans to handle various messages are faced with a problem of defining a number of JMS destinations and implementing different MDBs to listen to them. This may cause the Application Server to be cluttered with a variety of JMS resources and MDB deployments that perform potentially similar operations.
A solution to this issue can be the Message Driven Bean Proxy pattern. It should be applied to the situations where multiple message types need to be processed by an application and MDBs consuming them may or may not access shared business logic components to complete their work. MDBs performing highly specialized tasks, processing large amounts of data, or desiring to meet very high performance criteria should be excluded. The solution establishes a single application-wide MDB listening to a JMS Topic or Queue as a proxy for the specific operations that need to be performed in response to the message arrival. In this scenario, a single MDB will be responsible for handling all the messages for an application. The decision what functionality to execute will be based on the message properties assigned to it by the sender.
A factory pattern can be utilized to encapsulate the decision making process. When a message is received by the MDB, a factory mechanism is called to create a specific class that will perform all the necessary message handling. The factory class -- Message Handling Factory -- is responsible for instantiating the correct processing class -- Message Handling Class. Each message type should have a corresponding Message Handling Class that performs necessary operations.
When implementing the MDB Proxy pattern, the client sending a JMS message needs to set message properties with specific data uniquely identifying the message. The Message Handling Factory can decide which Message Handling Class to create based on these properties. Message properties are set and extracted through methods exposed in the JMS Message interface. Each Message Handling Class should contain the logic specific to the particular message type that it is processing. They, however, should not implement any business logic but instead call appropriate methods of business logic components. See Message Driven Bean Strategy pattern for more information. This approach limits the proxy MDB's job to creating a Message Handling Factory, instantiating a new Message Handling Class through it and passing the received message to the class for processing.
This pattern eliminates the need for creating multiple JMS destinations and MDB implementations for an application and establishes a common message handling framework. When a new message type is created, the only thing that needs to be done is develop a new instance of the Message Handling Class and update Message Handling Factory logic. This, however, introduces a need to re-deploy and re-test the proxy Bean but this operation should be more efficient than deploying and testing a new Message Driven Bean. A single JMS destination and MDB per application concept may create performance issues but a good load balancing or clustering technique can remedy the situation.
|
1 replies in this thread |
Reply |
Message Driven Bean Proxy |
|
Posted By: Gal Binyamini on December 14, 2001 in response to this message. |
As usual, Leo posts and I argue :)
First of all, I'll make a note about sharable business logic (SBL from here on) which sets the context of my argument. Suppose I'm developing a bogus bookstore application. I would consider the following to be SBL (the list is not exhaustive): - update order status - update inventory status - find pending orders for a specific item (for items we don't have in the inventory). Suppose I'm implementing the bogus backend using MDBs. When a message comes in from the inventory saying a new item is available again, I may want to: 1. update the inventory status 2. find all the orders for this item that were pending for it. 3. update as many of them as I can (as now the item is available). I do not view this chain of operations as SBL. It is the algorithm of a very specific application, my bogus backend application. I see no sense in putting this code in a component. Components should be used to compose applications such as this, not to contain the application in themselves. So in this case, I would put this code in the MDB (perhaps by delegating to a seperate class, but not to a seperate component).
My argument is that the scope of the pattern is ill-defined, and that the pattern fails to address it. I see two elements in the scope: - The first, and more heavily discussed element involves the problem of having "too many" topics and MDB deployments. The pattern claims it's own implementation would be more efficient, put less load on the app server, etc. The pattern does this by using JMS as a "flat namespace" and nest it's own namespace inside JMS using message properties. If this approach was so efficient, why wouldn't the JMS provider use it? And how is it that the message handlers created by the factory will be more effiient than MDBs? Why do MDBs strike you as something so heavy and difficuilt to maintain? The message handlers in the pattern are just MDBs stripped of the pooling capabilities. The application server manages pool's the MDBs and uses LRU caching, hit-counting schemes, etc to manage them efficiently. Why do you think you will do a better job? I think that in fact, no matter how well we implement the caching of these message handlers, we will still end up with a less efficient and scalable application. Here are just a couple of reasons (there are more, but I think these two make the point): 1. The JMS server will handle the routing better than you possibly can. For instance, in a cluster, if you used JMS facilities only nodes that need a specific message would get it. With your implementation, all nodes will recieve all messages, because the JMS server doesn't know anything about who needs what. 2. If just one of your MDBs needs durable subscription, it will have to subscribe durably for the big topic. This means that all messages will be stored up for your MDB, instead of just the important durable ones. It also means that a failure in a single MDB can cause all the system's messages to stack up. Aside from this, I also doubt even incredibly expirienced programmers could implement the caching as well as the app server (see note below about specific cases). There are also threading issues that an MDB programmer cannot control that may cause additional efficiency problems (I can list specific if anyone is interested). Reasons such as the ones above are one of the motivations behind the current design of JMS, which provides it's own namespace rather than let you make your own (although it may seem simple enogth to do that with message properties). You can find some good arguments in the JMs specs.
- The second element involves the creation of a "common message handling framework". I argue that in any case where JMS selectors suffice, you don't gain anything. When JMS selectors suffice, you can think of the app server as your central MDB and factory. It will create "message handlers" (in this case simply MDBs) according to your deployment settings. This factory can be just as good as yours, and maybe even better. It supports changing configuration "on-the-fly" (in every app server I know) and allows you to add new message handlers without shutting down the central MDB for redeployment (which you may need with this pattern, unless you go around this with dynamic code loading... I can't think of a "by-the-spec" way to do that, cause you can't change the accessible classes for your MDB without redeploying it's JAR).
I could go on about how this pattern is non-standard, will be less compatible with "normal systems" and harder to learn for developers that already know JMS and EJB, but I don't think these are the key problems (and this post is getting to big).
I think the only place where you should use this pattern is where the JMS topic seperation and selectors just aren't good enogth to route your messages to handler. You can allmost allways go around this by adding message properties, but I can't argue that this is allways the case. I would try my best shot at using selectors, and only if it's impossible backfall to this pattern. Even when that happens, the "central" MDB should only be responsible for these specific hard-to-route messages.
Note: I mentioned efficiency and scalability a couple of times. I know these are not the intent of the pattern. I tried to show that the pattern doesn't give you any real advantage, and that it also hurts these fields, which are allways important to some degree.
Regards Gal |
1 replies in this thread |
Reply |
Message Driven Bean Proxy |
|
Posted By: Leo Shuster on December 14, 2001 in response to this message. |
As always Gal makes excellent points. I do not disagree with them at all but it seems that the arguments are made due to slight misunderstanding of what the pattern tries to achieve.
First of all, this pattern does not propose to implement MDB’s specific message handling logic in separate components -- simply classes (see Message Handling Classes description). Second of all, the pattern does not offer its own implementation of an MDB mechanism -- just an extension on top of the existing one. The proxy MDB will still be executed in the container, so MDB pooling and caching will be handled by the app server. And so will JMS routing. This pattern does not even attempt to provide an alternative implementation of these mechanisms.
The pattern stated in one of the first paragraphs: “MDBs performing highly specialized tasks, processing large amounts of data, or desiring to meet very high performance criteria should be excluded.” The example of an MDB subscribing to a durable topic would constitute an exclusion condition.
This pattern is based on a simple fact that most often MDBs’ differences lie in the onMessage method. Bulk of functionality is performed there. If you wanted to handle another message, you would need to write the same skeleton for a new Bean and place the specific logic in the onMessage method. Knowing that a JMS message can be handled generically through the Message interface, a single proxy MDB can handle most of application’s messages. In essence, this is what this pattern is about.
The only difference between this and a normal MDB implementation is an extra call to the factory class. The performance of an individual transaction should remain virtually unchanged. Arguably, efficiency may suffer with this pattern since one Bean will now be responsible for handling multiple messages but if the proxy MDB pool setting is increased what is the difference between instantiating 100 proxy Beans as opposed to 50 Beans X and 50 Beans Y? Furthermore, this pattern does not introduce any non-standard features but is rather based on standard J2EE and JMS functionality.
|
1 replies in this thread |
Reply |
Message Driven Bean Proxy |
|
Posted By: Gal Binyamini on December 14, 2001 in response to this message. |
Leo makes good points as well :) but I too think that he didn't understand exactly what I mean...
"First of all, this pattern does not propose to implement MDB’s specific message handling logic in separate components -- simply classes (see Message Handling Classes description)." OK. I was just making sure that's what we both mean, because if you ment to put all logic in components I would have to word my arguments differently.
"Second of all, the pattern does not offer its own implementation of an MDB mechanism -- just an extension on top of the existing one. The proxy MDB will still be executed in the container, so MDB pooling and caching will be handled by the app server. And so will JMS routing. This pattern does not even attempt to provide an alternative implementation of these mechanisms." I understood that's what you ment. However, a large part of JMS's routing capabilities come from it's non-flat namespace. By flatting the namespace down, you damage some of these features. For instance: 1. A smart JMS server will only send messages to listeners that want them. For instance, if you have four nodes in a cluster and each takes care of different tasks (maybe some tasks are shared), messages will only get sent to the listeners that want them. While this is still true with your pattern, the JMS server doesn't "know about" which listeners want what. All the messages go to a single topic, and everybody listen on it. So in effect, the message filtering JMS provides won't be able to help you. I made some other examples (also involving the pooling of instances), maybe they will be easier to understand in this context, or am I still missing your point?
"The pattern stated in one of the first paragraphs: “MDBs performing highly specialized tasks, processing large amounts of data, or desiring to meet very high performance criteria should be excluded.” The example of an MDB subscribing to a durable topic would constitute an exclusion condition." I wouldn't call durable subscriptions a "specialized task", but I guess that depends on what kind of systems you are working on. Anyway, this was just one example.
"This pattern is based on a simple fact that most often MDBs’ differences lie in the onMessage method. Bulk of functionality is performed there. If you wanted to handle another message, you would need to write the same skeleton for a new Bean and place the specific logic in the onMessage method." This is indeed the case, IMHO. My question is, what about the "obvious" solution of building a superclass for MDBs that implements MDB related methods? After all, there are just two of them: set context and remove. Both can be implemented to provide basic functionality (save context, nop on remove) and be overriden when needed. This seems to me just as easy as what you describe. What gains does the pattern give you over this?
"The only difference between this and a normal MDB implementation is an extra call to the factory class. The performance of an individual transaction should remain virtually unchanged. Arguably, efficiency may suffer with this pattern since one Bean will now be responsible for handling multiple messages but if the proxy MDB pool setting is increased what is the difference between instantiating 100 proxy Beans as opposed to 50 Beans X and 50 Beans Y? Furthermore, this pattern does not introduce any non-standard features but is rather based on standard J2EE and JMS functionality." If you don't implement caching then this pattern adds an extra instanciation to every message. But perhaps more importantly, it may hurt the app server's optimization attempts. The app server is given less knowledge. This may interfere with the LRU impl, and make hit counting irrelevant. It's not that the cache won't work, it's just that the optimization will not help as much as it would normally. It's not critical, but I still don't understand why should you agree to this, when other approaches are just as good and don't have this de-optimizing effect. I guess I'm still missing the intent of the pattern.
As for the non-standart comment, I can explain in more detail what I ment, but as I said it's less important so I will wait until after I've understood the issues above.
Gal |
1 replies in this thread |
Reply |
Message Driven Bean Proxy |
|
Posted By: Leo Shuster on December 15, 2001 in response to this message. |
I think I understand now where our thinking diverges. All of the examples you provide deal with JMS topics, while this pattern is most suitable for queues. After reviewing the original post, it is clear that this limitation was not explicitly stated. Topics represent a significantly more complex mechanism and, as well articulated in Gal’s messages, are not suitable for this pattern in most situations.
The intent of this pattern is to minimize the amount of JMS destinations and MDB deployments. I like the idea of a superclass but it does not resolve the above issue. On the other hand, it presents a nice implementation mechanism and minimizes the amount of code that needs to be written. This approach would be great for all MDB implementation that fall outside of this pattern’s scope. Gal, how about writing this up and posting it on the site? This way, I can ask you to clarify your statements for a change. :-)
|
1 replies in this thread |
Reply |
Message Driven Bean Proxy |
|
Posted By: Gal Binyamini on December 15, 2001 in response to this message. |
Now when I understand the pattern addresses queues (atleast mostly), I can more easily understand Leo's logic. I think that even in the case of queues, there is no reason to use just one queue. The pattern doesn't say you should use just one, but it doesn't provide guidelines as to which types of messages should go to the same queue. I would use this seperation as a rule of thumb: - group the commands your server provides using async invocation in normal Java interfaces. - make a queue for each interface. - make messages that invoke operations from the same interface go in the same queue. Note that this is just a theoretical way to create the destination seperation. You don't actually need Java interfaces. However, I do actually create these Java interfaces and use them in the client. Their implementation can be made generic using Java dynamic proxies (I can describe my impl if anybody is interested, the actual code is copyright :( ). If we both agree on this kind of seperation scheme (not neccesarily this percise one), our differences were just semantic. I got the idea that you ment to send all the messages in your system to just one queue, which I think is a stronger requirement than the pattern's goal (avoid cluttering of server with many queues). If you ment something like what I described above, i.e simply avoid creating a queue for every single "method" of the systems, I completely agree. I also think that, for this kind of seperation scheme, you can also use topics rather than queues. The problems with topics arrise because *all* messages go to the same topic. If only messages to the same logical object went to the same topic, I think the harmful side-effects would be to small to notice.
As for the idea of using an MDB superclass, I don't know if it can be called a "pattern". Actually, many of the so called "patterns" in the Pattern section of TSS are not design patterns, IMHO. That isn't to say they do not provide good advices, cool implementation, etc. But a high/low key generation algorithm isn't a design pattern :) Pardon my drifting.
Besides, if I were to post a pattern you would comment on my vague writing and I would have to clarify... Where is the fun in that? :)
Regards Gal |
1 replies in this thread |
Reply |
Message Driven Bean Proxy |
|
Posted By: Leo Shuster on December 15, 2001 in response to this message. |
The actual implementation of this pattern depends on your needs. You can separate destinations by using the interface approach you described (which is quite interesting) or other means. However, you could also handle all the messages through a single MDB. The pattern simply offers a solution to consolidate JMS destinations (primarily queues) and minimize the amount of MDBs that need to be deployed. In case of topics, “if only messages to the same logical object went to the same topic”, that would make them virtually identical to queues. Thus, the pattern would be applicable in this situation.
If you read the pattern submission page, it states that it is "looking for J2EE patterns, design strategies, tips and tricks". So, the MDB superclass approach would definitely fit into these requirements.
|
1 replies in this thread |
Reply |
Message Driven Bean Proxy |
|
Posted By: Gal Binyamini on December 15, 2001 in response to this message. |
"However, you could also handle all the messages through a single MDB" I think that this is a fundenmental problem. I've listed some scalabillity/efficiency issues, but now I'd like to view this problem from a plain design perspective. One of the key advantages of using MOM is that the set of message handlers is dynamic and heterogenous. You can use different boxes to do different things, and the client doesn't have to know about it. For example, the parts of my system that did JPEG manipulation ran on proper hardware that could apply affine transforms using a hardware card. We didn't have these to begin with, but we easily added them without stopping the system. If you use this pattern, the computational set that implements your message handling must be homogenous. That is, they all need to be able to handle all the messages. I find this unacceptable. The only way around this is to use JMS selectors to get the right messages to the right consumers. As I mentioned in my first post, if you do that then you might as well use different queues. It may seem like having multiple queues would be harder on the messaging server, but it isn't. If the best way to go was hold a single queue with a property stating which logical queue each message belongs to, the messaging server would have done that itself. A decent messaging server won't do it that way, because there are much better alternatives in terms of efficiency. Anyway, my point is that if you use selectors to imitate "logical queues", you're doing something wrong. This pattern forces you to either do that or bind yourself to homogenous computational sets (i.e, each message handler can handle all messages). As I mentioned above, this is (for me) one of the key reasons for using MOM in the first place.
My solution to this problem is to create distinct queues, each representing a homogenous set. The queues are, amongst themselves, heterogenous. This is the approach I described above. You can make an analogy to RMI (CORBA, etc): you don't want to bind your entire system to running on the same box (in our case, the same type of box). So you create different remote interfaces (queues). Each interface represents functions that will run on the same box (type of box), but the system as a whole can span different boxes.
About the Patterns section, I didn't know it should contain anything other than design pattern. I don't know what fooled me, I think it was the name... Anyway, it seems to me that the MDB superclass idea is to trivial to post. It's like posting a message that sais "when you have a class that logically extends another class, use inheritance". If you think this idea is worth posting, you have my blessing :)
Gal |
1 replies in this thread |
Reply |
Message Driven Bean Proxy |
|
Posted By: Leo Shuster on December 16, 2001 in response to this message. |
Gal, as always, makes excellent points. However, the discussion has been taken a little bit out of the context. The solution to handle all messages through a single MDB is a hypothetical one. It is often hard to group messages due to the issues pointed out in Gal’s post. The pattern only offers a generic solution to a problem where a number of messages are handled by different MDBs, which require similar resources to perform the processing. These MDBs can be consolidated into a single proxy MDB. How the messages are grouped and whether or not they can be grouped is left for the developers to decide. The solution offered by Gal represents one of the potential examples. |
1 replies in this thread |
Reply |
MDB Dispatcher Pattern |
|
Posted By: Dmitri Colebatch on December 17, 2001 in response to this message. |
Coming in a bit late here, but would like to hear arguments based on the following input. I've thought through a similar pattern, and in fact posted something a while back (http://www.theserverside.com/discussion/thread.jsp?thread_id=9713). The post could be clearer, as I'm no expert in communicating patterns. In essence, it has some similar goals to what Leo describes.
Minimal number of queues: The key advantage of this, is that the sender doesn't need to know what happens to the message next. To use multiple queues, the sender must have knowledge of where it should send the message - I called my pattern the Message Dispatcher Pattern, which hopefully communicates this objective. This allows for a message to be processed multiple times, mutating as it goes, and the dispatcher decides, based on message state, what should happen next.
I do however agree with many of the points that Gal makes about performance and pooling, and about implementing something that really is container logic. But if one has a requirement for a system with loosely coupled messages, then what I (and now Leo) suggested is one option to do it in a spec compliant manner. The alternative - using separate queues - restricts the system in that the sender of the message must know what should be done with it. This means that you gain asynchronous processing, but still have relatively tight coupling.
I didn't get any feedback on what I suggested when I posted it, so if either of you have any comments on it I'd be glad to hear them.
cheers dim |
2 replies in this thread |
Reply |
MDB Dispatcher and Proxy Patterns |
|
Posted By: Leo Shuster on December 17, 2001 in response to this message. |
The MDB Dispatcher Pattern proposed by Dmitri seems to be similar to the MDB Proxy approach, although it suggests a much more definitive implementation. The Proxy pattern is more general and does not offer a specific solution. It also does not assume that the whole message will be given to the SLSBs for processing but rather allows it to be handled by a Message Handling Class. The main difference here lies in the fact that message parsing and business logic should be separated. The intent of both patterns, however, seems to be the same and approach similar. I think these patterns can be considered variations on the same theme.
I did not see Dmitri's pattern when I was submitting my post, even though I looked for similar submissions. I think somehow it got hidden from the pattern page. Maybe, that's why it did not receive any comments. |
0 replies in this thread |
Reply |
MDB Dispatcher Pattern |
|
Posted By: Gal Binyamini on December 18, 2001 in response to this message. |
I was un-aware of Dmitri's pattern and now, having taken a look at it, I agree with Leo that it is a variation of the same theme.
I would like to comment about Dmitri's rationale for the pattern, which I think is different than Leo's. First of all, I think before using this pattern you must ask yourself if you really need another indirection. Queues and Topics (as used in MOM) are essentially objects that reduce coupling amongst components. By using a Queue, you achieve a certain level of indirection, which is commonly enogth. That's, IMHO, why objects such as Queues have been so strongly standartized, and multiplexed-queues (which are logical equivalents of this pattern, implemented at messaging server level) have not been as widely adopted. I think it is also useful in this context to view this issue from the "object analogy" I listed earlier. The issue you are describing, in analogous terms, is that you have a client which shouldn't know exactly what object it is talking to, only make sure the job is done. Here are a couple of well known patterns you can use in that case: 1. Indirect the call to another object. In this case, you can indirect the call to a SLSB that has information about the specific objects (queues) responsible for each type of request. The SLSB can place the message in the correct queue. 2. Implement something similar to interfaces, by predefining queues for each type of request and have the servants (message listeners) serve messages on these queues. By defining "logical" queues for each type of request, you rid the client of having to know about the implementation of the servant. Servants are decoupled simply by the use of queues. 3. Use chain of responsibillity. Here you pass messages around from queue to queue until they can be properly handled. This pattern is less common and is not applicable when messages are not light-weight.
I think in allmost every case using these patterns will achieve the same degree of decoupling without the problems listed in earlier posts. Of course, you came up with this pattern given a specific problem. Perhaps if you describe that problem I can more easily understand the usefulness of this pattern.
Regards Gal |
| | |