Project

General

Profile

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

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

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

    
46
public class DatabaseUtils {
47

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

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

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

    
77
	public List<DnetDatabase> listAllDatabases() throws DatabaseException {
78
		final String query =
79
				"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 '"
80
						+ dbPrefix + "%' ORDER BY d.datname DESC";
81
		final JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(defaultDB);
82

    
83
		final List<DnetDatabase> list = Lists.newArrayList();
84
		for (Map<String, Object> map : jdbcTemplate.queryForList(query)) {
85
			list.add(new DnetDatabase(map.get("db").toString(), Boolean.parseBoolean(map.get("managed").toString())));
86
		}
87
		return list;
88

    
89
	}
90

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

    
94
		try {
95
			List<T> list = new ArrayList<T>();
96
			for (Object obj : jdbcTemplate.queryForList(query, clazz)) {
97
				list.add(clazz.cast(obj));
98
			}
99
			return list;
100
		} catch (DataAccessException e) {
101
			throw new DatabaseException(e);
102
		}
103
	}
104

    
105
	public List<String> getSimpleListFromSql(final String dbName, final String query) throws DatabaseException {
106

    
107
		JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(dbName);
108

    
109
		try {
110
			List<String> list = new ArrayList<>();
111
			for (Object obj : jdbcTemplate.queryForList(query)) {
112
				list.add(obj.toString());
113
			}
114
			return list;
115
		} catch (DataAccessException e) {
116
			throw new DatabaseException(e);
117
		}
118
	}
119

    
120
	public void executeSql(final String db, final String query) throws DatabaseException {
121
		executeSql(db, query, Void.class);
122
	}
123

    
124
	@SuppressWarnings("unchecked")
125
	public <T> T executeSql(final String dbName, final String query, final Class<T> clazz) throws DatabaseException {
126

    
127
		final JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(dbName);
128
		try {
129
			if (clazz == Integer.class) return (T) jdbcTemplate.queryForObject(query, Integer.class);
130
			else if (clazz == List.class) return (T) jdbcTemplate.queryForList(query);
131
			else if (clazz == Map.class) return (T) jdbcTemplate.queryForMap(query);
132
			else if (clazz == SqlRowSet.class) return (T) jdbcTemplate.queryForRowSet(query);
133
			else if (clazz == BlockingQueue.class) {
134
				log.debug("Creating Queue");
135

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

    
138
				Executors.newSingleThreadExecutor().submit(() -> {
139
					try {
140
						jdbcTemplate.query(query, rs -> {
141

    
142
							ResultSetMetaData md = rs.getMetaData();
143
							Map<String, Object> row = new HashMap<>();
144
							for (int i = 1; i <= md.getColumnCount(); i++) {
145
								row.put(md.getColumnName(i), rs.getObject(i));
146
							}
147
							try {
148
								if (!rs.isClosed() && !q.offer(row, BLOCKING_QUEUE_TIMEOUT, TimeUnit.SECONDS)) {
149
									log.warn("The consumer doesn't consume my queue, I stop");
150
									rs.close();
151
									return;
152
								}
153
								log.debug("Putted element in queue");
154
							} catch (InterruptedException e) {
155
								log.error("Error putting element in queue");
156
								throw new RuntimeException(e);
157
							}
158
						});
159
					} catch (Throwable e) {
160
						log.error("Exception executing SQL", e);
161
						throw new RuntimeException(e);
162
					}
163
					try {
164
						// An empty Map indicates the end of the resultset
165
						q.offer(new HashMap<>(), BLOCKING_QUEUE_TIMEOUT, TimeUnit.SECONDS);
166
					} catch (InterruptedException e) {
167
						log.error("Error putting LAST element in queue");
168
						throw new RuntimeException(e);
169
					}
170
					log.debug(" -- End of Sql Resultset");
171
				});
172

    
173
				log.debug("Returned Queue");
174

    
175
				return (T) q;
176
			} else {
177
				jdbcTemplate.update(query);
178
				return null;
179
			}
180
		} catch (Throwable e) {
181
			throw new DatabaseException(e);
182
		}
183
	}
