2013年2月15日金曜日

19.Google App Engine 1.7.5を早速試してみた

このエントリーをはてなブックマークに追加



Ryo Yamasaki(@vierjp)です。

Google App Engine 1.7.5がリリースされたので早速試してみました。


○関連リンク

Google App Engine Blog: App Engine 1.7.5 Released
SdkForJavaReleaseNotes - googleappengine - Google App Engine Java SDK
SDKダウンロードページ


○変更点

・High-Memory Instance(ハイメモリーインスタンス)の追加

使用可能なメモリの量が多いインスタンスが追加されています。
Admin ConsoleのApplication Settingを見ると「F4_1G」が追加されています。

・Frontend
Frontend classMemory limitCPU limitCost per hour per instance
F1 (default)128MB600MHz$0.08
F2256MB1.2GHz$0.16
F4512MB2.4GHz$0.32
F4_1G1024MB2.4GHz$0.48
参考:Adjusting Application Performance - Google App Engine — Google Developers


・Backend
class configurationMemory limitCPU limitCost per hour per instance
B1128MB600MHz$0.08
B2 (default)256MB1.2GHz$0.16
B4512MB2.4GHz$0.32
B4_1G1024MB2.4GHz$0.48
B81024MB4.8GHz$0.64
参考:Backends Java API Overview - Google App Engine — Google Developers

* 2013/02/15 02:35 Backendsについて追記



・Java7 Runtimeのサポート (Experimental)

In a future release, support for Java 6 will be removed, so it is a good idea to try running your existing Java 6-based app in the new Java 7 runtime while it is still optional.

将来のリリースでJava 6のサポートが削除されるだろうから、
Java7がオプションの間に既存のJava6ベースのアプリを新しいJava7ランタイムで実行してみる事をお勧めします。
(↑たぶんこんな感じ)


試してみた

・JDK1.7.0をダウンロードする (Java SE Downloads)
・「pkg」ファイルを実行してインストール(標準のjavaのpathが変わるので注意)
・eclipseに設定する
「環境設定」→「Java」→「インストール済みのJRE」→「追加」→「標準VM」
 JREホーム:/Library/Java/JavaVirtualMachines/jdk1.7.0_**.jdk/Contents/Home
 JRE名:JDK 1.7.0 (任意)

試しにJava7の構文を含むControllerを作ってデプロイしたところ期待通りに動作しました。
    protected Navigation run() throws Exception {
        ServletContext servletContext = ServletContextLocator.get();

        // JDK7 型推論
        Map<String,String> testMap = new HashMap<>();

        response.setContentType("text/plain; charset=UTF-8");

        // JDK7 try-with-resources
        try (PrintWriter writer = response.getWriter()) {
            writer.println("GAE Version : " + servletContext.getServerInfo());
            response.flushBuffer();
        }
        return null;
    }
参考:Java 7 Considerations - Google App Engine — Google Developers
   JDK7正式版の新機能一覧(Java言語仕様に関して) - R42日記



・バウンスメールの通知機能

「バウンスメール」とは
---- 引用 ここから --------------------------------------------------------------------
何らかのエラーにより、送信できずに送り返されてきたメールのこと。
ユーザーが送信したメールはMTAによって送信先相手へと届けられるが、
通信路や相手先メール・サーバの状態によっては、メールを相手にまで届けられないことがある。
このような場合は、
送信元のメール・アドレスに向けてエラーが発生したことを通知するメールが返信される。
これをバウンス・メールという。
-- 引用 ここまで --------------------------------------------------------------------
引用元:Insider's Computer Dictionary [バウンス・メール] - @IT

試してみた

・appengine-web.xml

 mail_bounce
↑を「<appengine-web-app>」直下に追記します。

・web.xml


 bouncehandler
 jp.vier.test.ver_1_7_5.servlet.BounceHandleServlet


 bouncehandler
 /_ah/bounce




 
  /_ah/bounce
 
 
   admin
 


・バウンスメールを受けとるServlet
public class BounceHandleServlet extends HttpServlet {

    private Logger log = Logger.getLogger(BounceHandleController.class
        .getName());

