§初めての Play アプリケーション
Play 2.0 でシンプルな TODO 管理アプリケーションを構築し、クラウドにデプロイしましょう。このサンプルはとても小規模で、数時間でやり通すことができます。
§前提条件
はじめに、 Play のインストールが成功している 事を確かめてください。 Java (バージョン 6 以上) のインストールと、Play のバイナリパッケージの unzip さえすれば始められます。
多くのコマンドを使用するため、 Unix 系 OS を利用する方が効率が良いです。 もし Windows システムを動作させているなら、コマンドプロンプトにいくつかのコマンドを入力する事で、動作させる事も可能です。
もちろん、テキストエディタは必要です。もし Eclipse や IntelliJ のようなフル機能の Java の IDE に慣れているのなら、それらを使うことも可能です。しかし、 Playであれば、編集や開発プロセス自身をフレームワークが管理してくれるため、 Textmate, Emacs もしくは vi のようなシンプルなテキストエディタで楽しみながら作業する事が可能です。
§プロジェクト作成
Play が正しくインストールされているとして、新しくアプリケーションを作成してみましょう。 Play アプリケーションを作成する事はとても簡単であり、 Play コマンドで全て管理されています。全ての Play アプリケーション間で標準のプロジェクト構成になります。
コマンドラインを開き以下のように入力してください:
$ play new todolist
いくつかの質問が表示されます。**simple Java application** を選択しましょう。
play new
コマンドが新しいディレクトリ todolist/
を作成し、一連のファイル、ディレクトリを生成します、重要なファイルは以下の通りです:
app/
ディレクトリには models 、 controllers 、そして views ディレクトリに分かれたアプリケーションのコアが入っています。本ディレクトリには .java ソースファイルが入っています。conf/
ディレクトリには全てのアプリケーションの設定ファイル (特にメインとなるapplication.conf
ファイル、routes
定義ファイル、国際化のためのmessages
ファイル) が入っています。project
ディレクトリにはビルドスクリプトが入っています。ビルドシステムは sbt に基づいています。新しい Play アプリケーションはアプリケーションを正常動作させるデフォルトのビルドスクリプトが同梱されています。public/
ディレクトリには全てのパブリックに利用可能なリソース (JavaScript 、スタイルシート、画像イメージ) が入っています。test/
ディレクトリにはアプリケーションのテストが入っています。 テストは JUnit で書くことができます。
Play では UTF-8 をエンコーディング形式として採用しており、上記のディレクトリ内に配置された全テキストファイルは本文字セットを使用してエンコードされます。テキストエディタに応じて設定を確認してください。Windows の場合はエディタの文字コード設定は ANSI にする必要があります。
§Play コンソールの使用
一度アプリケーションを作成したら、 Play コンソールを実行できます。新しく作成された todolist/
ディレクトリに行き、以下のコマンドを実行して下さい:
$ play
このコマンドで Play コンソールが起動されます。 Play コンソールから実行できることはいくつかありますが、まずアプリケーションを実行させてみましょう。コンソールプロンプトから、 run
とタイプしてください:
[todolist] $ run
development モードで本アプリケーションが実行されています。ブラウザを開き、 http://localhost:9000/ へアクセスしてください:
補足: 詳細は Play 2.0 コンソールを使う を確認してください。
§概要
このアプリケーションがどうやってページを表示しているか確認して行きましょう。
本アプリケーションの主なエントリーポイントは conf/routes
ファイルです。このファイルはアプリケーションがアクセス可能な URL の全てを定義しています。生成された routes ファイルを開けば、初期の route を確認できます:
GET / controllers.Application.index()
上記の記述は web サーバーが /
パスへの GET リクエストを受信した時に、 controllers.Application.index()
メソッドを呼び出すという事を簡単に説明しています。
どうやって controllers.Application.index
メソッドが実行されるのかを確認しましょう。 todolist/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."));
}
}
controllers.Application.index()
は Result
を返す事が確認できます。全てのアクションメソッドは web ブラウザへの HTTP レスポンスを表現する Result
を返す必要があります。
補足: アクションについての詳細は、 アクション、コントローラ、レスポンス を確認してください。
ここでは、アクションは HTML コンテントを含む 200 OK のレスポンスを返します。 HTML コンテントはテンプレートによって提供されます。 Play テンプレートは標準 Java メソッド、 views.html.index.render(String message)
としてコンパイルされます。
本テンプレートは app/views/index.scala.html
ソースファイル上で定義されます。
@(message: String)
@main("Welcome to Play 2.0") {
@play20.welcome(message)
}
最初の行は関数の仕様を定義します。ここでは、単一の String
型のパラメータを受け取ります。テンプレートの内容には Scala と HTML (もしくはテキストベースの言語) を一緒に記入しています。 Scala の式は特殊な @
文字から始まります。
ノート: テンプレートエンジンの式言語として Scala を使用していますが、心配する必要はありません。 Java 開発者にとっては問題にはならず、 Java とほとんど同等の物として使用することができるでしょう。
§開発フロー
さて、新しいアプリケーションにいくつかの変更を加えていきましょう。 Application.java
内でレスポンスの内容を書き換えてみます:
public static Result index() {
return ok("Hello world");
}
上記の変更によって index
アクションはシンプルな text/plain
の Hello world レスポンスを返すようになります。この変更を確かめるために、ブラウザ上でホームページを更新してみましょう:
変更を確認するのにコードを明示的にコンパイルする必要もサーバーを再起動する必要もありません。変更が検出されると、自動的にリロードされます。もし、コードを誤って変更したらどうなるでしょう? やってみましょう:
public static Result index() {
return ok("Hello world);
}
上記のように変更したら、ブラウザ上でホームページをリロードしてください:
確認された通り、ブラウザ上に直接エラーが表示されます。
§アプリケーションの準備
本 TODO 管理アプリケーションのために、いくつかのアクションと一致する URL が必要になります。 routes ファイルを定義することから始めましょう。
conf/routes
ファイルを編集してください。
# Home page
GET / controllers.Application.index()
# Tasks
GET /tasks controllers.Application.tasks()
POST /tasks controllers.Application.newTask()
POST /tasks/:id/delete controllers.Application.deleteTask(id: Long)
全てのタスクを一覧する route 定義を作成し、タスクの作成と削除を処理する2つの定義を作成します。タスク削除を扱うための route には URL パス内に id
変数を定義しています。 id
の値は deleteTask
アクションメソッドへ渡されます。
ブラウザをリロードすれば、 Play が routes ファイルをコンパイルできないことが確認できます。
これは routes ファイルが参照しているアクションメソッドが存在していないことが原因です。 Application.java
ファイルに追記していきましょう。
public class Application extends Controller {
public static Result index() {
return ok(index.render("Your new application is ready."));
}
public static Result tasks() {
return TODO;
}
public static Result newTask() {
return TODO;
}
public static Result deleteTask(Long id) {
return TODO;
}
}
上記のコードでアクションの暫定的な実装をするために TODO
という Result を使用しました。まだアクションの実装を書きたくない場合に、ビルドインの TODO
という Result を使用することが可能です。このアクションからは 501 Not Implemented
HTTP レスポンスが返るようになっています。
確認するために、 http://localhost:9000/tasks にアクセスできるか試してみましょう。
アクションの実装をはじめる前に修正が必要な最後の項目は index
アクションです。タスクの一覧ページに自動リダイレクトするようにします:
public static Result index() {
return redirect(routes.Application.tasks());
}
見ての通り、 303 See Other
の HTTP レスポンスを使用するために ok
の代わりに redirect
を使用しています。また、tasks
アクションの呼び出しに必要な URL を生成するため、リバースルーターを使っています。
補足: 詳細については HTTP ルーティング を参照してください。
§Task
モデルの準備
実装を続ける前にアプリケーション内で Task
の見え方を定義する必要があります。 class
を app/models/Task.java
ファイルに作成してください:
package models;
import java.util.*;
public class Task {
public Long id;
public String label;
public static List<Task> all() {
return new ArrayList<Task>();
}
public static void create(Task task) {
}
public static void delete(Long id) {
}
}
Task
の操作を管理するための static メソッドも上記のように作成しました。ここでは各操作のダミー実装を記載しましたが、このチュートリアルの後半、リレーショナルデータベースに関する項目の中でタスクを保存する実装を追加していきます。
§アプリケーションテンプレート
本アプリケーションはタスクリストとタスク作成フォームの両方を持つ単一のウェブページを用います。index.scala.html
テンプレートを変更していきましょう:
@(tasks: List[Task], taskForm: Form[Task])
@import helper._
@main("Todo list") {
<h1>@tasks.size() task(s)</h1>
<ul>
@for(task <- tasks) {
<li>
@task.label
@form(routes.Application.deleteTask(task.id)) {
<input type="submit" value="Delete">
}
</li>
}
</ul>
<h2>Add a new task</h2>
@form(routes.Application.newTask()) {
@inputText(taskForm("label"))
<input type="submit" value="Create">
}
}
2つのパラメータを受け取るように以下のテンプレートのシグネチャを変更しました:
- 表示用のタスクのリスト
- タスクのフォーム
フォーム作成用のヘルパー関数を提供する helper._
をインポートしました。これにより、 action
や method
属性がついた <form>
タグ付きの HTML を生成する form
関数や入力用のフォーム HTML を作成する inputText
関数が使用できます。
補足: 詳細については テンプレートエンジン と フォームテンプレートヘルパーの利用 を確認してください。
§タスクフォーム
Form
オブジェクトはバリデーションを含んだ HTML フォームの定義をカプセル化します。Task
クラスのためにフォームを作成しましょう。Application
コントローラに以下のコードを追加します。
static Form<Task> taskForm = Form.form(Task.class);
taskForm
の型はシンプルな Task
を生成するフォームである事を表す Form<Task>
型になります。その他に play.data.*
と models.*
をインポートする必要があります。
JSR-303 のアノテーションを使う事で Task
クラスに制約を追加することができます。label
フィールドを必須にしてみましょう。
package models;
import java.util.*;
import play.data.validation.Constraints.*;
public class Task {
public Long id;
@Required
public String label;
...
補足: 詳細は フォームの定義 を参照してください。
§最初のページをレンダリングする
現在、アプリケーションページを表示するために要求された全ての要素の用意ができました。 tasks
アクションを実装しましょう:
public static Result tasks() {
return ok(
views.html.index.render(Task.all(), taskForm)
);
}
本メソッドがタスクリストとタスクフォームを呼出し、 index.scala.html
テンプレートによってレンダリングされた HTML を含む 200 OK の結果を返します。
ブラウザ内で http://localhost:9000/tasks にアクセスしてみましょう:
§フォーム投稿処理
現在では、タスク作成フォームを投稿した場合でも、まだ TODO ページが取得されます。 newTask
アクションの実装をしていきましょう。
public static Result newTask() {
Form<Task> filledForm = taskForm.bindFromRequest();
if(filledForm.hasErrors()) {
return badRequest(
views.html.index.render(Task.all(), filledForm)
);
} else {
Task.create(filledForm.get());
return redirect(routes.Application.tasks());
}
}
リクエストデータが入力されている新しいフォームを作成するために bindFromRequest
を扱うことができます。もしフォーム内に何らかのエラーがあった場合、エラーを再表示します (ここでは、 200 OK の代わりに 400 Bad Request を使用します) 。もし何のエラーも発生しなければ、タスクを作成し、タスクリストにリダイレクトします。
補足: 詳細は フォーム送信を扱う を参考にしてください。
§データベース内のタスクを永続化する
アプリケーションを使いやすくするため、データベースにタスクの情報を永続化するようにしましょう。本アプリケーション内でデータベースを利用可能にします。 conf/application.conf
ファイル内に以下の内容を追記してください:
db.default.driver=org.h2.Driver
db.default.url="jdbc:h2:mem:play"
現在、 H2 というシンプルなインメモリデータベースを使用するようにしています。この定義を有効にするためにサーバーを再起動する必要はありません、ブラウザを更新するだけでデータベースをセットアップします。
これからチュートリアル内でデータベースを検索するために (Play のデフォルト ORM である) Ebean を使用します。使用するためには application.conf
ファイルで有効にする必要があります。
ebean.default="models.*"
これで、 default
データソースに接続された Ebean サーバーが作成され、models
パッケージ以下にある全てのエンティティを管理するようになります。Task
クラスを正しい Ebean エンティティに変換しましょう。
package models;
import java.util.*;
import play.db.ebean.*;
import play.data.validation.Constraints.*;
import javax.persistence.*;
@Entity
public class Task extends Model {
@Id
public Long id;
@Required
public String label;
public static Finder<Long,Task> find = new Finder(
Long.class, Task.class
);
...
Task
クラスを play.db.ebean.Model
スーパークラスから継承するようにしました。これで Play 組み込みの Ebean ヘルパーにアクセスする事ができます。また永続化のためのアノテーションと、クエリを作成するための find
ヘルパーを作成しました。
CRUD 操作のメソッドを実装しましょう。
public static List<Task> all() {
return find.all();
}
public static void create(Task task) {
task.save();
}
public static void delete(Long id) {
find.ref(id).delete();
}
本アプリケーションで遊んでみましょう、新しいタスク作成は動作すると考えられます。
補足: 詳細は Ebean を参照してください。
§タスクの削除
タスクを作成することができるようになった後はそれらのタスクを削除する事ができるようにする必要があります。 deleteTask
アクションの実装を (とてもシンプルに) 終わらせるだけです。
public static Result deleteTask(Long id) {
Task.delete(id);
return redirect(routes.Application.tasks());
}
§Heroku へのデプロイ
全ての機能が完成しました。本アプリケーションを成果物としてデプロイしましょう。 Heroku へデプロイします。はじめに Procfile
を Heroku 用に作成する必要があります。 Procfile
をアプリケーションの root ディレクトリに作成してください。
web: target/start -Dhttp.port=${PORT} -DapplyEvolutions.default=true -Ddb.default.url=${DATABASE_URL} -Ddb.default.driver=org.postgresql.Driver
補足: Heroku へのデプロイの詳細については Heroku へのデプロイ を参照してください。
Heroku 上で起動する際に、アプリケーションの設定を上書きするためにシステムプロパティを使用します。 Heroku は PostgreSQL データベースを提供しているため、 要求されたドライバーをアプリケーションの依存関係に追加する必要があります。
project/Build.scala
ファイルに依存関係を明記してください。
val appDependencies = Seq(
"postgresql" % "postgresql" % "8.4-702.jdbc4"
)
補足: 依存性の管理については 依存性の管理 を参照して下さい。
Heroku はアプリケーションのデプロイに git を使用します。まずは git リポジトリを初期化しましょう。
$ git init
$ git add .
$ git commit -m "init"
これで Heroku 上にアプリケーションを作ることが出来ます。
$ heroku create --stack cedar
Creating warm-frost-1289... done, stack is cedar
http://warm-1289.herokuapp.com/ | [email protected]:warm-1289.git
Git remote heroku added
さらに git push heroku master
を使ってアプリケーションをデプロイします。
$ git push heroku master
Counting objects: 34, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (20/20), done.
Writing objects: 100% (34/34), 35.45 KiB, done.
Total 34 (delta 0), reused 0 (delta 0)
-----> Heroku receiving push
-----> Scala app detected
-----> Building app with sbt v0.11.0
-----> Running: sbt clean compile stage
...
-----> Discovering process types
Procfile declares types -> web
-----> Compiled slug size is 46.3MB
-----> Launching... done, v5
http://8044.herokuapp.com deployed to Heroku
To [email protected]:floating-lightning-8044.git
* [new branch] master -> master
Heroku はアプリケーションを構築し、クラウド上のどこかのノードにデプロイします。アプリケーションのプロセス状態を確認できます。
$ heroku ps
Process State Command
------------ ------------------ ----------------------
web.1 up for 10s target/start
起動したら、ブラウザで開いてみましょう。
あなたの初めてのアプリケーションがアップロードされ、稼働しています!