1周遅れのscala入学 #nds41

44
41 回長岡 IT 技術者勉強会 周遅れの Scala 入学 2015/4/11 @nemuzuka

Upload: kazumune-katagiri

Post on 24-Jul-2015

507 views

Category:

Engineering


2 download

TRANSCRIPT

Page 1: 1周遅れのScala入学 #nds41

第41回長岡IT技術者勉強会

1週周遅れのScala入学

2015/4/11 @nemuzuka

Page 2: 1周遅れのScala入学 #nds41

自己紹介• 片桐 一宗(かたぎり かずむね)

• id:nemuzuka

• @nemuzuka

• サーバサイドにJavaを使用したWebアプリケーションの開発を主にしております

• フリーランス(vss.jp.net)

Page 3: 1周遅れのScala入学 #nds41

Scala再入学のきっかけ

Page 4: 1周遅れのScala入学 #nds41

システムを新しく構築するチャンス• AWSのサービス使用

• S3 / CloudSearch / CloudFront…

• 全ての操作で画面切り替えとかちょっと…

• AjaxでDOM書き換え

• HTML5

• IE?最新しかサポートしない!

• スケジュールがタイト

• 要望がコロコロ変わりそう…

Page 5: 1周遅れのScala入学 #nds41

そこで選んだのが

• サーバサイドはJava

• Tomcat

• Seasar2

• SAStruts

• S2Dao

+今まで培ってきた  ・diconファイル  ・薄いラッパー

Page 6: 1周遅れのScala入学 #nds41

ちょっと 開発のパワーが足りないから 外部の人の協力を得よう

Page 7: 1周遅れのScala入学 #nds41

『新規なのにJavaなんですか』

