обработка входящей почты движка приложения

Я разрабатываю приложение Google App Engine.
Я хочу получать почту по адресу '%username%@appid.appspotmail.com', где %username% принадлежит пользователю приложения.
Я просто не могу выясните, что определить в файле web.xml.
Любое подобное решение, например, письмо по адресу:

является приемлемым (если это упрощает работу с подстановочными знаками).

Я попытался (как предложил Gopi)
сопоставить соответствующий сервлет с <url-pattern>/_ah/mail/user.*</url-pattern> в файле web.xml. Это не работает.
Клиент получает сообщение о возврате, тогда как сервер регистрирует соответствующий запрос, полученный приложением, но отклоненный с ошибкой 404. Нет "Ни один обработчик не соответствует этому URL. " INFO добавляется в запись журнала. Кроме того, когда я получаю сгенерированный URL-адрес, я получаю не «Эта страница не поддерживает GET», а скорее обычную ошибку 404.
Однако если я отправлю письмо, чтобы сказать «[email protected]», в журналах отображается ошибка 404 (что и должно быть, поскольку она не отображается в файле web.xml). Кроме того, для такого запроса «Нет обработчиков, соответствующих этому URL-адресу». INFO добавляется в соответствующую запись журнала.

Излишне говорить, что Входящая почта находится в настроенных службах.


person David    schedule 25.08.2010    source источник


Ответы (6)


