Recepción de correo electrónico

Los mensajes de correo electrónico se envían a tu aplicación como solicitudes HTTP. Para procesar los mensajes de correo entrantes, debe asociar direcciones de correo electrónico con servlets en la configuración de su aplicación y, a continuación, incluir el código del servlet en su aplicación. El correo entrante genera solicitudes HTTP que se transfieren a los servlets correspondientes para que las gestionen.

Configurar tu aplicación para recibir correos

Cuando creas una aplicación, el correo entrante está inhabilitado de forma predeterminada. Si no habilitas explícitamente los mensajes de correo entrantes, se ignorarán los que se envíen a tu aplicación.

Para habilitar el servicio de correo entrante, modifica los archivos de configuración appengine-web.xml y web.xml:

Habilitar el correo en appengine-web.xml

Modifica appengine-web.xml añadiendo una sección inbound-services que habilite el servicio de correo entrante:

<inbound-services>
  <!-- Used to handle incoming mail. -->
  <service>mail</service>
  <!-- Used to handle bounced mail notifications. -->
  <service>mail_bounce</service>
</inbound-services>

Los mensajes de correo electrónico se envían a tu aplicación como solicitudes POST HTTP mediante la siguiente URL:

/_ah/mail/<ADDRESS>

donde <ADDRESS> es una dirección de correo completa, incluido el nombre de dominio. Ten en cuenta que, aunque tu aplicación se haya desplegado en un dominio personalizado, no podrá recibir correos enviados a direcciones de ese dominio.

Habilitar el correo en web.xml

Modifica web.xml asignando URLs de correo a servlets:

<servlet>
  <servlet-name>mailhandler</servlet-name>
  <servlet-class>com.example.appengine.mail.MailHandlerServlet</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>mailhandler</servlet-name>
  <url-pattern>/_ah/mail/*</url-pattern>
</servlet-mapping>
<security-constraint>
  <web-resource-collection>
    <web-resource-name>mail</web-resource-name>
    <url-pattern>/_ah/mail/*</url-pattern>
  </web-resource-collection>
  <auth-constraint>
    <role-name>admin</role-name>
  </auth-constraint>
</security-constraint>

En los fragmentos de código anteriores, /_ah/mail/* coincide con todos los correos dirigidos a la aplicación. Los servlets de correo se ejecutan en la versión de la aplicación que se está usando en App Engine.

Envío de mensajes entrantes basado en patrones

Si tu aplicación usa la coincidencia de patrones, te recomendamos que uses un enfoque basado en filtros con los siguientes fragmentos de código.

Gestor de hormigón

public class HandleDiscussionEmail extends MailHandlerBase {

  private static final Logger log = Logger.getLogger(HandleDiscussionEmail.class.getName());
  public HandleDiscussionEmail() { super("discuss-(.*)@(.*)"); }

  @Override
  protected boolean processMessage(HttpServletRequest req, HttpServletResponse res)
    throws ServletException
  {
    log.info("Received e-mail sent to discuss list.");
    MimeMessage msg = getMessageFromRequest(req);
    Matcher match = getMatcherFromRequest(req);
    // ...
    return true;
  }
}

El controlador concreto anterior se registra con el siguiente fragmento de código en web.xml:

<filter>
  <filter-name>HandleDiscussionEmail</filter-name>
  <filter-class>com.example.appengine.mail.HandleDiscussionEmail</filter-class>
</filter>
<filter-mapping>
  <filter-name>HandleDiscussionEmail</filter-name>
  <url-pattern>/_ah/mail/*</url-pattern>
</filter-mapping>

Ten en cuenta que las directivas security-constraint no se pueden usar en filtros, por lo que las políticas de seguridad del controlador se tendrán que introducir de otra forma.

Gestor abstracto

public abstract class MailHandlerBase implements Filter {

  private Pattern pattern = null;

  protected MailHandlerBase(String pattern) {
    if (pattern == null || pattern.trim().length() == 0)
    {
      throw new IllegalArgumentException("Expected non-empty regular expression");
    }
    this.pattern = Pattern.compile("/_ah/mail/"+pattern);
  }

  @Override public void init(FilterConfig config) throws ServletException { }

  @Override public void destroy() { }

  /**
   * Process the message. A message will only be passed to this method
   * if the servletPath of the message (typically the recipient for
   * appengine) satisfies the pattern passed to the constructor. If
   * the implementation returns false, control is passed
   * to the next filter in the chain. If the implementation returns
   * true, the filter chain is terminated.
   *
   * The Matcher for the pattern can be retrieved via
   * getMatcherFromRequest (e.g. if groups are used in the pattern).
   */
  protected abstract boolean processMessage(HttpServletRequest req, HttpServletResponse res) throws ServletException;

  @Override
  public void doFilter(ServletRequest sreq, ServletResponse sres, FilterChain chain)
      throws IOException, ServletException {

    HttpServletRequest req = (HttpServletRequest) sreq;
    HttpServletResponse res = (HttpServletResponse) sres;

    MimeMessage message = getMessageFromRequest(req);
    Matcher m = applyPattern(req);

    if (m != null && processMessage(req, res)) {
      return;
    }

    chain.doFilter(req, res); // Try the next one

  }

  private Matcher applyPattern(HttpServletRequest req) {
    Matcher m = pattern.matcher(req.getServletPath());
    if (!m.matches()) m = null;

    req.setAttribute("matcher", m);
    return m;
  }

  protected Matcher getMatcherFromRequest(ServletRequest req) {
    return (Matcher) req.getAttribute("matcher");
  }

  protected MimeMessage getMessageFromRequest(ServletRequest req) throws ServletException {
    MimeMessage message = (MimeMessage) req.getAttribute("mimeMessage");
    if (message == null) {
      try {
        Properties props = new Properties();
        Session session = Session.getDefaultInstance(props, null);
        message = new MimeMessage(session, req.getInputStream());
        req.setAttribute("mimeMessage", message);

      } catch (MessagingException e) {
        throw new ServletException("Error processing inbound message", e);
      } catch (IOException e) {
        throw new ServletException("Error processing inbound message", e);
      }
    }
    return message;
  }
}

