2016/04/30 このエントリーをはてなブックマークに追加 はてなブックマーク - Skinny Frameworkの素振りをする(2スイング目)

Skinny Frameworkの素振りをする(2スイング目)




さて、前回に引き続き、素振り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 

 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作ったもの。








モデルを見てみると、本当にシンプルなcase classで定義されたドメインクラスとSkinnyCRUDMapperというtrait実装のみ。
SkinnyCRUDMapperではResultSetをwrapしたものをメンバーにバインドするextractメソッドがあるのみ。






viewとしてはこんなものが出来ている。
_form.html.ssp・・・・共通的なフォーム
edit.html.ssp・・・編集(CRUDでいうUPDATE)
index.html.ssp・・・雑に言うとindexページ。ドメインの一覧表示をする画面になっている。
new.html.ssp・・・新規(CRUDでいうCREATE)
show.html.ssp・・・詳細表示(CRUDでいうREAD)


i18n対応もされている。っぽく見える。





ControllersというクラスでServletContextにマウントしている。
主にルーティングはここでやっているように見える。
MembersControllerというクラス(scaffoldしたやつ)でバリデーション、各CRUD操作の実装(呼び出し)がされている。
抽象的な処理はSkinnyResourceでほぼ完了している感じ。
SkinnyResourceはロギング、Skinny Micro、TypeConverter、SkinnyMapperなどを持った
共通的なコンテキストオブジェクトってイメージが正しいのかなぁ。







長くなったのでブログ記事ここまで!!
本当に良く出来ているので、何もしていないのにこんなに長くなってしまう。。
次回はもう少しSkinnyの内部の実装を追っていきたいところ。
あとは、ドキュメント読みつつサンプル真似してコード書くとか。



0 件のコメント:

コメントを投稿