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