Skip to content
This repository has been archived by the owner on Jan 27, 2019. It is now read-only.

MongoOptions Support fixes #36 #37

Merged
merged 3 commits into from
Feb 6, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,23 @@ now we need to setup our connections. The plugin is modeled after how plays DB p
# host2.port = 27018
#}

# Mongo Options
# ~~~~~
# http://api.mongodb.org/java/2.8.0/com/mongodb/MongoOptions.html
#
# For passing custom options to the MongoConnection add the properties under "options". Add just the ones which are different from defaults.

#mongodb.default.options {
# connectionsPerHost = 100
# threadsAllowedToBlockForConnectionMultiplier = 1000
# connectTimeout = 60000
#}

## More that one DB?
If you would like to connect to two databases you need to create two source names
If you would like to connect to two databases you need to create two source names. You also can specify different options per database

mongodb.myotherdb.db = "otherdb"
mongodb.myotherdb.options.connectionsPerHost = 80

Then you can call `mongoCollection("collectionname", "myotherdb")`

Expand Down
42 changes: 42 additions & 0 deletions src/main/scala/se/radley/plugin/salat/OptionsFromConfig.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package se.radley.plugin.salat

import play.api.Configuration
import com.mongodb.casbah.MongoOptions

object OptionsFromConfig {

def apply(config: Option[Configuration]): Option[com.mongodb.MongoOptions] = {
if (config.isDefined && config.get.keys.isEmpty) None
else config.map { implicit conf =>
val defaults = new com.mongodb.MongoOptions
MongoOptions(
autoConnectRetry = ("autoConnectRetry", defaults.autoConnectRetry),
connectionsPerHost = ("connectionsPerHost", defaults.connectionsPerHost),
threadsAllowedToBlockForConnectionMultiplier = ("threadsAllowedToBlockForConnectionMultiplier", defaults.threadsAllowedToBlockForConnectionMultiplier),
maxWaitTime = ("maxWaitTime", defaults.maxWaitTime),
connectTimeout = ("connectTimeout", defaults.connectTimeout),
socketTimeout = ("socketTimeout", defaults.socketTimeout),
socketKeepAlive = ("socketKeepAlive", defaults.socketKeepAlive),
maxAutoConnectRetryTime = ("maxAutoConnectRetryTime", defaults.maxAutoConnectRetryTime),
slaveOk = ("slaveOk", defaults.slaveOk),
safe = ("safe", defaults.safe),
w = ("w", defaults.w),
wTimeout = ("wtimeout", defaults.wtimeout),
fsync = ("fsync", defaults.fsync),
j = ("j", defaults.j),
dbDecoderFactory = ("dbDecoderFactory", defaults.dbDecoderFactory),
dbEncoderFactory = ("dbEncoderFactory", defaults.dbEncoderFactory),
//socketFactory = ("socketFactory", defaults.socketFactory), FIXME Dependency problem
description = ("description", defaults.description))
}
}

implicit def getBoolean(prop: (String, Boolean))(implicit conf: Configuration): Boolean = conf.getBoolean(prop._1) getOrElse prop._2
implicit def getString(prop: (String, String))(implicit conf: Configuration): String = conf.getString(prop._1) getOrElse prop._2
implicit def getInt(prop: (String, Int))(implicit conf: Configuration): Int = conf.getInt(prop._1) getOrElse prop._2
implicit def getLong(prop: (String, Long))(implicit conf: Configuration): Long = conf.getMilliseconds(prop._1) getOrElse prop._2
implicit def getFactory[F](prop: (String, F))(implicit conf: Configuration): F = {
conf.getString(prop._1).map { name => Class.forName(name).newInstance().asInstanceOf[F] } getOrElse prop._2
}

}
13 changes: 8 additions & 5 deletions src/main/scala/se/radley/plugin/salat/SalatPlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.mongodb.casbah._
import com.mongodb.{MongoException, ServerAddress}
import com.mongodb.casbah.gridfs.GridFS
import commons.MongoDBObject
import com.mongodb.MongoOptions