『新規なのにSeasar2 なんですか( ´_ゝ`)』

えもいわれぬ老害感

Page 8: 1周遅れのScala入学 #nds41

結局Javaの構成で 無事カットオーバー できましたが...

何か新しいこと やらないといけないかな

Page 9: 1周遅れのScala入学 #nds41

選択肢は2つ• 別のフレームワークを学ぶ

• JavaEE

• Spring

• Play Framework

• 新しい言語を学ぶ

• 静的型付けが良い

• IDEが使えた方が良い

Page 10: 1周遅れのScala入学 #nds41

あっ!

Page 11: 1周遅れのScala入学 #nds41

入学してた。

Page 12: 1周遅れのScala入学 #nds41

当時は「できない子」

• bot写経で躓き、「ガッ」できなかった

• みんなの「XXXのコード見せてください」の画面切り替えが早すぎて追いつけなかった

• 単純に手が遅い

Page 13: 1周遅れのScala入学 #nds41

再入学の後押し• Javaの資産が使える

• Apache Commonsとか

• AWSのSDKだって使える

• 関数型プログラミングもオブジェクト指向プログラミングもサポート

• とりあえず「使う」だけなら関数脳にならなくても良い

• 型推論でスクリプト言語のようにも見える

• でも、IDEで追いやすい

Page 14: 1周遅れのScala入学 #nds41

「Javaやってます」 よりも

「Scalaやってます」 の方がなんとなく仕事が来そう

Page 15: 1周遅れのScala入学 #nds41

というわけで Scala再入学することに

Page 16: 1周遅れのScala入学 #nds41

どうやって再入学したか?

• 今更、NullPoGaBot作ってもなー

• どうせやるなら何か案件が始まった時に使えるものにしたい

Page 17: 1周遅れのScala入学 #nds41

S2でやってたことを Scalaでやるならどうするか に置き換えてみよう

Page 18: 1周遅れのScala入学 #nds41

フレームワークの置き換え

機能 JAVA SCALA

WEBフレームワーク SAStrutsScalatra

+scalata-forms

テンプレートエンジン JSP Scalate(SSP)

ORM S2Dao Slick

Page 19: 1周遅れのScala入学 #nds41

こんなときどうする(1)

~トランザクション管理~

Page 20: 1周遅れのScala入学 #nds41

トランザクション• DBの変更を

• 適用する(commit)

• 取り消す(rollback)

• これをそれぞれのプログラムで行うと

• DBに対する操作とビジネスロジックが混在する

• ソースコードの見通しも良くない

• コネクションの解放漏れ等にも繋がる

Page 21: 1周遅れのScala入学 #nds41

S2だと DI• S2の仕組みに乗れば自動的にトランザクションが効く

• @Bindingを付与して実装クラスをInjection

• 正常終了(何も例外が発生しない)時commit

• 例外が発生すればrollback

• DI使うのに外部XMLは不要

• コネクションプールもdiconに書くだけで使える

Page 22: 1周遅れのScala入学 #nds41

Scala(Scalatra)だと filter• Scalatraのinitでコネクションプール設定

• filterでトランザクション制御

• トランザクション開始

• 処理呼び出し

• 正常終了(何も例外が発生しない)時commit

• 例外が発生すればrollback

Page 23: 1周遅れのScala入学 #nds41

ソースはこんな感じ

//Filter class class TransactionFilter(db: Database) extends Filter {

def doFilter(req: ServletRequest, res: ServletResponse, chain: FilterChain): Unit = {

db withDynTransaction { // 1. トランザクションを開始し、ThreadLocalにSessionを格納 chain.doFilter(req, res) // 2. Servletの処理を行う } // 3. 正常時commit、例外発生時rollback } }

DBDatabase.dynamicSession

をimportThreadLocal取得して処理を行うことが可能

class ScalatraBootstrap extends LifeCycle {

val cpds = new ComboPooledDataSource() // 1.設定ファイルを元にコネクションプールを生成

override def init(context: ServletContext) { // 2.Scalatra起動時に一回だけ呼ばれるメソッド

val db = Database.forDataSource(cpds) // 3. Slick用データソースの取得 context.addFilter("transactionFilter", new TransactionFilter(db)) // 4. TransactionFilterをfilterとして登録 context.getFilterRegistration(“transactionFilter").addMappingForUrlPatterns(

util.EnumSet.allOf(classOf[DispatcherType]), true, “/*")

// 5. Servletの定義が続く… } }

Page 24: 1周遅れのScala入学 #nds41

これでこんなLayer設計ができます

・Servlet ・Service  →DBアクセスしないので、Sessionをコード上で意識しない

・Dao  →ThreadLocalからSessionを取得してSQL発行

Page 25: 1周遅れのScala入学 #nds41

こんなときどうする(2)

~認証・認可チェック~

Page 26: 1周遅れのScala入学 #nds41

認証・認可• 認証

• リクエストを送ってきた人が正規のユーザであることを確認すること

• ID / パスワードでログインしている

• 本人しかしらない筈なので、正規のユーザとみなす

• 認可

• その機能を利用する権限の有無を確認すること

• 管理者権限を所有していないのに、管理者機能を使用できるのはNG

• Webアプリの場合、URLを直接叩かれる可能性があるので、特に注意する必要あり

Page 27: 1周遅れのScala入学 #nds41

S2だと S2AOP• ログイン成功時、HttpSessionにログイン情報を設定

• 受けたリクエストが認証済みでなければならない場合

• Interceptorを使用して、Actionの呼び出し前にログイン情報がHttpSessionに存在するかチェック

• 存在しなければログイン画面へリダイレクト

• 存在するが、そのActionを使用する権限をユーザが持っていない場合、不正アクセスが来たとみなし、しかるべき画面にリダイレクト

• AOPの定義はdiconファイルに定義

• パッケージ、クラス名等の正規表現で定義できる

Page 28: 1周遅れのScala入学 #nds41

Scala(Scalatra)だと filter• ログイン成功時、HttpSessionにログイン情報を設定

• リクエストURIを元に受けたリクエストが認証済みで無ければならない場合

• filterでログイン情報がHttpSessionに存在するかチェック

• 存在しなければログイン画面へリダイレクト

• 存在するが、そのServletを使用する権限をユーザが持っていない場合、不正アクセスが来たとみなし、しかるべき画面にリダイレクト(これは、Servlet側機能)

Page 29: 1周遅れのScala入学 #nds41

こんなときどうする(3)

~コード自動生成~

Page 30: 1周遅れのScala入学 #nds41

ER図を常に信じられる状態にしたい

3.ソースコード (絶対に手動で修正しない)

2.RDBMS

1.ER図

Page 31: 1周遅れのScala入学 #nds41

• S2Dao-CodeGen

• Slick code generator

S2だと

Scala(Slick)だと

Page 32: 1周遅れのScala入学 #nds41

こんなときどうする(4)

~動的SQL発行~

Page 33: 1周遅れのScala入学 #nds41

画面に入力された項目だけwhere句に追加

未入力の場合、その項目はwhere句に含めない

Page 34: 1周遅れのScala入学 #nds41

S2だと IFコメント• 条件に応じてSQLを変更することが可能

• /* IF 条件 */…/*END*/

• 条件がtrueの場合、/*IF*/と/*END*/に囲まれた部分が評価される

/*IF hoge != null*/hoge = /*hoge*/‘abc’/*END*/

引数hogeがnullでない場合にのみ、 hoge = hogeの値 がSQL文に追加される

Page 35: 1周遅れのScala入学 #nds41

Scala(Slick)だと 生Preparedstatement

• ゴニョゴニョ頑張れば他のやり方でできる気もするけど、こっちの方が確実だと思う

• where句のカラムが動的に変わらないのであれば、StaticQueryを使用した方が良い

• 複雑なテーブル結合する場合はStaticQueryやPreparedstatement使ったほうが余計なことにハマらなくて良いかも

• S2Dao使ってる時でも生SQLを発行してた

• ORMは楽になるところだけ使う

def coffeeByName(name: String) = sql"select * from coffees where name = $name".as[Coffee] println("Coffee Colombian: " + coffeeByName("Colombian").firstOption)

Page 36: 1周遅れのScala入学 #nds41

まだいろいろあるけど、 ドキュメント読めば何とかなります

Page 37: 1周遅れのScala入学 #nds41

Java使いがScalaと戯れた感想

Page 38: 1周遅れのScala入学 #nds41

型推論

• 変数名定義の時にクラス名の有無でこんなに違うかーってくらいスッキリします

[Scala] val credentials = new BasicAWSCredentials(accessKey, secretKey) val s3client = new AmazonS3Client(credentials) val localFile = new File("ローカルファイルパス") val bucketName = "バケット名" val filePath = "S3のアップロード先のパス" val upReq = new PutObjectRequest(bucketName, filePath, localFile) s3client.putObject(upReq)

[Java] AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey); AmazonS3 s3client = new AmazonS3Client(credentials); File localFile = new File("ローカルファイルパス"); String bucketName = "バケット名"; String filePath = "S3のアップロード先のパス"; PutObjectRequest upReq = new PutObjectRequest(bucketName, filePath, localFile); s3client.putObject(upReq);

Page 39: 1周遅れのScala入学 #nds41

名前付き引数• 大好きです。Javaにもあれば良いのに

• 使う側が意識して設定するようになるのがイイと思います

• 引数の順番が変わった時にも追従してくれます

• 特にSlickのデータモデル(case class)のインスタンスを生成するときは嬉しい

• カラムの追加や順番を変更することが多いので

[Scala]

case class User(age:Int, name:String) ・ ・ ・

val hanako = User(7, “はなこ”) // ① val jiro = User(age = 38, name = “二郎”) // ② val taro = User(name = “太郎”, age = 17) // ③

→全てUserクラスのインスタンスが生成可能だが、 case class User(name:String, age:Int)

と変更した場合、①はコンパイルエラーになる

Page 40: 1周遅れのScala入学 #nds41

index付きループ• Javaにもあれば良いのに

• Javaだと拡張forを諦めてfor(int i = 0; i < list.size(); i++)

[Scala]

val list = List("A", "B", "C") for((e, index) <- list.zipWithIndex) { // eには該当要素、indexには該当indexが格納される

・ ・ ・

}

Page 41: 1周遅れのScala入学 #nds41

まだまだあるよ• if とか for は式なので値を返せる

• 「このifで何をしたいんだっけ?」が見えるようになるのがイイ!

• 複数の戻り値を返せる(タプル)

• わざわざ戻り値用のclass作らなくてもいいんです

• traitでmix-inがすごい

• チェック例外がない

• 個人的にはあってもいいと思うけど…(設計思想変える必要あり)

• Either(さっきのヤツ!)

• 比較は「==」でOK

• equalsを使わなくて怒られる新人減ります

• breakが変

• もなど / かりー → よくわかりません

Page 42: 1周遅れのScala入学 #nds41

再入門に準備したもの

Page 43: 1周遅れのScala入学 #nds41

purchase from

https://gist.github.com/Shinpeim/6740436

Page 44: 1周遅れのScala入学 #nds41

まとめ

• 結構面白いです、Scala

• 潤沢なメモリ+SSD必須

• 金食い虫なので仕事に使うなら上の理解が必要かも

• 次はテスト周りをしっかりと