Play Framework でつぶやきアプリを開発(その1: Ebean を使う)

前回までの記事(↓)の内容をベースにつぶやきアプリ(超簡易版T○itter的なサムシング)を開発してみよう。
超簡易版と言ってもPlay Framework で開発する上でのノウハウが詰まっているので、非常に教育的なテーマだ。


Play Framework で「Hello World!!」 - hirahiro56のブログ


Play Framework でフォームを扱う(オウム返しアプリを作ろう) - hirahiro56のブログ


Play Framework でMySQL を利用する(環境構築) - hirahiro56のブログ

1. MySQL との連携設定
まずは、前回の記事で構築したMySQL 環境と、Play Framework のプロジェクト(chatapp)との間の連携設定をしていこう。
詳細は割愛するが、まずはMySQL のサイトからJDBCドライバ(Connecotr/J)をダウンロードしてくる。「mysql-connector-java-5.1.32-bin.jar」

2. jar の配置
次にプロジェクトフォルダ直下にlib フォルダを作成(chatapp => lib)する。
そして、そのフォルダに「mysql-connector-java-5.1.32-bin.jar」を配置する。

3. application.conf へ接続文字列を追記
本設定ファイルに下記行を追加する。
※設定内容は前回の記事に従って設定している。
■conf/application.conf

db.default.driver=com.mysql.jdbc.Driver
db.default.url="jdbc:mysql://localhost:3306/chatapp"
db.default.user="chatapp"
db.default.password="password"
ebean.default="models.*"


4. テーブルを作る。
「Create Table だ!!」といいたいところだが、Play Framework の場合Ebean というORマッパーというものが使える。
Ebean について詳細は説明しないが、ここでは、「Java でデータベース連携するアプリを作る時、RDBMSの世界とJavaオブジェクトの世界との間を繋いでくれる凄いヤツ」という理解でOKだ。
Create Table の代わりにappフォルダ以下にmodels パッケージを新規作成し、その中にGrpchat.java というクラスを下記の通り作成してもらいたい。
内容は後ほど説明するので、取りあえずコピペすればよい。

■Grpchat.java

package models;

import java.util.Date;
import java.util.List;
import javax.persistence.*;
import javax.validation.*;

import com.avaje.ebean.annotation.*;

import play.db.ebean.*;
import play.db.ebean.Model.Finder;
import play.data.validation.*;
import play.data.validation.Constraints.Required;

@Entity
public class Grpchat extends Model {
	@Id
	public Long id;

	public String username;

	public String message;

	public String photo;

	public String groupid;

	@CreatedTimestamp
	public Date posdate;

	public static Finder<Long, Grpchat> find =
			new Finder<Long, Grpchat>(Long.class, Grpchat.class);

	@Override
	public String toString(){
		return ("[id:"+id+",username:"+username+",photo:"+photo+",message:"+message+",date:"+posdate);
	}
    public void setPhoto(String photo) {
        this.photo = photo;
    }
    public String getUsername() {
        return this.username;
    }

    public void setUsername(String username) {
        this.username = username;
    }
    public String getPhoto() {
        return this.photo;
    }

    public String getGroupid() {
        return this.groupid;
    }

    public void setGroupid(String groupid) {
        this.groupid = groupid;
    }

    public void setMessage(String message){
    	this.message = message;
    }


}


上記クラスを配置したら、http://localhost:9000/inputchatにブラウザでアクセスしてみよう。すると、エラー画面「Database 'default' needs evolution!」とでるはずだ。
実はこのエラー画面はEabeanの仕業だ。
エラーの内容としては、grpchat というテーブルを作らないと、このクラスは使えませんぞ、
という意味となる。そしてgrpchat テーブルを作成するためのCREATE 文が表示されているわけだ。
よく見ると本画面の上段に「Apply this script now!」というボタンが表示されていると思う。これを押下してみよう。
すると、エラーメッセージが出なくなるはずだ。
次にMySQL のWorkbenchを起ち上げてみて欲しい。

