さて、前回に引き続き、素振り2スイング目。
1スイング 目では導入部分的なことをやった。
一瞬ハマったこと
前回、scaffold generatorでmembersを作った。
だからlocalhost:8080/membersにアクセス出来るはずなんだけど、Inernal Sever Errorが。
org.h2.jdbc.JdbcSQLException: 機能�サ�ート�れ����ん: "autoServerMode && (readOnly || fileLockMethod == NO || fileLockMethod == SERIALIZED || fileLockMethod == FS || inMemory)"
Feature not supported: "autoServerMode && (readOnly || fileLockMethod == NO || fileLockMethod == SERIALIZED || fileLockMethod == FS || inMemory)" [50100-191]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
at org.h2.message.DbException.get(DbException.java:179)
at org.h2.message.DbException.get(DbException.java:155)
at org.h2.message.DbException.getUnsupportedException(DbException.java:216)
at org.h2.engine.Database.open(Database.java:639)
at org.h2.engine.Database.openDatabase(Database.java:270)
at org.h2.engine.Database.(Database.java:264)
at org.h2.engine.Engine.openSession(Engine.java:65)
at org.h2.engine.Engine.openSession(Engine.java:175)
at org.h2.engine.Engine.createSessionAndValidate(Engine.java:153)
at org.h2.engine.Engine.createSession(Engine.java:136)
at org.h2.engine.Engine.createSession(Engine.java:28)
at org.h2.engine.SessionRemote.connectEmbeddedOrServer(SessionRemote.java:349)
at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:107)
at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:91)
at org.h2.Driver.connect(Driver.java:72)
at java.sql.DriverManager.getConnection(DriverManager.java:664)
at java.sql.DriverManager.getConnection(DriverManager.java:247)
at org.apache.commons.dbcp2.DriverManagerConnectionFactory.createConnection(DriverManagerConnectionFactory.java:77)
at org.apache.commons.dbcp2.PoolableConnectionFactory.makeObject(PoolableConnectionFactory.java:256)
at org.apache.commons.pool2.impl.GenericObjectPool.create(GenericObjectPool.java:868)
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:435)
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:363)
at org.apache.commons.dbcp2.PoolingDataSource.getConnection(PoolingDataSource.java:134)
at scalikejdbc.Commons2ConnectionPool.borrow(Commons2ConnectionPool.scala:46)
at scalikejdbc.DB$.readOnly(DB.scala:172)
at scalikejdbc.SQLToResult$class.apply(SQL.scala:578)
at scalikejdbc.SQLToOptionImpl.apply(SQL.scala:736)
at skinny.orm.feature.CalculationFeature$class.count(CalculationFeature.scala:30)
at model.Member$.count(Member.scala:17)
at skinny.orm.feature.CRUDFeatureWithId$class.countAllModels(CRUDFeature.scala:281)
at model.Member$.countAllModels(Member.scala:17)
at skinny.controller.SkinnyApiResourceActions$class.countResources(SkinnyApiResourceActions.scala:150)
at controller.MembersController.countResources(MembersController.scala:8)
at skinny.controller.SkinnyResourceActions$$anonfun$showResources$1.apply(SkinnyResourceActions.scala:84)
at skinny.controller.SkinnyResourceActions$$anonfun$showResources$1.apply(SkinnyResourceActions.scala:80)
at skinny.controller.feature.SkinnyControllerCommonBase$class.withFormat(SkinnyControllerCommonBase.scala:98)
at skinny.controller.SkinnyController.withFormat(SkinnyController.scala:8)
at skinny.controller.SkinnyResourceActions$class.showResources(SkinnyResourceActions.scala:80)
at controller.MembersController.showResources(MembersController.scala:8)
at skinny.controller.SkinnyResourceRoutes$$anonfun$1.apply(SkinnyResourceRoutes.scala:16)
at skinny.micro.SkinnyMicroBase$class.liftAction(SkinnyMicroBase.scala:302)
at skinny.controller.SkinnyController.liftAction(SkinnyController.scala:8)
at skinny.micro.SkinnyMicroBase$$anonfun$invoke$1.apply(SkinnyMicroBase.scala:296)
at skinny.micro.SkinnyMicroBase$$anonfun$invoke$1.apply(SkinnyMicroBase.scala:296)
at skinny.micro.ApiFormats$class.withRouteMultiParams(ApiFormats.scala:196)
at skinny.controller.SkinnyController.withRouteMultiParams(SkinnyController.scala:8)
at skinny.micro.SkinnyMicroBase$class.invoke(SkinnyMicroBase.scala:295)
at skinny.controller.SkinnyController.invoke(SkinnyController.scala:8)
at skinny.micro.SkinnyMicroBase$$anonfun$runActions$1$2.apply(SkinnyMicroBase.scala:208)
at skinny.micro.SkinnyMicroBase$$anonfun$runActions$1$2.apply(SkinnyMicroBase.scala:208)
at scala.collection.immutable.Stream.flatMap(Stream.scala:489)
at skinny.micro.SkinnyMicroBase$class.runActions$1(SkinnyMicroBase.scala:208)
at skinny.micro.SkinnyMicroBase$$anonfun$executeRoutes$1.apply$mcV$sp(SkinnyMicroBase.scala:241)
at skinny.micro.SkinnyMicroBase$$anonfun$executeRoutes$1.apply(SkinnyMicroBase.scala:241)
at skinny.micro.SkinnyMicroBase$$anonfun$executeRoutes$1.apply(SkinnyMicroBase.scala:241)
at skinny.micro.SkinnyMicroBase$class.cradleHalt$1(SkinnyMicroBase.scala:215)
at skinny.micro.SkinnyMicroBase$class.executeRoutes(SkinnyMicroBase.scala:239)
at skinny.controller.SkinnyController.executeRoutes(SkinnyController.scala:8)
at skinny.micro.SkinnyMicroBase$$anonfun$handle$1.apply$mcV$sp(SkinnyMicroBase.scala:110)
at skinny.micro.SkinnyMicroBase$$anonfun$handle$1.apply(SkinnyMicroBase.scala:110)
at skinny.micro.SkinnyMicroBase$$anonfun$handle$1.apply(SkinnyMicroBase.scala:110)
at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58)
at skinny.micro.base.SkinnyContextInitializer$class.withResponse(SkinnyContextInitializer.scala:106)
at skinny.controller.SkinnyController.withResponse(SkinnyController.scala:8)
at skinny.micro.base.SkinnyContextInitializer$$anonfun$withRequestResponse$1.apply(SkinnyContextInitializer.scala:81)
at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58)
at skinny.micro.base.SkinnyContextInitializer$class.withRequest(SkinnyContextInitializer.scala:94)
at skinny.controller.SkinnyController.withRequest(SkinnyController.scala:8)
at skinny.micro.base.SkinnyContextInitializer$class.withRequestResponse(SkinnyContextInitializer.scala:80)
at skinny.controller.SkinnyController.withRequestResponse(SkinnyController.scala:8)
at skinny.micro.SkinnyMicroBase$class.handle(SkinnyMicroBase.scala:109)
at skinny.controller.SkinnyController.skinny$micro$contrib$FlashMapSupport$$super$handle(SkinnyController.scala:8)
at skinny.micro.contrib.FlashMapSupport$$anonfun$handle$1.apply$mcV$sp(FlashMapSupport.scala:65)
at skinny.micro.contrib.FlashMapSupport$$anonfun$handle$1.apply(FlashMapSupport.scala:38)
at skinny.micro.contrib.FlashMapSupport$$anonfun$handle$1.apply(FlashMapSupport.scala:38)
at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58)
at skinny.micro.base.SkinnyContextInitializer$class.withRequest(SkinnyContextInitializer.scala:94)
at skinny.controller.SkinnyController.withRequest(SkinnyController.scala:8)
at skinny.micro.contrib.FlashMapSupport$class.handle(FlashMapSupport.scala:38)
at skinny.controller.SkinnyController.skinny$micro$contrib$ScalateSupport$$super$handle(SkinnyController.scala:8)
at skinny.micro.contrib.ScalateSupport$class.handle(ScalateSupport.scala:124)
at skinny.controller.SkinnyController.handle(SkinnyController.scala:8)
at skinny.micro.SkinnyMicroFilterBase$$anonfun$doFilter$1.apply$mcV$sp(SkinnyMicroFilterBase.scala:29)
at skinny.micro.SkinnyMicroFilterBase$$anonfun$doFilter$1.apply(SkinnyMicroFilterBase.scala:29)
at skinny.micro.SkinnyMicroFilterBase$$anonfun$doFilter$1.apply(SkinnyMicroFilterBase.scala:29)
at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58)
at skinny.micro.SkinnyMicroFilterBase$class.doFilter(SkinnyMicroFilterBase.scala:28)
at skinny.controller.SkinnyController.doFilter(SkinnyController.scala:8)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:585)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:577)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:223)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1127)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:515)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1061)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:215)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97)
at org.eclipse.jetty.server.Server.handle(Server.java:499)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:311)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:257)
at org.eclipse.jetty.io.AbstractConnection$2.run(AbstractConnection.java:544)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:635)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:555)
at java.lang.Thread.run(Thread.java:745)
なんだなんだ、JDBCの設定の問題かなぁ。。。とか思ったんですが、ファイルパーミッションの問題でした。てへぺろ。
無心でコマンド打ったら無事解決。
chmod -R 755 <プロジェクト名>
skinnyコマンドをもうちょっと調べる
これはもしかして、コマンドさえ駆使出来れば、かなり定型作業が省略されるのでは。
ということでコマンドを調べてみる。
skinny Usage: ./skinny [COMMAND] [OPTIONS]... new : will create new Skinny application run/server/s : will run application for local development run -precompile : will run application with Scalate precompilation debug/d : will run application with JDWP. default port 5005 clean : will clear target directory update : will run sbt update console : will run sbt console compile : will compile all the classes ~compile : will compile all the classes when changes are detected db:migrate : will execute database migration db:repair : will recover when previous migration failed routes : will display routes information test : will run all the tests ~test : will run all the tests when changes are detected testQuick : will run only failed tests ~testQuick : will run only failed tests when changes are detected testOnly : will run the specified test ~testOnly : will run the specified test when changes are detected test:coverage : will run all the tests and output coverage reports package : will create *.war file to deploy package:standalone : will create *.jar file to run as stand alone app publish : will publish *.war file to repository scalajs:watch : will watch Scala.js Scala code change and convert to JS scalajs:package : will convert Scala.js Scala code to JS file eclipse : will setup Scala IDE settings idea/gen-idea : will setup IntelliJ IDEA settings task:clean : will clean task project's target directory task:run : will run tasks g/generate controller : will generate controller g/generate model : will generate model g/generate migration : will generate db migration file g/generate scaffold : will generate scaffold files with ssp templates g/generate scaffold:scaml : will generate scaffold files with scaml templates g/generate scaffold:jade : will generate scaffold files with jade templates g/generate reverse-scaffold : will generate scaffold from existing database g/generate reverse-scaffold:scaml : will generate scaffold from existing database g/generate reverse-scaffold:jade : will generate scaffold from existing database
ふむ。特別変わったコマンドは無いですね。
あえて注目するとすればgenerate系。
モデル、コントローラー、マイグレーションファイルの生成、
素振りの1回目 でやったscaffold。
DBからreverseでscaffoldも出来る。このあたりを抑えておけば問題はなさそう。
自動生成されたページを見てみる
さて、改めてlocalhost:8080/membersにアクセス。
これが
こうなって
こうなって
こうじゃ
もはや何もせずともCRUD処理が出来てしまう。
ここまでScalaのコーディングはおろか、設定ファイルすらいじってない。
すごいなぁ。
生成されたソースを一度じっくり見る
ここで何が大事かを考える。と
・何が作られているか
・どういう仕組みで作られているか
の大枠をつかむことかなぁと思う。
ということでソースを読む。
とりあえずフォルダ構成的なもので言うと
src/main/scala
src/main/resources
これはまぁ、Scalaの基本的なアレだろう。
treeコマンド打ったら良いよね。
雑にこんな感じ
tree -f -I "bin|target|test|build|db|logs|project|task" -P "*.[class]" . ├── ./src │ └── ./src/main │ ├── ./src/main/resources │ ├── ./src/main/scala │ │ ├── ./src/main/scala/controller │ │ ├── ./src/main/scala/lib │ │ ├── ./src/main/scala/model │ │ ├── ./src/main/scala/templates │ │ └── ./src/main/scala/worker │ └── ./src/main/webapp │ ├── ./src/main/webapp/WEB-INF │ │ ├── ./src/main/webapp/WEB-INF/assets │ │ │ ├── ./src/main/webapp/WEB-INF/assets/coffee │ │ │ ├── ./src/main/webapp/WEB-INF/assets/jsx │ │ │ ├── ./src/main/webapp/WEB-INF/assets/less │ │ │ ├── ./src/main/webapp/WEB-INF/assets/scala │ │ │ └── ./src/main/webapp/WEB-INF/assets/scss │ │ ├── ./src/main/webapp/WEB-INF/layouts │ │ └── ./src/main/webapp/WEB-INF/views │ │ ├── ./src/main/webapp/WEB-INF/views/error │ │ ├── ./src/main/webapp/WEB-INF/views/members │ │ └── ./src/main/webapp/WEB-INF/views/root │ └── ./src/main/webapp/assets │ ├── ./src/main/webapp/assets/css │ └── ./src/main/webapp/assets/js └── ./standalone-build
ということで、controller、model、templates、workerなどが出来てる。 WEB-INF/assetsは、テンプレートエンジンのアレをアレしてるものだと思われる。
WEB-INF/views/membersが僕が作ったscaffold作ったもの。
Model
モデルを見てみると、本当にシンプルなcase classで定義されたドメインクラスとSkinnyCRUDMapperというtrait実装のみ。
SkinnyCRUDMapperではResultSetをwrapしたものをメンバーにバインドするextractメソッドがあるのみ。
View
viewとしてはこんなものが出来ている。
_form.html.ssp・・・・共通的なフォーム
edit.html.ssp・・・編集(CRUDでいうUPDATE)
index.html.ssp・・・雑に言うとindexページ。ドメインの一覧表示をする画面になっている。
new.html.ssp・・・新規(CRUDでいうCREATE)
show.html.ssp・・・詳細表示(CRUDでいうREAD)
i18n対応もされている。っぽく見える。
Controller
ControllersというクラスでServletContextにマウントしている。
主にルーティングはここでやっているように見える。
MembersControllerというクラス(scaffoldしたやつ)でバリデーション、各CRUD操作の実装(呼び出し)がされている。
抽象的な処理はSkinnyResourceでほぼ完了している感じ。
SkinnyResourceはロギング、Skinny Micro、TypeConverter、SkinnyMapperなどを持った
共通的なコンテキストオブジェクトってイメージが正しいのかなぁ。
次回予告
長くなったのでブログ記事ここまで!!
本当に良く出来ているので、何もしていないのにこんなに長くなってしまう。。
次回はもう少しSkinnyの内部の実装を追っていきたいところ。
あとは、ドキュメント読みつつサンプル真似してコード書くとか。




0 件のコメント:
コメントを投稿