Author: slotia
Date: 2012-09-18 18:19:04 -0700 (Tue, 18 Sep 2012)
New Revision: 30382
Modified:
core3/api/trunk/work-api/src/main/java/org/cytoscape/work/package-info.java
Log:
Updated package docs
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-18 21:29:37 UTC (rev 30381)
+++ core3/api/trunk/work-api/src/main/java/org/cytoscape/work/package-info.java
2012-09-19 01:19:04 UTC (rev 30382)
@@ -11,7 +11,7 @@
<li>Write an action listener. This could be a named class or an anonymous
class.</li>
<li>Add the {@code ActionListener} to the menu item by calling the {@code
addActionListener} method.
</ol>
- When the user selects the menu item, your action listener is invoked.
+ When the user selects the menu item, Swing invokes your action listener.
</p>
<h3>Why tasks?</h3>
@@ -26,11 +26,11 @@
</p>
<p>
- We establish <i>tasks</i> as a replacement for action listeners to take
advantage
+ We establish {@link org.cytoscape.work.Task}s as a replacement for action
listeners to take advantage
of OSGi's loose coupling. A single task object can be thought of as an
action listener. Tasks implement the {@code run} method, which is analogous
- to the action listener's {@code actionPerformed} method. <i>Task
factories</i>
- create tasks. You export task factories as an OSGi service, not tasks.
+ to the action listener's {@code actionPerformed} method. A {@link
org.cytoscape.work.TaskFactory}
+ creates tasks. You export a task factory as an OSGi service, not tasks.
</p>
<h3>Why task factories?</h3>
@@ -44,9 +44,14 @@
some parameters, then it returns a new task initialized with those
parameters.
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.
- (If you know a functional programming, here is an
+ </p>
+
+ <p>
+ Moreover, a well written task factory returns new instances of {@code Task}
each time
+ the task factory is invoked. A new task object per invocation neatly
contains state that does not
+ spill over into separate task invocations. Incorrectly written task factories
+ return the same task instance each time it is invoked.
+ (If you know a functional programming language, 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.)
@@ -78,11 +83,17 @@
<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
+ tasks after it is done. {@link org.cytoscape.work.TaskIterator}s let you
create a series of tasks
that you control what task to execute next, even while a task in that
iterator
is executing.
</p>
+ <p>
+ In most cases, you do not have to consider task iterators, since you
probably be executing a
+ single task and won't be executing a sequence tasks. In this case, when you
write a task factory
+ implementation, you return a new task iterator that only contains your task.
+ </p>
+
<h3>Task managers</h3>
<p>
Most of the time, you export a task factory service, and Cytoscape
@@ -100,7 +111,9 @@
<li>
Tasks are run asynchronously. This means you can encapsulate
complicated, long-running algorithms entirely in a single task.
- This will not freeze Cytoscape.
+ You do not have to think about threading issues, as this will not freeze
Cytoscape.
+ However, if a portion of your task's code invokes Swing, you may have to
wrap the
+ code in a {@link javax.swing.SwingUtilities#invokeLater}.
</li>
<li>
Because tasks can take a long time to complete,
@@ -110,11 +123,16 @@
</li>
<li>
Tasks can throw exceptions in the {@code run} method. Cytoscape will catch
the exception and
- inform the user.
+ inform the user. When a task encounters an exception, it is considered good
practice to catch
+ the exception and throw another exception with a high-level explanation
that the user
+ can understand. For example, let's say your task is reading from a file,
which can cause
+ an {@code IOException} to be thrown. Because the explanation of the {@code
IOException} will
+ probably be unhelpful for the user, you should catch {@code IOException}
and throw your own
+ exception with an easy to understand explanation and some solutions for the
user to remedy the problem.
</li>
<li>
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
+ Long-running tasks must respond to user cancellations and return from the
{@code run} method gracefully.
</li>
<li>
@@ -125,9 +143,9 @@
</li>
<li>
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.
- Tunables are great for algorithms with settings that the user can change
before
+ the task has tunables, it creates an interface for user input.
+ Cytoscape fills in the tunables with the user's input, then executes the
task.
+ Tunables are suited 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
@@ -139,19 +157,22 @@
<h3>Examples</h3>
<h4>Hello World Menu Item</h4>
<pre>{@code
- // Example that displays a user message at the bottom of the Cytoscape
desktop
- Logger logger = LoggerFactory.getLogger("CyUserMessages");
- Task myTask = new Task() {
+ class MyTask implements Task {
public void run(TaskMonitor monitor) {
logger.info("Hey chef");
}
public void cancel() {
}
- };
+ }
+
+ ...
+
+ // Example that displays a user message at the bottom of the Cytoscape
desktop
+ Logger logger = LoggerFactory.getLogger("CyUserMessages");
TaskFactory myTaskFactory = new TaskFactory() {
public TaskIterator createTaskIterator() {
- return new TaskIterator(myTask);
+ return new TaskIterator(new MyTask());
}
public boolean isReady() {
@@ -169,64 +190,69 @@
<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{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">
+ <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">
</p>
- <pre>{@code
- // Example that approximates π
+ <tt><pre>
+ // Example that approximates pi
Logger logger = LoggerFactory.getLogger("CyUserMessages");
- final int iterations = 1000;
- Task myPiTask = new Task() {
+
+ class MyPiTask implements Task {
+ final int iterations = 1000;
public void run(TaskMonitor monitor) {
double pi = 2.0;
- for (int n = 1; n <= iterations; n++) {
- pi *= (2 * n) * (2 * n) / ((2 * n - 1) * (2 * n + 1));
+ for (int n = 1; n <= iterations; n++) {
+ pi *= ((double) (2 * n) * (2 * n)) / ((2 * n - 1) * (2 * n +
1));
}
logger.info("Our approximation of pi is: " +
Double.toString(pi));
}
public void cancel() {
}
- };
+ }
+
+ ...
+
TaskFactory myTaskFactory = new TaskFactory() {
public TaskIterator createTaskIterator() {
- return new TaskIterator(myPiTask);
+ return new TaskIterator(new MyPiTask());
}
public boolean isReady() {
return true;
}
};
+
Properties props = new Properties();
props.setProperty(PREFERRED_MENU,"Apps");
props.setProperty(TITLE,"Approximate Pi");
props.setProperty(MENU_GRAVITY,"1.0");
props.setProperty(TOOLTIP,"Approximates pi using the Wallis product");
registerService(bc, myTaskFactory, TaskFactory.class, props);
- }</pre>
+ </tt></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
+ 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() {
+ <tt><pre>
+ class MyPiTask implements Task {
+ final int iterations = 1000;
public void run(TaskMonitor monitor) {
- monitor.setTitle("Calculating Pi");
+ <b>monitor.setTitle("Calculating Pi");</b>
double pi = 2.0;
- for (int n = 1; n <= iterations; n++) {
- monitor.setProgress(((double) n) / iterations);
- pi *= (2 * n) * (2 * n) / ((2 * n - 1) * (2 * n + 1));
+ for (int n = 1; n <= iterations; n++) {
+ <b>monitor.setProgress(((double) n) / iterations);</b>
+ pi *= ((double) (2 * n) * (2 * n)) / ((2 * n - 1) * (2 * n +
1));
}
logger.info("Our approximation of pi is: " +
Double.toString(pi));
}
public void cancel() {
}
- };
- }</pre>
+ }
+ </pre></tt>
<h4>Cancelling a task</h4>
The problem with our task is that it
@@ -238,31 +264,30 @@
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;
+ <tt><pre>
+ class MyPiTask implements Task {
+ final int iterations = 1000;
public void run(TaskMonitor monitor) {
monitor.setTitle("Calculating Pi");
double pi = 2.0;
- for (int n = 1; n <= iterations; n++) {
- if (cancelled)
- break;
+ for (int n = 1; n <= iterations; n++) {
+ <b>if (cancelled)</b>
+ <b>break;</b>
monitor.setProgress(((double) n) / iterations);
- pi *= (2 * n) * (2 * n) / ((2 * n - 1) * (2 * n + 1));
+ pi *= ((double) (2 * n) * (2 * n)) / ((2 * n - 1) * (2 * n +
1));
}
- if (!cancelled)
+ <b>if (!cancelled)</b>
logger.info("Our approximation of pi is: " +
Double.toString(pi));
}
public void cancel() {
- cancelled = true;
+ <b>cancelled = true;</b>
}
- };
- }</pre>
+ }
+ </pre></tt>
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
+ This method 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
@@ -272,22 +297,33 @@
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
+ Create a variable in your task 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.
+ in an I/O operation will sometimes stop it. Here's how this would look:
+ <tt><pre>
+ class MyTask implements Task {
+ Thread runThread = null;
+ public void run(TaskMonitor monitor) {
+ runThread = Thread.currentThread();
+
+ ... // complicated I/O operations
+ }
+
+ public void cancel() {
+ runThread.interrupt();
+ }
+ }
+ </pre></tt>
</li>
<li>
If your {@code run} method is reading from an {@link java.io.InputStream},
--
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.