chatapp ユーザで接続して、SHEMAS => chatapp => Tables 以下を見てみると。
何と驚くなかれ、grpchat というテーブルが作成されているのが分かる。

つまり、先ほどの「Apply this script now!」を押下した結果、MySQL 側でCREATE 文が発行されたのである。
その結果、Grpchatというオブジェクトに対応するテーブルが利用できるようになった。
これがEabean の機能なのである。
これにより、MySQL側のテーブルをJava オブジェクトとして自然にJava で開発できるのである。

では、先ほどのGrpchat.javaの内容を見ていこう。
内容は非常にシンプルで、id, username, message, grpid, photo, postdate といったフィールドとsetter とgetter メソッドを持つクラスである。
このフィールドをカラムに持つテーブルがMySQL側にCREATEされるのだが、grpchat テーブルをJava オブジェクト化したものという以外に何物でもない。

finder フィールドについては後ほど使うときになったら説明するが、テーブルからレコードを抽出するときに利用するフィールドと捉えればOKである。

Play Framework でMySQL を利用する(環境構築)

Play Framework でMySQL を利用するための環境を構築していく。

1. MySQL インストーラをダウンロードしてくる。

http://dev.mysql.com/downloads/mysql/
にアクセスし、
インストーラmysql-installer-community-5.6.22.0.msi」をダウンロードする。
(※)32-bit と書いてあるが、64-bit マシンであってもインストーラは同じ。

2. MySQL のインストール
(a) 上記msiファイルを実行する。
(b) License Agreement で同意して Next
(c) Setup Type ⇒ Full を選択
(d) Check Requirements でStatus にManual と記載されているもの以外を選択しExecute を押下
  ⇒ 必要なライブラリ一式をとってきてよしなにしてくれる。
(e) Installation でExecuteを押下
※いくつかセットアップに失敗すると思うが、特に気にせず進めてOK
(f) Configuration でNextを押下
(g) Type and Networking では、Development Machine を選択。後はデフォルトでOK。
(h) Accounts and Roles でroot(管理者) パスワードを設定する。
(i) Accounts and Roles でAdd User を押下してユーザ(一般ユーザ)を作成する。
  ユーザ名: chatapp
Role: DB Admin ※開発環境なので管理者権限を与えることとする。
(j) Windows Service で Next を押下
(k) Apply Server Configuration でExecute を押下
(l) Product Configuration でNext を押下
(m) Connect To Server でCheck ⇒ Next の順で押下
(n) Execute を押下

後は取りあえずNext とFinish を押下していけばOK。


3. スキーマを作成する。
先ほどchatapp というユーザを作成したが、このユーザが保有するスキーマを作成する。
MySQL Workbench を起ち上げて、root ユーザのコネクション(パネル)をダブルクリックする。
左側のNavigator のSCHEMASフィールドを右クリックして「Create Schema」を押下し、
Schema Nameを「chatapp」として、スキーマを作成すr。

4. コネクションの追加(接続設定)
最後に先ほど作成したchatappユーザ(スキーマ)向けのコネクション(接続設定)を追加する。
MySQL Workbench を起ち上げて、MySQL Connections のプラス(+)ボタンを押下する。
立ち上がった接続設定画面で、下記の通り設定する。
Connection Name: chatapp
Username: chatapp
Default Schema: chatapp

以上でMySQLのソフトウェアのセットアップは完了で、Workbench からchatapp ユーザでアクセスできるようになったはずだ。非常にEasy だったと思うがこれでいいのだ。

Play Framework でフォームを扱う(オウム返しアプリを作ろう)

前回の記事(↓)を踏まえて、今度は投稿フォームを利用したアプリ(オウム返しアプリ)をPlay Framework で作ってみる。

Play Framework で「Hello World!!」 - hirahiro56のブログ

作るアプリの機能としては、ユーザがフォームに入力した文字列をそのまま表示するだけのオウム返しアプリで実用性は無いものであるが、今後作るチャットアプリの基礎となるものである。

