ScalaでMySQLを使う

新しい言語触り始めた後にアプリつくろうと思うと、大体DB周り気になる。
ということでScalaからMySQLを触ってみたときのメモ。

今回はscalaqueryを使いました。
https://github.com/szeiger/scala-query

一応scala-dbcというのがscalaの標準ライブラリに入っているようなのですが、scala2.9.1でこいつを使うと、deprecatedのwarningに苛まれる事になります。
とりあえず2.9.1では使わないこと推奨?のようなので、Mavenでサクっとscalaquery入れた環境作りましょう。

使用するプロジェクトはmvn archetype:generateで適当にscalaのプロジェクトとして作成。

Mavenでscalaquery使うときは、

 <dependencies>
    <dependency>
      <groupId>org.scalaquery</groupId>
      <artifactId>scalaquery_2.8.1</artifactId>
      <version>0.9.5</version>
    </dependency>
  </dependencies>
  <repositories>
    <repository>
      <id>ScalaToolsMaven2Repository</id>
      <name>Scala-Tools Maven2 Repository</name>
      <url>http://scala-tools.org/repo-releases</url>
    </repository>
  </repositories>

をpom.xmlに追加しておいてください。
あと、mysqlのドライバもdependenciesの中に追記!

    <dependency>
	  <groupId>mysql</groupId>
	  <artifactId>mysql-connector-java</artifactId>
	  <version>5.1.6</version>
    </dependency>

そんでもってプロジェクトのフォルダで

mvn scala:console

を走らせると、必要なライブラリが入った状態のscalaコンソールが立ち上がります。

あとはこんな感じでmysqlと接続。

scala> import org.scalaquery.session.Database
import org.scalaquery.session.Database

scala> import org.scalaquery.session.Database.threadLocalSession
import org.scalaquery.session.Database.threadLocalSession

scala> import org.scalaquery.simple.StaticQuery._
import org.scalaquery.simple.StaticQuery._

scala> val db = Database.forURL("jdbc:mysql://localhost/test",driver="com.mysql.jdbc.Driver",user="hoge",password="hoge")
db: org.scalaquery.session.Database = org.scalaquery.session.Database$$anon$2@3c10098b

scala> val select = queryNA[(Int,String)]("select id,name from user")
select: org.scalaquery.simple.StaticQuery[Unit,(Int, String)] with org.scalaquery.UnitInvokerMixin[(Int, String)] = org.scalaquery.simple.StaticQuery$$anon$1@7cc3feb6

scala> db withSession{select.list}
res0: List[(Int, String)] = List((1,y_matsuwitter))

scalaqueryの詳しい使い方はまた調べて書きます。
では。

参考
http://scalaquery.org/download.html
http://d.hatena.ne.jp/tototoshi/20111119/1321677419

Scala初学者がチャットを作るまで 3

とりあえずWebSocketとつなごうと必死だった。
が、そもそもjava servletのこともマトモに知らなかったので勉強した。

その記録。

まずは普通のHttpリクエストを受けられるようにして、servletに関して学ぼうと思った。

maven使っているので、とりあえずプロジェクト生成。

mvn archetype:generate

捕捉しておくと、以下の条件でプロジェクトを生成した。

  • maven-archetype-webapp
  • groupId = com.ymatsu.websample
  • artifactId = websample

pom.xmlに以下を追記。※1

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

   <!-- 中略 -->

  <repositories>
    <repository>
      <id>scala-tools.org</id>
      <name>Scala-tools Maven2 Repository</name>
      <url>http://scala-tools.org/repo-releases</url>
    </repository>
  </repositories>
  
  <pluginRepositories>
    <pluginRepository>
      <id>scala-tools.org</id>
      <name>Scala-tools Maven2 Repository</name>
      <url>http://scala-tools.org/repo-releases</url>
    </pluginRepository>
  </pluginRepositories>

  <dependencies>
    <dependency>
      <groupId>org.scala-lang</groupId>
      <artifactId>scala-library</artifactId>
      <version>2.7.2</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.5</version>
    </dependency>

    <!-- jetty -->
    <dependency>
      <groupId>org.eclipse.jetty</groupId>
      <artifactId>jetty-server</artifactId>
      <version>7.0.1.v20091125</version>
    </dependency>
    <dependency>
      <groupId>org.eclipse.jetty</groupId>
      <artifactId>jetty-servlets</artifactId>
      <version>7.0.1.v20091125</version>
    </dependency>
    <dependency>
      <groupId>org.eclipse.jetty</groupId>
      <artifactId>jetty-webapp</artifactId>
      <version>7.0.1.v20091125</version>
    </dependency>
    <dependency>
      <groupId>org.eclipse.jetty</groupId>
      <artifactId>jetty-websocket</artifactId>
      <version>7.0.1.v20091125</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
    <sourceDirectory>src/main/scala</sourceDirectory>
    <outputDirectory>src/main/webapp/WEB-INF/classes</outputDirectory>
    <testSourceDirectory>src/test/scala</testSourceDirectory>
    <finalName>websample</finalName>
    <plugins>
      <plugin>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>maven-jetty-plugin</artifactId>
        <configuration>
          <scanIntervalSeconds>10</scanIntervalSeconds>
          <webAppConfig>
            <contextPath>/</contextPath>
          </webAppConfig>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.scala-tools</groupId>
        <artifactId>maven-scala-plugin</artifactId>
        <executions>
          <execution>
            <goals>
              <goal>compile</goal>
              <goal>testCompile</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

