ConnectionManagerの実装

ConnectionManagerの実装


liftでデータベースを使うには、ConnectionManagerトレイトの継承したクラスを作成する必要がある。

ConnectionManagerのサブクラスで実装するメソッド


  • newConnection( name: ConnectionIdentifier ): Box[Connection]
システムが新しいコネクションが必要な時に呼ばれる

  • releaseConnection(conn: Connection)
システムが使わなくなったコネクションをConnectionManagerに戻すときに呼ばれる

  • closeAllConnections_!()
全てのコネクションを閉じる。必ず実装する必要はないが、サーバを落とすときにこのメソッドが呼ばれるようにBoot.scalaで設定する。

実装例


コネクションプール機能を備えたDBVendorを実装する。
サーバ起動時に確保するコネクション数とプールしておく最大コネクション数をコンストラクタで指定するようにする。プールがいっぱいの時にnewConnectionが呼ばれた場合は、一時的に接続を確保し、releaseConnectionが呼ばれたら直ちに接続を閉じる。

 import net.liftweb._
 import mapper._
 import common._
 import scala.collection.mutable.Queue
 import java.io._
 import java.net._
 import java.sql.{Connection, DriverManager}

 class MyDBVendor(driver:String, url:String, user:Box[String], pwd:Box[String], initConnectionSize:Int, maxConnectionSize:Int) extends ConnectionManager with Logger {

   private val pool = new Queue[Connection]

   private var poolSize = 0
   private var connectionSize = 0
   private var tmpConnectionSize = 0

   initConnectionPool

   private def initConnectionPool = {
     debug( "initializing connection pool..." ) 
     for( i <- 1 to initConnectionSize ) {
getConnection match {
  case Full( conn ) => {
    poolSize += 1
    connectionSize += 1
    pool += conn
  }
  case _ => ()
}
     }
     debug( "connection pool is initialized." )
   }

   private def getConnection: Box[Connection] = try {
     debug( "making connection..." )
     Class.forName( driver )
     val conn = ( user, pwd ) match {
case (Full( u ), Full( p )) => DriverManager.getConnection( url, u, p )
case _ => DriverManager.getConnection( url )
     }
     debug( "connection is established: poolSize=%d, connectionSize=%d, tmpConnectionSize=%d, maxConnectionSize=%d".format(poolSize, connectionSize, tmpConnectionSize, maxConnectionSize) )
     Full( conn )
   } catch {
     case ex => {
error( "could not get connection", ex )
Empty
     }
   }

   private def useConnection: Box[Connection] = if( pool.isEmpty ) {
     if( connectionSize < maxConnectionSize ) {
connectionSize += 1
getConnection
     } else {
tmpConnectionSize += 1
getConnection
     }
   } else {
     debug( "use connection from pool: poolSize=%d->%d, connectionSize=%d, tmpConnectionSize=%d, maxConnectionSize=%d".format(poolSize, poolSize-1, connectionSize, tmpConnectionSize, maxConnectionSize) )
     poolSize -= 1
     Full(pool.dequeue)
   }

   private def freeConnection( conn: Connection ) = if( tmpConnectionSize != 0 ) {
     debug( "closing tmporary connection: poolSize=%d, connectionSize=%d, tmpConnectionSize=%d->%d, maxConnectionSize=%d".format(poolSize, connectionSize, tmpConnectionSize, tmpConnectionSize-1, maxConnectionSize) )
     tmpConnectionSize -= 1
     conn.close
   } else {
     debug( "putting connection back to pool: poolSize=%d->%d, connectionSize=%d, tmpConnectionSize=%d, maxConnectionSize=%d".format(poolSize, poolSize+1, connectionSize, tmpConnectionSize, maxConnectionSize) )
     poolSize += 1
     pool += conn
   }

   def newConnection( name: ConnectionIdentifier ): Box[Connection] = synchronized {
     useConnection
   }

   def releaseConnection(conn: Connection) = synchronized {
     freeConnection( conn )
   }

   def closeAllConnections_!():Unit = synchronized {
     try {
debug( "closing all connections" )

debug( "poolSize:%d, connectionSize:%d".format( poolSize, connectionSize ) )
while( poolSize != connectionSize ) wait( 1000 )

while( !pool.isEmpty ) {
  debug( "closeing connection: poolSize=%d->%d, connectionSize=%d->%d".format(poolSize, poolSize-1, connectionSize, connectionSize-1) )
  pool.dequeue.close
  poolSize -= 1
  connectionSize -= 1
}
debug( "all connections are closed: poolSize=%d, connectionSize:%d, tmpConnectionSize:%d".format(poolSize, connectionSize, tmpConnectionSize) )
     } catch {
case ex => error( "failed to close all connections: poolSize=%d, connectionSize:%d, tmpConnectionSize:%d".format(poolSize, connectionSize, tmpConnectionSize) )
     }
   }

 }

使用例


Boot.scalaで以下のように記述する

   if (!DB.jndiJdbcConnAvailable_?) {
     val vendor = new MyDBVendor(
Props.get("db.driver").openOr("com.mysql.jdbc.Driver"),
Props.get("db.url").openOr("jdbc:mysql://localhost/dbname"),
Props.get("db.username"), 
Props.get("db.password"),
Props.get("db.init.connection.size").openOr("5").toInt,
Props.get("db.max.connection.size").openOr("10").toInt
     )

     LiftRules.unloadHooks.append(vendor.closeAllConnections_! _)

     DB.defineConnectionManager(DefaultConnectionIdentifier, vendor)
   }
最終更新:2011年09月30日 13:57