それでは、このオウム返しアプリを実現するのには、どの様なものを実装すれば良いのかを考えよう。
必要なものは下記の通りである。
つまり、入出力画面2つと、それぞれのリクエスト(URL)を処理するためのコントローラ2つの合計4つのモジュールを作成する必要がある。

・ユーザの入力を受け付けるView
・文字列を出力するView
・入力を受け付ける画面へアクセスする際のリクエスト(URL)を処理するコントローラ
・入力画面で入力内容を投稿した後に出力用のView へ画面遷移する際のリクエストを処理するコントローラ

早速順番に実装して行こう。実装の順番は私自身が実装する手順をそのまま記載することとする。

1. ルーティングの設定(routesファイルの設定)
ユーザの入力を受け付ける画面にアクセスするURLを"/inputchat" とし、routes ファイルに下記の通り記載しよう。

GET     /inputchat                  controllers.Application.inputchat()

この設定により、ユーザがブラウザでhttp://localhost:9000/inputchatのURLでアクセスすると、
"controllers.Application.inputchat()"が呼び出されるようになる。
次に、この"controllers.Application.inputchat()"を実装していく。

2. Controller の実装
controllers => Application.java を開いて下記の通りinputchat メソッドを実装する。

package controllers;

import play.mvc.Controller;
import play.mvc.Result;
import views.html.*; //要注意!!

public class Application extends Controller {
  //中略
    public static Result inputchat() {
        return ok(inputchat.render("InputChatForm"));
    }

}

Eclipse でエラーが出てしまうが、取りあえずは無視で良い。import 文を修正していることに注意。
因みにEclipse の機能でimport 文が勝手に修正されてしまう場合は、Eclipse のウィンドウ => 設定 => Java => 保管アクションの設定で、
インポートの編成のチェックを外しておけば良い。

続いて、"inputchat.render()" で呼び出されるView の実装に入る。

3. ユーザの入力を受け付けるViewの実装
HTML(Scala)ファイルが無ければ始まらないので、viewsパッケージ以下に「inputchat.scala.html」を作成し、
以下の通り編集する。取りあえずガワだけ用意する。

■inputchat.scala.html

@(message: String)

This resource is @message.

ここまでできたら、
http://localhost:9000/inputchat
にアクセスして、"This resource is InputChatForm."とブラウザ上に表示されればOKである。

次に、この画面(inputchat.scala.html)に入力フォームを配置してみよう。
そのためにはちょっとしたお作法があるので最初は慣れないと思うが、Play Framework 以外のフレームワークでも共通する作法となるので、
徐々に慣れていって欲しい。

4. フォームクラスを定義
Play Framework でフォームを利用する際(他のJava フレームワークでも同様だが)、フォームクラスというものを定義する。
なぜ、このようなクラスを定義するかというと、ブラウザ上のフォームの内容をJavaオブジェクトとして扱えるという利点があるのだが、
ピンとこない場合は「お作法なのね。」「便利なのね。」と軽い気持ちで理解しておけば、後々分かってくるであろう。

それでは早速フォームクラスを定義するのだが、app以下にformsというパッケージを作成し、
下記の通りユーザがインプットする内容をフィールド値として持つようなクラスを定義する。

■PostChatForm.java

package form;

import javax.persistence.*;
import java.util.*;
import play.data.validation.Constraints.*;
import play.data.validation.Constraints;

public class PostChatForm {
	public String postMessage;
	
	public void setPostMessage(String postMessage){
		this.postMessage = postMessage;		
	}
	
	public String getPostMessage(){
		return this.postMessage;		
	}

}

次にコントローラの中で下記の通り実装し、View へForm の型を通知する。

■Application.java

package controllers;

import play.*;
import play.mvc.*;
import play.mvc.Http.Context;
import models.*;

import views.html.*;
import play.mvc.Http.MultipartFormData;
import play.data.*;
import static play.data.Form.*;
import java.util.*;
import forms.*;

public class Application extends Controller {

    public static Result index() {
        return ok(index.render("Your new application is ready."));
    }

