After switching my Rails 2.3 app from MRI Ruby 1.8.7 to JRuby 1.6.5, the app is no longer able to send mail. I'm using ActionMailer like this:
class MessageMailer < ActionMailer::Base
def message(msg, recipient, reply_to_email=nil)
template = (msg.message_type.nil?) ? "default" : msg.message_type.name.downcase.gsub(' ', '_')
recipients recipient
subject msg.subject
from (msg.sender.nil? or msg.sender.email.blank?) ? "\"no-reply\" <#{SYSTEM_EMAIL_ADDRESS}>" : msg.sender.email
content_type "text/html"
body render_message(template, :message => msg)
reply_to reply_to_email || ((msg.sender.nil? or msg.sender.email.blank?) ? "\"no-reply\" <#{SYSTEM_EMAIL_ADDRESS}>" : msg.sender.email)
end
...
end
MessageMailer.deliver_message(...)
That's probably irrelevant, since this all works under MRI Ruby 1.8.7.
The Rails app is configured to use sendmail in config/environments/production.rb:
config.action_mailer.delivery_method = :sendmail
What's more interesting are the sendmail logs (/var/log/mail.log):
# Sending mail under MRI Ruby 1.8.7
Jan 5 09:38:49 my sendmail[24755]: q05EcnCr024755: from=edwarda, size=310, class=0, nrcpts=1, msgid=<[email protected]>, relay=edwarda@localhost
Jan 5 09:38:49 my sm-mta[24757]: q05Ecn02024757: from=<[email protected]>, size=516, class=0, nrcpts=1, msgid=<[email protected]>, proto=ESMTP, daemon=MTA-v4, relay=localhost [127.0.0.1]
Jan 5 09:38:49 my sendmail[24755]: q05EcnCr024755: [email protected], ctladdr=edwarda (1011/1012), delay=00:00:00, xdelay=00:00:00, mailer=relay, pri=30310, relay=[127.0.0.1] [127.0.0.1], dsn=2.0.0, stat=Sent (q05Ecn02024757 Message accepted for delivery)
Jan 5 09:38:49 my sm-mta[24759]: STARTTLS=client, relay=aspmx.l.google.com., version=TLSv1/SSLv3, verify=FAIL, cipher=RC4-SHA, bits=128/128
Jan 5 09:38:49 my sm-mta[24759]: q05Ecn02024757: to=<[email protected]>, ctladdr=<[email protected]> (1011/1012), delay=00:00:00, xdelay=00:00:00, mailer=esmtp, pri=120516, relay=aspmx.l.google.com. [74.125.45.27], dsn=2.0.0, stat=Sent (OK 1325774329 o43si18661797yhk.140)
# Sending mail under JRuby 1.6.5
Jan 5 11:10:26 my sendmail[7623]: q05GAQkH007623: from=edwarda, size=199, class=0, nrcpts=0, relay=edwarda@localhost
Note that the nrcpts (number of recipients) is 0 when I'm running JRuby and 1 when running 1.8.7.
I'm using the exact same code and gems, except I'm using these gems in addition for JRuby:
gem 'activerecord-jdbc-adapter', '<= 1.2.0', :require => false
gem 'activerecord-jdbcpostgresql-adapter', '<= 1.2.0', :require => 'jdbc_adapter'
gem 'ffi-ncurses'
gem 'jruby-openssl'
gem 'torquebox', '2.0.0.beta1', :platforms => 'jruby'
gem 'torquebox-rake-support', :platforms => 'jruby'
gem 'torquebox-capistrano-support', '2.0.0.beta1'
In case it's useful, this is my Gemfile.lock.
There is no interesting or unusual output in my Rails logs; only the usual success messages.
edit: I cannot reproduce this problem on my development (OSX) machine.
Any thoughts on why the recipients might be getting lost or how I might troubleshoot this?
This is related to a JRuby bug: http://jira.codehaus.org/browse/JRUBY-6162
Apparently IO.popen
is closing prematurely when it tries to execute the sendmail command. Until this is fixed upstream, this monkey patch can be included in an initializer to work around the problem:
Rails 2:
module ActionMailer
class Base
def perform_delivery_sendmail(mail)
sendmail_args = sendmail_settings[:arguments]
sendmail_args += " -f \"#{mail['return-path']}\"" if mail['return-path']
IO.popen("#{sendmail_settings[:location]} #{sendmail_args}", "r+") do |f| #r+ geht, w+ geht nicht richtig, bzw. nur manchmal
f.puts mail.encoded.gsub(/\r/, '')
f.close_write
sleep 1 # <---- added this line in order to give sendmail some time to process
end
end
end
end
Rails 3:
module Mail
class Sendmail
def initialize(values)
self.settings = { :location => '/usr/sbin/sendmail',
:arguments => '-i -t' }.merge(values)
end
attr_accessor :settings
def deliver!(mail)
envelope_from = mail.return_path || mail.sender || mail.from_addrs.first
return_path = "-f \"#{envelope_from.to_s.shellescape}\"" if envelope_from
arguments = [settings[:arguments], return_path].compact.join(" ")
Sendmail.call(settings[:location], arguments, mail.destinations.collect(&:shellescape).join(" "), mail)
end
def Sendmail.call(path, arguments, destinations, mail)
IO.popen("#{path} #{arguments} #{destinations}", "r+") do |io|
io.puts mail.encoded.to_lf
io.close_write # <------ changed this from flush to close_write
sleep 1 # <-------- added this line
end
end
end
end