View Javadoc
1   package fr.ifremer.quadrige3.ui.swing.synchro.action;
2   
3   /*-
4    * #%L
5    * Quadrige3 Core :: Quadrige3 UI Common
6    * $Id:$
7    * $HeadURL:$
8    * %%
9    * Copyright (C) 2017 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.config.QuadrigeConfiguration;
27  import fr.ifremer.quadrige3.core.exception.QuadrigeTechnicalException;
28  import fr.ifremer.quadrige3.core.security.AuthenticationInfo;
29  import fr.ifremer.quadrige3.core.service.ClientServiceLocator;
30  import fr.ifremer.quadrige3.synchro.service.client.SynchroRestClientService;
31  import fr.ifremer.quadrige3.synchro.vo.SynchroImportContextVO;
32  import fr.ifremer.quadrige3.synchro.vo.SynchroProgressionStatus;
33  import fr.ifremer.quadrige3.ui.swing.synchro.SynchroUIContext;
34  import fr.ifremer.quadrige3.ui.swing.synchro.SynchroUIHandler;
35  import org.apache.commons.collections4.CollectionUtils;
36  import org.apache.commons.logging.Log;
37  import org.apache.commons.logging.LogFactory;
38  import org.nuiton.i18n.I18n;
39  
40  import java.util.Date;
41  import java.util.Objects;
42  import java.util.Set;
43  import java.util.function.Consumer;
44  import java.util.stream.Collectors;
45  
46  import static org.nuiton.i18n.I18n.t;
47  
48  /**
49   * <p>ImportSynchroCheckAction class.</p>
50   *
51   * @author Ludovic Pecquot <ludovic.pecquot@e-is.pro>
52   */
53  public class ImportSynchroCheckAction extends AbstractSynchroAction {
54  
55      private static final Log log = LogFactory.getLog(ImportSynchroCheckAction.class);
56  
57      private boolean checkReferentialOnly = false;
58      private boolean askConfirmationBeforeStart = true;
59      private boolean silentIfUpdate = false;
60      private boolean silentIfNoUpdate = false;
61      private boolean useOptimisticCheck = false;
62      private Consumer<SynchroUIContext> consumer;
63  
64      /**
65       * <p>Constructor for ImportSynchroCheckAction.</p>
66       *
67       * @param handler a {@link SynchroUIHandler} object.
68       */
69      public ImportSynchroCheckAction(SynchroUIHandler handler) {
70          super(handler);
71      }
72  
73      /**
74       * <p>isCheckReferentialOnly.</p>
75       *
76       * @return a boolean.
77       */
78      public boolean isCheckReferentialOnly() {
79          return checkReferentialOnly;
80      }
81  
82      /**
83       * <p>Setter for the field <code>checkReferentialOnly</code>.</p>
84       *
85       * @param checkReferentialOnly a boolean.
86       */
87      public void setCheckReferentialOnly(boolean checkReferentialOnly) {
88          this.checkReferentialOnly = checkReferentialOnly;
89          if (checkReferentialOnly) {
90              getModel().setImportReferential(true);
91              getModel().setImportData(false);
92          }
93      }
94  
95      /**
96       * <p>Setter for the field <code>askConfirmationBeforeStart</code>.</p>
97       *
98       * @param askConfirmationBeforeStart a boolean.
99       */
100     public void setAskConfirmationBeforeStart(boolean askConfirmationBeforeStart) {
101         this.askConfirmationBeforeStart = askConfirmationBeforeStart;
102     }
103 
104     /**
105      * <p>Setter for the field <code>silentIfNoUpdate</code>.</p>
106      *
107      * @param silentIfNoUpdate a boolean.
108      */
109     public void setSilentIfNoUpdate(boolean silentIfNoUpdate) {
110         this.silentIfNoUpdate = silentIfNoUpdate;
111     }
112 
113     public void setSilentIfUpdate(boolean silentIfUpdate) {
114         this.silentIfUpdate = silentIfUpdate;
115     }
116 
117     public void setConsumer(Consumer<SynchroUIContext> consumer) {
118         this.consumer = consumer;
119     }
120 
121     public void setUseOptimisticCheck(boolean useOptimisticCheck) {
122         this.useOptimisticCheck = useOptimisticCheck;
123     }
124 
125     public boolean isConsumerSet() {
126         return consumer != null;
127     }
128 
129     /** {@inheritDoc} */
130     @Override
131     public boolean initAction() {
132         super.initAction();
133 
134         getModel().getProgressionModel().clear();
135         getHandler().showProgressCard();
136 
137         return true;
138     }
139 
140     /** {@inheritDoc} */
141     @Override
142     public void doAction() {
143 
144         if (log.isDebugEnabled()) {
145             log.debug(String.format("try to connect to synchronization site : %s", getBaseUri()));
146         }
147 
148         getModel().getProgressionModel().setMessage(t("quadrige3.synchro.progress.check"));
149         getModel().getProgressionModel().setIndeterminate(true);
150         getModel().setStatus(SynchroProgressionStatus.RUNNING);
151         getModel().saveImportContext();
152         // reload context to update data update date if data date range has been filled
153         getModel().loadImportContext();
154 
155         SynchroRestClientService service = ClientServiceLocator.instance().getSynchroRestClientService();
156 
157         // Get programs to sync for referential
158         if (getContext().getAuthenticationInfo() != null) {
159             // Get program codes allowed by synchro server
160             Set<String> allowedProgramCodes = service.getCompatibleProgramCodes(getContext().getAuthenticationInfo());
161             // Update the local configuration for next import
162             getConfig().setSynchroProgramCodeIncludes(allowedProgramCodes);
163             getConfig().save();
164         }
165         // Reload and use it
166         Set<String> allowedProgramCodes = getConfig().getSynchroProgramCodeIncludes();
167         if (CollectionUtils.isEmpty(allowedProgramCodes)) {
168             // Try with the actual programs in db
169             allowedProgramCodes.addAll(ClientServiceLocator.instance().getProgramService().getAllProgramCodes());
170         }
171 
172         if (!checkReferentialOnly) {
173 
174             Set<String> dataProgramCodes = getContext().getSynchroContext().getImportDataProgramCodes();
175             if (dataProgramCodes != null) {
176                 dataProgramCodes = dataProgramCodes.stream().filter(Objects::nonNull).collect(Collectors.toSet());
177             }
178             if (CollectionUtils.isNotEmpty(dataProgramCodes)) {
179                 // Add new data program (missing program on local DB)
180                 allowedProgramCodes.addAll(dataProgramCodes);
181             }
182 
183         }
184         if (CollectionUtils.isEmpty(allowedProgramCodes)) {
185             // Force null if empty
186             allowedProgramCodes = null;
187         }
188         getContext().getSynchroContext().setImportReferentialProgramCodes(allowedProgramCodes);
189 
190 
191         // No authentication (=Anonymous) if user is not authenticate or as local user (mantis #26658)
192         AuthenticationInfo authenticationInfo = !getContext().isAuthenticated() || getContext().isAuthenticatedAsLocalUser()
193             ? null
194             : getContext().getAuthenticationInfo();
195 
196 
197         if (getModel().isImportReferential() && !getModel().isImportData() && useOptimisticCheck) {
198             // Try to get the current referential update date (Mantis #58999)
199             Date dbDate = getContext().getSynchroContext().getImportReferentialUpdateDate();
200             Date serverDate = service.getReferentialUpdateDate(authenticationInfo);
201             int offset = QuadrigeConfiguration.getInstance().getImportReferentialUpdateDateOffsetInSecond();
202             if ((serverDate.getTime() - dbDate.getTime()) / 1000 < offset) {
203                 log.debug(String.format("The update date between local db and server is less than the offset (%s seconds), optimistic behavior: skip update.", offset));
204                 getContext().getSynchroContext().setImportReferential(false);
205                 return;
206             }
207         }
208 
209         try {
210 
211             // Check referential context
212             SynchroImportContextVO updatedImportContext = service.checkImportContext(
213                     authenticationInfo,
214                     getModel().getSynchroImportContext());
215 
216             // Check data context
217             if (getModel().isImportData()) {
218                 updatedImportContext = service.checkImportDataContext(
219                         authenticationInfo,
220                         updatedImportContext
221                 );
222             }
223 
224             // Update the import context in model 
225             getModel().setSynchroImportContext(updatedImportContext);
226 
227         } catch (QuadrigeTechnicalException e) {
228             throw new SynchroException(I18n.t("quadrige3.error.synchro.connect", e.getMessage()), e);
229         }
230 
231     }
232 
233     /** {@inheritDoc} */
234     @Override
235     public void doneAction() {
236 
237         getModel().saveImportContext();
238 
239         // If a consumer is present, consume this action and quit
240         if (isConsumerSet()) {
241             consumer.accept(getModel());
242             return;
243         }
244 
245         // if action launched with lock, don't chain action
246         if (isWait()) {
247             return;
248         }
249 
250         // if action launched for check only, don't chain action but show confirmation message
251         if (isCheckReferentialOnly()) {
252             // if updates available
253             if (getModel().isImportReferential()) {
254 
255                 // Wait user confirmation before starting import
256                 if (askConfirmationBeforeStart) {
257                     // reset synchro status (enable menus)
258                     getModel().setStatus(SynchroProgressionStatus.NOT_STARTED);
259                     getHandler().showValidStartPopup();
260                     return;
261                 }
262             }
263 
264             // No updates available: update UI and exit
265             else {
266                 if (silentIfNoUpdate) {
267                     getHandler().report(t("quadrige3.synchro.report.idle"), false);
268                 } else {
269                     getHandler().report(t("quadrige3.synchro.report.noReferentialUpdate"));
270                 }
271                 getModel().resetImportContext();
272                 // save context with data date range
273                 getModel().saveImportContext(true, true);
274                 return;
275             }
276         }
277 
278         // read result
279         if (getModel().isImportReferential() || getModel().isImportData()) {
280 
281             // if silent report, no action after this point
282             if (silentIfUpdate) {
283                 return;
284             }
285 
286             // check photo estimated count
287             if (getModel().isImportData() && getModel().isImportPhoto()) {
288                 int threshold = getConfig().getSynchroPhotoMaxNumberThreshold();
289                 if (threshold > 0 && getModel().getImportEstimatedPhotoCount() > threshold) {
290                     getModel().setStatus(SynchroProgressionStatus.NOT_STARTED);
291                     getHandler().showValidStartPopup(t("quadrige3.synchro.import.photo.overThreshold.message", getModel().getImportEstimatedPhotoCount()));
292                     return;
293                 }
294             }
295 
296             // start import
297             getHandler().showPopup();
298             ImportSynchroStartAction action = getContext().getActionFactory().createNonBlockingUIAction(handler, ImportSynchroStartAction.class);
299             action.execute();
300         } else {
301 
302             // if silent report, no action after this point
303             if (silentIfNoUpdate) {
304                 return;
305             }
306 
307             getHandler().report(t("quadrige3.error.synchro.import.noData"));
308         }
309 
310     }
311 
312     @Override
313     public void failedAction(Throwable ex) {
314         super.failedAction(ex);
315 
316         if (isConsumerSet()) {
317             getHandler().hidePopup();
318             consumer.accept(getModel());
319         }
320     }
321 
322     /** {@inheritDoc} */
323     @Override
324     public void disposeAction() {
325         super.disposeAction();
326         checkReferentialOnly = false;
327         askConfirmationBeforeStart = true;
328         silentIfNoUpdate = false;
329         silentIfUpdate = false;
330         consumer = null;
331     }
332 
333 }