Skip to content

Commit

Permalink
Setup FindBugs
Browse files Browse the repository at this point in the history
  • Loading branch information
cchantep committed Sep 12, 2016
1 parent 58142d3 commit 2ada6d0
Show file tree
Hide file tree
Showing 31 changed files with 482 additions and 119 deletions.
3 changes: 2 additions & 1 deletion .ci_scripts/unitTests.sh
Expand Up @@ -18,6 +18,8 @@ EOF
)

sbt ++$TRAVIS_SCALA_VERSION ";error ;mimaReportBinaryIssues" || exit 2

sbt ++$TRAVIS_SCALA_VERSION ";project ReactiveMongo-BSON ;findbugs ;project ReactiveMongo-BSON-Macros ;findbugs ;project ReactiveMongo ;findbugs ;project ReactiveMongo-JMX ;findbugs" || exit 3
fi

# JVM/SBT setup
Expand All @@ -36,7 +38,6 @@ TEST_ARGS="$TEST_ARGS ;test-only BSONObjectID -- include unit"
TEST_ARGS="$TEST_ARGS ;test-only MongoURISpec -- include unit"
TEST_ARGS="$TEST_ARGS ;test-only NodeSetSpec -- include unit"
TEST_ARGS="$TEST_ARGS ;test-only reactivemongo.BsonSpec -- include unit"

TEST_ARGS="$TEST_ARGS ;project ReactiveMongo-BSON-Macros ;test-only"

sed -e 's/"-deprecation", //' < project/ReactiveMongo.scala > .tmp && mv .tmp project/ReactiveMongo.scala
Expand Down
27 changes: 27 additions & 0 deletions bson/findbugs-exclude-filters.xml
@@ -0,0 +1,27 @@
<FindBugsFilter>
<Match>
<Class name="~reactivemongo\.bson\.BSONDBPointer\$\$anon.+" />
<Or>
<Bug pattern="EI_EXPOSE_REP" />
<Bug pattern="EI_EXPOSE_REP2" />
</Or>
</Match>

