Question

Im a newbie scala and scalatra developer. Im trying to integrate c3p0 to get connection pooling in my application.

All the examples on the scalatra page is with squeryl etc, but I dont want orm and dsl. Do anyone have a ok examples with scalatra and c3p0.

Thanks all :)

Was it helpful?

Solution 2

Note: None of the code below has been compiled or checked, i'm just writing it into my browser. apologies for the inevitable glitches.

So, I've never used Scalatra. But I wrote c3p0, and have used the Servlets API a lot. A quick look at scalatra's guides suggests something like this would work:

import org.scalatra._
import com.mchange.v2.c3p0._
import javax.sql.DataSource
import javax.servlet.ServletContext

class ScalatraBootstrap extends LifeCycle {

  override def init(context: ServletContext) {
    val cpds = new ConnectionPoolDataSource();

    // perform any c3p0 config operations you might 
    // want here, or better yet, externalize all of 
    // that into a c3p0.properties, c3p0-config.xml,
    // or (c3p0 version 0.9.5 only) application.conf

    context.setAttribute( "appDataSource", cpds );
  }

  override def destroy(context: ServletContext) {
    val cpds = context.getAttribute( "appDataSource" );
    if ( cpds != null ) {
      try {
        cpds.close() 
      } catch {
        case e : Exception => e.printStackTrace(); //consider better logging than this
      }
    }
  }
}

To get access to the DataSource from a ServletRequest object, you'd call...

request.getServletContext().getAttribute( "appDataSource" ).asInstanceOf[DataSource]

You might want to use your Scala-fu to pimp ServletRequest and make access to the Connection pool easier and prettier. For example, you could write...

implicit class ConnectionPoolRequest( request : ServletRequest ) {
   def connectionPool : DataSource = request.getServletContext().getAttribute( "appDataSource" ).asInstanceOf[DataSource]
}

Put this in a package object or some object you import into your code, and while you handle requests you should be able to write stuff like...

val conn = request.connectionPool.getConnection();
// do stuff
conn.close()

However, the above is crappy, leak-prone code, because the close() isn't in a finally and will be skipped by an Exception. In Java7-style, you'd use try-with-resources to avoid this. In Scala, the naive way is to do this:

var conn = null;
try {
  conn = request.connectionPool.getConnection();
  // do stuff
} finally {
  try { if ( conn != null ) conn.close() } catch {
     case e : Exception => e.printStackTrace() // better logging would be nice
  }
}

However, a better way in Scala is to define utility methods like this:

def withConnection[T]( ds : DataSource )( op : (Connection) => T) : T = {
  var con : Connection = null;

  try {
    con = ds.getConnection();
    op(con);
  } finally { 
    attemptClose( con ); 
  }
}

def attemptClose( con : Connection ) {
  if ( con != null ) {
    try { if ( conn != null ) conn.close() } catch {
      case e : Exception => e.printStackTrace() // better logging would be nice
    }
  }
}

Then you can just write stuff like...

withConnection( request.connectionPool ) { conn =>
  // do stuff with the Connection
  // don't worry about cleaning up, that's taken care of for you
}

To really keep JDBC clean in scala, consider writing analogous methods like withStatement and withResultSet as well, so you can do

withConnection( request.connectionPool ) { conn =>
  withStatement( conn ) { stmt =>
    withResultSet( stmt.executeQuery("SELECT * FROM spacemen") ) { rs =>
      // read stuff from ResultSet
    }
  } 
}

OTHER TIPS

In addition to Steve's response, you can use a scala object for the collectionPoolDataSource instead of getting it from the request context.

For example, declaring:

object DBDataSource {

private val ds = new ComboPooledDataSource
ds.setDriverClass("org.mariadb.jdbc.Driver")
ds.setUser(dbUser)
ds.setPassword(dbPassword)
ds.setDebugUnreturnedConnectionStackTraces(true)
ds.setUnreturnedConnectionTimeout(7200)
ds.setMaxPoolSize(100)
ds.setMaxStatements(0)
ds.setCheckoutTimeout(60000)
ds.setMinPoolSize(5)
ds.setTestConnectionOnCheckin(true)
ds.setTestConnectionOnCheckout(false)
ds.setBreakAfterAcquireFailure(false)
ds.setIdleConnectionTestPeriod(50)
ds.setMaxIdleTimeExcessConnections(240)
ds.setAcquireIncrement(1)
ds.setAcquireRetryAttempts(5)
ds.setJdbcUrl(dbUrl)
ds.setPreferredTestQuery("SELECT 1")

def datasource = ds

}

and you can access to datasource without needing the request context:

def withConnection[T](op: (Connection) => T): T = { var con: Connection = null

try {
  con = DBDataSource.datasource.getConnection()
  op(con)
} finally {
  attemptClose(con)
}

}

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top