Here's the problem:
I have a synchronous front end. It asks my StatelessSessionBean what products are available for a given set of criteria, and needs all the available products returned to display with a JSP. The problem is, I have to interface with web-enabled legacy systems ( which I cannot change ) to determine which products are available. Currently I have 10 such systems, but the problem could be N many ( if we add more vendors ). It takes almost no processing power for me to send the request or get the response from these systems, but each system blocks for between 5 and 30 seconds from when I send the response to when I get the reply.
Now I can open multiple simultaneous sockets to these machines and call out all at once using threads, threby making the entire transaction take at most the longest time ( say 30 seconds ), instead of doing it one at a time( 300 seconds ).
However, I need to make this work as an EJB with the rest of our webservices, so I can't just start threads. I want to know what the best practices are around this kind of problem. Now I'm told the way in J2EE to do asynchronous processing is to use JMS and MessageDrivenBeans.
One such approach I have read about is for my SSB to create a temporary Queue, send the queue in the response message to the MDB, and have it respond to that temporary queue, which the SSB will block on for some amount of time waiting for all the messages to be responded to. I have read a couple of threads here at serverside suggesting that this might not be in line with the EJB spec ( for my SSB to use a temp queue ), and also this approach has problems with my EJB being passivated and the queue getting lost.
I've read many threads here that seem to skirt around an issue like this or make ambiguous references to someone somewhere who did something like this, but none of them seem to have a clear response as to what is EJB-compliant. Is everybody else just ignoring the thread rules for EJB? Or using this JMS temporary queue and not worrying about passivation? Or can this just not be done elegantly ( or at all ) within an EJB framework? I don't see alot of resolution here, and I'm hoping somebody out there knows the answer and will enlighten me before I end up writing my own Thread-pooling logic on a separate non-EJB appserver...
You ask a good question, and as you mentioned it was debated on TSS in several threads. Session beans do not naturally support asynchronous processing, so getting a solution to work and be portable at the same time can be a real pain. The only elegant solution I know is the one you mentioned, using a temporary queue, or using a global queue and the querying it using selectors.
There seems to be some comfusion regarding the passivation related problems. Passivation is a problem when you want the client call to return immediately, and enable the client to make a seperate call later on to query the results. In that case, the bean can be passivated between the calls, loosing it's queue. I don't think this is a problem in your case. In your case, the messages will be sent to the queue while the session bean is blocking. As long as the session bean is executing a thread, it cannot be passivated. So I see no reason why there should be a problem.
Also, check whether the very act of creating temporary queues for each such request will bog down your system. AFAIK, creating queues is not a trivial task.
Creating an Administered Global Queue with message selectors or simply creating a separate queue for each vendor would probably be a better idea. The results from each vendor could then be redirected to one "result-queue" where the session bean could block.
Comment about Das's post:
If you were right, why wouldn't the JMS provider implement the temporary topic by simply using a global queue and applying selectors on it? Temporary topics are an abstraction. In thw worse case, the JMS provider could use the exact approach you suggest to implement it. I would expect it to use a smarter approach, but at the very least the temporary topics should be as good as using a global queue. IMHO, the very reason you bring an abstraction like temporary topics into the spec rather than implement it using the other JMS abstractions is that it's implementation may be more efficient that way... Any comments?
Reply to Gal's comment:
You may be right in your hypothesis. My comment was based on a BOF that we had in JavaOne2001 where a similar design was presented in which there were temporary queues all over the place. The audience pretty much scoffed at it at the time - my impression was that the temporary queues didn't sit too well with them ...
But a bit more research on this topic tells me that this is a very standard architecture and BEA apparently tests out this situation (of a temp queue per user) for 2000+ users on a regular basis.
Thanks for pointing this out, Gal.
P.S: here's a newsgroup link which is related to this:
java.nio should allow you to wait for responses on multiple sockets without creating threads.
Having said that, I've never understood why creating threads was a bad thing, as long as their lifetime is less than the lifetime of a request. I haven't tried it either, and there may well be a good reason...
I would very surprised if EJBs will be allowed to use multiplexers, selectors, etc for IO. They are hardly allowed any type of direct IO now (no file IO, only socket connect, no accept). And I also think EJB should do it's best to mask such tasks away from the programmer. It would be better to just allow threads in the first place - atleast then programmers wouldn't have to focus on system-level code in their components.
GB:"I would very surprised if EJBs will be allowed to use multiplexers, selectors, etc for IO. They are hardly allowed any type of direct IO now (no file IO, only socket connect, no accept)."
Why? What's the difference between a Selector with some SocketChannels and using a Socket (which is allowed)?
Obviously you shouldn't use a ServerSocketChannel.
For one thing, opening a selector is an expensive operation that, much like thread creation, shouldn't be done repeatedly. In addition, selectors register system level resources. After a selector has been opened, system code will spend time on it even after the bean is done (unless it explicitly closes it). This contradicts EJBs processing model, much like asynchronous processing of JMS messages using listeners (which is also disallowed). Same goes for things like directly allocated buffers, Mapped buffers, etc.
I don't see a problem with using a SocketChannel per se, but I do see a problem with using features such as non-blocking IO and multiplexing of channels directly within EJBs.
Other than that, I also don't think it's a good idea to use such low-level IO libraries in your components in the first place. EJB should allow problems to be solved without focusing on low-level code. Otherwise it's useuless.
I think you should rethink the architecture.
I am assuming:
1) you send out some feelers which dont respond for 30 seconds, then you get responses
2) you cant know exactly when the reply may come so you have to make something wait and listen
3) you are not FORCED to make this solution an EJB, but rather it is an option
4) If it is "Browser" based then your users may time out on the longer requests
1 - (if you can add code to the legacy sides)
Use a "webservices" solution on both sides.
2 - (if you can only change the code on your side)
just use the part of the servlet spec, "ServletRequest.getInputStream()", which allows you to point to a socket and receive a response (though you could time out with such long requests).
I just noticed that in youre message you said you need to wait for a certain amount of time. I'd like to note that if you kow how many services will process your message, you can just read that amount of replies, and drop the potentially time-consuming 30-second sleep.
Thank you Gail.
My code will timeout after TIMEOUT time or get all the request and finish.
My pseudo-code for what I'm going to do is going to go something like this:
Queue sendQueue = jndi.lookup("foo");
TemporaryQuese receiveuese = conn.createTemporaryQueue()
MessageConsumer receiver = session.getConsumer(receiveQueue);
MessageProducer sender = session.getProducer( sendQueue );
Sender.sendMessage( new MyMessage( ... ) );
long startTime = System.currentTimeMillis();
long endTime = startTime + TIMEOUT;
long curTime = System.currentTimeMillis();
for( int i = 0; i < numMessages && curTime < endTime; ++i )
Message m = receive.receive( endTime -curTime );
if ( m == null )
long curTime = System.currentTimeMillis();
Does anyone have any comments, suggestions, improvements?
That's roughly what I would do. I have a couple of notes:
1. I would consider using a topic instead of a queue for the messages you post. That way, each one of the systems you query could have it's own listener that picks messages off the topic. Using a queue, only one client will get each message: so you will have to write the code that, given a message, dispatches it to the different systems you interact with. That seems a little less flexible to me.
2. I don't know what receiveQueue.remove() means... Is this a mistake, or is there a method I don't know about?
I'm not sure what I could do to change the architecture, or how that would make it better.
I can't change my vendors systems. They are running in machines I have a frame relay to, but they maintain and develop their own code. Even if a could make their systems more "webservices", I don't think it would solve the basic parallel processing problem. My website job is to be able to find all of the products in each of their systems.
I'm not going to say exactly what I'm working on ( it's not travel-related ), but a similar problem would be an airlines registration system. You have Delta, American Airlines, Continental, etc... Now each one maintains its own system that determines which flights are available when. It's obviously too complicated for me to mirror all their logic for flight prices/times ( and they won't tell me anyway ). I have a list of criteria given to me by the user, and I want a list of all the flights from all systems that meet the criteria ( i.e. I want to be Expedia ).
Responding to your email,
3) I have to interact with EJBs to get the criteria I want to search on in the vender system. So at some point, I do have to interact with EJBs. I could ( in theory ) find a way to have an EJB facade on some other virtual-machine that handles all these callouts, but I don't want to do that.
4) I throw up a "you have to wait" page in the browser that waits. It doesn't ever timeout, no matter how long it takes ( I didn't write that part, I'm not sure exactly how that was done ). As a courtesy, I just time out after awhile instead of waiting forever ( in case a vender went down or something )
1) How does that solve the parallel processing problem? I don't think there's anyway to get around running them together.
2) Wouldn't this still be serial? I'm not posting to the vendor sites, I'm literally Socket.write-ing. How can I use this to send/receive on several sockets simultaneously