View Javadoc
1   package fr.ifremer.quadrige2.ui.swing.common.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.quadrige2.core.exception.Quadrige2TechnicalException;
27  import fr.ifremer.quadrige2.core.config.Quadrige2CoreConfiguration;
28  import fr.ifremer.quadrige2.ui.swing.common.ApplicationUI;
29  import fr.ifremer.quadrige2.ui.swing.common.ApplicationUIContext;
30  import fr.ifremer.quadrige2.ui.swing.common.AbstractUIHandler;
31  import fr.ifremer.quadrige2.ui.swing.common.model.ProgressionModel;
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       * @throws Exception if any.
70       */
71      public abstract boolean initAction() throws Exception;
72  
73      /**
74       * <p>doAction.</p>
75       *
76       * @throws Exception if any.
77       */
78      public abstract void doAction() throws Exception;
79  
80      /**
81       * <p>doneAction.</p>
82       */
83      public abstract void doneAction();
84  
85      /**
86       * <p>failedAction.</p>
87       *
88       * @param ex a {@link Throwable} object.
89       */
90      public abstract void failedAction(Throwable ex);
91  
92      /**
93       * <p>disposeAction.</p>
94       */
95      public abstract void disposeAction();
96  
97      /**
98       * <p>Constructor for AbstractWorkerAction.</p>
99       *
100      * @param handler a H object.
101      * @param poolable a boolean.
102      */
103     public AbstractWorkerAction(H handler, boolean poolable) {
104 
105         super();
106         this.handler = handler;
107 
108         executor = ActionFactory.createThreadExecutor(poolable);
109     }
110 
111     /**
112      * <p>Getter for the field <code>handler</code>.</p>
113      *
114      * @return a H object.
115      */
116     public H getHandler() {
117         return handler;
118     }
119 
120     /**
121      * <p>getUI.</p>
122      *
123      * @return a UI object.
124      */
125     public UI getUI() {
126         return handler.getUI();
127     }
128 
129     /**
130      * <p>getModel.</p>
131      *
132      * @return a M object.
133      */
134     public M getModel() {
135         return handler.getModel();
136     }
137 
138     /**
139      * <p>getConfig.</p>
140      *
141      * @return a {@link Quadrige2CoreConfiguration} object.
142      */
143     public Quadrige2CoreConfiguration getConfig() {
144         return handler.getConfig();
145     }
146 
147     /**
148      * <p>getContext.</p>
149      *
150      * @return a {@link ApplicationUIContext} object.
151      */
152     public ApplicationUIContext getContext() {
153         return handler.getContext();
154     }
155 
156     /** {@inheritDoc} */
157     @Override
158     public boolean isEnabled() {
159         return super.isEnabled() && (worker == null || worker.isDone());
160     }
161 
162     /**
163      * <p>getActionDescription.</p>
164      *
165      * @return a {@link String} object.
166      */
167     public String getActionDescription() {
168         return (String) getValue(SHORT_DESCRIPTION);
169     }
170 
171     /**
172      * <p>setActionDescription.</p>
173      *
174      * @param actionDescription a {@link String} object.
175      */
176     public void setActionDescription(String actionDescription) {
177         putValue(SHORT_DESCRIPTION, actionDescription);
178     }
179 
180     /**
181      * <p>setActionIcon.</p>
182      *
183      * @param actionIcon a {@link Icon} object.
184      */
185     public void setActionIcon(Icon actionIcon) {
186         putValue(SMALL_ICON, actionIcon);
187         putValue(LARGE_ICON_KEY, actionIcon);
188     }
189 
190     /**
191      * <p>setActionKey.</p>
192      *
193      * @param actionKey a {@link String} object.
194      */
195     public void setActionKey(String actionKey) {
196         putValue(ACTION_COMMAND_KEY, actionKey);
197     }
198 
199     /**
200      * <p>setActionName.</p>
201      *
202      * @param actionName a {@link String} object.
203      */
204     public void setActionName(String actionName) {
205         putValue(NAME, actionName);
206     }
207 
208     /**
209      * <p>setActionMnemonic.</p>
210      *
211      * @param key a int.
212      */
213     public void setActionMnemonic(int key) {
214         putValue(MNEMONIC_KEY, key);
215     }
216 
217     /**
218      * <p>execute.</p>
219      */
220     public void execute() {
221         run();
222     }
223 
224     /**
225      * <p>executeAndWait.</p>
226      *
227      * @throws Exception if any.
228      */
229     public void executeAndWait() throws Exception {
230         wait = true;
231         run();
232         lock();
233         if (reportedException != null) {
234             throw reportedException;
235         }
236     }
237 
238     private void run() {
239 
240         boolean doWork = false;
241 
242         try {
243             doWork = initAction();
244         } catch (Exception ex) {
245             disposeAction();
246             unlock();
247             throw new Quadrige2TechnicalException(I18n.t("quadrige2.error.prepare.action", getActionDescription()), ex);
248         }
249 
250         if (doWork) {
251 
252             try {
253 
254                 // Threaded action
255                 worker = new Worker();
256                 executor.execute(worker);
257 
258             } catch (Exception ex) {
259                 disposeAction();
260                 throw new Quadrige2TechnicalException(I18n.t("quadrige2.error.execute.action", getActionDescription()), ex);
261             }
262 
263         }
264         else {
265 
266             disposeAction();
267             unlock();
268 
269         }
270     }
271 
272     /** {@inheritDoc} */
273     @Override
274     public void actionPerformed(ActionEvent e) {
275 
276         // call default run
277         run();
278 
279     }
280 
281     /**
282      * <p>lock.</p>
283      */
284     protected void lock() {
285         if (wait) {
286             synchronized (lock) {
287                 try {
288                     lock.wait();
289                 } catch (InterruptedException ex) {
290                     throw new Quadrige2TechnicalException(I18n.t("quadrige2.error.execute.wait.action", getActionDescription()), ex);
291                 } finally {
292                     wait = false;
293                 }
294             }
295         }
296     }
297 
298     /**
299      * <p>unlock.</p>
300      */
301     protected void unlock() {
302         if (wait) {
303             synchronized (lock) {
304                 lock.notifyAll();
305             }
306         }
307     }
308 
309     /**
310      * <p>isWait.</p>
311      *
312      * @return a boolean.
313      */
314     public boolean isWait() {
315         return wait;
316     }
317 
318     /**
319      * <p>Setter for the field <code>reportedException</code>.</p>
320      *
321      * @param reportedException a {@link Exception} object.
322      */
323     public void setReportedException(Exception reportedException) {
324         this.reportedException = reportedException;
325     }
326 
327     private class Worker<A extends AbstractWorkerAction> extends SwingWorker<Void, ProgressionModel> {
328 
329         public Worker() {
330         }
331 
332         @Override
333         protected Void doInBackground() throws Exception {
334 
335             doAction();
336 
337             return null;
338         }
339 
340         @Override
341         protected void done() {
342 
343             try {
344                 try {
345                     get();
346                     doneAction();
347                 } catch (InterruptedException ex) {
348                     if (wait) {
349                         throw new Quadrige2TechnicalException(ex);
350                     }
351                     else {
352                         failedAction(ex);
353                     }
354                 } catch (ExecutionException ex) {
355                     Throwable cause = ex.getCause();
356                     if (wait) {
357                         if (cause != null && cause instanceof Exception) {
358                             setReportedException((Exception) cause);
359                         }
360                         else {
361                             setReportedException(ex);
362                         }
363                     }
364                     else {
365                         failedAction(cause);
366                     }
367                 }
368             } finally {
369                 disposeAction();
370                 unlock();
371             }
372 
373         }
374 
375     }
376 
377 }