メールの受信

メール メッセージは、HTTP リクエストとしてアプリに送信されます。受信メール メッセージを処理するには、アプリケーションの構成でメールアドレスをサーブレットに関連付けてから、サーブレット コードをアプリに追加します。受信メールによって HTTP リクエストが生成され適切なサーブレットに渡されて、ハンドリングが行われます。

メールを受信するアプリの構成

新しくアプリケーションを作成すると、受信メールはデフォルトで無効になっています。明示的に有効にしていない場合、アプリに送信される受信メール メッセージは無視されます。

受信メールサービスを有効にするには、appengine-web.xml 構成ファイルと web.xml 構成ファイルを次のように変更します。

appengine-web.xml でメールを有効にする

受信メールサービスを有効にする inbound-services セクションを追加して、appengine-web.xml を変更します。

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

メール メッセージは、次の URL を使用する HTTP POST リクエストとしてアプリに送信されます。

/_ah/mail/<ADDRESS>

ここで <ADDRESS> は、ドメイン名を含む完全なメールアドレスです。アプリがカスタム ドメインにデプロイされている場合でも、アプリはそのドメインのアドレスに送信されたメールを受信できないことに注意してください。

web.xml でメールを有効にする

以下の方法で、メールの URL をサーブレットにマッピングして web.xml を変更します。

<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>

上記のスニペットでは、/_ah/mail/* はアプリ宛のすべてのメールと照合されます。メール サーブレットは、App Engine にあるアプリの現在の提供バージョンで実行されます。

受信メッセージのパターンに基づく送信

アプリパターン マッチングが使用されている場合、次のコード スニペットに基づいたフィルタベースのアプローチを使用することを検討してください。

コンクリート ハンドラ

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;
  }
}

上記のコンクリート ハンドラは、次の 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>

security-constraint ディレクティブはフィルタでは使用できないことに注意してください。ハンドラにセキュリティ ポリシーを適用するには別の方法を使用する必要があります。

抽象化ハンドラ

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;
  }
}

受信メールの処理

JavaMail API には、受信メール メッセージの解析に使用される MimeMessageクラスが含まれています。MimeMessage には、java.io.InputStream と JavaMail セッションを受け入れるコンストラクタがあります。これには、空の構成を含めることができます。

次のような MimeMessage インスタンスを作成します。

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) {
      // ...
    }
    // ...
  }
}

その後、各種メソッドを使用して message オブジェクトを解析できます。

  • getFrom() を呼び出して、このメッセージの送信者を返します。
  • getContentType() を呼び出して、メッセージのコンテンツ タイプを抽出します。getContent() メソッドは、Multipart インターフェースを実装するオブジェクトを返します。
  • getCount() を呼び出して、パーツ数を確認します。
  • getBodyPart(int index) を呼び出して、本文の特定の部分を返します。

受信メールを処理するようにアプリを設定したら、開発用サーバー コンソールを使用して、受信メール メッセージをシミュレートできます。開発用サーバーの起動方法など、詳細については、Java 開発用サーバーをご覧ください。ローカルの開発用サーバーでアプリケーションを起動した後、URL http://localhost:8888/_ah/admin/(ローカルの開発用サーバーにデフォルト ポートを使用していない場合は、「8888」の値を使用しているポートで置き換えます)を参照すると、アプリケーションにアクセスできます。

開発用サーバー コンソールの左側で [Inbound Mail] をクリックし、表示されたフォームに入力して [Send Email] をクリックします。