About us
Contact us
Products
Services
Articles
 Twitter    Bookmark and Share

13. Form Mailer

Contents | Previous Chapter | Next Chapter

This chapter describes the FormMailer servlet, which inherits the request handling mechanism from the GenericHandler class of com.devsphere.helpers.mapping and defines the process() method so that the valid bean objects are converted to text. The obtained text is emailed to one or more addresses.


13.1. Initialization

A significant part of the servlet's class initializes the email parameters, which are searched among the bean's resources:

[FormMailer.RESPONSE_PAGE]=...
[FormMailer.ERROR_PAGE]= ...
[FormMailer.EMAIL_SUBJECT]= ...
[FormMailer.EMAIL_FROM_PERSON]= ...
[FormMailer.EMAIL_FROM]= ...
[FormMailer.EMAIL_REPLAY_TO]= ...
[FormMailer.EMAIL_TO]= ...
[FormMailer.EMAIL_CC]= ...
[FormMailer.EMAIL_BCC]= ...
[FormMailer.EMAIL_CHARSET]= ...

The name of a SMTP host must be provided as init parameter. The BEAN_NAME and the BASE_PATH parameters used by GenericHandler must be present too.


13.2. Bean Processing

The processing is split in two parts sendBean() and sendResponse().

The sendBean() method converts the valid bean to text, builds the email message, connects to the SMTP host if necessary, and tries to send the message using JavaMail. If the sending fails, sendBean() tries to open a new connection. This makes possible the recovering from a crash of the SMTP server. The method returns true if the sending operation succeeds.

The sendResponse() method uses the value returned by sendBean() to decide whether a normal response page or an error page should be send to the user's browser.

FormMailer.java:

package com.devsphere.apps.mapping.mailer;

import com.devsphere.helpers.mapping.*;
import com.devsphere.mapping.*;
import com.devsphere.logging.*;

import javax.mail.*;
import javax.mail.internet.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;

/**
 * Handler that implements the process() method of GenericHandler
 * so that valid beans are mapped to text properties and e-mailed
 * to one or more addresses.
 *
 * Init parameters:
 *     BEAN_NAME - the name of the bean class
 *     BASE_PATH - the virtual folder of the HTML form
 *     ALLOW_MULTIPART - if true, the use of "multipart/form-data" is allowed
 *     LOG_SOCKET_EXCEPTION - if true, any java.net.SocketException,
 *         which occurs during the sending of the HTML response, is logged
 *     HOST - the name of the SMTP host
 *     PROTOCOL - the used protocol, normally "smtp"
 *     DEBUG_MAIL_SESSION - debugging flag
 *     BUFFER_SIZE - the size of the buffer used to send the response
 */
public class FormMailer extends GenericHandler {
    public static final String MAILER_RESOURCES
        = "com.devsphere.apps.mapping.mailer.MailerResources";
    public static final int DEFAULT_BUFFER_SIZE = 1024;
    public static final boolean DEFAULT_DEBUG_MAIL_SESSION = false;
    public static final String DEFAULT_PROTOCOL = "smtp";
    public static final String HOST_PARAM = "HOST";
    public static final String PROTOCOL_PARAM = "PROTOCOL";
    public static final String DEBUG_MAIL_SESSION_PARAM = "DEBUG_MAIL_SESSION";
    public static final String BUFFER_SIZE_PARAM = "BUFFER_SIZE";
    public static final String RESPONSE_PAGE_KEY = "[FormMailer.RESPONSE_PAGE]";
    public static final String ERROR_PAGE_KEY = "[FormMailer.ERROR_PAGE]";
    public static final String EMAIL_SUBJECT_KEY = "[FormMailer.EMAIL_SUBJECT]";
    public static final String EMAIL_FROM_PERSON_KEY
        = "[FormMailer.EMAIL_FROM_PERSON]";
    public static final String EMAIL_FROM_KEY = "[FormMailer.EMAIL_FROM]";
    public static final String EMAIL_REPLAY_TO_KEY
        = "[FormMailer.EMAIL_REPLAY_TO]";
    public static final String EMAIL_TO_KEY = "[FormMailer.EMAIL_TO]";
    public static final String EMAIL_CC_KEY = "[FormMailer.EMAIL_CC]";
    public static final String EMAIL_BCC_KEY = "[FormMailer.EMAIL_BCC]";
    public static final String EMAIL_CHARSET_KEY = "[FormMailer.EMAIL_CHARSET]";
    protected ResourceBundle mailerRes;
    protected Session session;
    protected Transport transport;
    protected String host;
    protected String protocol;
    protected boolean debug;
    protected int bufferSize;
    protected String responsePage;
    protected String errorPage;
    protected String emailSubject;
    protected Address emailFrom;
    protected Address emailReplyTo[];
    protected Address emailTo[];
    protected Address emailCc[];
    protected Address emailBcc[];
    protected String emailCharset;

//* INITIALIZATION

