I am trying to implement a long running background process that is spawned when a user visits a page. I would like to display the progress of the task just like in this example: http://web.archive.org/web/20130122091205/http://www.lunatech-research.com/archives/2011/10/31/progressbar-jqueryui-websockets-playframework
Does anyone know of a tutorial for PlayFramework 2.0 (using inbuilt AKKA) ? This one is for 1.2
After reading all the Akka Documentation for Java http://doc.akka.io/docs/akka/2.0.1/intro/getting-started-first-java.html i've come up with this which seems to work well.
The system works by first creating a unique Actor to process a "report" (when the generate page is loaded). This actor spawns a child actor which reports its progress back to the parent. The parent actor is then polled via JavaScript for the status of the child thread.
Once the child has finished it is terminated and once the Parent detects the child is finished it terminates itself.
Below is all the code, feel free to tear me apart if i've gone about this the wrong way! (Is it OK to store state in Actors?!?)
The controller code:
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());
}
}
)
);
}
}
The Master Actor:
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);
}
}
}
The Child Actor:
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);
}
}
The view page with the long polling 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>
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With