ActorContext.getParent for Java API #22413

This commit is contained in:
Johan Andrén 2017-03-16 09:30:00 +01:00 committed by GitHub
parent 5ff44194a3
commit 2eb226ed32
213 changed files with 1319 additions and 1523 deletions

View file

@ -0,0 +1,101 @@
/**
* Copyright (C) 2015-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.ddata;
//#data-bot
import static java.util.concurrent.TimeUnit.SECONDS;
import scala.concurrent.duration.Duration;
import java.util.concurrent.ThreadLocalRandom;
import akka.actor.AbstractActor;
import akka.actor.ActorRef;
import akka.actor.Cancellable;
import akka.cluster.Cluster;
import akka.cluster.ddata.DistributedData;
import akka.cluster.ddata.Key;
import akka.cluster.ddata.ORSet;
import akka.cluster.ddata.ORSetKey;
import akka.cluster.ddata.Replicator;
import akka.cluster.ddata.Replicator.Changed;
import akka.cluster.ddata.Replicator.Subscribe;
import akka.cluster.ddata.Replicator.Update;
import akka.cluster.ddata.Replicator.UpdateResponse;
import akka.event.Logging;
import akka.event.LoggingAdapter;
public class DataBot extends AbstractActor {
private static final String TICK = "tick";
private final LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this);
private final ActorRef replicator =
DistributedData.get(getContext().getSystem()).replicator();
private final Cluster node = Cluster.get(getContext().getSystem());
private final Cancellable tickTask = getContext().getSystem().scheduler().schedule(
Duration.create(5, SECONDS), Duration.create(5, SECONDS), getSelf(), TICK,
getContext().dispatcher(), getSelf());
private final Key<ORSet<String>> dataKey = ORSetKey.create("key");
@SuppressWarnings("unchecked")
@Override
public Receive createReceive() {
return receiveBuilder()
.match(String.class, a -> a.equals(TICK), a -> receiveTick())
.match(Changed.class, c -> c.key().equals(dataKey), c -> receiveChanged((Changed<ORSet<String>>) c))
.match(UpdateResponse.class, r -> receiveUpdateResoponse())
.build();
}
private void receiveTick() {
String s = String.valueOf((char) ThreadLocalRandom.current().nextInt(97, 123));
if (ThreadLocalRandom.current().nextBoolean()) {
// add
log.info("Adding: {}", s);
Update<ORSet<String>> update = new Update<>(
dataKey,
ORSet.create(),
Replicator.writeLocal(),
curr -> curr.add(node, s));
replicator.tell(update, getSelf());
} else {
// remove
log.info("Removing: {}", s);
Update<ORSet<String>> update = new Update<>(
dataKey,
ORSet.create(),
Replicator.writeLocal(),
curr -> curr.remove(node, s));
replicator.tell(update, getSelf());
}
}
private void receiveChanged(Changed<ORSet<String>> c) {
ORSet<String> data = c.dataValue();
log.info("Current elements: {}", data.getElements());
}
private void receiveUpdateResoponse() {
// ignore
}
@Override
public void preStart() {
Subscribe<ORSet<String>> subscribe = new Subscribe<>(dataKey, getSelf());
replicator.tell(subscribe, ActorRef.noSender());
}
@Override
public void postStop(){
tickTask.cancel();
}
}
//#data-bot

View file

@ -0,0 +1,423 @@
/**
* Copyright (C) 2015-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.ddata;
import java.util.HashSet;
import java.util.Arrays;
import java.util.Set;
import java.math.BigInteger;
import java.util.Optional;
import akka.actor.*;
import com.typesafe.config.ConfigFactory;
import docs.ddata.DistributedDataDocSpec;
import jdocs.AbstractJavaTest;
import scala.concurrent.duration.Duration;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import akka.cluster.Cluster;
import akka.cluster.ddata.*;
import akka.japi.pf.ReceiveBuilder;
import static akka.cluster.ddata.Replicator.*;
import akka.testkit.JavaTestKit;
@SuppressWarnings({"unchecked", "unused"})
public class DistributedDataDocTest extends AbstractJavaTest {
static ActorSystem system;
@BeforeClass
public static void setup() {
system = ActorSystem.create("DistributedDataDocTest",
ConfigFactory.parseString(DistributedDataDocSpec.config()));
}
@AfterClass
public static void tearDown() {
JavaTestKit.shutdownActorSystem(system);
system = null;
}
static
//#update
class DemonstrateUpdate extends AbstractActor {
final Cluster node = Cluster.get(getContext().getSystem());
final ActorRef replicator =
DistributedData.get(getContext().getSystem()).replicator();
final Key<PNCounter> counter1Key = PNCounterKey.create("counter1");
final Key<GSet<String>> set1Key = GSetKey.create("set1");
final Key<ORSet<String>> set2Key = ORSetKey.create("set2");
final Key<Flag> activeFlagKey = FlagKey.create("active");
@Override
public Receive createReceive() {
ReceiveBuilder b = receiveBuilder();
b.matchEquals("demonstrate update", msg -> {
replicator.tell(new Replicator.Update<PNCounter>(counter1Key, PNCounter.create(),
Replicator.writeLocal(), curr -> curr.increment(node, 1)), getSelf());
final WriteConsistency writeTo3 = new WriteTo(3, Duration.create(1, SECONDS));
replicator.tell(new Replicator.Update<GSet<String>>(set1Key, GSet.create(),
writeTo3, curr -> curr.add("hello")), getSelf());
final WriteConsistency writeMajority =
new WriteMajority(Duration.create(5, SECONDS));
replicator.tell(new Replicator.Update<ORSet<String>>(set2Key, ORSet.create(),
writeMajority, curr -> curr.add(node, "hello")), getSelf());
final WriteConsistency writeAll = new WriteAll(Duration.create(5, SECONDS));
replicator.tell(new Replicator.Update<Flag>(activeFlagKey, Flag.create(),
writeAll, curr -> curr.switchOn()), getSelf());
});
//#update
//#update-response1
b.match(UpdateSuccess.class, a -> a.key().equals(counter1Key), a -> {
// ok
});
//#update-response1
//#update-response2
b.match(UpdateSuccess.class, a -> a.key().equals(set1Key), a -> {
// ok
})
.match(UpdateTimeout.class, a -> a.key().equals(set1Key), a -> {
// write to 3 nodes failed within 1.second
});
//#update-response2
//#update
return b.build();
}
}
//#update
static
//#update-request-context
class DemonstrateUpdateWithRequestContext extends AbstractActor {
final Cluster node = Cluster.get(getContext().getSystem());
final ActorRef replicator =
DistributedData.get(getContext().getSystem()).replicator();
final WriteConsistency writeTwo = new WriteTo(2, Duration.create(3, SECONDS));
final Key<PNCounter> counter1Key = PNCounterKey.create("counter1");
@Override
public Receive createReceive() {
return receiveBuilder()
.match(String.class, a -> a.equals("increment"), a -> {
// incoming command to increase the counter
Optional<Object> reqContext = Optional.of(sender());
Replicator.Update<PNCounter> upd = new Replicator.Update<PNCounter>(counter1Key,
PNCounter.create(), writeTwo, reqContext, curr -> curr.increment(node, 1));
replicator.tell(upd, getSelf());
})
.match(UpdateSuccess.class, a -> a.key().equals(counter1Key), a -> {
ActorRef replyTo = (ActorRef) a.getRequest().get();
replyTo.tell("ack", getSelf());
})
.match(UpdateTimeout.class, a -> a.key().equals(counter1Key), a -> {
ActorRef replyTo = (ActorRef) a.getRequest().get();
replyTo.tell("nack", getSelf());
})
.build();
}
}
//#update-request-context
static
//#get
class DemonstrateGet extends AbstractActor {
final ActorRef replicator =
DistributedData.get(getContext().getSystem()).replicator();
final Key<PNCounter> counter1Key = PNCounterKey.create("counter1");
final Key<GSet<String>> set1Key = GSetKey.create("set1");
final Key<ORSet<String>> set2Key = ORSetKey.create("set2");
final Key<Flag> activeFlagKey = FlagKey.create("active");
@Override
public Receive createReceive() {
ReceiveBuilder b = receiveBuilder();
b.matchEquals("demonstrate get", msg -> {
replicator.tell(new Replicator.Get<PNCounter>(counter1Key,
Replicator.readLocal()), getSelf());
final ReadConsistency readFrom3 = new ReadFrom(3, Duration.create(1, SECONDS));
replicator.tell(new Replicator.Get<GSet<String>>(set1Key,
readFrom3), getSelf());
final ReadConsistency readMajority = new ReadMajority(Duration.create(5, SECONDS));
replicator.tell(new Replicator.Get<ORSet<String>>(set2Key,
readMajority), getSelf());
final ReadConsistency readAll = new ReadAll(Duration.create(5, SECONDS));
replicator.tell(new Replicator.Get<Flag>(activeFlagKey,
readAll), getSelf());
});
//#get
//#get-response1
b.match(GetSuccess.class, a -> a.key().equals(counter1Key), a -> {
GetSuccess<PNCounter> g = a;
BigInteger value = g.dataValue().getValue();
}).
match(NotFound.class, a -> a.key().equals(counter1Key), a -> {
// key counter1 does not exist
});
//#get-response1
//#get-response2
b.match(GetSuccess.class, a -> a.key().equals(set1Key), a -> {
GetSuccess<GSet<String>> g = a;
Set<String> value = g.dataValue().getElements();
}).
match(GetFailure.class, a -> a.key().equals(set1Key), a -> {
// read from 3 nodes failed within 1.second
}).
match(NotFound.class, a -> a.key().equals(set1Key), a -> {
// key set1 does not exist
});
//#get-response2
//#get
return b.build();
}
}
//#get
static
//#get-request-context
class DemonstrateGetWithRequestContext extends AbstractActor {
final ActorRef replicator =
DistributedData.get(getContext().getSystem()).replicator();
final ReadConsistency readTwo = new ReadFrom(2, Duration.create(3, SECONDS));
final Key<PNCounter> counter1Key = PNCounterKey.create("counter1");
@Override
public Receive createReceive() {
return receiveBuilder()
.match(String.class, a -> a.equals("get-count"), a -> {
// incoming request to retrieve current value of the counter
Optional<Object> reqContext = Optional.of(sender());
replicator.tell(new Replicator.Get<PNCounter>(counter1Key,
readTwo), getSelf());
})
.match(GetSuccess.class, a -> a.key().equals(counter1Key), a -> {
ActorRef replyTo = (ActorRef) a.getRequest().get();
GetSuccess<PNCounter> g = a;
long value = g.dataValue().getValue().longValue();
replyTo.tell(value, getSelf());
})
.match(GetFailure.class, a -> a.key().equals(counter1Key), a -> {
ActorRef replyTo = (ActorRef) a.getRequest().get();
replyTo.tell(-1L, getSelf());
})
.match(NotFound.class, a -> a.key().equals(counter1Key), a -> {
ActorRef replyTo = (ActorRef) a.getRequest().get();
replyTo.tell(0L, getSelf());
})
.build();
}
}
//#get-request-context
static
//#subscribe
class DemonstrateSubscribe extends AbstractActor {
final ActorRef replicator =
DistributedData.get(getContext().getSystem()).replicator();
final Key<PNCounter> counter1Key = PNCounterKey.create("counter1");
BigInteger currentValue = BigInteger.valueOf(0);
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Changed.class, a -> a.key().equals(counter1Key), a -> {
Changed<PNCounter> g = a;
currentValue = g.dataValue().getValue();
})
.match(String.class, a -> a.equals("get-count"), a -> {
// incoming request to retrieve current value of the counter
getSender().tell(currentValue, getSender());
})
.build();
}
@Override
public void preStart() {
// subscribe to changes of the Counter1Key value
replicator.tell(new Subscribe<PNCounter>(counter1Key, getSelf()), ActorRef.noSender());
}
}
//#subscribe
static
//#delete
class DemonstrateDelete extends AbstractActor {
final ActorRef replicator =
DistributedData.get(getContext().getSystem()).replicator();
final Key<PNCounter> counter1Key = PNCounterKey.create("counter1");
final Key<ORSet<String>> set2Key = ORSetKey.create("set2");
@Override
public Receive createReceive() {
return receiveBuilder()
.matchEquals("demonstrate delete", msg -> {
replicator.tell(new Delete<PNCounter>(counter1Key,
Replicator.writeLocal()), getSelf());
final WriteConsistency writeMajority =
new WriteMajority(Duration.create(5, SECONDS));
replicator.tell(new Delete<PNCounter>(counter1Key,
writeMajority), getSelf());
})
.build();
}
}
//#delete
public void demonstratePNCounter() {
//#pncounter
final Cluster node = Cluster.get(system);
final PNCounter c0 = PNCounter.create();
final PNCounter c1 = c0.increment(node, 1);
final PNCounter c2 = c1.increment(node, 7);
final PNCounter c3 = c2.decrement(node, 2);
System.out.println(c3.value()); // 6
//#pncounter
}
public void demonstratePNCounterMap() {
//#pncountermap
final Cluster node = Cluster.get(system);
final PNCounterMap<String> m0 = PNCounterMap.create();
final PNCounterMap<String> m1 = m0.increment(node, "a", 7);
final PNCounterMap<String> m2 = m1.decrement(node, "a", 2);
final PNCounterMap<String> m3 = m2.increment(node, "b", 1);
System.out.println(m3.get("a")); // 5
System.out.println(m3.getEntries());
//#pncountermap
}
public void demonstrateGSet() {
//#gset
final GSet<String> s0 = GSet.create();
final GSet<String> s1 = s0.add("a");
final GSet<String> s2 = s1.add("b").add("c");
if (s2.contains("a"))
System.out.println(s2.getElements()); // a, b, c
//#gset
}
public void demonstrateORSet() {
//#orset
final Cluster node = Cluster.get(system);
final ORSet<String> s0 = ORSet.create();
final ORSet<String> s1 = s0.add(node, "a");
final ORSet<String> s2 = s1.add(node, "b");
final ORSet<String> s3 = s2.remove(node, "a");
System.out.println(s3.getElements()); // b
//#orset
}
public void demonstrateORMultiMap() {
//#ormultimap
final Cluster node = Cluster.get(system);
final ORMultiMap<String, Integer> m0 = ORMultiMap.create();
final ORMultiMap<String, Integer> m1 = m0.put(node, "a",
new HashSet<>(Arrays.asList(1, 2, 3)));
final ORMultiMap<String, Integer> m2 = m1.addBinding(node, "a", 4);
final ORMultiMap<String, Integer> m3 = m2.removeBinding(node, "a", 2);
final ORMultiMap<String, Integer> m4 = m3.addBinding(node, "b", 1);
System.out.println(m4.getEntries());
//#ormultimap
}
public void demonstrateFlag() {
//#flag
final Flag f0 = Flag.create();
final Flag f1 = f0.switchOn();
System.out.println(f1.enabled());
//#flag
}
@Test
public void demonstrateLWWRegister() {
//#lwwregister
final Cluster node = Cluster.get(system);
final LWWRegister<String> r1 = LWWRegister.create(node, "Hello");
final LWWRegister<String> r2 = r1.withValue(node, "Hi");
System.out.println(r1.value() + " by " + r1.updatedBy() + " at " + r1.timestamp());
//#lwwregister
assertEquals("Hi", r2.value());
}
static
//#lwwregister-custom-clock
class Record {
public final int version;
public final String name;
public final String address;
public Record(int version, String name, String address) {
this.version = version;
this.name = name;
this.address = address;
}
}
//#lwwregister-custom-clock
public void demonstrateLWWRegisterWithCustomClock() {
//#lwwregister-custom-clock
final Cluster node = Cluster.get(system);
final LWWRegister.Clock<Record> recordClock = new LWWRegister.Clock<Record>() {
@Override
public long apply(long currentTimestamp, Record value) {
return value.version;
}
};
final Record record1 = new Record(1, "Alice", "Union Square");
final LWWRegister<Record> r1 = LWWRegister.create(node, record1);
final Record record2 = new Record(2, "Alice", "Madison Square");
final LWWRegister<Record> r2 = LWWRegister.create(node, record2);
final LWWRegister<Record> r3 = r1.merge(r2);
System.out.println(r3.value());
//#lwwregister-custom-clock
assertEquals("Madison Square", r3.value().address);
}
}

View file

@ -0,0 +1,273 @@
package jdocs.ddata;
import static java.util.concurrent.TimeUnit.SECONDS;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import scala.concurrent.duration.Duration;
import akka.actor.AbstractActor;
import akka.actor.ActorRef;
import akka.actor.Props;
import akka.cluster.Cluster;
import akka.cluster.ddata.DistributedData;
import akka.cluster.ddata.Key;
import akka.cluster.ddata.LWWMap;
import akka.cluster.ddata.LWWMapKey;
import akka.cluster.ddata.Replicator;
import akka.cluster.ddata.Replicator.GetFailure;
import akka.cluster.ddata.Replicator.GetResponse;
import akka.cluster.ddata.Replicator.GetSuccess;
import akka.cluster.ddata.Replicator.NotFound;
import akka.cluster.ddata.Replicator.ReadConsistency;
import akka.cluster.ddata.Replicator.ReadMajority;
import akka.cluster.ddata.Replicator.Update;
import akka.cluster.ddata.Replicator.UpdateFailure;
import akka.cluster.ddata.Replicator.UpdateSuccess;
import akka.cluster.ddata.Replicator.UpdateTimeout;
import akka.cluster.ddata.Replicator.WriteConsistency;
import akka.cluster.ddata.Replicator.WriteMajority;
@SuppressWarnings("unchecked")
public class ShoppingCart extends AbstractActor {
//#read-write-majority
private final WriteConsistency writeMajority =
new WriteMajority(Duration.create(3, SECONDS));
private final static ReadConsistency readMajority =
new ReadMajority(Duration.create(3, SECONDS));
//#read-write-majority
public static final String GET_CART = "getCart";
public static class AddItem {
public final LineItem item;
public AddItem(LineItem item) {
this.item = item;
}
}
public static class RemoveItem {
public final String productId;
public RemoveItem(String productId) {
this.productId = productId;
}
}
public static class Cart {
public final Set<LineItem> items;
public Cart(Set<LineItem> items) {
this.items = items;
}
}
public static class LineItem implements Serializable {
private static final long serialVersionUID = 1L;
public final String productId;
public final String title;
public final int quantity;
public LineItem(String productId, String title, int quantity) {
this.productId = productId;
this.title = title;
this.quantity = quantity;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((productId == null) ? 0 : productId.hashCode());
result = prime * result + quantity;
result = prime * result + ((title == null) ? 0 : title.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
LineItem other = (LineItem) obj;
if (productId == null) {
if (other.productId != null)
return false;
} else if (!productId.equals(other.productId))
return false;
if (quantity != other.quantity)
return false;
if (title == null) {
if (other.title != null)
return false;
} else if (!title.equals(other.title))
return false;
return true;
}
@Override
public String toString() {
return "LineItem [productId=" + productId + ", title=" + title + ", quantity=" + quantity + "]";
}
}
public static Props props(String userId) {
return Props.create(ShoppingCart.class, userId);
}
private final ActorRef replicator = DistributedData.get(getContext().getSystem()).replicator();
private final Cluster node = Cluster.get(getContext().getSystem());
@SuppressWarnings("unused")
private final String userId;
private final Key<LWWMap<String, LineItem>> dataKey;
public ShoppingCart(String userId) {
this.userId = userId;
this.dataKey = LWWMapKey.create("cart-" + userId);
}
@Override
public Receive createReceive() {
return matchGetCart()
.orElse(matchAddItem())
.orElse(matchRemoveItem())
.orElse(matchOther());
}
//#get-cart
private Receive matchGetCart() {
return receiveBuilder()
.matchEquals(GET_CART, s -> receiveGetCart())
.match(GetSuccess.class, this::isResponseToGetCart,
g -> receiveGetSuccess((GetSuccess<LWWMap<String, LineItem>>) g))
.match(NotFound.class, this::isResponseToGetCart,
n -> receiveNotFound((NotFound<LWWMap<String, LineItem>>) n))
.match(GetFailure.class, this::isResponseToGetCart,
f -> receiveGetFailure((GetFailure<LWWMap<String, LineItem>>) f))
.build();
}
private void receiveGetCart() {
Optional<Object> ctx = Optional.of(getSender());
replicator.tell(new Replicator.Get<LWWMap<String, LineItem>>(dataKey, readMajority, ctx),
getSelf());
}
private boolean isResponseToGetCart(GetResponse<?> response) {
return response.key().equals(dataKey) &&
(response.getRequest().orElse(null) instanceof ActorRef);
}
private void receiveGetSuccess(GetSuccess<LWWMap<String, LineItem>> g) {
Set<LineItem> items = new HashSet<>(g.dataValue().getEntries().values());
ActorRef replyTo = (ActorRef) g.getRequest().get();
replyTo.tell(new Cart(items), getSelf());
}
private void receiveNotFound(NotFound<LWWMap<String, LineItem>> n) {
ActorRef replyTo = (ActorRef) n.getRequest().get();
replyTo.tell(new Cart(new HashSet<>()), getSelf());
}
private void receiveGetFailure(GetFailure<LWWMap<String, LineItem>> f) {
// ReadMajority failure, try again with local read
Optional<Object> ctx = Optional.of(getSender());
replicator.tell(new Replicator.Get<LWWMap<String, LineItem>>(dataKey, Replicator.readLocal(),
ctx), getSelf());
}
//#get-cart
//#add-item
private Receive matchAddItem() {
return receiveBuilder()
.match(AddItem.class, this::receiveAddItem)
.build();
}
private void receiveAddItem(AddItem add) {
Update<LWWMap<String, LineItem>> update = new Update<>(dataKey, LWWMap.create(), writeMajority,
cart -> updateCart(cart, add.item));
replicator.tell(update, getSelf());
}
//#add-item
private LWWMap<String, LineItem> updateCart(LWWMap<String, LineItem> data, LineItem item) {
if (data.contains(item.productId)) {
LineItem existingItem = data.get(item.productId).get();
int newQuantity = existingItem.quantity + item.quantity;
LineItem newItem = new LineItem(item.productId, item.title, newQuantity);
return data.put(node, item.productId, newItem);
} else {
return data.put(node, item.productId, item);
}
}
private Receive matchRemoveItem() {
return receiveBuilder()
.match(RemoveItem.class, this::receiveRemoveItem)
.match(GetSuccess.class, this::isResponseToRemoveItem,
g -> receiveRemoveItemGetSuccess((GetSuccess<LWWMap<String, LineItem>>) g))
.match(GetFailure.class, this::isResponseToRemoveItem,
f -> receiveRemoveItemGetFailure((GetFailure<LWWMap<String, LineItem>>) f))
.match(NotFound.class, this::isResponseToRemoveItem, n -> {/* nothing to remove */})
.build();
}
//#remove-item
private void receiveRemoveItem(RemoveItem rm) {
// Try to fetch latest from a majority of nodes first, since ORMap
// remove must have seen the item to be able to remove it.
Optional<Object> ctx = Optional.of(rm);
replicator.tell(new Replicator.Get<LWWMap<String, LineItem>>(dataKey, readMajority, ctx),
getSelf());
}
private void receiveRemoveItemGetSuccess(GetSuccess<LWWMap<String, LineItem>> g) {
RemoveItem rm = (RemoveItem) g.getRequest().get();
removeItem(rm.productId);
}
private void receiveRemoveItemGetFailure(GetFailure<LWWMap<String, LineItem>> f) {
// ReadMajority failed, fall back to best effort local value
RemoveItem rm = (RemoveItem) f.getRequest().get();
removeItem(rm.productId);
}
private void removeItem(String productId) {
Update<LWWMap<String, LineItem>> update = new Update<>(dataKey, LWWMap.create(), writeMajority,
cart -> cart.remove(node, productId));
replicator.tell(update, getSelf());
}
private boolean isResponseToRemoveItem(GetResponse<?> response) {
return response.key().equals(dataKey) &&
(response.getRequest().orElse(null) instanceof RemoveItem);
}
//#remove-item
private Receive matchOther() {
return receiveBuilder()
.match(UpdateSuccess.class, u -> {
// ok
})
.match(UpdateTimeout.class, t -> {
// will eventually be replicated
})
.match(UpdateFailure.class, f -> {
throw new IllegalStateException("Unexpected failure: " + f);
})
.build();
}
}