Gestionar el correo entrante

La API JavaMail incluye la clase MimeMessage, que puedes usar para analizar los mensajes de correo entrantes. MimeMessage tiene un constructor que acepta un java.io.InputStream y una sesión de JavaMail, que puede tener una configuración vacía.

Crea una instancia de MimeMessage de esta forma:

import java.io.IOException;
import java.util.logging.Logger;
import java.util.Properties;

import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.internet.MimeMessage;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MailHandlerServlet extends HttpServlet {

  private static final Logger log = Logger.getLogger(MailHandlerServlet.class.getName());

  @Override
  public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    Properties props = new Properties();
    Session session = Session.getDefaultInstance(props, null);
    try {
      MimeMessage message = new MimeMessage(session, req.getInputStream());
      log.info("Received mail message.");
    } catch (MessagingException e) {
      // ...
    }
    // ...
  }
}

A continuación, puedes usar varios métodos para analizar el objeto message:

  • Llama a getFrom() para devolver el remitente del mensaje.
  • Llama a getContentType() para extraer el tipo de contenido del mensaje. El método getContent() devuelve un objeto que implementa la interfaz Multipart.
  • Llama al getCount() para determinar el número de piezas
  • Llama a getBodyPart(int index) para devolver una parte del cuerpo concreta.

Una vez que hayas configurado tu aplicación para gestionar el correo entrante, puedes usar la consola del servidor de desarrollo para simular mensajes de correo entrantes. Para obtener más información, incluido cómo iniciar el servidor de desarrollo, consulta Servidor de desarrollo de Java. Después de iniciar la aplicación en el servidor de desarrollo local, puedes acceder a ella a través de la URL http://localhost:8888/_ah/admin/. Si no usas el puerto predeterminado del servidor de desarrollo local, sustituye el valor 8888 por el puerto que estés usando.

En el servidor de desarrollo, haz clic en Correo entrante, en la parte izquierda, rellena el formulario que aparece y haz clic en Enviar correo.