1 package fr.ifremer.dali.ui.swing.action;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 import com.google.common.collect.Multimap;
27 import fr.ifremer.common.synchro.service.SynchroResult;
28 import fr.ifremer.dali.dto.DaliBeans;
29 import fr.ifremer.dali.dto.configuration.programStrategy.ProgramDTO;
30 import fr.ifremer.dali.service.DaliServiceLocator;
31 import fr.ifremer.dali.service.StatusFilter;
32 import fr.ifremer.dali.ui.swing.DaliUIContext;
33 import fr.ifremer.dali.ui.swing.content.DaliMainUIHandler;
34 import fr.ifremer.dali.ui.swing.content.synchro.program.ProgramSelectUI;
35 import fr.ifremer.dali.ui.swing.util.DaliUIs;
36 import fr.ifremer.quadrige3.core.dao.technical.ZipUtils;
37 import fr.ifremer.quadrige3.core.exception.QuadrigeTechnicalException;
38 import fr.ifremer.quadrige3.core.security.SecurityContextHelper;
39 import fr.ifremer.quadrige3.core.service.http.HttpNotFoundException;
40 import fr.ifremer.quadrige3.synchro.meta.data.DataSynchroTables;
41 import fr.ifremer.quadrige3.synchro.service.client.SynchroClientService;
42 import fr.ifremer.quadrige3.synchro.service.client.SynchroRejectedRowResolver;
43 import fr.ifremer.quadrige3.synchro.service.client.SynchroRestClientService;
44 import fr.ifremer.quadrige3.synchro.service.client.vo.SynchroClientExportResult;
45 import fr.ifremer.quadrige3.synchro.service.data.DataSynchroContext;
46 import fr.ifremer.quadrige3.synchro.vo.SynchroProgressionStatus;
47 import fr.ifremer.quadrige3.ui.swing.ApplicationUI;
48 import fr.ifremer.quadrige3.ui.swing.action.AbstractReloadCurrentScreenAction;
49 import fr.ifremer.quadrige3.ui.swing.model.ProgressionUIModel;
50 import fr.ifremer.quadrige3.ui.swing.synchro.SynchroDirection;
51 import fr.ifremer.quadrige3.ui.swing.synchro.SynchroUIContext;
52 import fr.ifremer.quadrige3.ui.swing.synchro.SynchroUIHandler;
53 import fr.ifremer.quadrige3.ui.swing.synchro.action.*;
54 import fr.ifremer.quadrige3.ui.swing.synchro.resolver.SynchroRejectedRowUIResolver;
55 import org.apache.commons.collections4.CollectionUtils;
56 import org.apache.commons.logging.Log;
57 import org.apache.commons.logging.LogFactory;
58 import org.nuiton.jaxx.application.ApplicationIOUtil;
59
60 import javax.swing.JOptionPane;
61 import java.awt.Dimension;
62 import java.io.File;
63 import java.util.LinkedHashSet;
64 import java.util.List;
65 import java.util.Set;
66 import java.util.stream.Collectors;
67
68 import static org.nuiton.i18n.I18n.t;
69
70
71
72
73
74
75 public class ExportSynchroAction extends AbstractReloadCurrentScreenAction {
76
77 private static final Log log = LogFactory.getLog(ExportSynchroAction.class);
78
79 private Set<String> programCodes;
80 private int userId;
81 private File dbDirToExport;
82 private boolean serverJobRunning = false;
83 private boolean serverFailed = false;
84 private boolean hasData = false;
85
86
87
88
89
90
91 public ExportSynchroAction(DaliMainUIHandler handler) {
92 super(handler, true);
93 setActionDescription(t("dali.action.synchro.export.title"));
94 }
95
96 @Override
97 public DaliUIContext getContext() {
98 return (DaliUIContext) super.getContext();
99 }
100
101 private SynchroUIContext getSynchroUIContext() {
102 return getContext().getSynchroContext();
103 }
104
105 private SynchroUIHandler getSynchroHandler() {
106 return getContext().getSynchroHandler();
107 }
108
109
110
111
112 @Override
113 public boolean prepareAction() throws Exception {
114 super.prepareAction();
115
116 if (!getContext().isSynchroEnabled() && getConfig().isSynchronizationUsingServer()) {
117 getContext().getDialogHelper().showWarningDialog(t("dali.synchro.unavailable"), t("dali.action.synchro.export.title"));
118 return false;
119 }
120
121
122 if (getContext().isAuthenticatedAsLocalUser()
123 || !getContext().getProgramStrategyService().hasRemoteAccessRightOnProgram(getContext().getAuthenticationInfo())) {
124 getContext().getDialogHelper().showWarningDialog(t("dali.synchro.export.accessDenied"), t("dali.action.synchro.export.title"));
125 return false;
126 }
127
128
129 getSynchroUIContext().setDirection(SynchroDirection.EXPORT);
130 getSynchroUIContext().loadExportContext();
131 userId = SecurityContextHelper.getQuadrigeUserId();
132
133
134 programCodes = getSynchroUIContext().getExportDataProgramCodes();
135
136
137 ProgramSelectUI programSelectUI = new ProgramSelectUI((ApplicationUI) getUI(), StatusFilter.ACTIVE, programCodes, false, false);
138
139
140 programSelectUI.getModel().setEnablePhoto(true);
141
142 handler.openDialog(programSelectUI, t("dali.action.synchro.export.dataProgramCodes.title"), new Dimension(800, 400));
143
144 List<ProgramDTO> programs = programSelectUI.getModel().getSelectedPrograms();
145
146
147 if (CollectionUtils.isEmpty(programs)) {
148 return false;
149 }
150
151
152 programCodes = new LinkedHashSet<>(DaliBeans.collectProperties(programs, ProgramDTO.PROPERTY_CODE));
153
154
155 getSynchroUIContext().setExportPhoto(programSelectUI.getModel().isEnablePhoto());
156
157 return true;
158 }
159
160
161
162
163 @Override
164 public void doActionBeforeReload() throws Exception {
165 dbDirToExport = null;
166 serverJobRunning = false;
167 serverFailed = false;
168 hasData = false;
169
170 SynchroClientService synchroService = DaliServiceLocator.instance().getSynchroClientService();
171
172 getSynchroUIContext().setExportDataProgramCodes(programCodes);
173 getSynchroUIContext().saveExportContext();
174
175 createProgressionUIModel(100);
176 getSynchroUIContext().setStatus(SynchroProgressionStatus.RUNNING);
177 getSynchroUIContext().saveExportContext();
178
179
180 getProgressionUIModel().setMessage(t("quadrige3.synchro.progress.export"));
181
182 if (!getConfig().isSynchronizationUsingServer()) {
183
184 SynchroRejectedRowResolver rejectResolver = new SynchroRejectedRowUIResolver(getContext().getDialogHelper(), false );
185
186
187 SynchroClientExportResult exportResult = synchroService.exportToServerDatabase(userId,
188 programCodes,
189 rejectResolver,
190 getProgressionUIModel(),
191 100);
192 hasData = exportResult.getDataResult().getTotalTreated() > 0;
193 if (!hasData) {
194 showNoDataMessage();
195 }
196
197 } else {
198
199
200 doImportReferential();
201
202
203 if (!checkWriteAccess()) return;
204
205
206 SynchroClientExportResult exportResult = synchroService.exportDataToTempDb(userId,
207 programCodes,
208 getSynchroUIContext().isExportPhoto(),
209 getProgressionUIModel(),
210 100);
211 DataSynchroContext context = exportResult.getDataContext();
212
213
214 hasData = context.getResult().getTotalTreated() > 0;
215 if (!hasData) {
216 showNoDataMessage();
217 return;
218 }
219
220
221 int nbPhoto = context.getResult().getNbRows(DataSynchroTables.PHOTO.name());
222 int threshold = getConfig().getSynchroPhotoMaxNumberThreshold();
223 if (threshold > 0 && nbPhoto > threshold) {
224 if (getContext().getDialogHelper().showConfirmDialog(
225 t("quadrige3.synchro.export.photo.overThreshold.message", nbPhoto),
226 t("dali.action.synchro.export.title"),
227 JOptionPane.YES_NO_OPTION) == JOptionPane.NO_OPTION)
228 return;
229 }
230
231
232 dbDirToExport = exportResult.getTempDbExportDirectory();
233 getProgressionUIModel().setMessage(t("quadrige3.synchro.progress.compress"));
234 String filename = dbDirToExport.getName() + ".zip";
235 File zipFile = new File(dbDirToExport.getParent(), filename);
236 ZipUtils.compressFilesInPath(dbDirToExport.toPath(), zipFile.toPath(), getProgressionUIModel(), false, true);
237
238 getSynchroUIContext().setExportFileName(zipFile.getName());
239 getSynchroUIContext().saveExportContext();
240
241
242 getProgressionUIModel().setTotal(100);
243 setProgressionUIModel(getSynchroUIContext().getProgressionModel());
244
245
246 ExportSynchroUploadAction uploadAction = getContext().getActionFactory().createNonBlockingUIAction(getContext().getSynchroHandler(),
247 ExportSynchroUploadAction.class);
248 uploadAction.executeAndWait();
249
250 boolean needDownloadResultAndFinish = false;
251 int retryCounter = 0;
252 while (true) {
253 try {
254
255 if (!serverJobRunning) {
256 getProgressionUIModel().clear();
257 ExportSynchroStartAction startAction = getContext().getActionFactory().createNonBlockingUIAction(getContext().getSynchroHandler(),
258 ExportSynchroStartAction.class);
259 startAction.executeAndWait();
260 serverJobRunning = true;
261
262 Thread.sleep(getConfig().getSynchronizationRefreshTimeout());
263 }
264
265
266 if (serverJobRunning) {
267 if (getSynchroUIContext().isRunningStatus()) {
268 try {
269 ExportSynchroGetStatusAction getStatusAction = getContext().getActionFactory().createNonBlockingUIAction(getContext().getSynchroHandler(),
270 ExportSynchroGetStatusAction.class);
271 getStatusAction.executeAndWait();
272 needDownloadResultAndFinish = true;
273 } catch (HttpNotFoundException e) {
274
275 getSynchroUIContext().setStatus(SynchroProgressionStatus.SUCCESS);
276 needDownloadResultAndFinish = true;
277 }
278
279
280 catch (SynchroException se) {
281 serverJobRunning = false;
282 serverFailed = true;
283 throw se;
284
285 }
286 } else {
287 needDownloadResultAndFinish = true;
288 }
289 }
290
291
292 if (needDownloadResultAndFinish) {
293 try {
294 finishExportThenCleanFiles(exportResult, getProgressionUIModel());
295 break;
296 }
297
298
299 catch (HttpNotFoundException e) {
300 getSynchroUIContext().setStatus(SynchroProgressionStatus.FAILED);
301 serverFailed = true;
302 throw new SynchroException(t("dali.action.synchro.export.failed.serverJobStarted.log", e.getMessage()), e);
303 }
304 }
305 } catch (QuadrigeTechnicalException e) {
306 retryCounter++;
307 log.debug(String.format("Error during export: %s", e.getMessage()), e);
308 getProgressionUIModel().setMessage(t("dali.action.synchro.export.retry.progression", retryCounter, getConfig().getSynchronizationMaxRetryCount()));
309
310
311 if (retryCounter < getConfig().getSynchronizationMaxRetryCount()) {
312 Thread.sleep(getConfig().getSynchronizationRetryTimeout());
313 } else {
314 int result = getContext().getDialogHelper().showOptionDialog(null,
315 serverJobRunning
316 ? t("dali.action.synchro.export.retry.ask.serverJobStarted",
317 getConfig().getAdminEmail())
318 : t("dali.action.synchro.export.retry.ask"),
319 t("dali.action.synchro.export.retry.title"), JOptionPane.ERROR_MESSAGE, JOptionPane.YES_NO_OPTION);
320
321
322 if (result == JOptionPane.NO_OPTION) throw e;
323
324
325 retryCounter = 0;
326 needDownloadResultAndFinish = true;
327 }
328 }
329 }
330
331 }
332
333
334 setSkipScreenReload(!hasData);
335 }
336
337
338
339
340 @Override
341 public void postSuccessAction() {
342
343 if (serverFailed) {
344 postFailedAction(null);
345 return;
346 }
347
348 super.postSuccessAction();
349 if (log.isInfoEnabled()) {
350 log.info("Synchronization export success");
351 }
352
353 getSynchroUIContext().resetExportContext();
354 getSynchroUIContext().saveExportContext();
355
356
357 if (!hasData) {
358 getContext().getSynchroHandler().report(t("dali.action.synchro.export.noData"), false);
359 } else {
360 getContext().getSynchroHandler().report(t("dali.action.synchro.export.success"));
361 }
362
363 }
364
365
366
367
368 @Override
369 public void postFailedAction(Throwable error) {
370 super.postFailedAction(error);
371
372 if (error != null) {
373 log.error(t("dali.action.synchro.export.failed.log", error.getMessage()));
374 } else if (getSynchroUIContext().getProgressionModel() != null
375 && getSynchroUIContext().getProgressionModel().getMessage() != null) {
376
377 String serverMessage = getSynchroUIContext().getProgressionModel().getMessage();
378 log.error(t("dali.action.synchro.export.failed.server.log", serverMessage));
379 } else {
380 log.error(t("dali.action.synchro.export.failed"));
381 }
382
383 getSynchroUIContext().resetExportContext();
384 getSynchroUIContext().saveExportContext();
385
386 if (serverJobRunning) {
387
388 getContext().getSynchroHandler().report(t("dali.action.synchro.export.failed.serverJobStarted",
389 getConfig().getAdminEmail()));
390
391
392 DaliServiceLocator.instance().getSynchroHistoryService().saveExportError(
393 userId,
394 getSynchroUIContext().getSynchroExportContext(),
395 t("dali.action.synchro.export.failed.serverJobStarted.history",
396 error != null ? error.getMessage() : ""));
397 } else {
398 getContext().getSynchroHandler().report(t("dali.action.synchro.export.failed"));
399 }
400 }
401
402
403
404 private void finishExportThenCleanFiles(SynchroClientExportResult exportResult,
405 ProgressionUIModel mainProgressionModel) throws Exception {
406
407
408 SynchroProgressionStatus serverStatus = getSynchroUIContext().getStatus();
409 serverFailed = serverStatus != SynchroProgressionStatus.SUCCESS;
410 String serverErrorMessage = serverFailed ? getSynchroUIContext().getProgressionModel().getMessage() : null;
411
412
413 boolean hasRejectMessages = false;
414 SynchroRestClientService restService = DaliServiceLocator.instance().getSynchroRestClientService();
415 SynchroResult serverResult = restService.downloadExportResult(
416 getContext().getAuthenticationInfo(),
417 getModel().getSynchroContext().getSynchroExportContext(),
418 getProgressionUIModel());
419 boolean resultFileExists = serverResult != null;
420
421
422 setProgressionUIModel(mainProgressionModel);
423
424
425 if (resultFileExists) {
426 exportResult.setServerResult(serverResult);
427
428 mainProgressionModel.setMessage(t("quadrige3.synchro.progress.finishExport"));
429
430 SynchroRejectedRowResolver rejectResolver = new SynchroRejectedRowUIResolver(getContext().getDialogHelper(), false );
431 SynchroClientService synchroService = DaliServiceLocator.instance().getSynchroClientService();
432
433 try {
434 hasRejectMessages = synchroService.finishExportData(userId, exportResult, rejectResolver, serverFailed, false );
435 } catch (QuadrigeTechnicalException e) {
436
437 throw new SynchroException(t("quadrige3.error.synchro.export.finish"), e);
438 }
439
440
441 DataSynchroContext context = exportResult.getDataContext();
442 Multimap<String, String> pksToRevert = context.getResult().getSourceMissingReverts();
443 revertPks(pksToRevert);
444 }
445
446
447 cleanFiles();
448
449
450 if (serverFailed) {
451
452
453 getSynchroUIContext().getProgressionModel().setMessage(serverErrorMessage);
454 getSynchroUIContext().setStatus(serverStatus);
455
456 if (!resultFileExists || !hasRejectMessages) {
457
458 throw new SynchroException(t("quadrige3.error.synchro.status", serverErrorMessage));
459 }
460 }
461 }
462
463 private void revertPks(Multimap<String, String> pksToRevert) throws Exception {
464 if (pksToRevert == null || pksToRevert.isEmpty()) {
465 return;
466 }
467 getSynchroUIContext().resetImportContext();
468 getSynchroUIContext().setImportData(true);
469 getSynchroUIContext().setImportReferential(false);
470
471 getSynchroUIContext().setImportPhoto(true);
472 getSynchroUIContext().setImportDataPkIncludes(pksToRevert);
473 getSynchroUIContext().setDirection(SynchroDirection.IMPORT);
474 getSynchroUIContext().setImportDataForceEditedRowOverride(true);
475
476
477 ImportSynchroStartAction reimportAction = getContext().getActionFactory().createNonBlockingUIAction(getContext().getSynchroHandler(),
478 ImportSynchroStartAction.class);
479
480
481 reimportAction.executeAndWait();
482
483
484 ImportSynchroDownloadAction action = getContext().getActionFactory().createNonBlockingUIAction(getContext().getSynchroHandler(),
485 ImportSynchroDownloadAction.class);
486
487
488 action.executeAndWait();
489
490
491 ImportSynchroApplyAction applyImportAction = getContext().getActionFactory().createLogicAction(getHandler(), ImportSynchroApplyAction.class);
492 applyImportAction.prepareAction();
493 applyImportAction.setSilent(true);
494 getContext().getActionEngine().runInternalAction(applyImportAction);
495 }
496
497 private void cleanFiles() throws Exception {
498
499
500 ApplicationIOUtil.forceDeleteOnExit(dbDirToExport, t("quadrige3.error.delete.directory", dbDirToExport.getAbsolutePath()));
501 File fileToDelete = new File(dbDirToExport.getParent(), dbDirToExport.getName() + ".zip");
502 ApplicationIOUtil.deleteFile(fileToDelete, t("quadrige3.error.delete.directory", fileToDelete.getAbsolutePath()));
503
504 fileToDelete = new File(dbDirToExport.getParent(), dbDirToExport.getName() + ".json");
505 if (fileToDelete.exists()) {
506 ApplicationIOUtil.deleteFile(fileToDelete, t("quadrige3.error.delete.directory", fileToDelete.getAbsolutePath()));
507 }
508
509
510 ExportSynchroAckAction ackAction = getContext().getActionFactory().createNonBlockingUIAction(getSynchroHandler(),
511 ExportSynchroAckAction.class);
512 ackAction.executeAndWait();
513 }
514
515 private void showNoDataMessage() {
516 getContext().getDialogHelper().showMessageDialog(t("dali.action.synchro.export.noData"),
517 t("dali.action.synchro.export.result.title"));
518 }
519
520
521
522
523 private void doImportReferential() {
524
525 ImportReferentialSynchroAtOnceAction importAction = getContext().getActionFactory().createLogicAction(getHandler(), ImportReferentialSynchroAtOnceAction.class);
526 getContext().getActionEngine().runInternalAction(importAction);
527
528
529 getSynchroUIContext().setStatus(SynchroProgressionStatus.RUNNING);
530 }
531
532 private boolean checkWriteAccess() {
533
534 List<ProgramDTO> programs = getContext().getProgramStrategyService().getRemoteProgramsByUser(getContext().getAuthenticationInfo());
535 Set<String> allowedProgramCodes = programs.stream().map(ProgramDTO::getCode).collect(Collectors.toSet());
536 if (CollectionUtils.isEmpty(programs) || !allowedProgramCodes.containsAll(programCodes)) {
537 getContext().getDialogHelper().showWarningDialog(
538 t("dali.synchro.export.accessDenied.program.topMessage"),
539 DaliUIs.getHtmlString(CollectionUtils.removeAll(programCodes, allowedProgramCodes)),
540 t("dali.synchro.export.accessDenied.program.bottomMessage"),
541 t("dali.action.synchro.export.title")
542 );
543 return false;
544 }
545 return true;
546 }
547 }