184

    
185
	public boolean contains(final String db, final String table, final String column, final String value) throws DatabaseException {
186
		String query = "";
187
		try {
188
			verifyParameters(db, table, column);
189
			query = "SELECT " + column + " FROM " + table + " WHERE " + column + " = '" + value + "'";
190
			List<String> res = getSimpleListFromSql(db, query);
191
			return res != null && res.size() > 0;
192
		} catch (Throwable e) {
193
			throw new DatabaseException("Error performing SQL: " + query, e);
194
		}
195
	}
196

    
197
	public List<Map<?, ?>> describeTable(final String database, final String table) throws DatabaseException {
198
		verifyParameters(database, table);
199

    
200
		try {
201
			JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(database);
202
			List<Map<?, ?>> response = new ArrayList<>();
203
			String query = "SELECT * FROM information_schema.columns WHERE table_name = ?";
204

    
205
			for (Object o : jdbcTemplate.queryForList(query, new Object[] { table })) {
206
				if (o instanceof Map<?, ?>) {
207
					response.add((Map<?, ?>) o);
208
				}
209
			}
210
			return response;
211
		} catch (DataAccessException e) {
212
			throw new DatabaseException(e);
213
		}
214
	}
215

    
216
	public String dumpTableAsXML(final String db, final String t) throws DatabaseException {
217
		return dumpTableAsDoc(db, t).asXML();
218
	}
219

    
220
	public Document dumpTableAsDoc(final String db, final String t) throws DatabaseException {
221
		Document doc = DocumentHelper.createDocument();
222

    
223
		Element root = doc.addElement("DB_TABLE");
224
		Element head = root.addElement("HEADER");
225

    
226
		head.addElement("DATABASE").addAttribute("value", db);
227
		head.addElement("TABLE").addAttribute("value", t);
228
		head.addElement("DATE").addAttribute("value", DateUtils.now_ISO8601());
229

    
230
		Element body = root.addElement("BODY");
231
		for (Document d : dumpTableAsList(db, t)) {
232
			body.add(d.getRootElement());
233
		}
234
		return doc;
235
	}
236

    
237
	public List<Document> dumpTableAsList(final String db, final String t) throws DatabaseException {
238
		JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(db);
239

    
240
		List<Document> list = new ArrayList<Document>();
241
		for (Object o : jdbcTemplate.queryForList("SELECT * FROM " + t)) {
242
			if (o instanceof Map<?, ?>) {
243
				list.add(rowToDocument((Map<?, ?>) o));
244
			}
245
		}
246
		return list;
247
	}
248

    
249
	public Document rowToDocument(final Map<?, ?> map) throws DatabaseException {
250
		Document doc = DocumentHelper.createDocument();
251

    
252
		Element row = doc.addElement("ROW");
253
		for (Map.Entry<?, ?> entry : map.entrySet()) {
254
			Element col = row.addElement("FIELD");
255
			col.addAttribute("name", "" + entry.getKey());
256
			addValue(col, entry.getValue());
257
		}
258
		return doc;
259
	}
260

    
261
	public Document getRowByResourceId(final String database, final String table, final String resourceId) throws DatabaseException {
262
		verifyParameters(database, table);
263

    
264
		JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(database);
265
		String query = "SELECT * FROM " + table + " WHERE " + DNET_RESOURCE_ID_FIELD + "=?";
266

    
267
		Map<?, ?> map = jdbcTemplate.queryForMap(query, resourceId);
268
		Document doc = DocumentHelper.createDocument();
269

    
270
		Element root = doc.addElement("DB_RECORD");
271
		Element head = root.addElement("HEADER");
272
		head.addElement("RESOURCE_IDENTIFIER").addAttribute("value", resourceId);
273
		head.addElement("DATABASE").addAttribute("value", database);
274
		head.addElement("TABLE").addAttribute("value", table);
275
		head.addElement("DATE").addAttribute("value", DateUtils.now_ISO8601());
276

    
277
		Element body = root.addElement("BODY");
278

    
279
		Element row = body.addElement("ROW");
280

    
281
		for (Map.Entry<?, ?> entry : map.entrySet()) {
282
			Element col = row.addElement("FIELD");
283
			col.addAttribute("name", "" + entry.getKey());
284
			addValue(col, entry.getValue());
285
		}
286

    
287
		return doc;
288
	}
