From 41a08237d9bf160bcb9a7f8670c80c11668ddab4 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Fri, 20 May 2011 19:40:11 +0200 Subject: [PATCH 01/20] Moved secure cookie exchange to on connect established, this means I could remove the synchronization on send, enabling muuuch more throughput, also, since the cookie isn`t sent in each message, message size should drop considerably when secure cookie handshakes are enabled. I do however have no way of testing this since it seems like the clustering stuff is totally not working when it comes to the RemoteSupport --- .../akka/remote/protocol/RemoteProtocol.java | 124 ++++++------------ .../src/main/protocol/RemoteProtocol.proto | 4 +- .../remote/netty/NettyRemoteSupport.scala | 94 +++++++------ .../serialization/SerializationProtocol.scala | 10 +- 4 files changed, 100 insertions(+), 132 deletions(-) diff --git a/akka-remote/src/main/java/akka/remote/protocol/RemoteProtocol.java b/akka-remote/src/main/java/akka/remote/protocol/RemoteProtocol.java index 4d2bdfdce1..f83430b478 100644 --- a/akka-remote/src/main/java/akka/remote/protocol/RemoteProtocol.java +++ b/akka-remote/src/main/java/akka/remote/protocol/RemoteProtocol.java @@ -10,7 +10,8 @@ public final class RemoteProtocol { } public enum CommandType implements com.google.protobuf.ProtocolMessageEnum { - SHUTDOWN(0, 1), + CONNECT(0, 1), + SHUTDOWN(1, 2), ; @@ -18,7 +19,8 @@ public final class RemoteProtocol { public static CommandType valueOf(int value) { switch (value) { - case 1: return SHUTDOWN; + case 1: return CONNECT; + case 2: return SHUTDOWN; default: return null; } } @@ -49,7 +51,7 @@ public final class RemoteProtocol { } private static final CommandType[] VALUES = { - SHUTDOWN, + CONNECT, SHUTDOWN, }; public static CommandType valueOf( com.google.protobuf.Descriptors.EnumValueDescriptor desc) { @@ -680,13 +682,6 @@ public final class RemoteProtocol { return metadata_.get(index); } - // optional string cookie = 9; - public static final int COOKIE_FIELD_NUMBER = 9; - private boolean hasCookie; - private java.lang.String cookie_ = ""; - public boolean hasCookie() { return hasCookie; } - public java.lang.String getCookie() { return cookie_; } - private void initFields() { uuid_ = akka.remote.protocol.RemoteProtocol.UuidProtocol.getDefaultInstance(); actorInfo_ = akka.remote.protocol.RemoteProtocol.ActorInfoProtocol.getDefaultInstance(); @@ -746,9 +741,6 @@ public final class RemoteProtocol { for (akka.remote.protocol.RemoteProtocol.MetadataEntryProtocol element : getMetadataList()) { output.writeMessage(8, element); } - if (hasCookie()) { - output.writeString(9, getCookie()); - } getUnknownFields().writeTo(output); } @@ -790,10 +782,6 @@ public final class RemoteProtocol { size += com.google.protobuf.CodedOutputStream .computeMessageSize(8, element); } - if (hasCookie()) { - size += com.google.protobuf.CodedOutputStream - .computeStringSize(9, getCookie()); - } size += getUnknownFields().getSerializedSize(); memoizedSerializedSize = size; return size; @@ -983,9 +971,6 @@ public final class RemoteProtocol { } result.metadata_.addAll(other.metadata_); } - if (other.hasCookie()) { - setCookie(other.getCookie()); - } this.mergeUnknownFields(other.getUnknownFields()); return this; } @@ -1075,10 +1060,6 @@ public final class RemoteProtocol { addMetadata(subBuilder.buildPartial()); break; } - case 74: { - setCookie(input.readString()); - break; - } } } } @@ -1375,27 +1356,6 @@ public final class RemoteProtocol { return this; } - // optional string cookie = 9; - public boolean hasCookie() { - return result.hasCookie(); - } - public java.lang.String getCookie() { - return result.getCookie(); - } - public Builder setCookie(java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - result.hasCookie = true; - result.cookie_ = value; - return this; - } - public Builder clearCookie() { - result.hasCookie = false; - result.cookie_ = getDefaultInstance().getCookie(); - return this; - } - // @@protoc_insertion_point(builder_scope:RemoteMessageProtocol) } @@ -1450,7 +1410,7 @@ public final class RemoteProtocol { public akka.remote.protocol.RemoteProtocol.CommandType getCommandType() { return commandType_; } private void initFields() { - commandType_ = akka.remote.protocol.RemoteProtocol.CommandType.SHUTDOWN; + commandType_ = akka.remote.protocol.RemoteProtocol.CommandType.CONNECT; } public final boolean isInitialized() { if (!hasCommandType) return false; @@ -1729,7 +1689,7 @@ public final class RemoteProtocol { } public Builder clearCommandType() { result.hasCommandType = false; - result.commandType_ = akka.remote.protocol.RemoteProtocol.CommandType.SHUTDOWN; + result.commandType_ = akka.remote.protocol.RemoteProtocol.CommandType.CONNECT; return this; } @@ -5710,46 +5670,46 @@ public final class RemoteProtocol { "\n\024RemoteProtocol.proto\"j\n\022AkkaRemoteProt" + "ocol\022\'\n\007message\030\001 \001(\0132\026.RemoteMessagePro" + "tocol\022+\n\013instruction\030\002 \001(\0132\026.RemoteContr" + - "olProtocol\"\277\002\n\025RemoteMessageProtocol\022\033\n\004" + + "olProtocol\"\257\002\n\025RemoteMessageProtocol\022\033\n\004" + "uuid\030\001 \002(\0132\r.UuidProtocol\022%\n\tactorInfo\030\002" + " \002(\0132\022.ActorInfoProtocol\022\016\n\006oneWay\030\003 \002(\010" + "\022!\n\007message\030\004 \001(\0132\020.MessageProtocol\022%\n\te" + "xception\030\005 \001(\0132\022.ExceptionProtocol\022%\n\016su" + "pervisorUuid\030\006 \001(\0132\r.UuidProtocol\022\'\n\006sen" + "der\030\007 \001(\0132\027.RemoteActorRefProtocol\022(\n\010me", - "tadata\030\010 \003(\0132\026.MetadataEntryProtocol\022\016\n\006" + - "cookie\030\t \001(\t\"J\n\025RemoteControlProtocol\022\016\n" + - "\006cookie\030\001 \001(\t\022!\n\013commandType\030\002 \002(\0162\014.Com" + - "mandType\":\n\026RemoteActorRefProtocol\022\017\n\007ad" + - "dress\030\001 \002(\t\022\017\n\007timeout\030\002 \001(\004\"\323\002\n\032Seriali" + - "zedActorRefProtocol\022\033\n\004uuid\030\001 \002(\0132\r.Uuid" + - "Protocol\022\017\n\007address\030\002 \002(\t\022\026\n\016actorClassn" + - "ame\030\003 \002(\t\022\025\n\ractorInstance\030\004 \001(\014\022\033\n\023seri" + - "alizerClassname\030\005 \001(\t\022\017\n\007timeout\030\006 \001(\004\022\026" + - "\n\016receiveTimeout\030\007 \001(\004\022%\n\tlifeCycle\030\010 \001(", - "\0132\022.LifeCycleProtocol\022+\n\nsupervisor\030\t \001(" + - "\0132\027.RemoteActorRefProtocol\022\024\n\014hotswapSta" + - "ck\030\n \001(\014\022(\n\010messages\030\013 \003(\0132\026.RemoteMessa" + - "geProtocol\"g\n\037SerializedTypedActorRefPro" + - "tocol\022-\n\010actorRef\030\001 \002(\0132\033.SerializedActo" + - "rRefProtocol\022\025\n\rinterfaceName\030\002 \002(\t\"r\n\017M" + - "essageProtocol\0225\n\023serializationScheme\030\001 " + - "\002(\0162\030.SerializationSchemeType\022\017\n\007message" + - "\030\002 \002(\014\022\027\n\017messageManifest\030\003 \001(\014\"R\n\021Actor" + - "InfoProtocol\022\033\n\004uuid\030\001 \002(\0132\r.UuidProtoco", - "l\022\017\n\007timeout\030\002 \002(\004\022\017\n\007address\030\003 \001(\t\")\n\014U" + - "uidProtocol\022\014\n\004high\030\001 \002(\004\022\013\n\003low\030\002 \002(\004\"3" + - "\n\025MetadataEntryProtocol\022\013\n\003key\030\001 \002(\t\022\r\n\005" + - "value\030\002 \002(\014\"6\n\021LifeCycleProtocol\022!\n\tlife" + - "Cycle\030\001 \002(\0162\016.LifeCycleType\"1\n\017AddressPr" + - "otocol\022\020\n\010hostname\030\001 \002(\t\022\014\n\004port\030\002 \002(\r\"7" + - "\n\021ExceptionProtocol\022\021\n\tclassname\030\001 \002(\t\022\017" + - "\n\007message\030\002 \002(\t*\033\n\013CommandType\022\014\n\010SHUTDO" + - "WN\020\001*]\n\027SerializationSchemeType\022\010\n\004JAVA\020" + - "\001\022\013\n\007SBINARY\020\002\022\016\n\nSCALA_JSON\020\003\022\r\n\tJAVA_J", - "SON\020\004\022\014\n\010PROTOBUF\020\005*-\n\rLifeCycleType\022\r\n\t" + - "PERMANENT\020\001\022\r\n\tTEMPORARY\020\002B\030\n\024akka.remot" + - "e.protocolH\001" + "tadata\030\010 \003(\0132\026.MetadataEntryProtocol\"J\n\025" + + "RemoteControlProtocol\022\016\n\006cookie\030\001 \001(\t\022!\n" + + "\013commandType\030\002 \002(\0162\014.CommandType\":\n\026Remo" + + "teActorRefProtocol\022\017\n\007address\030\001 \002(\t\022\017\n\007t" + + "imeout\030\002 \001(\004\"\323\002\n\032SerializedActorRefProto" + + "col\022\033\n\004uuid\030\001 \002(\0132\r.UuidProtocol\022\017\n\007addr" + + "ess\030\002 \002(\t\022\026\n\016actorClassname\030\003 \002(\t\022\025\n\ract" + + "orInstance\030\004 \001(\014\022\033\n\023serializerClassname\030" + + "\005 \001(\t\022\017\n\007timeout\030\006 \001(\004\022\026\n\016receiveTimeout" + + "\030\007 \001(\004\022%\n\tlifeCycle\030\010 \001(\0132\022.LifeCyclePro", + "tocol\022+\n\nsupervisor\030\t \001(\0132\027.RemoteActorR" + + "efProtocol\022\024\n\014hotswapStack\030\n \001(\014\022(\n\010mess" + + "ages\030\013 \003(\0132\026.RemoteMessageProtocol\"g\n\037Se" + + "rializedTypedActorRefProtocol\022-\n\010actorRe" + + "f\030\001 \002(\0132\033.SerializedActorRefProtocol\022\025\n\r" + + "interfaceName\030\002 \002(\t\"r\n\017MessageProtocol\0225" + + "\n\023serializationScheme\030\001 \002(\0162\030.Serializat" + + "ionSchemeType\022\017\n\007message\030\002 \002(\014\022\027\n\017messag" + + "eManifest\030\003 \001(\014\"R\n\021ActorInfoProtocol\022\033\n\004" + + "uuid\030\001 \002(\0132\r.UuidProtocol\022\017\n\007timeout\030\002 \002", + "(\004\022\017\n\007address\030\003 \001(\t\")\n\014UuidProtocol\022\014\n\004h" + + "igh\030\001 \002(\004\022\013\n\003low\030\002 \002(\004\"3\n\025MetadataEntryP" + + "rotocol\022\013\n\003key\030\001 \002(\t\022\r\n\005value\030\002 \002(\014\"6\n\021L" + + "ifeCycleProtocol\022!\n\tlifeCycle\030\001 \002(\0162\016.Li" + + "feCycleType\"1\n\017AddressProtocol\022\020\n\010hostna" + + "me\030\001 \002(\t\022\014\n\004port\030\002 \002(\r\"7\n\021ExceptionProto" + + "col\022\021\n\tclassname\030\001 \002(\t\022\017\n\007message\030\002 \002(\t*" + + "(\n\013CommandType\022\013\n\007CONNECT\020\001\022\014\n\010SHUTDOWN\020" + + "\002*]\n\027SerializationSchemeType\022\010\n\004JAVA\020\001\022\013" + + "\n\007SBINARY\020\002\022\016\n\nSCALA_JSON\020\003\022\r\n\tJAVA_JSON", + "\020\004\022\014\n\010PROTOBUF\020\005*-\n\rLifeCycleType\022\r\n\tPER" + + "MANENT\020\001\022\r\n\tTEMPORARY\020\002B\030\n\024akka.remote.p" + + "rotocolH\001" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { @@ -5769,7 +5729,7 @@ public final class RemoteProtocol { internal_static_RemoteMessageProtocol_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_RemoteMessageProtocol_descriptor, - new java.lang.String[] { "Uuid", "ActorInfo", "OneWay", "Message", "Exception", "SupervisorUuid", "Sender", "Metadata", "Cookie", }, + new java.lang.String[] { "Uuid", "ActorInfo", "OneWay", "Message", "Exception", "SupervisorUuid", "Sender", "Metadata", }, akka.remote.protocol.RemoteProtocol.RemoteMessageProtocol.class, akka.remote.protocol.RemoteProtocol.RemoteMessageProtocol.Builder.class); internal_static_RemoteControlProtocol_descriptor = diff --git a/akka-remote/src/main/protocol/RemoteProtocol.proto b/akka-remote/src/main/protocol/RemoteProtocol.proto index 37dce1914e..3b51a6e6d8 100644 --- a/akka-remote/src/main/protocol/RemoteProtocol.proto +++ b/akka-remote/src/main/protocol/RemoteProtocol.proto @@ -28,7 +28,6 @@ message RemoteMessageProtocol { optional UuidProtocol supervisorUuid = 6; optional RemoteActorRefProtocol sender = 7; repeated MetadataEntryProtocol metadata = 8; - optional string cookie = 9; } /** @@ -43,7 +42,8 @@ message RemoteControlProtocol { * Defines the type of the RemoteControlProtocol command type */ enum CommandType { - SHUTDOWN = 1; + CONNECT = 1; + SHUTDOWN = 2; } /** diff --git a/akka-remote/src/main/scala/akka/remote/netty/NettyRemoteSupport.scala b/akka-remote/src/main/scala/akka/remote/netty/NettyRemoteSupport.scala index 01a80fbc9b..de04a44a0f 100644 --- a/akka-remote/src/main/scala/akka/remote/netty/NettyRemoteSupport.scala +++ b/akka-remote/src/main/scala/akka/remote/netty/NettyRemoteSupport.scala @@ -161,7 +161,6 @@ abstract class RemoteClient private[akka] ( } private[remote] val runSwitch = new Switch() - private[remote] val isAuthenticated = new AtomicBoolean(false) private[remote] def isRunning = runSwitch.isOn @@ -196,18 +195,10 @@ abstract class RemoteClient private[akka] ( remoteAddress: InetSocketAddress, timeout: Long, isOneWay: Boolean, - actorRef: ActorRef): Option[CompletableFuture[T]] = synchronized { // FIXME: find better strategy to prevent race - + actorRef: ActorRef): Option[CompletableFuture[T]] = send(createRemoteMessageProtocolBuilder( - Some(actorRef), - Left(actorRef.uuid), - actorRef.address, - timeout, - Right(message), - isOneWay, - senderOption, - if (isAuthenticated.compareAndSet(false, true)) RemoteClientSettings.SECURE_COOKIE else None).build, senderFuture) - } + Some(actorRef), Left(actorRef.uuid), actorRef.address, timeout, Right(message), isOneWay, senderOption).build, + senderFuture) /** * Sends the message across the wire @@ -342,6 +333,14 @@ class ActiveRemoteClient private[akka] ( notifyListeners(RemoteClientError(connection.getCause, module, remoteAddress)) false } else { + + //Send cookie + val handshake = RemoteControlProtocol.newBuilder.setCommandType(CommandType.CONNECT) + if (SECURE_COOKIE.nonEmpty) + handshake.setCookie(SECURE_COOKIE.get) + + connection.getChannel.write(RemoteEncoder.encode(handshake.build)) + //Add a task that does GCing of expired Futures timer.newTimeout(new TimerTask() { def run(timeout: Timeout) = { @@ -361,7 +360,6 @@ class ActiveRemoteClient private[akka] ( } match { case true ⇒ true case false if reconnectIfAlreadyConnected ⇒ - isAuthenticated.set(false) openChannels.remove(connection.getChannel) connection.getChannel.close connection = bootstrap.connect(remoteAddress) @@ -369,7 +367,15 @@ class ActiveRemoteClient private[akka] ( if (!connection.isSuccess) { notifyListeners(RemoteClientError(connection.getCause, module, remoteAddress)) false - } else true + } else { + //Send cookie + val handshake = RemoteControlProtocol.newBuilder.setCommandType(CommandType.CONNECT) + if (SECURE_COOKIE.nonEmpty) + handshake.setCookie(SECURE_COOKIE.get) + + connection.getChannel.write(RemoteEncoder.encode(handshake.build)) + true + } case false ⇒ false } } @@ -577,10 +583,10 @@ class NettyRemoteServer(serverModule: NettyRemoteServerModule, val host: String, def shutdown() { try { val shutdownSignal = { - val b = RemoteControlProtocol.newBuilder + val b = RemoteControlProtocol.newBuilder.setCommandType(CommandType.SHUTDOWN) if (RemoteClientSettings.SECURE_COOKIE.nonEmpty) b.setCookie(RemoteClientSettings.SECURE_COOKIE.get) - b.setCommandType(CommandType.SHUTDOWN) + b.build } openChannels.write(RemoteEncoder.encode(shutdownSignal)).awaitUninterruptibly @@ -736,12 +742,39 @@ class RemoteServerPipelineFactory( MAX_TOTAL_MEMORY_SIZE, EXECUTION_POOL_KEEPALIVE.length, EXECUTION_POOL_KEEPALIVE.unit)) + val authenticator = if (REQUIRE_COOKIE) new RemoteServerAuthenticationHandler(SECURE_COOKIE) :: Nil else Nil val remoteServer = new RemoteServerHandler(name, openChannels, loader, server) - val stages: List[ChannelHandler] = dec ::: lenDec :: protobufDec :: enc ::: lenPrep :: protobufEnc :: execution :: remoteServer :: Nil + val stages: List[ChannelHandler] = dec ::: lenDec :: protobufDec :: enc ::: lenPrep :: protobufEnc :: execution :: authenticator ::: remoteServer :: Nil new StaticChannelPipeline(stages: _*) } } +@ChannelHandler.Sharable +class RemoteServerAuthenticationHandler(secureCookie: Option[String]) extends SimpleChannelUpstreamHandler { + val authenticated = new AnyRef + + override def messageReceived(ctx: ChannelHandlerContext, event: MessageEvent) = secureCookie match { + case None ⇒ ctx.sendUpstream(event) + case Some(cookie) ⇒ + ctx.getAttachment match { + case `authenticated` ⇒ ctx.sendUpstream(event) + case null ⇒ event.getMessage match { + case remoteProtocol: AkkaRemoteProtocol if remoteProtocol.hasInstruction ⇒ + remoteProtocol.getInstruction.getCookie match { + case `cookie` ⇒ + ctx.setAttachment(authenticated) + ctx.sendUpstream(event) + case _ ⇒ + throw new SecurityException( + "The remote client [" + ctx.getChannel.getRemoteAddress + "] secure cookie is not the same as remote server secure cookie") + } + case _ ⇒ + throw new SecurityException("The remote client [" + ctx.getChannel.getRemoteAddress + "] is not Authorized!") + } + } + } +} + /** * @author Jonas Bonér */ @@ -752,7 +785,6 @@ class RemoteServerHandler( val applicationLoader: Option[ClassLoader], val server: NettyRemoteServerModule) extends SimpleChannelUpstreamHandler { import RemoteServerSettings._ - val CHANNEL_INIT = "channel-init".intern applicationLoader.foreach(MessageSerializer.setClassLoader(_)) //TODO: REVISIT: THIS FEELS A BIT DODGY @@ -786,7 +818,6 @@ class RemoteServerHandler( val clientAddress = getClientAddress(ctx) sessionActors.set(event.getChannel(), new ConcurrentHashMap[String, ActorRef]()) server.notifyListeners(RemoteServerClientConnected(server, clientAddress)) - if (REQUIRE_COOKIE) ctx.setAttachment(CHANNEL_INIT) // signal that this is channel initialization, which will need authentication } override def channelDisconnected(ctx: ChannelHandlerContext, event: ChannelStateEvent) = { @@ -810,11 +841,8 @@ class RemoteServerHandler( override def messageReceived(ctx: ChannelHandlerContext, event: MessageEvent) = event.getMessage match { case null ⇒ throw new IllegalActorStateException("Message in remote MessageEvent is null: " + event) - //case remoteProtocol: AkkaRemoteProtocol if remoteProtocol.hasInstruction => RemoteServer cannot receive control messages (yet) - case remoteProtocol: AkkaRemoteProtocol if remoteProtocol.hasMessage ⇒ - val requestProtocol = remoteProtocol.getMessage - if (REQUIRE_COOKIE) authenticateRemoteClient(requestProtocol, ctx) - handleRemoteMessageProtocol(requestProtocol, event.getChannel) + case remote: AkkaRemoteProtocol if remote.hasMessage ⇒ handleRemoteMessageProtocol(remote.getMessage, event.getChannel) + //case remote: AkkaRemoteProtocol if remote.hasInstruction => RemoteServer cannot receive control messages (yet) case _ ⇒ //ignore } @@ -874,8 +902,7 @@ class RemoteServerHandler( actorInfo.getTimeout, r, true, - Some(actorRef), - None) + Some(actorRef)) // FIXME lift in the supervisor uuid management into toh createRemoteMessageProtocolBuilder method if (request.hasSupervisorUuid) messageBuilder.setSupervisorUuid(request.getSupervisorUuid) @@ -939,26 +966,11 @@ class RemoteServerHandler( actorInfo.getTimeout, Left(exception), true, - None, None) if (request.hasSupervisorUuid) messageBuilder.setSupervisorUuid(request.getSupervisorUuid) RemoteEncoder.encode(messageBuilder.build) } - private def authenticateRemoteClient(request: RemoteMessageProtocol, ctx: ChannelHandlerContext) = { - val attachment = ctx.getAttachment - if ((attachment ne null) && - attachment.isInstanceOf[String] && - attachment.asInstanceOf[String] == CHANNEL_INIT) { // is first time around, channel initialization - ctx.setAttachment(null) - val clientAddress = ctx.getChannel.getRemoteAddress.toString - if (!request.hasCookie) throw new SecurityException( - "The remote client [" + clientAddress + "] does not have a secure cookie.") - if (!(request.getCookie == SECURE_COOKIE.get)) throw new SecurityException( - "The remote client [" + clientAddress + "] secure cookie is not the same as remote server secure cookie") - } - } - protected def parseUuid(protocol: UuidProtocol): Uuid = uuidFrom(protocol.getHigh, protocol.getLow) } diff --git a/akka-remote/src/main/scala/akka/serialization/SerializationProtocol.scala b/akka-remote/src/main/scala/akka/serialization/SerializationProtocol.scala index 4a19cdd436..a0705d7a52 100644 --- a/akka-remote/src/main/scala/akka/serialization/SerializationProtocol.scala +++ b/akka-remote/src/main/scala/akka/serialization/SerializationProtocol.scala @@ -78,10 +78,9 @@ object ActorSerialization { actorRef.timeout, Right(m.message), false, - actorRef.getSender, - RemoteClientSettings.SECURE_COOKIE).build) + actorRef.getSender)) - requestProtocols.foreach(rp ⇒ builder.addMessages(rp)) + requestProtocols.foreach(builder.addMessages(_)) } actorRef.receiveTimeout.foreach(builder.setReceiveTimeout(_)) @@ -201,8 +200,7 @@ object RemoteActorSerialization { timeout: Long, message: Either[Throwable, Any], isOneWay: Boolean, - senderOption: Option[ActorRef], - secureCookie: Option[String]): RemoteMessageProtocol.Builder = { + senderOption: Option[ActorRef]): RemoteMessageProtocol.Builder = { val uuidProtocol = replyUuid match { case Left(uid) ⇒ UuidProtocol.newBuilder.setHigh(uid.getTime).setLow(uid.getClockSeqAndNode).build @@ -238,8 +236,6 @@ object RemoteActorSerialization { case s ⇒ s } - secureCookie.foreach(messageBuilder.setCookie(_)) - /* TODO invent new supervision strategy actorRef.foreach { ref => ref.registerSupervisorAsRemoteActor.foreach { id => From 1f024f1c9ee9544010537d796f0e0aacb34e6a55 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Fri, 20 May 2011 22:25:46 +0200 Subject: [PATCH 02/20] Harmonizing constructors and Dispatcher-factories, closing ticket #807 --- .../scala/akka/dispatch/Dispatchers.scala | 36 ++++++++++++------- .../akka/dispatch/ThreadBasedDispatcher.scala | 29 ++++++++------- project/build/AkkaProject.scala | 2 +- 3 files changed, 42 insertions(+), 25 deletions(-) diff --git a/akka-actor/src/main/scala/akka/dispatch/Dispatchers.scala b/akka-actor/src/main/scala/akka/dispatch/Dispatchers.scala index 07daf658d3..5de9e91d51 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Dispatchers.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Dispatchers.scala @@ -68,16 +68,10 @@ object Dispatchers { *

* E.g. each actor consumes its own thread. */ - def newThreadBasedDispatcher(actor: ActorRef) = new ThreadBasedDispatcher(actor) - - /** - * Creates an thread based dispatcher serving a single actor through the same single thread. - * Uses the default timeout - * If capacity is negative, it's Integer.MAX_VALUE - *

- * E.g. each actor consumes its own thread. - */ - def newThreadBasedDispatcher(actor: ActorRef, mailboxCapacity: Int) = new ThreadBasedDispatcher(actor, mailboxCapacity) + def newThreadBasedDispatcher(actor: ActorRef) = actor match { + case null ⇒ new ThreadBasedDispatcher() + case some ⇒ new ThreadBasedDispatcher(some) + } /** * Creates an thread based dispatcher serving a single actor through the same single thread. @@ -85,8 +79,26 @@ object Dispatchers { *

* E.g. each actor consumes its own thread. */ - def newThreadBasedDispatcher(actor: ActorRef, mailboxCapacity: Int, pushTimeOut: Duration) = - new ThreadBasedDispatcher(actor, mailboxCapacity, pushTimeOut) + def newThreadBasedDispatcher(actor: ActorRef, mailboxType: MailboxType) = actor match { + case null ⇒ new ThreadBasedDispatcher(mailboxType) + case some ⇒ new ThreadBasedDispatcher(some, mailboxType) + } + + /** + * Creates an thread based dispatcher serving a single actor through the same single thread. + *

+ * E.g. each actor consumes its own thread. + */ + def newThreadBasedDispatcher(name: String, mailboxType: MailboxType) = + new ThreadBasedDispatcher(name, mailboxType) + + /** + * Creates an thread based dispatcher serving a single actor through the same single thread. + *

+ * E.g. each actor consumes its own thread. + */ + def newThreadBasedDispatcher(name: String) = + new ThreadBasedDispatcher(name) /** * Creates a executor-based event-driven dispatcher serving multiple (millions) of actors through a thread pool. diff --git a/akka-actor/src/main/scala/akka/dispatch/ThreadBasedDispatcher.scala b/akka-actor/src/main/scala/akka/dispatch/ThreadBasedDispatcher.scala index c365329834..101a2ae2c5 100644 --- a/akka-actor/src/main/scala/akka/dispatch/ThreadBasedDispatcher.scala +++ b/akka-actor/src/main/scala/akka/dispatch/ThreadBasedDispatcher.scala @@ -4,31 +4,36 @@ package akka.dispatch -import akka.actor.{ ActorRef } import akka.util.Duration import java.util.concurrent.atomic.AtomicReference +import akka.actor.{ Actor, ActorRef } /** * Dedicates a unique thread for each actor passed in as reference. Served through its messageQueue. * * @author Jonas Bonér */ -class ThreadBasedDispatcher(_actor: ActorRef, _mailboxType: MailboxType) +class ThreadBasedDispatcher(_actor: ActorRef, _name: String, _mailboxType: MailboxType) extends ExecutorBasedEventDrivenDispatcher( - _actor.uuid.toString, Dispatchers.THROUGHPUT, -1, _mailboxType, ThreadBasedDispatcher.oneThread) { + _name, Dispatchers.THROUGHPUT, -1, _mailboxType, ThreadBasedDispatcher.oneThread) { + + def this(_name: String, _mailboxType: MailboxType) = this(null, _name, _mailboxType) + + def this(_actor: ActorRef, _name: String) = this(_actor, _name, Dispatchers.MAILBOX_TYPE) + + def this(_name: String) = this(null, _name, Dispatchers.MAILBOX_TYPE) + + def this(_mailboxType: MailboxType) = this(null, "anon", _mailboxType) + + def this(_actor: ActorRef, _mailboxType: MailboxType) = this(_actor, _actor.uuid.toString, _mailboxType) + + def this(_actor: ActorRef) = this(_actor, _actor.uuid.toString, Dispatchers.MAILBOX_TYPE) + + def this() = this(Dispatchers.MAILBOX_TYPE) private[akka] val owner = new AtomicReference[ActorRef](_actor) - def this(actor: ActorRef) = - this(actor, UnboundedMailbox()) // For Java API - - def this(actor: ActorRef, capacity: Int) = - this(actor, BoundedMailbox(capacity)) //For Java API - - def this(actor: ActorRef, capacity: Int, pushTimeOut: Duration) = //For Java API - this(actor, BoundedMailbox(capacity, pushTimeOut)) - override def register(actorRef: ActorRef) = { val actor = owner.get() if ((actor ne null) && actorRef != actor) throw new IllegalArgumentException("Cannot register to anyone but " + actor) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 53e79bb5e9..35c487d70c 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -19,7 +19,7 @@ class AkkaParentProject(info: ProjectInfo) extends ParentProject(info) with Exec val scalaCompileSettings = Seq("-deprecation", //"-Xmigration", - "-optimise", + //"-optimise", "-encoding", "utf8") val javaCompileSettings = Seq("-Xlint:unchecked") From 3181905fedf274e7dfb3cd865222ec8865aac16c Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Fri, 20 May 2011 22:41:41 +0200 Subject: [PATCH 03/20] Renaming EBEDD to Dispatcher, EBEDWSD to BalancingDispatcher, ThreadBasedDispatcher to PinnedDispatcher and PEBEDD to PriorityDispatcher, closing ticket #784 --- .../akka/actor/actor/TypedActorSpec.scala | 2 +- .../actor/supervisor/SupervisorMiscSpec.scala | 8 +- .../test/scala/akka/config/ConfigSpec.scala | 2 +- .../scala/akka/dispatch/ActorModelSpec.scala | 8 +- .../scala/akka/dispatch/DispatchersSpec.scala | 8 +- ...rBasedEventDrivenDispatcherActorSpec.scala | 16 ++-- ...BasedEventDrivenDispatcherActorsSpec.scala | 2 +- ...ventDrivenWorkStealingDispatcherSpec.scala | 8 +- .../dispatch/PriorityDispatcherSpec.scala | 4 +- .../akka/dispatch/ThreadBasedActorSpec.scala | 4 +- .../src/main/scala/akka/actor/ActorRef.scala | 2 +- ...atcher.scala => BalancingDispatcher.scala} | 6 +- ...rivenDispatcher.scala => Dispatcher.scala} | 28 +++---- .../scala/akka/dispatch/Dispatchers.scala | 76 +++++++++---------- ...ispatcher.scala => PinnedDispatcher.scala} | 8 +- .../main/scala/akka/event/EventHandler.scala | 2 +- .../src/main/scala/akka/cluster/Cluster.scala | 4 +- akka-docs/cluster/durable-mailbox.rst | 12 +-- akka-docs/general/configuration.rst | 2 +- akka-docs/java/dispatchers.rst | 32 ++++---- .../project/migration-guide-0.9.x-0.10.x.rst | 2 +- akka-docs/project/release-notes.rst | 8 +- akka-docs/scala/dispatchers.rst | 30 ++++---- .../actor/mailbox/DurableDispatcher.scala | 10 +-- .../akka/actor/mailbox/DurableMailbox.scala | 4 +- .../src/main/scala/akka/agent/Agent.scala | 2 +- config/akka-reference.conf | 20 ++--- project/build/AkkaProject.scala | 2 +- 28 files changed, 156 insertions(+), 156 deletions(-) rename akka-actor/src/main/scala/akka/dispatch/{ExecutorBasedEventDrivenWorkStealingDispatcher.scala => BalancingDispatcher.scala} (96%) rename akka-actor/src/main/scala/akka/dispatch/{ExecutorBasedEventDrivenDispatcher.scala => Dispatcher.scala} (90%) rename akka-actor/src/main/scala/akka/dispatch/{ThreadBasedDispatcher.scala => PinnedDispatcher.scala} (86%) diff --git a/akka-actor-tests/src/test/scala/akka/actor/actor/TypedActorSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/actor/TypedActorSpec.scala index 43111cc659..5b9f155d75 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/actor/TypedActorSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/actor/TypedActorSpec.scala @@ -264,7 +264,7 @@ class TypedActorSpec extends WordSpec with MustMatchers with BeforeAndAfterEach "be able to use work-stealing dispatcher" in { val config = Configuration( Duration(6600, "ms"), - Dispatchers.newExecutorBasedEventDrivenWorkStealingDispatcher("pooled-dispatcher") + Dispatchers.newBalancingDispatcher("pooled-dispatcher") .withNewThreadPoolWithLinkedBlockingQueueWithUnboundedCapacity .setCorePoolSize(60) .setMaxPoolSize(60) diff --git a/akka-actor-tests/src/test/scala/akka/actor/supervisor/SupervisorMiscSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/supervisor/SupervisorMiscSpec.scala index c81455b9e8..6b1017e79d 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/supervisor/SupervisorMiscSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/supervisor/SupervisorMiscSpec.scala @@ -16,7 +16,7 @@ class SupervisorMiscSpec extends WordSpec with MustMatchers { val countDownLatch = new CountDownLatch(4) val actor1 = Actor.actorOf(new Actor { - self.dispatcher = Dispatchers.newThreadBasedDispatcher(self) + self.dispatcher = Dispatchers.newPinnedDispatcher(self) override def postRestart(cause: Throwable) { countDownLatch.countDown() } protected def receive = { @@ -26,7 +26,7 @@ class SupervisorMiscSpec extends WordSpec with MustMatchers { }).start() val actor2 = Actor.actorOf(new Actor { - self.dispatcher = Dispatchers.newThreadBasedDispatcher(self) + self.dispatcher = Dispatchers.newPinnedDispatcher(self) override def postRestart(cause: Throwable) { countDownLatch.countDown() } protected def receive = { @@ -36,7 +36,7 @@ class SupervisorMiscSpec extends WordSpec with MustMatchers { }).start() val actor3 = Actor.actorOf(new Actor { - self.dispatcher = Dispatchers.newExecutorBasedEventDrivenDispatcher("test").build + self.dispatcher = Dispatchers.newDispatcher("test").build override def postRestart(cause: Throwable) { countDownLatch.countDown() } protected def receive = { @@ -46,7 +46,7 @@ class SupervisorMiscSpec extends WordSpec with MustMatchers { }).start() val actor4 = Actor.actorOf(new Actor { - self.dispatcher = Dispatchers.newThreadBasedDispatcher(self) + self.dispatcher = Dispatchers.newPinnedDispatcher(self) override def postRestart(cause: Throwable) { countDownLatch.countDown() } protected def receive = { diff --git a/akka-actor-tests/src/test/scala/akka/config/ConfigSpec.scala b/akka-actor-tests/src/test/scala/akka/config/ConfigSpec.scala index 916852a7b4..66d21435f4 100644 --- a/akka-actor-tests/src/test/scala/akka/config/ConfigSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/config/ConfigSpec.scala @@ -20,7 +20,7 @@ class ConfigSpec extends WordSpec with MustMatchers { getString("akka.time-unit") must equal(Some("seconds")) getString("akka.version") must equal(Some("2.0-SNAPSHOT")) - getString("akka.actor.default-dispatcher.type") must equal(Some("GlobalExecutorBasedEventDriven")) + getString("akka.actor.default-dispatcher.type") must equal(Some("GlobalDispatcher")) getInt("akka.actor.default-dispatcher.keep-alive-time") must equal(Some(60)) getDouble("akka.actor.default-dispatcher.core-pool-size-factor") must equal(Some(1.0)) getDouble("akka.actor.default-dispatcher.max-pool-size-factor") must equal(Some(4.0)) diff --git a/akka-actor-tests/src/test/scala/akka/dispatch/ActorModelSpec.scala b/akka-actor-tests/src/test/scala/akka/dispatch/ActorModelSpec.scala index c6af345d68..97dc67da22 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/ActorModelSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/ActorModelSpec.scala @@ -344,12 +344,12 @@ abstract class ActorModelSpec extends JUnitSuite { } } -class ExecutorBasedEventDrivenDispatcherModelTest extends ActorModelSpec { +class DispatcherModelTest extends ActorModelSpec { def newInterceptedDispatcher = - new ExecutorBasedEventDrivenDispatcher("foo") with MessageDispatcherInterceptor + new Dispatcher("foo") with MessageDispatcherInterceptor } -class ExecutorBasedEventDrivenWorkStealingDispatcherModelTest extends ActorModelSpec { +class BalancingDispatcherModelTest extends ActorModelSpec { def newInterceptedDispatcher = - new ExecutorBasedEventDrivenWorkStealingDispatcher("foo") with MessageDispatcherInterceptor + new BalancingDispatcher("foo") with MessageDispatcherInterceptor } diff --git a/akka-actor-tests/src/test/scala/akka/dispatch/DispatchersSpec.scala b/akka-actor-tests/src/test/scala/akka/dispatch/DispatchersSpec.scala index cc75c5d43a..f7df14e195 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/DispatchersSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/DispatchersSpec.scala @@ -21,15 +21,15 @@ object DispatchersSpec { val executorbounds = "executor-bounds" val allowcoretimeout = "allow-core-timeout" val rejectionpolicy = "rejection-policy" // abort, caller-runs, discard-oldest, discard - val throughput = "throughput" // Throughput for ExecutorBasedEventDrivenDispatcher + val throughput = "throughput" // Throughput for Dispatcher def instance(dispatcher: MessageDispatcher): (MessageDispatcher) ⇒ Boolean = _ == dispatcher def ofType[T <: MessageDispatcher: Manifest]: (MessageDispatcher) ⇒ Boolean = _.getClass == manifest[T].erasure def typesAndValidators: Map[String, (MessageDispatcher) ⇒ Boolean] = Map( - "ExecutorBasedEventDrivenWorkStealing" -> ofType[ExecutorBasedEventDrivenWorkStealingDispatcher], - "ExecutorBasedEventDriven" -> ofType[ExecutorBasedEventDrivenDispatcher], - "GlobalExecutorBasedEventDriven" -> instance(globalExecutorBasedEventDrivenDispatcher)) + "BalancingDispatcher" -> ofType[BalancingDispatcher], + "Dispatcher" -> ofType[Dispatcher], + "GlobalDispatcher" -> instance(globalDispatcher)) def validTypes = typesAndValidators.keys.toList diff --git a/akka-actor-tests/src/test/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcherActorSpec.scala b/akka-actor-tests/src/test/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcherActorSpec.scala index c330d948d3..69fc9ea635 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcherActorSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcherActorSpec.scala @@ -3,14 +3,14 @@ package akka.actor.dispatch import java.util.concurrent.{ CountDownLatch, TimeUnit } import org.scalatest.junit.JUnitSuite import org.junit.Test -import akka.dispatch.{ Dispatchers, ExecutorBasedEventDrivenDispatcher } +import akka.dispatch.{ Dispatchers, Dispatcher } import akka.actor.Actor import Actor._ import java.util.concurrent.atomic.{ AtomicBoolean, AtomicInteger } -object ExecutorBasedEventDrivenDispatcherActorSpec { +object DispatcherActorSpec { class TestActor extends Actor { - self.dispatcher = Dispatchers.newExecutorBasedEventDrivenDispatcher(self.uuid.toString).build + self.dispatcher = Dispatchers.newDispatcher(self.uuid.toString).build def receive = { case "Hello" ⇒ self.reply("World") @@ -23,14 +23,14 @@ object ExecutorBasedEventDrivenDispatcherActorSpec { val oneWay = new CountDownLatch(1) } class OneWayTestActor extends Actor { - self.dispatcher = Dispatchers.newExecutorBasedEventDrivenDispatcher(self.uuid.toString).build + self.dispatcher = Dispatchers.newDispatcher(self.uuid.toString).build def receive = { case "OneWay" ⇒ OneWayTestActor.oneWay.countDown() } } } -class ExecutorBasedEventDrivenDispatcherActorSpec extends JUnitSuite { - import ExecutorBasedEventDrivenDispatcherActorSpec._ +class DispatcherActorSpec extends JUnitSuite { + import DispatcherActorSpec._ private val unit = TimeUnit.MILLISECONDS @@ -74,7 +74,7 @@ class ExecutorBasedEventDrivenDispatcherActorSpec extends JUnitSuite { @Test def shouldRespectThroughput { val throughputDispatcher = Dispatchers. - newExecutorBasedEventDrivenDispatcher("THROUGHPUT", 101, 0, Dispatchers.MAILBOX_TYPE). + newDispatcher("THROUGHPUT", 101, 0, Dispatchers.MAILBOX_TYPE). setCorePoolSize(1). build @@ -110,7 +110,7 @@ class ExecutorBasedEventDrivenDispatcherActorSpec extends JUnitSuite { def shouldRespectThroughputDeadline { val deadlineMs = 100 val throughputDispatcher = Dispatchers. - newExecutorBasedEventDrivenDispatcher("THROUGHPUT", 2, deadlineMs, Dispatchers.MAILBOX_TYPE). + newDispatcher("THROUGHPUT", 2, deadlineMs, Dispatchers.MAILBOX_TYPE). setCorePoolSize(1). build val works = new AtomicBoolean(true) diff --git a/akka-actor-tests/src/test/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcherActorsSpec.scala b/akka-actor-tests/src/test/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcherActorsSpec.scala index d636ecc932..f7cc4a0956 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcherActorsSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcherActorsSpec.scala @@ -12,7 +12,7 @@ import Actor._ * * @author Jan Van Besien */ -class ExecutorBasedEventDrivenDispatcherActorsSpec extends JUnitSuite with MustMatchers { +class DispatcherActorsSpec extends JUnitSuite with MustMatchers { class SlowActor(finishedCounter: CountDownLatch) extends Actor { def receive = { diff --git a/akka-actor-tests/src/test/scala/akka/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcherSpec.scala b/akka-actor-tests/src/test/scala/akka/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcherSpec.scala index 064433c6ca..9ce2c9dccd 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcherSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcherSpec.scala @@ -10,9 +10,9 @@ import akka.actor.{ IllegalActorStateException, Actor } import Actor._ import akka.dispatch.{ MessageQueue, Dispatchers } -object ExecutorBasedEventDrivenWorkStealingDispatcherSpec { +object BalancingDispatcherSpec { - def newWorkStealer() = Dispatchers.newExecutorBasedEventDrivenWorkStealingDispatcher("pooled-dispatcher", 1).build + def newWorkStealer() = Dispatchers.newBalancingDispatcher("pooled-dispatcher", 1).build val delayableActorDispatcher, sharedActorDispatcher, parentActorDispatcher = newWorkStealer() @@ -52,8 +52,8 @@ object ExecutorBasedEventDrivenWorkStealingDispatcherSpec { /** * @author Jan Van Besien */ -class ExecutorBasedEventDrivenWorkStealingDispatcherSpec extends JUnitSuite with MustMatchers { - import ExecutorBasedEventDrivenWorkStealingDispatcherSpec._ +class BalancingDispatcherSpec extends JUnitSuite with MustMatchers { + import BalancingDispatcherSpec._ @Test def fastActorShouldStealWorkFromSlowActor { diff --git a/akka-actor-tests/src/test/scala/akka/dispatch/PriorityDispatcherSpec.scala b/akka-actor-tests/src/test/scala/akka/dispatch/PriorityDispatcherSpec.scala index ff2e85fb05..5740edb9b8 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/PriorityDispatcherSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/PriorityDispatcherSpec.scala @@ -8,7 +8,7 @@ import java.util.concurrent.CountDownLatch class PriorityDispatcherSpec extends WordSpec with MustMatchers { - "A PriorityExecutorBasedEventDrivenDispatcher" must { + "A PriorityDispatcher" must { "Order it's messages according to the specified comparator using an unbounded mailbox" in { testOrdering(UnboundedMailbox()) } @@ -19,7 +19,7 @@ class PriorityDispatcherSpec extends WordSpec with MustMatchers { } def testOrdering(mboxType: MailboxType) { - val dispatcher = new PriorityExecutorBasedEventDrivenDispatcher("Test", + val dispatcher = new PriorityDispatcher("Test", PriorityGenerator({ case i: Int ⇒ i //Reverse order case 'Result ⇒ Int.MaxValue diff --git a/akka-actor-tests/src/test/scala/akka/dispatch/ThreadBasedActorSpec.scala b/akka-actor-tests/src/test/scala/akka/dispatch/ThreadBasedActorSpec.scala index fd127c1a14..722c48fbdc 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/ThreadBasedActorSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/ThreadBasedActorSpec.scala @@ -10,7 +10,7 @@ import Actor._ object ThreadBasedActorSpec { class TestActor extends Actor { - self.dispatcher = Dispatchers.newThreadBasedDispatcher(self) + self.dispatcher = Dispatchers.newPinnedDispatcher(self) def receive = { case "Hello" ⇒ @@ -30,7 +30,7 @@ class ThreadBasedActorSpec extends JUnitSuite { def shouldSendOneWay { var oneWay = new CountDownLatch(1) val actor = actorOf(new Actor { - self.dispatcher = Dispatchers.newThreadBasedDispatcher(self) + self.dispatcher = Dispatchers.newPinnedDispatcher(self) def receive = { case "OneWay" ⇒ oneWay.countDown() } diff --git a/akka-actor/src/main/scala/akka/actor/ActorRef.scala b/akka-actor/src/main/scala/akka/actor/ActorRef.scala index 79a768f85e..61fbf56be9 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorRef.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorRef.scala @@ -160,7 +160,7 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] { scal /** * Akka Java API.

- * The default dispatcher is the Dispatchers.globalExecutorBasedEventDrivenDispatcher. + * The default dispatcher is the Dispatchers.globalDispatcher. * This means that all actors will share the same event-driven executor based dispatcher. *

* You can override it so it fits the specific use-case that the actor is used for. diff --git a/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcher.scala b/akka-actor/src/main/scala/akka/dispatch/BalancingDispatcher.scala similarity index 96% rename from akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcher.scala rename to akka-actor/src/main/scala/akka/dispatch/BalancingDispatcher.scala index eaf4472901..0460720a73 100644 --- a/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenWorkStealingDispatcher.scala +++ b/akka-actor/src/main/scala/akka/dispatch/BalancingDispatcher.scala @@ -19,18 +19,18 @@ import util.DynamicVariable * The preferred way of creating dispatchers is to use * the {@link akka.dispatch.Dispatchers} factory object. * - * @see akka.dispatch.ExecutorBasedEventDrivenWorkStealingDispatcher + * @see akka.dispatch.BalancingDispatcher * @see akka.dispatch.Dispatchers * * @author Viktor Klang */ -class ExecutorBasedEventDrivenWorkStealingDispatcher( +class BalancingDispatcher( _name: String, throughput: Int = Dispatchers.THROUGHPUT, throughputDeadlineTime: Int = Dispatchers.THROUGHPUT_DEADLINE_TIME_MILLIS, mailboxType: MailboxType = Dispatchers.MAILBOX_TYPE, config: ThreadPoolConfig = ThreadPoolConfig()) - extends ExecutorBasedEventDrivenDispatcher(_name, throughput, throughputDeadlineTime, mailboxType, config) { + extends Dispatcher(_name, throughput, throughputDeadlineTime, mailboxType, config) { def this(_name: String, throughput: Int, throughputDeadlineTime: Int, mailboxType: MailboxType) = this(_name, throughput, throughputDeadlineTime, mailboxType, ThreadPoolConfig()) // Needed for Java API usage diff --git a/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala b/akka-actor/src/main/scala/akka/dispatch/Dispatcher.scala similarity index 90% rename from akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala rename to akka-actor/src/main/scala/akka/dispatch/Dispatcher.scala index 3c985e6409..ab2287a589 100644 --- a/akka-actor/src/main/scala/akka/dispatch/ExecutorBasedEventDrivenDispatcher.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Dispatcher.scala @@ -28,7 +28,7 @@ import java.util.concurrent.{ TimeUnit, ExecutorService, RejectedExecutionExcept *

* Example usage: *

- *   val dispatcher = new ExecutorBasedEventDrivenDispatcher("name")
+ *   val dispatcher = new Dispatcher("name")
  *   dispatcher
  *     .withNewThreadPoolWithBoundedBlockingQueue(100)
  *     .setCorePoolSize(16)
@@ -43,7 +43,7 @@ import java.util.concurrent.{ TimeUnit, ExecutorService, RejectedExecutionExcept
  * 

* Example usage: *

- *   ExecutorBasedEventDrivenDispatcher dispatcher = new ExecutorBasedEventDrivenDispatcher("name");
+ *   Dispatcher dispatcher = new Dispatcher("name");
  *   dispatcher
  *     .withNewThreadPoolWithBoundedBlockingQueue(100)
  *     .setCorePoolSize(16)
@@ -63,7 +63,7 @@ import java.util.concurrent.{ TimeUnit, ExecutorService, RejectedExecutionExcept
  *                   always continues until the mailbox is empty.
  *                   Larger values (or zero or negative) increase throughput, smaller values increase fairness
  */
-class ExecutorBasedEventDrivenDispatcher(
+class Dispatcher(
   _name: String,
   val throughput: Int = Dispatchers.THROUGHPUT,
   val throughputDeadlineTime: Int = Dispatchers.THROUGHPUT_DEADLINE_TIME_MILLIS,
@@ -117,7 +117,7 @@ class ExecutorBasedEventDrivenDispatcher(
     case b: UnboundedMailbox ⇒
       new ConcurrentLinkedQueue[MessageInvocation] with MessageQueue with ExecutableMailbox {
         @inline
-        final def dispatcher = ExecutorBasedEventDrivenDispatcher.this
+        final def dispatcher = Dispatcher.this
         @inline
         final def enqueue(m: MessageInvocation) = this.add(m)
         @inline
@@ -126,7 +126,7 @@ class ExecutorBasedEventDrivenDispatcher(
     case b: BoundedMailbox ⇒
       new DefaultBoundedMessageQueue(b.capacity, b.pushTimeOut) with ExecutableMailbox {
         @inline
-        final def dispatcher = ExecutorBasedEventDrivenDispatcher.this
+        final def dispatcher = Dispatcher.this
       }
   }
 
@@ -173,11 +173,11 @@ class ExecutorBasedEventDrivenDispatcher(
 }
 
 /**
- * This is the behavior of an ExecutorBasedEventDrivenDispatchers mailbox.
+ * This is the behavior of an Dispatchers mailbox.
  */
 trait ExecutableMailbox extends Runnable { self: MessageQueue ⇒
 
-  def dispatcher: ExecutorBasedEventDrivenDispatcher
+  def dispatcher: Dispatcher
 
   final def run = {
     try {
@@ -237,7 +237,7 @@ object PriorityGenerator {
 
 /**
  * A PriorityGenerator is a convenience API to create a Comparator that orders the messages of a
- * PriorityExecutorBasedEventDrivenDispatcher
+ * PriorityDispatcher
  */
 abstract class PriorityGenerator extends java.util.Comparator[MessageInvocation] {
   def gen(message: Any): Int
@@ -247,18 +247,18 @@ abstract class PriorityGenerator extends java.util.Comparator[MessageInvocation]
 }
 
 /**
- * A version of ExecutorBasedEventDrivenDispatcher that gives all actors registered to it a priority mailbox,
+ * A version of Dispatcher that gives all actors registered to it a priority mailbox,
  * prioritized according to the supplied comparator.
  *
  * The dispatcher will process the messages with the _lowest_ priority first.
  */
-class PriorityExecutorBasedEventDrivenDispatcher(
+class PriorityDispatcher(
   name: String,
   val comparator: java.util.Comparator[MessageInvocation],
   throughput: Int = Dispatchers.THROUGHPUT,
   throughputDeadlineTime: Int = Dispatchers.THROUGHPUT_DEADLINE_TIME_MILLIS,
   mailboxType: MailboxType = Dispatchers.MAILBOX_TYPE,
-  config: ThreadPoolConfig = ThreadPoolConfig()) extends ExecutorBasedEventDrivenDispatcher(name, throughput, throughputDeadlineTime, mailboxType, config) with PriorityMailbox {
+  config: ThreadPoolConfig = ThreadPoolConfig()) extends Dispatcher(name, throughput, throughputDeadlineTime, mailboxType, config) with PriorityMailbox {
 
   def this(name: String, comparator: java.util.Comparator[MessageInvocation], throughput: Int, throughputDeadlineTime: Int, mailboxType: MailboxType) =
     this(name, comparator, throughput, throughputDeadlineTime, mailboxType, ThreadPoolConfig()) // Needed for Java API usage
@@ -277,14 +277,14 @@ class PriorityExecutorBasedEventDrivenDispatcher(
 }
 
 /**
- * Can be used to give an ExecutorBasedEventDrivenDispatcher's actors priority-enabled mailboxes
+ * Can be used to give an Dispatcher's actors priority-enabled mailboxes
  *
  * Usage:
- * new ExecutorBasedEventDrivenDispatcher(...) with PriorityMailbox {
+ * new Dispatcher(...) with PriorityMailbox {
  *   val comparator = ...comparator that determines mailbox priority ordering...
  * }
  */
-trait PriorityMailbox { self: ExecutorBasedEventDrivenDispatcher ⇒
+trait PriorityMailbox { self: Dispatcher ⇒
   def comparator: java.util.Comparator[MessageInvocation]
 
   override def createMailbox(actorRef: ActorRef): AnyRef = self.mailboxType match {
diff --git a/akka-actor/src/main/scala/akka/dispatch/Dispatchers.scala b/akka-actor/src/main/scala/akka/dispatch/Dispatchers.scala
index 5de9e91d51..963927582f 100644
--- a/akka-actor/src/main/scala/akka/dispatch/Dispatchers.scala
+++ b/akka-actor/src/main/scala/akka/dispatch/Dispatchers.scala
@@ -18,7 +18,7 @@ import java.util.concurrent.TimeUnit
  * 

* Example usage: *

- *   val dispatcher = Dispatchers.newExecutorBasedEventDrivenDispatcher("name")
+ *   val dispatcher = Dispatchers.newDispatcher("name")
  *   dispatcher
  *     .withNewThreadPoolWithLinkedBlockingQueueWithCapacity(100)
  *     .setCorePoolSize(16)
@@ -32,7 +32,7 @@ import java.util.concurrent.TimeUnit
  * 

* Example usage: *

- *   MessageDispatcher dispatcher = Dispatchers.newExecutorBasedEventDrivenDispatcher("name");
+ *   MessageDispatcher dispatcher = Dispatchers.newDispatcher("name");
  *   dispatcher
  *     .withNewThreadPoolWithLinkedBlockingQueueWithCapacity(100)
  *     .setCorePoolSize(16)
@@ -57,10 +57,10 @@ object Dispatchers {
   val MAILBOX_TYPE: MailboxType = if (MAILBOX_CAPACITY < 1) UnboundedMailbox() else BoundedMailbox()
 
   lazy val defaultGlobalDispatcher = {
-    config.getSection("akka.actor.default-dispatcher").flatMap(from).getOrElse(globalExecutorBasedEventDrivenDispatcher)
+    config.getSection("akka.actor.default-dispatcher").flatMap(from).getOrElse(globalDispatcher)
   }
 
-  object globalExecutorBasedEventDrivenDispatcher extends ExecutorBasedEventDrivenDispatcher("global", THROUGHPUT, THROUGHPUT_DEADLINE_TIME_MILLIS, MAILBOX_TYPE)
+  object globalDispatcher extends Dispatcher("global", THROUGHPUT, THROUGHPUT_DEADLINE_TIME_MILLIS, MAILBOX_TYPE)
 
   /**
    * Creates an thread based dispatcher serving a single actor through the same single thread.
@@ -68,9 +68,9 @@ object Dispatchers {
    * 

* E.g. each actor consumes its own thread. */ - def newThreadBasedDispatcher(actor: ActorRef) = actor match { - case null ⇒ new ThreadBasedDispatcher() - case some ⇒ new ThreadBasedDispatcher(some) + def newPinnedDispatcher(actor: ActorRef) = actor match { + case null ⇒ new PinnedDispatcher() + case some ⇒ new PinnedDispatcher(some) } /** @@ -79,9 +79,9 @@ object Dispatchers { *

* E.g. each actor consumes its own thread. */ - def newThreadBasedDispatcher(actor: ActorRef, mailboxType: MailboxType) = actor match { - case null ⇒ new ThreadBasedDispatcher(mailboxType) - case some ⇒ new ThreadBasedDispatcher(some, mailboxType) + def newPinnedDispatcher(actor: ActorRef, mailboxType: MailboxType) = actor match { + case null ⇒ new PinnedDispatcher(mailboxType) + case some ⇒ new PinnedDispatcher(some, mailboxType) } /** @@ -89,77 +89,77 @@ object Dispatchers { *

* E.g. each actor consumes its own thread. */ - def newThreadBasedDispatcher(name: String, mailboxType: MailboxType) = - new ThreadBasedDispatcher(name, mailboxType) + def newPinnedDispatcher(name: String, mailboxType: MailboxType) = + new PinnedDispatcher(name, mailboxType) /** * Creates an thread based dispatcher serving a single actor through the same single thread. *

* E.g. each actor consumes its own thread. */ - def newThreadBasedDispatcher(name: String) = - new ThreadBasedDispatcher(name) + def newPinnedDispatcher(name: String) = + new PinnedDispatcher(name) /** * Creates a executor-based event-driven dispatcher serving multiple (millions) of actors through a thread pool. *

* Has a fluent builder interface for configuring its semantics. */ - def newExecutorBasedEventDrivenDispatcher(name: String) = - ThreadPoolConfigDispatcherBuilder(config ⇒ new ExecutorBasedEventDrivenDispatcher(name, config), ThreadPoolConfig()) + def newDispatcher(name: String) = + ThreadPoolConfigDispatcherBuilder(config ⇒ new Dispatcher(name, config), ThreadPoolConfig()) /** * Creates a executor-based event-driven dispatcher serving multiple (millions) of actors through a thread pool. *

* Has a fluent builder interface for configuring its semantics. */ - def newExecutorBasedEventDrivenDispatcher(name: String, throughput: Int, mailboxType: MailboxType) = + def newDispatcher(name: String, throughput: Int, mailboxType: MailboxType) = ThreadPoolConfigDispatcherBuilder(config ⇒ - new ExecutorBasedEventDrivenDispatcher(name, throughput, THROUGHPUT_DEADLINE_TIME_MILLIS, mailboxType, config), ThreadPoolConfig()) + new Dispatcher(name, throughput, THROUGHPUT_DEADLINE_TIME_MILLIS, mailboxType, config), ThreadPoolConfig()) /** * Creates a executor-based event-driven dispatcher serving multiple (millions) of actors through a thread pool. *

* Has a fluent builder interface for configuring its semantics. */ - def newExecutorBasedEventDrivenDispatcher(name: String, throughput: Int, throughputDeadlineMs: Int, mailboxType: MailboxType) = + def newDispatcher(name: String, throughput: Int, throughputDeadlineMs: Int, mailboxType: MailboxType) = ThreadPoolConfigDispatcherBuilder(config ⇒ - new ExecutorBasedEventDrivenDispatcher(name, throughput, throughputDeadlineMs, mailboxType, config), ThreadPoolConfig()) + new Dispatcher(name, throughput, throughputDeadlineMs, mailboxType, config), ThreadPoolConfig()) /** * Creates a executor-based event-driven dispatcher, with work-stealing, serving multiple (millions) of actors through a thread pool. *

* Has a fluent builder interface for configuring its semantics. */ - def newExecutorBasedEventDrivenWorkStealingDispatcher(name: String) = - ThreadPoolConfigDispatcherBuilder(config ⇒ new ExecutorBasedEventDrivenWorkStealingDispatcher(name, config), ThreadPoolConfig()) + def newBalancingDispatcher(name: String) = + ThreadPoolConfigDispatcherBuilder(config ⇒ new BalancingDispatcher(name, config), ThreadPoolConfig()) /** * Creates a executor-based event-driven dispatcher, with work-stealing, serving multiple (millions) of actors through a thread pool. *

* Has a fluent builder interface for configuring its semantics. */ - def newExecutorBasedEventDrivenWorkStealingDispatcher(name: String, throughput: Int) = + def newBalancingDispatcher(name: String, throughput: Int) = ThreadPoolConfigDispatcherBuilder(config ⇒ - new ExecutorBasedEventDrivenWorkStealingDispatcher(name, throughput, THROUGHPUT_DEADLINE_TIME_MILLIS, MAILBOX_TYPE, config), ThreadPoolConfig()) + new BalancingDispatcher(name, throughput, THROUGHPUT_DEADLINE_TIME_MILLIS, MAILBOX_TYPE, config), ThreadPoolConfig()) /** * Creates a executor-based event-driven dispatcher, with work-stealing, serving multiple (millions) of actors through a thread pool. *

* Has a fluent builder interface for configuring its semantics. */ - def newExecutorBasedEventDrivenWorkStealingDispatcher(name: String, throughput: Int, mailboxType: MailboxType) = + def newBalancingDispatcher(name: String, throughput: Int, mailboxType: MailboxType) = ThreadPoolConfigDispatcherBuilder(config ⇒ - new ExecutorBasedEventDrivenWorkStealingDispatcher(name, throughput, THROUGHPUT_DEADLINE_TIME_MILLIS, mailboxType, config), ThreadPoolConfig()) + new BalancingDispatcher(name, throughput, THROUGHPUT_DEADLINE_TIME_MILLIS, mailboxType, config), ThreadPoolConfig()) /** * Creates a executor-based event-driven dispatcher, with work-stealing, serving multiple (millions) of actors through a thread pool. *

* Has a fluent builder interface for configuring its semantics. */ - def newExecutorBasedEventDrivenWorkStealingDispatcher(name: String, throughput: Int, throughputDeadlineMs: Int, mailboxType: MailboxType) = + def newBalancingDispatcher(name: String, throughput: Int, throughputDeadlineMs: Int, mailboxType: MailboxType) = ThreadPoolConfigDispatcherBuilder(config ⇒ - new ExecutorBasedEventDrivenWorkStealingDispatcher(name, throughput, throughputDeadlineMs, mailboxType, config), ThreadPoolConfig()) + new BalancingDispatcher(name, throughput, throughputDeadlineMs, mailboxType, config), ThreadPoolConfig()) /** * Utility function that tries to load the specified dispatcher config from the akka.conf * or else use the supplied default dispatcher @@ -181,7 +181,7 @@ object Dispatchers { * executor-bounds = -1 # Makes the Executor bounded, -1 is unbounded * allow-core-timeout = on # Allow core threads to time out * rejection-policy = "caller-runs" # abort, caller-runs, discard-oldest, discard - * throughput = 5 # Throughput for ExecutorBasedEventDrivenDispatcher + * throughput = 5 # Throughput for Dispatcher * } * ex: from(config.getConfigMap(identifier).get) * @@ -192,9 +192,9 @@ object Dispatchers { */ def from(cfg: Configuration): Option[MessageDispatcher] = { cfg.getString("type") map { - case "ExecutorBasedEventDriven" ⇒ new ExecutorBasedEventDrivenDispatcherConfigurator() - case "ExecutorBasedEventDrivenWorkStealing" ⇒ new ExecutorBasedEventDrivenWorkStealingDispatcherConfigurator() - case "GlobalExecutorBasedEventDriven" ⇒ GlobalExecutorBasedEventDrivenDispatcherConfigurator + case "Dispatcher" ⇒ new DispatcherConfigurator() + case "BalancingDispatcher" ⇒ new BalancingDispatcherConfigurator() + case "GlobalDispatcher" ⇒ GlobalDispatcherConfigurator case fqn ⇒ ReflectiveAccess.getClassFor[MessageDispatcherConfigurator](fqn) match { case r: Right[_, Class[MessageDispatcherConfigurator]] ⇒ @@ -212,13 +212,13 @@ object Dispatchers { } } -object GlobalExecutorBasedEventDrivenDispatcherConfigurator extends MessageDispatcherConfigurator { - def configure(config: Configuration): MessageDispatcher = Dispatchers.globalExecutorBasedEventDrivenDispatcher +object GlobalDispatcherConfigurator extends MessageDispatcherConfigurator { + def configure(config: Configuration): MessageDispatcher = Dispatchers.globalDispatcher } -class ExecutorBasedEventDrivenDispatcherConfigurator extends MessageDispatcherConfigurator { +class DispatcherConfigurator extends MessageDispatcherConfigurator { def configure(config: Configuration): MessageDispatcher = { - configureThreadPool(config, threadPoolConfig ⇒ new ExecutorBasedEventDrivenDispatcher( + configureThreadPool(config, threadPoolConfig ⇒ new Dispatcher( config.getString("name", newUuid.toString), config.getInt("throughput", Dispatchers.THROUGHPUT), config.getInt("throughput-deadline-time", Dispatchers.THROUGHPUT_DEADLINE_TIME_MILLIS), @@ -227,9 +227,9 @@ class ExecutorBasedEventDrivenDispatcherConfigurator extends MessageDispatcherCo } } -class ExecutorBasedEventDrivenWorkStealingDispatcherConfigurator extends MessageDispatcherConfigurator { +class BalancingDispatcherConfigurator extends MessageDispatcherConfigurator { def configure(config: Configuration): MessageDispatcher = { - configureThreadPool(config, threadPoolConfig ⇒ new ExecutorBasedEventDrivenWorkStealingDispatcher( + configureThreadPool(config, threadPoolConfig ⇒ new BalancingDispatcher( config.getString("name", newUuid.toString), config.getInt("throughput", Dispatchers.THROUGHPUT), config.getInt("throughput-deadline-time", Dispatchers.THROUGHPUT_DEADLINE_TIME_MILLIS), diff --git a/akka-actor/src/main/scala/akka/dispatch/ThreadBasedDispatcher.scala b/akka-actor/src/main/scala/akka/dispatch/PinnedDispatcher.scala similarity index 86% rename from akka-actor/src/main/scala/akka/dispatch/ThreadBasedDispatcher.scala rename to akka-actor/src/main/scala/akka/dispatch/PinnedDispatcher.scala index 101a2ae2c5..e03e6af9e2 100644 --- a/akka-actor/src/main/scala/akka/dispatch/ThreadBasedDispatcher.scala +++ b/akka-actor/src/main/scala/akka/dispatch/PinnedDispatcher.scala @@ -14,9 +14,9 @@ import akka.actor.{ Actor, ActorRef } * * @author Jonas Bonér */ -class ThreadBasedDispatcher(_actor: ActorRef, _name: String, _mailboxType: MailboxType) - extends ExecutorBasedEventDrivenDispatcher( - _name, Dispatchers.THROUGHPUT, -1, _mailboxType, ThreadBasedDispatcher.oneThread) { +class PinnedDispatcher(_actor: ActorRef, _name: String, _mailboxType: MailboxType) + extends Dispatcher( + _name, Dispatchers.THROUGHPUT, -1, _mailboxType, PinnedDispatcher.oneThread) { def this(_name: String, _mailboxType: MailboxType) = this(null, _name, _mailboxType) @@ -47,7 +47,7 @@ class ThreadBasedDispatcher(_actor: ActorRef, _name: String, _mailboxType: Mailb } } -object ThreadBasedDispatcher { +object PinnedDispatcher { val oneThread: ThreadPoolConfig = ThreadPoolConfig(allowCorePoolTimeout = true, corePoolSize = 1, maxPoolSize = 1) } diff --git a/akka-actor/src/main/scala/akka/event/EventHandler.scala b/akka-actor/src/main/scala/akka/event/EventHandler.scala index 5c2d7b89cc..28adf4946d 100644 --- a/akka-actor/src/main/scala/akka/event/EventHandler.scala +++ b/akka-actor/src/main/scala/akka/event/EventHandler.scala @@ -89,7 +89,7 @@ object EventHandler extends ListenerManagement { class EventHandlerException extends AkkaException - lazy val EventHandlerDispatcher = Dispatchers.newExecutorBasedEventDrivenDispatcher("event:handler").build + lazy val EventHandlerDispatcher = Dispatchers.newDispatcher("event:handler").build implicit object defaultListenerFormat extends StatelessActorFormat[DefaultListener] diff --git a/akka-cluster/src/main/scala/akka/cluster/Cluster.scala b/akka-cluster/src/main/scala/akka/cluster/Cluster.scala index 1eb0f624b3..48771b5bed 100644 --- a/akka-cluster/src/main/scala/akka/cluster/Cluster.scala +++ b/akka-cluster/src/main/scala/akka/cluster/Cluster.scala @@ -1580,7 +1580,7 @@ object RemoteClusterDaemon { val ADDRESS = "akka-cluster-daemon".intern // FIXME configure functionServerDispatcher to what? - val functionServerDispatcher = Dispatchers.newExecutorBasedEventDrivenDispatcher("akka:cloud:cluster:function:server").build + val functionServerDispatcher = Dispatchers.newDispatcher("akka:cloud:cluster:function:server").build } /** @@ -1591,7 +1591,7 @@ class RemoteClusterDaemon(cluster: ClusterNode) extends Actor { import RemoteClusterDaemon._ import Cluster._ - self.dispatcher = Dispatchers.newThreadBasedDispatcher(self) + self.dispatcher = Dispatchers.newPinnedDispatcher(self) def receive: Receive = { case message: RemoteDaemonMessageProtocol ⇒ diff --git a/akka-docs/cluster/durable-mailbox.rst b/akka-docs/cluster/durable-mailbox.rst index a1bc3add43..5ebc34df5a 100644 --- a/akka-docs/cluster/durable-mailbox.rst +++ b/akka-docs/cluster/durable-mailbox.rst @@ -59,13 +59,13 @@ actor is oblivious to which type of mailbox it is using. Here is an example:: or for a thread-based durable dispatcher:: - self.dispatcher = DurableThreadBasedDispatcher( + self.dispatcher = DurablePinnedDispatcher( self, FileDurableMailboxStorage) There are 2 different durable dispatchers, ``DurableEventBasedDispatcher`` and -``DurableThreadBasedDispatcher``, which are durable versions of -``ExecutorBasedEventDrivenDispatcher`` and ``ThreadBasedDispatcher``. +``DurablePinnedDispatcher``, which are durable versions of +``Dispatcher`` and ``PinnedDispatcher``. This gives you an excellent way of creating bulkheads in your application, where groups of actors sharing the same dispatcher also share the same backing @@ -120,7 +120,7 @@ Here is an example of how you can configure your dispatcher to use this mailbox: or for a thread-based durable dispatcher:: - self.dispatcher = DurableThreadBasedDispatcher( + self.dispatcher = DurablePinnedDispatcher( self, RedisDurableMailboxStorage) @@ -164,7 +164,7 @@ Here is an example of how you can configure your dispatcher to use this mailbox: or for a thread-based durable dispatcher:: - self.dispatcher = DurableThreadBasedDispatcher( + self.dispatcher = DurablePinnedDispatcher( self, ZooKeeperDurableMailboxStorage) @@ -202,7 +202,7 @@ Beanstalk documentation on how to do that. :: or for a thread-based durable dispatcher. :: - self.dispatcher = DurableThreadBasedDispatcher( + self.dispatcher = DurablePinnedDispatcher( self, BeanstalkDurableMailboxStorage) diff --git a/akka-docs/general/configuration.rst b/akka-docs/general/configuration.rst index bd6c9510e3..a6c045d42e 100644 --- a/akka-docs/general/configuration.rst +++ b/akka-docs/general/configuration.rst @@ -58,7 +58,7 @@ A custom ``akka.conf`` might look like this: "sample.myservice.Boot"] actor { - throughput = 10 # Throughput for ExecutorBasedEventDrivenDispatcher, set to 1 for complete fairness + throughput = 10 # Throughput for Dispatcher, set to 1 for complete fairness } remote { diff --git a/akka-docs/java/dispatchers.rst b/akka-docs/java/dispatchers.rst index 36bd22d8ba..b67cb1c02e 100644 --- a/akka-docs/java/dispatchers.rst +++ b/akka-docs/java/dispatchers.rst @@ -18,7 +18,7 @@ The event-based Actors currently consume ~600 bytes per Actor which means that y Default dispatcher ------------------ -For most scenarios the default settings are the best. Here we have one single event-based dispatcher for all Actors created. The dispatcher used is globalExecutorBasedEventDrivenDispatcher in akka.dispatch.Dispatchers. +For most scenarios the default settings are the best. Here we have one single event-based dispatcher for all Actors created. The dispatcher used is globalDispatcher in akka.dispatch.Dispatchers. But if you feel that you are starting to contend on the single dispatcher (the 'Executor' and its queue) or want to group a specific set of Actors for a dedicated dispatcher for better flexibility and configurability then you can override the defaults and define your own dispatcher. See below for details on which ones are available and how they can be configured. @@ -59,11 +59,11 @@ Let's now walk through the different dispatchers in more detail. Thread-based ^^^^^^^^^^^^ -The 'ThreadBasedDispatcher' binds a dedicated OS thread to each specific Actor. The messages are posted to a 'LinkedBlockingQueue' which feeds the messages to the dispatcher one by one. A 'ThreadBasedDispatcher' cannot be shared between actors. This dispatcher has worse performance and scalability than the event-based dispatcher but works great for creating "daemon" Actors that consumes a low frequency of messages and are allowed to go off and do their own thing for a longer period of time. Another advantage with this dispatcher is that Actors do not block threads for each other. +The 'PinnedDispatcher' binds a dedicated OS thread to each specific Actor. The messages are posted to a 'LinkedBlockingQueue' which feeds the messages to the dispatcher one by one. A 'PinnedDispatcher' cannot be shared between actors. This dispatcher has worse performance and scalability than the event-based dispatcher but works great for creating "daemon" Actors that consumes a low frequency of messages and are allowed to go off and do their own thing for a longer period of time. Another advantage with this dispatcher is that Actors do not block threads for each other. .. code-block:: java - Dispatcher dispatcher = Dispatchers.newThreadBasedDispatcher(actorRef); + Dispatcher dispatcher = Dispatchers.newPinnedDispatcher(actorRef); It would normally by used from within the actor like this: @@ -71,7 +71,7 @@ It would normally by used from within the actor like this: class MyActor extends UntypedActor { public MyActor() { - getContext().setDispatcher(Dispatchers.newThreadBasedDispatcher(getContext())); + getContext().setDispatcher(Dispatchers.newPinnedDispatcher(getContext())); } ... } @@ -79,7 +79,7 @@ It would normally by used from within the actor like this: Event-based ^^^^^^^^^^^ -The 'ExecutorBasedEventDrivenDispatcher' binds a set of Actors to a thread pool backed up by a 'BlockingQueue'. This dispatcher is highly configurable and supports a fluent configuration API to configure the 'BlockingQueue' (type of queue, max items etc.) as well as the thread pool. +The 'Dispatcher' binds a set of Actors to a thread pool backed up by a 'BlockingQueue'. This dispatcher is highly configurable and supports a fluent configuration API to configure the 'BlockingQueue' (type of queue, max items etc.) as well as the thread pool. The event-driven dispatchers **must be shared** between multiple Typed Actors and/or Actors. One best practice is to let each top-level Actor, e.g. the Actors you define in the declarative supervisor config, to get their own dispatcher but reuse the dispatcher for each new Actor that the top-level Actor creates. But you can also share dispatcher between multiple top-level Actors. This is very use-case specific and needs to be tried out on a case by case basis. The important thing is that Akka tries to provide you with the freedom you need to design and implement your system in the most efficient way in regards to performance, throughput and latency. @@ -109,7 +109,7 @@ Here is an example: class MyActor extends UntypedActor { public MyActor() { - getContext().setDispatcher(Dispatchers.newExecutorBasedEventDrivenDispatcher(name) + getContext().setDispatcher(Dispatchers.newDispatcher(name) .withNewThreadPoolWithLinkedBlockingQueueWithCapacity(100) .setCorePoolSize(16) .setMaxPoolSize(128) @@ -120,7 +120,7 @@ Here is an example: ... } -This 'ExecutorBasedEventDrivenDispatcher' allows you to define the 'throughput' it should have. This defines the number of messages for a specific Actor the dispatcher should process in one single sweep. +This 'Dispatcher' allows you to define the 'throughput' it should have. This defines the number of messages for a specific Actor the dispatcher should process in one single sweep. Setting this to a higher number will increase throughput but lower fairness, and vice versa. If you don't specify it explicitly then it uses the default value defined in the 'akka.conf' configuration file: .. code-block:: xml @@ -136,10 +136,10 @@ Browse the :ref:`scaladoc` or look at the code for all the options available. Priority event-based ^^^^^^^^^^^^^^^^^^^^ -Sometimes it's useful to be able to specify priority order of messages, that is done by using PriorityExecutorBasedEventDrivenDispatcher and supply +Sometimes it's useful to be able to specify priority order of messages, that is done by using PriorityDispatcher and supply a java.util.Comparator[MessageInvocation] or use a akka.dispatch.PriorityGenerator (recommended): -Creating a PriorityExecutorBasedEventDrivenDispatcher using PriorityGenerator: +Creating a PriorityDispatcher using PriorityGenerator: .. code-block:: java @@ -168,7 +168,7 @@ Creating a PriorityExecutorBasedEventDrivenDispatcher using PriorityGenerator: // We create an instance of the actor that will print out the messages it processes ActorRef ref = Actors.actorOf(MyActor.class); // We create a new Priority dispatcher and seed it with the priority generator - ref.setDispatcher(new PriorityExecutorBasedEventDrivenDispatcher("foo", gen)); + ref.setDispatcher(new PriorityDispatcher("foo", gen)); ref.start(); // Start the actor ref.getDispatcher().suspend(ref); // Suspending the actor so it doesn't start to treat the messages before we have enqueued all of them :-) @@ -196,14 +196,14 @@ lowpriority Work-stealing event-based ^^^^^^^^^^^^^^^^^^^^^^^^^ -The 'ExecutorBasedEventDrivenWorkStealingDispatcher' is a variation of the 'ExecutorBasedEventDrivenDispatcher' in which Actors of the same type can be set up to share this dispatcher and during execution time the different actors will steal messages from other actors if they have less messages to process. This can be a great way to improve throughput at the cost of a little higher latency. +The 'BalancingDispatcher' is a variation of the 'Dispatcher' in which Actors of the same type can be set up to share this dispatcher and during execution time the different actors will steal messages from other actors if they have less messages to process. This can be a great way to improve throughput at the cost of a little higher latency. Normally the way you use it is to define a static field to hold the dispatcher and then set in in the Actor explicitly. .. code-block:: java class MyActor extends UntypedActor { - public static MessageDispatcher dispatcher = Dispatchers.newExecutorBasedEventDrivenWorkStealingDispatcher(name).build(); + public static MessageDispatcher dispatcher = Dispatchers.newBalancingDispatcher(name).build(); public MyActor() { getContext().setDispatcher(dispatcher); @@ -236,7 +236,7 @@ Per-instance based configuration You can also do it on a specific dispatcher instance. -For the 'ExecutorBasedEventDrivenDispatcher' and the 'ExecutorBasedWorkStealingDispatcher' you can do it through their constructor +For the 'Dispatcher' and the 'ExecutorBasedWorkStealingDispatcher' you can do it through their constructor .. code-block:: java @@ -246,13 +246,13 @@ For the 'ExecutorBasedEventDrivenDispatcher' and the 'ExecutorBasedWorkStealingD Duration pushTimeout = new FiniteDuration(10, TimeUnit.SECONDS); MailboxType mailboxCapacity = new BoundedMailbox(false, capacity, pushTimeout); MessageDispatcher dispatcher = - Dispatchers.newExecutorBasedEventDrivenDispatcher(name, throughput, mailboxCapacity).build(); + Dispatchers.newDispatcher(name, throughput, mailboxCapacity).build(); getContext().setDispatcher(dispatcher); } ... } -For the 'ThreadBasedDispatcher', it is non-shareable between actors, and associates a dedicated Thread with the actor. +For the 'PinnedDispatcher', it is non-shareable between actors, and associates a dedicated Thread with the actor. Making it bounded (by specifying a capacity) is optional, but if you do, you need to provide a pushTimeout (default is 10 seconds). When trying to send a message to the Actor it will throw a MessageQueueAppendFailedException("BlockingMessageTransferQueue transfer timed out") if the message cannot be added to the mailbox within the time specified by the pushTimeout. .. code-block:: java @@ -261,7 +261,7 @@ Making it bounded (by specifying a capacity) is optional, but if you do, you nee public MyActor() { int mailboxCapacity = 100; Duration pushTimeout = new FiniteDuration(10, TimeUnit.SECONDS); - getContext().setDispatcher(Dispatchers.newThreadBasedDispatcher(getContext(), mailboxCapacity, pushTimeout)); + getContext().setDispatcher(Dispatchers.newPinnedDispatcher(getContext(), mailboxCapacity, pushTimeout)); } ... } diff --git a/akka-docs/project/migration-guide-0.9.x-0.10.x.rst b/akka-docs/project/migration-guide-0.9.x-0.10.x.rst index 78b2ddc303..ceaa42af9b 100644 --- a/akka-docs/project/migration-guide-0.9.x-0.10.x.rst +++ b/akka-docs/project/migration-guide-0.9.x-0.10.x.rst @@ -36,7 +36,7 @@ Configuration actor { timeout = 5 # default timeout for future based invocations - throughput = 5 # default throughput for ExecutorBasedEventDrivenDispatcher + throughput = 5 # default throughput for Dispatcher } ... } diff --git a/akka-docs/project/release-notes.rst b/akka-docs/project/release-notes.rst index 32b088c3a2..c6effb9b19 100644 --- a/akka-docs/project/release-notes.rst +++ b/akka-docs/project/release-notes.rst @@ -199,7 +199,7 @@ Release 1.0-MILESTONE1 - **FIX** - #420 REST endpoints should be able to be processed in parallel (Viktor Klang) - **FIX** - #422 Dispatcher config should work for ThreadPoolBuilder-based dispatchers (Viktor Klang) - **FIX** - #401 ActorRegistry should not leak memory (Viktor Klang) -- **FIX** - #250 Performance optimization for ExecutorBasedEventDrivenDispatcher (Viktor Klang) +- **FIX** - #250 Performance optimization for Dispatcher (Viktor Klang) - **FIX** - #419 Rename init and shutdown callbacks to preStart and postStop, and remove initTransactionalState (Viktor Klang) - **FIX** - #346 Make max no of restarts (and within) are now both optional (Viktor Klang) - **FIX** - #424 Actors self.supervisor not set by the time init() is called when started by startLink() (Viktor Klang) @@ -210,7 +210,7 @@ Release 1.0-MILESTONE1 - **FIX** - Logger.warn now properly works with varargs (Viktor Klang) - **FIX** - #450 Removed ActorRef lifeCycle boilerplate: Some(LifeCycle(Permanent)) => Permanent (Viktor Klang) - **FIX** - Moved ActorRef.trapExit into ActorRef.faultHandler and removed Option-boilerplate from faultHandler (Viktor Klang) -- **FIX** - ThreadBasedDispatcher cheaper for idling actors, also benefits from all that is ExecutorBasedEventDrivenDispatcher (Viktor Klang) +- **FIX** - PinnedDispatcher cheaper for idling actors, also benefits from all that is Dispatcher (Viktor Klang) - **FIX** - Fixing Futures.future, uses Actor.spawn under the hood, specify dispatcher to control where block is executed (Viktor Klang) - **FIX** - #469 Akka "dist" now uses a root folder to avoid loitering if unzipped in a folder (Viktor Klang) - **FIX** - Removed ScalaConfig, JavaConfig and rewrote Supervision configuration (Viktor Klang) @@ -224,7 +224,7 @@ Release 1.0-MILESTONE1 - **ADD** - #262 Add Java API for Agent (Viktor Klang) - **ADD** - #264 Add Java API for Dataflow (Viktor Klang) - **ADD** - Using JerseySimpleBroadcaster instead of JerseyBroadcaster in AkkaBroadcaster (Viktor Klang) -- **ADD** - #433 Throughput deadline added for ExecutorBasedEventDrivenDispatcher (Viktor Klang) +- **ADD** - #433 Throughput deadline added for Dispatcher (Viktor Klang) - **ADD** - Add possibility to set default cometSupport in akka.conf (Viktor Klang) - **ADD** - #451 Added possibility to use akka-http as a standalone REST server (Viktor Klang) - **ADD** - #446 Added support for Erlang-style receiveTimeout (Viktor Klang) @@ -308,7 +308,7 @@ Release 0.10 - Aug 21 2010 - **ADD** - Java API for the STM (Peter Vlugter) - **ADD** - #379 Create STM Atomic templates for Java API (Peter Vlugter) - **ADD** - #270 SBT plugin for Akka (Peter Vlugter) -- **ADD** - #198 support for ThreadBasedDispatcher in Spring config (Michael Kober) +- **ADD** - #198 support for PinnedDispatcher in Spring config (Michael Kober) - **ADD** - #377 support HawtDispatcher in Spring config (Michael Kober) - **ADD** - #376 support Spring config for untyped actors (Michael Kober) - **ADD** - #200 support WorkStealingDispatcher in Spring config (Michael Kober) diff --git a/akka-docs/scala/dispatchers.rst b/akka-docs/scala/dispatchers.rst index 91f3799d00..cc75b5c099 100644 --- a/akka-docs/scala/dispatchers.rst +++ b/akka-docs/scala/dispatchers.rst @@ -22,7 +22,7 @@ For most scenarios the default settings are the best. Here we have one single ev .. code-block:: scala - Dispatchers.globalExecutorBasedEventDrivenDispatcher + Dispatchers.globalDispatcher But if you feel that you are starting to contend on the single dispatcher (the 'Executor' and its queue) or want to group a specific set of Actors for a dedicated dispatcher for better flexibility and configurability then you can override the defaults and define your own dispatcher. See below for details on which ones are available and how they can be configured. @@ -61,7 +61,7 @@ Let's now walk through the different dispatchers in more detail. Thread-based ^^^^^^^^^^^^ -The 'ThreadBasedDispatcher' binds a dedicated OS thread to each specific Actor. The messages are posted to a 'LinkedBlockingQueue' which feeds the messages to the dispatcher one by one. A 'ThreadBasedDispatcher' cannot be shared between actors. This dispatcher has worse performance and scalability than the event-based dispatcher but works great for creating "daemon" Actors that consumes a low frequency of messages and are allowed to go off and do their own thing for a longer period of time. Another advantage with this dispatcher is that Actors do not block threads for each other. +The 'PinnedDispatcher' binds a dedicated OS thread to each specific Actor. The messages are posted to a 'LinkedBlockingQueue' which feeds the messages to the dispatcher one by one. A 'PinnedDispatcher' cannot be shared between actors. This dispatcher has worse performance and scalability than the event-based dispatcher but works great for creating "daemon" Actors that consumes a low frequency of messages and are allowed to go off and do their own thing for a longer period of time. Another advantage with this dispatcher is that Actors do not block threads for each other. It would normally by used from within the actor like this: @@ -69,7 +69,7 @@ It would normally by used from within the actor like this: class MyActor extends Actor { public MyActor() { - self.dispatcher = Dispatchers.newThreadBasedDispatcher(self) + self.dispatcher = Dispatchers.newPinnedDispatcher(self) } ... } @@ -77,7 +77,7 @@ It would normally by used from within the actor like this: Event-based ^^^^^^^^^^^ -The 'ExecutorBasedEventDrivenDispatcher' binds a set of Actors to a thread pool backed up by a 'BlockingQueue'. This dispatcher is highly configurable and supports a fluent configuration API to configure the 'BlockingQueue' (type of queue, max items etc.) as well as the thread pool. +The 'Dispatcher' binds a set of Actors to a thread pool backed up by a 'BlockingQueue'. This dispatcher is highly configurable and supports a fluent configuration API to configure the 'BlockingQueue' (type of queue, max items etc.) as well as the thread pool. The event-driven dispatchers **must be shared** between multiple Actors. One best practice is to let each top-level Actor, e.g. the Actors you define in the declarative supervisor config, to get their own dispatcher but reuse the dispatcher for each new Actor that the top-level Actor creates. But you can also share dispatcher between multiple top-level Actors. This is very use-case specific and needs to be tried out on a case by case basis. The important thing is that Akka tries to provide you with the freedom you need to design and implement your system in the most efficient way in regards to performance, throughput and latency. @@ -106,7 +106,7 @@ Here is an example: import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy class MyActor extends Actor { - self.dispatcher = Dispatchers.newExecutorBasedEventDrivenDispatcher(name) + self.dispatcher = Dispatchers.newDispatcher(name) .withNewThreadPoolWithLinkedBlockingQueueWithCapacity(100) .setCorePoolSize(16) .setMaxPoolSize(128) @@ -116,7 +116,7 @@ Here is an example: ... } -This 'ExecutorBasedEventDrivenDispatcher' allows you to define the 'throughput' it should have. This defines the number of messages for a specific Actor the dispatcher should process in one single sweep. +This 'Dispatcher' allows you to define the 'throughput' it should have. This defines the number of messages for a specific Actor the dispatcher should process in one single sweep. Setting this to a higher number will increase throughput but lower fairness, and vice versa. If you don't specify it explicitly then it uses the default value defined in the 'akka.conf' configuration file: .. code-block:: ruby @@ -132,10 +132,10 @@ Browse the `ScalaDoc `_ or look at the code for all the options availa Priority event-based ^^^^^^^^^^^^^^^^^^^^ -Sometimes it's useful to be able to specify priority order of messages, that is done by using PriorityExecutorBasedEventDrivenDispatcher and supply +Sometimes it's useful to be able to specify priority order of messages, that is done by using PriorityDispatcher and supply a java.util.Comparator[MessageInvocation] or use a akka.dispatch.PriorityGenerator (recommended): -Creating a PriorityExecutorBasedEventDrivenDispatcher using PriorityGenerator: +Creating a PriorityDispatcher using PriorityGenerator: .. code-block:: scala @@ -156,7 +156,7 @@ Creating a PriorityExecutorBasedEventDrivenDispatcher using PriorityGenerator: }) // We create a new Priority dispatcher and seed it with the priority generator - a.dispatcher = new PriorityExecutorBasedEventDrivenDispatcher("foo", gen) + a.dispatcher = new PriorityDispatcher("foo", gen) a.start // Start the Actor a.dispatcher.suspend(a) // Suspending the actor so it doesn't start to treat the messages before we have enqueued all of them :-) @@ -184,14 +184,14 @@ Prints: Work-stealing event-based ^^^^^^^^^^^^^^^^^^^^^^^^^ -The 'ExecutorBasedEventDrivenWorkStealingDispatcher' is a variation of the 'ExecutorBasedEventDrivenDispatcher' in which Actors of the same type can be set up to share this dispatcher and during execution time the different actors will steal messages from other actors if they have less messages to process. This can be a great way to improve throughput at the cost of a little higher latency. +The 'BalancingDispatcher' is a variation of the 'Dispatcher' in which Actors of the same type can be set up to share this dispatcher and during execution time the different actors will steal messages from other actors if they have less messages to process. This can be a great way to improve throughput at the cost of a little higher latency. Normally the way you use it is to create an Actor companion object to hold the dispatcher and then set in in the Actor explicitly. .. code-block:: scala object MyActor { - val dispatcher = Dispatchers.newExecutorBasedEventDrivenWorkStealingDispatcher(name).build + val dispatcher = Dispatchers.newBalancingDispatcher(name).build } class MyActor extends Actor { @@ -224,24 +224,24 @@ Per-instance based configuration You can also do it on a specific dispatcher instance. -For the 'ExecutorBasedEventDrivenDispatcher' and the 'ExecutorBasedWorkStealingDispatcher' you can do it through their constructor +For the 'Dispatcher' and the 'ExecutorBasedWorkStealingDispatcher' you can do it through their constructor .. code-block:: scala class MyActor extends Actor { val mailboxCapacity = BoundedMailbox(capacity = 100) - self.dispatcher = Dispatchers.newExecutorBasedEventDrivenDispatcher(name, throughput, mailboxCapacity).build + self.dispatcher = Dispatchers.newDispatcher(name, throughput, mailboxCapacity).build ... } -For the 'ThreadBasedDispatcher', it is non-shareable between actors, and associates a dedicated Thread with the actor. +For the 'PinnedDispatcher', it is non-shareable between actors, and associates a dedicated Thread with the actor. Making it bounded (by specifying a capacity) is optional, but if you do, you need to provide a pushTimeout (default is 10 seconds). When trying to send a message to the Actor it will throw a MessageQueueAppendFailedException("BlockingMessageTransferQueue transfer timed out") if the message cannot be added to the mailbox within the time specified by the pushTimeout. .. code-block:: scala class MyActor extends Actor { import akka.util.duration._ - self.dispatcher = Dispatchers.newThreadBasedDispatcher(self, mailboxCapacity = 100, + self.dispatcher = Dispatchers.newPinnedDispatcher(self, mailboxCapacity = 100, pushTimeOut = 10 seconds) ... } diff --git a/akka-durable-mailboxes/akka-mailboxes-common/src/main/scala/akka/actor/mailbox/DurableDispatcher.scala b/akka-durable-mailboxes/akka-mailboxes-common/src/main/scala/akka/actor/mailbox/DurableDispatcher.scala index 247956ff7c..8a1b2c60f1 100644 --- a/akka-durable-mailboxes/akka-mailboxes-common/src/main/scala/akka/actor/mailbox/DurableDispatcher.scala +++ b/akka-durable-mailboxes/akka-mailboxes-common/src/main/scala/akka/actor/mailbox/DurableDispatcher.scala @@ -52,7 +52,7 @@ case object FileDurableMailboxStorage extends DurableMailboxStorage("akka.a case object ZooKeeperDurableMailboxStorage extends DurableMailboxStorage("akka.actor.mailbox.ZooKeeperBasedMailbox") /** - * The durable equivalent of ExecutorBasedEventDrivenDispatcher + * The durable equivalent of Dispatcher * * @author Jonas Bonér */ @@ -62,7 +62,7 @@ case class DurableEventBasedDispatcher( _throughput: Int = Dispatchers.THROUGHPUT, _throughputDeadlineTime: Int = Dispatchers.THROUGHPUT_DEADLINE_TIME_MILLIS, _mailboxType: MailboxType = Dispatchers.MAILBOX_TYPE, - _config: ThreadPoolConfig = ThreadPoolConfig()) extends ExecutorBasedEventDrivenDispatcher( + _config: ThreadPoolConfig = ThreadPoolConfig()) extends Dispatcher( _name, _throughput, _throughputDeadlineTime, @@ -101,14 +101,14 @@ case class DurableEventBasedDispatcher( } /** - * The durable equivalent of ThreadBasedDispatcher + * The durable equivalent of PinnedDispatcher * * @author Jonas Bonér */ -case class DurableThreadBasedDispatcher( +case class DurablePinnedDispatcher( _actor: ActorRef, _storage: DurableMailboxStorage, - _mailboxType: MailboxType) extends ThreadBasedDispatcher(_actor,_mailboxType) { + _mailboxType: MailboxType) extends PinnedDispatcher(_actor,_mailboxType) { def this(actor: ActorRef, _storage: DurableMailboxStorage) = this(actor, _storage, UnboundedMailbox()) // For Java API diff --git a/akka-durable-mailboxes/akka-mailboxes-common/src/main/scala/akka/actor/mailbox/DurableMailbox.scala b/akka-durable-mailboxes/akka-mailboxes-common/src/main/scala/akka/actor/mailbox/DurableMailbox.scala index db5d98f04a..5b83f6a8c4 100644 --- a/akka-durable-mailboxes/akka-mailboxes-common/src/main/scala/akka/actor/mailbox/DurableMailbox.scala +++ b/akka-durable-mailboxes/akka-mailboxes-common/src/main/scala/akka/actor/mailbox/DurableMailbox.scala @@ -37,8 +37,8 @@ abstract class DurableExecutableMailbox(owner: ActorRef) extends MessageQueue wi EventHandler.debug(this, "Creating %s mailbox [%s]".format(getClass.getName, name)) - val dispatcher: ExecutorBasedEventDrivenDispatcher = owner.dispatcher match { - case e: ExecutorBasedEventDrivenDispatcher => e + val dispatcher: Dispatcher = owner.dispatcher match { + case e: Dispatcher => e case _ => null } diff --git a/akka-stm/src/main/scala/akka/agent/Agent.scala b/akka-stm/src/main/scala/akka/agent/Agent.scala index 0f371ea168..b556b90e50 100644 --- a/akka-stm/src/main/scala/akka/agent/Agent.scala +++ b/akka-stm/src/main/scala/akka/agent/Agent.scala @@ -293,7 +293,7 @@ class AgentUpdater[T](agent: Agent[T]) extends Actor { * Thread-based agent updater actor. Used internally for `sendOff` actions. */ class ThreadBasedAgentUpdater[T](agent: Agent[T]) extends Actor { - self.dispatcher = Dispatchers.newThreadBasedDispatcher(self) + self.dispatcher = Dispatchers.newPinnedDispatcher(self) val txFactory = TransactionFactory(familyName = "ThreadBasedAgentUpdater", readonly = false) diff --git a/config/akka-reference.conf b/config/akka-reference.conf index efb7492e66..8e5a39b280 100644 --- a/config/akka-reference.conf +++ b/config/akka-reference.conf @@ -30,8 +30,8 @@ akka { # - UntypedActor: sendRequestReply && sendRequestReplyFuture # - TypedActor: methods with non-void return type serialize-messages = off # Does a deep clone of (non-primitive) messages to ensure immutability - throughput = 5 # Default throughput for all ExecutorBasedEventDrivenDispatcher, set to 1 for complete fairness - throughput-deadline-time = -1 # Default throughput deadline for all ExecutorBasedEventDrivenDispatcher, set to 0 or negative for no deadline + throughput = 5 # Default throughput for all Dispatcher, set to 1 for complete fairness + throughput-deadline-time = -1 # Default throughput deadline for all Dispatcher, set to 0 or negative for no deadline dispatcher-shutdown-timeout = 1 # Using the akka.time-unit, how long dispatchers by default will wait for new actors until they shut down deployment { @@ -75,22 +75,22 @@ akka { } default-dispatcher { - type = "GlobalExecutorBasedEventDriven" # Must be one of the following, all "Global*" are non-configurable - # - ExecutorBasedEventDriven - # - ExecutorBasedEventDrivenWorkStealing - # - GlobalExecutorBasedEventDriven + type = "GlobalDispatcher" # Must be one of the following, all "Global*" are non-configurable + # - Dispatcher + # - BalancingDispatcher + # - GlobalDispatcher keep-alive-time = 60 # Keep alive time for threads core-pool-size-factor = 1.0 # No of core threads ... ceil(available processors * factor) max-pool-size-factor = 4.0 # Max no of threads ... ceil(available processors * factor) executor-bounds = -1 # Makes the Executor bounded, -1 is unbounded allow-core-timeout = on # Allow core threads to time out rejection-policy = "caller-runs" # abort, caller-runs, discard-oldest, discard - throughput = 5 # Throughput for ExecutorBasedEventDrivenDispatcher, set to 1 for complete fairness - throughput-deadline-time = -1 # Throughput deadline for ExecutorBasedEventDrivenDispatcher, set to 0 or negative for no deadline + throughput = 5 # Throughput for Dispatcher, set to 1 for complete fairness + throughput-deadline-time = -1 # Throughput deadline for Dispatcher, set to 0 or negative for no deadline mailbox-capacity = -1 # If negative (or zero) then an unbounded mailbox is used (default) # If positive then a bounded mailbox is used and the capacity is set using the property # NOTE: setting a mailbox to 'blocking' can be a bit dangerous, could lead to deadlock, use with care - # The following are only used for ExecutorBasedEventDriven and only if mailbox-capacity > 0 + # The following are only used for Dispatcher and only if mailbox-capacity > 0 mailbox-push-timeout-time = 10 # Specifies the timeout to add a new message to a mailbox that is full - negative number means infinite timeout # (in unit defined by the time-unit property) } @@ -197,7 +197,7 @@ akka { # If you are using akka.http.AkkaMistServlet mist-dispatcher { - #type = "GlobalExecutorBasedEventDriven" # Uncomment if you want to use a different dispatcher than the default one for Comet + #type = "GlobalDispatcher" # Uncomment if you want to use a different dispatcher than the default one for Comet } connection-close = true # toggles the addition of the "Connection" response header with a "close" value root-actor-id = "_httproot" # the id of the actor to use as the root endpoint diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 35c487d70c..53e79bb5e9 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -19,7 +19,7 @@ class AkkaParentProject(info: ProjectInfo) extends ParentProject(info) with Exec val scalaCompileSettings = Seq("-deprecation", //"-Xmigration", - //"-optimise", + "-optimise", "-encoding", "utf8") val javaCompileSettings = Seq("-Xlint:unchecked") From 50bf97bf54feb2838aa43e0b75dd482b9ec1e9d2 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Sat, 21 May 2011 12:53:46 +0200 Subject: [PATCH 04/20] Removing allocations and excessive traversals --- .../src/main/scala/akka/cluster/Routing.scala | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/akka-cluster/src/main/scala/akka/cluster/Routing.scala b/akka-cluster/src/main/scala/akka/cluster/Routing.scala index 68a0a29d3e..f207a8fda8 100644 --- a/akka-cluster/src/main/scala/akka/cluster/Routing.scala +++ b/akka-cluster/src/main/scala/akka/cluster/Routing.scala @@ -63,8 +63,8 @@ object Router { */ trait Direct extends Router { lazy val connection: Option[ActorRef] = { - if (connections.size == 0) throw new IllegalStateException("DirectRouter need a single replica connection found [0]") - connections.toList.map({ case (address, actor) ⇒ actor }).headOption + if (connections.isEmpty) throw new IllegalStateException("DirectRouter need a single replica connection found [0]") + connections.values.headOption } def route(message: Any)(implicit sender: Option[ActorRef]) { @@ -92,20 +92,17 @@ object Router { if (next.isDefined) next.get.!!!(message, timeout)(sender) else throw new RoutingException("No node connections for router") - private def next: Option[ActorRef] = { - val nrOfConnections = connections.size - if (nrOfConnections == 0) None - else Some(connections.toArray.apply(random.nextInt(nrOfConnections))._2) - } + private def next: Option[ActorRef] = + if (connections.isEmpty) None + else Some(connections.valuesIterator.drop(random.nextInt(connections.size)).next) } /** * @author Jonas Bonér */ trait RoundRobin extends Router { - private def items: List[ActorRef] = connections.toList.map({ case (address, actor) ⇒ actor }) + private def items: List[ActorRef] = connections.values.toList - @volatile private var current = items def route(message: Any)(implicit sender: Option[ActorRef]) { @@ -119,7 +116,7 @@ object Router { private def hasNext = items != Nil - private def next: Option[ActorRef] = { + private def next: Option[ActorRef] = synchronized { val rest = if (current == Nil) items else current current = rest.tail rest.headOption From 70137b45e531e8e91302f63570978656ccd6dfec Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Sat, 21 May 2011 13:13:28 +0200 Subject: [PATCH 05/20] Fixing a race in CyclicIterator --- akka-actor/src/main/scala/akka/routing/Routing.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/akka-actor/src/main/scala/akka/routing/Routing.scala b/akka-actor/src/main/scala/akka/routing/Routing.scala index 0ea431d88a..f63c312bac 100644 --- a/akka-actor/src/main/scala/akka/routing/Routing.scala +++ b/akka-actor/src/main/scala/akka/routing/Routing.scala @@ -80,18 +80,17 @@ trait InfiniteIterator[T] extends Iterator[T] { case class CyclicIterator[T](val items: Seq[T]) extends InfiniteIterator[T] { def this(items: java.util.List[T]) = this(items.toList) - @volatile private[this] var current: Seq[T] = items def hasNext = items != Nil - def next = { + def next = synchronized { val nc = if (current == Nil) items else current current = nc.tail nc.head } - override def exists(f: T ⇒ Boolean): Boolean = items.exists(f) + override def exists(f: T ⇒ Boolean): Boolean = items exists f } /** From e735b335fb2735332f3a7c7fd26961d5e8de2f60 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Sat, 21 May 2011 13:13:40 +0200 Subject: [PATCH 06/20] Removing lots of duplicated code --- .../src/main/scala/akka/cluster/Routing.scala | 56 +++++++------------ 1 file changed, 20 insertions(+), 36 deletions(-) diff --git a/akka-cluster/src/main/scala/akka/cluster/Routing.scala b/akka-cluster/src/main/scala/akka/cluster/Routing.scala index f207a8fda8..c81342c43b 100644 --- a/akka-cluster/src/main/scala/akka/cluster/Routing.scala +++ b/akka-cluster/src/main/scala/akka/cluster/Routing.scala @@ -53,46 +53,39 @@ object Router { trait Router { def connections: Map[InetSocketAddress, ActorRef] - def route(message: Any)(implicit sender: Option[ActorRef]) + def route(message: Any)(implicit sender: Option[ActorRef]): Unit def route[T](message: Any, timeout: Long)(implicit sender: Option[ActorRef]): Future[T] } - /** - * @author Jonas Bonér - */ - trait Direct extends Router { - lazy val connection: Option[ActorRef] = { - if (connections.isEmpty) throw new IllegalStateException("DirectRouter need a single replica connection found [0]") - connections.values.headOption + trait BasicRouter extends Router { + def route(message: Any)(implicit sender: Option[ActorRef]): Unit = next match { + case Some(actor) ⇒ actor.!(message)(sender) + case _ ⇒ throw new RoutingException("No node connections for router") } - def route(message: Any)(implicit sender: Option[ActorRef]) { - if (connection.isDefined) connection.get.!(message)(sender) - else throw new RoutingException("No node connections for router") + def route[T](message: Any, timeout: Long)(implicit sender: Option[ActorRef]): Future[T] = next match { + case Some(actor) ⇒ actor.!!!(message, timeout)(sender) + case _ ⇒ throw new RoutingException("No node connections for router") } - def route[T](message: Any, timeout: Long)(implicit sender: Option[ActorRef]): Future[T] = - if (connection.isDefined) connection.get.!!!(message, timeout)(sender) - else throw new RoutingException("No node connections for router") + protected def next: Option[ActorRef] } /** * @author Jonas Bonér */ - trait Random extends Router { + trait Direct extends BasicRouter { + lazy val next: Option[ActorRef] = connections.values.headOption + } + + /** + * @author Jonas Bonér + */ + trait Random extends BasicRouter { private val random = new java.util.Random(System.currentTimeMillis) - def route(message: Any)(implicit sender: Option[ActorRef]) { - if (next.isDefined) next.get.!(message)(sender) - else throw new RoutingException("No node connections for router") - } - - def route[T](message: Any, timeout: Long)(implicit sender: Option[ActorRef]): Future[T] = - if (next.isDefined) next.get.!!!(message, timeout)(sender) - else throw new RoutingException("No node connections for router") - - private def next: Option[ActorRef] = + def next: Option[ActorRef] = if (connections.isEmpty) None else Some(connections.valuesIterator.drop(random.nextInt(connections.size)).next) } @@ -100,23 +93,14 @@ object Router { /** * @author Jonas Bonér */ - trait RoundRobin extends Router { + trait RoundRobin extends BasicRouter { private def items: List[ActorRef] = connections.values.toList private var current = items - def route(message: Any)(implicit sender: Option[ActorRef]) { - if (next.isDefined) next.get.!(message)(sender) - else throw new RoutingException("No node connections for router") - } - - def route[T](message: Any, timeout: Long)(implicit sender: Option[ActorRef]): Future[T] = - if (next.isDefined) next.get.!!!(message, timeout)(sender) - else throw new RoutingException("No node connections for router") - private def hasNext = items != Nil - private def next: Option[ActorRef] = synchronized { + def next: Option[ActorRef] = synchronized { val rest = if (current == Nil) items else current current = rest.tail rest.headOption From 5f03cc5d875fd8c28b19324bab52487809e716ef Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Sat, 21 May 2011 15:37:09 +0200 Subject: [PATCH 07/20] Switching to a non-blocking strategy for the CyclicIterator and the RoundRobin router --- .../src/main/scala/akka/routing/Routing.scala | 22 +++++++++++++---- .../src/main/scala/akka/cluster/Routing.scala | 24 ++++++++++++++----- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/akka-actor/src/main/scala/akka/routing/Routing.scala b/akka-actor/src/main/scala/akka/routing/Routing.scala index f63c312bac..99b985053b 100644 --- a/akka-actor/src/main/scala/akka/routing/Routing.scala +++ b/akka-actor/src/main/scala/akka/routing/Routing.scala @@ -10,6 +10,8 @@ import akka.actor.Actor._ import akka.actor.ActorRef import scala.collection.JavaConversions._ import scala.collection.immutable.Seq +import java.util.concurrent.atomic.AtomicReference +import annotation.tailrec object Routing { @@ -80,14 +82,24 @@ trait InfiniteIterator[T] extends Iterator[T] { case class CyclicIterator[T](val items: Seq[T]) extends InfiniteIterator[T] { def this(items: java.util.List[T]) = this(items.toList) - private[this] var current: Seq[T] = items + private[this] val current: AtomicReference[Seq[T]] = new AtomicReference(items) def hasNext = items != Nil - def next = synchronized { - val nc = if (current == Nil) items else current - current = nc.tail - nc.head + def next: T = { + @tailrec + def findNext: T = { + val currentItems = current.get + val newItems = currentItems match { + case Nil ⇒ items + case xs ⇒ xs + } + + if (current.compareAndSet(currentItems, newItems.tail)) newItems.head + else findNext + } + + findNext } override def exists(f: T ⇒ Boolean): Boolean = items exists f diff --git a/akka-cluster/src/main/scala/akka/cluster/Routing.scala b/akka-cluster/src/main/scala/akka/cluster/Routing.scala index c81342c43b..d863299777 100644 --- a/akka-cluster/src/main/scala/akka/cluster/Routing.scala +++ b/akka-cluster/src/main/scala/akka/cluster/Routing.scala @@ -14,6 +14,8 @@ import akka.AkkaException import java.net.InetSocketAddress import com.eaio.uuid.UUID +import annotation.tailrec +import java.util.concurrent.atomic.AtomicReference class RoutingException(message: String) extends AkkaException(message) @@ -96,14 +98,24 @@ object Router { trait RoundRobin extends BasicRouter { private def items: List[ActorRef] = connections.values.toList - private var current = items + private val current = new AtomicReference[List[ActorRef]](items) - private def hasNext = items != Nil + private def hasNext = connections.nonEmpty - def next: Option[ActorRef] = synchronized { - val rest = if (current == Nil) items else current - current = rest.tail - rest.headOption + def next: Option[ActorRef] = { + @tailrec + def findNext: Option[ActorRef] = { + val currentItems = current.get + val newItems = currentItems match { + case Nil ⇒ items + case xs ⇒ xs + } + + if (current.compareAndSet(currentItems, newItems.tail)) newItems.headOption + else findNext + } + + findNext } } } From e5035be5fab833e42aab200488833c70e80448ac Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Sat, 21 May 2011 15:55:15 +0200 Subject: [PATCH 08/20] Tidying up some code in ClusteredActorRef --- .../scala/akka/cluster/ClusterActorRef.scala | 35 +++++++------------ 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/akka-cluster/src/main/scala/akka/cluster/ClusterActorRef.scala b/akka-cluster/src/main/scala/akka/cluster/ClusterActorRef.scala index ff6b89506d..c8ac1012f4 100644 --- a/akka-cluster/src/main/scala/akka/cluster/ClusterActorRef.scala +++ b/akka-cluster/src/main/scala/akka/cluster/ClusterActorRef.scala @@ -29,13 +29,14 @@ class ClusterActorRef private[akka] ( EventHandler.debug(this, "Creating a ClusterActorRef for actor with address [%s]".format(address)) private[akka] val addresses = new AtomicReference[Map[InetSocketAddress, ActorRef]]( - createConnections(actorAddresses)) + (Map[InetSocketAddress, ActorRef]() /: actorAddresses) { + case (map, (uuid, address)) ⇒ map + (address -> createRemoteActorRef(uuid, address)) + }) - def connections: Map[InetSocketAddress, ActorRef] = addresses.get.toMap + def connections: Map[InetSocketAddress, ActorRef] = addresses.get - override def postMessageToMailbox(message: Any, senderOption: Option[ActorRef]) { + override def postMessageToMailbox(message: Any, senderOption: Option[ActorRef]): Unit = route(message)(senderOption) - } override def postMessageToMailboxAndCreateFutureResultWithTimeout[T]( message: Any, @@ -46,26 +47,14 @@ class ClusterActorRef private[akka] ( private[akka] def failOver(from: InetSocketAddress, to: InetSocketAddress) { addresses set (addresses.get map { - case (address, actorRef) ⇒ - if (address == from) { - actorRef.stop() - (to, createRemoteActorRef(actorRef.uuid, to)) - } else (address, actorRef) + case (`from`, actorRef) ⇒ + actorRef.stop() + (to, createRemoteActorRef(actorRef.uuid, to)) + case other ⇒ other }) } - private def createConnections(addresses: Array[Tuple2[UUID, InetSocketAddress]]): Map[InetSocketAddress, ActorRef] = { - var connections = Map.empty[InetSocketAddress, ActorRef] - addresses foreach { - case (uuid, address) ⇒ - connections = connections + (address -> createRemoteActorRef(uuid, address)) - } - connections - } - - private def createRemoteActorRef(uuid: UUID, address: InetSocketAddress) = { - RemoteActorRef( - UUID_PREFIX + uuidToString(uuid), // clustered refs are always registered and looked up by UUID - Actor.TIMEOUT, None) - } + // clustered refs are always registered and looked up by UUID + private def createRemoteActorRef(uuid: UUID, address: InetSocketAddress) = + RemoteActorRef(UUID_PREFIX + uuidToString(uuid), Actor.TIMEOUT, None) } From 2f87da5dd433f9b52e528787aad1fe9895d7599d Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Sat, 21 May 2011 16:14:15 +0200 Subject: [PATCH 09/20] Removing some boilerplate code in Deployer --- .../src/main/scala/akka/actor/Deployer.scala | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/akka-actor/src/main/scala/akka/actor/Deployer.scala b/akka-actor/src/main/scala/akka/actor/Deployer.scala index d9e937487a..0fb15e7071 100644 --- a/akka-actor/src/main/scala/akka/actor/Deployer.scala +++ b/akka-actor/src/main/scala/akka/actor/Deployer.scala @@ -358,19 +358,11 @@ object LocalDeployer { } } - private[akka] def undeploy(deployment: Deploy) { - deployments.remove(deployment.address) - } + private[akka] def undeploy(deployment: Deploy): Unit = deployments.remove(deployment.address) - private[akka] def undeployAll() { - deployments.clear() - } + private[akka] def undeployAll(): Unit = deployments.clear() - private[akka] def lookupDeploymentFor(address: String): Option[Deploy] = { - val deployment = deployments.get(address) - if (deployment eq null) None - else Some(deployment) - } + private[akka] def lookupDeploymentFor(address: String): Option[Deploy] = Option(deployments.get(address)) } /** From 00f837418f4d75761e80b0a26345e62028bd3502 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Sat, 21 May 2011 16:55:32 +0200 Subject: [PATCH 10/20] Reformatting and some cleanup of the Cluster.scala code --- .../src/main/scala/akka/cluster/Cluster.scala | 64 +++++++------------ 1 file changed, 23 insertions(+), 41 deletions(-) diff --git a/akka-cluster/src/main/scala/akka/cluster/Cluster.scala b/akka-cluster/src/main/scala/akka/cluster/Cluster.scala index 48771b5bed..8d0f322dc8 100644 --- a/akka-cluster/src/main/scala/akka/cluster/Cluster.scala +++ b/akka-cluster/src/main/scala/akka/cluster/Cluster.scala @@ -227,22 +227,19 @@ object Cluster { properties = properties + property } - private def nodename: String = { - val overridden = properties.get("akka.cluster.nodename") - if (overridden.isDefined) overridden.get - else Config.nodename + private def nodename: String = properties.get("akka.cluster.nodename") match { + case Some(uberride) ⇒ uberride + case None ⇒ Config.nodename } - private def hostname: String = { - val overridden = properties.get("akka.cluster.hostname") - if (overridden.isDefined) overridden.get - else Config.hostname + private def hostname: String = properties.get("akka.cluster.hostname") match { + case Some(uberride) ⇒ uberride + case None ⇒ Config.hostname } - private def port: Int = { - val overridden = properties.get("akka.cluster.port") - if (overridden.isDefined) overridden.get.toInt - else Config.remoteServerPort + private def port: Int = properties.get("akka.cluster.port") match { + case Some(uberride) ⇒ uberride.toInt + case None ⇒ Config.remoteServerPort } val defaultSerializer = new SerializableSerializer @@ -1024,11 +1021,10 @@ class ClusterNode private[akka] ( zkClient.readData(actorRegistryFormatPathFor(uuid), new Stat).asInstanceOf[Serializer] } - val format = formats.head if (formats.isEmpty) throw new IllegalStateException("No Serializer found for [%s]".format(actorAddress)) - if (formats map (_ == format) exists (_ == false)) throw new IllegalStateException( - "Multiple Serializer classes found for [%s]".format(actorAddress)) - format + if (formats.forall(_ == formats.head) == false) throw new IllegalStateException("Multiple Serializer classes found for [%s]".format(actorAddress)) + + formats.head } /** @@ -1126,9 +1122,7 @@ class ClusterNode private[akka] ( } } }) match { - case Left(_) ⇒ { - /* do nothing */ - } + case Left(_) ⇒ /* do nothing */ case Right(exception) ⇒ throw exception } } @@ -1429,23 +1423,15 @@ class ClusterNode private[akka] ( import Cluster._ - override def start() { - self.start() - } + override def start(): Unit = self.start() - override def stop() { - self.shutdown() - } + override def stop(): Unit = self.shutdown() override def disconnect() = self.disconnect() - override def reconnect() { - self.reconnect() - } + override def reconnect(): Unit = self.reconnect() - override def resign() { - self.resign() - } + override def resign(): Unit = self.resign() override def isConnected = self.isConnected.isOn @@ -1479,15 +1465,11 @@ class ClusterNode private[akka] ( override def getAddressesForActorsInUseOnNode(nodeName: String) = self.addressesForActorsInUseOnNode(nodeName).map(_.toString).toArray - override def setConfigElement(key: String, value: String) { - self.setConfigElement(key, value.getBytes("UTF-8")) - } + override def setConfigElement(key: String, value: String): Unit = self.setConfigElement(key, value.getBytes("UTF-8")) override def getConfigElement(key: String) = new String(self.getConfigElement(key), "UTF-8") - override def removeConfigElement(key: String) { - self.removeConfigElement(key) - } + override def removeConfigElement(key: String): Unit = self.removeConfigElement(key) override def getConfigElementKeys = self.getConfigElementKeys.toArray } @@ -1664,8 +1646,8 @@ class RemoteClusterDaemon(cluster: ClusterNode) extends Actor { self.dispatcher = functionServerDispatcher def receive = { - case t: Tuple2[Function1[Any, Unit], Any] ⇒ try { - t._1(t._2) + case (fun: Function[Any, Unit], param: Any) ⇒ try { + fun(param) } finally { self.stop() } @@ -1677,8 +1659,8 @@ class RemoteClusterDaemon(cluster: ClusterNode) extends Actor { self.dispatcher = functionServerDispatcher def receive = { - case t: Tuple2[Function1[Any, Any], Any] ⇒ try { - self.reply(t._1(t._2)) + case (fun: Function[Any, Unit], param: Any) ⇒ try { + self.reply(fun(param)) } finally { self.stop() } From d84a169747c2f5292723b4e9180184e192593f35 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Sat, 21 May 2011 17:30:16 +0200 Subject: [PATCH 11/20] Removing excessive allocations and traversal --- .../src/main/scala/akka/cluster/Cluster.scala | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/akka-cluster/src/main/scala/akka/cluster/Cluster.scala b/akka-cluster/src/main/scala/akka/cluster/Cluster.scala index 8d0f322dc8..a32c462441 100644 --- a/akka-cluster/src/main/scala/akka/cluster/Cluster.scala +++ b/akka-cluster/src/main/scala/akka/cluster/Cluster.scala @@ -955,7 +955,9 @@ class ClusterNode private[akka] ( */ def uuidsForActorAddress(actorAddress: String): Array[UUID] = if (isConnected.isOn) { try { - zkClient.getChildren(actorAddressToUuidsPathFor(actorAddress)).toList.map(new UUID(_)).toArray.asInstanceOf[Array[UUID]] + zkClient.getChildren(actorAddressToUuidsPathFor(actorAddress)).toArray map { + case c: CharSequence ⇒ new UUID(c) + } } catch { case e: ZkNoNodeException ⇒ Array[UUID]() } @@ -966,7 +968,7 @@ class ClusterNode private[akka] ( */ def nodesForActorsInUseWithUuid(uuid: UUID): Array[String] = if (isConnected.isOn) { try { - zkClient.getChildren(actorLocationsPathFor(uuid)).toList.toArray.asInstanceOf[Array[String]] + zkClient.getChildren(actorLocationsPathFor(uuid)).toArray.asInstanceOf[Array[String]] } catch { case e: ZkNoNodeException ⇒ Array[String]() } @@ -979,8 +981,7 @@ class ClusterNode private[akka] ( flatten { actorUuidsForActorAddress(address) map { uuid ⇒ try { - val list = zkClient.getChildren(actorLocationsPathFor(uuid)) - list.toList.toArray.asInstanceOf[Array[String]] + zkClient.getChildren(actorLocationsPathFor(uuid)).toArray.asInstanceOf[Array[String]] } catch { case e: ZkNoNodeException ⇒ Array[String]() } @@ -993,7 +994,9 @@ class ClusterNode private[akka] ( */ def uuidsForActorsInUseOnNode(nodeName: String): Array[UUID] = if (isConnected.isOn) { try { - zkClient.getChildren(actorsAtNodePathFor(nodeName)).toList.map(new UUID(_)).toArray.asInstanceOf[Array[UUID]] + zkClient.getChildren(actorsAtNodePathFor(nodeName)).toArray map { + case c: CharSequence ⇒ new UUID(c) + } } catch { case e: ZkNoNodeException ⇒ Array[UUID]() } @@ -1005,7 +1008,9 @@ class ClusterNode private[akka] ( def addressesForActorsInUseOnNode(nodeName: String): Array[String] = if (isConnected.isOn) { val uuids = try { - zkClient.getChildren(actorsAtNodePathFor(nodeName)).toList.map(new UUID(_)).toArray.asInstanceOf[Array[UUID]] + zkClient.getChildren(actorsAtNodePathFor(nodeName)).toArray map { + case c: CharSequence ⇒ new UUID(c) + } } catch { case e: ZkNoNodeException ⇒ Array[UUID]() } From ce69b25593069a913ddd316e5123ec20ec1f1fd1 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Mon, 23 May 2011 16:45:46 +1200 Subject: [PATCH 12/20] Add individual options and config to multi-jvm tests --- .../akka/cluster/ClusterMultiJvmSpec.scala | 146 ------------------ .../cluster/multi/ClusterMultiJvmNode1.conf | 1 + .../cluster/multi/ClusterMultiJvmNode1.opts | 1 + .../cluster/multi/ClusterMultiJvmNode2.conf | 1 + .../cluster/multi/ClusterMultiJvmNode2.opts | 1 + .../cluster/multi/ClusterMultiJvmSpec.scala | 88 +++++++++++ project/build/AkkaProject.scala | 13 ++ project/build/MultiJvmTests.scala | 72 ++++++--- 8 files changed, 154 insertions(+), 169 deletions(-) delete mode 100644 akka-cluster/src/test/scala/akka/cluster/ClusterMultiJvmSpec.scala create mode 100644 akka-cluster/src/test/scala/akka/cluster/multi/ClusterMultiJvmNode1.conf create mode 100644 akka-cluster/src/test/scala/akka/cluster/multi/ClusterMultiJvmNode1.opts create mode 100644 akka-cluster/src/test/scala/akka/cluster/multi/ClusterMultiJvmNode2.conf create mode 100644 akka-cluster/src/test/scala/akka/cluster/multi/ClusterMultiJvmNode2.opts create mode 100644 akka-cluster/src/test/scala/akka/cluster/multi/ClusterMultiJvmSpec.scala diff --git a/akka-cluster/src/test/scala/akka/cluster/ClusterMultiJvmSpec.scala b/akka-cluster/src/test/scala/akka/cluster/ClusterMultiJvmSpec.scala deleted file mode 100644 index 36408dd76a..0000000000 --- a/akka-cluster/src/test/scala/akka/cluster/ClusterMultiJvmSpec.scala +++ /dev/null @@ -1,146 +0,0 @@ -/** - * Copyright (C) 2009-2011 Scalable Solutions AB - */ - -package akka.cluster - -import org.scalatest.WordSpec -import org.scalatest.matchers.MustMatchers -import org.scalatest.{ BeforeAndAfterAll, BeforeAndAfterEach } - -import akka.cluster.zookeeper._ -import org.I0Itec.zkclient._ - -object MultiNodeTest { - val NrOfNodes = 2 - val ClusterName = "test-cluster" - val DataPath = "_akka_cluster/data" - val LogPath = "_akka_cluster/log" -} - -trait MultiNodeTest extends WordSpec with MustMatchers with BeforeAndAfterAll with BeforeAndAfterEach { - import MultiNodeTest._ - - val nodeNr = nodeNumber - val port = 9000 + nodeNumber - - var zkServer: ZkServer = _ - var zkClient: ZkClient = _ - - def nodeNumber: Int - - def createNode = Cluster.node - - def barrier(name: String) = ZooKeeperBarrier(zkClient, ClusterName, name, "node-" + nodeNr, NrOfNodes) - - override def beforeAll() = { - if (nodeNr == 1) zkServer = Cluster.startLocalCluster(DataPath, LogPath) - zkClient = Cluster.newZkClient - } - - override def beforeEach() = { - // if (nodeNr == 1) Cluster.reset - } - - override def afterAll() = { - zkClient.close - if (nodeNr == 1) Cluster.shutdownLocalCluster - } -} - -class ClusterMultiJvmNode1 extends MultiNodeTest { - def nodeNumber = 1 - - "A cluster" should { - - "be able to start and stop - one node" in { - - Cluster setProperty ("akka.cluster.nodename" -> "node1") - Cluster setProperty ("akka.cluster.port" -> "9991") - import Cluster.node - - barrier("start-stop") { - node.start() - - Thread.sleep(500) - node.membershipNodes.size must be(1) - - // node.stop() - - Thread.sleep(500) - // node.membershipNodes.size must be(0) - // node.isRunning must be(false) - } - } - - "be able to start and stop - two nodes" in { - import Cluster.node - - barrier("start-node1") { - node.start() - Thread.sleep(500) - node.membershipNodes.size must be(1) - } - - barrier("start-node2") { - // let node2 start - } - - node.membershipNodes.size must be(2) - node.leader must be(node.leaderLock.getId) - - barrier("stop-node1") { - // node.stop() - Thread.sleep(500) - // node.isRunning must be(false) - } - - barrier("stop-node2") { - // let node2 stop - } - } - } -} - -class ClusterMultiJvmNode2 extends MultiNodeTest { - def nodeNumber = 2 - - "A cluster" should { - - "be able to start and stop - one node" in { - Cluster setProperty ("akka.cluster.nodename" -> "node2") - Cluster setProperty ("akka.cluster.port" -> "9992") - - barrier("start-stop") { - // let node1 start - } - } - - "be able to start and stop - two nodes" in { - import Cluster.node - - barrier("start-node1") { - // let node1 start - } - - barrier("start-node2") { - node.start() - Thread.sleep(500) - node.membershipNodes.size must be(2) - } - - barrier("stop-node1") { - // let node1 stop - } - - // node.membershipNodes.size must be(1) - // node.leader must be(node.leaderLock.getId) - - barrier("stop-node2") { - // node.stop() - // Thread.sleep(500) - // node.isRunning must be(false) - } - } - } -} diff --git a/akka-cluster/src/test/scala/akka/cluster/multi/ClusterMultiJvmNode1.conf b/akka-cluster/src/test/scala/akka/cluster/multi/ClusterMultiJvmNode1.conf new file mode 100644 index 0000000000..946238d603 --- /dev/null +++ b/akka-cluster/src/test/scala/akka/cluster/multi/ClusterMultiJvmNode1.conf @@ -0,0 +1 @@ +test.name = "node1" diff --git a/akka-cluster/src/test/scala/akka/cluster/multi/ClusterMultiJvmNode1.opts b/akka-cluster/src/test/scala/akka/cluster/multi/ClusterMultiJvmNode1.opts new file mode 100644 index 0000000000..a88c260d8c --- /dev/null +++ b/akka-cluster/src/test/scala/akka/cluster/multi/ClusterMultiJvmNode1.opts @@ -0,0 +1 @@ +-Dakka.cluster.nodename=node1 -Dakka.cluster.port=9991 diff --git a/akka-cluster/src/test/scala/akka/cluster/multi/ClusterMultiJvmNode2.conf b/akka-cluster/src/test/scala/akka/cluster/multi/ClusterMultiJvmNode2.conf new file mode 100644 index 0000000000..deeeb05a48 --- /dev/null +++ b/akka-cluster/src/test/scala/akka/cluster/multi/ClusterMultiJvmNode2.conf @@ -0,0 +1 @@ +test.name = "node2" diff --git a/akka-cluster/src/test/scala/akka/cluster/multi/ClusterMultiJvmNode2.opts b/akka-cluster/src/test/scala/akka/cluster/multi/ClusterMultiJvmNode2.opts new file mode 100644 index 0000000000..f1e01f253d --- /dev/null +++ b/akka-cluster/src/test/scala/akka/cluster/multi/ClusterMultiJvmNode2.opts @@ -0,0 +1 @@ +-Dakka.cluster.nodename=node2 -Dakka.cluster.port=9992 diff --git a/akka-cluster/src/test/scala/akka/cluster/multi/ClusterMultiJvmSpec.scala b/akka-cluster/src/test/scala/akka/cluster/multi/ClusterMultiJvmSpec.scala new file mode 100644 index 0000000000..fe1e07109f --- /dev/null +++ b/akka-cluster/src/test/scala/akka/cluster/multi/ClusterMultiJvmSpec.scala @@ -0,0 +1,88 @@ +/** + * Copyright (C) 2009-2011 Scalable Solutions AB + */ + +package akka.cluster.multi + +import org.scalatest.WordSpec +import org.scalatest.matchers.MustMatchers +import org.scalatest.BeforeAndAfterAll + +import akka.cluster._ + +object ClusterMultiJvmSpec { + val NrOfNodes = 2 +} + +class ClusterMultiJvmNode1 extends WordSpec with MustMatchers with BeforeAndAfterAll { + import ClusterMultiJvmSpec._ + + override def beforeAll() = { + Cluster.startLocalCluster() + // resetCluster() + } + + override def afterAll() = { + Cluster.shutdownLocalCluster() + } + + def resetCluster(): Unit = { + import akka.cluster.zookeeper._ + import akka.util.Helpers.ignore + import org.I0Itec.zkclient.exception.ZkNoNodeException + val zkClient = Cluster.newZkClient + ignore[ZkNoNodeException](zkClient.deleteRecursive("/" + Cluster.name)) + ignore[ZkNoNodeException](zkClient.deleteRecursive(ZooKeeperBarrier.BarriersNode)) + zkClient.close + } + + "A cluster" must { + + "have jvm options" in { + System.getProperty("akka.cluster.nodename", "") must be("node1") + System.getProperty("akka.cluster.port", "") must be("9991") + akka.config.Config.config.getString("test.name", "") must be("node1") + } + + "be able to start all nodes" in { + Cluster.node.barrier("start", NrOfNodes) { + // Cluster.node.start() + } + // Cluster.node.isRunning must be(true) + } + + "be able to shutdown all nodes" in { + Cluster.node.barrier("shutdown", NrOfNodes) { + // Cluster.node.shutdown() + } + // Cluster.node.isRunning must be(false) + } + } +} + +class ClusterMultiJvmNode2 extends WordSpec with MustMatchers { + import ClusterMultiJvmSpec._ + + "A cluster" must { + + "have jvm options" in { + System.getProperty("akka.cluster.nodename", "") must be("node2") + System.getProperty("akka.cluster.port", "") must be("9992") + akka.config.Config.config.getString("test.name", "") must be("node2") + } + + "be able to start all nodes" in { + Cluster.node.barrier("start", NrOfNodes) { + // Cluster.node.start() + } + // Cluster.node.isRunning must be(true) + } + + "be able to shutdown all nodes" in { + Cluster.node.barrier("shutdown", NrOfNodes) { + // Cluster.node.shutdown() + } + // Cluster.node.isRunning must be(false) + } + } +} diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 53e79bb5e9..255cb03091 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -324,8 +324,21 @@ class AkkaParentProject(info: ProjectInfo) extends ParentProject(info) with Exec lazy val clusterTest = multiJvmTest lazy val clusterRun = multiJvmRun + // test task runs normal tests and then all multi-jvm tests + lazy val normalTest = super.testAction + override def multiJvmTestAllAction = super.multiJvmTestAllAction dependsOn (normalTest) + override def testAction = task { None } dependsOn (normalTest, multiJvmTestAll) + override def multiJvmOptions = Seq("-Xmx256M") + override def multiJvmExtraOptions(className: String) = { + val confFiles = (testSourcePath ** (className + ".conf")).get + if (!confFiles.isEmpty) { + val filePath = confFiles.toList.head.absolutePath + Seq("-Dakka.config=" + filePath) + } else Seq.empty + } + lazy val replicationTestsEnabled = systemOptional[Boolean]("cluster.test.replication", false) override def testOptions = diff --git a/project/build/MultiJvmTests.scala b/project/build/MultiJvmTests.scala index cc54e5d2a1..94894ecc71 100644 --- a/project/build/MultiJvmTests.scala +++ b/project/build/MultiJvmTests.scala @@ -6,9 +6,10 @@ import java.io.{BufferedReader, Closeable, InputStream, InputStreamReader, IOExc import java.io.{PipedInputStream, PipedOutputStream} import scala.concurrent.SyncVar -trait MultiJvmTests extends BasicScalaProject { +trait MultiJvmTests extends DefaultProject { def multiJvmTestName = "MultiJvm" - def multiJvmOptions: Seq[String] = Nil + def multiJvmOptions: Seq[String] = Seq.empty + def multiJvmExtraOptions(className: String): Seq[String] = Seq.empty val MultiJvmTestName = multiJvmTestName @@ -25,32 +26,38 @@ trait MultiJvmTests extends BasicScalaProject { lazy val multiJvmTest = multiJvmTestAction lazy val multiJvmRun = multiJvmRunAction + lazy val multiJvmTestAll = multiJvmTestAllAction - def multiJvmTestAction = multiJvmAction(getMultiJvmTests, testScalaOptions) - def multiJvmRunAction = multiJvmAction(getMultiJvmApps, runScalaOptions) + def multiJvmTestAction = multiJvmMethod(getMultiJvmTests, testScalaOptions) + def multiJvmRunAction = multiJvmMethod(getMultiJvmApps, runScalaOptions) + def multiJvmTestAllAction = multiJvmTask(Nil, getMultiJvmTests, testScalaOptions) - def multiJvmAction(getMultiTestsMap: => Map[String, Seq[String]], scalaOptions: String => Seq[String]) = { + def multiJvmMethod(getMultiTestsMap: => Map[String, Seq[String]], scalaOptions: String => Seq[String]) = { task { args => - task { - val multiTestsMap = getMultiTestsMap - def process(tests: List[String]): Option[String] = { - if (tests.isEmpty) { - None - } else { - val testName = tests(0) - val failed = multiTestsMap.get(testName) match { - case Some(testClasses) => runMulti(testName, testClasses, scalaOptions) - case None => Some("No multi jvm test called " + testName) - } - failed orElse process(tests.tail) - } - } - val tests = if (args.size > 0) args.toList else multiTestsMap.keys.toList.asInstanceOf[List[String]] - process(tests) - } dependsOn (testCompile) + multiJvmTask(args.toList, getMultiTestsMap, scalaOptions) } completeWith(getMultiTestsMap.keys.toList) } + def multiJvmTask(tests: List[String], getMultiTestsMap: => Map[String, Seq[String]], scalaOptions: String => Seq[String]) = { + task { + val multiTestsMap = getMultiTestsMap + def process(runTests: List[String]): Option[String] = { + if (runTests.isEmpty) { + None + } else { + val testName = runTests(0) + val failed = multiTestsMap.get(testName) match { + case Some(testClasses) => runMulti(testName, testClasses, scalaOptions) + case None => Some("No multi jvm test called " + testName) + } + failed orElse process(runTests.tail) + } + } + val runTests = if (tests.size > 0) tests else multiTestsMap.keys.toList.asInstanceOf[List[String]] + process(runTests) + } dependsOn (testCompile) + } + def getMultiJvmTests(): Map[String, Seq[String]] = { val allTests = testCompileConditional.analysis.allTests.toList.map(_.className) filterMultiJvmTests(allTests) @@ -80,6 +87,10 @@ trait MultiJvmTests extends BasicScalaProject { className.substring(i + l) } + def testSimpleName(className: String) = { + className.split("\\.").last + } + def testScalaOptions(testClass: String) = { val scalaTestJars = testClasspath.get.filter(_.name.contains("scalatest")) val cp = Path.makeString(scalaTestJars) @@ -98,8 +109,23 @@ trait MultiJvmTests extends BasicScalaProject { case (testClass, index) => { val jvmName = "JVM-" + testIdentifier(testClass) val jvmLogger = new JvmLogger(jvmName) + val className = testSimpleName(testClass) + val optionsFiles = (testSourcePath ** (className + ".opts")).get + val optionsFromFile: Seq[String] = { + if (!optionsFiles.isEmpty) { + val file = optionsFiles.toList.head.asFile + log.info("Reading JVM options from %s" + file) + FileUtilities.readString(file, log) match { + case Right(opts: String) => opts.trim.split(" ").toSeq + case _ => Seq.empty + } + } else Seq.empty + } + val extraOptions = multiJvmExtraOptions(className) + val jvmOptions = multiJvmOptions ++ optionsFromFile ++ extraOptions log.info("Starting %s for %s" format (jvmName, testClass)) - (testClass, startJvm(multiJvmOptions, scalaOptions(testClass), jvmLogger, index == 0)) + log.info(" with JVM options: %s" format jvmOptions.mkString(" ")) + (testClass, startJvm(jvmOptions, scalaOptions(testClass), jvmLogger, index == 0)) } } val exitCodes = processes map { From ef1bb9c9c253ab734885365f2d0c6aad5f0067ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bone=CC=81r?= Date: Mon, 23 May 2011 10:22:12 +0200 Subject: [PATCH 13/20] removed ./docs --- docs/images/akka-as-kernel.png | Bin 39638 -> 0 bytes docs/images/akka-as-library-1.png | Bin 29399 -> 0 bytes docs/images/akka-as-library-2.png | Bin 36522 -> 0 bytes docs/images/akka-as-library-3.png | Bin 48196 -> 0 bytes docs/images/akka-as-library-4.png | Bin 39979 -> 0 bytes docs/images/akka-as-library-5.png | Bin 44526 -> 0 bytes 6 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 docs/images/akka-as-kernel.png delete mode 100644 docs/images/akka-as-library-1.png delete mode 100644 docs/images/akka-as-library-2.png delete mode 100644 docs/images/akka-as-library-3.png delete mode 100644 docs/images/akka-as-library-4.png delete mode 100644 docs/images/akka-as-library-5.png diff --git a/docs/images/akka-as-kernel.png b/docs/images/akka-as-kernel.png deleted file mode 100644 index fc3ca9ab5acd4298fe94666b2196ea0844cce0c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39638 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 diff --git a/docs/images/akka-as-library-1.png b/docs/images/akka-as-library-1.png deleted file mode 100644 index 6a9575a475622b013fdb4ef740f6e023ff98c28f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29399 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�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 E08pR@WdHyG diff --git a/docs/images/akka-as-library-2.png b/docs/images/akka-as-library-2.png deleted file mode 100644 index ea1785e61d0448ac6438c221eab3cf489bcfafb5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36522 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 r@URw8Fw_o}TUP9_{bP0l+XkKb)|%M diff --git a/docs/images/akka-as-library-3.png b/docs/images/akka-as-library-3.png deleted file mode 100644 index a86468b98796abca66025369e5e22ce3c16379d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48196 zcmeAS@N?(olHy`uVBq!ia0y~yV0^&9z<7a!je&uoa@|e~1_pljOlRi+PiJR^fTH}g z%$!sPh6={DGb$~v+`WJNd=6W(WOi9HXX@EoD^BdwH=Ne-#^;zs^64O#lR?`;G;U4~ z&`_M>)hfiLI%Vw|QPops=gW8Oe*gOWpLM^h{#BkUJ^Ovnx#xTA=NIpLKIgsVbx8(` zf_EZZoO}!%3he>8Jtc*G?Q;uWI5sdXYGCDPl6^PtponaCcCp7N{me>+1201JpS;}o z&-F`wnwi!W1qK64j{|RapJjjfX5(2FtM>K)Cxu@5xwe1r>{aM%i|F&(aIyIZ+mW1o zx9>Ql_vhv@+v9LIO z+*msy_Sj^n)|!p41-vgEXFavB#Ds~VG*;u-8T)Mx;tcN+7zBEL99Lt`tlZ78qV7}w z+xD~9A`VvUZmtcPvi;D(hy@WV_)Tu{Yp%$j$&izBAtT$>ec=SK?UfuKkHUbx(Tu`6EoYBN_fm68k=izw=mOi}65$9rgiRCBv)gu#HzNKzS zHA+=WIHB`$O=(VZgi5>Ca{X|{?;Pu2@Cn5)W^lU@yCT#j!i4R6o^?K-do0)dO~sq1vR`E8^08YUwT!n5dUxr?t->#BZ!h`H-|wsM zmaiv2=Y5`i>7r|f%=M{4<}ckH?0?oJFsdqcIF<5l=5brsn_Sh?)v2|){K)C6 z>l@WePnW&vjXA5$7{2-Qt97MM_G?RM%IHez)dgRQ{PmSyns6u+}{^*4hrdtU3-`c`gU&wuy&_4)P=RjYsY{k+?L z@YeTE=XZx69nMu=rmSx#^UL7J#2|Wnz9#FUV1KkefhQXOEmYbTz>hV zv5v9p=2x5F7N0AQe?EEtiMp5nXMHMQIVYWa}Yg1<7L<@Aml5yfyQFQd*?hjfxxJ6V_U;m{S>}zvFs^ zX<5sMtDpXhnFyT{nI{l>T($G8#|=+2Z%@zplYj2J?YGTuUbtew#@hY~oA!IneYI-W zwqqyve*f70yX@_E`+&7u3j!REhksvnPrG*3!aWmv3p-A-UcQ~?f0kYMna=e!hV4Ic zRB}Z*q{^(m9pf|W>uZmlvuAFnyN90QxFQIuc|zLfLgH4g+V}Ovx7V*W&T75YyZeRJ zx$2~^uXvyJ&ilT2meH*4=vQ(jb~X2Z{k(YTaj)5x;AiP?E=WCi_25NHEn~Ie?@hZ` zKMQp;-+6pR_?GX_U-y}_)++v0W8Tp?b^G7iO>*%v zNxg--5;G-k&v-PG*T82+-n5u7rQ3F8df)VKl=Vr^dA_r0=iEx3il^mop4>j!FDxJ| zBJ@c7nLwhTYv;dXx*ffp-#xZ?Ond7yoojO0w2QY?H_i4HQ`@a!uJu1?VVq&SW+K1m z%_!HvsiCD&Z#P!0V=G-R7{{`I#m|)ZYjy9LUqxxGWqkXZ-MN&fWM`@Rm#nYJyk~fS z@-FXrCvkknj2T64xI-Qz0xM+T`|35zB$Xb#(CE<$Gm(Rkk z>dDD5N#ExR*=!Xv7r9?!_5RF9mgAdub-H=BqZjGcm1nOPpdV0 z+IHtv#aU6=>F3-*_d_;Jb&_U?SUx=YIlN9xjkvbMV`t6&t*6 zsKiM4HK-qseyGi%+G796DoIAOtHd$Rk#8YOpx+AZ6@qMmZ2WCPZJAqaW_9H`O{?hGc)f_03T6;JHI}?SH2@Hz%J;=!|A1q6QD2FWA*1 z7ks@caa>To=2y6rlTvt4BGj_x}VkeE5uCeKIoY0YOkB~BlPR;9Mkf%Wphja9&7t@?CaUNv->mh zqJOsw?%=q0_(x*dhLQ;OE#(=9R(_cM!IOpWVbhMLihdCnImc&;?tzmu;zL-oCOS4h zWH~Hs!shGneBpA5PzisdZN`NLI}N59>l#ZN#Hal0IhY(|D6BI}CsHRq@@z!@row?c(>x&!EZa&VV9Nt{}g*rv13d$Z?+VQeOw>#W>f=a5&^71f`iAzG4n5WEp>vj9BmbSL{ z$+iN7^2%yhk?loiZ(?A4N2VlS?~ z{4Q}u+EBuK_Kev0HGLcV~pFe-(=M>>&p9w9~mfZ0Z z)YQxD;km4L?CzOO5tfRTm8{iAU6!`l25bFU<(75um8w*2#T2zV`ONy0js3R{vCl2p zR1*KXSlU8bOycZ}2fmZfw&{fE1g}}fKl8>Ng@W6vRtsd3=QI?r>fZ4>d3yjqul}0v ztKLf$9{nHGV6n4nkw}SpMZSmg46lmZmZX%>Eq^*!NgTZ~&EoKnW5yd;x;I;PCZFWe z-1vM~Fzcm`m*0&qipy*|rME7|?A*RN#-*(FUt{h?>?=DsS83;?O_%0g_s%;pB|Kas z{M!AXow32n;j_!9FV=f?&!sstIaIjVc;(X93uCfBExxIKEZ5cFe4g+vZL5RahfaT5 z*PGWpZ}r~d{13G!|1#$2_;yLU>hgrumXa^H1-qZ+F}I}5Us+)A#My?at~ zar*9f>HSxAVxoQiy!+++@%Z+om*)4`*XuuDw_@FvKSqD9{*8WJ{&dxWtV^$M%=#5| z>eh>0w|;eg-Tk^)`jK?Ixsll|^NiV2v!|T>bhdDoLa&3hf3@5E|8IW_{r$smrmAs? zgVwni|N7NG{(t9CXmu*zwDt(6ZR82LX<-3=&ojL`Oc_sAF1(*rFk>y_l;nl41+KMc zWPB2Tyywo>vkGEOZ@ebfRZSDv?2z-$*>p>z#HUtExnlFJZ8Lb2Hksc%CzhmPr!&jx z)~?5wET;-B^1lArG3J46&Ik9)0x#VQew^MuSt=`;eWS{f%IESKZr3m0j{5%T+W*!0 z1#U(sFUf3jetYO=hoah-f5pyg7=yQS&#vIl$ptZNEe$NOEP{6<0Zj zEX9uu?N3~}7%G=&73l}Duk5|Ue)VDe+3V;3-<|&U%?uV1YXN*2`X?|E9Fmg3K9 zIcL)(A-O;N4vUhe>#mmm!N9=4S>O>_%)r1c48n{Iv*t)JFeos1x;TbZFuvK#?+_At z@Bja0Z7ks#2NQUWOs-r=moz)M<*tdNcI(ppi}c#RNXH$=|rXR_6U2;!W(Mm7=>Ur^n1LR`3G6nPS|aUWN~BqC zFy3xB6bMm&fW>VVgY|=nYG*#p1RL4Fc|+j<-wigAz9guT2A5cFF!q}yPCL&s1FYl# z8)Ml4vjqhrP$L^BsvY2~Si-rv2Iep`j)<~Dn+i{v25vdK)YtLbqg=IT9xzE|p9AOp z@6Fx0d&<7~yZv{(NN|xUh<{%7?v7<-tl<6cbJkjjU+p~#^ZEwQ1GjcY_36G>I_xSQ ztMXghd(P9l%Qh&w?YhDkZ5{rwMEBg)M-4}Je=UrkyY};U+vD?L{@Bsgu+@314EJ*F zzN4pq-4IHzET77!S9suB-{nUsu5+G7?D_xUF#jfJvE$Q<7YR+<^XP8Tn^mVa%D~)N z%lV~r$)THF3iovNCq8@Bt^e*{&S}N(Q(wP%o@kxWeR+#TK=)?R_il>=>uSGV4X=2) zboz$!_i-lX=8Ly(y&7q2X$uPuNO+fJPD(23ZaT9^#Y=zs@91QJ4Cr$ z%hyLN5p}e9`LHF?G9ml9w#>StFT3uoE7-T(fBv!g|No@Fxw)CW{9dK{r_Y}SWo3I$ z>+i3*(Z#>~(wyngSZ*+7e0aW3*1D~&jcw15N8KJiK1XJmW>1(ltxf*#3-?X+n=KM2 zo)`6tD7zo>fo;)5?GFSyxo*~Iv_BxQGfp*AxB5Y9Zx2CKbg1Tg+#ry0z=YMjs*Sxx- z8B=;SbjQ10uQ$~GE-TyToGW|oI3(4Gw=}#k+4uEY^obKE4&-jX%XV?4P4zdOmVV*I zdmg1q{*+zSyG0Y@FDG_BELVFF%rZ?cwrl(Sy4}@sH;lw} zY8G-F610|Xd(xmQcmEQzSXV^Vfh5W8JFa#x3xAMdGAY^|)cJK+Ovdcn3$MQp2%pzK zX-%ug-MS;KSAVcAXa6$Q?M>*prlRNKW~XwT51tjCuuWAc%U8K%%e6&Y_X*u}-@IVX zZqZz`A`3CofSF-JOlptbKNa1PnlvF)Mc#<7hX8+_jT zKMMWq5j3?nQ|halQdd_P5xgb-U8elaiM^taYQk);y?yoehjmumfmh59k+p`Cmboix zC@>^m+&po!doPR3(ZZdV?=V>7qijNZIg>QU5! z_?gGV-rSgROINw{vEJlj^Rh<|q}MO{*dkao*Z9+h#Tj!gV~xsWH(t3=cGt&hiPHKN z3@4XFGk#x^>ixiep0mT7_>1@M-Mh=Y?T6h*y+4lv=59E#r!kI?Rm(-`y#B?k^v*o< zNqzG6dKY_7Uuu`H)42Fy^NVI{m3Co6#_x&?rk}c`czd0evAdS``YEqw-(Gtqdpei! zi#fes zwC_ztpV>z3_ZevZ>J$Tf87l+{W%m(0F!Bkt~+(i3NNr)^ow)_v>GITo9? zh97(v4Ys^_-*R90-}@&gCvUPBsk?a7;giRr$$T@~W=U-p*`xJsDu-)gGg~F)rlOnxprQKRW(Q`d8C3eJ8ao8viGR z1&8l~!7eedO-^~Nt`JJz?zq(nPjf2ZwD1FeXE7xKAgilx2P z&~VC~omv`xes1f;%lQmj<}a9P^it#d4v)`&)+z~czdUtd%HsT)_aFYhq~vK{A2q>D z^tI>p&gs9RHI?{Ur+t5zuDC2im{lwBz^<7kGb{|ROzHe@#K^Cz@Mliyzi=LVZg~&C z@H)%rrHYmbfgMX70(U%Bo$~Mii%p-y^Tep1GF`H~YTv~KM4D#YJiVcem-o&*i*CK< zf_SeGlh=t0?iL(T(#=h^ytZ2H_KGZ%Y0uOWf(s41xVJlm?vY5mwo+uV;Vir5)307+ zU43uMj8*Hnp4RUaWY*1}WurZ_DD~mgTlch=MBmdo^yzAJA)EMVzi&aOr!p+f`}ZR@ z=(oK<-p7+|a^H2WtFre#dF;1m!Xl@uN~W`JfBhnRy}9ke!p(Dc#OGNAnQP1b;(j2Z z_TV2&%jc~>Y#y@f@>OWevVPm(+NcyEv%!_|Va-iO{-vB2=ULSSO!#gtIr8cL8uwCf z_1L>X{W~5i%)8-Zd%1JEV$LVtfWY`|3D*@RGVO1i^N(*UI_I>fbkFgHk-rWt()+l5 zJ&W!_w`ozGq1{PUq3^3*MZ}ge-!^;aIN6TDvCS=Ko)tH@x70#`(~JBr{10CHH?N+n zBeJEYs7-r{RuscEhwqx@iykHjDv9SQx_*gxOb8h_K0K-`;va{c;I4IohyH>yw0JbgNgU|cEz_0EAOYq?vA_q^6No^gu7L$Px*ZgBy)1KXW#w2 zHN@qJwY1LlV}4IJTio9l&gdL?U}ck~p|FG?Q((mXh(=-0&j#9)WM-+ZC|aN!#}*~h zyL>g5pj|c}Q}}GZRYlq#4xABqk$v=eLeDm-rJ*kOIbN|SYa4MJSC!T`e^kD;Pw%Cn z?xy^Jq@aRno->TtOMh}dc`T_NydZX?fO*fQ2On;|T=x0XeShF6qE+6Bb#f*GApfQ<6%f zZ2nxWEkZGhrwziVv~8$vPq;NLf5EPqB@I*g_J01XuJ%BZrDgu+ z5}UQP4+6G)7oO>HgRQDMLWW<=rEw9rp4nZ2-*)xi@0Nd3yShclCvK7Myatsij}Nfe z^fw5~>YlwI`Nz=8LL^Z!ta$q(qbsj-GT%EX@CLTCe6`rT@ap7KS=p_Df-Ms!DBNs} zdeZTmN3>-2#JxgC@?z&!G5=xCec_R}b?wr*X_J`+{kDhPoSdk6`cq!)vKyJJFRyU$ zm~kTCC4Kq4S9hY_z3pzl|Moh~+V;2+<9P*zdH=P`s%5OFU90``<2VN&-?15n$pR7* z8t?gazqOS;lRVg0XaDxn#xi8+XVa|I8MGY0665a&$3x5wf@8G|`>kPxB`->Z$>Ud_)J@m%q_WId#d1TJA z*i1W6@MqrT;sf6_c-=XxjV$NETCLWC8-7@w{#{T~Jd;_jPYBVRT`!ox{w7=Z+}Bh{ zV{N;Ffew4l%ckgicNe*>VS%@+RT4f{PW!%4%v6P4himJeHQaLRjIM3^F#~Ra(2Liv zb^dM0|C6wbzgqN50_%=ndv>tC>NUUT;OWW9CvVrYYnN5H_`y%mp3Pa#j_5iy-&rj? zc33P~vgEO%uC(jlgWPT&JQ(~GE zKEL2}6r{0zphG}FcKwYT5kE`c@BRMh>-G5Kr>1IuJSralfWQ97;f^UUvlhxdY?xpB zEi&i+K3VztKOY6mSPM*zra{}-@7x?p&SYh0KTUiotnT+9zy9~^-79bS%Wp3~U|s(1 zi0$_~%vH~ntnb&IJuV1oOD8A_xI{0j`*c!$$HO-1g0rUA6HZQ2ofe$;({4%Vh3Bi) z?$`gf{rBUz{i02qg6@@nu9)VX zODBn^MY`_1yD-}N!K_(QcPbwD=CSvReu`t_-F@xC<0Ce>1J zH8RiNK4)$1RuT3Wha0~h4Lf(gs7&Mi{RbwN=d>ryb9q`HYbwGx<+;YA=SLo|C^*0< z{PFuIt~*8-L;I6u7J1F-<+Si{_`&zk`Htm|t;}NloZIXCR$4cw+}+dMp!7;AB`EZi zecm+fE8jla74>j_@$U||&zSUh*6WtlJ_paMbEl`cobBuFSbT5c{CJsbB5&K5UY7Q; zjK6)WSTFy)pk@7kd*6ygSI$e^+@4vSW&87AW|t@C$FW^|BAPJwMQgC+O6l^MRgYrb zHy9?%mc7xMP<^iM%5A+Hx1;~5hD><(nZMw{=CYk0$rBE z53MmenYkd#G~w~tk45`_%*+nBeX+ASe^1vj;X~5QzUNpODD|>x-xy~X|ws@wr%UDjjIB*J*#@BINfJ;P@Ni@x#Uw#@Gd9cYWovb zYfns9$o$H-GG@x$q)ED-Rx1NkQyfm|te*60m6xijtahQAlBHCwmEtBR+rnY2RFQ%}Huz9k1^(vp?CwU+e2lWf@KE|J*v3 ztH9nI>S=6fYsf71X~wIy>@Ro}eqA_Ps{g2O=H-nESCchV-tYXnc5&^iqwWQk|0Z&if|B z|8tf9nJn1teIatU_!2X%d2!t@FK*nS9^d_DO28b$U&;$(c+WmAS2oiUGW=Nh?2lu| z-1!X~zr1goovx&EK*YA|p55KeGuLT*eN_2cw0Lp)qlMG87apm4W9?M?fAf+8FUhjC zzK6~nss0eLPDtHmfpF3~ll_K`pS>DnKU5w)+gi!gcp`h{{kH2L>YmyaMRD$!*fvXa zavh)OwucT+t#5dzi=Uh0z&&~2K}FrOx7YQV^S-_$Q22*ymVuo1ohclg0e%M6dMt^@ ziat%Kb)GJ^G|6dais2v4`X6(?esS!c#3jXM*517QzGBxp8 zbIe%(>V(zHSVM2uHH*H^{&4vF+oE5q)!dalF1FcPW|rJ!nDb6$Y3ln*2KRj%bhv~) znSGqUysmGzbw6TJss1g;Yx(J^J}O6M=vljHfku3J{1#M;WPVsOr-}0iU*nCRryG5v zbmrWBY_K@(!UUtc4O=n-=TvrtHZD1yq4$+dRgE=$<^zr5fa&`i`m%lIZ`Brfx!=jo z@8tR;r(TBnt$Hhvu}h-$r?F4w+4|-w=BKqc&cFFva?(=^(`UXL?wp^(dG@wv$Ue_H zr(&r?e~)f3!@n`?>P2tt{aeegAdQ>5BYr>IDc@mY{|-I-fOGV$Ikuy+}yT@wlE1%M-I+~KOfl{ zS>3wzq&RWfRG->&xBuVhTy!MiP0XUB`+ln}n40&Mlb_r0v`_$fnl%O9U#0>AiJ+1ZoN%rNBO;W@G*aPb3w@fG5+ zPktOQs?WW1EF?nkyy^l6j~Rb?lbsIfXwRRs{$b=RMfr#VHm;&~g4dE>DE(mgYwdgZ z2%}=+lR8-*Mn{g<(|7d$KXj;g+-3kt|4ylR@|c3)7nh+l5mj+#$o>AFP<(H0)jH zxzh>KKzzW-a)YtjNqb`-lwS(s$4)M4g|zPu2y<*;{?-zfBnxfGy$11jsXgT~gftV? z1rpfbaIQUK1+lT=I;d%1>U&BQI=$cznr#q{>MTuxH1*djAK<(3de#)}BajC5JckC^ z4eDEjUY~&Q4DWz6@@*O!tP}h*RIg2h@b~mFSSPH{4q7Jw=_OP!F_s;e zcfo6oGL$dN*n0iy306_7;%7W%@9*`B$JYp!mX>DZ8DIN#>Yv?Dt~s1J_4D`d zUS!eVSiu{8HP3$=O)3is3u^=QMV2o=eeU(O==`G> z7rWa&+U`+sb9cFZ-QU;okDpGDcPl9|;o{}z_M@7_s^e;XRbMTN|-N9;^)J|?H`W_ z`#;$AdY!Zm_x$fhO#lBpx8GRvvnZzi@7IcNH`5j0oWFUX?1_%yd++yJ)>RK0**_eU z&VO*xUH<4I*KQlRDPgOR&Mm*!={Z^LO;Y;1z?XzY{?fHJM`b~}Pnzu1a*X?)#>P$WD-cbF1 zZ@EX(&Azslv#X7E9_o9y?|0swpU-Ch_;8qi@1dVvY++hc1^jlbUF$11>Dwwd`J~hv zjKVwq|D6B-;5^&vl;_&(5-fh`F<$(awD0YSiOQQ|Eu5R)o{)^XH@D29?8}qM{!Tuf ze_qG`pHv-s&*jylZhg1ZRMn{H=!w&(Klcrl4vMY0#|Y}wS3H^MUhv|AA`1%(52sT4 zCq1S^d|cezsk;TfJ@iPhE^3*Szf^LSZ0?;MfpWc{uiaPPnDKVy@_CcmuldildK&F^ zarrj8_YL!_-&vlSZSEiU;zB!b{g*_hq=(%bxDCbMK#X|5G|_s~-Die|h8|vCPKAXV z-?G>Vz5dJ>v8E|>Wr*FSJ2AfL938hOojQG5_SB)WCjx5oeV(k5?fkM&poLj%^^#i4 zAA6Yp{dnA8_;%~{Uz>Of?!FZeC<}~^&VHWrK4aVBdp19U`5Ct@mJ_??em$m`*Y4*N z;ny1Tt-n>W-??#ZCW;@jli|#9?+f^J)+EP$+ihDVC8p#3^4n`K zxhK`$W9+V;Rrz#tf5h&|S4FyAW$rTezCGc&!*0r)wve}DhK#(2uJspra^Lizxn~b8;@sC&^`dk;qyE|Iewee>oX9(oPUGthU4an;m}D^ zn~9(tpJRG$CM3t(%ww=l=+Adr2cDd4;H*#r<@g&}YmA{e-iER4fZY9-7;u5aApF4v zROD^E8j%hy@+v_5eVjWX&QW{N2`b`}R&P89E%H8s_%*^stgs@Fa|83YBVoyNp+(+f z5dVkT6E0}aSY0TA{Y~fEV^+ps8yk)bC$PWy=zBsGYXDfdF5s5Um3=0Yt8pr#`IX;t zmV!ru3J3TqSQc#Z0J*RsTJVKU&(4PnKY07J%#XJS%du|u{(INUd45Qn?1tNB)$Co9 z{=PW9iOs#o)~oEA*a7`@47K`kZ;yU|Qu1L%s=my>nF0)~37vIyz zF6$Hu=H)I~Ew(a6x~?{^aHVwNhNY)PMWtNYAahMdk@ddwgz79qo1c6wFAtbft?_2`PyC+1&gPdC*5ms)n9d2wlF*XMj?wx5Ue zH?QIP`n7b$>c!KSF?DZwZJ7RWTb+2T-g<+`n{iKdG|KA~yZ0%*^bGlTJ#?M^F>aNt z`SHDLp0P|x)jg1PHDt-UlLxLmy}jU!Uh}q966~8U6<#n>ddvFt>lLjH%w?CSU4Cjwhun z@-|+N&ppJg?KL4TqC;d#!uQ|T`t~zde=<6=cfQ4%+jRo!{cjGPJ*A_i$h|*iv-3a3 zJgXghm#?{EbERy?DXUG{vPPe_T@KwTKZ!f*!quSO-jjuCl_zh6CdW=IP5p60u8})Y z*(xaQ(bd9^zCK4Ep1sS|lYH~kVnynj(tb9b?U*GiEvWu|(&G)wGU~S{Ps>e67W`!7 z^LF2|1paUDPZ=-wJ=PaB+1+5<)i0ZR-(J-^=R_lb;=iN|1t8_T;r3F zKCy1n#&rt(D+`y&{hIam>fQS@m;H71=v!>`IA~({20=+l){ITxMJITFYO=o~wrcmZ zhkdEv7hAAu+}zZ=?|P`kpA)hjb1SVsz0~^Gy!cJy-9<}PJ#RVaw;$rS|D#Y^TI#vn zZ|QlZ8ioI5D@zM^R`t*d+_OM!|m7g!av~Udyzv?Yp zdbFsfDv|Z3>(nXAucs@U1_$19np(rBX*ntC^VE=4LRBJOrDwlRvO2C3o$S5xU)H?q z67FvgUo6oJ{^xX~BTjkc3B6)1Y1QU9^_L}9EB9{o(=xd|#o*#EqeT7*pRR^ZTIO^^ zSS55}*t%^;*)DOPUfLBjnV0jn=IRwwxQtKAP5BwL`-(>GV)7n(?_n{nQhl;Q^`cwbu_8~;`Ja#SA7|xTw`%>~tQ}GN zCaC|3admBd^S$N2b={vGZ}?l|MgCo!>F~)z$vPo@MsLNfvYRhgsoYeYS^vf8YH!g_ zHT9K~jE?rpZBqT!D`{}=LY(@_m&XJCJ$iY2Dc3`fr}De@T~%1FUB?o9N`-lD)>@JH zgrzRm873{ce#*Y)=F>w{yB4S~pQ$IB9i{3l8!O-tBj6Dfx^7aPjQ7(&MVr2P{p^nO zv7dL#Ny~g;z_sQ&!=+BDQ>VshT-_ez=e&D~>hXJeTxFXRepT_To3i*N+w><9UEE2^ zDczfxm(1r0jDH=TH`QQDndbakri$^GV>x?wd@#B_+iZT0LTs*8`h{6@cb;%p3rOGf z!}>>KMR=5@$7G+>brV)j?|QjN@8FvYrqR#5HY(lwBl~ijzw=xj>-fuhnJvD{X6Hmr zxg4kZ#?yTSJYa}^1G6{;X+inYu_TTMcJY!FXR;Mx|=&~g8%dn;&wYW?c0|1 zIP0g=JZ)J6en;WJ?_6#&H5;dd1S#81XuHhx_+>|0)BKyaZKtT8%heQ&n%{6x;hyfo z`aMn$`;*x>3HmrU{cBt}V|porgf$nJx7g|G>0(PwUiD5o`eFH&hsD<}v+H_kNhxq; zF7tnUsQJuF<_TW{1mDe+W7`|^FS6t2!TxVq6E_%ab1wr82^4LaXL!gV`N=6(BikZPCtsTsN=GJyo?xCN}o|1A(>978|VgRWA(N6LG@fn_82B=+pWbMft-S4HD<} zeQ{srW`1*`g{oTcwB)X%R|GmZCh=DuoN&ZSOokd1ZgE7zSa`!jL+ zfBQ8zj3lS_u3f;VHf!bVZegX|_ZL2W)0_1*)N!p~>ZR3vGDfO*w|z7%IWJOrf13l} z0uBD^zLNQhiT`%g82$aRZQdKf9E;|~Uo>k<;?LjWyt{PsLbc=f?!~lsU9#SgdDLc4 z$nEmn^8OXvl`i4elBQ%FxL$enJ=4vO6^q}P999+-5^CG9!NA4k{E@lZ!Y`uU-oK#j zmTa=rQ-Ag1Y%|-ED%%y}ZJ+kv$=LT{(d55k-y~aa%v1P%EvhD4wALx+ucZ)=?Mxpr zxrP-kL2pC)4~PB9n4>&tk5R9Zic}}}qzwsWIsVlZslK+$gWfdUU9(o|UIiQbgudUC z(v{WB#5Qg;oH4UT{OART_xr3SZ#&gFLq@E-bn4D4rg0r>P1D>nl;ghGtmJOeRz94t zq}9JZ(&O%(jkWdc)*rL(sYzXoYhChkkx$jpBag0KsrmIas<-)V_(Q!}f1>{e7)n|7 zW~|mcI7c)x!(>A8)cV_fi?;?h?r?pw@%sOBZ)R9;Ob@pDy3lx{z~#Gh4PW+$J@K8} za-}{)P)M-Z()=&eqREF38ZwC=b6ZpTy0Ap)T1v9Xszn`#l#H(AN`GJADc_*rtZe2# zx3yKZ*VZ;5+t94<@A@B_H=Q`U;#N)6*NNuKJIp1uC32=ic)8LumcRB_vrMH@oIT2? zFa_5;a;^10zV4P;$=1-v2Y*~GF}snn?e5DT4V*C&Om7~YPMgi&vzOS!x?V0{ZK!!e{^7^WfTt@a zib=El_g2<8s@8SC>%o$*Me8F?r{?|m&fS)EIbzC1U9G?=i*|qZIqqO`ZN=u5!TD0F zzVOd?zq>5yj@u^-9=8WfH#^o$v)`#FxTbgVGPOFbssq{u4b#NbZQT}|Z_%m?GVqkx7OcWqPC!}?04xWMNLr$1FxYN-^R%c}hPu69#yOTw{J zA{p29kMLHShMA>KGCk!rE%SnO$F9eQQ=V&WjF9X*@FDVt)4ofy+fFmBEq$1*R*=Z? zqcbV9A{TjRMs-%VWwluPxj6@4US96FTCotR*Vz7wZSvz2m4?^V+Wxs2&FMFC3#xBV zk+!~g?N)2U1p}6gF|#&q7Cv@BfhFSqH%k?doOE~4KnDlstCKlzUNr?SRH$6Gl} zE%Obs+}-47Q=Un;4T+WGc@Rn&b0 zKC8^Dm9lN6SL|ebuk$8vV7|Cc_V2O3Ph+@t)>dtn^_jE!{O&#bYDG%6)+qLiB`TVj zOv$lkIe74(gmD@V6Em}6))kE(zkfeo)GhP=`nNw_-vZ~@R|zuyTGZM(RpQwK4yjxH ziy}CMg|7YQzWx2Ufkuza`lTBf?}<7lb`+^RWH9a1wS$}-_|sfh+plumf6Zu7B)#}dLDjM@CSpYx_P)^<8!suvZtKy6bSkWl81! z?4+^-a@SMV7=Zd^2Mn41*>f$e{vGe^>nOBrw)>aev-mFa_9Rbqw7sbu+TL*Fsjc&; zU*|IepX=-rc5V?mGX0CD`^?@A_q4Tst32pGC-6WqBR!)q3fx6v7v$LePw$D=c1638 zH_Y?9LT=UD7BKNTrl%#{5qF(2E39cw^s;YPI_AyVGGA=pwrsA6^VYvCeZTvl!}mk- zbuG`kt220icT^uZ>~?|)+{?@JYIt`xt~#RqXLbD1-M8{HVUZrOo(SEANvgFTzc^iN{_ zoNntU)eC%Q-j2R?Vxn8}vbU|z9NbNVoYOBVl>84^IoaTL__T@N*YERAzQMR#M4dYs z)FIs=*r;fAN%@ufiJxhURu#%FW0^dA!Ox_{PXC;%8FmEzwTTfeJ0N$jVGVeJ?*X~S zh1Nm03e=5xjTirz$ouWaE~R<08?-ex@qoiAT1aA+a&P1oCXdOpq~++-?hm``O!-w8Ob;FERRT{A|$H*fz;}mG_CC%bYLo zV|;(E*Co7yDQb-JyKQcHfvtkPH7VCuY zi7A#~L(G*I*x%F0-jexIC+F4T+YZGC95|+lN`C0>@!GTeFW3FYLNSu|;+!9o+9Tqkp|XkNZ>t76z?pD{1 zkV|3?j_KNwn_ZZ_#1lK!Dulo4I$qwB6+IOJm^^N%lS~vHL ziKw_GeQmsFwX#TiWA@ve-O*lkf%`-QraJAfo_V+O(LJG`mjgI{?D_ulV}E^5{r}(h zofD5&?wNC-Q&_#=<P}R+YNrED5bwSakXLAHyf{W=CYw{t2`#T=rc>L?pk>hiMHuIoA9#GM_wdH#<8zvhb=EdJaNG98+-O&B ze0xIBYv0UclGa70im{IqH9lWYNSl%RbQ4pc@pPs6TvIComEFV`36 zn3$OFr_bv%n{H86Wxp=);iuw@R&Mb}puyXmoHf}Un?%+temJGQ-T}?@g%<@UFN}?8 zxpl=|P&(p4qWZc=h|Cnf4_tt&?>v)cRy>t3WR%xx@ zyuV@}1Vy&rud@yc4*s~@{x9dhe>stMm)O0c@+w06{H^5)cR%`T`l?+^Q~7FnSU%SC!1MM6 z>F4Hjt_)tDbb6ZZx+kYL*~~dm|NncvvYHy(A=s$lUXydTCZ6Ve**oL^sqhP7Qxtzb z4>oxE{QJT+PTl9P9a(TXWB(RU^W_YxS|V@PySlr(ufNW4^9}bCV>8YlHT)54{B*Rm z4wYVyZJ+<=iTaM$>vrcfJ@UA(2wGqM=c#_ZV{x&uOzhrMWlyZXJW$IS`5ey?Eps9L*3YqR5a_S?LUmoL3w z`qEc-xv1Ip$ia0Ec8QZ4&htA)ujrAtmpk4sf4utr-uBh&_eu5J|Ff|B|EKsxiIuld z-FGJ@(5TAo$SvD5$~P*!U)*n3bn9o-g$=Ti9zU+_Ui~)X#+RnFB>TH%SJr8|RP8%f z!#!ySuV>RihxhIB2OFZT*jWOPtBCAczrW=fGq2wSr}P)g3pTV{@I>d!3m>peSj{Hx zeBt-^_s4&~-`_85U3Nk{;r6XtAMTdlKiQT&bqfF9hKBj`<#UgVx1847&69kr=U{Tb zZCiBy-qIV3<;AS4zFrOAn0T0NXZYE&C#-DA%ek(Vm3%&HzIewD3H$#)&$s8gmvt3u z+`4hI(T_zj<=5RGw}1F;xoDGjzJITv+hQigF9vP%cI->aWbRM7b-+2d>%4w=@^>du z+eI6ge#?2uD8$EG{;5=8`u3B5%IWU&oeQ7e+?;+|Uw_@QyQ@0og$t*?$#(RyzEgg` z_QaVpN1FNVIu0CIXkX;J{`%wh_5ZES^6%-Ku70twy@pr4=4#)a#S5)JRv6gs)d1HU zEq<}N!D}0z72&NoepDVXn`tF|()q$Up~qk1JRK}Nd)=D-?S8UMR`)+veBSo?m)4FQ z1%li4d}kW5Zr!?dlTCcdzPL;UxzLpgckbWnx%PXxQo^JerXir_FD^|H~!s8#{~DTWt7z9ykebaCI*b zJ!f*jlRsIYc&Eun@Bh_t%`Z)!K8s2Cn|5W()>)IKE?)g&we6Dbk;nS7zg}EOc>3#B zrCigutzyrA@p&a&TNC+dRmDS(_xpb5RZ8!3m#+io?vz|;Mo-A)B{(e&A1+E>}J`) zo+HjX|3fL${$E$uKM}o@j*Gsx!0Ml(CTnDck}7vJKjqoa-^%d4q1~zX z^ADNEjmgK8-rdedVD|b87aV@5K7YtzzpCKIwgY?>H#j02whIgJeQ%S_n^1m2X!YWwZMv$e zsy|-G|JUO84G#~mwq{y4zg_6XpIKqPfhVF?xKw0lIIWG7=lKwE@W2BO%XquD(s$Qg zcD{a(qg3xMyAQwSnpM}8zGsL{%UM0~@`w2gK^wtJ%H~`?%~|vHYIwx99LW%^sWtrt zzP`LdLPAfXy6>%zsF*r&qM|*gCy%?iscEI9;JSIs1vzGw*+*}t zAGbX#-esEk;PwPi=U}r&5(Ek0{{;?$`} zZ@u?k(z4kS#5lJ?@u}7yrA*Ct`~TP7slOj|rgwhRR8@Dj7@w#Am3Nh{d}4Z~B3#70EEEUl1^PD`NhxTgk^) z+wq)kaO~nG{N9P|Zw~jK4}Gtw@QtCrx7Rf?Qu5o|+wJM|Dv!NnH|mi#=llPA|Nn0D z`!&Mf2st+Xu`Sw4N>M%JZzsaJ*8vuJ^m>@$HH?GlC7jhet(q z-Q8X8EYrR8`=atE+KxPHe>n;Ku4`{?J=wod#5V3?_ulXKs@r2rmj-ECCw{cK5&!?! z^-smiZuYhHUd?5VxE)wjWHe1bUhno4YYiK#B%>|e-2t4?D2TSR^`kbNcy5jqGw0whM}MXC1j* zxM=a>r}HMPw@}rViL2N6mSWQw#(Y~-pC&RVF)->$+U%uktt-fh`v0C{T z4wjjO%z8Kf{H?wt?=mkh_n)r+tS#Gp-HOVRPgYW1^B7WJh^edy65!PJy7DA6*lS_m zR{Q4LA1~$=?Yp_+%9(@rAB!vP^yz%j$=07;F|5X9k z^rTl?Ie+*^-L;uedi}{!Mco8-zV)+Onk?!QmNTwSvf6M?Uncm~*B$Cr3-0u7e{ z_T@FZJWyCz`03yIKA}YGqA63}FDbs(7jrrJ zwZpvmFCTKMUGmbH8kJFf_W7!SrMG)$esf!>TYNyn-GDmJ^%me)e%uScC`eh4VjnKI$m;YT=DFb_Ieo+?%Nj^bkrC% zZZ3Nw<>5FZeinCVp4gEReW584J3n#i%rIvT?0ff6Mx@)F@Ab!azA7E&rzB-A<`(EISH5J~d*1qiZS^+?9ht`mSbhl!^ec;OUt}qCqfchxli8VbHY#5i3R39V z*1Jdfv7pQ{(f_-ySxwRXA(qLVu4wdS`U<%WdC!kBiTfQ|+})HmUDuXrXFK)$7FWey zllk&ZV!yw4MY~uSH11<*iS77ZF3Sm>!#{W9;t7FQPQh;#Wi;wpXK%hzB*^*lq=b0J z{w?#KbF4o+C80=oOTy_D6aHVgcj4RTkCM+he(Yfu+s@SWmy4%PcUk9%Zy_63MX^~oH&{X5R(-7DRa@A|&1%bz@0=%xGY>;iTjo+@#{ z(&F|{GKtqi52&mB+`}wp9n(2AS7wGoJkudP@WO!=`nLaa6d&}+Mt3O&8X3Vzym9V&Y>P!aV4|z=Kv#Vc~CwPnT<-GivW4Y>z=ZxHRi<`eJ4@FMn zdG@{W(^t<^^6ZKS_$mY(YG#4QKO$M03huwI(otUO+J8OFK%&9H`ds)Db6admAI1}g8$d~i~ zu(HJWcK3}6O|k9TJcpm>I~TFKOgji#Tqk9bq~=DB>|7TxF% zwqKhpceF8BCtNQ`(FFJE*DGH5Y_g~GfUp8lG| zQkk0JasJ2@mjxf@TCM4l1{@;&3J>lC6tS0`o7FyV?)oVKT3au4^)A`X%jwupK30OpGM*>BG=;4NXjm?^!F)>1->)<7obO~ytjp93*%=vd+CM)a>FFu; z4GH&nTzLO|Ve?llas2u$^n$pEz4$qh<0UMaWUsPDZ2rsLGD9}w$`{rjM}L%Qte@5I zEL(dzG~w5#tv70KG+cAjJvFW8__d?O28t#Eb)Awo_HCEam960piqAUy^9)~w#TN^G zaJ2Mtvc#21Zd;jSi@m()O=lx*V zvUKLAPcL8XpZvLZ%H!Pk%~xBeDPO%2xPh&(u5reKInB?roIeIE6??_*kq01>}3K?hVtQ{pHrOSuMGr779(>b+~pO`<>3WD-U{T8!q)d zai;axwx2VE3RnJ#IGit?{jv9;yzsJP3LUoX-cqv+cd;mQLwv^FsIX+3`Dy;Z7k^Fe z%l+lKU)%2edumxfuDt&qR-|Nh3x3}Lye$LtR>rzx*y6H>r%1PZR zme-d5xMA1WCYzlykJU76|EzDfZp7`W{aAahB*{H1X8$(dGa3!6F>4 zw4bzgL~n54%x7qP_>7grg3T*?_g2jA%ylVCyzZrX?RC+@+<&}k?OGRplo>A5i|IJ` z!OEuo_~i>~a~l6=)Yts!pBk}8lW%INz0R4V=hNLc=des#!2smNK5NNuDF?T-Djh;qs&#Ay8mC?W-+OF@*&KTUvGXe zPd}(@cKrRGr`O&rk$*5H=E-vA$n$%Ww}%I8u9bZxbp61t#VS*_^u5oy)n9t@nVesY z5!bg%woQMV7}YcEJZA0@*eIx2;wsZJgLQSl%-MaJ$uHWCm83Rq+}P3Cckt??KDJbz zv?E7mJW@Jwcb+M?%?8o;TO(S^AHEH`d{$tZM4kK`hwBrsPgokdaD#>PjUbM-bAKDG zy?rBURq3AO-279vWp}P#*>|x+@U4|GXZEL;jc-DAKiM5xR_!&}ckh!OA#YZgnQz+2 zSh;fL32m(j?p9oUwT-rwl6UvMG`-F5bS6c@zoswz{@)n8cUdc!O%(I)x0c+Ub?ZWD zig;dFm#^UF52sD7pIn^Or(8X8Vun)J=DllGOwt^M z(3fX+ewET}w9Dq5v_#i$;!p9gtzMJ%R(PsEovgD1Rmch{pc_de~KddWNW<&?fl ztBi`5Z2IK2a^KFqzt?lxKw+{foan%-Ge)z4ZN#OJDAae#+TDMM!tqt_KHAziy4^ z1$6^j=C59{V#42fAL@_y%TNEi@I&_@dn;iNInNaKH$0m^&)XJTwoA)%@$yA4kB9!@ zNM9xGTXkao)F8igFWHxFIQ2&5?a|Bp^R~t4W=46=aMU-=?taOg_4LBp?DUMOr?`t% zLf7jTcZJ;35P8YDP1fM=hm~DRwM;E5KKCbXu8GmOdfI-*yvGek!;ef1=NA;0lYF&A zM9Y4+(+;WTC1T>PM?(L--2xALojFPStJFPGql#vh9!h)BzT#dI``IAA+G(;gV#@k! zubmWcO)0PGtPxEOh z#$Uc0Af-6}&6RJeY2}*~qgj92q^_(Zmz6a3AE>D0D?M$z>}Y_~cZH0NcBf0q@2`AcJ#Ed6 zZu`r15}6OT>|b54pfq)hu58oO-+x-x%)8LKjgM6<#m1%ju-Apxw*tc>yGl|y#g^H= zzEL21_Osv2TImI`z0SF^75``I2Be;KfAV5pkj=x6eRm#RFFtVHxca9x?n^2!#y@B|y6Mj`;YSCnlJ-5e`_mI5yfB)3 zG2^3kvkD(h`@{rUv~;c2IqJcIR~4K8)pdw->m2Kw^tt-5{AsO>)2GreS8MR7OMQ4H z963W^ZV&%kpVSKHzqU*H-Zb3ZyVT0`#wOS9H=De!Tr57g<(5^*)Qd3-s#wpioi%6e z4>{3n>C8Ga#>iqhhqXV;6r(>LTUI+`YmM!+>DMp5WqT6xMM~v@k>2P0EdJhehqrvP z6tR}_No8SaRY?10v?kX1?|b3&x91$cb1YEy5|=PyK5A0>q#HzNMUthTB3tvu@A;13BE~zgp%{v@JXZDAAspqv` z+-|;T(H^04E!}fl)pPeN*L(BCo)LSR=-iZ-2Q%ztSuz<6>}5 z4o{leeOE&hi|ywicqYsREIwp+}kN?w{U z<>ot!zFx85r1=Hc1~Jy%do9o_51!|g+Q*d|>J3vRO1-gKcNPf6^ec*gBxwi%Z$=bFP$SlFAkwKUva_}hCe zCoSD5?zsz&Ji0nNC5+Qj)WD;CV`Zq&As_FTxf33*`0UltcHQj4>4gWnH^>}%vdG0m zTwUz&laOzd!zV6Y;_BBLwA<$SHRJZ%5%YYDQzv-YSI&Rdm3!oRz2?*Cv+q@xq0(-r1^Kh^({Vdt$4|`r`?aQX?;GVo)w;$ zzv>Rxtj9|in4jJK?6Q-(AkSIRyWN{ytTwJ#)pSkZMDc-*NqiBLUR_a}Z8}%I+FyT~ zc=*fLzMuD$a4P9v(>gEu`M|PIN2)ivo$2m&joAD4q=|Y?lyPFl+DYODQJ+>8U0L?C zcy8FKZQF8F`$E!ZUYfRa*SCI+`x75z>}pJn)IGGQb%LnLe?2Ez-Dx4uzMZM&*LnPB zPN{G0+t;taYb~EQ{PkF{p@;X&w+$9@I&-Yc``U7OEULfhoSS3We0jP5?0YbDta%)g1;q)s9_)V^ zE%C?wvASTQ*?UE^vTX}@#@-2+^E!Ob_VB4=>-07SvogIYAVI+XWL0)W0de zQ>tFB__kqRRa6Aa#m_~}Zd<;CmpJ7;m?r4_wtm6AeJ}5Qobsk-N$0`_qg_+qToRIr z6SXe*_POGk>DR66H>`fjUHPOzP)f?nOt|U6gM=PQW46Y|MhVj_kw1U`7G8Ai*&^aX}ia&EFv5WckX?+U_qkA?r9F0${GLGTN}z(m@%f`s+%zD(%y%HkLJC1bY@V> zkgHs3<3U6Rix?1*S9);uCsXLiFw^)fge9OyzJ;6?AO;N?Sg-n}`&761T+h*CX zIKX||Ea~2rtIM>OPFcJwQRdO&!&QY}-~6gt_pNVt*7>!{+2CI92V15tfm!AInfq<( zZ*0j}Ya*|eE0*TVwOsV2q{X4?-SGvp6WD7G&TfEghv?C1Y?z>-q8IJDT}0|aRDA27 zHyIlbYqWFw9H=UJ&b0c_QOGWe11E$66m+W8!fcI-Q@Rsh@(DhW+u%IILJ-t72JH;F z4%&4QHBYA(G<FMl=TD0$s+k1M8liyfg#4QN+TfL3ZdH;jC8h z@u;}-q$P^azi;VTv~QnG%{|87vkv~=w9z`c{G;=W7yK-@CvGmORo`n|CL$)*mVCVL zC(E4t{eQ(4E?j6A7+yW~OC`IEoLt|uX=2qi7WdjYJLH`{Hot&uMao}Rc&u;To=;vi zKc7ywV_dazC1-d{VXOa@t8uEe_ZYdG^D{Cw9B+SIJELI(ccMIWY&1^7eAx)Af$Z|Nn9PV8q8tc_jsz_mfn;W#pRDtUoC}E?Bhc+^vZMds9z~ z$yk+e@Y{T75LT`9yY%(-_2lR0=4xnZeUe$~?h!n{{6N#|$NlzwplM<4@O3hZXLPPj z=HReiv0}x6XJ=>64VwM-gl0_K{E8K2A`%iE&FuWAzD=JprRCeVvh@k7ZF9M~xITP3 zt?#`2a^ja47vlogKMZM-HMl5knk537>9zTKCHT_@4t{t3<4bJJ*JqSJbTO6U1(ko^ zdOMG-JhegAvh{SfF#q*F7Kw**cJ12rB=kq{GM|anhu`o2zptiylKNfsnm<1ZL&C$8 z-`?6P@KwOJ`lF?W#)<6P_h)u(WH!6Z#ldml=H~QN<(XEcT6O<_-(S3GlhE0-XYERp z_ZT00{4(<>|ICc|i{}>Yb=}=9y86-P4ebkAwk=*5@cE66a-w@v$BqTRkK6xKv|c-5 zg20-%y;7iUWq%Dr) zM8@0&+uM(ROn7)_XR(0<&!){nds{9q_dk7@6RBXIZmE-)BK|1r{p#wLz54H4t9jRr)@(Ee z6+F(}pd%C?n6b>O`;}R_%0p+tlLIU$Ti1GIt;NpUeC7!X4t`py(bK|NAqCpFb}eZQ zXt?h#idzyr+c`{kWuY{0OQ8Y)*I(z@8oOA{XO2uS-}M|`%=&vb>Rf2GR~OtyQQDP zHhsTS+<(`kripWhG-KI;ch{8G@Czp}IV*htEf|uOmA$d6^!0>K#cFB=`#3eul{d!7 zflAv16^@Rz{6{yJq@SJD3OYRT_NhJ>OP$G_JGvOYH^jP50v!=kaFDa(tmVI7*Y}?~ zUuY)PD;^^s*1*Z*av)cB!wu%BHyf)IzctBmNX$=W@;R`Vv*T#QBe}$f9hN`z7`yok z)0y&KHyo9lm$dlLv}t1H&yMc`d8&wU&XjA{=ivmEP{ZeO%W=}zVIxt})AtrcqO50A2m3W1(j z6RI`!&kOZ(wi93r=CS;Hz5gHg=JfN^A4q;9xy#J(EKX*!XIoG%<^{r%%{|KrL2 zb`#5YO0Ls6cb;WN!+XIQ#_4^3jx4$V=b8Dw@JjhLA5Try4h#%zJUiQbckSD|yV!MJ z^h%j>J*u4VZ~IjwZ`VsTxxTXM$w{m;8-5E4Y*)E@{krp-zGn~SvK-m4`Nf7z6RFCF zcU7myJo+YDTFtJ*!N$V!;LXj=pU#;DFZVn8zW#r8NLbjSojYf~7cQ$7PG+5XpjBO= zuIFggBzE~4f!^L;@i@h62{%}d)b2aP&JnLr)AWHi>FMSlIyG(TEd4jTf@&J))qL`- zwA-;{2}}0%bw|VFYgNB@)c<(M?w@cLv?Kfh8;hFXoEL0S8@LXuDwt&nRlB;lx+-dF za?00!5j?U>(Vt81af5CE>w`CMa(GMLEOcjhs7XHM}rA zJJ(vgTTFM7{-apk`A-cQ(>W^UFqbVWnscp~qa)GLu;FG1gyo@MsMe{|9$g(j_N}TwFgE_ zN#!aVoyu-*O8s<8XgX(_qt5|PrZ)|9S&p5I^;b|=XMa?8_RJZd>)h#`J_pzsH=aDd zCG+x=ZaeAamR+24#F-9DHHjUzREoHFzWZ2AN4Q|c_q*j!ZL9g^?aq`v3Dtf5Z>nIm z>;Esy?T@bAeoyN>_wN12)-2pDu;GWz>E8igrGoe)M6xO)#cg6z*Q9>j_+8kd^3xI< zo`{&39?;^rd+~|u7Dfwl@bXT5&+X^ydvs;+@;hGUVaN7ze)(D|R)531ZbKB$Nk)FP zDd&wD-8q&lU+$bUV}HRzC-+IkQfd#1n9T3j7$0$tuls%b{;rsB|L@=1ejq$@&P)4} zW|gc(oK90aoC39@nwtJ`#$9m9N_;C6m7BXWLPIp>`qN8Qnp>h;*XdjdTcGD%V7kR& zwbRrKQEQwwEXi8I<{cokb8*uN)rl+~3M-%6pP#cQ@zc{eD(crK7vKB&_t~D}{WhQX zeXcW3-}iHigX;oCmJmksmI=qUb1|K_`>oT})n!or?oOCyJa;IIRQCb>x{ur+b|2IZ zTa%F^=DVC@3kQ?#s)nC^cg6R8VBI@+m1~vPVm8zuzgI+8JHcqO@kssZ(CRrwZLW zdpdYUmPliPL&JMk|9Li&kB{|UW_7XP@sQZ|aI(MM#$UZveQp|e7qP$OJ8I&|xVzrs zvM#Tp?fX5S-_*`oXySC)ji1T>=SlxJrG?+`6#IWEiK@P(DDa^Ag-y>9_y4qt;2Y&hTg+V%8#wtIEI-&TA+YwqY4wwU9C{Z?jnKG0e;t~R9$iUJSr zt8|O&yT!%DwP&n1+w@gnfn4?9uh$EY%a-48RTH1JWl_!T8rC!Gx*nw}uc%lW7;@m_ z$B$t@CpG5p`Iy3tM!jlsdcf4FS+aUMW77bU0UAxYHuUR>N#qt&FS8U(3dGp6+`9BBh zblTo`2X8-_6kHNtJY~b(EoP$s&p7DWjbuJcKX0%-N@h(-L%;U}v4)fcy>@PX;-}l6vERn{(E{5~^Yhrg_ zo0t3W&$4BYl^^6ucIz#kClq^Zs^0NgUeiu}d$alcv6<=fBE28(x_!adb;(f?gQ@zW zTH(h+Hf%bSx;&+NR`jHhbBh+N%yH#3-^x5o|Bt|f`m~)pcYd4nJMZqU)_CTvu3Zis z90gZDzg#~5(c10zHp%mIDy~-JEoO;%s=cdgSI;!B8C)|?U61`}H0NW;wU`DY`O}$Q znUk2Nxdk_FVqB8LGj-+anwfr6^rg>SeZMkzdDfII8VP~dd|x&^RgNMT?!{%3fkCpPHE}AMW3Fo3eWIfTi01V zS#*O&eBh283*N(-HzWhk#3RP)b$a|rWd^kyKE?UV{7*Gzw3M4zm>k;ncTN< zo0SFAp6Bz@rKah{X5IhrKxX-}WaR^fQ$h{CiiU5!lDep4I+Wvf=#yI{yEyy1-|r}yNaT+%@ntr%;}oKE3T>|yKp}Hx*Lq^s-8B8O*IhJ`FxQh*GbsKdxpY> zgo8{mwO_CPcxJv|Qcd?#ng-L_<^ykTZhrgwt#dnHt6k1EzGJz8t2F#yX-jE^>`GPj z(K;2OQ95ghVya}2U+}FvI;>~7m^Bm4e56jY1wX9XJ~#ENws!17-dQxTf6lu6SuOU6;0mX}1&bm)_lAY-+x3gLCE+;FxFB>SY|aa@TYz}<+b z`|Uw9|22cciUYSoAfGQ|z7Z^_cqZ^$pFYD;DyZLUdQN{8-3ax2)Iyzj$0l;%#$>>=2ei z9F04RpEo_14_gz#Shpj#SLs3oOVqRZPmWz%_EL$dvHt(}`tARJ_emHkDg9bJH-KeU zGlPD{gYWC!?|yIh+j94_DN8FCb2Ek>xM+Is>^t3j#z)%j`~N)EpB+0*YypQ07t@}3 zlcF!)|9$WKs~9a2&DtwLU=j^?ylVrV0Hkf5r!-4+# zKgqviOYU(jcN5IuVaoj`Uyv-dIP;J=W89KYbKln-k8`-NK7Rj)+!fn=1T)lGDl!j$ zdU_gkF3YX*$G^Y5z3r!aG0U2ZX&2Kw4dtun-aa{Nzo+M){r}JQw|A;W?Nt)k$5Hdr zJO0od%i=e-7x%P<_bLU*Gp%ZP?d<+ohpGPW>-djvHlKfFzW-;h(jpEGCSHXPpju;} z?1vYN`^_c_l=}g4$I{c%ZuL*Dd7B-7xb%8#`0^XaK2;=N-q@6o z+1z?Pa|+uQOQux~w*`vUKe+ZheUACP+*Q)gcg;NZy8N@L+CAU%%YWX^-!E%^zh?8_ zO;e@a%adE9FL9Qh&eZ7n*m!BjSN87m*Ma3JiV8J)S8iLk?Jiswed(IGn1iW;2jEx+q?d&2$bdB39B8qIcd zu1=jLy>ICbCyl&?Ii~M@ze$<~x51%O#phLkloiu1CO$^v^$K0{U3WC;<%Mrh^;socpM2S1{uT8~5C1t9iSe}> z`ugl|Pfk`33=B-vyt`$&-`q{}o1ZKc_^D@nH-~xU6nFc(OHxx)LG2{aGWo~i`+u~G z25`x+PMtD;=988Fb?TGD?(jT6zB;qgkx}=3_p;vZ?#wYzN9JxcSix8!m)3sA^3^=j{V z#HaE^=vu@pE{)cmJ9bF8_sKB+&t5tkRDk?=v-$j*3qgWiwsn7YlMkuWAD{fv{`;NcgKupX#_lfLS{L*zeYfk2R40z9Y+B8=;0n}+#l*qO?64}(0Cm4Q_xar zY0kv>jJ=yw9!?S5Hof+9&_vb1S8w?5ZfQ*Ee0ei%qWZRpZzu0OFjci~e?&?rKZ~cQ zXIPs?_!Q+|GvjaFi>Z86u}McNeA|*3>4o-UjG+gv_Bb>y5N!MwBq${)vn+Lnb)bh7 z7u){H+TSMo+Z|j!zpl%P%rdu{FP+M;C}c1+83J>XJUnzHQK zkw<<_pFSMqoII`EZ}O)_)qbmzel9rFId!t)#zjxHgO>cU)3)a;1T_XHuQ++~WZBdQ zetL1{$0sEkWwLZlH(8P;<>v6DNz+yD)nwU;=J`=}2Rdcm-rp||@)TDm*U_n}4>`3I z!uwu`MJ`I}5{X=7w9KaP-^WvvPE0xD`00T@|KBO92fbIE5PI467k0oAQl+brTmwF$TZLY~tMS!*(xJ;cv|(JuR(6-`?KdSp7Y(=EuYKYwzyv zzHT?Qi%H>fT**b(7dJPn?<#+vC(0r@d%n?Qmj%mSas+S|8;N+id9>E7Pz$CteIk51qJC)JwmU5nDG>DCNJnI}?|>$XLx%~`hH?o(0M zrP8OKDIJOHWDH{-{aEnzAiMmPiK?eWMA_Br?K}e8Ec&*5+Ta?wXz8cW%Oxxwn@>!% zd2nKmo8=>C0r|QePj!oqd=!X$v{bp~g5b`NrzVECsl-j$aADc^1%JO}M;38~M@7GNn}x^MZk-!q;aFfavD5j^91WEo z+a=bwcvD;5Qrbd9^zN>U37l3j-6GZVSi@@5^*Vul87ph9w(v8BoSUK=KJQ-jds`7P zv0MB5Qe=XIg5G3D$T+WC=bF~C>ywJn(Kxr+N_|?aZ$kIHYjJe-w$}{b^7`7^&FvHJ zRXpb1{kG_8X!uR-XBB5<)U)kc-K96ToHw~(q^oHEL394(>oHl@M)zC2u8nC)d73fr9S6Ol4Eo`$)`D z$gqt3plPG6%?)bBy}kDI`u=~VOO`HGw6(pPS6f>1@q(O*7^9zxrKM%W{yN)#UzXe7 z{J-x6zp$FmhUjZHCCZc8ry8pBm4C{}v6Z!yP3)pktjtd|bx({NVuUng@>_NCe?kqy?7 z9gm)79{&0H`R(b?-1}rQ96t=Oc+Zh{Ky1E&4(yeuAHBlu_C zkN&zZi=`rkTy?x6LJKS+VgS!|L^s0TMpjN`ts7oiEYo+T?hZ`SH5WJ zdfKTzZ$;%??|F|>wbxEv#XmdgE_-C)o|*}e!KmsMs-q=$|e>5+==U27ALX#JoP?$y3<*WXWur5)BlSqJ=)bZuWB&yDjsmw zut_=o-|Xi*mHYpvTs2VV)q3bL&*X{YwAHbvL`3y<y4FJQtHkc?eAV|-Rngf6*xX@RkwWT*Ls-U zqH@9G!?KmSmwGi4f^Jrcv7Xt-FJW+C-+I}X78(C%wKK#&Uh-+_!|bLh%2AOTuFX0d z-wUp9xalc%(`>S5^JI1PKq)z)hu0qY*L@QHowuI%ioELrDaL*8eeWssf5?lMvfDGU zJw;VRnEj{DBwls5sPBo>EKVGLbGQ6{Zv5%$2aW6pt_NSyVA5l%Vf_&Jd8XUDX+Co- zF1mHDJU=_TT{n8$jqCnxe6n5ZYo2PC9Rl4Y zl^d~ych|S&bu2%mgpSu8>yex$enOZ>xqm`<#*Ytfc8it@O=Gt?=KAXL?l0cIv{s9S zPm`-oq+M3AZ{7fagtVQgMpV;DG{(0$NpA~Pv`(c~( z8$QnSoF69ln!L*ss`Pr6eaXXI@W1>9o3+IWPd#JTIXrnSWb;u+n45j}yFGhstP?-H z_+$Nk4|C-v7|}S^Z$DYSQu7{5$7*L_S*Wwob=Tym0BK zr`oYK*Jp`e|6rRiQB^DB-nu2r9xESc4?HFG+~<^#X1U|9@9*Q4y=(U0o65V(Jm*G$ zviF`>ll~~QZ>m^#$oKxoz*TpfR|Fi`^!5yA^KIEZ`co&f-T%Ay>$T|DK2mxvXAUnn zP1M>|`B!EAj3=s0F}lUsL3!^UUhA6kp!jrP`ESJobNT1(nj&5EBX8B6dxx{XRp_ez z-@PPg!K0%^eI?$Y;7YMWPP+A-~Mk9fD?Y3zj}b`wo?u0D3n(i4{lkTn5%P{LNN<3ulq~RYIIqCNTP4AlImQ5e;HqW_VmK%4(=L?$qvlnFt&*Ms6sv2LgGQafMwxx@A>^yzo>XaI* zzS}dCcJ6v49`oe2?mO{`D`GT16e-&KYn)Cwo2&14!zlA}<}Ov<OFIca0GW}ajq>!c3Qk|1!%cIlX z%h@NZ+O6o)32ck0Tr^u=ci}>wzHmm%=koCQbDp47N-nBo8JM5~!Eo;+Nr8GWF! zAoH}rOrOZ*J~FwH_0`v1?+L^uMt8VZOMVjlufL4 zrXJ8t*%b6fBe!o$8Q1@UPaR^)wPIR--<^;MP%J84k#3Y?a(mvS`A2qbt^RqYw_Bt< zWDon9gO3tUxhOJgn*^~1|Gv15gXNL>rx2CTQ*WDZU!PZTyRYe*7$*nE{|+Blr}V4m&0C;6-?{I<$6QlgShOB17VB+Pva`*6F=`Vota_C%sp+* zv3r{mIGbC;7iyUE|6a{>JbbFi!>MzWDi-tSojg3PS+voZm1FIV{jBTP)Uif?W6w+Q z4Ad*yE>l#rDc15vkw?}td-30^SL@u|DY*X0wcP0<0YOumSvRL899zR0vA1wN%XVek z-p>bhciu>`3M$RuHoNw!*xF2=X;m1j3}f?(*Z>wQJ|@+Og6#4?fA;E07?p4c*;R?{ z6}mL>^{b6+*1>I8oz)g;bm-~c-^+A3zjA9I)4i{kT>kuP{>0|Jos;irLck}99BsV= zGoEa`A+liI-PP?0CsxJ!RX$x7yFKCRx~$gNZ({FcJ}GG>&s@TzUt=tKxcyG&cCR@) zSIH>dwQcdBh;^1fu9cyJm6?}}(2?)Evyc7AF0y%8GK z`YCqxgkQfGiLFzw>JDa=*_*uHHk{>_T;rzP8JpxP-ZXkjOta&8EEm_x+ugQs$rP{E zEUVLdzt-Q__djCKrpqm%Z;wyPR9;H2a4Cz2X;3&qaLDJ{ns6zp0f~*s$1#QEdIqmrp)g zZ{KUhI>S7 zXVDpaWI?g`lB07|U!R?6E%wql!tZd!EuLS&p!8m^|PuS!84+_p_K|C3$jTs_<4;!A}WYMvF|-_^Jy z{e;)%X*RiB^LFxEDvK|^6yB+R`PsvLsnOX=?`z(;ioB0(Kl`2Mpz)QstIqb*yA zY~1p=(4R3ue2<&v-hCSE<-I!&rD;^uDOTyvZ+vMV*^;{We}bH8)@*IJG{*iVQm!-l zB)O+4e0Pwyv8a?-_v2UXjJ!#HXXmwfcZWZk^zih?GJj;qm(Hqn`qPFV z6W7j|`rrG@O)-x3T5I-f*Oz-`$;}>LFI8)%f9%oJ+W~biL{*mCb*^kZs&W6j%C+<_ zTc2Ldn|Z%H>ALid!aM&(CKa43{LEl1w%z86u)n<#PipT0}MkkKAJdD#yPb*ZysP_*Ud;1LOCMF>><#j>7*M4n4nI@NU8HfDc!~ zKU_PdTJh}XREMbPQMtY|cS-rzE@fFgO^0Ri?x?rrLLQMV@9*AV?-XDDxbvrG#E!IT z-+hbBCSx}f7Dv_`3%uKM!u`JX0AJMD%1I+$1RDJ8!_!0tBysaSb5!Ho1JBl zrs8(rQfje#|FItzC1+@C4H0?JnzOlS-yGgs-Ann`y1zH(TW~?|f&8Ied$*d_)$A`^ zp>waXRQny%y??rGOH>yBUn8^l?C!@&n^*07a403`d|XD@$!^0lHfxT=+wepdU;5zm zPA){wl&k9OWeLLt{O7n^re~f$l&X1RwjI-5q2CFg{`@qEJ(lj8KYQEK2RF9G?D^$q z_)Gf!xx-ro-Z&MVXF{Vj|y{VL`_4zJ!>~l$3=~Xue(Slr?E6X`FnAUbU z96dL6hDoySFCMSsJJ{vj`yzL)n;F%xv;Gf5?5?8UX2;^IePni(`5w48)!yyYg!##z zCvGnMYpy0yT6k%V?$MBhtAFDer|3TCyt z8`iMpb5h}&9TG>6#v15mi|MVpXSS{Llv(n%J?l=?muiN+jcz`B-^=yb8>5)&%(tlp zOK;Bd+i7+-Zz;dD8PChZ`}!E&B8#%OKY2TSWA!idx8I6yF3VZBwD8f^`i8C4 zKW!$p;nkB{gQqB;m0MJkas8D4l3K1|EvD?-OBabcHg*f@4Gz6yfXR7Ly2GMsSmHs(^)SUe6oFZ=Zw4s z|E}!`KNDfXbiS&q`w1T6KZ|?G_9G~AlB~h~? z=?I)$Qfi9dhMM4fD+V*rQ~uxaMn3>`t0>Cso_n?r?hBf_%*DmJa zdz^Rpp!)I0^6pPI9u<>*u$(D#@$!cs*VHUIBb~SHo3`{ZV+T#CC+-hS^tF|`Kn;;C zMob%Pk15(`8?$}$wTQhu!}Z4Jw{{+Skv*Mv&;DPqS#oOAQ&#T6lj-UcZJ!90NaZ^j zo!PWHfoqdl?P9BZ8@BW34@~g3I_RRD?9!_J7#{;=lezqx<3H{XxTP5ON=ZQCmSgWAz) zNs}_gqfNE6`SaD6DjocGcYeXYw%Q#wYJ%>*^1;E!lQS~Kruq9g6)ktqY1;DgW<+}2!W~pDEtX}x& z2&asFoy^i_6OO0PuWeIy?|Tq^|CgB0f8~v@Z!HgVS}=L$hOjjejZ2oOsHmwaDJn9i z&#yIm^ytxrrQYJ3uU?mo|Kao`S8wO0khr+MO`A4FZTj*yzkc_{O(CIyO_LrK9XTS+ z^Efr@c|+*TqSF^G_2f2QJ>9%&lhBmVqmpZUcXe@v9656Bh@pF=C+8}kUoMjuE?RSF zisb8f)$l1d|CjAny>`zkP3~H7(TXEWd|md0Jn3KYbFIPCX^VHo)r*z?jGeshsmwn! z$&d+GbT|Gon7;o@SC`h2unb=fYrX3-kk)y2C)7u84#<=>KhG0`dL^TI{Tdb0NHDy@{(+`CLc z_v{F0=3EowsCxs-YznA`+u(A zUD^|;++qG`^|}3M#7l#gqXrqu;Pnz2u`EfCb{~A>u$ohuC4{m4V!{@m;8h!nAG77? zTc&nfr+PeUtu?GTa5U}m+Ps+e|E@2N-x@MaB=^WMTh*&S{T{yI{;hoX$>aa2uF}_& z`YoqgmITQE|DVlc8`84%XS+eci{2M$7Mu7hi2N;i_`PLzU|e388@$@+tP6tZ}s-v!}DxeBh2=RTlq)lUdgbG z^V`eHzc-{|Z}s;i|BLZw{yTBb__AAf+n2km)sudFn7-lrfw-Dao`PaLZ(1kLob3~1 z`Om7*qHX!TUZ?-sEoOh))PK+V@;z~SrLFRh*llJj|7kEe3f`DuDRz?i`NKK;u0Oi< z@9xC8S$?+XPZ<8W6}OCQ{XHWY?QL`S*=lc@7V_{y`|W^~C-0YLZhtT>Q$KNbp}6SV zmYse(-}+n?>oK_=oM%`XCYrh^xN2Fg`_7fT8mDABzp(z>xoBl-#y$p)4bD_2l%)lOJbJUSso5Ch@P{x3m7IKRn&NS8GoG zFU#ZMZ?wP8^lq&A%zeuLxLE!Mi8YoHFHg^(|IB-4e1!0J{pv;M_Z)bkZT@qUQNrc3 zCyj$e*Ga(E0!(MR6e9L(nbeyzr{(wU37W5;zpU{5na{^Ret*Drf6mWrpL-{apGbZ> zx2yEEj{d~ud@>dZl|6gh=5@O~J|*;fckS=8_`14%M<2F|$8Go{n6+QKT*v(9B^~~& zPjvM^oSkEFfBRvB^ZWMb#J4>){$aE%?dP|USs_P%?wcv8xozJR!_T$VCsG;@X?_2> zX<6Ehk|&p4rN7N=S-MX&`1p@ye&&1578yUgQ58|1_|0d2rR(wO(;l8Tw%>mvd`Hc$ z{6}YAs@?feS-9ZS4C5V|F)lN;R`>4=v?M^x$oyscK_w>UHPvT@zzyp>3%s|_{)DzP2P-`!H$JHHLv#zy4k?`(}w?yShEDLTi_oQ&V9?&v>lS;C^7 z|I&VLuHULXdDEY(CuUmQkI|FQH@nf0P=528m8({%Rw&CVK@I!D8)rT$7qa)MsUw`5|q`}KG$6W5b_>=`NT7Q4^_@h(&zt}Qut|hCoSU#sjnAj7! zm&%r>4c7lz{mkJK-C~k&j0-G-j_vPA3{IbUDtN-*%hewa zn_lj%wyG4#vOyI}}^2l4=fB*=*Ke%%A=JWxzK6)0@s#e^35(?N531ZtsoXxb@usmh;VM zJya@Dv^SD{j#XQ+zsQ4y^Z(0zU($d4q%!M{q92o=iT!2O_%q+S;Kj|SJCe_Qe(}@$ zo_cm%?+f#DcT%gB&DNYgdMl@R`r9cVJ!WR=9jgEFB_(Wk+*eJegMtRuoX_@NclopX zC->90hqiA>d6-tbmFDn zzpnSY$L!Jtl{GduO?h9Z-D{L{vvIpKyKP_on?o(kuX*3s(8ZJCAtZD2Q-z13U&-mu(uI^X8yv)gSq z+^E_3Li3GTe2tfJc~$HUZNZaMpWl|Ym~{5z^NQ;Dk`Hw9(TXXP+PU`@i@phxg5}&oj4(@VuMbacc6jyxU(MUM;_UuaVt&x?=8M z;|bBs7uU+ao3l%LfBaUV^ZM_4CpJ&^jn&y|Sbo!uY1^&#Y3>K^JPi3@$G3}h`qNq6 z5^OX5UyDBc7pFPn+oiNQg*U(MDE8@!_4&3l*^cY;wFh?=Rv#XziA-l}&eN=KNdmc2>gFl~3MPHU8JV*ELsv zLHH?wJzIWq=CPhm%V!cH~)Y`oGOOA>u&w zMCL7xj(hVS*q+w@``vhY&M~RQYo5Fpey4PqMf?3>-^v>Ys~5jE{d(W=?Eb#+L?P_bHY~F z*KoZK)^nfqm&3Vqkn;?f6ldk(54s4r6~J_$=6$QQ{wTDoSdZM(nLpk2S<=G^A9>urRumrvI%UKz;Jqn>y{%~(Cp@aez%4-en$o-lD%%gwgwXZPud&HUey z?{as;yyZ-HyZ`h?@ji4`w@;L?Pte(POl9Hp|4Lc2(<;7BpV{)A`Tpjdb(>@@B--}hk!T}Rzb+I~kPn>Eh$UNCnFOB`+ovnXc{=QlC^_4#d zM+D2+&NcIA+Vr z_kZ%Aa;Nan1#9*Q6+WBe?i*bBed|tc>uq7b&$2V0-DGZdY>83jnchnq&RKnTy?#{n zg6zf6XZtHJGeoCPUAcbGrpM7uZtM7S4N& zEsxctEL!&4&9iHrvHX%1i+{Yidp(Y&_mCKG?x!mj%y-1!nq0DCzAGpje3{{MELU*6 zamo7ayH>F(PK(DdE#uB8?XWFE~dmOkQRQZqS29x6+gsaP7D$ zki`gA3mRa5!LfpgPuBSCLfELhi8N#AfwOG^+ySs5cPDoT(SjEdYVw5~mJmjBX(wHfda#2yWEn#bkRCLRD*_J8wJK$LE57f+ zd%e3RPP2O#i3v>Q;P}5|O5R6zDUD0!FX#Wb>Cf4{#V))478~P3`I(C&SGR2S<-WCi z+KPY!Z<*I#lX}VFa+pQ%W%7!@-0>`n9QQ2>V{||Kply9d>7Qww|2f|Nu+6ICzZ2?x z+56(0i(L$<8c|O*UWpi}b4wP?wOADTWcL01>HRJ9XT@h7*J}JLCi!c6{mwxcb^K2T-}ev+p-Vm zXUs8i`Pe1Hx%bN@?~Mfy4|z!Cz4g{s`fww;|K=s@@^?AUlf$P)ZM?NX%j%J+fjYOL zQDDHwV@HleSXX{Z0d40izgsH)?afVPNWTLl#IC%79K8-F|<|L@W#zMq|Yp7_b= z?Z2v};AHbjKzmB9c;qaTOrd{|?f)^C->X#LmVaMP(e_&2789oG@9&QO`1ttam0H zQfTHBr4J9=<&zFJu})JzA^l_`yIci>ZPgcs`L|Y9w9IeWuT* z5~fU>_UQlL{r^EHP~Y;~@GC)jDbtgZ-;ew4w{ewUSm5~L`g(cLai#3?H5WedRJPYV z(MnX$cCmi@>B5_X|Id8+bkXCD=KuH~&+Y$ro;u|<2eKu>qVA8ysZ*y4zPt#WGG)qx z&FAe_7w-T6@Ar*uxwB(==T|

