├── .gitignore ├── .idea └── copyright │ ├── RxMongo.xml │ └── profiles_settings.xml ├── .travis.yml ├── LICENSE ├── README.md ├── bson └── src │ ├── main │ └── scala │ │ └── rxmongo │ │ └── bson │ │ ├── BSONArray.scala │ │ ├── BSONBuilder.scala │ │ ├── BSONDocument.scala │ │ ├── BSONDocumentBuilder.scala │ │ ├── BSONObject.scala │ │ ├── BSONReader.scala │ │ ├── BSONValue.scala │ │ ├── BinarySubtype.scala │ │ ├── ByteIteratorPimps.scala │ │ ├── ByteStringBuilderPimps.scala │ │ ├── Codec.scala │ │ ├── TypeCode.scala │ │ └── package.scala │ └── test │ └── scala │ └── rxmongo │ └── bson │ ├── BSONArraySpec.scala │ ├── BSONBuilderSpec.scala │ ├── BSONDocumentSpec.scala │ ├── BSONSpec.scala │ ├── BSONValueSpec.scala │ ├── BinarySubtypeSpec.scala │ ├── ByteStringBuilderPimpsSpec.scala │ ├── ByteStringTestUtils.scala │ └── CodecSpec.scala ├── client ├── rebel.xml └── src │ ├── main │ └── scala │ │ └── rxmongo │ │ └── client │ │ ├── Client.scala │ │ ├── Collection.scala │ │ ├── Cursor.scala │ │ ├── Database.scala │ │ ├── RxMongoComponent.scala │ │ └── UniformCollection.scala │ └── test │ └── scala │ └── rxmongo │ └── client │ ├── ClientSpec.scala │ ├── CollStatsSpec.scala │ ├── CollectionSpec.scala │ ├── CursorSpec.scala │ ├── DatabaseSpec.scala │ └── RxMongoTest.scala ├── driver └── src │ ├── main │ ├── resources │ │ ├── akka.conf │ │ ├── logback.xml │ │ └── rxmongo.conf │ └── scala │ │ └── rxmongo │ │ └── driver │ │ ├── ActorBase.scala │ │ ├── AkkaIOChannel.scala │ │ ├── Channel.scala │ │ ├── Connection.scala │ │ ├── ConnectionOptions.scala │ │ ├── Driver.scala │ │ ├── MongoURI.scala │ │ ├── ReadPreference.scala │ │ ├── StreamTcpChannel.scala │ │ └── Supervisor.scala │ └── test │ ├── resources │ └── logback-test.xml │ └── scala │ └── rxmongo │ └── driver │ ├── AkkaTest.scala │ ├── ConnectionOptionsSpec.scala │ ├── DriverSpec.scala │ ├── MongoURISpec.scala │ └── SupervisorSpec.scala ├── examples └── src │ └── main │ └── scala │ └── rxmongo │ └── examples │ ├── ClientBasics.scala │ ├── DriverBasics.scala │ └── LowLevel.scala ├── gridfs └── src │ └── main │ └── scala │ └── GridFS.scala ├── messages └── src │ ├── main │ └── scala │ │ └── rxmongo │ │ └── messages │ │ ├── AuthMechanism.scala │ │ ├── Command.scala │ │ ├── Delete.scala │ │ ├── Indices.scala │ │ ├── Message.scala │ │ ├── Operators.scala │ │ ├── Projection.scala │ │ ├── Query.scala │ │ ├── QueryOptions.scala │ │ ├── RetryStrategy.scala │ │ ├── Sort.scala │ │ ├── Update.scala │ │ ├── WriteConcern.scala │ │ ├── cmds │ │ ├── AdministrationCommands.scala │ │ ├── AggregationCommands.scala │ │ ├── AuthenticationCommands.scala │ │ ├── DatabaseCommands.scala │ │ ├── DiagnosticCommands.scala │ │ ├── GeoSpatialCommands.scala │ │ ├── GridFSCommands.scala │ │ ├── QueryPlanCacheCommands.scala │ │ ├── QueryWriteCommands.scala │ │ ├── ReplicationCommands.scala │ │ ├── RoleManagementCommands.scala │ │ ├── RolePrivilegeAction.scala │ │ ├── ShardingCommands.scala │ │ └── UserManagementCommands.scala │ │ ├── package.scala │ │ └── replies │ │ ├── BuildInfoReply.scala │ │ ├── CollStatsReply.scala │ │ ├── IsMasterReply.scala │ │ └── WriteResult.scala │ └── test │ └── scala │ └── rxmongo │ └── messages │ ├── CommandsSpec.scala │ ├── DeleteSpec.scala │ ├── IndicesSpec.scala │ ├── MessageSpec.scala │ ├── OperatorsSpec.scala │ ├── ProjectionSpec.scala │ ├── QuerySpec.scala │ ├── UpdateSpec.scala │ └── WriteResultSpec.scala ├── project ├── Dependencies.scala ├── RxMongo.scala ├── build.properties └── plugins.sbt └── version.sbt /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.log 3 | # sbt specific 4 | .cache/ 5 | .history/ 6 | .idea/ 7 | .lib/ 8 | */old/* 9 | dist/* 10 | target/ 11 | lib_managed/ 12 | src_managed/ 13 | project/boot/ 14 | project/plugins/project/ 15 | 16 | # Scala-IDE specific 17 | .scala_dependencies 18 | .worksheet 19 | -------------------------------------------------------------------------------- /.idea/copyright/RxMongo.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Travis Configuration File 2 | # See http://www.scala-sbt.org/0.13/docs/Travis-CI-with-sbt.html 3 | # See http://docs.travis-ci.com/ 4 | 5 | # Ensure we get a container-based build 6 | sudo: required 7 | 8 | # Our primary language is scala 9 | language: scala 10 | 11 | # The version of scala we use in our build 12 | scala: 13 | - "2.11.6" 14 | 15 | # Right now we only use JDK 8 16 | jdk: 17 | - oraclejdk8 18 | 19 | before_script: 20 | - sleep 15 21 | 22 | # How to run the build. Note the use of scoverage commands 23 | script: 24 | - sbt ++$TRAVIS_SCALA_VERSION -Dfile.encoding=UTF8 clean coverage test coverageAggregate 25 | 26 | services: 27 | - mongodb 28 | 29 | addons: 30 | apt: 31 | sources: 32 | - mongodb-3.0-precise 33 | packages: 34 | - mongodb-org-server 35 | 36 | # What to cache between builds (speeds up next build) 37 | cache: 38 | directories: 39 | - $HOME/.ivy2 40 | 41 | # Whenw e are done, 42 | after_success: 43 | - sbt coveralls 44 | # Tricks to avoid unnecessary cache updates 45 | - find $HOME/.sbt -name "*.lock" | xargs rm 46 | - find $HOME/.ivy2 -name "ivydata-*.properties" | xargs rm 47 | 48 | notifications: 49 | email: 50 | - reid@reactific.com 51 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Reactific Software LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /bson/src/main/scala/rxmongo/bson/BSONArray.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.bson 24 | 25 | import akka.util.{ ByteIterator, ByteString, ByteStringBuilder } 26 | 27 | import scala.collection._ 28 | 29 | case class BSONArray private[bson] ( final val doc : BSONDocument) extends BSONValue 30 | with SeqLike[BSONValue, BSONArray] { 31 | final val code : TypeCode = ArrayCode 32 | final def value : Seq[BSONValue] = seq 33 | 34 | override def equals(other : Any) : Boolean = { 35 | other match { 36 | case that : BSONArray ⇒ this.doc.equals(that.doc) 37 | case _ ⇒ false 38 | } 39 | } 40 | 41 | override def toByteString : ByteString = doc.toByteString 42 | 43 | private[bson] def addTo(bldr : ByteStringBuilder) : Unit = { 44 | bldr ++= doc.toByteString 45 | } 46 | 47 | def length : Int = doc.size 48 | 49 | def iterator : Iterator[BSONValue] = { 50 | new Iterator[BSONValue] { 51 | val i = doc.toSeq.sortBy { x => x._1.toInt }.iterator 52 | def hasNext : Boolean = i.hasNext 53 | def next() : BSONValue = { 54 | val (key, (b, bi)) = i.next() 55 | BSONValue(b, bi) 56 | } 57 | } 58 | } 59 | 60 | def apply(index : Int) : BSONValue = { 61 | doc.get(index.toString) match { 62 | case Some((typeCode, byteIterator)) ⇒ 63 | BSONValue(typeCode, byteIterator) 64 | case None ⇒ 65 | throw new NoSuchElementException(index.toString) 66 | } 67 | } 68 | 69 | protected[this] def newBuilder : mutable.Builder[BSONValue, BSONArray] = ??? 70 | 71 | def seq : Seq[BSONValue] = { 72 | doc.toSeq.sortBy { x => x._1.toInt } map { 73 | case (key, (b, bi)) ⇒ 74 | BSONValue(b, bi) 75 | } 76 | } 77 | 78 | def asArray : Array[BSONValue] = { 79 | doc.toSeq.sortBy { x => x._1.toInt} .map { 80 | case (key, (b, bi)) ⇒ BSONValue(b, bi) 81 | }.toArray 82 | } 83 | 84 | override def toString() : String = { 85 | val s = new StringBuilder 86 | val itr = iterator 87 | if (itr.isEmpty) { 88 | s.append("[]") 89 | } else { 90 | s.append("[ ") 91 | for (v ← itr) { 92 | s.append(v.toString()).append(", ") 93 | } 94 | s.setLength(s.length - 2) 95 | s.append(" ]") 96 | } 97 | s.toString() 98 | } 99 | 100 | def as[T](implicit codec : Codec[T]) : Iterator[T] = { 101 | iterator.map { 102 | case v : BSONValue if v.code == codec.code ⇒ 103 | codec.read(v) 104 | case v : BSONValue ⇒ 105 | throw new IllegalArgumentException(s"Expected type ${codec.typeName} but got type ${v.typeName}" 106 | ) 107 | } 108 | } 109 | } 110 | 111 | object BSONArray { 112 | 113 | object empty extends BSONArray(BSONDocument.empty) 114 | 115 | def apply(buffer : ByteString) : BSONArray = BSONArray(buffer.iterator) 116 | def apply(itr : ByteIterator) : BSONArray = new BSONArray(BSONDocument(itr)) 117 | def apply(values : Iterator[BSONValue]) : BSONArray = { 118 | val pairs : Iterator[(String, (Byte, ByteIterator))] = values.zipWithIndex.map { 119 | case (v, i) ⇒ i.toString → (v.code.code → v.toByteString.iterator) 120 | } 121 | new BSONArray(BSONDocument(pairs.toMap)) 122 | } 123 | 124 | def apply() : BSONArray = empty 125 | 126 | def apply(objs : BSONObject*) : BSONArray = { 127 | val bldr = ByteString.newBuilder 128 | bldr.putAnyArray(objs) 129 | BSONArray(bldr.result()) 130 | } 131 | 132 | def apply[T](data : Iterable[T])(implicit codec : Codec[T]) : BSONArray = { 133 | val bldr = ByteString.newBuilder 134 | bldr.putArray(data) 135 | BSONArray(bldr.result()) 136 | } 137 | 138 | def apply[T](data1 : T, data : T*)(implicit codec : Codec[T]) : BSONArray = { 139 | val bldr = ByteString.newBuilder 140 | val values = data1 +: data 141 | bldr.putArray(values) 142 | BSONArray(bldr.result()) 143 | } 144 | 145 | def fromAny(data : Array[Any]) : BSONArray = { 146 | val bldr = ByteString.newBuilder 147 | bldr.putAnyArray(data) 148 | BSONArray(bldr.result()) 149 | } 150 | 151 | def fromAny(data : Iterable[Any]) : BSONArray = { 152 | val bldr = ByteString.newBuilder 153 | bldr.putAnyArray(data) 154 | BSONArray(bldr.result()) 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /bson/src/main/scala/rxmongo/bson/BSONDocumentBuilder.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.bson 24 | 25 | import java.nio.ByteOrder 26 | 27 | import akka.util.{ ByteString, ByteStringBuilder, ByteIterator } 28 | 29 | import scala.collection.mutable 30 | 31 | class BSONDocumentBuilder(hint : Int) extends mutable.Builder[(String, (Byte, ByteIterator)), BSONDocument] { 32 | implicit val byteOrder = ByteOrder.LITTLE_ENDIAN 33 | val buffer : ByteStringBuilder = ByteString.newBuilder 34 | buffer.sizeHint(hint) 35 | 36 | def +=(elem : (String, (Byte, ByteIterator))) : BSONDocumentBuilder.this.type = ??? 37 | 38 | def result() : BSONDocument = ??? 39 | 40 | def clear() : Unit = ??? 41 | } 42 | 43 | object BSONDocumentBuilder { 44 | def apply(hint : Int = 512) : BSONDocumentBuilder = new BSONDocumentBuilder(hint) 45 | } 46 | -------------------------------------------------------------------------------- /bson/src/main/scala/rxmongo/bson/BSONReader.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.bson 24 | 25 | import akka.util.{ ByteIterator, ByteString } 26 | 27 | import scala.annotation.switch 28 | import scala.collection.{ Map, mutable } 29 | 30 | class BSONReader private[rxmongo] (itr : ByteIterator) { 31 | 32 | def toMap : Map[String, BSONValue] = { 33 | doc.map { case (key, (code, bs)) ⇒ key -> BSONValue(code, bs) } 34 | } 35 | 36 | type DecodedDoc = Map[String, (TypeCode, ByteString)] 37 | 38 | private val doc : DecodedDoc = { 39 | 40 | def advance(i : ByteIterator, code : Byte) : Int = { 41 | (code : @switch) match { 42 | case 1 ⇒ i.skipDouble // Double 43 | case 2 ⇒ i.skipLength // String 44 | case 3 ⇒ i.skipDocument // Object 45 | case 4 ⇒ i.skipDocument // Array 46 | case 5 ⇒ i.skipLength + i.skipByte // Binary 47 | case 6 ⇒ 0 // Undefined 48 | case 7 ⇒ i.skipObjId // ObjectID 49 | case 8 ⇒ i.skipByte // Boolean 50 | case 9 ⇒ i.skipLong // Date 51 | case 10 ⇒ 0 // Null 52 | case 11 ⇒ i.skipCStr + i.skipCStr // Regex 53 | case 12 ⇒ i.skipLength + i.skipObjId // DBPointer 54 | case 13 ⇒ i.skipLength // JavaScript 55 | case 14 ⇒ i.skipLength // Symbol 56 | case 15 ⇒ i.skipDocument // Scoped JavaScript 57 | case 16 ⇒ i.skipInt // Integer 58 | case 17 ⇒ i.skipLong // Timestamp 59 | case 18 ⇒ i.skipLong // Long 60 | case _ ⇒ throw new NoSuchElementException("Unrecognized BSON Type Code") 61 | } 62 | } 63 | val bldr = new mutable.MapBuilder[String, (TypeCode, ByteString), Map[String, (TypeCode, ByteString)]](Map.empty[String, (TypeCode, ByteString)]) 64 | val byteLen = itr.getInt 65 | var code = itr.getByte 66 | while (itr.hasNext && code != 0) { 67 | val key = itr.getCStr 68 | val save = itr.clone() 69 | val len = advance(itr, code) 70 | bldr += key -> (TypeCode(code) → save.slice(0, len).toByteString) 71 | code = itr.getByte 72 | } 73 | bldr.result() 74 | } 75 | } 76 | 77 | object BSONReader { 78 | def apply(bs : ByteString) = new BSONReader(bs.iterator) 79 | } 80 | -------------------------------------------------------------------------------- /bson/src/main/scala/rxmongo/bson/BinarySubtype.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.bson 24 | 25 | /** Binary fields specify their sub type which we model with instances of this trait */ 26 | trait BinarySubtype { 27 | /** The code value for the binary subtype as required by the BSON specification */ 28 | val code : Byte 29 | } 30 | 31 | object BinarySubtype { 32 | case object GenericBinary extends { val code = 0.toByte } with BinarySubtype 33 | case object FunctionBinary extends { val code = 1.toByte } with BinarySubtype 34 | case object DeprecatedGenericBinary extends { val code = 2.toByte } with BinarySubtype 35 | case object DeprecatedUUIDBinary extends { val code = 3.toByte } with BinarySubtype 36 | case object UUIDBinary extends { val code = 4.toByte } with BinarySubtype 37 | case object MD5SumBinary extends { val code = 5.toByte } with BinarySubtype 38 | case object UserDefinedBinary extends { val code : Byte = -128 } with BinarySubtype 39 | 40 | def apply(code : Byte) = code match { 41 | case 0 ⇒ GenericBinary 42 | case 1 ⇒ FunctionBinary 43 | case 2 ⇒ DeprecatedGenericBinary 44 | case 3 ⇒ DeprecatedUUIDBinary 45 | case 4 ⇒ UUIDBinary 46 | case 5 ⇒ MD5SumBinary 47 | case -128 ⇒ UserDefinedBinary 48 | case _ ⇒ throw new NoSuchElementException(s"BinarySubtype($code)") 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /bson/src/main/scala/rxmongo/bson/ByteIteratorPimps.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.bson 24 | 25 | import java.nio.charset.StandardCharsets 26 | import java.util.regex.Pattern 27 | 28 | import akka.util.ByteIterator 29 | 30 | import scala.annotation.switch 31 | 32 | /** Implicit Extensions To ByteIterator 33 | * 34 | * This makes it easier to use a ByteIterator with MongoDB. The extensions implement recognition of various 35 | * data types that BSON encoding requires. 36 | */ 37 | trait ByteIteratorPimps { 38 | 39 | val itr : ByteIterator 40 | 41 | @inline def getCStrItr : ByteIterator = { 42 | val s = itr.clone().takeWhile { p ⇒ p != 0 } 43 | itr.drop(s.len + 1) 44 | s 45 | } 46 | 47 | @inline def getCStr : String = { 48 | val s = itr.clone().takeWhile { p ⇒ p != 0 } 49 | itr.drop(s.len + 1) 50 | new String(s.toArray, StandardCharsets.UTF_8) 51 | } 52 | 53 | def getStr : String = { 54 | val len = itr.getInt - 1 55 | require(len < maxDocSize, s"Maximum string size is $maxDocSize bytes") 56 | val buf = Array.ofDim[Byte](len) 57 | itr.getBytes(buf) 58 | require(itr.getByte == 0.toByte, "Failed to read terminating null in String") 59 | new String(buf, StandardCharsets.UTF_8) 60 | } 61 | 62 | def getBytes(len : Int) : Array[Byte] = { 63 | val res = Array.ofDim[Byte](len) 64 | itr.getBytes(res) 65 | res 66 | } 67 | 68 | def getDoc : BSONDocument = { 69 | val save = itr.clone() 70 | val len = itr.getInt 71 | require(len < maxDocSize, s"Maximum object size is $maxDocSize bytes") 72 | itr.drop(len - 4) 73 | val bitr = save.slice(0, len) 74 | BSONDocument(bitr) 75 | } 76 | 77 | def getDocuments(count : Int) : Seq[BSONDocument] = { 78 | for (i ← 1 to count) yield { getDoc } 79 | } 80 | 81 | @inline def getObject : BSONObject = BSONObject(getDoc) 82 | 83 | @inline def getArray : BSONArray = BSONArray(getDoc) 84 | 85 | def getObjects(count : Int) : Seq[BSONObject] = { 86 | for (x ← 1 to count) yield { getObject } 87 | } 88 | 89 | def getBinary : (BinarySubtype, Array[Byte]) = { 90 | val len = itr.getInt 91 | val st = BinarySubtype(itr.getByte) 92 | val value = itr.getBytes(len) 93 | st → value 94 | } 95 | 96 | @inline def getObjectID : Array[Byte] = itr.getBytes(12) 97 | 98 | @inline def getBoolean : Boolean = itr.getByte != 0 99 | 100 | def getRegex : Pattern = { 101 | val pattern = itr.getCStr 102 | val options = itr.getCStr 103 | val flags = options.foldLeft(0) { 104 | case (flg, ch) ⇒ 105 | (ch : @switch) match { 106 | case 'i' ⇒ flg | Pattern.CASE_INSENSITIVE 107 | case 'm' ⇒ flg | Pattern.MULTILINE 108 | case 's' ⇒ flg | Pattern.DOTALL 109 | case 'u' ⇒ flg | Pattern.UNICODE_CHARACTER_CLASS | Pattern.UNICODE_CASE 110 | case 'x' ⇒ flg | Pattern.COMMENTS 111 | case _ ⇒ flg 112 | } 113 | } 114 | Pattern.compile(pattern, flags) 115 | } 116 | 117 | @inline def getDBPointer : (String, Array[Byte]) = { 118 | itr.getStr → itr.getBytes(12) 119 | } 120 | 121 | @inline def getScopedJavaScript : (String, BSONObject) = { 122 | itr.getInt 123 | itr.getStr → itr.getObject 124 | } 125 | 126 | @inline def skipLength : Int = { 127 | val len = itr.getInt 128 | itr.drop(len) 129 | len + 4 130 | } 131 | 132 | @inline def skipCStr : Int = { 133 | var count = 0 134 | itr.dropWhile { ch ⇒ count = count + 1; ch != 0 } 135 | itr.drop(1) 136 | count 137 | } 138 | 139 | @inline def skipDocument : Int = { 140 | val len = itr.getInt 141 | itr.drop(len - 4) 142 | len 143 | } 144 | 145 | @inline def skipLong : Int = { itr.drop(8); 8 } 146 | @inline def skipDouble : Int = skipLong 147 | @inline def skipInt : Int = { itr.drop(4); 4 } 148 | @inline def skipObjId : Int = { itr.drop(12); 12 } 149 | @inline def skipByte : Int = { itr.drop(1); 1 } 150 | 151 | } 152 | -------------------------------------------------------------------------------- /bson/src/main/scala/rxmongo/bson/TypeCode.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.bson 24 | 25 | import scala.annotation.switch 26 | 27 | /** Base class of the case objects for BSON code values 28 | * 29 | * Byte code identifiers from here: [[http://docs.mongodb.org/manual/reference/bson-types/]] 30 | */ 31 | sealed trait TypeCode { 32 | val code : Byte 33 | val typeName : String 34 | } 35 | 36 | case object NotACode extends { val code : Byte = 0; val typeName = "Not_A_BSONValue" } with TypeCode 37 | case object DoubleCode extends { val code : Byte = 1; val typeName = "BSONDouble" } with TypeCode 38 | case object StringCode extends { val code : Byte = 2; val typeName = "BSONString" } with TypeCode 39 | case object ObjectCode extends { val code : Byte = 3; val typeName = "BSONObject" } with TypeCode 40 | case object ArrayCode extends { val code : Byte = 4; val typeName = "BSONArray" } with TypeCode 41 | case object BinaryCode extends { val code : Byte = 5; val typeName = "BSONBinary" } with TypeCode 42 | /** @deprecated */ 43 | case object UndefinedCode extends { val code : Byte = 6; val typeName = "BSONUndefinede" } with TypeCode 44 | case object ObjectIDCode extends { val code : Byte = 7; val typeName = "BSONObjectID" } with TypeCode 45 | case object BooleanCode extends { val code : Byte = 8; val typeName = "BSONBoolean" } with TypeCode 46 | case object DateCode extends { val code : Byte = 9; val typeName = "BSONDate" } with TypeCode 47 | case object NullCode extends { val code : Byte = 10; val typeName = "BSONNull" } with TypeCode 48 | case object RegexCode extends { val code : Byte = 11; val typeName = "BSONRegex" } with TypeCode 49 | /** @deprecated */ 50 | case object DBPointerCode extends { val code : Byte = 12; val typeName = "BSONDBPointer" } with TypeCode 51 | case object JavaScriptCode extends { val code : Byte = 13; val typeName = "BSONJavaScript" } with TypeCode 52 | /** @deprecated */ 53 | case object SymbolCode extends { val code : Byte = 14; val typeName = "BSONSymbol" } with TypeCode 54 | case object ScopedJSCode extends { val code : Byte = 15; val typeName = "BSONScopedJSCode" } with TypeCode 55 | case object IntegerCode extends { val code : Byte = 16; val typeName = "BSONInteger" } with TypeCode 56 | case object TimestampCode extends { val code : Byte = 17; val typeName = "BSONTimestamp" } with TypeCode 57 | case object LongCode extends { val code : Byte = 18; val typeName = "BSONLong" } with TypeCode 58 | case object MinKey extends { val code : Byte = 255.toByte; val typeName = "MinKey" } with TypeCode 59 | case object MaxKey extends { val code : Byte = 127; val typeName = "MaxKey" } with TypeCode 60 | 61 | object TypeCode { 62 | def apply(code : Byte) : TypeCode = { 63 | (code : @switch) match { 64 | case 1 ⇒ DoubleCode 65 | case 2 ⇒ StringCode 66 | case 3 ⇒ ObjectCode 67 | case 4 ⇒ ArrayCode 68 | case 5 ⇒ BinaryCode 69 | case 6 ⇒ UndefinedCode 70 | case 7 ⇒ ObjectIDCode 71 | case 8 ⇒ BooleanCode 72 | case 9 ⇒ DateCode 73 | case 10 ⇒ NullCode 74 | case 11 ⇒ RegexCode 75 | case 12 ⇒ DBPointerCode 76 | case 13 ⇒ JavaScriptCode 77 | case 14 ⇒ SymbolCode 78 | case 15 ⇒ ScopedJSCode 79 | case 16 ⇒ IntegerCode 80 | case 17 ⇒ TimestampCode 81 | case 18 ⇒ LongCode 82 | case -1 ⇒ MinKey 83 | case 127 ⇒ MaxKey 84 | case _ ⇒ 85 | throw new NoSuchElementException(s"BSON TypeCode($code)") 86 | } 87 | } 88 | def apply(v : BSONValue) : TypeCode = v.code 89 | } 90 | -------------------------------------------------------------------------------- /bson/src/main/scala/rxmongo/bson/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo 24 | 25 | import java.nio.ByteOrder 26 | import java.nio.charset.Charset 27 | 28 | import akka.util.{ ByteIterator, ByteStringBuilder } 29 | import scala.language.implicitConversions 30 | 31 | /** The bson package object. 32 | * 33 | * This just contains values that are used throughout the bson package. 34 | */ 35 | package object bson { 36 | 37 | case class RxMongoError(message : String, cause : Option[Throwable] = None) extends Exception { 38 | override def getMessage = message 39 | override def getCause = cause.orNull 40 | } 41 | 42 | // Everything in Mongo is Little Endian 43 | implicit val byteOrder = ByteOrder.LITTLE_ENDIAN 44 | 45 | /** Maximum Document Size. 46 | * 47 | * Use Mongo's maximum doc size to ensure that we're having sane reading of length fields and we don't OOM 48 | * by trying to allocate all memory. 49 | * @see [[http://docs.mongodb.org/manual/reference/command/isMaster/#dbcmd.isMaster]] 50 | * 51 | */ 52 | final val maxDocSize = 16 * 1024 * 1024 53 | 54 | /** Implicit Extensions to ByteStringBuilder 55 | * 56 | * This makes using a ByteStringBuilder with BSON easier by providing functions that build BSON data structures. 57 | * 58 | * @param bldr The builder we are extending 59 | */ 60 | implicit class ByteStringBuilderPimpsClass(val bldr : ByteStringBuilder) extends ByteStringBuilderPimps 61 | 62 | /** Implicit Extensions To ByteIterator 63 | * 64 | * This makes it easier to use a ByteIterator with MongoDB. The extensions implement recognition of various 65 | * data types that BSON encoding requires. 66 | * @param itr The wrapped iterator 67 | */ 68 | implicit class ByteIteratorPimpsClass(val itr : ByteIterator) extends ByteIteratorPimps 69 | 70 | } 71 | -------------------------------------------------------------------------------- /bson/src/test/scala/rxmongo/bson/BinarySubtypeSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.bson 24 | 25 | import BinarySubtype._ 26 | import org.specs2.mutable.Specification 27 | 28 | /** Test Cases For BinarySubtype 29 | */ 30 | class BinarySubtypeSpec extends Specification { 31 | 32 | "BinarySubtype" should { 33 | "match encoded bytes correctly" in { 34 | BinarySubtype(0.toByte) must beEqualTo(GenericBinary) 35 | BinarySubtype(1.toByte) must beEqualTo(FunctionBinary) 36 | BinarySubtype(2.toByte) must beEqualTo(DeprecatedGenericBinary) 37 | BinarySubtype(3.toByte) must beEqualTo(DeprecatedUUIDBinary) 38 | BinarySubtype(4.toByte) must beEqualTo(UUIDBinary) 39 | BinarySubtype(5.toByte) must beEqualTo(MD5SumBinary) 40 | BinarySubtype(-128.toByte) must beEqualTo(UserDefinedBinary) 41 | } 42 | "not match unknown byte encodings" in { 43 | BinarySubtype(17.toByte) must throwA[NoSuchElementException] 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /bson/src/test/scala/rxmongo/bson/ByteStringTestUtils.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.bson 24 | 25 | import java.lang.management.ManagementFactory 26 | import java.nio.ByteOrder 27 | import java.nio.charset.StandardCharsets 28 | 29 | import akka.util.{ ByteString, ByteStringBuilder } 30 | import com.reactific.hsp.Profiler 31 | import rxmongo.bson.BinarySubtype.UserDefinedBinary 32 | 33 | trait ByteStringTestUtils { 34 | 35 | implicit val byteOrder = ByteOrder.LITTLE_ENDIAN 36 | 37 | def cstring(bldr : ByteStringBuilder, str : String) : ByteStringBuilder = { 38 | bldr.putBytes(str.getBytes(StandardCharsets.UTF_8)) // c string 39 | bldr.putByte(0) // termination of c string 40 | bldr 41 | } 42 | 43 | def string(bldr : ByteStringBuilder, str : String) : ByteStringBuilder = { 44 | val bytes = str.getBytes(StandardCharsets.UTF_8) 45 | bldr.putInt(bytes.length + 1) 46 | bldr.putBytes(bytes) 47 | bldr.putByte(0) 48 | } 49 | 50 | def field(bldr : ByteStringBuilder, code : Byte, fieldName : String) : ByteStringBuilder = { 51 | bldr.putByte(code) // code 52 | cstring(bldr, fieldName) 53 | } 54 | 55 | /** Preamble for a new object buffer and its first field 56 | * 57 | * @param len The length of the whole object 58 | * @param code The type code byte of the first field 59 | * @param fieldName the name of the first field 60 | * @return ByteStringBuilder with first field preamble constructed 61 | */ 62 | def preamble(len : Int, code : Byte, fieldName : String) : ByteStringBuilder = { 63 | val bldr : ByteStringBuilder = ByteString.newBuilder 64 | bldr.putInt(len) 65 | field(bldr, code, fieldName) 66 | } 67 | 68 | val data = Array[Byte](0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) 69 | val anArray = Seq(42.0D, 84.0D) 70 | val anArraySeq = Seq(BSONDouble(42.0D), BSONDouble(84.0D)) 71 | val anArrayBSON : BSONArray = BSONArray(anArray) 72 | val anObject = BSONObject("one" -> BSONDouble(84.0D), "two" -> BSONString("eighty-four")) 73 | val aTime = System.currentTimeMillis() 74 | 75 | def makeAnObject(profiler : Profiler = Profiler) : BSONBuilder = profiler.profile("makeAnObject") { 76 | val b = BSONBuilder() 77 | b.double("double", 42.0D) 78 | .string("string", "fourty-two") 79 | .obj("obj", anObject) 80 | .array("array", anArrayBSON) 81 | .binary("binary", data, UserDefinedBinary) 82 | .undefined("undefined") 83 | .objectID("objectid", data) 84 | .boolean("boolean", value = true) 85 | .date("date", aTime) 86 | .nil("null") 87 | .regex("regex", "pattern", "ilmsux") 88 | .dbPointer("dbpointer", "referent", data) 89 | .jsCode("jscode", "function(x) { return x + 1; };") 90 | .symbol("symbol", "symbol") 91 | .scopedJsCode("scopedjscode", "function(x)", anObject) 92 | .integer("integer", 42) 93 | .timestamp("timestamp", 42L) 94 | .long("long", 42L) 95 | b 96 | } 97 | 98 | def makeObject() : BSONObject = makeObject(Profiler) 99 | 100 | def makeObject(profiler : Profiler) : BSONObject = { 101 | profiler.profile("makeObject") { makeAnObject(profiler).toBSONObject } 102 | } 103 | 104 | def makeObject(width : Int, depth : Int, profiler : Profiler = Profiler) : BSONObject = { 105 | val bldr = makeAnObject(profiler) 106 | if (depth > 0) { 107 | val kids = for (i ← 1 to width) yield { 108 | makeObject(width, depth - 1, profiler) 109 | } 110 | profiler.profile("append kids") { bldr.array("kids", kids) } 111 | } 112 | profiler.profile("toBSONObject") { bldr.toBSONObject } 113 | } 114 | 115 | val suitableForTimingTests : Boolean = { 116 | if (System.getenv("TRAVIS") != null) 117 | false 118 | else { 119 | val os = ManagementFactory.getOperatingSystemMXBean 120 | val processors = os.getAvailableProcessors 121 | val avg = os.getSystemLoadAverage 122 | avg < processors / 2 123 | } 124 | } 125 | 126 | def timedTest(maxNanoSeconds : Double, name : String)(func : (Profiler) ⇒ Unit) : Profiler = { 127 | val p = new Profiler 128 | val r = p.profile(name) { func(p) } 129 | val (count, time) = Profiler.get_one_item(name) 130 | p.print_profile_summary(System.out) 131 | println() 132 | val limit = if (suitableForTimingTests) maxNanoSeconds else maxNanoSeconds*10 133 | if (time > limit) { 134 | throw new Exception(s"Test '$name' took ${time}ns which exceeded limit of ${maxNanoSeconds}ns") 135 | } 136 | p 137 | } 138 | 139 | def timedAndCountedTests(testName: String, expected: Map[String,(Long,Double)])(func : (Profiler) => Unit) : Profiler = { 140 | val p = new Profiler 141 | val r = p.profile(testName) { func(p) } 142 | p.print_profile_summary(System.out) 143 | println() 144 | for ( (name, (maxCount,maxNanoSeconds)) <- expected) { 145 | val (count, time) = Profiler.get_one_item(name) 146 | val limit = if (suitableForTimingTests) maxNanoSeconds else maxNanoSeconds*10 147 | if (time > limit) { 148 | throw new Exception(s"Test '$name' took ${time}ns which exceeded limit of ${limit}ns") 149 | } 150 | } 151 | p 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /bson/src/test/scala/rxmongo/bson/CodecSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.bson 24 | 25 | import java.time.Instant 26 | import java.util 27 | import java.util.Date 28 | import java.util.regex.Pattern 29 | 30 | import akka.util.ByteString 31 | import org.specs2.mutable.Specification 32 | 33 | /** Test Cases For Codec */ 34 | class CodecSpec extends Specification with ByteStringTestUtils { 35 | 36 | "Codec" should { 37 | "reflect Double" in { 38 | import Codec.DoubleCodec._ 39 | val bldr = BSONBuilder() 40 | read(write(42.0,bldr).bldr.result().iterator) must beEqualTo(42.0) 41 | } 42 | "reflect String" in { 43 | import Codec.StringCodec._ 44 | val bldr = BSONBuilder() 45 | read(write("foo",bldr).bldr.result().iterator) must beEqualTo("foo") 46 | } 47 | "reflect BSONObject" in { 48 | import Codec.BSONObjectCodec._ 49 | val bldr = BSONBuilder() 50 | val obj = makeObject() 51 | read(write(obj,bldr).bldr.result().iterator) must beEqualTo(obj) 52 | } 53 | "reflect BSONArray" in { 54 | import Codec.BSONArrayCodec._ 55 | val bldr = BSONBuilder() 56 | val array = BSONArray.fromAny(Seq(1,2L,"3",4.0,false)) 57 | val result = read(write(array,bldr).bldr.result().iterator) 58 | result.equals(array) must beTrue 59 | } 60 | "reflect Binary" in { 61 | import Codec.BinaryCodec._ 62 | val bldr = BSONBuilder() 63 | val binary = (BinarySubtype.GenericBinary, Array[Byte](1,2)) 64 | val result = read(write(binary,bldr).bldr.result().iterator) 65 | result._1 must beEqualTo(binary._1) 66 | util.Arrays.equals(result._2, binary._2) 67 | } 68 | "reflect BSONBinary" in { 69 | import Codec.BSONBinaryCodec._ 70 | val bldr = BSONBuilder() 71 | val binary = BSONBinary(Array[Byte](1,2), BinarySubtype.GenericBinary) 72 | read(write(binary,bldr).bldr.result().iterator) must beEqualTo(binary) 73 | } 74 | "reflect ObjectID" in { 75 | import Codec.BSONObjectIDCodec._ 76 | val bldr = BSONBuilder() 77 | val objid = BSONObjectID(Array[Byte](1,2,3,4,5,6,7,8,9,10,11,12)) 78 | read(write(objid,bldr).bldr.result().iterator) must beEqualTo(objid) 79 | } 80 | "reflect Boolean" in { 81 | import Codec.BooleanCodec._ 82 | val bldr = BSONBuilder() 83 | read(write(false,bldr).bldr.result().iterator) must beEqualTo(false) 84 | } 85 | "reflect Date" in { 86 | import Codec.DateCodec._ 87 | val bldr = BSONBuilder() 88 | val now = new Date() 89 | read(write(now,bldr).bldr.result().iterator) must beEqualTo(now) 90 | } 91 | "reflect Regex" in { 92 | import Codec.RegexCodec._ 93 | val bldr = BSONBuilder() 94 | val pattern : Pattern = ".*".r.pattern 95 | val result = read(write(pattern,bldr).bldr.result().iterator) 96 | result.pattern.equals(pattern.pattern) must beTrue 97 | } 98 | "reflect DBPointer" in { 99 | import Codec.DBPointerCodec._ 100 | val bldr = BSONBuilder() 101 | val dbp = BSONDBPointer("foo",Array[Byte](0,1,2,3,4,5,6,7,8,9,10,11)) 102 | val result = read(write(dbp,bldr).bldr.result().iterator) 103 | result.equals(dbp) must beTrue 104 | } 105 | "reflect JavaScript" in { 106 | import Codec.JavaScriptCodec._ 107 | val bldr = BSONBuilder() 108 | read(write("foo",bldr).bldr.result().iterator) must beEqualTo("foo") 109 | } 110 | "reflect Symbol" in { 111 | import Codec.SymbolCodec._ 112 | val bldr = BSONBuilder() 113 | read(write("foo",bldr).bldr.result().iterator) must beEqualTo("foo") 114 | } 115 | "reflect ScopedJavaScript" in { 116 | import Codec.ScopedJavaScriptCodec._ 117 | val bldr = BSONBuilder() 118 | val sjs = ("foo", makeObject()) 119 | val result = read(write(sjs,bldr).bldr.result().iterator) 120 | result.equals(sjs) must beTrue 121 | } 122 | "reflect Int" in { 123 | import Codec.IntCodec._ 124 | val bldr = BSONBuilder() 125 | read(write(42,bldr).bldr.result().iterator) must beEqualTo(42) 126 | } 127 | "reflect Instant" in { 128 | import Codec.InstantCodec._ 129 | val bldr = BSONBuilder() 130 | val now = Instant.now() 131 | read(write(now,bldr).bldr.result().iterator) must beEqualTo(now) 132 | } 133 | "reflect Long" in { 134 | import Codec.LongCodec._ 135 | val bldr = BSONBuilder() 136 | read(write(42L,bldr).bldr.result().iterator) must beEqualTo(42L) 137 | } 138 | "reflect ByteString" in { 139 | import Codec.ByteStringCodec._ 140 | val bldr = BSONBuilder() 141 | val bs = ByteString.newBuilder.putByte(1).putByte(2).putByte(3).result() 142 | val result = read(write(bs,bldr).bldr.result().iterator) 143 | result.equals(bs) must beTrue 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /client/rebel.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /client/src/main/scala/rxmongo/client/Client.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.client 24 | 25 | import java.io.Closeable 26 | 27 | import akka.actor.ActorRef 28 | import akka.util.Timeout 29 | import com.typesafe.config.Config 30 | 31 | import rxmongo.driver.{ Driver, MongoURI } 32 | import rxmongo.messages.{ WriteConcern, Query } 33 | 34 | import scala.concurrent.Await 35 | import scala.util.{ Failure, Success } 36 | 37 | /** Primary Application Interface To RxMongo 38 | * 39 | * Each Client instance provides a separate interface to MongoDB. It can manage connections to multiple 40 | * replica sets and coordinate the activity between them. Your application must instantiate an Client in order 41 | * to use RxMongo as all other objects provided by RxMongo are accessed through this one. You may instantiate multiple 42 | * Client instances, but each will maintain its own set of connections and view of the databases. This may be 43 | * useful in testing but is unlikely to be useful in an application unless you wish to keep replica set traffic 44 | * separate. 45 | * 46 | * The Client is a wrapper around the [[rxmongo.driver.Driver]] object that allow you to avoid the low level 47 | * interface of the Driver. Instead of dealing directly with connections to a MongoDB server, you deal with 48 | * abstractions like [[rxmongo.client.Database]], [[rxmongo.client.Collection]], [[Query]] and 49 | * [[rxmongo.client.Cursor]]. 50 | */ 51 | case class Client(uri : MongoURI, config : Option[Config], name : String)(override implicit val timeout : Timeout, override implicit val writeConcern : WriteConcern) 52 | extends RxMongoComponent(Driver(config, name)) with Closeable { 53 | 54 | private[client] val connection : ActorRef = { 55 | Await.result(driver.connect(uri, None)(timeout), timeout.duration) 56 | } 57 | 58 | def close() = { 59 | driver.close() 60 | } 61 | 62 | /** Get a Database Object. 63 | * 64 | * Note that instantiation of this does not imply creation of the database. Databases are created on demand by 65 | * MongoDB. So, you can create numerous instances of Database without interacting with MongoDB at all. Once you do 66 | * something with the returned Database instance, however, you will be interacting with MongoDB. 67 | * @param name The name of the database 68 | * @return A lightweight interface to a MongoDB Database. 69 | */ 70 | def database(name : String)(implicit to : Timeout = timeout, wc : WriteConcern = writeConcern) : Database = { 71 | Database(name, this)(to, wc) 72 | } 73 | 74 | /** Get a Collection Object. 75 | * 76 | * Note that instantiona of this does not imply creation of a collection. Collections are created on demand by 77 | * MongoDB. So, you can create numerous instances of Collection through this method without any interactions with 78 | * MongoDB at all. Once you do something with the returned Collection instance, however, you will be interacting 79 | * with MongoDB. 80 | * @param dbName The name of the database 81 | * @param collName The name of the collection 82 | * @return 83 | */ 84 | def collection(dbName : String, collName : String)(implicit to : Timeout = timeout, wc : WriteConcern = writeConcern) : Collection = { 85 | Database(dbName, this)(to, wc).collection(collName)(to, wc) 86 | } 87 | 88 | } 89 | 90 | object Client { 91 | 92 | /** Client constructor. 93 | * Create a Client from a URI string, configuration options, and a name. The string uri is parsed into a MongoURI 94 | * and then the arguments are passed to the Client class's constructor. If the uri cannot be parsed then an 95 | * exception will be thrown 96 | * @param uri The uri specifying what to connect to and how 97 | * @param config rxmongo configuration options 98 | * @param name The name for this client 99 | * @return An instance of [[rxmongo.client.Client]] for interacting with MongoDB 100 | */ 101 | def apply(uri : String, config : Option[Config] = None, name : String = "RxMongo")(implicit to : Timeout = Driver.defaultTimeout, wc : WriteConcern = WriteConcern.default) = { 102 | MongoURI(uri) match { 103 | case Success(u) ⇒ new Client(u, config, name)(to, wc) 104 | case Failure(x) ⇒ throw x 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /client/src/main/scala/rxmongo/client/RxMongoComponent.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.client 24 | 25 | import akka.event.{ Logging, LoggingAdapter } 26 | import akka.util.Timeout 27 | import rxmongo.driver.Driver 28 | import rxmongo.messages.WriteConcern 29 | 30 | import scala.concurrent.ExecutionContext 31 | 32 | abstract class RxMongoComponent(private[rxmongo] val driver : Driver) { 33 | val log : LoggingAdapter = Logging.getLogger(driver.system, this.getClass) 34 | implicit val executionContext : ExecutionContext = driver.system.dispatcher 35 | implicit val timeout : Timeout = Driver.defaultTimeout 36 | implicit val writeConcern : WriteConcern = WriteConcern.default 37 | } 38 | -------------------------------------------------------------------------------- /client/src/test/scala/rxmongo/client/ClientSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.client 24 | 25 | import org.specs2.mutable.Specification 26 | 27 | /** Test Suite For RxMongoClient */ 28 | class ClientSpec extends Specification { 29 | 30 | "Client" should { 31 | "allow connection" in { 32 | Client("mongodb://localhost/mydb") 33 | success 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /client/src/test/scala/rxmongo/client/CollStatsSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.client 24 | 25 | import rxmongo.bson.BSONObject 26 | import rxmongo.messages.replies.WriteResult 27 | 28 | import scala.concurrent.Await 29 | import scala.concurrent.duration.FiniteDuration 30 | 31 | /** Test Suite For Collection Statistics */ 32 | class CollStatsSpec extends RxMongoTest("rxmongo", "collstats") { 33 | 34 | val objs = Seq( 35 | BSONObject("a" → 21.0, "b" → 21L, "c" → 21), 36 | BSONObject("a" → 28.0, "b" → 28L, "c" → 28), 37 | BSONObject("a" → 35.0, "b" → 35L, "c" → 35), 38 | BSONObject("a" → 42.0, "b" → 42L, "c" → 42), 39 | BSONObject("a" → 49.0, "b" → 49L, "c" → 49), 40 | BSONObject("a" → 56.0, "b" → 56L, "c" → 56) 41 | ) 42 | 43 | "Collection" should { 44 | "drop collection before populating" in mongoTest { () ⇒ 45 | val result = Await.result(collection.drop(), FiniteDuration(1, "seconds")) 46 | success 47 | } 48 | 49 | "populate collection before testing" in mongoTest { () ⇒ 50 | val future = collection.insert(objs) 51 | val result = Await.result(future, FiniteDuration(1, "seconds")) 52 | result.ok must beEqualTo(1) 53 | result.n must beEqualTo(6) 54 | } 55 | 56 | "permit collection stats retrieval" in mongoTest { () ⇒ 57 | val stats = collection.stats 58 | stats.count must beEqualTo(6) 59 | collection.count must beEqualTo(stats.count) 60 | } 61 | 62 | "implmenet avgObjSize" in mongoTest { () ⇒ 63 | val size = collection.avgObjSize 64 | size must beGreaterThan(26) 65 | } 66 | 67 | "implement capped" in mongoTest { () ⇒ 68 | val capped = collection.capped 69 | capped must beFalse 70 | } 71 | 72 | "implement storageSize" in mongoTest { () ⇒ 73 | val size = collection.storageSize 74 | size must beGreaterThan(26 * 6L) 75 | } 76 | 77 | "implement indexSize" in mongoTest { () ⇒ 78 | val size = collection.indexSize 79 | size must beEqualTo(7168L) 80 | } 81 | 82 | "implement numIndexes" in mongoTest { () ⇒ 83 | val size = collection.numIndexes 84 | size must beEqualTo(1) 85 | } 86 | 87 | "implement numExtents" in mongoTest { () ⇒ 88 | val size = collection.numExtents 89 | size must beEqualTo(1) 90 | } 91 | 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /client/src/test/scala/rxmongo/client/CollectionSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.client 24 | 25 | import rxmongo.bson._ 26 | import rxmongo.messages._ 27 | import rxmongo.messages.replies.WriteResult 28 | 29 | import scala.concurrent.Await 30 | import scala.concurrent.duration.FiniteDuration 31 | 32 | class CollectionSpec extends RxMongoTest("rxmongo", "collection") { 33 | 34 | val obj1 = BSONObject("key1" → 42.0, "key2" → 42L, "key3" → 42) 35 | 36 | val atMost = FiniteDuration(2, "seconds") 37 | 38 | "Collection" should { 39 | 40 | "insert" in mongoTest { () ⇒ 41 | val future = collection.insertOne(obj1) 42 | val result = Await.result(future, atMost) 43 | result.ok must beEqualTo(1) 44 | result.n must beEqualTo(1) 45 | } 46 | 47 | "create index" in mongoTest { () ⇒ 48 | val index = Index("key1", ascending = true) 49 | val options = IndexOptions() 50 | val result = Await.result(collection.createIndex(index, options), atMost) 51 | result.asDouble("ok") must beEqualTo(1.0) 52 | } 53 | 54 | "find" in mongoTest { () ⇒ 55 | val result = Await.result(collection.findOne(Query("key1" $eq 42.0)), atMost) 56 | result.isDefined must beTrue 57 | val obj : BSONDocument = result.get 58 | obj.contains("key1") must beTrue 59 | obj.contains("key2") must beTrue 60 | obj.contains("key3") must beTrue 61 | obj.asDouble("key1") must beEqualTo(42.0) 62 | obj.asLong("key2") must beEqualTo(42L) 63 | obj.asInt("key3") must beEqualTo(42) 64 | } 65 | 66 | "update" in mongoTest { () ⇒ 67 | val upd = Update("key1" → 42.0, $set("key2", 84L), upsert = false, multi = false, isolated = false) 68 | val result = Await.result(collection.updateOne(upd), atMost) 69 | result.ok must beEqualTo(1) 70 | result.n must beEqualTo(1) 71 | } 72 | 73 | "delete" in mongoTest { () ⇒ 74 | val del = Delete("key1" → 42.0, limit = 1) 75 | val future = collection.deleteOne(del) 76 | val result = Await.result(future, atMost) 77 | result.ok must beEqualTo(1) 78 | result.n must beEqualTo(1) 79 | } 80 | 81 | "rename" in mongoTest { () ⇒ 82 | val result = Await.result(collection.renameCollection("collection2", dropTarget = true), atMost) 83 | result.isEmpty must beFalse 84 | collection = result.get 85 | collection.name must beEqualTo("collection2") 86 | } 87 | 88 | "drop" in mongoTest { () ⇒ 89 | val result = Await.result(collection.drop(), atMost) 90 | result must beTrue 91 | } 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /client/src/test/scala/rxmongo/client/CursorSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.client 24 | 25 | import rxmongo.bson._ 26 | import rxmongo.messages._ 27 | 28 | import scala.concurrent.Await 29 | import scala.concurrent.duration._ 30 | import scala.concurrent.ExecutionContext.Implicits.global 31 | 32 | class CursorSpec extends RxMongoTest("rxmongo", "cursor") { 33 | 34 | val objs = Seq( 35 | BSONObject("a" → 21.0, "b" → 21L, "c" → 21), 36 | BSONObject("a" → 28.0, "b" → 28L, "c" → 28), 37 | BSONObject("a" → 35.0, "b" → 35L, "c" → 35), 38 | BSONObject("a" → 42.0, "b" → 42L, "c" → 42), 39 | BSONObject("a" → 49.0, "b" → 49L, "c" → 49), 40 | BSONObject("a" → 56.0, "b" → 56L, "c" → 56) 41 | ) 42 | 43 | "Cursor" should { 44 | "drop collection before populating" in mongoTest { () ⇒ 45 | val result = Await.result(collection.drop(), FiniteDuration(1, "seconds")) 46 | success 47 | } 48 | 49 | "insert 6 records to test" in mongoTest { () ⇒ 50 | val future = collection.insert(objs) 51 | val result = Await.result(future, FiniteDuration(1, "seconds")) 52 | result.ok must beEqualTo(1) 53 | result.n must beEqualTo(6) 54 | } 55 | 56 | "iterate in 3 batches of 2" in mongoTest { () ⇒ 57 | val future = collection.findRaw(Query("a" $gte 21.0), None, QueryOptions(numberToReturn = 6)) map { msg : ReplyMessage ⇒ 58 | val itr = ReplyIterator(collection, msg, 2) 59 | var count = 0 60 | while (itr.hasNext) { 61 | val seq = itr.next() 62 | count += seq.length 63 | } 64 | count 65 | } 66 | val result = Await.result(future, FiniteDuration(1, "seconds")) 67 | result must beEqualTo(6) 68 | } 69 | 70 | "find all 6 documents" in mongoTest { () ⇒ 71 | val result = Await.result(collection.find(Query("a" $gte 21.0), 72 | options = QueryOptions(numberToReturn = 2)), FiniteDuration(1, "seconds")) 73 | result.hasNext must beEqualTo(true) 74 | val contents = Await.result(result.toFlatSeq, FiniteDuration(1, "seconds")) 75 | contents.length must beEqualTo(6) 76 | contents.exists(doc ⇒ BSONObject(doc).matches(BSONObject("a" → 21.0, "b" → 21L, "c" → 21))) must beTrue 77 | contents.exists(doc ⇒ BSONObject(doc).matches(BSONObject("a" → 28.0, "b" → 28L, "c" → 28))) must beTrue 78 | contents.exists(doc ⇒ BSONObject(doc).matches(BSONObject("a" → 35.0, "b" → 35L, "c" → 35))) must beTrue 79 | contents.exists(doc ⇒ BSONObject(doc).matches(BSONObject("a" → 42.0, "b" → 42L, "c" → 42))) must beTrue 80 | contents.exists(doc ⇒ BSONObject(doc).matches(BSONObject("a" → 49.0, "b" → 49L, "c" → 49))) must beTrue 81 | contents.exists(doc ⇒ BSONObject(doc).matches(BSONObject("a" → 56.0, "b" → 56L, "c" → 56))) must beTrue 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /client/src/test/scala/rxmongo/client/DatabaseSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.client 24 | 25 | import org.specs2.mutable.Specification 26 | 27 | class DatabaseSpec extends Specification { 28 | 29 | } 30 | -------------------------------------------------------------------------------- /client/src/test/scala/rxmongo/client/RxMongoTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.client 24 | 25 | import java.net.{ Socket, InetSocketAddress, InetAddress } 26 | import javax.net.SocketFactory 27 | 28 | import org.specs2.execute.Result 29 | import org.specs2.mutable.Specification 30 | 31 | abstract class RxMongoTest(dbName : String, collName : String) extends Specification { 32 | 33 | val client = Client("mongodb://localhost:27017/" + dbName) 34 | 35 | var database = client.database(dbName) 36 | 37 | var collection = database.collection(collName) 38 | 39 | lazy val haveLocalMongo : Boolean = { 40 | val addr = InetAddress.getLoopbackAddress 41 | val port = 27017 42 | val socketAddress = new InetSocketAddress(addr, port) 43 | try { 44 | val socket : Socket = SocketFactory.getDefault.createSocket 45 | socket.connect(socketAddress, 1000) 46 | socket.close() 47 | true 48 | } catch { 49 | case x : Throwable ⇒ false 50 | } 51 | } 52 | 53 | def mongoTest(f : () ⇒ Result) : Result = { 54 | if (haveLocalMongo) { 55 | f() 56 | } else { 57 | skipped(": no local mongo") 58 | } 59 | } 60 | 61 | sequential 62 | 63 | } 64 | -------------------------------------------------------------------------------- /driver/src/main/resources/akka.conf: -------------------------------------------------------------------------------- 1 | akka { 2 | # Control log level to the console on standard out 3 | stdout-loglevel = "ERROR" 4 | #stdout-loglevel = "OFF" 5 | 6 | # Control log level to log files 7 | loglevel = "DEBUG" 8 | 9 | # Specify which logger to use. Defer to Slf4J 10 | loggers = ["akka.event.slf4j.Slf4jLogger"] 11 | 12 | # Use the backend (e.g. logback) definition of events to filter them 13 | logging-filter = "akka.event.slf4j.Slf4jLoggingFilter" 14 | 15 | # Log the complete configuration at INFO level when the actor system is started. 16 | # This is useful when you are uncertain of what configuration is used. 17 | log-config-on-start = off 18 | 19 | # Controls how many dead letter messages are logged 20 | log-dead-letters = 10 21 | 22 | # Controls whether dead letters are logged during shutdown of actor system 23 | log-dead-letters-during-shutdown = off 24 | 25 | actor { 26 | debug { 27 | # enable DEBUG logging of all AutoReceiveMessages (Kill, PoisonPill etc.) 28 | autoreceive = off 29 | # enable DEBUG logging of actor lifecycle changes 30 | lifecycle = off 31 | # enable DEBUG logging of all LoggingFSMs for events, transitions and timers 32 | fsm = off 33 | # enable DEBUG logging of subscription changes on the eventStream 34 | event-stream = off 35 | # enable WARN logging of misconfigured routers 36 | router-misconfiguration = off 37 | } 38 | 39 | # What kind of ExecutionService to use 40 | executor = "fork-join-executor" 41 | 42 | fork-join-executor { 43 | # Min number of threads to cap factor-based parallelism number to 44 | parallelism-min = 1 45 | 46 | # The parallelism factor is used to determine thread pool size using the 47 | # following formula: ceil(available processors * factor). Resulting size 48 | # is then bounded by the parallelism-min and parallelism-max values. 49 | parallelism-factor = 1.0 50 | 51 | # Max number of threads to cap factor-based parallelism number to 52 | parallelism-max = 64 53 | } 54 | # Throughput defines the maximum number of messages to be 55 | # processed per actor before the thread jumps to the next actor. 56 | # Set to 1 for as fair as possible. 57 | throughput = 100 58 | } 59 | 60 | io { 61 | tcp { 62 | # nr-of-selectors = 10 63 | # batch-accept-limit 64 | # direct-buffer-size 65 | # direct-buffer-pool-limit 66 | # register-timeout = 5000 67 | # max-received-message-size 68 | # received-message-size-limit 69 | # management-dispatcher 70 | # file-io-dispatcher 71 | # file-io-transferTo-limit 72 | # finish-connect-retries 73 | # windows-connection-abort-workaround-enabled 74 | 75 | # max-channels 76 | # selector-association-retries 77 | # selector-dispatcher 78 | # worker-dispatcher 79 | trace-logging = off 80 | } 81 | } 82 | 83 | # Used to set the behavior of the scheduler. 84 | # Changing the default values may change the system behavior drastically so make 85 | # sure you know what you're doing! See the Scheduler section of the Akka 86 | # Documentation for more details. 87 | scheduler { 88 | # The LightArrayRevolverScheduler is used as the default scheduler in the 89 | # system. It does not execute the scheduled tasks on exact time, but on every 90 | # tick, it will run everything that is (over)due. You can increase or decrease 91 | # the accuracy of the execution timing by specifying smaller or larger tick 92 | # duration. If you are scheduling a lot of tasks you should consider increasing 93 | # the ticks per wheel. 94 | # Note that it might take up to 1 tick to stop the Timer, so setting the 95 | # tick-duration to a high value will make shutting down the actor system 96 | # take longer. 97 | tick-duration = 1ms 98 | 99 | # The timer uses a circular wheel of buckets to store the timer tasks. 100 | # This should be set such that the majority of scheduled timeouts (for high 101 | # scheduling frequency) will be shorter than one rotation of the wheel 102 | # (ticks-per-wheel * ticks-duration) 103 | # THIS MUST BE A POWER OF TWO! 104 | ticks-per-wheel = 1024 105 | 106 | # This setting selects the timer implementation which shall be loaded at 107 | # system start-up. 108 | # The class given here must implement the akka.actor.Scheduler interface 109 | # and offer a public constructor which takes three arguments: 110 | # 1) com.typesafe.config.Config 111 | # 2) akka.event.LoggingAdapter 112 | # 3) java.util.concurrent.ThreadFactory 113 | implementation = akka.actor.LightArrayRevolverScheduler 114 | 115 | # When shutting down the scheduler, there will typically be a thread which 116 | # needs to be stopped, and this timeout determines how long to wait for 117 | # that to happen. In case of timeout the shutdown of the actor system will 118 | # proceed without running possibly still enqueued tasks. 119 | shutdown-timeout = 5s 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /driver/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 21 | 22 | ${logs_dir:-logs}/rxmongo.log 23 | 24 | ${logs_dir:-logs}/rxmongo.%i.log.zip 25 | 1 26 | 20 27 | 28 | 29 | 50MB 30 | 31 | 32 | %d %-7relative %-5level [%thread:%logger{30}] - %msg%n%xException 33 | false 34 | true 35 | 36 | 37 | 38 | 39 | %date %-5level %logger{30}: %message%n%xException{5} 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /driver/src/main/resources/rxmongo.conf: -------------------------------------------------------------------------------- 1 | rxmongo { 2 | include "akka.conf" 3 | } 4 | -------------------------------------------------------------------------------- /driver/src/main/scala/rxmongo/driver/Channel.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.driver 24 | 25 | import java.net.InetSocketAddress 26 | 27 | import akka.actor._ 28 | import akka.event.LoggingReceive 29 | import akka.util.ByteString 30 | import rxmongo.messages.{ ReplyMessage, RequestMessage } 31 | 32 | import scala.collection.mutable 33 | 34 | object Channel { 35 | def props(remote : InetSocketAddress, options : ConnectionOptions, replies : ActorRef, 36 | isPrimary : Boolean, useStreams : Boolean = false) = { 37 | if (useStreams) { 38 | Props(classOf[StreamTcpChannel], remote, options, replies, isPrimary) 39 | } else { 40 | Props(classOf[AkkaIOChannel], remote, options, replies, isPrimary) 41 | } 42 | } 43 | 44 | sealed trait ChannelRequest 45 | case class CloseWithAck(ack : Any) extends ChannelRequest 46 | case class SendMessage(msg : RequestMessage, replyTo : ActorRef) extends ChannelRequest 47 | case object Close extends ChannelRequest 48 | case object GetStatistics extends ChannelRequest 49 | 50 | sealed trait ChannelResponse 51 | case class ConnectionFailed(msg : String) extends ChannelResponse 52 | case class ConnectionSucceeded(remote : InetSocketAddress) extends ChannelResponse 53 | case class UnsolicitedReply(reply : ReplyMessage) extends ChannelResponse 54 | case class WriteFailed(msg : String) extends ChannelResponse 55 | case class Statistics( 56 | requests : Long, 57 | requestBytes : Long, 58 | replies : Long, 59 | replyBytes : Long, 60 | writeFailures : Long, 61 | unsolicitedReplies : Long, 62 | spuriousMessages : Long) extends ChannelResponse 63 | } 64 | 65 | abstract class Channel(remote : InetSocketAddress, options : ConnectionOptions, listener : ActorRef, isPrimary : Boolean) 66 | extends Actor with ActorLogging { 67 | 68 | log.debug(s"Establishing ${if (isPrimary) "primary" else "secondary"} Channel to $remote ") 69 | 70 | val pendingResponses = mutable.HashMap.empty[Int, (Boolean, ActorRef)] 71 | var requestCounter : Long = 0L 72 | var requestBytes : Long = 0L 73 | var replyCounter : Long = 0L 74 | var replyBytes : Long = 0L 75 | var writeFailures : Long = 0L 76 | var unsolicitedReplies : Long = 0L 77 | var spuriousMessages : Long = 0L 78 | 79 | def makeStats : Channel.Statistics = { 80 | Channel.Statistics(requestCounter, requestBytes, replyCounter, replyBytes, 81 | writeFailures, unsolicitedReplies, spuriousMessages) 82 | } 83 | 84 | def handleRequest(requestMsg : RequestMessage, msg_to_send : ByteString) : Unit 85 | 86 | def handleReply(replyMsg : ReplyMessage, toActor : ActorRef) : Unit = { 87 | toActor ! replyMsg 88 | } 89 | 90 | def handleClose() : Unit = { 91 | context become closing 92 | } 93 | 94 | final def doRequest(msg : Channel.SendMessage) = { 95 | val message = msg.msg 96 | val replyTo = msg.replyTo 97 | requestCounter += 1 98 | // Send a message to Mongo via connection actor 99 | val msg_to_send = message.finish 100 | requestBytes += msg_to_send.length 101 | pendingResponses.put(message.requestId, message.requiresResponse -> replyTo) 102 | handleRequest(message, msg_to_send) 103 | } 104 | 105 | final def doReply(msg : ByteString) = { 106 | replyCounter += 1 107 | replyBytes += msg.length 108 | val reply = ReplyMessage(msg) // Encapsulate that in a ReplyMessage 109 | pendingResponses.get(reply.responseTo) match { 110 | case Some((requiresResponse, actor)) ⇒ 111 | pendingResponses.remove(reply.responseTo) 112 | if (requiresResponse) { 113 | log.debug("Handling Reply: {} ({} bytes)", reply, msg.length) 114 | handleReply(reply, actor) 115 | } 116 | case None ⇒ 117 | log.warning(s"Received reply ({}) but matching request was not found", reply) 118 | listener ! Channel.UnsolicitedReply(reply) 119 | } 120 | } 121 | 122 | override def preStart() = { 123 | context.become(unconnected) 124 | } 125 | 126 | def unconnected = LoggingReceive { 127 | case Channel.Close ⇒ 128 | log.info(s"Channel.Close requested while unconnected so terminating") 129 | context stop self 130 | 131 | case Channel.GetStatistics ⇒ 132 | sender() ! makeStats 133 | 134 | case x : Any ⇒ 135 | log.debug(s"In unconnected, got other message: {}", x) 136 | spuriousMessages += 1 137 | } 138 | 139 | def connected : Receive = LoggingReceive { 140 | case Channel.Close ⇒ 141 | log.info("Channel is closing") 142 | handleClose 143 | 144 | case Channel.GetStatistics ⇒ 145 | sender() ! makeStats 146 | 147 | case msg : Channel.SendMessage ⇒ 148 | doRequest(msg) 149 | 150 | case x : Any ⇒ 151 | log.debug(s"In connected, got other message: {}", x) 152 | spuriousMessages += 1 153 | } 154 | 155 | def closing : Receive = LoggingReceive { 156 | case Channel.Close ⇒ 157 | log.info("Channel.Close ignored as channel is already closing") 158 | 159 | case Channel.SendMessage ⇒ 160 | log.info("Channel.SendMessage ignored as channel is closing") 161 | 162 | case x : Any ⇒ 163 | log.debug(s"In closing, got other message: {}", x) 164 | spuriousMessages += 1 165 | } 166 | 167 | final def receive : Receive = { 168 | case x : Any ⇒ 169 | throw new IllegalStateException("Channels should not be in receive state") 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /driver/src/main/scala/rxmongo/driver/MongoURI.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.driver 24 | 25 | import rxmongo.bson.RxMongoError 26 | 27 | import akka.http.scaladsl.model.Uri 28 | import akka.http.scaladsl.model.Uri.Path 29 | import akka.http.scaladsl.model.Uri.Path.{ Segment, Slash, Empty } 30 | 31 | import java.net.InetSocketAddress 32 | 33 | import scala.util.Try 34 | 35 | /** MongoDB Connection URI format. 36 | * 37 | * This represents the information parsed from a MongoDB Connection URI. It is in a form that is ready to be 38 | * consumed by RxMongo. 39 | * 40 | * A MongoDB URI has the following structure: 41 | * {{{ 42 | * mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]] 43 | * }}} 44 | * 45 | * The components of this string are: 46 | * {{{ 47 | * mongodb:// 48 | * A required prefix to identify that this is a string in the standard connection format. 49 | * username:password@ 50 | * Optional. If specified, the client will attempt to log in to the specific database using these 51 | * credentials after connecting to the mongod instance. 52 | * host1 53 | * This the only required part of the URI. It identifies a server address to connect to. It identifies 54 | * either a hostname, IP address, or UNIX domain socket. 55 | * :port1 56 | * Optional. The default value is :27017 if not specified. 57 | * hostX 58 | * Optional. You can specify as many hosts as necessary. You would specify multiple hosts, for example, 59 | * for connections to replica sets. 60 | * :portX 61 | * Optional. The default value is :27017 if not specified. 62 | * /database 63 | * Optional. The name of the database to authenticate if the connection string includes authentication 64 | * credentials in the form of username:password@. If /database is not specified and the connection string includes 65 | * credentials, the driver will authenticate to the admin database. 66 | * ?options 67 | * Connection specific options. See Connection String Options for a full description of these options. 68 | * }}} 69 | * 70 | * If the connection string does not specify a database/ you must specify a slash (i.e. /) between the last 71 | * hostN and the question mark that begins the string of options. 72 | * 73 | * @param hosts The resolved InetSocketAddresses for the hosts in the replica set 74 | * @param database The (optional) name of the database to authenticate to (defaults to admin database) 75 | * @param credentials The (optional) credentials for authentication 76 | * @param options The options parsed from the URI or defaulted 77 | * 78 | * @see [[http://docs.mongodb.org/master/reference/connection-string/]] 79 | * 80 | */ 81 | case class MongoURI( 82 | hosts : List[InetSocketAddress], 83 | database : Option[String], 84 | credentials : Option[Credentials], 85 | options : ConnectionOptions) 86 | 87 | object MongoURI { 88 | 89 | val scheme = "mongodb://" 90 | val DefaultPort = 27017 91 | 92 | def apply(uri_str : String) : Try[MongoURI] = Try { 93 | if (!uri_str.startsWith(scheme)) 94 | throw RxMongoError(s"Mongo URI must start with '$scheme'") 95 | val parts = uri_str.substring(scheme.length).split("/") 96 | val (authority : String, rest : String) = parts.length match { 97 | case 2 ⇒ parts(0) -> parts(1) 98 | case 1 ⇒ parts(0) -> "" 99 | case _ ⇒ 100 | throw RxMongoError(s"Mongo URI must contain an authority.") 101 | } 102 | val parts2 = authority.split("@") 103 | val (credentials : String, hostpart : String) = parts2.length match { 104 | case 2 ⇒ parts2(0) -> parts2(1) 105 | case 1 ⇒ "" -> parts(0) 106 | case _ ⇒ throw RxMongoError(s"Mongo URI must contain an authority section") 107 | } 108 | val hosts = hostpart.split(",") 109 | if (hosts.length < 1) 110 | throw RxMongoError(s"Mongo URI must contain at least one host") 111 | 112 | val str = scheme + credentials + "@" + hosts(0) + "/" + rest 113 | val uri = Uri(str) 114 | val auth = uri.authority 115 | val database = uri.path match { 116 | case Slash(Segment(p : String, Empty)) ⇒ Some(p) 117 | case Path.SingleSlash ⇒ None 118 | case Empty ⇒ None 119 | case _ ⇒ throw RxMongoError(s"Mongo URI path must only be one segment") 120 | } 121 | val creds = if (auth.userinfo.isEmpty) None else Some(Credentials(auth.userinfo)) 122 | val options = ConnectionOptions(uri.query()) 123 | 124 | val hostList : List[InetSocketAddress] = { 125 | hosts.map { h ⇒ 126 | val parts = h.split(":") 127 | val (host, port) = parts.size match { 128 | case 2 ⇒ parts(0) -> parts(1).toInt 129 | case 1 ⇒ parts(0) -> DefaultPort 130 | case _ ⇒ throw RxMongoError("Mongo URI host names must not be empty or contain more than one :") 131 | } 132 | new InetSocketAddress(host, port) 133 | } 134 | }.toList 135 | MongoURI(hostList, database, creds, options) 136 | } 137 | } 138 | 139 | case class Credentials(user : String, password : String) { 140 | override def toString = s"Credentials($user)" 141 | } 142 | 143 | object Credentials { 144 | def apply(str : String) : Credentials = { 145 | val parts = str.split(":") 146 | parts.length match { 147 | case 2 ⇒ Credentials(parts(0), parts(1)) 148 | case 1 ⇒ Credentials(parts(0), "") 149 | case _ ⇒ throw RxMongoError("User credentials must contain a user name") 150 | } 151 | } 152 | } 153 | 154 | -------------------------------------------------------------------------------- /driver/src/main/scala/rxmongo/driver/ReadPreference.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.driver 24 | 25 | sealed trait ReadPreference 26 | case object PrimaryRP extends ReadPreference { override def toString = "primary" } 27 | case object PrimaryPreferredRP extends ReadPreference { override def toString = "primaryPreferred" } 28 | case object SecondaryRP extends ReadPreference { override def toString = "secondary" } 29 | case object SecondaryPreferredRP extends ReadPreference { override def toString = "secondaryPreferred" } 30 | case object NearestRP extends ReadPreference { override def toString = "nearest" } 31 | 32 | object ReadPreference { 33 | def apply(str : String) : ReadPreference = { 34 | str match { 35 | case "primary" ⇒ PrimaryRP 36 | case "primaryPreferred" ⇒ PrimaryPreferredRP 37 | case "secondary" ⇒ SecondaryRP 38 | case "secondaryPreferred" ⇒ SecondaryPreferredRP 39 | case "nearest" ⇒ NearestRP 40 | case _ ⇒ PrimaryRP 41 | } 42 | } 43 | 44 | def tags(str : String) : Iterable[(String, String)] = { 45 | val parts = str.split(",") 46 | for (part ← parts if part.contains(":")) yield { 47 | val parts = part.split(":") 48 | parts(0) -> parts(1) 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /driver/src/main/scala/rxmongo/driver/StreamTcpChannel.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.driver 24 | 25 | import java.net.InetSocketAddress 26 | 27 | import akka.actor.ActorRef 28 | import akka.io.Inet.SocketOption 29 | import akka.io.Tcp.SO 30 | import akka.stream.stage.{ SyncDirective, Context, PushStage } 31 | import akka.stream.scaladsl.Tcp 32 | import akka.stream.scaladsl.Flow 33 | import akka.util.ByteString 34 | import rxmongo.messages.RequestMessage 35 | 36 | import scala.collection.mutable 37 | import scala.concurrent.duration.Duration 38 | 39 | /** Tcp Channel 40 | * 41 | * Description of thing 42 | */ 43 | case class StreamTcpChannel( 44 | remote : InetSocketAddress, 45 | options : ConnectionOptions, 46 | listener : ActorRef, 47 | isPrimary : Boolean) extends Channel(remote, options, listener, isPrimary) { 48 | 49 | implicit val system = context.system 50 | 51 | val streamTcp = Tcp(system) 52 | 53 | val connection = streamTcp.outgoingConnection( 54 | remoteAddress = remote, 55 | localAddress = Some(new InetSocketAddress(options.localIP.orNull, options.localPort)), 56 | options = List[SocketOption]( 57 | SO.KeepAlive(options.tcpKeepAlive), 58 | SO.OOBInline(options.tcpOOBInline), 59 | SO.TcpNoDelay(options.tcpNoDelay) 60 | ), 61 | connectTimeout = options.connectTimeoutMS match { case 0 ⇒ Duration.Inf; case x : Long ⇒ Duration(x, "ms") } 62 | ) 63 | 64 | val stage = new PushStage[ByteString, ByteString] { 65 | def onPush(elem : ByteString, ctx : Context[ByteString]) : SyncDirective = { 66 | doReply(elem) 67 | ctx.push(ByteString.empty) 68 | } 69 | } 70 | 71 | val handler = Flow[ByteString].map { msg ⇒ doReply(msg); msg } 72 | 73 | val materialized = connection.join[Unit](handler) 74 | 75 | val pendingRequestQueue = mutable.Queue.empty[RequestMessage] 76 | 77 | def handleRequest(requestMsg : RequestMessage, msg_to_send : ByteString) : Unit = {} 78 | } 79 | 80 | -------------------------------------------------------------------------------- /driver/src/main/scala/rxmongo/driver/Supervisor.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.driver 24 | 25 | import akka.actor._ 26 | import akka.event.LoggingReceive 27 | 28 | import scala.collection.mutable 29 | 30 | object Supervisor { 31 | def props() : Props = { 32 | Props(classOf[Supervisor]) 33 | } 34 | 35 | sealed trait Request 36 | case class AddConnection(uri : MongoURI, name : String) extends Request // reply with Connection actor 37 | case class DropConnection(uri : MongoURI) extends Request // no reply on success, NoSuchConnection if not found 38 | case object Shutdown extends Request // no reply, Supervisor shuts down 39 | case object NumConnections extends Request // reply with NumConnectionsReply 40 | case object GetConnections extends Request 41 | 42 | sealed trait Reply 43 | case class NumConnectionsReply(num : Int) extends Reply 44 | case class NoSuchConnection(uri : MongoURI) extends Reply 45 | case class GetConnectionsReply(connections : Map[MongoURI, ActorRef]) extends Reply 46 | } 47 | 48 | class Supervisor extends Actor with ActorLogging { 49 | 50 | import Supervisor._ 51 | 52 | /** Keep a list of all connections so that we can terminate the actors */ 53 | val connections = mutable.HashMap.empty[MongoURI, ActorRef] 54 | 55 | def numConnections : Int = connections.size 56 | 57 | def removeConnection(connection : ActorRef) : Unit = { 58 | connections.find { case (uri, conn) ⇒ conn == connection } match { 59 | case Some((uri, actor)) ⇒ connections.remove(uri) 60 | case None ⇒ // do nothing 61 | } 62 | } 63 | 64 | def exit() = { 65 | log.debug("RxMongo Supervisor has terminated connections and is stopping") 66 | context stop self 67 | } 68 | 69 | override def receive = LoggingReceive { 70 | case AddConnection(uri : MongoURI, name : String) ⇒ 71 | log.debug(s"AddConnection($uri,$name)") 72 | val connection = context.actorOf(Connection.props(uri), Driver.actorName(name)) 73 | context.watch(connection) 74 | connections.put(uri, connection) 75 | sender ! connection 76 | 77 | case DropConnection(uri : MongoURI) ⇒ 78 | log.debug(s"DropConnection($uri)") 79 | connections.get(uri) match { 80 | case Some(connection) ⇒ connection ! Connection.Close 81 | case None ⇒ sender() ! NoSuchConnection(uri) 82 | } 83 | 84 | case NumConnections ⇒ 85 | sender() ! NumConnectionsReply(numConnections) 86 | 87 | case GetConnections ⇒ 88 | sender() ! GetConnectionsReply(connections.toMap) 89 | 90 | case Terminated(actor) ⇒ 91 | removeConnection(actor) 92 | 93 | case Shutdown ⇒ 94 | log.debug("RxMongo Supervisor is terminating connections") 95 | if (connections.isEmpty) { 96 | exit() 97 | } else { 98 | connections.foreach { 99 | case (uri, connection) ⇒ 100 | connection ! PoisonPill 101 | } 102 | context.become(closing) 103 | } 104 | } 105 | 106 | def closing : Receive = LoggingReceive { 107 | case ac : AddConnection ⇒ 108 | log.warning("Refusing to add connection while RxMongo Supervisor is closing.") 109 | 110 | case dc : DropConnection ⇒ 111 | log.warning("Refusing to drop connection while RxMongo Supervisor is closing.") 112 | 113 | case Shutdown ⇒ 114 | log.warning("Terminate ignored, already terminating.") 115 | 116 | case NumConnections ⇒ 117 | sender ! NumConnectionsReply(numConnections) 118 | 119 | case Terminated(actor) ⇒ 120 | removeConnection(actor) 121 | if (connections.isEmpty) { 122 | exit() 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /driver/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 20 | ${logs_dir:-logs}/rxmongo.log 21 | 22 | ${logs_dir:-logs}/rxmongo.%i.log.zip 23 | 1 24 | 20 25 | 26 | 27 | 50MB 28 | 29 | 30 | %d %-7relative %-5level [%X{sourceThread}:%logger{30}:%X{akkaSource}]%n %msg%n%rootException 31 | false 32 | true 33 | 34 | 35 | 36 | 37 | %X{akkaTimestamp} %-5level [%X{sourceThread}:%logger{30}:%X{akkaSource}]%n %msg%n%rootException 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /driver/src/test/scala/rxmongo/driver/AkkaTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.driver 24 | 25 | import akka.actor.ActorSystem 26 | import akka.testkit.{ ImplicitSender, TestKit } 27 | import org.specs2.matcher.Matchers 28 | import org.specs2.mutable.SpecificationLike 29 | import org.specs2.specification.Scope 30 | import org.specs2.time.NoTimeConversions 31 | 32 | abstract class AkkaTest(_actorSystem : ActorSystem) extends TestKit(_actorSystem) 33 | with SpecificationLike with ImplicitSender with Matchers with Scope { 34 | 35 | } 36 | -------------------------------------------------------------------------------- /driver/src/test/scala/rxmongo/driver/ConnectionOptionsSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.driver 24 | 25 | import java.util.concurrent.TimeUnit 26 | 27 | import org.specs2.mutable.Specification 28 | import rxmongo.messages._ 29 | 30 | import scala.concurrent.duration.{ Duration, FiniteDuration } 31 | 32 | class ConnectionOptionsSpec extends Specification { 33 | 34 | "WriteConcern" should { 35 | "yield NoAcknowledgement for -1" in { 36 | WriteConcern("-1") must beEqualTo(WriteConcern(NoAcknowledgmentWC)) 37 | } 38 | "yield ErrorsOnly for 0" in { 39 | WriteConcern(" 0") must beEqualTo(WriteConcern(ErrorsOnlyWC)) 40 | } 41 | "yield BasicAcknowledgment for 1" in { 42 | WriteConcern("1 ") must beEqualTo(WriteConcern(BasicAcknowledgmentWC)) 43 | } 44 | "yield WaitForMembers for > 1" in { 45 | WriteConcern(" 2 ") must beEqualTo(WriteConcern(WaitForMembersWC(2))) 46 | } 47 | "yield Majority for 'majority'" in { 48 | WriteConcern("majority") must beEqualTo(WriteConcern(MajorityWC)) 49 | } 50 | "yield MembersWithTag for other strings" in { 51 | WriteConcern(" foo ") must beEqualTo(WriteConcern(MembersWithTagWC("foo"))) 52 | } 53 | "yield BasicAck for empty string" in { 54 | WriteConcern("") must beEqualTo(WriteConcern(BasicAcknowledgmentWC)) 55 | } 56 | } 57 | 58 | "ConnectionOptions" should { 59 | "validate connectTimeoutMS" in { 60 | ConnectionOptions(connectTimeoutMS = 3700000).validate should throwA[IllegalArgumentException] 61 | } 62 | "validate socketTimeoutMS" in { 63 | ConnectionOptions(socketTimeoutMS = -1).validate should throwA[IllegalArgumentException] 64 | } 65 | "validate maxPoolSize" in { 66 | ConnectionOptions(maxPoolSize = 0).validate should throwA[IllegalArgumentException] 67 | } 68 | "validate minPoolSize" in { 69 | ConnectionOptions(minPoolSize = 0).validate should throwA[IllegalArgumentException] 70 | } 71 | "validate maxPoolSize >= minPoolSize" in { 72 | ConnectionOptions(maxPoolSize = 9, minPoolSize = 10).validate should throwA[IllegalArgumentException] 73 | } 74 | "validate maxIdleTimeMS" in { 75 | ConnectionOptions(maxIdleTimeMS = 24 * 3600 * 1000 + 1).validate should throwA[IllegalArgumentException] 76 | } 77 | "validate wtimeoutMS" in { 78 | ConnectionOptions(writeConcern = 79 | WriteConcern(BasicAcknowledgmentWC, timeout = Duration(-1, TimeUnit.MILLISECONDS))).validate should 80 | throwA[IllegalArgumentException] 81 | } 82 | "validate rampupRate" in { 83 | ConnectionOptions(rampupRate = 2).validate should throwA[IllegalArgumentException] 84 | } 85 | "validate backoffThreshold" in { 86 | ConnectionOptions(backoffThreshold = 0.0).validate should throwA[IllegalArgumentException] 87 | } 88 | "validate backoffRate" in { 89 | ConnectionOptions(backoffRate = 0).validate should throwA[IllegalArgumentException] 90 | } 91 | "validate messagesPerResize" in { 92 | ConnectionOptions(messagesPerResize = -1).validate should throwA[IllegalArgumentException] 93 | } 94 | "validate channelReconnectPeriod" in { 95 | ConnectionOptions(channelReconnectPeriod = FiniteDuration(-1, "s")).validate should throwA[IllegalArgumentException] 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /driver/src/test/scala/rxmongo/driver/DriverSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.driver 24 | 25 | import java.net.{ Socket, InetSocketAddress, InetAddress } 26 | import javax.net.SocketFactory 27 | 28 | import akka.actor.{ ActorSystem, ActorRef } 29 | import akka.pattern.ask 30 | 31 | import org.specs2.execute.Result 32 | import rxmongo.bson.BSONObject 33 | import rxmongo.messages.cmds.BuildInfoCmd 34 | import rxmongo.messages.replies.BuildInfoReply 35 | import rxmongo.messages.replies.BuildInfoReply.BuildInfoCodec 36 | import rxmongo.messages.{ ReplyMessage, QueryMessage } 37 | 38 | import scala.concurrent.{ Await } 39 | import scala.concurrent.duration._ 40 | 41 | class DriverSpec extends AkkaTest(ActorSystem("DriverSpec")) { 42 | 43 | implicit val timeout = Driver.defaultTimeout 44 | 45 | def mongoTest(f : () ⇒ Result) : Result = { 46 | if (Helper.haveLocalMongo) { 47 | f() 48 | } else { 49 | skipped(": no local mongo") 50 | } 51 | } 52 | 53 | sequential 54 | "Driver" should { 55 | "confirm verson 3 in BuildInfo" in mongoTest { () => 56 | val driver = Driver(None, "BuildInfo") 57 | val future = driver.connect("mongodb://localhost/") 58 | val conn = Await.result(future, Duration(1, "s")) 59 | conn.isInstanceOf[ActorRef] must beTrue 60 | val c = conn.asInstanceOf[ActorRef] 61 | val future2 = c.ask(BuildInfoCmd) 62 | val x = Await.result(future2, 1.seconds) 63 | driver.close(500.millis) 64 | x.isInstanceOf[ReplyMessage] must beTrue 65 | val reply : ReplyMessage = x.asInstanceOf[ReplyMessage] 66 | reply.documents.size must beEqualTo(1) 67 | val buildInfoReply = reply.documents.head.to[BuildInfoReply] 68 | buildInfoReply.versionArray(0) must beGreaterThanOrEqualTo(3) 69 | } 70 | /* 71 | "mind its lifecycle" in { 72 | val driver = Driver(None, "Lifecycle") 73 | driver.close(1.second) 74 | driver.isClosed must beTrue 75 | } 76 | 77 | "make a simple connection" in mongoTest { () ⇒ 78 | val driver = Driver(None, "Simple") 79 | val future = driver.connect("mongodb://localhost/") 80 | val conn = Await.result(future, 1.seconds) 81 | conn.isInstanceOf[ActorRef] must beTrue 82 | driver.close(500.millis) 83 | success 84 | } 85 | */ 86 | 87 | "send an innocuous query" in mongoTest { () ⇒ 88 | val driver = Driver(None, "Innocuous") 89 | val future = driver.connect("mongodb://localhost/") 90 | val conn = Await.result(future, Duration(1, "s")) 91 | conn.isInstanceOf[ActorRef] must beTrue 92 | val c = conn.asInstanceOf[ActorRef] 93 | val msg = QueryMessage("rxmongo.test", BSONObject("foo" -> 1)) 94 | val future2 = c.ask(msg) 95 | val x = Await.result(future2, 1.seconds) 96 | driver.close(500.millis) 97 | x.isInstanceOf[ReplyMessage] must beTrue 98 | } 99 | /* 100 | "handle a CheckReplicaSet" in mongoTest { () ⇒ 101 | val driver = Driver(None, "Innocuous") 102 | val future = driver.connect("mongodb://localhost/") map { conn : ActorRef ⇒ 103 | conn ! Connection.CheckReplicaSet 104 | } 105 | Await.result(future, Duration(1, "s")) 106 | success 107 | } 108 | 109 | "return 0 for numConnections when first established" in { 110 | val driver = Driver(None, "numConnections=0") 111 | Await.result(driver.numConnections, 1.seconds) must beEqualTo(0) 112 | } 113 | 114 | "return 0 before a connection is established, 1 afterwards" in mongoTest { () ⇒ 115 | val driver = Driver(None, "ConnectionCount") 116 | val before = driver.numConnections 117 | val future = driver.connect("mongodb://localhost/") 118 | val conn = Await.result(future, 1.second) 119 | conn.isInstanceOf[ActorRef] must beTrue 120 | val after = driver.numConnections 121 | val result = Await.result(Future.sequence(Seq(before, after)), 1.second) 122 | result must beEqualTo(Seq(0, 1)) 123 | driver.close(1.second) 124 | success 125 | } 126 | */ 127 | } 128 | } 129 | 130 | object Helper { 131 | 132 | lazy val haveLocalMongo : Boolean = { 133 | val addr = InetAddress.getLoopbackAddress 134 | val port = 27017 135 | val socketAddress = new InetSocketAddress(addr, port) 136 | try { 137 | val socket : Socket = SocketFactory.getDefault.createSocket 138 | socket.connect(socketAddress, 1000) 139 | socket.close() 140 | true 141 | } catch { 142 | case x : Throwable ⇒ false 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /driver/src/test/scala/rxmongo/driver/MongoURISpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.driver 24 | 25 | import java.net.{ InetAddress, InetSocketAddress } 26 | import java.util.concurrent.TimeUnit 27 | 28 | import org.specs2.mutable.Specification 29 | import rxmongo.messages.{ WaitForMembersWC, WriteConcern, PLAIN } 30 | 31 | import scala.concurrent.duration.Duration 32 | 33 | class MongoURISpec extends Specification { 34 | 35 | val simple = "mongodb://reid@somewhere.com/" 36 | val multi_host = "mongodb://host1.com,host2.com:8989/" 37 | val options_uri = "mongodb://host1.com/?localPort=29292" 38 | val complex = 39 | "mongodb://joe:blow@host1.com:999,host-with-dashes.com:1000,other_host.net:1001/dbName?replicaSet=james&ssl=true&connectTimeoutMS=10000&socketTimeoutMS=45000&maxPoolSize=10&minPoolSize=2&maxIdleTimeMS=30000&waitQueueMultiple=4&waitQueueTimeoutMS=30000&w=7&wtimeoutMS=10000&journal=true&readPreference=primaryPreferred&readPreferenceTags=k:v,l:w&authSource=other&authMechanism=PLAIN&gssapiServiceName=mygss&readPreferenceTags=a:b,c:d&tcpNoDelay=false&tcpKeepAlive=false&tcpOOBInline=true&localIP=0.0.0.0&localPort=27272" 40 | 41 | def getUri(s : String) : MongoURI = { 42 | val try_uri = MongoURI(s) 43 | if (try_uri.isFailure) 44 | throw try_uri.failed.get 45 | try_uri.get 46 | } 47 | 48 | def addr(h : String, p : Int) = new InetSocketAddress(h, p) 49 | 50 | "MongoURI" should { 51 | s"parse a simple url: $simple" in { 52 | val uri = getUri(simple) 53 | uri.hosts.head must beEqualTo(addr("somewhere.com", MongoURI.DefaultPort)) 54 | uri.credentials must beEqualTo(Some(Credentials("reid", ""))) 55 | uri.database must beEqualTo(None) 56 | uri.options must beEqualTo(ConnectionOptions()) 57 | } 58 | 59 | s"parse a uri with multiple hosts: $multi_host" in { 60 | val uri = getUri(multi_host) 61 | uri.hosts.head must beEqualTo(addr("host1.com", MongoURI.DefaultPort)) 62 | uri.hosts.tail.head must beEqualTo(addr("host2.com", 8989)) 63 | uri.credentials must beEqualTo(None) 64 | uri.database must beEqualTo(None) 65 | uri.options must beEqualTo(ConnectionOptions()) 66 | } 67 | 68 | s"parse a uri with options: $options_uri" in { 69 | val uri = getUri(options_uri) 70 | uri.hosts.head must beEqualTo(addr("host1.com", MongoURI.DefaultPort)) 71 | uri.hosts.tail must beEqualTo(List.empty[InetSocketAddress]) 72 | uri.database must beEqualTo(None) 73 | uri.options must beEqualTo(ConnectionOptions(localPort = 29292)) 74 | } 75 | 76 | s"parse a complex url: $complex" in { 77 | val uri = getUri(complex) 78 | val hosts = uri.hosts 79 | hosts.length must beEqualTo(3) 80 | hosts(0) must beEqualTo(addr("host1.com", 999)) 81 | hosts(1) must beEqualTo(addr("host-with-dashes.com", 1000)) 82 | hosts(2) must beEqualTo(addr("other_host.net", 1001)) 83 | uri.database must beEqualTo(Some("dbName")) 84 | uri.credentials must beEqualTo(Some(Credentials("joe", "blow"))) 85 | uri.options must beEqualTo(ConnectionOptions( 86 | replicaSet = Some("james"), 87 | ssl = true, 88 | connectTimeoutMS = 10000, 89 | socketTimeoutMS = 45000, 90 | maxPoolSize = 10, 91 | minPoolSize = 2, 92 | maxIdleTimeMS = 30000, // One minute 93 | waitQueueMultiple = 4, // Unconstrained 94 | waitQueueTimeoutMS = 30000, // One minute 95 | WriteConcern(WaitForMembersWC(7), Duration(10000, TimeUnit.MILLISECONDS), journal = true), 96 | readPreference = PrimaryPreferredRP, 97 | readPreferenceTags = List(List("a" -> "b", "c" -> "d"), List("k" -> "v", "l" -> "w")), 98 | authSource = Some("other"), 99 | authMechanism = PLAIN, 100 | gssapiServiceName = Some("mygss"), 101 | tcpNoDelay = false, 102 | tcpKeepAlive = false, 103 | tcpOOBInline = true, 104 | localIP = Some(InetAddress.getByName("0.0.0.0")), 105 | localPort = 27272) 106 | ) 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /driver/src/test/scala/rxmongo/driver/SupervisorSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.driver 24 | 25 | import akka.actor._ 26 | import akka.pattern.ask 27 | import akka.testkit.TestProbe 28 | import rxmongo.driver.Supervisor.NumConnectionsReply 29 | 30 | import scala.concurrent.Await 31 | import scala.concurrent.duration._ 32 | 33 | class SupervisorSpec extends AkkaTest(ActorSystem("SupervisorTest")) { 34 | 35 | sequential 36 | 37 | "Supervisor" should { 38 | "handle Shutdown" in { 39 | val s = system.actorOf(Supervisor.props()) 40 | s ! Supervisor.Shutdown 41 | val probe = TestProbe() 42 | probe watch s 43 | within(500.millis) { 44 | probe.expectTerminated(s) 45 | } 46 | success 47 | } 48 | 49 | "return 0 for NumChannels at startup" in { 50 | val s = system.actorOf(Supervisor.props()) 51 | val future = s.ask(Supervisor.NumConnections)(500.millis) 52 | val reply = Await.result(future, 500.millis) 53 | reply must beEqualTo(NumConnectionsReply(0)) 54 | } 55 | 56 | "add a connection" in { 57 | val s = system.actorOf(Supervisor.props()) 58 | val uri = MongoURI("mongodb://localhost/").get 59 | s ! Supervisor.AddConnection(uri, "MyConnection") 60 | expectMsgType[ActorRef](750.millis) 61 | success 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /examples/src/main/scala/rxmongo/examples/ClientBasics.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.examples 24 | 25 | import rxmongo.bson.BSONDocument 26 | import rxmongo.client.Client 27 | import rxmongo.messages.Query 28 | import scala.concurrent.Await 29 | import scala.concurrent.duration._ 30 | import scala.concurrent.ExecutionContext.Implicits.global 31 | 32 | /** Basic Client Usage Example 33 | * 34 | * This example shows how to use the Client interface to create the client [1], obtain a database from the client [2], 35 | * obtain a collection from the database [3], query the collection [4], obtain the results [5], and handle query 36 | * errors [6]. 37 | */ 38 | object ClientBasics extends App { 39 | 40 | // [1] Make a connection to the mydb database on the local host 41 | val client = Client("mongodb://localhost/mydb") 42 | 43 | try { 44 | 45 | // [2] Obtain a database from the client 46 | val db = client.database("mydb") 47 | 48 | // [3] Obtain a collection from the database 49 | val coll = db.collection("mycoll") 50 | 51 | // [4] Obtain a cursor for finding the documents with field name set to "foo" 52 | val cursor = coll.find(Query("name" -> "foo")) 53 | 54 | // [5] When the results are ready, open them 55 | val future = cursor.map { crsr ⇒ 56 | // [6] For each result 57 | while (crsr.hasNext) { 58 | crsr.next.map { results : BSONDocument ⇒ 59 | // [7] We got an answer to our query, print the results 60 | println("Results: " + results) 61 | } recover { 62 | case xcptn : Throwable ⇒ 63 | // [8] We got an error in our query, print the error message 64 | println("Error in query: " + xcptn) 65 | } 66 | } 67 | } 68 | 69 | // [9] Wait for all the asynchronous processing to complete, within 5 seconds. 70 | Await.result(future, 5.seconds) 71 | } finally { 72 | // [10] Close the client and its driver, releasing background threads 73 | client.close() 74 | } 75 | // [11] Terminate the program. 76 | println(s"Finished.") 77 | } 78 | -------------------------------------------------------------------------------- /examples/src/main/scala/rxmongo/examples/DriverBasics.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.examples 24 | 25 | import akka.actor.ActorRef 26 | import akka.pattern.ask 27 | 28 | import com.typesafe.config.ConfigFactory 29 | 30 | import rxmongo.driver._ 31 | import rxmongo.bson._ 32 | import rxmongo.messages.{ ReplyMessage, QueryMessage } 33 | 34 | import scala.concurrent.ExecutionContext.Implicits.global 35 | 36 | /** Basic Driver Example 37 | * 38 | * This example shows how to instantiate the driver[1], connect to a MongoDB instance using a 39 | * Mongodb URL [2], create a query message [3], send a query and get its response in a non-blocking 40 | * fashion [4], and decode the reply from MongoDB [5]. 41 | */ 42 | object DriverBasics extends App { 43 | 44 | // You can specify your own timeout for asynchronous calls instead of using this default value from Driver. 45 | implicit val timeout = Driver.defaultTimeout 46 | 47 | // [1] Instantiate with a specific configuration and a specific name for the driver 48 | val driver = Driver(ConfigFactory.load("rxmongo"), "MyDriver") 49 | 50 | // [2] Make a connection to database "mydb" on the local host at the default port and set the 51 | // maxPoolSize configuration value to 19 so at most 19 channels to the server will be open. This 52 | // will yield a Future[ActorRef] in "conn". If the connection succeeds, conn will have a value 53 | // that can be used to talk to the members of a MongoDB replica set. 54 | val conn = driver.connect("mongodb://localhost/mydb?maxPoolSize=19") 55 | 56 | // [3] Create a query message to find the documents with the name "foo" in the mydb.mycoll namespace. 57 | // You can also create all of the other kinds of messages that MongoDB supports. 58 | val msg = QueryMessage("mydb.mycoll", BSONObject("name" -> "foo")) 59 | 60 | // [4] When the connection is successful, extract the ActorRef and send it the query using the ask pattern 61 | // which will asynchronously deliver a ReplyMessage when the response comes back from MongoDB. 62 | conn.map { 63 | case connection : ActorRef ⇒ 64 | connection.ask(msg).map { 65 | case reply : ReplyMessage ⇒ 66 | // [5] We got a reply from MongoDB, now we need to decipher it. The numberReturned field tells us how 67 | // many documents were returned by the query. If we got at least one, just print out the head document. 68 | if (reply.numberReturned > 0) { 69 | println("Document returned: " + reply.documents.head) 70 | } else if (reply.QueryFailure) { 71 | // In this case, there was something wrong with the query. 72 | println("Query Failed") 73 | } else { 74 | // Chances are if you run this program, you will get this output because you don't have a 75 | // database named "mydb.mycoll" and if you do, it probably doesn't have a document whose 76 | // name field is "foo". 77 | println("No results: " + reply) 78 | } 79 | } recover { 80 | case xcptn : Throwable ⇒ 81 | // If the query fails for any reason, you can recover from it here. 82 | println("Error from MongoDB: " + xcptn) 83 | } 84 | } recover { 85 | case xcptn : Throwable ⇒ 86 | // If the connection fails for any reason, you can recover from it here. 87 | println("Could not connect to MongoDB: " + xcptn) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /gridfs/src/main/scala/GridFS.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | import rxmongo.client.Client 24 | 25 | class GridFS(client: Client) { 26 | 27 | } 28 | -------------------------------------------------------------------------------- /messages/src/main/scala/rxmongo/messages/AuthMechanism.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.messages 24 | 25 | sealed trait AuthMechanism { val asStr : String } 26 | case object MONGODB_X509 extends AuthMechanism { override def toString = asStr; val asStr = "MONGODB-X509"; } 27 | case object MONGODB_CR extends AuthMechanism { override def toString = asStr; val asStr = "MONGODB-CR" } 28 | case object GSSAPI extends AuthMechanism { override def toString = asStr; val asStr = "GSSAPI" } 29 | case object PLAIN extends AuthMechanism { override def toString = asStr; val asStr = "PLAIN" } 30 | 31 | object AuthMechanism { 32 | def apply(str : String) : AuthMechanism = { 33 | str match { 34 | case MONGODB_X509.asStr ⇒ MONGODB_X509 35 | case MONGODB_CR.asStr ⇒ MONGODB_CR 36 | case GSSAPI.asStr ⇒ GSSAPI 37 | case PLAIN.asStr ⇒ PLAIN 38 | case _ ⇒ MONGODB_X509 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /messages/src/main/scala/rxmongo/messages/Command.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.messages 24 | 25 | import rxmongo.bson._ 26 | 27 | /** Generic command issued on a database 28 | * Commands are basically query commands issued to the "\$cmd" collection. The selector of the query forms the 29 | * content of the command and its parameters. Subclasses of this class form all the standard commands that 30 | * MongoDB knows to process from a driver. 31 | * @param db The name of the database towards which the command should be directed. 32 | * @param selector The content of the command. 33 | */ 34 | class Command( 35 | db : String, 36 | val selector : BSONObject) extends GenericQueryMessage { 37 | val fullCollectionName = s"$db.$$cmd" 38 | val options = QueryOptions.default 39 | val returnFieldsSelector : Option[BSONObject] = None 40 | override def appendTo(builder : StringBuilder) : StringBuilder = { 41 | super.appendTo(builder). 42 | append(",db=").append(db). 43 | append(",options=").append(options.withNumberToReturn(-1)). 44 | append(",selector=").append(selector). 45 | append(",returnFieldsSelector=").append(returnFieldsSelector) 46 | } 47 | } 48 | 49 | /** An Administrative Command 50 | * This is like a generic command but it is always directed towards the database named "admin". 51 | * @param query 52 | */ 53 | class AdminCommand(query : BSONObject) extends Command("admin", query) 54 | -------------------------------------------------------------------------------- /messages/src/main/scala/rxmongo/messages/Delete.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.messages 24 | 25 | import rxmongo.bson._ 26 | 27 | case class Delete(query : BSONObject, limit : Int) 28 | 29 | object Delete { 30 | 31 | def apply(selector : BooleanExpression, limit : Int) : Delete = { 32 | Delete(selector.result, limit) 33 | } 34 | 35 | def apply(query : Query, limit : Int = 0) : Delete = { 36 | Delete(query.result, limit) 37 | } 38 | 39 | implicit object Codec extends DocumentCodec[Delete] { 40 | def read(doc : BSONDocument) : Delete = { 41 | Delete(doc.asObject("q"), doc.asInt("limit")) 42 | } 43 | def write(value : Delete, builder : BSONBuilder) : BSONBuilder = { 44 | builder.obj("q", value.query) 45 | builder.integer("limit", value.limit) 46 | builder 47 | } 48 | } 49 | } 50 | 51 | -------------------------------------------------------------------------------- /messages/src/main/scala/rxmongo/messages/Query.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.messages 24 | 25 | import rxmongo.bson.BSONBuilder 26 | 27 | /** Represent a MongoDB Query 28 | * 29 | * A query is built 30 | */ 31 | case class Query() extends BSONBuilder { 32 | 33 | def select(what : BooleanExpression) : Query = { 34 | obj("$query", what) 35 | this 36 | } 37 | 38 | def comment(msg : String) : Query = { 39 | string("$comment", msg) 40 | this 41 | } 42 | 43 | /** Forces MongoDB to use a specific index. See hint() 44 | * @see [[http://docs.mongodb.org/master/reference/operator/meta/hint/]] 45 | * @param index 46 | * @return 47 | */ 48 | def hint(index : String) : Query = { 49 | obj("$hint", index, 1) 50 | this 51 | } 52 | 53 | /** Limits the number of documents scanned. 54 | * @see [[http://docs.mongodb.org/master/reference/operator/meta/maxScan/]] 55 | * @param num_docs 56 | * @return 57 | */ 58 | def maxScan(num_docs : Int) : Query = { 59 | integer("$maxScan", num_docs) 60 | this 61 | } 62 | 63 | /** Specifies a cumulative time limit in milliseconds for processing operations on a cursor. 64 | * @see [[http://docs.mongodb.org/master/reference/operator/meta/maxTimeMS/]] 65 | * @param millis 66 | * @return 67 | */ 68 | def maxTimeMS(millis : Long) : Query = { 69 | long("$maxTimeMS", millis) 70 | this 71 | } 72 | 73 | /** Specifies an exclusive upper limit for the index to use in a query. 74 | * @see [[http://docs.mongodb.org/master/reference/operator/meta/max/]] 75 | * @param fields 76 | * @return 77 | */ 78 | def max(fields : (String, Any)*) : Query = { 79 | anyObj("$max", fields.head, fields.tail : _*) 80 | this 81 | } 82 | 83 | /** Specifies an inclusive lower limit for the index to use in a query. 84 | * @see [[http://docs.mongodb.org/master/reference/operator/meta/min/]] 85 | * @param fields 86 | * @return 87 | */ 88 | def min(fields : (String, Any)*) : Query = { 89 | anyObj("$min", fields.head, fields.tail : _*) 90 | this 91 | } 92 | 93 | /** Returns a cursor with documents sorted according to a sort specification. 94 | * @see [[http://docs.mongodb.org/master/reference/operator/meta/orderby/]] 95 | * @param field 96 | * @param ascending 97 | * @return 98 | */ 99 | def orderBy(field : String, ascending : Boolean = true) : Query = { 100 | obj("$orderby", field, if (ascending) 1 else -1) 101 | this 102 | } 103 | 104 | /** Forces the cursor to only return fields included in the index. 105 | * @see [[http://docs.mongodb.org/master/reference/operator/meta/returnKey/]] 106 | * @return 107 | */ 108 | def returnKey() : Query = { 109 | boolean("$returnKey", value = true) 110 | this 111 | } 112 | 113 | /** Modifies the documents returned to include references to the on-disk location of each document. 114 | * @see [[http://docs.mongodb.org/master/reference/operator/meta/showDiskLoc/]] 115 | * @return 116 | */ 117 | def showDiskLoc() : Query = { 118 | boolean("$showDiskLoc", value = true) 119 | this 120 | } 121 | 122 | /** Forces the query to use the index on the _id field. 123 | * @see [[http://docs.mongodb.org/master/reference/operator/meta/snapshot/]] 124 | * @return 125 | */ 126 | def snapshot() : Query = { 127 | boolean("$snapshot", value = true) 128 | this 129 | } 130 | 131 | /** A special sort order that orders documents using the order of documents on disk. 132 | * 133 | * @see [[http://docs.mongodb.org/master/reference/operator/meta/natural/]] 134 | * @param reverse 135 | * @return 136 | */ 137 | def natural(reverse : Boolean = false) : Query = { 138 | integer("$natural", if (reverse) -1 else 1) 139 | this 140 | } 141 | 142 | } 143 | 144 | object Query { 145 | def apply(what : BooleanExpression) : Query = new Query().select(what) 146 | 147 | implicit class LimitQueryModifier(q : Query) {} 148 | 149 | } 150 | -------------------------------------------------------------------------------- /messages/src/main/scala/rxmongo/messages/QueryOptions.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.messages 24 | 25 | /** The Options For A Query 26 | * 27 | * @param numberToSkip The number of documents to skip. Sets the number of documents to omit - starting from the first 28 | * document in the resulting dataset - when returning the result of the query. 29 | * @param numberToReturn The number of documents to return in the first OP_REPLY batch. Limits the number of 30 | * documents in the first OP_REPLY message to the query. However, the database will still 31 | * establish a cursor and return the cursorID to the client if there are more results than 32 | * numberToReturn. If the client driver offers ‘limit’ functionality (like the SQL LIMIT 33 | * keyword), then it is up to the client driver to ensure that no more than the specified 34 | * number of documents are returned to the calling application. If numberToReturn is 0, the 35 | * db will use the default return size. If the number is negative, then the database will 36 | * return that number and close the cursor. No further results for that query can be fetched. 37 | * If numberToReturn is 1 the server will treat it as -1 (closing the cursor automatically). 38 | * @param tailableCursor Tailable means cursor is not closed when the last data is retrieved. Rather, the cursor 39 | * marks the final object’s position. You can resume using the cursor later, from where it was 40 | * located, if more data were received. Like any “latent cursor”, the cursor may become invalid 41 | * at some point (CursorNotFound) – for example if the final object it references were deleted. 42 | * @param slaveOk Allow query of replica slave. Normally these return an error except for namespace “local”. 43 | * @param noCursorTimeout The server normally times out idle cursors after an inactivity period (10 minutes) to 44 | * prevent excess memory use. Set this option to prevent that. 45 | * @param awaitData Use with TailableCursor. If we are at the end of the data, block for a while rather than 46 | * returning no data. After a timeout period, we do return as normal. 47 | * @param exhaust Stream the data down full blast in multiple “more” packages, on the assumption that the client 48 | * will fully read all data queried. Faster when you are pulling a lot of data and know you want to 49 | * pull it all down. Note : the client is not allowed to not read all the data unless it closes 50 | * the connection. 51 | * @param partial Get partial results from a mongos if some shards are down (instead of throwing an error) 52 | */ 53 | case class QueryOptions( 54 | numberToSkip : Int = 0, 55 | numberToReturn : Int = -1, 56 | tailableCursor : Boolean = false, 57 | slaveOk : Boolean = false, 58 | noCursorTimeout : Boolean = false, 59 | awaitData : Boolean = false, 60 | exhaust : Boolean = false, 61 | partial : Boolean = false) { 62 | 63 | def flags : Int = { 64 | 0 | 65 | (if (tailableCursor) 2 else 0) | 66 | (if (slaveOk) 4 else 0) | 67 | // 8 (4th bit) is reserved for opLogReplay which is internal to MongoDB 68 | (if (noCursorTimeout) 16 else 0) | 69 | (if (awaitData) 32 else 0) | 70 | (if (exhaust) 64 else 0) | 71 | (if (partial) 128 else 0) 72 | } 73 | 74 | def withNumberToSkip(n : Int) = copy(numberToSkip = n) 75 | def withNumberToReturn(n : Int) = copy(numberToReturn = n) 76 | def withTailableCursor = copy(tailableCursor = true) 77 | def withSlaveOk = copy(slaveOk = true) 78 | def withNoCursorTimeout = copy(noCursorTimeout = true) 79 | def withAwaitData = copy(awaitData = true) 80 | def withExhaust = copy(exhaust = true) 81 | def withPartial = copy(partial = true) 82 | } 83 | 84 | object QueryOptions { 85 | val default = QueryOptions() 86 | 87 | def apply(skip : Int, ret : Int, flags : Int) : QueryOptions = { 88 | val tailableCursor = (flags & 2) != 0 89 | val slaveOk = (flags & 4) != 0 90 | val noCursorTimeout = (flags & 16) != 0 91 | val awaitData = (flags & 32) != 0 92 | val exhaust = (flags & 64) != 0 93 | val partial = (flags & 128) != 0 94 | QueryOptions(skip, ret, tailableCursor, slaveOk, noCursorTimeout, awaitData, exhaust, partial) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /messages/src/main/scala/rxmongo/messages/RetryStrategy.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.messages 24 | 25 | import scala.concurrent.duration._ 26 | 27 | /** RxMongo Retry Strategy. 28 | * 29 | * This represents the strategy for retrying database operations under certain conditions. When a database operation 30 | * fails because a replica set became unavailable, the RetryStrategy is used to determine how many times to repeat the 31 | * operation before giving up with a failure. This approach provides high availability and fault tolerance by 32 | * eliminating "normal" failures that can be recovered by doing a simple retry. 33 | */ 34 | class RetryStrategy extends Iterator[FiniteDuration] { 35 | val maxRetries : Int = 5 36 | protected var retry : Int = 0 37 | def hasNext : Boolean = { retry < maxRetries } 38 | def next() : FiniteDuration = { 39 | if (hasNext) 40 | throw new NoSuchElementException("Retries exhausted") 41 | retry += 1 42 | nextDelay 43 | } 44 | def nextDelay : FiniteDuration = 500.millis 45 | def reset : Unit = { retry = 0 } 46 | } 47 | 48 | object RetryStrategy { 49 | def apply(retries : Int = 5) : RetryStrategy = new RetryStrategy { override val maxRetries = retries } 50 | val default : RetryStrategy = RetryStrategy() 51 | } 52 | 53 | /** Simple Retry Strategy 54 | * This retry strategy simply differentiates between the initial delay and subsequent delays 55 | * @param initialDelay The initial delay before the first retry (defaults to 250ms) 56 | * @param repeatDelay The subsequent delay after the first retry (defaults to 500ms) 57 | * @param maxRetries The maximum number of retries to attempt (defaults to 5) 58 | */ 59 | case class SimpleRetryStrategy( 60 | initialDelay : FiniteDuration = 250.millis, 61 | repeatDelay : FiniteDuration = 500.millis, 62 | override val maxRetries : Int = 5) extends RetryStrategy { 63 | 64 | override def nextDelay : FiniteDuration = { 65 | if (retry == 1) 66 | initialDelay 67 | else 68 | repeatDelay 69 | } 70 | } 71 | 72 | /** Linear Retry Strategy 73 | * A retry strategy that increases the delay for each retry linearly in an arithmetic progression 74 | * @param delay The amount of delay to increase on each successive attempt (defaults to 250ms) 75 | * @param maxRetries The maximum number of retries to attempt (defaults to 5) 76 | */ 77 | case class LinearRetryStrategy( 78 | delay : FiniteDuration = 250.millis, 79 | override val maxRetries : Int = 5) extends RetryStrategy { 80 | override def nextDelay : FiniteDuration = { 81 | delay * retry 82 | } 83 | } 84 | 85 | /** Fibonacci Retry Strategy 86 | * A retry strategy that uses the Fibonacci series to increase the delay. The series is multiplied by the 87 | * delay so you get 1*delay, 1*delay, 2*delay, 3*delay, 5*delay, 8*delay, etc. 88 | * @param delay The delay factor to multiply the fibonacci value with to arrive at a delay (defaults to 250ms) 89 | * @param maxRetries Maximum number of retries to attempt (defaults to 5) 90 | */ 91 | case class FibonacciRetryStrategy( 92 | delay : FiniteDuration = 250.millis, 93 | override val maxRetries : Int = 5) extends RetryStrategy { 94 | var last = 1 95 | var lastLast = 0 96 | override def nextDelay : FiniteDuration = { 97 | val nextLast = last + lastLast 98 | lastLast = last 99 | last = nextLast 100 | delay * nextLast 101 | } 102 | override def reset : Unit = { super.reset; last = 1; lastLast = 0 } 103 | } 104 | 105 | /** Geometric Retry Strategy 106 | * A retry strategy that increases the delay geometrically by multiplying the base delay by the square of the 107 | * retry number (1, 4, 9, 16, 25...) 108 | * @param delay The base delay factor that is multiplied by a geometrically increasing number. 109 | * @param maxRetries The maximum number of retry to attempt (defaults to 5) 110 | */ 111 | case class GeometricRetryStrategy( 112 | delay : FiniteDuration = 250.millis, 113 | override val maxRetries : Int = 5) extends RetryStrategy { 114 | override def nextDelay : FiniteDuration = { 115 | delay * retry * retry 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /messages/src/main/scala/rxmongo/messages/Sort.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.messages 24 | 25 | import rxmongo.bson.{ BSONDocument, DocumentCodec, BSONBuilder } 26 | 27 | /** A Sort Specification 28 | * 29 | * Description of thing 30 | */ 31 | case class Sort(fields : Seq[(String, Boolean)]) 32 | 33 | object Sort { 34 | val empty = Sort(Seq.empty[(String, Boolean)]) 35 | 36 | implicit object Codec extends DocumentCodec[Sort] { 37 | def write(value : Sort, bldr : BSONBuilder) : BSONBuilder = { 38 | for ((key, ascending) ← value.fields) { 39 | bldr.integer(key, if (ascending) 1 else -1) 40 | } 41 | bldr 42 | } 43 | def read(doc : BSONDocument) : Sort = { 44 | Sort(doc.asSeqOf[Int].map { case (key, value) ⇒ key -> (if (value < 0) false else true) }) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /messages/src/main/scala/rxmongo/messages/Update.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.messages 24 | 25 | import akka.util.{ ByteString } 26 | import rxmongo.bson._ 27 | 28 | case class Update(selector : BSONObject, updater : BSONObject, upsert : Boolean, multi : Boolean, isolated : Boolean) 29 | 30 | object Update { 31 | def apply(selector : BooleanExpression, updater : UpdateExpression, upsert : Boolean, multi : Boolean, isolated : Boolean) : Update = { 32 | Update(selector.result, updater.result, upsert, multi, isolated) 33 | } 34 | 35 | def apply(selector : Query, updater : UpdateExpression, upsert : Boolean, multi : Boolean, isolated : Boolean) : Update = { 36 | Update(selector.result, updater.result, upsert, multi, isolated) 37 | } 38 | 39 | implicit object Codec extends DocumentCodec[Update] { 40 | def read(doc : BSONDocument) : Update = { 41 | val q = doc.asObject("q") 42 | val (selector, isolated) = { 43 | if (q.contains("$isolated")) 44 | (q - "$isolated") → true 45 | else 46 | q → false 47 | } 48 | Update(selector, doc.asObject("u"), doc.asBoolean("upsert"), doc.asBoolean("multi"), isolated) 49 | } 50 | def write(value : Update, builder : BSONBuilder) : BSONBuilder = { 51 | val selector = if (value.isolated) { 52 | val bldr = ByteString.newBuilder 53 | value.selector.doc.addTo(bldr) 54 | bldr.integer("$isolated", 1) 55 | BSONObject(bldr.wrapAndTerminate) 56 | } else value.selector 57 | builder. 58 | obj("q", selector). 59 | obj("u", value.updater). 60 | boolean("upsert", value.upsert). 61 | boolean("multi", value.multi) 62 | builder 63 | } 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /messages/src/main/scala/rxmongo/messages/WriteConcern.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.messages 24 | 25 | import java.util.concurrent.TimeUnit 26 | 27 | import rxmongo.bson._ 28 | 29 | import scala.concurrent.duration._ 30 | 31 | sealed trait WriteConcernKind 32 | case object NoAcknowledgmentWC extends WriteConcernKind { override def toString = "-1" } 33 | case object ErrorsOnlyWC extends WriteConcernKind { override def toString = "0" } 34 | case object BasicAcknowledgmentWC extends WriteConcernKind { override def toString = "1" } 35 | case object MajorityWC extends WriteConcernKind { override def toString = "majority" } 36 | case class WaitForMembersWC(numMembers : Int) extends WriteConcernKind { override def toString = numMembers.toString } 37 | case class MembersWithTagWC(tag : String) extends WriteConcernKind { override def toString = tag } 38 | 39 | /** MongoDB Write Concern 40 | * This represents the WriteConcern values that are part of driver, client, database, collection 41 | * and write operation specifications. The Write Concern controls isolation and durability requirements for a write. 42 | * @see [[http://docs.mongodb.org/master/reference/write-concern/]] 43 | * @param kind How writes should be done. 44 | * @param timeout The timeout for the replica set (when WaitForMembersWC.numMbers > 1) to complete the write. 45 | * @param journal Whether journaling of the write must be finished before return (guarantees durability) 46 | */ 47 | case class WriteConcern( 48 | kind : WriteConcernKind, 49 | timeout : FiniteDuration, 50 | journal : Boolean) extends BSONProvider { 51 | def wrapAndTerminate = { WriteConcern.Codec.write(this) } 52 | override def toBSONObject = { BSONObject(WriteConcern.Codec.write(this)) } 53 | } 54 | 55 | object WriteConcern { 56 | val default = WriteConcern() 57 | 58 | private def int2WCK(i : Int) : WriteConcernKind = { 59 | i match { 60 | case -1 ⇒ NoAcknowledgmentWC 61 | case 0 ⇒ ErrorsOnlyWC 62 | case 1 ⇒ BasicAcknowledgmentWC 63 | case x : Int ⇒ WaitForMembersWC(x) 64 | } 65 | } 66 | 67 | private def str2WCK(s : String) : WriteConcernKind = { 68 | s match { 69 | case "majority" ⇒ MajorityWC 70 | case tag : String if tag.length > 0 ⇒ MembersWithTagWC(tag) 71 | case _ ⇒ BasicAcknowledgmentWC 72 | } 73 | } 74 | 75 | implicit object Codec extends DocumentCodec[WriteConcern] { 76 | val size : Int = +4 + 3 + 4 + 10 + 1 + 3 77 | def write(value : WriteConcern, bldr : BSONBuilder) : BSONBuilder = { 78 | bldr.sizeHint(bldr.length + size) 79 | value.kind match { 80 | case NoAcknowledgmentWC ⇒ bldr.integer("w", -1) 81 | case ErrorsOnlyWC ⇒ bldr.integer("w", 0) 82 | case BasicAcknowledgmentWC ⇒ bldr.integer("w", 1) 83 | case MajorityWC ⇒ bldr.string("w", "majority") 84 | case WaitForMembersWC(num) ⇒ bldr.integer("w", num) 85 | case MembersWithTagWC(tag) ⇒ bldr.string("w", tag) 86 | case k ⇒ throw new RxMongoError(s"Invalid WriteConcernKind: $k") 87 | } 88 | bldr.integer("wtimeout", value.timeout.toMillis.toInt) 89 | bldr.boolean("j", value.journal) 90 | } 91 | 92 | def read(doc : BSONDocument) : WriteConcern = { 93 | val kind = doc.get("w") match { 94 | case Some((tc, i)) ⇒ 95 | TypeCode(tc) match { 96 | case StringCode ⇒ str2WCK(i.getStr) 97 | case IntegerCode ⇒ int2WCK(i.getInt) 98 | case c ⇒ throw new RxMongoError(s"Invalid TypeCode for WriteConcernKind: $c") 99 | } 100 | case _ ⇒ throw new NoSuchElementException("w") 101 | } 102 | WriteConcern(kind, Duration(doc.asInt("wtimeout"), TimeUnit.MILLISECONDS), doc.asBoolean("j")) 103 | } 104 | } 105 | 106 | def apply(str : String = "", timeout : FiniteDuration = 10.seconds, journal : Boolean = false) : WriteConcern = { 107 | val kind = str.trim match { 108 | case num : String if num.length > 0 && (num forall { ch ⇒ ch.isDigit | ch == '-' }) ⇒ 109 | num.toInt match { 110 | case -1 ⇒ NoAcknowledgmentWC 111 | case 0 ⇒ ErrorsOnlyWC 112 | case 1 ⇒ BasicAcknowledgmentWC 113 | case x : Int ⇒ WaitForMembersWC(x) 114 | } 115 | case "majority" ⇒ MajorityWC 116 | case tag : String if tag.length > 0 ⇒ MembersWithTagWC(tag) 117 | case _ ⇒ BasicAcknowledgmentWC 118 | } 119 | WriteConcern(kind, timeout, journal) 120 | } 121 | 122 | def apply(wck : WriteConcernKind) : WriteConcern = WriteConcern(wck, 10.seconds, journal = false) 123 | 124 | def apply(wck : WriteConcernKind, timeout : FiniteDuration) : WriteConcern = WriteConcern(wck, timeout, journal = false) 125 | } 126 | -------------------------------------------------------------------------------- /messages/src/main/scala/rxmongo/messages/cmds/AuthenticationCommands.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.messages.cmds 24 | 25 | import rxmongo.bson.BSONObject 26 | import rxmongo.messages.{ AuthMechanism, Command } 27 | 28 | /** logout 29 | * @see [[http://docs.mongodb.org/master/reference/command/logout]] 30 | * @param db 31 | */ 32 | case class LogoutCmd(db : String) extends Command(db, BSONObject("logout" → 1)) 33 | 34 | /** authenticate 35 | * Starts an authenticated session using a username and password. 36 | * @see [[http://docs.mongodb.org/master/reference/command/authenticate/#dbcmd.authenticate]] 37 | * @param db 38 | * @param user 39 | * @param pass 40 | * @param mechanism 41 | */ 42 | case class AuthenticateCmd( 43 | db : String, 44 | user : String, 45 | pass : String, 46 | mechanism : AuthMechanism) extends Command(db, BSONObject( 47 | "authenticate" -> 1, "username" -> user, "password" -> pass, "mechanism" -> mechanism.asStr) 48 | ) 49 | 50 | /** copydbgetnonce 51 | * This is an internal command to generate a one-time password for use with the copydb command. 52 | * @see [[http://docs.mongodb.org/master/reference/command/copydbgetnonce/#dbcmd.copydbgetnonce]] 53 | * @param db 54 | */ 55 | case class CopyDbGetNonceCmd( 56 | db : String) extends Command(db, BSONObject("copydbgetnonce" -> 1)) 57 | 58 | /** getnonce 59 | * This is an internal command to generate a one-time password for authentication. 60 | * @see [[http://docs.mongodb.org/master/reference/command/getnonce/#dbcmd.getnonce]] 61 | * @param db The name of the database 62 | */ 63 | case class GetNonceCmd( 64 | db : String) extends Command(db, BSONObject("getnonce" -> 1)) 65 | 66 | -------------------------------------------------------------------------------- /messages/src/main/scala/rxmongo/messages/cmds/GridFSCommands.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.messages.cmds 24 | 25 | import rxmongo.bson.{ BSONObject, BSONObjectID } 26 | import rxmongo.messages.AdminCommand 27 | 28 | /** filemd5 29 | * @see [[http://docs.mongodb.org/master/reference/command/filemd5/]] 30 | * @param fileMD5 The ObjectID of the file 31 | * @param root The root 32 | */ 33 | case class FileMD5Cmd(fileMD5 : BSONObjectID, root : String) extends AdminCommand( 34 | BSONObject("filemd5" → BSONObjectID, "root" → root) 35 | ) 36 | -------------------------------------------------------------------------------- /messages/src/main/scala/rxmongo/messages/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo 24 | 25 | import rxmongo.bson.Codec 26 | 27 | import scala.language.implicitConversions 28 | 29 | package object messages { 30 | 31 | implicit def booleanFieldExpression(fieldName : String) : BooleanFieldExpression = 32 | new BooleanFieldExpression(fieldName) 33 | 34 | implicit def logicalExpression(exp1 : BooleanExpression) : LogicalExpression = 35 | new LogicalExpression(exp1) 36 | 37 | implicit def pairLiteral[T](pair : (String, T))(implicit codec : Codec[T]) : BooleanExpression = { 38 | new BooleanFieldExpression(pair._1).$eq[T](pair._2) 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /messages/src/main/scala/rxmongo/messages/replies/BuildInfoReply.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.messages.replies 24 | 25 | import rxmongo.bson.{BSONInteger, BSONDocument, BSONBuilder, DocumentCodec} 26 | 27 | /** Reply From The BuildInfo Command 28 | * 29 | * Prototype: 30 | * {{{ 31 | * { 32 | "version" : "", 33 | "gitVersion" : "", 34 | "sysInfo" : "", 35 | "loaderFlags" : "", 36 | "compilerFlags" : "", 37 | "allocator" : "", 38 | "versionArray" : [ , , <...> ], 39 | "javascriptEngine" : "", 40 | "bits" : , 41 | "debug" : , 42 | "maxBsonObjectSize" : , 43 | "ok" : 44 | } 45 | * }}} 46 | */ 47 | case class BuildInfoReply( 48 | version: String, 49 | gitVersion: String, 50 | sysInfo: String, 51 | loaderFlags : String, 52 | compilerFlags : String, 53 | allocator : String, 54 | versionArray : Array[Int], 55 | javascriptEngine : String, 56 | bits: Int, 57 | debug : Boolean, 58 | maxBsonObjectSize : Int 59 | ) 60 | 61 | object BuildInfoReply { 62 | implicit object BuildInfoCodec extends DocumentCodec[BuildInfoReply] { 63 | /** Write BuildInfo into BSONBulder 64 | * 65 | * @param value The value, T, to be written to BSON 66 | * @return A Try[BSONValue] resulting from writing T to BSON 67 | */ 68 | override def write(value : BuildInfoReply, b : BSONBuilder) : BSONBuilder = { 69 | b.string("version", value.version) 70 | b.string("gitVersion", value.gitVersion) 71 | b.string("sysInfo", value.sysInfo) 72 | b.string("loaderFlags", value.loaderFlags) 73 | b.string("compilerFlags", value.compilerFlags) 74 | b.string("allocator", value.allocator) 75 | b.array("versionArray", value.versionArray) 76 | b.string("javascriptEngine", value.javascriptEngine) 77 | b.integer("bits", value.bits) 78 | b.boolean("debug", value.debug) 79 | b.integer("maxBsonObjectSize", value.maxBsonObjectSize) 80 | b 81 | } 82 | 83 | /** Convert BSONValue Into T 84 | * 85 | * @param value The BSONValue to be converted 86 | * @return A Try[T] that results from reading T from BSON 87 | */ 88 | override def read(value : BSONDocument) : BuildInfoReply = { 89 | BuildInfoReply( 90 | value.asString("version"), 91 | value.asString("gitVersion"), 92 | value.asString("sysInfo"), 93 | value.asString("loaderFlags"), 94 | value.asString("compilerFlags"), 95 | value.asString("allocator"), 96 | value.asArray("versionArray").asArray.map { bv => bv.value.asInstanceOf[Integer].toInt }, 97 | value.asString("javascriptEngine"), 98 | value.asInt("bits"), 99 | value.asBoolean("debug"), 100 | value.asInt("maxBsonObjectSize") 101 | ) 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /messages/src/main/scala/rxmongo/messages/replies/IsMasterReply.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.messages.replies 24 | 25 | import java.util.Date 26 | 27 | import rxmongo.bson._ 28 | 29 | /** Represents The IsMasterCmd Response From Mongo 30 | * 31 | * The optional parameters will not occur if the response came from a server that is not part of a replica set. 32 | * @param ok Has value 1 if the content is "ok" or an error otherwise 33 | * @param ismaster True if the responding server is the primary, master or only server 34 | * @param maxBsonObjectSize Maximum size of an object the server will accept 35 | * @param maxMessageSizeBytes Maximum size of a message the server will accept 36 | * @param maxWriteBatchSize Maximum number of write messages that can be batched 37 | * @param localTime The local time of the server in ISODate UTC 38 | * @param maxWireVersion The maximum wire version the server will accept 39 | * @param minWireVersion The minimum wire version the server will accept 40 | * @param secondary 41 | * @param primary 42 | * @param setName 43 | * @param hosts 44 | * @param setVersion 45 | * @param me 46 | * @param passives 47 | * @param arbiters 48 | * @param arbiterOnly 49 | * @param passive 50 | * @param hidden 51 | * @param tags 52 | * @param electionId 53 | */ 54 | case class IsMasterReply( 55 | ok : Double = 0, 56 | ismaster : Boolean = false, 57 | maxBsonObjectSize : Int = 0, 58 | maxMessageSizeBytes : Int = 0, 59 | maxWriteBatchSize : Int = 0, 60 | localTime : Date = new Date(0), 61 | maxWireVersion : Int = 0, 62 | minWireVersion : Int = 0, 63 | secondary : Option[Boolean] = None, 64 | primary : Option[String] = None, 65 | setName : Option[String] = None, 66 | setVersion : Option[Int] = None, 67 | me : Option[String] = None, 68 | hosts : Option[Iterable[String]] = None, 69 | passives : Option[Iterable[String]] = None, 70 | arbiters : Option[Iterable[String]] = None, 71 | arbiterOnly : Option[Boolean] = None, 72 | passive : Option[Boolean] = None, 73 | hidden : Option[Boolean] = None, 74 | tags : Option[Map[String, String]] = None, 75 | electionId : Option[Long] = None) 76 | 77 | object IsMasterReply { 78 | implicit object IsMasterReplyCodec extends DocumentCodec[IsMasterReply] { 79 | /** Convert T into BSONValue 80 | * 81 | * @param value The value, T, to be written to BSON 82 | * @return A Try[BSONValue] resulting from writing T to BSON 83 | */ 84 | override def write(value : IsMasterReply, bldr : BSONBuilder) : BSONBuilder = { 85 | bldr.double("ok", value.ok) 86 | bldr.boolean("ismaster", value.ismaster) 87 | bldr.integer("maxBsonObjectSize", value.maxBsonObjectSize) 88 | bldr.integer("maxMessageSizeBytes", value.maxMessageSizeBytes) 89 | bldr.integer("maxWriteBatchSize", value.maxWriteBatchSize) 90 | bldr.date("localTime", value.localTime) 91 | bldr.integer("maxWireVersion", value.maxWireVersion) 92 | bldr.integer("minWireVersion", value.minWireVersion) 93 | value.secondary.map { v ⇒ bldr.boolean("secondary", v) } 94 | value.primary.map { v ⇒ bldr.string("primary", v) } 95 | value.setName.map { v ⇒ bldr.string("setName", v) } 96 | value.setVersion.map { v ⇒ bldr.integer("setVersion", v) } 97 | value.me.map { v ⇒ bldr.string("me", v) } 98 | value.hosts.map { v ⇒ bldr.array("hosts", v) } 99 | value.passives.map { v ⇒ bldr.array("passives", v) } 100 | value.arbiters.map { v ⇒ bldr.array("arbiters", v) } 101 | value.arbiterOnly.map { v ⇒ bldr.boolean("arbiterOnly", v) } 102 | value.passive.map { v ⇒ bldr.boolean("passive", v) } 103 | value.hidden.map { v ⇒ bldr.boolean("hidden", v) } 104 | value.tags.map { v ⇒ bldr.obj("tags", v) } 105 | value.electionId.map { v ⇒ bldr.long("electionId", v) } 106 | bldr 107 | } 108 | 109 | /** Convert BSONValue Into T 110 | * 111 | * @param value The BSONValue to be converted 112 | * @return A Try[T] that results from reading T from BSON 113 | */ 114 | override def read(doc : BSONDocument) : IsMasterReply = { 115 | IsMasterReply( 116 | doc.asDoubleOrElse("ok", 0.0D), 117 | doc.asBooleanOrElse("ismaster", d = false), 118 | doc.asIntOrElse("maxBsonObjectSize", 0), 119 | doc.asIntOrElse("maxMessageSizeBytes", 0), 120 | doc.asIntOrElse("maxWriteBatchSize", 0), 121 | doc.asDateOrElse("localTime", new Date(0)), 122 | doc.asIntOrElse("maxWireVersion", 0), 123 | doc.asIntOrElse("minWireVersion", 0), 124 | doc.asOptionalBoolean("secondary"), 125 | doc.asOptionalString("primary"), 126 | doc.asOptionalString("setName"), 127 | doc.asOptionalInt("setVersion"), 128 | doc.asOptionalString("me"), 129 | doc.asOptionalArray[String]("hosts"), 130 | doc.asOptionalArray[String]("passives"), 131 | doc.asOptionalArray[String]("arbiters"), 132 | doc.asOptionalBoolean("arbiterOnly"), 133 | doc.asOptionalBoolean("passive"), 134 | doc.asOptionalBoolean("hidden"), 135 | doc.asOptionalMap[String]("tags"), 136 | doc.asOptionalLong("electionId") 137 | ) 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /messages/src/main/scala/rxmongo/messages/replies/WriteResult.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.messages.replies 24 | 25 | import rxmongo.bson._ 26 | 27 | case class WriteError(index : Int, code : Int, errmsg : String) 28 | 29 | object WriteError { 30 | implicit object Codec extends DocumentCodec[WriteError] { 31 | override def write(value : WriteError, bldr : BSONBuilder) : BSONBuilder = { 32 | bldr. 33 | integer("index", value.index). 34 | integer("code", value.code). 35 | string("errmsg", value.errmsg) 36 | } 37 | override def read(doc : BSONDocument) : WriteError = { 38 | WriteError(doc.asInt("index"), doc.asInt("code"), doc.asString("errmsg")) 39 | } 40 | } 41 | } 42 | 43 | case class WriteConcernError(code : Int, errmsg : String) 44 | 45 | object WriteConcernError { 46 | implicit object Codec extends DocumentCodec[WriteConcernError] { 47 | override def write(value : WriteConcernError, bldr : BSONBuilder) : BSONBuilder = { 48 | bldr. 49 | integer("code", value.code). 50 | string("errmsg", value.errmsg) 51 | } 52 | override def read(doc : BSONDocument) : WriteConcernError = { 53 | WriteConcernError(doc.asInt("code"), doc.asString("errmsg")) 54 | } 55 | } 56 | } 57 | 58 | case class WriteResult private[rxmongo] (doc : BSONDocument) { 59 | val ok : Int = doc.asInt("ok") 60 | lazy val n : Int = doc.asInt("n") 61 | lazy val writeErrors = doc.asOptionalArray[WriteError]("writeErrors") 62 | lazy val writeConcernError = doc.asOptionalObjectOfType[WriteConcernError]("writeConcernError") 63 | } 64 | 65 | case class BulkWriteResult private[rxmongo] (doc : BSONDocument) { 66 | lazy val nInserted : Int = doc.asInt("nInserted") 67 | lazy val nMatched : Int = doc.asInt("nMatched") 68 | lazy val nModified : Int = doc.asInt("nModified") 69 | lazy val nRemoved : Int = doc.asInt("nRemoved") 70 | lazy val nUpserted : Int = doc.asInt("nUpserted") 71 | lazy val upserted = doc.asOptionalSeq[BSONObject]("upserted") 72 | lazy val writeErrors = doc.asOptionalSeq[WriteError]("writeErrors") 73 | lazy val writeConcernErrors = doc.asOptionalSeq[WriteConcernError]("writeConcernErrors") 74 | } 75 | -------------------------------------------------------------------------------- /messages/src/test/scala/rxmongo/messages/CommandsSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.messages 24 | 25 | import org.specs2.mutable.Specification 26 | import rxmongo.bson._ 27 | import rxmongo.messages.cmds.FindAndModifyCmd 28 | 29 | class CommandsSpec extends Specification { 30 | 31 | sequential 32 | 33 | "Commands" should { 34 | "print themselves out" in { 35 | val cmd = new Command("db", BSONObject("cmd" → 1)) 36 | cmd.toString.matches("Command\\(opcode=OP_QUERY,requestId=\\d+,requiresResponse=true,db=db,options=QueryOptions\\(0,-1,false,false,false,false,false,false\\),selector=\\{ cmd->1 \\},returnFieldsSelector=None\\)") must beTrue 37 | } 38 | "print out case class subclasses" in { 39 | val cmd = FindAndModifyCmd("db", "coll", Some(Query("a" $eq "b").toBSONObject), Seq("a" → true), None, Some(true)) 40 | cmd.toString.matches("FindAndModifyCmd\\(opcode=OP_QUERY,requestId=\\d+,requiresResponse=true,db=db,options=QueryOptions\\(0,-1,false,false,false,false,false,false\\),selector=\\{ findAndModify->coll, query->\\{ \\$query->\\{ a->b \\} \\}, remove->true \\},returnFieldsSelector=None\\)") 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /messages/src/test/scala/rxmongo/messages/DeleteSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.messages 24 | 25 | import org.specs2.mutable.Specification 26 | import rxmongo.bson._ 27 | 28 | class DeleteSpec extends Specification { 29 | 30 | "Delete" should { 31 | "construction from boolean expression" in { 32 | Delete("a" $ne "b", 1) must beEqualTo(Delete(BSONObject("a" → BSONObject("$ne" -> "b")), 1)) 33 | } 34 | "construct from Query" in { 35 | Delete(Query("a" $ne "b"), 1) must beEqualTo(Delete( 36 | BSONObject("$query" → BSONObject("a" → BSONObject("$ne" -> "b"))), 1)) 37 | } 38 | "produce correct BSONObject" in { 39 | val bs = Delete.Codec.write(Delete("a" $ne "b", 1)) 40 | val obj = BSONObject(bs) 41 | obj must beEqualTo(BSONObject("q" → BSONObject("a" → BSONObject("$ne" -> "b")), "limit" → 1) 42 | ) 43 | } 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /messages/src/test/scala/rxmongo/messages/IndicesSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.messages 24 | 25 | import org.specs2.mutable.Specification 26 | import rxmongo.bson._ 27 | 28 | /** Test Case For Indices module */ 29 | class IndicesSpec extends Specification { 30 | 31 | "WireTigerConfig" should { 32 | "construct from an object" in { 33 | val wtc = WiredTigerConfig(BSONObject("config" -> "value")) 34 | wtc.result must beEqualTo(BSONObject("WiredTiger" -> BSONObject("config" -> "value"))) 35 | } 36 | 37 | } 38 | 39 | "MMapV1Config" should { 40 | "construct from an object" in { 41 | val wtc = MMapV1Config(BSONObject("config" -> "value")) 42 | wtc.result must beEqualTo(BSONObject("mmapv1" -> BSONObject("config" -> "value"))) 43 | } 44 | } 45 | 46 | "IndexOptions" should { 47 | "construct with all default arguments" in { 48 | val io = IndexOptions() 49 | io.result must beEqualTo(BSONObject()) 50 | } 51 | "construct with some arguments defaulted" in { 52 | val io = IndexOptions(sparse = Some(true), unique = Some(false)) 53 | io.result must beEqualTo(BSONObject("unique" -> false, "sparse" -> true)) 54 | } 55 | "construct with all arguments specified" in { 56 | val io = IndexOptions(Some(true), Some(false), Some(true), Some("name"), Some(10), 57 | Some(MMapV1Config(BSONObject("a" -> "b"))), 58 | Seq("foo" -> 1, "bar" -> 10), Some("en"), Some("lang"), 59 | bits = Some(1), min = Some(1.0), max = Some(100.0), bucketSize = Some(32.0)) 60 | io.result must beEqualTo(BSONObject( 61 | "unique" -> true, "sparse" -> false, "background" -> true, "name" -> "name", 62 | "expireAfterSeconds" -> 10, "storageEngine" -> BSONObject("mmapv1" -> BSONObject("a" -> "b")), 63 | "weights" -> BSONObject("foo" -> 1, "bar" -> 10), "default_language" -> "en", 64 | "language_override" -> "lang", "bits" -> 1, "min" -> 1.0, "max" -> 100.0, 65 | "bucketSize" -> 32.0 66 | )) 67 | } 68 | } 69 | 70 | "Index" should { 71 | "construct with a simple field" in { 72 | val i = Index("name", ascending = true) 73 | i.result must beEqualTo(BSONObject("name" -> 1)) 74 | } 75 | } 76 | 77 | "CompoundIndex" should { 78 | "construct with a sequence of pairs" in { 79 | val ci = CompoundIndex("name" -> true, "score" -> false) 80 | ci.result must beEqualTo(BSONObject("name" -> 1, "score" -> -1)) 81 | } 82 | } 83 | 84 | "TextIndex" should { 85 | "construct with a sequence of field names" in { 86 | val ti = TextIndex("a", "b", "c") 87 | ti.result must beEqualTo(BSONObject("a" -> "text", "b" -> "text", "c" -> "text")) 88 | } 89 | } 90 | 91 | "HashedIndex" should { 92 | "construct with a single field name" in { 93 | val hi = HashedIndex("a") 94 | hi.result must beEqualTo(BSONObject("a" -> "hashed")) 95 | } 96 | } 97 | 98 | "TwoDIndex" should { 99 | "construct with a single location field" in { 100 | val twodi = TwoDIndex("a") 101 | twodi.result must beEqualTo(BSONObject("a" -> "2d")) 102 | } 103 | "construct with a location and other fields" in { 104 | val twodi = TwoDIndex("a", "b" -> true, "c" -> false) 105 | twodi.result must beEqualTo(BSONObject("a" -> "2d", "b" -> 1, "c" -> -1)) 106 | } 107 | } 108 | 109 | "TwoDSphereIndex" should { 110 | "construct with a single location field" in { 111 | val twodsi = TwoDSphereIndex(locationField = "a") 112 | twodsi.result must beEqualTo(BSONObject("a" -> "2dsphere")) 113 | } 114 | "construct with a prefix and a location" in { 115 | val twodsi = TwoDSphereIndex(Seq("a" -> true), "b") 116 | twodsi.result must beEqualTo(BSONObject("a" -> 1, "b" -> "2dsphere")) 117 | } 118 | "construct wiht a prefix, location and suffix" in { 119 | val twodsi = TwoDSphereIndex(Seq("a" -> true), "b", Seq("c" -> false)) 120 | twodsi.result must beEqualTo(BSONObject("a" -> 1, "b" -> "2dsphere", "c" -> -1)) 121 | } 122 | } 123 | "GeoHaystackIndex" should { 124 | "construct with a single location field" in { 125 | val gh = GeoHaystackIndex("a") 126 | gh.result must beEqualTo(BSONObject("a" -> "geoHaystack")) 127 | } 128 | "construct with a location and other fields" in { 129 | val gh = GeoHaystackIndex("a", "b" -> true, "c" -> false) 130 | gh.result must beEqualTo(BSONObject("a" -> "geoHaystack", "b" -> 1, "c" -> -1)) 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /messages/src/test/scala/rxmongo/messages/OperatorsSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.messages 24 | 25 | import java.util.Date 26 | 27 | import org.specs2.mutable.Specification 28 | import rxmongo.bson._ 29 | 30 | class OperatorsSpec extends Specification { 31 | 32 | val foo_str = "foo" $eq "bar" 33 | val foo_str_equiv = BSONObject("foo" -> "bar") 34 | val foo_str2 = "foo" $eq "baz" 35 | val foo_str2_equiv = BSONObject("foo" → "baz") 36 | val foo_bool = "foo" $ne false 37 | val foo_bool_equiv = BSONObject("foo" -> BSONObject("$ne" -> false)) 38 | val foo_int = "foo" $gt 23 39 | val foo_int_equiv = BSONObject("foo" -> BSONObject("$gt" -> 23)) 40 | val foo_date = "foo" $lt new Date(42) 41 | val foo_date_equiv = BSONObject("foo" -> BSONObject("$lt" -> new Date(42))) 42 | val foo_dbl = "foo" $gte 42.0 43 | val foo_dbl_equiv = BSONObject("foo" -> BSONObject("$gte" -> 42.0)) 44 | val foo_null = "foo" $lte Seq("bar") 45 | val foo_null_equiv = BSONObject("foo" -> BSONObject("$lte" -> BSONArray("bar"))) 46 | 47 | "Operators" should { 48 | "construct $eq properly" in { 49 | foo_str.result must beEqualTo (foo_str_equiv) 50 | } 51 | 52 | "construct $ne properly" in { 53 | foo_bool.result must beEqualTo(foo_bool_equiv) 54 | } 55 | 56 | "construct $gt properly" in { 57 | foo_int.result must beEqualTo(foo_int_equiv) 58 | } 59 | 60 | "construct $lt properly" in { 61 | foo_date.result must beEqualTo(foo_date_equiv) 62 | } 63 | 64 | "construct $gte properly" in { 65 | foo_dbl.result must beEqualTo(foo_dbl_equiv) 66 | } 67 | 68 | "construct $lte properly" in { 69 | foo_null.result must beEqualTo(foo_null_equiv) 70 | } 71 | 72 | "construct $in properly" in { 73 | val query = "foo" $in ("bar", "baz", "bong") 74 | val obj = query.result 75 | val equiv = BSONObject("foo" -> BSONObject("$in" -> BSONArray("bar", "baz", "bong"))) 76 | obj must beEqualTo(equiv) 77 | } 78 | 79 | "construct $nin properly" in { 80 | val query = "foo" $nin ("bar", "baz", "bong") 81 | val obj = query.result 82 | val equiv = BSONObject("foo" -> BSONObject("$nin" -> BSONArray("bar", "baz", "bong"))) 83 | obj must beEqualTo(equiv) 84 | } 85 | 86 | "construct infix $and properly" in { 87 | val obj = foo_str $and foo_str2 88 | obj.result must beEqualTo(BSONObject("$and" -> BSONArray(foo_str_equiv, foo_str2_equiv))) 89 | } 90 | 91 | "construct prefix $and properly" in { 92 | val obj = $and(foo_str, foo_bool, foo_int, foo_date, foo_dbl) 93 | obj.result must beEqualTo(BSONObject("$and" -> BSONArray( 94 | foo_str_equiv, foo_bool_equiv, foo_int_equiv, foo_date_equiv, foo_dbl_equiv 95 | ))) 96 | } 97 | } 98 | 99 | "UpdateExpression" should { 100 | "construct $inc correctly" in { 101 | val e = $inc("a", 1) 102 | e.result must beEqualTo(BSONObject("$inc" → BSONObject("a" → 1))) 103 | } 104 | "construct $mul correctly" in { 105 | val e = $mul("b", 42.0) 106 | e.result must beEqualTo(BSONObject("$mul" → BSONObject("b" → 42.0))) 107 | } 108 | "join two expression with +" in { 109 | val e = $inc("a", 1) + $mul("b", 42.0) 110 | e.result must beEqualTo(BSONObject( 111 | "$inc" → BSONObject("a" → 1), 112 | "$mul" → BSONObject("b" → 42.0) 113 | )) 114 | } 115 | 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /messages/src/test/scala/rxmongo/messages/ProjectionSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.messages 24 | 25 | import org.specs2.mutable.Specification 26 | import rxmongo.bson.BSONObject 27 | 28 | class ProjectionSpec extends Specification { 29 | 30 | "Projection" should { 31 | "construct from except" in { 32 | val p1 = Projection.except("one") 33 | p1.result must beEqualTo(BSONObject("one" → 0)) 34 | val p2 = Projection.except("one", "two") 35 | p2.result must beEqualTo(BSONObject("one" → 0, "two" → 0)) 36 | } 37 | 38 | "construct from only" in { 39 | val p1 = Projection.only("one") 40 | p1.result must beEqualTo(BSONObject("one" → 1, "_id" → 0)) 41 | val p2 = Projection.only("one", "two") 42 | p2.result must beEqualTo(BSONObject("one" → 1, "two" → 1, "_id" → 0)) 43 | } 44 | "construct from specific" in { 45 | val p1 = Projection.specific("one" → true) 46 | p1.result must beEqualTo(BSONObject("one" → 1)) 47 | val p2 = Projection.specific("one" → true, "two" → false) 48 | p2.result must beEqualTo(BSONObject("one" → 1, "two" → 0)) 49 | } 50 | "allow includes" in { 51 | val p1 = Projection() 52 | p1.result must beEqualTo(BSONObject()) 53 | p1.include("a", "b", "c") 54 | p1.result must beEqualTo(BSONObject("a" → 1, "b" → 1, "c" → 1)) 55 | 56 | } 57 | "allow excludes" in { 58 | val p1 = Projection() 59 | p1.result must beEqualTo(BSONObject()) 60 | p1.exclude("a", "b", "c") 61 | p1.result must beEqualTo(BSONObject("a" → 0, "b" → 0, "c" → 0)) 62 | } 63 | "allow slice" in { 64 | pending 65 | } 66 | "allow sliceFromStart" in { 67 | pending 68 | } 69 | "allow sliceFromEnd" in { 70 | pending 71 | } 72 | "allow metaTextSearch" in { 73 | pending 74 | } 75 | "allow elemMatch" in { 76 | pending 77 | } 78 | "allow positional" in { 79 | pending 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /messages/src/test/scala/rxmongo/messages/QuerySpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.messages 24 | 25 | import org.specs2.mutable.Specification 26 | import rxmongo.bson._ 27 | 28 | class QuerySpec extends Specification with ByteStringTestUtils { 29 | 30 | "Query" should { 31 | "construct valid byte string" in { pending } 32 | /** TODO: Validate Query against byte string 33 | * val q = Query("a" $eq "b") 34 | * val embedded : ByteString 35 | * val bytes : ByteString = { 36 | * val builder = preamble(21, 1, "$query") 37 | * builder.putDouble(data) // double value 38 | * builder.putByte(0) // terminating null 39 | * builder.result() 40 | * } 41 | * 42 | * } 43 | */ 44 | 45 | "accept a simple selector in constructor" in { 46 | val q = Query("a" $eq "b") 47 | q.result must beEqualTo(BSONObject("$query" → BSONObject("a" → "b"))) 48 | } 49 | 50 | "accept a complex selector" in { 51 | val q = Query((("a" $ne 42.0) $and ("b" $gte 21.0)) $or ("c" $lt 84.0)) 52 | q.result must beEqualTo(BSONObject( 53 | "$query" → BSONObject("$or" → 54 | BSONArray( 55 | BSONObject("$and" → 56 | BSONArray( 57 | BSONObject("a" → BSONObject("$ne" → 42.0)), 58 | BSONObject("b" → BSONObject("$gte" → 21.0)) 59 | ) 60 | ), 61 | BSONObject("c" → BSONObject("$lt" → 84.0)) 62 | ) 63 | ) 64 | )) 65 | } 66 | 67 | "handle ascending sorting on one field" in { 68 | val q = Query("a" $eq "b").orderBy("c") 69 | q.result must beEqualTo(BSONObject("$query" → BSONObject("a" → "b"), "$orderby" → BSONObject("c" → 1))) 70 | } 71 | 72 | "handle descending sorting on one field" in { 73 | val q = Query("a" $eq "b").orderBy("c", ascending = false) 74 | q.result must beEqualTo(BSONObject("$query" → BSONObject("a" → "b"), "$orderby" → BSONObject("c" → -1))) 75 | } 76 | 77 | "handle adding a comment" in { 78 | val q = Query("a" $eq "b").comment("This is for profiling") 79 | q.result must beEqualTo(BSONObject("$query" → BSONObject("a" → "b"), "$comment" → "This is for profiling")) 80 | } 81 | 82 | "handle adding an index hint" in { 83 | val q = Query("a" $eq "b").hint("_id") 84 | q.result must beEqualTo(BSONObject("$query" → BSONObject("a" → "b"), "$hint" → BSONObject("_id" → 1))) 85 | 86 | } 87 | 88 | "handle maxScan(num_docs)" in { 89 | pending 90 | } 91 | 92 | "handle maxTimeMS(millis)" in { 93 | pending 94 | } 95 | 96 | "handle max(fields)" in { 97 | pending 98 | } 99 | "handle min(fields)" in { 100 | pending 101 | } 102 | 103 | "handle returnKey()" in { 104 | pending 105 | } 106 | 107 | "handle showDiskLoc()" in { 108 | pending 109 | } 110 | 111 | "handle snapshot()" in { 112 | pending 113 | } 114 | 115 | "handle natural()" in { 116 | pending 117 | } 118 | 119 | "handle complex query construction" in { 120 | val q = Query("a" $eq "b"). 121 | comment("foo"). 122 | hint("a"). 123 | maxScan(1000). 124 | maxTimeMS(1000). 125 | max("count" → 10). 126 | min("count" → 1). 127 | orderBy("count"). 128 | returnKey(). 129 | showDiskLoc(). 130 | snapshot(). 131 | natural() 132 | val actual = q.result 133 | val expected = BSONObject( 134 | "$query" → BSONObject("a" → "b"), 135 | "$comment" → "foo", 136 | "$hint" → BSONObject("a" → 1), 137 | "$maxScan" → 1000, 138 | "$maxTimeMS" → 1000L, 139 | "$max" → BSONObject("count" → 10), 140 | "$min" → BSONObject("count" → 1), 141 | "$orderby" → BSONObject("count" → 1), 142 | "$returnKey" → true, 143 | "$showDiskLoc" → true, 144 | "$snapshot" → true, 145 | "$natural" → 1 146 | ) 147 | 148 | actual must beEqualTo(expected) 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /messages/src/test/scala/rxmongo/messages/UpdateSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.messages 24 | 25 | import org.specs2.mutable.Specification 26 | 27 | import rxmongo.bson._ 28 | 29 | class UpdateSpec extends Specification { 30 | 31 | "Update" should { 32 | "construct from BooleanExpression" in { 33 | Update("a" $ne "b", $set("foo" → "bar"), upsert = true, multi = false, isolated = false) must beEqualTo( 34 | Update(BSONObject("a" → BSONObject("$ne" → "b")), BSONObject("$set" → BSONObject("foo" → "bar")), true, false, false) 35 | ) 36 | } 37 | 38 | "construct from Query" in { 39 | val actual = Update(Query("a" $ne "b"), $set("foo" → "bar"), upsert = true, multi = false, isolated = false) 40 | val expected = Update(BSONObject("$query" → BSONObject("a" → BSONObject("$ne" → "b"))), 41 | BSONObject("$set" → BSONObject("foo" → "bar")), upsert = true, multi = false, isolated = false) 42 | actual must beEqualTo(expected) 43 | } 44 | 45 | "produce correct BSONObject" in { 46 | val bs = Update.Codec.write(Update("a" $ne "b", $set("foo" → "bar"), upsert = true, multi = false, isolated = true)) 47 | val obj = BSONObject(bs) 48 | obj must beEqualTo( 49 | BSONObject( 50 | "q" → BSONObject("a" → BSONObject("$ne" → "b"), "$isolated" → 1), 51 | "u" → BSONObject("$set" → BSONObject("foo" → "bar")), 52 | "upsert" → true, 53 | "multi" → false 54 | ) 55 | ) 56 | } 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /messages/src/test/scala/rxmongo/messages/WriteResultSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package rxmongo.messages 24 | 25 | import org.specs2.mutable.Specification 26 | 27 | /** Test cases for the WriteResult 28 | * 29 | * Description of thing 30 | */ 31 | class WriteResultSpec extends Specification { 32 | 33 | "WriteResult" should { "provide some test cases" in { pending } } 34 | } 35 | -------------------------------------------------------------------------------- /project/Dependencies.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | import sbt._ 24 | import scala.language.postfixOps 25 | 26 | object Dependencies { 27 | 28 | // Akka Stuff 29 | val akkaV = "2.3.12" 30 | val akkaStreamsV = "2.0-M1" 31 | val akka_actor = "com.typesafe.akka" %% "akka-actor" % akkaV 32 | val akka_slf4j = "com.typesafe.akka" %% "akka-slf4j" % akkaV 33 | val akka_testkit = "com.typesafe.akka" %% "akka-testkit" % akkaV 34 | val akka_streams = "com.typesafe.akka" %% "akka-stream-experimental" % akkaStreamsV 35 | val akka_http_core = "com.typesafe.akka" %% "akka-http-core-experimental" % akkaStreamsV 36 | val akka_http = "com.typesafe.akka" %% "akka-http-experimental" % akkaStreamsV 37 | val helpers = "com.reactific" %% "helpers" % "0.1.0" 38 | val hsp = "com.reactific" %% "hotspot-profiler" % "0.3.0" % "test" 39 | val scala_compiler = "org.scala-lang" % "scala-compiler" % "2.11.6" 40 | 41 | val common = Seq(helpers, hsp) 42 | 43 | val bson = common ++ Seq( akka_actor) 44 | 45 | val macros = common ++ Seq( scala_compiler ) 46 | 47 | val messages = common 48 | 49 | val driver = common ++ Seq( akka_streams, akka_actor, akka_testkit, akka_slf4j, akka_http_core) 50 | 51 | val client = common ++ Seq( ) 52 | 53 | val gridfs = common ++ Seq( ) 54 | 55 | val examples = common ++ Seq( ) 56 | } 57 | -------------------------------------------------------------------------------- /project/RxMongo.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | import com.reactific.sbt.ProjectPlugin 24 | import com.reactific.sbt.ProjectPlugin.autoImport._ 25 | import sbt._ 26 | import sbt.Keys._ 27 | import scoverage.ScoverageSbtPlugin 28 | import scala.language.postfixOps 29 | 30 | 31 | object RxMongo extends Build { 32 | 33 | val name = "RxMongo" 34 | 35 | val filter = { (ms: Seq[(File, String)]) => 36 | ms filter { 37 | case (file, path) => 38 | path != "logback.xml" && !path.startsWith("toignore") && !path.startsWith("samples") 39 | } 40 | } 41 | 42 | val classesIgnoredByScoverage : String = Seq[String]( 43 | "rxmongo.examples", 44 | "" // Avoids warnings from scoverage 45 | ).mkString(";") 46 | 47 | val buildSettings: Seq[sbt.Def.Setting[_]] = Defaults.coreDefaultSettings ++ 48 | Seq( 49 | organization := "com.reactific", 50 | copyrightYears := Seq(2015), 51 | copyrightHolder := "Reactific Software LLC", 52 | codePackage := "com.reactific.slickery", 53 | titleForDocs := "Reactific Slick Utilities", 54 | developerUrl := url("http://www.reactific.com/"), 55 | ScoverageSbtPlugin.ScoverageKeys.coverageFailOnMinimum := true, 56 | ScoverageSbtPlugin.ScoverageKeys.coverageExcludedPackages := classesIgnoredByScoverage, 57 | mappings in(Compile, packageBin) ~= filter, 58 | mappings in(Compile, packageSrc) ~= filter, 59 | mappings in(Compile, packageDoc) ~= filter 60 | ) 61 | 62 | 63 | lazy val root = sbt.Project(name, file(".")) 64 | .enablePlugins(ProjectPlugin) 65 | .settings(buildSettings:_*) 66 | .settings( 67 | libraryDependencies ++= Dependencies.examples, 68 | ScoverageSbtPlugin.ScoverageKeys.coverageMinimum := 45 69 | ). 70 | dependsOn(examples, client, driver, messages, bson_deps). 71 | aggregate(bson, messages, driver, client, examples) 72 | 73 | lazy val gridfs = sbt.Project(s"$name-GridFS", file("./gridfs")) 74 | .enablePlugins(ProjectPlugin) 75 | .settings(buildSettings:_*) 76 | .settings( 77 | libraryDependencies ++= Dependencies.gridfs, 78 | ScoverageSbtPlugin.ScoverageKeys.coverageMinimum := 0 79 | ) 80 | .dependsOn(client, bson_deps, driver) 81 | 82 | lazy val client = sbt.Project(s"$name-Client", file("./client")) 83 | .enablePlugins(ProjectPlugin) 84 | .settings(buildSettings:_*) 85 | .settings( 86 | libraryDependencies ++= Dependencies.client, 87 | ScoverageSbtPlugin.ScoverageKeys.coverageMinimum := 35 88 | ) 89 | .dependsOn(bson_deps, driver) 90 | 91 | lazy val driver = sbt.Project(s"$name-Driver", file("./driver")) 92 | .enablePlugins(ProjectPlugin) 93 | .settings(buildSettings:_*) 94 | .settings( 95 | libraryDependencies ++= Dependencies.driver, 96 | ScoverageSbtPlugin.ScoverageKeys.coverageMinimum := 64 97 | ) 98 | .dependsOn(bson_deps, messages) 99 | 100 | /* lazy val macros = 101 | Project(s"${BuildSettings.name}-Macros", file("./macros"), 102 | settings = buildSettings ++ Seq( 103 | libraryDependencies := Dependencies.macros 104 | )). 105 | dependsOn(bson_deps) 106 | */ 107 | lazy val messages = sbt.Project(s"$name-Messages", file("./messages")) 108 | .enablePlugins(ProjectPlugin) 109 | .settings(buildSettings:_*) 110 | .settings( 111 | libraryDependencies ++= Dependencies.messages, 112 | ScoverageSbtPlugin.ScoverageKeys.coverageMinimum := 25 113 | ) 114 | .dependsOn(bson_deps) 115 | 116 | lazy val bson = sbt.Project(s"$name-BSON", file("./bson")) 117 | .enablePlugins(ProjectPlugin) 118 | .settings(buildSettings:_*) 119 | .settings( 120 | libraryDependencies ++= Dependencies.bson, 121 | ScoverageSbtPlugin.ScoverageKeys.coverageMinimum := 50 122 | ) 123 | lazy val bson_deps = bson % "compile->compile;test->test" 124 | 125 | lazy val examples = sbt.Project(s"$name-Examples", file("./examples")) 126 | .enablePlugins(ProjectPlugin) 127 | .settings(buildSettings:_*) 128 | .settings( 129 | libraryDependencies ++= Dependencies.examples, 130 | ScoverageSbtPlugin.ScoverageKeys.coverageMinimum := 0 131 | ). 132 | dependsOn(client,driver) 133 | } 134 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright © 2015 Reactific Software LLC. All Rights Reserved. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in all 12 | # copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | # SOFTWARE. 21 | # 22 | 23 | sbt.version=0.13.9 24 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | // Comment to get more information during initialization 2 | logLevel := Level.Info 3 | 4 | libraryDependencies ++= Seq( "org.slf4j" % "slf4j-simple" % "1.7.12" ) 5 | 6 | addSbtPlugin("com.reactific" % "sbt-project" % "0.3.0" ) 7 | -------------------------------------------------------------------------------- /version.sbt: -------------------------------------------------------------------------------- 1 | version := "0.1.0-SNAPSHOT" 2 | --------------------------------------------------------------------------------