289

    
290
	private void addValue(final Element elem, final Object value) throws DatabaseException {
291
		if (value instanceof Array) {
292
			try {
293
				for (Object o : (Object[]) ((Array) value).getArray()) {
294
					addValue(elem.addElement("ITEM"), o);
295
				}
296
			} catch (Exception e) {
297
				throw new DatabaseException("Error procsessing a Array", e);
298
			}
299
		} else if (value != null) {
300
			elem.addText(Sanitizer.sanitize(value.toString()));
301
		} else {
302
			elem.addAttribute("isNull", "true");
303
		}
304
	}
305

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

    
309
		for (String p : params) {
310
			log.debug("TESTING SQL PARAM:" + p);
311
			if (p == null) throw new DatabaseException("Parameter is null");
312
			else if (!pattern.matcher(p).matches()) throw new DatabaseException("Parameter [" + p + "] contains an invalid character");
313
			else {
314
				log.debug("TEST OK");
315
			}
316
		}
317
	}
318

    
319
	public void importFromIterable(final String db, final Iterable<String> iterable) throws DatabaseException {
320
		verifyParameters(db);
321

    
322
		final DataSource dataSource = dataSourceFactory.createDataSource(db);
323
		final JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(dataSource);
324
		final TransactionTemplate transactionTemplate = transactionTemplateFactory.createTransactionTemplate(dataSource);
325

    
326
		int counterTotal = 0;
327

    
328
		long start = DateUtils.now();
329

    
330
		final List<GenericRow> rows = new ArrayList<>();
331
		for (String s : iterable) {
332
			rows.addAll(obtainListOfRows(s));
333
			if (rows.size() > numbersOfRecordsForTransaction) {
334
				counterTotal += rows.size();
335
				importTransaction(jdbcTemplate, transactionTemplate, rows);
336
				rows.clear();
337
			}
338
		}
339
		counterTotal += rows.size();
340
		importTransaction(jdbcTemplate, transactionTemplate, rows);
341

    
342
		long end = DateUtils.now();
343

    
344
		log.info("**********************************************************");
345
		log.info("Processed " + counterTotal + " rows in " + ((end - start) / 1000) + " seconds");
346
		log.info("**********************************************************");
347
	}
348

    
349
	private void importTransaction(final JdbcTemplate jdbcTemplate, final TransactionTemplate transactionTemplate, List<GenericRow> rows)
350
			throws DatabaseException {
351
		if (rows != null && rows.size() > 0) {
352
			importTransactionInternal(jdbcTemplate, transactionTemplate, rows);
353
		}
354
	}
355

    
356
	private List<GenericRow> importTransactionInternal(final JdbcTemplate jdbcTemplate,
357
			final TransactionTemplate transactionTemplate,
358
			final List<GenericRow> rows) throws DatabaseException {
359

    
360
		final AtomicReference<DatabaseException> error = new AtomicReference<>();
361

    
362
		try {
363
			return transactionTemplate.execute(status -> {
364
				final List<GenericRow> ok = Lists.newArrayList();
365
				try {
366
					for (GenericRow row : rows) {
367
						if (row.isToDelete()) {
368
							deleteRow(jdbcTemplate, row);
369
						} else {
370
							addOrUpdateRow(jdbcTemplate, row);
371
						}
372
						ok.add(row);
373
					}
374
				} catch (DatabaseException e) {
375
					log.warn("Transaction failed", e);
376
					status.setRollbackOnly();
377
					error.set(e);
378
				}
379
				return ok;
380
			});
381
		} finally {
382
			if (error.get() != null) {
383
				throw error.get();
384
			}
385
		}
386
	}