View file

@ -0,0 +1,48 @@
/**
* Copyright (C) 2015-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.ddata;
import java.util.HashSet;
import java.util.Set;
import akka.cluster.ddata.AbstractReplicatedData;
import akka.cluster.ddata.GSet;
//#twophaseset
public class TwoPhaseSet extends AbstractReplicatedData<TwoPhaseSet> {
public final GSet<String> adds;
public final GSet<String> removals;
public TwoPhaseSet(GSet<String> adds, GSet<String> removals) {
this.adds = adds;
this.removals = removals;
}
public static TwoPhaseSet create() {
return new TwoPhaseSet(GSet.create(), GSet.create());
}
public TwoPhaseSet add(String element) {
return new TwoPhaseSet(adds.add(element), removals);
}
public TwoPhaseSet remove(String element) {
return new TwoPhaseSet(adds, removals.add(element));
}
public Set<String> getElements() {
Set<String> result = new HashSet<>(adds.getElements());
result.removeAll(removals.getElements());
return result;
}
@Override
public TwoPhaseSet mergeData(TwoPhaseSet that) {
return new TwoPhaseSet(this.adds.merge(that.adds),
this.removals.merge(that.removals));
}
}
//#twophaseset

View file

@ -0,0 +1,92 @@
/**
* Copyright (C) 2015-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.ddata.protobuf;
//#serializer
import jdocs.ddata.TwoPhaseSet;
import docs.ddata.protobuf.msg.TwoPhaseSetMessages;
import docs.ddata.protobuf.msg.TwoPhaseSetMessages.TwoPhaseSet.Builder;
import java.util.ArrayList;
import java.util.Collections;
import akka.actor.ExtendedActorSystem;
import akka.cluster.ddata.GSet;
import akka.cluster.ddata.protobuf.AbstractSerializationSupport;
public class TwoPhaseSetSerializer extends AbstractSerializationSupport {
private final ExtendedActorSystem system;
public TwoPhaseSetSerializer(ExtendedActorSystem system) {
this.system = system;
}
@Override
public ExtendedActorSystem system() {
return this.system;
}
@Override
public boolean includeManifest() {
return false;
}
@Override
public int identifier() {
return 99998;
}
@Override
public byte[] toBinary(Object obj) {
if (obj instanceof TwoPhaseSet) {
return twoPhaseSetToProto((TwoPhaseSet) obj).toByteArray();
} else {
throw new IllegalArgumentException(
"Can't serialize object of type " + obj.getClass());
}
}
@Override
public Object fromBinaryJava(byte[] bytes, Class<?> manifest) {
return twoPhaseSetFromBinary(bytes);
}
protected TwoPhaseSetMessages.TwoPhaseSet twoPhaseSetToProto(TwoPhaseSet twoPhaseSet) {
Builder b = TwoPhaseSetMessages.TwoPhaseSet.newBuilder();
ArrayList<String> adds = new ArrayList<>(twoPhaseSet.adds.getElements());
if (!adds.isEmpty()) {
Collections.sort(adds);
b.addAllAdds(adds);
}
ArrayList<String> removals = new ArrayList<>(twoPhaseSet.removals.getElements());
if (!removals.isEmpty()) {
Collections.sort(removals);
b.addAllRemovals(removals);
}
return b.build();
}
protected TwoPhaseSet twoPhaseSetFromBinary(byte[] bytes) {
try {
TwoPhaseSetMessages.TwoPhaseSet msg =
TwoPhaseSetMessages.TwoPhaseSet.parseFrom(bytes);
GSet<String> adds = GSet.create();
for (String elem : msg.getAddsList()) {
adds = adds.add(elem);
}
GSet<String> removals = GSet.create();
for (String elem : msg.getRemovalsList()) {
removals = removals.add(elem);
}
// GSet will accumulate deltas when adding elements,
// but those are not of interest in the result of the deserialization
return new TwoPhaseSet(adds.resetDelta(), removals.resetDelta());
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}
//#serializer

View file

@ -0,0 +1,87 @@
/**
* Copyright (C) 2015-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.ddata.protobuf;
//#serializer
import jdocs.ddata.TwoPhaseSet;
import docs.ddata.protobuf.msg.TwoPhaseSetMessages;
import docs.ddata.protobuf.msg.TwoPhaseSetMessages.TwoPhaseSet2.Builder;
import akka.actor.ExtendedActorSystem;
import akka.cluster.ddata.GSet;
import akka.cluster.ddata.protobuf.AbstractSerializationSupport;
import akka.cluster.ddata.protobuf.ReplicatedDataSerializer;
public class TwoPhaseSetSerializer2 extends AbstractSerializationSupport {
private final ExtendedActorSystem system;
private final ReplicatedDataSerializer replicatedDataSerializer;
public TwoPhaseSetSerializer2(ExtendedActorSystem system) {
this.system = system;
this.replicatedDataSerializer = new ReplicatedDataSerializer(system);
}
@Override
public ExtendedActorSystem system() {
return this.system;
}
@Override
public boolean includeManifest() {
return false;
}
@Override
public int identifier() {
return 99998;
}
@Override
public byte[] toBinary(Object obj) {
if (obj instanceof TwoPhaseSet) {
return twoPhaseSetToProto((TwoPhaseSet) obj).toByteArray();
} else {
throw new IllegalArgumentException(
"Can't serialize object of type " + obj.getClass());
}
}
@Override
public Object fromBinaryJava(byte[] bytes, Class<?> manifest) {
return twoPhaseSetFromBinary(bytes);
}
protected TwoPhaseSetMessages.TwoPhaseSet2 twoPhaseSetToProto(TwoPhaseSet twoPhaseSet) {
Builder b = TwoPhaseSetMessages.TwoPhaseSet2.newBuilder();
if (!twoPhaseSet.adds.isEmpty())
b.setAdds(otherMessageToProto(twoPhaseSet.adds).toByteString());
if (!twoPhaseSet.removals.isEmpty())
b.setRemovals(otherMessageToProto(twoPhaseSet.removals).toByteString());
return b.build();
}
@SuppressWarnings("unchecked")
protected TwoPhaseSet twoPhaseSetFromBinary(byte[] bytes) {
try {
TwoPhaseSetMessages.TwoPhaseSet2 msg =
TwoPhaseSetMessages.TwoPhaseSet2.parseFrom(bytes);
GSet<String> adds = GSet.create();
if (msg.hasAdds())
adds = (GSet<String>) otherMessageFromBinary(msg.getAdds().toByteArray());
GSet<String> removals = GSet.create();
if (msg.hasRemovals())
adds = (GSet<String>) otherMessageFromBinary(msg.getRemovals().toByteArray());
return new TwoPhaseSet(adds, removals);
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}
//#serializer

View file

@ -0,0 +1,32 @@
/**
* Copyright (C) 2015-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package jdocs.ddata.protobuf;
import jdocs.ddata.TwoPhaseSet;
import akka.actor.ExtendedActorSystem;
public class TwoPhaseSetSerializerWithCompression extends TwoPhaseSetSerializer {
public TwoPhaseSetSerializerWithCompression(ExtendedActorSystem system) {
super(system);
}
//#compression
@Override
public byte[] toBinary(Object obj) {
if (obj instanceof TwoPhaseSet) {
return compress(twoPhaseSetToProto((TwoPhaseSet) obj));
} else {
throw new IllegalArgumentException(
"Can't serialize object of type " + obj.getClass());
}
}
@Override
public Object fromBinaryJava(byte[] bytes, Class<?> manifest) {
return twoPhaseSetFromBinary(decompress(bytes));
}
//#compression
}