Newer
Older
/*
* _____________________________________________________________________________
*
* INAF - OATS National Institute for Astrophysics - Astronomical Observatory of
* Trieste INAF - IA2 Italian Center for Astronomical Archives
* _____________________________________________________________________________
*
* Copyright (C) 2016 Istituto Nazionale di Astrofisica
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License Version 3 as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package it.inaf.ia2.tsm;
import it.inaf.ia2.tsm.datalayer.DBBroker;
import it.inaf.ia2.tsm.datalayer.DBBrokerFactory;
import it.inaf.ia2.tsm.datalayer.DBWrapper;
import it.inaf.ia2.tsm.model.PropertyModel;
import it.inaf.ia2.tsm.model.TableModel;
import it.inaf.ia2.tsm.model.TapSchemaModel;
import it.inaf.ia2.tsm.model.TapSchemaModels;
import java.io.Serializable;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The main implementation of {@link TapSchema}.
*
* @author Sonia Zorba {@literal <zorba at oats.inaf.it>}
*/
public class TapSchema implements EntitiesContainer<Schema>, Serializable {
// Mandatory tables constants
public static final String TABLES_TABLE = "tables";
public static final String SCHEMAS_TABLE = "schemas";
public static final String COLUMNS_TABLE = "columns";
public static final String KEYS_TABLE = "keys";
public static final String KEY_COLUMNS_TABLE = "key_columns";
public static final String DESCRIPTION_KEY = "description";
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
private static final long serialVersionUID = 1678083091602571256L;
private static final Logger LOG = LoggerFactory.getLogger(TapSchema.class);
private final Map<String, Schema> schemas;
private final Set<Key> allKeys;
private final ConsistencyChecks consistencyChecks;
private boolean loading;
private String version;
private DBWrapper dbWrapper;
private String tapSchemaName;
private boolean exists;
private transient DBBroker sourceDBBroker;
private transient DBBroker tapSchemaDBBroker;
public final DBBroker getSourceDBBroker() {
if (sourceDBBroker == null) {
sourceDBBroker = DBBrokerFactory.getDBBroker(dbWrapper.getSourceDataSourceWrapper());
}
return sourceDBBroker;
}
public final DBBroker getTapSchemaDBBroker() {
if (tapSchemaDBBroker == null) {
tapSchemaDBBroker = DBBrokerFactory.getDBBroker(dbWrapper.getTapSchemaDataSourceWrapper());
}
return tapSchemaDBBroker;
}
private TapSchema() {
// for serialization
schemas = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
allKeys = new HashSet<>();
consistencyChecks = new ConsistencyChecks();
}
public TapSchema(String version, DBWrapper dbWrapper, String tapSchemaName, boolean exists) throws SQLException {
this();
loading = true;
this.version = version;
this.dbWrapper = dbWrapper;
this.tapSchemaName = tapSchemaName;
this.exists = exists;
// Initializing schemas map
for (String schemaName : getSourceDBBroker().getAllSchemaNames()) {
schemas.put(schemaName, null);
}
schemas.put(tapSchemaName, null); // the TAP_SCHEMA contains itself
if (exists) {
loadSavedTapSchema();
}
loading = false;
checkKeys();
}
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
/**
* Loads saved TAP_SCHEMA data and performs consistency checking.
*
* @throws SQLException
*/
private void loadSavedTapSchema() throws SQLException {
DBBroker broker = getTapSchemaDBBroker();
// Schemata
for (Map<String, Object> schemaProps : broker.getSavedItems(getName(), getTableModel(SCHEMAS_TABLE))) {
String schemaName = (String) schemaProps.get(Schema.SCHEMA_NAME_KEY);
Schema schema = addChild(schemaName);
if (schema == null) {
consistencyChecks.addUnexistingSchema(schemaName);
} else {
schema.init(schemaProps);
schema.setStatus(Status.ADDED_PERSISTED);
}
}
// Tables
for (Map<String, Object> tableProps : broker.getSavedItems(getName(), getTableModel(TABLES_TABLE))) {
String tableCompleteName = (String) tableProps.get(Table.TABLE_NAME_KEY);
String[] tableNameSplit = tableCompleteName.split(Pattern.quote("."));
String schemaName = tableNameSplit[0];
String tableName = tableNameSplit[1];
Schema schema = getChild(schemaName, Status.ADDED_PERSISTED);
if (schema == null) {
consistencyChecks.addUnexistingSchema(schemaName);
} else {
Table table = schema.addChild(tableName);
if (table == null) {
consistencyChecks.addUnexistingTable(schemaName, tableName);
} else {
table.init(tableProps);
table.setStatus(Status.ADDED_PERSISTED);
}
}
}
// Columns
for (Map<String, Object> columnProps : broker.getSavedItems(getName(), getTableModel(COLUMNS_TABLE))) {
String tableCompleteName = (String) columnProps.get(Column.TABLE_NAME_KEY);
String[] tableNameSplit = tableCompleteName.split(Pattern.quote("."));
String schemaName = tableNameSplit[0];
String tableName = tableNameSplit[1];
String columnName = (String) columnProps.get(Column.COLUMN_NAME_KEY);
Schema schema = getChild(schemaName, Status.ADDED_PERSISTED);
if (schema == null) {
consistencyChecks.addUnexistingSchema(schemaName);
} else {
Table table = schema.getChild(tableName, Status.ADDED_PERSISTED);
if (table == null) {
consistencyChecks.addUnexistingTable(schemaName, tableName);
} else {
Column column = table.addChild(columnName);
if (column == null) {
consistencyChecks.addUnexistingColumn(tableCompleteName, columnName);
} else {
column.init(columnProps);
column.setStatus(Status.ADDED_PERSISTED);
}
}
}
}
// Keys
List<Map<String, Object>> keysProps = broker.getSavedItems(getName(), getTableModel(KEYS_TABLE));
List<Map<String, Object>> keysColumnsProps = broker.getSavedItems(getName(), getTableModel(KEY_COLUMNS_TABLE));
for (Map<String, Object> keyProp : keysProps) {
// Searching the key
boolean keyFound = false;
for (Key key : allKeys) {
String fromTable = (String) keyProp.get(Key.FROM_TABLE_KEY);
String targetTable = (String) keyProp.get(Key.TARGET_TABLE_KEY);
if (key.getFromTableCompleteName().equals(fromTable) && key.getTargetTableCompleteName().equals(targetTable)) {
// Search the key columns having proper key id
List<Map<String, Object>> kcPropsById = new ArrayList<>();
for (Map<String, Object> kcp : keysColumnsProps) {
if (kcp.get(KeyColumn.KEY_ID_KEY).equals(key.getId())) {
kcPropsById.add(kcp);
}
}
// Verifying the matching
List<KeyColumn> matchedKeyColumns = new ArrayList<>();
if (kcPropsById.size() == key.getKeyColumns().size()) {
for (Map<String, Object> kcp : kcPropsById) {
String fromColumn = (String) kcp.get(KeyColumn.FROM_COLUMN_KEY);
String targetColumn = (String) kcp.get(KeyColumn.TARGET_COLUMN_KEY);
for (KeyColumn keyColumn : key.getKeyColumns()) {
if (keyColumn.getFromColumn().equals(fromColumn)
&& keyColumn.getTargetColumn().equals(targetColumn)) {
matchedKeyColumns.add(keyColumn);
}
}
}
}
if (kcPropsById.size() == matchedKeyColumns.size()) {
keyFound = true;
int index = 0;
key.init(keyProp);
for (Map<String, Object> kcp : kcPropsById) {
KeyColumn kc = matchedKeyColumns.get(index);
kc.init(kcp);
index++;
}
}
}
}
if (!keyFound) {
// TODO
}
}
}
public DBBroker getDBBroker(String schemaName) {
if (schemaName.equals(tapSchemaName)) {
return getTapSchemaDBBroker();
} else {
return getSourceDBBroker();
}
}
/**
* The name of the TAP_SCHEMA schema.
*/
public final String getName() {
return tapSchemaName;
}
/**
* The version selected for this TAP_SCHEMA.
*/
public String getVersion() {
return version;
}
private void loadSchemaKeysMetadata(String schemaName) throws SQLException {
allKeys.addAll(getDBBroker(schemaName).getKeys(this, schemaName));
public Set<Key> getAllKeys() {
return allKeys;
}
/**
* {@inheritDoc}
*/
@Override
public final Schema addChild(String schemaName) throws SQLException {
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
LOG.debug("Adding schema {}", schemaName);
Schema schema;
if (!schemas.containsKey(schemaName)) {
schema = null;
} else {
schema = schemas.get(schemaName);
if (schema == null) {
schema = new Schema(this, schemaName);
schema.setStatus(Status.ADDED_NOT_PERSISTED);
schemas.put(schemaName, schema);
loadSchemaKeysMetadata(schemaName);
} else {
switch (schema.getStatus()) {
case TO_REMOVE:
schema.setStatus(Status.ADDED_PERSISTED);
break;
case REMOVED_NOT_PERSISTED:
schema.setStatus(Status.ADDED_NOT_PERSISTED);
break;
default:
throw new IllegalArgumentException("Cannot add the schema " + schemaName + ". Invalid status. Schema status is " + schema.getStatus());
}
}
checkKeys();
}
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
return schema;
}
/**
* {@inheritDoc}
*/
@Override
public void removeChild(String schemaName) {
LOG.debug("Removing schema {}", schemaName);
if (!schemas.containsKey(schemaName)) {
throw new IllegalArgumentException("The database doesn't contains a schema named " + schemaName);
}
Schema schema = schemas.get(schemaName);
if (schema == null || schema.getStatus() == Status.LOADED) {
throw new IllegalArgumentException("Cannot remove the schema " + schemaName + ". It has never been added.");
}
if (schema.getStatus() == Status.ADDED_NOT_PERSISTED) {
schema.setStatus(Status.REMOVED_NOT_PERSISTED);
} else if (schema.getStatus() == Status.ADDED_PERSISTED) {
schema.setStatus(Status.TO_REMOVE);
}
checkKeys();
}
/**
* {@inheritDoc}
*/
@Override
public final Schema getChild(String childName, Status... statuses) {
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
return TSMUtil.getChild(schemas, childName, statuses);
}
/**
* {@inheritDoc}
*/
@Override
public List<Schema> getChildren(Status... statuses) {
return TSMUtil.getChildrenByStatus(schemas.values(), statuses);
}
/**
* {@inheritDoc}
*/
@Override
public List<String> getAddableChildrenNames() {
return TSMUtil.getAddableChildrenNames(schemas);
}
/**
* {@inheritDoc}
*/
@Override
public List<Schema> getAddedChildren() {
return getChildren(Status.ADDED_PERSISTED, Status.ADDED_NOT_PERSISTED);
}
/**
* {@inheritDoc}
*/
@Override
public List<Schema> getAddedOrRemovedChildren() {
return getChildren(Status.ADDED_PERSISTED, Status.ADDED_NOT_PERSISTED, Status.TO_REMOVE, Status.REMOVED_NOT_PERSISTED);
}
/**
* This method has to be used after TAP_SCHEMA modifications are committed
* to the database, in order to remove from the memory the schemas with
* status {@code Status.TO_REMOVE} or {@code Status.REMOVED_NOT_PERSISTED}.
*/
public void cleanSchema(String schemaName) {
if (!schemas.containsKey(schemaName)) {
throw new IllegalArgumentException("The TAP_SCHEMA doesn't contain the schema " + schemaName);
}
schemas.put(schemaName, null);
}
/**
* Save or update the TAP_SCHEMA changes into the database.
*/
public void save() throws SQLException {
DBBroker broker = getTapSchemaDBBroker();
if (!exists) {
broker.createTapSchemaStructure(getName(), getTapSchemaModel());
// Adding TAP_SCHEMA into TAP_SCHEMA
Schema tapSchemaSchema = addChild(tapSchemaName);
for (String tableName : tapSchemaSchema.getAddableChildrenNames()) {
Table table = tapSchemaSchema.addChild(tableName);
for (String columnName : table.getAddableChildrenNames()) {
table.addChild(columnName);
}
}
fillTapSchemaDescriptions();
fillKeyIds();
broker.save(this);
exists = true;
consistencyChecks.getInconsistencies().clear();
}
/**
* Retrieve the maximum key id from all the schemas that are added into the
* TAP_SCHEMA.
*
* @return the maximum key, if it exists, zero otherwise.
*/
private int getMaxKeyId() {
int maxKeyId = 0;
for (Key key : allKeys) {
if (key.getId() != null) {
int keyId = Integer.parseInt(key.getId());
if (keyId > maxKeyId) {
maxKeyId = keyId;
}
}
}
return maxKeyId;
}
private void fillKeyIds() {
List<Key> newKeys = new ArrayList<>();
for (Key key : allKeys) {
if (key.isVisible()) {
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
newKeys.add(key);
}
}
int maxKeyId = getMaxKeyId();
for (Key newKey : newKeys) {
maxKeyId++;
newKey.setId(maxKeyId + "");
}
}
// public void addFictitiousKey(Table fromTable, String[] fromColumns, Table targetTable, String[] targetColumns) {
// Key key = new Key(dbWrapper, this, fromTable.getCompleteName(), targetTable.getCompleteName());
// key.setId((getMaxKeyId() + 1) + "");
//
// for (int i = 0; i < fromColumns.length; i++) {
// key.addKeyColumn(fromColumns[i], targetColumns[i]);
// }
//
// fromTable.addFromKey(key);
// targetTable.addTargetKey(key);
//
// allKeys.add(key);
// checkKeys();
// }
/**
* Set keys visibility based on other entities visibility (a key is visible
* if all schemas, tables and columns involved have
* {@link Status} {@code ADDED_PERSISTED} or {@code ADDED_NOT_PERSISTED}).
*/
public final void checkKeys() {
if (!loading) {
for (Key key : allKeys) {
// Check if key should be exposed in TAP_SCHEMA
boolean keyVisible = true;
for (KeyColumn keyColumn : key.getKeyColumns()) {
String schemaName = keyColumn.getParent().getFromSchemaName();
String tableName = keyColumn.getParent().getFromTableSimpleName();
String columnName = keyColumn.getFromColumn();
if (!isColumnVisible(schemaName, tableName, columnName)) {
keyVisible = false;
break;
}
schemaName = keyColumn.getParent().getTargetSchemaName();
tableName = keyColumn.getParent().getTargetTableSimpleName();
columnName = keyColumn.getTargetColumn();
if (!isColumnVisible(schemaName, tableName, columnName)) {
keyVisible = false;
break;
}
}
// TODO: use status instead of set visibile [?]
key.setVisible(keyVisible);
}
}
}
/**
* Print all TAP_SCHEMA tree (useful for debugging).
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder("\n");
sb.append(String.format(">> TAP_SCHEMA %s <<\n", tapSchemaName));
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
for (Schema schema : getChildren()) {
sb.append("--");
sb.append(schema.getName());
sb.append(String.format(" [%s]", schema.getStatus()));
sb.append("\n");
List<Table> tables = schema.getChildren();
for (int i = 0; i < tables.size(); i++) {
Table table = tables.get(i);
sb.append(" |--");
sb.append(table.getName());
sb.append(String.format(" [%s]", table.getStatus()));
sb.append("\n");
String padder = i < tables.size() - 1 ? " | " : " ";
for (Column column : table.getChildren()) {
sb.append(padder);
sb.append("|--");
sb.append(column.getName());
sb.append(String.format(" [%s]", column.getStatus()));
sb.append("\n");
}
sb.append("\n");
}
}
sb.append("** Keys **\n");
for (Key key : getVisibileKeys()) {
sb.append(key);
sb.append("\n");
}
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
return sb.toString();
}
public boolean isSchemaVisible(String schemaName) {
Schema schema = schemas.get(schemaName);
if (schema == null) {
return false;
}
return schema.getStatus() == Status.ADDED_PERSISTED
|| schema.getStatus() == Status.ADDED_NOT_PERSISTED;
}
public boolean isTableVisible(String schemaName, String tableName) {
if (!isSchemaVisible(schemaName)) {
return false;
}
Table table = schemas.get(schemaName).getChild(tableName);
if (table == null) {
return false;
}
if (table.getStatus() == Status.ADDED_PERSISTED
|| table.getStatus() == Status.ADDED_NOT_PERSISTED) {
return isSchemaVisible(schemaName);
}
return false;
}
public boolean isColumnVisible(String schemaName, String tableName, String columnName) {
if (!isTableVisible(schemaName, tableName)) {
return false;
}
Column column = schemas.get(schemaName).getChild(tableName).getChild(columnName);
if (column == null) {
return false;
}
if (column.getStatus() == Status.ADDED_PERSISTED
|| column.getStatus() == Status.ADDED_NOT_PERSISTED) {
return isTableVisible(schemaName, tableName);
}
return false;
}
/**
* Define if the TAP_SCHEMA schema was already written into the database.
*/
public boolean exists() {
return exists;
}
public ConsistencyChecks getConsistencyChecks() {
return consistencyChecks;
}
public TapSchemaModel getTapSchemaModel() {
return TapSchemaModels.getTapSchemaModel(version);
}
public final TableModel getTableModel(String tableName) {
return getTapSchemaModel().getTables().get(tableName);
}
public Map<String, Object> getSchemaMetadata(String schemaName) {
Map<String, Object> metadata = new HashMap<>();
metadata.put(Schema.SCHEMA_NAME_KEY, schemaName);
return metadata;
}
public List<Key> getVisibileKeys() {
List<Key> visibleKeys = new ArrayList<>();
for (Key key : allKeys) {
if (key.isVisible()) {
visibleKeys.add(key);
}
}
return visibleKeys;
}
/**
* Fill descriptions of the TAP_SCHEMA schema entities.
*/
private void fillTapSchemaDescriptions() {
TapSchemaModel tapSchemaModel = getTapSchemaModel();
Schema tapSchema = getChild(tapSchemaName);
tapSchema.setValue(DESCRIPTION_KEY, tapSchemaModel.getDescription());
for (TableModel tableModel : getTapSchemaModel().getTables().values()) {
Table table = tapSchema.getChild(tableModel.getName());
table.setValue(DESCRIPTION_KEY, tableModel.getDescription());
for (PropertyModel propertyModel : tableModel.getProperties().values()) {
Column column = table.getChild(propertyModel.getName());
column.setValue(DESCRIPTION_KEY, propertyModel.getDescription());
}
}
}