How to obtain Task instance from Service handler method in JavaFX - java

I have a JavaFX service in which Task is created to do some work:
public class MyService extends Service<Void> {
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
// do some work
return null;
}
};
}
}
I also have a handler for success:
myService.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle( final WorkerStateEvent event ) {
// check what Task has finished
}
});
As it is possible to run the service more than once concurrently, I wan't to be able to distinguish from success handler what instance of Task has finished. Looking into the doc, I could not find any way, how to do it.
My idea was to extend Task to contain some id, e.g. integer, and then to be able obtain that instance from event handler.
Any idea, how to achieve such behavior would be much welcome.
Thanks in advance.

First, I would question why you needed to do this. The task itself should encapsulate all the logic that is specific to its own particular instance. It feels like there should be a better solution to whatever it is you're trying to achieve.
So the most obvious way to do this would be to have your Task return the value you are interested in. E.g.
public class MyService extends Service<Integer> {
private int nextId = 0 ;
#Override
protected Task<Integer> createTask() {
private final int id = ++nextId ;
return new Task<Integer>() {
#Override
protected Integer call() throws Exception {
// do some work
return id;
}
};
}
}
Then
Service<Integer> service = new MyService();
service.setOnSucceeded(e -> System.out.println("Task " + service.getValue() + " finished"));
Note also that you can override the succeeded() method of Task (as well as registering a handler with the service). So you can do
public class MyService extends Service<Void> {
private int nextId = 0;
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
final int id = ++nextId ;
#Override
protected Void call() throws Exception {
// do some work
return null;
}
#Override
protected void succeeded() {
super.succeeded();
System.out.println("Task "+id+" completed successfully");
}
};
}
}
The succeeded() method is invoked on the FX Application Thread.
If you really want to manage this via the service, you could just keep a reference to the latest task that was started in the service implementation. Since a Service can only run one task at a time, when the service enters the SUCCEEDED state, this is guaranteed to be the task that just completed. For example:
public class MyService extends Service<Void> {
private Task<Void> mostRecentTask ;
#Override
protected Task<Void> createTask() {
Task<Void> task = new Task<Void>() {
#Override
protected Void call() throws Exception {
// do some work
return null;
}
};
task.setOnRunning(e -> mostRecentTask = task);
return task ;
}
public Task<Void> getMostRecentTask() {
return mostRecentTask ;
}
}
Then
MyService service = new MyService();
service.setOnSucceeded(e -> {
Task<Void> completedTask = service.getMostRecentTask();
// ...
});
But, as I said before, it feels like there should be a more elegant way to achieve whatever it is you're trying to do at a more fundamental level.

Related

How to run task using Thread in Background

I wanted to do some task in the background like in android we can use AsyncTask to do some work using UI thread, in Harmony we have EventHandler which allows us to send and process InnerEvent and Runnable objects on asynchronous threads.
I just want a simple example on how to use it.
please check the sample -
public class EventHandlerImplementation extends EventHandler {
private EventHandlerImplementation(EventRunner runner) {
super(runner);
}
#Override
public void processEvent(InnerEvent event) {
getUITaskDispatcher().asyncDispatch(() -> {
// do your stuff here
});
}
}
private final int eventUpdateGet = 1001;
private final int eventUpdateSend = 1002;
private class MyEventHandler extends EventHandler {
private MyEventHandler(EventRunner runner) throws IllegalArgumentException {
super(runner);
}
#Override
protected void processEvent(InnerEvent event) {
super.processEvent(event);
switch (event.eventId) {
case eventUpdateGet:
Object object = event.object;
txGet.setText(String.valueOf(object));
break;
case eventUpdateSend:
....
break;
default:
break;
}
}
}
#Override
protected void onStart(Intent intent) {
myHandler = new MyEventHandler(EventRunner.current());
}
When you use, you could :
String msgGet = "......"
InnerEvent event = InnerEvent.get(eventUpdateGet, msgGet);
myHandler.sendEvent(event);
For more details, pls kindly refer to this official Docs.

How to unit test asynchronous code made synchronous using CountdownLatch

