From 9076fbf26850567e14eef95edce20414ead7f373 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Tue, 1 May 2012 17:05:09 +0200 Subject: [PATCH 01/74] Optimized ByteString1.++ --- akka-actor/src/main/scala/akka/util/ByteString.scala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 6d869826a8..c335855f39 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -142,7 +142,11 @@ object ByteString { def ++(that: ByteString): ByteString = that match { case b: ByteString1C ⇒ ByteStrings(this, b.toByteString1) - case b: ByteString1 ⇒ ByteStrings(this, b) + case b: ByteString1 ⇒ { + if ((bytes eq b.bytes) && (startIndex + length == b.startIndex)) + new ByteString1(bytes, startIndex, length + b.length) + else ByteStrings(this, b) + } case bs: ByteStrings ⇒ ByteStrings(this, bs) } From 322edb2eca2f5b0336dc181a6cc1656e5b6f5478 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Wed, 2 May 2012 01:40:42 +0200 Subject: [PATCH 02/74] Introduced ContByteString to complement CompactByteString Many operations are necessarily more efficient on contiguous data (represented by a contiguous part of an array) than on chunked data (e.g. wrapping in a ByteBuffer, etc.). It should be possibly to check whether a ByteString fulfills this requirement and to specifically request such a ByteString for specific applications. The new abstract ContByteString, as a natural bridge between CompactByteString and ByteString, provides this. --- .../src/main/scala/akka/util/ByteString.scala | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index c335855f39..a291761d66 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -79,8 +79,6 @@ object ByteString { override def clone: ByteString1C = new ByteString1C(toArray) - def compact: ByteString1C = this - def asByteBuffer: ByteBuffer = toByteString1.asByteBuffer @@ -107,7 +105,7 @@ object ByteString { /** * An unfragmented ByteString. */ - final class ByteString1 private (private val bytes: Array[Byte], private val startIndex: Int, val length: Int) extends ByteString { + final class ByteString1 private (private val bytes: Array[Byte], private val startIndex: Int, val length: Int) extends ContByteString { private def this(bytes: Array[Byte]) = this(bytes, 0, bytes.length) @@ -268,6 +266,7 @@ object ByteString { case bs: ByteStrings ⇒ ByteStrings(this, bs) } + def contiguous = compact def compact: CompactByteString = { val ar = new Array[Byte](length) var pos = 0 @@ -320,8 +319,14 @@ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimiz def copyToBuffer(buffer: ByteBuffer): Int /** - * Create a new ByteString with all contents compacted into a single - * byte array. + * Create a new ByteString with all contents contiguous in memory + * (in a single section of a byte array). + */ + def contiguous: ContByteString + + /** + * Create a new ByteString with all contents compacted into a single, + * full byte array. */ def compact: CompactByteString @@ -426,9 +431,24 @@ object CompactByteString { } /** - * A compact, unfragmented ByteString. + * A contiguous (i.e. non-fragmented) ByteString. + * + * The ByteString is guarantieed to be contiguous in memory, making certain + * operations more efficient than on chunked ByteString instances. */ -sealed abstract class CompactByteString extends ByteString with Serializable +sealed abstract class ContByteString extends ByteString { + def contiguous = this +} + +/** + * A compact and contiguous ByteString. + * + * The ByteString is guarantied to be contiguous in memory and to blocks only + * as much memory as required for its contents. + */ +sealed abstract class CompactByteString extends ContByteString with Serializable { + def compact = this +} /** * A mutable builder for efficiently creating a [[akka.util.ByteString]]. From 3436c3b14dd17703fabc624a8e295bb7b517fe63 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 17 May 2012 13:13:47 +0200 Subject: [PATCH 03/74] Added ByteString1.apply(bytes, startIndex, length) --- akka-actor/src/main/scala/akka/util/ByteString.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index a291761d66..4efc71a325 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -100,6 +100,7 @@ object ByteString { private[akka] object ByteString1 { def apply(bytes: Array[Byte]) = new ByteString1(bytes) + def apply(bytes: Array[Byte], startIndex: Int, length: Int) = new ByteString1(bytes, startIndex, length) } /** From 76dab6354ef191a590681365d319d1d85177741f Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Tue, 1 May 2012 18:41:04 +0200 Subject: [PATCH 04/74] Added ByteIterator --- .../test/scala/akka/util/ByteStringSpec.scala | 71 +++- .../main/scala/akka/util/ByteIterator.scala | 354 ++++++++++++++++++ .../src/main/scala/akka/util/ByteString.scala | 9 + 3 files changed, 433 insertions(+), 1 deletion(-) create mode 100644 akka-actor/src/main/scala/akka/util/ByteIterator.scala diff --git a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala index 94997a49de..1a4dd40ffe 100644 --- a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala @@ -14,7 +14,9 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { def genSimpleByteString(min: Int, max: Int) = for { n ← choose(min, max) b ← Gen.containerOfN[Array, Byte](n, arbitrary[Byte]) - } yield ByteString(b) + from ← choose(0, b.length) + until ← choose(from, b.length) + } yield ByteString(b).slice(from, until) implicit val arbitraryByteString: Arbitrary[ByteString] = Arbitrary { Gen.sized { s ⇒ @@ -25,6 +27,30 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { } } + type ByteStringSlice = (ByteString, Int, Int) + + implicit val arbitraryByteStringSlice: Arbitrary[ByteStringSlice] = Arbitrary { + for { + xs ← arbitraryByteString.arbitrary + from ← choose(0, xs.length) + until ← choose(from, xs.length) + } yield (xs, from, until) + } + + def likeVecIt(bs: ByteString)(body: BufferedIterator[Byte] ⇒ Any, strict: Boolean = true): Boolean = { + val bsIterator = bs.iterator + val vecIterator = Vector(bs: _*).iterator.buffered + (body(bsIterator) == body(vecIterator)) && + (!strict || (bsIterator.toSeq == vecIterator.toSeq)) + } + + def likeVecIts(a: ByteString, b: ByteString)(body: (BufferedIterator[Byte], BufferedIterator[Byte]) ⇒ Any, strict: Boolean = true): Boolean = { + val (bsAIt, bsBIt) = (a.iterator, b.iterator) + val (vecAIt, vecBIt) = (Vector(a: _*).iterator.buffered, Vector(b: _*).iterator.buffered) + (body(bsAIt, bsBIt) == body(vecAIt, vecBIt)) && + (!strict || (bsAIt.toSeq, bsBIt.toSeq) == (vecAIt.toSeq, vecBIt.toSeq)) + } + "A ByteString" must { "have correct size" when { "concatenating" in { check((a: ByteString, b: ByteString) ⇒ (a ++ b).size == a.size + b.size) } @@ -35,4 +61,47 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { "dropping" in { check((a: ByteString, b: ByteString) ⇒ (a ++ b).drop(a.size) == b) } } } + + "A ByteStringIterator" must { + "behave like a buffered Vector Iterator" when { + "concatenating" in { check { (a: ByteString, b: ByteString) ⇒ likeVecIts(a, b) { (a, b) ⇒ (a ++ b).toSeq } } } + + "calling head" in { check { a: ByteString ⇒ a.isEmpty || likeVecIt(a) { _.head } } } + "calling next" in { check { a: ByteString ⇒ a.isEmpty || likeVecIt(a) { _.next() } } } + "calling hasNext" in { check { a: ByteString ⇒ likeVecIt(a) { _.hasNext } } } + "calling length" in { check { a: ByteString ⇒ likeVecIt(a) { _.length } } } + "calling duplicate" in { check { a: ByteString ⇒ likeVecIt(a)({ _.duplicate match { case (a, b) ⇒ (a.toSeq, b.toSeq) } }, strict = false) } } + "calling span" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a)({ _.span(_ == b) match { case (a, b) ⇒ (a.toSeq, b.toSeq) } }, strict = false) } } + "calling takeWhile" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a)({ _.takeWhile(_ == b).toSeq }, strict = false) } } + "calling dropWhile" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a) { _.dropWhile(_ == b).toSeq } } } + "calling indexWhere" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a) { _.indexWhere(_ == b) } } } + "calling indexOf" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a) { _.indexOf(b) } } } + "calling toSeq" in { check { a: ByteString ⇒ likeVecIt(a) { _.toSeq } } } + "calling foreach" in { check { a: ByteString ⇒ likeVecIt(a) { it ⇒ var acc = 0; it foreach { acc += _ }; acc } } } + "calling foldLeft" in { check { a: ByteString ⇒ likeVecIt(a) { _.foldLeft(0) { _ + _ } } } } + "calling toArray" in { check { a: ByteString ⇒ likeVecIt(a) { _.toArray.toSeq } } } + + "calling slice" in { + check { slice: ByteStringSlice ⇒ + slice match { + case (xs, from, until) ⇒ likeVecIt(xs)({ + _.slice(from, until).toSeq + }, strict = false) + } + } + } + + "calling copyToArray" in { + check { slice: ByteStringSlice ⇒ + slice match { + case (xs, from, until) ⇒ likeVecIt(xs)({ it ⇒ + val array = Array.ofDim[Byte](xs.length) + it.slice(from, until).copyToArray(array, from, until) + array.toSeq + }, strict = false) + } + } + } + } + } } diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala new file mode 100644 index 0000000000..0c6146e93d --- /dev/null +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -0,0 +1,354 @@ +/** + * Copyright (C) 2009-2012 Typesafe Inc. + */ + +package akka.util + +import java.nio.ByteBuffer + +import scala.collection.IndexedSeqOptimized +import scala.collection.mutable.{ Builder, WrappedArray } +import scala.collection.immutable.{ IndexedSeq, VectorBuilder } +import scala.collection.generic.CanBuildFrom +import scala.collection.mutable.{ ListBuffer } +import scala.annotation.tailrec + +import java.nio.ByteBuffer + +abstract class ByteIterator extends BufferedIterator[Byte] { + def isIdenticalTo(that: Iterator[Byte]): Boolean + + def len: Int + + def head: Byte + + def next(): Byte + + protected def clear(): Unit + + def ++(that: TraversableOnce[Byte]): ByteIterator = + ByteArrayIterator(that.toArray) + + // *must* be overridden by derived classes + override def clone: ByteIterator = null + + final override def duplicate = (this, clone) + + // *must* be overridden by derived classes + override def take(n: Int): this.type = null + + // *must* be overridden by derived classes + override def drop(n: Int): this.type = null + + final override def slice(from: Int, until: Int): this.type = + drop(from).take(until - from) + + // *must* be overridden by derived classes + override def takeWhile(p: Byte ⇒ Boolean): this.type = null + + // *must* be overridden by derived classes + override def dropWhile(p: Byte ⇒ Boolean): this.type = null + + final override def span(p: Byte ⇒ Boolean): (ByteIterator, ByteIterator) = { + val that = clone + that.takeWhile(p) + drop(that.len) + (that, this) + } + + final override def indexWhere(p: Byte ⇒ Boolean): Int = { + var index = 0 + var found = false + while (!found && hasNext) if (p(next())) { found = true } else { index += 1 } + if (found) index else -1 + } + + final def indexOf(elem: Byte): Int = { + var index = 0 + var found = false + while (!found && hasNext) if (elem == next()) { found = true } else { index += 1 } + if (found) index else -1 + } + + final override def indexOf[B >: Byte](elem: B): Int = { + var index = 0 + var found = false + while (!found && hasNext) if (elem == next()) { found = true } else { index += 1 } + if (found) index else -1 + } + + def toByteString: ByteString + + override def toSeq: ByteString = toByteString + + @inline final override def foreach[@specialized U](f: Byte ⇒ U): Unit = + while (hasNext) f(next()) + + final override def foldLeft[@specialized B](z: B)(op: (B, Byte) ⇒ B): B = { + var acc = z + while (hasNext) acc = op(acc, next()) + acc + } + + final override def toArray[B >: Byte](implicit arg0: ClassManifest[B]): Array[B] = { + val target = Array.ofDim[B](len) + copyToArray(target) + target + } + + /** + * Copy as many bytes as possible to a ByteBuffer, starting from it's + * current position. This method will not overflow the buffer. + * + * @param buffer a ByteBuffer to copy bytes to + * @return the number of bytes actually copied + */ + def copyToBuffer(buffer: ByteBuffer): Int +} + +object ByteArrayIterator { + private val emptyArray = Array.ofDim[Byte](0) + + protected[akka] def apply(array: Array[Byte]): ByteArrayIterator = + new ByteArrayIterator(array, 0, array.length) + + protected[akka] def apply(array: Array[Byte], from: Int, until: Int): ByteArrayIterator = + new ByteArrayIterator(array, from, until) + + val empty: ByteArrayIterator = apply(Array.empty[Byte]) +} + +class ByteArrayIterator private (private var array: Array[Byte], private var from: Int, private var until: Int) extends ByteIterator { + protected[util] final def internalArray = array + protected[util] final def internalFrom = from + protected[util] final def internalUntil = until + + final def isIdenticalTo(that: Iterator[Byte]) = that match { + case that: ByteArrayIterator ⇒ + ((this.array) eq (that.internalArray)) && + ((this.from) == (that.from)) && ((this.until) == (that.until)) + case _ ⇒ false + } + + @inline final def len = until - from + + @inline final def hasNext = from < until + + @inline final def head = array(from) + + final def next() = { + if (!hasNext) Iterator.empty.next + else { val i = from; from = from + 1; array(i) } + } + + def clear() { this.array = ByteArrayIterator.emptyArray; from = 0; until = from } + + final override def length = { val l = len; drop(len); l } + + final override def ++(that: TraversableOnce[Byte]) = that match { + case that: ByteArrayIterator ⇒ { + if (this.isEmpty) that + else if ((this.array eq that.array) && (this.until == that.from)) { + this.until = that.until + that.clear() + this + } else { + val result = MultiByteArrayIterator(List(this, that)) + this.clear() + result + } + } + case that: MultiByteArrayIterator ⇒ if (this.isEmpty) that else (this +: that) + case _ ⇒ super.++(that) + } + + final override def clone = new ByteArrayIterator(array, from, until) + + final override def take(n: Int) = { + until = until min (from + (0 max n)) + this + } + + final override def drop(n: Int) = { + from = until min (from + (0 max n)) + this + } + + final override def takeWhile(p: Byte ⇒ Boolean) = { + val prev = from + dropWhile(p) + until = from; from = prev + this + } + + final override def dropWhile(p: Byte ⇒ Boolean) = { + var stop = false + while (!stop && hasNext) { + if (p(array(from))) { from = from + 1 } else { stop = true } + } + this + } + + final override def copyToArray[B >: Byte](xs: Array[B], start: Int, len: Int): Unit = { + val n = 0 max ((xs.length - start) min this.len min len) + Array.copy(this.array, from, xs, start, n) + this.drop(n) + } + + final override def toByteString = { + val result = + if ((from == 0) && (until == array.length)) ByteString.ByteString1C(array) + else ByteString.ByteString1(array, from, len) + clear() + result + } + + def copyToBuffer(buffer: ByteBuffer): Int = { + val copyLength = math.min(buffer.remaining, len) + if (copyLength > 0) { + buffer.put(array, from, copyLength) + drop(copyLength) + } + copyLength + } +} + +object MultiByteArrayIterator { + protected val clearedList = List(ByteArrayIterator.empty) + + val empty = new MultiByteArrayIterator(Nil) + + protected[akka] def apply(iterators: List[ByteArrayIterator]): MultiByteArrayIterator = + new MultiByteArrayIterator(iterators) +} + +class MultiByteArrayIterator private (private var iterators: List[ByteArrayIterator]) extends ByteIterator { + // After normalization: + // * iterators.isEmpty == false + // * (!iterator.head.isEmpty || iterators.tail.isEmpty) == true + private def normalize(): this.type = { + @tailrec def norm(xs: List[ByteArrayIterator]): List[ByteArrayIterator] = { + if (xs.isEmpty) MultiByteArrayIterator.clearedList + else if (xs.head.isEmpty) norm(xs.tail) + else xs + } + iterators = norm(iterators) + this + } + normalize() + + @inline private def current = iterators.head + @inline private def dropCurrent() { iterators = iterators.tail } + @inline def clear() { iterators = MultiByteArrayIterator.empty.iterators } + + final def isIdenticalTo(that: Iterator[Byte]) = false + + @inline final def hasNext = current.hasNext + + @inline final def head = current.head + + final def next() = { + val result = current.next() + normalize() + result + } + + final override def len = iterators.foldLeft(0) { _ + _.len } + + final override def length = { + var result = len + clear() + result + } + + def +:(that: ByteArrayIterator): this.type = { + iterators = that +: iterators + this + } + + final override def ++(that: TraversableOnce[Byte]) = that match { + case that: ByteArrayIterator ⇒ if (this.isEmpty) that else { + iterators = iterators :+ that + that.clear() + this + } + case that: MultiByteArrayIterator ⇒ if (this.isEmpty) that else { + iterators = this.iterators ++ that.iterators + that.clear() + this + } + case _ ⇒ super.++(that) + } + + final override def clone = new MultiByteArrayIterator(iterators map { _.clone }) + + final override def take(n: Int) = { + var rest = n + val builder = new ListBuffer[ByteArrayIterator] + while ((rest > 0) && !iterators.isEmpty) { + current.take(rest) + if (current.hasNext) { + rest -= current.len + builder += current + } + iterators = iterators.tail + } + iterators = builder.result + normalize() + } + + @tailrec final override def drop(n: Int) = if ((n > 0) && !isEmpty) { + val nCurrent = math.min(n, current.len) + current.drop(n) + val rest = n - nCurrent + assert(current.isEmpty || (rest == 0)) + normalize() + drop(rest) + } else this + + final override def takeWhile(p: Byte ⇒ Boolean) = { + var stop = false + var builder = new ListBuffer[ByteArrayIterator] + while (!stop && !iterators.isEmpty) { + val lastLen = current.len + current.takeWhile(p) + if (current.hasNext) builder += current + if (current.len < lastLen) stop = true + dropCurrent() + } + iterators = builder.result + normalize() + } + + @tailrec final override def dropWhile(p: Byte ⇒ Boolean) = if (!isEmpty) { + current.dropWhile(p) + val dropMore = current.isEmpty + normalize() + if (dropMore) dropWhile(p) else this + } else this + + final override def copyToArray[B >: Byte](xs: Array[B], start: Int, len: Int): Unit = { + var pos = start + var rest = len + while ((rest > 0) && !iterators.isEmpty) { + val n = 0 max ((xs.length - pos) min current.len min rest) + current.copyToArray(xs, pos, n) + pos += n + rest -= n + dropCurrent() + } + normalize() + } + + final override def toByteString = { + val result = iterators.foldLeft(ByteString.empty) { _ ++ _.toByteString } + clear() + result + } + + def copyToBuffer(buffer: ByteBuffer): Int = { + val n = iterators.foldLeft(0) { _ + _.copyToBuffer(buffer) } + normalize() + n + } +} diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 4efc71a325..ba3cca51d6 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -73,6 +73,8 @@ object ByteString { override def length = bytes.length + override def iterator = ByteArrayIterator(bytes, 0, bytes.length) + def toArray: Array[Byte] = bytes.clone def toByteString1: ByteString1 = ByteString1(bytes) @@ -112,6 +114,8 @@ object ByteString { def apply(idx: Int): Byte = bytes(checkRangeConvert(idx)) + override def iterator = ByteArrayIterator(bytes, startIndex, startIndex + length) + private def checkRangeConvert(index: Int) = { if (0 <= index && length > index) index + startIndex @@ -224,6 +228,8 @@ object ByteString { bytestrings(pos)(idx - seen) } else throw new IndexOutOfBoundsException(idx.toString) + override def iterator = MultiByteArrayIterator(bytestrings.toList.map { _.iterator }) + override def slice(from: Int, until: Int): ByteString = { val start = math.max(from, 0) val end = math.min(until, length) @@ -305,6 +311,9 @@ object ByteString { sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimized[Byte, ByteString] { override protected[this] def newBuilder = ByteString.newBuilder + // *must* be overridden by derived classes + override def iterator: ByteIterator = null + /** * Efficiently concatenate another ByteString. */ From c8d7c995dbfe5607e92f8f23183f267c45689772 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Tue, 1 May 2012 19:22:16 +0200 Subject: [PATCH 05/74] Enhanced ByteString implementation using ByteIterator --- .../main/scala/akka/util/ByteIterator.scala | 2 +- .../src/main/scala/akka/util/ByteString.scala | 108 +++++------------- 2 files changed, 31 insertions(+), 79 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 0c6146e93d..877dbef6a9 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -143,7 +143,7 @@ class ByteArrayIterator private (private var array: Array[Byte], private var fro def clear() { this.array = ByteArrayIterator.emptyArray; from = 0; until = from } - final override def length = { val l = len; drop(len); l } + final override def length = { val l = len; clear(); l } final override def ++(that: TraversableOnce[Byte]) = that match { case that: ByteArrayIterator ⇒ { diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index ba3cca51d6..e070d294a6 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -75,8 +75,6 @@ object ByteString { override def iterator = ByteArrayIterator(bytes, 0, bytes.length) - def toArray: Array[Byte] = bytes.clone - def toByteString1: ByteString1 = ByteString1(bytes) override def clone: ByteString1C = new ByteString1C(toArray) @@ -92,12 +90,6 @@ object ByteString { override def slice(from: Int, until: Int): ByteString = if ((from != 0) || (until != length)) toByteString1.slice(from, until) else this - - override def copyToArray[A >: Byte](xs: Array[A], start: Int, len: Int): Unit = - toByteString1.copyToArray(xs, start, len) - - def copyToBuffer(buffer: ByteBuffer): Int = - toByteString1.copyToBuffer(buffer) } private[akka] object ByteString1 { @@ -123,12 +115,6 @@ object ByteString { throw new IndexOutOfBoundsException(index.toString) } - def toArray: Array[Byte] = { - val ar = new Array[Byte](length) - Array.copy(bytes, startIndex, ar, 0, length) - ar - } - override def clone: CompactByteString = ByteString1C(toArray) def compact: CompactByteString = @@ -152,23 +138,6 @@ object ByteString { } case bs: ByteStrings ⇒ ByteStrings(this, bs) } - - override def slice(from: Int, until: Int): ByteString = { - val newStartIndex = math.max(from, 0) + startIndex - val newLength = math.min(until, length) - from - if (newLength <= 0) ByteString.empty - else new ByteString1(bytes, newStartIndex, newLength) - } - - override def copyToArray[A >: Byte](xs: Array[A], start: Int, len: Int): Unit = - Array.copy(bytes, startIndex, xs, start, math.min(math.min(length, len), xs.length - start)) - - def copyToBuffer(buffer: ByteBuffer): Int = { - val copyLength = math.min(buffer.remaining, length) - if (copyLength > 0) buffer.put(bytes, startIndex, copyLength) - copyLength - } - } private[akka] object ByteStrings { @@ -230,43 +199,6 @@ object ByteString { override def iterator = MultiByteArrayIterator(bytestrings.toList.map { _.iterator }) - override def slice(from: Int, until: Int): ByteString = { - val start = math.max(from, 0) - val end = math.min(until, length) - if (end <= start) - ByteString.empty - else { - val iter = bytestrings.iterator - var cur = iter.next - var pos = 0 - var seen = 0 - while (from >= seen + cur.length) { - seen += cur.length - pos += 1 - cur = iter.next - } - val startpos = pos - val startidx = start - seen - while (until > seen + cur.length) { - seen += cur.length - pos += 1 - cur = iter.next - } - val endpos = pos - val endidx = end - seen - if (startpos == endpos) - cur.slice(startidx, endidx) - else { - val first = bytestrings(startpos).drop(startidx).asInstanceOf[ByteString1] - val last = cur.take(endidx).asInstanceOf[ByteString1] - if ((endpos - startpos) == 1) - new ByteStrings(Vector(first, last), until - from) - else - new ByteStrings(first +: bytestrings.slice(startpos + 1, endpos) :+ last, until - from) - } - } - } - def ++(that: ByteString): ByteString = that match { case b: ByteString1C ⇒ ByteStrings(this, b.toByteString1) case b: ByteString1 ⇒ ByteStrings(this, b) @@ -287,15 +219,6 @@ object ByteString { def asByteBuffer: ByteBuffer = compact.asByteBuffer def decodeString(charset: String): String = compact.decodeString(charset) - - def copyToBuffer(buffer: ByteBuffer): Int = { - val copyLength = math.min(buffer.remaining, length) - val iter = bytestrings.iterator - while (iter.hasNext && buffer.hasRemaining) { - iter.next.copyToBuffer(buffer) - } - copyLength - } } } @@ -314,6 +237,35 @@ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimiz // *must* be overridden by derived classes override def iterator: ByteIterator = null + @inline final override def head: Byte = this(0) + @inline final override def tail: ByteString = this.drop(1) + @inline final override def last: Byte = this(this.length - 1) + override def init: ByteString = this.take(this.length - 1) + + override def slice(from: Int, until: Int): ByteString = + if ((from == 0) && (until == length)) this + else iterator.slice(from, until).toByteString + + override def take(n: Int): ByteString = slice(0, n) + override def takeRight(n: Int): ByteString = slice(length - n, length) + override def drop(n: Int): ByteString = slice(n, length) + override def dropRight(n: Int): ByteString = slice(0, length - n) + + override def takeWhile(p: Byte ⇒ Boolean) = iterator.takeWhile(p).toByteString + override def dropWhile(p: Byte ⇒ Boolean) = iterator.dropWhile(p).toByteString + override def span(p: Byte ⇒ Boolean): (ByteString, ByteString) = + { val (a, b) = iterator.span(p); (a.toByteString, b.toByteString) } + + override def splitAt(n: Int): (ByteString, ByteString) = (take(n), drop(n)) + + override def indexWhere(p: Byte ⇒ Boolean): Int = iterator.indexWhere(p) + override def indexOf[B >: Byte](elem: B): Int = iterator.indexOf(elem) + + override def toArray[B >: Byte](implicit arg0: ClassManifest[B]) = iterator.toArray + override def copyToArray[B >: Byte](xs: Array[B], start: Int, len: Int) = iterator.copyToArray(xs, start, len) + + @inline final override def foreach[@specialized U](f: Byte ⇒ U): Unit = iterator foreach f + /** * Efficiently concatenate another ByteString. */ @@ -326,7 +278,7 @@ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimiz * @param buffer a ByteBuffer to copy bytes to * @return the number of bytes actually copied */ - def copyToBuffer(buffer: ByteBuffer): Int + def copyToBuffer(buffer: ByteBuffer): Int = iterator.copyToBuffer(buffer) /** * Create a new ByteString with all contents contiguous in memory From 018f1c92d51d8f189e3d48ed602128be8d73d107 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 17 May 2012 22:34:50 +0200 Subject: [PATCH 06/74] Improved implementations of ByteString.++ --- .../src/main/scala/akka/util/ByteString.scala | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index e070d294a6..091a0d728a 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -85,7 +85,9 @@ object ByteString { def decodeString(charset: String): String = new String(bytes, charset) def ++(that: ByteString): ByteString = - if (!that.isEmpty) toByteString1 ++ that else this + if (that.isEmpty) this + else if (this.isEmpty) that + else toByteString1 ++ that override def slice(from: Int, until: Int): ByteString = if ((from != 0) || (until != length)) toByteString1.slice(from, until) @@ -129,14 +131,18 @@ object ByteString { def decodeString(charset: String): String = new String(if (length == bytes.length) bytes else toArray, charset) - def ++(that: ByteString): ByteString = that match { - case b: ByteString1C ⇒ ByteStrings(this, b.toByteString1) - case b: ByteString1 ⇒ { - if ((bytes eq b.bytes) && (startIndex + length == b.startIndex)) - new ByteString1(bytes, startIndex, length + b.length) - else ByteStrings(this, b) + def ++(that: ByteString): ByteString = { + if (that.isEmpty) this + else if (this.isEmpty) that + else that match { + case b: ByteString1C ⇒ ByteStrings(this, b.toByteString1) + case b: ByteString1 ⇒ { + if ((bytes eq b.bytes) && (startIndex + length == b.startIndex)) + new ByteString1(bytes, startIndex, length + b.length) + else ByteStrings(this, b) + } + case bs: ByteStrings ⇒ ByteStrings(this, bs) } - case bs: ByteStrings ⇒ ByteStrings(this, bs) } } @@ -199,10 +205,14 @@ object ByteString { override def iterator = MultiByteArrayIterator(bytestrings.toList.map { _.iterator }) - def ++(that: ByteString): ByteString = that match { - case b: ByteString1C ⇒ ByteStrings(this, b.toByteString1) - case b: ByteString1 ⇒ ByteStrings(this, b) - case bs: ByteStrings ⇒ ByteStrings(this, bs) + def ++(that: ByteString): ByteString = { + if (that.isEmpty) this + else if (this.isEmpty) that + else that match { + case b: ByteString1C ⇒ ByteStrings(this, b.toByteString1) + case b: ByteString1 ⇒ ByteStrings(this, b) + case bs: ByteStrings ⇒ ByteStrings(this, bs) + } } def contiguous = compact From b7e2246ce86d09429ff1a3870f10e0916a7ca2d1 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 17 May 2012 22:56:59 +0200 Subject: [PATCH 07/74] Improved implementations of ByteIterator.++ --- .../main/scala/akka/util/ByteIterator.scala | 61 ++++++++++++------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 877dbef6a9..33440ff640 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -26,8 +26,10 @@ abstract class ByteIterator extends BufferedIterator[Byte] { protected def clear(): Unit - def ++(that: TraversableOnce[Byte]): ByteIterator = - ByteArrayIterator(that.toArray) + def ++(that: TraversableOnce[Byte]): ByteIterator = { + if (that.isEmpty) this + else ByteArrayIterator(that.toArray) + } // *must* be overridden by derived classes override def clone: ByteIterator = null @@ -146,20 +148,25 @@ class ByteArrayIterator private (private var array: Array[Byte], private var fro final override def length = { val l = len; clear(); l } final override def ++(that: TraversableOnce[Byte]) = that match { - case that: ByteArrayIterator ⇒ { - if (this.isEmpty) that - else if ((this.array eq that.array) && (this.until == that.from)) { - this.until = that.until - that.clear() - this - } else { - val result = MultiByteArrayIterator(List(this, that)) - this.clear() - result + case that: ByteIterator ⇒ { + if (that.isEmpty) this + else if (this.isEmpty) that + else that match { + case that: ByteArrayIterator ⇒ { + if ((this.array eq that.array) && (this.until == that.from)) { + this.until = that.until + that.clear() + this + } else { + val result = MultiByteArrayIterator(List(this, that)) + this.clear() + result + } + } + case that: MultiByteArrayIterator ⇒ this +: that } } - case that: MultiByteArrayIterator ⇒ if (this.isEmpty) that else (this +: that) - case _ ⇒ super.++(that) + case _ ⇒ super.++(that) } final override def clone = new ByteArrayIterator(array, from, until) @@ -267,15 +274,23 @@ class MultiByteArrayIterator private (private var iterators: List[ByteArrayItera } final override def ++(that: TraversableOnce[Byte]) = that match { - case that: ByteArrayIterator ⇒ if (this.isEmpty) that else { - iterators = iterators :+ that - that.clear() - this - } - case that: MultiByteArrayIterator ⇒ if (this.isEmpty) that else { - iterators = this.iterators ++ that.iterators - that.clear() - this + case that: ByteIterator ⇒ { + if (that.isEmpty) this + else if (this.isEmpty) that + else { + that match { + case that: ByteArrayIterator ⇒ { + iterators = this.iterators :+ that + that.clear() + this + } + case that: MultiByteArrayIterator ⇒ { + iterators = this.iterators ++ that.iterators + that.clear() + this + } + } + } } case _ ⇒ super.++(that) } From 3263f43060624e58f3c11e6d6889e25dd2e72166 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Wed, 2 May 2012 21:16:49 +0200 Subject: [PATCH 08/74] Added a companion object, length and sizeHint to class ByteStreamBuilder --- .../src/main/scala/akka/util/ByteString.scala | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 091a0d728a..5ce80b0549 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -422,6 +422,10 @@ sealed abstract class CompactByteString extends ContByteString with Serializable def compact = this } +object ByteStringBuilder { + def apply(initialSize: Int = 0): ByteStringBuilder = new ByteStringBuilder(initialSize) +} + /** * A mutable builder for efficiently creating a [[akka.util.ByteString]]. * @@ -435,6 +439,17 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { private var _tempLength = 0 private var _tempCapacity = 0 + def this(initialSize: Int) = { + this() + if (initialSize > 0) sizeHint(initialSize) + } + + def length: Int = _length + + override def sizeHint(len: Int) { + resizeTemp(len - (_length - _tempLength)) + } + private def clearTemp() { if (_tempLength > 0) { val arr = new Array[Byte](_tempLength) From 53a01ecc125f2a0bf651a3f495b0d37a1c30aa3b Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Wed, 2 May 2012 22:19:09 +0200 Subject: [PATCH 09/74] Added a java.io.OutputStream wrapper to ByteStringBuilder --- .../src/main/scala/akka/util/ByteString.scala | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 5ce80b0549..d75fb01374 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -424,6 +424,15 @@ sealed abstract class CompactByteString extends ContByteString with Serializable object ByteStringBuilder { def apply(initialSize: Int = 0): ByteStringBuilder = new ByteStringBuilder(initialSize) + + /** + * An OutputStream that directly wraps a ByteStringBuilder. + */ + class OutputStreamWrapper(val builder: ByteStringBuilder) extends java.io.OutputStream { + def write(b: Int) = builder += b.toByte + + override def write(b: Array[Byte], off: Int, len: Int) { builder.putBytes(b, off, len) } + } } /** @@ -526,4 +535,9 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { ByteStrings(bytestrings, _length) } + /** + * Directly wraps this ByteStringBuilder in an OutputStream. Write + * operations on the stream are forwarded to the builder. + */ + def asOutputStream = new ByteStringBuilder.OutputStreamWrapper(this) } From 4517b44e8b42f2a7b44d6b56e7198ca4b636d71c Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Wed, 2 May 2012 02:09:32 +0200 Subject: [PATCH 10/74] Added a java.io.InputStream wrapper for ByteIterator. --- .../main/scala/akka/util/ByteIterator.scala | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 33440ff640..4bb8beec74 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -15,6 +15,29 @@ import scala.annotation.tailrec import java.nio.ByteBuffer +object ByteIterator { + /** + * An InputStream that directly wraps a ByteIterator without copying + */ + final class InputStreamWrapper(val iterator: ByteIterator) extends java.io.InputStream { + override def available = iterator.len + + def read = iterator.next.toInt + + override def read(b: Array[Byte], off: Int, len: Int) = { + val nRead = math.min(iterator.len, len - off) + iterator.copyToArray(b, off, nRead) + nRead + } + + override def skip(n: Long) = { + val nSkip = math.min(iterator.len, n.toInt) + iterator.drop(nSkip) + nSkip + } + } +} + abstract class ByteIterator extends BufferedIterator[Byte] { def isIdenticalTo(that: Iterator[Byte]): Boolean @@ -106,6 +129,13 @@ abstract class ByteIterator extends BufferedIterator[Byte] { * @return the number of bytes actually copied */ def copyToBuffer(buffer: ByteBuffer): Int + + /** + * Directly wraps this ByteIterator in an InputStream without copying. + * Read and skip operations on the stream will advance the iterator + * accordingly. + */ + def asInputStream: java.io.InputStream = new ByteIterator.InputStreamWrapper(this) } object ByteArrayIterator { From f127b50ca2c99ace657f5ada58a02256b96cba28 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 17 May 2012 13:26:18 +0200 Subject: [PATCH 11/74] Added methods putBytes, putShorts, etc. to ByteStringBuilder --- .../src/main/scala/akka/util/ByteString.scala | 55 ++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index d75fb01374..baf77fba6f 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -4,7 +4,7 @@ package akka.util -import java.nio.ByteBuffer +import java.nio.{ ByteBuffer, ByteOrder } import scala.collection.IndexedSeqOptimized import scala.collection.mutable.{ Builder, WrappedArray } @@ -448,6 +448,23 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { private var _tempLength = 0 private var _tempCapacity = 0 + protected def fillArray(len: Int)(fill: (Array[Byte], Int) ⇒ Unit): this.type = { + ensureTempSize(_tempLength + len) + fill(_temp, _tempLength) + _tempLength += len + _length += len + this + } + + protected def fillByteBuffer(len: Int, byteOrder: ByteOrder)(fill: ByteBuffer ⇒ Unit): this.type = { + fillArray(len) { + case (array, start) ⇒ + val buffer = ByteBuffer.wrap(array, start, len) + buffer.order(byteOrder) + fill(buffer) + } + } + def this(initialSize: Int) = { this() if (initialSize > 0) sizeHint(initialSize) @@ -518,6 +535,42 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { this } + /** + * Add a number of Bytes from an array to this builder. + */ + def putBytes(array: Array[Byte], start: Int, len: Int): this.type = + fillArray(len) { case (target, targetOffset) ⇒ Array.copy(array, start, target, targetOffset, len) } + + /** + * Add a number of Shorts from an array to this builder. + */ + def putShorts(array: Array[Short], start: Int, len: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + fillByteBuffer(len * 2, byteOrder) { _.asShortBuffer.put(array, start, len) } + + /** + * Add a number of Ints from an array to this builder. + */ + def putInts(array: Array[Int], start: Int, len: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + fillByteBuffer(len * 4, byteOrder) { _.asIntBuffer.put(array, start, len) } + + /** + * Add a number of Longs from an array to this builder. + */ + def putLongs(array: Array[Long], start: Int, len: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + fillByteBuffer(len * 8, byteOrder) { _.asLongBuffer.put(array, start, len) } + + /** + * Add a number of Floats from an array to this builder. + */ + def putFloats(array: Array[Float], start: Int, len: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + fillByteBuffer(len * 4, byteOrder) { _.asFloatBuffer.put(array, start, len) } + + /** + * Add a number of Doubles from an array to this builder. + */ + def putDoubles(array: Array[Double], start: Int, len: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + fillByteBuffer(len * 8, byteOrder) { _.asDoubleBuffer.put(array, start, len) } + def clear() { _builder.clear _length = 0 From f4ee17dcac02fbc0be376bdfb6742633b3993ecf Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 17 May 2012 14:12:14 +0200 Subject: [PATCH 12/74] Added methods getBytes, getShorts, etc. to ByteIterator --- .../main/scala/akka/util/ByteIterator.scala | 87 ++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 4bb8beec74..d574a6fbdd 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -4,7 +4,7 @@ package akka.util -import java.nio.ByteBuffer +import java.nio.{ ByteBuffer, ByteOrder } import scala.collection.IndexedSeqOptimized import scala.collection.mutable.{ Builder, WrappedArray } @@ -121,6 +121,37 @@ abstract class ByteIterator extends BufferedIterator[Byte] { target } + /** + * Get a specific number of Bytes from this iterator. In contrast to + * copyToArray, this method will fail if length < n or if (xs.length - offset) < n. + */ + def getBytes(xs: Array[Byte], offset: Int, n: Int): this.type + + /** + * Get a number of Shorts from this iterator. + */ + def getShorts(xs: Array[Short], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type + + /** + * Get a number of Ints from this iterator. + */ + def getInts(xs: Array[Int], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type + + /** + * Get a number of Longs from this iterator. + */ + def getLongs(xs: Array[Long], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type + + /** + * Get a number of Floats from this iterator. + */ + def getFloats(xs: Array[Float], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type + + /** + * Get a number of Doubles from this iterator. + */ + def getDoubles(xs: Array[Double], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type + /** * Copy as many bytes as possible to a ByteBuffer, starting from it's * current position. This method will not overflow the buffer. @@ -240,6 +271,28 @@ class ByteArrayIterator private (private var array: Array[Byte], private var fro result } + def getBytes(xs: Array[Byte], offset: Int, n: Int) = { + if (n <= this.len) { + Array.copy(this.array, this.from, xs, offset, n) + this + } else Iterator.empty.next + } + + def getShorts(xs: Array[Short], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + { toByteString.asByteBuffer.order(byteOrder).asShortBuffer.get(xs, offset, n); drop(2 * n) } + + def getInts(xs: Array[Int], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + { toByteString.asByteBuffer.order(byteOrder).asIntBuffer.get(xs, offset, n); drop(4 * n) } + + def getLongs(xs: Array[Long], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + { toByteString.asByteBuffer.order(byteOrder).asLongBuffer.get(xs, offset, n); drop(8 * n) } + + def getFloats(xs: Array[Float], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + { toByteString.asByteBuffer.order(byteOrder).asFloatBuffer.get(xs, offset, n); drop(4 * n) } + + def getDoubles(xs: Array[Double], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + { toByteString.asByteBuffer.order(byteOrder).asDoubleBuffer.get(xs, offset, n); drop(8 * n) } + def copyToBuffer(buffer: ByteBuffer): Int = { val copyLength = math.min(buffer.remaining, len) if (copyLength > 0) { @@ -391,6 +444,38 @@ class MultiByteArrayIterator private (private var iterators: List[ByteArrayItera result } + @tailrec protected final def getToArray[A](xs: Array[A], offset: Int, n: Int, elemSize: Int)(getSingle: ⇒ A)(getMult: (Array[A], Int, Int) ⇒ Unit): this.type = if (n <= 0) this else { + if (isEmpty) Iterator.empty.next + val nDone = if (current.len >= elemSize) { + val nCurrent = math.min(elemSize, current.len / elemSize) + getMult(xs, offset, nCurrent) + nCurrent + } else { + xs(offset) = getSingle + 1 + } + normalize() + getToArray(xs, offset + nDone, n - nDone, elemSize)(getSingle)(getMult) + } + + def getBytes(xs: Array[Byte], offset: Int, n: Int) = + getToArray(xs, offset, n, 1) { getByte } { getBytes(_, _, _) } + + def getShorts(xs: Array[Short], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + getToArray(xs, offset, n, 2) { getShort(byteOrder) } { current.getShorts(_, _, _)(byteOrder) } + + def getInts(xs: Array[Int], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + getToArray(xs, offset, n, 4) { getInt(byteOrder) } { current.getInts(_, _, _)(byteOrder) } + + def getLongs(xs: Array[Long], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + getToArray(xs, offset, n, 8) { getLong(byteOrder) } { current.getLongs(_, _, _)(byteOrder) } + + def getFloats(xs: Array[Float], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + getToArray(xs, offset, n, 8) { getFloat(byteOrder) } { current.getFloats(_, _, _)(byteOrder) } + + def getDoubles(xs: Array[Double], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + getToArray(xs, offset, n, 8) { getDouble(byteOrder) } { current.getDoubles(_, _, _)(byteOrder) } + def copyToBuffer(buffer: ByteBuffer): Int = { val n = iterators.foldLeft(0) { _ + _.copyToBuffer(buffer) } normalize() From 15507dff858bfcbcf99a9d0ff99ea6c8b9ffa105 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 17 May 2012 23:14:46 +0200 Subject: [PATCH 13/74] Added methods putByte, putShort, etc. to ByteStringBuilder --- .../src/main/scala/akka/util/ByteString.scala | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index baf77fba6f..3e11b1560a 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -535,6 +535,88 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { this } + /** + * Add a single Byte to this builder. + */ + def putByte(x: Byte): this.type = this += x + + /** + * Add a single Short to this builder. + */ + def putShort(x: Short, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = { + if (byteOrder == ByteOrder.BIG_ENDIAN) { + this += (x >>> 8).toByte + this += (x >>> 0).toByte + } else if (byteOrder == ByteOrder.LITTLE_ENDIAN) { + this += (x >>> 0).toByte + this += (x >>> 8).toByte + } else throw new IllegalArgumentException("Unknown byte order " + byteOrder) + } + + /** + * Add a single Int to this builder. + */ + def putInt(x: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = { + fillArray(4) { + case (target, offset) ⇒ { + if (byteOrder == ByteOrder.BIG_ENDIAN) { + target(offset + 0) = (x >>> 24).toByte + target(offset + 1) = (x >>> 16).toByte + target(offset + 2) = (x >>> 8).toByte + target(offset + 3) = (x >>> 0).toByte + } else if (byteOrder == ByteOrder.LITTLE_ENDIAN) { + target(offset + 0) = (x >>> 0).toByte + target(offset + 1) = (x >>> 8).toByte + target(offset + 2) = (x >>> 16).toByte + target(offset + 3) = (x >>> 24).toByte + } else throw new IllegalArgumentException("Unknown byte order " + byteOrder) + } + } + this + } + + /** + * Add a single Long to this builder. + */ + def putLong(x: Long, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = { + fillArray(8) { + case (target, offset) ⇒ { + if (byteOrder == ByteOrder.BIG_ENDIAN) { + target(offset + 0) = (x >>> 56).toByte + target(offset + 1) = (x >>> 48).toByte + target(offset + 2) = (x >>> 40).toByte + target(offset + 3) = (x >>> 32).toByte + target(offset + 4) = (x >>> 24).toByte + target(offset + 5) = (x >>> 16).toByte + target(offset + 6) = (x >>> 8).toByte + target(offset + 7) = (x >>> 0).toByte + } else if (byteOrder == ByteOrder.LITTLE_ENDIAN) { + target(offset + 0) = (x >>> 0).toByte + target(offset + 1) = (x >>> 8).toByte + target(offset + 2) = (x >>> 16).toByte + target(offset + 3) = (x >>> 24).toByte + target(offset + 4) = (x >>> 32).toByte + target(offset + 5) = (x >>> 40).toByte + target(offset + 6) = (x >>> 48).toByte + target(offset + 7) = (x >>> 56).toByte + } else throw new IllegalArgumentException("Unknown byte order " + byteOrder) + } + } + this + } + + /** + * Add a single Float to this builder. + */ + def putFloat(x: Float, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + putInt(java.lang.Float.floatToRawIntBits(x), byteOrder) + + /** + * Add a single Double to this builder. + */ + def putDouble(x: Double, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + putLong(java.lang.Double.doubleToRawLongBits(x), byteOrder) + /** * Add a number of Bytes from an array to this builder. */ From 844f09a5c7286dc4fcf019f781ecac3deb7533b0 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 6 May 2012 20:47:14 +0200 Subject: [PATCH 14/74] Added methods getByte, getShort, etc. to ByteIterator --- .../main/scala/akka/util/ByteIterator.scala | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index d574a6fbdd..9ccef832c7 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -121,6 +121,70 @@ abstract class ByteIterator extends BufferedIterator[Byte] { target } + /** + * Get a single Byte from this iterator. Identical to next(). + */ + def getByte = next() + + /** + * Get a single Short from this iterator. + */ + def getShort(byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Short = { + if (byteOrder == ByteOrder.BIG_ENDIAN) + ((next() & 0xff) << 8 | (next() & 0xff) << 0).toShort + else if (byteOrder == ByteOrder.LITTLE_ENDIAN) + ((next() & 0xff) << 0 | (next() & 0xff) << 8).toShort + else throw new IllegalArgumentException("Unknown byte order " + byteOrder) + } + + /** + * Get a single Int from this iterator. + */ + def getInt(byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Int = { + if (byteOrder == ByteOrder.BIG_ENDIAN) + ((next() & 0xff) << 24 + | (next() & 0xff) << 16 + | (next() & 0xff) << 8 + | (next() & 0xff) << 0) + else if (byteOrder == ByteOrder.LITTLE_ENDIAN) + ((next() & 0xff) << 0 + | (next() & 0xff) << 8 + | (next() & 0xff) << 16 + | (next() & 0xff) << 24) + else throw new IllegalArgumentException("Unknown byte order " + byteOrder) + } + + /** + * Get a single Long from this iterator. + */ + def getLong(byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Long = { + if (byteOrder == ByteOrder.BIG_ENDIAN) + ((next().toLong & 0xff) << 56 + | (next().toLong & 0xff) << 48 + | (next().toLong & 0xff) << 40 + | (next().toLong & 0xff) << 32 + | (next().toLong & 0xff) << 24 + | (next().toLong & 0xff) << 16 + | (next().toLong & 0xff) << 8 + | (next().toLong & 0xff) << 0) + else if (byteOrder == ByteOrder.LITTLE_ENDIAN) + ((next().toLong & 0xff) << 0 + | (next().toLong & 0xff) << 8 + | (next().toLong & 0xff) << 16 + | (next().toLong & 0xff) << 24 + | (next().toLong & 0xff) << 32 + | (next().toLong & 0xff) << 40 + | (next().toLong & 0xff) << 48 + | (next().toLong & 0xff) << 56) + else throw new IllegalArgumentException("Unknown byte order " + byteOrder) + } + + def getFloat(byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Float = + java.lang.Float.intBitsToFloat(getInt(byteOrder)) + + def getDouble(byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Double = + java.lang.Double.longBitsToDouble(getLong(byteOrder)) + /** * Get a specific number of Bytes from this iterator. In contrast to * copyToArray, this method will fail if length < n or if (xs.length - offset) < n. From 5d7823fd49d1f9df85b7eab13034f9fb8e0a597e Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Fri, 11 May 2012 19:36:13 +0200 Subject: [PATCH 15/74] Made byteOrder implicit in ByteStringBuilder methods --- .../src/main/scala/akka/util/ByteString.scala | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 3e11b1560a..b11a43b42e 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -543,7 +543,7 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { /** * Add a single Short to this builder. */ - def putShort(x: Short, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = { + def putShort(x: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = { if (byteOrder == ByteOrder.BIG_ENDIAN) { this += (x >>> 8).toByte this += (x >>> 0).toByte @@ -556,7 +556,7 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { /** * Add a single Int to this builder. */ - def putInt(x: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = { + def putInt(x: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = { fillArray(4) { case (target, offset) ⇒ { if (byteOrder == ByteOrder.BIG_ENDIAN) { @@ -578,7 +578,7 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { /** * Add a single Long to this builder. */ - def putLong(x: Long, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = { + def putLong(x: Long)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = { fillArray(8) { case (target, offset) ⇒ { if (byteOrder == ByteOrder.BIG_ENDIAN) { @@ -608,14 +608,14 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { /** * Add a single Float to this builder. */ - def putFloat(x: Float, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = - putInt(java.lang.Float.floatToRawIntBits(x), byteOrder) + def putFloat(x: Float)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + putInt(java.lang.Float.floatToRawIntBits(x))(byteOrder) /** * Add a single Double to this builder. */ - def putDouble(x: Double, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = - putLong(java.lang.Double.doubleToRawLongBits(x), byteOrder) + def putDouble(x: Double)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + putLong(java.lang.Double.doubleToRawLongBits(x))(byteOrder) /** * Add a number of Bytes from an array to this builder. @@ -626,31 +626,31 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { /** * Add a number of Shorts from an array to this builder. */ - def putShorts(array: Array[Short], start: Int, len: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + def putShorts(array: Array[Short], start: Int, len: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = fillByteBuffer(len * 2, byteOrder) { _.asShortBuffer.put(array, start, len) } /** * Add a number of Ints from an array to this builder. */ - def putInts(array: Array[Int], start: Int, len: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + def putInts(array: Array[Int], start: Int, len: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = fillByteBuffer(len * 4, byteOrder) { _.asIntBuffer.put(array, start, len) } /** * Add a number of Longs from an array to this builder. */ - def putLongs(array: Array[Long], start: Int, len: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + def putLongs(array: Array[Long], start: Int, len: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = fillByteBuffer(len * 8, byteOrder) { _.asLongBuffer.put(array, start, len) } /** * Add a number of Floats from an array to this builder. */ - def putFloats(array: Array[Float], start: Int, len: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + def putFloats(array: Array[Float], start: Int, len: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = fillByteBuffer(len * 4, byteOrder) { _.asFloatBuffer.put(array, start, len) } /** * Add a number of Doubles from an array to this builder. */ - def putDoubles(array: Array[Double], start: Int, len: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + def putDoubles(array: Array[Double], start: Int, len: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = fillByteBuffer(len * 8, byteOrder) { _.asDoubleBuffer.put(array, start, len) } def clear() { From 18e6e3ed7a764c9026e94b1cb770575358a8c80e Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 17 May 2012 14:13:36 +0200 Subject: [PATCH 16/74] Made byteOrder implicit in ByteIterator methods --- .../main/scala/akka/util/ByteIterator.scala | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 9ccef832c7..2b8e970d3e 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -129,7 +129,7 @@ abstract class ByteIterator extends BufferedIterator[Byte] { /** * Get a single Short from this iterator. */ - def getShort(byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Short = { + def getShort(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Short = { if (byteOrder == ByteOrder.BIG_ENDIAN) ((next() & 0xff) << 8 | (next() & 0xff) << 0).toShort else if (byteOrder == ByteOrder.LITTLE_ENDIAN) @@ -140,7 +140,7 @@ abstract class ByteIterator extends BufferedIterator[Byte] { /** * Get a single Int from this iterator. */ - def getInt(byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Int = { + def getInt(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Int = { if (byteOrder == ByteOrder.BIG_ENDIAN) ((next() & 0xff) << 24 | (next() & 0xff) << 16 @@ -157,7 +157,7 @@ abstract class ByteIterator extends BufferedIterator[Byte] { /** * Get a single Long from this iterator. */ - def getLong(byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Long = { + def getLong(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Long = { if (byteOrder == ByteOrder.BIG_ENDIAN) ((next().toLong & 0xff) << 56 | (next().toLong & 0xff) << 48 @@ -179,10 +179,10 @@ abstract class ByteIterator extends BufferedIterator[Byte] { else throw new IllegalArgumentException("Unknown byte order " + byteOrder) } - def getFloat(byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Float = + def getFloat(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Float = java.lang.Float.intBitsToFloat(getInt(byteOrder)) - def getDouble(byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Double = + def getDouble(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Double = java.lang.Double.longBitsToDouble(getLong(byteOrder)) /** @@ -194,27 +194,27 @@ abstract class ByteIterator extends BufferedIterator[Byte] { /** * Get a number of Shorts from this iterator. */ - def getShorts(xs: Array[Short], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type + def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type /** * Get a number of Ints from this iterator. */ - def getInts(xs: Array[Int], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type + def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type /** * Get a number of Longs from this iterator. */ - def getLongs(xs: Array[Long], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type + def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type /** * Get a number of Floats from this iterator. */ - def getFloats(xs: Array[Float], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type + def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type /** * Get a number of Doubles from this iterator. */ - def getDoubles(xs: Array[Double], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type + def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type /** * Copy as many bytes as possible to a ByteBuffer, starting from it's @@ -342,19 +342,19 @@ class ByteArrayIterator private (private var array: Array[Byte], private var fro } else Iterator.empty.next } - def getShorts(xs: Array[Short], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = { toByteString.asByteBuffer.order(byteOrder).asShortBuffer.get(xs, offset, n); drop(2 * n) } - def getInts(xs: Array[Int], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = { toByteString.asByteBuffer.order(byteOrder).asIntBuffer.get(xs, offset, n); drop(4 * n) } - def getLongs(xs: Array[Long], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = { toByteString.asByteBuffer.order(byteOrder).asLongBuffer.get(xs, offset, n); drop(8 * n) } - def getFloats(xs: Array[Float], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = { toByteString.asByteBuffer.order(byteOrder).asFloatBuffer.get(xs, offset, n); drop(4 * n) } - def getDoubles(xs: Array[Double], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = { toByteString.asByteBuffer.order(byteOrder).asDoubleBuffer.get(xs, offset, n); drop(8 * n) } def copyToBuffer(buffer: ByteBuffer): Int = { @@ -525,19 +525,19 @@ class MultiByteArrayIterator private (private var iterators: List[ByteArrayItera def getBytes(xs: Array[Byte], offset: Int, n: Int) = getToArray(xs, offset, n, 1) { getByte } { getBytes(_, _, _) } - def getShorts(xs: Array[Short], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = getToArray(xs, offset, n, 2) { getShort(byteOrder) } { current.getShorts(_, _, _)(byteOrder) } - def getInts(xs: Array[Int], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = getToArray(xs, offset, n, 4) { getInt(byteOrder) } { current.getInts(_, _, _)(byteOrder) } - def getLongs(xs: Array[Long], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = getToArray(xs, offset, n, 8) { getLong(byteOrder) } { current.getLongs(_, _, _)(byteOrder) } - def getFloats(xs: Array[Float], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = getToArray(xs, offset, n, 8) { getFloat(byteOrder) } { current.getFloats(_, _, _)(byteOrder) } - def getDoubles(xs: Array[Double], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = getToArray(xs, offset, n, 8) { getDouble(byteOrder) } { current.getDoubles(_, _, _)(byteOrder) } def copyToBuffer(buffer: ByteBuffer): Int = { From 266c67679aa0d354724b8410824c1c41c15b3839 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 17 May 2012 17:52:33 +0200 Subject: [PATCH 17/74] Added methods isCompact and isContiguous to ByteString * The ability to check whether a ByteString is compact / contiguous can be very useful when optimizing user code --- .../src/main/scala/akka/util/ByteString.scala | 49 +++++++++++++++---- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index b11a43b42e..94fa7a4ea3 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -119,8 +119,10 @@ object ByteString { override def clone: CompactByteString = ByteString1C(toArray) + def isCompact = (length == bytes.length) + def compact: CompactByteString = - if (length == bytes.length) ByteString1C(bytes) else clone + if (isCompact) ByteString1C(bytes) else clone def asByteBuffer: ByteBuffer = { val buffer = ByteBuffer.wrap(bytes, startIndex, length).asReadOnlyBuffer @@ -191,6 +193,7 @@ object ByteString { * A ByteString with 2 or more fragments. */ final class ByteStrings private (val bytestrings: Vector[ByteString1], val length: Int) extends ByteString { + if (bytestrings.isEmpty) throw new IllegalArgumentException("bytestrings must not be empty") def apply(idx: Int): Byte = if (0 <= idx && idx < length) { @@ -215,15 +218,29 @@ object ByteString { } } - def contiguous = compact - def compact: CompactByteString = { - val ar = new Array[Byte](length) - var pos = 0 - bytestrings foreach { b ⇒ - b.copyToArray(ar, pos, b.length) - pos += b.length + def isContiguous = (bytestrings.length == 1) + + def contiguous = { + if (isContiguous) bytestrings.head + else compact + } + + def isCompact = { + if (bytestrings.length == 1) bytestrings.head.isCompact + else false + } + + def compact = { + if (isCompact) bytestrings.head.compact + else { + val ar = new Array[Byte](length) + var pos = 0 + bytestrings foreach { b ⇒ + b.copyToArray(ar, pos, b.length) + pos += b.length + } + ByteString1C(ar) } - ByteString1C(ar) } def asByteBuffer: ByteBuffer = compact.asByteBuffer @@ -296,12 +313,24 @@ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimiz */ def contiguous: ContByteString + /** + * Check whether this ByteString is contiguous in memory + * (i.e. represented by a single section of a byte array). + */ + def isContiguous: Boolean + /** * Create a new ByteString with all contents compacted into a single, * full byte array. */ def compact: CompactByteString + /** + * Check whether this ByteString is compact in memory + * (i.e. represented by a single, full byte array). + */ + def isCompact: Boolean + /** * Returns a read-only ByteBuffer that directly wraps this ByteString * if it is not fragmented. @@ -409,6 +438,7 @@ object CompactByteString { * operations more efficient than on chunked ByteString instances. */ sealed abstract class ContByteString extends ByteString { + def isContiguous = true def contiguous = this } @@ -419,6 +449,7 @@ sealed abstract class ContByteString extends ByteString { * as much memory as required for its contents. */ sealed abstract class CompactByteString extends ContByteString with Serializable { + def isCompact = true def compact = this } From bc0693a2de80760949adc099adfd793bcaa5b793 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 17 May 2012 19:23:29 +0200 Subject: [PATCH 18/74] Added variants of putBytes, putShorts, etc. to ByteStringBuilder Also removed byteOrder defaults for all put... methods. --- .../src/main/scala/akka/util/ByteString.scala | 56 +++++++++++++++---- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 94fa7a4ea3..5d769d9fb6 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -574,7 +574,7 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { /** * Add a single Short to this builder. */ - def putShort(x: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = { + def putShort(x: Int)(implicit byteOrder: ByteOrder): this.type = { if (byteOrder == ByteOrder.BIG_ENDIAN) { this += (x >>> 8).toByte this += (x >>> 0).toByte @@ -587,7 +587,7 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { /** * Add a single Int to this builder. */ - def putInt(x: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = { + def putInt(x: Int)(implicit byteOrder: ByteOrder): this.type = { fillArray(4) { case (target, offset) ⇒ { if (byteOrder == ByteOrder.BIG_ENDIAN) { @@ -609,7 +609,7 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { /** * Add a single Long to this builder. */ - def putLong(x: Long)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = { + def putLong(x: Long)(implicit byteOrder: ByteOrder): this.type = { fillArray(8) { case (target, offset) ⇒ { if (byteOrder == ByteOrder.BIG_ENDIAN) { @@ -639,15 +639,21 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { /** * Add a single Float to this builder. */ - def putFloat(x: Float)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + def putFloat(x: Float)(implicit byteOrder: ByteOrder): this.type = putInt(java.lang.Float.floatToRawIntBits(x))(byteOrder) /** * Add a single Double to this builder. */ - def putDouble(x: Double)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + def putDouble(x: Double)(implicit byteOrder: ByteOrder): this.type = putLong(java.lang.Double.doubleToRawLongBits(x))(byteOrder) + /** + * Add a number of Bytes from an array to this builder. + */ + def putBytes(array: Array[Byte]): this.type = + putBytes(array, 0, array.length) + /** * Add a number of Bytes from an array to this builder. */ @@ -657,31 +663,61 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { /** * Add a number of Shorts from an array to this builder. */ - def putShorts(array: Array[Short], start: Int, len: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + def putShorts(array: Array[Short])(implicit byteOrder: ByteOrder): this.type = + putShorts(array, 0, array.length)(byteOrder) + + /** + * Add a number of Shorts from an array to this builder. + */ + def putShorts(array: Array[Short], start: Int, len: Int)(implicit byteOrder: ByteOrder): this.type = fillByteBuffer(len * 2, byteOrder) { _.asShortBuffer.put(array, start, len) } /** * Add a number of Ints from an array to this builder. */ - def putInts(array: Array[Int], start: Int, len: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + def putInts(array: Array[Int])(implicit byteOrder: ByteOrder): this.type = + putInts(array, 0, array.length)(byteOrder) + + /** + * Add a number of Ints from an array to this builder. + */ + def putInts(array: Array[Int], start: Int, len: Int)(implicit byteOrder: ByteOrder): this.type = fillByteBuffer(len * 4, byteOrder) { _.asIntBuffer.put(array, start, len) } /** * Add a number of Longs from an array to this builder. */ - def putLongs(array: Array[Long], start: Int, len: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + def putLongs(array: Array[Long])(implicit byteOrder: ByteOrder): this.type = + putLongs(array, 0, array.length)(byteOrder) + + /** + * Add a number of Longs from an array to this builder. + */ + def putLongs(array: Array[Long], start: Int, len: Int)(implicit byteOrder: ByteOrder): this.type = fillByteBuffer(len * 8, byteOrder) { _.asLongBuffer.put(array, start, len) } /** * Add a number of Floats from an array to this builder. */ - def putFloats(array: Array[Float], start: Int, len: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + def putFloats(array: Array[Float])(implicit byteOrder: ByteOrder): this.type = + putFloats(array, 0, array.length)(byteOrder) + + /** + * Add a number of Floats from an array to this builder. + */ + def putFloats(array: Array[Float], start: Int, len: Int)(implicit byteOrder: ByteOrder): this.type = fillByteBuffer(len * 4, byteOrder) { _.asFloatBuffer.put(array, start, len) } /** * Add a number of Doubles from an array to this builder. */ - def putDoubles(array: Array[Double], start: Int, len: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + def putDoubles(array: Array[Double])(implicit byteOrder: ByteOrder): this.type = + putDoubles(array, 0, array.length)(byteOrder) + + /** + * Add a number of Doubles from an array to this builder. + */ + def putDoubles(array: Array[Double], start: Int, len: Int)(implicit byteOrder: ByteOrder): this.type = fillByteBuffer(len * 8, byteOrder) { _.asDoubleBuffer.put(array, start, len) } def clear() { From a032603858b49f5001ae9d8b721e03b7cde96d67 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 17 May 2012 19:23:59 +0200 Subject: [PATCH 19/74] Added variants of getBytes, getShorts, etc. to ByteByteIterator Also removed byteOrder defaults for all get... methods. --- .../main/scala/akka/util/ByteIterator.scala | 76 ++++++++++++++----- 1 file changed, 56 insertions(+), 20 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 2b8e970d3e..c08a50be89 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -129,7 +129,7 @@ abstract class ByteIterator extends BufferedIterator[Byte] { /** * Get a single Short from this iterator. */ - def getShort(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Short = { + def getShort(implicit byteOrder: ByteOrder): Short = { if (byteOrder == ByteOrder.BIG_ENDIAN) ((next() & 0xff) << 8 | (next() & 0xff) << 0).toShort else if (byteOrder == ByteOrder.LITTLE_ENDIAN) @@ -140,7 +140,7 @@ abstract class ByteIterator extends BufferedIterator[Byte] { /** * Get a single Int from this iterator. */ - def getInt(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Int = { + def getInt(implicit byteOrder: ByteOrder): Int = { if (byteOrder == ByteOrder.BIG_ENDIAN) ((next() & 0xff) << 24 | (next() & 0xff) << 16 @@ -157,7 +157,7 @@ abstract class ByteIterator extends BufferedIterator[Byte] { /** * Get a single Long from this iterator. */ - def getLong(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Long = { + def getLong(implicit byteOrder: ByteOrder): Long = { if (byteOrder == ByteOrder.BIG_ENDIAN) ((next().toLong & 0xff) << 56 | (next().toLong & 0xff) << 48 @@ -179,12 +179,18 @@ abstract class ByteIterator extends BufferedIterator[Byte] { else throw new IllegalArgumentException("Unknown byte order " + byteOrder) } - def getFloat(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Float = + def getFloat(implicit byteOrder: ByteOrder): Float = java.lang.Float.intBitsToFloat(getInt(byteOrder)) - def getDouble(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Double = + def getDouble(implicit byteOrder: ByteOrder): Double = java.lang.Double.longBitsToDouble(getLong(byteOrder)) + /** + * Get a specific number of Bytes from this iterator. In contrast to + * copyToArray, this method will fail if this.len < xs.length. + */ + def getBytes(xs: Array[Byte]): this.type = getBytes(xs, 0, xs.length) + /** * Get a specific number of Bytes from this iterator. In contrast to * copyToArray, this method will fail if length < n or if (xs.length - offset) < n. @@ -194,27 +200,57 @@ abstract class ByteIterator extends BufferedIterator[Byte] { /** * Get a number of Shorts from this iterator. */ - def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type + def getShorts(xs: Array[Short])(implicit byteOrder: ByteOrder): this.type = + getShorts(xs, 0, xs.length)(byteOrder) + + /** + * Get a number of Shorts from this iterator. + */ + def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type /** * Get a number of Ints from this iterator. */ - def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type + def getInts(xs: Array[Int])(implicit byteOrder: ByteOrder): this.type = + getInts(xs, 0, xs.length)(byteOrder) + + /** + * Get a number of Ints from this iterator. + */ + def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type /** * Get a number of Longs from this iterator. */ - def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type + def getLongs(xs: Array[Long])(implicit byteOrder: ByteOrder): this.type = + getLongs(xs, 0, xs.length)(byteOrder) + + /** + * Get a number of Longs from this iterator. + */ + def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type /** * Get a number of Floats from this iterator. */ - def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type + def getFloats(xs: Array[Float])(implicit byteOrder: ByteOrder): this.type = + getFloats(xs, 0, xs.length)(byteOrder) + + /** + * Get a number of Floats from this iterator. + */ + def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type /** * Get a number of Doubles from this iterator. */ - def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type + def getDoubles(xs: Array[Double])(implicit byteOrder: ByteOrder): this.type = + getDoubles(xs, 0, xs.length)(byteOrder) + + /** + * Get a number of Doubles from this iterator. + */ + def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type /** * Copy as many bytes as possible to a ByteBuffer, starting from it's @@ -342,19 +378,19 @@ class ByteArrayIterator private (private var array: Array[Byte], private var fro } else Iterator.empty.next } - def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = { toByteString.asByteBuffer.order(byteOrder).asShortBuffer.get(xs, offset, n); drop(2 * n) } - def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = { toByteString.asByteBuffer.order(byteOrder).asIntBuffer.get(xs, offset, n); drop(4 * n) } - def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = { toByteString.asByteBuffer.order(byteOrder).asLongBuffer.get(xs, offset, n); drop(8 * n) } - def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = { toByteString.asByteBuffer.order(byteOrder).asFloatBuffer.get(xs, offset, n); drop(4 * n) } - def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = { toByteString.asByteBuffer.order(byteOrder).asDoubleBuffer.get(xs, offset, n); drop(8 * n) } def copyToBuffer(buffer: ByteBuffer): Int = { @@ -525,19 +561,19 @@ class MultiByteArrayIterator private (private var iterators: List[ByteArrayItera def getBytes(xs: Array[Byte], offset: Int, n: Int) = getToArray(xs, offset, n, 1) { getByte } { getBytes(_, _, _) } - def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = getToArray(xs, offset, n, 2) { getShort(byteOrder) } { current.getShorts(_, _, _)(byteOrder) } - def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = getToArray(xs, offset, n, 4) { getInt(byteOrder) } { current.getInts(_, _, _)(byteOrder) } - def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = getToArray(xs, offset, n, 8) { getLong(byteOrder) } { current.getLongs(_, _, _)(byteOrder) } - def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = getToArray(xs, offset, n, 8) { getFloat(byteOrder) } { current.getFloats(_, _, _)(byteOrder) } - def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = getToArray(xs, offset, n, 8) { getDouble(byteOrder) } { current.getDoubles(_, _, _)(byteOrder) } def copyToBuffer(buffer: ByteBuffer): Int = { From 7d3edcddbc21d2cfc8ec7cd05e87b43903996162 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Fri, 18 May 2012 17:36:16 +0200 Subject: [PATCH 20/74] Improved implementation of ByteStrings.iterator --- akka-actor/src/main/scala/akka/util/ByteString.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 5d769d9fb6..56b1b98b8d 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -206,7 +206,7 @@ object ByteString { bytestrings(pos)(idx - seen) } else throw new IndexOutOfBoundsException(idx.toString) - override def iterator = MultiByteArrayIterator(bytestrings.toList.map { _.iterator }) + override def iterator = MultiByteArrayIterator(bytestrings.map(_.iterator)(collection.breakOut)) def ++(that: ByteString): ByteString = { if (that.isEmpty) this From 0c76e0f0f6726ec1f14b8cd387b85667e42d063a Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sat, 26 May 2012 11:30:23 +0200 Subject: [PATCH 21/74] Moved ByteIterator implementations into object ByteIterator --- .../main/scala/akka/util/ByteIterator.scala | 630 +++++++++--------- .../src/main/scala/akka/util/ByteString.scala | 6 +- 2 files changed, 318 insertions(+), 318 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index c08a50be89..8b9b95d31b 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -36,6 +36,320 @@ object ByteIterator { nSkip } } + + object ByteArrayIterator { + private val emptyArray = Array.ofDim[Byte](0) + + protected[akka] def apply(array: Array[Byte]): ByteArrayIterator = + new ByteArrayIterator(array, 0, array.length) + + protected[akka] def apply(array: Array[Byte], from: Int, until: Int): ByteArrayIterator = + new ByteArrayIterator(array, from, until) + + val empty: ByteArrayIterator = apply(Array.empty[Byte]) + } + + class ByteArrayIterator private (private var array: Array[Byte], private var from: Int, private var until: Int) extends ByteIterator { + protected[util] final def internalArray = array + protected[util] final def internalFrom = from + protected[util] final def internalUntil = until + + final def isIdenticalTo(that: Iterator[Byte]) = that match { + case that: ByteArrayIterator ⇒ + ((this.array) eq (that.internalArray)) && + ((this.from) == (that.from)) && ((this.until) == (that.until)) + case _ ⇒ false + } + + @inline final def len = until - from + + @inline final def hasNext = from < until + + @inline final def head = array(from) + + final def next() = { + if (!hasNext) Iterator.empty.next + else { val i = from; from = from + 1; array(i) } + } + + def clear() { this.array = ByteArrayIterator.emptyArray; from = 0; until = from } + + final override def length = { val l = len; clear(); l } + + final override def ++(that: TraversableOnce[Byte]) = that match { + case that: ByteIterator ⇒ { + if (that.isEmpty) this + else if (this.isEmpty) that + else that match { + case that: ByteArrayIterator ⇒ { + if ((this.array eq that.array) && (this.until == that.from)) { + this.until = that.until + that.clear() + this + } else { + val result = MultiByteArrayIterator(List(this, that)) + this.clear() + result + } + } + case that: MultiByteArrayIterator ⇒ this +: that + } + } + case _ ⇒ super.++(that) + } + + final override def clone = new ByteArrayIterator(array, from, until) + + final override def take(n: Int) = { + until = until min (from + (0 max n)) + this + } + + final override def drop(n: Int) = { + from = until min (from + (0 max n)) + this + } + + final override def takeWhile(p: Byte ⇒ Boolean) = { + val prev = from + dropWhile(p) + until = from; from = prev + this + } + + final override def dropWhile(p: Byte ⇒ Boolean) = { + var stop = false + while (!stop && hasNext) { + if (p(array(from))) { from = from + 1 } else { stop = true } + } + this + } + + final override def copyToArray[B >: Byte](xs: Array[B], start: Int, len: Int): Unit = { + val n = 0 max ((xs.length - start) min this.len min len) + Array.copy(this.array, from, xs, start, n) + this.drop(n) + } + + final override def toByteString = { + val result = + if ((from == 0) && (until == array.length)) ByteString.ByteString1C(array) + else ByteString.ByteString1(array, from, len) + clear() + result + } + + def getBytes(xs: Array[Byte], offset: Int, n: Int) = { + if (n <= this.len) { + Array.copy(this.array, this.from, xs, offset, n) + this + } else Iterator.empty.next + } + + def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + { toByteString.asByteBuffer.order(byteOrder).asShortBuffer.get(xs, offset, n); drop(2 * n) } + + def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + { toByteString.asByteBuffer.order(byteOrder).asIntBuffer.get(xs, offset, n); drop(4 * n) } + + def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + { toByteString.asByteBuffer.order(byteOrder).asLongBuffer.get(xs, offset, n); drop(8 * n) } + + def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + { toByteString.asByteBuffer.order(byteOrder).asFloatBuffer.get(xs, offset, n); drop(4 * n) } + + def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + { toByteString.asByteBuffer.order(byteOrder).asDoubleBuffer.get(xs, offset, n); drop(8 * n) } + + def copyToBuffer(buffer: ByteBuffer): Int = { + val copyLength = math.min(buffer.remaining, len) + if (copyLength > 0) { + buffer.put(array, from, copyLength) + drop(copyLength) + } + copyLength + } + } + + object MultiByteArrayIterator { + protected val clearedList = List(ByteArrayIterator.empty) + + val empty = new MultiByteArrayIterator(Nil) + + protected[akka] def apply(iterators: List[ByteArrayIterator]): MultiByteArrayIterator = + new MultiByteArrayIterator(iterators) + } + + class MultiByteArrayIterator private (private var iterators: List[ByteArrayIterator]) extends ByteIterator { + // After normalization: + // * iterators.isEmpty == false + // * (!iterator.head.isEmpty || iterators.tail.isEmpty) == true + private def normalize(): this.type = { + @tailrec def norm(xs: List[ByteArrayIterator]): List[ByteArrayIterator] = { + if (xs.isEmpty) MultiByteArrayIterator.clearedList + else if (xs.head.isEmpty) norm(xs.tail) + else xs + } + iterators = norm(iterators) + this + } + normalize() + + @inline private def current = iterators.head + @inline private def dropCurrent() { iterators = iterators.tail } + @inline def clear() { iterators = MultiByteArrayIterator.empty.iterators } + + final def isIdenticalTo(that: Iterator[Byte]) = false + + @inline final def hasNext = current.hasNext + + @inline final def head = current.head + + final def next() = { + val result = current.next() + normalize() + result + } + + final override def len = iterators.foldLeft(0) { _ + _.len } + + final override def length = { + var result = len + clear() + result + } + + def +:(that: ByteArrayIterator): this.type = { + iterators = that +: iterators + this + } + + final override def ++(that: TraversableOnce[Byte]) = that match { + case that: ByteIterator ⇒ { + if (that.isEmpty) this + else if (this.isEmpty) that + else { + that match { + case that: ByteArrayIterator ⇒ { + iterators = this.iterators :+ that + that.clear() + this + } + case that: MultiByteArrayIterator ⇒ { + iterators = this.iterators ++ that.iterators + that.clear() + this + } + } + } + } + case _ ⇒ super.++(that) + } + + final override def clone = new MultiByteArrayIterator(iterators map { _.clone }) + + final override def take(n: Int) = { + var rest = n + val builder = new ListBuffer[ByteArrayIterator] + while ((rest > 0) && !iterators.isEmpty) { + current.take(rest) + if (current.hasNext) { + rest -= current.len + builder += current + } + iterators = iterators.tail + } + iterators = builder.result + normalize() + } + + @tailrec final override def drop(n: Int) = if ((n > 0) && !isEmpty) { + val nCurrent = math.min(n, current.len) + current.drop(n) + val rest = n - nCurrent + assert(current.isEmpty || (rest == 0)) + normalize() + drop(rest) + } else this + + final override def takeWhile(p: Byte ⇒ Boolean) = { + var stop = false + var builder = new ListBuffer[ByteArrayIterator] + while (!stop && !iterators.isEmpty) { + val lastLen = current.len + current.takeWhile(p) + if (current.hasNext) builder += current + if (current.len < lastLen) stop = true + dropCurrent() + } + iterators = builder.result + normalize() + } + + @tailrec final override def dropWhile(p: Byte ⇒ Boolean) = if (!isEmpty) { + current.dropWhile(p) + val dropMore = current.isEmpty + normalize() + if (dropMore) dropWhile(p) else this + } else this + + final override def copyToArray[B >: Byte](xs: Array[B], start: Int, len: Int): Unit = { + var pos = start + var rest = len + while ((rest > 0) && !iterators.isEmpty) { + val n = 0 max ((xs.length - pos) min current.len min rest) + current.copyToArray(xs, pos, n) + pos += n + rest -= n + dropCurrent() + } + normalize() + } + + final override def toByteString = { + val result = iterators.foldLeft(ByteString.empty) { _ ++ _.toByteString } + clear() + result + } + + @tailrec protected final def getToArray[A](xs: Array[A], offset: Int, n: Int, elemSize: Int)(getSingle: ⇒ A)(getMult: (Array[A], Int, Int) ⇒ Unit): this.type = if (n <= 0) this else { + if (isEmpty) Iterator.empty.next + val nDone = if (current.len >= elemSize) { + val nCurrent = math.min(elemSize, current.len / elemSize) + getMult(xs, offset, nCurrent) + nCurrent + } else { + xs(offset) = getSingle + 1 + } + normalize() + getToArray(xs, offset + nDone, n - nDone, elemSize)(getSingle)(getMult) + } + + def getBytes(xs: Array[Byte], offset: Int, n: Int) = + getToArray(xs, offset, n, 1) { getByte } { getBytes(_, _, _) } + + def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + getToArray(xs, offset, n, 2) { getShort(byteOrder) } { current.getShorts(_, _, _)(byteOrder) } + + def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + getToArray(xs, offset, n, 4) { getInt(byteOrder) } { current.getInts(_, _, _)(byteOrder) } + + def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + getToArray(xs, offset, n, 8) { getLong(byteOrder) } { current.getLongs(_, _, _)(byteOrder) } + + def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + getToArray(xs, offset, n, 8) { getFloat(byteOrder) } { current.getFloats(_, _, _)(byteOrder) } + + def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + getToArray(xs, offset, n, 8) { getDouble(byteOrder) } { current.getDoubles(_, _, _)(byteOrder) } + + def copyToBuffer(buffer: ByteBuffer): Int = { + val n = iterators.foldLeft(0) { _ + _.copyToBuffer(buffer) } + normalize() + n + } + } } abstract class ByteIterator extends BufferedIterator[Byte] { @@ -51,7 +365,7 @@ abstract class ByteIterator extends BufferedIterator[Byte] { def ++(that: TraversableOnce[Byte]): ByteIterator = { if (that.isEmpty) this - else ByteArrayIterator(that.toArray) + else ByteIterator.ByteArrayIterator(that.toArray) } // *must* be overridden by derived classes @@ -268,317 +582,3 @@ abstract class ByteIterator extends BufferedIterator[Byte] { */ def asInputStream: java.io.InputStream = new ByteIterator.InputStreamWrapper(this) } - -object ByteArrayIterator { - private val emptyArray = Array.ofDim[Byte](0) - - protected[akka] def apply(array: Array[Byte]): ByteArrayIterator = - new ByteArrayIterator(array, 0, array.length) - - protected[akka] def apply(array: Array[Byte], from: Int, until: Int): ByteArrayIterator = - new ByteArrayIterator(array, from, until) - - val empty: ByteArrayIterator = apply(Array.empty[Byte]) -} - -class ByteArrayIterator private (private var array: Array[Byte], private var from: Int, private var until: Int) extends ByteIterator { - protected[util] final def internalArray = array - protected[util] final def internalFrom = from - protected[util] final def internalUntil = until - - final def isIdenticalTo(that: Iterator[Byte]) = that match { - case that: ByteArrayIterator ⇒ - ((this.array) eq (that.internalArray)) && - ((this.from) == (that.from)) && ((this.until) == (that.until)) - case _ ⇒ false - } - - @inline final def len = until - from - - @inline final def hasNext = from < until - - @inline final def head = array(from) - - final def next() = { - if (!hasNext) Iterator.empty.next - else { val i = from; from = from + 1; array(i) } - } - - def clear() { this.array = ByteArrayIterator.emptyArray; from = 0; until = from } - - final override def length = { val l = len; clear(); l } - - final override def ++(that: TraversableOnce[Byte]) = that match { - case that: ByteIterator ⇒ { - if (that.isEmpty) this - else if (this.isEmpty) that - else that match { - case that: ByteArrayIterator ⇒ { - if ((this.array eq that.array) && (this.until == that.from)) { - this.until = that.until - that.clear() - this - } else { - val result = MultiByteArrayIterator(List(this, that)) - this.clear() - result - } - } - case that: MultiByteArrayIterator ⇒ this +: that - } - } - case _ ⇒ super.++(that) - } - - final override def clone = new ByteArrayIterator(array, from, until) - - final override def take(n: Int) = { - until = until min (from + (0 max n)) - this - } - - final override def drop(n: Int) = { - from = until min (from + (0 max n)) - this - } - - final override def takeWhile(p: Byte ⇒ Boolean) = { - val prev = from - dropWhile(p) - until = from; from = prev - this - } - - final override def dropWhile(p: Byte ⇒ Boolean) = { - var stop = false - while (!stop && hasNext) { - if (p(array(from))) { from = from + 1 } else { stop = true } - } - this - } - - final override def copyToArray[B >: Byte](xs: Array[B], start: Int, len: Int): Unit = { - val n = 0 max ((xs.length - start) min this.len min len) - Array.copy(this.array, from, xs, start, n) - this.drop(n) - } - - final override def toByteString = { - val result = - if ((from == 0) && (until == array.length)) ByteString.ByteString1C(array) - else ByteString.ByteString1(array, from, len) - clear() - result - } - - def getBytes(xs: Array[Byte], offset: Int, n: Int) = { - if (n <= this.len) { - Array.copy(this.array, this.from, xs, offset, n) - this - } else Iterator.empty.next - } - - def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = - { toByteString.asByteBuffer.order(byteOrder).asShortBuffer.get(xs, offset, n); drop(2 * n) } - - def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = - { toByteString.asByteBuffer.order(byteOrder).asIntBuffer.get(xs, offset, n); drop(4 * n) } - - def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = - { toByteString.asByteBuffer.order(byteOrder).asLongBuffer.get(xs, offset, n); drop(8 * n) } - - def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = - { toByteString.asByteBuffer.order(byteOrder).asFloatBuffer.get(xs, offset, n); drop(4 * n) } - - def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = - { toByteString.asByteBuffer.order(byteOrder).asDoubleBuffer.get(xs, offset, n); drop(8 * n) } - - def copyToBuffer(buffer: ByteBuffer): Int = { - val copyLength = math.min(buffer.remaining, len) - if (copyLength > 0) { - buffer.put(array, from, copyLength) - drop(copyLength) - } - copyLength - } -} - -object MultiByteArrayIterator { - protected val clearedList = List(ByteArrayIterator.empty) - - val empty = new MultiByteArrayIterator(Nil) - - protected[akka] def apply(iterators: List[ByteArrayIterator]): MultiByteArrayIterator = - new MultiByteArrayIterator(iterators) -} - -class MultiByteArrayIterator private (private var iterators: List[ByteArrayIterator]) extends ByteIterator { - // After normalization: - // * iterators.isEmpty == false - // * (!iterator.head.isEmpty || iterators.tail.isEmpty) == true - private def normalize(): this.type = { - @tailrec def norm(xs: List[ByteArrayIterator]): List[ByteArrayIterator] = { - if (xs.isEmpty) MultiByteArrayIterator.clearedList - else if (xs.head.isEmpty) norm(xs.tail) - else xs - } - iterators = norm(iterators) - this - } - normalize() - - @inline private def current = iterators.head - @inline private def dropCurrent() { iterators = iterators.tail } - @inline def clear() { iterators = MultiByteArrayIterator.empty.iterators } - - final def isIdenticalTo(that: Iterator[Byte]) = false - - @inline final def hasNext = current.hasNext - - @inline final def head = current.head - - final def next() = { - val result = current.next() - normalize() - result - } - - final override def len = iterators.foldLeft(0) { _ + _.len } - - final override def length = { - var result = len - clear() - result - } - - def +:(that: ByteArrayIterator): this.type = { - iterators = that +: iterators - this - } - - final override def ++(that: TraversableOnce[Byte]) = that match { - case that: ByteIterator ⇒ { - if (that.isEmpty) this - else if (this.isEmpty) that - else { - that match { - case that: ByteArrayIterator ⇒ { - iterators = this.iterators :+ that - that.clear() - this - } - case that: MultiByteArrayIterator ⇒ { - iterators = this.iterators ++ that.iterators - that.clear() - this - } - } - } - } - case _ ⇒ super.++(that) - } - - final override def clone = new MultiByteArrayIterator(iterators map { _.clone }) - - final override def take(n: Int) = { - var rest = n - val builder = new ListBuffer[ByteArrayIterator] - while ((rest > 0) && !iterators.isEmpty) { - current.take(rest) - if (current.hasNext) { - rest -= current.len - builder += current - } - iterators = iterators.tail - } - iterators = builder.result - normalize() - } - - @tailrec final override def drop(n: Int) = if ((n > 0) && !isEmpty) { - val nCurrent = math.min(n, current.len) - current.drop(n) - val rest = n - nCurrent - assert(current.isEmpty || (rest == 0)) - normalize() - drop(rest) - } else this - - final override def takeWhile(p: Byte ⇒ Boolean) = { - var stop = false - var builder = new ListBuffer[ByteArrayIterator] - while (!stop && !iterators.isEmpty) { - val lastLen = current.len - current.takeWhile(p) - if (current.hasNext) builder += current - if (current.len < lastLen) stop = true - dropCurrent() - } - iterators = builder.result - normalize() - } - - @tailrec final override def dropWhile(p: Byte ⇒ Boolean) = if (!isEmpty) { - current.dropWhile(p) - val dropMore = current.isEmpty - normalize() - if (dropMore) dropWhile(p) else this - } else this - - final override def copyToArray[B >: Byte](xs: Array[B], start: Int, len: Int): Unit = { - var pos = start - var rest = len - while ((rest > 0) && !iterators.isEmpty) { - val n = 0 max ((xs.length - pos) min current.len min rest) - current.copyToArray(xs, pos, n) - pos += n - rest -= n - dropCurrent() - } - normalize() - } - - final override def toByteString = { - val result = iterators.foldLeft(ByteString.empty) { _ ++ _.toByteString } - clear() - result - } - - @tailrec protected final def getToArray[A](xs: Array[A], offset: Int, n: Int, elemSize: Int)(getSingle: ⇒ A)(getMult: (Array[A], Int, Int) ⇒ Unit): this.type = if (n <= 0) this else { - if (isEmpty) Iterator.empty.next - val nDone = if (current.len >= elemSize) { - val nCurrent = math.min(elemSize, current.len / elemSize) - getMult(xs, offset, nCurrent) - nCurrent - } else { - xs(offset) = getSingle - 1 - } - normalize() - getToArray(xs, offset + nDone, n - nDone, elemSize)(getSingle)(getMult) - } - - def getBytes(xs: Array[Byte], offset: Int, n: Int) = - getToArray(xs, offset, n, 1) { getByte } { getBytes(_, _, _) } - - def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = - getToArray(xs, offset, n, 2) { getShort(byteOrder) } { current.getShorts(_, _, _)(byteOrder) } - - def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = - getToArray(xs, offset, n, 4) { getInt(byteOrder) } { current.getInts(_, _, _)(byteOrder) } - - def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = - getToArray(xs, offset, n, 8) { getLong(byteOrder) } { current.getLongs(_, _, _)(byteOrder) } - - def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = - getToArray(xs, offset, n, 8) { getFloat(byteOrder) } { current.getFloats(_, _, _)(byteOrder) } - - def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = - getToArray(xs, offset, n, 8) { getDouble(byteOrder) } { current.getDoubles(_, _, _)(byteOrder) } - - def copyToBuffer(buffer: ByteBuffer): Int = { - val n = iterators.foldLeft(0) { _ + _.copyToBuffer(buffer) } - normalize() - n - } -} diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 56b1b98b8d..bfa75894ed 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -73,7 +73,7 @@ object ByteString { override def length = bytes.length - override def iterator = ByteArrayIterator(bytes, 0, bytes.length) + override def iterator = ByteIterator.ByteArrayIterator(bytes, 0, bytes.length) def toByteString1: ByteString1 = ByteString1(bytes) @@ -108,7 +108,7 @@ object ByteString { def apply(idx: Int): Byte = bytes(checkRangeConvert(idx)) - override def iterator = ByteArrayIterator(bytes, startIndex, startIndex + length) + override def iterator = ByteIterator.ByteArrayIterator(bytes, startIndex, startIndex + length) private def checkRangeConvert(index: Int) = { if (0 <= index && length > index) @@ -206,7 +206,7 @@ object ByteString { bytestrings(pos)(idx - seen) } else throw new IndexOutOfBoundsException(idx.toString) - override def iterator = MultiByteArrayIterator(bytestrings.map(_.iterator)(collection.breakOut)) + override def iterator = ByteIterator.MultiByteArrayIterator(bytestrings.map(_.iterator)(collection.breakOut)) def ++(that: ByteString): ByteString = { if (that.isEmpty) this From d3177bf08f2d08b29a6035f1a73130e238c66ed3 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Tue, 22 May 2012 11:14:02 +0200 Subject: [PATCH 22/74] Improved return type specialization for methods implemented in super-type --- .../main/scala/akka/util/ByteIterator.scala | 30 ++++++++++++------- .../src/main/scala/akka/util/ByteString.scala | 6 ++-- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 8b9b95d31b..6bf53eeaba 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -368,25 +368,35 @@ abstract class ByteIterator extends BufferedIterator[Byte] { else ByteIterator.ByteArrayIterator(that.toArray) } - // *must* be overridden by derived classes - override def clone: ByteIterator = null + // *must* be overridden by derived classes. This construction is necessary + // to specialize the return type, as the method is already implemented in + // the parent class. + override def clone: ByteIterator = throw new UnsupportedOperationException("Method clone is not implemented in ByteIterator") final override def duplicate = (this, clone) - // *must* be overridden by derived classes - override def take(n: Int): this.type = null + // *must* be overridden by derived classes. This construction is necessary + // to specialize the return type, as the method is already implemented in + // the parent class. + override def take(n: Int): this.type = throw new UnsupportedOperationException("Method take is not implemented in ByteIterator") - // *must* be overridden by derived classes - override def drop(n: Int): this.type = null + // *must* be overridden by derived classes. This construction is necessary + // to specialize the return type, as the method is already implemented in + // the parent class. + override def drop(n: Int): this.type = throw new UnsupportedOperationException("Method drop is not implemented in ByteIterator") final override def slice(from: Int, until: Int): this.type = drop(from).take(until - from) - // *must* be overridden by derived classes - override def takeWhile(p: Byte ⇒ Boolean): this.type = null + // *must* be overridden by derived classes. This construction is necessary + // to specialize the return type, as the method is already implemented in + // the parent class. + override def takeWhile(p: Byte ⇒ Boolean): this.type = throw new UnsupportedOperationException("Method takeWhile is not implemented in ByteIterator") - // *must* be overridden by derived classes - override def dropWhile(p: Byte ⇒ Boolean): this.type = null + // *must* be overridden by derived classes. This construction is necessary + // to specialize the return type, as the method is already implemented in + // the parent class. + override def dropWhile(p: Byte ⇒ Boolean): this.type = throw new UnsupportedOperationException("Method dropWhile is not implemented in ByteIterator") final override def span(p: Byte ⇒ Boolean): (ByteIterator, ByteIterator) = { val that = clone diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index bfa75894ed..3f3f754857 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -261,8 +261,10 @@ object ByteString { sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimized[Byte, ByteString] { override protected[this] def newBuilder = ByteString.newBuilder - // *must* be overridden by derived classes - override def iterator: ByteIterator = null + // *must* be overridden by derived classes. This construction is necessary + // to specialize the return type, as the method is already implemented in + // a parent trait. + override def iterator: ByteIterator = throw new UnsupportedOperationException("Method iterator is not implemented in ByteString") @inline final override def head: Byte = this(0) @inline final override def tail: ByteString = this.drop(1) From 7273ac2f02fbead065c4559b75b658d17685f7dd Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 24 May 2012 10:08:25 +0200 Subject: [PATCH 23/74] Fixed implementation of ByteIterator.InputStreamWrapper.next --- akka-actor/src/main/scala/akka/util/ByteIterator.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 6bf53eeaba..138c549d0b 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -22,7 +22,7 @@ object ByteIterator { final class InputStreamWrapper(val iterator: ByteIterator) extends java.io.InputStream { override def available = iterator.len - def read = iterator.next.toInt + def read = if (iterator.hasNext) (iterator.next.toInt & 0xff) else -1 override def read(b: Array[Byte], off: Int, len: Int) = { val nRead = math.min(iterator.len, len - off) From 5d08cd0b923e9963c0bd288f6105ea3200bfbc8e Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sat, 26 May 2012 11:40:23 +0200 Subject: [PATCH 24/74] Removed ContByteString A proper implementation of ContByteString with specialized return types for methods like take, drop, etc. would have made things more complex than it is worth. --- .../src/main/scala/akka/util/ByteString.scala | 38 ++----------------- 1 file changed, 4 insertions(+), 34 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 3f3f754857..e4b7026056 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -102,7 +102,7 @@ object ByteString { /** * An unfragmented ByteString. */ - final class ByteString1 private (private val bytes: Array[Byte], private val startIndex: Int, val length: Int) extends ContByteString { + final class ByteString1 private (private val bytes: Array[Byte], private val startIndex: Int, val length: Int) extends ByteString { private def this(bytes: Array[Byte]) = this(bytes, 0, bytes.length) @@ -218,13 +218,6 @@ object ByteString { } } - def isContiguous = (bytestrings.length == 1) - - def contiguous = { - if (isContiguous) bytestrings.head - else compact - } - def isCompact = { if (bytestrings.length == 1) bytestrings.head.isCompact else false @@ -309,18 +302,6 @@ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimiz */ def copyToBuffer(buffer: ByteBuffer): Int = iterator.copyToBuffer(buffer) - /** - * Create a new ByteString with all contents contiguous in memory - * (in a single section of a byte array). - */ - def contiguous: ContByteString - - /** - * Check whether this ByteString is contiguous in memory - * (i.e. represented by a single section of a byte array). - */ - def isContiguous: Boolean - /** * Create a new ByteString with all contents compacted into a single, * full byte array. @@ -434,23 +415,12 @@ object CompactByteString { } /** - * A contiguous (i.e. non-fragmented) ByteString. + * A compact ByteString. * - * The ByteString is guarantieed to be contiguous in memory, making certain - * operations more efficient than on chunked ByteString instances. - */ -sealed abstract class ContByteString extends ByteString { - def isContiguous = true - def contiguous = this -} - -/** - * A compact and contiguous ByteString. - * - * The ByteString is guarantied to be contiguous in memory and to blocks only + * The ByteString is guarantied to be contiguous in memory and to use only * as much memory as required for its contents. */ -sealed abstract class CompactByteString extends ContByteString with Serializable { +sealed abstract class CompactByteString extends ByteString with Serializable { def isCompact = true def compact = this } From 18e5b625f493ee63af2592d268ecdd2cf40142fc Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Tue, 22 May 2012 19:21:49 +0200 Subject: [PATCH 25/74] Added missing class description of ByteIterator --- akka-actor/src/main/scala/akka/util/ByteIterator.scala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 138c549d0b..4aa242f38c 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -352,6 +352,10 @@ object ByteIterator { } } +/** + * An iterator over a ByteString. + */ + abstract class ByteIterator extends BufferedIterator[Byte] { def isIdenticalTo(that: Iterator[Byte]): Boolean From 75c04d89449061f42bbb4d894405c2c4456df0b9 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sat, 26 May 2012 12:08:44 +0200 Subject: [PATCH 26/74] Explicit types for everything related to ByteString and ByteStringIterator --- .../main/scala/akka/util/ByteIterator.scala | 104 +++++++++--------- .../src/main/scala/akka/util/ByteString.scala | 72 ++++++------ 2 files changed, 91 insertions(+), 85 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 4aa242f38c..e4bd8dd84e 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -20,17 +20,17 @@ object ByteIterator { * An InputStream that directly wraps a ByteIterator without copying */ final class InputStreamWrapper(val iterator: ByteIterator) extends java.io.InputStream { - override def available = iterator.len + override def available: Int = iterator.len - def read = if (iterator.hasNext) (iterator.next.toInt & 0xff) else -1 + def read: Int = if (iterator.hasNext) (iterator.next.toInt & 0xff) else -1 - override def read(b: Array[Byte], off: Int, len: Int) = { + override def read(b: Array[Byte], off: Int, len: Int): Int = { val nRead = math.min(iterator.len, len - off) iterator.copyToArray(b, off, nRead) nRead } - override def skip(n: Long) = { + override def skip(n: Long): Long = { val nSkip = math.min(iterator.len, n.toInt) iterator.drop(nSkip) nSkip @@ -38,7 +38,7 @@ object ByteIterator { } object ByteArrayIterator { - private val emptyArray = Array.ofDim[Byte](0) + private val emptyArray: Array[Byte] = Array.ofDim[Byte](0) protected[akka] def apply(array: Array[Byte]): ByteArrayIterator = new ByteArrayIterator(array, 0, array.length) @@ -54,29 +54,29 @@ object ByteIterator { protected[util] final def internalFrom = from protected[util] final def internalUntil = until - final def isIdenticalTo(that: Iterator[Byte]) = that match { + final def isIdenticalTo(that: Iterator[Byte]): Boolean = that match { case that: ByteArrayIterator ⇒ ((this.array) eq (that.internalArray)) && ((this.from) == (that.from)) && ((this.until) == (that.until)) case _ ⇒ false } - @inline final def len = until - from + @inline final def len: Int = until - from - @inline final def hasNext = from < until + @inline final def hasNext: Boolean = from < until - @inline final def head = array(from) + @inline final def head: Byte = array(from) - final def next() = { + final def next(): Byte = { if (!hasNext) Iterator.empty.next else { val i = from; from = from + 1; array(i) } } - def clear() { this.array = ByteArrayIterator.emptyArray; from = 0; until = from } + def clear(): Unit = { this.array = ByteArrayIterator.emptyArray; from = 0; until = from } - final override def length = { val l = len; clear(); l } + final override def length: Int = { val l = len; clear(); l } - final override def ++(that: TraversableOnce[Byte]) = that match { + final override def ++(that: TraversableOnce[Byte]): ByteIterator = that match { case that: ByteIterator ⇒ { if (that.isEmpty) this else if (this.isEmpty) that @@ -98,26 +98,26 @@ object ByteIterator { case _ ⇒ super.++(that) } - final override def clone = new ByteArrayIterator(array, from, until) + final override def clone: ByteArrayIterator = new ByteArrayIterator(array, from, until) - final override def take(n: Int) = { + final override def take(n: Int): this.type = { until = until min (from + (0 max n)) this } - final override def drop(n: Int) = { + final override def drop(n: Int): this.type = { from = until min (from + (0 max n)) this } - final override def takeWhile(p: Byte ⇒ Boolean) = { + final override def takeWhile(p: Byte ⇒ Boolean): this.type = { val prev = from dropWhile(p) until = from; from = prev this } - final override def dropWhile(p: Byte ⇒ Boolean) = { + final override def dropWhile(p: Byte ⇒ Boolean): this.type = { var stop = false while (!stop && hasNext) { if (p(array(from))) { from = from + 1 } else { stop = true } @@ -131,7 +131,7 @@ object ByteIterator { this.drop(n) } - final override def toByteString = { + final override def toByteString: ByteString = { val result = if ((from == 0) && (until == array.length)) ByteString.ByteString1C(array) else ByteString.ByteString1(array, from, len) @@ -139,26 +139,26 @@ object ByteIterator { result } - def getBytes(xs: Array[Byte], offset: Int, n: Int) = { + def getBytes(xs: Array[Byte], offset: Int, n: Int): this.type = { if (n <= this.len) { Array.copy(this.array, this.from, xs, offset, n) this } else Iterator.empty.next } - def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = { toByteString.asByteBuffer.order(byteOrder).asShortBuffer.get(xs, offset, n); drop(2 * n) } - def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = { toByteString.asByteBuffer.order(byteOrder).asIntBuffer.get(xs, offset, n); drop(4 * n) } - def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = { toByteString.asByteBuffer.order(byteOrder).asLongBuffer.get(xs, offset, n); drop(8 * n) } - def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = { toByteString.asByteBuffer.order(byteOrder).asFloatBuffer.get(xs, offset, n); drop(4 * n) } - def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = { toByteString.asByteBuffer.order(byteOrder).asDoubleBuffer.get(xs, offset, n); drop(8 * n) } def copyToBuffer(buffer: ByteBuffer): Int = { @@ -172,9 +172,9 @@ object ByteIterator { } object MultiByteArrayIterator { - protected val clearedList = List(ByteArrayIterator.empty) + protected val clearedList: List[ByteArrayIterator] = List(ByteArrayIterator.empty) - val empty = new MultiByteArrayIterator(Nil) + val empty: MultiByteArrayIterator = new MultiByteArrayIterator(Nil) protected[akka] def apply(iterators: List[ByteArrayIterator]): MultiByteArrayIterator = new MultiByteArrayIterator(iterators) @@ -195,25 +195,25 @@ object ByteIterator { } normalize() - @inline private def current = iterators.head - @inline private def dropCurrent() { iterators = iterators.tail } - @inline def clear() { iterators = MultiByteArrayIterator.empty.iterators } + @inline private def current: ByteArrayIterator = iterators.head + @inline private def dropCurrent(): Unit = { iterators = iterators.tail } + @inline def clear(): Unit = { iterators = MultiByteArrayIterator.empty.iterators } - final def isIdenticalTo(that: Iterator[Byte]) = false + final def isIdenticalTo(that: Iterator[Byte]): Boolean = false - @inline final def hasNext = current.hasNext + @inline final def hasNext: Boolean = current.hasNext - @inline final def head = current.head + @inline final def head: Byte = current.head - final def next() = { + final def next(): Byte = { val result = current.next() normalize() result } - final override def len = iterators.foldLeft(0) { _ + _.len } + final override def len: Int = iterators.foldLeft(0) { _ + _.len } - final override def length = { + final override def length: Int = { var result = len clear() result @@ -224,7 +224,7 @@ object ByteIterator { this } - final override def ++(that: TraversableOnce[Byte]) = that match { + final override def ++(that: TraversableOnce[Byte]): ByteIterator = that match { case that: ByteIterator ⇒ { if (that.isEmpty) this else if (this.isEmpty) that @@ -246,9 +246,9 @@ object ByteIterator { case _ ⇒ super.++(that) } - final override def clone = new MultiByteArrayIterator(iterators map { _.clone }) + final override def clone: MultiByteArrayIterator = new MultiByteArrayIterator(iterators map { _.clone }) - final override def take(n: Int) = { + final override def take(n: Int): this.type = { var rest = n val builder = new ListBuffer[ByteArrayIterator] while ((rest > 0) && !iterators.isEmpty) { @@ -263,7 +263,7 @@ object ByteIterator { normalize() } - @tailrec final override def drop(n: Int) = if ((n > 0) && !isEmpty) { + @tailrec final override def drop(n: Int): this.type = if ((n > 0) && !isEmpty) { val nCurrent = math.min(n, current.len) current.drop(n) val rest = n - nCurrent @@ -272,7 +272,7 @@ object ByteIterator { drop(rest) } else this - final override def takeWhile(p: Byte ⇒ Boolean) = { + final override def takeWhile(p: Byte ⇒ Boolean): this.type = { var stop = false var builder = new ListBuffer[ByteArrayIterator] while (!stop && !iterators.isEmpty) { @@ -286,7 +286,7 @@ object ByteIterator { normalize() } - @tailrec final override def dropWhile(p: Byte ⇒ Boolean) = if (!isEmpty) { + @tailrec final override def dropWhile(p: Byte ⇒ Boolean): this.type = if (!isEmpty) { current.dropWhile(p) val dropMore = current.isEmpty normalize() @@ -306,7 +306,7 @@ object ByteIterator { normalize() } - final override def toByteString = { + final override def toByteString: ByteString = { val result = iterators.foldLeft(ByteString.empty) { _ ++ _.toByteString } clear() result @@ -326,22 +326,22 @@ object ByteIterator { getToArray(xs, offset + nDone, n - nDone, elemSize)(getSingle)(getMult) } - def getBytes(xs: Array[Byte], offset: Int, n: Int) = + def getBytes(xs: Array[Byte], offset: Int, n: Int): this.type = getToArray(xs, offset, n, 1) { getByte } { getBytes(_, _, _) } - def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = getToArray(xs, offset, n, 2) { getShort(byteOrder) } { current.getShorts(_, _, _)(byteOrder) } - def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = getToArray(xs, offset, n, 4) { getInt(byteOrder) } { current.getInts(_, _, _)(byteOrder) } - def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = getToArray(xs, offset, n, 8) { getLong(byteOrder) } { current.getLongs(_, _, _)(byteOrder) } - def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = getToArray(xs, offset, n, 8) { getFloat(byteOrder) } { current.getFloats(_, _, _)(byteOrder) } - def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = getToArray(xs, offset, n, 8) { getDouble(byteOrder) } { current.getDoubles(_, _, _)(byteOrder) } def copyToBuffer(buffer: ByteBuffer): Int = { @@ -377,7 +377,7 @@ abstract class ByteIterator extends BufferedIterator[Byte] { // the parent class. override def clone: ByteIterator = throw new UnsupportedOperationException("Method clone is not implemented in ByteIterator") - final override def duplicate = (this, clone) + override def duplicate: (ByteIterator, ByteIterator) = (this, clone) // *must* be overridden by derived classes. This construction is necessary // to specialize the return type, as the method is already implemented in @@ -402,7 +402,7 @@ abstract class ByteIterator extends BufferedIterator[Byte] { // the parent class. override def dropWhile(p: Byte ⇒ Boolean): this.type = throw new UnsupportedOperationException("Method dropWhile is not implemented in ByteIterator") - final override def span(p: Byte ⇒ Boolean): (ByteIterator, ByteIterator) = { + override def span(p: Byte ⇒ Boolean): (ByteIterator, ByteIterator) = { val that = clone that.takeWhile(p) drop(that.len) @@ -452,7 +452,7 @@ abstract class ByteIterator extends BufferedIterator[Byte] { /** * Get a single Byte from this iterator. Identical to next(). */ - def getByte = next() + def getByte: Byte = next() /** * Get a single Short from this iterator. diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index e4b7026056..f5c27ca19f 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -53,11 +53,13 @@ object ByteString { val empty: ByteString = CompactByteString(Array.empty[Byte]) - def newBuilder = new ByteStringBuilder + def newBuilder: ByteStringBuilder = new ByteStringBuilder - implicit def canBuildFrom = new CanBuildFrom[TraversableOnce[Byte], Byte, ByteString] { - def apply(from: TraversableOnce[Byte]) = newBuilder - def apply() = newBuilder + implicit def canBuildFrom: CanBuildFrom[TraversableOnce[Byte], Byte, ByteString] = { + new CanBuildFrom[TraversableOnce[Byte], Byte, ByteString] { + def apply(from: TraversableOnce[Byte]) = newBuilder + def apply() = newBuilder + } } private[akka] object ByteString1C { @@ -71,9 +73,9 @@ object ByteString { final class ByteString1C private (private val bytes: Array[Byte]) extends CompactByteString { def apply(idx: Int): Byte = bytes(idx) - override def length = bytes.length + override def length: Int = bytes.length - override def iterator = ByteIterator.ByteArrayIterator(bytes, 0, bytes.length) + override def iterator: ByteIterator.ByteArrayIterator = ByteIterator.ByteArrayIterator(bytes, 0, bytes.length) def toByteString1: ByteString1 = ByteString1(bytes) @@ -95,8 +97,9 @@ object ByteString { } private[akka] object ByteString1 { - def apply(bytes: Array[Byte]) = new ByteString1(bytes) - def apply(bytes: Array[Byte], startIndex: Int, length: Int) = new ByteString1(bytes, startIndex, length) + def apply(bytes: Array[Byte]): ByteString1 = new ByteString1(bytes) + def apply(bytes: Array[Byte], startIndex: Int, length: Int): ByteString1 = + new ByteString1(bytes, startIndex, length) } /** @@ -108,9 +111,10 @@ object ByteString { def apply(idx: Int): Byte = bytes(checkRangeConvert(idx)) - override def iterator = ByteIterator.ByteArrayIterator(bytes, startIndex, startIndex + length) + override def iterator: ByteIterator.ByteArrayIterator = + ByteIterator.ByteArrayIterator(bytes, startIndex, startIndex + length) - private def checkRangeConvert(index: Int) = { + private def checkRangeConvert(index: Int): Int = { if (0 <= index && length > index) index + startIndex else @@ -119,7 +123,7 @@ object ByteString { override def clone: CompactByteString = ByteString1C(toArray) - def isCompact = (length == bytes.length) + def isCompact: Boolean = (length == bytes.length) def compact: CompactByteString = if (isCompact) ByteString1C(bytes) else clone @@ -206,7 +210,8 @@ object ByteString { bytestrings(pos)(idx - seen) } else throw new IndexOutOfBoundsException(idx.toString) - override def iterator = ByteIterator.MultiByteArrayIterator(bytestrings.map(_.iterator)(collection.breakOut)) + override def iterator: ByteIterator.MultiByteArrayIterator = + ByteIterator.MultiByteArrayIterator(bytestrings.map(_.iterator)(collection.breakOut)) def ++(that: ByteString): ByteString = { if (that.isEmpty) this @@ -218,12 +223,12 @@ object ByteString { } } - def isCompact = { + def isCompact: Boolean = { if (bytestrings.length == 1) bytestrings.head.isCompact else false } - def compact = { + def compact: CompactByteString = { if (isCompact) bytestrings.head.compact else { val ar = new Array[Byte](length) @@ -252,7 +257,7 @@ object ByteString { * TODO: Add performance characteristics */ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimized[Byte, ByteString] { - override protected[this] def newBuilder = ByteString.newBuilder + override protected[this] def newBuilder: ByteStringBuilder = ByteString.newBuilder // *must* be overridden by derived classes. This construction is necessary // to specialize the return type, as the method is already implemented in @@ -273,8 +278,8 @@ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimiz override def drop(n: Int): ByteString = slice(n, length) override def dropRight(n: Int): ByteString = slice(0, length - n) - override def takeWhile(p: Byte ⇒ Boolean) = iterator.takeWhile(p).toByteString - override def dropWhile(p: Byte ⇒ Boolean) = iterator.dropWhile(p).toByteString + override def takeWhile(p: Byte ⇒ Boolean): ByteString = iterator.takeWhile(p).toByteString + override def dropWhile(p: Byte ⇒ Boolean): ByteString = iterator.dropWhile(p).toByteString override def span(p: Byte ⇒ Boolean): (ByteString, ByteString) = { val (a, b) = iterator.span(p); (a.toByteString, b.toByteString) } @@ -283,8 +288,9 @@ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimiz override def indexWhere(p: Byte ⇒ Boolean): Int = iterator.indexWhere(p) override def indexOf[B >: Byte](elem: B): Int = iterator.indexOf(elem) - override def toArray[B >: Byte](implicit arg0: ClassManifest[B]) = iterator.toArray - override def copyToArray[B >: Byte](xs: Array[B], start: Int, len: Int) = iterator.copyToArray(xs, start, len) + override def toArray[B >: Byte](implicit arg0: ClassManifest[B]): Array[B] = iterator.toArray + override def copyToArray[B >: Byte](xs: Array[B], start: Int, len: Int): Unit = + iterator.copyToArray(xs, start, len) @inline final override def foreach[@specialized U](f: Byte ⇒ U): Unit = iterator foreach f @@ -421,8 +427,8 @@ object CompactByteString { * as much memory as required for its contents. */ sealed abstract class CompactByteString extends ByteString with Serializable { - def isCompact = true - def compact = this + def isCompact: Boolean = true + def compact: this.type = this } object ByteStringBuilder { @@ -432,9 +438,9 @@ object ByteStringBuilder { * An OutputStream that directly wraps a ByteStringBuilder. */ class OutputStreamWrapper(val builder: ByteStringBuilder) extends java.io.OutputStream { - def write(b: Int) = builder += b.toByte + def write(b: Int): Unit = builder += b.toByte - override def write(b: Array[Byte], off: Int, len: Int) { builder.putBytes(b, off, len) } + override def write(b: Array[Byte], off: Int, len: Int): Unit = { builder.putBytes(b, off, len) } } } @@ -445,11 +451,11 @@ object ByteStringBuilder { */ final class ByteStringBuilder extends Builder[Byte, ByteString] { import ByteString.{ ByteString1C, ByteString1, ByteStrings } - private var _length = 0 - private val _builder = new VectorBuilder[ByteString1]() + private var _length: Int = 0 + private val _builder: VectorBuilder[ByteString1] = new VectorBuilder[ByteString1]() private var _temp: Array[Byte] = _ - private var _tempLength = 0 - private var _tempCapacity = 0 + private var _tempLength: Int = 0 + private var _tempCapacity: Int = 0 protected def fillArray(len: Int)(fill: (Array[Byte], Int) ⇒ Unit): this.type = { ensureTempSize(_tempLength + len) @@ -475,11 +481,11 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { def length: Int = _length - override def sizeHint(len: Int) { + override def sizeHint(len: Int): Unit = { resizeTemp(len - (_length - _tempLength)) } - private def clearTemp() { + private def clearTemp(): Unit = { if (_tempLength > 0) { val arr = new Array[Byte](_tempLength) Array.copy(_temp, 0, arr, 0, _tempLength) @@ -488,14 +494,14 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { } } - private def resizeTemp(size: Int) { + private def resizeTemp(size: Int): Unit = { val newtemp = new Array[Byte](size) if (_tempLength > 0) Array.copy(_temp, 0, newtemp, 0, _tempLength) _temp = newtemp _tempCapacity = _temp.length } - private def ensureTempSize(size: Int) { + private def ensureTempSize(size: Int): Unit = { if (_tempCapacity < size || _tempCapacity == 0) { var newSize = if (_tempCapacity == 0) 16 else _tempCapacity * 2 while (newSize < size) newSize *= 2 @@ -692,7 +698,7 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { def putDoubles(array: Array[Double], start: Int, len: Int)(implicit byteOrder: ByteOrder): this.type = fillByteBuffer(len * 8, byteOrder) { _.asDoubleBuffer.put(array, start, len) } - def clear() { + def clear(): Unit = { _builder.clear _length = 0 _tempLength = 0 @@ -713,5 +719,5 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { * Directly wraps this ByteStringBuilder in an OutputStream. Write * operations on the stream are forwarded to the builder. */ - def asOutputStream = new ByteStringBuilder.OutputStreamWrapper(this) + def asOutputStream: java.io.OutputStream = new ByteStringBuilder.OutputStreamWrapper(this) } From 07dd65484922ca7328edf5b6027cfd0c04830157 Mon Sep 17 00:00:00 2001 From: Gert Vanthienen Date: Tue, 29 May 2012 12:41:09 +0200 Subject: [PATCH 27/74] Adding a convenience BundleActivator implementation to bootstrap Akka from an OSGi bundle --- .../akka/osgi/ActorSystemActivator.scala | 78 +++++++++++++++++++ .../akka/osgi/ActorSystemActivatorTest.scala | 75 ++++++++++++++++++ project/AkkaBuild.scala | 16 ++++ 3 files changed, 169 insertions(+) create mode 100644 akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala create mode 100644 akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala diff --git a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala new file mode 100644 index 0000000000..d63404334a --- /dev/null +++ b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala @@ -0,0 +1,78 @@ +package akka.osgi + +import com.typesafe.config.{ Config, ConfigFactory } +import akka.actor.ActorSystem +import org.osgi.framework.{ BundleContext, BundleActivator } +import java.util.Properties + +/** + * Abstract {@link BundleActivator} implementation to bootstrap and configure an {@link ActorSystem} in an + * OSGi environment. + */ +abstract class ActorSystemActivator extends BundleActivator { + + var system: ActorSystem = null + + /** + * Implement this method to add your own actors to the ActorSystem + * + * @param system the ActorSystem that was created by the activator + */ + def configure(system: ActorSystem) + + /** + * Sets up a new ActorSystem and registers it in the OSGi Service Registry + * + * @param context the BundleContext + */ + def start(context: BundleContext) { + system = createActorSystem(context) + configure(system) + + val properties = new Properties(); + properties.put("name", getActorSystemName(context)) + context.registerService(classOf[ActorSystem].getName, system, properties) + } + + /** + * Shuts down the ActorSystem when the bundle is stopped. + * + * @param context the BundleContext + */ + def stop(context: BundleContext) { + if (system != null) { + system.shutdown() + system.shutdown() + system = null + } + } + + /** + * Strategy method to create the ActorSystem. + */ + def createActorSystem(context: BundleContext) = + ActorSystem(getActorSystemName(context), getActorSystemConfig(context), getClass.getClassLoader) + + + /** + * Strategy method to create the Config for the ActorSystem, ensuring that the default/reference configuration is + * loaded from the akka-actor bundle. + */ + def getActorSystemConfig(context: BundleContext): Config = { + val reference = ConfigFactory.defaultReference(classOf[ActorSystem].getClassLoader) + ConfigFactory.load(getClass.getClassLoader).withFallback(reference) + } + + /** + * Strategy method to determine the ActorSystem name - override this method to define the ActorSytem name yourself. + * + * The default implementation will use 'bundle--ActorSystem' where matches the bundle id for the containing bundle. + * + * @param context the BundleContext + * @return the ActorSystem name + */ + def getActorSystemName(context: BundleContext): String = { + "bundle-%s-ActorSystem".format(context.getBundle().getBundleId) + } + +} diff --git a/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala b/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala new file mode 100644 index 0000000000..ffcc3cc0e7 --- /dev/null +++ b/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala @@ -0,0 +1,75 @@ +package akka.osgi + +import java.util.{ ServiceLoader, HashMap } +import de.kalpatec.pojosr.framework.launch.{ ClasspathScanner, PojoServiceRegistryFactory } +import org.scalatest.FlatSpec +import org.osgi.framework.BundleContext +import akka.actor.{ Actor, Props, ActorSystem } +import akka.pattern.ask +import akka.dispatch.Await +import akka.util.duration._ +import akka.util.Timeout + +/** + * Test cases for {@link ActorSystemActivator} + */ +class ActorSystemActivatorTest extends FlatSpec { + + abstract class TestMessage + + case object Ping extends TestMessage + case object Pong extends TestMessage + + class PongActor extends Actor { + def receive = { + case Ping ⇒ + sender ! Pong + } + } + + lazy val context: BundleContext = { + val config = new HashMap[String, AnyRef](); + config.put(PojoServiceRegistryFactory.BUNDLE_DESCRIPTORS, new ClasspathScanner().scanForBundles()); + + val loader = ServiceLoader.load(classOf[PojoServiceRegistryFactory]); + + val registry = loader.iterator().next().newPojoServiceRegistry(config); + registry.getBundleContext + } + + val activator = new ActorSystemActivator { + def configure(system: ActorSystem) { + system.actorOf(Props(new PongActor), name = "pong") + } + } + + "ActorSystemActivator" should "start and register the ActorSystem on start" in { + + activator.start(context) + + val reference = context.getServiceReference(classOf[ActorSystem].getName) + assert(reference != null) + + val system = context.getService(reference).asInstanceOf[ActorSystem] + val actor = system.actorFor("/user/pong") + + implicit val timeout = Timeout(5 seconds) + val future = actor ? Ping + val result = Await.result(future, timeout.duration) + assert(result != null) + } + + it should "stop the ActorSystem on bundle stop" in { + val reference = context.getServiceReference(classOf[ActorSystem].getName) + assert(reference != null) + + val system = context.getService(reference).asInstanceOf[ActorSystem] + assert(!system.isTerminated) + + activator.stop(context) + + system.awaitTermination() + assert(system.isTerminated) + } + +} diff --git a/project/AkkaBuild.scala b/project/AkkaBuild.scala index dbe9fbae9e..f0e6446879 100644 --- a/project/AkkaBuild.scala +++ b/project/AkkaBuild.scala @@ -209,6 +209,15 @@ object AkkaBuild extends Build { ) ) + lazy val osgi = Project( + id = "akka-osgi", + base = file("akka-osgi"), + dependencies = Seq(actor), + settings = defaultSettings ++ OSGi.osgi ++ Seq( + libraryDependencies ++= Dependencies.osgi + ) + ) + lazy val akkaSbtPlugin = Project( id = "akka-sbt-plugin", base = file("akka-sbt-plugin"), @@ -419,6 +428,8 @@ object Dependencies { val camel = Seq(camelCore, Test.scalatest, Test.junit, Test.mockito) + val osgi = Seq(osgiCore, Test.pojosr, Test.scalatest, Test.junit) + val tutorials = Seq(Test.scalatest, Test.junit) val docs = Seq(Test.scalatest, Test.junit, Test.specs2) @@ -434,6 +445,7 @@ object Dependency { val Camel = "2.8.0" val Logback = "0.9.28" val Netty = "3.3.0.Final" + val OSGi = "4.2.0" val Protobuf = "2.4.1" val ScalaStm = "0.5" val Scalatest = "1.6.1" @@ -444,6 +456,7 @@ object Dependency { val camelCore = "org.apache.camel" % "camel-core" % V.Camel // ApacheV2 val netty = "io.netty" % "netty" % V.Netty // ApacheV2 + val osgiCore = "org.osgi" % "org.osgi.core" % V.OSGi // ApacheV2 val protobuf = "com.google.protobuf" % "protobuf-java" % V.Protobuf // New BSD val scalaStm = "org.scala-tools" % "scala-stm_2.9.1" % V.ScalaStm // Modified BSD (Scala) val slf4jApi = "org.slf4j" % "slf4j-api" % V.Slf4j // MIT @@ -463,6 +476,7 @@ object Dependency { val junit = "junit" % "junit" % "4.5" % "test" // Common Public License 1.0 val logback = "ch.qos.logback" % "logback-classic" % V.Logback % "test" // EPL 1.0 / LGPL 2.1 val mockito = "org.mockito" % "mockito-all" % "1.8.1" % "test" // MIT + val pojosr = "com.googlecode.pojosr" % "de.kalpatec.pojosr.framework" % "0.1.8" % "test" // ApacheV2 val scalatest = "org.scalatest" % "scalatest_2.9.1" % V.Scalatest % "test" // ApacheV2 val scalacheck = "org.scala-tools.testing" % "scalacheck_2.9.1" % "1.9" % "test" // New BSD val specs2 = "org.specs2" % "specs2_2.9.1" % "1.9" % "test" // Modified BSD / ApacheV2 @@ -487,6 +501,8 @@ object OSGi { val mailboxesCommon = exports(Seq("akka.actor.mailbox.*")) + val osgi = exports(Seq("akka.osgi.*")) + val remote = exports(Seq("akka.remote.*", "akka.routing.*", "akka.serialization.*")) val slf4j = exports(Seq("akka.event.slf4j.*")) From d3e878effb0ff7976586339ddeb09e7f79a18cd4 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Tue, 29 May 2012 21:35:57 +0200 Subject: [PATCH 28/74] Fixed ByteIterator.{take,drop,slice} for extreme argument values --- akka-actor/src/main/scala/akka/util/ByteIterator.scala | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index e4bd8dd84e..60bc9dfdb8 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -101,12 +101,12 @@ object ByteIterator { final override def clone: ByteArrayIterator = new ByteArrayIterator(array, from, until) final override def take(n: Int): this.type = { - until = until min (from + (0 max n)) + if (n < len) until = { if (n > 0) (from + n) else from } this } final override def drop(n: Int): this.type = { - from = until min (from + (0 max n)) + if (n > 0) from = { if (n < len) (from + n) else until } this } @@ -389,8 +389,10 @@ abstract class ByteIterator extends BufferedIterator[Byte] { // the parent class. override def drop(n: Int): this.type = throw new UnsupportedOperationException("Method drop is not implemented in ByteIterator") - final override def slice(from: Int, until: Int): this.type = - drop(from).take(until - from) + final override def slice(from: Int, until: Int): this.type = { + if (from > 0) drop(from).take(until - from) + else take(until) + } // *must* be overridden by derived classes. This construction is necessary // to specialize the return type, as the method is already implemented in From 696cd5a192129e94bd932977849785ab70d07fac Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 27 May 2012 16:28:45 +0200 Subject: [PATCH 29/74] Fixed implementations of ByteIterator.getBytes, getShorts, etc. Implementations were broken. --- .../main/scala/akka/util/ByteIterator.scala | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 60bc9dfdb8..37346d4cdd 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -142,24 +142,26 @@ object ByteIterator { def getBytes(xs: Array[Byte], offset: Int, n: Int): this.type = { if (n <= this.len) { Array.copy(this.array, this.from, xs, offset, n) - this + this.drop(n) } else Iterator.empty.next } + private def wrappedByteBuffer: ByteBuffer = ByteBuffer.wrap(array, from, len).asReadOnlyBuffer + def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = - { toByteString.asByteBuffer.order(byteOrder).asShortBuffer.get(xs, offset, n); drop(2 * n) } + { wrappedByteBuffer.order(byteOrder).asShortBuffer.get(xs, offset, n); drop(2 * n) } def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = - { toByteString.asByteBuffer.order(byteOrder).asIntBuffer.get(xs, offset, n); drop(4 * n) } + { wrappedByteBuffer.order(byteOrder).asIntBuffer.get(xs, offset, n); drop(4 * n) } def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = - { toByteString.asByteBuffer.order(byteOrder).asLongBuffer.get(xs, offset, n); drop(8 * n) } + { wrappedByteBuffer.order(byteOrder).asLongBuffer.get(xs, offset, n); drop(8 * n) } def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = - { toByteString.asByteBuffer.order(byteOrder).asFloatBuffer.get(xs, offset, n); drop(4 * n) } + { wrappedByteBuffer.order(byteOrder).asFloatBuffer.get(xs, offset, n); drop(4 * n) } def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = - { toByteString.asByteBuffer.order(byteOrder).asDoubleBuffer.get(xs, offset, n); drop(8 * n) } + { wrappedByteBuffer.order(byteOrder).asDoubleBuffer.get(xs, offset, n); drop(8 * n) } def copyToBuffer(buffer: ByteBuffer): Int = { val copyLength = math.min(buffer.remaining, len) @@ -315,7 +317,7 @@ object ByteIterator { @tailrec protected final def getToArray[A](xs: Array[A], offset: Int, n: Int, elemSize: Int)(getSingle: ⇒ A)(getMult: (Array[A], Int, Int) ⇒ Unit): this.type = if (n <= 0) this else { if (isEmpty) Iterator.empty.next val nDone = if (current.len >= elemSize) { - val nCurrent = math.min(elemSize, current.len / elemSize) + val nCurrent = math.min(n, current.len / elemSize) getMult(xs, offset, nCurrent) nCurrent } else { @@ -327,7 +329,7 @@ object ByteIterator { } def getBytes(xs: Array[Byte], offset: Int, n: Int): this.type = - getToArray(xs, offset, n, 1) { getByte } { getBytes(_, _, _) } + getToArray(xs, offset, n, 1) { getByte } { current.getBytes(_, _, _) } def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = getToArray(xs, offset, n, 2) { getShort(byteOrder) } { current.getShorts(_, _, _)(byteOrder) } From c185f72c379598103d181e5d01e9280458d22e0c Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Tue, 29 May 2012 23:58:03 +0200 Subject: [PATCH 30/74] Fixed ByteIterator InputStream wrapping Result of ByteIterator.asInputStream did not work correctly in all cases --- .../main/scala/akka/util/ByteIterator.scala | 74 +++++++++++++------ 1 file changed, 52 insertions(+), 22 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 37346d4cdd..3e3b119369 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -16,27 +16,6 @@ import scala.annotation.tailrec import java.nio.ByteBuffer object ByteIterator { - /** - * An InputStream that directly wraps a ByteIterator without copying - */ - final class InputStreamWrapper(val iterator: ByteIterator) extends java.io.InputStream { - override def available: Int = iterator.len - - def read: Int = if (iterator.hasNext) (iterator.next.toInt & 0xff) else -1 - - override def read(b: Array[Byte], off: Int, len: Int): Int = { - val nRead = math.min(iterator.len, len - off) - iterator.copyToArray(b, off, nRead) - nRead - } - - override def skip(n: Long): Long = { - val nSkip = math.min(iterator.len, n.toInt) - iterator.drop(nSkip) - nSkip - } - } - object ByteArrayIterator { private val emptyArray: Array[Byte] = Array.ofDim[Byte](0) @@ -50,6 +29,8 @@ object ByteIterator { } class ByteArrayIterator private (private var array: Array[Byte], private var from: Int, private var until: Int) extends ByteIterator { + iterator ⇒ + protected[util] final def internalArray = array protected[util] final def internalFrom = from protected[util] final def internalUntil = until @@ -171,6 +152,28 @@ object ByteIterator { } copyLength } + + def asInputStream: java.io.InputStream = new java.io.InputStream { + override def available: Int = iterator.len + + def read: Int = if (hasNext) (next().toInt & 0xff) else -1 + + override def read(b: Array[Byte], off: Int, len: Int): Int = { + if ((off < 0) || (len < 0) || (off + len > b.length)) throw new IndexOutOfBoundsException + if (len == 0) 0 + else if (!isEmpty) { + val nRead = math.min(available, len) + copyToArray(b, off, nRead) + nRead + } else -1 + } + + override def skip(n: Long): Long = { + val nSkip = math.min(iterator.len, n.toInt) + iterator.drop(nSkip) + nSkip + } + } } object MultiByteArrayIterator { @@ -351,6 +354,33 @@ object ByteIterator { normalize() n } + + def asInputStream: java.io.InputStream = new java.io.InputStream { + override def available: Int = current.len + + def read: Int = if (hasNext) (next().toInt & 0xff) else -1 + + override def read(b: Array[Byte], off: Int, len: Int): Int = { + val nRead = current.asInputStream.read(b, off, len) + normalize() + nRead + } + + override def skip(n: Long): Long = { + @tailrec def skipImpl(n: Long, skipped: Long): Long = if (n > 0) { + if (!isEmpty) { + val m = current.asInputStream.skip(n) + normalize() + val newN = n - m + val newSkipped = skipped + m + if (newN > 0) skipImpl(newN, newSkipped) + else newSkipped + } else 0 + } else 0 + + skipImpl(n, 0) + } + } } } @@ -598,5 +628,5 @@ abstract class ByteIterator extends BufferedIterator[Byte] { * Read and skip operations on the stream will advance the iterator * accordingly. */ - def asInputStream: java.io.InputStream = new ByteIterator.InputStreamWrapper(this) + def asInputStream: java.io.InputStream } From 0aff0ff1016b21ff285d4d3c816907df2badc9c2 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 27 May 2012 16:26:22 +0200 Subject: [PATCH 31/74] Added lots of missing tests to ByteStringSpec --- .../test/scala/akka/util/ByteStringSpec.scala | 201 ++++++++++++++++++ 1 file changed, 201 insertions(+) diff --git a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala index 1a4dd40ffe..396fcd6b00 100644 --- a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala @@ -9,6 +9,9 @@ import org.scalacheck.Arbitrary._ import org.scalacheck.Prop._ import org.scalacheck.Gen._ +import java.nio.{ ByteBuffer, ShortBuffer, IntBuffer, FloatBuffer, DoubleBuffer } +import java.nio.{ ByteOrder }, ByteOrder.{ BIG_ENDIAN, LITTLE_ENDIAN } + class ByteStringSpec extends WordSpec with MustMatchers with Checkers { def genSimpleByteString(min: Int, max: Int) = for { @@ -37,6 +40,29 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { } yield (xs, from, until) } + type ArraySlice[A] = (Array[A], Int, Int) + + def arbSlice[A](arbArray: Arbitrary[Array[A]]): Arbitrary[ArraySlice[A]] = Arbitrary { + for { + xs ← arbArray.arbitrary + from ← choose(0, xs.length) + until ← choose(from, xs.length) + } yield (xs, from, until) + } + + val arbitraryByteArray: Arbitrary[Array[Byte]] = Arbitrary { Gen.sized { n ⇒ Gen.containerOfN[Array, Byte](n, arbitrary[Byte]) } } + implicit val arbitraryByteArraySlice: Arbitrary[ArraySlice[Byte]] = arbSlice(arbitraryByteArray) + val arbitraryShortArray: Arbitrary[Array[Short]] = Arbitrary { Gen.sized { n ⇒ Gen.containerOfN[Array, Short](n, arbitrary[Short]) } } + implicit val arbitraryShortArraySlice: Arbitrary[ArraySlice[Short]] = arbSlice(arbitraryShortArray) + val arbitraryIntArray: Arbitrary[Array[Int]] = Arbitrary { Gen.sized { n ⇒ Gen.containerOfN[Array, Int](n, arbitrary[Int]) } } + implicit val arbitraryIntArraySlice: Arbitrary[ArraySlice[Int]] = arbSlice(arbitraryIntArray) + val arbitraryLongArray: Arbitrary[Array[Long]] = Arbitrary { Gen.sized { n ⇒ Gen.containerOfN[Array, Long](n, arbitrary[Long]) } } + implicit val arbitraryLongArraySlice: Arbitrary[ArraySlice[Long]] = arbSlice(arbitraryLongArray) + val arbitraryFloatArray: Arbitrary[Array[Float]] = Arbitrary { Gen.sized { n ⇒ Gen.containerOfN[Array, Float](n, arbitrary[Float]) } } + implicit val arbitraryFloatArraySlice: Arbitrary[ArraySlice[Float]] = arbSlice(arbitraryFloatArray) + val arbitraryDoubleArray: Arbitrary[Array[Double]] = Arbitrary { Gen.sized { n ⇒ Gen.containerOfN[Array, Double](n, arbitrary[Double]) } } + implicit val arbitraryDoubleArraySlice: Arbitrary[ArraySlice[Double]] = arbSlice(arbitraryDoubleArray) + def likeVecIt(bs: ByteString)(body: BufferedIterator[Byte] ⇒ Any, strict: Boolean = true): Boolean = { val bsIterator = bs.iterator val vecIterator = Vector(bs: _*).iterator.buffered @@ -51,6 +77,84 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { (!strict || (bsAIt.toSeq, bsBIt.toSeq) == (vecAIt.toSeq, vecBIt.toSeq)) } + def testShortDecoding(slice: ByteStringSlice, byteOrder: ByteOrder): Boolean = { + val elemSize = 2 + val (bytes, from, until) = slice + val (n, a, b) = (bytes.length / elemSize, from / elemSize, until / elemSize) + val reference = Array.ofDim[Short](n) + bytes.asByteBuffer.order(byteOrder).asShortBuffer.get(reference, 0, n) + val input = bytes.iterator + val decoded = Array.ofDim[Short](n) + for (i ← 0 to a - 1) decoded(i) = input.getShort(byteOrder) + input.getShorts(decoded, a, b - a)(byteOrder) + for (i ← b to n - 1) decoded(i) = input.getShort(byteOrder) + (decoded.toSeq == reference.toSeq) && (input.toSeq == bytes.drop(n * elemSize)) + } + + def testIntDecoding(slice: ByteStringSlice, byteOrder: ByteOrder): Boolean = { + val elemSize = 4 + val (bytes, from, until) = slice + val (n, a, b) = (bytes.length / elemSize, from / elemSize, until / elemSize) + val reference = Array.ofDim[Int](n) + bytes.asByteBuffer.order(byteOrder).asIntBuffer.get(reference, 0, n) + val input = bytes.iterator + val decoded = Array.ofDim[Int](n) + for (i ← 0 to a - 1) decoded(i) = input.getInt(byteOrder) + input.getInts(decoded, a, b - a)(byteOrder) + for (i ← b to n - 1) decoded(i) = input.getInt(byteOrder) + (decoded.toSeq == reference.toSeq) && (input.toSeq == bytes.drop(n * elemSize)) + } + + def testLongDecoding(slice: ByteStringSlice, byteOrder: ByteOrder): Boolean = { + val elemSize = 8 + val (bytes, from, until) = slice + val (n, a, b) = (bytes.length / elemSize, from / elemSize, until / elemSize) + val reference = Array.ofDim[Long](n) + bytes.asByteBuffer.order(byteOrder).asLongBuffer.get(reference, 0, n) + val input = bytes.iterator + val decoded = Array.ofDim[Long](n) + for (i ← 0 to a - 1) decoded(i) = input.getLong(byteOrder) + input.getLongs(decoded, a, b - a)(byteOrder) + for (i ← b to n - 1) decoded(i) = input.getLong(byteOrder) + (decoded.toSeq == reference.toSeq) && (input.toSeq == bytes.drop(n * elemSize)) + } + + def testShortEncoding(slice: ArraySlice[Short], byteOrder: ByteOrder): Boolean = { + val elemSize = 2 + val (data, from, until) = slice + val reference = Array.ofDim[Byte](data.length * elemSize) + ByteBuffer.wrap(reference).order(byteOrder).asShortBuffer.put(data) + val builder = ByteString.newBuilder + for (i ← 0 to from - 1) builder.putShort(data(i))(byteOrder) + builder.putShorts(data, from, until - from)(byteOrder) + for (i ← until to data.length - 1) builder.putShort(data(i))(byteOrder) + reference.toSeq == builder.result + } + + def testIntEncoding(slice: ArraySlice[Int], byteOrder: ByteOrder): Boolean = { + val elemSize = 4 + val (data, from, until) = slice + val reference = Array.ofDim[Byte](data.length * elemSize) + ByteBuffer.wrap(reference).order(byteOrder).asIntBuffer.put(data) + val builder = ByteString.newBuilder + for (i ← 0 to from - 1) builder.putInt(data(i))(byteOrder) + builder.putInts(data, from, until - from)(byteOrder) + for (i ← until to data.length - 1) builder.putInt(data(i))(byteOrder) + reference.toSeq == builder.result + } + + def testLongEncoding(slice: ArraySlice[Long], byteOrder: ByteOrder): Boolean = { + val elemSize = 8 + val (data, from, until) = slice + val reference = Array.ofDim[Byte](data.length * elemSize) + ByteBuffer.wrap(reference).order(byteOrder).asLongBuffer.put(data) + val builder = ByteString.newBuilder + for (i ← 0 to from - 1) builder.putLong(data(i))(byteOrder) + builder.putLongs(data, from, until - from)(byteOrder) + for (i ← until to data.length - 1) builder.putLong(data(i))(byteOrder) + reference.toSeq == builder.result + } + "A ByteString" must { "have correct size" when { "concatenating" in { check((a: ByteString, b: ByteString) ⇒ (a ++ b).size == a.size + b.size) } @@ -59,6 +163,14 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { "be sequential" when { "taking" in { check((a: ByteString, b: ByteString) ⇒ (a ++ b).take(a.size) == a) } "dropping" in { check((a: ByteString, b: ByteString) ⇒ (a ++ b).drop(a.size) == b) } + + "recombining" in { + check { (xs: ByteString, from: Int, until: Int) ⇒ + val (tmp, c) = xs.splitAt(until) + val (a, b) = tmp.splitAt(from) + (a ++ b ++ c) == xs + } + } } } @@ -103,5 +215,94 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { } } } + + "function as expected" when { + "getting Bytes, using getByte and getBytes" in { + // mixing getByte and getBytes here for more rigorous testing + check { slice: ByteStringSlice ⇒ + val (bytes, from, until) = slice + val input = bytes.iterator + val output = Array.ofDim[Byte](bytes.length) + for (i ← 0 to from - 1) output(i) = input.getByte + input.getBytes(output, from, until - from) + for (i ← until to bytes.length - 1) output(i) = input.getByte + (output.toSeq == bytes) && (input.isEmpty) + } + } + + "getting Bytes, using the InputStream wrapper" in { + // combining skip and both read methods here for more rigorous testing + check { slice: ByteStringSlice ⇒ + val (bytes, from, until) = slice + val a = (0 max from) min bytes.length + val b = (a max until) min bytes.length + val input = bytes.iterator + val output = Array.ofDim[Byte](bytes.length) + + input.asInputStream.skip(a) + + val toRead = b - a + var (nRead, eof) = (0, false) + while ((nRead < toRead) && !eof) { + val n = input.asInputStream.read(output, a + nRead, toRead - nRead) + if (n == -1) eof = true + else nRead += n + } + if (eof) throw new RuntimeException("Unexpected EOF") + + for (i ← b to bytes.length - 1) output(i) = input.asInputStream.read().toByte + + (output.toSeq.drop(a) == bytes.drop(a)) && + (input.asInputStream.read() == -1) && + ((output.length < 1) || (input.asInputStream.read(output, 0, 1) == -1)) + } + } + } + + "decode data correctly" when { + "decoding Short in big-endian" in { check { slice: ByteStringSlice ⇒ testShortDecoding(slice, BIG_ENDIAN) } } + "decoding Short in little-endian" in { check { slice: ByteStringSlice ⇒ testShortDecoding(slice, LITTLE_ENDIAN) } } + "decoding Int in big-endian" in { check { slice: ByteStringSlice ⇒ testIntDecoding(slice, BIG_ENDIAN) } } + "decoding Int in little-endian" in { check { slice: ByteStringSlice ⇒ testIntDecoding(slice, LITTLE_ENDIAN) } } + "decoding Long in big-endian" in { check { slice: ByteStringSlice ⇒ testLongDecoding(slice, BIG_ENDIAN) } } + "decoding Long in little-endian" in { check { slice: ByteStringSlice ⇒ testLongDecoding(slice, LITTLE_ENDIAN) } } + } + } + + "A ByteStringBuilder" must { + "function as expected" when { + "putting Bytes, using putByte and putBytes" in { + // mixing putByte and putBytes here for more rigorous testing + check { slice: ArraySlice[Byte] ⇒ + val (data, from, until) = slice + val builder = ByteString.newBuilder + for (i ← 0 to from - 1) builder.putByte(data(i)) + builder.putBytes(data, from, until - from) + for (i ← until to data.length - 1) builder.putByte(data(i)) + data.toSeq == builder.result + } + } + + "putting Bytes, using the OutputStream wrapper" in { + // mixing the write methods here for more rigorous testing + check { slice: ArraySlice[Byte] ⇒ + val (data, from, until) = slice + val builder = ByteString.newBuilder + for (i ← 0 to from - 1) builder.asOutputStream.write(data(i).toInt) + builder.asOutputStream.write(data, from, until - from) + for (i ← until to data.length - 1) builder.asOutputStream.write(data(i).toInt) + data.toSeq == builder.result + } + } + } + + "encode data correctly" when { + "encoding Short in big-endian" in { check { slice: ArraySlice[Short] ⇒ testShortEncoding(slice, BIG_ENDIAN) } } + "encoding Short in little-endian" in { check { slice: ArraySlice[Short] ⇒ testShortEncoding(slice, LITTLE_ENDIAN) } } + "encoding Int in big-endian" in { check { slice: ArraySlice[Int] ⇒ testIntEncoding(slice, BIG_ENDIAN) } } + "encoding Int in little-endian" in { check { slice: ArraySlice[Int] ⇒ testIntEncoding(slice, LITTLE_ENDIAN) } } + "encoding Long in big-endian" in { check { slice: ArraySlice[Long] ⇒ testLongEncoding(slice, BIG_ENDIAN) } } + "encoding Long in little-endian" in { check { slice: ArraySlice[Long] ⇒ testLongEncoding(slice, LITTLE_ENDIAN) } } + } } } From 151b74262ee67fed5b9d22d0939a60ec4129b3b5 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sat, 26 May 2012 12:28:33 +0200 Subject: [PATCH 32/74] Improved MultiByteArrayIterator.toByteString MultiByteArrayIterator.toByteString now returns the optimal ByteString representation for the data. --- akka-actor/src/main/scala/akka/util/ByteIterator.scala | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 3e3b119369..494a053d8e 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -312,9 +312,12 @@ object ByteIterator { } final override def toByteString: ByteString = { - val result = iterators.foldLeft(ByteString.empty) { _ ++ _.toByteString } - clear() - result + if (iterators.tail isEmpty) iterators.head.toByteString + else { + val result = iterators.foldLeft(ByteString.empty) { _ ++ _.toByteString } + clear() + result + } } @tailrec protected final def getToArray[A](xs: Array[A], offset: Int, n: Int, elemSize: Int)(getSingle: ⇒ A)(getMult: (Array[A], Int, Int) ⇒ Unit): this.type = if (n <= 0) this else { From b54e9ba78e2a297fc1d422dc0960a828ab39657c Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sat, 26 May 2012 12:29:03 +0200 Subject: [PATCH 33/74] Improved documentation of ByteString methods compact and isCompact --- akka-actor/src/main/scala/akka/util/ByteString.scala | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index f5c27ca19f..3eff32f10f 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -311,12 +311,16 @@ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimiz /** * Create a new ByteString with all contents compacted into a single, * full byte array. + * If isCompact returns true, compact is an O(1) operation, but + * might return a different object with an optimized implementation. */ def compact: CompactByteString /** - * Check whether this ByteString is compact in memory - * (i.e. represented by a single, full byte array). + * Check whether this ByteString is compact in memory. + * If the ByteString is compact, it might, however, not be represented + * by an object that takes full adventage of that fact. Use compact to + * get such an object. */ def isCompact: Boolean From 93d5e440eaeee367cf25ea45c23c29d741252437 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sat, 26 May 2012 12:59:48 +0200 Subject: [PATCH 34/74] Removed object ByteStringBuilder * ByteStringBuilder.apply was unusual for Scala Builders * Moved OutputStreamWrapper implementation into ByteStringBuilder --- .../src/main/scala/akka/util/ByteString.scala | 28 ++++++------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 3eff32f10f..ef2e16ca5e 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -435,25 +435,14 @@ sealed abstract class CompactByteString extends ByteString with Serializable { def compact: this.type = this } -object ByteStringBuilder { - def apply(initialSize: Int = 0): ByteStringBuilder = new ByteStringBuilder(initialSize) - - /** - * An OutputStream that directly wraps a ByteStringBuilder. - */ - class OutputStreamWrapper(val builder: ByteStringBuilder) extends java.io.OutputStream { - def write(b: Int): Unit = builder += b.toByte - - override def write(b: Array[Byte], off: Int, len: Int): Unit = { builder.putBytes(b, off, len) } - } -} - /** * A mutable builder for efficiently creating a [[akka.util.ByteString]]. * * The created ByteString is not automatically compacted. */ final class ByteStringBuilder extends Builder[Byte, ByteString] { + builder ⇒ + import ByteString.{ ByteString1C, ByteString1, ByteStrings } private var _length: Int = 0 private val _builder: VectorBuilder[ByteString1] = new VectorBuilder[ByteString1]() @@ -478,11 +467,6 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { } } - def this(initialSize: Int) = { - this() - if (initialSize > 0) sizeHint(initialSize) - } - def length: Int = _length override def sizeHint(len: Int): Unit = { @@ -719,9 +703,15 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { ByteStrings(bytestrings, _length) } + private val outputStreamWrapper: java.io.OutputStream = new java.io.OutputStream { + def write(b: Int): Unit = builder += b.toByte + + override def write(b: Array[Byte], off: Int, len: Int): Unit = { builder.putBytes(b, off, len) } + } + /** * Directly wraps this ByteStringBuilder in an OutputStream. Write * operations on the stream are forwarded to the builder. */ - def asOutputStream: java.io.OutputStream = new ByteStringBuilder.OutputStreamWrapper(this) + def asOutputStream: java.io.OutputStream = outputStreamWrapper } From 7a854a9a8340f17d076b0a8adbe07fc13c4a4957 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sat, 26 May 2012 14:11:53 +0200 Subject: [PATCH 35/74] Changed MultiByteArrayIterator to operate on a LinearSeq ByteString.ByteStrings new creates it's iterator on a Stream instead of a List, making ByteStings.iterator an O(1) operation. --- .../src/main/scala/akka/util/ByteIterator.scala | 13 ++++++++----- .../src/main/scala/akka/util/ByteString.scala | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 494a053d8e..0d19faf2ae 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -6,7 +6,7 @@ package akka.util import java.nio.{ ByteBuffer, ByteOrder } -import scala.collection.IndexedSeqOptimized +import scala.collection.{ LinearSeq, IndexedSeqOptimized } import scala.collection.mutable.{ Builder, WrappedArray } import scala.collection.immutable.{ IndexedSeq, VectorBuilder } import scala.collection.generic.CanBuildFrom @@ -181,16 +181,16 @@ object ByteIterator { val empty: MultiByteArrayIterator = new MultiByteArrayIterator(Nil) - protected[akka] def apply(iterators: List[ByteArrayIterator]): MultiByteArrayIterator = + protected[akka] def apply(iterators: LinearSeq[ByteArrayIterator]): MultiByteArrayIterator = new MultiByteArrayIterator(iterators) } - class MultiByteArrayIterator private (private var iterators: List[ByteArrayIterator]) extends ByteIterator { + class MultiByteArrayIterator private (private var iterators: LinearSeq[ByteArrayIterator]) extends ByteIterator { // After normalization: // * iterators.isEmpty == false // * (!iterator.head.isEmpty || iterators.tail.isEmpty) == true private def normalize(): this.type = { - @tailrec def norm(xs: List[ByteArrayIterator]): List[ByteArrayIterator] = { + @tailrec def norm(xs: LinearSeq[ByteArrayIterator]): LinearSeq[ByteArrayIterator] = { if (xs.isEmpty) MultiByteArrayIterator.clearedList else if (xs.head.isEmpty) norm(xs.tail) else xs @@ -251,7 +251,10 @@ object ByteIterator { case _ ⇒ super.++(that) } - final override def clone: MultiByteArrayIterator = new MultiByteArrayIterator(iterators map { _.clone }) + final override def clone: MultiByteArrayIterator = { + val clonedIterators: List[ByteArrayIterator] = iterators.map(_.clone)(collection.breakOut) + new MultiByteArrayIterator(clonedIterators) + } final override def take(n: Int): this.type = { var rest = n diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index ef2e16ca5e..8e88e6d9fd 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -211,7 +211,7 @@ object ByteString { } else throw new IndexOutOfBoundsException(idx.toString) override def iterator: ByteIterator.MultiByteArrayIterator = - ByteIterator.MultiByteArrayIterator(bytestrings.map(_.iterator)(collection.breakOut)) + ByteIterator.MultiByteArrayIterator(bytestrings.toStream map { _.iterator }) def ++(that: ByteString): ByteString = { if (that.isEmpty) this From ff57958688db781339aac17a6355cd5f81a2495c Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 27 May 2012 01:12:45 +0200 Subject: [PATCH 36/74] Workaround in ByteStringSpec for Scala-2.9 iterator.span bug --- .../src/test/scala/akka/util/ByteStringSpec.scala | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala index 396fcd6b00..8700c50336 100644 --- a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala @@ -183,7 +183,12 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { "calling hasNext" in { check { a: ByteString ⇒ likeVecIt(a) { _.hasNext } } } "calling length" in { check { a: ByteString ⇒ likeVecIt(a) { _.length } } } "calling duplicate" in { check { a: ByteString ⇒ likeVecIt(a)({ _.duplicate match { case (a, b) ⇒ (a.toSeq, b.toSeq) } }, strict = false) } } - "calling span" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a)({ _.span(_ == b) match { case (a, b) ⇒ (a.toSeq, b.toSeq) } }, strict = false) } } + + // Have to used toList instead of toSeq here, iterator.span (new in + // Scala-2.9) seems to be broken in combination with toSeq for the + // scala.collection default Iterator (see Scala issue SI-5838). + "calling span" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a)({ _.span(_ == b) match { case (a, b) ⇒ (a.toList, b.toList) } }, strict = false) } } + "calling takeWhile" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a)({ _.takeWhile(_ == b).toSeq }, strict = false) } } "calling dropWhile" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a) { _.dropWhile(_ == b).toSeq } } } "calling indexWhere" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a) { _.indexWhere(_ == b) } } } From 85b92b9547f0b86449f803d7599e0e1c592afc52 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 27 May 2012 01:13:45 +0200 Subject: [PATCH 37/74] Improved ByteStringSpec tests for iterator.{span, takeWhile, dropWhile} --- .../src/test/scala/akka/util/ByteStringSpec.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala index 8700c50336..98f878ee90 100644 --- a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala @@ -187,10 +187,10 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { // Have to used toList instead of toSeq here, iterator.span (new in // Scala-2.9) seems to be broken in combination with toSeq for the // scala.collection default Iterator (see Scala issue SI-5838). - "calling span" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a)({ _.span(_ == b) match { case (a, b) ⇒ (a.toList, b.toList) } }, strict = false) } } + "calling span" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a)({ _.span(_ != b) match { case (a, b) ⇒ (a.toList, b.toList) } }, strict = false) } } - "calling takeWhile" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a)({ _.takeWhile(_ == b).toSeq }, strict = false) } } - "calling dropWhile" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a) { _.dropWhile(_ == b).toSeq } } } + "calling takeWhile" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a)({ _.takeWhile(_ != b).toSeq }, strict = false) } } + "calling dropWhile" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a) { _.dropWhile(_ != b).toSeq } } } "calling indexWhere" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a) { _.indexWhere(_ == b) } } } "calling indexOf" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a) { _.indexOf(b) } } } "calling toSeq" in { check { a: ByteString ⇒ likeVecIt(a) { _.toSeq } } } From 76010d5b5670767342381e1f9e3deaa84729994e Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 27 May 2012 01:20:01 +0200 Subject: [PATCH 38/74] Changed ByteIterator.span to return operand iterator first in tuple Makes things more consistent, since ByteIterator.duplicate also returns the operand iterator first. --- akka-actor/src/main/scala/akka/util/ByteIterator.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 0d19faf2ae..52f0d96ced 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -444,9 +444,9 @@ abstract class ByteIterator extends BufferedIterator[Byte] { override def span(p: Byte ⇒ Boolean): (ByteIterator, ByteIterator) = { val that = clone - that.takeWhile(p) - drop(that.len) - (that, this) + this.takeWhile(p) + that.drop(this.len) + (this, that) } final override def indexWhere(p: Byte ⇒ Boolean): Int = { From 9865a4e3e1e4dfa2abcaa873f12c5c9251e8539d Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 27 May 2012 01:58:17 +0200 Subject: [PATCH 39/74] Added specialized implementation of foreach to MultiByteArrayIterator --- akka-actor/src/main/scala/akka/util/ByteIterator.scala | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 52f0d96ced..ad2d93e6d2 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -314,6 +314,11 @@ object ByteIterator { normalize() } + override def foreach[@specialized U](f: Byte ⇒ U): Unit = { + iterators foreach { _ foreach f } + clear() + } + final override def toByteString: ByteString = { if (iterators.tail isEmpty) iterators.head.toByteString else { @@ -474,12 +479,12 @@ abstract class ByteIterator extends BufferedIterator[Byte] { override def toSeq: ByteString = toByteString - @inline final override def foreach[@specialized U](f: Byte ⇒ U): Unit = + override def foreach[@specialized U](f: Byte ⇒ U): Unit = while (hasNext) f(next()) final override def foldLeft[@specialized B](z: B)(op: (B, Byte) ⇒ B): B = { var acc = z - while (hasNext) acc = op(acc, next()) + foreach { byte ⇒ acc = op(acc, byte) } acc } From 34e469c609105382cb174b76d4776d256524b6b1 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 27 May 2012 02:34:25 +0200 Subject: [PATCH 40/74] Fixed typos in ByteString comments --- akka-actor/src/main/scala/akka/util/ByteString.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 8e88e6d9fd..383371db0d 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -67,7 +67,7 @@ object ByteString { } /** - * A compact (unsliced) and unfragmented ByteString, implementaton of ByteString1C. + * A compact (unsliced) and unfragmented ByteString, implementation of ByteString1C. */ @SerialVersionUID(3956956327691936932L) final class ByteString1C private (private val bytes: Array[Byte]) extends CompactByteString { @@ -319,7 +319,7 @@ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimiz /** * Check whether this ByteString is compact in memory. * If the ByteString is compact, it might, however, not be represented - * by an object that takes full adventage of that fact. Use compact to + * by an object that takes full advantage of that fact. Use compact to * get such an object. */ def isCompact: Boolean From 60c7a57ddf6ae65b1eba0cc0290ecc117a35b7ba Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 27 May 2012 12:55:48 +0200 Subject: [PATCH 41/74] Code formatting changes as requested by Victor --- .../main/scala/akka/util/ByteIterator.scala | 20 ++++++------------- .../src/main/scala/akka/util/ByteString.scala | 14 ++++--------- 2 files changed, 10 insertions(+), 24 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index ad2d93e6d2..10a6ab64ac 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -58,11 +58,11 @@ object ByteIterator { final override def length: Int = { val l = len; clear(); l } final override def ++(that: TraversableOnce[Byte]): ByteIterator = that match { - case that: ByteIterator ⇒ { + case that: ByteIterator ⇒ if (that.isEmpty) this else if (this.isEmpty) that else that match { - case that: ByteArrayIterator ⇒ { + case that: ByteArrayIterator ⇒ if ((this.array eq that.array) && (this.until == that.from)) { this.until = that.until that.clear() @@ -72,10 +72,8 @@ object ByteIterator { this.clear() result } - } case that: MultiByteArrayIterator ⇒ this +: that } - } case _ ⇒ super.++(that) } @@ -230,24 +228,21 @@ object ByteIterator { } final override def ++(that: TraversableOnce[Byte]): ByteIterator = that match { - case that: ByteIterator ⇒ { + case that: ByteIterator ⇒ if (that.isEmpty) this else if (this.isEmpty) that else { that match { - case that: ByteArrayIterator ⇒ { + case that: ByteArrayIterator ⇒ iterators = this.iterators :+ that that.clear() this - } - case that: MultiByteArrayIterator ⇒ { + case that: MultiByteArrayIterator ⇒ iterators = this.iterators ++ that.iterators that.clear() this - } } } - } case _ ⇒ super.++(that) } @@ -410,10 +405,7 @@ abstract class ByteIterator extends BufferedIterator[Byte] { protected def clear(): Unit - def ++(that: TraversableOnce[Byte]): ByteIterator = { - if (that.isEmpty) this - else ByteIterator.ByteArrayIterator(that.toArray) - } + def ++(that: TraversableOnce[Byte]): ByteIterator = if (that.isEmpty) this else ByteIterator.ByteArrayIterator(that.toArray) // *must* be overridden by derived classes. This construction is necessary // to specialize the return type, as the method is already implemented in diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 383371db0d..128373076d 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -142,11 +142,10 @@ object ByteString { else if (this.isEmpty) that else that match { case b: ByteString1C ⇒ ByteStrings(this, b.toByteString1) - case b: ByteString1 ⇒ { + case b: ByteString1 ⇒ if ((bytes eq b.bytes) && (startIndex + length == b.startIndex)) new ByteString1(bytes, startIndex, length + b.length) else ByteStrings(this, b) - } case bs: ByteStrings ⇒ ByteStrings(this, bs) } } @@ -223,10 +222,7 @@ object ByteString { } } - def isCompact: Boolean = { - if (bytestrings.length == 1) bytestrings.head.isCompact - else false - } + def isCompact: Boolean = if (bytestrings.length == 1) bytestrings.head.isCompact else false def compact: CompactByteString = { if (isCompact) bytestrings.head.compact @@ -555,7 +551,7 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { */ def putInt(x: Int)(implicit byteOrder: ByteOrder): this.type = { fillArray(4) { - case (target, offset) ⇒ { + case (target, offset) ⇒ if (byteOrder == ByteOrder.BIG_ENDIAN) { target(offset + 0) = (x >>> 24).toByte target(offset + 1) = (x >>> 16).toByte @@ -567,7 +563,6 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { target(offset + 2) = (x >>> 16).toByte target(offset + 3) = (x >>> 24).toByte } else throw new IllegalArgumentException("Unknown byte order " + byteOrder) - } } this } @@ -577,7 +572,7 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { */ def putLong(x: Long)(implicit byteOrder: ByteOrder): this.type = { fillArray(8) { - case (target, offset) ⇒ { + case (target, offset) ⇒ if (byteOrder == ByteOrder.BIG_ENDIAN) { target(offset + 0) = (x >>> 56).toByte target(offset + 1) = (x >>> 48).toByte @@ -597,7 +592,6 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { target(offset + 6) = (x >>> 48).toByte target(offset + 7) = (x >>> 56).toByte } else throw new IllegalArgumentException("Unknown byte order " + byteOrder) - } } this } From d59fbbf8e445e34d0ee402eec7e4303841fc3c55 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 27 May 2012 12:56:28 +0200 Subject: [PATCH 42/74] Made bytestrings private in ByteString.ByteStrings --- akka-actor/src/main/scala/akka/util/ByteString.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 128373076d..dba4ed77f7 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -195,7 +195,7 @@ object ByteString { /** * A ByteString with 2 or more fragments. */ - final class ByteStrings private (val bytestrings: Vector[ByteString1], val length: Int) extends ByteString { + final class ByteStrings private (private[akka] val bytestrings: Vector[ByteString1], val length: Int) extends ByteString { if (bytestrings.isEmpty) throw new IllegalArgumentException("bytestrings must not be empty") def apply(idx: Int): Byte = From b5fa527d2cb42eea10e2d54ebbc3d5cc3c5892ca Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 27 May 2012 13:00:40 +0200 Subject: [PATCH 43/74] Simplified implementation of ByteIterator.indexOf --- .../src/main/scala/akka/util/ByteIterator.scala | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 10a6ab64ac..460246380a 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -453,19 +453,9 @@ abstract class ByteIterator extends BufferedIterator[Byte] { if (found) index else -1 } - final def indexOf(elem: Byte): Int = { - var index = 0 - var found = false - while (!found && hasNext) if (elem == next()) { found = true } else { index += 1 } - if (found) index else -1 - } + final def indexOf(elem: Byte): Int = indexWhere { _ == elem } - final override def indexOf[B >: Byte](elem: B): Int = { - var index = 0 - var found = false - while (!found && hasNext) if (elem == next()) { found = true } else { index += 1 } - if (found) index else -1 - } + final override def indexOf[B >: Byte](elem: B): Int = indexWhere { _ == elem } def toByteString: ByteString From 4658e4f16590ca43bc0351223b5b0d4ced02b1d3 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Wed, 30 May 2012 18:05:54 +0200 Subject: [PATCH 44/74] Removed some remnant exposed internals from ByteArrayIterator --- akka-actor/src/main/scala/akka/util/ByteIterator.scala | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 460246380a..de7cb3ff76 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -31,13 +31,9 @@ object ByteIterator { class ByteArrayIterator private (private var array: Array[Byte], private var from: Int, private var until: Int) extends ByteIterator { iterator ⇒ - protected[util] final def internalArray = array - protected[util] final def internalFrom = from - protected[util] final def internalUntil = until - final def isIdenticalTo(that: Iterator[Byte]): Boolean = that match { case that: ByteArrayIterator ⇒ - ((this.array) eq (that.internalArray)) && + ((this.array) eq (that.array)) && ((this.from) == (that.from)) && ((this.until) == (that.until)) case _ ⇒ false } From a0ea833d54e0492333fbde69aed58e3490a694dd Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Wed, 30 May 2012 18:06:01 +0200 Subject: [PATCH 45/74] Improved ByteArrayIterator.empty --- akka-actor/src/main/scala/akka/util/ByteIterator.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index de7cb3ff76..13915335fa 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -25,7 +25,7 @@ object ByteIterator { protected[akka] def apply(array: Array[Byte], from: Int, until: Int): ByteArrayIterator = new ByteArrayIterator(array, from, until) - val empty: ByteArrayIterator = apply(Array.empty[Byte]) + val empty: ByteArrayIterator = apply(emptyArray) } class ByteArrayIterator private (private var array: Array[Byte], private var from: Int, private var until: Int) extends ByteIterator { From b285b0821f3287de5085e4b757d85b57821573f6 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 31 May 2012 00:39:15 +0200 Subject: [PATCH 46/74] Made things that don't need to be public private in ByteString / -Iterator --- akka-actor/src/main/scala/akka/util/ByteIterator.scala | 4 ++-- akka-actor/src/main/scala/akka/util/ByteString.scala | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 13915335fa..a2bc455c38 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -68,7 +68,7 @@ object ByteIterator { this.clear() result } - case that: MultiByteArrayIterator ⇒ this +: that + case that: MultiByteArrayIterator ⇒ this ++: that } case _ ⇒ super.++(that) } @@ -218,7 +218,7 @@ object ByteIterator { result } - def +:(that: ByteArrayIterator): this.type = { + private[akka] def ++:(that: ByteArrayIterator): this.type = { iterators = that +: iterators this } diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index dba4ed77f7..8aeed6daf2 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -77,7 +77,7 @@ object ByteString { override def iterator: ByteIterator.ByteArrayIterator = ByteIterator.ByteArrayIterator(bytes, 0, bytes.length) - def toByteString1: ByteString1 = ByteString1(bytes) + private[akka] def toByteString1: ByteString1 = ByteString1(bytes) override def clone: ByteString1C = new ByteString1C(toArray) From 5b83dc366ed1614e56ad4a65f61c9775fe698b2d Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 31 May 2012 00:44:21 +0200 Subject: [PATCH 47/74] Removed unnecessary implementations of ByteString.clone * As all ByteString implementations are immutable, an explicit override of Object.clone doesn't seem necessary. --- akka-actor/src/main/scala/akka/util/ByteString.scala | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 8aeed6daf2..98623429e3 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -79,8 +79,6 @@ object ByteString { private[akka] def toByteString1: ByteString1 = ByteString1(bytes) - override def clone: ByteString1C = new ByteString1C(toArray) - def asByteBuffer: ByteBuffer = toByteString1.asByteBuffer @@ -121,12 +119,10 @@ object ByteString { throw new IndexOutOfBoundsException(index.toString) } - override def clone: CompactByteString = ByteString1C(toArray) - def isCompact: Boolean = (length == bytes.length) def compact: CompactByteString = - if (isCompact) ByteString1C(bytes) else clone + if (isCompact) ByteString1C(bytes) else ByteString1C(toArray) def asByteBuffer: ByteBuffer = { val buffer = ByteBuffer.wrap(bytes, startIndex, length).asReadOnlyBuffer From 3c9409a4b0086e39da09ad7365ec7ddd274a7127 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 31 May 2012 00:48:56 +0200 Subject: [PATCH 48/74] Added test for ByteString.compact to ByteStringSpec --- .../src/test/scala/akka/util/ByteStringSpec.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala index 98f878ee90..da4b3a87a0 100644 --- a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala @@ -163,7 +163,9 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { "be sequential" when { "taking" in { check((a: ByteString, b: ByteString) ⇒ (a ++ b).take(a.size) == a) } "dropping" in { check((a: ByteString, b: ByteString) ⇒ (a ++ b).drop(a.size) == b) } - + } + "be equal to the original" when { + "compacting" in { check { xs: ByteString ⇒ val ys = xs.compact; (xs == ys) && ys.isCompact } } "recombining" in { check { (xs: ByteString, from: Int, until: Int) ⇒ val (tmp, c) = xs.splitAt(until) From 904f1ba0bcbc57acbaec56dfd460539af48cfb04 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 31 May 2012 00:56:33 +0200 Subject: [PATCH 49/74] Removed obsolete ByteIterator.isIdenticalTo --- .../src/main/scala/akka/util/ByteIterator.scala | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index a2bc455c38..5de9f5f333 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -31,13 +31,6 @@ object ByteIterator { class ByteArrayIterator private (private var array: Array[Byte], private var from: Int, private var until: Int) extends ByteIterator { iterator ⇒ - final def isIdenticalTo(that: Iterator[Byte]): Boolean = that match { - case that: ByteArrayIterator ⇒ - ((this.array) eq (that.array)) && - ((this.from) == (that.from)) && ((this.until) == (that.until)) - case _ ⇒ false - } - @inline final def len: Int = until - from @inline final def hasNext: Boolean = from < until @@ -198,8 +191,6 @@ object ByteIterator { @inline private def dropCurrent(): Unit = { iterators = iterators.tail } @inline def clear(): Unit = { iterators = MultiByteArrayIterator.empty.iterators } - final def isIdenticalTo(that: Iterator[Byte]): Boolean = false - @inline final def hasNext: Boolean = current.hasNext @inline final def head: Byte = current.head @@ -391,8 +382,6 @@ object ByteIterator { */ abstract class ByteIterator extends BufferedIterator[Byte] { - def isIdenticalTo(that: Iterator[Byte]): Boolean - def len: Int def head: Byte From 205b8ee7c13ad570429492081d6277e7c0882f69 Mon Sep 17 00:00:00 2001 From: Gert Vanthienen Date: Thu, 31 May 2012 22:53:15 +0200 Subject: [PATCH 50/74] Initial stab at a Blueprint namespace handler --- .../additional/code/osgi/Activator.scala | 16 +++ akka-docs/additional/code/osgi/blueprint.xml | 10 ++ akka-docs/additional/osgi.rst | 17 +++ .../blueprint/akka-namespacehandler.xml | 15 +++ .../akka/osgi/blueprint/aries/akka.xsd | 67 ++++++++++++ .../akka/osgi/ActorSystemActivator.scala | 39 +------ .../akka/osgi/OsgiActorSystemFactory.scala | 56 ++++++++++ .../BlueprintActorSystemFactory.scala | 34 ++++++ .../blueprint/aries/NamespaceHandler.scala | 100 ++++++++++++++++++ .../impl/BundleDelegatingClassLoader.scala | 74 +++++++++++++ .../akka/osgi/blueprint/aries/simple.xml | 9 ++ .../akka/osgi/ActorSystemActivatorTest.scala | 3 - project/AkkaBuild.scala | 4 +- 13 files changed, 406 insertions(+), 38 deletions(-) create mode 100644 akka-docs/additional/code/osgi/Activator.scala create mode 100644 akka-docs/additional/code/osgi/blueprint.xml create mode 100644 akka-osgi/src/main/resources/OSGI-INF/blueprint/akka-namespacehandler.xml create mode 100644 akka-osgi/src/main/resources/akka/osgi/blueprint/aries/akka.xsd create mode 100644 akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala create mode 100644 akka-osgi/src/main/scala/akka/osgi/blueprint/BlueprintActorSystemFactory.scala create mode 100644 akka-osgi/src/main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala create mode 100644 akka-osgi/src/main/scala/akka/osgi/impl/BundleDelegatingClassLoader.scala create mode 100644 akka-osgi/src/test/resources/akka/osgi/blueprint/aries/simple.xml diff --git a/akka-docs/additional/code/osgi/Activator.scala b/akka-docs/additional/code/osgi/Activator.scala new file mode 100644 index 0000000000..0e3a5c82ee --- /dev/null +++ b/akka-docs/additional/code/osgi/Activator.scala @@ -0,0 +1,16 @@ +import akka.actor.{Props, ActorSystem} +import akka.osgi.ActorSystemActivator +import org.apache.servicemix.examples.akka.Listener +import org.apache.servicemix.examples.akka.Master + +//#Activator +class Activator extends ActorSystemActivator("PiSystem") { + + def configure(system: ActorSystem) { + val listener = system.actorOf(Props[Listener], name = "listener") + val master = system.actorOf(Props(new Master(4, 10000, 10000, listener)), name = "master") + master ! Calculate + } + +} +//#Activator \ No newline at end of file diff --git a/akka-docs/additional/code/osgi/blueprint.xml b/akka-docs/additional/code/osgi/blueprint.xml new file mode 100644 index 0000000000..f817da85b0 --- /dev/null +++ b/akka-docs/additional/code/osgi/blueprint.xml @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/akka-docs/additional/osgi.rst b/akka-docs/additional/osgi.rst index aea554ef9c..3bedc8c7dd 100644 --- a/akka-docs/additional/osgi.rst +++ b/akka-docs/additional/osgi.rst @@ -8,3 +8,20 @@ To use Akka in an OSGi environment, the ``org.osgi.framework.bootdelegation`` property must be set to always delegate the ``sun.misc`` package to the boot classloader instead of resolving it through the normal OSGi class space. + +Activator +--------- + +To bootstrap Akka inside an OSGi environment, you can use the akka.osgi.AkkaSystemActivator class +to conveniently set up the ActorSystem. + +.. includecode:: code/osgi/Activator.scala#Activator + + +Blueprint +--------- + +For the Apache Aries Blueprint implementation, there's also a namespace handler available. The namespace URI +is http://akka.io/xmlns/blueprint/v1.0.0 and it can be used to set up an ActorSystem. + +.. includecode:: code/osgi/blueprint.xml diff --git a/akka-osgi/src/main/resources/OSGI-INF/blueprint/akka-namespacehandler.xml b/akka-osgi/src/main/resources/OSGI-INF/blueprint/akka-namespacehandler.xml new file mode 100644 index 0000000000..650738b10a --- /dev/null +++ b/akka-osgi/src/main/resources/OSGI-INF/blueprint/akka-namespacehandler.xml @@ -0,0 +1,15 @@ + + + + + + + + http://akka.io/xmlns/blueprint/v1.0.0 + + + + + + + diff --git a/akka-osgi/src/main/resources/akka/osgi/blueprint/aries/akka.xsd b/akka-osgi/src/main/resources/akka/osgi/blueprint/aries/akka.xsd new file mode 100644 index 0000000000..256dff22e9 --- /dev/null +++ b/akka-osgi/src/main/resources/akka/osgi/blueprint/aries/akka.xsd @@ -0,0 +1,67 @@ + + + + + + + + + + + + Defines the configuration elements for setting up Akka with Blueprint + + + + + + + + Defines an Akka ActorSystem + + + + + + + + + + + + + + + + Defines an Akka Actor + + + + + + + + + diff --git a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala index d63404334a..c6d24e8262 100644 --- a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala +++ b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala @@ -9,7 +9,10 @@ import java.util.Properties * Abstract {@link BundleActivator} implementation to bootstrap and configure an {@link ActorSystem} in an * OSGi environment. */ -abstract class ActorSystemActivator extends BundleActivator { +abstract class ActorSystemActivator(nameFor: (BundleContext) ⇒ String) extends BundleActivator { + + def this() = this({ context: BundleContext ⇒ null }) + def this(name: String) = this({ context: BundleContext ⇒ name }) var system: ActorSystem = null @@ -26,12 +29,8 @@ abstract class ActorSystemActivator extends BundleActivator { * @param context the BundleContext */ def start(context: BundleContext) { - system = createActorSystem(context) + system = OsgiActorSystemFactory(context).createActorSystem(nameFor(context)) configure(system) - - val properties = new Properties(); - properties.put("name", getActorSystemName(context)) - context.registerService(classOf[ActorSystem].getName, system, properties) } /** @@ -47,32 +46,4 @@ abstract class ActorSystemActivator extends BundleActivator { } } - /** - * Strategy method to create the ActorSystem. - */ - def createActorSystem(context: BundleContext) = - ActorSystem(getActorSystemName(context), getActorSystemConfig(context), getClass.getClassLoader) - - - /** - * Strategy method to create the Config for the ActorSystem, ensuring that the default/reference configuration is - * loaded from the akka-actor bundle. - */ - def getActorSystemConfig(context: BundleContext): Config = { - val reference = ConfigFactory.defaultReference(classOf[ActorSystem].getClassLoader) - ConfigFactory.load(getClass.getClassLoader).withFallback(reference) - } - - /** - * Strategy method to determine the ActorSystem name - override this method to define the ActorSytem name yourself. - * - * The default implementation will use 'bundle--ActorSystem' where matches the bundle id for the containing bundle. - * - * @param context the BundleContext - * @return the ActorSystem name - */ - def getActorSystemName(context: BundleContext): String = { - "bundle-%s-ActorSystem".format(context.getBundle().getBundleId) - } - } diff --git a/akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala b/akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala new file mode 100644 index 0000000000..8c41521964 --- /dev/null +++ b/akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala @@ -0,0 +1,56 @@ +package akka.osgi + +import impl.BundleDelegatingClassLoader +import org.osgi.framework.BundleContext +import java.util.Properties +import akka.actor.ActorSystem +import com.typesafe.config.{ ConfigFactory, Config } + +/** + * Factory class to create ActorSystem implementations in an OSGi environment. This mainly involves dealing with + * bundle classloaders appropriately to ensure that configuration files and classes get loaded properly + */ +class OsgiActorSystemFactory(val context: BundleContext) { + + /* + * Classloader that delegates to the bundle for which the factory is creating an ActorSystem + */ + val classloader = BundleDelegatingClassLoader.createFor(context) + + /** + * Creates the ActorSystem and registers it in the OSGi Service Registry + */ + def createActorSystem(name: String) = { + val system = ActorSystem(actorSystemName(name), actorSystemConfig(context), classloader) + registerService(system) + system + } + + def registerService(system: ActorSystem) { + val properties = new Properties(); + properties.put("name", system.name) + context.registerService(classOf[ActorSystem].getName, system, properties) + } + + /** + * Strategy method to create the Config for the ActorSystem, ensuring that the default/reference configuration is + * loaded from the akka-actor bundle. + */ + def actorSystemConfig(context: BundleContext): Config = { + val reference = ConfigFactory.defaultReference(classOf[ActorSystem].getClassLoader) + ConfigFactory.load(classloader).withFallback(reference) + } + + /** + * Determine a the ActorSystem name + */ + def actorSystemName(name: String): String = + Option(name).getOrElse("bundle-%s-ActorSystem".format(context.getBundle().getBundleId)) + +} + +object OsgiActorSystemFactory { + + def apply(context: BundleContext) = new OsgiActorSystemFactory(context) + +} diff --git a/akka-osgi/src/main/scala/akka/osgi/blueprint/BlueprintActorSystemFactory.scala b/akka-osgi/src/main/scala/akka/osgi/blueprint/BlueprintActorSystemFactory.scala new file mode 100644 index 0000000000..92e7e8a099 --- /dev/null +++ b/akka-osgi/src/main/scala/akka/osgi/blueprint/BlueprintActorSystemFactory.scala @@ -0,0 +1,34 @@ +package akka.osgi.blueprint + +import org.osgi.framework.BundleContext +import akka.osgi.OsgiActorSystemFactory +import collection.mutable.Buffer +import akka.actor.{ Actor, Props, ActorSystem } + +/** + * A set of helper/factory classes to build a Akka system using Blueprint + */ +class BlueprintActorSystemFactory(context: BundleContext, name: String) extends OsgiActorSystemFactory(context) { + + val systems: Buffer[ActorSystem] = Buffer() + + def this(context: BundleContext) = this(context, null) + + def create: ActorSystem = create(null) + def create(name: String): ActorSystem = { + val system = super.createActorSystem(name) + systems += system + system + } + + def destroy = for (system ← systems) { + system.shutdown() + } +} + +class BlueprintActorSystem(context: BundleContext, system: ActorSystem) { + + def createActor(name: String) = system.actorOf(Props(context.getBundle.loadClass(name).asInstanceOf[Class[Actor]])) + +} + diff --git a/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala b/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala new file mode 100644 index 0000000000..b1412eae91 --- /dev/null +++ b/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala @@ -0,0 +1,100 @@ +package akka.osgi.blueprint.aries + +import org.apache.aries.blueprint.ParserContext +import org.osgi.service.blueprint.container.ComponentDefinitionException +import org.apache.aries.blueprint.mutable.MutableBeanMetadata + +import collection.JavaConversions.setAsJavaSet +import org.osgi.framework.BundleContext +import org.apache.aries.blueprint.reflect.{ ValueMetadataImpl, RefMetadataImpl, BeanArgumentImpl } +import org.w3c.dom.{ NodeList, Element, Node } +import org.osgi.service.blueprint.reflect.{ BeanMetadata, ComponentMetadata } +import akka.actor.{ ActorRef, ActorSystem } +import akka.osgi.blueprint.{ BlueprintActorSystem, BlueprintActorSystemFactory } + +/** + * Aries Blueprint namespace handler implementation + */ +class NamespaceHandler extends org.apache.aries.blueprint.NamespaceHandler { + + val CLASS_ATTRIBUTE = "class"; + val ID_ATTRIBUTE = "id"; + val NAME_ATTRIBUTE = "name"; + + var idCounter = 1 + + def getSchemaLocation(namespace: String) = getClass().getResource("akka.xsd") + + def getManagedClasses = setAsJavaSet(Set(classOf[BlueprintActorSystemFactory])) + + def parse(element: Element, context: ParserContext) = { + val factory = context.createMetadata(classOf[MutableBeanMetadata]) + factory.setId(getId(context, element)) + factory.setScope(BeanMetadata.SCOPE_SINGLETON) + factory.setProcessor(true) + factory.setClassName(classOf[BlueprintActorSystemFactory].getName) + factory.setDestroyMethod("destroy") + factory.addArgument(new BeanArgumentImpl(new RefMetadataImpl("blueprintBundleContext"), classOf[BundleContext].getName, -1)) + + val system = context.createMetadata(classOf[MutableBeanMetadata]) + system.setId(getId(context, element)) + system.setFactoryComponent(factory) + system.setFactoryMethod("create") + system.setRuntimeClass(classOf[ActorSystem]) + if (element.hasAttribute(NAME_ATTRIBUTE)) { + system.addArgument(new BeanArgumentImpl(new ValueMetadataImpl(element.getAttribute(NAME_ATTRIBUTE)), classOf[String].getName, -1)) + } + + val actorsystem = context.createMetadata(classOf[MutableBeanMetadata]) + actorsystem.setId(getId(context, element)) + actorsystem.setClassName(classOf[BlueprintActorSystem].getName) + actorsystem.addArgument(new BeanArgumentImpl(new RefMetadataImpl("blueprintBundleContext"), classOf[BundleContext].getName, -1)) + actorsystem.addArgument(new BeanArgumentImpl(system, classOf[ActorSystem].getName, -1)) + context.getComponentDefinitionRegistry.registerComponentDefinition(actorsystem) + + val nodelist = element.getChildNodes + var i = 0 + while (i < nodelist.getLength) { + val node = nodelist.item(i) + node.getLocalName match { + case "actor" if node.isInstanceOf[Element] ⇒ parseActor(node.asInstanceOf[Element], context, actorsystem) + case _ ⇒ + } + i += 1 + } + factory + } + + def parseActor(node: Element, context: ParserContext, actorsystem: MutableBeanMetadata) = { + val actor = context.createMetadata(classOf[MutableBeanMetadata]) + actor.setFactoryComponent(actorsystem) + if (node.hasAttribute(CLASS_ATTRIBUTE)) { + actor.addArgument(new BeanArgumentImpl(new ValueMetadataImpl(node.getAttribute(CLASS_ATTRIBUTE)), classOf[String].getName, -1)) + } + actor.setId(getId(context, node)) + actor.setFactoryMethod("createActor") + // actor.setRuntimeClass(classOf[ActorRef]) + context.getComponentDefinitionRegistry.registerComponentDefinition(actor) + } + + def decorate(node: Node, component: ComponentMetadata, context: ParserContext) = + throw new ComponentDefinitionException("Bad xml syntax: node decoration is not supported"); + + def getId(context: ParserContext, element: Element) = { + if (element.hasAttribute(ID_ATTRIBUTE)) { + element.getAttribute(ID_ATTRIBUTE); + } else { + generateId(context); + } + } + + def generateId(context: ParserContext): String = { + var id = ""; + do { + idCounter += 1 + id = ".akka-" + idCounter; + } while (context.getComponentDefinitionRegistry().containsComponentDefinition(id)); + id; + } + +} diff --git a/akka-osgi/src/main/scala/akka/osgi/impl/BundleDelegatingClassLoader.scala b/akka-osgi/src/main/scala/akka/osgi/impl/BundleDelegatingClassLoader.scala new file mode 100644 index 0000000000..74592392d9 --- /dev/null +++ b/akka-osgi/src/main/scala/akka/osgi/impl/BundleDelegatingClassLoader.scala @@ -0,0 +1,74 @@ +package akka.osgi.impl + +import java.net.URL +import java.util.Enumeration + +import org.osgi.framework.{ BundleContext, Bundle } + +/* + * Companion object to create bundle delegating classloader instances + */ +object BundleDelegatingClassLoader { + + /* + * Create a bundle delegating classloader for the bundle context's bundle + */ + def createFor(context: BundleContext) = new BundleDelegatingClassLoader(context.getBundle) + +} + +/* + * A bundle delegating classloader implemenation - this will try to load classes and resources from the bundle + * specified first and if there's a classloader specified, that will be used as a fallback + */ +class BundleDelegatingClassLoader(bundle: Bundle, classLoader: Option[ClassLoader]) extends ClassLoader { + + def this(bundle: Bundle) = this(bundle, None) + + protected override def findClass(name: String): Class[_] = bundle.loadClass(name) + + protected override def findResource(name: String): URL = { + val resource: URL = bundle.getResource(name) + classLoader match { + case Some(loader) if resource == null ⇒ loader.getResource(name) + case _ ⇒ resource + } + } + + @SuppressWarnings(Array("unchecked", "rawtypes")) + protected override def findResources(name: String): Enumeration[URL] = + bundle.getResources(name).asInstanceOf[Enumeration[URL]] + + protected override def loadClass(name: String, resolve: Boolean): Class[_] = { + val clazz: Class[_] = try { + findClass(name) + } catch { + case cnfe: ClassNotFoundException ⇒ { + classLoader match { + case Some(loader) ⇒ loadClass(name, loader) + case None ⇒ rethrowClassNotFoundException(name, cnfe) + } + } + } + if (resolve) { + resolveClass(clazz) + } + clazz + } + + private def loadClass(name: String, classLoader: ClassLoader) = + try { + classLoader.loadClass(name) + } catch { + case cnfe: ClassNotFoundException ⇒ rethrowClassNotFoundException(name, cnfe) + } + + def rethrowClassNotFoundException(name: String, cnfe: ClassNotFoundException): Nothing = + throw new ClassNotFoundException(name + " from bundle " + bundle.getBundleId + " (" + bundle.getSymbolicName + ")", cnfe) + + def getBundle: Bundle = bundle + + override def toString = String.format("BundleDelegatingClassLoader(%s)", bundle) + +} + diff --git a/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/simple.xml b/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/simple.xml new file mode 100644 index 0000000000..d276ee86a0 --- /dev/null +++ b/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/simple.xml @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala b/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala index ffcc3cc0e7..34472e3537 100644 --- a/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala +++ b/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala @@ -29,10 +29,7 @@ class ActorSystemActivatorTest extends FlatSpec { lazy val context: BundleContext = { val config = new HashMap[String, AnyRef](); - config.put(PojoServiceRegistryFactory.BUNDLE_DESCRIPTORS, new ClasspathScanner().scanForBundles()); - val loader = ServiceLoader.load(classOf[PojoServiceRegistryFactory]); - val registry = loader.iterator().next().newPojoServiceRegistry(config); registry.getBundleContext } diff --git a/project/AkkaBuild.scala b/project/AkkaBuild.scala index f0e6446879..629d0475f3 100644 --- a/project/AkkaBuild.scala +++ b/project/AkkaBuild.scala @@ -428,7 +428,7 @@ object Dependencies { val camel = Seq(camelCore, Test.scalatest, Test.junit, Test.mockito) - val osgi = Seq(osgiCore, Test.pojosr, Test.scalatest, Test.junit) + val osgi = Seq(osgiCore, ariesBlueprint, Runtime.logback, Test.pojosr, Test.tinybundles, Test.scalatest, Test.junit) val tutorials = Seq(Test.scalatest, Test.junit) @@ -454,6 +454,7 @@ object Dependency { // Compile + val ariesBlueprint = "org.apache.aries.blueprint" % "org.apache.aries.blueprint" % "0.3.1" // ApacheV2 val camelCore = "org.apache.camel" % "camel-core" % V.Camel // ApacheV2 val netty = "io.netty" % "netty" % V.Netty // ApacheV2 val osgiCore = "org.osgi" % "org.osgi.core" % V.OSGi // ApacheV2 @@ -480,6 +481,7 @@ object Dependency { val scalatest = "org.scalatest" % "scalatest_2.9.1" % V.Scalatest % "test" // ApacheV2 val scalacheck = "org.scala-tools.testing" % "scalacheck_2.9.1" % "1.9" % "test" // New BSD val specs2 = "org.specs2" % "specs2_2.9.1" % "1.9" % "test" // Modified BSD / ApacheV2 + val tinybundles = "org.ops4j.pax.tinybundles" % "tinybundles" % "1.0.0" % "test" // ApacheV2 val zookeeper = "org.apache.hadoop.zookeeper" % "zookeeper" % "3.4.0" % "test" // ApacheV2 val log4j = "log4j" % "log4j" % "1.2.14" % "test" // ApacheV2 } From 7231b48c055724693f313b0101433a9eacb23b37 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Fri, 8 Jun 2012 17:57:41 +0900 Subject: [PATCH 51/74] Added tests for Float/Double encoding/decoding to ByteStringSpec --- .../test/scala/akka/util/ByteStringSpec.scala | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala index da4b3a87a0..db7162c3be 100644 --- a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala @@ -11,6 +11,8 @@ import org.scalacheck.Gen._ import java.nio.{ ByteBuffer, ShortBuffer, IntBuffer, FloatBuffer, DoubleBuffer } import java.nio.{ ByteOrder }, ByteOrder.{ BIG_ENDIAN, LITTLE_ENDIAN } +import java.lang.Float.floatToRawIntBits +import java.lang.Double.doubleToRawLongBits class ByteStringSpec extends WordSpec with MustMatchers with Checkers { @@ -119,6 +121,36 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { (decoded.toSeq == reference.toSeq) && (input.toSeq == bytes.drop(n * elemSize)) } + def testFloatDecoding(slice: ByteStringSlice, byteOrder: ByteOrder): Boolean = { + val elemSize = 4 + val (bytes, from, until) = slice + val (n, a, b) = (bytes.length / elemSize, from / elemSize, until / elemSize) + val reference = Array.ofDim[Float](n) + bytes.asByteBuffer.order(byteOrder).asFloatBuffer.get(reference, 0, n) + val input = bytes.iterator + val decoded = Array.ofDim[Float](n) + for (i ← 0 to a - 1) decoded(i) = input.getFloat(byteOrder) + input.getFloats(decoded, a, b - a)(byteOrder) + for (i ← b to n - 1) decoded(i) = input.getFloat(byteOrder) + ((decoded.toSeq map floatToRawIntBits) == (reference.toSeq map floatToRawIntBits)) && + (input.toSeq == bytes.drop(n * elemSize)) + } + + def testDoubleDecoding(slice: ByteStringSlice, byteOrder: ByteOrder): Boolean = { + val elemSize = 8 + val (bytes, from, until) = slice + val (n, a, b) = (bytes.length / elemSize, from / elemSize, until / elemSize) + val reference = Array.ofDim[Double](n) + bytes.asByteBuffer.order(byteOrder).asDoubleBuffer.get(reference, 0, n) + val input = bytes.iterator + val decoded = Array.ofDim[Double](n) + for (i ← 0 to a - 1) decoded(i) = input.getDouble(byteOrder) + input.getDoubles(decoded, a, b - a)(byteOrder) + for (i ← b to n - 1) decoded(i) = input.getDouble(byteOrder) + ((decoded.toSeq map doubleToRawLongBits) == (reference.toSeq map doubleToRawLongBits)) && + (input.toSeq == bytes.drop(n * elemSize)) + } + def testShortEncoding(slice: ArraySlice[Short], byteOrder: ByteOrder): Boolean = { val elemSize = 2 val (data, from, until) = slice @@ -155,6 +187,30 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { reference.toSeq == builder.result } + def testFloatEncoding(slice: ArraySlice[Float], byteOrder: ByteOrder): Boolean = { + val elemSize = 4 + val (data, from, until) = slice + val reference = Array.ofDim[Byte](data.length * elemSize) + ByteBuffer.wrap(reference).order(byteOrder).asFloatBuffer.put(data) + val builder = ByteString.newBuilder + for (i ← 0 to from - 1) builder.putFloat(data(i))(byteOrder) + builder.putFloats(data, from, until - from)(byteOrder) + for (i ← until to data.length - 1) builder.putFloat(data(i))(byteOrder) + reference.toSeq == builder.result + } + + def testDoubleEncoding(slice: ArraySlice[Double], byteOrder: ByteOrder): Boolean = { + val elemSize = 8 + val (data, from, until) = slice + val reference = Array.ofDim[Byte](data.length * elemSize) + ByteBuffer.wrap(reference).order(byteOrder).asDoubleBuffer.put(data) + val builder = ByteString.newBuilder + for (i ← 0 to from - 1) builder.putDouble(data(i))(byteOrder) + builder.putDoubles(data, from, until - from)(byteOrder) + for (i ← until to data.length - 1) builder.putDouble(data(i))(byteOrder) + reference.toSeq == builder.result + } + "A ByteString" must { "have correct size" when { "concatenating" in { check((a: ByteString, b: ByteString) ⇒ (a ++ b).size == a.size + b.size) } @@ -273,6 +329,10 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { "decoding Int in little-endian" in { check { slice: ByteStringSlice ⇒ testIntDecoding(slice, LITTLE_ENDIAN) } } "decoding Long in big-endian" in { check { slice: ByteStringSlice ⇒ testLongDecoding(slice, BIG_ENDIAN) } } "decoding Long in little-endian" in { check { slice: ByteStringSlice ⇒ testLongDecoding(slice, LITTLE_ENDIAN) } } + "decoding Float in big-endian" in { check { slice: ByteStringSlice ⇒ testFloatDecoding(slice, BIG_ENDIAN) } } + "decoding Float in little-endian" in { check { slice: ByteStringSlice ⇒ testFloatDecoding(slice, LITTLE_ENDIAN) } } + "decoding Double in big-endian" in { check { slice: ByteStringSlice ⇒ testDoubleDecoding(slice, BIG_ENDIAN) } } + "decoding Double in little-endian" in { check { slice: ByteStringSlice ⇒ testDoubleDecoding(slice, LITTLE_ENDIAN) } } } } @@ -310,6 +370,10 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { "encoding Int in little-endian" in { check { slice: ArraySlice[Int] ⇒ testIntEncoding(slice, LITTLE_ENDIAN) } } "encoding Long in big-endian" in { check { slice: ArraySlice[Long] ⇒ testLongEncoding(slice, BIG_ENDIAN) } } "encoding Long in little-endian" in { check { slice: ArraySlice[Long] ⇒ testLongEncoding(slice, LITTLE_ENDIAN) } } + "encoding Float in big-endian" in { check { slice: ArraySlice[Float] ⇒ testFloatEncoding(slice, BIG_ENDIAN) } } + "encoding Float in little-endian" in { check { slice: ArraySlice[Float] ⇒ testFloatEncoding(slice, LITTLE_ENDIAN) } } + "encoding Double in big-endian" in { check { slice: ArraySlice[Double] ⇒ testDoubleEncoding(slice, BIG_ENDIAN) } } + "encoding Double in little-endian" in { check { slice: ArraySlice[Double] ⇒ testDoubleEncoding(slice, LITTLE_ENDIAN) } } } } } From f01d5c619ec3c9465a3de1e6ec82129dd1b81b41 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 31 May 2012 10:15:44 +0200 Subject: [PATCH 52/74] Completed ByteIterator tests in ByteStringSpec --- .../test/scala/akka/util/ByteStringSpec.scala | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala index db7162c3be..d3d4371c26 100644 --- a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala @@ -266,6 +266,16 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { } } + "calling take and drop" in { + check { slice: ByteStringSlice ⇒ + slice match { + case (xs, from, until) ⇒ likeVecIt(xs)({ + _.drop(from).take(until - from).toSeq + }, strict = false) + } + } + } + "calling copyToArray" in { check { slice: ByteStringSlice ⇒ slice match { @@ -320,6 +330,18 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { ((output.length < 1) || (input.asInputStream.read(output, 0, 1) == -1)) } } + + "calling copyToBuffer" in { + check { bytes: ByteString ⇒ + import java.nio.ByteBuffer + val buffer = ByteBuffer.allocate(bytes.size) + bytes.copyToBuffer(buffer) + buffer.flip() + val array = Array.ofDim[Byte](bytes.size) + buffer.get(array) + bytes == array.toSeq + } + } } "decode data correctly" when { From a87faade0608cc598c598a2a56f79b9939fd76cb Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Tue, 12 Jun 2012 14:12:46 +0900 Subject: [PATCH 53/74] Fixed serious bug in ByteStringBuilder.++= --- akka-actor/src/main/scala/akka/util/ByteString.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 98623429e3..271e17289b 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -515,9 +515,11 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { clearTemp() _builder += ByteString1(xs.array.clone) _length += xs.length - case _: collection.IndexedSeq[_] ⇒ + case seq: collection.IndexedSeq[_] ⇒ ensureTempSize(_tempLength + xs.size) xs.copyToArray(_temp, _tempLength) + _tempLength += seq.length + _length += seq.length case _ ⇒ super.++=(xs) } From e01e324c80cef26092f3366be5cbe3cc1a003b44 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Tue, 12 Jun 2012 14:13:17 +0900 Subject: [PATCH 54/74] Extended ByteStringBuilder tests in ByteStringSpec --- .../test/scala/akka/util/ByteStringSpec.scala | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala index d3d4371c26..cc9c5f93a7 100644 --- a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala @@ -9,6 +9,8 @@ import org.scalacheck.Arbitrary._ import org.scalacheck.Prop._ import org.scalacheck.Gen._ +import scala.collection.mutable.Builder + import java.nio.{ ByteBuffer, ShortBuffer, IntBuffer, FloatBuffer, DoubleBuffer } import java.nio.{ ByteOrder }, ByteOrder.{ BIG_ENDIAN, LITTLE_ENDIAN } import java.lang.Float.floatToRawIntBits @@ -79,6 +81,16 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { (!strict || (bsAIt.toSeq, bsBIt.toSeq) == (vecAIt.toSeq, vecBIt.toSeq)) } + def likeVecBld(body: Builder[Byte, _] ⇒ Unit): Boolean = { + val bsBuilder = ByteString.newBuilder + val vecBuilder = Vector.newBuilder[Byte] + + body(bsBuilder) + body(vecBuilder) + + bsBuilder.result == vecBuilder.result + } + def testShortDecoding(slice: ByteStringSlice, byteOrder: ByteOrder): Boolean = { val elemSize = 2 val (bytes, from, until) = slice @@ -359,6 +371,19 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { } "A ByteStringBuilder" must { + "function like a VectorBuilder" when { + "adding various contents using ++= and +=" in { + check { (array1: Array[Byte], array2: Array[Byte], bs1: ByteString, bs2: ByteString, bs3: ByteString) ⇒ + likeVecBld { builder ⇒ + builder ++= array1 + bs1 foreach { b ⇒ builder += b } + builder ++= bs2 + bs3 foreach { b ⇒ builder += b } + builder ++= Vector(array2: _*) + } + } + } + } "function as expected" when { "putting Bytes, using putByte and putBytes" in { // mixing putByte and putBytes here for more rigorous testing From 91ff7a29b911fabb85aa5c7640bc04170e60ef64 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Tue, 12 Jun 2012 15:03:07 +0900 Subject: [PATCH 55/74] Completed ByteString tests in ByteStringSpec --- .../test/scala/akka/util/ByteStringSpec.scala | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala index cc9c5f93a7..81b972f520 100644 --- a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala @@ -67,6 +67,17 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { val arbitraryDoubleArray: Arbitrary[Array[Double]] = Arbitrary { Gen.sized { n ⇒ Gen.containerOfN[Array, Double](n, arbitrary[Double]) } } implicit val arbitraryDoubleArraySlice: Arbitrary[ArraySlice[Double]] = arbSlice(arbitraryDoubleArray) + def likeVector(bs: ByteString)(body: IndexedSeq[Byte] ⇒ Any): Boolean = { + val vec = Vector(bs: _*) + body(bs) == body(vec) + } + + def likeVectors(bsA: ByteString, bsB: ByteString)(body: (IndexedSeq[Byte], IndexedSeq[Byte]) ⇒ Any): Boolean = { + val vecA = Vector(bsA: _*) + val vecB = Vector(bsB: _*) + body(bsA, bsB) == body(vecA, vecB) + } + def likeVecIt(bs: ByteString)(body: BufferedIterator[Byte] ⇒ Any, strict: Boolean = true): Boolean = { val bsIterator = bs.iterator val vecIterator = Vector(bs: _*).iterator.buffered @@ -228,10 +239,12 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { "concatenating" in { check((a: ByteString, b: ByteString) ⇒ (a ++ b).size == a.size + b.size) } "dropping" in { check((a: ByteString, b: ByteString) ⇒ (a ++ b).drop(b.size).size == a.size) } } + "be sequential" when { "taking" in { check((a: ByteString, b: ByteString) ⇒ (a ++ b).take(a.size) == a) } "dropping" in { check((a: ByteString, b: ByteString) ⇒ (a ++ b).drop(a.size) == b) } } + "be equal to the original" when { "compacting" in { check { xs: ByteString ⇒ val ys = xs.compact; (xs == ys) && ys.isCompact } } "recombining" in { @@ -242,6 +255,81 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { } } } + + "behave as expected" when { + "created from and decoding to String" in { check { s: String ⇒ ByteString(s, "UTF-8").decodeString("UTF-8") == s } } + + "compacting" in { + check { a: ByteString ⇒ + val wasCompact = a.isCompact + val b = a.compact + ((!wasCompact) || (b eq a)) && + (b == a) && + b.isCompact && + (b.compact eq b) + } + } + } + "behave like a Vector" when { + "concatenating" in { check { (a: ByteString, b: ByteString) ⇒ likeVectors(a, b) { (a, b) ⇒ (a ++ b) } } } + + "calling apply" in { + check { slice: ByteStringSlice ⇒ + slice match { + case (xs, i1, i2) ⇒ likeVector(xs) { seq ⇒ + (if ((i1 >= 0) && (i1 < seq.length)) seq(i1) else 0, + if ((i2 >= 0) && (i2 < seq.length)) seq(i2) else 0) + } + } + } + } + + "calling head" in { check { a: ByteString ⇒ a.isEmpty || likeVector(a) { _.head } } } + "calling last" in { check { a: ByteString ⇒ a.isEmpty || likeVector(a) { _.last } } } + "calling length" in { check { a: ByteString ⇒ likeVector(a) { _.length } } } + + "calling span" in { check { (a: ByteString, b: Byte) ⇒ likeVector(a)({ _.span(_ != b) match { case (a, b) ⇒ (a, b) } }) } } + + "calling takeWhile" in { check { (a: ByteString, b: Byte) ⇒ likeVector(a)({ _.takeWhile(_ != b) }) } } + "calling dropWhile" in { check { (a: ByteString, b: Byte) ⇒ likeVector(a) { _.dropWhile(_ != b) } } } + "calling indexWhere" in { check { (a: ByteString, b: Byte) ⇒ likeVector(a) { _.indexWhere(_ == b) } } } + "calling indexOf" in { check { (a: ByteString, b: Byte) ⇒ likeVector(a) { _.indexOf(b) } } } + "calling foreach" in { check { a: ByteString ⇒ likeVector(a) { it ⇒ var acc = 0; it foreach { acc += _ }; acc } } } + "calling foldLeft" in { check { a: ByteString ⇒ likeVector(a) { _.foldLeft(0) { _ + _ } } } } + "calling toArray" in { check { a: ByteString ⇒ likeVector(a) { _.toArray.toSeq } } } + + "calling slice" in { + check { slice: ByteStringSlice ⇒ + slice match { + case (xs, from, until) ⇒ likeVector(xs)({ + _.slice(from, until) + }) + } + } + } + + "calling take and drop" in { + check { slice: ByteStringSlice ⇒ + slice match { + case (xs, from, until) ⇒ likeVector(xs)({ + _.drop(from).take(until - from) + }) + } + } + } + + "calling copyToArray" in { + check { slice: ByteStringSlice ⇒ + slice match { + case (xs, from, until) ⇒ likeVector(xs)({ it ⇒ + val array = Array.ofDim[Byte](xs.length) + it.slice(from, until).copyToArray(array, from, until) + array.toSeq + }) + } + } + } + } } "A ByteStringIterator" must { From cc79aae1a41a36b3eaa3f2f96a1154e6b6cf9092 Mon Sep 17 00:00:00 2001 From: Gert Vanthienen Date: Tue, 12 Jun 2012 16:08:19 +0200 Subject: [PATCH 56/74] Adding PojoSR tests and a lot of code cleanup --- .../additional/code/osgi/Activator.scala | 2 +- .../akka/osgi/blueprint/aries/akka.xsd | 41 +---- .../akka/osgi/ActorSystemActivator.scala | 1 - .../akka/osgi/OsgiActorSystemFactory.scala | 14 +- .../BlueprintActorSystemFactory.scala | 38 +++-- .../blueprint/aries/NamespaceHandler.scala | 154 +++++++++++------- .../osgi/blueprint/aries/ParserHelper.scala | 16 ++ .../akka/osgi/blueprint/aries/config.xml | 13 ++ .../akka/osgi/blueprint/aries/injection.xml | 13 ++ .../akka/osgi/blueprint/aries/simple.xml | 6 +- akka-osgi/src/test/resources/logback-test.xml | 23 +++ .../akka/osgi/ActorSystemActivatorTest.scala | 56 ++----- .../scala/akka/osgi/PojoSRTestSupport.scala | 150 +++++++++++++++++ .../aries/NamespaceHandlerTest.scala | 94 +++++++++++ .../akka/osgi/test/ActorSystemAwareBean.scala | 11 ++ .../test/scala/akka/osgi/test/PingPong.scala | 22 +++ .../osgi/test/TestActorSystemActivator.scala | 16 ++ project/AkkaBuild.scala | 17 +- 18 files changed, 517 insertions(+), 170 deletions(-) create mode 100644 akka-osgi/src/main/scala/akka/osgi/blueprint/aries/ParserHelper.scala create mode 100644 akka-osgi/src/test/resources/akka/osgi/blueprint/aries/config.xml create mode 100644 akka-osgi/src/test/resources/akka/osgi/blueprint/aries/injection.xml create mode 100644 akka-osgi/src/test/resources/logback-test.xml create mode 100644 akka-osgi/src/test/scala/akka/osgi/PojoSRTestSupport.scala create mode 100644 akka-osgi/src/test/scala/akka/osgi/blueprint/aries/NamespaceHandlerTest.scala create mode 100644 akka-osgi/src/test/scala/akka/osgi/test/ActorSystemAwareBean.scala create mode 100644 akka-osgi/src/test/scala/akka/osgi/test/PingPong.scala create mode 100644 akka-osgi/src/test/scala/akka/osgi/test/TestActorSystemActivator.scala diff --git a/akka-docs/additional/code/osgi/Activator.scala b/akka-docs/additional/code/osgi/Activator.scala index 0e3a5c82ee..06a538d242 100644 --- a/akka-docs/additional/code/osgi/Activator.scala +++ b/akka-docs/additional/code/osgi/Activator.scala @@ -1,4 +1,4 @@ -import akka.actor.{Props, ActorSystem} +import akka.actor.{ Props, ActorSystem } import akka.osgi.ActorSystemActivator import org.apache.servicemix.examples.akka.Listener import org.apache.servicemix.examples.akka.Master diff --git a/akka-osgi/src/main/resources/akka/osgi/blueprint/aries/akka.xsd b/akka-osgi/src/main/resources/akka/osgi/blueprint/aries/akka.xsd index 256dff22e9..d7d0f77a2c 100644 --- a/akka-osgi/src/main/resources/akka/osgi/blueprint/aries/akka.xsd +++ b/akka-osgi/src/main/resources/akka/osgi/blueprint/aries/akka.xsd @@ -1,25 +1,4 @@ - - - - - + + - - - - - Defines an Akka Actor - - - - - - + + + + Defines an Akka ActorSystem configuration + + diff --git a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala index c6d24e8262..ef04607976 100644 --- a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala +++ b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala @@ -40,7 +40,6 @@ abstract class ActorSystemActivator(nameFor: (BundleContext) ⇒ String) extends */ def stop(context: BundleContext) { if (system != null) { - system.shutdown() system.shutdown() system = null } diff --git a/akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala b/akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala index 8c41521964..cddf797d07 100644 --- a/akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala +++ b/akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala @@ -2,9 +2,9 @@ package akka.osgi import impl.BundleDelegatingClassLoader import org.osgi.framework.BundleContext -import java.util.Properties import akka.actor.ActorSystem import com.typesafe.config.{ ConfigFactory, Config } +import java.util.{ Dictionary, Properties } /** * Factory class to create ActorSystem implementations in an OSGi environment. This mainly involves dealing with @@ -20,16 +20,18 @@ class OsgiActorSystemFactory(val context: BundleContext) { /** * Creates the ActorSystem and registers it in the OSGi Service Registry */ - def createActorSystem(name: String) = { + def createActorSystem(name: String): ActorSystem = createActorSystem(Option(name)) + + def createActorSystem(name: Option[String]): ActorSystem = { val system = ActorSystem(actorSystemName(name), actorSystemConfig(context), classloader) registerService(system) system } def registerService(system: ActorSystem) { - val properties = new Properties(); + val properties = new Properties() properties.put("name", system.name) - context.registerService(classOf[ActorSystem].getName, system, properties) + context.registerService(classOf[ActorSystem].getName, system, properties.asInstanceOf[Dictionary[String, Any]]) } /** @@ -44,8 +46,8 @@ class OsgiActorSystemFactory(val context: BundleContext) { /** * Determine a the ActorSystem name */ - def actorSystemName(name: String): String = - Option(name).getOrElse("bundle-%s-ActorSystem".format(context.getBundle().getBundleId)) + def actorSystemName(name: Option[String]): String = + name.getOrElse("bundle-%s-ActorSystem".format(context.getBundle().getBundleId)) } diff --git a/akka-osgi/src/main/scala/akka/osgi/blueprint/BlueprintActorSystemFactory.scala b/akka-osgi/src/main/scala/akka/osgi/blueprint/BlueprintActorSystemFactory.scala index 92e7e8a099..51c3e7291f 100644 --- a/akka-osgi/src/main/scala/akka/osgi/blueprint/BlueprintActorSystemFactory.scala +++ b/akka-osgi/src/main/scala/akka/osgi/blueprint/BlueprintActorSystemFactory.scala @@ -4,31 +4,39 @@ import org.osgi.framework.BundleContext import akka.osgi.OsgiActorSystemFactory import collection.mutable.Buffer import akka.actor.{ Actor, Props, ActorSystem } +import com.typesafe.config.ConfigFactory /** * A set of helper/factory classes to build a Akka system using Blueprint */ class BlueprintActorSystemFactory(context: BundleContext, name: String) extends OsgiActorSystemFactory(context) { - val systems: Buffer[ActorSystem] = Buffer() + var config: Option[String] = None - def this(context: BundleContext) = this(context, null) + lazy val system = super.createActorSystem(stringToOption(name)) - def create: ActorSystem = create(null) - def create(name: String): ActorSystem = { - val system = super.createActorSystem(name) - systems += system - system + def setConfig(config: String) = { this.config = Some(config) } + + def create = system + + def destroy = system.shutdown() + + def stringToOption(original: String) = if (original == null || original.isEmpty) { + None + } else { + Some(original) } - def destroy = for (system ← systems) { - system.shutdown() + /** + * Strategy method to create the Config for the ActorSystem, ensuring that the default/reference configuration is + * loaded from the akka-actor bundle. + */ + override def actorSystemConfig(context: BundleContext) = { + config match { + case Some(value) ⇒ ConfigFactory.parseString(value).withFallback(super.actorSystemConfig(context)) + case None ⇒ super.actorSystemConfig(context) + } + } } -class BlueprintActorSystem(context: BundleContext, system: ActorSystem) { - - def createActor(name: String) = system.actorOf(Props(context.getBundle.loadClass(name).asInstanceOf[Class[Actor]])) - -} - diff --git a/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala b/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala index b1412eae91..245ea538b6 100644 --- a/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala +++ b/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala @@ -9,92 +9,122 @@ import org.osgi.framework.BundleContext import org.apache.aries.blueprint.reflect.{ ValueMetadataImpl, RefMetadataImpl, BeanArgumentImpl } import org.w3c.dom.{ NodeList, Element, Node } import org.osgi.service.blueprint.reflect.{ BeanMetadata, ComponentMetadata } -import akka.actor.{ ActorRef, ActorSystem } -import akka.osgi.blueprint.{ BlueprintActorSystem, BlueprintActorSystemFactory } +import akka.actor.{ ActorSystem } +import akka.osgi.blueprint.{ BlueprintActorSystemFactory } +import java.util.concurrent.atomic.AtomicInteger + +import ParserHelper.childElements /** * Aries Blueprint namespace handler implementation */ class NamespaceHandler extends org.apache.aries.blueprint.NamespaceHandler { - val CLASS_ATTRIBUTE = "class"; - val ID_ATTRIBUTE = "id"; - val NAME_ATTRIBUTE = "name"; + import NamespaceHandler._ - var idCounter = 1 + val idCounter = new AtomicInteger(0) def getSchemaLocation(namespace: String) = getClass().getResource("akka.xsd") def getManagedClasses = setAsJavaSet(Set(classOf[BlueprintActorSystemFactory])) - def parse(element: Element, context: ParserContext) = { - val factory = context.createMetadata(classOf[MutableBeanMetadata]) - factory.setId(getId(context, element)) - factory.setScope(BeanMetadata.SCOPE_SINGLETON) - factory.setProcessor(true) - factory.setClassName(classOf[BlueprintActorSystemFactory].getName) - factory.setDestroyMethod("destroy") - factory.addArgument(new BeanArgumentImpl(new RefMetadataImpl("blueprintBundleContext"), classOf[BundleContext].getName, -1)) - - val system = context.createMetadata(classOf[MutableBeanMetadata]) - system.setId(getId(context, element)) - system.setFactoryComponent(factory) - system.setFactoryMethod("create") - system.setRuntimeClass(classOf[ActorSystem]) - if (element.hasAttribute(NAME_ATTRIBUTE)) { - system.addArgument(new BeanArgumentImpl(new ValueMetadataImpl(element.getAttribute(NAME_ATTRIBUTE)), classOf[String].getName, -1)) - } - - val actorsystem = context.createMetadata(classOf[MutableBeanMetadata]) - actorsystem.setId(getId(context, element)) - actorsystem.setClassName(classOf[BlueprintActorSystem].getName) - actorsystem.addArgument(new BeanArgumentImpl(new RefMetadataImpl("blueprintBundleContext"), classOf[BundleContext].getName, -1)) - actorsystem.addArgument(new BeanArgumentImpl(system, classOf[ActorSystem].getName, -1)) - context.getComponentDefinitionRegistry.registerComponentDefinition(actorsystem) - - val nodelist = element.getChildNodes - var i = 0 - while (i < nodelist.getLength) { - val node = nodelist.item(i) - node.getLocalName match { - case "actor" if node.isInstanceOf[Element] ⇒ parseActor(node.asInstanceOf[Element], context, actorsystem) - case _ ⇒ - } - i += 1 - } - factory - } - - def parseActor(node: Element, context: ParserContext, actorsystem: MutableBeanMetadata) = { - val actor = context.createMetadata(classOf[MutableBeanMetadata]) - actor.setFactoryComponent(actorsystem) - if (node.hasAttribute(CLASS_ATTRIBUTE)) { - actor.addArgument(new BeanArgumentImpl(new ValueMetadataImpl(node.getAttribute(CLASS_ATTRIBUTE)), classOf[String].getName, -1)) - } - actor.setId(getId(context, node)) - actor.setFactoryMethod("createActor") - // actor.setRuntimeClass(classOf[ActorRef]) - context.getComponentDefinitionRegistry.registerComponentDefinition(actor) + def parse(element: Element, context: ParserContext) = element.getLocalName match { + case ACTORSYSTEM_ELEMENT_NAME ⇒ parseActorSystem(element, context) + case _ ⇒ throw new ComponentDefinitionException("Unexpected element for Akka namespace: %s".format(element)) } def decorate(node: Node, component: ComponentMetadata, context: ParserContext) = throw new ComponentDefinitionException("Bad xml syntax: node decoration is not supported"); + /* + * Parse + */ + def parseActorSystem(element: Element, context: ParserContext) = { + val factory = createFactoryBean(context, element.getAttribute(NAME_ATTRIBUTE)) + + for (child ← childElements(element)) { + child.getLocalName match { + case CONFIG_ELEMENT_NAME ⇒ parseConfig(child, context, factory) + case _ ⇒ throw new ComponentDefinitionException("Unexpected child element %s found in %s".format(child, element)) + } + } + + createActorSystemBean(context, element, factory) + } + + /* + * Parse + */ + def parseConfig(node: Element, context: ParserContext, factory: MutableBeanMetadata) = { + factory.addProperty("config", new ValueMetadataImpl(node.getTextContent)) + } + + /* + * Create the bean definition for the ActorSystem + */ + def createActorSystemBean(context: ParserContext, element: Element, factory: MutableBeanMetadata): MutableBeanMetadata = { + val system = context.createMetadata(classOf[MutableBeanMetadata]) + system.setId(getId(context, element)) + system.setFactoryComponent(factory) + + system.setFactoryMethod(FACTORY_METHOD_NAME) + system.setRuntimeClass(classOf[ActorSystem]) + system + } + + /* + * Create the bean definition for the BlueprintActorSystemFactory + */ + def createFactoryBean(context: ParserContext, name: String): MutableBeanMetadata = { + val factory = context.createMetadata(classOf[MutableBeanMetadata]) + factory.setId(findAvailableId(context)) + factory.setScope(BeanMetadata.SCOPE_SINGLETON) + factory.setProcessor(true) + factory.setClassName(classOf[BlueprintActorSystemFactory].getName) + + factory.setDestroyMethod(DESTROY_METHOD_NAME) + + factory.addArgument(new BeanArgumentImpl(new RefMetadataImpl(BUNDLE_CONTEXT_REFID), classOf[BundleContext].getName, -1)) + factory.addArgument(new BeanArgumentImpl(new ValueMetadataImpl(name), classOf[String].getName, -1)) + factory.setProcessor(true) + context.getComponentDefinitionRegistry.registerComponentDefinition(factory) + factory + } + + /* + * Get the assigned id or generate a suitable id + */ def getId(context: ParserContext, element: Element) = { if (element.hasAttribute(ID_ATTRIBUTE)) { element.getAttribute(ID_ATTRIBUTE); } else { - generateId(context); + findAvailableId(context); } } - def generateId(context: ParserContext): String = { - var id = ""; - do { - idCounter += 1 - id = ".akka-" + idCounter; - } while (context.getComponentDefinitionRegistry().containsComponentDefinition(id)); - id; + /* + * Find the next available component id + */ + def findAvailableId(context: ParserContext): String = { + val id = ".akka-" + idCounter.incrementAndGet() + if (context.getComponentDefinitionRegistry.containsComponentDefinition(id)) { + // id already exists, let's try the next one + findAvailableId(context) + } else id } +} + +object NamespaceHandler { + + private val ID_ATTRIBUTE = "id"; + private val NAME_ATTRIBUTE = "name"; + + private val BUNDLE_CONTEXT_REFID = "blueprintBundleContext" + + private val ACTORSYSTEM_ELEMENT_NAME = "actor-system" + private val CONFIG_ELEMENT_NAME = "config" + + private val DESTROY_METHOD_NAME = "destroy" + private val FACTORY_METHOD_NAME = "create" } diff --git a/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/ParserHelper.scala b/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/ParserHelper.scala new file mode 100644 index 0000000000..82fb7bc113 --- /dev/null +++ b/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/ParserHelper.scala @@ -0,0 +1,16 @@ +package akka.osgi.blueprint.aries + +import org.w3c.dom.{ Node, Element } + +/** + * Helper class to deal with the W3C DOM types + */ +object ParserHelper { + + def childElements(element: Element) = children(element).filter(_.getNodeType == Node.ELEMENT_NODE).asInstanceOf[Seq[Element]] + + private[this] def children(element: Element) = { + val nodelist = element.getChildNodes + for (index ← 0 until nodelist.getLength) yield nodelist.item(index) + } +} diff --git a/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/config.xml b/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/config.xml new file mode 100644 index 0000000000..6bd3d49c9d --- /dev/null +++ b/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/config.xml @@ -0,0 +1,13 @@ + + + + + + some.config { + key=value + } + + + + diff --git a/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/injection.xml b/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/injection.xml new file mode 100644 index 0000000000..9712ee6d1f --- /dev/null +++ b/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/injection.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + diff --git a/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/simple.xml b/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/simple.xml index d276ee86a0..a46834f74b 100644 --- a/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/simple.xml +++ b/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/simple.xml @@ -1,9 +1,7 @@ + xmlns:akka="http://akka.io/xmlns/blueprint/v1.0.0"> - - - + diff --git a/akka-osgi/src/test/resources/logback-test.xml b/akka-osgi/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..9c441a6fb6 --- /dev/null +++ b/akka-osgi/src/test/resources/logback-test.xml @@ -0,0 +1,23 @@ + + + + + + + %date{ISO8601} %-5level %logger %X{akkaSource} %X{sourceThread} - %msg%n + + + + + target/akka-osgi.log + true + + %date{ISO8601} %-5level %logger %X{akkaSource} %X{sourceThread} - %msg%n + + + + + + + + diff --git a/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala b/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala index 34472e3537..0b2fdd19ac 100644 --- a/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala +++ b/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala @@ -1,53 +1,28 @@ package akka.osgi -import java.util.{ ServiceLoader, HashMap } -import de.kalpatec.pojosr.framework.launch.{ ClasspathScanner, PojoServiceRegistryFactory } import org.scalatest.FlatSpec -import org.osgi.framework.BundleContext -import akka.actor.{ Actor, Props, ActorSystem } +import akka.actor.ActorSystem import akka.pattern.ask import akka.dispatch.Await import akka.util.duration._ import akka.util.Timeout +import de.kalpatec.pojosr.framework.launch.BundleDescriptor +import test.TestActorSystemActivator +import test.PingPong._ +import PojoSRTestSupport.bundle /** * Test cases for {@link ActorSystemActivator} */ -class ActorSystemActivatorTest extends FlatSpec { +class ActorSystemActivatorTest extends FlatSpec with PojoSRTestSupport { - abstract class TestMessage + val TEST_BUNDLE_NAME = "akka.osgi.test.activator" - case object Ping extends TestMessage - case object Pong extends TestMessage + val testBundles: Seq[BundleDescriptor] = Seq( + bundle(TEST_BUNDLE_NAME).withActivator(classOf[TestActorSystemActivator])) - class PongActor extends Actor { - def receive = { - case Ping ⇒ - sender ! Pong - } - } - - lazy val context: BundleContext = { - val config = new HashMap[String, AnyRef](); - val loader = ServiceLoader.load(classOf[PojoServiceRegistryFactory]); - val registry = loader.iterator().next().newPojoServiceRegistry(config); - registry.getBundleContext - } - - val activator = new ActorSystemActivator { - def configure(system: ActorSystem) { - system.actorOf(Props(new PongActor), name = "pong") - } - } - - "ActorSystemActivator" should "start and register the ActorSystem on start" in { - - activator.start(context) - - val reference = context.getServiceReference(classOf[ActorSystem].getName) - assert(reference != null) - - val system = context.getService(reference).asInstanceOf[ActorSystem] + "ActorSystemActivator" should "start and register the ActorSystem when bundle starts" in { + val system = serviceForType[ActorSystem] val actor = system.actorFor("/user/pong") implicit val timeout = Timeout(5 seconds) @@ -56,14 +31,11 @@ class ActorSystemActivatorTest extends FlatSpec { assert(result != null) } - it should "stop the ActorSystem on bundle stop" in { - val reference = context.getServiceReference(classOf[ActorSystem].getName) - assert(reference != null) - - val system = context.getService(reference).asInstanceOf[ActorSystem] + it should "stop the ActorSystem when bundle stops" in { + val system = serviceForType[ActorSystem] assert(!system.isTerminated) - activator.stop(context) + bundleForName(TEST_BUNDLE_NAME).stop() system.awaitTermination() assert(system.isTerminated) diff --git a/akka-osgi/src/test/scala/akka/osgi/PojoSRTestSupport.scala b/akka-osgi/src/test/scala/akka/osgi/PojoSRTestSupport.scala new file mode 100644 index 0000000000..cbed282c18 --- /dev/null +++ b/akka-osgi/src/test/scala/akka/osgi/PojoSRTestSupport.scala @@ -0,0 +1,150 @@ +package akka.osgi + +import de.kalpatec.pojosr.framework.launch.{ BundleDescriptor, PojoServiceRegistryFactory, ClasspathScanner } + +import scala.collection.JavaConversions.seqAsJavaList +import scala.collection.JavaConversions.collectionAsScalaIterable +import org.apache.commons.io.IOUtils.copy + +import org.osgi.framework._ +import java.net.URL + +import java.util.jar.JarInputStream +import java.io.{ FileInputStream, FileOutputStream, File } +import java.util.{ Date, ServiceLoader, HashMap } +import org.scalatest.{ BeforeAndAfterAll, Suite } + +/** + * Trait that provides support for building akka-osgi tests using PojoSR + */ +trait PojoSRTestSupport extends Suite with BeforeAndAfterAll { + + val MAX_WAIT_TIME = 8000; + val START_WAIT_TIME = 100; + + implicit def buildBundleDescriptor(builder: BundleDescriptorBuilder) = builder.build + + /** + * All bundles being found on the test classpath are automatically installed and started in the PojoSR runtime. + * Implement this to define the extra bundles that should be available for testing. + */ + val testBundles: Seq[BundleDescriptor] + + lazy val context: BundleContext = { + val config = new HashMap[String, AnyRef](); + System.setProperty("org.osgi.framework.storage", "target/akka-osgi/" + System.currentTimeMillis) + + val bundles = new ClasspathScanner().scanForBundles() + bundles.addAll(testBundles) + config.put(PojoServiceRegistryFactory.BUNDLE_DESCRIPTORS, bundles); + + val loader: ServiceLoader[PojoServiceRegistryFactory] = ServiceLoader.load(classOf[PojoServiceRegistryFactory]) + + val registry = loader.iterator.next.newPojoServiceRegistry(config) + registry.getBundleContext + } + + // Ensure bundles get stopped at the end of the test to release resources and stop threads + override protected def afterAll() = context.getBundles.foreach(_.stop) + + /** + * Convenience method to find a bundle by symbolic name + */ + def bundleForName(name: String) = context.getBundles.find(_.getSymbolicName == name) match { + case Some(bundle) ⇒ bundle + case None ⇒ fail("Unable to find bundle with symbolic name %s".format(name)) + } + + /** + * Convenience method to find a service by interface. If the service is not already available in the OSGi Service + * Registry, this method will wait for a few seconds for the service to appear. + */ + def serviceForType[T](implicit manifest: Manifest[T]): T = { + val reference = awaitReference(manifest.erasure) + context.getService(reference).asInstanceOf[T] + } + + def awaitReference(serviceType: Class[_]): ServiceReference = awaitReference(serviceType, START_WAIT_TIME) + + def awaitReference(serviceType: Class[_], wait: Long): ServiceReference = { + val option = Option(context.getServiceReference(serviceType.getName)) + option match { + case Some(reference) ⇒ reference; + case None if (wait > MAX_WAIT_TIME) ⇒ fail("Gave up waiting for service of type %s".format(serviceType)) + case None ⇒ { + Thread.sleep(wait); + awaitReference(serviceType, wait * 2); + } + } + } +} + +object PojoSRTestSupport { + + /** + * Convenience method to define additional test bundles + */ + def bundle(name: String) = new BundleDescriptorBuilder(name) + +} + +/** + * Helper class to make it easier to define test bundles + */ +class BundleDescriptorBuilder(name: String) { + + import org.ops4j.pax.tinybundles.core.TinyBundles + + val tinybundle = TinyBundles.bundle.set(Constants.BUNDLE_SYMBOLICNAME, name) + + def withBlueprintFile(name: String, contents: URL) = + returnBuilder(tinybundle.add("OSGI-INF/blueprint/%s".format(name), contents)) + + def withBlueprintFile(contents: URL): BundleDescriptorBuilder = withBlueprintFile(filename(contents), contents) + + def withActivator(activator: Class[_ <: BundleActivator]) = + returnBuilder(tinybundle.set(Constants.BUNDLE_ACTIVATOR, activator.getName)) + + def returnBuilder(block: ⇒ Unit) = { + block + this + } + + def build = { + val file: File = tinybundleToJarFile(name) + + new BundleDescriptor( + getClass().getClassLoader(), + new URL("jar:" + file.toURI().toString() + "!/"), + extractHeaders(file)); + } + + def extractHeaders(file: File): HashMap[String, String] = { + val headers = new HashMap[String, String](); + + val jis = new JarInputStream(new FileInputStream(file)); + try { + for (entry ← jis.getManifest().getMainAttributes().entrySet()) { + headers.put(entry.getKey().toString(), entry.getValue().toString()); + } + } finally { + jis.close() + } + + headers + } + + def tinybundleToJarFile(name: String): File = { + val file = new File("target/%s-%tQ.jar".format(name, new Date())); + val fos = new FileOutputStream(file); + try { + copy(tinybundle.build(), fos); + } finally { + fos.close(); + } + file + } + + private[this] def filename(url: URL) = url.getFile.split("/").last +} + diff --git a/akka-osgi/src/test/scala/akka/osgi/blueprint/aries/NamespaceHandlerTest.scala b/akka-osgi/src/test/scala/akka/osgi/blueprint/aries/NamespaceHandlerTest.scala new file mode 100644 index 0000000000..bbe26b5e92 --- /dev/null +++ b/akka-osgi/src/test/scala/akka/osgi/blueprint/aries/NamespaceHandlerTest.scala @@ -0,0 +1,94 @@ +package akka.osgi.blueprint.aries + +import org.scalatest.FlatSpec +import akka.actor.ActorSystem +import de.kalpatec.pojosr.framework.launch.BundleDescriptor +import akka.osgi.PojoSRTestSupport +import akka.osgi.PojoSRTestSupport.bundle +import akka.osgi.test.ActorSystemAwareBean + +/** + * Test cases for {@link ActorSystemActivator} + */ +object NamespaceHandlerTest { + + /* + * Bundle-SymbolicName to easily find our test bundle + */ + val TEST_BUNDLE_NAME = "akka.osgi.test.aries.namespace" + + /* + * Bundle descriptor representing the akka-osgi bundle itself + */ + val AKKA_OSGI_BLUEPRINT = + bundle("akka-osgi").withBlueprintFile(getClass.getResource("/OSGI-INF/blueprint/akka-namespacehandler.xml")) + +} + +class SimpleNamespaceHandlerTest extends FlatSpec with PojoSRTestSupport { + + import NamespaceHandlerTest._ + + val testBundles: Seq[BundleDescriptor] = Seq( + AKKA_OSGI_BLUEPRINT, + bundle(TEST_BUNDLE_NAME).withBlueprintFile(getClass.getResource("simple.xml"))) + + "simple.xml" should "set up ActorSystem when bundle starts" in { + val system = serviceForType[ActorSystem] + assert(system != null) + } + + it should "stop the ActorSystem when bundle stops" in { + val system = serviceForType[ActorSystem] + assert(!system.isTerminated) + + bundleForName(TEST_BUNDLE_NAME).stop() + + system.awaitTermination() + assert(system.isTerminated) + } + +} + +class ConfigNamespaceHandlerTest extends FlatSpec with PojoSRTestSupport { + + import NamespaceHandlerTest._ + + val testBundles: Seq[BundleDescriptor] = Seq( + AKKA_OSGI_BLUEPRINT, + bundle(TEST_BUNDLE_NAME).withBlueprintFile(getClass.getResource("config.xml"))) + + "config.xml" should "set up ActorSystem when bundle starts" in { + val system = serviceForType[ActorSystem] + assert(system != null) + + assert(system.settings.config.getString("some.config.key") == "value") + } + + it should "stop the ActorSystem when bundle stops" in { + val system = serviceForType[ActorSystem] + assert(!system.isTerminated) + + bundleForName(TEST_BUNDLE_NAME).stop() + + system.awaitTermination() + assert(system.isTerminated) + } + +} + +class DependencyInjectionNamespaceHandlerTest extends FlatSpec with PojoSRTestSupport { + + import NamespaceHandlerTest._ + + val testBundles: Seq[BundleDescriptor] = Seq( + AKKA_OSGI_BLUEPRINT, + bundle(TEST_BUNDLE_NAME).withBlueprintFile(getClass.getResource("injection.xml"))) + + "injection.xml" should "set up bean containing ActorSystem" in { + val bean = serviceForType[ActorSystemAwareBean] + assert(bean != null) + assert(bean.system != null) + } + +} diff --git a/akka-osgi/src/test/scala/akka/osgi/test/ActorSystemAwareBean.scala b/akka-osgi/src/test/scala/akka/osgi/test/ActorSystemAwareBean.scala new file mode 100644 index 0000000000..ca0df7cc04 --- /dev/null +++ b/akka-osgi/src/test/scala/akka/osgi/test/ActorSystemAwareBean.scala @@ -0,0 +1,11 @@ +package akka.osgi.test + +import akka.actor.ActorSystem + +/** + * Just a simple POJO that can contain an actor system. + * Used for testing dependency injection with Blueprint + */ +class ActorSystemAwareBean(val system: ActorSystem) { + +} diff --git a/akka-osgi/src/test/scala/akka/osgi/test/PingPong.scala b/akka-osgi/src/test/scala/akka/osgi/test/PingPong.scala new file mode 100644 index 0000000000..6a7409c667 --- /dev/null +++ b/akka-osgi/src/test/scala/akka/osgi/test/PingPong.scala @@ -0,0 +1,22 @@ +package akka.osgi.test + +import akka.actor.Actor + +/** + * Simple ping-pong actor, used for testing + */ +object PingPong { + + abstract class TestMessage + + case object Ping extends TestMessage + case object Pong extends TestMessage + + class PongActor extends Actor { + def receive = { + case Ping ⇒ + sender ! Pong + } + } + +} diff --git a/akka-osgi/src/test/scala/akka/osgi/test/TestActorSystemActivator.scala b/akka-osgi/src/test/scala/akka/osgi/test/TestActorSystemActivator.scala new file mode 100644 index 0000000000..2a44e91e4a --- /dev/null +++ b/akka-osgi/src/test/scala/akka/osgi/test/TestActorSystemActivator.scala @@ -0,0 +1,16 @@ +package akka.osgi.test + +import akka.osgi.ActorSystemActivator +import akka.actor.{ Props, ActorSystem } +import PingPong._ + +/** + * Sample ActorSystemActivator implementation used for testing purposes + */ +class TestActorSystemActivator extends ActorSystemActivator { + + def configure(system: ActorSystem) { + system.actorOf(Props(new PongActor), name = "pong") + } + +} diff --git a/project/AkkaBuild.scala b/project/AkkaBuild.scala index 79a2aeb694..7b3f5bf65c 100644 --- a/project/AkkaBuild.scala +++ b/project/AkkaBuild.scala @@ -480,7 +480,7 @@ object Dependencies { val camel = Seq(camelCore, Test.scalatest, Test.junit, Test.mockito) - val osgi = Seq(osgiCore, ariesBlueprint, Runtime.logback, Test.pojosr, Test.tinybundles, Test.scalatest, Test.junit) + val osgi = Seq(osgiCore, ariesBlueprint, Runtime.logback, Test.ariesProxy, Test.commonsIo, Test.pojosr, Test.tinybundles, Test.scalatest, Test.junit) val tutorials = Seq(Test.scalatest, Test.junit) @@ -505,7 +505,7 @@ object Dependency { } // Compile - val ariesBlueprint = "org.apache.aries.blueprint" % "org.apache.aries.blueprint" % "0.3.1" // ApacheV2 + val ariesBlueprint = "org.apache.aries.blueprint" % "org.apache.aries.blueprint" % "0.3.2" // ApacheV2 val config = "com.typesafe" % "config" % "0.4.1" // ApacheV2 val camelCore = "org.apache.camel" % "camel-core" % V.Camel // ApacheV2 val netty = "io.netty" % "netty" % V.Netty // ApacheV2 @@ -518,12 +518,13 @@ object Dependency { // Test object Test { + val ariesProxy = "org.apache.aries.proxy" % "org.apache.aries.proxy.impl" % "0.3" % "test" // ApacheV2 val commonsMath = "org.apache.commons" % "commons-math" % "2.1" % "test" // ApacheV2 - val commonsIo = "commons-io" % "commons-io" % "2.0.1" % "test"// ApacheV2 + val commonsIo = "commons-io" % "commons-io" % "2.0.1" % "test"// ApacheV2 val junit = "junit" % "junit" % "4.5" % "test" // Common Public License 1.0 val logback = "ch.qos.logback" % "logback-classic" % V.Logback % "test" // EPL 1.0 / LGPL 2.1 val mockito = "org.mockito" % "mockito-all" % "1.8.1" % "test" // MIT - val pojosr = "com.googlecode.pojosr" % "de.kalpatec.pojosr.framework" % "0.1.8" % "test" // ApacheV2 + val pojosr = "com.googlecode.pojosr" % "de.kalpatec.pojosr.framework" % "0.1.4" % "test" // ApacheV2 val scalatest = "org.scalatest" % "scalatest_2.9.1" % V.Scalatest % "test" // ApacheV2 val scalacheck = "org.scala-tools.testing" % "scalacheck_2.9.1" % "1.9" % "test" // New BSD val specs2 = "org.specs2" % "specs2_2.9.1" % "1.9" % "test" // Modified BSD / ApacheV2 @@ -548,7 +549,10 @@ object OSGi { val mailboxesCommon = exports(Seq("akka.actor.mailbox.*")) - val osgi = exports(Seq("akka.osgi.*")) + val osgi = exports(Seq("akka.osgi.*")) ++ Seq( + OsgiKeys.importPackage := Seq("org.apache.aries.blueprint.*;resolution:=optional", + "org.osgi.service.blueprint.*;resolution:=optional") ++ defaultImports + ) val remote = exports(Seq("akka.remote.*", "akka.routing.*", "akka.serialization.*")) @@ -559,10 +563,11 @@ object OSGi { val zeroMQ = exports(Seq("akka.zeromq.*")) def exports(packages: Seq[String]) = osgiSettings ++ Seq( - OsgiKeys.importPackage := Seq("!sun.misc", akkaImport(), configImport(), scalaImport(), "*"), + OsgiKeys.importPackage := defaultImports, OsgiKeys.exportPackage := packages ) + def defaultImports = Seq("!sun.misc", akkaImport(), configImport(), scalaImport(), "*") def akkaImport(packageName: String = "akka.*") = "%s;version=\"[2.1,2.2)\"".format(packageName) def configImport(packageName: String = "com.typesafe.config.*") = "%s;version=\"[0.4.1,0.5)\"".format(packageName) def scalaImport(packageName: String = "scala.*") = "%s;version=\"[2.9.2,2.10)\"".format(packageName) From cfbf13ec2a042760cb35c21dc8972550c90eff2b Mon Sep 17 00:00:00 2001 From: Gert Vanthienen Date: Tue, 12 Jun 2012 16:22:21 +0200 Subject: [PATCH 57/74] Using Test.logback instead of Runtime.logback --- project/AkkaBuild.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/AkkaBuild.scala b/project/AkkaBuild.scala index 7b3f5bf65c..7b2b7846e9 100644 --- a/project/AkkaBuild.scala +++ b/project/AkkaBuild.scala @@ -480,7 +480,7 @@ object Dependencies { val camel = Seq(camelCore, Test.scalatest, Test.junit, Test.mockito) - val osgi = Seq(osgiCore, ariesBlueprint, Runtime.logback, Test.ariesProxy, Test.commonsIo, Test.pojosr, Test.tinybundles, Test.scalatest, Test.junit) + val osgi = Seq(osgiCore, ariesBlueprint, Test.logback, Test.ariesProxy, Test.commonsIo, Test.pojosr, Test.tinybundles, Test.scalatest, Test.junit) val tutorials = Seq(Test.scalatest, Test.junit) From f33c45090dfd03717148f15f03edaf72a7a3e2bd Mon Sep 17 00:00:00 2001 From: Gert Vanthienen Date: Tue, 12 Jun 2012 16:57:25 +0200 Subject: [PATCH 58/74] Update docs and use Option[String] in the activator instead of null --- akka-docs/additional/code/osgi/blueprint.xml | 12 ++++++++---- .../main/scala/akka/osgi/ActorSystemActivator.scala | 6 +++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/akka-docs/additional/code/osgi/blueprint.xml b/akka-docs/additional/code/osgi/blueprint.xml index f817da85b0..8fcedb990c 100644 --- a/akka-docs/additional/code/osgi/blueprint.xml +++ b/akka-docs/additional/code/osgi/blueprint.xml @@ -2,9 +2,13 @@ - - - - + + + + some.config { + key=value + } + + diff --git a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala index ef04607976..7f60aebccc 100644 --- a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala +++ b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala @@ -9,10 +9,10 @@ import java.util.Properties * Abstract {@link BundleActivator} implementation to bootstrap and configure an {@link ActorSystem} in an * OSGi environment. */ -abstract class ActorSystemActivator(nameFor: (BundleContext) ⇒ String) extends BundleActivator { +abstract class ActorSystemActivator(nameFor: (BundleContext) ⇒ Option[String]) extends BundleActivator { - def this() = this({ context: BundleContext ⇒ null }) - def this(name: String) = this({ context: BundleContext ⇒ name }) + def this() = this({ context: BundleContext ⇒ None }) + def this(name: String) = this({ context: BundleContext ⇒ Some(name) }) var system: ActorSystem = null From 2bf2cec282d1606d10692aaca770a53b5bc52917 Mon Sep 17 00:00:00 2001 From: Gert Vanthienen Date: Wed, 13 Jun 2012 22:37:41 +0200 Subject: [PATCH 59/74] Service registration is now optional, service references get cleaned up properly --- .../additional/code/osgi/Activator.scala | 5 ++- .../akka/osgi/ActorSystemActivator.scala | 35 +++++++++++++------ .../akka/osgi/OsgiActorSystemFactory.scala | 16 ++------- .../akka/osgi/blueprint/aries/config.xml | 4 ++- .../akka/osgi/blueprint/aries/simple.xml | 4 ++- .../osgi/test/TestActorSystemActivator.scala | 4 ++- 6 files changed, 40 insertions(+), 28 deletions(-) diff --git a/akka-docs/additional/code/osgi/Activator.scala b/akka-docs/additional/code/osgi/Activator.scala index 06a538d242..34e83fcf77 100644 --- a/akka-docs/additional/code/osgi/Activator.scala +++ b/akka-docs/additional/code/osgi/Activator.scala @@ -6,7 +6,10 @@ import org.apache.servicemix.examples.akka.Master //#Activator class Activator extends ActorSystemActivator("PiSystem") { - def configure(system: ActorSystem) { + def configure(context: BundleContext, system: ActorSystem) { + // optionally register the ActorSystem in the OSGi Service Registry + registerService(context, system) + val listener = system.actorOf(Props[Listener], name = "listener") val master = system.actorOf(Props(new Master(4, 10000, 10000, listener)), name = "master") master ! Calculate diff --git a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala index 7f60aebccc..546ff8c2c4 100644 --- a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala +++ b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala @@ -2,8 +2,8 @@ package akka.osgi import com.typesafe.config.{ Config, ConfigFactory } import akka.actor.ActorSystem -import org.osgi.framework.{ BundleContext, BundleActivator } -import java.util.Properties +import java.util.{ Dictionary, Properties } +import org.osgi.framework.{ ServiceRegistration, BundleContext, BundleActivator } /** * Abstract {@link BundleActivator} implementation to bootstrap and configure an {@link ActorSystem} in an @@ -14,14 +14,16 @@ abstract class ActorSystemActivator(nameFor: (BundleContext) ⇒ Option[String]) def this() = this({ context: BundleContext ⇒ None }) def this(name: String) = this({ context: BundleContext ⇒ Some(name) }) - var system: ActorSystem = null + var system: Option[ActorSystem] = None + var registration: Option[ServiceRegistration] = None /** * Implement this method to add your own actors to the ActorSystem * + * @param context the bundle context * @param system the ActorSystem that was created by the activator */ - def configure(system: ActorSystem) + def configure(context: BundleContext, system: ActorSystem) /** * Sets up a new ActorSystem and registers it in the OSGi Service Registry @@ -29,20 +31,31 @@ abstract class ActorSystemActivator(nameFor: (BundleContext) ⇒ Option[String]) * @param context the BundleContext */ def start(context: BundleContext) { - system = OsgiActorSystemFactory(context).createActorSystem(nameFor(context)) - configure(system) + system = Some(OsgiActorSystemFactory(context).createActorSystem(nameFor(context))) + system.foreach(configure(context, _)) } /** - * Shuts down the ActorSystem when the bundle is stopped. + * Shuts down the ActorSystem when the bundle is stopped and, if necessary, unregisters a service registration * * @param context the BundleContext */ def stop(context: BundleContext) { - if (system != null) { - system.shutdown() - system = null - } + registration.foreach(_.unregister()) + system.foreach(_.shutdown()) + } + + /** + * Register the actor system in the OSGi service registry + * + * @param context the bundle context + * @param system the actor system + */ + def registerService(context: BundleContext, system: ActorSystem) { + val properties = new Properties() + properties.put("name", system.name) + registration = Some(context.registerService(classOf[ActorSystem].getName, system, + properties.asInstanceOf[Dictionary[String, Any]])) } } diff --git a/akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala b/akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala index cddf797d07..2c5a6eca14 100644 --- a/akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala +++ b/akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala @@ -1,10 +1,9 @@ package akka.osgi import impl.BundleDelegatingClassLoader -import org.osgi.framework.BundleContext import akka.actor.ActorSystem import com.typesafe.config.{ ConfigFactory, Config } -import java.util.{ Dictionary, Properties } +import org.osgi.framework.BundleContext /** * Factory class to create ActorSystem implementations in an OSGi environment. This mainly involves dealing with @@ -22,17 +21,8 @@ class OsgiActorSystemFactory(val context: BundleContext) { */ def createActorSystem(name: String): ActorSystem = createActorSystem(Option(name)) - def createActorSystem(name: Option[String]): ActorSystem = { - val system = ActorSystem(actorSystemName(name), actorSystemConfig(context), classloader) - registerService(system) - system - } - - def registerService(system: ActorSystem) { - val properties = new Properties() - properties.put("name", system.name) - context.registerService(classOf[ActorSystem].getName, system, properties.asInstanceOf[Dictionary[String, Any]]) - } + def createActorSystem(name: Option[String]): ActorSystem = + ActorSystem(actorSystemName(name), actorSystemConfig(context), classloader) /** * Strategy method to create the Config for the ActorSystem, ensuring that the default/reference configuration is diff --git a/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/config.xml b/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/config.xml index 6bd3d49c9d..ce9f48c551 100644 --- a/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/config.xml +++ b/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/config.xml @@ -2,7 +2,9 @@ - + + + some.config { key=value diff --git a/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/simple.xml b/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/simple.xml index a46834f74b..2ac6552f80 100644 --- a/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/simple.xml +++ b/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/simple.xml @@ -2,6 +2,8 @@ - + + + diff --git a/akka-osgi/src/test/scala/akka/osgi/test/TestActorSystemActivator.scala b/akka-osgi/src/test/scala/akka/osgi/test/TestActorSystemActivator.scala index 2a44e91e4a..90305bc663 100644 --- a/akka-osgi/src/test/scala/akka/osgi/test/TestActorSystemActivator.scala +++ b/akka-osgi/src/test/scala/akka/osgi/test/TestActorSystemActivator.scala @@ -3,14 +3,16 @@ package akka.osgi.test import akka.osgi.ActorSystemActivator import akka.actor.{ Props, ActorSystem } import PingPong._ +import org.osgi.framework.BundleContext /** * Sample ActorSystemActivator implementation used for testing purposes */ class TestActorSystemActivator extends ActorSystemActivator { - def configure(system: ActorSystem) { + def configure(context: BundleContext, system: ActorSystem) { system.actorOf(Props(new PongActor), name = "pong") + registerService(context, system) } } From 2ed00f0936234595ac3b5dcdef31dd2ad9ec1f1f Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Fri, 15 Jun 2012 17:27:27 +0900 Subject: [PATCH 60/74] Removed some superfluous braces. --- akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala | 2 +- akka-actor/src/main/scala/akka/util/ByteString.scala | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala index 81b972f520..3029ca5df0 100644 --- a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala @@ -12,7 +12,7 @@ import org.scalacheck.Gen._ import scala.collection.mutable.Builder import java.nio.{ ByteBuffer, ShortBuffer, IntBuffer, FloatBuffer, DoubleBuffer } -import java.nio.{ ByteOrder }, ByteOrder.{ BIG_ENDIAN, LITTLE_ENDIAN } +import java.nio.ByteOrder, ByteOrder.{ BIG_ENDIAN, LITTLE_ENDIAN } import java.lang.Float.floatToRawIntBits import java.lang.Double.doubleToRawLongBits diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 271e17289b..8b26a9325e 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -55,12 +55,11 @@ object ByteString { def newBuilder: ByteStringBuilder = new ByteStringBuilder - implicit def canBuildFrom: CanBuildFrom[TraversableOnce[Byte], Byte, ByteString] = { + implicit def canBuildFrom: CanBuildFrom[TraversableOnce[Byte], Byte, ByteString] = new CanBuildFrom[TraversableOnce[Byte], Byte, ByteString] { def apply(from: TraversableOnce[Byte]) = newBuilder def apply() = newBuilder } - } private[akka] object ByteString1C { def apply(bytes: Array[Byte]) = new ByteString1C(bytes) From 394cce0af40f8d4c83783b4c2f1236e093edd8c2 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 17 Jun 2012 17:58:20 +0200 Subject: [PATCH 61/74] Simplified implementation of ByteStringBuilder.asOutputStream --- akka-actor/src/main/scala/akka/util/ByteString.scala | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 8b26a9325e..a3f1634bfc 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -694,15 +694,13 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { ByteStrings(bytestrings, _length) } - private val outputStreamWrapper: java.io.OutputStream = new java.io.OutputStream { - def write(b: Int): Unit = builder += b.toByte - - override def write(b: Array[Byte], off: Int, len: Int): Unit = { builder.putBytes(b, off, len) } - } - /** * Directly wraps this ByteStringBuilder in an OutputStream. Write * operations on the stream are forwarded to the builder. */ - def asOutputStream: java.io.OutputStream = outputStreamWrapper + def asOutputStream: java.io.OutputStream = new java.io.OutputStream { + def write(b: Int): Unit = builder += b.toByte + + override def write(b: Array[Byte], off: Int, len: Int): Unit = { builder.putBytes(b, off, len) } + } } From 8d4986dde3d1fde28bcce24fd74f53df17b98696 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 17 Jun 2012 19:28:08 +0200 Subject: [PATCH 62/74] Added new ByteString features to IO (Scala) documentation --- akka-docs/scala/io.rst | 101 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 1 deletion(-) diff --git a/akka-docs/scala/io.rst b/akka-docs/scala/io.rst index 2916dcd03d..d2ac3783a9 100644 --- a/akka-docs/scala/io.rst +++ b/akka-docs/scala/io.rst @@ -17,10 +17,109 @@ ByteString A primary goal of Akka's IO module is to only communicate between actors with immutable objects. When dealing with network IO on the jvm ``Array[Byte]`` and ``ByteBuffer`` are commonly used to represent collections of ``Byte``\s, but they are mutable. Scala's collection library also lacks a suitably efficient immutable collection for ``Byte``\s. Being able to safely and efficiently move ``Byte``\s around is very important for this IO module, so ``ByteString`` was developed. -``ByteString`` is a `Rope-like `_ data structure that is immutable and efficient. When 2 ``ByteString``\s are concatenated together they are both stored within the resulting ``ByteString`` instead of copying both to a new ``Array``. Operations such as ``drop`` and ``take`` return ``ByteString``\s that still reference the original ``Array``, but just change the offset and length that is visible. Great care has also been taken to make sure that the internal ``Array`` cannot be modified. Whenever a potentially unsafe ``Array`` is used to create a new ``ByteString`` a defensive copy is created. +``ByteString`` is a `Rope-like `_ data structure that is immutable and efficient. When 2 ``ByteString``\s are concatenated together they are both stored within the resulting ``ByteString`` instead of copying both to a new ``Array``. Operations such as ``drop`` and ``take`` return ``ByteString``\s that still reference the original ``Array``, but just change the offset and length that is visible. Great care has also been taken to make sure that the internal ``Array`` cannot be modified. Whenever a potentially unsafe ``Array`` is used to create a new ``ByteString`` a defensive copy is created. If you require a ``ByteString`` that only blocks a much memory as necessary for it's content, use the ``compact`` method to get a ``CompactByteString`` instance. If the ``ByteString`` represented only a slice of the original array, this will result in copying all bytes in that slice. ``ByteString`` inherits all methods from ``IndexedSeq``, and it also has some new ones. For more information, look up the ``akka.util.ByteString`` class and it's companion object in the ScalaDoc. +``ByteString`` also comes with it's own optimized builder and iterator classes ``ByteStringBuilder`` and ``ByteIterator`` which provides special features in addition to the standard builder / iterator methods: + +Compatibility with java.io +.......................... + +A ``ByteStringBuilder`` can be wrapped in a `java.io.OutputStream` via the ``asOutputStream`` method. Likewise, ``ByteIterator`` can we wrapped in a ``java.io.InputStream`` via ``asInputStream``. Using these, ``akka.io`` applications can integrate legacy code based on ``java.io`` streams. + +Encoding and decoding of binary data +.................................... + +``ByteStringBuilder`` and ``ByteIterator`` support encoding and decoding of binary data. As an example, consider a stream of binary data frames with the following format: + +.. code-block:: text + + frameLen: Int + n: Int + m: Int + n times { + a: Short + b: Long + } + data: m times Double + +In this example, the data is to be stored in arrays of ``a``, ``b`` and ``data``. + +Decoding of such frames can be efficiently implemented in the following fashion: + +.. code-block:: scala + + implicit val byteOrder = java.nio.ByteOrder.BIG_ENDIAN + + val FrameDecoder = for { + frameLenBytes <- IO.take(4) + frameLen = frameLenBytes.iterator.getInt + frame <- IO.take(frameLen) + } yield { + val in = frame.iterator + + val n = in.getInt + val m = in.getInt + + val a = ArrayBuilder.make[Short]() + val b = ArrayBuilder.make[Long]() + + for (i <- 1 to n) { + a += in.getShort + b += in.getInt + } + + val data = Array.ofDim[Double](m) + in.getDoubles(data) + + (a.result, b.result, data) + } + +This implementation naturally follows the example data format. In a true Scala application, one might, of course, want use specialized immutable Short/Long/Double containers instead of mutable Arrays. + +After extracting data from a ``ByteIterator``, the remaining content can be turned back into a ``ByteString`` using the ``toSeq`` method + +.. code-block:: scala + + val bytes: ByteString = ... + val in: bytes.iterator + ... = in.getInt() + ... = in.get... + val rest: ByteString = in.toSeq + +with no copying from bytes to rest involved. In general, conversions from ByteString to ByteIterator and vice versa are O(1) for non-chunked ``ByteString``s and (at worst) O(nChunks) for chunked ByteStrings. + + +Encoding of data also is very natural, using ``ByteStringBuilder`` + +.. code-block:: scala + + implicit val byteOrder = java.nio.ByteOrder.BIG_ENDIAN + + val frameBuilder = ByteString.newBuilder + + val n = a.length + val m = data.length + + frameBuilder.putInt(n) + frameBuilder.putInt(m) + + for (i <- 0 to n-1) { + frameBuilder.putShort(a(i)) + frameBuilder.putLong(b(i)) + } + frameBuilder.putDoubles(data) + val frame = frameBuilder.result() + +The encoded data then can be sent over socket (see ``IOManager``): + +.. code-block:: scala + + socket.send(ByteString.newBuilder.putInt(frame.length).result) + socket.send(frame) + + IO.Handle ^^^^^^^^^ From 3b67a4fdef450f6297f16337111973acd255b621 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 17 Jun 2012 22:11:55 +0200 Subject: [PATCH 63/74] Added explicit apply method with return type Byte to class ByteString This is necessary to prevent boxing/unboxing of Bytes on apply. --- akka-actor/src/main/scala/akka/util/ByteString.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index a3f1634bfc..7b727cd3a6 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -248,6 +248,8 @@ object ByteString { * TODO: Add performance characteristics */ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimized[Byte, ByteString] { + def apply(idx: Int): Byte + override protected[this] def newBuilder: ByteStringBuilder = ByteString.newBuilder // *must* be overridden by derived classes. This construction is necessary From b6b117cf54a60c42e55d8d7be4b69cba135606c6 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 17 Jun 2012 22:18:51 +0200 Subject: [PATCH 64/74] Improved ByteString.{head,tail,last, init} and added missing tests --- .../src/test/scala/akka/util/ByteStringSpec.scala | 2 ++ akka-actor/src/main/scala/akka/util/ByteString.scala | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala index 3029ca5df0..981d407472 100644 --- a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala @@ -285,7 +285,9 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { } "calling head" in { check { a: ByteString ⇒ a.isEmpty || likeVector(a) { _.head } } } + "calling tail" in { check { a: ByteString ⇒ a.isEmpty || likeVector(a) { _.tail } } } "calling last" in { check { a: ByteString ⇒ a.isEmpty || likeVector(a) { _.last } } } + "calling init" in { check { a: ByteString ⇒ a.isEmpty || likeVector(a) { _.init } } } "calling length" in { check { a: ByteString ⇒ likeVector(a) { _.length } } } "calling span" in { check { (a: ByteString, b: Byte) ⇒ likeVector(a)({ _.span(_ != b) match { case (a, b) ⇒ (a, b) } }) } } diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 7b727cd3a6..714075c6e8 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -257,10 +257,10 @@ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimiz // a parent trait. override def iterator: ByteIterator = throw new UnsupportedOperationException("Method iterator is not implemented in ByteString") - @inline final override def head: Byte = this(0) - @inline final override def tail: ByteString = this.drop(1) - @inline final override def last: Byte = this(this.length - 1) - override def init: ByteString = this.take(this.length - 1) + override def head: Byte = apply(0) + override def tail: ByteString = drop(1) + override def last: Byte = apply(length - 1) + override def init: ByteString = dropRight(1) override def slice(from: Int, until: Int): ByteString = if ((from == 0) && (until == length)) this From 20e313e6a5d628e5de9bf43d9f3c367d72d257a8 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 17 Jun 2012 22:22:40 +0200 Subject: [PATCH 65/74] Removed final specifier on some ByteString and ByteIterator methods --- .../src/main/scala/akka/util/ByteIterator.scala | 12 ++++++------ akka-actor/src/main/scala/akka/util/ByteString.scala | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 5de9f5f333..dc5e4a3d5c 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -409,7 +409,7 @@ abstract class ByteIterator extends BufferedIterator[Byte] { // the parent class. override def drop(n: Int): this.type = throw new UnsupportedOperationException("Method drop is not implemented in ByteIterator") - final override def slice(from: Int, until: Int): this.type = { + override def slice(from: Int, until: Int): this.type = { if (from > 0) drop(from).take(until - from) else take(until) } @@ -431,16 +431,16 @@ abstract class ByteIterator extends BufferedIterator[Byte] { (this, that) } - final override def indexWhere(p: Byte ⇒ Boolean): Int = { + override def indexWhere(p: Byte ⇒ Boolean): Int = { var index = 0 var found = false while (!found && hasNext) if (p(next())) { found = true } else { index += 1 } if (found) index else -1 } - final def indexOf(elem: Byte): Int = indexWhere { _ == elem } + def indexOf(elem: Byte): Int = indexWhere { _ == elem } - final override def indexOf[B >: Byte](elem: B): Int = indexWhere { _ == elem } + override def indexOf[B >: Byte](elem: B): Int = indexWhere { _ == elem } def toByteString: ByteString @@ -449,13 +449,13 @@ abstract class ByteIterator extends BufferedIterator[Byte] { override def foreach[@specialized U](f: Byte ⇒ U): Unit = while (hasNext) f(next()) - final override def foldLeft[@specialized B](z: B)(op: (B, Byte) ⇒ B): B = { + override def foldLeft[@specialized B](z: B)(op: (B, Byte) ⇒ B): B = { var acc = z foreach { byte ⇒ acc = op(acc, byte) } acc } - final override def toArray[B >: Byte](implicit arg0: ClassManifest[B]): Array[B] = { + override def toArray[B >: Byte](implicit arg0: ClassManifest[B]): Array[B] = { val target = Array.ofDim[B](len) copyToArray(target) target diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 714075c6e8..b0dc19d49f 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -285,7 +285,7 @@ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimiz override def copyToArray[B >: Byte](xs: Array[B], start: Int, len: Int): Unit = iterator.copyToArray(xs, start, len) - @inline final override def foreach[@specialized U](f: Byte ⇒ U): Unit = iterator foreach f + override def foreach[@specialized U](f: Byte ⇒ U): Unit = iterator foreach f /** * Efficiently concatenate another ByteString. @@ -327,7 +327,7 @@ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimiz * Creates a new ByteBuffer with a copy of all bytes contained in this * ByteString. */ - final def toByteBuffer: ByteBuffer = ByteBuffer.wrap(toArray) + def toByteBuffer: ByteBuffer = ByteBuffer.wrap(toArray) /** * Decodes this ByteString as a UTF-8 encoded String. From 4f0fe8723bd2d8c0b72a88cbc7dffa2f0f954dc7 Mon Sep 17 00:00:00 2001 From: Gert Vanthienen Date: Mon, 18 Jun 2012 19:00:36 +0200 Subject: [PATCH 66/74] Using setRuntimeClass() avoids the need for the user bundle to import akka.osgi.blueprint --- .../main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala b/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala index 245ea538b6..b20e959f23 100644 --- a/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala +++ b/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala @@ -80,7 +80,7 @@ class NamespaceHandler extends org.apache.aries.blueprint.NamespaceHandler { factory.setId(findAvailableId(context)) factory.setScope(BeanMetadata.SCOPE_SINGLETON) factory.setProcessor(true) - factory.setClassName(classOf[BlueprintActorSystemFactory].getName) + factory.setRuntimeClass(classOf[BlueprintActorSystemFactory]) factory.setDestroyMethod(DESTROY_METHOD_NAME) From 9c6deaa47576c020a17df83d8e0f0d90b9304173 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Tue, 19 Jun 2012 17:16:30 +0200 Subject: [PATCH 67/74] Changed ByteString docs to hoist example code from separate file --- .../code/akka/docs/io/BinaryCoding.scala | 84 +++++++++++++++++++ akka-docs/scala/io.rst | 65 ++------------ 2 files changed, 93 insertions(+), 56 deletions(-) create mode 100644 akka-docs/scala/code/akka/docs/io/BinaryCoding.scala diff --git a/akka-docs/scala/code/akka/docs/io/BinaryCoding.scala b/akka-docs/scala/code/akka/docs/io/BinaryCoding.scala new file mode 100644 index 0000000000..d9aeb334fc --- /dev/null +++ b/akka-docs/scala/code/akka/docs/io/BinaryCoding.scala @@ -0,0 +1,84 @@ +/** + * Copyright (C) 2012 Typesafe Inc. + */ +package akka.docs.io + +//#imports +import akka.actor._ +import akka.util.{ ByteString, ByteStringBuilder, ByteIterator } +//#imports + +abstract class BinaryDecoding { + //#decoding + implicit val byteOrder = java.nio.ByteOrder.BIG_ENDIAN + + val FrameDecoder = for { + frameLenBytes ← IO.take(4) + frameLen = frameLenBytes.iterator.getInt + frame ← IO.take(frameLen) + } yield { + val in = frame.iterator + + val n = in.getInt + val m = in.getInt + + val a = Array.newBuilder[Short] + val b = Array.newBuilder[Long] + + for (i ← 1 to n) { + a += in.getShort + b += in.getInt + } + + val data = Array.ofDim[Double](m) + in.getDoubles(data) + + (a.result, b.result, data) + } + + //#decoding +} + +abstract class RestToSeq { + implicit val byteOrder = java.nio.ByteOrder.BIG_ENDIAN + val bytes: ByteString + val in = bytes.iterator + + //#rest-to-seq + val n = in.getInt + val m = in.getInt + // ... in.get... + val rest: ByteString = in.toSeq + //#rest-to-seq +} + +abstract class BinaryEncoding { + //#encoding + implicit val byteOrder = java.nio.ByteOrder.BIG_ENDIAN + + val a: Array[Short] + val b: Array[Long] + val data: Array[Double] + + val frameBuilder = ByteString.newBuilder + + val n = a.length + val m = data.length + + frameBuilder.putInt(n) + frameBuilder.putInt(m) + + for (i ← 0 to n - 1) { + frameBuilder.putShort(a(i)) + frameBuilder.putLong(b(i)) + } + frameBuilder.putDoubles(data) + val frame = frameBuilder.result() + //#encoding + + //#sending + val socket: IO.SocketHandle + socket.write(ByteString.newBuilder.putInt(frame.length).result) + socket.write(frame) + //#sending +} diff --git a/akka-docs/scala/io.rst b/akka-docs/scala/io.rst index d2ac3783a9..85d2b6bef5 100644 --- a/akka-docs/scala/io.rst +++ b/akka-docs/scala/io.rst @@ -48,76 +48,29 @@ In this example, the data is to be stored in arrays of ``a``, ``b`` and ``data`` Decoding of such frames can be efficiently implemented in the following fashion: -.. code-block:: scala - - implicit val byteOrder = java.nio.ByteOrder.BIG_ENDIAN - - val FrameDecoder = for { - frameLenBytes <- IO.take(4) - frameLen = frameLenBytes.iterator.getInt - frame <- IO.take(frameLen) - } yield { - val in = frame.iterator - - val n = in.getInt - val m = in.getInt - - val a = ArrayBuilder.make[Short]() - val b = ArrayBuilder.make[Long]() - - for (i <- 1 to n) { - a += in.getShort - b += in.getInt - } - - val data = Array.ofDim[Double](m) - in.getDoubles(data) - - (a.result, b.result, data) - } +.. includecode:: code/akka/docs/io/BinaryCoding.scala + :include: decoding This implementation naturally follows the example data format. In a true Scala application, one might, of course, want use specialized immutable Short/Long/Double containers instead of mutable Arrays. -After extracting data from a ``ByteIterator``, the remaining content can be turned back into a ``ByteString`` using the ``toSeq`` method +After extracting data from a ``ByteIterator``, the remaining content can also be turned back into a ``ByteString`` using the ``toSeq`` method -.. code-block:: scala - - val bytes: ByteString = ... - val in: bytes.iterator - ... = in.getInt() - ... = in.get... - val rest: ByteString = in.toSeq +.. includecode:: code/akka/docs/io/BinaryCoding.scala + :include: rest-to-seq with no copying from bytes to rest involved. In general, conversions from ByteString to ByteIterator and vice versa are O(1) for non-chunked ``ByteString``s and (at worst) O(nChunks) for chunked ByteStrings. Encoding of data also is very natural, using ``ByteStringBuilder`` -.. code-block:: scala +.. includecode:: code/akka/docs/io/BinaryCoding.scala + :include: encoding - implicit val byteOrder = java.nio.ByteOrder.BIG_ENDIAN - - val frameBuilder = ByteString.newBuilder - - val n = a.length - val m = data.length - - frameBuilder.putInt(n) - frameBuilder.putInt(m) - - for (i <- 0 to n-1) { - frameBuilder.putShort(a(i)) - frameBuilder.putLong(b(i)) - } - frameBuilder.putDoubles(data) - val frame = frameBuilder.result() The encoded data then can be sent over socket (see ``IOManager``): -.. code-block:: scala - - socket.send(ByteString.newBuilder.putInt(frame.length).result) - socket.send(frame) +.. includecode:: code/akka/docs/io/BinaryCoding.scala + :include: sending IO.Handle From 379c0e16b462bf6354a1b7c83512b9519ce758f1 Mon Sep 17 00:00:00 2001 From: Gert Vanthienen Date: Tue, 26 Jun 2012 22:07:57 +0200 Subject: [PATCH 68/74] Move Aries Blueprint namespace handler into a separate akka-osgi-aries module --- .../blueprint/akka-namespacehandler.xml | 2 +- .../akka/osgi/aries/blueprint}/akka.xsd | 0 .../BlueprintActorSystemFactory.scala | 4 +--- .../aries/blueprint}/NamespaceHandler.scala | 7 +++--- .../osgi/aries/blueprint}/ParserHelper.scala | 2 +- .../akka/osgi/aries/blueprint}/config.xml | 0 .../akka/osgi/aries/blueprint}/injection.xml | 4 ++-- .../akka/osgi/aries/blueprint}/simple.xml | 0 .../blueprint}/ActorSystemAwareBean.scala | 2 +- .../blueprint}/NamespaceHandlerTest.scala | 3 +-- .../akka/osgi/ActorSystemActivator.scala | 1 - project/AkkaBuild.scala | 24 +++++++++++++++---- 12 files changed, 29 insertions(+), 20 deletions(-) rename {akka-osgi => akka-osgi-aries}/src/main/resources/OSGI-INF/blueprint/akka-namespacehandler.xml (87%) rename {akka-osgi/src/main/resources/akka/osgi/blueprint/aries => akka-osgi-aries/src/main/resources/akka/osgi/aries/blueprint}/akka.xsd (100%) rename {akka-osgi/src/main/scala/akka/osgi => akka-osgi-aries/src/main/scala/akka/osgi/aries}/blueprint/BlueprintActorSystemFactory.scala (91%) rename {akka-osgi/src/main/scala/akka/osgi/blueprint/aries => akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint}/NamespaceHandler.scala (96%) rename {akka-osgi/src/main/scala/akka/osgi/blueprint/aries => akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint}/ParserHelper.scala (92%) rename {akka-osgi/src/test/resources/akka/osgi/blueprint/aries => akka-osgi-aries/src/test/resources/akka/osgi/aries/blueprint}/config.xml (100%) rename {akka-osgi/src/test/resources/akka/osgi/blueprint/aries => akka-osgi-aries/src/test/resources/akka/osgi/aries/blueprint}/injection.xml (68%) rename {akka-osgi/src/test/resources/akka/osgi/blueprint/aries => akka-osgi-aries/src/test/resources/akka/osgi/aries/blueprint}/simple.xml (100%) rename {akka-osgi/src/test/scala/akka/osgi/test => akka-osgi-aries/src/test/scala/akka/osgi/aries/blueprint}/ActorSystemAwareBean.scala (86%) rename {akka-osgi/src/test/scala/akka/osgi/blueprint/aries => akka-osgi-aries/src/test/scala/akka/osgi/aries/blueprint}/NamespaceHandlerTest.scala (97%) diff --git a/akka-osgi/src/main/resources/OSGI-INF/blueprint/akka-namespacehandler.xml b/akka-osgi-aries/src/main/resources/OSGI-INF/blueprint/akka-namespacehandler.xml similarity index 87% rename from akka-osgi/src/main/resources/OSGI-INF/blueprint/akka-namespacehandler.xml rename to akka-osgi-aries/src/main/resources/OSGI-INF/blueprint/akka-namespacehandler.xml index 650738b10a..8cc52e85f2 100644 --- a/akka-osgi/src/main/resources/OSGI-INF/blueprint/akka-namespacehandler.xml +++ b/akka-osgi-aries/src/main/resources/OSGI-INF/blueprint/akka-namespacehandler.xml @@ -9,7 +9,7 @@ - + diff --git a/akka-osgi/src/main/resources/akka/osgi/blueprint/aries/akka.xsd b/akka-osgi-aries/src/main/resources/akka/osgi/aries/blueprint/akka.xsd similarity index 100% rename from akka-osgi/src/main/resources/akka/osgi/blueprint/aries/akka.xsd rename to akka-osgi-aries/src/main/resources/akka/osgi/aries/blueprint/akka.xsd diff --git a/akka-osgi/src/main/scala/akka/osgi/blueprint/BlueprintActorSystemFactory.scala b/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/BlueprintActorSystemFactory.scala similarity index 91% rename from akka-osgi/src/main/scala/akka/osgi/blueprint/BlueprintActorSystemFactory.scala rename to akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/BlueprintActorSystemFactory.scala index 51c3e7291f..876d0f0045 100644 --- a/akka-osgi/src/main/scala/akka/osgi/blueprint/BlueprintActorSystemFactory.scala +++ b/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/BlueprintActorSystemFactory.scala @@ -1,9 +1,7 @@ -package akka.osgi.blueprint +package akka.osgi.aries.blueprint import org.osgi.framework.BundleContext import akka.osgi.OsgiActorSystemFactory -import collection.mutable.Buffer -import akka.actor.{ Actor, Props, ActorSystem } import com.typesafe.config.ConfigFactory /** diff --git a/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala b/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/NamespaceHandler.scala similarity index 96% rename from akka-osgi/src/main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala rename to akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/NamespaceHandler.scala index b20e959f23..13b644010e 100644 --- a/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/NamespaceHandler.scala +++ b/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/NamespaceHandler.scala @@ -1,4 +1,4 @@ -package akka.osgi.blueprint.aries +package akka.osgi.aries.blueprint import org.apache.aries.blueprint.ParserContext import org.osgi.service.blueprint.container.ComponentDefinitionException @@ -7,10 +7,9 @@ import org.apache.aries.blueprint.mutable.MutableBeanMetadata import collection.JavaConversions.setAsJavaSet import org.osgi.framework.BundleContext import org.apache.aries.blueprint.reflect.{ ValueMetadataImpl, RefMetadataImpl, BeanArgumentImpl } -import org.w3c.dom.{ NodeList, Element, Node } +import org.w3c.dom.{ Element, Node } import org.osgi.service.blueprint.reflect.{ BeanMetadata, ComponentMetadata } -import akka.actor.{ ActorSystem } -import akka.osgi.blueprint.{ BlueprintActorSystemFactory } +import akka.actor.ActorSystem import java.util.concurrent.atomic.AtomicInteger import ParserHelper.childElements diff --git a/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/ParserHelper.scala b/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/ParserHelper.scala similarity index 92% rename from akka-osgi/src/main/scala/akka/osgi/blueprint/aries/ParserHelper.scala rename to akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/ParserHelper.scala index 82fb7bc113..35bf72931a 100644 --- a/akka-osgi/src/main/scala/akka/osgi/blueprint/aries/ParserHelper.scala +++ b/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/ParserHelper.scala @@ -1,4 +1,4 @@ -package akka.osgi.blueprint.aries +package akka.osgi.aries.blueprint import org.w3c.dom.{ Node, Element } diff --git a/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/config.xml b/akka-osgi-aries/src/test/resources/akka/osgi/aries/blueprint/config.xml similarity index 100% rename from akka-osgi/src/test/resources/akka/osgi/blueprint/aries/config.xml rename to akka-osgi-aries/src/test/resources/akka/osgi/aries/blueprint/config.xml diff --git a/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/injection.xml b/akka-osgi-aries/src/test/resources/akka/osgi/aries/blueprint/injection.xml similarity index 68% rename from akka-osgi/src/test/resources/akka/osgi/blueprint/aries/injection.xml rename to akka-osgi-aries/src/test/resources/akka/osgi/aries/blueprint/injection.xml index 9712ee6d1f..6fd21db5ef 100644 --- a/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/injection.xml +++ b/akka-osgi-aries/src/test/resources/akka/osgi/aries/blueprint/injection.xml @@ -2,8 +2,8 @@ - - + + diff --git a/akka-osgi/src/test/resources/akka/osgi/blueprint/aries/simple.xml b/akka-osgi-aries/src/test/resources/akka/osgi/aries/blueprint/simple.xml similarity index 100% rename from akka-osgi/src/test/resources/akka/osgi/blueprint/aries/simple.xml rename to akka-osgi-aries/src/test/resources/akka/osgi/aries/blueprint/simple.xml diff --git a/akka-osgi/src/test/scala/akka/osgi/test/ActorSystemAwareBean.scala b/akka-osgi-aries/src/test/scala/akka/osgi/aries/blueprint/ActorSystemAwareBean.scala similarity index 86% rename from akka-osgi/src/test/scala/akka/osgi/test/ActorSystemAwareBean.scala rename to akka-osgi-aries/src/test/scala/akka/osgi/aries/blueprint/ActorSystemAwareBean.scala index ca0df7cc04..6e4bac39dd 100644 --- a/akka-osgi/src/test/scala/akka/osgi/test/ActorSystemAwareBean.scala +++ b/akka-osgi-aries/src/test/scala/akka/osgi/aries/blueprint/ActorSystemAwareBean.scala @@ -1,4 +1,4 @@ -package akka.osgi.test +package akka.osgi.aries.blueprint import akka.actor.ActorSystem diff --git a/akka-osgi/src/test/scala/akka/osgi/blueprint/aries/NamespaceHandlerTest.scala b/akka-osgi-aries/src/test/scala/akka/osgi/aries/blueprint/NamespaceHandlerTest.scala similarity index 97% rename from akka-osgi/src/test/scala/akka/osgi/blueprint/aries/NamespaceHandlerTest.scala rename to akka-osgi-aries/src/test/scala/akka/osgi/aries/blueprint/NamespaceHandlerTest.scala index bbe26b5e92..44178978ec 100644 --- a/akka-osgi/src/test/scala/akka/osgi/blueprint/aries/NamespaceHandlerTest.scala +++ b/akka-osgi-aries/src/test/scala/akka/osgi/aries/blueprint/NamespaceHandlerTest.scala @@ -1,11 +1,10 @@ -package akka.osgi.blueprint.aries +package akka.osgi.aries.blueprint import org.scalatest.FlatSpec import akka.actor.ActorSystem import de.kalpatec.pojosr.framework.launch.BundleDescriptor import akka.osgi.PojoSRTestSupport import akka.osgi.PojoSRTestSupport.bundle -import akka.osgi.test.ActorSystemAwareBean /** * Test cases for {@link ActorSystemActivator} diff --git a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala index 546ff8c2c4..28912cf6b0 100644 --- a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala +++ b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala @@ -1,6 +1,5 @@ package akka.osgi -import com.typesafe.config.{ Config, ConfigFactory } import akka.actor.ActorSystem import java.util.{ Dictionary, Properties } import org.osgi.framework.{ ServiceRegistration, BundleContext, BundleActivator } diff --git a/project/AkkaBuild.scala b/project/AkkaBuild.scala index d9d5517703..f1d71729c2 100644 --- a/project/AkkaBuild.scala +++ b/project/AkkaBuild.scala @@ -226,6 +226,15 @@ object AkkaBuild extends Build { ) ) + lazy val osgiAries = Project( + id = "akka-osgi-aries", + base = file("akka-osgi-aries"), + dependencies = Seq(osgi % "compile;test->test"), + settings = defaultSettings ++ OSGi.osgiAries ++ Seq( + libraryDependencies ++= Dependencies.osgiAries + ) + ) + lazy val akkaSbtPlugin = Project( id = "akka-sbt-plugin", base = file("akka-sbt-plugin"), @@ -480,7 +489,9 @@ object Dependencies { val camel = Seq(camelCore, Test.scalatest, Test.junit, Test.mockito) - val osgi = Seq(osgiCore, ariesBlueprint, Test.logback, Test.ariesProxy, Test.commonsIo, Test.pojosr, Test.tinybundles, Test.scalatest, Test.junit) + val osgi = Seq(osgiCore,Test.logback, Test.commonsIo, Test.pojosr, Test.tinybundles, Test.scalatest, Test.junit) + + val osgiAries = Seq(osgiCore, ariesBlueprint, Test.ariesProxy) val tutorials = Seq(Test.scalatest, Test.junit) @@ -551,9 +562,12 @@ object OSGi { val mailboxesCommon = exports(Seq("akka.actor.mailbox.*")) - val osgi = exports(Seq("akka.osgi.*")) ++ Seq( - OsgiKeys.importPackage := Seq("org.apache.aries.blueprint.*;resolution:=optional", - "org.osgi.service.blueprint.*;resolution:=optional") ++ defaultImports + val osgi = exports(Seq("akka.osgi")) ++ Seq( + OsgiKeys.privatePackage := Seq("akka.osgi.impl") + ) + + val osgiAries = exports() ++ Seq( + OsgiKeys.privatePackage := Seq("akka.osgi.aries.*") ) val remote = exports(Seq("akka.remote.*", "akka.routing.*", "akka.serialization.*")) @@ -564,7 +578,7 @@ object OSGi { val zeroMQ = exports(Seq("akka.zeromq.*")) - def exports(packages: Seq[String]) = osgiSettings ++ Seq( + def exports(packages: Seq[String] = Seq()) = osgiSettings ++ Seq( OsgiKeys.importPackage := defaultImports, OsgiKeys.exportPackage := packages ) From 85cf1c0580aec43f112df8d8308119d4585b0c74 Mon Sep 17 00:00:00 2001 From: Gert Vanthienen Date: Wed, 27 Jun 2012 14:01:09 +0200 Subject: [PATCH 69/74] Add types to API methods and clarify Scaladocs --- .../blueprint/akka-namespacehandler.xml | 3 ++ .../BlueprintActorSystemFactory.scala | 6 ++- .../aries/blueprint/NamespaceHandler.scala | 21 ++++++++- .../osgi/aries/blueprint/ParserHelper.scala | 5 ++- .../akka/osgi/ActorSystemActivator.scala | 45 +++++++++++++------ .../akka/osgi/OsgiActorSystemFactory.scala | 17 +++++-- .../impl/BundleDelegatingClassLoader.scala | 4 +- 7 files changed, 76 insertions(+), 25 deletions(-) diff --git a/akka-osgi-aries/src/main/resources/OSGI-INF/blueprint/akka-namespacehandler.xml b/akka-osgi-aries/src/main/resources/OSGI-INF/blueprint/akka-namespacehandler.xml index 8cc52e85f2..99492bedf2 100644 --- a/akka-osgi-aries/src/main/resources/OSGI-INF/blueprint/akka-namespacehandler.xml +++ b/akka-osgi-aries/src/main/resources/OSGI-INF/blueprint/akka-namespacehandler.xml @@ -1,6 +1,9 @@ + diff --git a/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/BlueprintActorSystemFactory.scala b/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/BlueprintActorSystemFactory.scala index 876d0f0045..40c9d7367b 100644 --- a/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/BlueprintActorSystemFactory.scala +++ b/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/BlueprintActorSystemFactory.scala @@ -5,7 +5,11 @@ import akka.osgi.OsgiActorSystemFactory import com.typesafe.config.ConfigFactory /** - * A set of helper/factory classes to build a Akka system using Blueprint + * A set of helper/factory classes to build a Akka system using Blueprint. This class is only meant to be used by + * the [[akka.osgi.aries.blueprint.NamespaceHandler]] class, you should not use this class directly. + * + * If you're looking for a way to set up Akka using Blueprint without the namespace handler, you should use + * [[akka.osgi.OsgiActorSystemFactory]] instead. */ class BlueprintActorSystemFactory(context: BundleContext, name: String) extends OsgiActorSystemFactory(context) { diff --git a/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/NamespaceHandler.scala b/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/NamespaceHandler.scala index 13b644010e..2ab1306a0f 100644 --- a/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/NamespaceHandler.scala +++ b/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/NamespaceHandler.scala @@ -15,7 +15,26 @@ import java.util.concurrent.atomic.AtomicInteger import ParserHelper.childElements /** - * Aries Blueprint namespace handler implementation + * Aries Blueprint namespace handler implementation. This namespace handler will allow users of Apache Aries' Blueprint + * implementation to define their Akka [[akka.actor.ActorSystem]] using a syntax like this: + * + * {{{ + * + * + * + * + * + * some.config { + * key=value + * } + * + * + * + * + * }}} + * + * Users of other IoC frameworks in an OSGi environment should use [[akka.osgi.OsgiActorSystemFactory]] instead. */ class NamespaceHandler extends org.apache.aries.blueprint.NamespaceHandler { diff --git a/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/ParserHelper.scala b/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/ParserHelper.scala index 35bf72931a..585037db09 100644 --- a/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/ParserHelper.scala +++ b/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/ParserHelper.scala @@ -7,9 +7,10 @@ import org.w3c.dom.{ Node, Element } */ object ParserHelper { - def childElements(element: Element) = children(element).filter(_.getNodeType == Node.ELEMENT_NODE).asInstanceOf[Seq[Element]] + def childElements(element: Element): Seq[Element] = + children(element).filter(_.getNodeType == Node.ELEMENT_NODE).asInstanceOf[Seq[Element]] - private[this] def children(element: Element) = { + private[this] def children(element: Element): Seq[Node] = { val nodelist = element.getChildNodes for (index ← 0 until nodelist.getLength) yield nodelist.item(index) } diff --git a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala index 28912cf6b0..794eec0317 100644 --- a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala +++ b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala @@ -5,52 +5,69 @@ import java.util.{ Dictionary, Properties } import org.osgi.framework.{ ServiceRegistration, BundleContext, BundleActivator } /** - * Abstract {@link BundleActivator} implementation to bootstrap and configure an {@link ActorSystem} in an - * OSGi environment. + * Abstract bundle activator implementation to bootstrap and configure an actor system in an + * OSGi environment. It also provides a convenience method to register the actor system in + * the OSGi Service Registry for sharing it with other OSGi bundles. + * + * This convenience activator is mainly useful for setting up a single [[akka.actor.ActorSystem]] instance and sharing that + * with other bundles in the OSGi Framework. If you want to set up multiple systems in the same bundle context, look at + * the [[akka.osgi.OsgiActorSystemFactory]] instead. + * + * @param nameFor a function that allows you to determine the name of the [[akka.actor.ActorSystem]] at bundle startup time */ abstract class ActorSystemActivator(nameFor: (BundleContext) ⇒ Option[String]) extends BundleActivator { + /** + * No-args constructor - a default name (`bundle--ActorSystem`) will be assigned to the [[akka.actor.ActorSystem]] + */ def this() = this({ context: BundleContext ⇒ None }) - def this(name: String) = this({ context: BundleContext ⇒ Some(name) }) - - var system: Option[ActorSystem] = None - var registration: Option[ServiceRegistration] = None /** - * Implement this method to add your own actors to the ActorSystem + * Create the activator, specifying the name of the [[akka.actor.ActorSystem]] to be created + */ + def this(name: String) = this({ context: BundleContext ⇒ Some(name) }) + + private var system: Option[ActorSystem] = None + private var registration: Option[ServiceRegistration] = None + + /** + * Implement this method to add your own actors to the ActorSystem. If you want to share the actor + * system with other bundles, call the `registerService(BundleContext, ActorSystem)` method from within + * this method. * * @param context the bundle context * @param system the ActorSystem that was created by the activator */ - def configure(context: BundleContext, system: ActorSystem) + def configure(context: BundleContext, system: ActorSystem): Unit /** - * Sets up a new ActorSystem and registers it in the OSGi Service Registry + * Sets up a new ActorSystem * * @param context the BundleContext */ - def start(context: BundleContext) { + def start(context: BundleContext): Unit = { system = Some(OsgiActorSystemFactory(context).createActorSystem(nameFor(context))) system.foreach(configure(context, _)) } /** - * Shuts down the ActorSystem when the bundle is stopped and, if necessary, unregisters a service registration + * Shuts down the ActorSystem when the bundle is stopped and, if necessary, unregisters a service registration. * * @param context the BundleContext */ - def stop(context: BundleContext) { + def stop(context: BundleContext): Unit = { registration.foreach(_.unregister()) system.foreach(_.shutdown()) } /** - * Register the actor system in the OSGi service registry + * Register the actor system in the OSGi service registry. The activator itself will ensure that this service + * is unregistered again when the bundle is being stopped. * * @param context the bundle context * @param system the actor system */ - def registerService(context: BundleContext, system: ActorSystem) { + def registerService(context: BundleContext, system: ActorSystem): Unit = { val properties = new Properties() properties.put("name", system.name) registration = Some(context.registerService(classOf[ActorSystem].getName, system, diff --git a/akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala b/akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala index 2c5a6eca14..ae36406a60 100644 --- a/akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala +++ b/akka-osgi/src/main/scala/akka/osgi/OsgiActorSystemFactory.scala @@ -14,13 +14,18 @@ class OsgiActorSystemFactory(val context: BundleContext) { /* * Classloader that delegates to the bundle for which the factory is creating an ActorSystem */ - val classloader = BundleDelegatingClassLoader.createFor(context) + private val classloader = BundleDelegatingClassLoader.createFor(context) /** - * Creates the ActorSystem and registers it in the OSGi Service Registry + * Creates the [[akka.actor.ActorSystem]], using the name specified */ def createActorSystem(name: String): ActorSystem = createActorSystem(Option(name)) + /** + * Creates the [[akka.actor.ActorSystem]], using the name specified. + * + * A default name (`bundle--ActorSystem`) is assigned when you pass along [[scala.None]] instead. + */ def createActorSystem(name: Option[String]): ActorSystem = ActorSystem(actorSystemName(name), actorSystemConfig(context), classloader) @@ -34,7 +39,8 @@ class OsgiActorSystemFactory(val context: BundleContext) { } /** - * Determine a the ActorSystem name + * Determine the name for the [[akka.actor.ActorSystem]] + * Returns a default value of `bundle--ActorSystem` is no name is being specified */ def actorSystemName(name: Option[String]): String = name.getOrElse("bundle-%s-ActorSystem".format(context.getBundle().getBundleId)) @@ -43,6 +49,9 @@ class OsgiActorSystemFactory(val context: BundleContext) { object OsgiActorSystemFactory { - def apply(context: BundleContext) = new OsgiActorSystemFactory(context) + /* + * Create an [[OsgiActorSystemFactory]] instance to set up Akka in an OSGi environment + */ + def apply(context: BundleContext): OsgiActorSystemFactory = new OsgiActorSystemFactory(context) } diff --git a/akka-osgi/src/main/scala/akka/osgi/impl/BundleDelegatingClassLoader.scala b/akka-osgi/src/main/scala/akka/osgi/impl/BundleDelegatingClassLoader.scala index 74592392d9..0231a77714 100644 --- a/akka-osgi/src/main/scala/akka/osgi/impl/BundleDelegatingClassLoader.scala +++ b/akka-osgi/src/main/scala/akka/osgi/impl/BundleDelegatingClassLoader.scala @@ -63,11 +63,9 @@ class BundleDelegatingClassLoader(bundle: Bundle, classLoader: Option[ClassLoade case cnfe: ClassNotFoundException ⇒ rethrowClassNotFoundException(name, cnfe) } - def rethrowClassNotFoundException(name: String, cnfe: ClassNotFoundException): Nothing = + private def rethrowClassNotFoundException(name: String, cnfe: ClassNotFoundException): Nothing = throw new ClassNotFoundException(name + " from bundle " + bundle.getBundleId + " (" + bundle.getSymbolicName + ")", cnfe) - def getBundle: Bundle = bundle - override def toString = String.format("BundleDelegatingClassLoader(%s)", bundle) } From 88c1f69050952188dc9db9b79500c43525f164b9 Mon Sep 17 00:00:00 2001 From: Gert Vanthienen Date: Fri, 29 Jun 2012 14:12:36 +0200 Subject: [PATCH 70/74] A few more code-style fixes --- .../main/scala/akka/osgi/ActorSystemActivator.scala | 10 +++++----- .../akka/osgi/impl/BundleDelegatingClassLoader.scala | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala index 794eec0317..e92415e1e1 100644 --- a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala +++ b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala @@ -20,12 +20,12 @@ abstract class ActorSystemActivator(nameFor: (BundleContext) ⇒ Option[String]) /** * No-args constructor - a default name (`bundle--ActorSystem`) will be assigned to the [[akka.actor.ActorSystem]] */ - def this() = this({ context: BundleContext ⇒ None }) + def this() = this(context ⇒ None) /** * Create the activator, specifying the name of the [[akka.actor.ActorSystem]] to be created */ - def this(name: String) = this({ context: BundleContext ⇒ Some(name) }) + def this(name: String) = this(context ⇒ Some(name)) private var system: Option[ActorSystem] = None private var registration: Option[ServiceRegistration] = None @@ -47,7 +47,7 @@ abstract class ActorSystemActivator(nameFor: (BundleContext) ⇒ Option[String]) */ def start(context: BundleContext): Unit = { system = Some(OsgiActorSystemFactory(context).createActorSystem(nameFor(context))) - system.foreach(configure(context, _)) + system foreach (configure(context, _)) } /** @@ -56,8 +56,8 @@ abstract class ActorSystemActivator(nameFor: (BundleContext) ⇒ Option[String]) * @param context the BundleContext */ def stop(context: BundleContext): Unit = { - registration.foreach(_.unregister()) - system.foreach(_.shutdown()) + registration foreach (_.unregister()) + system foreach (_.shutdown()) } /** diff --git a/akka-osgi/src/main/scala/akka/osgi/impl/BundleDelegatingClassLoader.scala b/akka-osgi/src/main/scala/akka/osgi/impl/BundleDelegatingClassLoader.scala index 0231a77714..08dee0344e 100644 --- a/akka-osgi/src/main/scala/akka/osgi/impl/BundleDelegatingClassLoader.scala +++ b/akka-osgi/src/main/scala/akka/osgi/impl/BundleDelegatingClassLoader.scala @@ -28,7 +28,7 @@ class BundleDelegatingClassLoader(bundle: Bundle, classLoader: Option[ClassLoade protected override def findClass(name: String): Class[_] = bundle.loadClass(name) protected override def findResource(name: String): URL = { - val resource: URL = bundle.getResource(name) + val resource = bundle.getResource(name) classLoader match { case Some(loader) if resource == null ⇒ loader.getResource(name) case _ ⇒ resource @@ -40,7 +40,7 @@ class BundleDelegatingClassLoader(bundle: Bundle, classLoader: Option[ClassLoade bundle.getResources(name).asInstanceOf[Enumeration[URL]] protected override def loadClass(name: String, resolve: Boolean): Class[_] = { - val clazz: Class[_] = try { + val clazz = try { findClass(name) } catch { case cnfe: ClassNotFoundException ⇒ { @@ -66,7 +66,7 @@ class BundleDelegatingClassLoader(bundle: Bundle, classLoader: Option[ClassLoade private def rethrowClassNotFoundException(name: String, cnfe: ClassNotFoundException): Nothing = throw new ClassNotFoundException(name + " from bundle " + bundle.getBundleId + " (" + bundle.getSymbolicName + ")", cnfe) - override def toString = String.format("BundleDelegatingClassLoader(%s)", bundle) + override def toString: String = String.format("BundleDelegatingClassLoader(%s)", bundle) } From a151afbe6813b0b4827ec1668eac45a5ebd45ed2 Mon Sep 17 00:00:00 2001 From: Roland Date: Sat, 30 Jun 2012 00:23:49 +0200 Subject: [PATCH 71/74] link to Config lib docs, see #2296 --- .../src/main/scala/akka/actor/ActorSystem.scala | 12 ++++++++++++ project/AkkaBuild.scala | 4 +--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/akka-actor/src/main/scala/akka/actor/ActorSystem.scala b/akka-actor/src/main/scala/akka/actor/ActorSystem.scala index eb0f241177..261a6b3c58 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorSystem.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorSystem.scala @@ -55,11 +55,15 @@ object ActorSystem { * obtains the current ClassLoader by first inspecting the current threads' getContextClassLoader, * then tries to walk the stack to find the callers class loader, then falls back to the ClassLoader * associated with the ActorSystem class. + * + * @see The Typesafe Config Library API Documentation */ def create(name: String, config: Config): ActorSystem = apply(name, config) /** * Creates a new ActorSystem with the name "default", the specified Config, and specified ClassLoader + * + * @see The Typesafe Config Library API Documentation */ def create(name: String, config: Config, classLoader: ClassLoader): ActorSystem = apply(name, config, classLoader) @@ -89,11 +93,15 @@ object ActorSystem { * obtains the current ClassLoader by first inspecting the current threads' getContextClassLoader, * then tries to walk the stack to find the callers class loader, then falls back to the ClassLoader * associated with the ActorSystem class. + * + * @see The Typesafe Config Library API Documentation */ def apply(name: String, config: Config): ActorSystem = apply(name, config, findClassLoader()) /** * Creates a new ActorSystem with the name "default", the specified Config, and specified ClassLoader + * + * @see The Typesafe Config Library API Documentation */ def apply(name: String, config: Config, classLoader: ClassLoader): ActorSystem = new ActorSystemImpl(name, config, classLoader).start() @@ -101,11 +109,15 @@ object ActorSystem { * Settings are the overall ActorSystem Settings which also provides a convenient access to the Config object. * * For more detailed information about the different possible configuration options, look in the Akka Documentation under "Configuration" + * + * @see The Typesafe Config Library API Documentation */ class Settings(classLoader: ClassLoader, cfg: Config, final val name: String) { /** * The backing Config of this ActorSystem's Settings + * + * @see The Typesafe Config Library API Documentation */ final val config: Config = { val config = cfg.withFallback(ConfigFactory.defaultReference(classLoader)) diff --git a/project/AkkaBuild.scala b/project/AkkaBuild.scala index 340516aa03..3a670062ad 100644 --- a/project/AkkaBuild.scala +++ b/project/AkkaBuild.scala @@ -440,9 +440,7 @@ object AkkaBuild extends Build { object Dependencies { import Dependency._ - val actor = Seq( - config - ) + val actor = Seq(config) val testkit = Seq(Test.scalatest, Test.junit) From 3204e8a426e7216828d4388d71130675b2861b32 Mon Sep 17 00:00:00 2001 From: Gert Vanthienen Date: Sat, 30 Jun 2012 20:30:36 +0200 Subject: [PATCH 72/74] A few more code-style improvements and an easier API for the activator --- .../aries/blueprint/NamespaceHandler.scala | 10 +-- .../blueprint/NamespaceHandlerTest.scala | 78 ++++++++++--------- .../akka/osgi/ActorSystemActivator.scala | 25 +++--- .../akka/osgi/ActorSystemActivatorTest.scala | 70 ++++++++++++----- .../scala/akka/osgi/PojoSRTestSupport.scala | 56 +++++++------ .../scala/akka/osgi/test/TestActivators.scala | 39 ++++++++++ .../osgi/test/TestActorSystemActivator.scala | 18 ----- 7 files changed, 181 insertions(+), 115 deletions(-) create mode 100644 akka-osgi/src/test/scala/akka/osgi/test/TestActivators.scala delete mode 100644 akka-osgi/src/test/scala/akka/osgi/test/TestActorSystemActivator.scala diff --git a/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/NamespaceHandler.scala b/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/NamespaceHandler.scala index 2ab1306a0f..0570a027b6 100644 --- a/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/NamespaceHandler.scala +++ b/akka-osgi-aries/src/main/scala/akka/osgi/aries/blueprint/NamespaceHandler.scala @@ -52,7 +52,7 @@ class NamespaceHandler extends org.apache.aries.blueprint.NamespaceHandler { } def decorate(node: Node, component: ComponentMetadata, context: ParserContext) = - throw new ComponentDefinitionException("Bad xml syntax: node decoration is not supported"); + throw new ComponentDefinitionException("Bad xml syntax: node decoration is not supported") /* * Parse @@ -114,9 +114,9 @@ class NamespaceHandler extends org.apache.aries.blueprint.NamespaceHandler { */ def getId(context: ParserContext, element: Element) = { if (element.hasAttribute(ID_ATTRIBUTE)) { - element.getAttribute(ID_ATTRIBUTE); + element.getAttribute(ID_ATTRIBUTE) } else { - findAvailableId(context); + findAvailableId(context) } } @@ -134,8 +134,8 @@ class NamespaceHandler extends org.apache.aries.blueprint.NamespaceHandler { object NamespaceHandler { - private val ID_ATTRIBUTE = "id"; - private val NAME_ATTRIBUTE = "name"; + private val ID_ATTRIBUTE = "id" + private val NAME_ATTRIBUTE = "name" private val BUNDLE_CONTEXT_REFID = "blueprintBundleContext" diff --git a/akka-osgi-aries/src/test/scala/akka/osgi/aries/blueprint/NamespaceHandlerTest.scala b/akka-osgi-aries/src/test/scala/akka/osgi/aries/blueprint/NamespaceHandlerTest.scala index 44178978ec..4f4eb647e0 100644 --- a/akka-osgi-aries/src/test/scala/akka/osgi/aries/blueprint/NamespaceHandlerTest.scala +++ b/akka-osgi-aries/src/test/scala/akka/osgi/aries/blueprint/NamespaceHandlerTest.scala @@ -1,10 +1,11 @@ package akka.osgi.aries.blueprint -import org.scalatest.FlatSpec +import org.scalatest.WordSpec import akka.actor.ActorSystem import de.kalpatec.pojosr.framework.launch.BundleDescriptor import akka.osgi.PojoSRTestSupport import akka.osgi.PojoSRTestSupport.bundle +import org.scalatest.matchers.MustMatchers /** * Test cases for {@link ActorSystemActivator} @@ -24,70 +25,77 @@ object NamespaceHandlerTest { } -class SimpleNamespaceHandlerTest extends FlatSpec with PojoSRTestSupport { +class SimpleNamespaceHandlerTest extends WordSpec with MustMatchers with PojoSRTestSupport { import NamespaceHandlerTest._ - val testBundles: Seq[BundleDescriptor] = Seq( + val testBundles: Seq[BundleDescriptor] = buildTestBundles(Seq( AKKA_OSGI_BLUEPRINT, - bundle(TEST_BUNDLE_NAME).withBlueprintFile(getClass.getResource("simple.xml"))) + bundle(TEST_BUNDLE_NAME).withBlueprintFile(getClass.getResource("simple.xml")))) - "simple.xml" should "set up ActorSystem when bundle starts" in { - val system = serviceForType[ActorSystem] - assert(system != null) - } + "simple.xml" must { + "set up ActorSystem when bundle starts" in { + val system = serviceForType[ActorSystem] + assert(system != null) + } - it should "stop the ActorSystem when bundle stops" in { - val system = serviceForType[ActorSystem] - assert(!system.isTerminated) + "stop the ActorSystem when bundle stops" in { + val system = serviceForType[ActorSystem] + assert(!system.isTerminated) - bundleForName(TEST_BUNDLE_NAME).stop() + bundleForName(TEST_BUNDLE_NAME).stop() - system.awaitTermination() - assert(system.isTerminated) + system.awaitTermination() + assert(system.isTerminated) + } } } -class ConfigNamespaceHandlerTest extends FlatSpec with PojoSRTestSupport { +class ConfigNamespaceHandlerTest extends WordSpec with MustMatchers with PojoSRTestSupport { import NamespaceHandlerTest._ - val testBundles: Seq[BundleDescriptor] = Seq( + val testBundles: Seq[BundleDescriptor] = buildTestBundles(Seq( AKKA_OSGI_BLUEPRINT, - bundle(TEST_BUNDLE_NAME).withBlueprintFile(getClass.getResource("config.xml"))) + bundle(TEST_BUNDLE_NAME).withBlueprintFile(getClass.getResource("config.xml")))) - "config.xml" should "set up ActorSystem when bundle starts" in { - val system = serviceForType[ActorSystem] - assert(system != null) + "config.xml" must { + "set up ActorSystem when bundle starts" in { + val system = serviceForType[ActorSystem] + assert(system != null) - assert(system.settings.config.getString("some.config.key") == "value") - } + assert(system.settings.config.getString("some.config.key") == "value") + } - it should "stop the ActorSystem when bundle stops" in { - val system = serviceForType[ActorSystem] - assert(!system.isTerminated) + "stop the ActorSystem when bundle stops" in { + val system = serviceForType[ActorSystem] + assert(!system.isTerminated) - bundleForName(TEST_BUNDLE_NAME).stop() + bundleForName(TEST_BUNDLE_NAME).stop() - system.awaitTermination() - assert(system.isTerminated) + system.awaitTermination() + assert(system.isTerminated) + } } } -class DependencyInjectionNamespaceHandlerTest extends FlatSpec with PojoSRTestSupport { +class DependencyInjectionNamespaceHandlerTest extends WordSpec with MustMatchers with PojoSRTestSupport { import NamespaceHandlerTest._ - val testBundles: Seq[BundleDescriptor] = Seq( + val testBundles: Seq[BundleDescriptor] = buildTestBundles(Seq( AKKA_OSGI_BLUEPRINT, - bundle(TEST_BUNDLE_NAME).withBlueprintFile(getClass.getResource("injection.xml"))) + bundle(TEST_BUNDLE_NAME).withBlueprintFile(getClass.getResource("injection.xml")))) - "injection.xml" should "set up bean containing ActorSystem" in { - val bean = serviceForType[ActorSystemAwareBean] - assert(bean != null) - assert(bean.system != null) + "injection.xml" must { + + "set up bean containing ActorSystem" in { + val bean = serviceForType[ActorSystemAwareBean] + assert(bean != null) + assert(bean.system != null) + } } } diff --git a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala index e92415e1e1..e279247dbc 100644 --- a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala +++ b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala @@ -12,20 +12,8 @@ import org.osgi.framework.{ ServiceRegistration, BundleContext, BundleActivator * This convenience activator is mainly useful for setting up a single [[akka.actor.ActorSystem]] instance and sharing that * with other bundles in the OSGi Framework. If you want to set up multiple systems in the same bundle context, look at * the [[akka.osgi.OsgiActorSystemFactory]] instead. - * - * @param nameFor a function that allows you to determine the name of the [[akka.actor.ActorSystem]] at bundle startup time */ -abstract class ActorSystemActivator(nameFor: (BundleContext) ⇒ Option[String]) extends BundleActivator { - - /** - * No-args constructor - a default name (`bundle--ActorSystem`) will be assigned to the [[akka.actor.ActorSystem]] - */ - def this() = this(context ⇒ None) - - /** - * Create the activator, specifying the name of the [[akka.actor.ActorSystem]] to be created - */ - def this(name: String) = this(context ⇒ Some(name)) +abstract class ActorSystemActivator extends BundleActivator { private var system: Option[ActorSystem] = None private var registration: Option[ServiceRegistration] = None @@ -46,7 +34,7 @@ abstract class ActorSystemActivator(nameFor: (BundleContext) ⇒ Option[String]) * @param context the BundleContext */ def start(context: BundleContext): Unit = { - system = Some(OsgiActorSystemFactory(context).createActorSystem(nameFor(context))) + system = Some(OsgiActorSystemFactory(context).createActorSystem(Option(getActorSystemName(context)))) system foreach (configure(context, _)) } @@ -74,4 +62,13 @@ abstract class ActorSystemActivator(nameFor: (BundleContext) ⇒ Option[String]) properties.asInstanceOf[Dictionary[String, Any]])) } + /** + * By default, the [[akka.actor.ActorSystem]] name will be set to `bundle--ActorSystem`. Override this + * method to define another name for your [[akka.actor.ActorSystem]] instance. + * + * @param context the bundle context + * @return the actor system name + */ + def getActorSystemName(context: BundleContext): String = null + } diff --git a/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala b/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala index 0b2fdd19ac..37002975e4 100644 --- a/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala +++ b/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala @@ -1,44 +1,74 @@ package akka.osgi -import org.scalatest.FlatSpec +import org.scalatest.WordSpec import akka.actor.ActorSystem import akka.pattern.ask import akka.dispatch.Await import akka.util.duration._ import akka.util.Timeout import de.kalpatec.pojosr.framework.launch.BundleDescriptor -import test.TestActorSystemActivator +import test.{ RuntimeNameActorSystemActivator, TestActivators, PingPongActorSystemActivator } import test.PingPong._ import PojoSRTestSupport.bundle +import org.scalatest.matchers.MustMatchers /** - * Test cases for {@link ActorSystemActivator} + * Test cases for [[akka.osgi.ActorSystemActivator]] in 2 different scenarios: + * - no name configured for [[akka.actor.ActorSystem]] + * - runtime name configuration */ -class ActorSystemActivatorTest extends FlatSpec with PojoSRTestSupport { +object ActorSystemActivatorTest { val TEST_BUNDLE_NAME = "akka.osgi.test.activator" - val testBundles: Seq[BundleDescriptor] = Seq( - bundle(TEST_BUNDLE_NAME).withActivator(classOf[TestActorSystemActivator])) +} - "ActorSystemActivator" should "start and register the ActorSystem when bundle starts" in { - val system = serviceForType[ActorSystem] - val actor = system.actorFor("/user/pong") +class PingPongActorSystemActivatorTest extends WordSpec with MustMatchers with PojoSRTestSupport { - implicit val timeout = Timeout(5 seconds) - val future = actor ? Ping - val result = Await.result(future, timeout.duration) - assert(result != null) - } + import ActorSystemActivatorTest._ - it should "stop the ActorSystem when bundle stops" in { - val system = serviceForType[ActorSystem] - assert(!system.isTerminated) + val testBundles: Seq[BundleDescriptor] = buildTestBundles(Seq( + bundle(TEST_BUNDLE_NAME).withActivator(classOf[PingPongActorSystemActivator]))) - bundleForName(TEST_BUNDLE_NAME).stop() + "PingPongActorSystemActivator" must { - system.awaitTermination() - assert(system.isTerminated) + "start and register the ActorSystem when bundle starts" in { + val system = serviceForType[ActorSystem] + val actor = system.actorFor("/user/pong") + + implicit val timeout = Timeout(5 seconds) + val future = actor ? Ping + val result = Await.result(future, timeout.duration) + assert(result != null) + } + + "stop the ActorSystem when bundle stops" in { + val system = serviceForType[ActorSystem] + assert(!system.isTerminated) + + bundleForName(TEST_BUNDLE_NAME).stop() + + system.awaitTermination() + assert(system.isTerminated) + } } } + +class RuntimeNameActorSystemActivatorTest extends WordSpec with MustMatchers with PojoSRTestSupport { + + import ActorSystemActivatorTest._ + + val testBundles: Seq[BundleDescriptor] = buildTestBundles(Seq( + bundle(TEST_BUNDLE_NAME).withActivator(classOf[RuntimeNameActorSystemActivator]))) + + "RuntimeNameActorSystemActivator" must { + + "register an ActorSystem and add the bundle id to the system name" in { + val system = serviceForType[ActorSystem] + val bundle = bundleForName(TEST_BUNDLE_NAME) + system.name must equal(TestActivators.ACTOR_SYSTEM_NAME_PATTERN.format(bundle.getBundleId)) + } + } + +} \ No newline at end of file diff --git a/akka-osgi/src/test/scala/akka/osgi/PojoSRTestSupport.scala b/akka-osgi/src/test/scala/akka/osgi/PojoSRTestSupport.scala index cbed282c18..3ba9068907 100644 --- a/akka-osgi/src/test/scala/akka/osgi/PojoSRTestSupport.scala +++ b/akka-osgi/src/test/scala/akka/osgi/PojoSRTestSupport.scala @@ -19,10 +19,8 @@ import org.scalatest.{ BeforeAndAfterAll, Suite } */ trait PojoSRTestSupport extends Suite with BeforeAndAfterAll { - val MAX_WAIT_TIME = 8000; - val START_WAIT_TIME = 100; - - implicit def buildBundleDescriptor(builder: BundleDescriptorBuilder) = builder.build + val MAX_WAIT_TIME = 12800 + val START_WAIT_TIME = 50 /** * All bundles being found on the test classpath are automatically installed and started in the PojoSR runtime. @@ -31,12 +29,12 @@ trait PojoSRTestSupport extends Suite with BeforeAndAfterAll { val testBundles: Seq[BundleDescriptor] lazy val context: BundleContext = { - val config = new HashMap[String, AnyRef](); + val config = new HashMap[String, AnyRef]() System.setProperty("org.osgi.framework.storage", "target/akka-osgi/" + System.currentTimeMillis) val bundles = new ClasspathScanner().scanForBundles() bundles.addAll(testBundles) - config.put(PojoServiceRegistryFactory.BUNDLE_DESCRIPTORS, bundles); + config.put(PojoServiceRegistryFactory.BUNDLE_DESCRIPTORS, bundles) val loader: ServiceLoader[PojoServiceRegistryFactory] = ServiceLoader.load(classOf[PojoServiceRegistryFactory]) @@ -68,15 +66,15 @@ trait PojoSRTestSupport extends Suite with BeforeAndAfterAll { def awaitReference(serviceType: Class[_], wait: Long): ServiceReference = { val option = Option(context.getServiceReference(serviceType.getName)) + Thread.sleep(wait) option match { - case Some(reference) ⇒ reference; + case Some(reference) ⇒ reference case None if (wait > MAX_WAIT_TIME) ⇒ fail("Gave up waiting for service of type %s".format(serviceType)) - case None ⇒ { - Thread.sleep(wait); - awaitReference(serviceType, wait * 2); - } + case None ⇒ awaitReference(serviceType, wait * 2) } } + + protected def buildTestBundles(builders: Seq[BundleDescriptorBuilder]): Seq[BundleDescriptor] = builders map (_.build) } object PojoSRTestSupport { @@ -97,35 +95,47 @@ class BundleDescriptorBuilder(name: String) { val tinybundle = TinyBundles.bundle.set(Constants.BUNDLE_SYMBOLICNAME, name) - def withBlueprintFile(name: String, contents: URL) = + /** + * Add a Blueprint XML file to our test bundle + */ + def withBlueprintFile(name: String, contents: URL): BundleDescriptorBuilder = returnBuilder(tinybundle.add("OSGI-INF/blueprint/%s".format(name), contents)) + /** + * Add a Blueprint XML file to our test bundle + */ def withBlueprintFile(contents: URL): BundleDescriptorBuilder = withBlueprintFile(filename(contents), contents) - def withActivator(activator: Class[_ <: BundleActivator]) = + /** + * Add a Bundle activator to our test bundle + */ + def withActivator(activator: Class[_ <: BundleActivator]): BundleDescriptorBuilder = returnBuilder(tinybundle.set(Constants.BUNDLE_ACTIVATOR, activator.getName)) - def returnBuilder(block: ⇒ Unit) = { + private def returnBuilder(block: ⇒ Unit) = { block this } - def build = { + /** + * Build the actual PojoSR BundleDescriptor instance + */ + def build: BundleDescriptor = { val file: File = tinybundleToJarFile(name) new BundleDescriptor( getClass().getClassLoader(), new URL("jar:" + file.toURI().toString() + "!/"), - extractHeaders(file)); + extractHeaders(file)) } def extractHeaders(file: File): HashMap[String, String] = { - val headers = new HashMap[String, String](); + val headers = new HashMap[String, String]() - val jis = new JarInputStream(new FileInputStream(file)); + val jis = new JarInputStream(new FileInputStream(file)) try { for (entry ← jis.getManifest().getMainAttributes().entrySet()) { - headers.put(entry.getKey().toString(), entry.getValue().toString()); + headers.put(entry.getKey().toString(), entry.getValue().toString()) } } finally { jis.close() @@ -135,12 +145,12 @@ class BundleDescriptorBuilder(name: String) { } def tinybundleToJarFile(name: String): File = { - val file = new File("target/%s-%tQ.jar".format(name, new Date())); - val fos = new FileOutputStream(file); + val file = new File("target/%s-%tQ.jar".format(name, new Date())) + val fos = new FileOutputStream(file) try { - copy(tinybundle.build(), fos); + copy(tinybundle.build(), fos) } finally { - fos.close(); + fos.close() } file } diff --git a/akka-osgi/src/test/scala/akka/osgi/test/TestActivators.scala b/akka-osgi/src/test/scala/akka/osgi/test/TestActivators.scala new file mode 100644 index 0000000000..54369d88ca --- /dev/null +++ b/akka-osgi/src/test/scala/akka/osgi/test/TestActivators.scala @@ -0,0 +1,39 @@ +package akka.osgi.test + +import akka.osgi.ActorSystemActivator +import akka.actor.{ Props, ActorSystem } +import PingPong._ +import org.osgi.framework.BundleContext + +/** + * A set of [[akka.osgi.ActorSystemActivator]]s for testing purposes + */ +object TestActivators { + + val ACTOR_SYSTEM_NAME_PATTERN = "actor-system-for-bundle-%s" + +} + +/** + * Simple ActorSystemActivator that starts the sample ping-pong application + */ +class PingPongActorSystemActivator extends ActorSystemActivator { + + def configure(context: BundleContext, system: ActorSystem) { + system.actorOf(Props(new PongActor), name = "pong") + registerService(context, system) + } + +} + +/** + * [[akka.osgi.ActorSystemActivator]] implementation that determines [[akka.actor.ActorSystem]] name at runtime + */ +class RuntimeNameActorSystemActivator extends ActorSystemActivator { + + def configure(context: BundleContext, system: ActorSystem) = registerService(context, system); + + override def getActorSystemName(context: BundleContext) = + TestActivators.ACTOR_SYSTEM_NAME_PATTERN.format(context.getBundle.getBundleId) + +} \ No newline at end of file diff --git a/akka-osgi/src/test/scala/akka/osgi/test/TestActorSystemActivator.scala b/akka-osgi/src/test/scala/akka/osgi/test/TestActorSystemActivator.scala deleted file mode 100644 index 90305bc663..0000000000 --- a/akka-osgi/src/test/scala/akka/osgi/test/TestActorSystemActivator.scala +++ /dev/null @@ -1,18 +0,0 @@ -package akka.osgi.test - -import akka.osgi.ActorSystemActivator -import akka.actor.{ Props, ActorSystem } -import PingPong._ -import org.osgi.framework.BundleContext - -/** - * Sample ActorSystemActivator implementation used for testing purposes - */ -class TestActorSystemActivator extends ActorSystemActivator { - - def configure(context: BundleContext, system: ActorSystem) { - system.actorOf(Props(new PongActor), name = "pong") - registerService(context, system) - } - -} From b684eb1d2fb36dd0b7387081ab449ba02f478716 Mon Sep 17 00:00:00 2001 From: Roland Date: Sat, 30 Jun 2012 21:04:13 +0200 Subject: [PATCH 73/74] =?UTF-8?q?re-add=20=E2=80=9Crepl=E2=80=9D=20script?= =?UTF-8?q?=20for=20priming=20the=20REPL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- repl | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 repl diff --git a/repl b/repl new file mode 100644 index 0000000000..29f505e292 --- /dev/null +++ b/repl @@ -0,0 +1,16 @@ +import akka.actor._ +import akka.dispatch.{ Future, Promise } +import com.typesafe.config.ConfigFactory +import akka.testkit._ +val remoteConfig = try { + Class.forName("akka.remote.RemoteActorRefProvider") + "\nakka.actor.provider=akka.remote.RemoteActorRefProvider" + } catch { + case _: ClassNotFoundException => "" + } +val config=ConfigFactory.parseString("akka.daemonic=on" + remoteConfig) +val sys=ActorSystem("repl", config.withFallback(ConfigFactory.load())).asInstanceOf[ExtendedActorSystem] +implicit val ec=sys.dispatcher +import akka.util.duration._ +import akka.util.Timeout +implicit val timeout=Timeout(5 seconds) From 3797b72c45708a6a0c0168d27b791f264af5cbff Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Sun, 1 Jul 2012 13:31:11 +0200 Subject: [PATCH 74/74] Making sure that OSGi docs don't break the build and making sure that the osgi artifacts are bundled in the project --- .../additional/code/osgi/Activator.scala | 22 ++++--- .../blueprint/NamespaceHandlerTest.scala | 19 +++--- .../akka/osgi/ActorSystemActivatorTest.scala | 15 ++--- .../scala/akka/osgi/PojoSRTestSupport.scala | 61 +++++++------------ project/AkkaBuild.scala | 12 ++-- 5 files changed, 53 insertions(+), 76 deletions(-) diff --git a/akka-docs/additional/code/osgi/Activator.scala b/akka-docs/additional/code/osgi/Activator.scala index 34e83fcf77..4f432452c3 100644 --- a/akka-docs/additional/code/osgi/Activator.scala +++ b/akka-docs/additional/code/osgi/Activator.scala @@ -1,18 +1,24 @@ -import akka.actor.{ Props, ActorSystem } -import akka.osgi.ActorSystemActivator -import org.apache.servicemix.examples.akka.Listener -import org.apache.servicemix.examples.akka.Master +package docs.osgi + +case object SomeMessage + +class SomeActor extends akka.actor.Actor { + def receive = { case SomeMessage ⇒ } +} //#Activator -class Activator extends ActorSystemActivator("PiSystem") { +import akka.actor.{ Props, ActorSystem } +import org.osgi.framework.BundleContext +import akka.osgi.ActorSystemActivator + +class Activator extends ActorSystemActivator { def configure(context: BundleContext, system: ActorSystem) { // optionally register the ActorSystem in the OSGi Service Registry registerService(context, system) - val listener = system.actorOf(Props[Listener], name = "listener") - val master = system.actorOf(Props(new Master(4, 10000, 10000, listener)), name = "master") - master ! Calculate + val someActor = system.actorOf(Props[SomeActor], name = "someName") + someActor ! SomeMessage } } diff --git a/akka-osgi-aries/src/test/scala/akka/osgi/aries/blueprint/NamespaceHandlerTest.scala b/akka-osgi-aries/src/test/scala/akka/osgi/aries/blueprint/NamespaceHandlerTest.scala index 4f4eb647e0..3bc32c6141 100644 --- a/akka-osgi-aries/src/test/scala/akka/osgi/aries/blueprint/NamespaceHandlerTest.scala +++ b/akka-osgi-aries/src/test/scala/akka/osgi/aries/blueprint/NamespaceHandlerTest.scala @@ -35,18 +35,17 @@ class SimpleNamespaceHandlerTest extends WordSpec with MustMatchers with PojoSRT "simple.xml" must { "set up ActorSystem when bundle starts" in { - val system = serviceForType[ActorSystem] - assert(system != null) + serviceForType[ActorSystem] must not be (null) } "stop the ActorSystem when bundle stops" in { val system = serviceForType[ActorSystem] - assert(!system.isTerminated) + system.isTerminated must be(false) bundleForName(TEST_BUNDLE_NAME).stop() system.awaitTermination() - assert(system.isTerminated) + system.isTerminated must be(true) } } @@ -63,19 +62,19 @@ class ConfigNamespaceHandlerTest extends WordSpec with MustMatchers with PojoSRT "config.xml" must { "set up ActorSystem when bundle starts" in { val system = serviceForType[ActorSystem] - assert(system != null) + system must not be (null) - assert(system.settings.config.getString("some.config.key") == "value") + system.settings.config.getString("some.config.key") must be("value") } "stop the ActorSystem when bundle stops" in { val system = serviceForType[ActorSystem] - assert(!system.isTerminated) + system.isTerminated must be(false) bundleForName(TEST_BUNDLE_NAME).stop() system.awaitTermination() - assert(system.isTerminated) + system.isTerminated must be(true) } } @@ -93,8 +92,8 @@ class DependencyInjectionNamespaceHandlerTest extends WordSpec with MustMatchers "set up bean containing ActorSystem" in { val bean = serviceForType[ActorSystemAwareBean] - assert(bean != null) - assert(bean.system != null) + bean must not be (null) + bean.system must not be (null) } } diff --git a/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala b/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala index 37002975e4..6fa89886dd 100644 --- a/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala +++ b/akka-osgi/src/test/scala/akka/osgi/ActorSystemActivatorTest.scala @@ -37,19 +37,17 @@ class PingPongActorSystemActivatorTest extends WordSpec with MustMatchers with P val actor = system.actorFor("/user/pong") implicit val timeout = Timeout(5 seconds) - val future = actor ? Ping - val result = Await.result(future, timeout.duration) - assert(result != null) + Await.result(actor ? Ping, timeout.duration) must be(Pong) } "stop the ActorSystem when bundle stops" in { val system = serviceForType[ActorSystem] - assert(!system.isTerminated) + system.isTerminated must be(false) bundleForName(TEST_BUNDLE_NAME).stop() system.awaitTermination() - assert(system.isTerminated) + system.isTerminated must be(true) } } @@ -59,15 +57,12 @@ class RuntimeNameActorSystemActivatorTest extends WordSpec with MustMatchers wit import ActorSystemActivatorTest._ - val testBundles: Seq[BundleDescriptor] = buildTestBundles(Seq( - bundle(TEST_BUNDLE_NAME).withActivator(classOf[RuntimeNameActorSystemActivator]))) + val testBundles: Seq[BundleDescriptor] = buildTestBundles(Seq(bundle(TEST_BUNDLE_NAME).withActivator(classOf[RuntimeNameActorSystemActivator]))) "RuntimeNameActorSystemActivator" must { "register an ActorSystem and add the bundle id to the system name" in { - val system = serviceForType[ActorSystem] - val bundle = bundleForName(TEST_BUNDLE_NAME) - system.name must equal(TestActivators.ACTOR_SYSTEM_NAME_PATTERN.format(bundle.getBundleId)) + serviceForType[ActorSystem].name must equal(TestActivators.ACTOR_SYSTEM_NAME_PATTERN.format(bundleForName(TEST_BUNDLE_NAME).getBundleId)) } } diff --git a/akka-osgi/src/test/scala/akka/osgi/PojoSRTestSupport.scala b/akka-osgi/src/test/scala/akka/osgi/PojoSRTestSupport.scala index 3ba9068907..b19a90bf21 100644 --- a/akka-osgi/src/test/scala/akka/osgi/PojoSRTestSupport.scala +++ b/akka-osgi/src/test/scala/akka/osgi/PojoSRTestSupport.scala @@ -36,10 +36,7 @@ trait PojoSRTestSupport extends Suite with BeforeAndAfterAll { bundles.addAll(testBundles) config.put(PojoServiceRegistryFactory.BUNDLE_DESCRIPTORS, bundles) - val loader: ServiceLoader[PojoServiceRegistryFactory] = ServiceLoader.load(classOf[PojoServiceRegistryFactory]) - - val registry = loader.iterator.next.newPojoServiceRegistry(config) - registry.getBundleContext + ServiceLoader.load(classOf[PojoServiceRegistryFactory]).iterator.next.newPojoServiceRegistry(config).getBundleContext } // Ensure bundles get stopped at the end of the test to release resources and stop threads @@ -48,25 +45,21 @@ trait PojoSRTestSupport extends Suite with BeforeAndAfterAll { /** * Convenience method to find a bundle by symbolic name */ - def bundleForName(name: String) = context.getBundles.find(_.getSymbolicName == name) match { - case Some(bundle) ⇒ bundle - case None ⇒ fail("Unable to find bundle with symbolic name %s".format(name)) - } + def bundleForName(name: String) = + context.getBundles.find(_.getSymbolicName == name).getOrElse(fail("Unable to find bundle with symbolic name %s".format(name))) /** * Convenience method to find a service by interface. If the service is not already available in the OSGi Service * Registry, this method will wait for a few seconds for the service to appear. */ - def serviceForType[T](implicit manifest: Manifest[T]): T = { - val reference = awaitReference(manifest.erasure) - context.getService(reference).asInstanceOf[T] - } + def serviceForType[T](implicit manifest: Manifest[T]): T = + context.getService(awaitReference(manifest.erasure)).asInstanceOf[T] def awaitReference(serviceType: Class[_]): ServiceReference = awaitReference(serviceType, START_WAIT_TIME) def awaitReference(serviceType: Class[_], wait: Long): ServiceReference = { val option = Option(context.getServiceReference(serviceType.getName)) - Thread.sleep(wait) + Thread.sleep(wait) //FIXME No sleep please option match { case Some(reference) ⇒ reference case None if (wait > MAX_WAIT_TIME) ⇒ fail("Gave up waiting for service of type %s".format(serviceType)) @@ -78,12 +71,10 @@ trait PojoSRTestSupport extends Suite with BeforeAndAfterAll { } object PojoSRTestSupport { - /** * Convenience method to define additional test bundles */ def bundle(name: String) = new BundleDescriptorBuilder(name) - } /** @@ -98,22 +89,24 @@ class BundleDescriptorBuilder(name: String) { /** * Add a Blueprint XML file to our test bundle */ - def withBlueprintFile(name: String, contents: URL): BundleDescriptorBuilder = - returnBuilder(tinybundle.add("OSGI-INF/blueprint/%s".format(name), contents)) + def withBlueprintFile(name: String, contents: URL): BundleDescriptorBuilder = { + tinybundle.add("OSGI-INF/blueprint/%s".format(name), contents) + this + } /** * Add a Blueprint XML file to our test bundle */ - def withBlueprintFile(contents: URL): BundleDescriptorBuilder = withBlueprintFile(filename(contents), contents) + def withBlueprintFile(contents: URL): BundleDescriptorBuilder = { + val filename = contents.getFile.split("/").last + withBlueprintFile(filename, contents) + } /** * Add a Bundle activator to our test bundle */ - def withActivator(activator: Class[_ <: BundleActivator]): BundleDescriptorBuilder = - returnBuilder(tinybundle.set(Constants.BUNDLE_ACTIVATOR, activator.getName)) - - private def returnBuilder(block: ⇒ Unit) = { - block + def withActivator(activator: Class[_ <: BundleActivator]): BundleDescriptorBuilder = { + tinybundle.set(Constants.BUNDLE_ACTIVATOR, activator.getName) this } @@ -122,11 +115,7 @@ class BundleDescriptorBuilder(name: String) { */ def build: BundleDescriptor = { val file: File = tinybundleToJarFile(name) - - new BundleDescriptor( - getClass().getClassLoader(), - new URL("jar:" + file.toURI().toString() + "!/"), - extractHeaders(file)) + new BundleDescriptor(getClass().getClassLoader(), new URL("jar:" + file.toURI().toString() + "!/"), extractHeaders(file)) } def extractHeaders(file: File): HashMap[String, String] = { @@ -134,12 +123,9 @@ class BundleDescriptorBuilder(name: String) { val jis = new JarInputStream(new FileInputStream(file)) try { - for (entry ← jis.getManifest().getMainAttributes().entrySet()) { + for (entry ← jis.getManifest().getMainAttributes().entrySet()) headers.put(entry.getKey().toString(), entry.getValue().toString()) - } - } finally { - jis.close() - } + } finally jis.close() headers } @@ -147,14 +133,9 @@ class BundleDescriptorBuilder(name: String) { def tinybundleToJarFile(name: String): File = { val file = new File("target/%s-%tQ.jar".format(name, new Date())) val fos = new FileOutputStream(file) - try { - copy(tinybundle.build(), fos) - } finally { - fos.close() - } + try copy(tinybundle.build(), fos) finally fos.close() + file } - - private[this] def filename(url: URL) = url.getFile.split("/").last } diff --git a/project/AkkaBuild.scala b/project/AkkaBuild.scala index 30c1db8996..7b83364ed0 100644 --- a/project/AkkaBuild.scala +++ b/project/AkkaBuild.scala @@ -43,7 +43,7 @@ object AkkaBuild extends Build { sphinxLatex <<= sphinxLatex in LocalProject(docs.id), sphinxPdf <<= sphinxPdf in LocalProject(docs.id) ), - aggregate = Seq(actor, testkit, actorTests, remote, remoteTests, camel, cluster, slf4j, agent, transactor, mailboxes, zeroMQ, kernel, akkaSbtPlugin, samples, tutorials, docs) + aggregate = Seq(actor, testkit, actorTests, remote, remoteTests, camel, cluster, slf4j, agent, transactor, mailboxes, zeroMQ, kernel, akkaSbtPlugin, samples, tutorials, osgi, osgiAries, docs) ) lazy val actor = Project( @@ -299,7 +299,7 @@ object AkkaBuild extends Build { id = "akka-docs", base = file("akka-docs"), dependencies = Seq(actor, testkit % "test->test", mailboxesCommon % "compile;test->test", - remote, cluster, slf4j, agent, transactor, fileMailbox, zeroMQ, camel), + remote, cluster, slf4j, agent, transactor, fileMailbox, zeroMQ, camel, osgi, osgiAries), settings = defaultSettings ++ Sphinx.settings ++ Seq( unmanagedSourceDirectories in Test <<= baseDirectory { _ ** "code" get }, libraryDependencies ++= Dependencies.docs, @@ -560,13 +560,9 @@ object OSGi { val mailboxesCommon = exports(Seq("akka.actor.mailbox.*")) - val osgi = exports(Seq("akka.osgi")) ++ Seq( - OsgiKeys.privatePackage := Seq("akka.osgi.impl") - ) + val osgi = exports(Seq("akka.osgi")) ++ Seq(OsgiKeys.privatePackage := Seq("akka.osgi.impl")) - val osgiAries = exports() ++ Seq( - OsgiKeys.privatePackage := Seq("akka.osgi.aries.*") - ) + val osgiAries = exports() ++ Seq(OsgiKeys.privatePackage := Seq("akka.osgi.aries.*")) val remote = exports(Seq("akka.remote.*", "akka.routing.*", "akka.serialization.*"))