developer's diary

最近はc#のエントリが多いです

play framework2をjenkinsでコンパイルする場合。-Dsbt.log.noformat=trueのオプションをつける

問題

jenkinsでコンパイルする際ののログに以下のように色付きの文字がおかしくログに残る。

0m[[0minfo[0m] [0mLoading project definition from ....

jenkinsで単純に以下のコマンドを実行するようにしていると、色付きの文字がおかしくログに残ってしまいます。

~/play2.1.1/play clean update compile test

解決方法

オプションを追加

play -Dsbt.log.noformat=true clean update compile test

すると色付きでおかしかった文字が問題なく色なしでログに残ります。

[info] Loading project definition from ....

ソース探索

Play20 / framework / src / console / src / main / scala / Console.scalaの200行目あたりに今回の該当する部分がありました。

object Colors {

  import scala.Console._

  lazy val isANSISupported = {
    Option(System.getProperty("sbt.log.noformat")).map(_ != "true").orElse {
      Option(System.getProperty("os.name"))
        .map(_.toLowerCase)
        .filter(_.contains("windows"))
        .map(_ => false)
    }.getOrElse(true)
  }

  def red(str: String): String = if (isANSISupported) (RED + str + RESET) else str
  def blue(str: String): String = if (isANSISupported) (BLUE + str + RESET) else str
  def cyan(str: String): String = if (isANSISupported) (CYAN + str + RESET) else str
  def green(str: String): String = if (isANSISupported) (GREEN + str + RESET) else str
  def magenta(str: String): String = if (isANSISupported) (MAGENTA + str + RESET) else str
  def white(str: String): String = if (isANSISupported) (WHITE + str + RESET) else str
  def black(str: String): String = if (isANSISupported) (BLACK + str + RESET) else str
  def yellow(str: String): String = if (isANSISupported) (YELLOW + str + RESET) else str

}

色づけの条件(isANSISupported)に、

Option(System.getProperty("sbt.log.noformat")).map(_ != "true")

とあるので、-Dsbt.log.noformat=trueとしておくことで色づけされなくなります。

play framework2 の起動(run)でポートを指定する方法と、失敗パターン

-Dオプションでポートを指定する

play -Dhttp.port=9001  run 

すると↓のログが出力されます。

[info] play - Listening for HTTP on /0:0:0:0:0:0:0:0%0:9001

失敗するパターン

play  run -Dhttp.port=9001 

この方法だと失敗します。

[info] play - Listening for HTTP on /0:0:0:0:0:0:0:0%0:9000

runの後ろにオプションを指定しても反映されない。 デフォルト値の9000で実行されます。

※追加するオプションはrunの手前で実行する。

ソースを覗く

port指定する場所をソースからみてみます。

Play20 / framework / src / play / src / main / scala / play / core / server / NettyServer.scalaの230行目あたり。

val server = new NettyServer(
    new StaticApplication(applicationPath),
    Option(System.getProperty("http.port")).map(Integer.parseInt(_)).getOrElse(9000),
    Option(System.getProperty("https.port")).map(Integer.parseInt(_)),
    Option(System.getProperty("http.address")).getOrElse("0.0.0.0")
)

ここで指定しているオプションは、http.port、https.port、http.addressの3つ。

OptionはScala特有の記述方法で、PHPでいうとissetとかempty関数みたいな利用方法のよう。 scala-cookbook:Option Monad nullや存在しない値の処理を隠蔽するに詳しく書いてました。

Scalaの見方が分かってくると少しずつ楽しくなってきます。

def createServer(applicationPath: File): Option[NettyServer] = {
//~~~省略~~~
}

createServerという関数?の定義にOption[NettyServer]が利用されていて、 使っているところが以下の部分です。

 def main(args: Array[String]) {
    args.headOption.orElse(
      Option(System.getProperty("user.dir"))).map(new File(_)).filter(p => p.exists && p.isDirectory).map { applicationPath =>
        createServer(applicationPath).getOrElse(System.exit(-1))
      }.getOrElse {
        println("Not a valid Play application")
      }
  }

Optionが多用されてますね。user.dirのプロパティがあれば、 その値を元にFileオブジェクトを生成してexistsとisDirectorycで存在チェックし、問題なければサーバ起動という処理の流れです。

System.getProperty("user.dir")は何を示しているのか。

System.getProperty("user.dir")が何を表すのか分からなかったので、調べてみました。 ソースを探しても見つからないのでググる。 プロパティで指定したキー("user.dir")はカレントディレクトリだそうです。

応用 Java Application - Utility classes - Propertiesにありました。

代表的なプロパティのキーバリューをコピペして整形してメモる。

key value
file.separator ファイルの区切り文字(UNIXなら、'/')
java.class.path Java クラスパス
java.class.version Java クラスのバージョン
java.home Java がインストールされているディレクトリ
java.vendor.url Java ベンダのURL
java.version Java のバージョン
line.separator 行の区切り
os.arch OSのアーキテクチャ
os.name OS名
path.separator パスの区切り(':')
user.dir カレント・ディレクトリ
user.home ユーザのホーム・ディレクトリ
user.name ユーザ名

他の言語であれば、envとかで取得する項目のようですが、javaではプロパティを利用するそうです。 .netでいうと、ConfigurationSettingsみたいなものでしょうか。

ec2のmicroインスタンスでPlay!2.1.1を動かす。

play frameworkに関するのslideshare(Play2.0+Javaでサービスを本番稼働させた話 #play_ja)を見ててそうそうと自分もハマった部分があったので、自分なりの解決策を。

問題

Amazon EC2 microでPlay!2.0が起動しない。
java -­‐Xms512M -­‐Xmx1536M
でPlay!2.0はデフォルト起動される      
  • --Xms512M:初期ヒープサイズを512Mに設定
  • --Xmx1536M:最大ヒープサイズを1536Mに設定
  • microインスタンスのメモリは613 Mでswapがない

解決策

cloud-initのYAMLをgistに書いてみました。

ec2のマイクロインスタンスでplayを動かす準備 ・時間の設定 ・文字コードの設定 ・swap領域の作成

cloud-initを利用して、インスタンス立ち上げ時にswapを作るという方法です。

swapの作成方法はAdding swap to an EC2 micro instance on Amazonを参考にしました。

いつものスポットインスタンスの起動スクリプトに組み込んで使ってます。

#!/bin/bash

AMI='ami-xxxxxxxx';
INSTANCE_TYPE='t1.micro';
PRICE='.003';
SUBNET='subnet-xxxxxxxx';
KEYPAIR='xxxxxxxxxx';
SECURITYGROUP='sg-xxxxxxxx';
DATA='#cloud-config
repo_upgrade: all
packages:
  - java-devel
  - git

runcmd:
  - [cp, /usr/share/zoneinfo/Asia/Tokyo, /etc/localtime]
  - [sed, -i, 's/LANG="en_US.UTF-8"/LANG="ja_JP.UTF-8"/', /etc/sysconfig/i18n]
  - [dd, if=/dev/zero, of=/var/swapfile, bs=1M, count=1024]
  - [mkswap, /var/swapfile]
  - [swapon, /var/swapfile]
';

#スポットインスタンスのリクエストを行う
SIR_ID=` ec2rsi $AMI -t $INSTANCE_TYPE -p $PRICE -n 1 -r one-time -s $SUBNET -k $KEYPAIR -g $SECURITYGROUP -d "$DATA" | awk '{if(NR==1) print $2;}' `

#2分間待機
sleep 120

TMP_EC2='';
while true; do
  #ec2のインスタンス情報取得(スポットインスタンスのIDを指定)
  TMP_EC2=`ec2din --filter "spot-instance-request-id=$SIR_ID"`;

  if test ${#TMP_EC2} -gt 0 ; then
    #情報が取得できた場合

    #インスタンス情報、ローカルIPの情報を取得する
    INSTANCE_ID=`echo $TMP_EC2 | awk '{ print $5 }'`;
    LOCAL_IP=`echo $TMP_EC2 | awk '{ print $52 }'`;

    #ループを抜ける
    break
  fi

  #30秒待機
  sleep 30
done

#IPアドレスを取得する
EIP_OBJ=`ec2allocaddr -d vpc`;
EIPALLOC=`echo $EIP_OBJ | awk '{ print $4 }'`
GLOBAL_IP=`echo $EIP_OBJ | awk '{ print $2 }'`

sleep 10

#IPアドレスをec2インスタンスに割り当てる
EIPASSOC_OBJ=`ec2assocaddr -a $EIPALLOC  -i $INSTANCE_ID -p $LOCAL_IP `

sleep 10

EIPASSOC=`echo $EIPASSOC_OBJ | awk '{ print $4}'`

#結果出力
echo "[info]instance_id: $INSTANCE_ID";
echo "[info]globalId: $GLOBAL_IP";
echo "[info]eipassoc: $EIPASSOC";
echo "[info]eipalloc: $EIPALLOC";
echo "[example] ssh -i ${KEYPAIR}.pem ec2-user@${GLOBAL_IP} ";
echo "[example] ec2disaddr -a $EIPASSOC ; ec2reladdr -a $EIPALLOC ; ec2kill $INSTANCE_ID ; "

なんだかんだいって、microインスタンスとsmallインスタンスには価格的な大きな壁があるので、 play framework2もmicroで使いたいです。 24h365d動かすならawsは少し高い気がしますが、実験はawsが手っ取り早いです。

iframeフォーエバー! play20にあるcometのサンプルを改造して簡易チャットルームを作りました。

akkaのアクターモデルがちょびっとだけ理解できたmitsugeekです。

今回はplay2.1.1のsampleにあるcomet-clockを改造して簡単なチャットルームを作ってみました。

cometを利用したwebの時刻通知アプリcomet-clock

comet-clockは↓のような画面で、サーバから100ミリ秒毎に時刻をブラウザにpushするアプリです。 cometの実装方法としてiframeを利用してscriptタグのレスポンスを小刻みに返す方法で、Forever Iframeと呼ぶそうです。

f:id:mitsugi-bb:20130502003052p:plain

このアプリがplay framework2のサンプルとして付属してます。 今回はこのサンプルを改造して、チャットアプリにしてみました。

イメージ・・・

f:id:mitsugi-bb:20130502003447p:plain

ニックネームを入力してチャットルームに入室。

f:id:mitsugi-bb:20130502003525p:plain

入力域に文字を入力して、送信を押すとメッセージが送信されます。

f:id:mitsugi-bb:20130502003619p:plain

入室と退室(ブラウザを閉じる)とメッセージの送信それだけです。 メッセージの保存等は行っていません。超シンプルです。

別タブでアクセスして、入室すると入室したことがリアルタイムで通知されます。

f:id:mitsugi-bb:20130502003824p:plain

ブラウザを閉じると、退室も通知します。

f:id:mitsugi-bb:20130502003857p:plain

ipadで簡単に試したのですが(wifi)、safariを閉じて別のアプリを起動してからsafariに戻っても、通信はそのまま繋がっているみたいでびっくりしました。枯れた技術の組み合わせでリアルタイムなチャットができるんだとちょっとだけ感動。

感想

lingrには到底及ばないですが、ひとまずチャットができるところまで作ってみましたのでgithubに上げてときます。(iframeを利用したcometですので通信が途切れたかどうかの判定ができてませんし、XSSの脆弱性あります。)

今回はcometという技術とアクターモデルというアーキテクチャを噛み砕きながら作っていく感じでかなり勉強になりました。技術的にはまだまだ身にしみない感じです。ココ(ie10関連ページ)見る限り、IE10ではComet ストリーミングのサポートって書いてて、XMLHttpRequestの応答をマルチパートで返せるみたいな感じらしいので、ajaxでcometするのが良いお作法なんだと思います。

このサンプルを動かしてみたい

#java-develのパッケージをインストール(playがjavacを利用します。)
sudo yum install java-devel
#ホームディレクトリで作業
cd ~
#play2.1.1をダウンロード
wget http://downloads.typesafe.com/play/2.1.1/play-2.1.1.zip
#解凍
unzip play-2.1.1.zip 
#チャットアプリを取得
git clone https://github.com/mitsugeek/comet_chat_play2_example_iframe.git
#カレントディレクトリを移動
cd comet_chat_play2_example_iframe/
#アプリ起動
~/play-2.1.1/play run

以上で 9000番ポートにアクセスすると動くと思います。

参考

GMOクラウド VPSにplay2.1.0のセットアップ

yum install wget
yum install unzip
yum install java-1.6.0-openjdk
yum install java-1.6.0-openjdk-devel
wget http://downloads.typesafe.com/play/2.1.0/play-2.1.0.zip
unzip play-2.1.0.zip
vi .bash_profile #> play-2.1.0のパスを.bash_profileに追加
source .bash_profile

.bash_profile

# .bash_profile

# Get the aliases and functions
if [ -f ~/.bashrc ]; then
        . ~/.bashrc
fi

# User specific environment and startup programs

PATH=$PATH:$HOME/bin
PATH=$PATH:$HOME/play-2.1.0

export PATH

playのセットアップ完了。

アプリの作成

play new test #>[Enter] [2](java) [Enter]
cd test
play clean compile test stage #クリーン、コンパイル、テスト、
./stage/start #> アプリケーション実行

f:id:mitsugi-bb:20130314074015p:plain

これでOK!

少しだけソース修正

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.")
        return ok(index.render("あなたの新しいアプリケーションの準備完了!")
    }

}

コンパイル〜テスト。。。

play clean compile test stage
[info] Loading project definition from /root/test/project
[info] Set current project to test (in build file:/root/test/)
[success] Total time: 0 s, completed 2013/03/14 7:43:48
[info] Updating {file:/root/test/}test...
[info] Resolving org.hibernate.javax.persistence#hibernate-jpa-2.0-api;1.0.1.Fin                                                                                [info] Done updating.                                                        
[info] Compiling 4 Scala sources and 2 Java sources to /root/test/target/scala-2.10/classes...
[success] Total time: 17 s, completed 2013/03/14 7:44:05
[info] Compiling 2 Java sources to /root/test/target/scala-2.10/test-classes...
[error] Test IntegrationTest.test failed: java.lang.AssertionError: <'<?xml version="1.0" encoding="utf-8"?>
[error] <html>
[error]   <head>
[error]     <title>
[error]       Welcome to Play 2.1
[error]     </title>
[error]     <link rel="stylesheet" media="screen" href="/assets/stylesheets/main.css"/>
[error]     <link rel="shortcut icon" type="image/png" href="/assets/images/favicon.png"/>
[error]     <script src="/assets/javascripts/jquery-1.9.0.min.js" type="text/javascript">
[error]     </script>
[error]   </head>
[error]   <body style="zoom: 1;">
[error]     <h1>
[error]       あなたの新しいアプリケーションの準備完了!
[error]     </h1>
[error]   </body>
[error] </html>
[error] '> should contain the String:<'Your new application is ready.'>
[error]     at play.test.Helpers.running(Helpers.java:426)
[error]     at IntegrationTest.test(IntegrationTest.java:20)
[error]     ...
[error] Caused by: java.lang.AssertionError: <'<?xml version="1.0" encoding="utf-8"?>
[error] <html>
[error]   <head>
[error]     <title>
[error]       Welcome to Play 2.1
[error]     </title>
[error]     <link rel="stylesheet" media="screen" href="/assets/stylesheets/main.css"/>
[error]     <link rel="shortcut icon" type="image/png" href="/assets/images/favicon.png"/>
[error]     <script src="/assets/javascripts/jquery-1.9.0.min.js" type="text/javascript">
[error]     </script>
[error]   </head>
[error]   <body style="zoom: 1;">
[error]     <h1>
[error]       あなたの新しいアプリケーションの準備完了!
[error]     </h1>
[error]   </body>
[error] </html>
[error] '> should contain the String:<'Your new application is ready.'>
[error]     at org.fest.assertions.Fail.failure(Fail.java:228)
[error]     at org.fest.assertions.Assert.failure(Assert.java:149)
[error]     at org.fest.assertions.StringAssert.contains(StringAssert.java:73)
[error]     at IntegrationTest$1.invoke(IntegrationTest.java:23)
[error]     at IntegrationTest$1.invoke(IntegrationTest.java:20)
[error]     at play.test.Helpers.running(Helpers.java:424)
[error]     ... 32 more
[info] IntegrationTest
[info] x IntegrationTest.test
[info] 
[info] 
[info] Total for test IntegrationTest
[info] Finished in 0.039 seconds
[info] 1 tests, 1 failures, 0 errors
[info] ApplicationTest
[info] + ApplicationTest.simpleCheck
[info] + ApplicationTest.renderTemplate
[info] 
[info] 
[info] Total for test ApplicationTest
[info] Finished in 0.014 seconds
[info] 2 tests, 0 failures, 0 errors
[error] Failed: : Total 3, Failed 1, Errors 0, Passed 2, Skipped 0
[error] Failed tests:
[error]     IntegrationTest
java.lang.RuntimeException: Tests unsuccessful
    at scala.sys.package$.error(package.scala:27)
    at scala.Predef$.error(Predef.scala:123)
    at sbt.Tests$.showResults(Tests.scala:192)
    at sbt.Defaults$$anonfun$testTasks$6.apply(Defaults.scala:296)
    at sbt.Defaults$$anonfun$testTasks$6.apply(Defaults.scala:294)
    at sbt.Scoped$$anonfun$hf4$1.apply(Structure.scala:580)
    at sbt.Scoped$$anonfun$hf4$1.apply(Structure.scala:580)
    at scala.Function1$$anonfun$compose$1.apply(Function1.scala:49)
    at sbt.Scoped$Reduced$$anonfun$combine$1$$anonfun$apply$12.apply(Structure.scala:311)
    at sbt.Scoped$Reduced$$anonfun$combine$1$$anonfun$apply$12.apply(Structure.scala:311)
    at sbt.$tilde$greater$$anonfun$$u2219$1.apply(TypeFunctions.scala:41)
    at sbt.std.Transform$$anon$5.work(System.scala:71)
    at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:232)
    at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:232)
    at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:18)
    at sbt.Execute.work(Execute.scala:238)
    at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:232)
    at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:232)
    at sbt.ConcurrentRestrictions$$anon$4$$anonfun$1.apply(ConcurrentRestrictions.scala:160)
    at sbt.CompletionService$$anon$2.call(CompletionService.scala:30)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
    at java.util.concurrent.FutureTask.run(FutureTask.java:166)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
    at java.util.concurrent.FutureTask.run(FutureTask.java:166)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1146)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:679)