</project>

※1 あとで分かるのだが、jettyのバージョンでハマった。

 

そんでもって、scalaで記述するために必要なディレクトリ生成

mkdir -p src/main/scala/com/ymatsu/websample
mkdir -p src/test/scala

コードはこんな感じのものを、src/main/scala/以下に適切なpathで配置。

package com.ymatsu.websample
import javax.servlet.http._

class Demo extends HttpServlet {
   override def doGet(request: HttpServletRequest, response: HttpServletResponse) = response.getWriter().println("Hello World (From Scala!)")
}

あとは、web.xmlをsrc/main/webapp/WEB-INF/に配置。
中身は以下。

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>

  <servlet>
    <servlet-name>Home</servlet-name>
    <servlet-class>com.ymatsu.websample.Demo</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>Home</servlet-name>
    <url-pattern>/home</url-pattern>
  </servlet-mapping>
</web-app>
mvn jetty:run

でサーバー起動。
http://localhost:8080/home にアクセスしてHello world的な物が表示されればおk!!

んで本題のWebSocket。
とりあえずここのコードを使わせていただいて動かしてみた。

package com.ymatsu.websample

import scala.collection.mutable.Set
import javax.servlet.http._
import org.eclipse.jetty.websocket._
import org.eclipse.jetty.websocket.WebSocket.Outbound

class ChatServlet extends WebSocketServlet {
  val clients = Set.empty[ChatWebSocket]

  override def doGet(req:HttpServletRequest, res:HttpServletResponse ) =
    getServletContext.getNamedDispatcher("Home").forward(req, res)

  override def doWebSocketConnect(req:HttpServletRequest, protocol:String ) =
    new ChatWebSocket

  class ChatWebSocket extends WebSocket {

    var outbound:Outbound = _

    override def onConnect(outbound:Outbound ) = {
      this.outbound = outbound
      clients += this
      onMessage( 0, "WebSocket is success!!!");
    }

    override def onMessage(frame:Byte, data:Array[Byte], offset:Int, length:Int ) = {}

    override def onMessage(frame:Byte, data:String ) =
      clients.foreach{ c => c.outbound.sendMessage( frame, data ) }

    override def onDisconnect = clients -= this

  }
}

 

