Thursday, April 16, 2009

Contact Me application with Google Apps

Blogosphere is buzzing with Google App Engine for Java (GAEJ), so thought to give it a try. Google has put decent documentation to start with. Any Java web developer can start straight away with it. Only learning curve is to know the limitations of GAEJ. I would not talk about the features and limitations of it as there is lot of information is made available by Google and the people who dissected it before me :-)

I wanted to have a small application having contact form, which I wanted to put on my blog instead of displaying the email address. Here is the html / jsp page having contact form, gathering name, email, subject and the message-

<%@ taglib prefix='c' uri='http://java.sun.com/jstl/core'%>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <title>Contact Me</title>
</head>

<body>
<h1>Contact Me!</h1>
<!-- Show error message, if any. -->
<font color='red'><c:out value="${requestScope.errorMessage}" escapeXml="false" /></font>

<form action="contactme" method="post">
<table>
    <tr>
        <td><label for="name">Name : </label></td>
        <td><input type="text" size="40" maxlength="40" title="Name" id="name" name="name" value="<c:out value='${param.name}' />" /></td>
    </tr>
    <tr>
        <td><label for="email">Email : </label></td>
        <td><input type="text" size="40" maxlength="60" title="Email" id="email" name="email" value="<c:out value='${param.email}' />" /></td>
    </tr>
    <tr>
        <td><label for="subject">Subject : </label></td>
        <td><input type="text" size="40" maxlength="80" title="Subject" id="subject" name="subject" value="<c:out value='${param.subject}' />" /></td>
    </tr>
    <tr>
        <td><label for="message" style="vertical-align: text-top">Message : </label></td>
        <td><textarea rows="10" cols="60" id="message" name="message" ><c:out value='${param.message}' /></textarea></td>
    </tr>
    <tr>
        <td colspan="2"><input type="submit" title="Submit" value="Submit">
        <br /> All fields are required.</td>
    </tr>
</table>
</form>
</body>
</html>

This is the same html / jsp, which we are using for several years. Okay, now here is the servlet backing this form-
package com.vinodsingh.gae.contact;

import java.io.IOException;
import java.util.Map;
import java.util.Properties;
import java.util.Map.Entry;

import javax.mail.Address;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.appengine.repackaged.org.apache.commons.logging.Log;
import com.google.appengine.repackaged.org.apache.commons.logging.LogFactory;

public class ContactMeServlet extends HttpServlet {

    private Log log = LogFactory.getLog(getClass());

    public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        try {
            if (validate(req, resp)) {
                sendMail(req, resp);
                req.setAttribute("name", req.getParameter("name"));
                req.getRequestDispatcher("/thanks.jsp").forward(req, resp);
            } else {
                req.getRequestDispatcher("/index.jsp").forward(req, resp);
            }
        } catch (Exception e) {
            log.error("Error while processing contact request", e);
        }
    }

    // do very basic validation
    @SuppressWarnings("unchecked")
    private boolean validate(HttpServletRequest req, HttpServletResponse resp) {
        StringBuilder errorMessage = new StringBuilder();
        Map<String, String[]> params = req.getParameterMap();

        for (Entry<String, String[]> param : params.entrySet()) {
            String key = param.getKey();
            String value = param.getValue()[0];

            if ("name".equals(key))
                if (value.length() < 5)
                    errorMessage.append("Please enter a valid name.\n");

            if ("email".equals(key)) {
                if (value.length() < 12)
                    errorMessage.append("Please enter a valid email address.<br />");
                else if (value.indexOf('@') < 1)
                    errorMessage.append("Please enter a valid email address.<br />");
            }

            if ("subject".equals(key))
                if (value.length() < 15)
                    errorMessage.append("Please write a subject of at least 15 characters.<br />");

            if ("message".equals(key))
                if (value.length() < 50)
                    errorMessage.append("Please write a message of at least 50 characters.<br />");
        }

        if (errorMessage.length() > 0) {
            req.setAttribute("errorMessage", errorMessage.toString());
            return false;
        }

        return true;
    }

    // Send email
    private void sendMail(HttpServletRequest req, HttpServletResponse resp) {
        Session session = Session.getDefaultInstance(new Properties(), null);

        try {
            log.info("Got a contact request from: " + req.getParameter("email") + ", " + req.getParameter("name"));
            Message msg = new MimeMessage(session);
            msg.setFrom(new InternetAddress("me@mydomain.com", req.getParameter("name")));
            msg.setReplyTo(new Address[] { new InternetAddress(req.getParameter("email"), req.getParameter("name")) });

            // send email to both myself and the sender
            msg.addRecipient(Message.RecipientType.TO, new InternetAddress(req.getParameter("email"), req
                    .getParameter("name")));
            msg.addRecipient(Message.RecipientType.CC, new InternetAddress("me@mydomain.com", "Vinod Singh"));
            msg.setSubject(req.getParameter("subject"));
            msg.setText(req.getParameter("message"));
            Transport.send(msg);

        } catch (Exception e) {
            log.error("Error while sending emai", e);
        }
    }
}
Here only unusual thing is the mail sending part, where we do not need to have the SMTP thing to send the email, GAE will use application owner's email instead. All is well till now.

So it is the time to deploy the application. BOOM, deploy is failed miserably with this error-

Unable to upload:
java.lang.IllegalStateException: cannot find javac executable based on java.home, tried "C:\Program Files\Java\jre6\bin\javac.exe" and "C:\Program Files\Java\bin\javac.exe"
at com.google.appengine.tools.admin.AppAdminFactory$ApplicationProcessingOptions.getJavaCompiler(AppAdminFactory.java:325)
at com.google.appengine.tools.admin.Application.compileJavaFiles(Application.java:340)
at com.google.appengine.tools.admin.Application.compileJsps(Application.java:326)
at com.google.appengine.tools.admin.Application.createStagingDirectory(Application.java:235)
at com.google.appengine.tools.admin.AppAdminImpl.update(AppAdminImpl.java:39)
at com.google.appengine.tools.admin.AppCfg$UpdateAction.execute(AppCfg.java:469)
at com.google.appengine.tools.admin.AppCfg.<init>(AppCfg.java:114)
at com.google.appengine.tools.admin.AppCfg.main(AppCfg.java:59)


I have Java Development Kit 1.5.x and 1.6.x along with one more JRE on my machine (Vista), still GAE SDK can't find the 'javac' :-( This issue is logged here. Removing all JREs and keeping just the JDK makes it work. Okay finally my application is up and running and ready to receive the contact requests. I tried it myself and it worked as expected. As I was adding the contact person's email address in 'Reply-to' field, so I was expecting to GMail to fill that in 'TO' address on hitting the 'Reply' button. Here I came to know that GMail discards 'Reply-to' field altogether. Anyway this GMail issue has nothing to do with GAE.

GAE for Java looks a good step in adopting the Java for hobby. Before GAE if I wanted to create an application in java just for hobby, there was no hosting solution available at reasonable prices. While there are numerous options for PHP. I believe this will have a good impact on Java but if some of the restrictions removed as they do not conform to WORA concept of Java.

0 Comments: