Groovy Adventures

A developer's journey.

A Groovy Java Puzzler: Setting a Property When Its Setter Is Overloaded

| Comments

Trying out Spring’s email support, I wanted to send a simple message to a single recipient using code like:

1
2
3
4
SimpleMailMessage mailMessage = new SimpleMailMessage()
mailMessage.to = 'me@trash-mail.com'
// ...
mailSender.send(mailMessage)

To my astonishment this threw an exception:

1
2
3
4
5
6
7
8
9
10
11
12
13
javax.mail.internet.AddressException: Missing local name in string ``@''
  at javax.mail.internet.InternetAddress.checkAddress(InternetAddress.java:1209)
  at javax.mail.internet.InternetAddress.parse(InternetAddress.java:1091)
  at javax.mail.internet.InternetAddress.parse(InternetAddress.java:633)
  at javax.mail.internet.InternetAddress.parse(InternetAddress.java:610)
  at org.springframework.mail.javamail.MimeMessageHelper.parseAddress(MimeMessageHelper.java:707)
  at org.springframework.mail.javamail.MimeMessageHelper.setTo(MimeMessageHelper.java:593)
  at org.springframework.mail.javamail.MimeMailMessage.setTo(MimeMailMessage.java:109)
  at org.springframework.mail.SimpleMailMessage.copyTo(SimpleMailMessage.java:194)
  at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:305)
  at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:297)
  at org.springframework.mail.MailSender$send.call(Unknown Source)
  ...

Taking a closer look, I recognized that the setTo method of SimpleMailMessage is overloaded as follows:

1
2
public void setTo(String to)
public void setTo(String[] to)

So the second method allows for passing a list of recipients.

My intuition told me to check the bytecode the Groovy compiler has generated for setting the to property, using the wonderful Procyon decompiler:

1
ScriptBytecodeAdapter.setProperty((Object)"me@trash-mail.com", (Class)null, (Object)mailMessage, "to");

Not having the book at hand at that time but vaguely remembering a Java Puzzler for passing null to an overloaded method, I searched the Java Language Specification for how methods are chosen in my case. I found out and refreshed my memory that Java chooses the most specific method.

As an array parameter is more specific than its single reference counterpart, the setTo(String[]) method is called.

With the Groovy logic to transform a String to a List of that String’s characters when that String is passed to a method with a String array parameter, this results figuratively in an invocation like:

1
mailMessage.setTo(['m', 'e', '@', 't', 'r', 'a', 's', 'h', '-', 'm', 'a', 'i', 'l', '.', 'c', 'o', 'm'])

As each of those array entries is handled like a recipient, each is passed within a loop to InternetAddress.checkAddress(..). The looping terminates while its third iteration as the “recipient” @ is passed to checkAddress(..) - the AddressException is thrown, stating Missing local name in string ``@''. Finally, this message makes more sense, doesn’t it?

By the way, to solve the problem I just wrote:

1
mailMessage.to = ['me@trash-mail.com']

All in all, isn’t that a groovy Java Puzzler?

Comments