そんでもってweb.xmlに適当なパスを追記。(今回はws://localhost:8080/chat)
また、src/main/webapp直下にindex.htmlとして以下のファイルを配置。

<?xml version='1.0' encoding='utf-8' ?>
<html>
  <head>
    <title>websocket test</title>
    <script src='http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js' type='text/javascript'></script>
    <script type='text/javascript'>
    var ws = new WebSocket("ws://localhost:8080/socket");

    ws.onmessage = function(e){
    trace(e.data);
    };
    ws.onclose = function(){
    log("ws closed");
    };
    ws.onopen = function(){
    log('connected!!');
    };
    
    $(function(){
    $('input#post').click(function(){
        var name = $('input#name').val();
    var mes = $('input#message').val();
    ws.send(name+" : "+mes);
    $('input#message').val("");
    });
    });
    function log(message){
    trace("[log] "+message);
    };
    function trace(message){
    var mes_div = $('<div />').html(message);
    $('div#chat').prepend(mes_div);
    };
    </script>
  </head>
  <body>
    <form id='message'>
      <input id='name' size='15' type='text' value='名前' />
      <input id='message' size='80' type='text' value='Hello world!!' />
      <input id='post' type='button' value='post' />
    </form>
    <div id='chat'></div>
  </body>
</html>

ここまで来ればあとはmvn jetty:run で動く!
と思っていた。

が!

が!

動かない。

ずっと原因を追っていくと、servletがwebsocketのリクエストをGetとしてしか受け取ってくれない!!!

なぜだ!

と思ったがふとWebSocketの仕様が頭に浮かんで調べてみると、案の定jettyのver7.0は現状のchromeのwebsocket仕様に追いついていなかったらしい。

なのでpom.xmlのjettyのバージョンを以下に書き換え。
各jettyのdependencyに関して

- <version>7.0.1.v20091125</version>
+ <version>7.5.4.v20111024</version>

pluginに関して

- <artifactId>maven-jetty-plugin</artifactId>
+ <artifactId>jetty-maven-plugin</artifactId>
+ <version>7.5.4.v20111024</version>

そんでバージョンに合わせてソースコードを以下のように編集。

package com.ymatsu.websample

import scala.collection.mutable.Set
import javax.servlet.http._
import org.eclipse.jetty.websocket._
import org.eclipse.jetty.websocket.WebSocket.Connection

class App extends WebSocketServlet {
  val clients = Set.empty[ChatWebSocket]

  override def doGet(req:HttpServletRequest, res:HttpServletResponse ) =
    getServletContext.getNamedDispatcher("Home").forward(req, res)

  override def doWebSocketConnect(req:HttpServletRequest, protocol:String ) =
    new ChatWebSocket

  class ChatWebSocket extends WebSocket.OnTextMessage {
    var connection:Connection = _

    override def onOpen(connection:Connection ) = {
      this.connection = connection
      clients += this
      onMessage("WebSocket is success!!!");
    }

    override def onMessage(data:String ) =
      clients.foreach{ c => c.connection.sendMessage(data) }

    override def onClose(code:Int,message:String) = clients -= this

  }
}

以上でやっとこさ動きましたとさ。

チャットとしてはこれでも十分動きますが、チャットルームとか欲しいし、保存したいのでもっと色々勉強。

次はMongoDBとの接続なりやってみようかなと思う次第。

Scala初学者がチャットを作るまで 2

とりあえずまずScalaの環境を構築するところから始める。
なんか既にチャットでいいのか?という気がしてきたがとりあえず作ろう。

Scala自体のインストールは、
Macであれば

brew install scala

ubuntuなら

sudo apt-get install scala

次に、インストールは終わってるのですがEmacsの環境構築のためにScalaを公式ページよりダウンロード。

wget http://www.scala-lang.org/downloads/distrib/files/scala-2.9.1.final.tgz
tar zxf scala-2.9.1.final.tgz

その中にEmacsへのサポートが含まれているのでそれらを~/.emacs.d以下にコピーしましょう。

cd scala-2.9.1.final
cp -r misc/scala-tool-support/emacs ~/.emacs.d/scala-mode

さらにIDE的な機能の追加。

brew install sbt
wget https://github.com/downloads/aemoncannon/ensime/ensime_2.9.2-SNAPSHOT-0.8.0.RC3.tar.gz
tar zxf ensime_2.9.2-SNAPSHOT-0.8.0.RC3.tar.gz
cp -r ensime_2.9.2-SNAPSHOT-0.8.0.RC3 ~/.emacs.d/ensime

最後に.emacsに追記。

(add-to-list 'load-path "~/.emacs.d/scala-mode")
(require 'scala-mode-auto)
(require 'scala-mode-feature-electric)
(add-hook 'scala-mode-hook
     (lambda ()
	(scala-electric-mode)))
(add-to-list 'load-path "~/.emacs.d/ensime/elisp/")
(require 'ensime)
(add-hook 'scala-mode-hook 'ensime-scala-mode-hook)

このensimeというやつはmavenなどと連携してプロジェクト管理を上手くやってくれるらしいのだが、javaあまりやらないから別途勉強することにして、自動補完機能にだけ感謝しておく。

以上でとりあえずEmacsでScalaの作業がしやすくなった。

ちなみにEnsimeに関しては公式ドキュメント読んでください。

参考にしたページ
http://codezine.jp/article/detail/5193
http://d.hatena.ne.jp/tototoshi/20100925/1285420294
http://d.hatena.ne.jp/tototoshi/20100927/1285595939
Ensime 公式ドキュメント