Project

General

Profile

1
package eu.dnetlib.enabling.database.utils;
2

    
3
import java.io.StringReader;
4
import java.sql.*;
5
import java.sql.Date;
6
import java.text.ParseException;
7
import java.text.SimpleDateFormat;
8
import java.util.*;
9
import java.util.concurrent.ArrayBlockingQueue;
10
import java.util.concurrent.BlockingQueue;
11
import java.util.concurrent.Executors;
12
import java.util.concurrent.TimeUnit;
13
import java.util.concurrent.atomic.AtomicReference;
14
import java.util.regex.Pattern;
15
import javax.sql.DataSource;
16

    
17
import com.google.common.collect.Lists;
18
import com.google.common.collect.Queues;
19
import com.google.common.collect.Sets;
20
import eu.dnetlib.enabling.database.DataSourceFactory;
21
import eu.dnetlib.enabling.database.TransactionTemplateFactory;
22
import eu.dnetlib.enabling.database.objects.DnetDatabase;
23
import eu.dnetlib.enabling.database.rmi.DatabaseException;
24
import eu.dnetlib.miscutils.datetime.DateUtils;
25
import eu.dnetlib.miscutils.functional.string.Sanitizer;
26
import org.apache.commons.lang.BooleanUtils;
27
import org.apache.commons.logging.Log;
28
import org.apache.commons.logging.LogFactory;
29
import org.apache.velocity.app.VelocityEngine;
30
import org.dom4j.Document;
31
import org.dom4j.DocumentHelper;
32
import org.dom4j.Element;
33
import org.dom4j.Node;
34
import org.dom4j.io.SAXReader;
35
import org.joda.time.DateTime;
36
import org.joda.time.format.ISODateTimeFormat;
37
import org.springframework.beans.factory.annotation.Required;
38
import org.springframework.dao.DataAccessException;
39
import org.springframework.jdbc.core.JdbcTemplate;
40
import org.springframework.jdbc.core.RowCallbackHandler;
41
import org.springframework.jdbc.support.rowset.SqlRowSet;
42
import org.springframework.transaction.TransactionStatus;
43
import org.springframework.transaction.support.TransactionCallback;
44
import org.springframework.transaction.support.TransactionTemplate;
45
import org.springframework.ui.velocity.VelocityEngineUtils;
46

    
47
public class DatabaseUtils {
48

    
49
	public static final String DNET_RESOURCE_ID_FIELD = "_dnet_resource_identifier_";
50
	private static final int BLOCKING_QUEUE_TIMEOUT = 300;
51
	private static final String SQL_DATE_FORMAT = "yyyy-MM-dd";
52
	private static final Log log = LogFactory.getLog(DatabaseUtils.class); // NOPMD by marko on 11/24/08 5:02 PM
53
	private static final int BLOCKING_QUEUE_SIZE = 200;
54
	private static final Set<String> TRUE_VALUES = Sets.newHashSet("true", "t", "yes", "y", "vero", "v");
55
	private static final Set<String> FALSE_VALUES = Sets.newHashSet("false", "f", "no", "n", "falso");
56
	private DataSourceFactory dataSourceFactory;
57
	private JdbcTemplateFactory jdbcTemplateFactory;
58
	private TransactionTemplateFactory transactionTemplateFactory;
59
	private String defaultDB;
60
	private VelocityEngine velocityEngine;
61
	private String dbPrefix;
62
	private int numbersOfRecordsForTransaction;
63

    
64
	private int blockingQueueTimeout = BLOCKING_QUEUE_TIMEOUT;
65

    
66
	public List<String> listCommonDBTables(final String database) throws DatabaseException {
67
		String query =
68
				"SELECT table_name FROM information_schema.tables " + "WHERE table_schema = 'public' " + "AND table_type != 'VIEW' "
69
						+ "AND table_name NOT LIKE '%_log'";
70
		return getTypedListFromSql(database, query, String.class);
71
	}
72

    
73
	public List<String> listCommonDBViews(final String database) throws DatabaseException {
74
		String query =
75
				"SELECT table_name FROM information_schema.tables " + "WHERE table_schema = 'public' " + "AND table_type = 'VIEW' "
76
						+ "AND table_name NOT LIKE '%_log'";
77
		return getTypedListFromSql(database, query, String.class);
78
	}
79

    
80
	public Map<String, TableDates> getTableDatesForDB(final String db) throws DatabaseException {
81
		Map<String, TableDates> res = new HashMap<String, TableDates>();
82

    
83
		for (String table : listCommonDBTables(db)) {
84
			try {
85
				TableDates dates = new TableDates();
86

    
87
				String query =
88
						"select lastinsert, lastupdate, lastdelete from " + "(select max(date) as lastinsert from " + table
89
								+ "_log where operation='insert') as t1, " + "(select max(date) as lastupdate from " + table
90
								+ "_log where operation='update') as t2, " + "(select max(date) as lastdelete from " + table
91
								+ "_log where operation='delete') as t3";
92

    
93
				SqlRowSet srs = executeSql(db, query, SqlRowSet.class);
94
				if (srs.next()) {
95
					dates.setLastInsert(srs.getDate("lastinsert"));
96
					dates.setLastUpdate(srs.getDate("lastupdate"));
97
					dates.setLastDelete(srs.getDate("lastdelete"));
98
				}
99
				res.put(table, dates);
100
			} catch (Exception e) {
101
				log.warn("Error obtaing dates for table " + table, e);
102
			}
103
		}
104
		return res;
105
	}
106

    
107
	public List<DnetDatabase> listAllDatabases() throws DatabaseException {
108
		final String query =
109
				"SELECT d.datname AS db, COALESCE(dsc.description,'')='isManaged' AS managed FROM pg_database d LEFT OUTER JOIN pg_shdescription dsc ON (d.oid = dsc.objoid) WHERE d.datname LIKE '"
110
						+ dbPrefix + "%' ORDER BY d.datname DESC";
111
		final JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(defaultDB);
112

    
113
		final List<DnetDatabase> list = Lists.newArrayList();
114
		for (Map<String, Object> map : jdbcTemplate.queryForList(query)) {
115
			list.add(new DnetDatabase(map.get("db").toString(), Boolean.parseBoolean(map.get("managed").toString())));
116
		}
117
		return list;
118

    
119
	}
120

    
121
	public <T> List<T> getTypedListFromSql(final String dbName, final String query, final Class<T> clazz) throws DatabaseException {
122
		JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(dbName);
123

    
124
		try {
125
			List<T> list = new ArrayList<T>();
126
			for (Object obj : jdbcTemplate.queryForList(query, clazz)) {
127
				list.add(clazz.cast(obj));
128
			}
129
			return list;
130
		} catch (DataAccessException e) {
131
			throw new DatabaseException(e);
132
		}
133
	}
134

    
135
	public List<String> getSimpleListFromSql(final String dbName, final String query) throws DatabaseException {
136

    
137
		JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(dbName);
138

    
139
		try {
140
			List<String> list = new ArrayList<String>();
141
			for (Object obj : jdbcTemplate.queryForList(query)) {
142
				list.add(obj.toString());
143
			}
144
			return list;
145
		} catch (DataAccessException e) {
146
			throw new DatabaseException(e);
147
		}
148
	}
149

    
150
	public void executeSql(final String db, final String query) throws DatabaseException {
151
		executeSql(db, query, Void.class);
152
	}
153

    
154
	@SuppressWarnings("unchecked")
155
	public <T> T executeSql(final String dbName, final String query, final Class<T> clazz) throws DatabaseException {
156

    
157
		final JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(dbName);
158
		try {
159
			if (clazz == Integer.class) return (T) jdbcTemplate.queryForObject(query, Integer.class);
160
			else if (clazz == List.class) return (T) jdbcTemplate.queryForList(query);
161
			else if (clazz == Map.class) return (T) jdbcTemplate.queryForMap(query);
162
			else if (clazz == SqlRowSet.class) return (T) jdbcTemplate.queryForRowSet(query);
163
			else if (clazz == BlockingQueue.class) {
164
				log.debug("Creating Queue");
165

    
166
				final ArrayBlockingQueue<Map<String, Object>> q = Queues.newArrayBlockingQueue(BLOCKING_QUEUE_SIZE);
167

    
168
				Runnable run = new Runnable() {
169

    
170
					@Override
171
					public void run() {
172
						try {
173
							jdbcTemplate.query(query, getRowCallback(q));
174
						} catch (Throwable e) {
175
							log.error("Exception executing SQL", e);
176
							throw new RuntimeException(e);
177
						}
178
						try {
179
							// An empty Map indicates the end of the resultset
180
							q.offer(new HashMap<String, Object>(), getBlockingQueueTimeout(), TimeUnit.SECONDS);
181
						} catch (InterruptedException e) {
182
							log.error("Error putting LAST element in queue");
183
							throw new RuntimeException(e);
184
						}
185
						log.debug(" -- End of Sql Resultset");
186
					}
187

    
188
					private RowCallbackHandler getRowCallback(final BlockingQueue<Map<String, Object>> q) {
189
						return new RowCallbackHandler() {
190

    
191
							@Override
192
							public void processRow(final ResultSet rs) throws SQLException {
193

    
194
								ResultSetMetaData md = rs.getMetaData();
195
								Map<String, Object> row = new HashMap<String, Object>();
196
								for (int i = 1; i <= md.getColumnCount(); i++) {
197
									row.put(md.getColumnName(i), rs.getObject(i));
198
								}
199
								try {
200
									if (!rs.isClosed() && !q.offer(row, getBlockingQueueTimeout(), TimeUnit.SECONDS)) {
201
										log.warn("The consumer doesn't consume my queue, I stop");
202
										rs.close();
203
										return;
204
									}
205
									log.debug("Putted element in queue");
206
								} catch (InterruptedException e) {
207
									log.error("Error putting element in queue");
208
									throw new RuntimeException(e);
209
								}
210
							}
211
						};
212
					}
213
				};
214
				Executors.newSingleThreadExecutor().submit(run);
215

    
216
				log.debug("Returned Queue");
217

    
218
				return (T) q;
219
			} else {
220
				jdbcTemplate.update(query);
221
				return null;
222
			}
223
		} catch (Throwable e) {
224
			throw new DatabaseException(e);
225
		}
226
	}
227

    
228
	public boolean contains(final String db, final String table, final String column, final String value) throws DatabaseException {
229
		String query = "";
230
		try {
231
			verifyParameters(db, table, column);
232
			query = "SELECT " + column + " FROM " + table + " WHERE " + column + " = '" + value + "'";
233
			List<String> res = getSimpleListFromSql(db, query);
234
			return res != null && res.size() > 0;
235
		} catch (Throwable e) {
236
			throw new DatabaseException("Error performing SQL: " + query, e);
237
		}
238
	}
239

    
240
	public List<Map<?, ?>> describeTable(final String database, final String table) throws DatabaseException {
241
		verifyParameters(database, table);
242

    
243
		try {
244
			JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(database);
245
			List<Map<?, ?>> response = new ArrayList<Map<?, ?>>();
246
			String query = "SELECT * FROM information_schema.columns WHERE table_name = ?";
247

    
248
			for (Object o : jdbcTemplate.queryForList(query, new Object[] { table })) {
249
				if (o instanceof Map<?, ?>) {
250
					response.add((Map<?, ?>) o);
251
				}
252
			}
253
			return response;
254
		} catch (DataAccessException e) {
255
			throw new DatabaseException(e);
256
		}
257
	}
258

    
259
	public String dumpTableAsXML(final String db, final String t) throws DatabaseException {
260
		return dumpTableAsDoc(db, t).asXML();
261
	}
262

    
263
	public Document dumpTableAsDoc(final String db, final String t) throws DatabaseException {
264
		Document doc = DocumentHelper.createDocument();
265

    
266
		Element root = doc.addElement("DB_TABLE");
267
		Element head = root.addElement("HEADER");
268

    
269
		head.addElement("DATABASE").addAttribute("value", db);
270
		head.addElement("TABLE").addAttribute("value", t);
271
		head.addElement("DATE").addAttribute("value", DateUtils.now_ISO8601());
272

    
273
		Element body = root.addElement("BODY");
274
		for (Document d : dumpTableAsList(db, t)) {
275
			body.add(d.getRootElement());
276
		}
277
		return doc;
278
	}
279

    
280
	public List<Document> dumpTableAsList(final String db, final String t) throws DatabaseException {
281
		JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(db);
282

    
283
		List<Document> list = new ArrayList<Document>();
284
		for (Object o : jdbcTemplate.queryForList("SELECT * FROM " + t)) {
285
			if (o instanceof Map<?, ?>) {
286
				list.add(rowToDocument((Map<?, ?>) o));
287
			}
288
		}
289
		return list;
290
	}
291

    
292
	public Document rowToDocument(final Map<?, ?> map) throws DatabaseException {
293
		Document doc = DocumentHelper.createDocument();
294

    
295
		Element row = doc.addElement("ROW");
296
		for (Map.Entry<?, ?> entry : map.entrySet()) {
297
			Element col = row.addElement("FIELD");
298
			col.addAttribute("name", "" + entry.getKey());
299
			addValue(col, entry.getValue());
300
		}
301
		return doc;
302
	}
303

    
304
	public Document getRowByResourceId(final String database, final String table, final String resourceId) throws DatabaseException {
305
		verifyParameters(database, table);
306

    
307
		JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(database);
308
		String query = "SELECT * FROM " + table + " WHERE " + DNET_RESOURCE_ID_FIELD + "=?";
309

    
310
		Map<?, ?> map = jdbcTemplate.queryForMap(query, resourceId);
311
		Document doc = DocumentHelper.createDocument();
312

    
313
		Element root = doc.addElement("DB_RECORD");
314
		Element head = root.addElement("HEADER");
315
		head.addElement("RESOURCE_IDENTIFIER").addAttribute("value", resourceId);
316
		head.addElement("DATABASE").addAttribute("value", database);
317
		head.addElement("TABLE").addAttribute("value", table);
318
		head.addElement("DATE").addAttribute("value", DateUtils.now_ISO8601());
319

    
320
		Element body = root.addElement("BODY");
321

    
322
		Element row = body.addElement("ROW");
323

    
324
		for (Map.Entry<?, ?> entry : map.entrySet()) {
325
			Element col = row.addElement("FIELD");
326
			col.addAttribute("name", "" + entry.getKey());
327
			addValue(col, entry.getValue());
328
		}
329

    
330
		return doc;
331
	}
332

    
333
	private void addValue(final Element elem, final Object value) throws DatabaseException {
334
		if (value instanceof Array) {
335
			try {
336
				for (Object o : (Object[]) ((Array) value).getArray()) {
337
					addValue(elem.addElement("ITEM"), o);
338
				}
339
			} catch (Exception e) {
340
				throw new DatabaseException("Error procsessing a Array", e);
341
			}
342
		} else if (value != null) {
343
			elem.addText(Sanitizer.sanitize(value.toString()));
344
		} else {
345
			elem.addAttribute("isNull", "true");
346
		}
347
	}
348

    
349
	private void verifyParameters(final String... params) throws DatabaseException {
350
		Pattern pattern = Pattern.compile("\\w{1,128}");
351

    
352
		for (String p : params) {
353
			log.debug("TESTING SQL PARAM:" + p);
354
			if (p == null) throw new DatabaseException("Parameter is null");
355
			else if (!pattern.matcher(p).matches()) throw new DatabaseException("Parameter [" + p + "] contains an invalid character");
356
			else {
357
				log.debug("TEST OK");
358
			}
359
		}
360
	}
361

    
362
	public void importFromIterable(final String db, final Iterable<String> iterable) throws DatabaseException {
363
		verifyParameters(db);
364

    
365
		final DataSource dataSource = dataSourceFactory.createDataSource(db);
366
		final JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(dataSource);
367
		final TransactionTemplate transactionTemplate = transactionTemplateFactory.createTransactionTemplate(dataSource);
368

    
369
		int counterTotal = 0;
370

    
371
		long start = DateUtils.now();
372

    
373
		List<GenericRow> rows = new ArrayList<GenericRow>();
374
		for (String prof : iterable) {
375
			rows.addAll(obtainListOfRows(prof));
376
			if (rows.size() > numbersOfRecordsForTransaction) {
377
				counterTotal += rows.size();
378
				importTransaction(jdbcTemplate, transactionTemplate, rows);
379
				rows.clear();
380
			}
381
		}
382
		counterTotal += rows.size();
383
		importTransaction(jdbcTemplate, transactionTemplate, rows);
384

    
385
		long end = DateUtils.now();
386

    
387
		log.info("**********************************************************");
388
		log.info("Processed " + counterTotal + " rows in " + ((end - start) / 1000) + " seconds");
389
		log.info("**********************************************************");
390
	}
391

    
392
	private void importTransaction(final JdbcTemplate jdbcTemplate, final TransactionTemplate transactionTemplate, List<GenericRow> rows)
393
			throws DatabaseException {
394
		if (rows != null && rows.size() > 0) {
395
			importTransactionInternal(jdbcTemplate, transactionTemplate, rows);
396
		}
397
	}
398

    
399
	private List<GenericRow> importTransactionInternal(final JdbcTemplate jdbcTemplate,
400
			final TransactionTemplate transactionTemplate,
401
			final List<GenericRow> rows) throws DatabaseException {
402

    
403
		final AtomicReference<DatabaseException> error = new AtomicReference<DatabaseException>();
404

    
405
		try {
406
			return transactionTemplate.execute(new TransactionCallback<List<GenericRow>>() {
407

    
408
				@Override
409
				public List<GenericRow> doInTransaction(final TransactionStatus status) {
410
					final List<GenericRow> ok = Lists.newArrayList();
411
					try {
412
						for (GenericRow row : rows) {
413
							if (row.isToDelete()) {
414
								deleteRow(jdbcTemplate, row.getTable(), row.getFields());
415
							} else {
416
								addOrUpdateRow(jdbcTemplate, row.getTable(), row.getFields());
417
							}
418
							ok.add(row);
419
						}
420
					} catch (DatabaseException e) {
421
						log.warn("Transaction failed", e);
422
						status.setRollbackOnly();
423
						error.set(e);
424
					}
425
					return ok;
426
				}
427
			});
428
		} finally {
429
			if (error.get() != null) {
430
				throw error.get();
431
			}
432
		}
433
	}
434

    
435
	protected void addOrUpdateRow(final JdbcTemplate jdbcTemplate, final String table, final Map<String, Object> rowFields) throws DatabaseException {
436
		try {
437

    
438
			if (log.isDebugEnabled()) {
439
				log.debug("Adding or updating element to table " + table);
440
			}
441
			verifyParameters(table);
442
			verifyParameters(rowFields.keySet().toArray(new String[rowFields.size()]));
443

    
444
			String fields = "";
445
			String values = "";
446
			List<Object> list = new ArrayList<Object>();
447

    
448
			for (Map.Entry<String, Object> e : rowFields.entrySet()) {
449
				if (!fields.isEmpty()) {
450
					fields += ",";
451
				}
452
				fields += e.getKey();
453
				if (!values.isEmpty()) {
454
					values += ",";
455
				}
456
				values += "?";
457
				list.add(e.getValue());
458
			}
459

    
460
			int count = 0;
461
			if (rowFields.containsKey(DNET_RESOURCE_ID_FIELD)) {
462
				List<Object> list2 = new ArrayList<Object>();
463
				list2.addAll(list);
464
				list2.add(rowFields.get(DNET_RESOURCE_ID_FIELD));
465
				count =
466
						jdbcTemplate.update("UPDATE " + table + " SET (" + fields + ") = (" + values + ") WHERE " + DNET_RESOURCE_ID_FIELD + "=?",
467
								list2.toArray());
468
			}
469
			if (count == 0) {
470
				jdbcTemplate.update("INSERT INTO " + table + " (" + fields + ") VALUES (" + values + ")", list.toArray());
471
			}
472
		} catch (final Exception e) {
473
			throw new DatabaseException("Error adding or updating record", e);
474
		}
475
	}
476

    
477
	protected void deleteRow(final JdbcTemplate jdbcTemplate, final String table, final Map<String, Object> rowFields) throws DatabaseException {
478
		if (log.isDebugEnabled()) {
479
			log.debug("Deleting element from table " + table);
480
		}
481
		verifyParameters(table);
482
		verifyParameters(rowFields.keySet().toArray(new String[rowFields.size()]));
483

    
484
		List<Object> list = new ArrayList<Object>();
485

    
486
		String where = "";
487

    
488
		for (Map.Entry<String, Object> e : rowFields.entrySet()) {
489
			if (!where.isEmpty()) {
490
				where += " AND ";
491
			}
492
			where += e.getKey() + "=?";
493
			list.add(e.getValue());
494
		}
495

    
496
		if (where.isEmpty()) throw new DatabaseException("Delete condition is empty");
497
		int n = jdbcTemplate.update("DELETE FROM " + table + " WHERE " + where, list.toArray());
498

    
499
		if (log.isDebugEnabled()) {
500
			log.debug("Number of Deleted records: " + n);
501
		}
502
	}
503

    
504
	public void deleteRowByResourceId(final String database, final String table, final String resourceIdentifier) throws DatabaseException {
505
		verifyParameters(database, table, resourceIdentifier);
506
		JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(database);
507
		jdbcTemplate.update("DELETE FROM " + table + " WHERE " + DNET_RESOURCE_ID_FIELD + "=?", resourceIdentifier);
508
	}
509

    
510
	public void clearTable(final String database, final String table) throws DatabaseException {
511
		verifyParameters(database, table);
512

    
513
		JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(database);
514
		jdbcTemplate.update("DELETE FROM " + table);
515
	}
516

    
517
	public void prepareManagementOfTable(final String database, final String table) throws DatabaseException {
518
		verifyParameters(database, table);
519
		JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(database);
520

    
521
		if (!isManagedTable(jdbcTemplate, table)) {
522
			jdbcTemplate.update(getSQLFromTemplate("manageTable", database, table, null));
523
			log.info("Added management of table " + table);
524
		}
525

    
526
		addLogTable(database, table);
527
	}
528

    
529
	public void addLogTable(final String database, final String table) throws DatabaseException {
530
		verifyParameters(database, table);
531
		JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(database);
532

    
533
		if (!isLoggedTable(jdbcTemplate, table)) {
534
			jdbcTemplate.update(getSQLFromTemplate("removeLogTable", database, table, null));
535
			jdbcTemplate.update(getSQLFromTemplate("addLogTable", database, table, null));
536
			log.info("Added logs of table " + table);
537
		}
538
	}
539

    
540
	public void removeManagementOfTable(final String database, final String table) throws DatabaseException {
541
		verifyParameters(database, table);
542
		JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(database);
543

    
544
		if (isManagedTable(jdbcTemplate, table)) {
545
			jdbcTemplate.update(getSQLFromTemplate("unmanageTable", database, table, null));
546
			log.info("Removed management of table " + table);
547
		}
548
		removeLogTable(database, table);
549
	}
550

    
551
	public void removeLogTable(final String database, final String table) throws DatabaseException {
552
		verifyParameters(database, table);
553
		JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(database);
554

    
555
		if (isLoggedTable(jdbcTemplate, table)) {
556
			jdbcTemplate.update(getSQLFromTemplate("removeLogTable", database, table, null));
557
			log.info("Removed logs of table " + table);
558
		}
559
	}
560

    
561
	public boolean isManagedTable(final String database, final String table) throws DatabaseException {
562
		verifyParameters(database, table);
563
		JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(database);
564
		return isManagedTable(jdbcTemplate, table);
565
	}
566

    
567
	private boolean isManagedTable(final JdbcTemplate jdbcTemplate, final String table) {
568
		return jdbcTemplate.queryForObject("SELECT count(*) FROM information_schema.columns WHERE table_name = ? AND column_name = ?", Integer.class,
569
				table, DNET_RESOURCE_ID_FIELD) == 1;
570
	}
571

    
572
	public boolean isLoggedTable(final String database, final String table) throws DatabaseException {
573
		verifyParameters(database, table);
574
		JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(database);
575
		return isLoggedTable(jdbcTemplate, table);
576
	}
577

    
578
	private boolean isLoggedTable(final JdbcTemplate jdbcTemplate, final String table) {
579
		return jdbcTemplate.queryForObject("SELECT count(*) FROM information_schema.tables WHERE table_name = ?", Integer.class,
580
				table + "_log") == 1;
581
	}
582

    
583
	public String getDefaultDnetIdentifier(final String database, final String table) throws DatabaseException {
584
		verifyParameters(database, table);
585
		JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(database);
586
		if (isManagedTable(jdbcTemplate, table)) return jdbcTemplate.queryForObject(
587
				"SELECT column_default FROM information_schema.columns WHERE table_name = ? AND column_name = ?", new Object[] {
588
						table, DNET_RESOURCE_ID_FIELD }, String.class);
589
		return "";
590
	}
591

    
592
	public void reassignDefaultDnetIdentifiers(final String db) throws DatabaseException {
593
		for (String t : listCommonDBTables(db)) {
594
			reassignDefaultDnetIdentifiers(db, t);
595
		}
596
	}
597

    
598
	public void reassignDefaultDnetIdentifiers(final String db, final String t) throws DatabaseException {
599
		if (!isManagedTable(db, t)) return;
600

    
601
		SqlRowSet rows =
602
				executeSql(db, "SELECT pg_attribute.attname as pkey FROM pg_index, pg_class, pg_attribute " + "WHERE pg_class.oid = '" + t + "'::regclass "
603
						+ "AND indrelid = pg_class.oid " + "AND pg_attribute.attrelid = pg_class.oid "
604
						+ "AND pg_attribute.attnum = any(pg_index.indkey) AND indisprimary " + "ORDER BY pkey", SqlRowSet.class);
605

    
606
		String defaultValue = "";
607
		while (rows.next()) {
608
			if (!defaultValue.isEmpty()) {
609
				defaultValue += "||'@@'||";
610
			}
611
			defaultValue += rows.getString("pkey");
612
		}
613
		executeSql(db, "UPDATE " + t + " SET " + DatabaseUtils.DNET_RESOURCE_ID_FIELD + " = " + defaultValue);
614
		log.info("Reassigned dnetId for table " + t);
615
	}
616

    
617
	public String getSQLFromTemplate(final String sqlTemplate, final String db, final String table, Map<String, Object> map) {
618
		if (map == null) {
619
			map = new HashMap<String, Object>();
620
		}
621

    
622
		map.put("mainDB", defaultDB);
623
		map.put("db", db);
624
		map.put("table", table);
625
		map.put("idField", DNET_RESOURCE_ID_FIELD);
626

    
627
		return VelocityEngineUtils.mergeTemplateIntoString(velocityEngine, "eu/dnetlib/enabling/database/velocity/" + sqlTemplate + ".sql.vm", "UTF-8", map);
628
	}
629

    
630
	public List<GenericRow> obtainListOfRows(final String xml) throws DatabaseException {
631
		try {
632
			Document doc = new SAXReader().read(new StringReader(xml));
633

    
634
			List<GenericRow> list = new ArrayList<GenericRow>();
635

    
636
			for (Object or : doc.selectNodes("//ROW")) {
637
				Element row = (Element) or;
638

    
639
				String table = row.valueOf("@table");
640

    
641
				if ((table == null) || table.isEmpty()) throw new DatabaseException("Attribute table is missing in XSLT");
642

    
643
				boolean toDelete = "deleted".equals(row.valueOf("@status"));
644

    
645
				Map<String, Object> fields = new HashMap<String, Object>();
646

    
647
				for (Object of : row.selectNodes("./FIELD")) {
648
					Node node = (Node) of;
649
					String key = node.valueOf("@name");
650
					String type = node.valueOf("@type");
651
					String format = node.valueOf("@format");
652
					String valueS = node.getText().trim();
653

    
654
					if ((key != null) && !key.isEmpty()) {
655
						Object value = valueS;
656
						if (type != null) {
657

    
658
							try {
659
								// probably an empty string in a typed field means null
660
								if ("".equals(valueS)) {
661
									value = null;
662
								} else if (type.equals("int")) {
663
									value = Integer.parseInt(valueS);
664
								} else if (type.equals("float")) {
665
									value = Float.parseFloat(valueS);
666
								} else if (type.equals("boolean")) {
667
									value = parseBoolean(valueS);
668
								} else if (type.equals("date")) {
669
									value = parseDate(valueS, format);
670
								} else if (type.equals("iso8601Date")) {
671
									DateTime date = ISODateTimeFormat.dateTimeParser().parseDateTime(valueS);
672
									value = date.toDate();
673
									// value = new DateUtils().parse(valueS);
674
								}
675
							} catch (IllegalArgumentException e) {
676
								log.fatal("cannot convert '" + valueS + "' to " + type, e);
677
								throw e;
678
							}
679
						}
680
						fields.put(key, value);
681
					}
682
				}
683

    
684
				list.add(new GenericRow(table, fields, toDelete));
685
			}
686
			return list;
687
		} catch (Exception e) {
688
			log.error("Error obtaining list of rows from xml: " + xml);
689
			throw new DatabaseException(e);
690
		}
691
	}
692

    
693
	protected boolean parseBoolean(final String s) {
694
		if (TRUE_VALUES.contains(s.toLowerCase().trim())) return true;
695
		if (FALSE_VALUES.contains(s.toLowerCase().trim())) return false;
696

    
697
		return BooleanUtils.toBoolean(s);
698
	}
699

    
700
	public void setManaged(final String dbName, final boolean managed) throws DatabaseException {
701
		verifyParameters(dbName);
702
		JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(dbName);
703
		if (managed) {
704
			jdbcTemplate.update("COMMENT ON DATABASE " + dbName + " IS 'isManaged'");
705
		} else {
706
			jdbcTemplate.update("COMMENT ON DATABASE " + dbName + " IS NULL");
707
		}
708
	}
709

    
710
	// public for testing
711
	public Date parseDate(final String date, String format) {
712
		if ((format == null) || format.isEmpty()) {
713
			format = SQL_DATE_FORMAT;
714
		}
715
		try {
716
			java.util.Date parsed = new SimpleDateFormat(format).parse(date);
717
			String ret = new SimpleDateFormat(SQL_DATE_FORMAT).format(parsed);
718
			return Date.valueOf(ret);
719
		} catch (ParseException e) {
720
			return null;
721
		}
722
	}
723

    
724
	@Required
725
	public void setVelocityEngine(final VelocityEngine velocityEngine) {
726
		this.velocityEngine = velocityEngine;
727
	}
728

    
729
	public String getDbPrefix() {
730
		return dbPrefix;
731
	}
732

    
733
	@Required
734
	public void setDbPrefix(final String dbPrefix) {
735
		this.dbPrefix = dbPrefix;
736
	}
737

    
738
	public DataSourceFactory getDataSourceFactory() {
739
		return dataSourceFactory;
740
	}
741

    
742
	@Required
743
	public void setDataSourceFactory(final DataSourceFactory dataSourceFactory) {
744
		this.dataSourceFactory = dataSourceFactory;
745
	}
746

    
747
	public JdbcTemplateFactory getJdbcTemplateFactory() {
748
		return jdbcTemplateFactory;
749
	}
750

    
751
	@Required
752
	public void setJdbcTemplateFactory(final JdbcTemplateFactory jdbcTemplateFactory) {
753
		this.jdbcTemplateFactory = jdbcTemplateFactory;
754
	}
755

    
756
	public TransactionTemplateFactory getTransactionTemplateFactory() {
757
		return transactionTemplateFactory;
758
	}
759

    
760
	@Required
761
	public void setTransactionTemplateFactory(final TransactionTemplateFactory transactionTemplateFactory) {
762
		this.transactionTemplateFactory = transactionTemplateFactory;
763
	}
764

    
765
	public int getNumbersOfRecordsForTransaction() {
766
		return numbersOfRecordsForTransaction;
767
	}
768

    
769
	@Required
770
	public void setNumbersOfRecordsForTransaction(final int numbersOfRecordsForTransaction) {
771
		this.numbersOfRecordsForTransaction = numbersOfRecordsForTransaction;
772
	}
773

    
774
	public String getDefaultDB() {
775
		return defaultDB;
776
	}
777

    
778
	@Required
779
	public void setDefaultDB(final String defaultDB) {
780
		this.defaultDB = defaultDB;
781
	}
782

    
783
	public int getBlockingQueueTimeout() {
784
		return blockingQueueTimeout;
785
	}
786

    
787
	public void setBlockingQueueTimeout(final int blockingQueueTimeout) {
788
		this.blockingQueueTimeout = blockingQueueTimeout;
789
	}
790

    
791
	public class TableDates {
792

    
793
		private Date lastInsert;
794
		private Date lastUpdate;
795
		private Date lastDelete;
796

    
797
		public Date getLastInsert() {
798
			return lastInsert;
799
		}
800

    
801
		public void setLastInsert(final Date lastInsert) {
802
			this.lastInsert = lastInsert;
803
		}
804

    
805
		public Date getLastUpdate() {
806
			return lastUpdate;
807
		}
808

    
809
		public void setLastUpdate(final Date lastUpdate) {
810
			this.lastUpdate = lastUpdate;
811
		}
812

    
813
		public Date getLastDelete() {
814
			return lastDelete;
815
		}
816

    
817
		public void setLastDelete(final Date lastDelete) {
818
			this.lastDelete = lastDelete;
819
		}
820
	}
821

    
822
}
(1-1/3)