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.
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.
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}
A previous chapter describes the GenericHandler servlet and its error handling mechanism.
A later example uses the FormMailer servlet.
|