[error] (test:test) Tests unsuccessful
[error] Total time: 9 s, completed 2013/03/14 7:44:14

IntegrationTestで失敗。

「inMemoryDatabaseでポート3333でfakeApplicationを立ち上げて、ブラウザでアクセスして結果に該当の文字列が含まれているかどうか」というテストに引っかかっておりました。

というわけでテスト修正

import org.junit.*;

import play.mvc.*;
import play.test.*;
import play.libs.F.*;

import static play.test.Helpers.*;
import static org.fest.assertions.Assertions.*;

import static org.fluentlenium.core.filter.FilterConstructor.*;

public class IntegrationTest {

    /**
     * add your integration test here
     * in this example we just check if the welcome page is being shown
     */   
    @Test
    public void test() {
        running(testServer(3333, fakeApplication(inMemoryDatabase())), HTMLUNIT, new Callback<TestBrowser>() {
            public void invoke(TestBrowser browser) {
                browser.goTo("http://localhost:3333");
//                assertThat(browser.pageSource()).contains("Your new application is ready.");
                assertThat(browser.pageSource()).contains("あなたの新しいアプリケーションの準備完了!");
            }
        });
    }
  
}

再度コンパイル、テスト。でそのまま実行

play clean compile test stage
./target/start

f:id:mitsugi-bb:20130314075119p:plain