    /**
     * Initializes the servlet
     */
    protected void initialize() throws ServletException {
        super.initialize();

        // Get the mailer resources
        try {
            mailerRes = ResourceBundle.getBundle(MAILER_RESOURCES);
        } catch (MissingResourceException e) {
            logger.log(e);
            throw new ServletException(e.getMessage());
        }

        // Add the mailer resources to errorHandler
        errorHandler.addResources(mailerRes);

        // Get the host parameter
        host = getInitParameter(HOST_PARAM);
        if (host == null || host.length() == 0)
            errorHandler.fatalError("[MISSING_INIT_PARAM]", null, HOST_PARAM);

        // Get the protocol parameter
        protocol = getInitParameter(PROTOCOL_PARAM);
        if (protocol == null || protocol.length() == 0)
            protocol = DEFAULT_PROTOCOL;

        // Get the debug parameter
        debug = DEFAULT_DEBUG_MAIL_SESSION;
        String debugParam = getInitParameter(DEBUG_MAIL_SESSION_PARAM);
        if (debugParam != null)
            debug = Boolean.valueOf(debugParam).booleanValue();

        // Get the buffer size parameter
        bufferSize = DEFAULT_BUFFER_SIZE;
        String bufferSizeParam = getInitParameter(BUFFER_SIZE_PARAM);
        if (bufferSizeParam != null)
            try {
                bufferSize = Integer.parseInt(bufferSizeParam);
            } catch (NumberFormatException e) {
                errorHandler.fatalError("[INVALID_INIT_PARAM]", null,
                    BUFFER_SIZE_PARAM, bufferSizeParam);
            }

        // Get the name of the HTML response file
        responsePage = getBeanResourceAsString(RESPONSE_PAGE_KEY, false).trim();

        // Get the name of the HTML file that reports an error
        errorPage = getBeanResourceAsString(ERROR_PAGE_KEY, false).trim();

        // Get the e-mail subject
        emailSubject = getBeanResourceAsString(EMAIL_SUBJECT_KEY, true);

        // Get the e-mail from address
        String emailFromPerson
            = getBeanResourceAsString(EMAIL_FROM_PERSON_KEY, true);
        String emailFromAddress
            = getBeanResourceAsString(EMAIL_FROM_KEY, true).trim();
        emailFrom = getInternetAddress(emailFromAddress, emailFromPerson);

        // Get the e-mail replay-to addresses
        emailReplyTo = getAddressList(EMAIL_REPLAY_TO_KEY);

        // Get the e-mail to addresses
        emailTo = getAddressList(EMAIL_TO_KEY);

        // Get the e-mail cc addresses
        emailCc = getAddressList(EMAIL_CC_KEY);

        // Get the e-mail bcc addresses
        emailBcc = getAddressList(EMAIL_BCC_KEY);

        // Get the e-mail charset
        emailCharset = getBeanResourceAsString(EMAIL_CHARSET_KEY, true);

        // Create the session's properties
        Properties props = new Properties();
        props.put("mail." + protocol + ".host", host);
        if (debug)
            props.put("mail.debug", "true");

        // Get the session object
        session = Session.getInstance(props, null);
        session.setDebug(debug);

        // Get the transport object
        try {
            transport = session.getTransport(protocol);
        } catch (NoSuchProviderException e) {
            errorHandler.fatalError("[NO_PROTOCOL_PROVIDER]", e, protocol);
        }
        if (transport == null)
            errorHandler.fatalError("[COULDNT_GET_TRANSPORT]", null, protocol);
    }

