play2.4 になってだいぶかわったので書き直し
JAVA 用です!
はじめに†
play コマンドがなくなって activator コマンドになった
activator コマンドがなくなって sbt コマンドになった(May/2017)(んもう!
インストールも実行も全部 activator
mkdir /usr/local/play24
https://www.playframework.com/download
install†
activator をダウンロードしてどこかに置く
cygwin では実行するとエコーを奪われてしまうので戻す必要がある
.bashrc
function activator(){
/usr/local/play24/activator $@
stty sane
}
function playkill(){
ps aux -W |grep java |uniq | head -1 |awk '{print $1;}' |xargs kill
}
alias activatordebug='activator -jvm-debug 9999 run'
eclipse†
eclipse
hello world†
activator
UI で、java のシンプルなテンプレートを選ぶ
cd helloworld
2.4 でいろいろ変わった設定†
- play eclipse がプリインストールではなくなりました。
- ebean がプリインストールではなくなりました。
- evolution がデフォではなくなりました
- PlayEbean がデフォではなくなりました
project/plugins.sbt
addSbtPlugin("com.typesafe.sbt" % "sbt-play-ebean" % "1.0.0")
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "4.0.0-RC2")
activator
eclipse with-source=true
exit
build.sbt
下記行を変更
lazy val root = (project in file(".")).enablePlugins(PlayJava,PlayEbean)
fork in run := false
sources in (Compile,doc) := Seq.empty
publishArtifact in (Compile, packageDoc) := false
import com.typesafe.sbt.packager.Keys.scriptClasspath
scriptClasspath := {
val originalClasspath = scriptClasspath.value
val manifest = new java.util.jar.Manifest()
manifest.getMainAttributes().putValue("Class-Path", originalClasspath.mkString(" "))
val classpathJar = (target in Universal).value / "lib" / "classpath.jar"
IO.jar(Seq.empty, classpathJar, manifest)
Seq(classpathJar.getName)
}
mappings in Universal += (((target in Universal).value / "lib" / "classpath.jar") -> "lib/classpath.jar")
libraryDependencies ++= に追加
evolutions,
filters,
conf/application.conf
include "server.conf"
下記コマンドでシークレットを作成
activator playGenerateSecret
conf/server.conf
新規作成して、下記行に、上記コマンドで作られたキーを入力
play.crypto.secret = "xxxxxxxxxxxxxxxxxxxxx"
conf/routes
Play では基本的に html 等は jar にまとめられてしまうため、生のファイルをリンクしたい場合は下記行を追加
GET /files/*file controllers.Assets.versioned(path="/files", file: Asset)
eclipse でインポート†
外部ツールの構成 -> プログラム
ロケーション | c:\cygwin\usr\local\play24\activator.bat |
作業ディレクトリー | ${project_loc} |
引数 | -Djava.net.preferIPv4Stack=true -jvm-debug 9999 run |
DBをpostgres†
- DBを作成しておく
psql -U postgres
create database play24base;
build.sbt
libraryDependencies += "org.postgresql" % "postgresql" % "9.4-1201-jdbc41"
activator compile
activator eclipse
conf/server.conf
db.default.driver=org.postgresql.Driver
db.default.url="jdbc:postgresql://localhost:5432/play24base"
db.default.username="postgres"
db.default.password="postgres"
ebean.default=["models.*"]
debug†
break point†
activator -jvm-debug 9999 run
eclipse で 9999をデバッグポートに指定
log†
ログに日付を†
conf/logback.xml
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%-5level] - %logger - %message%n%xException</pattern>
RAW SQL をログ出力†
conf/logback.xml
<logger name="org.avaje.ebean.SQL" level="TRACE"/>
dist†
デプロイ用パッケージの作成
activator dist
下記に生成されている
target\universal\xxxxx.zip
このファイルをサーバに持っていって展開
bin 以下にある実行ファイルを実行
ちなみに、conf 以下のフォルダはそのままパックされるのでもって行きたい外部ファイル等はconf 以下に置くと楽
設定でかけるとは思うけれど・・
public/ 以下の画像ファイル等は jar に固められている
起動スクリプト†
#!/usr/bin/bash
BIN=appname
APP=${BIN}-1.0-SNAPSHOT
ps -auxww |grep java |grep ${APP} |awk '{print $2;}' |xargs kill -9 2>/dev/null
rm -rf ${APP}/RUNNING_PID
rm -rf ${APP}
cp ~/${APP}.zip ./
unzip ${APP}.zip
cp service.conf ${APP}/conf/
rm -rf ${APP}/share
cd ${APP}
bin/${BIN} -Djava.net.preferIPv4Stack=true -Dfile.encoding=UTF-8 >> /var/log/application/${BIN}.log &
本当の初回に evolution を一回だけやりたい場合は
bin/${BIN} -Dplay.evolutions.db.default.autoApply=true -Dplay.evolutions.db.default.autoApplyDowns=true
SessionCache を永続化†
conf/ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd" updateCheck="false">
<!-- ehcache 永続化ファイルのパスを指定 -->
<diskStore path="user.dir/.ehcache" />
<!-- diskPersistent="true" で永続化を有効にする (デフォルトは false) -->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"
maxElementsOnDisk="10000000"
diskPersistent="true"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
</ehcache>
sample†
model†
package models;
import java.util.Date;
import javax.persistence.*;
import com.avaje.ebean.*;
import com.avaje.ebean.annotation.*;
import play.data.validation.Constraints.*;
@Entity
public class Country extends Model {
@Id
public Long id;
@Required
public String name;
// @OneToMany(cascade = CascadeType.ALL)
// public List<Holiday> holidays;
@CreatedTimestamp
public Date created_date;
@UpdatedTimestamp
public Date updated_date;
public static final Find<Long, Country> finder = new Find<Long, Country>() {
};
}
テンプレートを velocity にしたい時†
build.sbt
libraryDependencies += "org.apache.velocity" % "velocity" % "1.7"
libraryDependencies += "velocity-tools" % "velocity-tools" % "1.4"
activator eclipse
Global.java
import org.apache.velocity.app.Velocity;
import play.Application;
import play.GlobalSettings;
public class Global extends GlobalSettings {
@Override
public void onStart(final Application app) {
Velocity.setProperty("file.resource.loader.path", "conf/templates/");
Velocity.init();
}
}
CSRF†
- build.sbt に filters を追加
- application.conf の play.crypto.secret を適切に設定
Controller
@AddCSRFToken
public Result form() {
return ok(session().get("csrfToken"));
}
@RequireCSRFCheck
public Result post() {
Form<PostParam> form = Form.form(PostParam.class).bindFromRequest();
PostParam query = form.get();
return ok(query.name);
}
public static class PostParam {
public String name;
}
外部プロジェクト†
eclipse でプロジェクト参照しても activator 側で認識できない。
build.sbt
lazy val PlayUtil = RootProject(file("../PlayUtil"))
lazy val root = (project in file(".")).enablePlugins(PlayJava,PlayEbean).dependsOn(PlayUtil)
activator eclipse
※ 注意: 参照される側のプロジェクトが単なるライブラリ的な存在の場合は application.conf が上書きで読み込まれるため消しておいたほうが良い
cron†
build.sbt
resolvers += "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/"
libraryDependencies += "com.typesafe.akka" % "akka-remote_2.11" % "2.3.13"
libraryDependencies += "com.typesafe.akka" % "akka-actor_2.11" % "2.3.13"
libraryDependencies += "com.typesafe.akka" % "akka-kernel_2.11" % "2.3.13"
libraryDependencies += "com.enragedginger" % "akka-quartz-scheduler_2.11" % "1.4.0-akka-2.3.x"
これだけだと scala-library の部分が eclipse に認識されない。
今のところ手動で activator eclipse した後に、追加しているけれど・・
<classpathentry kind="lib" path="scala-library-2.11.5.jar"/>
application.conf
akka {
quartz {
schedules {
EveryMM {
description = "毎分"
expression = "0 * * ? * *"
timezone = "Asia/Tokyo"
}
}
}
}
actors/MinutelyActor.java
public class MinutelyActor extends UntypedActor implements VelocityController {
@Override
public void onReceive(Object arg0) throws Exception {
Logger.debug("CRON MINUTERY: " + new Unixtime().format("yyyy-MM-dd HH:mm"));
//do something
}
}
Global.java
try{
ActorSystem system = ActorSystem.create("system");
try{
ActorRef ref = system.actorOf(Props.create(MinutelyActor.class), "MinutelyActor");
QuartzSchedulerExtension scheduler = (QuartzSchedulerExtension) QuartzSchedulerExtension.get(system);
scheduler.schedule("EveryMM", ref, "minutely");
}catch(Exception e){
}
}catch(Exception e){
}
web socket†
actor を使うので、build.sbt 等は上記の cron と同じ設定を行う。
conf/routes
GET /connect controllers.SocketController.socket
GET /send controllers.SocketController.send(message)
controllers/SocketController.java
public class SocketController extends Controller {
public WebSocket<String> socket() {
return WebSocket.withActor(WebSocketActor::props);
}
public Result send(String message) {
WebSocketActor.members.forEach(a -> a.send(message));
return ok("OK");
}
}
actors/WebSocketActor.java
public class WebSocketActor extends UntypedActor {
private final ActorRef out;
public static List<WebSocketActor> members = new ArrayList<WebSocketActor>();
public static Props props(ActorRef out) {
return Props.create(WebSocketActor.class, out);
}
public WebSocketActor(ActorRef out) {
this.out = out;
members.add(this);
}
@Override
public void onReceive(Object arg0) throws Exception {
System.out.println(arg0);
}
@Override
public void postStop() throws Exception {
members.remove(this);
System.out.println("bye");
}
public void send(String message) {
out.tell(message, self());
}
}
test†
http://websocket.org/echo.html
ws://localhost:9000/connect
ebean†
relation系†
OneToOne | public String | cascade 使用すると子オブジェクトは親に従属 |
OneToMany | public String[] | cascade 使用すると子配列は親に従属 |
ManyToOne | public String | cascade は使用しない。子を保存する場合は子側で保存 |
ManyToMany | public String[] | cascade は使用しない。子配列を保存する場合は子側で保存 |
date†
form からテキストで入ってきた date string を Date 型にする場合
import play.data.format.Formats;
@DateTime(pattern = "yyyy/MM/dd")
public Date date;
validate系†
import play.data.validation.Constaints;
@Required | 必須 |
@Email | email 型 |
@MaxLength | 最大長 |
@MinLength | 最小長 |
@NotNull | 必須 |
DB系†
@Table | 実際のDB上のテーブル名を指定できる |
@SerializedName | 実際の DB上の field 名を指定できる |
@Entity | Model として認識するために必要 |
@MappedSuperclass | モデルの super class に作る。このモデルはテーブルを作らない |
@Transient | DB にフィールドを作らない(一時計算用のフィールド等) |
@Column | DBに実際作られるフィールド (columnDefinition = "text") で256文字より長い文字 |
前処理系†
@PostLoad | select の後に呼ばれる callback |
@PrePersist | insert の前に呼ばれる callback |
@PreUpdate | update の前に呼ばれる callback |
@PreRemove | delete の前に呼ばれる callback |
基本フィールド†
@Id | ユニークな値の方針を決めたりできる |
@CreatedTimestamp | 作成日が自動挿入される |
@UpdatedTimestamp | 更新日が自動挿入される |
トラブルシュート†
https/http†
play.core.server.netty.PlayDefaultUpstreamHandler - Exception caught in Netty
- 【原因】httpsの設定をしていないのに htpps でアクセスした
- 【対応】http でアクセスする
memory†
java.lang.OutOfMemoryError: Java heap space
deploy†
useradd sample
mkdir /var/log/sample
chown sample.sample /var/log/sample
/etc/logrotate.d/sample
/var/log/sample/sample.log {
weekly
dateext
missingok
rotate 52
compress
delaycompress
copytruncate
notifempty
sharedscripts
}
su sample
cd
ln -s /var/log/sample logs
cat > ~/.emacs
(setq make-backup-files nil)
(setq auto-save-default nil)
cp sample-1.0-SNAPSHOT.zip ~/
~/server.conf
play.crypto.secret="sample"
# postgres
db.default.driver=org.postgresql.Driver
db.default.url="jdbc:postgresql://localhost:5432/sample"
db.default.username="sample"
db.default.password="sample"
ebean.default=["models.*"]
play.http.session.secure=false
~/deploy.sh
#!/usr/bin/bash
BIN=sample
APP=${BIN}-1.0-SNAPSHOT
DB=sample
STAMP=`date '+%y%m%d%H%M'`
USER=sample
ps -auxww |grep java |grep ${APP} |awk '{print $2;}' |xargs kill -9 2>/dev/null
rm -rf ${APP}/RUNNING_PID
cp -rf ${APP}/conf backup/conf.bak.${STAMP}
rm -rf ${APP}
cp ${APP}.zip ./backup/${APP}.zip.${STAMP}
cp /home/${USER}/${APP}.zip ./
unzip ${APP}.zip
rm -rf ${APP}/share
rm -rf ${APP}/conf/files
cp -rfp conf/files ${APP}/conf
cp server.conf ${APP}/conf/
cd ${APP}
bin/${BIN} \
-J-Xmx2048M -J-Xms2048M \
-Djava.net.preferIPv4Stack=true \
-Dhttp.port=9000 \
-Dplay.evolutions.db.default.autoApply=true \
-Dfile.encoding=UTF-8 >> /var/log/${DB}/${DB}.log 2>&1 &
tail -f /var/log/${DB}/${DB}.log
chmod +x deploy.sh
database†
createuser -U postgres sample
psql -U postgres -c "alter role sample with password 'sample';"
psql -U postgres -c "create database sample;"
psql -U postgres sample -c "create extension postgis;"
psql -U postgres -c "alter database sample owner to sample;"