1 package fr.ifremer.quadrige3.synchro.service.doc;
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.ImmutableList;
27 import com.google.common.collect.Lists;
28 import com.google.common.collect.Sets;
29 import fr.ifremer.common.synchro.config.SynchroConfiguration;
30 import fr.ifremer.common.synchro.meta.SynchroDatabaseMetadata;
31 import fr.ifremer.common.synchro.meta.SynchroJoinMetadata;
32 import fr.ifremer.common.synchro.meta.SynchroTableMetadata;
33 import fr.ifremer.quadrige3.core.dao.technical.Assert;
34 import fr.ifremer.quadrige3.core.dao.technical.Daos;
35 import fr.ifremer.quadrige3.core.dao.technical.gson.Gsons;
36 import fr.ifremer.quadrige3.synchro.meta.DatabaseColumns;
37 import fr.ifremer.quadrige3.synchro.meta.data.DataSynchroTables;
38 import fr.ifremer.quadrige3.synchro.meta.referential.ReferentialSynchroTables;
39 import fr.ifremer.quadrige3.synchro.meta.system.RuleSynchroTables;
40 import fr.ifremer.quadrige3.synchro.service.SynchroDirection;
41 import fr.ifremer.quadrige3.synchro.service.data.DataSynchroContext;
42 import fr.ifremer.quadrige3.synchro.service.data.DataSynchroDatabaseConfiguration;
43 import fr.ifremer.quadrige3.synchro.service.referential.ReferentialSynchroContext;
44 import fr.ifremer.quadrige3.synchro.service.referential.ReferentialSynchroDatabaseConfiguration;
45 import fr.ifremer.quadrige3.synchro.vo.SynchroTableMetaVO;
46 import fr.ifremer.quadrige3.synchro.vo.SynchroTableRelationsVO;
47 import org.apache.commons.logging.Log;
48 import org.apache.commons.logging.LogFactory;
49 import org.nuiton.i18n.I18n;
50 import org.springframework.beans.factory.annotation.Autowired;
51 import org.springframework.context.annotation.Lazy;
52 import org.springframework.stereotype.Service;
53
54 import java.io.File;
55 import java.sql.Connection;
56 import java.sql.SQLException;
57 import java.util.*;
58
59
60
61
62 @Service("docSynchroService")
63 @Lazy
64 public class DocSynchroServiceImpl implements DocSynchroService {
65
66 private static final Log log = LogFactory.getLog(DocSynchroServiceImpl.class);
67 private static final SynchroDirection DEFAULT_SYNCHRO_DIRECTION = SynchroDirection.IMPORT_SERVER2TEMP;
68
69 @Autowired
70 public DocSynchroServiceImpl(SynchroConfiguration config) {
71 }
72
73 @Override
74 public void createTableRelationsFile(Properties connectionProperties, File tableRelationFileCache) throws SQLException {
75 createTableRelationsFile(connectionProperties, tableRelationFileCache, DEFAULT_SYNCHRO_DIRECTION);
76 }
77
78 @Override
79 public void createTableRelationsFile(Properties connectionProperties, File tableRelationFileCache, SynchroDirection synchroDirection)
80 throws SQLException {
81
82 Assert.isTrue(Daos.isValidConnectionProperties(connectionProperties));
83 Assert.notNull(tableRelationFileCache);
84 Assert.notNull(synchroDirection);
85
86 log.info(I18n.t("quadrige3.synchro.doc.createTableRelations", tableRelationFileCache.getPath()));
87
88 Connection connection = Daos.createConnection(connectionProperties);
89
90
91 Set<String> tableNames = Sets.newHashSet(ReferentialSynchroTables.tableNames());
92
93 tableNames.addAll(RuleSynchroTables.tableNames());
94
95
96 ReferentialSynchroDatabaseConfiguration referentialConfiguration = new ReferentialSynchroDatabaseConfiguration(
97 new ReferentialSynchroContext(synchroDirection, null), connectionProperties, false);
98 referentialConfiguration.setFullMetadataEnable(true);
99 referentialConfiguration.setColumnUpdateDate(DatabaseColumns.UPDATE_DT.name().toLowerCase());
100
101
102 referentialConfiguration.excludeUnusedColumns();
103
104
105 SynchroDatabaseMetadata referentialMetadata = new SynchroDatabaseMetadata(connection, referentialConfiguration);
106 referentialMetadata.prepare(tableNames);
107 referentialMetadata.close();
108
109
110 DataSynchroDatabaseConfiguration dataConfiguration = new DataSynchroDatabaseConfiguration(
111 new DataSynchroContext(synchroDirection, null), connectionProperties, false);
112 dataConfiguration.setFullMetadataEnable(true);
113 dataConfiguration.setColumnRemoteId(DatabaseColumns.REMOTE_ID.name().toLowerCase());
114 dataConfiguration.setColumnSynchronizationStatus(DatabaseColumns.SYNCHRONIZATION_STATUS.name().toLowerCase());
115
116
117 dataConfiguration.excludeMeasurementUnusedColumns();
118
119
120 tableNames.addAll(DataSynchroTables.getImportTablesIncludes());
121 SynchroDatabaseMetadata dataMetadata = new SynchroDatabaseMetadata(connection, dataConfiguration);
122 dataMetadata.prepare(tableNames);
123 dataMetadata.close();
124
125
126 Daos.closeSilently(connection);
127
128
129 SynchroTableRelationsVO tableRelations = new SynchroTableRelationsVO();
130
131 for (String tableName : referentialMetadata.getLoadedTableNames()) {
132 populateTableRelations(tableRelations, synchroDirection, referentialMetadata.getLoadedTable(tableName), false);
133 }
134
135 for (String tableName : dataMetadata.getLoadedTableNames()) {
136 populateTableRelations(tableRelations, synchroDirection, dataMetadata.getLoadedTable(tableName), true);
137 }
138
139
140 addMoreRelations(tableRelations, synchroDirection);
141
142
143 Gsons.serializeToFile(tableRelations, tableRelationFileCache);
144 }
145
146 protected void addMoreRelations(SynchroTableRelationsVO tableRelations, SynchroDirection synchroDirection) {
147
148 }
149
150 protected boolean isJoinValid(SynchroJoinMetadata join, SynchroDirection synchroDirection) {
151 return join.isValid();
152 }
153
154 private void populateTableRelations(SynchroTableRelationsVO tableRelations, SynchroDirection synchroDirection, SynchroTableMetadata table,
155 boolean isData) {
156 populateTableRelations(tableRelations, synchroDirection, table, isData, Collections.emptyList());
157 }
158
159 private void populateTableRelations(SynchroTableRelationsVO tableRelations, SynchroDirection synchroDirection, SynchroTableMetadata table,
160 boolean isData, Collection<String> tablesNamesToIgnore) {
161
162 SynchroTableMetaVO tableMeta = tableRelations.getOrCreateTable(table.getName(), table.isRoot(), table.isWithUpdateDateColumn(), isData);
163
164 if (log.isDebugEnabled()) {
165 log.debug(String.format("populateTableRelations in %s for %s", isData ? "DATA" : "REFERENTIAL", tableMeta.toExtendedString()));
166 }
167
168 if (tablesNamesToIgnore.contains(table.getName())) {
169 if (log.isDebugEnabled()) {
170 log.debug(String.format("table %s has been ignored", tableMeta.toExtendedString()));
171 }
172 return;
173 }
174
175 for (SynchroJoinMetadata join : table.getChildJoins()) {
176 SynchroTableMetadata joinTable = join.getTargetTable();
177
178 boolean joinValid = isJoinValid(join, synchroDirection);
179 if (!joinValid
180 || tableRelations.exists(joinTable.getName(), !isData)
181 || table.getName().equalsIgnoreCase(joinTable.getName())
182 || tablesNamesToIgnore.contains(joinTable.getName())) {
183 if (log.isDebugEnabled()) {
184 if (!joinValid) {
185 log.debug(String.format("join from %s to %s is not valid", tableMeta.toExtendedString(), joinTable.getName()));
186 }
187 if (tableRelations.exists(joinTable.getName(), !isData)) {
188 log.debug(String.format("joined table %s already exists in referential", joinTable.getName()));
189 }
190 if (table.getName().equalsIgnoreCase(joinTable.getName())) {
191 log.debug(String.format("join from %s to %s is same table", tableMeta.toExtendedString(), joinTable.getName()));
192 }
193 if (tablesNamesToIgnore.contains(joinTable.getName())) {
194 log.debug(String.format("joined table %s has been ignored", joinTable.getName()));
195 }
196 }
197 continue;
198 }
199
200 SynchroTableMetaVO joinTableMeta = tableRelations.getOrCreateTable(joinTable.getName(), joinTable.isRoot(),
201 joinTable.isWithUpdateDateColumn(), isData);
202 tableRelations.addChildTable(tableMeta, joinTableMeta);
203 if (log.isDebugEnabled()) {
204 log.debug(String.format("table %s is child of %s", joinTableMeta.toExtendedString(), tableMeta.toExtendedString()));
205 }
206 }
207 }
208
209 @Override
210 public List<SynchroTableMetaVO> getAffectedTablesForUpdateHierarchy(SynchroTableRelationsVO tableRelations, SynchroTableMetaVO table) {
211 List<SynchroTableMetaVO> result = Lists.newArrayList();
212 SynchroTableMetaVO tableCopy = new SynchroTableMetaVO(table);
213
214 for (SynchroTableMetaVO rootTable : getFirstRootParentTables(tableRelations, tableCopy)) {
215 result.add(populateRelationTables(tableRelations, rootTable, tableCopy).setToUpdate(true));
216 }
217
218 return result;
219 }
220
221 @Override
222 public List<SynchroTableMetaVO> getAffectedTablesForDeleteHierarchy(SynchroTableRelationsVO tableRelations, SynchroTableMetaVO table) {
223 List<SynchroTableMetaVO> result = Lists.newArrayList();
224 SynchroTableMetaVO tableCopy = new SynchroTableMetaVO(table);
225
226
227 for (SynchroTableMetaVO rootTable : getFirstRootParentTables(tableRelations, tableCopy)) {
228 result.add(populateRelationTables(tableRelations, rootTable, tableCopy).setToUpdate(true));
229 }
230
231
232 if (table.isRoot()) {
233
234
235 for (SynchroTableMetaVO child : tableRelations.getChildTables(table)) {
236 if (child.isRoot() && (table.isData() != child.isData())) {
237 SynchroTableMetaVO childCopy = new SynchroTableMetaVO(child);
238 childCopy.addChildren(tableCopy);
239 result.add(childCopy.setToUpdate(true));
240 }
241 }
242
243
244 for (SynchroTableMetaVO child : tableRelations.getChildTables(table)) {
245 if (!child.isRoot() && (table.isData() != child.isData())) {
246 for (SynchroTableMetaVO firstRootParent : getFirstRootParentTables(tableRelations, child, false)) {
247 if (!result.contains(firstRootParent) && !table.equals(firstRootParent)) {
248 result.add(populateRelationTables(tableRelations, firstRootParent, tableCopy).setToUpdate(true));
249 }
250 }
251 }
252 }
253
254 } else {
255
256
257 for (SynchroTableMetaVO itsParent : getFirstRootParentTables(tableRelations, table, false)) {
258 if (!result.contains(itsParent) && !table.equals(itsParent)) {
259 result.add(populateRelationTables(tableRelations, itsParent, tableCopy).setToUpdate(true));
260 }
261 }
262
263 }
264
265 return result;
266 }
267
268 protected List<SynchroTableMetaVO> getFirstRootParentTables(SynchroTableRelationsVO tableRelations, SynchroTableMetaVO table) {
269 return getFirstRootParentTables(tableRelations, table, false);
270 }
271
272 protected List<SynchroTableMetaVO> getFirstRootParentTables(SynchroTableRelationsVO tableRelations, SynchroTableMetaVO table,
273 boolean dataTableOnly) {
274 if (table.isRoot() && !table.isData()) {
275 return Collections.emptyList();
276 }
277 Set<SynchroTableMetaVO> parents = Sets.newHashSet();
278 for (SynchroTableMetaVO parentTable : findParentContainsChild(tableRelations, table)) {
279 boolean dataOnly = dataTableOnly || table.isData();
280
281 if (parentTable.isRoot() && parentTable.isData() == dataOnly) {
282 parents.add(parentTable);
283 }
284 if (dataOnly) {
285 parents.addAll(getFirstRootParentTables(tableRelations, parentTable, true));
286 }
287 }
288 List<SynchroTableMetaVO> result = Lists.newArrayList(parents);
289 Collections.sort(result);
290 return ImmutableList.copyOf(parents);
291 }
292
293 private List<SynchroTableMetaVO> findParentContainsChild(SynchroTableRelationsVO tableRelations, SynchroTableMetaVO targetChildTable) {
294 List<SynchroTableMetaVO> result = Lists.newArrayList();
295 for (SynchroTableMetaVO parentTable : tableRelations.getChildTableMap().keySet()) {
296 if (tableRelations.getChildTables(parentTable).contains(targetChildTable)) {
297 result.add(parentTable);
298 }
299 }
300 return ImmutableList.copyOf(result);
301 }
302
303 private SynchroTableMetaVO populateRelationTables(SynchroTableRelationsVO tableRelations, SynchroTableMetaVO base, SynchroTableMetaVO target) {
304 SynchroTableMetaVO result = new SynchroTableMetaVO(base);
305 List<SynchroTableMetaVO> nextChildTables = getNextChildTable(tableRelations, base, target);
306 if (!nextChildTables.isEmpty()) {
307 for (SynchroTableMetaVO nextChild : nextChildTables) {
308 if (nextChild.equals(target)) {
309 result.addChildren(new SynchroTableMetaVO(nextChild));
310 } else {
311 result.addChildren(populateRelationTables(tableRelations, nextChild, target));
312 }
313 }
314 } else {
315
316 for (SynchroTableMetaVO nextParent : getNextParentTable(tableRelations, base, target)) {
317 if (nextParent.equals(target)) {
318 result.addChildren(new SynchroTableMetaVO(nextParent));
319 }
320 }
321 }
322 return result;
323 }
324
325 private List<SynchroTableMetaVO> getNextChildTable(SynchroTableRelationsVO tableRelations, SynchroTableMetaVO base, SynchroTableMetaVO target) {
326 List<SynchroTableMetaVO> result = Lists.newArrayList();
327
328 for (SynchroTableMetaVO child : tableRelations.getChildTables(base)) {
329 if (child.equals(target)) {
330 result.add(child);
331 } else if (findTableInChildren(tableRelations, child, target)) {
332
333 result.add(child);
334 } else if (findTableInParents(tableRelations, child, target)) {
335 result.add(child);
336 }
337 }
338 Collections.sort(result);
339 return ImmutableList.copyOf(result);
340 }
341
342 private List<SynchroTableMetaVO> getNextParentTable(SynchroTableRelationsVO tableRelations, SynchroTableMetaVO base, SynchroTableMetaVO target) {
343 List<SynchroTableMetaVO> result = Lists.newArrayList();
344
345 for (SynchroTableMetaVO parent : findParentContainsChild(tableRelations, base)) {
346 if (parent.equals(target)) {
347 result.add(parent);
348 }
349 }
350 Collections.sort(result);
351 return ImmutableList.copyOf(result);
352 }
353
354 private boolean findTableInChildren(SynchroTableRelationsVO tableRelations, SynchroTableMetaVO table, SynchroTableMetaVO target) {
355 for (SynchroTableMetaVO child : tableRelations.getChildTables(table)) {
356 if (child.equals(target)) {
357 return true;
358 }
359 }
360 for (SynchroTableMetaVO child : tableRelations.getChildTables(table)) {
361 if (findTableInChildren(tableRelations, child, target)) {
362 return true;
363 }
364 if (findTableInParents(tableRelations, child, target)) {
365 return true;
366 }
367 }
368 return false;
369 }
370
371 private boolean findTableInParents(SynchroTableRelationsVO tableRelations, SynchroTableMetaVO table, SynchroTableMetaVO target) {
372 for (SynchroTableMetaVO parent : findParentContainsChild(tableRelations, table)) {
373 if (parent.equals(target)) {
374 return true;
375 }
376 }
377 return false;
378 }
379
380 protected boolean notContains(List<SynchroTableMetaVO> tables, SynchroTableMetaVO tableToFind) {
381 if (tables.contains(tableToFind)) {
382 return false;
383 }
384
385 for (SynchroTableMetaVO table : tables) {
386 boolean notFound = notContains(table.getChildren(), tableToFind);
387 if (!notFound) {
388 return false;
389 }
390 }
391
392 return true;
393 }
394
395 }