何か ScalaJP で Xitrum という単語があったので使ってみた。
まず新しいプロジェクトを作ろうか。
curl -L -o xitrum-new.zip https://github.com/xitrum-framework/xitrum-new/archive/master.zip
後は解凍して終わり。
インストールが必要ないのは楽でいいね。
中身を見れば大体分かる。
README.rst build.sbt screenshot.png ./config: akka.conf application.conf flash_socket_policy.xml logback.xml ssl_example.crt ssl_example.key xitrum.conf ./project: build.properties plugins.sbt ./public: 404.html 500.html app.css favicon.ico robots.txt whale.png ./sbt: sbt sbt-launch-0.13.5.jar sbt.bat ./script: runner runner.bat scalive scalive-1.2.jar scalive.bat ./src: main ./src/main: scala scalate ./src/main/scala: quickstart ./src/main/scala/quickstart: Boot.scala action ./src/main/scala/quickstart/action: DefaultLayout.scala Errors.scala SiteIndex.scala ./src/main/scalate: quickstart ./src/main/scalate/quickstart: action ./src/main/scalate/quickstart/action: DefaultLayout.jade NotFoundError.jade ServerError.jade SiteIndex.jade
そのままだよね。
デフォルトでは model に該当するクラスは用意されていない。好きに作ればいいのかな?
構成が殆んどプレーンな sbt なので、ScalaActiveRecord 突っ込めば色々楽ができるだろうね。
Controller
この辺を見る。
まぁファイル名を見れば分かるでしょう。
DefaultLayout.scala : Controller 規定
package quickstart.action import xitrum.Action trait DefaultLayout extends Action { override def layout = renderViewNoLayout[DefaultLayout]() }
Action クラスの継承がコントローラクラスの条件のようだ。layout
は View 名を省略したときに自動で適用される View テンプレートの選択ロジックに見える。
trait なのが気になるがまぁいい。
Errors.scala
package quickstart.action import xitrum.annotation.{Error404, Error500} @Error404 class NotFoundError extends DefaultLayout { def execute() { respondView() } } @Error500 class ServerError extends DefaultLayout { def execute() { respondView() } }
DefaultLayout を継承してコントローラを作成している。execute メソッドがコントローラ本体。この構成はぱっと見 PHP の Smarty にも見える。
コントローラクラスの定義が class である事を考えると、アクセスごとにインスタンスを生成している可能性がある?
てなことで、SiteIndex.scala の中身が
package quickstart.action import xitrum.annotation.GET import java.rmi.server.UID @GET("") class SiteIndex extends DefaultLayout { lazy val uid = new UID() def execute() { println(s"Instance UNIQUE ID: $uid") respondView() } }
で、起動(sbt run
)してログを,,,,,ってぇ!
8GB メモリのカスタム MacBookAir でまさかの OutOfMemory ... byobu + Chromeでタブ3枚でダメか…。
仕方ないので 16GB の窓8.1で再チャレンジ。
凄まじく大量の resolve... 落ちた原因はこれか。
で、起動して早速 http://localhost:8000 に...
[WARN] Caught exception java.lang.StringIndexOutOfBoundsException: String index out of range: 1 at java.lang.String.charAt(String.java:658) ~[na:1.7.0_45] at java.util.regex.Matcher.appendReplacement(Matcher.java:762) ~[na:1.7.0_45] at java.util.regex.Matcher.replaceAll(Matcher.java:906) ~[na:1.7.0_45] at java.lang.String.replaceAll(String.java:2162) ~[na:1.7.0_45] at xitrum.util.ClassFileLoader.loadClassData(ClassFileLoader.scala:39) ~[xitrum_2.11-3.14.jar:3.14] at xitrum.util.ClassFileLoader.findClass(ClassFileLoader.scala:26) ~[xitrum_2.11-3.14.jar:3.14] at xitrum.util.ClassFileLoader.loadClass(ClassFileLoader.scala:17) ~[xitrum_2.11-3.14.jar:3.14] at xitrum.handler.inbound.Dispatcher$.devDispatch(Dispatcher.scala:139) ~[xitrum_2.11-3.14.jar:3.14] at xitrum.handler.inbound.Dispatcher$.dispatch(Dispatcher.scala:68) ~[xitrum_2.11-3.14.jar:3.14] at xitrum.handler.inbound.Dispatcher.channelRead0(Dispatcher.scala:176) ~[xitrum_2.11-3.14.jar:3.14] at xitrum.handler.inbound.Dispatcher.channelRead0(Dispatcher.scala:154) ~[xitrum_2.11-3.14.jar:3.14] at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:103) [netty-all-4.0.20.Final.jar:4.0.20.Final] at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:332) [netty-all-4.0.20.Final.jar:4.0.20.Final] at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:318) [netty-all-4.0.20.Final.jar:4.0.20.Final] at xitrum.handler.inbound.MethodOverrider.channelRead0(MethodOverrider.scala:45) [xitrum_2.11-3.14.jar:3.14] at xitrum.handler.inbound.MethodOverrider.channelRead0(MethodOverrider.scala:19) [xitrum_2.11-3.14.jar:3.14] at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:103) [netty-all-4.0.20.Final.jar:4.0.20.Final] at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:332) [netty-all-4.0.20.Final.jar:4.0.20.Final] at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:318) [netty-all-4.0.20.Final.jar:4.0.20.Final] at xitrum.handler.inbound.UriParser.channelRead0(UriParser.scala:33) [xitrum_2.11-3.14.jar:3.14] at xitrum.handler.inbound.UriParser.channelRead0(UriParser.scala:16) [xitrum_2.11-3.14.jar:3.14] at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:103) [netty-all-4.0.20.Final.jar:4.0.20.Final] at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:332) [netty-all-4.0.20.Final.jar:4.0.20.Final] at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:318) [netty-all-4.0.20.Final.jar:4.0.20.Final] at xitrum.handler.inbound.WebJarsServer.channelRead0(WebJarsServer.scala:33) [xitrum_2.11-3.14.jar:3.14] at xitrum.handler.inbound.WebJarsServer.channelRead0(WebJarsServer.scala:23) [xitrum_2.11-3.14.jar:3.14] at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:103) [netty-all-4.0.20.Final.jar:4.0.20.Final] at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:332) [netty-all-4.0.20.Final.jar:4.0.20.Final] at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:318) [netty-all-4.0.20.Final.jar:4.0.20.Final] at xitrum.handler.inbound.PublicFileServer.channelRead0(PublicFileServer.scala:33) [xitrum_2.11-3.14.jar:3.14] at xitrum.handler.inbound.PublicFileServer.channelRead0(PublicFileServer.scala:23) [xitrum_2.11-3.14.jar:3.14] at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:103) [netty-all-4.0.20.Final.jar:4.0.20.Final] at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:332) [netty-all-4.0.20.Final.jar:4.0.20.Final] at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:318) [netty-all-4.0.20.Final.jar:4.0.20.Final] at xitrum.handler.inbound.BaseUrlRemover.channelRead0(BaseUrlRemover.scala:23) [xitrum_2.11-3.14.jar:3.14] at xitrum.handler.inbound.BaseUrlRemover.channelRead0(BaseUrlRemover.scala:11) [xitrum_2.11-3.14.jar:3.14] at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:103) [netty-all-4.0.20.Final.jar:4.0.20.Final] at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:332) [netty-all-4.0.20.Final.jar:4.0.20.Final] at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:318) [netty-all-4.0.20.Final.jar:4.0.20.Final] at xitrum.handler.inbound.Request2Env.sendUpstream(Request2Env.scala:283) [xitrum_2.11-3.14.jar:3.14] at xitrum.handler.inbound.Request2Env.channelRead0(Request2Env.scala:96) [xitrum_2.11-3.14.jar:3.14] at xitrum.handler.inbound.Request2Env.channelRead0(Request2Env.scala:44) [xitrum_2.11-3.14.jar:3.14] at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:103) [netty-all-4.0.20.Final.jar:4.0.20.Final] at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:332) [netty-all-4.0.20.Final.jar:4.0.20.Final] at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:318) [netty-all-4.0.20.Final.jar:4.0.20.Final] at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:163) [netty-all-4.0.20.Final.jar:4.0.20.Final] at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:332) [netty-all-4.0.20.Final.jar:4.0.20.Final] at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:318) [netty-all-4.0.20.Final.jar:4.0.20.Final] at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:787) [netty-all-4.0.20.Final.jar:4.0.20.Final] at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:125) [netty-all-4.0.20.Final.jar:4.0.20.Final] at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:507) [netty-all-4.0.20.Final.jar:4.0.20.Final] at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:464) [netty-all-4.0.20.Final.jar:4.0.20.Final] at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:378) [netty-all-4.0.20.Final.jar:4.0.20.Final] at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:350) [netty-all-4.0.20.Final.jar:4.0.20.Final] at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:116) [netty-all-4.0.20.Final.jar:4.0.20.Final] at java.lang.Thread.run(Thread.java:744) [na:1.7.0_45]
|||orz
俺が書いた部分が問題なのかと思って、修正した部分を削ったが状況は変わらず…。
ぱっと見 routing の名前解決で落ちてるのか?
てことで
package quickstart.action import xitrum.annotation.GET import java.rmi.server.UID @GET("/") class SiteIndex extends DefaultLayout { def execute() { respondView() } }
でもダメだった
org.fusesource.scalate.TemplateException: temp file rename failed
で起動失敗…2014/06/29 20:54 現在の master で発生…。
今のバージョン特有の問題なのか…?
バージョン番号は xitrum-new の 1.0-SNAPSHOT ...
SNAPSHOT だしなぁ
routing は慣れれば楽だが、class 数が爆発しそうな気がしてはいる。事実上の routing は annotation で指定している。
レスポンスの仕様は ここ 見ればわかるけど、結構綺麗だ。
テンプレートエンジンは scalate を使用。
同様に、このテンプレートの仕様を眺めていると、1アクション1テンプレート構成を徹底しているらしい。つまり、1アクションの中で複数のテンプレートを使いたければ、Routing アノテーションを付与しない複数の Action クラスを定義して、respondView[HomeAction_NormalUser]()
とか叩くらしい。
一つのアクションで複数のテンプレートを返すという挙動は設計的にどうなのとは思うけど、これはこれで冗長だなぁ。
Playframework の implicit マジックの雨霰とどっちがいいかは好みが分かれそうな。