I've got the basic framework down. If RemoteDeliveryMDB can't send to some of the recipients it throws an EJBException causing it to retry. After the max number of retries it gets placed on an alternative DLQ with another MDB at the end of it, which is all well and good.
The problem with this way of doing it is with how MailQueueReceiverMDB/SMTPSender works. If the Mail in the ObjectMessage originally has 10 recipients and say five of them fail (and five worked), the message is retried and will have all the original ten recipients there, so if the message gets retried five times using JMS the five recipients who receive the message will receive multiple copies of the message. There is no obvious way of keeping track of who has been sent to. I've tried modifying the Mail object and setting it in the ObjectMessage before throwing my exception, but as expected that didn't work (if I rememebr correctly the message is cloned somewhere in the container/interceptor stack).
Also, I'm not sure if there is any way to delay retries:
Suggestions please! I could well be missing something, so if anybody knows a way around the problem described above I'm all ears.
Another way of doing it though, might be to make the queued ObjectMessage contain a MailWrapper object which contains the Mail object. The wrapper could contain a variable containing the mail object, and the number of tries and the recipients successfully sent to so far. If RemoteDeliveryMDB can not send the message to ALL recipients it increments the number of tries and updates the successful recpient list in the wrapper, and places it in a new ObjectMessage on the offServer queue itself. That way it could also set the JMS_JBOSS_REDELIVERY_DELAY property as well to allow for delay. Once it receives a wrapper with a number of retries greater than the maximum number of retries it can throw an Exception so the message is placed on the DLQ (in which case the max number. An added bonus is that we would be able to specify a different retry interval for each retry, so that we could do it more frequently the first few times. If somebody knows a way around the problem mentioned above this should not be necessary.
You have to trhow a runtime exception. As for redelivery, I think we need some smarter enveloping. The smarter envelope should say *who* its to be redelivered to. Basically transient headers. Yeah MailWrapper, same idea. I don't know if we can but we should put this on a custom DLQ rahter than DLQ itself... (Think of an application server with mail as one issue of several, the DLQ may be used for other things).
Custom DLQ not a problem. I got that set that up.
The thing that brought me onto the EJBException/RuntimeException is that the message was not retried until I added the container-tranaction bits to the MDB ejb-jar.xml. According to Adrian/JMS spec, throwing RuntimeExceptions from onMessage is not allowed, but from what I gather it is done quite a lot in practice. Anyway, retries work if I include the container-transaction stuff in the assembly descriptor.
Not sure I get the smarter enveloping stuff, am I missing something? If the MDB receives the jms message, successfully sends the mail to some recipients and then throws a runtimeexception the jms message ends up back on the original queue, and is retried by jms. When retried it should only send to the recipients that failed, so we need to keep track of who succeeded/falied somehow. But from my initial investigations it does not seem to be possible to modify the stuff stored in the jms message body, headers or properties from the jms listener/MDB. And since retries are handled by JMS, the sender doesn't get access to change anything either? Also, it doesn't seem to be possible to delay retries. I'll investigate some more, but if you have answers please shed some light!
If not, I think the alternative mechanism outlined should do the job, but is seems like a slightly roundabout way of doing stuff.
So what exception does he think you're supposed to throw?
He doesn't say, and not sure I dare ask :-) I'll look into it a bit more soon though. If we have a container-transaction configured it works.
I'm more concerned about the delaying retries/keeping track of who the message has been successfuly sent to.
I've made a few changes for retry. I went with the alternative implementation I suggested since I got stuck with the JMS/DLQ stuff (Still not sure if it is possible). If a mail fails for remote/local delivery the MDB puts it in a wrapper object and calls JMSMailListener again.
The wrapper object keeps track of the number of retries, who to resend to etc. and JMSMailListener now generates the bounce messages. The config for JMSMailListener contains the retry intervals. You can set up how many times you want to retry, and configure the delays between the retries so you can have it try frequently in the beginning and then less frequently later. It might be a more roundabout way of doing it, but I think it is more flexible.
If I have tagged cvs correctly, the changes should be between the following tags on the head branch:
lets not add a second wrapper, lets add something like "transient header" or something to the existing objects.
This one has been taking a looooong time for me to understand! :-)
Is what you mean that rather than having a wrapper around the Mail class with the extra info, you would prefer to have this info contained in a separate "transient header" class pointed to by the mail object itself?
I'll wait till you're back/have spelled it out for me until I do something else.
We already have the wrapper :-) Just add a new map. (Its up to you I was just suggesting -- this is primarily a do-ocracy with excessive feedback from the troll because he hasn't gotten to do much coding this week and is jealous :-) )
BTW I addded your mail account. Have fun.