View Javadoc
1   package fr.ifremer.quadrige3.ui.swing.action;
2   
3   /*
4    * #%L
5    * Reef DB :: UI
6    * $Id:$
7    * $HeadURL:$
8    * %%
9    * Copyright (C) 2013 - 2014 Ifremer
10   * %%
11   * This program is free software: you can redistribute it and/or modify
12   * it under the terms of the GNU Affero General Public License as published by
13   * the Free Software Foundation, either version 3 of the License, or
14   * (at your option) any later version.
15   * 
16   * This program is distributed in the hope that it will be useful,
17   * but WITHOUT ANY WARRANTY; without even the implied warranty of
18   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   * GNU General Public License for more details.
20   * 
21   * You should have received a copy of the GNU Affero General Public License
22   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23   * #L%
24   */
25  
26  import fr.ifremer.quadrige3.core.exception.QuadrigeTechnicalException;
27  import fr.ifremer.quadrige3.core.config.QuadrigeCoreConfiguration;
28  import fr.ifremer.quadrige3.ui.swing.ApplicationUI;
29  import fr.ifremer.quadrige3.ui.swing.ApplicationUIContext;
30  import fr.ifremer.quadrige3.ui.swing.AbstractUIHandler;
31  import fr.ifremer.quadrige3.ui.swing.model.ProgressionUIModel;
32  import org.jdesktop.beans.AbstractBean;
33  import org.nuiton.i18n.I18n;
34  
35  import javax.swing.AbstractAction;
36  import javax.swing.Icon;
37  import javax.swing.SwingWorker;
38  import java.awt.event.ActionEvent;
39  import java.util.concurrent.ExecutionException;
40  import java.util.concurrent.ThreadPoolExecutor;
41  
42  /**
43   * <p>Abstract AbstractWorkerAction class.</p>
44   *
45   * @author Ludovic Pecquot <ludovic.pecquot@e-is.pro>
46   * @param <M>
47   * @param <UI>
48   * @param <H>
49   */
50  public abstract class AbstractWorkerAction<M extends AbstractBean, UI extends ApplicationUI<M, ?>, H extends AbstractUIHandler<M, UI>>
51      extends AbstractAction {
52  
53      protected final H handler;
54  
55      private Worker worker;
56  
57      private final ThreadPoolExecutor executor;
58  
59      private final Object lock = new Object();
60  
61      private boolean wait;
62  
63      private Exception reportedException;
64  
65      /**
66       * <p>initAction.</p>
67       *
68       * @return a boolean.
69       */
70      public abstract boolean initAction();
71  
72      /**
73       * <p>doAction.</p>
74       *
75       * @throws Exception if any.
76       */
77      public abstract void doAction() throws Exception;
78  
79      /**
80       * <p>doneAction.</p>
81       */
82      public abstract void doneAction();
83  
84      /**
85       * <p>failedAction.</p>
86       *
87       * @param ex a {@link Throwable} object.
88       */
89      public abstract void failedAction(Throwable ex);
90  
91      /**
92       * <p>disposeAction.</p>
93       */
94      public abstract void disposeAction();
95  
96      /**
97       * <p>Constructor for AbstractWorkerAction.</p>
98       *  @param handler a H object.
99       *
100      */
101     public AbstractWorkerAction(H handler) {
102 
103         super();
104         this.handler = handler;
105 
106         // executor for worker action must not be pooled
107         executor = ActionFactory.createSingleThreadExecutor(ActionFactory.ExecutionMode.UNIQUE);
108     }
109 
110     /**
111      * <p>Getter for the field <code>handler</code>.</p>
112      *
113      * @return a H object.
114      */
115     public H getHandler() {
116         return handler;
117     }
118 
119     /**
120      * <p>getUI.</p>
121      *
122      * @return a UI object.
123      */
124     public UI getUI() {
125         return handler.getUI();
126     }
127 
128     /**
129      * <p>getModel.</p>
130      *
131      * @return a M object.
132      */
133     public M getModel() {
134         return handler.getModel();
135     }
136 
137     /**
138      * <p>getConfig.</p>
139      *
140      * @return a {@link QuadrigeCoreConfiguration} object.
141      */
142     public QuadrigeCoreConfiguration getConfig() {
143         return handler.getConfig();
144     }
145 
146     /**
147      * <p>getContext.</p>
148      *
149      * @return a {@link ApplicationUIContext} object.
150      */
151     public ApplicationUIContext getContext() {
152         return handler.getContext();
153     }
154 
155     /** {@inheritDoc} */
156     @Override
157     public boolean isEnabled() {
158         return super.isEnabled() && (worker == null || worker.isDone());
159     }
160 
161     public boolean isRunning() {
162         return executor.getActiveCount() > 0;
163     }
164 
165     /**
166      * <p>getActionDescription.</p>
167      *
168      * @return a {@link String} object.
169      */
170     public String getActionDescription() {
171         return (String) getValue(SHORT_DESCRIPTION);
172     }
173 
174     /**
175      * <p>setActionDescription.</p>
176      *
177      * @param actionDescription a {@link String} object.
178      */
179     public void setActionDescription(String actionDescription) {
180         putValue(SHORT_DESCRIPTION, actionDescription);
181     }
182 
183     /**
184      * <p>setActionIcon.</p>
185      *
186      * @param actionIcon a {@link Icon} object.
187      */
188     public void setActionIcon(Icon actionIcon) {
189         putValue(SMALL_ICON, actionIcon);
190         putValue(LARGE_ICON_KEY, actionIcon);
191     }
192 
193     /**
194      * <p>setActionKey.</p>
195      *
196      * @param actionKey a {@link String} object.
197      */
198     public void setActionKey(String actionKey) {
199         putValue(ACTION_COMMAND_KEY, actionKey);
200     }
201 
202     /**
203      * <p>setActionName.</p>
204      *
205      * @param actionName a {@link String} object.
206      */
207     public void setActionName(String actionName) {
208         putValue(NAME, actionName);
209     }
210 
211     /**
212      * <p>setActionMnemonic.</p>
213      *
214      * @param key a int.
215      */
216     public void setActionMnemonic(int key) {
217         putValue(MNEMONIC_KEY, key);
218     }
219 
220     /**
221      * <p>execute.</p>
222      */
223     public void execute() {
224         run();
225     }
226 
227     /**
228      * <p>executeAndWait.</p>
229      *
230      * @throws Exception if any.
231      */
232     public void executeAndWait() throws Exception {
233         wait = true;
234         run();
235         lock();
236         if (reportedException != null) {
237             throw reportedException;
238         }
239     }
240 
241     private void run() {
242 
243         boolean doWork;
244 
245         try {
246             doWork = initAction();
247         } catch (Exception ex) {
248             disposeAction();
249             unlock();
250             throw new QuadrigeTechnicalException(I18n.t("quadrige3.error.prepare.action", getActionDescription()), ex);
251         }
252 
253         if (doWork) {
254 
255             try {
256 
257                 // Threaded action
258                 worker = new Worker();
259                 executor.execute(worker);
260 
261             } catch (Exception ex) {
262                 disposeAction();
263                 throw new QuadrigeTechnicalException(I18n.t("quadrige3.error.execute.action", getActionDescription()), ex);
264             }
265 
266         }
267         else {
268 
269             disposeAction();
270             unlock();
271 
272         }
273     }
274 
275     /** {@inheritDoc} */
276     @Override
277     public void actionPerformed(ActionEvent e) {
278 
279         // call default run
280         run();
281 
282     }
283 
284     /**
285      * <p>lock.</p>
286      */
287     protected void lock() {
288         if (wait) {
289             synchronized (lock) {
290                 try {
291                     lock.wait();
292                 } catch (InterruptedException ex) {
293                     throw new QuadrigeTechnicalException(I18n.t("quadrige3.error.execute.wait.action", getActionDescription()), ex);
294                 } finally {
295                     wait = false;
296                 }
297             }
298         }
299     }
300 
301     /**
302      * <p>unlock.</p>
303      */
304     protected void unlock() {
305         if (wait) {
306             synchronized (lock) {
307                 lock.notifyAll();
308             }
309         }
310     }
311 
312     /**
313      * <p>isWait.</p>
314      *
315      * @return a boolean.
316      */
317     public boolean isWait() {
318         return wait;
319     }
320 
321     /**
322      * <p>Setter for the field <code>reportedException</code>.</p>
323      *
324      * @param reportedException a {@link Exception} object.
325      */
326     public void setReportedException(Exception reportedException) {
327         this.reportedException = reportedException;
328     }
329 
330     private class Worker extends SwingWorker<Void, ProgressionUIModel> {
331 
332         public Worker() {
333         }
334 
335         @Override
336         protected Void doInBackground() throws Exception {
337 
338             doAction();
339 
340             return null;
341         }
342 
343         @Override
344         protected void done() {
345 
346             try {
347                 try {
348                     get();
349                     doneAction();
350                 } catch (InterruptedException ex) {
351                     if (wait) {
352                         throw new QuadrigeTechnicalException(ex);
353                     }
354                     else {
355                         failedAction(ex);
356                     }
357                 } catch (ExecutionException ex) {
358                     Throwable cause = ex.getCause();
359                     if (wait) {
360                         if (cause != null && cause instanceof Exception) {
361                             setReportedException((Exception) cause);
362                         }
363                         else {
364                             setReportedException(ex);
365                         }
366                     }
367                     else {
368                         failedAction(cause);
369                     }
370                 }
371             } finally {
372                 disposeAction();
373                 unlock();
374 
375             }
376 
377         }
378 
379     }
380 
381 }