Below is the class I want to test :
SomeClass.java
public void SomeClass {
final CountDownLatch latch = new CountDownLatch(1);
int result;
registerCallbackWithService(new MyCallback());
public int callToExternalService(){
//Do some stuff and make service call
latch.await();
return result;
}
class MyCallback implements ServiceCallback {
#Override
public void onResult(final int res) {
//do something
result = res;
latch.countdown();
}
}
}
The callback MyCallback was registered earlier before invoking callToExternalService().
If I write a simple test to just mock the service call made in callToExternalService(), the test keeps on running infinitely because of latch.await().
How can I test the logic in callToExternalService() as well as in onResult() ?
I modified by code to expose the callback that I am registering using a package-protected function as below :
public void SomeClass {
private final CountDownLatch latch = new CountDownLatch(1);
private int result;
registerCallback(new MyCallback());
public int callToExternalService(){
//Do some stuff and make service call
latch.await();
return result;
}
private class MyCallback implements ServiceCallback {
#Override
public void onResult(final int res) {
//do something
result = res;
latch.countdown();
}
}
protected registerCallback(ServiceCallback callback) {
registerCallbackWithService(callback);
}
}
Now, for testing I do my testing by creating a new class SomeClassTest extends SomeClass and do my testing using an instance of this class. In SomeClassTest all I do is override registerCallback() to access the callback instance that is being registered.
public class ServiceTest {
private ServiceCallback mServiceCallback;
class SomeClassTest extends SomeClass {
#Override
registerCallback(ServiceCallback callback) {
mServiceCallback = callback;
super.registerCallback(callback);
}
}
}
Now all I have to do it using doAnswer, invoke the callback upon service request which results in the execution of latch.countdown() on the same latch reference that is put on await just after making the service request.
SomeClassTest someClassInstance = new SomeClassTest();
doAnswer(new Answer() {
#Override
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
mServiceVCallback.onResult(int_I_want_to_test_for)
return null;
}
}).when(someClassInstance).service_request_before_latch_await();
int response = someClassInstance.callToExternalService();
assertEquals(response, expected_response);

Call task's updateProgress

I was reading the documentation for the class Task
final Task<Void> task = new Task<Void>() {
#Override public Void call() {
for(int i=0;i<datesAndStudies.length;i++){
updateProgress(i,datesAndStudies.length);
DoSomething something = new DoSomething();
something.VeryLongAndTimeConsumingMethod(i);
}
return null;
}
};
And I notice that updateProgress is protected and workdone/totalwork are both defined as public final ReadOnlyDoubleProperty.
Is there a way/workaround to update/call updateProgress or edit those values(workdone/totalwork) from the method: VeryLongAndTimeConsumingMethod(int i) in the class DoSomething ?
Even if updateProgress(...) were public, you'd have to pass a reference to the Task to your DoSomething class, which creates some really ugly coupling. If you have that level of coupling between your Task implementation and your DoSomething class, you may as well just define the long, time consuming method in the Task subclass itself, and get rid of the other class:
final Task<Void> task = new Task<Void>() {
#Override
public Void call() {
for (int i=0; i<datesAndStudies.length; i++) {
veryLongAndTimeConsumingMethod(i);
}
return null ;
}
private void veryLongAndTimeConsumingMethod(int i) {
// do whatever...
updateProgress(...);
}
};
To preserve your decoupling, just define a DoubleProperty representing the progress in DoSomething, and observe it from the Task, calling updateProgress(...) when it changes:
public class DoSomething {
private final ReadOnlyDoubleWrapper progress = new ReadOnlyDoubleWrapper(this, "progress");
public double getProgress() {
return progress.get();
}
public ReadOnlyDoubleProperty progressProperty() {
return progress.getReadOnlyProperty();
}
public void veryLongAndTimeConsumingMethod(int i) {
// ..
progress.set(...);
}
}
Then:
final Task<Void> task = new Task<>() {
#Override
public Void call() {
for (int i=0; i<datesAndStudies.length; i++) {
DoSomething something = new DoSomething();
something.progressProperty().addListener(
(obs, oldProgress, newProgress) -> updateProgress(...));
something.veryLongAndTimeConsumingMethod();
}
}
}

Call a method only once after 1 second from a method which get called 2-3 times

The below method onReceivedTitlegets called 2-3 times with in a second when webview url changes. I want to call a method in it, when onReceivedTitle is being called last time. I am doing this because I just want to monitor url changes with in webview. shouldOverrideUrlLoading is not getting called when url changes through ajax.
class MyWebChromeClient extends WebChromeClient {
#Override
public void onReceivedTitle(WebView view, String title) {
Log.v("onReceivedTitle", "=>" + title);
// callAMehod();
super.onReceivedTitle(view, title);
}
}
If you want to throttle how often a method call causes another method call you can do so for example via a Handler. The simplest version enqueues a delayed message on the first call and any subsequent call while there is an enqueued message will not enqueue a new one. That results in 1 call every X time to go though - but it take at least that amount of time until the first action happens.
Example implementation (you can put that class unmodified somewhere in your code)
public abstract class ThrottleExecutor {
private final long mMinDelay;
public ThrottleExecutor(long minDelay) {
mMinDelay = minDelay;
}
/** Implement to do something */
public abstract void doThrottled();
public final void scheduleExecution() {
if (mHandler.hasMessages(0)) {
// message already enqueued, do nothing
} else {
// otherwise enqueue a message for later
mHandler.sendEmptyMessageDelayed(0, mMinDelay);
}
}
public final void cancelExecution() {
mHandler.removeMessages(0);
}
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message msg) {
doThrottled();
}
};
}
And then use it for example like so
class Usage {
private ThrottleExecutor mThrottle = new ThrottleExecutor(2000) {
#Override
public void doThrottled() {
// happens at most every 2000ms
methodToBeThrottled();
}
};
void methodThatHappensTooOften() {
mThrottle.scheduleExecution();
}
void methodToBeThrottled() {
Log.d("TAG", "triggered at 2000ms before");
}
}
You might want to use Handler and do something like this:
class MyWebChromeClient extends WebChromeClient {
private boolean mOnReceivedTitleInvoked;
#Override
public synchronized void onReceivedTitle(final WebView view, final String title) {
if (!mOnReceivedTitleInvoked) {
mOnReceivedTitleInvoked = true;
Log.v("onReceivedTitle", "=>" + title);
handler.postDelayed(new Runnable() {
#Override
public void run() {
super.onReceivedTitle(view, title);
mOnReceivedTitleInvoked = false;
}
}, 1000);
}
}
}
Although you might want to reconsider the onReceivedTitle behaviour.

