Over the last couple of years I've been learning other languages particularly Javascript and Ruby. I really like Javascript as a language, API could use some help. I love Ruby's style of meta-programming and the fact that everything is an object. What I'm really jealous is how powerful a single function in these languages can be. For example in javascript it's easy to create widgets with Ext.js:
var button = new Ext.Button( 'okButton', {
text: "OK",
click: function( evt ) {
alert("Hey!");
},
"class": "myCssClass"
} );
In Swing this would be 25 lines of setter methods to customize components (OK maybe I'm exaggerating a little). Or in Ruby active record is just so simple and elegant with things like:
books = Book.find( :all, :condition => [ "where pubdate > ?", pubDate ], :include => [ :tags, :comments ] )
The ability to hand in optional arguments regardless of position turns simple functions into really expressive mini languages. Dare I say DSLs. I really miss these features when I have to use Java. Since Java opted for the C++ route of data structures. Data structures live in the API, and not the language, it's difficult for us to mimic this type of power.
I remember reading the Javascript Bible several years ago, and in there is a chapter titled "Functions as Data". And, the author is trying to show how functions can be assigned to variables, and passed around the system such one part of the program is unaware of which exact function it's calling. He's trying break the stereotype of functions being just actions, and show you how functions can be data as well. It's a good chapter for any developer.
This got me thinking about stereotypes we have about other languages. In Java we, all too often, get stuck thinking of our objects as largely data with some marginal functions mashed in to manipulate or manage that data. We usually create huge single instance, infinite lifetime objects that then do all the heavy lifting. However, what if we created objects that lived really only for a small period of time just to perform small functions? In fact what if we took what we would normally write as a function and turned it into an object. How would that change what we could express?
Now we need a problem to practice our idea on. Let's say we want to build a simple email utility because I think everyone who has worked on a web application in the past has had to send out email. For example, say we want to send an email from the site when people register to welcome them to the system, or when they forget their password. And, as you know Javamail, while a pretty good general purpose API, is very verbose to use. So let's say we create some utility method for sending email:
public MailHelper {
public static MimeMessage createEmail( String to, String from, String subject, String template ) {
}
}
The idea of this method is that we give it the to, from, subject, and a template file to use for the body, and it returns a MimeMessage, which is a Javamail class. It's basically an object representing an email. The template is either a Velocity or Freemarker template. I'm going to use Velocity in this example. If you're not familiar with these libraries they're really simple. They take in text like:
Hi $name,
Welcome to my cool new website. Thanks for taking the time to register with us. We're so glad to call you a member. Here is your password: $password in case you forgot, and here are some great places to get started $interestingPlace.
Thanks
The Man
And replace all of the "$" references with something you assign to them. So in your program you might say "name"="Charlie", and "password"="wouldnt you like to know", and "interestingPlace"="Tahiti". If you've used properties in Ant it's really a similar concept. It's not rocket science.
Now back to our utility. The problem with our utility is that we need a way to set all of those interesting variables that we'll substitute into our template. But, how do we do that with our single static method?
In Javascript or Ruby they might just pass a hash map like:
function createEmail( to, from, subject, template, bindings ) {
bindings = bindings || {}; // these notation is used for optional arguments.
}
What's cool about the function above is that if you don't have any bindings, just don't include it. Javascript doesn't require you pass all of the arguments to the function so the first line just happily sets bindings to empty object if they aren't specified.
So if we copy this in Java we'd add a HashMap onto our arguments:
public MailHelper {
public static MimeMessage createEmail( String to, String from, String subject, String template, Map bindings ) {
}
}
Then our usage would be something like:
public static void main( String args[] ) {
Map bindings = new HashMap();
bindings.put("name", "Unsuspecting Luser");
bindings.put("password", "wouldnt you like to know" );
bindings.put("interestingPlaces", "Tahiti" );
MimeMessage message = MailHelper.createEmail( "ycnangerp1@hotmail.com", "kevben@coldmail.ca", "Hello New Member", "email.vm", bindings );
}
Now I don't know about you, but that kinda code drives me nuts. It's so verbose, and
boring! The Javascript and Ruby versions of this are so much more fun. Now what if we wanted to add some attachments, CC some people, or add HTML and text versions of the email? Our poor little static function just can't hang. What we need is something better.
If you're still not convinced that statics aren't up for this consider that I'm leaving out some very important architecture here. Particularly, I need a VelocityEngine instance inside the static function in order to evaluate the template. Now I have to make a choice. One I make a simplifying assumption that I only need one instance of VelcoityEngine and initialize it in the static utility. That would mean I give up on the ability to have multiple template locations, or mail servers, etc. This would make it hard to unit test. It also makes reusing this utility harder because everything is hardcoded structurally. And, singleton's don't help this! The other choice is force the user to manage of the data this static utility needs, but I think that would compromise the ease and usefulness of the utility. Kinda of like well geez if you're going to make me do all of that I might as well just do it myself. Both of them get us into ugly Java.
What if we take our function and turn it into an object? I'm going to approach this from the outside interface, and then show the implementation:
public static void main( String[] args ) {
Emailer emailer = new Emailer();
MimeMessage mime = emailer.email("ycnangerp1@hotmail.com", "Hello New Member", "email.vm" )
.bind( "name", "Bad Spammer" )
.bind("password", "wouldnt you like to know")
.bind("interestingPlaces", "Hell" )
.toMail();
}
Look at that! Much easier to use. Notice how I don't have to create a seperate HashMap. Our new object provides this for us so binding is completely optional. If the email you're sending doesn't have any bindings then just don't call the bind() method. Also notice that emailer instance has a email() method to create an email. It almost reads like English. It also scales nicely for other types of data like attachments, or CC'ing:
public static void main( String[] args ) {
Emailer emailer = new Emailer();
MimeMessage mime = emailer.email("ycnangerp1@hotmail.com", "Hello New Member", "email.vm" )
.bind( "name", "Unsuspecting Luser" )
.bind("password", "wouldnt you like to know")
.bind("interestingPlaces", "Tahiti" )
.cc( "manager@spambot.com" )
.attach( stockHypeImage )
.toMail();
}
I'm using email addresses of known spammers to increase the chances spam bots will pick them up and blast emails at them.
If you'll notice I moved one parameter out from the first function: the from parameter. I'll add this back in at a later point in time. You'll also notice the toMail() method. Since we're making multiple calls we'll need a function to end the transaction or kick off the action. I'm going to enhance the version of the code below to also send the email instead of just creating it. I'm not going to implement the toMail() method since sending is the ultimate goal. So now let's look at the code for implementing this kinda class:
public class Emailer {
private static final Logger logger = Logger.getLogger(Emailer.class);
private String from;
private String url;
private Properties mailProperties;
private VelocityEngine engine;
public Emailer( VelocityEngine anEngine, Properties mailProps, String from, String url ) {
this.engine = anEngine;
this.mailProperties = mailProps;
this.from = from;
this.url = url;
}
public Email email( String to, String subject, String mailTemplate ) {
return new Email( to, subject, mailTemplate );
}
public Email email( String to, String subject, String mailTemplate, String htmlTemplate ) {
return new Email( to, subject, mailTemplate, htmlTemplate );
}
public class Email {
private String to;
private String subject;
private String textTemplate;
private String htmlTemplate;
private Mapparams;
private Email() {
this.params = new HashMap();
if( url != null ) {
this.params.put("website", url);
}
}
public Email(String to, String subject, String textTemplate) {
this();
this.to = to;
this.subject = subject;
this.textTemplate = textTemplate;
}
public Email(String to, String subject, String textTemplate, String htmlTemplate) {
this();
this.to = to;
this.subject = subject;
this.textTemplate = textTemplate;
this.htmlTemplate = htmlTemplate;
}
public Email bind( String key, Object obj ) {
params.put( key, obj );
return this;
}
public void send() {
try {
Session session = Session.getInstance( mailProperties );
MimeMessage message = new MimeMessage( session );
message.setRecipient( MimeMessage.RecipientType.TO, new InternetAddress( to ) );
message.setFrom( new InternetAddress( from ) );
message.setSubject( subject );
if( htmlTemplate == null ) {
message.setText( renderTemplate( textTemplate ) );
} else {
String text = renderTemplate( textTemplate );
String html = renderTemplate( htmlTemplate );
MimeMultipart part = new MimeMultipart("alternative");
part.addBodyPart( createBody( text,"text/plain") );
part.addBodyPart( createBody( html,"text/html") );
message.setContent( part );
}
message.saveChanges();
Transport transport = session.getTransport("smtp");
transport.connect();
transport.sendMessage( message, message.getAllRecipients() );
transport.close();
} catch( Exception mex ) {
logger.error( "There was an problem emailing: " + subject + " to: " + to, mex );
}
}
private BodyPart createBody(String text, String mimetype) throws MessagingException {
MimeBodyPart body = new MimeBodyPart();
body.setContent( text, mimetype );
return body;
}
private String renderTemplate( String template ) throws Exception {
StringWriter writer = new StringWriter();
Template tmpl = engine.getTemplate( template );
VelocityContext context = new VelocityContext( params );
tmpl.merge( context, writer );
return writer.toString();
}
}
}
The two interesting methods are the Emailer.email() methods. The two versions are for sending text only emails, or multipart HTML emails. At the start it's really quite simple. Invoking email() creates an Email instance which is an inner class for the Emailer. At that point you can call bind() method for every object you want to bind into the template. Notice how bind() returns the Email instance allowing you to chain method calls. Finally once you're done call send() to actually send the email. This was something that you couldn't do with the static method. For one it was too complex to configure through a single method call, and you're static method would have to have access to java properties, velocity engine, etc. By limiting our interaction to statics we force our users to take on more responsibilities. In the static case the user has to use some other utility to send the email. Either we have to force user to manage the architecture by gathering up the velocity engine instance, java mail properties, etc. Or we hard code it with statics in such that our tool will only work for a single velocity engine, from, and java mail properties. Neither is very helpful. One doesn't do enough to make it helpful, and the other limits our architecture and impacts how we can reuse it.
The Emailer class is also split into two parts. The outer class which holds all of the architectural/configuration items (velocity engine, java mail properties, etc). By splitting the code this way those portions can be configured at some place else in the code, say at application initialization, and the parts of the code that need to send email can be blissfully unaware of the architecture used to do so. This outer class acts as the glue between the java mail settings, velocity engine, and simple data like the email address of the author. It's really similar to functional language technique of currying functions.
The second part is the inner class Email. Using an inner classes allows us to create an instance that holds temporary state (i.e. bindings, the template for a specific email, to, subject, etc), but still has access to long living state (i.e. mail host, properties, velocity engine, etc). If we mixed the two inside the Emailer class we couldn't share that across multiple threads. Notice there's no setter methods on Emailer. That means once the Emailer is created other threads can't modify the internal state of it. It's immutable so it's safe to share the Emailer across multiple threads. This pattern allows Emailer to be shared, while Email instances only live on the stack of each Thread. There by separating data being written to (i.e. specifics for sending a single email) from the data that's not changing (i.e. the architecture of sending emails). Safety without synchronizing!
Notice how what started out as our verb function, createEmail(), and turned into a Noun, Email, that acted like a verb. That's usually what happens when you move from static utility methods to active function Object. For lack of a better term.
What's cool about this approach is our Emailer class acts more like a complex function like we saw in Javascript and Ruby. In a single line we easily send an email that requires potentially complex optional data. In addition it allows clients of this class to focus only on sending email, and not worry about the details of how that email is actually sent. Since we're in Java our function Object can tell the user all the options you can call on it with our IDE's code completion feature. Something Ruby and Javascript can't do reliably enough to be useful. With those scripting languages you're relying on the documentation to tell you the specific options of the function.
This pattern in Java looks more like Smalltalk to me. Your functions can end up looking very english-like. By using method chaining and inner classes we can get the same power without having to sacrifice the compact, expressiveness of functions we enjoyed with Javascript and Ruby. Not only did we improve the code's reusability, but we delivered more features by switching from static method to function object. We went from just being able to create the email to actually creating and sending it using objects as functions. In the end, we can now create very powerful single line "functions" just like those scripting languages by using the one construct Java does well. Objects.
Now as I promised da code. This example create a sub directory called templates under your current working directory, and put a test.vm template in there. Copy the template text above, and it will work:
package mail;
import org.apache.log4j.Logger;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.Template;
import javax.mail.*;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.InternetAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.io.StringWriter;
public class Emailer {
private static final Logger logger = Logger.getLogger(Emailer.class);
private String from;
private Properties mailProperties;
private VelocityEngine engine;
protected Emailer() {
}
public Emailer(VelocityEngine engine, Properties mailProperties, String from) {
this.engine = engine;
this.mailProperties = mailProperties;
this.from = from;
}
public Email email( String to, String subject, String mailTemplate ) {
return new Email( to, subject, mailTemplate );
}
public Email email( String to, String subject, String mailTemplate, String htmlTemplate ) {
return new Email( to, subject, mailTemplate, htmlTemplate );
}
public class Email {
private String to;
private String subject;
private String textTemplate;
private String htmlTemplate;
private Mapparams;
private Email() {
this.params = new HashMap();
}
public Email(String to, String subject, String textTemplate) {
this();
this.to = to;
this.subject = subject;
this.textTemplate = textTemplate;
}
public Email(String to, String subject, String textTemplate, String htmlTemplate) {
this();
this.to = to;
this.subject = subject;
this.textTemplate = textTemplate;
this.htmlTemplate = htmlTemplate;
}
public Email bind( String key, Object obj ) {
params.put( key, obj );
return this;
}
public void send() {
try {
Session session = Session.getInstance( mailProperties );
MimeMessage message = new MimeMessage( session );
message.setRecipient( MimeMessage.RecipientType.TO, new InternetAddress( to ) );
message.setFrom( new InternetAddress( from ) );
message.setSubject( subject );
if( htmlTemplate == null ) {
message.setText( renderTemplate( textTemplate ) );
} else {
String text = renderTemplate( textTemplate );
String html = renderTemplate( htmlTemplate );
MimeMultipart part = new MimeMultipart("alternative");
part.addBodyPart( createBody( text,"text/plain") );
part.addBodyPart( createBody( html,"text/html") );
message.setContent( part );
}
message.saveChanges();
Transport transport = session.getTransport("smtp");
transport.connect();
transport.sendMessage( message, message.getAllRecipients() );
transport.close();
} catch( Exception mex ) {
logger.error( "There was an problem emailing: " + subject + " to: " + to, mex );
}
}
private BodyPart createBody(String text, String mimetype) throws MessagingException {
MimeBodyPart body = new MimeBodyPart();
body.setContent( text, mimetype );
return body;
}
private String renderTemplate( String template ) throws Exception {
StringWriter writer = new StringWriter();
Template tmpl = engine.getTemplate( template );
VelocityContext context = new VelocityContext( params );
tmpl.merge( context, writer );
return writer.toString();
}
}
public static void main(String[] args) throws Exception {
Properties p = new Properties();
p.setProperty("file.resource.loader.path", "./templates");
VelocityEngine engine = new VelocityEngine( p );
engine.init();
String from = "author@yourcompany.com";
Properties javamailProps = new Properties();
javamailProps.setProperty("mail.host", "!YOUR_MAIL_HOST");
javamailProps.setProperty("mail.user", "!YOUR_MAIL_USER");
javamailProps.setProperty("mail.password", "!YOUR_MAIL_PASSWORD");
javamailProps.setProperty("mail.port","25");
Emailer emailer = new Emailer( engine, javamailProps, from );
emailer.email("person@somedomain.com", "This is a test", "test.vm")
.bind("name", "Chuck")
.bind("password","KutzMutz")
.bind("interestingPlaces","Tahiti")
.send();
System.out.println("Email sent!");
}
}