Это изменение произошло, когда App Engine начал использовать настоящий веб-сервер Java (поэтому объяснение Тоби верно... к сожалению, я не могу восстановить свой логин, чтобы проголосовать за него!). Я рекомендую использовать фильтр. Я поэкспериментировал с фильтром ниже, когда писал игрушечное приложение для GAE. После того, как вы определили базовый класс в конце этого поста, вы можете создать серию почтовых обработчиков (например, следующие). Все, что вам нужно сделать, это зарегистрировать каждый фильтр в вашем файле web.xml для обработки /_ah/mail/*.

public class HandleDiscussionEmail extends MailHandlerBase {

  public HandleDiscussionEmail() { super("discuss-(.*)@(.*)"); }

  @Override
  protected boolean processMessage(HttpServletRequest req, HttpServletResponse res)
    throws ServletException 
  { 
    MimeMessage msg = getMessageFromRequest(req); 
    Matcher match = getMatcherFromRequest(req);
    ...
 }

}

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 <code>false</code>, control is passed
   * o the next filter in the chain. If the implementation returns
   * <code>true</code>, 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;
  }



}
person user1867056    schedule 30.11.2012

следующее дает правдоподобное объяснение благодаря url-pattern и wildcards, который ссылается на http://jcp.org/aboutJava/communityprocess/mrel/jsr154/index2.html (перейдите к разделу 11.2)

В шаблоне URL подстановочный знак * ведет себя не так, как можно было бы предположить, он обрабатывается как обычный символ, за исключением случаев, когда строка заканчивается на /* для «сопоставления пути» или начинается с *. для "сопоставления расширений"

Жаль, было бы неплохо сопоставить адреса получателей электронной почты с подстановочными знаками для разных сервлетов, как показано в примерах документации API Google. Теперь я использую абсолютные совпадения, которые не так чисты, как нужно включать appid.

person Toby    schedule 06.02.2011

Я думаю, что запись, аналогичная приведенной ниже, в ваш web.xml должна работать, чтобы соответствовать вашему второму случаю 'usermailbox.%username%@appid.appspotmail.com

<servlet>
  <servlet-name>handlemail</servlet-name>
  <servlet-class>HandleMyMail</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>handlemail</servlet-name>
  <url-pattern>/_ah/mail/usermailbox.*</url-pattern>
</servlet-mapping>
person Gopi    schedule 25.08.2010
comment
Это сообщение о возврате на стороне клиента и 404 в журналах сервера. Как ни странно, я не получаю обработчиков, соответствующих этому URL-адресу. Кроме того, я вижу, что запрос исходит из механизма самопересылки сервера по его IP-адресу: 0.1.0.20. - person David; 26.08.2010
comment
ЗАПИСЬ В ЖУРНАЛЕ # 1. 08-25 15:25 40.743 /_ah/mail/[email protected] 404 13 мс 19cpu_ms 0kb Подробнее 0.1.0.20 - - [25/Aug/2010:15:25:40 -0700] POST /_ah/mail/[email protected] HTTP/1.1 404 234 - - appid.appspot.com ms=13 cpu_ms=19 api_cpu_ms=0 cpm_usd=0,000947 - person David; 26.08.2010

Что ж... Перепробовав все возможные решения/сопоставления URL-адресов, я остановился на быстром и уродливом.
Суть в том, чтобы иметь единый почтовый сервлет "улавливать все", чтобы работать в качестве диспетчера для других конкретных сервлетов. . Это как гигантский switch, где параметром является URL-адрес запроса.
Это НЕ то, что я хотел, но это работает, и, кажется, единственное, что работает.

У меня есть один сервлет IncomingMail, который обрабатывает ВСЮ входящую почту. период.
Итак, теперь единственным сопоставлением URL-адресов под /_ah/mail/ является следующее:

<servlet>
    <servlet-name>IncomingMail</servlet-name>
    <servlet-class>IncomingMail</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>IncomingMail</servlet-name>
    <url-pattern>/_ah/mail/*</url-pattern>
</servlet-mapping>

Кроме того, у меня есть следующий сервлет, отображаемый как «обычный старый сервлет»:
(обратите внимание на <url-pattern>, а не на сервлет с «почтовым сопоставлением»)

<servlet>
    <servlet-name>GetUserMail</servlet-name>
    <servlet-class>GetUserMail</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>GetUserMail</servlet-name>
    <url-pattern>/serv/userMail</url-pattern>
</servlet-mapping>

Универсальный сервлет (в конце концов) будет выглядеть как гигантский переключатель:

public class IncomingMail extends HttpServlet {
    private final String USER_MAIL_PREFIX="http://appid.appspot.com/_ah/mail/user.";
    private final String USER_MAIL_SERVLET="/serv/userMail";
    ...
    public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        String url = req.getRequestURL().toString();
        System.out.println("IncomingMail called, with URL: "+url);
        String email;
        String servlet;

        if (url.startsWith(USER_MAIL_PREFIX)) {
            email=url.replace(USER_MAIL_PREFIX, "");
            servlet=USER_MAIL_SERVLET;
        }//userMail 
        if (url.startsWith(OTHER_PREFIX)) {
            //Redirect to OTHER servlet
        }
        ...
        System.out.println("forward to '"+servlet+"', with email '"+email+"'");
        RequestDispatcher dispatcher=req.getRequestDispatcher(servlet);
        try {
            req.setAttribute("email", email);
            dispatcher.forward(req, resp);
        } catch (ServletException e) {              
            System.err.println(e);
        }           

    }
}

Сервлет назначения (в данном случае GetUserMail) выполняет getRequestParameter("email"), чтобы увидеть конкретный целевой почтовый ящик.
Он будет получать все письма, отправленные на «user.%un%@appid.appspotmail.com», где %un% равно имя пользователя в пространстве приложения.
Параметр электронной почты, полученный сервлетом, будет иметь форму '%un%@appid.appspotmail.com', без отличительного префикса.
Каждый такой "конкретный" сервлет будет получить «свою вырезку» из сервлета диспетчера почты с параметром электронной почты уже без проницательного префикса.

Одно примечание, которое я добавлю в разделе безопасности:
Если вы беспокоитесь о фиктивных запросах к «конкретным сервлетам», просто определите их все в общем виртуальном пространстве имен, скажем, /servmail/ на вашем сайте, и определите новое <security-constraint>, чтобы разрешить запросы к возникают только внутри самого приложения.
Вот так (внутри web.xml):

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>MailServlets</web-resource-name>
            <description>policy for specific mail servlets</description>
            <url-pattern>/servmail/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>admin</role-name>
        </auth-constraint>
    </security-constraint>

Тем не менее, хотелось бы получить известие от кого-то, кто попробовал и преуспел в создании сопоставления почты с подстановочными знаками <url-pattern>, отличного от универсального.

person David    schedule 26.08.2010

У меня была аналогичная проблема (с использованием Python, поэтому файлы конфигурации yaml, а не XML), и причина оказалась в том, что я поставил:

- url: /_ah/mail/.+ 
  script: handle_incoming_email.py 
  login: admin

перед существующей всеобъемлющей записью:

- url: /.*
  script: main.py

Это дало 404 на сервере и «Ошибка отправки сообщения» при отправке тестовых сообщений.

Перемещение его после всеобъемлющей записи решило проблему.

person greg    schedule 01.08.2011

Я почти уверен, что проблема в том, что вы пытаетесь использовать .*. Выражения URL-адресов в web.xml являются универсальными, а не регулярными выражениями, поэтому вместо них следует использовать только *.* будет соответствовать только строкам, начинающимся с точки.

person Nick Johnson    schedule 05.12.2011