Google App Engine for Java2. Outline
• Google App Engine for Java (GAE/J) といろ
いろ組み合わせるとすごそうなので、
そのすごそうさ具合を説明します。
• 最後に、簡単なデモとその作り方を紹
介します。
6. Google App Engine
• 無料*の PaaS 環境(Python, Java, Go)
• NoSQL データストア (BigTable)
• いろいろなサービスへの API
• そこそこ充実している管理画面
* いろいろな制限はありますが、課金で制限を緩和できます。
9. Why GAE for Java?
• Java は不滅!(あと10年くらいは……)
• Google Web Toolkit (GWT) との親和性
• VMware と Google のクラウド連携強化*
- Spring, Google App Engine, SpringSource Tool Suite
- Spring Roo, Google Web Toolkit
- Spring Insight, Google Speed Tracer
* http://www.vmware.com/jp/company/news/releases/vmware-google.html
10. GAE/J
Specification
• Servlet 2.5
• JSP 2.2 (except Unified EL)
• JSTL 1.1
• JDO 2.3
• JPA 1.0
12. Google Web Toolkit
• Java で AJAX フロントエンドを記述
• JavaScript にコンパイルして実行
• クロスブラウザ(Internet Explorer 6 対応)
• 豊富なウィジェット (3rd party: Ext GWT*)
* http://www.sencha.com/products/extgwt
15. Pre-required
Software
• Java SE Development Kit 6
• Google Chrome (optional)
• 英語のドキュメントに負けない気持ち
20. Pleiades All in One
• 日本語化版 Eclipse +便利プラグイン
• Pleiades = Eclipse プラグイン日本語化プラグイン
• Pleiades All in One for Java Developers 推奨
22. Google Plugin
for Eclipse
• GAE/J & GWT 開発用プラグイン
• ローカル実行用サーバー環境
• GAE デプロイツール
• GWT コンパイラ
23. Ex. Development
Environment
• Windows XP Professional SP3
• Java SE Development Kit 6 Update 31
• Google Chrome 17.0
• Pleiades All in One for Java Developers
(based on Eclipse Indigo 3.7.2)
• Google Plugin for Eclipse*
* Eclipse マーケットプレースからインストールする。
24. Installation Note
• Pleiades のインストールフォルダ*
推奨:C:¥pleiades
自分:C:¥Document and Settings¥takuya¥My Documents¥pleiades
• Google Plugin のインストール
1. Eclipse を起動
2. ヘルプ>Eclipse マーケットプレースをクリック
3. Google Plugin で検索してインストール
* Windows では、パスが長いとインストール(ZIP の解凍)に失敗するそうです。
27. Create GAE Project
1. Eclipse を起動
2. ファイル>新規>プロジェクトをクリック
3. Google>Web アプリケーション・プロジェクトをクリック
4. Windows ファイアウォールの警告:ブロック解除
5. 新規 Web アプリケーション・プロジェクトウィザード
プロジェクト名:Guestbook
パッケージ名:com.example.guestbook
Google Web ツールキットを使用:使用しない
30. Run Application
1. 実行>実行をクリック
2. http://localhost:8888/guestbook にアクセス
• エラーの場合、下記を設定*
1. プロジェクト>プロパティをクリック
2. 実行/デバッグ設定>Guestbook>編集をクリック
3. 引数>VM 引数>につぎの引数を追加
-Dappengine.user.timezone=UTC
* バグみたいです。(2012年3月17日現在)
34. Demo Application
“Guestbook”*
* Google App Engine のサイトにあるチュートリアルをベースにしてつくってます。
40. 1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta charset="UTF-8">
5 <title>Guestbook</title>
6 </head>
7 <body>
8 <h1>Guestbook</h1>
9 <p><a href="#">サインイン</a>するとコメントに名前が表示されます。</p>
10 <p>こんにちは、takuya0301 さん。(<a href="#">サインアウト</a>)</p>
11 <ul>
12 <li>つっちぃ (@takuya0301) です。自己紹介して Twitter ID を晒しましょうw
13 -- takuya0301 (2012-03-24 04:24:43)</li>
14 </ul>
15 <form method="POST" action="/guestbook">
16 <input type="text" size="80">
17 <input type="submit">
18 </form>
19 <p>エラー:コメントを入力してから送信ボタンをクリックしてください。</p>
20 </body>
21 </html>
index.html
45. 16 public class GuestbookServlet extends HttpServlet {
17 public void doGet(HttpServletRequest request, HttpServletResponse response)
18 throws IOException, ServletException {
19 // User サービスの取得
20 UserService userService = UserServiceFactory.getUserService();
21
22 // ログイン中ユーザーの取得(ユーザーがいないときは null)
23 User user = userService.getCurrentUser();
24
25 // ログイン/ログアウト用 URL の生成(引数は戻り URL)
26 String loginURL = userService.createLoginURL(request.getRequestURI());
27 String logoutURL = userService.createLogoutURL(request.getRequestURI());
28
29 request.setAttribute("user", user);
30 request.setAttribute("loginURL", loginURL);
31 request.setAttribute("logoutURL", logoutURL);
32
33 request.getRequestDispatcher("/guestbook.jsp").forward(request, response);
34 }
35 }
GuestbookServlet.java
46. 1 <%@ page contentType="text/html;charset=UTF-8" %>
2 <%@ page trimDirectiveWhitespaces="true" %>
3 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
4 <!DOCTYPE html>
5 <html>
6 <head>
7 <meta charset="UTF-8">
8 <title>Guestbook</title>
9 </head>
10 <body>
11 <h1>Guestbook</h1>
12 <%-- ログインユーザーの有無で分岐 --%>
13 <c:choose>
14 <c:when test="${empty user}">
15 <p>
16 <a href="${loginURL}">サインイン</a>するとコメントに名前が表示されます。
17 </p>
18 </c:when>
19 <c:otherwise>
20 <p>
21 こんにちは、${user.nickname} さん。(<a href="${logoutURL}">サインアウト</a>)
22 </p>
23 </c:otherwise>
24 </c:choose>
25 </body>
26 </html>
guestbook.jsp
47. 4. Comment Posting
• HTML フォームからコメントを投稿
• JDO* でコメントを永続化
• エラー処理:空コメント
* JDO (Java Data Objects) は、Java オブジェクトの永続性に関する仕様です。
52. 4 <!DOCTYPE html>
5 <html>
6 <head>
7 <meta charset="UTF-8">
8 <title>Guestbook</title>
9 </head>
10 <body>
11 <h1>Guestbook</h1>
12 <%-- ログインユーザーの有無で分岐 --%>
13 <c:choose>
14 <c:when test="${empty user}">
15 <p>
16 <a href="${loginURL}">サインイン</a>するとコメントに名前が表示されます。
17 </p>
18 </c:when>
19 <c:otherwise>
20 <p>
21 こんにちは、${user.nickname} さん。(<a href="${logoutURL}">サインアウト</a>)
22 </p>
23 </c:otherwise>
24 </c:choose>
25 <%-- コメント投稿フォーム --%>
26 <form method="POST" action="/guestbook">
27 <input type="text" name="comment" size="80"> <input type="submit">
28 </form>
29 <%-- 空コメントの場合、エラーメッセージを表示 --%>
30 <c:if test="${isEmptyCommentError}">
31 <p>エラー:コメントを入力してから送信ボタンをクリックしてください。</p>
32 </c:if>
33 </body>
34 </html>
guestbook.jsp
53. 39 public void doPost(HttpServletRequest request, HttpServletResponse response)
40 throws IOException, ServletException {
41 String comment = request.getParameter("comment");
42
43 // エラー:空コメント
44 if (comment.isEmpty()) {
45 request.setAttribute("isEmptyCommentError", true);
46 doGet(request, response);
47 return;
48 }
49
50 UserService userService = UserServiceFactory.getUserService();
51 User user = userService.getCurrentUser();
52
53 String id = user != null ? user.getUserId() : "";
54 String name = user != null ? user.getNickname() : "";
55 Date postingDate = new Date();
56
57 // コメントオブジェクトの生成
58 Comment c = new Comment(id, name, comment, postingDate);
59
60 // コメントオブジェクトの永続化
61 PersistenceManager pm = PMF.get().getPersistenceManager();
62 try {
63 pm.makePersistent(c);
64 } finally {
65 pm.close();
66 }
67
68 doGet(request, response);
69 }
GuestbookServlet.java
54. 12 @PersistenceCapable
13 public class Comment {
14 @PrimaryKey
15 @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
16 private Key key;
17
18 @Persistent
19 private String id;
20
21 @Persistent
22 private String name;
23
24 @Persistent
25 private String comment;
26
27 @Persistent
28 private Date postingDate;
29
30 public Comment(String id, String name, String comment, Date postingDate) {
31 this.id = id;
32 this.name = name;
33 this.comment = comment;
34 this.postingDate = postingDate;
35 }
36
// ゲッターメソッドが続く
56 }
Comment.java
55. 1 package com.example.guestbook;
2
3 import javax.jdo.JDOHelper;
4 import javax.jdo.PersistenceManagerFactory;
5
6 public final class PMF {
7 private static final PersistenceManagerFactory pmfInstance =
8 JDOHelper.getPersistenceManagerFactory("transactions-optional");
9
10 private PMF() {}
11
12 public static PersistenceManagerFactory get() {
13 return pmfInstance;
14 }
15 }
インスタンス生成処理負荷軽減のためのシングルトンラッパークラスです。
PMF.java
58. 10 <body>
11 <h1>Guestbook</h1>
12 <%-- ログインユーザーの有無で分岐 --%>
13 <c:choose>
14 <c:when test="${empty user}">
15 <p><a href="${loginURL}">サインイン</a>するとコメントに名前が表示されます。</p>
16 </c:when>
17 <c:otherwise>
18 <p>
19 こんにちは、${user.nickname} さん。(<a href="${logoutURL}">サインアウト</a>)
20 </p>
21 </c:otherwise>
22 </c:choose>
23 <%-- コメント投稿フォーム --%>
24 <form method="POST" action="/guestbook">
25 <input type="text" name="comment" size="80"> <input type="submit">
26 </form>
27 <%-- 空コメントの場合、エラーメッセージを表示 --%>
28 <c:if test="${isEmptyCommentError}">
29 <p>エラー:コメントを入力してから送信ボタンをクリックしてください。</p>
30 </c:if>
31 <%-- コメントリスト --%>
32 <ul>
33 <c:if test="${not empty newComment}">
34 <li>${newComment.comment} -- ${newComment.name} (${newComment.postingDate})</li>
35 </c:if>
36 <c:forEach var="comment" items="${comments}">
37 <li>${comment.comment} -- ${comment.name} (${comment.postingDate})</li>
38 </c:forEach>
39 </ul>
40 </body>
guestbook.jsp
59. 20 public void doGet(HttpServletRequest request, HttpServletResponse response)
21 throws IOException, ServletException {
/* user, loginURL, logoutURL などを request 属性に設定 */
35 PersistenceManager pm = PMF.get().getPersistenceManager();
36 try {
37 // コメントの取得
38 Query query = pm.newQuery(Comment.class);
39 query.setOrdering("postingDate desc");
40 query.setFilter("postingDate < dateParam");
41 query.declareParameters("java.util.Date dateParam");
42
43 try {
44 // コメント投稿直後の特別処理
45 // コメントがデータストアに反映されていないこともあるので、
46 // 新しいコメントは独立して表示し、それ以前の投稿を取得する。
47 List<Comment> comments;
48 Comment newComment = (Comment) request.getAttribute("newComment");
49 if (newComment != null) {
50 comments = ((List<Comment>) query.execute(newComment.getPostingDate()));
51 } else {
52 comments = (List<Comment>) query.execute(new Date());
53 }
54
55 request.setAttribute("comments", comments);
56
57 request.getRequestDispatcher("/guestbook.jsp").forward(request, response);
58 } finally {
59 query.closeAll();
60 }
61 } finally {
62 pm.close();
63 }
64 } GuestbookServlet.java
60. 66 public void doPost(HttpServletRequest request, HttpServletResponse response)
67 throws IOException, ServletException {
68 // エラー:空コメント
69 String comment = request.getParameter("comment");
70 if (comment.isEmpty()) {
71 request.setAttribute("isEmptyCommentError", true);
72 doGet(request, response);
73 return;
74 }
75
76 // User データの取得
77 UserService userService = UserServiceFactory.getUserService();
78 User user = userService.getCurrentUser();
79
80 String id = user != null ? user.getUserId() : "";
81 String name = user != null ? user.getNickname() : "";
82 Date postingDate = new Date();
83
84 // コメントオブジェクトの生成
85 Comment newComment = new Comment(id, name, comment, postingDate);
86
87 // コメントオブジェクトの永続化
88 PersistenceManager pm = PMF.get().getPersistenceManager();
89 try {
90 pm.makePersistent(newComment);
91 } finally {
92 pm.close();
93 }
94
95 // 新しいコメントを属性として設定
96 request.setAttribute("newComment", newComment);
97
98 doGet(request, response);
99 }
GuestbookServlet.java
65. というか、PaaS 勉強会を
開催しませんか?
AWS Elastic Beanstalk も熱いですね。Windows Azure や Heroku も興味あります。
てか、PaaS 勉強会って絶対あると思うんで調べて参加しろって話ですね。
Editor's Notes \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n