387

    
388
	protected void addOrUpdateRow(final JdbcTemplate jdbcTemplate, final GenericRow row) throws DatabaseException {
389
		try {
390

    
391
			if (log.isDebugEnabled()) {
392
				log.debug("Adding or updating element to table " + row.getTable());
393
			}
394
			verifyParameters(row.getTable());
395
			verifyParameters(row.getFields().keySet().toArray(new String[row.getFields().size()]));
396

    
397
			final List<Map<String, Object>> res =
398
					jdbcTemplate.queryForList("SELECT 1 FROM datasources d WHERE d.id = ? and d.managed is true", row.getDatasourceId());
399

    
400
			if (res != null && !res.isEmpty()) {
401
				log.debug(String.format("avoiding to update managed datasource %s", row.getDatasourceId()));
402
				return;
403
			}
404

    
405
			String fields = "";
406
			String values = "";
407
			List<Object> list = new ArrayList<>();
408

    
409
			for (Map.Entry<String, Object> e : row.getFields().entrySet()) {
410
				if (!fields.isEmpty()) {
411
					fields += ",";
412
				}
413
				fields += e.getKey();
414
				if (!values.isEmpty()) {
415
					values += ",";
416
				}
417
				values += "?";
418
				list.add(e.getValue());
419
			}
420

    
421
			int count = 0;
422
			if (row.getFields().containsKey(DNET_RESOURCE_ID_FIELD)) {
423
				List<Object> list2 = new ArrayList<>();
424
				list2.addAll(list);
425
				list2.add(row.getFields().get(DNET_RESOURCE_ID_FIELD));
426

    
427
				count = jdbcTemplate.update(String.format("UPDATE %s SET (%s) = (%s) WHERE %s = ?", row.getTable(), fields, values, DNET_RESOURCE_ID_FIELD), list2.toArray());
428
			}
429
			if (count == 0) {
430
				jdbcTemplate.update(String.format("INSERT INTO %s (%s) VALUES (%s)", row.getTable(), fields, values), list.toArray());
431
			}
432
		} catch (final Exception e) {
433
			throw new DatabaseException("Error adding or updating record", e);
434
		}
435
	}
436

    
437
	protected void deleteRow(final JdbcTemplate jdbcTemplate, final GenericRow row) throws DatabaseException {
438
		if (log.isDebugEnabled()) {
439
			log.debug("Deleting element from table " + row.getTable());
440
		}
441
		verifyParameters(row.getTable());
442
		verifyParameters(row.getFields().keySet().toArray(new String[row.getFields().size()]));
443

    
444
		List<Object> list = new ArrayList<>();
445

    
446
		String where = "";
447

    
448
		for (Map.Entry<String, Object> e : row.getFields().entrySet()) {
449
			if (!where.isEmpty()) {
450
				where += " AND ";
451
			}
452
			where += e.getKey() + "=?";
453
			list.add(e.getValue());
454
		}
455

    
456
		if (where.isEmpty()) throw new DatabaseException("Delete condition is empty");
457
		int n = jdbcTemplate.update("DELETE FROM " + row.getTable() + " WHERE " + where, list.toArray());
458

    
459
		if (log.isDebugEnabled()) {
460
			log.debug("Number of Deleted records: " + n);
461
		}
462
	}
463

    
464
	public void deleteRowByResourceId(final String database, final String table, final String resourceIdentifier) throws DatabaseException {
465
		verifyParameters(database, table, resourceIdentifier);
466
		JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(database);
467
		jdbcTemplate.update("DELETE FROM " + table + " WHERE " + DNET_RESOURCE_ID_FIELD + "=?", resourceIdentifier);
468
	}
469

    
470
	public void clearTable(final String database, final String table) throws DatabaseException {
471
		verifyParameters(database, table);
472

    
473
		JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(database);
474
		jdbcTemplate.update("DELETE FROM " + table);
475
	}
476

    
477
	public void prepareManagementOfTable(final String database, final String table) throws DatabaseException {
478
		verifyParameters(database, table);
479
		JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(database);
480

    
481
		if (!isManagedTable(jdbcTemplate, table)) {
482
			jdbcTemplate.update(getSQLFromTemplate("manageTable", database, table, null));
483
			log.info("Added management of table " + table);
484
		}
485
	}
486

    
487
	public void removeManagementOfTable(final String database, final String table) throws DatabaseException {
488
		verifyParameters(database, table);
489
		JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(database);
490

    
491
		if (isManagedTable(jdbcTemplate, table)) {
492
			jdbcTemplate.update(getSQLFromTemplate("unmanageTable", database, table, null));
493
			log.info("Removed management of table " + table);
494
		}
495
	}
496

    
497
	public boolean isManagedTable(final String database, final String table) throws DatabaseException {
498
		verifyParameters(database, table);
499
		JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(database);
500
		return isManagedTable(jdbcTemplate, table);
501
	}