    public static Result inputchat() {

    	//フォームを定義
		Form<PostChatForm> postChatForm = new Form(PostChatForm.class);

        return ok(inputchat.render("InputChatForm",postChatForm));
    }

}


続いて、View 側で上記Controller から渡されたForm の型を受け取り、Play Framework のフォームヘルパーを用いて下記の通り実装する。

■inputchat.scala.html

@(message: String, postchatform: Form[forms.PostChatForm])

@import helper._

This resource is @message.

@form(routes.Application.index()) {
	@inputText(postchatform("postMessage"))
}

ここまでできたら、
http://localhost:9000/inputchat
へアクセスしてフォームが配置されていることを確認してみよう。
生成されたフォームのコードはブラウザのソースを表示から確認できるが、
下記コードによりフォーム用のHTMLを自動生成してくれていることが分かると思う。

@form(routes.Application.index()) {
	@inputText(postchatform("postMessage"))
}

5. フォーム内容表示画面の開発
まずは、上記作成したView を下記の通り書き換える。

@(message: String, postchatform: Form[forms.PostChatForm])

@import helper._

This resource is @message.

@form(routes.Application.postchat()) {
	@inputText(postchatform("postMessage"))
}

これにより、フォームを入力し投稿するとhttp://localhost:9000/postchatというURLでリクエストが飛ぶようになる。
それでは、このリクエスト"/postchat"に対する処理を実装していこう。
まずは、routesファイルを下記の通り記載する。
■routes

GET     /postchat                   controllers.Application.postchat()

次に、Application.java に、上記ルーティングに呼び出されるpoctchat()メソッドを追加する。
ここでのポイントは、FormクラスのbindFromRequest() でフォームの内容をインスタンス化するところである。フォームのname属性とFormクラスのフィールド名が同一であれば、フォームの値でフィールドを初期化されたFormクラスがインスタンス化される点である。
これは、今後多用するので覚えておきたい。

    public static Result postchat(){
    	//フォームの内容を取得して、PostChatFormクラスのインスタンスに格納する。
    	Form<PostChatForm> postChatForm = new Form(PostChatForm.class).bindFromRequest();
    	
    	//フォームの内容をpostMessageに代入する。
    	String postMessage = postChatForm.get().getPostMessage();

        return ok(postchat.render(postMessage));
    }

最後に、views以下に下記の投稿メッセージ出力用Viewを用意する。


■postchat.scala.html

@(postmessage:String)

@postmessage

ここまでできたら、下記URLにアクセスし、動作を確認してみよう。
フォームに入力した内容がそのままオウム返しされるはずだ。

http://localhost:9000/inputchat

Play Framework で「Hello World!!」

Play Framework の環境構築については、

id: hidemium の下記記事を参照されたい。



Play Framework(Java)の開発環境を構築する - hidemium's blog

上記手順でEclipse 上にPlay Framework のプロジェクトが作成された状態からスタートする。

何はともあれ、お決まりの「Hello World !!」アプリを作っていこう。

作成したプロジェクトのappフォルダ以下のviews => index.scala.html を

以下の様に編集しよう。

■index.scala.html

@(message: String)

Hello World!!


上記コードを保存し下記URLでアクセスしてみよう。
http://localhost:9000/

これで、無事「Hello World!!」 が表示されたはずだ。
Play Framework の世界へようこそ。

ところで、何も説明せずに「Hello World!!」を作ってみたが、簡単に処理の流れを説明する。

まず、ユーザのhttp://localhost:9000/に対するリクエストがどの様に処理されるかを順を追って説明する。

(1) フレームワークの入口はroutesファイル
ユーザのhttp://localhost:9000/に対するリクエストにより、どのようなプログラムが呼び出されるかは、
routes ファイルを見れば分かる。
Java EE 開発の経験があるならば、このroutes ファイルが web.xml に相当するものと理解して良い。

プロジェクトフォルダ => app => conf => routes ファイルを参照すると、
下記の記載があるはずだ。

■routes

