java初心者がAppEngineで初めてのBigTable。落書き帳完成。

Googleのインフラ上でWebアプリケーションを動作させることが可能な、GoogleAppEngineというものがと〜っても気になりだしている今日この頃。
このAppEngineの言語にjavaが追加されてから少し経つのですが、
いまさらながらそのjavaを使って何か作ってみよう(というか、作り方の勉強)というわけです。
唐突な流れで。eclipseインストールして。AppsEngineのプラグイン入れて。


javaでwebアプリを作るとなるともちろんservletサーブレット)を使うことになります。で、私はサーブレットを作るのもままならない程度の能力しか持ち合わせていないのですが
まぁ、何とかなるでしょう。特殊なdatastore(データストア)とやらを除いて。


datastoreとは簡単に言うとAppEngine専用データベースである。AppEngineでは、サーバー上に普通にファイル保存は出来ないのでデータを保存する場合はすべてdatastoreに保存する必要がある。
で、このdatastoreなのだが巷でよく使われるリレーショナルデータベース(RDB)とは違う。キーバリューストアというタイプなのだとか。なぜ、Googleはこのタイプを使うのかというとRDBではGoogle先生にとって遅すぎるらしい。ちなみにGoogleが作ったこのデータストアの名称は「BigTable」。まぁ、こんな話はどうでもいい。先に進めよう。


で、このdatastoreを使うのが厄介だった。
やってみると、RDBみたいにデータベースに対しテーブル定義を行いそしてデータをがんがん入れていくのではなく
javaのオブジェクトでデータを定義してそのオブジェクトをデータベースに入れるような感じでコーディングを行うというイメージだった。(私の個人的なイメージ)


Googleの説明通り作るだけなのだがlong型はlongではなくラッパークラスのLongを使わなければいけないところを少し悩んでしまったり、
「文字化けしてぜんぜん使えねぇーーー」と悩んでいたらただ単にサーブレットの文字エンコーディングをちゃんと設定してなかっただけだったりと、へたれプログラマーぶりを遺憾なく発揮した作業であった。もちろん、自分の足につまづくことも忘れなかった。


そんなことして何を作ったかというと、名前とコメントを入れて送信ボタンを押すと
掲示板みたいに一言メッセージとして登録されるという至極どーでもいいWebアプリである。


以下、私が無駄に時間をかけて作ってみたソースである。
(無駄に時間がかかったのはEclipseが重いせいですよ?そのうちPC自作します。)
あまり参考にはならないけど。

package cc.co.necoLabo.dataTest;

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Message {

	@PrimaryKey
	@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
	private Long id;

	@Persistent
	private String name;

	@Persistent
	private String message;

	/**
	 * idを取得します。
	 * @return id
	 */
	public Long getId() {
	    return id;
	}

	/**
	 * nameを取得します。
	 * @return name
	 */
	public String getName() {
	    return name;
	}

	/**
	 * nameを設定します。
	 * @param name name
	 */
	public void setName(String name) {
	    this.name = name;
	}

	/**
	 * messageを取得します。
	 * @return message
	 */
	public String getMessage() {
	    return message;
	}

	/**
	 * messageを設定します。
	 * @param message message
	 */
	public void setMessage(String message) {
	    this.message = message;
	}
}
  • MessageServlet.java
package cc.co.necoLabo.dataTest;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MessageServlet extends HttpServlet {

	private static final long serialVersionUID = -2743084515697088124L;

	@SuppressWarnings("unchecked")
	@Override
	public void doGet(HttpServletRequest req, HttpServletResponse response) throws ServletException,IOException {

		response.setContentType("text/html; charset=utf-8");
		PrintWriter pw = response.getWriter();
		pw.println("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" +
				"<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"ja\" lang=\"ja\">\n" +
				"<head>");
		pw.println("<title>落書き ぼ〜ど。</title>");
		pw.println("</head>\n<body>");
		pw.println("<p>落書き ぼ〜ど。</p>");
		pw.println("<form method=\"post\" action=\"./\">");
		pw.println("  名前: <input type=\"text\" name=\"name\"><br />");
		pw.println("コメント: <input type=\"text\" name=\"comm\"><br />");
		pw.println(" <input type=\"submit\" value=\"送信\">");
		pw.println("</form><br />");

		PersistenceManager pm = PMF.get().getPersistenceManager();
		try{
			String query = "select from " + Message.class.getName() +" order by id desc";
			Query query1 = pm.newQuery(query);

			List<Message> results = (List<Message>) query1.execute();

			if (results.iterator().hasNext()) {
	            for (Message m : results) {
	            	pw.println("<p>");
	            	pw.println("id:" + m.getId() + "<br />");
	            	pw.println("Name:" + m.getName() + "<br />");
	            	pw.println("Message:" + m.getMessage() + "<br />");
	            	pw.println("<br /></p>");
	            }
	        } else {
	            // ... no results ...
	        }
		}finally{
			pm.close();
		}

		pw.println("</body></html>");
	}

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

		req.setCharacterEncoding("utf-8");

		Message mess = new Message();

		PersistenceManager pm = PMF.get().getPersistenceManager();
		try{
			mess.setName(req.getParameter("name"));
			mess.setMessage(req.getParameter("comm"));

			pm.makePersistent(mess);
		}finally{
			pm.close();
		}
		resp.sendRedirect("../message/");
	}

}

Googleのドキュメントに書いてあるものと同様なので省略。
http://code.google.com/intl/ja/appengine/docs/java/gettingstarted/usingdatastore.html




動かしてみたページがこれ
http://neco-apps.appspot.com/message/
って、激しく文字化けしてないですか?サーブレットでは文字エンコードutf-8に設定してるんだけど・・・。




とまぁ、こんな感じです。
そのままだとXSSとかの脆弱性満載の超危険サイトと化すので対策しましょうww


これだけのものを作るのに数時間かけてしまった・・・。
しかも、EclipsePleiadesオールインワンの全部入りUltimateなやつをなぜかダウンロードしたので糞重い!
早くPC組みたいのだが、Win7まで待つし、グラボも新しいやつが出るまで待つ。
いつになるのやら。