502

    
503
	private boolean isManagedTable(final JdbcTemplate jdbcTemplate, final String table) {
504
		return jdbcTemplate.queryForObject("SELECT count(*) FROM information_schema.columns WHERE table_name = ? AND column_name = ?", Integer.class,
505
				table, DNET_RESOURCE_ID_FIELD) == 1;
506
	}
507

    
508
	public String getDefaultDnetIdentifier(final String database, final String table) throws DatabaseException {
509
		verifyParameters(database, table);
510
		JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(database);
511
		if (isManagedTable(jdbcTemplate, table)) return jdbcTemplate.queryForObject(
512
				"SELECT column_default FROM information_schema.columns WHERE table_name = ? AND column_name = ?", new Object[] {
513
						table, DNET_RESOURCE_ID_FIELD }, String.class);
514
		return "";
515
	}
516

    
517
	public void reassignDefaultDnetIdentifiers(final String db) throws DatabaseException {
518
		for (String t : listCommonDBTables(db)) {
519
			reassignDefaultDnetIdentifiers(db, t);
520
		}
521
	}
522

    
523
	public void reassignDefaultDnetIdentifiers(final String db, final String t) throws DatabaseException {
524
		if (!isManagedTable(db, t)) return;
525

    
526
		SqlRowSet rows =
527
				executeSql(db, "SELECT pg_attribute.attname as pkey FROM pg_index, pg_class, pg_attribute " + "WHERE pg_class.oid = '" + t + "'::regclass "
528
						+ "AND indrelid = pg_class.oid " + "AND pg_attribute.attrelid = pg_class.oid "
529
						+ "AND pg_attribute.attnum = any(pg_index.indkey) AND indisprimary " + "ORDER BY pkey", SqlRowSet.class);
530

    
531
		String defaultValue = "";
532
		while (rows.next()) {
533
			if (!defaultValue.isEmpty()) {
534
				defaultValue += "||'@@'||";
535
			}
536
			defaultValue += rows.getString("pkey");
537
		}
538
		executeSql(db, "UPDATE " + t + " SET " + DatabaseUtils.DNET_RESOURCE_ID_FIELD + " = " + defaultValue);
539
		log.info("Reassigned dnetId for table " + t);
540
	}