GET     /                           controllers.Application.index()

"/" つまり、http://localhost:9000/にアクセスした場合、「controllers.Application.index()」を呼び出すと
読むことができる。

次に「controllers.Application.index()」がどこで実装されているかを説明する。

(2) リクエストの受け手(Controller)
app => controllers => Application.java を見て欲しい。

package controllers;

import play.*;
import play.mvc.*;

import views.html.*;

public class Application extends Controller {

    public static Result index() {
        return ok(index.render("Your new application is ready."));
    }

}


当該クラスで index() というメソッドが定義されている。つまり、先ほどのroutes ファイルに従って呼び出されるメソッドは、このApplication.index()メソッドとなる。

つまり、
http://localhost:9000/
というURLでアクセスするとApplication.index()メソッドが呼び出される流れとなる。

(3) ブラウザへ表示するためのView
Application.index() の下記記載を見て欲しい。

return ok(index.render("Your new application is ready."));

上記行により、ブラウザで表示するHTMLを作成するプログラム(View)の「index.scala.html」が呼び出され、その結果ブラウザ上に「Hello World!!」が表示されるのである。

■index.scala.html

@(message: String)

Hello World!!

(4) Controller からView への値の受け渡しについて
下記コードのindex.render(***)の引数を見て欲しい。ここで、"Your new application is ready." という文字列をView に渡している。

return ok(index.render("Your new application is ready."));

それでは、この文字列をView 側で受け止めて表示してみよう。下記の通りView を編集する。

■index.scala.html

@(message: String)

@message <br>
Hello World!!


同様にhttp://localhost:9000/にアクセスしてみよう。
すると、ブラウザに"Your new application is ready."の文字列が表示されるはずだ。
View 側では、
@(message: String)
で、コントローラ側からString 型の文字列を受け取り、message という変数に代入する。

そして、@message により代入した(コントローラから渡された)文字列を出力する。

ここで、お気づきだろうが、Play Framework ではView はScala で記載される。
@で始まる文字列はPlay Framework により、Scala 文法に従って解釈される。
Java EE 開発におけるJSP に相当するものである。


以上が、Play Framework における処理の流れである。
Java EE開発とは少々勝手が違うが、今後より複雑なアプリを作成することでPlay Framework の開発手法に慣れて欲しい。

Strutsを読む ~6日目: initInternal() の機能(結局のところ) ~

ActionServlet#init()#initInternal() では、結局のところ、

ActionResources*.properties の内容をkey - value 形式(HashMap)で

インスタンス化することが目的。

実際に読み込んでる箇所は、

PropertyMessageResources.loadLocale() の部分になることが分かった。

Debug 仕込んでこのメソッドが呼び出されているのは確実だが、

なぜこのメソッドが呼び出されるのかが未だ不明。。。

コンストラクタの中にも含まれていないのに。。。

まさか、internal.getMessage(*)で読み込んでるのか???

Strutsを読む ~5日目: ちょっとPending ファクトリを理解する。~

いきなり、ActionServlet#init()#initInternal()でつまずいた。

ただ、このメソッドの目的がKey=Value形式のPropertiesファイルをHashMap形式(MessageResources)のクラスにインスタンス化することがわかった。

「オブジェクト脳のつくり方」のp53までを読んでファクトリとやらを

噛みしめるべし。

 

 

 

Strutsを読む ~ 4日目: defaultFactory.createResources("org.apache.struts.action.ActionResources") ~

掲題のメソッドは抽象クラスで下記の通り定義されている。

public abstract MessageResources createResources(String config);

 

これを実装しているものが、PropertyMessageResourcesFactory クラスになり、

下記の通りコーディングされている。

#####################################################

public MessageResources createResources(String config) {

    PropertyMessageResources messageResources = 

        new PropertyMessageResources(this, config, this.returnNull);

        String mode = null;

        if (getConfig() != null) {

            mode = getConfig().getProperty("mode");

        }

    messageResources.setMode(mode);

    return messageResources;

}

#####################################################