    public void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {

        HttpServletRequest request = RequestLocator.get();
        log.info("called.");

        try {
            BounceNotification bounce = BounceNotificationParser.parse(request);
            // The following data is available in a BounceNotification object
            String from = bounce.getOriginal().getFrom();
            String to = bounce.getOriginal().getTo();
            String subject = bounce.getOriginal().getSubject();
            String text = bounce.getOriginal().getText();
            String notifyFrom = bounce.getNotification().getFrom();
            String notifyTo = bounce.getNotification().getTo();
            String notifySubject = bounce.getNotification().getSubject();
            String notifyText = bounce.getNotification().getText();

            log.info("from : " + from);
            log.info("to : " + to);
            log.info("subject : " + subject);
            log.info("text : " + text);
            log.info("notifyFrom : " + notifyFrom);
            log.info("notifyTo : " + notifyTo);
            log.info("notifySubject : " + notifySubject);
            log.info("notifyText : " + notifyText);
        } catch (MessagingException e) {
            throw new RuntimeException(e);
        }
    }
}

・バウンスメールが返ってくるようなメールを送信するテスト用のController

「/sendErrorMail?flag=false」でアクセスする。

public class SendErrorMailController extends Controller {

    @Override
    protected Navigation run() throws Exception {

        Properties props = new Properties();
        Session session = Session.getDefaultInstance(props, null);

        boolean isSuccess = asBoolean("flag");
        String toAddress;
        if (isSuccess) {
            toAddress = "****@vier.jp";
        } else {
            toAddress = "test@example.jp";
        }

        Message msg = new MimeMessage(session);
        // Admin ConsoleのPermissionに登録してあるメールアドレスを送信元に指定する
        msg.setFrom(new InternetAddress("******@gmail.com", "Test Admin")); 
        msg.addRecipient(Message.RecipientType.TO, new InternetAddress(
            toAddress,
            "Recipient"));
        msg.setSubject("test message subject " + new Date().getTime());
        msg.setText("test message body.");
        Transport.send(msg);

        response.setContentType("text/plain; charset=UTF-8");
        // JDK7 try-with-resources
        try (PrintWriter writer = response.getWriter()) {
            writer.println("Sent Mail.");
            response.flushBuffer();
        }

        return null;
    }
}

・実行結果ログ


基本的にほとんど公式ドキュメントのサンプルのままです。
最初はバウンスメールを受け取った際のrequestも
AppRouterを使ってSlim3のControllerで処理しようとしたのですが、
BounceNotification bounce = BounceNotificationParser.parse(request);
でエラーになってしまったので結局サンプル通りのServletにしました(´・ω・`)
その時のエラーは以下の内容。
/_ah/bounce
java.lang.IllegalStateException: Input stream already read, or empty.
 at com.google.appengine.api.utils.HttpRequestParser.parseMultipartRequest(HttpRequestParser.java:48)
 at com.google.appengine.api.mail.BounceNotificationParser.parse(BounceNotificationParser.java:31)
 at jp.vier.test.ver_1_7_5.controller.BounceHandleController.run(BounceHandleController.java:38)
 ・・・

* 2013/02/15 03:01 ↓Slim3のSimpleControllerを使った方法について追記ここから↓

Google+でShinichi Ogawaさんに教えていただきました。(´▽`)
「Slim3で標準入力を取得したい場合」は「SimpleController」を使えば良いそうです。
上述の「BounceHandleServlet」と「web.xmlの定義」の代わりに以下を作成することで、
ServletではなくControllerで同じ処理を行うことができました。

・AppRouter.java
public class AppRouter extends RouterImpl {
    public AppRouter() {
        addRouting("/_ah/bounce", "/bounceHandle");
    }
}
・BounceHandleController.java
public class BounceHandleController extends SimpleController {

    private Logger log = Logger.getLogger(BounceHandleController.class
        .getName());

    @Override
    protected Navigation run() throws Exception {

        log.info("called.");

        HttpServletRequest request = RequestLocator.get();
        BounceNotification bounce = BounceNotificationParser.parse(request);
        // The following data is available in a BounceNotification object
        String from = bounce.getOriginal().getFrom();
        String to = bounce.getOriginal().getTo();
        String subject = bounce.getOriginal().getSubject();
        String text = bounce.getOriginal().getText();
        String notifyFrom = bounce.getNotification().getFrom();
        String notifyTo = bounce.getNotification().getTo();
        String notifySubject = bounce.getNotification().getSubject();
        String notifyText = bounce.getNotification().getText();

        log.info("from : " + from);
        log.info("to : " + to);
        log.info("subject : " + subject);
        log.info("text : " + text);
        log.info("notifyFrom : " + notifyFrom);
        log.info("notifyTo : " + notifyTo);
        log.info("notifySubject : " + notifySubject);
        log.info("notifyText : " + notifyText);
        return null;
    }
}