プログラム修正ができました。

play framework 2.1.0(java) + redisをサクッと試す。(2)

前回の続きです。

前回はplay framework2.0.xだったのだけれども、playがバージョンアップしたので今回は2.1.0でjedisを使ってみました。

versionアップに関しては、この辺の差分を見れば前回のソースを利用できると思いますが、今回はplay new からのやり直しで試しました。 基本的には前回と同じ手順で問題ありません。

Application.javaの変更点は、redisへのセットだけです。

jedis.set("test", "residOK!");

一応ソース全体を。

package controllers;

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

import views.html.*;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class Application extends Controller {

    public static Result index() {
        JedisPool pool = new JedisPool(new JedisPoolConfig(), "localhost");
        Jedis jedis = pool.getResource();
        String result = "";
        try {
            jedis.set("test", "residOK!");
            result = jedis.get("test");
        } finally {
            pool.returnResource(jedis);
        }
        pool.destroy();
        return ok(index.render("jedis.set:" + result));
    }

    public static Result redisHello(){
        return ok("test");
    }

}

f:id:mitsugi-bb:20130224233046p:plain

次回はredisのソートとかをやってみたいと思います。

play framework(java) + redisをサクッと試す。(1)

playのインストールは このへんを参考に。

redisのインストールは redis 2.2.12のインストールメモ(amazon Linux)を参考に。

