Google App Engineで受信メールの処理ができるようになった。
具体的な手順はこちら。
http://code.google.com/intl/en/appengine/docs/java/mail/receiving.html
手順はこう。
まず、appengine-web.xmlに次の設定を追加
<inbound-services> <service>mail</service> </inbound-services>
そうすると、string@appid.appspotmail.comにメールが来たら /_ah/mail/<address> というURLが呼び出されるようになる。
なので、次のようなサーブレットマッピングをweb.xmlに追加してサーブレットで処理をする。
<servlet> <servlet-name>mailhandler</servlet-name> <servlet-class>MailHandlerServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>mailhandler</servlet-name> <url-pattern>/_ah/mail/*</url-pattern> </servlet-mapping>
特定のメールだけを受け取るときのurl-patternには、ドメイン名まで含める必要があるので注意。
<servlet-mapping> <servlet-name>mailhandler</servlet-name> <url-pattern>/_ah/mail/hoge@example.com</url-pattern> </servlet-mapping>
このとき、そのままだと/_ah/mail/hogeに外からアクセスされてしまうので、管理者権限じゃないとアクセスできないようにする。このあたりはqueueとかと一緒。
<security-constraint> <web-resource-collection> <url-pattern>/_ah/mail/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> </auth-constraint> </security-constraint>
あとは、サーブレットを記述する。
問題は、この一文が正しくないこと。(※2009/12/3 挙動が変わって正しくなったようだ)
The getContent() method returns an object that implements the Multipart interface. You can then call getCount() to determine the number of parts and getBodyPart(int index) to return a particular body part.
getContentはMultipartをimplementsしたオブジェクトじゃなくByteArrayInputStreamを返す。
メールがplain/textの場合は、それをそのまま読み込むと本文になっている。
multipart/alternative(HTMLメール)とかmultipart/mixed(添付メール)の場合は、これをByteArrayDataSourceに渡してMimeMultipartオブジェクトを生成する。
ということで、メールを処理する普通のサーブレットを書けばいいということになる。
ここでは、次のようなエンティティを保存するコードを書いてみる。
@PersistenceCapable(identityType = IdentityType.APPLICATION) public class ReceivedMail { @PrimaryKey @Persistent(valueStrategy=IdGeneratorStrategy.IDENTITY) Key id; @Persistent String from; @Persistent String subject; @Persistent String text; }
PMFは、JDOのドキュメントのサンプルで定義されてるやつね。あとは適当に補完してそれっぽいクラスをimportする。
public class MailHandlerServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PersistenceManager pm = PMF.get().getPersistenceManager(); Properties props = new Properties(); Session session = Session.getDefaultInstance(props, null); try { MimeMessage message = new MimeMessage(session, request.getInputStream()); ReceivedMail mail = new ReceivedMail(); mail.setSubject(message.getSubject()); //ローカルサーバーでは文字化けするので次のようなコードが必要。本番サーバーでは不要 //new String(message.getSubject().getBytes("8859_1"), "UTF-8")); mail.setFrom(message.getFrom()[0].toString()); String contentType = message.getContentType(); InputStream is = null; //2009/12/11 挙動がかわったことに対応 String mess = ""; if(message.isMimeType("text/plain")){ //ふつうのメールの処理 /* 2009/12/11 ここも挙動が変わってたので、ClassCastExceptionになります。 is = (InputStream) message.getContent(); */ mess = (String)message.getContent(); }else{ //HTMLメールや添付メールの処理 /* 2009/12/3 挙動が変わったのでこれではClassCastExceptionが発生する Multipart content = new MimeMultipart( new ByteArrayDataSource( (InputStream)message.getContent(), message.getContentType())); */ Multipart content = (Multipart)message.getContent(); for(int i = 0; i < content.getCount(); ++i){ BodyPart bp = content.getBodyPart(i); if(!bp.isMimeType("text/plain")) continue; is = bp.getInputStream(); contentType = bp.getContentType(); break; } } if(is != null){ //contentTypeからエンコーディングを取得 String encoding = null; String[] elms = contentType.split(";"); for(String elm : elms){ if(elm.trim().startsWith("charset=")){ encoding = elm.trim().substring("charset=".length()); } } Reader r = null; if(encoding != null){ //エンコーディングが入っている if(encoding.startsWith("\"")) encoding = encoding.substring(1); if(encoding.endsWith("\"")) encoding = encoding.substring(0, encoding.length() - 1); r = new InputStreamReader(is, encoding); }else{ //エンコーディングが入っていない r = new InputStreamReader(is); } //2009/12/11 挙動がかわったことに対応 //String mess = ""; BufferedReader buf = new BufferedReader(r); for(String line; (line = buf.readLine()) != null;){ mess += line + "\n"; } //2009/12/11 挙動がかわったことに対応 //mail.setText(mess); } //2009/12/11 挙動がかわったことに対応 mail.setText(mess); pm.makePersistent(mail); } catch (MessagingException ex) { throw new ServletException(ex); }finally{ pm.close(); } } }