diff --git a/akka-actor/src/main/scala/akka/routing/ConsistentHashingRouter.scala b/akka-actor/src/main/scala/akka/routing/ConsistentHashingRouter.scala index d8d384c5c0..9650a038e9 100644 --- a/akka-actor/src/main/scala/akka/routing/ConsistentHashingRouter.scala +++ b/akka-actor/src/main/scala/akka/routing/ConsistentHashingRouter.scala @@ -83,6 +83,9 @@ object ConsistentHashingRouter { * Note that it's not the hash that is to be returned, but the data to be * hashed. * + * May return `null` to indicate that the message is not handled by + * this mapping. + * * If returning an `Array[Byte]` or String it will be used as is, * otherwise the configured [[akka.akka.serialization.Serializer]] * will be applied to the returned data. @@ -99,9 +102,9 @@ object ConsistentHashingRouter { * * 1. You can define `consistentHashRoute` / `withConsistentHashMapping` * of the router to map incoming messages to their consistent hash key. - * This makes the makes the decision transparent for the sender. + * This makes the decision transparent for the sender. * - * 2. The messages may implement [[akka.routing.ConsistentHashable]]. + * 2. The messages may implement [[akka.routing.ConsistentHashingRouter.ConsistentHashable]]. * The key is part of the message and it's convenient to define it together * with the message definition. * @@ -183,11 +186,12 @@ case class ConsistentHashingRouter( def withVirtualNodesFactor(vnodes: Int): ConsistentHashingRouter = copy(virtualNodesFactor = vnodes) /** - * Java API for setting the mapping from message to the data to use for the consistent hash key + * Java API for setting the mapping from message to the data to use for the consistent hash key. */ def withConsistentHashMapping(mapping: ConsistentHashingRouter.ConsistentHashMapping) = { copy(consistentHashRoute = { - case message ⇒ mapping.consistentHashKey(message) + case message if (mapping.consistentHashKey(message).asInstanceOf[AnyRef] ne null) ⇒ + mapping.consistentHashKey(message) }) } @@ -199,7 +203,7 @@ case class ConsistentHashingRouter( * that can't be defined in configuration. */ override def withFallback(other: RouterConfig): RouterConfig = other match { - case fromConfig: FromConfig ⇒ this + case _: FromConfig ⇒ this case otherRouter: ConsistentHashingRouter ⇒ val useResizer = if (this.resizer.isEmpty && otherRouter.resizer.isDefined) otherRouter.resizer diff --git a/akka-docs/java/code/docs/jrouting/ConsistentHashingRouterDocTest.scala b/akka-docs/java/code/docs/jrouting/ConsistentHashingRouterDocTest.scala new file mode 100644 index 0000000000..a043f9cb73 --- /dev/null +++ b/akka-docs/java/code/docs/jrouting/ConsistentHashingRouterDocTest.scala @@ -0,0 +1,5 @@ +package docs.jrouting; + +import org.scalatest.junit.JUnitSuite + +class ConsistentHashingRouterDocTest extends ConsistentHashingRouterDocTestBase with JUnitSuite diff --git a/akka-docs/java/code/docs/jrouting/ConsistentHashingRouterDocTestBase.java b/akka-docs/java/code/docs/jrouting/ConsistentHashingRouterDocTestBase.java new file mode 100644 index 0000000000..6f00ed1509 --- /dev/null +++ b/akka-docs/java/code/docs/jrouting/ConsistentHashingRouterDocTestBase.java @@ -0,0 +1,136 @@ +/** + * Copyright (C) 2009-2012 Typesafe Inc. + */ +package docs.jrouting; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import akka.testkit.JavaTestKit; +import akka.actor.ActorSystem; + +//#imports1 +import akka.actor.UntypedActor; +import akka.routing.ConsistentHashingRouter.ConsistentHashable; +import java.util.Map; +import java.util.HashMap; +import java.io.Serializable; +//#imports1 + +//#imports2 +import akka.actor.Props; +import akka.actor.ActorRef; +import akka.routing.ConsistentHashingRouter; +import akka.routing.ConsistentHashingRouter.ConsistentHashMapping; +import akka.routing.ConsistentHashingRouter.ConsistentHashableEnvelope; +//#imports2 + +public class ConsistentHashingRouterDocTestBase { + + static ActorSystem system; + + @BeforeClass + public static void setup() { + system = ActorSystem.create(); + } + + @AfterClass + public static void teardown() { + system.shutdown(); + } + + //#cache-actor + + public static class Cache extends UntypedActor { + Map cache = new HashMap(); + + public void onReceive(Object msg) { + if (msg instanceof Entry) { + Entry entry = (Entry) msg; + cache.put(entry.key, entry.value); + } else if (msg instanceof Get) { + Get get = (Get) msg; + Object value = cache.get(get.key); + getSender().tell(value == null ? NOT_FOUND : value, + getContext().self()); + } else if (msg instanceof Evict) { + Evict evict = (Evict) msg; + cache.remove(evict.key); + } else { + unhandled(msg); + } + } + } + + public static class Evict implements Serializable { + public final String key; + public Evict(String key) { + this.key = key; + } + } + + public static class Get implements Serializable, ConsistentHashable { + public final String key; + public Get(String key) { + this.key = key; + } + public Object consistentHashKey() { + return key; + } + } + + public static class Entry implements Serializable { + public final String key; + public final String value; + public Entry(String key, String value) { + this.key = key; + this.value = value; + } + } + + public static final String NOT_FOUND = "NOT_FOUND"; + //#cache-actor + + + @Test + public void demonstrateUsageOfConsistentHashableRouter() { + + new JavaTestKit(system) {{ + + //#consistent-hashing-router + + final ConsistentHashMapping consistentHashMapping = new ConsistentHashMapping() { + @Override + public Object consistentHashKey(Object message) { + if (message instanceof Evict) { + return ((Evict) message).key; + } else { + return null; + } + } + }; + + ActorRef cache = system.actorOf(new Props(Cache.class).withRouter( + new ConsistentHashingRouter(10).withConsistentHashMapping(consistentHashMapping)), + "cache"); + + cache.tell(new ConsistentHashableEnvelope( + new Entry("hello", "HELLO"), "hello"), getRef()); + cache.tell(new ConsistentHashableEnvelope( + new Entry("hi", "HI"), "hi"), getRef()); + + cache.tell(new Get("hello"), getRef()); + expectMsgEquals("HELLO"); + + cache.tell(new Get("hi"), getRef()); + expectMsgEquals("HI"); + + cache.tell(new Evict("hi"), getRef()); + cache.tell(new Get("hi"), getRef()); + expectMsgEquals(NOT_FOUND); + + //#consistent-hashing-router + }}; + } + +} diff --git a/akka-docs/java/routing.rst b/akka-docs/java/routing.rst index dad68ecf30..149a3a315b 100644 --- a/akka-docs/java/routing.rst +++ b/akka-docs/java/routing.rst @@ -287,10 +287,10 @@ insight into how consistent hashing is implemented. There is 3 ways to define what data to use for the consistent hash key. * You can define ``withConsistentHashMapping`` of the router to map incoming - messages to their consistent hash key. This makes the makes the decision + messages to their consistent hash key. This makes the the decision transparent for the sender. -* The messages may implement ``akka.routing.ConsistentHashable``. +* The messages may implement ``akka.routing.ConsistentHashingRouter.ConsistentHashable``. The key is part of the message and it's convenient to define it together with the message definition. @@ -303,7 +303,11 @@ the same time for one router. The ``withConsistentHashMapping`` is tried first. Code example: -FIXME Java example of consistent routing +.. includecode:: code/docs/jrouting/ConsistentHashingRouterDocTestBase.java + :include: imports1,cache-actor + +.. includecode:: code/docs/jrouting/ConsistentHashingRouterDocTestBase.java + :include: imports2,consistent-hashing-router In the above example you see that the ``Get`` message implements ``ConsistentHashable`` itself, while the ``Entry`` message is wrapped in a ``ConsistentHashableEnvelope``. The ``Evict`` diff --git a/akka-docs/scala/code/docs/routing/ConsistentHashingRouterDocSpec.scala b/akka-docs/scala/code/docs/routing/ConsistentHashingRouterDocSpec.scala index 945a78e1eb..7041db6049 100644 --- a/akka-docs/scala/code/docs/routing/ConsistentHashingRouterDocSpec.scala +++ b/akka-docs/scala/code/docs/routing/ConsistentHashingRouterDocSpec.scala @@ -11,7 +11,6 @@ object ConsistentHashingRouterDocSpec { //#cache-actor import akka.actor.Actor import akka.routing.ConsistentHashingRouter.ConsistentHashable - import akka.routing.ConsistentHashingRouter.ConsistentHashableEnvelope class Cache extends Actor { var cache = Map.empty[String, String] diff --git a/akka-docs/scala/routing.rst b/akka-docs/scala/routing.rst index e10a353988..4766b2e6ca 100644 --- a/akka-docs/scala/routing.rst +++ b/akka-docs/scala/routing.rst @@ -292,10 +292,10 @@ insight into how consistent hashing is implemented. There is 3 ways to define what data to use for the consistent hash key. * You can define ``consistentHashRoute`` of the router to map incoming - messages to their consistent hash key. This makes the makes the decision + messages to their consistent hash key. This makes the decision transparent for the sender. -* The messages may implement ``akka.routing.ConsistentHashable``. +* The messages may implement ``akka.routing.ConsistentHashingRouter.ConsistentHashable``. The key is part of the message and it's convenient to define it together with the message definition.