1 package fr.ifremer.quadrige3.synchro.service.client;
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.*;
27 import fr.ifremer.common.synchro.config.SynchroConfiguration;
28 import fr.ifremer.common.synchro.dao.DataIntegrityViolationOnDeleteException;
29 import fr.ifremer.common.synchro.meta.SynchroTableMetadata;
30 import fr.ifremer.common.synchro.service.RejectedRow;
31 import fr.ifremer.common.synchro.service.SynchroDatabaseConfiguration;
32 import fr.ifremer.common.synchro.service.SynchroResult;
33 import fr.ifremer.common.synchro.type.ProgressionModel;
34 import fr.ifremer.quadrige3.core.ProgressionCoreModel;
35 import fr.ifremer.quadrige3.core.config.QuadrigeConfiguration;
36 import fr.ifremer.quadrige3.core.dao.administration.user.PrivilegeCode;
37 import fr.ifremer.quadrige3.core.dao.data.survey.SurveyExtendDao;
38 import fr.ifremer.quadrige3.core.dao.referential.ReferentialJdbcDao;
39 import fr.ifremer.quadrige3.core.dao.referential.ReferentialJdbcDaoImpl;
40 import fr.ifremer.quadrige3.core.dao.referential.StatusCode;
41 import fr.ifremer.quadrige3.core.dao.technical.*;
42 import fr.ifremer.quadrige3.core.dao.technical.decorator.Decorator;
43 import fr.ifremer.quadrige3.core.exception.DatabaseSchemaUpdateException;
44 import fr.ifremer.quadrige3.core.exception.QuadrigeBusinessException;
45 import fr.ifremer.quadrige3.core.exception.QuadrigeTechnicalException;
46 import fr.ifremer.quadrige3.core.service.administration.user.UserService;
47 import fr.ifremer.quadrige3.core.service.decorator.DecoratorService;
48 import fr.ifremer.quadrige3.core.vo.data.survey.LightSurveyVO;
49 import fr.ifremer.quadrige3.synchro.dao.SynchroClientDao;
50 import fr.ifremer.quadrige3.synchro.meta.administration.MoratoriumSynchroTables;
51 import fr.ifremer.quadrige3.synchro.meta.administration.ProgramStrategySynchroTables;
52 import fr.ifremer.quadrige3.synchro.meta.data.DataSynchroTables;
53 import fr.ifremer.quadrige3.synchro.meta.referential.CampaignOccasionSynchroTables;
54 import fr.ifremer.quadrige3.synchro.meta.referential.ReferentialSynchroTables;
55 import fr.ifremer.quadrige3.synchro.meta.system.ContextAndFilterSynchroTables;
56 import fr.ifremer.quadrige3.synchro.meta.system.RuleSynchroTables;
57 import fr.ifremer.quadrige3.synchro.meta.system.TechnicalSynchroTables;
58 import fr.ifremer.quadrige3.synchro.service.SynchroDirection;
59 import fr.ifremer.quadrige3.synchro.service.client.vo.SynchroClientExportResult;
60 import fr.ifremer.quadrige3.synchro.service.client.vo.SynchroClientExportToFileResult;
61 import fr.ifremer.quadrige3.synchro.service.client.vo.SynchroClientImportFromFileResult;
62 import fr.ifremer.quadrige3.synchro.service.client.vo.SynchroClientImportResult;
63 import fr.ifremer.quadrige3.synchro.service.data.DataSynchroContext;
64 import fr.ifremer.quadrige3.synchro.service.data.DataSynchroDatabaseConfiguration;
65 import fr.ifremer.quadrige3.synchro.service.data.DataSynchroService;
66 import fr.ifremer.quadrige3.synchro.service.referential.ReferentialSynchroContext;
67 import fr.ifremer.quadrige3.synchro.service.referential.ReferentialSynchroDatabaseConfiguration;
68 import fr.ifremer.quadrige3.synchro.service.referential.ReferentialSynchroService;
69 import fr.ifremer.quadrige3.synchro.vo.SynchroChangesVO;
70 import fr.ifremer.quadrige3.synchro.vo.SynchroDateOperatorVO;
71 import fr.ifremer.quadrige3.synchro.vo.SynchroImportContextVO;
72 import org.apache.commons.collections4.CollectionUtils;
73 import org.apache.commons.collections4.MapUtils;
74 import org.apache.commons.io.FileUtils;
75 import org.apache.commons.lang3.StringUtils;
76 import org.apache.commons.lang3.time.DateUtils;
77 import org.apache.commons.logging.Log;
78 import org.apache.commons.logging.LogFactory;
79 import org.hibernate.cfg.Environment;
80 import org.hibernate.dialect.Dialect;
81 import org.nuiton.i18n.I18n;
82 import org.springframework.beans.factory.annotation.Autowired;
83 import org.springframework.context.annotation.Lazy;
84 import org.springframework.jdbc.datasource.DataSourceUtils;
85 import org.springframework.stereotype.Service;
86 import org.springframework.transaction.interceptor.TransactionInterceptor;
87
88 import javax.annotation.Resource;
89 import javax.sql.DataSource;
90 import java.io.File;
91 import java.io.IOException;
92 import java.nio.charset.Charset;
93 import java.sql.Connection;
94 import java.sql.SQLException;
95 import java.sql.Timestamp;
96 import java.util.*;
97
98 import static org.nuiton.i18n.I18n.t;
99
100
101
102
103
104
105
106 @Service("synchroClientService")
107 @Lazy
108 public class SynchroClientServiceImpl implements SynchroClientInternalService {
109
110 private static final String TABLE_SURVEY = "SURVEY";
111
112 private static final Log log = LogFactory.getLog(SynchroClientServiceImpl.class);
113
114 @Resource(name = "databaseSchemaDao")
115 private DatabaseSchemaDao dbSchemaDao;
116
117 @Resource(name = "surveyDao")
118 private SurveyExtendDao surveyDao;
119
120 @Resource(name = "synchroClientDao")
121 private SynchroClientDao synchroClientDao;
122
123 @Resource(name = "dataSynchroService")
124 private DataSynchroService dataSynchroService;
125
126 @Resource(name = "userService")
127 private UserService userService;
128
129 @Resource(name = "referentialSynchroService")
130 private ReferentialSynchroService referentialSynchroService;
131
132 @Resource
133 private SynchroHistoryService synchroHistoryService;
134
135 @Resource
136 private DataSource dataSource;
137
138 private final DecoratorService decoratorService;
139
140 @Resource(name = "synchroClientService")
141 private SynchroClientInternalService synchroClientInternalService;
142
143 private final QuadrigeConfiguration config;
144 private final SynchroConfiguration synchroConfig;
145
146
147
148
149
150
151
152
153
154
155
156
157
158 @Autowired
159 public SynchroClientServiceImpl(QuadrigeConfiguration config, SynchroConfiguration synchroConfig, DecoratorService decoratorService) {
160 super();
161 this.config = config;
162 this.synchroConfig = synchroConfig;
163 this.decoratorService = decoratorService;
164 }
165
166
167 @Override
168 public SynchroClientImportResult importFromTempDb(
169 int userId,
170 File dbDirToImport,
171 SynchroImportContextVO importContext,
172 SynchroRejectedRowResolver dataRejectResolver,
173 ProgressionCoreModel progressionModel,
174 int progressionModelMaxCount) {
175
176 SynchroClientImportResult result = null;
177 try {
178 result = synchroClientInternalService.importFromTempDbTransactional(
179 userId,
180 dbDirToImport,
181 importContext,
182 dataRejectResolver,
183 progressionModel,
184 progressionModelMaxCount);
185
186 } catch (DataIntegrityViolationOnDeleteException deleteException) {
187
188
189 SynchroDatabaseConfiguration target = new SynchroDatabaseConfiguration(QuadrigeConfiguration.getInstance().getConnectionProperties(),
190 true);
191 handleDeleteException(deleteException, target);
192 }
193
194 return result;
195 }
196
197
198 @Override
199 public SynchroClientImportResult importFromTempDbTransactional(
200 int userId,
201 File dbDirToImport,
202 SynchroImportContextVO importContext,
203 SynchroRejectedRowResolver dataRejectResolver,
204 ProgressionCoreModel progressionModel,
205 int progressionModelMaxCount) {
206 Assert.notNull(progressionModel);
207 Assert.notNull(importContext);
208 SynchroClientImportResult result = new SynchroClientImportResult();
209
210 int progressionStepCount;
211 if (importContext.isWithReferential() && importContext.isWithData()) {
212 progressionStepCount = progressionModelMaxCount / 3;
213 } else if (importContext.isWithReferential()) {
214 progressionStepCount = progressionModelMaxCount / 2;
215 } else {
216 progressionStepCount = progressionModelMaxCount;
217 }
218 int progressionStepNumber = 0;
219
220 Properties tempDbConnectionProperties = getConnectionPropertiesFromDbDirectory(dbDirToImport);
221
222 File versionFile = new File(dbDirToImport, Daos.DB_VERSION_FILE);
223 String newVersion = null;
224 try {
225 newVersion = FileUtils.readFileToString(versionFile, Charset.defaultCharset()).trim();
226 } catch (IOException ex) {
227 log.warn(t("quadrige3.error.read.file", versionFile.getAbsolutePath()));
228 }
229
230 Date synchroDate = StringUtils.isEmpty(newVersion) ? new Date() : DateVersions.convertVersion2Date(newVersion,
231 QuadrigeConfiguration.getInstance().getDbTimezone());
232
233 try {
234
235 if (importContext.isWithReferential()) {
236 ReferentialSynchroContext referentialContext = createContextAndImportReferentialWithoutDelete(userId,
237 dbDirToImport,
238 true,
239 progressionModel,
240 ++progressionStepNumber,
241 progressionStepCount);
242 result.setReferentialContext(referentialContext);
243 }
244
245
246 if (importContext.isWithData()) {
247
248 DataSynchroContext temp2LocalContext = createContextAndImportDataFromTempDB(
249 userId,
250 dbDirToImport,
251 importContext,
252 progressionModel, ++progressionStepNumber,
253 progressionStepCount);
254
255
256 resolveRejectsAndFinishImportData(temp2LocalContext, dataRejectResolver);
257
258
259 temp2LocalContext.setProgramCodes(importContext.getDataProgramCodes());
260
261 result.setDataContext(temp2LocalContext);
262 }
263
264
265 if (importContext.isWithReferential()) {
266
267 updateContextAndImportReferentialDelete(
268 result.getReferentialContext(),
269 true,
270 progressionModel,
271 ++progressionStepNumber,
272 progressionStepCount);
273 }
274
275
276
277 if (importContext.isWithData()) {
278 result.getDataContext().setEnableInsertOrUpdate(true);
279 result.getDataContext().setEnableDelete(true);
280
281 Date newSynchronizationDate = computeDataSynchronizationDate(result.getDataResult().getRejectedRows(), synchroDate,
282 importContext.getDataUpdateDate());
283 result.setDataSynchronizationDate(newSynchronizationDate);
284 }
285 if (importContext.isWithReferential()) {
286 result.getReferentialContext().setEnableInsertOrUpdate(true);
287 result.getReferentialContext().setEnableDelete(true);
288
289 result.setReferentialSynchronizationDate(DateUtils.addSeconds(synchroDate, -config.getImportReferentialUpdateDateOffsetInSecond()));
290 }
291 } finally {
292 shutdownDatabaseSilently(tempDbConnectionProperties);
293 }
294
295
296 synchroHistoryService.save(userId, result);
297
298 return result;
299 }
300
301
302 @Override
303 public SynchroClientImportResult importFromServerDatabase(int userId,
304 SynchroImportContextVO importContext,
305 SynchroRejectedRowResolver dataRejectResolver,
306 ProgressionCoreModel progressionModel,
307 int progressionModelMaxCount) {
308
309 try {
310 return synchroClientInternalService.importFromServerDatabaseTransactional(
311 userId,
312 importContext,
313 dataRejectResolver,
314 progressionModel,
315 progressionModelMaxCount);
316
317 } catch (DataIntegrityViolationOnDeleteException deleteException) {
318
319
320 SynchroDatabaseConfiguration target = new SynchroDatabaseConfiguration(QuadrigeConfiguration.getInstance().getConnectionProperties(),
321 true);
322 handleDeleteException(deleteException, target);
323 }
324 return null;
325 }
326
327
328 @Override
329 public SynchroClientImportResult importFromServerDatabaseTransactional(int userId, SynchroImportContextVO importContext,
330 SynchroRejectedRowResolver dataRejectResolver, ProgressionCoreModel progressionModel, int progressionModelMaxCount) {
331 Assert.notNull(dataRejectResolver);
332 Assert.notNull(progressionModel);
333 Assert.notNull(importContext);
334 SynchroClientImportResult result = new SynchroClientImportResult();
335
336 Properties sourceConnectionProperties = synchroConfig.getImportConnectionProperties();
337
338 boolean withReferential = importContext.isWithReferential();
339 boolean withData = importContext.isWithData();
340 Properties tempDbConnectionProperties = null;
341 File tempDbDirectory = null;
342
343 Date synchronizationDate = getServerCurrentTimestamp(sourceConnectionProperties);
344
345 if (withData) {
346 tempDbDirectory = new File(config.getSynchronizationDirectory(), String.format("%s/import/db-%s",
347 userId,
348 DateVersions.convertDate2Version(synchronizationDate).toString()
349 ));
350 tempDbConnectionProperties = createTempEmptyDb(tempDbDirectory);
351 }
352
353 int progressionStepCount;
354 if (withReferential && withData) {
355 progressionStepCount = progressionModelMaxCount / 4;
356 } else {
357 progressionStepCount = progressionModelMaxCount / 2;
358 }
359 int progressionStepNumber = 0;
360
361 try {
362
363 if (withReferential) {
364
365 ReferentialSynchroContext referentialContext = createContextAndImportReferentialWithoutDelete(
366 userId,
367 sourceConnectionProperties,
368 importContext,
369 false,
370 progressionModel,
371 ++progressionStepNumber,
372 progressionStepCount);
373 result.setReferentialContext(referentialContext);
374 }
375
376
377 if (withData) {
378
379 DataSynchroContext server2TempContext = createContextAndImportDataFromServerToTempDB(
380 userId,
381 sourceConnectionProperties,
382 tempDbConnectionProperties,
383 importContext,
384 progressionModel,
385 ++progressionStepNumber,
386 progressionStepCount);
387
388
389 DataSynchroContext temp2LocalContext = createContextAndImportDataFromTempDB(
390 userId,
391 tempDbConnectionProperties,
392 importContext,
393 progressionModel,
394 ++progressionStepNumber,
395 progressionStepCount);
396
397
398 resolveRejectsAndFinishImportData(temp2LocalContext, dataRejectResolver);
399
400 result.setDataContext(temp2LocalContext);
401 }
402
403
404 if (withReferential) {
405 updateContextAndImportReferentialDelete(
406 result.getReferentialContext(),
407 false,
408 progressionModel,
409 ++progressionStepNumber,
410 progressionStepCount);
411 }
412
413
414
415 {
416 if (importContext.isWithData()) {
417 result.getDataContext().setEnableInsertOrUpdate(true);
418 result.getDataContext().setEnableDelete(true);
419
420 Date newDataSynchroDate = computeDataSynchronizationDate(result.getDataResult().getRejectedRows(), synchronizationDate,
421 importContext.getDataUpdateDate());
422 result.setDataSynchronizationDate(newDataSynchroDate);
423
424 }
425 if (importContext.isWithReferential()) {
426 result.getReferentialContext().setEnableInsertOrUpdate(true);
427 result.getReferentialContext().setEnableDelete(true);
428
429 result.setReferentialSynchronizationDate(DateUtils.addSeconds(synchronizationDate, -config.getImportReferentialUpdateDateOffsetInSecond()));
430 }
431 }
432 } finally {
433 if (withData) {
434 shutdownDatabaseSilently(tempDbConnectionProperties);
435 deleteSilently(tempDbDirectory);
436 }
437 }
438
439 return result;
440 }
441
442
443 @Override
444 public SynchroChangesVO getImportFileInsertAndUpdateChangesTransactional(int userId, File dbZipFile, SynchroImportContextVO importContext,
445 ProgressionCoreModel progressionModel, int progressionModelMaxCount, boolean keepTempDirectory) {
446 Assert.notNull(dbZipFile);
447 Assert.notNull(importContext);
448 Assert.notNull(progressionModel);
449 Assert.isTrue(progressionModelMaxCount > 0);
450
451 if (log.isInfoEnabled()) {
452 log.info(I18n.t("quadrige3.service.synchro.changelog"));
453 }
454
455 int progressionStepCount;
456 if (importContext.isWithReferential() && importContext.isWithData()) {
457 progressionStepCount = (progressionModelMaxCount - (2 )) / 2;
458 } else {
459 progressionStepCount = progressionModelMaxCount - (2 );
460 }
461 int progressionStepNumber = 0;
462
463
464 checkValidImportFile(dbZipFile);
465
466 File tempDirectory = new File(
467 config.getSynchroImportDirectoryByUser(userId),
468 config.getSynchroZipFilePrefix() + DateVersions.convertDate2Version(new Date()).toString());
469
470
471 File changesFile = new File(tempDirectory, "changes.properties");
472 if (changesFile.exists()) {
473 try {
474 FileUtils.forceDelete(changesFile);
475 } catch (IOException e) {
476 throw new QuadrigeTechnicalException("Unable to create change log file :" + changesFile.getPath(), e);
477 }
478 }
479
480
481 try {
482 progressionModel.setMessage(t("quadrige3.synchro.progress.uncompress"));
483
484 FileUtils.forceMkdir(tempDirectory);
485 ZipUtils.uncompressFileToPath(dbZipFile.toPath(), tempDirectory.toPath(), progressionModel, false);
486
487 progressionModel.increments(1);
488 } catch (IOException e) {
489 throw new QuadrigeTechnicalException(t("quadrige3.error.synchro.import.dbZipFile.notZipFile", dbZipFile.getPath()), e);
490 }
491
492
493 File dbDirToImport = Daos.checkAndNormalizeDbDirectory(tempDirectory);
494
495 Properties tempDbConnectionProperties = getConnectionPropertiesFromDbDirectory(dbDirToImport);
496
497
498 SynchroChangesVO result = new SynchroChangesVO();
499 result.setConnectionProperties(tempDbConnectionProperties);
500
501
502 TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
503
504 try {
505 Map<String, Map<String, Map<String, Object>>> dataRemapValues = null;
506
507
508 if (importContext.isWithReferential()) {
509 ReferentialSynchroContext referentialSynchroContext = createContextAndImportReferentialFromFile(
510 userId,
511 dbDirToImport,
512 changesFile,
513 true,
514 null,
515 t("quadrige3.service.synchro.changelog.message"),
516 progressionModel,
517 ++progressionStepNumber,
518 progressionStepCount);
519
520
521 resolveFileReferentialRejectsAndFinishImportFromFile(referentialSynchroContext, newReferentialSynchroRejectedRowResolver());
522
523
524 dataRemapValues = referentialSynchroContext.getResult().getSourceMissingUpdates();
525
526
527 result.addRejects(referentialSynchroContext.getResult().getRejectedRows());
528 }
529
530
531 if (importContext.isWithData()) {
532 DataSynchroContext dataContext = createContextAndImportFromFile(
533 userId,
534 dbDirToImport,
535 changesFile,
536 true,
537 null,
538 dataRemapValues,
539 false,
540 t("quadrige3.service.synchro.changelog.message"),
541 progressionModel,
542 ++progressionStepNumber,
543 progressionStepCount);
544
545
546 filterRejects(dataContext);
547
548
549 result.addRejects(dataContext.getResult().getRejectedRows());
550 }
551
552
553 progressionModel.setMessage(t("quadrige3.synchro.progress.changeLog"));
554 if (changesFile.exists()) {
555
556 result.addFromFile(changesFile);
557
558
559 FileUtils.deleteQuietly(changesFile);
560 }
561
562 if (!keepTempDirectory) {
563 FileUtils.deleteQuietly(tempDirectory);
564 }
565
566 progressionModel.increments(1);
567
568 return result;
569
570 } finally {
571 shutdownDatabaseSilently(tempDbConnectionProperties);
572 }
573 }
574
575
576
577
578
579
580 private void filterRejects(DataSynchroContext dataContext) {
581
582 String rejectString = dataContext.getResult().getRejectedRows(DataSynchroTables.SURVEY.name());
583 if (StringUtils.isNotBlank(rejectString)) {
584
585 dataContext.getResult().getRejectedRows().remove(DataSynchroTables.SURVEY.name());
586
587 try {
588
589 Connection sourceConnection = createConnection(dataContext.getSource().getConnectionProperties());
590 Connection targetConnection = createConnection(dataContext.getTarget().getConnectionProperties());
591 String sql = "SELECT REMOTE_ID FROM SURVEY WHERE SURVEY_ID = %s";
592
593 List<RejectedRow> rejectedRows = RejectedRow.parseFromString(rejectString);
594 for (RejectedRow rejectedRow : rejectedRows) {
595
596
597 Object sourceRemoteId = Daos.sqlUniqueOrNull(sourceConnection, String.format(sql, rejectedRow.pkStr));
598 Object targetRemoteId = Daos.sqlUniqueOrNull(targetConnection, String.format(sql, rejectedRow.targetPkStr));
599
600
601 if (sourceRemoteId != null && sourceRemoteId.equals(targetRemoteId)) {
602
603 if (log.isDebugEnabled()) log.debug("this survey is already in target database: " + rejectedRow.toString());
604 continue;
605 }
606
607
608 dataContext.getResult().addReject(DataSynchroTables.SURVEY.name(), rejectedRow.toString());
609
610 }
611
612 } catch (SQLException e) {
613 log.error("error when filter rejects", e);
614 }
615 }
616 }
617
618
619 @Override
620 public SynchroChangesVO getImportFileReferentialDeleteChangesTransactional(int userId, File dbZipFile, SynchroImportContextVO importContext,
621 ProgressionCoreModel progressionModel, int progressionModelMaxCount, boolean keepTempDirectory) {
622
623 Assert.notNull(dbZipFile);
624 Assert.notNull(importContext);
625 Assert.notNull(progressionModel);
626 Assert.isTrue(progressionModelMaxCount > 0);
627
628 if (log.isInfoEnabled()) {
629 log.info(I18n.t("quadrige3.service.synchro.changelog"));
630 }
631
632 int progressionStepCount = progressionModelMaxCount - (2 );
633 int progressionStepNumber = 0;
634
635
636 checkValidImportFile(dbZipFile);
637
638 File tempDirectory = new File(
639 config.getSynchroImportDirectoryByUser(userId),
640 config.getSynchroZipFilePrefix() + DateVersions.convertDate2Version(new Date()).toString());
641
642
643 File changesFile = new File(tempDirectory, "changes.properties");
644 if (changesFile.exists()) {
645 try {
646 FileUtils.forceDelete(changesFile);
647 } catch (IOException e) {
648 throw new QuadrigeTechnicalException("Unable to create change log file :" + changesFile.getPath(), e);
649 }
650 }
651
652
653 try {
654 progressionModel.setMessage(t("quadrige3.synchro.progress.uncompress"));
655
656 FileUtils.forceMkdir(tempDirectory);
657 ZipUtils.uncompressFileToPath(dbZipFile.toPath(), tempDirectory.toPath(), progressionModel, false);
658
659 progressionModel.increments(1);
660 } catch (IOException e) {
661 throw new QuadrigeTechnicalException(t("quadrige3.error.synchro.import.dbZipFile.notZipFile", dbZipFile.getPath()), e);
662 }
663
664
665 File dbDirToImport = Daos.checkAndNormalizeDbDirectory(tempDirectory);
666
667 Properties tempDbConnectionProperties = getConnectionPropertiesFromDbDirectory(dbDirToImport);
668
669
670 SynchroChangesVO result = new SynchroChangesVO();
671 result.setConnectionProperties(tempDbConnectionProperties);
672
673
674 TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
675
676 try {
677
678
679 if (importContext.isWithReferential()) {
680 ReferentialSynchroContext referentialSynchroContext = createContextAndImportReferentialDeleteFromFile(
681 userId,
682 dbDirToImport,
683 changesFile,
684 true,
685 null,
686 t("quadrige3.service.synchro.changelog.message"),
687 progressionModel,
688 ++progressionStepNumber,
689 progressionStepCount);
690
691
692 result.addRejects(referentialSynchroContext.getResult().getRejectedRows());
693 }
694
695
696 progressionModel.setMessage(t("quadrige3.synchro.progress.changeLog"));
697 if (changesFile.exists()) {
698
699 result.addFromFile(changesFile);
700
701
702 FileUtils.deleteQuietly(changesFile);
703 }
704
705 if (!keepTempDirectory) {
706 FileUtils.deleteQuietly(tempDirectory);
707 }
708
709 progressionModel.increments(1);
710
711 return result;
712
713 } finally {
714 shutdownDatabaseSilently(tempDbConnectionProperties);
715 }
716 }
717
718
719 @Override
720 public void cleanUpUnusedData(int userId) {
721
722 List<Integer> personIdsToRemove = Lists.newArrayList();
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752 List<Integer> observedLocationIdsToRemove = surveyDao.getCleanableSurveyIds();
753 if (CollectionUtils.isNotEmpty(observedLocationIdsToRemove)) {
754 surveyDao.removeByIds(observedLocationIdsToRemove);
755 }
756
757
758 if (CollectionUtils.isNotEmpty(personIdsToRemove)) {
759 for (Integer personIdToRemove : personIdsToRemove) {
760 File personDirectory = getSynchroDirectoryByUser(personIdToRemove);
761
762 if (personDirectory.exists()) {
763 FileUtils.deleteQuietly(personDirectory);
764 }
765 }
766 }
767 }
768
769
770 @Override
771 public SynchroClientExportResult exportDataToTempDb(int userId, Set<String> programCodes, boolean enablePhotos, ProgressionCoreModel progressionModel,
772 int progressionModelMaxCount) {
773
774 Assert.notNull(progressionModel);
775
776 SynchroClientExportResult result = new SynchroClientExportResult();
777
778
779 File tempDirectory = new File(
780 config.getSynchroExportDirectoryByUser(userId),
781 "quadrige3-db-" + DateVersions.convertDate2Version(new Date()).toString());
782
783 File tempDbDirectory = new File(tempDirectory, Daos.DB_DIRECTORY);
784 Properties tempDbConnectionProperties = createTempEmptyDb(tempDbDirectory);
785
786
787 if (enablePhotos) {
788 tempDbConnectionProperties.put(DataSynchroDatabaseConfiguration.DB_PHOTO_DIRECTORY,
789 new File(tempDirectory, Daos.PHOTO_DIRECTORY).getPath());
790 }
791
792 if (!Daos.isValidConnectionProperties(tempDbConnectionProperties)) {
793 throw new QuadrigeTechnicalException(t("quadrige3.error.synchro.export.create"));
794 }
795
796 result.setTempDbExportDirectory(tempDirectory);
797
798
799 int progressionStepNumber = 1;
800
801 try {
802
803 DataSynchroContext dataContext = createContextAndExportDataToTempDb(tempDbConnectionProperties,
804 userId,
805 programCodes,
806 progressionModel,
807 progressionStepNumber,
808 progressionModelMaxCount);
809
810 result.setDataContext(dataContext);
811
812 } finally {
813 shutdownDatabaseSilently(tempDbConnectionProperties);
814 }
815
816 return result;
817 }
818
819
820 @Override
821 public SynchroClientExportResult exportNationalProgramsToTempDb(int userId,
822 Set<String> programCodes,
823 ProgressionCoreModel progressionModel, int progressionModelMaxCount) {
824 Assert.notNull(progressionModel);
825
826 SynchroClientExportResult result = new SynchroClientExportResult();
827
828
829 File tempDbDirectory = new File(
830 config.getSynchroExportDirectoryByUser(userId),
831 "quadrige3-db-" + DateVersions.convertDate2Version(new Date()).toString());
832
833 Properties tempDbConnectionProperties = createTempEmptyDb(tempDbDirectory);
834
835 if (!Daos.isValidConnectionProperties(tempDbConnectionProperties)) {
836 throw new QuadrigeTechnicalException(t("quadrige3.error.synchro.export.create"));
837 }
838
839 result.setTempDbExportDirectory(tempDbDirectory);
840
841
842 int progressionStepNumber = 1;
843
844 try {
845
846 ReferentialSynchroContext context = createContextAndExportProgramsToTempDb(tempDbConnectionProperties,
847 userId,
848 programCodes,
849 progressionModel,
850 progressionStepNumber,
851 progressionModelMaxCount);
852 result.setReferentialContext(context);
853
854 } finally {
855 shutdownDatabaseSilently(tempDbConnectionProperties);
856 }
857
858 return result;
859 }
860
861
862 @Override
863 public SynchroClientExportResult exportToServerDatabase(int userId, Set<String> programCodes, SynchroRejectedRowResolver rejectResolver,
864 ProgressionCoreModel progressionModel, int progressionModelMaxCount) {
865
866
867 File tempDirectory = new File(
868 config.getSynchroExportDirectoryByUser(userId),
869 "quadrige3-db-" + DateVersions.convertDate2Version(new Date()).toString());
870
871
872 File tempDbDirectory = new File(tempDirectory, Daos.DB_DIRECTORY);
873 Properties tempDbConnectionProperties = createTempEmptyDb(tempDbDirectory);
874
875
876 tempDbConnectionProperties.put(DataSynchroDatabaseConfiguration.DB_PHOTO_DIRECTORY,
877 new File(tempDirectory, Daos.PHOTO_DIRECTORY).getPath());
878
879 if (!Daos.isValidConnectionProperties(tempDbConnectionProperties)) {
880 throw new QuadrigeTechnicalException(t("quadrige3.error.synchro.export.create"));
881 }
882
883
884 int progressionStepCount = progressionModelMaxCount / 2;
885 int progressionStepNumber = 0;
886
887 SynchroClientExportResult result = new SynchroClientExportResult();
888
889 try {
890
891 DataSynchroContext local2tempSynchroContext = createContextAndExportDataToTempDb(tempDbConnectionProperties,
892 userId,
893 programCodes,
894 progressionModel,
895 ++progressionStepNumber,
896 progressionStepCount);
897 result.setDataContext(local2tempSynchroContext);
898
899 boolean hasData = local2tempSynchroContext.getResult().getTotalTreated() > 0;
900
901
902 if (hasData) {
903
904
905
906 DataSynchroContext temp2ServerSynchroContext = createContextAndExportDataFromTempDbToServer(tempDbConnectionProperties,
907 userId,
908 progressionModel,
909 ++progressionStepNumber,
910 progressionStepCount);
911 result.setServerResult(temp2ServerSynchroContext.getResult());
912
913
914 finishExportData(userId,
915 result,
916 rejectResolver,
917 false ,
918 true );
919 }
920 result.setDataContext(local2tempSynchroContext);
921
922 } finally {
923 shutdownDatabaseSilently(tempDbConnectionProperties);
924 deleteSilently(tempDbDirectory);
925 }
926
927 return result;
928 }
929
930
931 @Override
932 public boolean finishExportData(int userId, SynchroClientExportResult exportResult,
933 SynchroRejectedRowResolver rejectResolver, boolean synchroFailed, boolean runPkRevert) {
934 Assert.notNull(exportResult.getServerResult());
935
936 boolean hasShownRejectMessage = false;
937 DataSynchroContext dataSynchroContext = exportResult.getDataContext();
938 SynchroResult serverResult = exportResult.getServerResult();
939
940
941
942 inverseRejectsSourceAndTargetPks(serverResult);
943
944 Map<RejectedRow.Cause, RejectedRow.ResolveStrategy> rejectStrategies = Maps.newHashMap();
945
946
947 Map<RejectedRow.Cause, String> rejectedRows = decorateRejectedRows(serverResult.getRejectedRows());
948
949
950 if (rejectedRows.containsKey(RejectedRow.Cause.DUPLICATE_KEY)) {
951 rejectResolver.showRejectMessage(rejectedRows, RejectedRow.Cause.DUPLICATE_KEY, synchroFailed);
952 hasShownRejectMessage = true;
953 }
954
955
956 if (rejectedRows.containsKey(RejectedRow.Cause.LOCKED)) {
957 rejectResolver.showRejectMessage(rejectedRows, RejectedRow.Cause.LOCKED, synchroFailed);
958 hasShownRejectMessage = true;
959 }
960
961
962 if (!synchroFailed) {
963 RejectedRow.ResolveStrategy deletedRowStrategy = resolveRejects(rejectedRows, RejectedRow.Cause.DELETED, rejectResolver);
964 rejectStrategies.put(RejectedRow.Cause.DELETED, deletedRowStrategy);
965 } else if (rejectedRows.containsKey(RejectedRow.Cause.DELETED)) {
966 rejectResolver.showRejectMessage(rejectedRows, RejectedRow.Cause.DELETED, true);
967 hasShownRejectMessage = true;
968 }
969
970
971 if (!synchroFailed) {
972 RejectedRow.ResolveStrategy badUpdateDateStrategy = resolveRejects(rejectedRows, RejectedRow.Cause.BAD_UPDATE_DATE, rejectResolver);
973 rejectStrategies.put(RejectedRow.Cause.BAD_UPDATE_DATE, badUpdateDateStrategy);
974 } else if (rejectedRows.containsKey(RejectedRow.Cause.BAD_UPDATE_DATE)) {
975 rejectResolver.showRejectMessage(rejectedRows, RejectedRow.Cause.BAD_UPDATE_DATE, true);
976 hasShownRejectMessage = true;
977 }
978
979 if (synchroFailed) {
980 return hasShownRejectMessage;
981 }
982
983
984
985 DataSynchroDatabaseConfiguration target = dataSynchroContext.getTarget();
986 dataSynchroContext.setTarget(dataSynchroContext.getSource());
987 dataSynchroContext.setSource(null);
988
989
990 try {
991 SynchroResult tempResult = new SynchroResult();
992 dataSynchroContext.setResult(tempResult);
993
994
995
996 dataSynchroService.finish(dataSynchroContext, serverResult, rejectStrategies);
997
998
999 inverseRejectsSourceAndTargetPks(tempResult);
1000
1001 if (!tempResult.isSuccess()) {
1002
1003 serverResult.setError(tempResult.getError());
1004 throw new QuadrigeTechnicalException(t("quadrige3.error.synchro.export.finish"), tempResult.getError());
1005 }
1006
1007
1008 serverResult.getRejectedRows().clear();
1009 serverResult.getSourceMissingUpdates().clear();
1010 serverResult.getSourceMissingDeletes().clear();
1011 serverResult.getRejectedRows().putAll(tempResult.getRejectedRows());
1012
1013 Multimap<String, String> pksToRevert = ArrayListMultimap.create(tempResult.getSourceMissingReverts());
1014 if (pksToRevert.isEmpty()) {
1015
1016 return hasShownRejectMessage;
1017 }
1018
1019
1020 if (runPkRevert) {
1021 SynchroImportContextVO importContextVO = new SynchroImportContextVO();
1022 importContextVO.setWithData(true);
1023 importContextVO.setWithReferential(false);
1024 importContextVO.setDataPkIncludes(pksToRevert);
1025 importContextVO.setDataForceEditedRowOverride(true);
1026
1027 ProgressionCoreModel progressionModel = new ProgressionCoreModel();
1028 progressionModel.setCurrent(0);
1029 progressionModel.setTotal(100);
1030
1031 importFromServerDatabase(userId, importContextVO, rejectResolver, progressionModel, 100);
1032 } else {
1033
1034 serverResult.getSourceMissingReverts().putAll(pksToRevert);
1035 }
1036
1037 } finally {
1038
1039 dataSynchroContext.setResult(serverResult);
1040 dataSynchroContext.setSource(dataSynchroContext.getTarget());
1041 dataSynchroContext.setTarget(target);
1042
1043
1044 synchroHistoryService.save(userId, exportResult);
1045 }
1046
1047 return hasShownRejectMessage;
1048 }
1049
1050
1051 @Override
1052 public SynchroClientExportToFileResult exportToFile(int userId,
1053 File file, Set<String> programCodes,
1054 boolean dirtyOnly,
1055 SynchroDateOperatorVO dateOperator,
1056 Date startDate,
1057 Date endDate,
1058 ProgressionCoreModel progressionModel, int progressionModelMaxCount) {
1059
1060
1061 return exportToFile(userId,
1062 file,
1063 programCodes,
1064 dirtyOnly,
1065 true,
1066 dateOperator,
1067 startDate,
1068 endDate,
1069 progressionModel,
1070 progressionModelMaxCount);
1071 }
1072
1073
1074 @Override
1075 public SynchroClientExportToFileResult exportToFile(int userId,
1076 File file, Set<String> programCodes,
1077 boolean dirtyOnly,
1078 boolean includeReferential,
1079 SynchroDateOperatorVO dateOperator,
1080 Date startDate,
1081 Date endDate,
1082 ProgressionCoreModel progressionModel, int progressionModelMaxCount) {
1083
1084 Assert.notNull(file);
1085 Assert.notNull(progressionModel);
1086 Assert.isTrue(progressionModelMaxCount > 4);
1087
1088
1089 File tempDirectory = new File(
1090 config.getSynchroExportDirectoryByUser(userId),
1091 config.getSynchroZipFilePrefix() + DateVersions.convertDate2Version(new Date()).toString());
1092 File tempDbDirectory = new File(tempDirectory, Daos.DB_DIRECTORY);
1093 Properties tempDbConnectionProperties = createTempEmptyDb(tempDbDirectory);
1094 if (!Daos.isValidConnectionProperties(tempDbConnectionProperties)) {
1095 throw new QuadrigeTechnicalException(t("quadrige3.error.synchro.export.create"));
1096 }
1097
1098
1099
1100
1101
1102 int progressionStepCount;
1103
1104
1105
1106
1107 if (includeReferential) {
1108
1109 progressionStepCount = (progressionModelMaxCount - 2 ) / 2;
1110 } else {
1111 progressionStepCount = (progressionModelMaxCount - 2 );
1112 }
1113
1114 int progressionStepNumber = 0;
1115
1116 SynchroClientExportToFileResult result = new SynchroClientExportToFileResult();
1117 result.setFile(file);
1118
1119
1120 try {
1121
1122 DataSynchroContext dataSynchroContext = createContextAndExportDataToFile(tempDbConnectionProperties,
1123 userId,
1124 programCodes,
1125 dirtyOnly,
1126 dateOperator,
1127 startDate,
1128 endDate,
1129 progressionModel,
1130 ++progressionStepNumber,
1131 progressionStepCount);
1132 result.setDataContext(dataSynchroContext);
1133
1134
1135 if (includeReferential) {
1136 ReferentialSynchroContext referentialSynchroContext = createContextAndExportLocalReferential(
1137 userId,
1138 tempDbConnectionProperties,
1139 progressionModel,
1140 ++progressionStepNumber,
1141 progressionStepCount);
1142 result.setReferentialContext(referentialSynchroContext);
1143 }
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162 shutdownDatabaseSilently(tempDbConnectionProperties);
1163
1164
1165 boolean hasDataRows = result.getDataResult().getTotalTreated() > 0;
1166 boolean hasReferentialRows = includeReferential && result.getReferentialResult().getTotalTreated() > 0;
1167
1168
1169 if (hasDataRows || hasReferentialRows) {
1170
1171 progressionModel.increments(t("quadrige3.synchro.progress.compress"));
1172
1173 try {
1174 ZipUtils.compressFilesInPath(tempDbDirectory.toPath(), file.toPath(), false);
1175 } catch (IOException e) {
1176 throw new QuadrigeTechnicalException(t("quadrige3.error.synchro.export.compress"), e);
1177 }
1178
1179
1180 if (hasDataRows) {
1181 progressionModel.increments(t("quadrige3.synchro.progress.finishExport"));
1182 finishExportDataToFile(result);
1183 }
1184
1185
1186 synchroHistoryService.save(userId, result);
1187 } else {
1188 progressionModel.increments(2);
1189 }
1190
1191 } finally {
1192
1193 deleteSilently(tempDbDirectory);
1194 }
1195
1196 return result;
1197 }
1198
1199
1200 @Override
1201 @Deprecated
1202 public SynchroClientExportToFileResult exportReferentialToFile(int userId,
1203 File file,
1204 Set<String> programCodes,
1205 ProgressionCoreModel progressionModel,
1206 int progressionModelMaxCount) {
1207
1208 Assert.notNull(file);
1209 Assert.notNull(progressionModel);
1210 Assert.isTrue(progressionModelMaxCount > 1);
1211
1212
1213 File tempDirectory = new File(
1214 config.getSynchroExportDirectoryByUser(userId),
1215 config.getSynchroZipFilePrefix() + DateVersions.convertDate2Version(new Date()).toString());
1216 File tempDbDirectory = new File(tempDirectory, Daos.DB_DIRECTORY);
1217 Properties tempDbConnectionProperties = createTempEmptyDb(tempDbDirectory);
1218 if (!Daos.isValidConnectionProperties(tempDbConnectionProperties)) {
1219 throw new QuadrigeTechnicalException(t("quadrige3.error.synchro.export.create"));
1220 }
1221
1222
1223 boolean isUserLocalAdmin = userService.hasPrivilege(userId, PrivilegeCode.LOCAL_ADMINISTRATOR.getValue());
1224 Assert.isTrue(isUserLocalAdmin || CollectionUtils.isEmpty(programCodes),
1225 "Not implemented: only local administrator is allow to export referential with a filter on programs");
1226 boolean exportAdditionalReferential = isUserLocalAdmin;
1227
1228 int progressionStepCount;
1229 if (exportAdditionalReferential) {
1230
1231 progressionStepCount = (progressionModelMaxCount - 1 ) / 2;
1232 } else {
1233
1234 progressionStepCount = (progressionModelMaxCount - 1 );
1235 }
1236 int progressionStepNumber = 0;
1237
1238 SynchroClientExportToFileResult result = new SynchroClientExportToFileResult();
1239 result.setFile(file);
1240
1241
1242 try {
1243
1244 ReferentialSynchroContext referentialSynchroContext = createContextAndExportLocalReferential(
1245 userId,
1246 tempDbConnectionProperties,
1247 progressionModel,
1248 ++progressionStepNumber,
1249 progressionStepCount);
1250 result.setReferentialContext(referentialSynchroContext);
1251
1252
1253 if (exportAdditionalReferential) {
1254 ReferentialSynchroContext programsAndRulesContext = createContextAndExportAdditionalReferentialToFile(
1255 userId,
1256 programCodes,
1257 tempDbConnectionProperties,
1258 progressionModel,
1259 ++progressionStepNumber,
1260 progressionStepCount);
1261
1262
1263 referentialSynchroContext.getResult().addAll(programsAndRulesContext.getResult());
1264 }
1265
1266
1267 shutdownDatabaseSilently(tempDbConnectionProperties);
1268
1269
1270 boolean hasReferentialRows = result.getReferentialResult().getTotalTreated() > 0;
1271
1272
1273 if (hasReferentialRows) {
1274
1275 progressionModel.increments(t("quadrige3.synchro.progress.compress"));
1276 try {
1277 ZipUtils.compressFilesInPath(tempDbDirectory.toPath(), file.toPath(), false);
1278 } catch (IOException e) {
1279 throw new QuadrigeTechnicalException(t("quadrige3.error.synchro.export.compress"), e);
1280 }
1281
1282
1283 synchroHistoryService.save(userId, result);
1284 }
1285
1286 } finally {
1287
1288 deleteSilently(tempDbDirectory);
1289 }
1290
1291 return result;
1292 }
1293
1294
1295 @Override
1296 public SynchroClientExportToFileResult exportAllReferentialToFile(int userId, File file, ProgressionCoreModel progressionModel,
1297 int progressionModelMaxCount) {
1298
1299 Assert.notNull(file);
1300 Assert.notNull(progressionModel);
1301 Assert.isTrue(progressionModelMaxCount > 1);
1302
1303
1304 File tempDirectory = new File(
1305 config.getSynchroExportDirectoryByUser(userId),
1306 config.getSynchroZipFilePrefix() + DateVersions.convertDate2Version(new Date()).toString());
1307 File tempDbDirectory = new File(tempDirectory, Daos.DB_DIRECTORY);
1308 Properties tempDbConnectionProperties = createTempEmptyDb(tempDbDirectory);
1309 if (!Daos.isValidConnectionProperties(tempDbConnectionProperties)) {
1310 throw new QuadrigeTechnicalException(t("quadrige3.error.synchro.export.create"));
1311 }
1312
1313
1314 int progressionStepCount = (progressionModelMaxCount - 1 );
1315 int progressionStepNumber = 0;
1316
1317 SynchroClientExportToFileResult result = new SynchroClientExportToFileResult();
1318 result.setFile(file);
1319
1320
1321 try {
1322
1323
1324 {
1325 File srcDbVersionFile = new File(config.getDbDirectory(), Daos.DB_VERSION_FILE);
1326 if (srcDbVersionFile.exists()) {
1327 File destDbVersionFile = new File(tempDbDirectory, Daos.DB_VERSION_FILE);
1328 try {
1329 FileUtils.copyFile(srcDbVersionFile, destDbVersionFile);
1330 } catch (IOException e) {
1331 throw new QuadrigeTechnicalException(t("quadrige3.error.synchro.export.copy", srcDbVersionFile.getPath()), e);
1332 }
1333
1334 }
1335 }
1336
1337
1338 ReferentialSynchroContext referentialSynchroContext = createContextAndExportAllReferential(
1339 userId,
1340 tempDbConnectionProperties,
1341 progressionModel,
1342 ++progressionStepNumber,
1343 progressionStepCount);
1344 result.setReferentialContext(referentialSynchroContext);
1345
1346
1347 shutdownDatabaseSilently(tempDbConnectionProperties);
1348
1349
1350 boolean hasReferentialRows = result.getReferentialResult().getTotalTreated() > 0;
1351
1352
1353 if (hasReferentialRows) {
1354
1355 progressionModel.increments(t("quadrige3.synchro.progress.compress"));
1356 try {
1357 ZipUtils.compressFilesInPath(tempDbDirectory.toPath(), file.toPath(), progressionModel, false);
1358 } catch (IOException e) {
1359 throw new QuadrigeTechnicalException(t("quadrige3.error.synchro.export.compress"), e);
1360 }
1361
1362
1363 synchroHistoryService.save(userId, result);
1364 }
1365
1366 } finally {
1367
1368 deleteSilently(tempDbDirectory);
1369 }
1370
1371 return result;
1372 }
1373
1374
1375 @Override
1376 public SynchroClientImportFromFileResult importFromFile(int userId,
1377 File file,
1378 SynchroImportContextVO importContext,
1379 SynchroRejectedRowResolver dataRejectResolver,
1380 ProgressionCoreModel progressionModel,
1381 int progressionModelMaxCount) {
1382 Assert.notNull(file);
1383 Assert.notNull(importContext);
1384 Assert.notNull(dataRejectResolver);
1385 Assert.notNull(progressionModel);
1386 Assert.isTrue(progressionModelMaxCount > 0);
1387
1388 int progressionStepCount;
1389 if (importContext.isWithReferential() && importContext.isWithData()) {
1390 progressionStepCount = progressionModelMaxCount / 2;
1391 } else {
1392 progressionStepCount = progressionModelMaxCount;
1393 }
1394 int progressionStepNumber = 1;
1395
1396 try {
1397 DataSourceUtils.getConnection(dataSource).setAutoCommit(false);
1398 } catch (SQLException ignored) {
1399 }
1400
1401 SynchroClientImportFromFileResult result = new SynchroClientImportFromFileResult();
1402 result.setFile(file);
1403
1404
1405 checkValidImportFile(file);
1406
1407 File tempDirectory = new File(
1408 config.getSynchroImportDirectoryByUser(userId),
1409 config.getSynchroZipFilePrefix() + DateVersions.convertDate2Version(new Date()).toString());
1410
1411
1412 try {
1413 progressionModel.setMessage(t("quadrige3.synchro.progress.uncompress"));
1414
1415 FileUtils.forceMkdir(tempDirectory);
1416 ZipUtils.uncompressFileToPath(file.toPath(), tempDirectory.toPath(), progressionModel, false);
1417 } catch (IOException e) {
1418 throw new QuadrigeTechnicalException(t("quadrige3.error.synchro.import.file.notZipFile", file.getPath()), e);
1419 }
1420
1421
1422 File dbDirToImport = Daos.checkAndNormalizeDbDirectory(tempDirectory);
1423
1424 Properties tempDbConnectionProperties = getConnectionPropertiesFromDbDirectory(dbDirToImport);
1425
1426 try {
1427
1428 if (importContext.isWithReferential()) {
1429 ReferentialSynchroContext temp2LocalContext = createContextAndImportReferentialFromFile(
1430 userId,
1431 dbDirToImport,
1432 importContext.getReferentialPkIncludes(),
1433 progressionModel,
1434 progressionStepNumber++,
1435 progressionStepCount);
1436
1437
1438 resolveFileReferentialRejectsAndFinishImportFromFile(
1439 temp2LocalContext,
1440 newReferentialSynchroRejectedRowResolver());
1441
1442 result.setReferentialContext(temp2LocalContext);
1443 }
1444
1445
1446 if (importContext.isWithData()) {
1447
1448
1449 Map<String, Map<String, Map<String, Object>>> dataRemapValues = getDataRemapValuesFromReferentialResult(result.getReferentialResult());
1450
1451 DataSynchroContext temp2LocalContext = createContextAndImportFromFile(
1452 userId,
1453 dbDirToImport,
1454 importContext.getDataPkIncludes(),
1455 dataRemapValues,
1456 importContext.isForceDuplication(),
1457 progressionModel,
1458 progressionStepNumber++,
1459 progressionStepCount);
1460
1461
1462 resolveFileRejectsAndFinishImportFromFile(temp2LocalContext, dataRejectResolver);
1463
1464 result.setDataContext(temp2LocalContext);
1465 }
1466
1467
1468 if (importContext.isWithReferential()) {
1469
1470 updateContextAndImportReferentialDelete(
1471 result.getReferentialContext(),
1472 true,
1473 progressionModel,
1474 progressionStepNumber,
1475 progressionStepCount);
1476
1477 }
1478
1479
1480
1481 {
1482 if (importContext.isWithReferential()) {
1483 result.getReferentialContext().setEnableInsertOrUpdate(true);
1484 result.getReferentialContext().setEnableDelete(false);
1485 result.setReferentialSynchronizationDate(null);
1486 }
1487 if (importContext.isWithData()) {
1488 result.getDataContext().setEnableInsertOrUpdate(true);
1489 result.getDataContext().setEnableDelete(false);
1490 result.setDataSynchronizationDate(null);
1491 }
1492 }
1493 } finally {
1494 shutdownDatabaseSilently(tempDbConnectionProperties);
1495 }
1496
1497
1498 synchroHistoryService.save(userId, result);
1499
1500 return result;
1501 }
1502
1503
1504
1505
1506
1507
1508 @Override
1509 public SynchroChangesVO getImportFileInsertAndUpdateChanges(
1510 int userId,
1511 File dbZipFile,
1512 SynchroImportContextVO importContext,
1513 ProgressionCoreModel progressionModel,
1514 int progressionModelMaxCount) {
1515
1516 return synchroClientInternalService.getImportFileInsertAndUpdateChangesTransactional(
1517 userId,
1518 dbZipFile,
1519 importContext,
1520 progressionModel,
1521 progressionModelMaxCount,
1522 false);
1523 }
1524
1525
1526 @Override
1527 public SynchroChangesVO getImportFileReferentialDeleteChanges(
1528 int userId,
1529 File dbZipFile,
1530 SynchroImportContextVO importContext,
1531 ProgressionCoreModel progressionModel,
1532 int progressionModelMaxCount) {
1533
1534 return synchroClientInternalService.getImportFileReferentialDeleteChangesTransactional(
1535 userId,
1536 dbZipFile,
1537 importContext,
1538 progressionModel,
1539 progressionModelMaxCount,
1540 false);
1541 }
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554 protected Properties createTempEmptyDb(File tempDbDirectory) {
1555
1556
1557 String tempDbName = config.getDbName();
1558 Properties tempConnectionProperties = new Properties();
1559 tempConnectionProperties.putAll(config.getConnectionProperties());
1560 String tempDbUrl = Daos.getJdbcUrl(tempDbDirectory, tempDbName);
1561
1562
1563 String overridedTempDbUrl = config.getApplicationConfig().getOption("quadrige3.synchro.import.tempDb.jdbc.url");
1564 if (StringUtils.isNotBlank(overridedTempDbUrl)) {
1565 tempDbUrl = overridedTempDbUrl;
1566 }
1567
1568 tempConnectionProperties.setProperty(Environment.URL, tempDbUrl);
1569
1570
1571 if (Daos.isHsqlFileDatabase(tempDbUrl)) {
1572
1573 boolean isDbCreated = false;
1574
1575
1576 if (Daos.isHsqlFileDatabase(config.getJdbcURL())) {
1577
1578 String dbDirectoryFromUrl = Daos.getDbDirectoryFromJdbcUrl(config.getJdbcURL());
1579 File scriptFile = new File(dbDirectoryFromUrl, config.getDbName() + ".script");
1580
1581
1582
1583
1584 if (!scriptFile.exists()) {
1585 scriptFile = new File(config.getDbDirectory(), config.getDbName() + ".script");
1586 }
1587
1588
1589 if (scriptFile.exists()) {
1590
1591 dbSchemaDao.generateNewDb(tempDbDirectory, true, scriptFile, tempConnectionProperties, true
1592
1593
1594 );
1595 isDbCreated = true;
1596 }
1597 }
1598
1599
1600
1601 if (!isDbCreated) {
1602
1603
1604 dbSchemaDao.generateNewDb(tempDbDirectory, true, null, tempConnectionProperties, true);
1605
1606
1607 try {
1608 dbSchemaDao.updateSchema(tempConnectionProperties);
1609 } catch (DatabaseSchemaUpdateException e) {
1610 throw new QuadrigeTechnicalException(e.getMessage(), e);
1611 }
1612 }
1613 }
1614
1615
1616 else {
1617 try {
1618 FileUtils.forceMkdir(tempDbDirectory);
1619 } catch (IOException e) {
1620 throw new QuadrigeTechnicalException("Could not create temp DB directory", e);
1621 }
1622 }
1623
1624 return tempConnectionProperties;
1625 }
1626
1627 private ReferentialSynchroContext createContextAndImportReferentialWithoutDelete(int userId,
1628 Properties sourceConnectionProperties,
1629 SynchroImportContextVO contextVO,
1630 boolean isSourceTemporaryDb,
1631 ProgressionCoreModel progressionModel,
1632 int progressionStepNumber, int progressionStepCount) {
1633
1634
1635 ReferentialSynchroContext synchroContext = referentialSynchroService.createSynchroContext(sourceConnectionProperties,
1636 Times.getTimestampOrNull(contextVO.getReferentialUpdateDate()),
1637 false,
1638 true,
1639 SynchroDirection.IMPORT_TEMP2LOCAL,
1640 userId,
1641 null );
1642
1643 doImportReferential(
1644 synchroContext,
1645 isSourceTemporaryDb,
1646 t("quadrige3.service.synchro.import.referential.message"),
1647 progressionModel,
1648 progressionStepNumber,
1649 progressionStepCount);
1650
1651 return synchroContext;
1652 }
1653
1654 private ReferentialSynchroContext createContextAndImportReferentialWithoutDelete(int userId,
1655 File dbDirToImport,
1656 boolean isSourceTemporaryDb,
1657 ProgressionCoreModel progressionModel,
1658 int progressionStepNumber,
1659 int progressionStepCount) {
1660
1661 ReferentialSynchroContext synchroContext = referentialSynchroService.createSynchroContext(dbDirToImport,
1662 null,
1663 false,
1664 true,
1665 SynchroDirection.IMPORT_TEMP2LOCAL,
1666 userId,
1667 null );
1668
1669 Set<String> tableNamesForced = new HashSet<>();
1670
1671 tableNamesForced.addAll(ProgramStrategySynchroTables.tableNames());
1672
1673 tableNamesForced.addAll(MoratoriumSynchroTables.tableNames());
1674
1675 tableNamesForced.add(ReferentialSynchroTables.QUSER.name());
1676 tableNamesForced.add(ReferentialSynchroTables.DEPARTMENT.name());
1677 synchroContext.setTableNamesForced(tableNamesForced);
1678
1679
1680 if (config.isEnableImportTablesRules()) {
1681 Set<String> tableNames = Sets.newLinkedHashSet(synchroContext.getTableNames());
1682 tableNames.addAll(RuleSynchroTables.tableNames());
1683 synchroContext.setTableNames(tableNames);
1684 }
1685
1686
1687 doImportReferential(synchroContext,
1688 isSourceTemporaryDb,
1689 t("quadrige3.service.synchro.import.referential.message"),
1690 progressionModel,
1691 progressionStepNumber,
1692 progressionStepCount);
1693
1694 return synchroContext;
1695 }
1696
1697 private ReferentialSynchroContext createContextAndImportReferentialFromFile(int userId,
1698 File dbDirToImport,
1699 Multimap<String, String> referentialPkIncludes,
1700 ProgressionCoreModel progressionModel,
1701 int progressionStepNumber,
1702 int progressionStepCount) {
1703 return createContextAndImportReferentialFromFile(
1704 userId,
1705 dbDirToImport,
1706 null ,
1707 false ,
1708 referentialPkIncludes,
1709 t("quadrige3.service.synchro.import.referential.message"),
1710 progressionModel,
1711 progressionStepNumber,
1712 progressionStepCount);
1713 }
1714
1715 private ReferentialSynchroContext createContextAndImportReferentialFromFile(int userId,
1716 File dbDirToImport,
1717 File changeLogFile,
1718 boolean rollbackOnly,
1719 Multimap<String, String> referentialPkIncludes,
1720 String progressionBaseMessage,
1721 ProgressionCoreModel progressionModel,
1722 int progressionStepNumber,
1723 int progressionStepCount) {
1724
1725 ReferentialSynchroContext synchroContext = referentialSynchroService.createSynchroContext(dbDirToImport,
1726 null,
1727 false,
1728 true,
1729 SynchroDirection.IMPORT_FILE2LOCAL,
1730 userId,
1731 null );
1732
1733
1734 synchroContext.setChangeLogFile(changeLogFile);
1735
1736
1737 synchroContext.setPkIncludes(referentialPkIncludes);
1738
1739
1740 synchroContext.getTarget().setRollbackOnly(rollbackOnly);
1741
1742
1743 {
1744 Set<String> tableNames = Sets.newLinkedHashSet(synchroContext.getTableNames());
1745
1746
1747 tableNames.removeAll(ProgramStrategySynchroTables.tableNames());
1748 tableNames.removeAll(MoratoriumSynchroTables.tableNames());
1749
1750
1751 tableNames.removeAll(RuleSynchroTables.tableNames());
1752
1753 synchroContext.setTableNames(tableNames);
1754 }
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772 synchroContext.setTableNamesForced(new HashSet<>());
1773
1774 doImportReferential(synchroContext,
1775 true ,
1776 progressionBaseMessage,
1777 progressionModel,
1778 progressionStepNumber,
1779 progressionStepCount);
1780
1781 return synchroContext;
1782 }
1783
1784 private ReferentialSynchroContext createContextAndImportReferentialDeleteFromFile(int userId,
1785 File dbDirToImport,
1786 File changeLogFile,
1787 boolean rollbackOnly,
1788 Multimap<String, String> referentialPkIncludes,
1789 String progressionBaseMessage,
1790 ProgressionCoreModel progressionModel,
1791 int progressionStepNumber,
1792 int progressionStepCount) {
1793
1794 ReferentialSynchroContext synchroContext = referentialSynchroService.createSynchroContext(dbDirToImport,
1795 null,
1796 true,
1797 false,
1798 SynchroDirection.IMPORT_FILE2LOCAL,
1799 userId,
1800 ImmutableSet.of(StatusCode.LOCAL_ENABLE.getValue(), StatusCode.LOCAL_DISABLE.getValue()) );
1801
1802
1803 synchroContext.setChangeLogFile(changeLogFile);
1804
1805
1806 synchroContext.setPkIncludes(referentialPkIncludes);
1807
1808
1809 synchroContext.getTarget().setRollbackOnly(rollbackOnly);
1810
1811
1812 {
1813 Set<String> tableNames = Sets.newLinkedHashSet(synchroContext.getTableNames());
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823 synchroContext.setTableNames(tableNames);
1824 }
1825
1826
1827 synchroContext.setTableNamesForced(new HashSet<>());
1828
1829 doImportReferential(synchroContext,
1830 true ,
1831 progressionBaseMessage,
1832 progressionModel,
1833 progressionStepNumber,
1834 progressionStepCount);
1835
1836 return synchroContext;
1837 }
1838
1839 private void updateContextAndImportReferentialDelete(
1840 ReferentialSynchroContext synchroContext,
1841 boolean isSourceTemporaryDb,
1842 ProgressionCoreModel progressionModel,
1843 int progressionStepNumber,
1844 int progressionStepCount) {
1845
1846
1847 synchroContext.setEnableDelete(true);
1848 synchroContext.setEnableInsertOrUpdate(false);
1849
1850
1851 SynchroResult previousResult = synchroContext.getResult();
1852 SynchroResult newResult = new SynchroResult();
1853 synchroContext.setResult(newResult);
1854
1855 doImportReferential(
1856 synchroContext,
1857 isSourceTemporaryDb,
1858 t("quadrige3.service.synchro.import.referential.delete.message"),
1859 progressionModel,
1860 progressionStepNumber,
1861 progressionStepCount);
1862
1863 previousResult.addAll(newResult);
1864 synchroContext.setResult(previousResult);
1865 }
1866
1867 private void doImportReferential(
1868 ReferentialSynchroContext synchroContext,
1869 boolean isSourceTemporaryDb,
1870 String progressionBaseMessage,
1871 ProgressionCoreModel progressionModel,
1872 int progressionStepNumber,
1873 int progressionStepCount) {
1874 Assert.isTrue(progressionStepNumber > 0);
1875
1876
1877 synchroContext.getSource().setIsMirrorDatabase(isSourceTemporaryDb);
1878
1879 synchroContext.getTarget().setIsMirrorDatabase(false);
1880 SynchroResult result = synchroContext.getResult();
1881
1882
1883 int progressionStepOffset = progressionStepCount * (progressionStepNumber - 1);
1884 addProgressionListeners(
1885 progressionBaseMessage,
1886 progressionModel,
1887 result.getProgressionModel(),
1888 progressionStepOffset,
1889 progressionStepCount);
1890
1891 referentialSynchroService.prepare(synchroContext);
1892
1893 if (!result.isSuccess()) {
1894 throw new QuadrigeTechnicalException(t("quadrige3.error.synchro.import.prepare"), result.getError());
1895 }
1896
1897 referentialSynchroService.synchronize(synchroContext);
1898
1899 if (!result.isSuccess()) {
1900 Exception error = result.getError();
1901
1902
1903
1904 if (error instanceof DataIntegrityViolationOnDeleteException) {
1905 DataIntegrityViolationOnDeleteException deleteException = (DataIntegrityViolationOnDeleteException) error;
1906 if (!synchroContext.isEnableDelete()) {
1907 handleDeleteException(deleteException, synchroContext.getTarget());
1908 } else {
1909 throw deleteException;
1910 }
1911 }
1912
1913 throw new QuadrigeTechnicalException(t("quadrige3.error.synchro.import.synchro"), error);
1914 }
1915
1916
1917 progressionModel.setCurrent(progressionStepOffset + progressionStepCount);
1918 }
1919
1920 private DataSynchroContext createContextAndImportDataFromTempDB(int userId,
1921 File dbDirToImport,
1922 SynchroImportContextVO contextVO,
1923 ProgressionCoreModel progressionModel, int stepNumber,
1924 int progressionModelStepCount) {
1925
1926 DataSynchroContext synchroContext = dataSynchroService.createSynchroContext(
1927 dbDirToImport,
1928 SynchroDirection.IMPORT_TEMP2LOCAL,
1929 userId,
1930 null,
1931 true,
1932 true
1933 );
1934
1935
1936 synchroContext.setForceEditedRowOverride(contextVO.isDataForceEditedRowOverride());
1937
1938 synchroContext.setEnablePhoto(contextVO.isWithPhoto());
1939
1940
1941 File photoDir = new File(dbDirToImport.getParent(), Daos.PHOTO_DIRECTORY);
1942 if (photoDir.exists()) {
1943 synchroContext.getSource().setDbPhotoDirectory(photoDir);
1944 }
1945
1946 doImportData(synchroContext,
1947 t("quadrige3.service.synchro.import.data.message"),
1948 progressionModel,
1949 stepNumber,
1950 progressionModelStepCount);
1951
1952 return synchroContext;
1953 }
1954
1955 private DataSynchroContext createContextAndImportDataFromTempDB(
1956 int userId,
1957 Properties sourceConnectionProperties,
1958 SynchroImportContextVO contextVO,
1959 ProgressionCoreModel progressionModel,
1960 int stepNumber,
1961 int progressionModelStepCount) {
1962
1963 DataSynchroContext synchroContext = dataSynchroService.createSynchroContext(
1964 sourceConnectionProperties,
1965 SynchroDirection.IMPORT_TEMP2LOCAL,
1966 userId,
1967 null,
1968 true,
1969 true
1970 );
1971
1972
1973 synchroContext.setForceEditedRowOverride(contextVO.isDataForceEditedRowOverride());
1974
1975 doImportData(synchroContext,
1976 t("quadrige3.service.synchro.import.data.message"),
1977 progressionModel,
1978 stepNumber,
1979 progressionModelStepCount);
1980
1981 return synchroContext;
1982 }
1983
1984 private DataSynchroContext createContextAndImportDataFromServerToTempDB(
1985 int userId,
1986 Properties sourceConnectionProperties,
1987 Properties targetConnectionProperties,
1988 SynchroImportContextVO contextVO,
1989 ProgressionCoreModel progressionModel,
1990 int stepNumber,
1991 int progressionModelStepCount) {
1992 Assert.notNull(sourceConnectionProperties);
1993 Assert.notNull(targetConnectionProperties);
1994
1995 Timestamp lastDataUpdateDate = Times.getTimestampOrNull(contextVO.getDataUpdateDate());
1996
1997
1998
1999
2000 boolean enableDelete = (lastDataUpdateDate != null)
2001 || !synchroClientDao.isAllTablesEmpty(DataSynchroTables.getImportTablesIncludes());
2002
2003 DataSynchroContext synchroContext = dataSynchroService.createSynchroContext(
2004 sourceConnectionProperties,
2005 SynchroDirection.IMPORT_SERVER2TEMP,
2006 userId,
2007 lastDataUpdateDate,
2008 enableDelete,
2009 true
2010 );
2011
2012 synchroContext.getTarget().putAllProperties(targetConnectionProperties);
2013 synchroContext.setDataStartDate(Times.getTimestampOrNull(contextVO.getDataStartDate()));
2014 synchroContext.setDataEndDate(Times.getTimestampOrNull(contextVO.getDataEndDate()));
2015 synchroContext.setPkIncludes(contextVO.getDataPkIncludes());
2016
2017 doImportData(synchroContext,
2018 t("quadrige3.service.synchro.import.data.message"),
2019 progressionModel,
2020 stepNumber,
2021 progressionModelStepCount);
2022
2023 return synchroContext;
2024 }
2025
2026 private void doImportData(DataSynchroContext synchroContext,
2027 String progressionBaseMessage,
2028 ProgressionCoreModel progressionModel,
2029 int progressionStepNumber,
2030 int progressionStepCount) {
2031 Assert.isTrue(progressionStepNumber > 0);
2032 SynchroResult result = synchroContext.getResult();
2033
2034
2035 int progressionStepOffset = progressionStepCount * (progressionStepNumber - 1);
2036 addProgressionListeners(
2037 progressionBaseMessage,
2038 progressionModel,
2039 result.getProgressionModel(),
2040 progressionStepOffset,
2041 progressionStepCount);
2042
2043 dataSynchroService.prepare(synchroContext);
2044
2045 if (!result.isSuccess()) {
2046 throw new QuadrigeTechnicalException(t("quadrige3.error.synchro.import.prepare", result.getError()));
2047 }
2048
2049 dataSynchroService.synchronize(synchroContext);
2050
2051 if (!result.isSuccess()) {
2052 Exception error = result.getError();
2053
2054
2055 if (error instanceof DataIntegrityViolationOnDeleteException) {
2056 throw new QuadrigeBusinessException(t("quadrige3.error.synchro.import.synchro.delete",
2057 error.getMessage()));
2058 }
2059
2060 throw new QuadrigeTechnicalException(t("quadrige3.error.synchro.import.synchro"), error);
2061 }
2062
2063
2064 progressionModel.setCurrent(progressionStepOffset + progressionStepCount);
2065
2066 }
2067
2068 private DataSynchroContext createContextAndExportDataToTempDb(Properties tempDbConnectionProperties,
2069 int userId,
2070 Set<String> programCodes,
2071 ProgressionCoreModel progressionModel,
2072 int progressionStepNumber,
2073 int progressionStepCount) {
2074
2075 Properties localConnectionProperties = config.getConnectionProperties();
2076
2077
2078 DataSynchroContext context = dataSynchroService.createSynchroContext(localConnectionProperties,
2079 SynchroDirection.EXPORT_LOCAL2TEMP,
2080 userId,
2081 null,
2082 true,
2083 true );
2084 context.getTarget().putAllProperties(tempDbConnectionProperties);
2085
2086
2087 context.setEnablePhoto(tempDbConnectionProperties.containsKey(DataSynchroDatabaseConfiguration.DB_PHOTO_DIRECTORY));
2088
2089 context.setProgramCodes(programCodes);
2090
2091 context = doExportData(context,
2092 t("quadrige3.synchro.progress.export"),
2093 progressionModel,
2094 progressionStepNumber,
2095 progressionStepCount);
2096
2097 return context;
2098 }
2099
2100 private ReferentialSynchroContext createContextAndExportProgramsToTempDb(Properties tempDbConnectionProperties,
2101 int userId,
2102 Set<String> programCodes,
2103 ProgressionCoreModel progressionModel,
2104 int progressionStepNumber,
2105 int progressionStepCount) {
2106
2107 Properties localConnectionProperties = config.getConnectionProperties();
2108
2109
2110 ReferentialSynchroContext context = referentialSynchroService.createSynchroContext(localConnectionProperties,
2111 null,
2112 true,
2113 true ,
2114 SynchroDirection.EXPORT_LOCAL2TEMP,
2115 userId,
2116 null
2117 );
2118 context.getTarget().putAllProperties(tempDbConnectionProperties);
2119
2120
2121 context.setTableNames(ProgramStrategySynchroTables.tableNames());
2122
2123
2124 context.setProgramCodes(programCodes);
2125
2126 doExportReferential(context,
2127 t("quadrige3.synchro.progress.export"),
2128 progressionModel,
2129 progressionStepNumber,
2130 progressionStepCount);
2131
2132 return context;
2133 }
2134
2135 private DataSynchroContext createContextAndExportDataFromTempDbToServer(Properties tempDbConnectionProperties,
2136 int userId,
2137 ProgressionCoreModel progressionModel,
2138 int progressionStepNumber,
2139 int progressionStepCount) {
2140
2141 Properties serverConnectionProperties = synchroConfig.getImportConnectionProperties();
2142
2143
2144 DataSynchroContext dataSynchroContext = dataSynchroService.createSynchroContext(tempDbConnectionProperties,
2145 SynchroDirection.EXPORT_TEMP2SERVER,
2146 userId,
2147 null,
2148 true ,
2149 true );
2150 dataSynchroContext.getTarget().putAllProperties(serverConnectionProperties);
2151
2152 dataSynchroContext = doExportData(dataSynchroContext,
2153 t("quadrige3.synchro.progress.export"),
2154 progressionModel,
2155 progressionStepNumber,
2156 progressionStepCount);
2157
2158 return dataSynchroContext;
2159 }
2160
2161 private DataSynchroContext doExportData(DataSynchroContext synchroContext,
2162 String progressionBaseMessage,
2163 ProgressionCoreModel progressionModel,
2164 int progressionStepNumber,
2165 int progressionStepCount) {
2166
2167 Assert.isTrue(progressionStepNumber > 0);
2168 SynchroResult result = synchroContext.getResult();
2169
2170
2171 int progressionStepOffset = progressionStepCount * (progressionStepNumber - 1);
2172 addProgressionListeners(
2173 progressionBaseMessage,
2174 progressionModel,
2175 result.getProgressionModel(),
2176 progressionStepOffset,
2177 progressionStepCount);
2178
2179 dataSynchroService.prepare(synchroContext);
2180
2181 if (!result.isSuccess()) {
2182 throw new QuadrigeTechnicalException(t("quadrige3.error.synchro.export.prepare"), result.getError());
2183 }
2184
2185 dataSynchroService.synchronize(synchroContext);
2186
2187 if (!result.isSuccess()) {
2188 throw new QuadrigeTechnicalException(t("quadrige3.error.synchro.export.synchro"), result.getError());
2189 }
2190
2191
2192 progressionModel.setCurrent(progressionStepOffset + progressionStepCount);
2193
2194 return synchroContext;
2195
2196 }
2197
2198 private void doExportReferential(
2199 ReferentialSynchroContext synchroContext,
2200 String progressionBaseMessage,
2201 ProgressionCoreModel progressionModel,
2202 int progressionStepNumber,
2203 int progressionStepCount) {
2204 Assert.isTrue(progressionStepNumber > 0);
2205
2206
2207 synchroContext.getSource().setIsMirrorDatabase(false);
2208
2209 synchroContext.getTarget().setIsMirrorDatabase(true);
2210 SynchroResult result = synchroContext.getResult();
2211
2212
2213 int progressionStepOffset = progressionStepCount * (progressionStepNumber - 1);
2214 addProgressionListeners(
2215 progressionBaseMessage,
2216 progressionModel,
2217 result.getProgressionModel(),
2218 progressionStepOffset,
2219 progressionStepCount);
2220
2221 referentialSynchroService.prepare(synchroContext);
2222
2223 if (!result.isSuccess()) {
2224 throw new QuadrigeTechnicalException(t("quadrige3.error.synchro.export.prepare"), result.getError());
2225 }
2226
2227 referentialSynchroService.synchronize(synchroContext);
2228
2229 if (!result.isSuccess()) {
2230 Exception error = result.getError();
2231
2232
2233 if (error instanceof DataIntegrityViolationOnDeleteException) {
2234 throw new QuadrigeBusinessException(t("quadrige3.error.synchro.export.synchro.delete",
2235 error.getMessage()));
2236 }
2237
2238 throw new QuadrigeTechnicalException(t("quadrige3.error.synchro.export.synchro"), error);
2239 }
2240
2241
2242 progressionModel.setCurrent(progressionStepOffset + progressionStepCount);
2243 }
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258 protected void addProgressionListeners(
2259 final String baseMessage,
2260 final ProgressionCoreModel progressionModel,
2261 final ProgressionModel synchroProgressionModel,
2262 final int progressionModelOffset,
2263 final int progressionCount) {
2264
2265 synchroProgressionModel.addPropertyChangeListener(ProgressionModel.PROPERTY_CURRENT,
2266 evt -> {
2267 Integer current = (Integer) evt.getNewValue();
2268
2269 onProgressionCurrentChanged(progressionModel,
2270 current,
2271 synchroProgressionModel.getTotal(),
2272 progressionModelOffset,
2273 progressionCount);
2274 });
2275
2276
2277 synchroProgressionModel.addPropertyChangeListener(ProgressionModel.PROPERTY_MESSAGE,
2278 evt -> {
2279 String message = (String) evt.getNewValue();
2280 progressionModel.setMessage(String.format(baseMessage, message));
2281 });
2282 }
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300 protected void onProgressionCurrentChanged(ProgressionCoreModel progressionModel,
2301 Integer current,
2302 Integer total,
2303 int progressionOffset,
2304 int progressionCount) {
2305
2306 if (current == null || total == null) {
2307 progressionModel.setCurrent(progressionOffset);
2308 return;
2309 }
2310 int progression = progressionOffset
2311 + Math.round(progressionCount * current / total);
2312 if (progression >= progressionOffset + progressionCount) {
2313 progression = progressionOffset + progressionCount - 2;
2314
2315 }
2316 progressionModel.setCurrent(progression);
2317 }
2318
2319 private DataSynchroContext createContextAndExportDataToFile(Properties tempDbConnectionProperties,
2320 int userId,
2321 Set<String> programCodes,
2322 boolean dirtyOnly, SynchroDateOperatorVO dateOperator, Date startDate, Date endDate,
2323 ProgressionCoreModel progressionModel,
2324 int progressionStepNumber,
2325 int progressionStepCount) {
2326
2327 Properties localConnectionProperties = config.getConnectionProperties();
2328
2329
2330 DataSynchroContext context = dataSynchroService.createSynchroContext(localConnectionProperties,
2331 SynchroDirection.EXPORT_LOCAL2FILE,
2332 userId,
2333 null,
2334 true,
2335 true );
2336 context.getTarget().putAllProperties(tempDbConnectionProperties);
2337 context.setProgramCodes(programCodes);
2338 context.setDirtyOnly(dirtyOnly);
2339 context.setDataStartDate(startDate);
2340 context.setDataEndDate(endDate);
2341 context.setDateOperator(dateOperator);
2342
2343
2344 Set<String> tableNames = new LinkedHashSet<>(context.getTableNames());
2345 tableNames.remove(DataSynchroTables.PHOTO.name());
2346 context.setTableNames(tableNames);
2347
2348 context = doExportData(context,
2349 t("quadrige3.synchro.progress.export"),
2350 progressionModel,
2351 progressionStepNumber,
2352 progressionStepCount);
2353
2354 return context;
2355 }
2356
2357 private ReferentialSynchroContext createContextAndExportLocalReferential(int userId,
2358 Properties tempDbConnectionProperties,
2359 ProgressionCoreModel progressionModel,
2360 int progressionStepNumber,
2361 int progressionStepCount) {
2362
2363 Properties localConnectionProperties = config.getConnectionProperties();
2364
2365
2366 ReferentialSynchroContext context = referentialSynchroService.createSynchroContext(localConnectionProperties,
2367 null,
2368 true,
2369 true ,
2370 SynchroDirection.EXPORT_LOCAL2FILE,
2371 userId,
2372
2373 Sets.newHashSet(StatusCode.LOCAL_ENABLE.getValue(),
2374 StatusCode.LOCAL_DISABLE.getValue())
2375 );
2376 context.getTarget().putAllProperties(tempDbConnectionProperties);
2377
2378
2379 {
2380 Set<String> tableNames = Sets.newHashSet(context.getTableNames());
2381
2382
2383 tableNames.removeAll(ProgramStrategySynchroTables.tableNames());
2384 tableNames.removeAll(MoratoriumSynchroTables.tableNames());
2385
2386
2387 tableNames.removeAll(CampaignOccasionSynchroTables.tableNames());
2388
2389
2390 tableNames.removeAll(RuleSynchroTables.tableNames());
2391
2392 context.setTableNames(tableNames);
2393 }
2394
2395
2396 doExportReferential(context,
2397 t("quadrige3.synchro.progress.export"),
2398 progressionModel,
2399 progressionStepNumber,
2400 progressionStepCount);
2401
2402 return context;
2403 }
2404
2405 @Deprecated
2406 private ReferentialSynchroContext createContextAndExportAdditionalReferentialToFile(int userId,
2407 Set<String> programCodes,
2408 Properties tempDbConnectionProperties,
2409 ProgressionCoreModel progressionModel,
2410 int progressionStepNumber,
2411 int progressionStepCount) {
2412
2413 Properties localConnectionProperties = config.getConnectionProperties();
2414
2415
2416 ReferentialSynchroContext context = referentialSynchroService.createSynchroContext(localConnectionProperties,
2417 null,
2418 true,
2419 true ,
2420 SynchroDirection.EXPORT_LOCAL2FILE,
2421 userId,
2422
2423 null
2424 );
2425 context.getTarget().putAllProperties(tempDbConnectionProperties);
2426
2427
2428 if (CollectionUtils.isNotEmpty(programCodes)) {
2429 context.setProgramCodes(programCodes);
2430 }
2431
2432
2433 {
2434 Set<String> tableNames = Sets.newHashSet();
2435
2436
2437 tableNames.addAll(ProgramStrategySynchroTables.tableNames());
2438 tableNames.addAll(MoratoriumSynchroTables.tableNames());
2439
2440
2441 tableNames.addAll(RuleSynchroTables.tableNames());
2442
2443
2444 tableNames.addAll(CampaignOccasionSynchroTables.tableNames());
2445
2446 context.setTableNames(tableNames);
2447 }
2448
2449
2450 doExportReferential(context,
2451 t("quadrige3.synchro.progress.export"),
2452 progressionModel,
2453 progressionStepNumber,
2454 progressionStepCount);
2455
2456 return context;
2457 }
2458
2459 private ReferentialSynchroContext createContextAndExportAllReferential(int userId,
2460 Properties tempDbConnectionProperties,
2461 ProgressionCoreModel progressionModel,
2462 int progressionStepNumber,
2463 int progressionStepCount) {
2464
2465 Properties localConnectionProperties = config.getConnectionProperties();
2466
2467
2468 ReferentialSynchroContext context = referentialSynchroService.createSynchroContext(localConnectionProperties,
2469 null,
2470 true,
2471 true ,
2472 SynchroDirection.EXPORT_LOCAL2FILE,
2473 userId,
2474
2475 null
2476 );
2477 context.getTarget().putAllProperties(tempDbConnectionProperties);
2478
2479
2480
2481 context.getTarget().setIsMirrorDatabase(false);
2482
2483
2484 {
2485
2486 Set<String> tableNames = Sets.newHashSet(context.getTableNames());
2487
2488
2489 tableNames.addAll(RuleSynchroTables.tableNames());
2490
2491
2492 tableNames.addAll(ContextAndFilterSynchroTables.tableNames());
2493
2494
2495 tableNames.addAll(TechnicalSynchroTables.tableNames());
2496
2497 context.setTableNames(tableNames);
2498 }
2499
2500
2501 doExportReferential(context,
2502 t("quadrige3.synchro.progress.export"),
2503 progressionModel,
2504 progressionStepNumber,
2505 progressionStepCount);
2506
2507 return context;
2508 }
2509
2510 private Properties getConnectionPropertiesFromDbDirectory(File dbDirectory) {
2511 Properties dbConnectionProperties = new Properties();
2512 dbConnectionProperties.putAll(config.getConnectionProperties());
2513 dbConnectionProperties.setProperty(Environment.URL, Daos.getJdbcUrl(dbDirectory, config.getDbName()));
2514 return dbConnectionProperties;
2515 }
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530 protected <O> String decorate(O object, String name) {
2531 Decorator<O> decorator = decoratorService.getDecorator(object, name);
2532 Assert.notNull(decorator);
2533 return decorator.toString(object);
2534 }
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547 protected <O> String decorate(O object) {
2548 Decorator<O> decorator = decoratorService.getDecorator(object);
2549 Assert.notNull(decorator);
2550 return decorator.toString(object);
2551 }
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561 protected void shutdownDatabaseSilently(Properties connectionProperties) {
2562 try {
2563 Daos.shutdownDatabase(connectionProperties);
2564 } catch (Exception e1) {
2565
2566 log.warn(t("quadrige3.error.synchro.import.shutdown"));
2567 }
2568 }
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578 protected void deleteSilently(File dirToDelete) {
2579 try {
2580 FileUtils.forceDelete(dirToDelete);
2581 } catch (IOException e1) {
2582
2583 log.warn(String.format("Could not delete temp directory: %s", dirToDelete.getAbsolutePath()));
2584 }
2585 }
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596 protected Date getServerCurrentTimestamp(Properties serverConnectionProperties) {
2597 Connection connection = null;
2598
2599 try {
2600 connection = createConnection(serverConnectionProperties);
2601 Dialect dialect = Dialect.getDialect(serverConnectionProperties);
2602 return fr.ifremer.common.synchro.dao.Daos.getCurrentTimestamp(connection, dialect);
2603 } catch (SQLException e) {
2604 throw new QuadrigeTechnicalException(e);
2605 } finally {
2606 Daos.closeSilently(connection);
2607 }
2608 }
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620 protected void resolveRejectsAndFinishImportData(
2621 DataSynchroContext temp2LocalContext,
2622 SynchroRejectedRowResolver resolver
2623 ) {
2624 Assert.notNull(temp2LocalContext);
2625
2626 Map<RejectedRow.Cause, RejectedRow.ResolveStrategy> rejectStrategies = Maps.newHashMap();
2627
2628
2629 Map<RejectedRow.Cause, String> rejectedRows = decorateRejectedRows(temp2LocalContext.getResult().getRejectedRows());
2630
2631
2632 RejectedRow.ResolveStrategy deletedRowStrategy = resolveRejects(rejectedRows, RejectedRow.Cause.BAD_UPDATE_DATE, resolver);
2633 rejectStrategies.put(RejectedRow.Cause.BAD_UPDATE_DATE, deletedRowStrategy);
2634
2635
2636 temp2LocalContext.setEnableDelete(true);
2637 temp2LocalContext.setEnableInsertOrUpdate(true);
2638 doFinishImportData(temp2LocalContext, rejectStrategies);
2639
2640 SynchroResult result = temp2LocalContext.getResult();
2641 if (!result.isSuccess()) {
2642 throw new QuadrigeTechnicalException(t("quadrige3.error.synchro.import.finish", result.getError()));
2643 }
2644 }
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656 protected void resolveFileRejectsAndFinishImportFromFile(
2657 DataSynchroContext file2LocalContext,
2658 SynchroRejectedRowResolver resolver
2659 ) {
2660 Assert.notNull(file2LocalContext);
2661
2662 Map<RejectedRow.Cause, RejectedRow.ResolveStrategy> rejectStrategies = Maps.newHashMap();
2663
2664
2665 Map<RejectedRow.Cause, String> rejectedRows = decorateRejectedRows(file2LocalContext.getResult().getRejectedRows());
2666
2667
2668 RejectedRow.ResolveStrategy deletedRowStrategy = resolveRejects(rejectedRows, RejectedRow.Cause.BAD_UPDATE_DATE, resolver);
2669 rejectStrategies.put(RejectedRow.Cause.BAD_UPDATE_DATE, deletedRowStrategy);
2670
2671
2672 deletedRowStrategy = resolveRejects(rejectedRows, RejectedRow.Cause.DUPLICATE_KEY, resolver);
2673 rejectStrategies.put(RejectedRow.Cause.DUPLICATE_KEY, deletedRowStrategy);
2674
2675
2676 file2LocalContext.setEnableDelete(true);
2677 file2LocalContext.setEnableInsertOrUpdate(true);
2678 doFinishImportData(file2LocalContext, rejectStrategies);
2679
2680 SynchroResult result = file2LocalContext.getResult();
2681 if (!result.isSuccess()) {
2682 throw new QuadrigeTechnicalException(t("quadrige3.error.synchro.import.finish"), result.getError());
2683 }
2684 }
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697 protected Map<String, Map<String, Map<String, String>>> getReplaceValuesFromResult(
2698 SynchroResult synchroResult
2699 ) {
2700 Assert.notNull(synchroResult);
2701
2702 if (MapUtils.isEmpty(synchroResult.getRejectedRows())) {
2703 return null;
2704 }
2705
2706 Map<String, Map<String, Map<String, String>>> allReplaceValues = Maps.newHashMap();
2707
2708 for (String tableName : synchroResult.getRejectedRows().keySet()) {
2709
2710 allReplaceValues.put(tableName, Maps.newHashMap());
2711
2712 for (RejectedRow rejectedRow : RejectedRow.parseFromString(synchroResult.getRejectedRows().get(tableName))) {
2713
2714 }
2715 }
2716
2717 return allReplaceValues;
2718 }
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730 protected void resolveFileReferentialRejectsAndFinishImportFromFile(
2731 ReferentialSynchroContext file2LocalContext,
2732 SynchroRejectedRowResolver resolver
2733 ) {
2734 Assert.notNull(file2LocalContext);
2735
2736 SynchroResult result = file2LocalContext.getResult();
2737
2738 Map<RejectedRow.Cause, RejectedRow.ResolveStrategy> rejectStrategies = Maps.newHashMap();
2739
2740
2741
2742
2743 Map<String, Map<String, String>> remapPks = file2LocalContext.getTarget().getRemapPks();
2744 if (MapUtils.isNotEmpty(remapPks)) {
2745 for (String tableName : remapPks.keySet()) {
2746 Map<String, String> tableRemapPks = remapPks.get(tableName);
2747 for (String sourcePkStr : tableRemapPks.keySet()) {
2748 String targetPkStr = tableRemapPks.get(sourcePkStr);
2749 addRejectForRemappedPkFromFile(result, tableName, sourcePkStr, targetPkStr);
2750 }
2751 }
2752 }
2753
2754
2755 Map<RejectedRow.Cause, String> rejectedRows = decorateRejectedRows(result.getRejectedRows());
2756
2757
2758 RejectedRow.ResolveStrategy deletedRowStrategy = resolveRejects(rejectedRows, RejectedRow.Cause.BAD_UPDATE_DATE, resolver);
2759 rejectStrategies.put(RejectedRow.Cause.BAD_UPDATE_DATE, deletedRowStrategy);
2760
2761
2762 deletedRowStrategy = resolveRejects(rejectedRows, RejectedRow.Cause.DUPLICATE_KEY, resolver);
2763 rejectStrategies.put(RejectedRow.Cause.DUPLICATE_KEY, deletedRowStrategy);
2764
2765
2766 file2LocalContext.setEnableDelete(true);
2767 file2LocalContext.setEnableInsertOrUpdate(true);
2768 doFinishImportReferential(file2LocalContext, rejectStrategies);
2769
2770 if (!result.isSuccess()) {
2771 throw new QuadrigeTechnicalException(t("quadrige3.error.synchro.import.referential.finish"), result.getError());
2772 }
2773 }
2774
2775 private void addRejectForRemappedPkFromFile(SynchroResult result, String tableName, String sourcePkStr, String targetPkStr) {
2776
2777
2778 result.addReject(tableName, sourcePkStr, RejectedRow.Cause.DUPLICATE_KEY.name(), targetPkStr, "REMAPPED_PK");
2779
2780
2781 if (ReferentialSynchroTables.TAXON_NAME.name().equalsIgnoreCase(tableName)) {
2782
2783 result.addReject(ReferentialSynchroTables.REFERENCE_TAXON.name(), sourcePkStr, RejectedRow.Cause.DUPLICATE_KEY.name(), targetPkStr,
2784 "REMAPPED_PK");
2785 }
2786 }
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801 protected RejectedRow.ResolveStrategy resolveRejects(
2802 Map<RejectedRow.Cause, String> rejectedRows,
2803 RejectedRow.Cause rejectedStatus,
2804 SynchroRejectedRowResolver resolver) {
2805
2806 RejectedRow.ResolveStrategy result = null;
2807
2808 if (rejectedRows.containsKey(rejectedStatus)) {
2809 String rejectMessage = rejectedRows.get(rejectedStatus);
2810
2811 result = resolver.resolveReject(rejectedStatus, null, rejectMessage);
2812 }
2813
2814 return result != null ? result : RejectedRow.ResolveStrategy.DO_NOTHING;
2815 }
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825 protected void doFinishImportData(
2826 DataSynchroContext temp2LocalContext,
2827 Map<RejectedRow.Cause, RejectedRow.ResolveStrategy> rejectStrategies) {
2828 Assert.notNull(temp2LocalContext.getResult());
2829
2830
2831 SynchroResult result = temp2LocalContext.getResult();
2832
2833
2834 SynchroResult tempResult = new SynchroResult();
2835 temp2LocalContext.setResult(tempResult);
2836
2837 try {
2838
2839 DataSynchroDatabaseConfiguration tempDbConfiguration = temp2LocalContext.getSource();
2840 temp2LocalContext.setSource(null);
2841 dataSynchroService.finish(temp2LocalContext, result, rejectStrategies);
2842 if (!tempResult.isSuccess()) {
2843
2844 result.setError(tempResult.getError());
2845 return;
2846 }
2847
2848
2849
2850 result.getRejectedRows().clear();
2851 result.addAll(tempResult);
2852
2853
2854 Multimap<String, String> pksToRevert = ArrayListMultimap.create(result.getSourceMissingReverts());
2855 if (pksToRevert.isEmpty()) {
2856
2857 return;
2858 }
2859
2860
2861
2862 temp2LocalContext.setPkIncludes(pksToRevert);
2863 temp2LocalContext.setForceEditedRowOverride(true);
2864 temp2LocalContext.setSource(tempDbConfiguration);
2865 tempResult.clear();
2866
2867 dataSynchroService.prepare(temp2LocalContext);
2868
2869 if (!tempResult.isSuccess()) {
2870 result.setError(tempResult.getError());
2871 return;
2872 }
2873
2874 dataSynchroService.synchronize(temp2LocalContext);
2875
2876
2877 result.getSourceMissingReverts().clear();
2878 result.addAll(tempResult);
2879
2880 if (!tempResult.isSuccess()) {
2881 result.setError(tempResult.getError());
2882 }
2883
2884 }
2885
2886
2887 finally {
2888 temp2LocalContext.setResult(result);
2889 }
2890 }
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900 protected void doFinishImportReferential(
2901 ReferentialSynchroContext temp2LocalContext,
2902 Map<RejectedRow.Cause, RejectedRow.ResolveStrategy> rejectStrategies) {
2903 Assert.notNull(temp2LocalContext.getResult());
2904
2905
2906 SynchroResult result = temp2LocalContext.getResult();
2907
2908
2909 SynchroResult tempResult = new SynchroResult();
2910 temp2LocalContext.setResult(tempResult);
2911
2912 ReferentialSynchroDatabaseConfiguration tempDbConfiguration = temp2LocalContext.getSource();
2913 try {
2914
2915 temp2LocalContext.setSource(null);
2916 referentialSynchroService.finish(temp2LocalContext, result, rejectStrategies);
2917 if (!tempResult.isSuccess()) {
2918
2919 result.setError(tempResult.getError());
2920 return;
2921 }
2922
2923
2924
2925 result.getRejectedRows().clear();
2926 result.addAll(tempResult);
2927 }
2928
2929
2930 finally {
2931 temp2LocalContext.setSource(tempDbConfiguration);
2932 temp2LocalContext.setResult(result);
2933 }
2934 }
2935
2936 private Map<RejectedRow.Cause, String> decorateRejectedRows(Map<String, String> rejectedRows) {
2937
2938 Map<RejectedRow.Cause, StringBuilder> result = Maps.newHashMap();
2939
2940 if (CollectionUtils.isEmpty(rejectedRows.keySet())) {
2941 return Maps.newHashMap();
2942 }
2943
2944 for (String tableName : rejectedRows.keySet()) {
2945 for (RejectedRow reject : RejectedRow.parseFromString(rejectedRows.get(tableName))) {
2946
2947 String remotePkStr = reject.pkStr;
2948 String pkStr;
2949 if (StringUtils.isNotBlank(reject.targetPkStr)) {
2950 pkStr = reject.targetPkStr;
2951 }
2952 else {
2953 pkStr = remotePkStr;
2954 }
2955
2956
2957 StringBuilder sb = result.get(reject.cause);
2958 if (sb == null) {
2959 sb = new StringBuilder();
2960 result.put(reject.cause, sb);
2961 }
2962
2963
2964 try {
2965 switch (tableName.toUpperCase()) {
2966 case TABLE_SURVEY: {
2967 Integer id = Integer.valueOf(pkStr);
2968 LightSurveyVO survey = surveyDao.getLightSurveyById(id);
2969 sb.append(decorate(survey));
2970 }
2971 break;
2972 default: {
2973 sb.append(t("quadrige3.service.synchro.rejection.object", tableName, pkStr));
2974 }
2975 }
2976 } catch (Exception e) {
2977 log.error("unable to get the object", e);
2978 sb.append(t("quadrige3.service.synchro.rejection.object", tableName, pkStr));
2979 }
2980
2981
2982 if (reject.validUpdateDate != null) {
2983 sb.append(' ');
2984 sb.append(t("quadrige3.service.synchro.rejection.BAD_UPDATE_DATE.updateDate", reject.validUpdateDate.toString()));
2985 }
2986 sb.append('\n');
2987
2988 }
2989 }
2990
2991 return Maps.transformValues(result, StringBuilder::toString);
2992 }
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004 private Date computeDataSynchronizationDate(Map<String, String> rejectedRows,
3005 Date synchroDate,
3006 Date previousSynchroDate) {
3007
3008 if (MapUtils.isEmpty(rejectedRows)) {
3009 return synchroDate;
3010 } else {
3011 long minUpdateDate = synchroDate.getTime();
3012 for (String tableName : rejectedRows.keySet()) {
3013 for (RejectedRow rejectRow : RejectedRow.parseFromString(rejectedRows.get(tableName))) {
3014 if (rejectRow.cause == RejectedRow.Cause.BAD_UPDATE_DATE) {
3015 Timestamp rowUpdateDate = rejectRow.validUpdateDate;
3016 if (rowUpdateDate != null && rowUpdateDate.getTime() < minUpdateDate) {
3017
3018 minUpdateDate = rowUpdateDate.getTime() - 1000;
3019 }
3020 } else {
3021
3022
3023 log.warn(String.format("Data synchronization date not udpated: rejected rows (other than %s) detected on import: %s",
3024 RejectedRow.Cause.BAD_UPDATE_DATE,
3025 rejectRow));
3026 return previousSynchroDate;
3027 }
3028 }
3029 }
3030
3031 return new Date(minUpdateDate);
3032 }
3033 }
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044 protected File getSynchroDirectoryByUser(int userId) {
3045 return new File(
3046 config.getSynchronizationDirectory(),
3047 String.valueOf(userId));
3048 }
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058 protected void checkValidImportFile(File file) {
3059 if (!file.exists()) {
3060 throw new QuadrigeTechnicalException(t("quadrige3.error.synchro.import.file.notExists", file.getPath()));
3061 }
3062
3063 if (!ZipUtils.isZipFile(file)) {
3064 throw new QuadrigeTechnicalException(t("quadrige3.error.synchro.import.file.notZipFile", file.getPath()));
3065 }
3066 }
3067
3068 private DataSynchroContext createContextAndImportFromFile(int userId,
3069 File dbDirToImport,
3070 Multimap<String, String> pkToIncludes,
3071 Map<String, Map<String, Map<String, Object>>> remapValues,
3072 boolean forceDuplication,
3073 ProgressionCoreModel progressionModel,
3074 int stepNumber,
3075 int progressionModelStepCount) {
3076 return createContextAndImportFromFile(userId,
3077 dbDirToImport,
3078 null,
3079 false,
3080 pkToIncludes,
3081 remapValues,
3082 forceDuplication,
3083 t("quadrige3.service.synchro.import.data.message"),
3084 progressionModel,
3085 stepNumber,
3086 progressionModelStepCount);
3087 }
3088
3089 private DataSynchroContext createContextAndImportFromFile(int userId,
3090 File dbDirToImport,
3091 File changeLogFile,
3092 boolean rollbackOnly,
3093 Multimap<String, String> pkToIncludes,
3094 Map<String, Map<String, Map<String, Object>>> remapValues,
3095 boolean forceDuplication,
3096 String progressionBaseMessage,
3097 ProgressionCoreModel progressionModel,
3098 int stepNumber,
3099 int progressionModelStepCount) {
3100
3101 DataSynchroContext synchroContext = dataSynchroService.createSynchroContext(
3102 dbDirToImport,
3103 SynchroDirection.IMPORT_FILE2LOCAL,
3104 userId,
3105 null ,
3106 false ,
3107 true
3108 );
3109
3110
3111 synchroContext.setChangeLogFile(changeLogFile);
3112
3113
3114 synchroContext.setPkIncludes(pkToIncludes);
3115
3116
3117 synchroContext.setRemapValues(remapValues);
3118
3119
3120 synchroContext.setForceDuplication(forceDuplication);
3121
3122
3123 synchroContext.getTarget().setRollbackOnly(rollbackOnly);
3124
3125
3126 synchroContext.setForceEditedRowOverride(false);
3127
3128
3129 Set<String> tableNames = new LinkedHashSet<>(synchroContext.getTableNames());
3130 tableNames.remove(DataSynchroTables.PHOTO.name());
3131 synchroContext.setTableNames(tableNames);
3132
3133 doImportData(synchroContext,
3134 progressionBaseMessage,
3135 progressionModel,
3136 stepNumber,
3137 progressionModelStepCount);
3138
3139 return synchroContext;
3140 }
3141
3142 private void finishExportDataToFile(SynchroClientExportToFileResult exportResult) {
3143 Assert.notNull(exportResult.getDataResult());
3144
3145
3146 SynchroResult result = exportResult.getDataResult();
3147 DataSynchroContext dataSynchroContext = exportResult.getDataContext();
3148 DataSynchroDatabaseConfiguration target = dataSynchroContext.getTarget();
3149 dataSynchroContext.setTarget(dataSynchroContext.getSource());
3150 dataSynchroContext.setSource(null);
3151
3152
3153 try {
3154 SynchroResult tempResult = new SynchroResult();
3155 dataSynchroContext.setResult(tempResult);
3156
3157
3158
3159 Map<RejectedRow.Cause, RejectedRow.ResolveStrategy> rejectedRowStrategy =
3160 ImmutableMap.<RejectedRow.Cause, RejectedRow.ResolveStrategy> builder()
3161 .put(RejectedRow.Cause.DUPLICATE_KEY, RejectedRow.ResolveStrategy.DO_NOTHING)
3162 .put(RejectedRow.Cause.DELETED, RejectedRow.ResolveStrategy.DO_NOTHING)
3163 .put(RejectedRow.Cause.BAD_UPDATE_DATE, RejectedRow.ResolveStrategy.DO_NOTHING)
3164 .build();
3165 dataSynchroService.finish(dataSynchroContext, result, rejectedRowStrategy);
3166
3167 if (!tempResult.isSuccess()) {
3168
3169 result.setError(tempResult.getError());
3170 throw new QuadrigeTechnicalException(t("quadrige3.error.synchro.export.finish"), tempResult.getError());
3171 }
3172
3173
3174 result.getRejectedRows().clear();
3175 result.getSourceMissingUpdates().clear();
3176 result.getSourceMissingDeletes().clear();
3177 result.getRejectedRows().putAll(tempResult.getRejectedRows());
3178
3179 } finally {
3180
3181 dataSynchroContext.setResult(result);
3182 dataSynchroContext.setSource(dataSynchroContext.getTarget());
3183 dataSynchroContext.setTarget(target);
3184 }
3185 }
3186
3187
3188
3189
3190
3191
3192
3193
3194 protected DecoratorService getDecoratorService() {
3195 return decoratorService;
3196 }
3197
3198
3199
3200
3201
3202
3203
3204
3205 protected QuadrigeConfiguration getConfig() {
3206 return config;
3207 }
3208
3209
3210
3211
3212
3213
3214
3215
3216 protected SynchroRejectedRowResolver newReferentialSynchroRejectedRowResolver() {
3217
3218 return new SynchroRejectedRowResolver() {
3219 @Override
3220 public void showRejectMessage(Map<RejectedRow.Cause, String> rejectedRowsByCause, RejectedRow.Cause causeFilter, boolean failMessage) {
3221
3222 }
3223
3224 @Override
3225 public RejectedRow.ResolveStrategy resolveReject(RejectedRow.Cause rejectCause, String rejectInfos, String rejectMessage) {
3226 if (rejectCause == RejectedRow.Cause.DUPLICATE_KEY) {
3227 return RejectedRow.ResolveStrategy.KEEP_LOCAL;
3228 }
3229 return RejectedRow.ResolveStrategy.DO_NOTHING;
3230 }
3231 };
3232 }
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243 protected Map<String, Map<String, Map<String, Object>>> getDataRemapValuesFromReferentialResult(SynchroResult referentialResult) {
3244 if (referentialResult == null || MapUtils.isEmpty(referentialResult.getSourceMissingUpdates())) {
3245 return null;
3246 }
3247
3248 final Set<String> dataTableIncludes = DataSynchroTables.getImportTablesIncludes();
3249
3250
3251 return Maps.filterKeys(referentialResult.getSourceMissingUpdates(), dataTableIncludes::contains);
3252 }
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262 private void handleDeleteException(DataIntegrityViolationOnDeleteException deleteException, SynchroDatabaseConfiguration target) {
3263
3264 String tableName = deleteException.getTableName();
3265 String pkStr = deleteException.getPkStr();
3266 String i18nTableName = decorate(tableName, DecoratorService.TABLE_NAME);
3267
3268 String i18nRow = null;
3269
3270 if (StringUtils.isNoneBlank(tableName) && StringUtils.isNoneBlank(pkStr) && SynchroTableMetadata.fromPkStr(pkStr).size() == 1) {
3271 ReferentialJdbcDao referentialJdbcDao = new ReferentialJdbcDaoImpl(target.getConnectionProperties());
3272 try {
3273 Object entity = referentialJdbcDao.getVOByTableNameAndPk(deleteException.getTableName(), pkStr);
3274 if (entity != null) {
3275 i18nRow = decorate(entity);
3276 if (StringUtils.isBlank(tableName)) {
3277 i18nRow = String.format("%s (pk=%s)", tableName, pkStr);
3278 }
3279 }
3280 } catch (QuadrigeTechnicalException e) {
3281
3282 i18nRow = null;
3283 }
3284 }
3285
3286
3287 if (i18nTableName != null && i18nRow != null) {
3288 throw new QuadrigeBusinessException(t("quadrige3.error.synchro.import.synchro.delete.details",
3289 i18nTableName,
3290 i18nRow),
3291 deleteException);
3292 } else {
3293 throw new QuadrigeBusinessException(t("quadrige3.error.synchro.import.synchro.delete",
3294 deleteException.getMessage()));
3295 }
3296
3297 }
3298
3299
3300
3301
3302
3303
3304
3305 private void inverseRejectsSourceAndTargetPks(SynchroResult result) {
3306
3307 Map<String, String> rejectedRows = result.getRejectedRows();
3308
3309 Map<String, String> inversedRejectedRows = Maps.newTreeMap();
3310
3311 if (CollectionUtils.isNotEmpty(rejectedRows.keySet())) {
3312
3313 for (String tableName : rejectedRows.keySet()) {
3314
3315 String rejectLines = rejectedRows.get(tableName);
3316
3317
3318 for (RejectedRow rejectedRow : RejectedRow.parseFromString(rejectLines)) {
3319 if (rejectedRow.targetPkStr == null) {
3320 throw new QuadrigeTechnicalException(String.format(
3321 "Invalid reject data [%s]: missing 'targetPkStr'. Could process this reject.", rejectLines));
3322 }
3323
3324 rejectedRow.inverse();
3325
3326 RejectedRow.appendAsString(inversedRejectedRows, tableName, rejectedRow.toString());
3327 }
3328 }
3329 }
3330
3331 rejectedRows.clear();
3332 rejectedRows.putAll(inversedRejectedRows);
3333 }
3334
3335 private Connection createConnection(Properties connectionProperties) throws SQLException {
3336 String jdbcUrl = connectionProperties.getProperty(Environment.URL);
3337 if (Objects.equals(config.getJdbcURL(), jdbcUrl) && this.dataSource != null) {
3338 return Daos.createConnection(this.dataSource);
3339 } else {
3340 return Daos.createConnection(connectionProperties);
3341 }
3342 }
3343
3344 }