<Match>
<!-- Value class -->
<Class name="reactivemongo.bson.BSONValue$ExtendedBSONValue" />
<Bug pattern="EQ_UNUSUAL" />
</Match>
<Match>
<Class name="reactivemongo.bson.BSONObjectID" />
<Bug pattern="EI_EXPOSE_REP2" />
</Match>
<Match>
<Class name="~reactivemongo\.bson\.BSONValue\$.*" />
<Bug pattern="NM_METHOD_NAMING_CONVENTION" />
</Match>
<Match>
<Class name="reactivemongo.bson.BSONDBPointer" />
<Bug code="BC" />
</Match>
</FindBugsFilter>
10 changes: 5 additions & 5 deletions bson/src/main/scala/buffer.scala
Expand Up @@ -35,11 +35,11 @@ trait WritableBuffer { self =>
/** Writes the bytes stored in the given `buffer` into this buffer. */
def writeBytes(buffer: ReadableBuffer): self.type = {
@annotation.tailrec
def write(buffer: ReadableBuffer): self.type = {
if (buffer.readable > 1024) {
writeBytes(buffer.readArray(1024))
write(buffer)
} else writeBytes(buffer.readArray(buffer.readable))
def write(buf: ReadableBuffer): self.type = {
if (buf.readable > 1024) {
writeBytes(buf.readArray(1024))
write(buf)
} else writeBytes(buf.readArray(buf.readable))
}

write(buffer.slice(buffer.readable))
Expand Down
11 changes: 9 additions & 2 deletions bson/src/main/scala/bufferhandlers.scala
Expand Up @@ -186,10 +186,17 @@ object DefaultBufferHandler extends BufferHandler {
def write(regex: BSONRegex, buffer: WritableBuffer) = { buffer writeCString regex.value; buffer writeCString regex.flags }
def read(buffer: ReadableBuffer) = BSONRegex(buffer.readCString, buffer.readCString)
}

object BSONDBPointerBufferHandler extends BufferRW[BSONDBPointer] {
def write(pointer: BSONDBPointer, buffer: WritableBuffer) = { buffer writeCString pointer.value; buffer writeBytes pointer.id }
def read(buffer: ReadableBuffer) = BSONDBPointer(buffer.readCString, buffer.readArray(12))
def write(pointer: BSONDBPointer, buffer: WritableBuffer) = {
buffer.writeCString(pointer.value)
pointer.withId { buffer.writeBytes(_) }
}

def read(buffer: ReadableBuffer) =
BSONDBPointer(buffer.readCString, buffer.readArray(12))
}

object BSONJavaScriptBufferHandler extends BufferRW[BSONJavaScript] {
def write(js: BSONJavaScript, buffer: WritableBuffer) = buffer writeString js.value
def read(buffer: ReadableBuffer) = BSONJavaScript(buffer.readString)
Expand Down
10 changes: 8 additions & 2 deletions bson/src/main/scala/lowlevel.scala
Expand Up @@ -20,7 +20,10 @@ case class DoubleField(name: String, value: Double) extends Field with ValueFiel
}
case class NoValue(tpe: Byte, name: String) extends Field
case class LongField(tpe: Byte, name: String, value: Long) extends Field with ValueField[Long]
case class StructureField[A <: ReadableBuffer](tpe: Byte, name: String, reader: LowLevelBsonDocReader[A]) extends Field

@SerialVersionUID(587711495L)
case class StructureField[A <: ReadableBuffer](tpe: Byte, name: String, @transient reader: LowLevelBsonDocReader[A]) extends Field

case class LazyField[A <: ReadableBuffer](tpe: Byte, name: String, buffer: A) extends Field

object LoweLevelDocumentIterator extends (ReadableBuffer => Iterator[ReadableBuffer]) {
Expand Down Expand Up @@ -63,7 +66,8 @@ class LowLevelBsonDocReader[A <: ReadableBuffer](rbuf: A) {
def stream(): Stream[Field] = {
val tpe = buf.readByte
val name = buf.readCString
val field = tpe match {

val field = (0xFF & tpe) match {
case 0x01 =>
DoubleField(name, buf.readDouble)
case 0x09 | 0x11 | 0x12 =>
Expand Down Expand Up @@ -112,8 +116,10 @@ class LowLevelBsonDocReader[A <: ReadableBuffer](rbuf: A) {
case 0x0F =>
// TODO
???

case 0x06 | 0x0A | 0xFF | 0x7F =>
NoValue(tpe, name)

case x => throw new RuntimeException(s"unexpected type $x")
}
if (buf.readable > 1)
Expand Down
86 changes: 71 additions & 15 deletions bson/src/main/scala/types.scala
Expand Up @@ -335,7 +335,7 @@ case object BSONUndefined
}

/** BSON ObjectId value. */
@SerialVersionUID(1L)
@SerialVersionUID(239421902L)
class BSONObjectID private (private val raw: Array[Byte])
extends BSONValue with Serializable with Equals {

Expand All @@ -351,8 +351,9 @@ class BSONObjectID private (private val raw: Array[Byte])

override def canEqual(that: Any): Boolean = that.isInstanceOf[BSONObjectID]

override def equals(that: Any): Boolean = {
canEqual(that) && Arrays.equals(this.raw, that.asInstanceOf[BSONObjectID].raw)
override def equals(that: Any): Boolean = that match {
case BSONObjectID(other) => Arrays.equals(raw, other)
case _ => false
}

override lazy val hashCode: Int = Arrays.hashCode(raw)
Expand Down Expand Up @@ -391,10 +392,11 @@ object BSONObjectID {

private val machineId = {
import java.net._
def p(n: String) = System.getProperty(n)
val validPlatform = Try {
val correctVersion = System.getProperty("java.version").substring(0, 3).toFloat >= 1.8
val noIpv6 = System.getProperty("java.net.preferIPv4Stack") == true
val isLinux = System.getProperty("os.name") == "Linux"
val correctVersion = p("java.version").substring(0, 3).toFloat >= 1.8
val noIpv6 = p("java.net.preferIPv4Stack").toBoolean == true
val isLinux = p("os.name") == "Linux"

!isLinux || correctVersion || noIpv6
}.getOrElse(false)
Expand All @@ -410,7 +412,7 @@ object BSONObjectID {
val networkInterfaces = scala.collection.JavaConverters.enumerationAsScalaIteratorConverter(networkInterfacesEnum).asScala
val ha = networkInterfaces.find(ha => Try(ha.getHardwareAddress).isSuccess && ha.getHardwareAddress != null && ha.getHardwareAddress.length == 6)
.map(_.getHardwareAddress)
.getOrElse(InetAddress.getLocalHost.getHostName.getBytes)
.getOrElse(InetAddress.getLocalHost.getHostName.getBytes("UTF-8"))
Converters.md5(ha).take(3)
} else {
val threadId = Thread.currentThread.getId.toInt
Expand Down Expand Up @@ -542,22 +544,76 @@ case class BSONRegex(value: String, flags: String)
}

/** BSON DBPointer value. */
case class BSONDBPointer(value: String, id: Array[Byte])
extends BSONValue {

class BSONDBPointer private[bson] (
val value: String,
internalId: () => Array[Byte]
) extends BSONValue with Product with Serializable with java.io.Serializable {
val code = 0x0C.toByte

@deprecated("", "0.12.0")
def this(value: String, id: Array[Byte]) = this(value, { () =>
val idCopy = Array.ofDim[Byte](id.size)
id.copyToArray(idCopy)
idCopy
})

/** The BSONObjectID representation of this reference. */
val objectId = BSONObjectID(id)
val objectId = BSONObjectID(internalId())

@deprecated("Do not use", "0.12.0")
def id: Array[Byte] = {
val bytes = internalId()
val idCopy = Array.ofDim[Byte](bytes.size)
bytes.copyToArray(idCopy)
idCopy
}

private[bson] def withId[T](f: Array[Byte] => T): T = f(internalId())

// ---

def canEqual(that: Any): Boolean = that.isInstanceOf[BSONDBPointer]

@deprecated("No longer a case class", "0.12.0")
val productArity = 2

@deprecated("No longer a case class", "0.12.0")
def productElement(n: Int): Any = n match {
case 0 => value
case 1 => id
}

@deprecated("No longer a case class", "0.12.0")
def copy(value: String = this.value, id: Array[Byte] = internalId()): BSONDBPointer = BSONDBPointer(value, id)

override def canEqual(that: Any): Boolean = that.isInstanceOf[BSONDBPointer]
override def equals(that: Any): Boolean = {
canEqual(that) && {
val other = that.asInstanceOf[BSONDBPointer]
this.value.equals(other.value) &&
java.util.Arrays.equals(this.id, other.id)
this.value.equals(other.value) && (this.objectId == other.objectId)
}
}

override def hashCode: Int = (value, objectId).hashCode
}

object BSONDBPointer extends scala.runtime.AbstractFunction2[String, Array[Byte], BSONDBPointer] {

/** Returns a new DB pointer */
@deprecated("", "0.12.0")
def apply(value: String, id: Array[Byte]): BSONDBPointer = {
val idCopy = Array.ofDim[Byte](id.size)
id.copyToArray(idCopy)

new BSONDBPointer(value, () => idCopy)
}

/** Returns a new DB pointer */
def apply(value: String, id: => Array[Byte]): BSONDBPointer =
new BSONDBPointer(value, () => id)

/** Extractor */
def unapply(pointer: BSONDBPointer): Option[(String, Array[Byte])] =
pointer.withId { id => Some(pointer.value -> id) }
}

/**
Expand Down Expand Up @@ -842,7 +898,7 @@ case class BSONDocument(stream: Stream[Try[BSONElement]])
/** Alias for `add(doc: BSONDocument): BSONDocument` */
def ++(doc: BSONDocument): BSONDocument = merge(doc)

@deprecated("Use `merge`", "0.12.0")
/** Alias for `:~` or `merge` */
def ++(elements: Producer[BSONElement]*): BSONDocument = merge(elements: _*)

def :~(elements: Producer[BSONElement]*): BSONDocument = merge(elements: _*)
Expand Down
38 changes: 32 additions & 6 deletions bson/src/main/scala/utils.scala
Expand Up @@ -42,12 +42,38 @@ object Converters {
bytes
}

/** Computes the MD5 hash of the given String. */
def md5(s: String) = java.security.MessageDigest.getInstance("MD5").digest(s.getBytes)
/**
* Returns the MD5 hash for the given UTF-8 `string`,
* and turns it into a hexadecimal String representation.
*/
@deprecated("Use `md5Hex` with explicit encoding", "0.12.0")
def md5Hex(string: String): String = md5Hex(string, "UTF-8")

/** Computes the MD5 hash of the given Array of Bytes. */
def md5(array: Array[Byte]) = java.security.MessageDigest.getInstance("MD5").digest(array)
/**
* Returns the MD5 hash for the given `string`,
* and turns it into a hexadecimal String representation.
*
* @param string the string to be hashed
* @param encoding the string encoding/charset
*/
def md5Hex(string: String, encoding: String): String =
hex2Str(md5(string, encoding))

/** Returns the MD5 hash of the given UTF-8 `string`. */
@deprecated("Use `md5` with explicit encoding", "0.12.0")
def md5(string: String): Array[Byte] = md5(string, "UTF-8")

/**
* Returns the MD5 hash of the given `string`.
*
* @param string the string to be hashed
* @param encoding the string encoding/charset
*/
def md5(string: String, encoding: String): Array[Byte] =
md5(string.getBytes(encoding))

/** Computes the MD5 hash of the given `bytes`. */
def md5(bytes: Array[Byte]): Array[Byte] =
java.security.MessageDigest.getInstance("MD5").digest(bytes)

/** Computes the MD5 hash of the given String and turns it into a hexadecimal String representation. */
def md5Hex(s: String): String = hex2Str(md5(s))
}
81 changes: 81 additions & 0 deletions driver/findbugs-exclude-filters.xml
@@ -0,0 +1,81 @@
<FindBugsFilter>
<Match>
<Or>
<Class name="~.+reactivemongo\$api\$DefaultCursor\$Impl\$\$tailResponse.*" />
<Class name="~.+reactivemongo\.api\.DefaultCursor\$Impl\$FoldResponses.+\$procResp.*" />
</Or>

<Bug pattern="DB_DUPLICATE_BRANCHES" />
</Match>

<Match>
<Class name="reactivemongo.core.commands.CollStatsResult" />
<Field name="indexSizes" /><!-- deprecated -->
<Or>
<Bug pattern="EI_EXPOSE_REP" />
<Bug pattern="EI_EXPOSE_REP2" />
</Or>
</Match>

<Match><!-- deprecated -->
<Class name="reactivemongo.core.nodeset.Node" />
<Or>
<Field name="connected" />
<Field name="connections" />
</Or>
<Bug pattern="SE_TRANSIENT_FIELD_NOT_RESTORED" />
</Match>

<Match><!-- private core -->
<Or>
<Class name="~reactivemongo\.core\.commands\.ScramSha1.+" />
<Class name="~reactivemongo\.core\.actors\.MongoScramSha1.+" />
</Or>
<Or>
<Field name="payload" />
<Field name="payload$1" />
<Field name="sig$1" />
<Field name="serverSignature" />
<Field name="salt$1" />
<Field name="ClientKeySeed" />
<Field name="ServerKeySeed" />
</Or>
<Or>
<Bug pattern="EI_EXPOSE_REP" />
<Bug pattern="EI_EXPOSE_REP2" />
</Or>
</Match>

<Match>
<Or>
<Class name="~reactivemongo\.api\.DefaultCursor\$Impl\$\$anonfun\$tailableCursorEnumerateResponses.*" />
<Class name="~.+MongoConnection\$\$makeOptions\$.+" />
<Class name="~.+CustomEnumeratee\$\$anon.+" />
<Class name="~.+StateEnumerator\$\$anon.+" />
</Or>
<Or>
<Local name="current" />
<Local name="kv" />
<Local name="x1" />
<Local name="x2" />
</Or>
<Bug pattern="NP_LOAD_OF_KNOWN_NULL_VALUE" />
</Match>

<Match><!-- private -->
<Class name="~reactivemongo\.api\.gridfs\.GridFS\$Chunk.*" />
</Match>

<Match>
<Or>
<Class name="~reactivemongo\.core\.protocol\.MongoWireVersion\$V.+\$" />
<Class name="reactivemongo.api.ReadPreference$BSONDocumentWrapper" />
</Or>
<Bug pattern="EQ_UNUSUAL" /><!-- Value class -->
</Match>

<Match>
<Field name="toString" />
<Bug pattern="IS2_INCONSISTENT_SYNC" />
</Match>
</FindBugsFilter>
6 changes: 4 additions & 2 deletions driver/src/main/scala/api/ReadPreference.scala
Expand Up @@ -205,9 +205,11 @@ object ReadPreference {

}

private implicit class BSONDocumentWrapper(val underlying: BSONDocument) extends AnyVal {
def contains(doc: BSONDocument): Boolean = {
private implicit class BSONDocumentWrapper(
val underlying: BSONDocument
) extends AnyVal {

def contains(doc: BSONDocument): Boolean = {
val els = underlying.elements
doc.elements.forall { element =>
els.find {
Expand Down

0 comments on commit 2ada6d0

Please sign in to comment.