Sunday, May 22, 2011

How To Install The MySQL Gem On Mac OS

For my latest ruby on rails project I had to install the MySQL gem on my laptop with Mac OS 10.6.
I had some trouble with this. Finally the following command did the trick:

gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config

Update: For rails 3 it is advisable to use the mysql2 gem.

Sunday, May 15, 2011

How To Send Emails With The Lift Framework

The Lift Framework has built in support for sending emails by the Mailer class. Lift leverages the Java Mail API to send mails.

If you want to use Mailer in your application (for example for user registration via MegaProtoUser), Lift needs to know, which smtp server to use for sending mails. By default it tries to use a mail server on localhost, port 25. If you want to use a different server or if your mail server requires password authentication, you have to provide configuration values for Mailer.

There are several ways to configure the Mailing System:
  • Use system properties
  • Use Lift properties
  • Use a mail session from the JNDI context of your container

Configuring Mailer by system properties

To keep things short: I do not recommend this. Use Lift properties. They are way more flexible.

Configuring Mailer by Lift properties

Lift properties allow you to provide configuration values for different run modes of your application. You can provide different values for development, test, staging, pilot, production or profile mode.

To configure Mailer for development, simply put this

mail.smtp.host={your mail host here}

into src/main/resources/default.props. Production values can be provided in the file production.default.props. In general the naming scheme for Lift property files is modeName.userName.hostName.props with hostName and userName being optional. For development, the modeName is empty.

The port of your SMTP host can be specified by the property mail.smpt.port.
Usually you do not have to change this, since 25 is the default port for SMTP servers.

During development it can be useful to enable the debug mode for the mailing system. This can be done by setting mail.debug=true in your props file.

If your mailserver requires password authentication, some extra work needs to be done. You need to provide the username and the password in the configuration file:

mail.user={your username here}
mail.password={your password here}

In Boot.scala you need to create an authenticator:

...
import javax.mail.{ Authenticator, PasswordAuthentication }
import net.liftweb.util.{ Mailer, Props }
...

class Boot {
  def boot {
    ...  
    Mailer.authenticator = (Props.get("mail.user"), Props.get("mail.password")) match {
      case (Full(u), Full(p)) => Full(Authenticator {
        override def getPasswordAuthentication = new javax.mail.PasswordAuthentication(u, p)
      })
      case _ => Empty
    }
    ...
  }
}

This sets up password authentication for your mail session. The drawback of this solution is that you have to provide your SMTP credentials in your configuration files.


Use a mail session from the JNDI context of your container

Your Lift application runs in a servlet container like Jetty or Tomcat. Servlet containers allow you to create mail sessions in the JNDI context of your container. This means the servlet container handles the creation of the mail session and the application simply requests the pre-configured session from the container.

To create a mail session in Jetty, put this into your jetty.xml:

<configure class="org.mortbay.jetty.webapp.WebAppContext" id="wac">
...
<new class="org.mortbay.jetty.plus.naming.Resource" id="mail">
     <arg><ref id="wac"></ref></arg>
     <arg>mail/Session</arg>
     <arg>
       <new class="org.mortbay.naming.factories.MailSessionReference">
         <set name="user">YOUR_USERNAME</set>
         <set name="password">OBF:2xmk4B261z0f3f1c1xsR</set>
         <set name="properties">
           <new class="java.util.Properties">
             <put name="mail.smtp.host">YOUR_MAIL_HOST</put>
             <put name="mail.debug">true</put>
           </new>
          </set>
       </new>
     </arg>
</new>
</configure>

mail/Session is the name of the mail session, that can be used to reference the session from your application.

This example uses jetty's password obfuscation feature to avoid plain text paswords in your configuration files. More information about configuring JNDI mail sessions in jetty can be found here.

To use the session in your application, put the following code into your web.xml:

<resource-ref>
  <description>Mailer Session</description>
  <res-ref-name>mail/Session</res-ref-name>
  <res-type>javax.mail.Session</res-type>
  <res-auth>Container</res-auth>
</resource-ref>

This code tells your application, that a javax.mail.Session is available under the name mail/session. The res-auth Container part says, that the container handles authentication for this resource.

To configure Lift's mailer with this mail session, simply put the following code into your Boot.scala:

...
import net.liftweb.util. Mailer
...

class Boot {
  def boot {
    ...  
    Mailer.jndiName = Full("mail/Session")
    ...
  }
}

There is one last thing you have to do in order to get JNDI mail sessions with Lift Mailer working:

If you configure a mail JNDI-session in your servlet container, the mail session is created with classes loaded form the mail.jar in your container. If your application also includes mail.jar, you will run into classpath issues and Mailer will not work. Because of this, you will have to exclude the transitive dependencies on mail.jar declared by some lift modules as lift-proto, lift-mapper and lift webkit.

If you are using sbt, the following code in your Project.scala excludes the transitive dependency of lift-mapper to mail.jar from your project. The dependency on mail.jar with scope "provided" includes mail.jar into your classpath during development but the jar will not be included in your war file.

override def ivyXML =
    <dependencies>  
      <dependency name="lift-mapper_2.8.1" org="net.liftweb" rev="{liftVersion}"> 
        <exclude name="mail" org="javax.mail">
        </exclude>
      </dependency>
    </dependencies>  

  val mail = "javax.mail" % "mail" % "1.4" % "provided->default"

The line declaring the original dependency on lift-mapper has to be removed.

If you are using maven, the following code in you pom.xml excludes the transitive dependencies on mail.jar:

<dependency> 
    <groupid>javax.mail</groupid> 
    <artifactid>mail</artifactid> 
    <version>1.4.1</version> 
    <scope>provided</scope> 
</dependency> 
<dependency> 
  <groupid>net.liftweb</groupid> 
  <artifactid>lift-mapper_2.7.7</artifactid> 
  <version>2.1-SNAPSHOT</version> 
  <exclusions> 
    <exclusion> 
        <groupid>javax.mail</groupid> 
        <artifactid>mail</artifactid> 
    </exclusion> 
  </exclusions> 
</dependency> 


After this configuration change you have to run the commands reload and update in sbt.

Now Mailer should be working with the JNDI mail session.

Using JNDI mail sessions with sbt

Since sbt version 0.7.5 you can also use JNDI mail sessions when running jetty from sbt. To use this feature, you have to declare the following dependencies in your project definition.

val jetty6 = "org.mortbay.jetty" % "jetty" % "6.1.22" % "test->default"  
  val jetty6plus = "org.mortbay.jetty" % "jetty-plus" % "6.1.22" % "test->default"  
  val jetty6naming = "org.mortbay.jetty" % "jetty-naming" % "6.1.22" % "test->default"  

Then specify the path to your jetty-configuration:

override def jettyEnvXml = Some( 
  (sourcePath / "main" / "jetty" / "jetty-env.xml").asFile

Now you can put your JNDI-configuration into this file.