Author: slotia
Date: 2012-09-06 17:09:22 -0700 (Thu, 06 Sep 2012)
New Revision: 30329
Modified:
core3/api/trunk/work-api/src/main/java/org/cytoscape/work/package-info.java
Log:
Added examples to javadoc
Modified:
core3/api/trunk/work-api/src/main/java/org/cytoscape/work/package-info.java
===================================================================
--- core3/api/trunk/work-api/src/main/java/org/cytoscape/work/package-info.java
2012-09-06 20:54:52 UTC (rev 30328)
+++ core3/api/trunk/work-api/src/main/java/org/cytoscape/work/package-info.java
2012-09-07 00:09:22 UTC (rev 30329)
@@ -1,40 +1,7 @@
-/*
-This package contains the task framework, where
-{@link org.cytoscape.work.Task}s represent a single unit of
-asynchronous work meant to run in a separate thread.
-Examples of work that might be wrapped by a task include
-loading a network or applying a layout.
-
-<p>
-{@link org.cytoscape.work.Task} objects are meant to be single
-use objects and are produced by {@link org.cytoscape.work.TaskFactory}
singletons. Each type of task
-is produced by a unique {@link org.cytoscape.work.TaskFactory}, meaning there
are many
-{@link org.cytoscape.work.TaskFactory}s in the system at once.
-</p>
-
-<p>
-Rather than executing directly,
-{@link org.cytoscape.work.Task}s are executed by a {@link
org.cytoscape.work.TaskManager}.
-{@link org.cytoscape.work.TaskManager}s execute {@link
org.cytoscape.work.TaskIterator}s, which
-contain one or more {@link org.cytoscape.work.Task}s to be executed in order.
It
-is frequently useful to break one unit of work into multiple {@link
org.cytoscape.work.Task}s.
-The {@link org.cytoscape.work.TaskManager} handles the parameterization,
execution,
-{@link org.cytoscape.work.TaskMonitor}ing, and error handling related to the
execution of
-{@link org.cytoscape.work.Task}s.
-</p>
-
-
-{@link org.cytoscape.work.Task}s can be parameterized with user
-input (e.g. the name of a file to load) using {@link
org.cytoscape.work.Tunable} annotations. By
-annotating public fields and methods in {@link org.cytoscape.work.Task}
objects, the
-{@link org.cytoscape.work.TaskManager} will prompt the user to input required
values and set the
-values in the {@link org.cytoscape.work.Task} prior to execution.
- */
-
/**
-
This package defines the task framework, where tasks are units of work.
+ <h3>Tasks are like action listeners</h3>
<p>
Tasks are somewhat analogous to Swing action listeners. To recap Swing action
listeners, let's say you want to create a menu item and listen to its
@@ -47,6 +14,7 @@
When the user selects the menu item, your action listener is invoked.
</p>
+ <h3>Why tasks?</h3>
<p>
Cytoscape is in an OSGi environment. Its components are deliberately made to
be loosely coupled, and this loose coupling changes how we think
@@ -65,6 +33,7 @@
create tasks. You export task factories as an OSGi service, not tasks.
</p>
+ <h3>Why task factories?</h3>
<p>
Why even have task factories? Why not just use tasks directly?
Many times tasks need input parameters.
@@ -76,13 +45,14 @@
The task can obtain those parameters either through its constructor or as an
inner class that has access to the task factory's members.
Moreover, a single task object neatly contains state that does not
- spill over into separate task invocations because of task factories.
+ spill over into separate task invocations.
(If you know a functional programming, here is an
analogy: a task is like an inner function
that the task factory function returns; the task gets inputs from free
variables
defined in the task factory function.)
</p>
+ <h3>Overview of a menu item with a task factory</h3>
<p>
To illustrate tasks and task factories, here is how you create a menu item
in Cytoscape's OSGi environment:
@@ -100,17 +70,20 @@
</ol>
Cytoscape picks up the task factory, reads its properties, and creates a
menu item just for that task factory. When that menu item is selected by the
user,
- Cytoscape tells the task factory to create a task, then invokes it. Note
that tasks
- are used beyond just creating menu items. They are used for creating toolbar
items
- as well.
+ Cytoscape tells the task factory to create a task, then invokes it. Tasks
+ are used for more than just creating menu items. For example, they are used
for creating
+ toolbar items as well.
</p>
+ <h3>Task iterators</h3>
<p>
Sometimes when a task is executing, you need to run additional
tasks after it is done. <i>Task iterators</i> let you create a series of
tasks
- that you control while a task in that iterator is executing.
+ that you control what task to execute next, even while a task in that
iterator
+ is executing.
</p>
+ <h3>Task managers</h3>
<p>
Most of the time, you export a task factory service, and Cytoscape
invokes the task for you. Sometimes, you will want to invoke a task yourself.
@@ -119,45 +92,46 @@
run the task.
</p>
+ <h3>Action listeners are dead—long live tasks</h3>
<p>
- The analogy of a task as an OSGi version of the action listener sweeps many
- of its advantages under the rug. Tasks:
+ The analogy of tasks as an OSGi equivalent of action listeners sweeps many
+ of its advantages under the rug. Here are some additional benefits of tasks:
<ul>
<li>
- are run asynchronously. This means you can encapsulate
+ Tasks are run asynchronously. This means you can encapsulate
complicated, long-running algorithms entirely in a single task.
This will not freeze Cytoscape.
</li>
<li>
- can inform the user of its progress through {@link
org.cytoscape.work.TaskMonitor}.
+ Because tasks can take a long time to complete,
+ they can inform the user of its progress through {@link
org.cytoscape.work.TaskMonitor}.
{@code TaskMonitor}s are passed in to the task's {@code run} method.
In the task's {@code run} method, you call its methods to update the user
interface.
</li>
<li>
- can throw exceptions in the {@code run} method. Cytoscape will catch the
exception and
+ Tasks can throw exceptions in the {@code run} method. Cytoscape will catch
the exception and
inform the user.
</li>
<li>
- can be cancelled by the user. Tasks are required to implement the {@code
cancel} method.
+ Tasks can be cancelled by the user. Tasks are required to implement the
{@code cancel} method.
Long-running tasks are required to respond to user cancellations and return
from the
{@code run} method gracefully.
</li>
<li>
- are independent of Swing. Provided that
+ Tasks are independent of Swing. Provided that
the {@code run} method does not explicitly use Swing,
- the task can be used in other environments like
- the command-line. When Cytoscape is run as a Swing application,
- it will create a Swing interface behind the scenes without you having to
- create one.
+ the task can be used in other environments besides Swing, like
+ the command-line.
</li>
<li>
- can have user-defined inputs called <i>tunables</i>. When Cytoscape detects
that
+ Tasks can have user-defined inputs called <i>tunables</i>. When Cytoscape
detects that
the task has tunables, it creates a user interface for user input.
Cytoscape then fills in the tunables with the user's input, then executes
the task.
- Because the work framework is independent of Swing, you will not have to
create a
- Swing dialog to get user input. Also, if the task is run in the
command-line, the user
- can still provide input. Of course, when Cytoscape is running as a
- Swing application, it will, behind-the-scenes, create a Swing dialog for
the tunables.
+ Tunables are great for algorithms with settings that the user can change
before
+ running the task.
+ With tunables, you do not have to manually create a
+ Swing dialog to get user input. If the task is run in the command-line, the
user
+ can still provide input.
</li>
</ul>
</p>
@@ -192,16 +166,22 @@
registerService(bc, myTaskFactory, TaskFactory.class, props);
}</pre>
- <h4>Task monitors and approximating pi</h4>
- Let's say you want to approximate pi using the Wallis product:
+ <h4>Approximating π</h4>
+ Here is another example of a task that approximates π using the Wallis
product:
<p align="center">
- <img src="http://latex.codecogs.com/gif.latex?\dpi{120}
\frac{\pi}{2}=\prod^\infty_{n=1}\left( \frac{2n}{2n-1} \cdot \frac{2n}{2n+1}
\right )=\left( \frac{2}{1} \cdot \frac{2}{3} \right ) \cdot \left( \frac{4}{3}
\cdot \frac{4}{5} \right ) \cdot \left( \frac{6}{5} \cdot \frac{6}{7} \right )
\ldots" title="\dpi{150} \frac{\pi}{2}=\prod^\infty_{n=1}\left( \frac{2n}{2n-1}
\cdot \frac{2n}{2n+1} \right )=\left( \frac{2}{1} \cdot \frac{2}{3} \right )
\cdot \left( \frac{4}{3} \cdot \frac{4}{5} \right ) \cdot \left( \frac{6}{5}
\cdot \frac{6}{7} \right ) \ldots">
+ <img src="http://latex.codecogs.com/gif.latex?\dpi{100}
\frac{\pi}{2}=\prod^\infty_{n=1}\left( \frac{2n}{2n-1} \cdot \frac{2n}{2n+1}
\right )=\left( \frac{2}{1} \cdot \frac{2}{3} \right ) \cdot \left( \frac{4}{3}
\cdot \frac{4}{5} \right ) \cdot \left( \frac{6}{5} \cdot \frac{6}{7} \right )
\ldots" title="\dpi{150} \frac{\pi}{2}=\prod^\infty_{n=1}\left( \frac{2n}{2n-1}
\cdot \frac{2n}{2n+1} \right )=\left( \frac{2}{1} \cdot \frac{2}{3} \right )
\cdot \left( \frac{4}{3} \cdot \frac{4}{5} \right ) \cdot \left( \frac{6}{5}
\cdot \frac{6}{7} \right ) \ldots">
</p>
<pre>{@code
- // Example that displays a user message at the bottom of the Cytoscape
desktop
+ // Example that approximates π
Logger logger = LoggerFactory.getLogger("CyUserMessages");
- Task myTask = new Task() {
+ final int iterations = 1000;
+ Task myPiTask = new Task() {
public void run(TaskMonitor monitor) {
+ double pi = 2.0;
+ for (int n = 0; n < iterations; n++) {
+ pi *= (2 * n) * (2 * n) / ((2 * n - 1) * (2 * n + 1));
+ }
+ logger.info("Our approximation of pi is: " +
Double.toString(pi));
}
public void cancel() {
@@ -209,7 +189,7 @@
};
TaskFactory myTaskFactory = new TaskFactory() {
public TaskIterator createTaskIterator() {
- return new TaskIterator(myTask);
+ return new TaskIterator(myPiTask);
}
public boolean isReady() {
@@ -218,10 +198,141 @@
};
Properties props = new Properties();
props.setProperty(PREFERRED_MENU,"Apps");
- props.setProperty(TITLE,"Calculate Pi");
+ props.setProperty(TITLE,"Approximate Pi");
props.setProperty(MENU_GRAVITY,"1.0");
- props.setProperty(TOOLTIP,"Demonstrates how cool the work framework is");
+ props.setProperty(TOOLTIP,"Approximates pi using the Wallis product");
registerService(bc, myTaskFactory, TaskFactory.class, props);
}</pre>
+
+ <h4>Using a task monitor</h4>
+ The problem with the above example is that if the π calculation takes a
long time,
+ the user is not informed of its progress.
+ Here, we modify {@code myPiTask} so that it informs the user during this
long calculation
+ by using the task monitor.
+
+ <pre>{@code
+ final int iterations = 1000;
+ Task myPiTask = new Task() {
+ public void run(TaskMonitor monitor) {
+ monitor.setTitle("Calculating Pi");
+ double pi = 2.0;
+ for (int n = 0; n < iterations; n++) {
+ monitor.setProgress(((double) n) / iterations);
+ pi *= (2 * n) * (2 * n) / ((2 * n - 1) * (2 * n + 1));
+ }
+ logger.info("Our approximation of pi is: " +
Double.toString(pi));
+ }
+
+ public void cancel() {
+ }
+ };
+ }</pre>
+
+ <h4>Cancelling a task</h4>
+ The problem with our task is that it
+ does not adequately respond user cancellation.
+ When the user cancels the task, nothing happens. We have to fill in the
+ {@code cancel} method. The main challenge of dealing with cancellation
+ is that the thread executing {@code run} is different
+ from the one executing {@code cancel}. We have to come up with a way
+ to communicate cancellation from {@code cancel} to {@code run}.
+ Here is how we deal with this issue:
+
+ <pre>{@code
+ final int iterations = 1000;
+ Task myPiTask = new Task() {
+ boolean cancelled = false;
+ public void run(TaskMonitor monitor) {
+ monitor.setTitle("Calculating Pi");
+ double pi = 2.0;
+ for (int n = 0; n < iterations; n++) {
+ if (cancelled)
+ break;
+ monitor.setProgress(((double) n) / iterations);
+ pi *= (2 * n) * (2 * n) / ((2 * n - 1) * (2 * n + 1));
+ }
+ if (!cancelled)
+ logger.info("Our approximation of pi is: " +
Double.toString(pi));
+ }
+
+ public void cancel() {
+ cancelled = true;
+ }
+ };
+ }</pre>
+
+ When the user hits the Cancel button, Cytoscape invokes the {@code cancel}
method.
+ This switches the {@code cancelled} variable to true. When {@code run}
starts another
+ iteration of the {@code for} loop, it checks the {@code cancelled} variable.
+ Since {@code cancelled} was flipped, it stops the loop.
+ In other words, the {@code cancel} method tells the {@code run} method to
stop through
+ the {@code cancelled} variable.
+
+ <p>
+ Communicating cancellation through a boolean variable
+ works well for tasks with loops, since each loop iteration
+ can just check if the variable has changed and stop.
+ </p>
+
+ <p>
+ Tasks that do I/O operations, however, require more consideration,
+ like a task that downloads a file from some URL.
+ There are several ways you can deal with cancellation during an
+ I/O operation:
+ <ul>
+ <li>
+ Create a variable that holds the thread executing {@code run}. When
+ {@code run} first starts, fill in this variable with the method
+ {@link java.lang.Thread#currentThread}. Now the {@code cancel} method
+ can know the thread that is executing {@code run}.
+ When cancellation occurs, call {@link java.lang.Thread#interrupt}
+ on the thread executing {@code run}. Interrupting a thread
+ in an I/O operation will sometimes stop it.
+ </li>
+ <li>
+ If your {@code run} method is reading from an {@link java.io.InputStream},
+ a {@link java.io.Reader}, a {@link java.net.Socket}, or a {@link
java.net.URLConnection},
+ call its {@code close} method from the {@code cancel} method.
+ This will stop the I/O operation in the {@code run} method.
+ </li>
+ <li>
+ Use Java's non-blocking I/O package: {@link java.nio}. Since non-blocking
I/O operations
+ are usually done in a loop, it works well with the approach of checking for
cancellation
+ from a variable. Using non-blocking I/O is more elegant, but it takes
+ a lot more effort to write.
+ </li>
+ </ul>
+ </p>
+
+ <h4>A task with tunables</h4>
+ We have hard-coded a value for the number of iterations to
+ approximate π. Thanks to tunables, we can easily give the
+ user the option to specify
+ the number of iterations. That way the user can decide how long to
+ run our task—or how accurate our approximation should be.
+ We make {@code iterations} a tunable whose value the user can specify.
+ <pre>
+ Task myPiTask = new Task() {
+ {@literal @}Tunable(description="How many iterations of the Wallis
product to compute?")
+ public int iterations = 1000;
+ boolean cancelled = false;
+ public void run(TaskMonitor monitor) {
+ monitor.setTitle("Calculating Pi");
+ double pi = 2.0;
+ for (int n = 0; n < iterations; n++) {
+ if (cancelled)
+ break;
+ monitor.setProgress(((double) n) / iterations);
+ pi *= (2 * n) * (2 * n) / ((2 * n - 1) * (2 * n + 1));
+ }
+ if (!cancelled)
+ logger.info("Our approximation of pi is: " +
Double.toString(pi));
+ }
+
+ public void cancel() {
+ cancelled = true;
+ }
+ };
+ </pre>
*/
package org.cytoscape.work;
--
You received this message because you are subscribed to the Google Groups
"cytoscape-cvs" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/cytoscape-cvs?hl=en.