package com.vaadin.vaadininmuija.akka;
import com.vaadin.vaadininmuija.akka.messages.StockQuote;
import com.vaadin.vaadininmuija.akka.messages.FakeStockQuote;
import com.vaadin.vaadininmuija.akka.messages.StockHistory;
import com.vaadin.vaadininmuija.akka.messages.StockUpdate;
import com.vaadin.vaadininmuija.akka.messages.UnwatchStock;
import com.vaadin.vaadininmuija.akka.messages.WatchStock;
import akka.actor.ActorRef;
import akka.actor.Cancellable;
import akka.actor.UntypedActor;
import java.io.Serializable;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import scala.concurrent.duration.FiniteDuration;
/**
* These actors are instantiated by the hub, one per stock symbol, and they
* generate some fake data for stock values.
*/
public class StockActor extends UntypedActor {
private static final int HISTORY_SIZE = 50;
private final FakeStockQuote fakeStockQuote = new FakeStockQuote();
private final LinkedList<StockQuote> history = new LinkedList<>();
private final Set<ActorRef> watchers = new HashSet<>();
private final String symbol;
private Cancellable stockTick;
private final Random random = new Random();
public StockActor(String symbol) {
this.symbol = symbol;
}
@Override
public void onReceive(Object message) throws Exception {
if (message instanceof FetchLatest) {
// react randomly on every third sheduling to make data more
// realistic
if (random.nextInt(3) == 0) {
// add a new stock price to the history and drop the oldest
StockQuote newPrice = fakeStockQuote.create();
history.add(newPrice);
history.remove();
for (ActorRef r : watchers) {
r.tell(new StockUpdate(symbol, newPrice), getSelf());
}
}
} else if (message instanceof WatchStock) {
ensureDemoDataGeneration();
// add to watchers...
watchers.add(getSender());
// ... and tell the last updates
getSender().tell(new StockHistory(symbol, history), getSelf());
} else if (message instanceof UnwatchStock) {
watchers.remove(getSender());
if (watchers.isEmpty()) {
// TODO original example stps here, but instead should somehow
// lazily stop the actor, if nobody reconnects in a minute.
// With this toggling shows random values
// getContext().stop(getSelf());
// stockTick.cancel();
}
} else {
unhandled(message);
}
}
private void ensureDemoDataGeneration() {
if (stockTick == null || stockTick.isCancelled()) {
// create some demo data for initial history
if (history.isEmpty()) {
for (int i = 0; i < HISTORY_SIZE; i++) {
history.add(fakeStockQuote.create((HISTORY_SIZE - i) * 3));
}
}
// Schedule stock updates
stockTick = getContext().system().scheduler().schedule(FiniteDuration.Zero(),
FiniteDuration.create(1000, TimeUnit.MILLISECONDS), () -> {
self().tell(new FetchLatest(), self());
}, getContext().dispatcher());
}
}
/**
* Simple internally used message to schedule demo data generation
*/
public static class FetchLatest implements Serializable {
private static final long serialVersionUID = -4896383249835327085L;
}
}