* 2013/02/15 03:01 Slim3のSimpleControllerを使った方法について追記ここまで↑


これによって「メールアドレスを登録したユーザーにメールを配信するようなシステム」で
使われていないメールアドレスを検知することで、
そのユーザーのステータスを変更したり
メール送信対象から除外してQUOTAやコストの節約をしたり
ができるようになるでしょう。

余談ですが、テスト用にシステムで使うメールアドレスは自分の所有するアドレス以外なら
「example.com」や「example.jp」等を使いましょう。

「example」はRFC2606で定義されている「予約されたセカンドレベル・ドメイン名」で、
このドメインは誰にも利用されていない事が決まっています。

間違っても実在するドメイン名のアドレスを適当に使ったりしないようにしましょう。
(昔働いていた職場で見たことがあります(-_-;))

参考:Receiving Bounce Notification - Google App Engine — Google Developers



・Cloud Storageのための Blobstore サービスの変更

Issue 8337 の対応で、
Cloud Storageを使った場合に Blobstore サービスがblobKeyの代わりにファイル名を返すようになった。

この方式でアップロードした場合にファイル名がランダムになった上に、
アップロード後のcallbackの中でCloudStorage上でのファイル名を取得できなかったようです。
それがblogKeyの代わりにファイル名を取得できるようになったのでしょうか。

むしろこの方式でCloud Storageにアップロードできるのを知らなかったのですが、
サイズが大きいファイル(アップに一分以上かかるような)の場合にはこの形式が良いのかな・・・?

参考:Blobstore Java API の概要 - Google App Engine — Google Developers



・Cloud Endpointsのサポート (Experimental)

Cloud Endpointsの概要は「appengine ja night #22 Google Cloud EndPoints」を参照。
これは別途まとまった時間に試してみようかと思います。


* 2013/02/19 15:50 追記
Blog @vierjp : Google Cloud Endpointsを試してみた (1/2)



参考:Google Cloud Endpoints - Google App Engine — Google Developers



・Eclipse Pluginのバージョンアップ

eclipseのメニューから「ヘルプ」→「更新の確認」でアップデートできます。



 ・SearchAPIのdeprecatedなクラスが削除された

これらのクラスを使っている場合は次のAppEngineのリリースまでに
これらのクラスを参照しない新しいバージョンをデプロイしないと動かなくなる、とのこと。
SearchAPIはまだ使ったこと無いけどこの辺のクラスは結構前からdeprecatedなのでしょうか。
SDKのリリースノートにしか書いてないので、
気づかないまま既存のアプリをそのまま運用してしまう人がいないかちょっと心配。



・前回のリリースで廃止された「Conversion API」はSDKから削除された

HTML, PDF, text, 画像を相互変換するAPIでしたが、いつのまにか無くなったのですね。。




 ・Channel APIの変更

Channel APIはチャネルが作成された場所に関係なく
アプリの任意のバージョンまたはBackendsからチャネルメッセージを送信できるようになった。



・DataNucleus Pluginが2.1.2にアップグレードした

JDO・JPA関連のライブラリだそうです。



・URL Fetch サービスがPATCHメソッドでのリクエストをサポート

したそうです。



・Windowsのローカル環境でDatastore callback アノテーションが機能していなかった問題を修正

Datastore callback アノテーションを使うとDatastoreのメソッドに前処理を挟んだりできます。
すっかり存在を忘れていましたがこのリリースを見て思い出しました。
以前読んだ時は家も職場もWindows環境だったので導入を諦めてしまいましたが、
put時にEntityの登録時刻や更新時刻をこれで自動でセットできたら楽ですね。

* 2013/02/15 09:55 追記
Slim3の「Automatic Values」という機能でこれができると知って愕然としている。。(´・ω・`)



参考:Datastore Callbacks - Google App Engine — Google Developers



このエントリーをはてなブックマークに追加