<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements.  See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file except in
compliance with the License.  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

<document id="background-tasks">
    <properties>
        <title>Background Tasks</title>
    </properties>

    <body>
        <p>
            Often, an application will need to perform a long-running task (such as loading a large
            file or executing a computationally-intensive operation). Executing such code on the UI
            thread will cause the application to stop responding to repaints while the operation is
            running, giving the appearance that it is "hung".
        </p>

        <p>
            Pivot includes the <tt>org.apache.pivot.util.concurrent.Task</tt> class to help resolve
            this problem. A <tt>Task</tt> represents an instance of an operation that may be
            performed by a background thread. It is an abstract class, whose <tt>execute()</tt>
            method is overridden by an application to perform the actual task, optionally returning
            a value when it is complete.
        </p>

        <p>
            Though callers can invoke the <tt>execute()</tt> method directly, this would have the
            same effect as simply executing the code inline: the UI will appear to hang until
            <tt>execute()</tt> returns. However, <tt>Task</tt> provides an additional version of
            the <tt>execute()</tt> method that takes an instance of
            <tt>org.apache.pivot.util.concurrent.TaskListener</tt>. When this version of the method
            is invoked, the <tt>Task</tt> class creates (or obtains) an instance of a background
            thread and calls the abstract version of the method on this thread instead of the UI
            thread. When the abstract <tt>execute()</tt> returns, the task notifies the caller by
            invoking one of the methods defined by the listener interface:
        </p>

        <p>
            <tt>public void taskExecuted(Task&lt;V&gt; task);</tt><br/>
            <tt>public void executeFailed(Task&lt;V&gt; task);</tt><br/>
        </p>

        <p>
            If the <tt>execute()</tt> method returns successfully, the <tt>taskExecuted()</tt>
            method is called, and the result of the operation can be obtained by calling the
            <tt>getResult()</tt> method of the task object. However, if the <tt>execute()</tt> call
            fails (by throwing an exception), the <tt>executeFailed()</tt> listener method is
            called, and the exception that was thrown can be obtained via the <tt>getFault()</tt>
            method of the task object.
        </p>

        <p>
            The following example demonstrates the behavior of an application when a task is
            executed synchronously vs. asynchronously. It uses a simple "sleep task" to simulate an
            operation that runs for five seconds:
        </p>

        <application class="org.apache.pivot.wtk.ScriptApplication"
            width="240" height="240">
            <libraries>
                <library>core</library>
                <library>wtk</library>
                <library>wtk-terra</library>
                <library>tutorials</library>
            </libraries>
            <startup-properties>
                <src>/org/apache/pivot/tutorials/backgroundtasks/background_tasks.bxml</src>
            </startup-properties>
        </application>

        <p>
            Pressing the "Execute Synchronously" button activates an activity indicator component
            and calls the synchronous version of the <tt>execute()</tt> method of the sleep task.
            Though the task runs and returns correctly, the activity indicator is never shown,
            because the UI thread has been unable to respond to repaints while the task was
            running. However, when the "Execute Asynchronously" button is pressed, the activity
            indicator appears correctly and runs for five seconds until the task is complete.
        </p>

        <p>
            The BXML for this example is shown below:
        </p>

        <source type="xml" location="org/apache/pivot/tutorials/backgroundtasks/background_tasks.bxml">
            <![CDATA[
            <backgroundtasks:BackgroundTasks title="Background Tasks" maximized="true"
                xmlns:bxml="http://pivot.apache.org/bxml"
                xmlns:backgroundtasks="org.apache.pivot.tutorials.backgroundtasks"
                xmlns="org.apache.pivot.wtk">
                <BoxPane orientation="vertical"
                    styles="{horizontalAlignment:'center', verticalAlignment:'center'}">
                    <Border styles="{padding:2}">
                        <ActivityIndicator bxml:id="activityIndicator"/>
                    </Border>

                    <PushButton bxml:id="executeSynchronousButton" buttonData="Execute Synchronously"/>
                    <PushButton bxml:id="executeAsynchronousButton" buttonData="Execute Asynchronously"/>
                </BoxPane>
            </backgroundtasks:BackgroundTasks>
            ]]>
        </source>

        <p>
            The Java source for <tt>SleepTask</tt> is as follows. It simply sleeps for 5000
            milliseconds and then returns a simulated result value:
        </p>

        <source type="java" location="org/apache/pivot/tutorials/backgroundtasks/SleepTask.java">
            <![CDATA[
            package org.apache.pivot.tutorials.backgroundtasks;

            import org.apache.pivot.util.concurrent.Task;
            import org.apache.pivot.util.concurrent.TaskExecutionException;

            public class SleepTask extends Task<String> {
                @Override
                public String execute() throws TaskExecutionException {
                    // Simulate a long-running activity (5s)
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException exception) {
                        throw new TaskExecutionException(exception);
                    }

                    // Return a simulated result value
                    return "Done sleeping!";
                }
            }
            ]]>
        </source>

        <p>
            The Java source for the application is shown below:
        </p>

        <source type="java" location="org/apache/pivot/tutorials/backgroundtasks/BackgroundTasks.java">
            <![CDATA[
            package org.apache.pivot.tutorials.backgroundtasks;

            import java.net.URL;

            import org.apache.pivot.beans.Bindable;
            import org.apache.pivot.collections.Map;
            import org.apache.pivot.util.Resources;
            import org.apache.pivot.util.concurrent.Task;
            import org.apache.pivot.util.concurrent.TaskExecutionException;
            import org.apache.pivot.util.concurrent.TaskListener;
            import org.apache.pivot.wtk.ActivityIndicator;
            import org.apache.pivot.wtk.Button;
            import org.apache.pivot.wtk.ButtonPressListener;
            import org.apache.pivot.wtk.PushButton;
            import org.apache.pivot.wtk.TaskAdapter;
            import org.apache.pivot.wtk.Window;

            public class BackgroundTasks extends Window implements Bindable {
                private ActivityIndicator activityIndicator = null;
                private PushButton executeSynchronousButton = null;
                private PushButton executeAsynchronousButton = null;

                @Override
                public void initialize(Map<String, Object> namespace, URL location, Resources resources) {
                    activityIndicator = (ActivityIndicator)namespace.get("activityIndicator");
                    executeSynchronousButton = (PushButton)namespace.get("executeSynchronousButton");
                    executeAsynchronousButton = (PushButton)namespace.get("executeAsynchronousButton");

                    executeSynchronousButton.getButtonPressListeners().add(new ButtonPressListener() {
                        @Override
                        public void buttonPressed(Button button) {
                            activityIndicator.setActive(true);

                            System.out.println("Starting synchronous task execution.");

                            SleepTask sleepTask = new SleepTask();

                            String result = null;
                            try {
                                result = sleepTask.execute();
                            } catch (TaskExecutionException exception) {
                                System.err.println(exception);
                            }

                            System.out.println("Synchronous task execution complete: \"" + result + "\"");

                            activityIndicator.setActive(false);
                        }
                    });

                    executeAsynchronousButton.getButtonPressListeners().add(new ButtonPressListener() {
                        @Override
                        public void buttonPressed(Button button) {
                            activityIndicator.setActive(true);
                            setEnabled(false);

                            System.out.println("Starting asynchronous task execution.");

                            SleepTask sleepTask = new SleepTask();
                            TaskListener<String> taskListener = new TaskListener<String>() {
                                @Override
                                public void taskExecuted(Task<String> task) {
                                    activityIndicator.setActive(false);
                                    setEnabled(true);

                                    System.out.println("Synchronous task execution complete: \""
                                        + task.getResult() + "\"");
                                }

                                @Override
                                public void executeFailed(Task<String> task) {
                                    activityIndicator.setActive(false);
                                    setEnabled(true);

                                    System.err.println(task.getFault());
                                }
                            };

                            sleepTask.execute(new TaskAdapter<String>(taskListener));
                        }
                    });
                }
            }
            ]]>
        </source>

        <p>
            Note that the button press listener for the "Execute Asynchronously" button wraps the
            actual task listener in an instance of <tt>org.apache.pivot.wtk.TaskAdapter</tt>. This
            is because, like most UI toolkits, Pivot user interfaces are single-threaded: all UI
            operations must occur on the same thread (which AWT calls the "event dispatch thread").
            Wrapping the task listener in a <tt>TaskAdapter</tt> ensures that the result listener
            will be called on the UI thread, rather than the background thread, which is what would
            occur if the listener was not wrapped in the adapter.
        </p>
    </body>
</document>