541

    
542
	public String getSQLFromTemplate(final String sqlTemplate, final String db, final String table, Map<String, Object> map) {
543
		if (map == null) {
544
			map = new HashMap<>();
545
		}
546

    
547
		map.put("mainDB", defaultDB);
548
		map.put("db", db);
549
		map.put("table", table);
550
		map.put("idField", DNET_RESOURCE_ID_FIELD);
551

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

    
555
	public List<GenericRow> obtainListOfRows(final String xml) throws DatabaseException {
556
		try {
557
			final Document doc = new SAXReader().read(new StringReader(xml));
558

    
559
			final String datasourceId = doc.valueOf("//ROWS/ROW[@table='datasources']/FIELD[@name='id']/text()");
560
			if (StringUtils.isBlank(datasourceId)) {
561
				throw new DatabaseException("datasource Id is missing, check your mappings!");
562
			}
563

    
564
			final List<GenericRow> list = Lists.newArrayList();
565

    
566
			for (Object or : doc.selectNodes("//ROW")) {
567
				Element row = (Element) or;
568

    
569
				String table = row.valueOf("@table");
570

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

    
573
				boolean toDelete = "deleted".equals(row.valueOf("@status"));
574

    
575
				Map<String, Object> fields = new HashMap<String, Object>();
576

    
577
				for (Object of : row.selectNodes("./FIELD")) {
578
					Node node = (Node) of;
579
					String key = node.valueOf("@name");
580
					String type = node.valueOf("@type");
581
					String format = node.valueOf("@format");
582
					String valueS = node.getText().trim();
583

    
584
					if ((key != null) && !key.isEmpty()) {
585
						Object value = valueS;
586
						if (type != null) {
587

    
588
							try {
589
								// probably an empty string in a typed field means null
590
								if ("".equals(valueS)) {
591
									value = null;
592
								} else if (type.equals("int")) {
593
									value = Integer.parseInt(valueS);
594
								} else if (type.equals("float")) {
595
									value = Float.parseFloat(valueS);
596
								} else if (type.equals("boolean")) {
597
									value = parseBoolean(valueS);
598
								} else if (type.equals("date")) {
599
									value = parseDate(valueS, format);
600
								} else if (type.equals("iso8601Date")) {
601
									DateTime date = ISODateTimeFormat.dateTimeParser().parseDateTime(valueS);
602
									value = date.toDate();
603
									// value = new DateUtils().parse(valueS);
604
								}
605
							} catch (IllegalArgumentException e) {
606
								log.fatal("cannot convert '" + valueS + "' to " + type, e);
607
								throw e;
608
							}
609
						}
610
						fields.put(key, value);
611
					}
612
				}
613

    
614
				list.add(new GenericRow(datasourceId, table, fields, toDelete));
615
			}
616
			return list;
617
		} catch (Exception e) {
618
			log.error("Error obtaining list of rows from xml: " + xml);
619
			throw new DatabaseException(e);
620
		}
621
	}
622

    
623
	protected boolean parseBoolean(final String s) {
624
		if (TRUE_VALUES.contains(s.toLowerCase().trim())) return true;
625
		if (FALSE_VALUES.contains(s.toLowerCase().trim())) return false;
626

    
627
		return BooleanUtils.toBoolean(s);
628
	}
629

    
630
	public void setManaged(final String dbName, final boolean managed) throws DatabaseException {
631
		verifyParameters(dbName);
632
		JdbcTemplate jdbcTemplate = jdbcTemplateFactory.createJdbcTemplate(dbName);
633
		if (managed) {
634
			jdbcTemplate.update("COMMENT ON DATABASE " + dbName + " IS 'isManaged'");
635
		} else {
636
			jdbcTemplate.update("COMMENT ON DATABASE " + dbName + " IS NULL");
637
		}
638
	}
639

    
640
	// public for testing
641
	public Date parseDate(final String date, String format) {
642
		if ((format == null) || format.isEmpty()) {
643
			format = SQL_DATE_FORMAT;
644
		}
645
		try {
646
			java.util.Date parsed = new SimpleDateFormat(format).parse(date);
647
			String ret = new SimpleDateFormat(SQL_DATE_FORMAT).format(parsed);
648
			return Date.valueOf(ret);
649
		} catch (ParseException e) {
650
			return null;
651
		}
652
	}
653

    
654
	@Required
655
	public void setVelocityEngine(final VelocityEngine velocityEngine) {
656
		this.velocityEngine = velocityEngine;
657
	}
658

    
659
	public String getDbPrefix() {
660
		return dbPrefix;
661
	}
662

    
663
	@Required
664
	public void setDbPrefix(final String dbPrefix) {
665
		this.dbPrefix = dbPrefix;
666
	}
667

    
668
	public DataSourceFactory getDataSourceFactory() {
669
		return dataSourceFactory;
670
	}
671

    
672
	@Required
673
	public void setDataSourceFactory(final DataSourceFactory dataSourceFactory) {
674
		this.dataSourceFactory = dataSourceFactory;
675
	}
676

    
677
	public JdbcTemplateFactory getJdbcTemplateFactory() {
678
		return jdbcTemplateFactory;
679
	}
680

    
681
	@Required
682
	public void setJdbcTemplateFactory(final JdbcTemplateFactory jdbcTemplateFactory) {
683
		this.jdbcTemplateFactory = jdbcTemplateFactory;
684
	}
685

    
686
	public TransactionTemplateFactory getTransactionTemplateFactory() {
687
		return transactionTemplateFactory;
688
	}
689

    
690
	@Required
691
	public void setTransactionTemplateFactory(final TransactionTemplateFactory transactionTemplateFactory) {
692
		this.transactionTemplateFactory = transactionTemplateFactory;
693
	}
694

    
695
	public int getNumbersOfRecordsForTransaction() {
696
		return numbersOfRecordsForTransaction;
697
	}
698

    
699
	@Required
700
	public void setNumbersOfRecordsForTransaction(final int numbersOfRecordsForTransaction) {
701
		this.numbersOfRecordsForTransaction = numbersOfRecordsForTransaction;
702
	}
703

    
704
	public String getDefaultDB() {
705
		return defaultDB;
706
	}
707

    
708
	@Required
709
	public void setDefaultDB(final String defaultDB) {
710
		this.defaultDB = defaultDB;
711
	}
712

    
713
}
(1-1/3)