class SalatPlugin(app: Application) extends Plugin {

Expand All @@ -18,12 +19,13 @@ class SalatPlugin(app: Application) extends Plugin {
val writeConcern: com.mongodb.WriteConcern,
val user: Option[String] = None,
val password: Option[String] = None,
val options: Option[MongoOptions],
private var conn: MongoConnection = null
){

def connection: MongoConnection = {
if (conn == null) {
conn = MongoConnection(hosts)
conn = options.map(opts => MongoConnection(hosts, opts)).getOrElse(MongoConnection(hosts))

val authOpt = for {
u <- user
Expand Down Expand Up @@ -68,12 +70,13 @@ class SalatPlugin(app: Application) extends Plugin {
override def toString() = {
(if (user.isDefined) user.get + "@" else "") +
hosts.map(h => h.getHost + ":" + h.getPort).mkString(", ") +
"/" + dbName
"/" + dbName + options.map(" with Options[" + _ + "]").getOrElse("")
}
}

lazy val sources: Map[String, MongoSource] = configuration.subKeys.map { sourceKey =>
val source = configuration.getConfig(sourceKey).getOrElse(Configuration.empty)
val options = OptionsFromConfig(source.getConfig("options"))

source.getString("uri").map { str =>
// MongoURI config - http://www.mongodb.org/display/DOCS/Connections
Expand All @@ -90,7 +93,7 @@ class SalatPlugin(app: Application) extends Plugin {
val writeConcern = uri.options.getWriteConcern
val user = uri.username
val password = uri.password.map(_.mkString).filterNot(_.isEmpty)
sourceKey -> MongoSource(hosts, db, writeConcern, user, password)
sourceKey -> MongoSource(hosts, db, writeConcern, user, password, options)
}.getOrElse {
val dbName = source.getString("db").getOrElse(throw configuration.reportError("mongodb." + sourceKey + ".db", "db missing for source[" + sourceKey + "]"))

Expand All @@ -114,9 +117,9 @@ class SalatPlugin(app: Application) extends Plugin {

// If there are replicasets configured go with those otherwise fallback to simple config
if (hosts.isEmpty)
sourceKey -> MongoSource(List(new ServerAddress(host, port)), dbName, writeConcern, user, password)
sourceKey -> MongoSource(List(new ServerAddress(host, port)), dbName, writeConcern, user, password, options)
else
sourceKey -> MongoSource(hosts, dbName, writeConcern, user, password)
sourceKey -> MongoSource(hosts, dbName, writeConcern, user, password, options)
}
}.toMap

Expand Down
125 changes: 125 additions & 0 deletions src/test/scala/se/radley/plugin/salat/OptionsFromConfigSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package se.radley.plugin.salat

import org.specs2.mutable.Specification
import com.mongodb.MongoOptions
import play.api.Configuration
import com.mongodb.DBDecoderFactory
import com.mongodb.DBEncoderFactory
import javax.net.SocketFactory
import com.mongodb.DBDecoder
import org.specs2.mutable.SpecificationWithJUnit
import org.specs2.specification.AllExpectations
import scala.reflect.ClassManifest

class OptionsFromConfigSpec extends SpecificationWithJUnit with AllExpectations {

"OptionsFromConfig" should {
"Override all defaults when all props are present" in {
val allNonDefaultConfiguration = Map(
("mongodb.default.options.autoConnectRetry" -> "true"),
("mongodb.default.options.connectionsPerHost" -> "333"),
("mongodb.default.options.threadsAllowedToBlockForConnectionMultiplier" -> "22"),
("mongodb.default.options.maxWaitTime" -> "68000"),
("mongodb.default.options.connectTimeout" -> "34000"),
("mongodb.default.options.socketTimeout" -> "21000"),
("mongodb.default.options.socketKeepAlive" -> "true"),
("mongodb.default.options.maxAutoConnectRetryTime" -> "20"),
("mongodb.default.options.slaveOk" -> "true"),
("mongodb.default.options.safe" -> "true"),
("mongodb.default.options.w" -> "1"),
("mongodb.default.options.wtimeout" -> "10"),
("mongodb.default.options.fsync" -> "true"),
("mongodb.default.options.j" -> "true"),
("mongodb.default.options.dbDecoderFactory" -> "se.radley.plugin.salat.NonDefaultDBDecoderFactory"),
("mongodb.default.options.dbEncoderFactory" -> "se.radley.plugin.salat.NonDefaultDBEncoderFactory"),
("mongodb.default.options.socketFactory" -> "se.radley.plugin.salat.NonDefaultDBSocketFactory"),
("mongodb.default.options.description" -> "Some Description"))

val sourceConfig = Configuration.from(allNonDefaultConfiguration).getConfig("mongodb.default").get
val optionsConfig = sourceConfig.getConfig("options")
val optionsOpt = OptionsFromConfig(optionsConfig)
optionsOpt must beSome
val options = optionsOpt.get
// All Overridden
options.autoConnectRetry must beTrue
options.connectionsPerHost must be equalTo(333)
options.threadsAllowedToBlockForConnectionMultiplier must be equalTo(22)
options.maxWaitTime must be equalTo(68000)
options.connectTimeout must be equalTo(34000)
options.socketTimeout must be equalTo(21000)
options.socketKeepAlive must beTrue
options.maxAutoConnectRetryTime must be equalTo(20)
options.slaveOk must beTrue
options.safe must beTrue
options.w must be equalTo(1)
options.wtimeout must be equalTo(10)
options.fsync must beTrue
options.j must beTrue
options.description must be equalTo("Some Description")
options.dbDecoderFactory must haveClass[NonDefaultDBDecoderFactory]
options.dbEncoderFactory must haveClass[NonDefaultDBEncoderFactory]
//options.socketFactory must haveClass[NonDefaultSocketFactory] FIXME Dependency problem
}

"Override some defaults for present props" in {
val someNonDefaultConfiguration = Map(
("mongodb.default.options.connectionsPerHost" -> "255"),
("mongodb.default.options.threadsAllowedToBlockForConnectionMultiplier" -> "24"),
("mongodb.default.options.connectTimeout" -> "60000"))

val defaultOptions = new MongoOptions

val sourceConfig = Configuration.from(someNonDefaultConfiguration).getConfig("mongodb.default").get
val optionsConfig = sourceConfig.getConfig("options")
val optionsOpt = OptionsFromConfig(optionsConfig)
optionsOpt must beSome
val options = optionsOpt.get
// Overridden
options.connectionsPerHost must be equalTo(255)
options.threadsAllowedToBlockForConnectionMultiplier must be equalTo(24)
options.connectTimeout must be equalTo(60000)
// Remain defaults
options.autoConnectRetry must beFalse
options.maxWaitTime must be equalTo(1000 * 60 * 2)
options.socketTimeout must be equalTo(0)
options.socketKeepAlive must beFalse
options.maxAutoConnectRetryTime must be equalTo(0)
options.slaveOk must beFalse
options.safe must beFalse
options.w must be equalTo(0)
options.wtimeout must be equalTo(0)
options.fsync must beFalse
options.j must beFalse
options.description must beNull
options.dbDecoderFactory must be(defaultOptions.dbDecoderFactory)
options.dbEncoderFactory must be(defaultOptions.dbEncoderFactory)
options.socketFactory must be(defaultOptions.socketFactory)
}

"Return none options if config is not defined" in {
val undefinedConfig = None
val options = OptionsFromConfig(undefinedConfig)
options must beNone
}

"Return none options if config is empty" in {
val emptyConfig = Some(Configuration.empty)
val options = OptionsFromConfig(emptyConfig)
options must beNone
}
}

}

class NonDefaultDBDecoderFactory extends DBDecoderFactory {
def create() = null
}
class NonDefaultDBEncoderFactory extends DBEncoderFactory {
def create() = null
}
class NonDefaultSocketFactory extends SocketFactory {
def createSocket(host: String, port: Int) = null
def createSocket(address: java.net.InetAddress, port: Int) = null
def createSocket(host: String, port: Int, clientAddress: java.net.InetAddress, clientPort: Int) = null
def createSocket(address: java.net.InetAddress, port: Int, clientAddress: java.net.InetAddress, clientPort: Int) = null
}
46 changes: 35 additions & 11 deletions src/test/scala/se/radley/plugin/salat/SalatSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,14 @@ import com.mongodb.ServerAddress
object SalatSpec extends Specification {

lazy val salatApp = FakeApplication(
additionalPlugins = Seq("se.radley.plugin.salat.SalatPlugin")
)
additionalPlugins = Seq("se.radley.plugin.salat.SalatPlugin"))

"Salat Plugin with basic config" should {

lazy val app = salatApp.copy(
additionalConfiguration = Map(
("mongodb.default.db" -> "salat-test"),
("mongodb.default.writeconcern" -> "normal")
)
)
("mongodb.default.writeconcern" -> "normal")))

lazy val salat = app.plugin[SalatPlugin].get

Expand Down Expand Up @@ -59,9 +56,7 @@ object SalatSpec extends Specification {

lazy val app = salatApp.copy(
additionalConfiguration = Map(
("mongodb.default.uri" -> "mongodb://127.0.0.1:27017/salat-test")
)
)
("mongodb.default.uri" -> "mongodb://127.0.0.1:27017/salat-test")))

lazy val salat = app.plugin[SalatPlugin].get

Expand Down Expand Up @@ -95,9 +90,7 @@ object SalatSpec extends Specification {
"Salat Plugin with multiple uri config" should {
lazy val app = salatApp.copy(
additionalConfiguration = Map(
("mongodb.default.uri" -> "mongodb://127.0.0.1:27017,mongodb.org:1337/salat-test")
)
)
("mongodb.default.uri" -> "mongodb://127.0.0.1:27017,mongodb.org:1337/salat-test")))

lazy val salat = app.plugin[SalatPlugin].get

Expand Down Expand Up @@ -143,4 +136,35 @@ object SalatSpec extends Specification {
}
}
}

"Salat Plugin with options" should {

lazy val app = salatApp.copy(
additionalConfiguration = Map(
("mongodb.default.db" -> "salat-with-options"),
("mongodb.default.options.connectionsPerHost" -> "255"),
("mongodb.default.options.threadsAllowedToBlockForConnectionMultiplier" -> "24")))

lazy val salat = app.plugin[SalatPlugin].get

running(app) {
"start" in {
salat must beAnInstanceOf[SalatPlugin]
}

"return a MongoCollection" in {
val col = salat.collection("salat-collection")
col must beAnInstanceOf[MongoCollection]
}

"set mongo options" in {
val col = salat.collection("salat-collection")
val options = col.db.underlying.getMongo().getMongoOptions()
options.connectionsPerHost must equalTo(255)
options.threadsAllowedToBlockForConnectionMultiplier must equalTo(24)
}

}
}

}