/[schmitzm]/trunk/src/skrueger/geotools/RenderingExecutor.java
ViewVC logotype

Annotation of /trunk/src/skrueger/geotools/RenderingExecutor.java

Parent Directory Parent Directory | Revision Log Revision Log


Revision 524 - (hide annotations)
Wed Nov 18 09:56:47 2009 UTC (15 years, 3 months ago) by alfonx
Original Path: branches/1.0-gt2-2.6/src/skrueger/geotools/RenderingExecutor.java
File size: 11399 byte(s)
* XMapPane is now only using one RenderingExecutor, and its now properly disposed, not leaving any threads in the ThreadPool
* Locks are not happening, memory leak still happens :-/
* Corrected isValid() against is WellDefined() in XMapPane
1 alfonx 510 package skrueger.geotools;
2    
3     /*
4     * GeoTools - The Open Source Java GIS Toolkit
5     * http://geotools.org
6     *
7     * (C) 2002-2008, Open Source Geospatial Foundation (OSGeo)
8     *
9     * This library is free software; you can redistribute it and/or
10     * modify it under the terms of the GNU Lesser General Public
11     * License as published by the Free Software Foundation;
12     * version 2.1 of the License.
13     *
14     * This library is distributed in the hope that it will be useful,
15     * but WITHOUT ANY WARRANTY; without even the implied warranty of
16     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17     * Lesser General Public License for more details.
18     */
19    
20    
21     import gtmig.org.geotools.swing.XMapPane;
22    
23     import java.awt.AlphaComposite;
24     import java.awt.Color;
25     import java.awt.Composite;
26     import java.awt.Graphics2D;
27     import java.awt.Rectangle;
28     import java.util.concurrent.Callable;
29     import java.util.concurrent.CountDownLatch;
30     import java.util.concurrent.ExecutorService;
31     import java.util.concurrent.Executors;
32     import java.util.concurrent.Future;
33     import java.util.concurrent.ScheduledExecutorService;
34     import java.util.concurrent.ScheduledFuture;
35     import java.util.concurrent.TimeUnit;
36     import java.util.concurrent.atomic.AtomicBoolean;
37    
38     import org.geotools.geometry.jts.ReferencedEnvelope;
39     import org.geotools.renderer.GTRenderer;
40     import org.geotools.renderer.RenderListener;
41     import org.geotools.swing.JMapPane;
42     import org.opengis.feature.simple.SimpleFeature;
43    
44     /**
45     * This class is used by {@code JMapPane} to handle the scheduling and running of
46     * rendering tasks on a background thread. It functions as a single thread, non-
47     * queueing executor, ie. only one rendering task can run at any given time and,
48     * while it is running, any other submitted tasks will be rejected.
49     * <p>
50     * Whether a rendering task is accepted or rejected can be tested on submission:
51     * <pre><code>
52     * ReferencedEnvelope areaToDraw = ...
53     * Graphics2D graphicsToDrawInto = ...
54     * boolean accepted = renderingExecutor.submit(areaToDraw, graphicsToDrawInto);
55     * </code></pre>
56     *
57     * The status of the executor can also be checked at any time like this:
58     * <pre><code>
59     * boolean busy = renderingExecutor.isRunning();
60     * </code></pre>
61     *
62     * While a rendering task is running it is regularly polled to see if it has completed
63     * and, if so, whether it finished normally, was cancelled or failed. The interval between
64     * polling can be adjusted which might be useful to tune the executor for particular
65     * applications:
66     * <pre><code>
67     * RenderingExecutor re = new RenderingExecutor( mapPane );
68     * re.setPollingInterval( 150 ); // 150 milliseconds
69     * </code></pre>
70     *
71     * @author Michael Bedward
72     * @since 2.7
73     * @source $URL: http://svn.osgeo.org/geotools/branches/2.6.x/modules/unsupported/swing/src/main/java/org/geotools/swing/RenderingExecutor.java $
74     * @version $Id: RenderingExecutor.java 34285 2009-10-30 10:48:49Z mbedward $
75     *
76     * @see JMapPane
77     */
78     public class RenderingExecutor {
79    
80     private final XMapPane mapPane;
81     private final ExecutorService taskExecutor;
82     private final ScheduledExecutorService watchExecutor;
83    
84 alfonx 513 // /** The default interval (milliseconds) for polling the result of a rendering task */
85     // public static final long DEFAULT_POLLING_INTERVAL = 300L;
86 alfonx 510
87     private long pollingInterval;
88    
89     /*
90     * This latch is used to avoid a race between the cancellation of
91     * a current task and the submittal of a new task
92     */
93     private CountDownLatch cancelLatch;
94    
95     /**
96     * Constants to indicate the result of a rendering task
97     */
98     public enum TaskResult {
99     PENDING,
100     COMPLETED,
101     CANCELLED,
102     FAILED;
103     }
104    
105     private long numFeatures;
106    
107     /**
108     * A rendering task
109     */
110     private class Task implements Callable<TaskResult>, RenderListener {
111    
112     private final ReferencedEnvelope envelope;
113     private final Rectangle paintArea;
114     private final Graphics2D graphics;
115    
116     private boolean cancelled;
117     private boolean failed;
118     final private GTRenderer renderer;
119    
120     /**
121     * Constructor. Creates a new rendering task
122     *
123     * @param envelope map area to render (world coordinates)
124     * @param paintArea drawing area (image or display coordinates)a
125     * @param graphics graphics object used to draw into the image or display
126     */
127     public Task(final ReferencedEnvelope envelope, final Rectangle paintArea, final Graphics2D graphics, GTRenderer renderer) {
128     this.envelope = envelope;
129     this.paintArea = paintArea;
130     this.graphics = graphics;
131     this.cancelled = false;
132     this.renderer = renderer;
133     failed = false;
134     }
135    
136     /**
137     * Called by the executor to run this rendering task.
138     *
139     * @return result of the task: completed, cancelled or failed
140     * @throws Exception
141     */
142     public TaskResult call() throws Exception {
143     if (!cancelled) {
144    
145     try {
146     renderer.addRenderListener(this);
147    
148     Composite composite = graphics.getComposite();
149     //graphics.setComposite(AlphaComposite.Src);
150     //graphics.setBackground(Color.WHITE);
151     graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
152     graphics.fill(paintArea);
153     graphics.setComposite(composite);
154    
155    
156     numFeatures = 0;
157     renderer.paint(graphics, mapPane.getVisibleRect(), envelope, mapPane.getWorldToScreenTransform());
158    
159     } finally {
160     renderer.removeRenderListener(this);
161     }
162     }
163    
164     if (cancelled) {
165     return TaskResult.CANCELLED;
166     } else if (failed) {
167     return TaskResult.FAILED;
168     } else {
169     return TaskResult.COMPLETED;
170     }
171     }
172    
173     /**
174     * Cancel the rendering task if it is running. If called before
175     * being run the task will be abandoned.
176     */
177     public synchronized void cancel() {
178     if (isRunning()) {
179     cancelled = true;
180     renderer.stopRendering();
181     }
182     }
183    
184     /**
185     * Called by the renderer when each feature is drawn.
186     *
187     * @param feature the feature just drawn
188     */
189     public void featureRenderer(SimpleFeature feature) {
190     // @todo update a progress listener
191     numFeatures++ ;
192     }
193    
194     /**
195     * Called by the renderer on error
196     *
197     * @param e cause of the error
198     */
199     public void errorOccurred(Exception e) {
200     renderingException = e;
201     graphics.setColor(Color.white);
202     graphics.drawString(e.getMessage(), 11, 11);
203     graphics.drawString(e.getMessage(), 9, 9);
204     graphics.setColor(Color.black);
205     graphics.drawString(e.getMessage(), 10, 10);
206     failed = true;
207     }
208    
209     }
210    
211     private AtomicBoolean taskRunning;
212     private Task task;
213     private Future<TaskResult> taskResult;
214     private ScheduledFuture<?> watcher;
215     private Exception renderingException;
216    
217     /**
218     * Constructor. Creates a new executor to service the specified map pane.
219     *
220     * @param mapPane the map pane to be serviced
221 alfonx 513 * @param l
222 alfonx 510 */
223 alfonx 513 public RenderingExecutor(final XMapPane mapPane, long pollingInterval) {
224 alfonx 510 taskRunning = new AtomicBoolean(false);
225     this.mapPane = mapPane;
226     taskExecutor = Executors.newSingleThreadExecutor();
227     watchExecutor = Executors.newSingleThreadScheduledExecutor();
228 alfonx 513 this.pollingInterval = pollingInterval;
229 alfonx 510 cancelLatch = new CountDownLatch(0);
230     }
231    
232     /**
233     * Get the interval for polling the result of a rendering task
234     *
235     * @return polling interval in milliseconds
236     */
237     public long getPollingInterval() {
238     return pollingInterval;
239     }
240    
241     /**
242     * Set the interval for polling the result of a rendering task
243     *
244     * @param interval interval in milliseconds (values {@code <=} 0 are ignored)
245     */
246     public void setPollingInterval(long interval) {
247     if (interval > 0) {
248     pollingInterval = interval;
249     }
250     }
251    
252     /**
253     * Submit a new rendering task. If no rendering task is presently running
254     * this new task will be accepted; otherwise it will be rejected (ie. there
255     * is no task queue).
256     *
257     * @param envelope the map area (world coordinates) to be rendered
258     * @param graphics the graphics object to draw on
259     *
260     * @return true if the rendering task was accepted; false if it was
261     * rejected
262     */
263     public synchronized boolean submit(ReferencedEnvelope envelope, Rectangle paintArea, Graphics2D graphics, final GTRenderer renderer) {
264 alfonx 524 if (!isRunning()) {
265     // try {
266     // // wait for any cancelled task to finish its shutdown
267     // cancelLatch.await();
268     // } catch (InterruptedException ex) {
269     // return false;
270     // }
271 alfonx 510
272     task = new Task(envelope, paintArea, graphics, renderer);
273     taskRunning.set(true);
274     taskResult = taskExecutor.submit(task);
275     watcher = watchExecutor.scheduleAtFixedRate(new Runnable() {
276    
277     public void run() {
278     pollTaskResult();
279     }
280 alfonx 513 }, pollingInterval, pollingInterval, TimeUnit.MILLISECONDS);
281 alfonx 510
282     return true;
283     }
284    
285     return false;
286     }
287    
288     /**
289     * Cancel the current rendering task if one is running
290     */
291     public synchronized void cancelTask() {
292     if (isRunning()) {
293     task.cancel();
294 alfonx 524
295     // Cancelling to often... can that be the reason?
296     if (cancelLatch.getCount() < 1) {
297     cancelLatch = new CountDownLatch(1);
298     }
299 alfonx 510 }
300     }
301    
302     private void pollTaskResult() {
303     if (!taskResult.isDone()) {
304     return;
305     }
306    
307     TaskResult result = TaskResult.PENDING;
308    
309     try {
310     result = taskResult.get();
311     } catch (Exception ex) {
312     throw new IllegalStateException("When getting rendering result", ex);
313     }
314    
315     watcher.cancel(false);
316     taskRunning.set(false);
317    
318     switch (result) {
319     case CANCELLED:
320     cancelLatch.countDown();
321     mapPane.onRenderingCancelled();
322     break;
323    
324     case COMPLETED:
325     mapPane.onRenderingCompleted();
326     break;
327    
328     case FAILED:
329     mapPane.onRenderingFailed(renderingException);
330     break;
331 alfonx 513
332     case PENDING:
333     mapPane.onRenderingPending();
334     break;
335    
336 alfonx 510 }
337     }
338    
339     public synchronized boolean isRunning() {
340     return taskRunning.get();
341     }
342    
343     @Override
344     protected void finalize() throws Throwable {
345     if (this.isRunning()) {
346     taskExecutor.shutdownNow();
347     watchExecutor.shutdownNow();
348     }
349     }
350 alfonx 524
351     public void dispose() {
352     taskExecutor.shutdownNow();
353     watchExecutor.shutdownNow();
354     }
355 alfonx 510 }
356    

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26