EZ8x{K7(K!-NA2HBYtUH%`t|c_i|-cGap?hd}3H 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 diff --git a/docs/images/akka-as-library-4.png b/docs/images/akka-as-library-4.png deleted file mode 100644 index e9adb4e10402430788f2ce3280092cf8b24df4db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39979 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�~hvN0CS8pz#^)qGjlgCf0 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 Date: Mon, 23 May 2011 10:45:53 +0200 Subject: [PATCH 14/20] Added docs about setting JVM options and override akka.conf options to multi-jvm-testing.rst --- akka-docs/dev/multi-jvm-testing.rst | 47 ++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/akka-docs/dev/multi-jvm-testing.rst b/akka-docs/dev/multi-jvm-testing.rst index 4665545a2e..c69ad34adb 100644 --- a/akka-docs/dev/multi-jvm-testing.rst +++ b/akka-docs/dev/multi-jvm-testing.rst @@ -19,7 +19,7 @@ You can specify JVM options for the forked JVMs:: There are two sbt commands: ``multi-jvm-run`` for running applications and ``multi-jvm-test`` for running ScalaTest tests. -The ``MultiJvmTests`` trait resides in the ``project/build`` directory. +The ``MultiJvmTests`` trait resides in the ``project/build`` directory. Creating application tests ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -91,6 +91,51 @@ You can change what the ``MultiJvm`` identifier is. For example, to change it to Your tests should now be named ``{TestName}ClusterTest{NodeName}``. +Configuration of the JVM instances +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Setting JVM options +------------------- + +You can define specific JVM options for each of the spawned JVMs. You do that by creating +a file named after the node in the test with suffix ``.opts``. + +For example, to feed the JVM options ``-Dakka.cluster.nodename=node1`` and +``-Dakka.cluster.port=9991`` to the ``TestMultiJvmNode1`` let's create three ``*.opts`` files +and add the options to them. + +``TestMultiJvmNode1.opts``:: + + -Dakka.cluster.nodename=node1 -Dakka.cluster.port=9991 + +``TestMultiJvmNode2.opts``:: + + -Dakka.cluster.nodename=node2 -Dakka.cluster.port=9992 + +``TestMultiJvmNode3.opts``:: + + -Dakka.cluster.nodename=node3 -Dakka.cluster.port=9993 + +Overriding akka.conf options +---------------------------- + +You can also override the options in the ``akka.conf`` file with different options for each +spawned JVM. You do that by creating a file named after the node in the test with suffix ``.conf``. + +For example, to override the configuration option ``akka.cluster.name`` let's create three ``*.conf`` files +and add the option to them. + +``TestMultiJvmNode1.conf``:: + + akka.cluster.name = "test-cluster" + +``TestMultiJvmNode2.conf``:: + + akka.cluster.name = "test-cluster" + +``TestMultiJvmNode3.conf``:: + + akka.cluster.name = "test-cluster" ScalaTest ~~~~~~~~~ From aa52486fdc8679f6f90aa2b05d590cd7afb59b70 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 23 May 2011 11:27:02 +0200 Subject: [PATCH 15/20] Fixing erronous use of actor uuid as string in ActorRegistry, closing ticket #886 --- .../src/main/scala/akka/actor/ActorRegistry.scala | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/akka-actor/src/main/scala/akka/actor/ActorRegistry.scala b/akka-actor/src/main/scala/akka/actor/ActorRegistry.scala index c6710d60ff..f3c38887fb 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorRegistry.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorRegistry.scala @@ -34,7 +34,7 @@ private[actor] final class ActorRegistry private[actor] () extends ListenerManag //private val isClusterEnabled = ReflectiveAccess.isClusterEnabled private val actorsByAddress = new ConcurrentHashMap[String, ActorRef] - private val actorsByUuid = new ConcurrentHashMap[String, ActorRef] + private val actorsByUuid = new ConcurrentHashMap[Uuid, ActorRef] private val typedActorsByUuid = new ConcurrentHashMap[Uuid, AnyRef] private val guard = new ReadWriteGuard @@ -66,7 +66,7 @@ private[actor] final class ActorRegistry private[actor] () extends ListenerManag // throw new IllegalStateException("Actor 'address' [" + address + "] is already in use, can't register actor [" + actor + "]") actorsByAddress.put(address, actor) - actorsByUuid.put(actor.uuid.toString, actor) + actorsByUuid.put(actor.uuid, actor) notifyListeners(ActorRegistered(address, actor)) } @@ -121,7 +121,7 @@ private[actor] final class ActorRegistry private[actor] () extends ListenerManag */ class LocalActorRegistry( private val actorsByAddress: ConcurrentHashMap[String, ActorRef], - private val actorsByUuid: ConcurrentHashMap[String, ActorRef], + private val actorsByUuid: ConcurrentHashMap[Uuid, ActorRef], private val typedActorsByUuid: ConcurrentHashMap[Uuid, AnyRef]) { /** @@ -153,11 +153,8 @@ class LocalActorRegistry( /** * Finds the actor that have a specific uuid. */ - private[akka] def actorFor(uuid: Uuid): Option[ActorRef] = { - val uuidAsString = uuid.toString - if (actorsByUuid.containsKey(uuidAsString)) Some(actorsByUuid.get(uuidAsString)) - else None - } + private[akka] def actorFor(uuid: Uuid): Option[ActorRef] = + Option(actorsByUuid.get(uuid)) /** * Finds the typed actor that have a specific address. From 8a790b1ddfd6789f937590eae25f991dab10ad21 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 23 May 2011 11:31:01 +0200 Subject: [PATCH 16/20] Renaming CompletableFuture to Promise, Renaming AlreadyCompletedFuture to KeptPromise, closing ticket #854 --- .../akka/actor/actor/TypedActorSpec.scala | 6 +-- .../akka/dispatch/MailboxConfigSpec.scala | 2 +- .../src/main/scala/akka/actor/ActorRef.scala | 14 +++--- .../src/main/scala/akka/dispatch/Future.scala | 50 +++++++++---------- .../scala/akka/dispatch/MessageHandling.scala | 6 +-- .../remoteinterface/RemoteInterface.scala | 6 +-- .../scala/akka/util/ReflectiveAccess.scala | 2 +- .../scala/akka/cluster/ClusterActorRef.scala | 6 +-- .../akka/cluster/ReplicatedClusterRef.scala | 2 +- .../scala/akka/cluster/TransactionLog.scala | 16 +++--- akka-docs/java/dataflow.rst | 2 +- akka-docs/java/untyped-actors.rst | 4 +- akka-docs/scala/actors.rst | 2 +- .../remote/netty/NettyRemoteSupport.scala | 24 ++++----- .../src/main/scala/akka/agent/Agent.scala | 6 +-- 15 files changed, 74 insertions(+), 74 deletions(-) diff --git a/akka-actor-tests/src/test/scala/akka/actor/actor/TypedActorSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/actor/TypedActorSpec.scala index 5b9f155d75..f21f49b023 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/actor/TypedActorSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/actor/TypedActorSpec.scala @@ -11,7 +11,7 @@ import org.scalatest.{ BeforeAndAfterAll, WordSpec, BeforeAndAfterEach } import akka.actor.TypedActor._ import akka.japi.{ Option ⇒ JOption } import akka.util.Duration -import akka.dispatch.{ Dispatchers, Future, AlreadyCompletedFuture } +import akka.dispatch.{ Dispatchers, Future, KeptPromise } import akka.routing.CyclicIterator object TypedActorSpec { @@ -43,7 +43,7 @@ object TypedActorSpec { def pigdog = "Pigdog" - def futurePigdog(): Future[String] = new AlreadyCompletedFuture(Right(pigdog)) + def futurePigdog(): Future[String] = new KeptPromise(Right(pigdog)) def futurePigdog(delay: Long): Future[String] = { Thread.sleep(delay) futurePigdog @@ -51,7 +51,7 @@ object TypedActorSpec { def futurePigdog(delay: Long, numbered: Int): Future[String] = { Thread.sleep(delay) - new AlreadyCompletedFuture(Right(pigdog + numbered)) + new KeptPromise(Right(pigdog + numbered)) } def futureComposePigdogFrom(foo: Foo): Future[String] = diff --git a/akka-actor-tests/src/test/scala/akka/dispatch/MailboxConfigSpec.scala b/akka-actor-tests/src/test/scala/akka/dispatch/MailboxConfigSpec.scala index 773a68ef89..e71ca14721 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/MailboxConfigSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/MailboxConfigSpec.scala @@ -68,7 +68,7 @@ abstract class MailboxSpec extends WordSpec with MustMatchers with BeforeAndAfte //CANDIDATE FOR TESTKIT def spawn[T <: AnyRef](fun: ⇒ T)(implicit within: Duration): Future[T] = { - val result = new DefaultCompletableFuture[T](within.length, within.unit) + val result = new DefaultPromise[T](within.length, within.unit) val t = new Thread(new Runnable { def run = try { result.completeWithResult(fun) diff --git a/akka-actor/src/main/scala/akka/actor/ActorRef.scala b/akka-actor/src/main/scala/akka/actor/ActorRef.scala index 61fbf56be9..f41f2b46ae 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorRef.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorRef.scala @@ -208,7 +208,7 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] { scal * The reference sender future of the last received message. * Is defined if the message was sent with sent with '!!' or '!!!', else None. */ - def getSenderFuture: Option[CompletableFuture[Any]] = senderFuture + def getSenderFuture: Option[Promise[Any]] = senderFuture /** * Is the actor being restarted? @@ -482,7 +482,7 @@ trait ActorRef extends ActorRefShared with java.lang.Comparable[ActorRef] { scal message: Any, timeout: Long, senderOption: Option[ActorRef], - senderFuture: Option[CompletableFuture[T]]): CompletableFuture[T] + senderFuture: Option[Promise[T]]): Promise[T] protected[akka] def actorInstance: AtomicReference[Actor] @@ -698,10 +698,10 @@ class LocalActorRef private[akka] (private[this] val actorFactory: () ⇒ Actor, message: Any, timeout: Long, senderOption: Option[ActorRef], - senderFuture: Option[CompletableFuture[T]]): CompletableFuture[T] = { - val future = if (senderFuture.isDefined) senderFuture else Some(new DefaultCompletableFuture[T](timeout)) + senderFuture: Option[Promise[T]]): Promise[T] = { + val future = if (senderFuture.isDefined) senderFuture else Some(new DefaultPromise[T](timeout)) dispatcher dispatchMessage new MessageInvocation( - this, message, senderOption, future.asInstanceOf[Some[CompletableFuture[Any]]]) + this, message, senderOption, future.asInstanceOf[Some[Promise[Any]]]) future.get } @@ -1020,7 +1020,7 @@ private[akka] case class RemoteActorRef private[akka] ( message: Any, timeout: Long, senderOption: Option[ActorRef], - senderFuture: Option[CompletableFuture[T]]): CompletableFuture[T] = { + senderFuture: Option[Promise[T]]): Promise[T] = { val future = Actor.remote.send[T]( message, senderOption, senderFuture, remoteAddress, timeout, false, this, loader) @@ -1155,7 +1155,7 @@ trait ScalaActorRef extends ActorRefShared { ref: ActorRef ⇒ * The reference sender future of the last received message. * Is defined if the message was sent with sent with '!!' or '!!!', else None. */ - def senderFuture(): Option[CompletableFuture[Any]] = { + def senderFuture(): Option[Promise[Any]] = { val msg = currentMessage if (msg eq null) None else msg.senderFuture diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index 3ffdc0e1a6..4ebe2b7f05 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -56,7 +56,7 @@ object Futures { * Returns a Future to the result of the first future in the list that is completed */ def firstCompletedOf[T](futures: Iterable[Future[T]], timeout: Long = Long.MaxValue): Future[T] = { - val futureResult = new DefaultCompletableFuture[T](timeout) + val futureResult = new DefaultPromise[T](timeout) val completeFirst: Future[T] ⇒ Unit = _.value.foreach(futureResult complete _) for (f ← futures) f onComplete completeFirst @@ -83,9 +83,9 @@ object Futures { */ def fold[T, R](zero: R, timeout: Long = Actor.TIMEOUT)(futures: Iterable[Future[T]])(foldFun: (R, T) ⇒ R): Future[R] = { if (futures.isEmpty) { - new AlreadyCompletedFuture[R](Right(zero)) + new KeptPromise[R](Right(zero)) } else { - val result = new DefaultCompletableFuture[R](timeout) + val result = new DefaultPromise[R](timeout) val results = new ConcurrentLinkedQueue[T]() val allDone = futures.size @@ -135,9 +135,9 @@ object Futures { */ def reduce[T, R >: T](futures: Iterable[Future[T]], timeout: Long = Actor.TIMEOUT)(op: (R, T) ⇒ T): Future[R] = { if (futures.isEmpty) - new AlreadyCompletedFuture[R](Left(new UnsupportedOperationException("empty reduce left"))) + new KeptPromise[R](Left(new UnsupportedOperationException("empty reduce left"))) else { - val result = new DefaultCompletableFuture[R](timeout) + val result = new DefaultPromise[R](timeout) val seedFound = new AtomicBoolean(false) val seedFold: Future[T] ⇒ Unit = f ⇒ { if (seedFound.compareAndSet(false, true)) { //Only the first completed should trigger the fold @@ -202,7 +202,7 @@ object Futures { * in parallel. * * def traverse[A, B, M[_] <: Traversable[_]](in: M[A], timeout: Long = Actor.TIMEOUT)(fn: A => Future[B])(implicit cbf: CanBuildFrom[M[A], B, M[B]]): Future[M[B]] = - * in.foldLeft(new DefaultCompletableFuture[Builder[B, M[B]]](timeout).completeWithResult(cbf(in)): Future[Builder[B, M[B]]]) { (fr, a) => + * in.foldLeft(new DefaultPromise[Builder[B, M[B]]](timeout).completeWithResult(cbf(in)): Future[Builder[B, M[B]]]) { (fr, a) => * val fb = fn(a.asInstanceOf[A]) * for (r <- fr; b <-fb) yield (r += b) * }.map(_.result) @@ -230,7 +230,7 @@ object Future { /** * Create an empty Future with default timeout */ - def empty[T](timeout: Long = Actor.TIMEOUT) = new DefaultCompletableFuture[T](timeout) + def empty[T](timeout: Long = Actor.TIMEOUT) = new DefaultPromise[T](timeout) import scala.collection.mutable.Builder import scala.collection.generic.CanBuildFrom @@ -240,7 +240,7 @@ object Future { * Useful for reducing many Futures into a single Future. */ def sequence[A, M[_] <: Traversable[_]](in: M[Future[A]], timeout: Long = Actor.TIMEOUT)(implicit cbf: CanBuildFrom[M[Future[A]], A, M[A]]): Future[M[A]] = - in.foldLeft(new DefaultCompletableFuture[Builder[A, M[A]]](timeout).completeWithResult(cbf(in)): Future[Builder[A, M[A]]])((fr, fa) ⇒ for (r ← fr; a ← fa.asInstanceOf[Future[A]]) yield (r += a)).map(_.result) + in.foldLeft(new DefaultPromise[Builder[A, M[A]]](timeout).completeWithResult(cbf(in)): Future[Builder[A, M[A]]])((fr, fa) ⇒ for (r ← fr; a ← fa.asInstanceOf[Future[A]]) yield (r += a)).map(_.result) /** * Transforms a Traversable[A] into a Future[Traversable[B]] using the provided Function A => Future[B]. @@ -251,7 +251,7 @@ object Future { *

*/ def traverse[A, B, M[_] <: Traversable[_]](in: M[A], timeout: Long = Actor.TIMEOUT)(fn: A ⇒ Future[B])(implicit cbf: CanBuildFrom[M[A], B, M[B]]): Future[M[B]] = - in.foldLeft(new DefaultCompletableFuture[Builder[B, M[B]]](timeout).completeWithResult(cbf(in)): Future[Builder[B, M[B]]]) { (fr, a) ⇒ + in.foldLeft(new DefaultPromise[Builder[B, M[B]]](timeout).completeWithResult(cbf(in)): Future[Builder[B, M[B]]]) { (fr, a) ⇒ val fb = fn(a.asInstanceOf[A]) for (r ← fr; b ← fb) yield (r += b) }.map(_.result) @@ -267,14 +267,14 @@ object Future { * * This allows working with Futures in an imperative style without blocking for each result. * - * Completing a Future using 'CompletableFuture << Future' will also suspend execution until the + * Completing a Future using 'Promise << Future' will also suspend execution until the * value of the other Future is available. * * The Delimited Continuations compiler plugin must be enabled in order to use this method. */ def flow[A](body: ⇒ A @cps[Future[Any]], timeout: Long = Actor.TIMEOUT): Future[A] = { val future = Promise[A](timeout) - (reset(future.asInstanceOf[CompletableFuture[Any]].completeWithResult(body)): Future[Any]) onComplete { f ⇒ + (reset(future.asInstanceOf[Promise[Any]].completeWithResult(body)): Future[Any]) onComplete { f ⇒ val opte = f.exception if (opte.isDefined) future completeWithException (opte.get) } @@ -417,7 +417,7 @@ sealed trait Future[+T] { *
*/ final def collect[A](pf: PartialFunction[Any, A]): Future[A] = { - val fa = new DefaultCompletableFuture[A](timeoutInNanos, NANOS) + val fa = new DefaultPromise[A](timeoutInNanos, NANOS) onComplete { ft ⇒ val v = ft.value.get fa complete { @@ -450,7 +450,7 @@ sealed trait Future[+T] { *
*/ final def failure[A >: T](pf: PartialFunction[Throwable, A]): Future[A] = { - val fa = new DefaultCompletableFuture[A](timeoutInNanos, NANOS) + val fa = new DefaultPromise[A](timeoutInNanos, NANOS) onComplete { ft ⇒ val opte = ft.exception fa complete { @@ -482,7 +482,7 @@ sealed trait Future[+T] { *
*/ final def map[A](f: T ⇒ A): Future[A] = { - val fa = new DefaultCompletableFuture[A](timeoutInNanos, NANOS) + val fa = new DefaultPromise[A](timeoutInNanos, NANOS) onComplete { ft ⇒ val optv = ft.value if (optv.isDefined) { @@ -518,7 +518,7 @@ sealed trait Future[+T] { * */ final def flatMap[A](f: T ⇒ Future[A]): Future[A] = { - val fa = new DefaultCompletableFuture[A](timeoutInNanos, NANOS) + val fa = new DefaultPromise[A](timeoutInNanos, NANOS) onComplete { ft ⇒ val optv = ft.value if (optv.isDefined) { @@ -546,7 +546,7 @@ sealed trait Future[+T] { } final def filter(p: Any ⇒ Boolean): Future[Any] = { - val f = new DefaultCompletableFuture[T](timeoutInNanos, NANOS) + val f = new DefaultPromise[T](timeoutInNanos, NANOS) onComplete { ft ⇒ val optv = ft.value if (optv.isDefined) { @@ -596,16 +596,16 @@ sealed trait Future[+T] { object Promise { - def apply[A](timeout: Long): CompletableFuture[A] = new DefaultCompletableFuture[A](timeout) + def apply[A](timeout: Long): Promise[A] = new DefaultPromise[A](timeout) - def apply[A](): CompletableFuture[A] = apply(Actor.TIMEOUT) + def apply[A](): Promise[A] = apply(Actor.TIMEOUT) } /** * Essentially this is the Promise (or write-side) of a Future (read-side). */ -trait CompletableFuture[T] extends Future[T] { +trait Promise[T] extends Future[T] { /** * Completes this Future with the specified result, if not already completed. * @return this @@ -637,7 +637,7 @@ trait CompletableFuture[T] extends Future[T] { final def <<(value: T): Future[T] @cps[Future[Any]] = shift { cont: (Future[T] ⇒ Future[Any]) ⇒ cont(complete(Right(value))) } final def <<(other: Future[T]): Future[T] @cps[Future[Any]] = shift { cont: (Future[T] ⇒ Future[Any]) ⇒ - val fr = new DefaultCompletableFuture[Any](Actor.TIMEOUT) + val fr = new DefaultPromise[Any](Actor.TIMEOUT) this completeWith other onComplete { f ⇒ try { fr completeWith cont(f) @@ -655,7 +655,7 @@ trait CompletableFuture[T] extends Future[T] { /** * The default concrete Future implementation. */ -class DefaultCompletableFuture[T](timeout: Long, timeunit: TimeUnit) extends CompletableFuture[T] { +class DefaultPromise[T](timeout: Long, timeunit: TimeUnit) extends Promise[T] { def this() = this(0, MILLIS) @@ -722,7 +722,7 @@ class DefaultCompletableFuture[T](timeout: Long, timeunit: TimeUnit) extends Com } } - def complete(value: Either[Throwable, T]): DefaultCompletableFuture[T] = { + def complete(value: Either[Throwable, T]): DefaultPromise[T] = { _lock.lock val notifyTheseListeners = try { if (_value.isEmpty && !isExpired) { //Only complete if we aren't expired @@ -764,7 +764,7 @@ class DefaultCompletableFuture[T](timeout: Long, timeunit: TimeUnit) extends Com this } - def onComplete(func: Future[T] ⇒ Unit): CompletableFuture[T] = { + def onComplete(func: Future[T] ⇒ Unit): Promise[T] = { _lock.lock val notifyNow = try { if (_value.isEmpty) { @@ -800,10 +800,10 @@ class DefaultCompletableFuture[T](timeout: Long, timeunit: TimeUnit) extends Com * An already completed Future is seeded with it's result at creation, is useful for when you are participating in * a Future-composition but you already have a value to contribute. */ -sealed class AlreadyCompletedFuture[T](suppliedValue: Either[Throwable, T]) extends CompletableFuture[T] { +sealed class KeptPromise[T](suppliedValue: Either[Throwable, T]) extends Promise[T] { val value = Some(suppliedValue) - def complete(value: Either[Throwable, T]): CompletableFuture[T] = this + def complete(value: Either[Throwable, T]): Promise[T] = this def onComplete(func: Future[T] ⇒ Unit): Future[T] = { func(this); this } def await(atMost: Duration): Future[T] = this def await: Future[T] = this diff --git a/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala b/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala index f64c8109cb..f5cda388c2 100644 --- a/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala +++ b/akka-actor/src/main/scala/akka/dispatch/MessageHandling.scala @@ -19,7 +19,7 @@ import akka.actor._ final case class MessageInvocation(receiver: ActorRef, message: Any, sender: Option[ActorRef], - senderFuture: Option[CompletableFuture[Any]]) { + senderFuture: Option[Promise[Any]]) { if (receiver eq null) throw new IllegalArgumentException("Receiver can't be null") def invoke() { @@ -32,7 +32,7 @@ final case class MessageInvocation(receiver: ActorRef, } } -final case class FutureInvocation[T](future: CompletableFuture[T], function: () ⇒ T, cleanup: () ⇒ Unit) extends Runnable { +final case class FutureInvocation[T](future: Promise[T], function: () ⇒ T, cleanup: () ⇒ Unit) extends Runnable { def run() { future complete (try { Right(function()) @@ -99,7 +99,7 @@ trait MessageDispatcher { private[akka] final def dispatchFuture[T](block: () ⇒ T, timeout: Long): Future[T] = { futures.getAndIncrement() try { - val future = new DefaultCompletableFuture[T](timeout) + val future = new DefaultPromise[T](timeout) if (active.isOff) guard withGuard { diff --git a/akka-actor/src/main/scala/akka/remoteinterface/RemoteInterface.scala b/akka-actor/src/main/scala/akka/remoteinterface/RemoteInterface.scala index 13311138a9..91478c8eb2 100644 --- a/akka-actor/src/main/scala/akka/remoteinterface/RemoteInterface.scala +++ b/akka-actor/src/main/scala/akka/remoteinterface/RemoteInterface.scala @@ -7,7 +7,7 @@ package akka.remoteinterface import akka.japi.Creator import akka.actor._ import akka.util._ -import akka.dispatch.CompletableFuture +import akka.dispatch.Promise import akka.serialization._ import akka.AkkaException @@ -300,10 +300,10 @@ trait RemoteClientModule extends RemoteModule { self: RemoteModule ⇒ protected[akka] def send[T](message: Any, senderOption: Option[ActorRef], - senderFuture: Option[CompletableFuture[T]], + senderFuture: Option[Promise[T]], remoteAddress: InetSocketAddress, timeout: Long, isOneWay: Boolean, actorRef: ActorRef, - loader: Option[ClassLoader]): Option[CompletableFuture[T]] + loader: Option[ClassLoader]): Option[Promise[T]] } diff --git a/akka-actor/src/main/scala/akka/util/ReflectiveAccess.scala b/akka-actor/src/main/scala/akka/util/ReflectiveAccess.scala index 3efdb67a63..e47973984c 100644 --- a/akka-actor/src/main/scala/akka/util/ReflectiveAccess.scala +++ b/akka-actor/src/main/scala/akka/util/ReflectiveAccess.scala @@ -4,7 +4,7 @@ package akka.util -import akka.dispatch.{ Future, CompletableFuture, MessageInvocation } +import akka.dispatch.{ Future, Promise, MessageInvocation } import akka.config.{ Config, ModuleNotAvailableException } import akka.remoteinterface.RemoteSupport import akka.actor._ diff --git a/akka-cluster/src/main/scala/akka/cluster/ClusterActorRef.scala b/akka-cluster/src/main/scala/akka/cluster/ClusterActorRef.scala index c8ac1012f4..7776e8f1d5 100644 --- a/akka-cluster/src/main/scala/akka/cluster/ClusterActorRef.scala +++ b/akka-cluster/src/main/scala/akka/cluster/ClusterActorRef.scala @@ -8,7 +8,7 @@ import Cluster._ import akka.actor._ import akka.actor.Actor._ import akka.event.EventHandler -import akka.dispatch.CompletableFuture +import akka.dispatch.Promise import java.net.InetSocketAddress import java.util.concurrent.atomic.AtomicReference @@ -42,8 +42,8 @@ class ClusterActorRef private[akka] ( message: Any, timeout: Long, senderOption: Option[ActorRef], - senderFuture: Option[CompletableFuture[T]]): CompletableFuture[T] = - route[T](message, timeout)(senderOption).asInstanceOf[CompletableFuture[T]] + senderFuture: Option[Promise[T]]): Promise[T] = + route[T](message, timeout)(senderOption).asInstanceOf[Promise[T]] private[akka] def failOver(from: InetSocketAddress, to: InetSocketAddress) { addresses set (addresses.get map { diff --git a/akka-cluster/src/main/scala/akka/cluster/ReplicatedClusterRef.scala b/akka-cluster/src/main/scala/akka/cluster/ReplicatedClusterRef.scala index cf4bff9859..4b075c7f91 100644 --- a/akka-cluster/src/main/scala/akka/cluster/ReplicatedClusterRef.scala +++ b/akka-cluster/src/main/scala/akka/cluster/ReplicatedClusterRef.scala @@ -86,7 +86,7 @@ class ReplicatedActorRef private[akka] (actorRef: ActorRef, val address: String) message: Any, timeout: Long, senderOption: Option[ActorRef], - senderFuture: Option[CompletableFuture[T]]): CompletableFuture[T] = actorRef.postMessageToMailboxAndCreateFutureResultWithTimeout(message, timeout, senderOption, senderFuture) + senderFuture: Option[Promise[T]]): Promise[T] = actorRef.postMessageToMailboxAndCreateFutureResultWithTimeout(message, timeout, senderOption, senderFuture) protected[akka] def actorInstance: AtomicReference[Actor] = actorRef.actorInstance protected[akka] def supervisor_=(sup: Option[ActorRef]) { actorRef.supervisor_=(sup) diff --git a/akka-cluster/src/main/scala/akka/cluster/TransactionLog.scala b/akka-cluster/src/main/scala/akka/cluster/TransactionLog.scala index b6e30fca1c..f5c96250b4 100644 --- a/akka-cluster/src/main/scala/akka/cluster/TransactionLog.scala +++ b/akka-cluster/src/main/scala/akka/cluster/TransactionLog.scala @@ -13,7 +13,7 @@ import akka.config._ import Config._ import akka.util._ import akka.event.EventHandler -import akka.dispatch.{ DefaultCompletableFuture, CompletableFuture } +import akka.dispatch.{ DefaultPromise, Promise } import akka.AkkaException import akka.cluster.zookeeper._ @@ -140,7 +140,7 @@ class TransactionLog private (ledger: LedgerHandle, val id: String, val isAsync: "Reading entries [%s -> %s] for log [%s]".format(from, to, logId)) if (isAsync) { - val future = new DefaultCompletableFuture[Vector[Array[Byte]]](timeout) + val future = new DefaultPromise[Vector[Array[Byte]]](timeout) ledger.asyncReadEntries( from, to, new AsyncCallback.ReadCallback { @@ -149,7 +149,7 @@ class TransactionLog private (ledger: LedgerHandle, val id: String, val isAsync: ledgerHandle: LedgerHandle, enumeration: Enumeration[LedgerEntry], ctx: AnyRef) { - val future = ctx.asInstanceOf[CompletableFuture[Vector[Array[Byte]]]] + val future = ctx.asInstanceOf[Promise[Vector[Array[Byte]]]] var entries = Vector[Array[Byte]]() while (enumeration.hasMoreElements) { entries = entries :+ enumeration.nextElement.getEntry @@ -362,7 +362,7 @@ object TransactionLog { if (zkClient.exists(txLogPath)) throw new ReplicationException( "Transaction log for UUID [" + id + "] already exists") - val future = new DefaultCompletableFuture[LedgerHandle](timeout) + val future = new DefaultPromise[LedgerHandle](timeout) if (isAsync) { bookieClient.asyncCreateLedger( ensembleSize, quorumSize, digestType, password, @@ -371,7 +371,7 @@ object TransactionLog { returnCode: Int, ledgerHandle: LedgerHandle, ctx: AnyRef) { - val future = ctx.asInstanceOf[CompletableFuture[LedgerHandle]] + val future = ctx.asInstanceOf[Promise[LedgerHandle]] if (returnCode == BKException.Code.OK) future.completeWithResult(ledgerHandle) else future.completeWithException(BKException.create(returnCode)) } @@ -422,7 +422,7 @@ object TransactionLog { val ledger = try { if (isAsync) { - val future = new DefaultCompletableFuture[LedgerHandle](timeout) + val future = new DefaultPromise[LedgerHandle](timeout) bookieClient.asyncOpenLedger( logId, digestType, password, new AsyncCallback.OpenCallback { @@ -430,7 +430,7 @@ object TransactionLog { returnCode: Int, ledgerHandle: LedgerHandle, ctx: AnyRef) { - val future = ctx.asInstanceOf[CompletableFuture[LedgerHandle]] + val future = ctx.asInstanceOf[Promise[LedgerHandle]] if (returnCode == BKException.Code.OK) future.completeWithResult(ledgerHandle) else future.completeWithException(BKException.create(returnCode)) } @@ -447,7 +447,7 @@ object TransactionLog { TransactionLog(ledger, id, isAsync) } - private[akka] def await[T](future: CompletableFuture[T]): T = { + private[akka] def await[T](future: Promise[T]): T = { future.await if (future.result.isDefined) future.result.get else if (future.exception.isDefined) handleError(future.exception.get) diff --git a/akka-docs/java/dataflow.rst b/akka-docs/java/dataflow.rst index 52437647a5..901fdb2e38 100644 --- a/akka-docs/java/dataflow.rst +++ b/akka-docs/java/dataflow.rst @@ -8,7 +8,7 @@ Dataflow Concurrency (Java) Introduction ------------ -**IMPORTANT: As of Akka 1.1, Akka Future, CompletableFuture and DefaultCompletableFuture have all the functionality of DataFlowVariables, they also support non-blocking composition and advanced features like fold and reduce, Akka DataFlowVariable is therefor deprecated and will probably resurface in the following release as a DSL on top of Futures.** +**IMPORTANT: As of Akka 1.1, Akka Future, Promise and DefaultPromise have all the functionality of DataFlowVariables, they also support non-blocking composition and advanced features like fold and reduce, Akka DataFlowVariable is therefor deprecated and will probably resurface in the following release as a DSL on top of Futures.** Akka implements `Oz-style dataflow concurrency `_ through dataflow (single assignment) variables and lightweight (event-based) processes/threads. diff --git a/akka-docs/java/untyped-actors.rst b/akka-docs/java/untyped-actors.rst index c03dd8e50c..ddeedbe829 100644 --- a/akka-docs/java/untyped-actors.rst +++ b/akka-docs/java/untyped-actors.rst @@ -308,9 +308,9 @@ Reply using the sender future If a message was sent with the 'sendRequestReply' or 'sendRequestReplyFuture' methods, which both implements request-reply semantics using Future's, then you either have the option of replying using the 'reply' method as above. This method will then resolve the Future. But you can also get a reference to the Future directly and resolve it yourself or if you would like to store it away to resolve it later, or pass it on to some other Actor to resolve it. -The reference to the Future resides in the 'ActorRef' instance and can be retrieved using 'Option getSenderFuture()'. +The reference to the Future resides in the 'ActorRef' instance and can be retrieved using 'Option getSenderFuture()'. -CompletableFuture is a future with methods for 'completing the future: +Promise is a future with methods for 'completing the future: * completeWithResult(..) * completeWithException(..) diff --git a/akka-docs/scala/actors.rst b/akka-docs/scala/actors.rst index bd550b807b..38076d7b58 100644 --- a/akka-docs/scala/actors.rst +++ b/akka-docs/scala/actors.rst @@ -331,7 +331,7 @@ Reply using the sender future If a message was sent with the ``!!`` or ``!!!`` methods, which both implements request-reply semantics using Future's, then you either have the option of replying using the ``reply`` method as above. This method will then resolve the Future. But you can also get a reference to the Future directly and resolve it yourself or if you would like to store it away to resolve it later, or pass it on to some other Actor to resolve it. -The reference to the Future resides in the ``senderFuture: Option[CompletableFuture[_]]`` member field in the ``ActorRef`` class. +The reference to the Future resides in the ``senderFuture: Option[Promise[_]]`` member field in the ``ActorRef`` class. Here is an example of how it can be used: diff --git a/akka-remote/src/main/scala/akka/remote/netty/NettyRemoteSupport.scala b/akka-remote/src/main/scala/akka/remote/netty/NettyRemoteSupport.scala index de04a44a0f..6411104295 100644 --- a/akka-remote/src/main/scala/akka/remote/netty/NettyRemoteSupport.scala +++ b/akka-remote/src/main/scala/akka/remote/netty/NettyRemoteSupport.scala @@ -4,7 +4,7 @@ package akka.remote.netty -import akka.dispatch.{ DefaultCompletableFuture, CompletableFuture, Future } +import akka.dispatch.{ DefaultPromise, Promise, Future } import akka.remote.{ MessageSerializer, RemoteClientSettings, RemoteServerSettings } import akka.remote.protocol.RemoteProtocol._ import akka.serialization.RemoteActorSerialization @@ -73,12 +73,12 @@ trait NettyRemoteClientModule extends RemoteClientModule { self: ListenerManagem protected[akka] def send[T](message: Any, senderOption: Option[ActorRef], - senderFuture: Option[CompletableFuture[T]], + senderFuture: Option[Promise[T]], remoteAddress: InetSocketAddress, timeout: Long, isOneWay: Boolean, actorRef: ActorRef, - loader: Option[ClassLoader]): Option[CompletableFuture[T]] = + loader: Option[ClassLoader]): Option[Promise[T]] = withClientFor(remoteAddress, loader)(_.send[T](message, senderOption, senderFuture, remoteAddress, timeout, isOneWay, actorRef)) private[akka] def withClientFor[T]( @@ -154,7 +154,7 @@ abstract class RemoteClient private[akka] ( remoteAddress.getAddress.getHostAddress + "::" + remoteAddress.getPort - protected val futures = new ConcurrentHashMap[Uuid, CompletableFuture[_]] + protected val futures = new ConcurrentHashMap[Uuid, Promise[_]] protected val pendingRequests = { if (transactionLogCapacity < 0) new ConcurrentLinkedQueue[(Boolean, Uuid, RemoteMessageProtocol)] else new LinkedBlockingQueue[(Boolean, Uuid, RemoteMessageProtocol)](transactionLogCapacity) @@ -191,11 +191,11 @@ abstract class RemoteClient private[akka] ( def send[T]( message: Any, senderOption: Option[ActorRef], - senderFuture: Option[CompletableFuture[T]], + senderFuture: Option[Promise[T]], remoteAddress: InetSocketAddress, timeout: Long, isOneWay: Boolean, - actorRef: ActorRef): Option[CompletableFuture[T]] = + actorRef: ActorRef): Option[Promise[T]] = send(createRemoteMessageProtocolBuilder( Some(actorRef), Left(actorRef.uuid), actorRef.address, timeout, Right(message), isOneWay, senderOption).build, senderFuture) @@ -205,7 +205,7 @@ abstract class RemoteClient private[akka] ( */ def send[T]( request: RemoteMessageProtocol, - senderFuture: Option[CompletableFuture[T]]): Option[CompletableFuture[T]] = { + senderFuture: Option[Promise[T]]): Option[Promise[T]] = { if (isRunning) { if (request.getOneWay) { try { @@ -227,7 +227,7 @@ abstract class RemoteClient private[akka] ( None } else { val futureResult = if (senderFuture.isDefined) senderFuture.get - else new DefaultCompletableFuture[T](request.getActorInfo.getTimeout) + else new DefaultPromise[T](request.getActorInfo.getTimeout) val futureUuid = uuidFrom(request.getUuid.getHigh, request.getUuid.getLow) futures.put(futureUuid, futureResult) // Add future prematurely, remove it if write fails @@ -410,7 +410,7 @@ class ActiveRemoteClient private[akka] ( */ class ActiveRemoteClientPipelineFactory( name: String, - futures: ConcurrentMap[Uuid, CompletableFuture[_]], + futures: ConcurrentMap[Uuid, Promise[_]], bootstrap: ClientBootstrap, remoteAddress: InetSocketAddress, timer: HashedWheelTimer, @@ -439,7 +439,7 @@ class ActiveRemoteClientPipelineFactory( @ChannelHandler.Sharable class ActiveRemoteClientHandler( val name: String, - val futures: ConcurrentMap[Uuid, CompletableFuture[_]], + val futures: ConcurrentMap[Uuid, Promise[_]], val bootstrap: ClientBootstrap, val remoteAddress: InetSocketAddress, val timer: HashedWheelTimer, @@ -457,7 +457,7 @@ class ActiveRemoteClientHandler( case arp: AkkaRemoteProtocol if arp.hasMessage ⇒ val reply = arp.getMessage val replyUuid = uuidFrom(reply.getActorInfo.getUuid.getHigh, reply.getActorInfo.getUuid.getLow) - val future = futures.remove(replyUuid).asInstanceOf[CompletableFuture[Any]] + val future = futures.remove(replyUuid).asInstanceOf[Promise[Any]] if (reply.hasMessage) { if (future eq null) throw new IllegalActorStateException("Future mapped to UUID " + replyUuid + " does not exist") @@ -891,7 +891,7 @@ class RemoteServerHandler( message, request.getActorInfo.getTimeout, None, - Some(new DefaultCompletableFuture[Any](request.getActorInfo.getTimeout). + Some(new DefaultPromise[Any](request.getActorInfo.getTimeout). onComplete(_.value.get match { case l: Left[Throwable, Any] ⇒ write(channel, createErrorReplyMessage(l.a, request)) case r: Right[Throwable, Any] ⇒ diff --git a/akka-stm/src/main/scala/akka/agent/Agent.scala b/akka-stm/src/main/scala/akka/agent/Agent.scala index b556b90e50..dfa3cfd6b8 100644 --- a/akka-stm/src/main/scala/akka/agent/Agent.scala +++ b/akka-stm/src/main/scala/akka/agent/Agent.scala @@ -7,7 +7,7 @@ package akka.agent import akka.stm._ import akka.actor.Actor import akka.japi.{ Function ⇒ JFunc, Procedure ⇒ JProc } -import akka.dispatch.{ DefaultCompletableFuture, Dispatchers, Future } +import akka.dispatch.{ DefaultPromise, Dispatchers, Future } /** * Used internally to send functions. @@ -122,7 +122,7 @@ class Agent[T](initialValue: T) { def alter(f: T ⇒ T)(timeout: Long): Future[T] = { def dispatch = updater.!!!(Update(f), timeout) if (Stm.activeTransaction) { - val result = new DefaultCompletableFuture[T](timeout) + val result = new DefaultPromise[T](timeout) get //Join xa deferred { result completeWith dispatch @@ -164,7 +164,7 @@ class Agent[T](initialValue: T) { * still be executed in order. */ def alterOff(f: T ⇒ T)(timeout: Long): Future[T] = { - val result = new DefaultCompletableFuture[T](timeout) + val result = new DefaultPromise[T](timeout) send((value: T) ⇒ { suspend val threadBased = Actor.actorOf(new ThreadBasedAgentUpdater(this)).start() From cf0970d27719c018ddfba8856c16c94ebe1b37b8 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 23 May 2011 11:54:41 +0200 Subject: [PATCH 17/20] Removing duplicate code for TypedActor --- .../src/main/scala/akka/actor/TypedActor.scala | 15 +++++---------- .../src/main/scala/akka/dispatch/Future.scala | 15 ++++++--------- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/akka-actor/src/main/scala/akka/actor/TypedActor.scala b/akka-actor/src/main/scala/akka/actor/TypedActor.scala index 839474a3ba..f09e070c9c 100644 --- a/akka-actor/src/main/scala/akka/actor/TypedActor.scala +++ b/akka-actor/src/main/scala/akka/actor/TypedActor.scala @@ -42,18 +42,13 @@ object TypedActor { case m if m.isOneWay ⇒ actor ! m null - case m if m.returnsJOption_? ⇒ - (actor !!! m).as[JOption[Any]] match { - case Some(null) | None ⇒ JOption.none[Any] - case Some(joption) ⇒ joption - } - case m if m.returnsOption_? ⇒ - (actor !!! m).as[AnyRef] match { - case Some(null) | None ⇒ None - case Some(option) ⇒ option - } case m if m.returnsFuture_? ⇒ actor !!! m + case m if m.returnsJOption_? || m.returnsOption_? ⇒ + (actor !!! m).as[AnyRef] match { + case Some(null) | None ⇒ if (m.returnsJOption_?) JOption.none[Any] else None + case Some(joption) ⇒ joption + } case m ⇒ (actor !!! m).get } diff --git a/akka-actor/src/main/scala/akka/dispatch/Future.scala b/akka-actor/src/main/scala/akka/dispatch/Future.scala index 4ebe2b7f05..d235d7f46e 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Future.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Future.scala @@ -20,8 +20,6 @@ import java.lang.{ Iterable ⇒ JIterable } import java.util.{ LinkedList ⇒ JLinkedList } import scala.annotation.tailrec -import scala.collection.generic.CanBuildFrom -import scala.collection.mutable.Builder import scala.collection.mutable.Stack class FutureTimeoutException(message: String, cause: Throwable = null) extends AkkaException(message, cause) @@ -280,10 +278,6 @@ object Future { } future } - - private[akka] val callbacksPendingExecution = new ThreadLocal[Option[Stack[() ⇒ Unit]]]() { - override def initialValue = None - } } sealed trait Future[+T] { @@ -600,6 +594,9 @@ object Promise { def apply[A](): Promise[A] = apply(Actor.TIMEOUT) + private[akka] val callbacksPendingExecution = new ThreadLocal[Option[Stack[() ⇒ Unit]]]() { + override def initialValue = None + } } /** @@ -746,7 +743,7 @@ class DefaultPromise[T](timeout: Long, timeunit: TimeUnit) extends Promise[T] { } } - val pending = Future.callbacksPendingExecution.get + val pending = Promise.callbacksPendingExecution.get if (pending.isDefined) { //Instead of nesting the calls to the callbacks (leading to stack overflow) pending.get.push(() ⇒ { // Linearize/aggregate callbacks at top level and then execute val doNotify = notifyCompleted _ //Hoist closure to avoid garbage @@ -755,9 +752,9 @@ class DefaultPromise[T](timeout: Long, timeunit: TimeUnit) extends Promise[T] { } else { try { val callbacks = Stack[() ⇒ Unit]() // Allocate new aggregator for pending callbacks - Future.callbacksPendingExecution.set(Some(callbacks)) // Specify the callback aggregator + Promise.callbacksPendingExecution.set(Some(callbacks)) // Specify the callback aggregator runCallbacks(notifyTheseListeners, callbacks) // Execute callbacks, if they trigger new callbacks, they are aggregated - } finally { Future.callbacksPendingExecution.set(None) } // Ensure cleanup + } finally { Promise.callbacksPendingExecution.set(None) } // Ensure cleanup } } From 3b8c39582a07adf8b74a2ed3513b98bee8c71c50 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 23 May 2011 11:59:17 +0200 Subject: [PATCH 18/20] Adding assertions to ensure that the registry doesnt include the actor after stop --- .../src/test/scala/akka/misc/ActorRegistrySpec.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/akka-actor-tests/src/test/scala/akka/misc/ActorRegistrySpec.scala b/akka-actor-tests/src/test/scala/akka/misc/ActorRegistrySpec.scala index 6214c84da3..7f264bd3de 100644 --- a/akka-actor-tests/src/test/scala/akka/misc/ActorRegistrySpec.scala +++ b/akka-actor-tests/src/test/scala/akka/misc/ActorRegistrySpec.scala @@ -41,6 +41,7 @@ class ActorRegistrySpec extends JUnitSuite { assert(actor2.get.address === actor1.address) assert(actor2.get.address === "test-actor-1") actor2.get.stop + assert(Actor.registry.actorFor(actor1.address).isEmpty) } @Test @@ -54,6 +55,7 @@ class ActorRegistrySpec extends JUnitSuite { assert(actorOrNone.get.uuid === uuid) assert(actorOrNone.get.address === "test-actor-1") actor.stop + assert(Actor.registry.local.actorFor(uuid).isEmpty) } @Test From 19cf26b6a922d0dac3ab584cf169496a1b6bba7b Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 23 May 2011 12:08:38 +0200 Subject: [PATCH 19/20] Rewriting one of the tests in ActorRegistrySpec not to use non-volatile global state for verification --- .../scala/akka/misc/ActorRegistrySpec.scala | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/akka-actor-tests/src/test/scala/akka/misc/ActorRegistrySpec.scala b/akka-actor-tests/src/test/scala/akka/misc/ActorRegistrySpec.scala index 7f264bd3de..df6184c292 100644 --- a/akka-actor-tests/src/test/scala/akka/misc/ActorRegistrySpec.scala +++ b/akka-actor-tests/src/test/scala/akka/misc/ActorRegistrySpec.scala @@ -3,15 +3,14 @@ package akka.actor import org.scalatest.junit.JUnitSuite import org.junit.Test import Actor._ -import java.util.concurrent.{ CyclicBarrier, TimeUnit, CountDownLatch } import org.scalatest.Assertions._ +import java.util.concurrent.{ ConcurrentLinkedQueue, CyclicBarrier, TimeUnit, CountDownLatch } +import akka.dispatch.Future object ActorRegistrySpec { - var record = "" class TestActor extends Actor { def receive = { case "ping" ⇒ - record = "pong" + record self.reply("got ping") } } @@ -19,10 +18,8 @@ object ActorRegistrySpec { class TestActor2 extends Actor { def receive = { case "ping" ⇒ - record = "pong" + record self.reply("got ping") case "ping2" ⇒ - record = "pong" + record self.reply("got ping") } } @@ -73,10 +70,8 @@ class ActorRegistrySpec extends JUnitSuite { @Test def shouldGetAllActorsFromLocalActorRegistry { Actor.registry.local.shutdownAll - val actor1 = actorOf[TestActor]("test-actor-1") - actor1.start - val actor2 = actorOf[TestActor]("test-actor-2") - actor2.start + val actor1 = actorOf[TestActor]("test-actor-1").start + val actor2 = actorOf[TestActor]("test-actor-2").start val actors = Actor.registry.local.actors assert(actors.size === 2) assert(actors.head.actor.isInstanceOf[TestActor]) @@ -90,13 +85,15 @@ class ActorRegistrySpec extends JUnitSuite { @Test def shouldGetResponseByAllActorsInLocalActorRegistryWhenInvokingForeach { Actor.registry.local.shutdownAll - val actor1 = actorOf[TestActor]("test-actor-1") - actor1.start - val actor2 = actorOf[TestActor]("test-actor-2") - actor2.start - record = "" - Actor.registry.local.foreach(actor ⇒ actor !! "ping") - assert(record === "pongpong") + val actor1 = actorOf[TestActor]("test-actor-1").start + val actor2 = actorOf[TestActor]("test-actor-2").start + val results = new ConcurrentLinkedQueue[Future[String]] + + Actor.registry.local.foreach(actor ⇒ results.add(actor.!!![String]("ping"))) + + assert(results.size === 2) + val i = results.iterator + while (i.hasNext) assert(i.next.get === "got ping") actor1.stop() actor2.stop() } From 1f5a04c678e12c09241c3866290d736cc4f44d2e Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 23 May 2011 13:50:58 +0200 Subject: [PATCH 20/20] Adding support for customization of the TypedActor impl to be used when creating a new TypedActor, internal only, intended for things like ActorPool etc --- .../main/scala/akka/actor/TypedActor.scala | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/akka-actor/src/main/scala/akka/actor/TypedActor.scala b/akka-actor/src/main/scala/akka/actor/TypedActor.scala index f09e070c9c..52a6cf7622 100644 --- a/akka-actor/src/main/scala/akka/actor/TypedActor.scala +++ b/akka-actor/src/main/scala/akka/actor/TypedActor.scala @@ -15,20 +15,23 @@ object TypedActor { private val selfReference = new ThreadLocal[AnyRef] def self[T <: AnyRef] = selfReference.get.asInstanceOf[T] - class TypedActor[TI <: AnyRef](proxyRef: AtomicReference[AnyRef], createInstance: ⇒ TI) extends Actor { - val me = createInstance - def receive = { + trait TypedActor[Iface <: AnyRef, Impl <: Iface] { self: Actor ⇒ + val proxyRef: AtomicReference[Iface] + def callMethod(methodCall: MethodCall): Unit + def receive: Receive = { case m: MethodCall ⇒ selfReference set proxyRef.get - try { - m match { - case m if m.isOneWay ⇒ m(me) - case m if m.returnsFuture_? ⇒ self.senderFuture.get completeWith m(me).asInstanceOf[Future[Any]] - case m ⇒ self reply m(me) - } - } finally { - selfReference set null - } + try { callMethod(m) } finally { selfReference set null } + } + } + + class DefaultTypedActor[Iface <: AnyRef, Impl <: Iface]( + val proxyRef: AtomicReference[Iface], createInstance: ⇒ Impl) extends TypedActor[Iface, Impl] with Actor { + val me = createInstance + def callMethod(methodCall: MethodCall): Unit = methodCall match { + case m if m.isOneWay ⇒ m(me) + case m if m.returnsFuture_? ⇒ self.senderFuture.get completeWith m(me).asInstanceOf[Future[Any]] + case m ⇒ self reply m(me) } } @@ -98,12 +101,15 @@ object TypedActor { newTypedActor(clazz.getInterfaces, clazz.newInstance, config, if (loader eq null) clazz.getClassLoader else loader) } - protected def newTypedActor[R <: AnyRef, T <: R](interfaces: Array[Class[_]], constructor: ⇒ T, config: Configuration, loader: ClassLoader): R = { - val proxyRef = new AtomicReference[AnyRef](null) - configureAndProxyLocalActorRef[T](interfaces, proxyRef, actorOf(new TypedActor[T](proxyRef, constructor)), config, loader) + private[akka] def newTypedActor[R <: AnyRef, T <: R](interfaces: Array[Class[_]], constructor: ⇒ T, config: Configuration, loader: ClassLoader): R = + newTypedActor[R, T](interfaces, (ref: AtomicReference[R]) ⇒ new DefaultTypedActor[R, T](ref, constructor), config, loader) + + private[akka] def newTypedActor[R <: AnyRef, T <: R](interfaces: Array[Class[_]], constructor: (AtomicReference[R]) ⇒ TypedActor[R, T], config: Configuration, loader: ClassLoader): R = { + val proxyRef = new AtomicReference[R] + configureAndProxyLocalActorRef[R](interfaces, proxyRef, actorOf(constructor(proxyRef).asInstanceOf[Actor]), config, loader) } - protected def configureAndProxyLocalActorRef[T <: AnyRef](interfaces: Array[Class[_]], proxyRef: AtomicReference[AnyRef], actor: ActorRef, config: Configuration, loader: ClassLoader): T = { + protected def configureAndProxyLocalActorRef[T <: AnyRef](interfaces: Array[Class[_]], proxyRef: AtomicReference[T], actor: ActorRef, config: Configuration, loader: ClassLoader): T = { actor.timeout = config.timeout.toMillis actor.dispatcher = config.dispatcher