    /**
     * Gets an InternetAddress object
     */
    protected InternetAddress getInternetAddress(
        String emailAddress, String emailPerson) throws ServletException {
        if (emailAddress.length() > 0)
            if (emailPerson != null && emailPerson.length() > 0)
                try {
                    return new InternetAddress(emailAddress, emailPerson);
                } catch (UnsupportedEncodingException e) {
                    errorHandler.fatalError("[UNSUPPORTED_ENCODING]", e,
                        emailPerson, emailAddress);
                } else
                try {
                    return new InternetAddress(emailAddress);
                } catch (AddressException e) {
                    errorHandler.fatalError("[WRONGLY_FORMATTED_ADDRESS]", e,
                        emailAddress);
                }
        return null;
    }

    /**
     * Gets a list of addresses
     */
    protected Address[] getAddressList(String key) throws ServletException {
        // Get the list as a String array
        String addressArray[] = null;
        Object obj = getBeanResourceAsStringOrStringArray(key, true);
        if (obj instanceof String[])
            addressArray = (String[]) obj;
        else {
            Vector addressVector = new Vector();
            StringTokenizer stk = new StringTokenizer((String) obj);
            while (stk.hasMoreTokens()) {
                String token = stk.nextToken();
                addressVector.addElement(token);
            }
            addressArray = new String[addressVector.size()];
            for (int i = 0; i < addressArray.length; i++)
                addressArray[i] = (String) addressVector.elementAt(i);
        }

        // Create the array of InternetAddress objects
        int length = addressArray.length;
        if (length == 0)
            return null;
        Address list[] = new Address[length];
        for (int i = 0; i < length; i++)
            list[i] = getInternetAddress(addressArray[i], null);
        return list;
    }

    /**
     * Warning about some addresses
     */
    protected void addressWarning(String key, Address list[]) {
        if (list != null && list.length > 0) {
            StringBuffer buf = new StringBuffer();
            for (int i = 0; i < list.length; i++)
                buf.append("  ").append(list[i].toString());
            errorHandler.warning(key, new String[] { buf.toString() });
        }
    }

//* PROCESSING

    /**
     * Converts a bean to text and tries to e-mail it.
     * Returns true if the operation is successful
     */
    protected boolean sendBean(Object beanObject) {
        // Bean-to-text mapping
        String text = null;
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            try {
                TextUtils.beanToText(beanObject, null, out, logger);
            } finally {
                out.close();
            }
            text = out.toString(CharacterEncoding.getJavaName(
                CharacterEncoding.ASCII));
        } catch (IOException e) {
            errorHandler.error("[COULDNT_MAP_BEAN_TO_TEXT]", e);
            return false;
        }

        // Build the message
        MimeMessage msg = new MimeMessage(session);
        try {
            if (emailSubject.length() > 0)
                msg.setSubject(emailSubject);
            if (emailFrom != null)
                msg.setFrom(emailFrom);
            if (emailReplyTo != null)
                msg.setReplyTo(emailReplyTo);
            if (emailTo != null)
                msg.setRecipients(Message.RecipientType.TO, emailTo);
            if (emailCc != null)
                msg.setRecipients(Message.RecipientType.CC, emailCc);
            if (emailBcc != null)
                msg.setRecipients(Message.RecipientType.BCC, emailBcc);
            if (emailCharset.length() > 0)
                msg.setText(text, emailCharset);
            else
                msg.setText(text);
            msg.setSentDate(new Date());
        } catch (MessagingException e) {
            errorHandler.error("[COULDNT_BUILD_MESSAGE]", e);
            return false;
        }

