Akka Documentation for Java http://doc.akka.io/docs/akka/2.0.1/intro/getting-started-first-java.htmlをすべて読んだ後、うまく機能しているように見えるこれを思いつきました。
システムは、最初に「レポート」を処理する一意のアクタを作成することによって機能します (生成ページが読み込まれるとき)。このアクターは、進行状況を親に報告する子アクターを生成します。次に、親アクターは、子スレッドのステータスについて JavaScript を介してポーリングされます。
子が終了すると終了し、親が子が終了したことを検出すると、それ自体を終了します。
以下はすべてのコードです。間違った方法で行った場合は、遠慮なく私を引き裂いてください! (状態をアクターに保存してもよろしいですか?!?)
コントローラーコード:
public class Application extends Controller {
public static Result generateReport()
{
//create akka job
//we should really create the actor with UUID name so that someone can't guess
//and use the API to view the status of other peoples jobs, it be fairly easy
//to guess as it goes $a,$b,$c etc...
ActorRef myActor = Akka.system().actorOf(new Props(MyGeneratorMaster.class));
System.out.println( myActor.path());
myActor.tell(new ConfigMessage("blarg message"));
return ok(generating.render("blarg","title",myActor.path().name()));
}
public static Result status(String uuid)
{
uuid = "akka://application/user/"+uuid;
ActorRef myActor = Akka.system().actorFor(uuid);
if(myActor.isTerminated())
{
return ok("Report Generated - All Actors Terminated") ;
}
else
{
return async(
Akka.asPromise(ask(myActor,new StatusMessage(), 3000)).map(
new F.Function<Object,Result>() {
public Result apply(Object response) {
if(response instanceof ResultMessage)
{
return ok(((ResultMessage) response).getResult());
}
return ok(response.toString());
}
}
)
);
}
}
マスター アクター:
public class MyGeneratorMaster extends UntypedActor {
private int completed = 0;
@Override
public void postStop() {
super.postStop();
System.out.println("Master Killed");
}
@Override
public void onReceive(Object message) throws Exception {
if (message instanceof actors.messages.ConfigMessage) {
ConfigMessage config = (ConfigMessage) message;
System.out.println("Received Config:" + config.getConfig());
//Need to spawn child actor here..
ActorRef child = this.getContext().actorOf(new Props(MyGeneratorChildWorker.class));
//make the child thread do stuff
child.tell(new ConfigMessage("doSomething!"));
child.tell(akka.actor.PoisonPill.getInstance());//kill the child after the work is complete...
} else if (message instanceof StatusUpdate) {
System.out.println("Got Status Update");
completed = ((StatusUpdate) message).getProgress();
} else if (message instanceof StatusMessage) {
System.out.println("Got Status Message");
getSender().tell(new ResultMessage("Status: " + completed + "%"), getSelf());
if(completed == 100)
{
//kill this actor, we're done!
//could also call stop...
this.getSelf().tell(akka.actor.PoisonPill.getInstance());
}
} else {
System.out.println("unhandled message"+message.toString());
unhandled(message);
}
}
}
子役:
public class MyGeneratorChildWorker extends UntypedActor {
@Override
public void postStop() {
super.postStop();
System.out.println("Child Killed");
}
@Override
public void onReceive(Object message) throws Exception {
if (message instanceof ConfigMessage) {
System.out.println("Created Child Worker");
System.out.println("Doing Work:");
try {
for (int i = 0; i <= 100; i++) {
//update parent
this.context().parent().tell(new StatusUpdate(i));
long j = 1;
//waste loads of cpu cycles
while (j < 1E8) {
j = j + 1;
}
}
} catch (Exception ex) {
}
System.out.println("Done Work:");
} else
unhandled(message);
}
}
ロング ポーリング JavaScript を使用したビュー ページ:
@(message: String)(title: String)(id: String)@main(title) {
<h2>@message</h2>
<script type="text/javascript">
function getPercentage()
{
$.ajax({
type: "GET",
url: "/status/@id",
dataType: "html",
success: function(html)
{
$('#status').html(html);
}
});
}
$(document).ready(function() {
setInterval("getPercentage()",100);
});
</script>
<div id="status">
</div>
}