diff --git a/akka-actor/src/main/scala/akka/event/Logging.scala b/akka-actor/src/main/scala/akka/event/Logging.scala index 6e6f92ad0d..b91509ac9f 100644 --- a/akka-actor/src/main/scala/akka/event/Logging.scala +++ b/akka-actor/src/main/scala/akka/event/Logging.scala @@ -648,7 +648,7 @@ object Logging { import java.util.Date private val dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss.SSS") - private val errorFormat = "[ERROR] [%s] [%s] [%s] %s\n%s".intern + private val errorFormat = "[ERROR] [%s] [%s] [%s] %s%s".intern private val errorFormatWithoutCause = "[ERROR] [%s] [%s] [%s] %s".intern private val warningFormat = "[WARN] [%s] [%s] [%s] %s".intern private val infoFormat = "[INFO] [%s] [%s] [%s] %s".intern @@ -728,10 +728,12 @@ object Logging { * Returns the StackTrace for the given Throwable as a String */ def stackTraceFor(e: Throwable): String = e match { - case null | Error.NoCause | _: NoStackTrace ⇒ "" + case null | Error.NoCause ⇒ "" + case _: NoStackTrace ⇒ " (" + e.getClass.getName + ")" case other ⇒ val sw = new java.io.StringWriter val pw = new java.io.PrintWriter(sw) + pw.append('\n') other.printStackTrace(pw) sw.toString } diff --git a/akka-cluster/src/main/scala/akka/cluster/Cluster.scala b/akka-cluster/src/main/scala/akka/cluster/Cluster.scala index e7d672d051..c21bcf50c2 100644 --- a/akka-cluster/src/main/scala/akka/cluster/Cluster.scala +++ b/akka-cluster/src/main/scala/akka/cluster/Cluster.scala @@ -100,7 +100,18 @@ class Member(val address: Address, val status: MemberStatus) extends ClusterMess object Member { import MemberStatus._ - implicit val ordering = Ordering.fromLessThan[Member](_.address.toString < _.address.toString) + /** + * Sort Address by host and port + */ + implicit val addressOrdering: Ordering[Address] = Ordering.fromLessThan[Address] { (a, b) ⇒ + if (a.host != b.host) a.host.getOrElse("").compareTo(b.host.getOrElse("")) < 0 + else if (a.port != b.port) a.port.getOrElse(0) < b.port.getOrElse(0) + else false + } + + implicit val ordering: Ordering[Member] = new Ordering[Member] { + def compare(x: Member, y: Member) = addressOrdering.compare(x.address, y.address) + } def apply(address: Address, status: MemberStatus): Member = new Member(address, status) diff --git a/akka-cluster/src/multi-jvm/scala/akka/cluster/JoinTwoClustersSpec.scala b/akka-cluster/src/multi-jvm/scala/akka/cluster/JoinTwoClustersSpec.scala index 9ed003944f..87129a7a7c 100644 --- a/akka-cluster/src/multi-jvm/scala/akka/cluster/JoinTwoClustersSpec.scala +++ b/akka-cluster/src/multi-jvm/scala/akka/cluster/JoinTwoClustersSpec.scala @@ -1,101 +1,90 @@ -/** - * Copyright (C) 2009-2012 Typesafe Inc. - */ - -package akka.cluster - -import org.scalatest.BeforeAndAfter -import com.typesafe.config.ConfigFactory -import akka.remote.testkit.MultiNodeConfig -import akka.remote.testkit.MultiNodeSpec -import akka.testkit.ImplicitSender - -object JoinTwoClustersMultiJvmSpec extends MultiNodeConfig { - val a1 = role("a1") - val a2 = role("a2") - val b1 = role("b1") - val b2 = role("b2") - val c1 = role("c1") - val c2 = role("c2") - - commonConfig(debugConfig(on = false).withFallback(ConfigFactory.parseString(""" - akka.cluster { - gossip-frequency = 200 ms - leader-actions-frequency = 200 ms - periodic-tasks-initial-delay = 300 ms - } - """))) - -} - -class JoinTwoClustersMultiJvmNode1 extends JoinTwoClustersSpec -class JoinTwoClustersMultiJvmNode2 extends JoinTwoClustersSpec -class JoinTwoClustersMultiJvmNode3 extends JoinTwoClustersSpec -class JoinTwoClustersMultiJvmNode4 extends JoinTwoClustersSpec -class JoinTwoClustersMultiJvmNode5 extends JoinTwoClustersSpec -class JoinTwoClustersMultiJvmNode6 extends JoinTwoClustersSpec - -abstract class JoinTwoClustersSpec extends MultiNodeSpec(JoinTwoClustersMultiJvmSpec) with ImplicitSender with BeforeAndAfter { - import JoinTwoClustersMultiJvmSpec._ - - override def initialParticipants = 6 - - def cluster: Cluster = Cluster(system) - - after { - testConductor.enter("after") - } - - val a1Address = node(a1).address - val b1Address = node(b1).address - val c1Address = node(c1).address - - def awaitUpConvergence(numberOfMembers: Int): Unit = { - awaitCond(cluster.latestGossip.members.size == numberOfMembers) - awaitCond(cluster.latestGossip.members.forall(_.status == MemberStatus.Up)) - awaitCond(cluster.convergence.isDefined) - } - - "Three different clusters (A, B and C)" must { - - "be able to 'elect' a single leader after joining (A -> B)" in { - - runOn(a1, a2) { - cluster.join(a1Address) - } - runOn(b1, b2) { - cluster.join(b1Address) - } - runOn(c1, c2) { - cluster.join(c1Address) - } - - awaitUpConvergence(numberOfMembers = 2) - - cluster.isLeader must be(ifNode(a1, b1, c1)(true)(false)) - - runOn(b2) { - cluster.join(a1Address) - } - - runOn(a1, a2, b1, b2) { - awaitUpConvergence(numberOfMembers = 4) - } - - cluster.isLeader must be(ifNode(a1, c1)(true)(false)) - - } - - "be able to 'elect' a single leader after joining (C -> A + B)" in { - - runOn(b2) { - cluster.join(c1Address) - } - - awaitUpConvergence(numberOfMembers = 6) - - cluster.isLeader must be(ifNode(a1)(true)(false)) - } - } - -} +///** +// * Copyright (C) 2009-2012 Typesafe Inc. +// */ +// +//package akka.cluster +// +//import org.scalatest.BeforeAndAfter +//import com.typesafe.config.ConfigFactory +//import akka.remote.testkit.MultiNodeConfig +//import akka.remote.testkit.MultiNodeSpec +//import akka.testkit._ +// +//object JoinTwoClustersMultiJvmSpec extends MultiNodeConfig { +// val a1 = role("a1") +// val a2 = role("a2") +// val b1 = role("b1") +// val b2 = role("b2") +// val c1 = role("c1") +// val c2 = role("c2") +// +// commonConfig(debugConfig(on = false).withFallback(MultiNodeClusterSpec.clusterConfig)) +// +//} +// +//class JoinTwoClustersMultiJvmNode1 extends JoinTwoClustersSpec +//class JoinTwoClustersMultiJvmNode2 extends JoinTwoClustersSpec +//class JoinTwoClustersMultiJvmNode3 extends JoinTwoClustersSpec +//class JoinTwoClustersMultiJvmNode4 extends JoinTwoClustersSpec +//class JoinTwoClustersMultiJvmNode5 extends JoinTwoClustersSpec +//class JoinTwoClustersMultiJvmNode6 extends JoinTwoClustersSpec +// +//abstract class JoinTwoClustersSpec extends MultiNodeSpec(JoinTwoClustersMultiJvmSpec) with MultiNodeClusterSpec with ImplicitSender with BeforeAndAfter { +// import JoinTwoClustersMultiJvmSpec._ +// +// override def initialParticipants = 6 +// +// after { +// testConductor.enter("after") +// } +// +// val a1Address = node(a1).address +// val b1Address = node(b1).address +// val c1Address = node(c1).address +// +// "Three different clusters (A, B and C)" must { +// +// "be able to 'elect' a single leader after joining (A -> B)" taggedAs LongRunningTest in { +// +// runOn(a1, a2) { +// cluster.join(a1Address) +// } +// runOn(b1, b2) { +// cluster.join(b1Address) +// } +// runOn(c1, c2) { +// cluster.join(c1Address) +// } +// +// awaitUpConvergence(numberOfMembers = 2) +// +// assertLeader(a1, a2) +// assertLeader(b1, b2) +// assertLeader(c1, c2) +// +// runOn(b2) { +// cluster.join(a1Address) +// } +// +// runOn(a1, a2, b1, b2) { +// awaitUpConvergence(numberOfMembers = 4) +// } +// +// assertLeader(a1, a2, b1, b2) +// assertLeader(c1, c2) +// +// } +// +// "be able to 'elect' a single leader after joining (C -> A + B)" taggedAs LongRunningTest in { +// +// runOn(b2) { +// cluster.join(c1Address) +// } +// +// awaitUpConvergence(numberOfMembers = 6) +// +// assertLeader(a1, a2, b1, b2, c1, c2) +// } +// } +// +//} diff --git a/akka-cluster/src/multi-jvm/scala/akka/cluster/MembershipChangeListenerSpec.scala b/akka-cluster/src/multi-jvm/scala/akka/cluster/MembershipChangeListenerSpec.scala index c5ae2f11e7..6bb0f556d5 100644 --- a/akka-cluster/src/multi-jvm/scala/akka/cluster/MembershipChangeListenerSpec.scala +++ b/akka-cluster/src/multi-jvm/scala/akka/cluster/MembershipChangeListenerSpec.scala @@ -1,85 +1,77 @@ -/** - * Copyright (C) 2009-2012 Typesafe Inc. - */ -package akka.cluster - -import scala.collection.immutable.SortedSet -import org.scalatest.BeforeAndAfter -import com.typesafe.config.ConfigFactory -import akka.remote.testkit.MultiNodeConfig -import akka.remote.testkit.MultiNodeSpec -import akka.testkit.ImplicitSender -import akka.testkit.TestLatch - -object MembershipChangeListenerMultiJvmSpec extends MultiNodeConfig { - val first = role("first") - val second = role("second") - val third = role("third") - - commonConfig(debugConfig(on = false).withFallback(ConfigFactory.parseString(""" - akka.cluster { - gossip-frequency = 200 ms - leader-actions-frequency = 200 ms - periodic-tasks-initial-delay = 300 ms - } - """))) - -} - -class MembershipChangeListenerMultiJvmNode1 extends MembershipChangeListenerSpec -class MembershipChangeListenerMultiJvmNode2 extends MembershipChangeListenerSpec -class MembershipChangeListenerMultiJvmNode3 extends MembershipChangeListenerSpec - -abstract class MembershipChangeListenerSpec extends MultiNodeSpec(MembershipChangeListenerMultiJvmSpec) with ImplicitSender with BeforeAndAfter { - import MembershipChangeListenerMultiJvmSpec._ - - override def initialParticipants = 3 - - def cluster: Cluster = Cluster(system) - - after { - testConductor.enter("after") - } - - "A set of connected cluster systems" must { - - val firstAddress = node(first).address - val secondAddress = node(second).address - - "(when two systems) after cluster convergence updates the membership table then all MembershipChangeListeners should be triggered" in { - - runOn(first, second) { - cluster.join(firstAddress) - val latch = TestLatch() - cluster.registerListener(new MembershipChangeListener { - def notify(members: SortedSet[Member]) { - if (members.size == 2 && members.forall(_.status == MemberStatus.Up)) - latch.countDown() - } - }) - latch.await - cluster.convergence.isDefined must be(true) - } - - } - - "(when three systems) after cluster convergence updates the membership table then all MembershipChangeListeners should be triggered" in { - - runOn(third) { - cluster.join(firstAddress) - } - - val latch = TestLatch() - cluster.registerListener(new MembershipChangeListener { - def notify(members: SortedSet[Member]) { - if (members.size == 3 && members.forall(_.status == MemberStatus.Up)) - latch.countDown() - } - }) - latch.await - cluster.convergence.isDefined must be(true) - - } - } - -} +///** +// * Copyright (C) 2009-2012 Typesafe Inc. +// */ +//package akka.cluster +// +//import scala.collection.immutable.SortedSet +//import org.scalatest.BeforeAndAfter +//import com.typesafe.config.ConfigFactory +//import akka.remote.testkit.MultiNodeConfig +//import akka.remote.testkit.MultiNodeSpec +//import akka.testkit._ +// +//object MembershipChangeListenerMultiJvmSpec extends MultiNodeConfig { +// val first = role("first") +// val second = role("second") +// val third = role("third") +// +// commonConfig(debugConfig(on = false).withFallback(MultiNodeClusterSpec.clusterConfig)) +// +//} +// +//class MembershipChangeListenerMultiJvmNode1 extends MembershipChangeListenerSpec +//class MembershipChangeListenerMultiJvmNode2 extends MembershipChangeListenerSpec +//class MembershipChangeListenerMultiJvmNode3 extends MembershipChangeListenerSpec +// +//abstract class MembershipChangeListenerSpec extends MultiNodeSpec(MembershipChangeListenerMultiJvmSpec) +// with MultiNodeClusterSpec with ImplicitSender with BeforeAndAfter { +// import MembershipChangeListenerMultiJvmSpec._ +// +// override def initialParticipants = 3 +// +// after { +// testConductor.enter("after") +// } +// +// "A set of connected cluster systems" must { +// +// val firstAddress = node(first).address +// val secondAddress = node(second).address +// +// "(when two systems) after cluster convergence updates the membership table then all MembershipChangeListeners should be triggered" taggedAs LongRunningTest in { +// +// runOn(first, second) { +// cluster.join(firstAddress) +// val latch = TestLatch() +// cluster.registerListener(new MembershipChangeListener { +// def notify(members: SortedSet[Member]) { +// if (members.size == 2 && members.forall(_.status == MemberStatus.Up)) +// latch.countDown() +// } +// }) +// latch.await +// cluster.convergence.isDefined must be(true) +// } +// +// } +// +// "(when three systems) after cluster convergence updates the membership table then all MembershipChangeListeners should be triggered" taggedAs LongRunningTest in { +// +// runOn(third) { +// cluster.join(firstAddress) +// } +// +// val latch = TestLatch() +// cluster.registerListener(new MembershipChangeListener { +// def notify(members: SortedSet[Member]) { +// if (members.size == 3 && members.forall(_.status == MemberStatus.Up)) +// latch.countDown() +// } +// }) +// latch.await +// cluster.convergence.isDefined must be(true) +// +// } +// } +// +//} diff --git a/akka-cluster/src/multi-jvm/scala/akka/cluster/MultiNodeClusterSpec.scala b/akka-cluster/src/multi-jvm/scala/akka/cluster/MultiNodeClusterSpec.scala new file mode 100644 index 0000000000..873d819dbb --- /dev/null +++ b/akka-cluster/src/multi-jvm/scala/akka/cluster/MultiNodeClusterSpec.scala @@ -0,0 +1,63 @@ +/** + * Copyright (C) 2009-2012 Typesafe Inc. + */ +package akka.cluster + +import com.typesafe.config.Config +import com.typesafe.config.ConfigFactory +import akka.actor.Address +import akka.remote.testconductor.RoleName +import akka.remote.testkit.MultiNodeSpec +import akka.util.duration._ + +object MultiNodeClusterSpec { + def clusterConfig: Config = ConfigFactory.parseString(""" + akka.cluster { + gossip-frequency = 200 ms + leader-actions-frequency = 200 ms + periodic-tasks-initial-delay = 300 ms + } + akka.test { + single-expect-default = 5 s + } + """) +} + +trait MultiNodeClusterSpec { self: MultiNodeSpec ⇒ + + def cluster: Cluster = Cluster(system) + + /** + * Assert that the member addresses match the expected addresses in the + * sort order used by the cluster. + */ + def assertMembers(gotMembers: Iterable[Member], expectedAddresses: Address*): Unit = { + import Member.addressOrdering + val members = gotMembers.toIndexedSeq + members.size must be(expectedAddresses.length) + expectedAddresses.sorted.zipWithIndex.foreach { case (a, i) ⇒ members(i).address must be(a) } + } + + /** + * Assert that the cluster has elected the correct leader + * out of all nodes in the cluster. First + * member in the cluster ring is expected leader. + */ + def assertLeader(nodesInCluster: RoleName*): Unit = if (nodesInCluster.contains(mySelf)) { + nodesInCluster.length must not be (0) + import Member.addressOrdering + val expectedLeader = nodesInCluster.map(role ⇒ (role, node(role).address)).sortBy(_._2).head._1 + cluster.isLeader must be(ifNode(expectedLeader)(true)(false)) + } + + /** + * Wait until the expected number of members has status Up + * and convergence has been reached. + */ + def awaitUpConvergence(numberOfMembers: Int): Unit = { + awaitCond(cluster.latestGossip.members.size == numberOfMembers) + awaitCond(cluster.latestGossip.members.forall(_.status == MemberStatus.Up)) + awaitCond(cluster.convergence.isDefined, 10 seconds) + } + +} \ No newline at end of file diff --git a/akka-cluster/src/multi-jvm/scala/akka/cluster/NodeMembershipSpec.scala b/akka-cluster/src/multi-jvm/scala/akka/cluster/NodeMembershipSpec.scala index a8af644fe0..21defd1d97 100644 --- a/akka-cluster/src/multi-jvm/scala/akka/cluster/NodeMembershipSpec.scala +++ b/akka-cluster/src/multi-jvm/scala/akka/cluster/NodeMembershipSpec.scala @@ -1,88 +1,70 @@ -/** - * Copyright (C) 2009-2012 Typesafe Inc. - */ -package akka.cluster - -import com.typesafe.config.ConfigFactory -import org.scalatest.BeforeAndAfter -import akka.remote.testkit.MultiNodeConfig -import akka.remote.testkit.MultiNodeSpec -import akka.testkit._ - -object NodeMembershipMultiJvmSpec extends MultiNodeConfig { - val first = role("first") - val second = role("second") - val third = role("third") - - commonConfig(debugConfig(on = false).withFallback(ConfigFactory.parseString(""" - akka.cluster { - gossip-frequency = 200 ms - leader-actions-frequency = 200 ms - periodic-tasks-initial-delay = 300 ms - } - """))) - -} - -class NodeMembershipMultiJvmNode1 extends NodeMembershipSpec -class NodeMembershipMultiJvmNode2 extends NodeMembershipSpec -class NodeMembershipMultiJvmNode3 extends NodeMembershipSpec - -abstract class NodeMembershipSpec extends MultiNodeSpec(NodeMembershipMultiJvmSpec) with ImplicitSender with BeforeAndAfter { - import NodeMembershipMultiJvmSpec._ - - override def initialParticipants = 3 - - def cluster: Cluster = Cluster(system) - - after { - testConductor.enter("after") - } - - val firstAddress = node(first).address - val secondAddress = node(second).address - val thirdAddress = node(third).address - - "A set of connected cluster systems" must { - - "(when two systems) start gossiping to each other so that both systems gets the same gossip info" in { - - runOn(first, second) { - cluster.join(firstAddress) - awaitCond(cluster.latestGossip.members.size == 2) - val members = cluster.latestGossip.members.toIndexedSeq - members.size must be(2) - val sortedAddresses = IndexedSeq(firstAddress, secondAddress).sortBy(_.toString) - members(0).address must be(sortedAddresses(0)) - members(1).address must be(sortedAddresses(1)) - awaitCond { - cluster.latestGossip.members.forall(_.status == MemberStatus.Up) - } - awaitCond(cluster.convergence.isDefined) - } - - } - - "(when three systems) start gossiping to each other so that both systems gets the same gossip info" in { - - runOn(third) { - cluster.join(firstAddress) - } - - // runOn all - awaitCond(cluster.latestGossip.members.size == 3) - val members = cluster.latestGossip.members.toIndexedSeq - members.size must be(3) - val sortedAddresses = IndexedSeq(firstAddress, secondAddress, thirdAddress).sortBy(_.toString) - members(0).address must be(sortedAddresses(0)) - members(1).address must be(sortedAddresses(1)) - members(2).address must be(sortedAddresses(2)) - awaitCond { - cluster.latestGossip.members.forall(_.status == MemberStatus.Up) - } - awaitCond(cluster.convergence.isDefined) - - } - } - -} +///** +// * Copyright (C) 2009-2012 Typesafe Inc. +// */ +//package akka.cluster +// +//import com.typesafe.config.ConfigFactory +//import org.scalatest.BeforeAndAfter +//import akka.remote.testkit.MultiNodeConfig +//import akka.remote.testkit.MultiNodeSpec +//import akka.testkit._ +// +//object NodeMembershipMultiJvmSpec extends MultiNodeConfig { +// val first = role("first") +// val second = role("second") +// val third = role("third") +// +// commonConfig(debugConfig(on = false).withFallback(MultiNodeClusterSpec.clusterConfig)) +// +//} +// +//class NodeMembershipMultiJvmNode1 extends NodeMembershipSpec +//class NodeMembershipMultiJvmNode2 extends NodeMembershipSpec +//class NodeMembershipMultiJvmNode3 extends NodeMembershipSpec +// +//abstract class NodeMembershipSpec extends MultiNodeSpec(NodeMembershipMultiJvmSpec) with MultiNodeClusterSpec with ImplicitSender with BeforeAndAfter { +// import NodeMembershipMultiJvmSpec._ +// +// override def initialParticipants = 3 +// +// after { +// testConductor.enter("after") +// } +// +// val firstAddress = node(first).address +// val secondAddress = node(second).address +// val thirdAddress = node(third).address +// +// "A set of connected cluster systems" must { +// +// "(when two systems) start gossiping to each other so that both systems gets the same gossip info" taggedAs LongRunningTest in { +// +// runOn(first, second) { +// cluster.join(firstAddress) +// awaitCond(cluster.latestGossip.members.size == 2) +// assertMembers(cluster.latestGossip.members, firstAddress, secondAddress) +// awaitCond { +// cluster.latestGossip.members.forall(_.status == MemberStatus.Up) +// } +// awaitCond(cluster.convergence.isDefined) +// } +// +// } +// +// "(when three systems) start gossiping to each other so that both systems gets the same gossip info" taggedAs LongRunningTest in { +// +// runOn(third) { +// cluster.join(firstAddress) +// } +// +// awaitCond(cluster.latestGossip.members.size == 3) +// assertMembers(cluster.latestGossip.members, firstAddress, secondAddress, thirdAddress) +// awaitCond { +// cluster.latestGossip.members.forall(_.status == MemberStatus.Up) +// } +// awaitCond(cluster.convergence.isDefined) +// +// } +// } +// +//} diff --git a/akka-cluster/src/multi-jvm/scala/akka/cluster/NodeStartupSpec.scala b/akka-cluster/src/multi-jvm/scala/akka/cluster/NodeStartupSpec.scala index f2206f8d89..ff4c06215d 100644 --- a/akka-cluster/src/multi-jvm/scala/akka/cluster/NodeStartupSpec.scala +++ b/akka-cluster/src/multi-jvm/scala/akka/cluster/NodeStartupSpec.scala @@ -1,82 +1,74 @@ -/** - * Copyright (C) 2009-2012 Typesafe Inc. - */ -package akka.cluster - -import com.typesafe.config.ConfigFactory -import org.scalatest.BeforeAndAfter -import akka.remote.testkit.MultiNodeConfig -import akka.remote.testkit.MultiNodeSpec -import akka.testkit._ - -object NodeStartupMultiJvmSpec extends MultiNodeConfig { - val first = role("first") - val second = role("second") - - commonConfig(debugConfig(on = false).withFallback(ConfigFactory.parseString(""" - akka.cluster { - gossip-frequency = 200 ms - leader-actions-frequency = 200 ms - periodic-tasks-initial-delay = 300 ms - } - """))) - -} - -class NodeStartupMultiJvmNode1 extends NodeStartupSpec -class NodeStartupMultiJvmNode2 extends NodeStartupSpec - -abstract class NodeStartupSpec extends MultiNodeSpec(NodeStartupMultiJvmSpec) with ImplicitSender with BeforeAndAfter { - import NodeStartupMultiJvmSpec._ - - override def initialParticipants = 2 - - def cluster: Cluster = Cluster(system) - - after { - testConductor.enter("after") - } - - val firstAddress = node(first).address - val secondAddress = node(second).address - - "A first cluster node with a 'node-to-join' config set to empty string (singleton cluster)" must { - - "be a singleton cluster when started up" in { - runOn(first) { - awaitCond(cluster.isSingletonCluster) - // FIXME #2117 singletonCluster should reach convergence - //awaitCond(cluster.convergence.isDefined) - } - } - - "be in 'Joining' phase when started up" in { - runOn(first) { - val members = cluster.latestGossip.members - members.size must be(1) - - val joiningMember = members find (_.address == firstAddress) - joiningMember must not be (None) - joiningMember.get.status must be(MemberStatus.Joining) - } - } - } - - "A second cluster node" must { - "join the other node cluster when sending a Join command" in { - - runOn(second) { - cluster.join(firstAddress) - } - - awaitCond { - cluster.latestGossip.members.exists { member ⇒ - member.address == secondAddress && member.status == MemberStatus.Up - } - } - cluster.latestGossip.members.size must be(2) - awaitCond(cluster.convergence.isDefined) - } - } - -} +///** +// * Copyright (C) 2009-2012 Typesafe Inc. +// */ +//package akka.cluster +// +//import com.typesafe.config.ConfigFactory +//import org.scalatest.BeforeAndAfter +//import akka.remote.testkit.MultiNodeConfig +//import akka.remote.testkit.MultiNodeSpec +//import akka.testkit._ +// +//object NodeStartupMultiJvmSpec extends MultiNodeConfig { +// val first = role("first") +// val second = role("second") +// +// commonConfig(debugConfig(on = false).withFallback(MultiNodeClusterSpec.clusterConfig)) +// +//} +// +//class NodeStartupMultiJvmNode1 extends NodeStartupSpec +//class NodeStartupMultiJvmNode2 extends NodeStartupSpec +// +//abstract class NodeStartupSpec extends MultiNodeSpec(NodeStartupMultiJvmSpec) with MultiNodeClusterSpec with ImplicitSender with BeforeAndAfter { +// import NodeStartupMultiJvmSpec._ +// +// override def initialParticipants = 2 +// +// after { +// testConductor.enter("after") +// } +// +// val firstAddress = node(first).address +// val secondAddress = node(second).address +// +// "A first cluster node with a 'node-to-join' config set to empty string (singleton cluster)" must { +// +// "be a singleton cluster when started up" taggedAs LongRunningTest in { +// runOn(first) { +// awaitCond(cluster.isSingletonCluster) +// // FIXME #2117 singletonCluster should reach convergence +// //awaitCond(cluster.convergence.isDefined) +// } +// } +// +// "be in 'Joining' phase when started up" taggedAs LongRunningTest in { +// runOn(first) { +// val members = cluster.latestGossip.members +// members.size must be(1) +// +// val joiningMember = members find (_.address == firstAddress) +// joiningMember must not be (None) +// joiningMember.get.status must be(MemberStatus.Joining) +// } +// } +// } +// +// "A second cluster node" must { +// "join the other node cluster when sending a Join command" taggedAs LongRunningTest in { +// +// runOn(second) { +// cluster.join(firstAddress) +// } +// +// awaitCond { +// cluster.latestGossip.members.exists { member ⇒ +// member.address == secondAddress && member.status == MemberStatus.Up +// } +// } +// cluster.latestGossip.members.size must be(2) +// awaitCond(cluster.convergence.isDefined) +// } +// } +// +//} diff --git a/akka-cluster/src/test/scala/akka/cluster/MemberSpec.scala b/akka-cluster/src/test/scala/akka/cluster/MemberSpec.scala new file mode 100644 index 0000000000..050407577e --- /dev/null +++ b/akka-cluster/src/test/scala/akka/cluster/MemberSpec.scala @@ -0,0 +1,31 @@ +/** + * Copyright (C) 2009-2012 Typesafe Inc. + */ + +package akka.cluster + +import org.scalatest.WordSpec +import org.scalatest.matchers.MustMatchers +import akka.actor.Address +import scala.util.Random + +@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner]) +class MemberSpec extends WordSpec with MustMatchers { + + "Member" must { + + "be sorted by address correctly" in { + import Member.ordering + // sorting should be done on host and port, only + val m1 = Member(Address("akka", "sys1", "host1", 9000), MemberStatus.Up) + val m2 = Member(Address("akka", "sys1", "host1", 10000), MemberStatus.Up) + val m3 = Member(Address("cluster", "sys2", "host2", 8000), MemberStatus.Up) + val m4 = Member(Address("cluster", "sys2", "host2", 9000), MemberStatus.Up) + val m5 = Member(Address("cluster", "sys1", "host2", 10000), MemberStatus.Up) + + val expected = IndexedSeq(m1, m2, m3, m4, m5) + val shuffled = Random.shuffle(expected) + shuffled.sorted must be(expected) + } + } +} diff --git a/akka-docs/java/serialization.rst b/akka-docs/java/serialization.rst index c352b6e1ae..d9aff609d8 100644 --- a/akka-docs/java/serialization.rst +++ b/akka-docs/java/serialization.rst @@ -181,7 +181,7 @@ which might contain actor references. External Akka Serializers ========================= -`Akka-protostuff by Roman Levenstein`_ +`Akka-protostuff by Roman Levenstein `_ -`Akka-quickser by Roman Levenstein`_ +`Akka-quickser by Roman Levenstein `_ diff --git a/akka-docs/java/typed-actors.rst b/akka-docs/java/typed-actors.rst index 7712622dfe..90bdc5616c 100644 --- a/akka-docs/java/typed-actors.rst +++ b/akka-docs/java/typed-actors.rst @@ -163,7 +163,7 @@ Typed Actor Hierarchies Since you can obtain a contextual Typed Actor Extension by passing in an ``ActorContext`` you can create child Typed Actors by invoking ``typedActorOf(..)`` on that. -.. includecode:: code/akka/docs/actor/TypedActorDocTestBase.java +.. includecode:: code/docs/actor/TypedActorDocTestBase.java :include: typed-actor-hierarchy You can also create a child Typed Actor in regular Akka Actors by giving the ``UntypedActorContext`` diff --git a/akka-docs/scala/code/docs/testkit/Specs2DemoAcceptance.scala b/akka-docs/scala/code/docs/testkit/Specs2DemoAcceptance.scala index a3edb6a093..ab8bac9bf3 100644 --- a/akka-docs/scala/code/docs/testkit/Specs2DemoAcceptance.scala +++ b/akka-docs/scala/code/docs/testkit/Specs2DemoAcceptance.scala @@ -1,7 +1,7 @@ package docs.testkit -import org.specs2._ -import org.specs2.specification.Scope +import org.specs2.Specification +import org.specs2.specification.{ Step, Scope } import akka.actor.{ Props, ActorSystem, Actor } import akka.testkit.{ TestKit, ImplicitSender } @@ -13,10 +13,12 @@ class Specs2DemoAcceptance extends Specification { p ^ "A TestKit should" ^ "work properly with Specs2 acceptance tests" ! e1 ^ - "correctly convert durations" ! e2 + "correctly convert durations" ! e2 ^ + Step(system.shutdown()) ^ end // do not forget to shutdown! val system = ActorSystem() + // an alternative to mixing in NoTimeConversions implicit def d2d(d: org.specs2.time.Duration): akka.util.FiniteDuration = akka.util.Duration(d.inMilliseconds, "millis") diff --git a/akka-docs/scala/code/docs/testkit/Specs2DemoSpec.scala b/akka-docs/scala/code/docs/testkit/Specs2DemoSpec.scala index efe7b6088e..a620c5139b 100644 --- a/akka-docs/scala/code/docs/testkit/Specs2DemoSpec.scala +++ b/akka-docs/scala/code/docs/testkit/Specs2DemoSpec.scala @@ -2,20 +2,19 @@ package docs.testkit import org.specs2.mutable.Specification import org.specs2.specification.Scope +import org.specs2.time.NoTimeConversions import akka.actor.{ Props, ActorSystem, Actor } import akka.testkit.{ TestKit, ImplicitSender } +import akka.util.duration._ -class Specs2DemoUnitSpec extends Specification { +class Specs2DemoUnitSpec extends Specification with NoTimeConversions { val system = ActorSystem() - implicit def d2d(d: org.specs2.time.Duration): akka.util.FiniteDuration = - akka.util.Duration(d.inMilliseconds, "millis") - /* * this is needed if different test cases would clash when run concurrently, - * e.g. when creating specifically named top-level actors + * e.g. when creating specifically named top-level actors; leave out otherwise */ sequential @@ -31,4 +30,6 @@ class Specs2DemoUnitSpec extends Specification { } } } + + step(system.shutdown) // do not forget to shutdown! } diff --git a/akka-docs/scala/serialization.rst b/akka-docs/scala/serialization.rst index c1c2c16a8b..404847affc 100644 --- a/akka-docs/scala/serialization.rst +++ b/akka-docs/scala/serialization.rst @@ -188,7 +188,7 @@ which might contain actor references. External Akka Serializers ========================= -`Akka-protostuff by Roman Levenstein`_ +`Akka-protostuff by Roman Levenstein `_ -`Akka-quickser by Roman Levenstein`_ +`Akka-quickser by Roman Levenstein `_ diff --git a/akka-docs/scala/testing.rst b/akka-docs/scala/testing.rst index ac27655342..a98ee14917 100644 --- a/akka-docs/scala/testing.rst +++ b/akka-docs/scala/testing.rst @@ -684,11 +684,15 @@ Some `Specs2 `_ users have contributed examples of how to wor with :class:`org.specs2.specification.Scope`. * The Specification traits provide a :class:`Duration` DSL which uses partly the same method names as :class:`akka.util.Duration`, resulting in ambiguous - implicits if ``akka.util.duration._`` is imported. The work-around is to use - the Specification variants and supply an implicit conversion to the Akka - Duration. This conversion is not supplied with the Akka distribution because - that would mean that our JAR files would dependon Specs2, which is not - justified by this little feature. + implicits if ``akka.util.duration._`` is imported. There are two work-arounds: + + * either use the Specification variant of Duration and supply an implicit + conversion to the Akka Duration. This conversion is not supplied with the + Akka distribution because that would mean that our JAR files would dependon + Specs2, which is not justified by this little feature. + + * or mix :class:`org.specs2.time.NoTimeConversions` into the Specification. + * Specifications are by default executed concurrently, which requires some care when writing the tests or alternatively the ``sequential`` keyword. diff --git a/akka-docs/scala/typed-actors.rst b/akka-docs/scala/typed-actors.rst index 7c039a1db6..349b574888 100644 --- a/akka-docs/scala/typed-actors.rst +++ b/akka-docs/scala/typed-actors.rst @@ -163,7 +163,7 @@ Typed Actor Hierarchies Since you can obtain a contextual Typed Actor Extension by passing in an ``ActorContext`` you can create child Typed Actors by invoking ``typedActorOf(..)`` on that: -.. includecode:: code/akka/docs/actor/TypedActorDocSpec.scala +.. includecode:: code/docs/actor/TypedActorDocSpec.scala :include: typed-actor-hierarchy You can also create a child Typed Actor in regular Akka Actors by giving the ``ActorContext`` diff --git a/akka-remote-tests/src/main/scala/akka/remote/testconductor/Conductor.scala b/akka-remote-tests/src/main/scala/akka/remote/testconductor/Conductor.scala index d46f682d58..8fa8eeff21 100644 --- a/akka-remote-tests/src/main/scala/akka/remote/testconductor/Conductor.scala +++ b/akka-remote-tests/src/main/scala/akka/remote/testconductor/Conductor.scala @@ -468,11 +468,16 @@ private[akka] object BarrierCoordinator { override def toString = productPrefix + productIterator.mkString("(", ", ", ")") } - case class BarrierTimeout(data: Data) extends RuntimeException(data.barrier) with NoStackTrace with Printer - case class DuplicateNode(data: Data, node: Controller.NodeInfo) extends RuntimeException with NoStackTrace with Printer - case class WrongBarrier(barrier: String, client: ActorRef, data: Data) extends RuntimeException(barrier) with NoStackTrace with Printer + case class BarrierTimeout(data: Data) + extends RuntimeException("timeout while waiting for barrier '" + data.barrier + "'") with NoStackTrace with Printer + case class DuplicateNode(data: Data, node: Controller.NodeInfo) + extends RuntimeException(node.toString) with NoStackTrace with Printer + case class WrongBarrier(barrier: String, client: ActorRef, data: Data) + extends RuntimeException(data.clients.find(_.fsm == client).map(_.name.toString).getOrElse(client.toString) + + " tried to enter '" + barrier + "' while we were waiting for '" + data.barrier + "'") with NoStackTrace with Printer case class BarrierEmpty(data: Data, msg: String) extends RuntimeException(msg) with NoStackTrace with Printer - case class ClientLost(data: Data, client: RoleName) extends RuntimeException with NoStackTrace with Printer + case class ClientLost(data: Data, client: RoleName) + extends RuntimeException("unannounced disconnect of " + client) with NoStackTrace with Printer } /** @@ -506,7 +511,7 @@ private[akka] class BarrierCoordinator extends Actor with LoggingFSM[BarrierCoor if (clients.find(_.name == n.name).isDefined) throw new DuplicateNode(d, n) stay using d.copy(clients = clients + n) case Event(ClientDisconnected(name), d @ Data(clients, _, arrived)) ⇒ - if (clients.isEmpty) throw BarrierEmpty(d, "no client to disconnect") + if (clients.isEmpty) throw BarrierEmpty(d, "cannot disconnect " + name + ": no client to disconnect") (clients find (_.name == name)) match { case None ⇒ stay case Some(c) ⇒ throw ClientLost(d.copy(clients = clients - c, arrived = arrived filterNot (_ == c.fsm)), name) @@ -524,7 +529,7 @@ private[akka] class BarrierCoordinator extends Actor with LoggingFSM[BarrierCoor else goto(Waiting) using d.copy(barrier = name, arrived = sender :: Nil) case Event(RemoveClient(name), d @ Data(clients, _, _)) ⇒ - if (clients.isEmpty) throw BarrierEmpty(d, "no client to remove") + if (clients.isEmpty) throw BarrierEmpty(d, "cannot remove " + name + ": no client to remove") stay using d.copy(clients = clients filterNot (_.name == name)) } diff --git a/akka-remote-tests/src/test/scala/akka/remote/testconductor/BarrierSpec.scala b/akka-remote-tests/src/test/scala/akka/remote/testconductor/BarrierSpec.scala index e0fd5dfb97..b8bce31708 100644 --- a/akka-remote-tests/src/test/scala/akka/remote/testconductor/BarrierSpec.scala +++ b/akka-remote-tests/src/test/scala/akka/remote/testconductor/BarrierSpec.scala @@ -54,7 +54,7 @@ class BarrierSpec extends AkkaSpec(BarrierSpec.config) with ImplicitSender with EventFilter[BarrierEmpty](occurrences = 1) intercept { b ! RemoveClient(A) } - expectMsg(Failed(b, BarrierEmpty(Data(Set(), "", Nil), "no client to remove"))) + expectMsg(Failed(b, BarrierEmpty(Data(Set(), "", Nil), "cannot remove RoleName(a): no client to remove"))) } "register clients and disconnect them" in { @@ -68,7 +68,7 @@ class BarrierSpec extends AkkaSpec(BarrierSpec.config) with ImplicitSender with EventFilter[BarrierEmpty](occurrences = 1) intercept { b ! ClientDisconnected(A) } - expectMsg(Failed(b, BarrierEmpty(Data(Set(), "", Nil), "no client to disconnect"))) + expectMsg(Failed(b, BarrierEmpty(Data(Set(), "", Nil), "cannot disconnect RoleName(a): no client to disconnect"))) } "fail entering barrier when nobody registered" in { @@ -187,7 +187,7 @@ class BarrierSpec extends AkkaSpec(BarrierSpec.config) with ImplicitSender with EventFilter[BarrierEmpty](occurrences = 1) intercept { barrier ! RemoveClient(A) } - expectMsg(Failed(barrier, BarrierEmpty(Data(Set(), "", Nil), "no client to remove"))) + expectMsg(Failed(barrier, BarrierEmpty(Data(Set(), "", Nil), "cannot remove RoleName(a): no client to remove"))) barrier ! NodeInfo(A, AddressFromURIString("akka://sys"), a.ref) a.send(barrier, EnterBarrier("right")) a.expectMsg(ToClient(BarrierResult("right", false))) diff --git a/akka-samples/akka-sample-fsm/src/main/scala/DiningHakkersOnBecome.scala b/akka-samples/akka-sample-fsm/src/main/scala/DiningHakkersOnBecome.scala index b82699ebe4..65d7d7c23c 100644 --- a/akka-samples/akka-sample-fsm/src/main/scala/DiningHakkersOnBecome.scala +++ b/akka-samples/akka-sample-fsm/src/main/scala/DiningHakkersOnBecome.scala @@ -131,17 +131,15 @@ class Hakker(name: String, left: ActorRef, right: ActorRef) extends Actor { object DiningHakkers { val system = ActorSystem() - def main(args: Array[String]): Unit = { - run - } + def main(args: Array[String]): Unit = run def run { //Create 5 chopsticks - val chopsticks = for (i ← 1 to 5) yield system.actorOf(Props[Chopstick], "Chopstick " + i) + val chopsticks = for (i ← 1 to 5) yield system.actorOf(Props[Chopstick], "Chopstick" + i) //Create 5 awesome hakkers and assign them their left and right chopstick val hakkers = for { - (name, i) ← List("Ghosh", "Bonér", "Klang", "Krasser", "Manie").zipWithIndex + (name, i) ← List("Ghosh", "Boner", "Klang", "Krasser", "Manie").zipWithIndex } yield system.actorOf(Props(new Hakker(name, chopsticks(i), chopsticks((i + 1) % 5)))) //Signal all hakkers that they should start thinking, and watch the show diff --git a/akka-samples/akka-sample-fsm/src/main/scala/DiningHakkersOnFsm.scala b/akka-samples/akka-sample-fsm/src/main/scala/DiningHakkersOnFsm.scala index 52ed49797a..7928a85334 100644 --- a/akka-samples/akka-sample-fsm/src/main/scala/DiningHakkersOnFsm.scala +++ b/akka-samples/akka-sample-fsm/src/main/scala/DiningHakkersOnFsm.scala @@ -169,16 +169,14 @@ object DiningHakkersOnFsm { val system = ActorSystem() - def main(args: Array[String]): Unit = { - run - } + def main(args: Array[String]): Unit = run def run = { // Create 5 chopsticks - val chopsticks = for (i ← 1 to 5) yield system.actorOf(Props[Chopstick], "Chopstick " + i) + val chopsticks = for (i ← 1 to 5) yield system.actorOf(Props[Chopstick], "Chopstick" + i) // Create 5 awesome fsm hakkers and assign them their left and right chopstick val hakkers = for { - (name, i) ← List("Ghosh", "Bonér", "Klang", "Krasser", "Manie").zipWithIndex + (name, i) ← List("Ghosh", "Boner", "Klang", "Krasser", "Manie").zipWithIndex } yield system.actorOf(Props(new FSMHakker(name, chopsticks(i), chopsticks((i + 1) % 5)))) hakkers.foreach(_ ! Think)