How to get Data from a Task in afterExecute of ScheduledThreadPoolExecutor

I'm using ScheduledThreadPoolExecutor and I don't know hot to deal with something.
I'm scheduling some tasks this way:
scheduledExecService = new ExtendedScheduledExecutor(numThreads, myThreadFactory);
TareaActualizacion act = new TareaActualizacion(inst);
ScheduledFuture<?> handle = scheduledExecService.scheduleWithFixedDelay(act, retrasoInicial, segundosRefresco, TimeUnit.SECONDS);
act is a Runnable class that recive some data by parameter:
public class TareaActualizacion implements Runnable {
private Instalacion instalacion;
public TareaActualizacion(Instalacion instalacion) {
this.instalacion = instalacion;
}
#Override
public void run() {
//Do something
}
public Instalacion getInstalacion() {
return instalacion;
}
}
Now in the afterExecute method of the ExtendedSecheduledExecutor I want to get the object Instalacion of the task TareaActualizacion but I don't know how to do it.
My ExtendedScheduledExecutor class looks like this:
public class ExtendedScheduledExecutor extends ScheduledThreadPoolExecutor{
public ExtendedScheduledExecutor(int arg0) {
super(arg0);
}
public ExtendedScheduledExecutor(int arg0, ThreadFactory arg1) {
super(arg0, arg1);
}
#Override
protected void afterExecute(Runnable r, Throwable t)
{
super.afterExecute(r, t);
System.out.println("Executing afterExecute. Throwable is " + t);
if (t != null)
t.printStackTrace();
//I need to get the Instalacion attribute from TareaActualizacion task. How can I do it??
}
}
Any idea of how can I solve it??
Thank you!
Neus
As Stephan already pointed out in https://stackoverflow.com/a/22145530 , you should try to decouple the scheduling and execution from the notification.
One approach for this could be to wrap the actual task (TareaActualizacion) into another implementation of the Runnable interface that only executes the actual task, and afterwards notifies a callback about the task that has been executed.
Depending on your precise requirements, there may be several degrees of freedom for the implementation, but a general approach could roughly look like this:
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledTaskNotification
{
public static void main(String[] args) throws Exception
{
ScheduledExecutorService executor = Executors.newScheduledThreadPool(4);
int n = 3;
for (int i = 0; i < n; i++)
{
UpdateTask updateTask = new UpdateTask(i);
RunnableCallback<UpdateTask> callback = new RunnableCallback<UpdateTask>()
{
#Override
public void runnableFinished(UpdateTask updateTask)
{
System.out.println("Finished "+updateTask+", id "+updateTask.getID());
}
};
Runnable runnableWithCallback =
createRunnableWithCallback(updateTask, callback);
executor.scheduleWithFixedDelay(
runnableWithCallback, 1000, 200+i*200,
TimeUnit.MILLISECONDS);
}
}
static interface RunnableCallback<T extends Runnable>
{
void runnableFinished(T runnable);
}
private static <T extends Runnable> Runnable createRunnableWithCallback(
final T runnable, final RunnableCallback<T> callback)
{
return new Runnable()
{
#Override
public void run()
{
runnable.run();
callback.runnableFinished(runnable);
}
};
}
private static class UpdateTask implements Runnable
{
private final int id;
UpdateTask(int id)
{
this.id = id;
}
#Override
public void run()
{
System.out.println("Run "+this);
}
int getID()
{
return id;
}
#Override
public String toString()
{
return "UpdateTask "+id;
}
}
}
This is a bay way. You should not trying to get the result out of the Executor, because it is only responsible for scheduling and executing tasks, not whats happening inside of them.
Your TareaActualizacion runnable should post the result to another piece of code, where you need it. This can be achieved using a queue or in the easiest case SwingUtilities.invokeLater().

Categories