では早速playのプロジェクトを作るところから、ライブラリの導入等まで。

・プロジェクト作成

 play new redisplay

・jedis導入(redisのjavaクライアントの1つ)

 cd redisplay/
 mkdir lib
 wget https://github.com/downloads/xetorthio/jedis/jedis-2.1.0.jar

・commons-pool導入(jedisが必要としている)

 wget http://ftp.riken.jp/net/apache//commons/pool/binaries/commons-pool-1.6-bin.tar.gz
 tar zxvf commons-pool-1.6-bin.tar.gz 
 mv commons-pool-1.6/commons-pool-1.6.jar  ./
 rm commons-pool-1.6 -rf
 rm commons-pool-1.6-bin.tar.gz  

・ライブラリの導入ができたので、次にソースの修正

 cd ../
 vim app/controllers/Application.java 
package controllers;

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

import views.html.*;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class Application extends Controller {

  public static Result index() {
    
    JedisPool pool = new JedisPool(new JedisPoolConfig(), "localhost");
    Jedis jedis = pool.getResource();

    String result = "";

    try {
      result = jedis.get("testKey");
    } finally {
      pool.returnResource(jedis);
    }

    pool.destroy();

    return ok(result);
  }

}

そして実行

play run

ブラウザで確認。

f:id:mitsugi-bb:20130201015425p:plain