        synchronized (this) {
            if (!transport.isConnected())
                // Connect to the host
                try {
                    transport.connect();
                } catch (MessagingException e) {
                    errorHandler.error("[COULDNT_CONNECT]", e, host);
                    return false;
                }

            // Send the message
            try {
                Address addressList[] = msg.getAllRecipients();
                if (addressList != null && addressList.length > 0)
                    transport.sendMessage(msg, addressList);
                else {
                    errorHandler.warning("[NO_RECIPIENTS]");
                    return false;
                }
            } catch (MessagingException e) {
                boolean retryFlag = true;

                // Log the error
                synchronized (logger) {
                    errorHandler.error("[COULDNT_SEND_MESSAGE]", e);
                    if (e instanceof SendFailedException) {
                        SendFailedException sfe = (SendFailedException) e;
                        Address[] invalid = sfe.getInvalidAddresses();
                        addressWarning("[INVALID_ADDRESSES]", invalid);
                        Address[] validUnsent = sfe.getValidUnsentAddresses();
                        addressWarning("[VALID_UNSENT_ADDRESSES]", validUnsent);
                        Address[] validSent = sfe.getValidSentAddresses();
                        addressWarning("[VALID_SENT_ADDRESSES]", validSent);
                        if (invalid != null && invalid.length > 0)
                            retryFlag = false; // there is no point to retry
                    }
                }

                // Retry with a new transport object. Don't log any new errors.
                if (retryFlag) {
                    Transport newTransport = null;
                    try {
                        // Try to resend the message using a new connection
                        newTransport = session.getTransport(protocol);
                        newTransport.connect();
                        newTransport.sendMessage(msg, msg.getAllRecipients());

                        // Try to close the old transport
                        try {
                            transport.close();
                        } catch (Exception t) {
                        }

                        // The new transport object worked
                        transport = newTransport;
                        return true;
                    } catch (Exception t) {
                        // The new transport failed too. Try to close it
                        if (newTransport != null)
                            try {
                                newTransport.close();
                            } catch (Exception t2) {
                            }
                    }
                }
                return false;
            }
        }
        return true;
    }

    /**
     * Sends an HTML response to the user's browser
     */
    protected void sendResponse(HttpServletResponse response, boolean ok)
        throws ServletException, IOException {
        // Get the path of the response page
        String respPath = getFilePath(ok ? responsePage : errorPage);
        File file = new File(respPath);
        if (!file.exists())
            errorHandler.fatalError("[PAGE_NOT_FOUND]", null, new String[] {
                ok ? "Response" : "Error", respPath, BASE_PATH_PARAM,
                ok ? RESPONSE_PAGE_KEY : ERROR_PAGE_KEY } );

        // Set content-type
        String contentType = getServletContext().getMimeType(respPath);
        if (contentType != null)
            response.setContentType(contentType);

        // Send the HTML content
        FileInputStream in = new FileInputStream(respPath);
        try {
            OutputStream out = response.getOutputStream();
            byte buf[] = new byte[bufferSize];
            while (true) {
                int count = in.read(buf);
                if (count == -1)
                    break;
                out.write(buf, 0, count);
            }
        } finally {
            in.close();
        }
    }

    /**
     * Processes a valid bean objects
     */
    protected void process(HandlingContext handlingContext,
        Object beanObject) throws ServletException, IOException {
        boolean ok = sendBean(beanObject);
        sendResponse(handlingContext.getResponse(), ok);
    }

//* SHUTDOWN

    /**
     * Closes the connection to the SMTP host
     */
    public void destroy() {
        synchronized (this) {
            try {
                if (transport != null)
                    transport.close();
            } catch (MessagingException e) {
                errorHandler.error("[COULDNT_CLOSE_CONNECTION]", e, host);
            }
        }
        super.destroy();
    }

}

MailerResources.properties:

# FormMailer

[NO_PROTOCOL_PROVIDER]=No provider for the {0} protocol
[COULDNT_GET_TRANSPORT]=Couldn''t get the transport object for the {0} protocol
[COULDNT_CONNECT]=Couldn''t connect to {0}
[UNSUPPORTED_ENCODING]=Unsupported encoding of {0}, {1}
[WRONGLY_FORMATTED_ADDRESS]=Wrongly formatted address\: {0}
[COULDNT_MAP_BEAN_TO_TEXT]=Couldn''t map the bean object to text
[COULDNT_BUILD_MESSAGE]=Couldn''t build the message object
[NO_RECIPIENTS]=No TO/CC/BCC recipients
[COULDNT_SEND_MESSAGE]=Couldn''t send the e-mail message
[INVALID_ADDRESSES]=Invalid addresses\: {0}
[VALID_UNSENT_ADDRESSES]=Valid unsent addresses\: {0}
[VALID_SENT_ADDRESSES]=Valid sent addresses\: {0}
[PAGE_NOT_FOUND]={0} page not found\: {1} \
    \nVerify the {2} init parameter and the {3} bean resource
[COULDNT_CLOSE_CONNECTION]=Couldn''t close the connection to {0}

13.3. Hints and Tips

A previous chapter describes the GenericHandler servlet and its error handling mechanism.

A later example uses the FormMailer servlet.

Contents | Previous Chapter | Next Chapter

Copyright © 2000-2009 Devsphere

About us
Contact us
Products
Services
Articles