├── .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 |
4 |
5 |
6 |
7 |
8 |
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 |
--------------------------------------------------------------------------------