エラー発生。redisの戻り値がnullの為エラー。

・redis-cliを立ち上げて値をセットする

[root@localhost redisplay]#  ~/redis-2.6.9/src/redis-cli
redis 127.0.0.1:6379> set testKey "test ABC"
OK

セットできたので、 アプリを再度起動して、ブラウザでアクセス。

f:id:mitsugi-bb:20130201020045p:plain

test ABCと画面に表示されました。いい感じです。

今回はredis導入して、redisから値を取得して画面に表示する所までサクッと試してみました。 次回は値のセットをやってみたいと思います。

参考: http://www.playframework-ja.org/documentation/2.0.2/SBTDependencies http://d.hatena.ne.jp/hrendoh/20110901/1314887550 http://blog.livedoor.jp/tattyamm/archives/4208836.html

centOSにplay2.0をインストール。

rootで作業を開始

sudo su -
cd ~/

java-1.6.0-openjdk-develをインストール

yum install java-devel

playをダウンロードして展開

wget http://download.playframework.org/releases/play-2.0.4.zip
unzip play-2.0.4.zip

playコマンドをどこでも利用できるようにPATHに追加する為、.bash_profileを編集

vim ~/.bash_profile

PATHにplayを追加

export PATH="$PATH":$JAVA_HOME/bin:$HOME/play-2.0.4

変更内容を反映

source ~/.bash_profile

playを試す

play new HelloWorld
cd HelloWorld
play run

ブラウザで、9000番ポートにアクセス。

f:id:mitsugi-bb:20130120225459p:plain

参考: http://ottokeyo.blogspot.jp/2012/09/centosplay-framework.html