Project

General

Profile

1
package eu.dnetlib.data.mdstore.modular.mongodb;
2

    
3
import java.util.ArrayList;
4
import java.util.Collections;
5
import java.util.Date;
6
import java.util.List;
7

    
8
import com.google.common.collect.Lists;
9
import com.mongodb.BasicDBList;
10
import com.mongodb.BasicDBObject;
11
import com.mongodb.DBObject;
12
import com.mongodb.WriteConcern;
13
import com.mongodb.client.FindIterable;
14
import com.mongodb.client.MongoCollection;
15
import com.mongodb.client.MongoDatabase;
16
import com.mongodb.client.MongoIterable;
17
import com.mongodb.client.model.Filters;
18
import com.mongodb.client.model.IndexOptions;
19
import com.mongodb.client.model.UpdateOptions;
20
import eu.dnetlib.data.mdstore.MDStoreServiceException;
21
import eu.dnetlib.data.mdstore.modular.connector.*;
22
import eu.dnetlib.data.mdstore.modular.mongodb.utils.MDStoreUtils;
23
import org.apache.commons.lang.StringUtils;
24
import org.apache.commons.logging.Log;
25
import org.apache.commons.logging.LogFactory;
26
import org.bson.conversions.Bson;
27
import org.joda.time.DateTime;
28
import org.joda.time.Days;
29
import org.springframework.beans.factory.annotation.Required;
30

    
31
/**
32
 * The Class MDStoreTransactionManager.
33
 */
34
public class MDStoreTransactionManagerImpl implements MDStoreTransactionManager {
35

    
36
	/** The Constant log. */
37
	private static final Log log = LogFactory.getLog(MDStoreTransactionManagerImpl.class);
38

    
39
	/**
40
	 * The table name.
41
	 */
42
	private static String TABLE_NAME = "metadataManager";
43

    
44
	/** The max number of concurrent transactions per mdstore. */
45
	private int maxTransactions = 1;
46

    
47
	/**
48
	 * The db.
49
	 */
50
	private MongoDatabase db;
51

    
52
	/**
53
	 * The manager table.
54
	 */
55
	private MongoCollection<DBObject> managerTable;
56

    
57
	/** The expired days. */
58
	private int expiredDays;
59

    
60
	private final IndexOptions options = new IndexOptions().background(true);
61

    
62
	/**
63
	 * Bootstrap manager.
64
	 */
65
	private void bootstrapManager() {
66
		log.debug("Bootstrap Manager start");
67
		final MongoCollection<DBObject> metadataColl = db.getCollection("metadata", DBObject.class);
68
		final FindIterable<DBObject> values = metadataColl.find();
69
		this.setManagerTable(db.getCollection(TABLE_NAME, DBObject.class));
70
		for (DBObject object : values) {
71
			final String id = (String) object.get("mdId");
72
			String newId = id;
73
			if (id.contains("_")) {
74
				newId = StringUtils.substringBefore(id, "_");
75
			}
76
			final BasicDBObject input = new BasicDBObject();
77
			input.put("mdId", id);
78
			input.put("currentId", newId);
79
			input.put("expiring", new String[] {});
80
			input.put("transactions", new String[] {});
81
			getManagerTable().insertOne(input);
82
			log.debug(String.format("Added %s to Metadata Manager data structure", id));
83

    
84
		}
85
		final BasicDBObject ensureIndex = new BasicDBObject();
86
		ensureIndex.put("mdId", 1);
87
		log.debug("Create index in MetadaManager ");
88
		this.getManagerTable().createIndex(ensureIndex, options);
89
	}
90

    
91
	/**
92
	 * Gets the DBObject describing an mdstore. null if there is no mdstore with the given id.
93
	 *
94
	 * @param mdstoreID
95
	 * @return DBObject or null
96
	 */
97
	private DBObject getMDStoreAsDBObject(String mdstoreID) throws MDStoreServiceException {
98
		final BasicDBObject query = new BasicDBObject();
99
		query.put("mdId", mdstoreID);
100
		final FindIterable<DBObject> it = this.getManagerTable().find(query);
101
		DBObject mdstoreInfo = it.first();
102
		return mdstoreInfo;
103
	}
104

    
105
	/**
106
	 * Verify consistency.
107
	 *
108
	 * @throws MDStoreServiceException
109
	 *             the MD store service exception
110
	 */
111
	@Override
112
	public void verifyConsistency() throws MDStoreServiceException {
113
		if (this.getManagerTable() == null) {
114
			if (!Lists.newArrayList(db.listCollectionNames()).contains(TABLE_NAME))
115
				bootstrapManager();
116
			else {
117
				this.setManagerTable(db.getCollection(TABLE_NAME, DBObject.class));
118
			}
119
		}
120
		if (this.getManagerTable() == null) throw new MDStoreServiceException("Something bad happen, unable to create managerTable");
121
	}
122

    
123
	/**
124
	 * {@inheritDoc}
125
	 *
126
	 * @see eu.dnetlib.data.mdstore.modular.connector.MDStoreTransactionManager#createMDStore(java.lang.String)
127
	 */
128
	@Override
129
	public void createMDStore(final String mdId) throws MDStoreServiceException {
130
		log.debug("Creating new mdstore");
131
		verifyConsistency();
132
		String newId = mdId;
133
		if (mdId.contains("_")) {
134
			newId = StringUtils.substringBefore(mdId, "_");
135
		}
136
		final BasicDBObject instance = new BasicDBObject();
137
		instance.put("mdId", mdId);
138
		instance.put("currentId", newId);
139
		instance.put("expiring", new String[] {});
140
		getManagerTable().insertOne(instance);
141
	}
142

    
143
	/**
144
	 * {@inheritDoc}
145
	 *
146
	 * @see eu.dnetlib.data.mdstore.modular.connector.MDStoreTransactionManager#dropMDStore(java.lang.String)
147
	 */
148
	@Override
149
	public void dropMDStore(final String mdId) throws MDStoreServiceException {
150
		verifyConsistency();
151
		log.debug("Droping MDStore: " + mdId);
152
		final BasicDBObject query = new BasicDBObject();
153
		query.put("mdId", mdId);
154
		final DBObject dropped = this.getManagerTable().findOneAndDelete(query);
155
		garbage();
156
		final String collectionName = (String) dropped.get("currentId");
157
		this.db.getCollection(collectionName).drop();
158
		this.db.getCollection("discarded-" + collectionName).drop();
159
	}
160

    
161
	/**
162
	 * {@inheritDoc}
163
	 *
164
	 * @see eu.dnetlib.data.mdstore.modular.connector.MDStoreTransactionManager#getMDStoreCollection(java.lang.String)
165
	 */
166
	@Override
167
	public String getMDStoreCollection(final String mdId) throws MDStoreServiceException {
168
		verifyConsistency();
169
		DBObject mdstoreInfo = getMDStoreAsDBObject(mdId);
170
		if (mdstoreInfo != null)
171
			return (String) mdstoreInfo.get("currentId");
172
		else return null;
173
	}
174

    
175
	/**
176
	 * {@inheritDoc}
177
	 *
178
	 * @see eu.dnetlib.data.mdstore.modular.connector.MDStoreTransactionManager#startTransaction(java.lang.String, boolean)
179
	 */
180
	@Override
181
	public String startTransaction(final String mdId, final boolean refresh) throws MDStoreServiceException {
182
		verifyConsistency();
183
		log.info("Start transaction for metadata store " + mdId);
184
		final DBObject mdstoreInfo = getMDStoreAsDBObject(mdId);
185
		if (mdstoreInfo == null) throw new MDStoreServiceException("Error, unable to find Mdstore with Id " + mdId);
186
		String idCreation = StringUtils.substringBefore(mdId, "_");
187
		idCreation = idCreation + "::" + System.currentTimeMillis();
188

    
189
		BasicDBList values;
190
		if (mdstoreInfo.containsField("transactions")) {
191
			values = (BasicDBList) mdstoreInfo.get("transactions");
192
			if (values.size() > getMaxTransactions())
193
				throw new MDStoreServiceException("Cannot create more than " + getMaxTransactions() + " transactions, found: " + values.size() + ", mdId:"
194
						+ mdId);
195
		} else {
196
			values = new BasicDBList();
197
		}
198
		final BasicDBObject transactionMetadata = new BasicDBObject();
199
		transactionMetadata.put("id", idCreation.toString());
200
		transactionMetadata.put("refresh", refresh);
201
		transactionMetadata.put("date", new Date());
202
		values.add(transactionMetadata);
203
		mdstoreInfo.put("transactions", values);
204
		this.getManagerTable().findOneAndReplace(new BasicDBObject("_id", mdstoreInfo.get("_id")), mdstoreInfo);
205
		return idCreation.toString();
206
	}
207

    
208
	/**
209
	 * {@inheritDoc}
210
	 *
211
	 * @see eu.dnetlib.data.mdstore.modular.connector.MDStoreTransactionManager#commit(java.lang.String, java.lang.String,
212
	 *      eu.dnetlib.data.mdstore.modular.connector.MDStore)
213
	 */
214
	@Override
215
	public boolean commit(final String transactionId, final String mdstoreId, final MDStore current) throws MDStoreServiceException {
216
		verifyConsistency();
217
		final DBObject mdstoreInfo = getMDStoreAsDBObject(mdstoreId);
218
		if (mdstoreInfo == null) throw new MDStoreServiceException("Error, unable to find Mdstore with Id " + mdstoreId);
219
		final BasicDBList transactions = (BasicDBList) mdstoreInfo.get("transactions");
220
		final DBObject transaction = findTransaction(transactions, transactionId);
221
		if (transaction == null) throw new MDStoreServiceException("Error, unable to find transaction with Id " + transactionId);
222
		final boolean refresh = (Boolean) transaction.get("refresh");
223
		transactions.remove(transaction);
224
		final String oldId = (String) mdstoreInfo.get("currentId");
225
		if (refresh) {
226
			mdstoreInfo.put("currentId", transactionId);
227
			final BasicDBList stillUsed = (BasicDBList) mdstoreInfo.get("expiring");
228
			if (stillUsed.size() == 0) {
229
				db.getCollection(oldId).drop();
230
				db.getCollection("discarded-" + oldId).drop();
231
			}
232
			log.debug("Replaced collection ");
233
		} else {
234
			log.debug("commit incremental ");
235
			updateIncremental(transactionId, oldId);
236
			db.getCollection(transactionId).drop();
237
			db.getCollection("discarded-" + transactionId).drop();
238
		}
239
		this.getManagerTable().findOneAndReplace(new BasicDBObject("_id", mdstoreInfo.get("_id")), mdstoreInfo);
240

    
241
		log.info("Committed transaction for metadata store " + mdstoreId);
242
		return true;
243
	}
244

    
245
	/**
246
	 * Find transaction.
247
	 *
248
	 * @param transactions
249
	 *            the transactions
250
	 * @param transactionId
251
	 *            the transaction id
252
	 * @return the DB object
253
	 */
254
	private DBObject findTransaction(final BasicDBList transactions, final String transactionId) {
255
		if (transactions.size() == 0) return null;
256
		for (int i = 0; i < transactions.size(); i++) {
257
			final BasicDBObject transaction = (BasicDBObject) transactions.get(i);
258
			final String id = (String) transaction.get("id");
259
			if (transactionId.equals(id)) return transaction;
260
		}
261
		return null;
262

    
263
	}
264

    
265
	/**
266
	 * Gets the db.
267
	 *
268
	 * @return the db
269
	 */
270
	public MongoDatabase getDb() {
271
		return db;
272
	}
273

    
274
	/**
275
	 * Sets the db.
276
	 *
277
	 * @param db
278
	 *            the db to set
279
	 */
280
	@Required
281
	public void setDb(final MongoDatabase db) {
282
		this.db = db;
283
	}
284

    
285
	/**
286
	 * {@inheritDoc}
287
	 *
288
	 * @see eu.dnetlib.data.mdstore.modular.connector.MDStoreTransactionManager#readMdStore(java.lang.String)
289
	 */
290
	@Override
291
	public String readMdStore(final String mdStoreId) throws MDStoreServiceException {
292
		verifyConsistency();
293
		final DBObject mdstoreInfo = getMDStoreAsDBObject(mdStoreId);
294
		if (mdstoreInfo == null) throw new MDStoreServiceException("Error, unable to find Mdstore with Id " + mdStoreId);
295
		final String currentId = (String) mdstoreInfo.get("currentId");
296
		final BasicDBList values = (BasicDBList) mdstoreInfo.get("expiring");
297
		updateMdstoreUsed(values, currentId);
298
		this.getManagerTable().findOneAndReplace(new BasicDBObject("_id", mdstoreInfo.get("_id")), mdstoreInfo);
299
		return currentId;
300

    
301
	}
302

    
303

    
304
	/**
305
	 * Update mdstore used.
306
	 *
307
	 * @param values
308
	 *            the values
309
	 * @param mdId
310
	 *            the md id
311
	 */
312
	private void updateMdstoreUsed(final BasicDBList values, final String mdId) {
313
		if (values.size() > 0) {
314
			for (int i = 0; i < values.size(); i++) {
315
				final DBObject obj = (DBObject) values.get(i);
316
				final String id = (String) obj.get("id");
317
				if (mdId.equals(id)) {
318
					obj.put("lastRead", new Date());
319
					return;
320
				}
321
			}
322
		}
323
		final BasicDBObject readStore = new BasicDBObject();
324
		readStore.put("id", mdId);
325
		readStore.put("lastRead", new Date());
326
		values.add(readStore);
327
	}
328

    
329
	/**
330
	 * Gets the manager table.
331
	 *
332
	 * @return the managerTable
333
	 */
334
	public MongoCollection<DBObject> getManagerTable() {
335
		return managerTable;
336
	}
337

    
338
	/**
339
	 * Sets the manager table.
340
	 *
341
	 * @param managerTable
342
	 *            the managerTable to set
343
	 */
344
	public void setManagerTable(final MongoCollection<DBObject> managerTable) {
345
		this.managerTable = managerTable;
346
	}
347

    
348
	/*
349
	 * (non-Javadoc)
350
	 *
351
	 * @see eu.dnetlib.data.mdstore.modular.connector.MDStoreTransactionManager#getInfoForCurrentMdStore(java.lang.String)
352
	 */
353
	@Override
354
	public MDStoreManagerInfo getInfoForCurrentMdStore(final String mdStoreId) throws MDStoreServiceException {
355
		verifyConsistency();
356
		final DBObject mdstoreInfo = getMDStoreAsDBObject(mdStoreId);
357
		if (mdstoreInfo == null) throw new MDStoreServiceException("Error, unable to find Mdstore with Id " + mdStoreId);
358
		final MDStoreManagerInfo result = new MDStoreManagerInfo();
359
		result.setCurrentId((String) mdstoreInfo.get("currentId"));
360
		result.setMdId((String) mdstoreInfo.get("mdId"));
361
		final BasicDBList values = (BasicDBList) mdstoreInfo.get("expiring");
362
		for (int i = 0; i < values.size(); i++) {
363
			final MDStoreExpiredInfo stillused = new MDStoreExpiredInfo();
364
			final DBObject value = (DBObject) values.get(i);
365
			stillused.setId((String) value.get("id"));
366
			stillused.setLastRead((Date) value.get("lastRead"));
367
			result.addExpiredItem(stillused);
368
		}
369
		final BasicDBList transactions = (BasicDBList) mdstoreInfo.get("transactions");
370
		if (transactions != null) {
371
			for (int i = 0; i < transactions.size(); i++) {
372
				final MDStoreTransactionInfo transaction = new MDStoreTransactionInfo();
373
				final DBObject value = (DBObject) transactions.get(i);
374
				final String transactionId = (String) value.get("id");
375
				transaction.setId(transactionId);
376
				transaction.setDate((Date) value.get("date"));
377
				transaction.setRefresh((Boolean) value.get("refresh"));
378
				transaction.setSize(db.getCollection(transactionId).count());
379
				result.addTransactionInfo(transaction);
380
			}
381
		}
382
		return result;
383
	}
384

    
385
	/*
386
	 * (non-Javadoc)
387
	 *
388
	 * @see eu.dnetlib.data.mdstore.modular.connector.MDStoreTransactionManager#dropUsed(java.lang.String, java.lang.String)
389
	 */
390
	@Override
391
	public Boolean dropUsed(final String mdId, final String idToDrop) throws MDStoreServiceException {
392
		verifyConsistency();
393
		final DBObject mdstoreInfo = getMDStoreAsDBObject(mdId);
394
		if (mdstoreInfo == null) throw new MDStoreServiceException("Error, unable to find Mdstore with Id " + mdId);
395
		return dropStore(mdstoreInfo, idToDrop, "expiring");
396
	}
397

    
398
	private boolean dropStore(DBObject mdstoreInfo, final String idToDrop, String transactionListName) throws MDStoreServiceException {
399
		final BasicDBList transactionList = (BasicDBList) mdstoreInfo.get(transactionListName);
400
		for (int i = 0; i < transactionList.size(); i++) {
401
			final DBObject value = (DBObject) transactionList.get(i);
402
			final String currentUsedId = (String) value.get("id");
403
			if (currentUsedId.equals(idToDrop)) {
404
				db.getCollection(idToDrop).drop();
405
				db.getCollection("discarded-" + idToDrop).drop();
406
				transactionList.remove(value);
407
				mdstoreInfo.put(transactionListName, transactionList);
408
				this.getManagerTable().findOneAndReplace(new BasicDBObject("_id", mdstoreInfo.get("_id")), mdstoreInfo);
409
				return true;
410
			}
411
		}
412
		throw new MDStoreServiceException("Error, unable to drop collection " + idToDrop);
413
	}
414

    
415
	/*
416
	 * (non-Javadoc)
417
	 *
418
	 * @see eu.dnetlib.data.mdstore.modular.connector.MDStoreTransactionManager#garbage()
419
	 */
420
	@Override
421
	public void garbage() throws MDStoreServiceException {
422
		verifyConsistency();
423
		log.info("Start garbage collection of MdStore");
424
		final FindIterable<DBObject> it = this.managerTable.find();
425
		int totalDeleted = 0;
426
		for (DBObject currentObject :  it){
427
			if (log.isDebugEnabled()) {
428
				log.debug("start to check id: " + currentObject.get("currentId"));
429
			}
430
			garbageExpiring(currentObject, (String) currentObject.get("currentId"));
431
			garbageTransactions(currentObject, (String) currentObject.get("currentId"));
432
			this.getManagerTable().findOneAndReplace(new BasicDBObject("_id", currentObject.get("_id")), currentObject);
433
		}
434

    
435
		// DELETING Collection that are not in the metadataManager table
436
		MongoIterable<String> collections = this.db.listCollectionNames();
437
		for (String collection : collections) {
438
			if ((collection.length() > 30) && (collection.contains("discarded-") == false)) {
439
				final DBObject item = getMetadataObjectForCollections(collection);
440

    
441
				if (shouldDelete(collection, item)) {
442
					if (log.isDebugEnabled()) {
443
						log.debug("delete collection: " + collection + " from mongo");
444
					}
445
					db.getCollection(collection).drop();
446
					db.getCollection("discarded-" + collection).drop();
447
					if (log.isDebugEnabled()) {
448
						log.debug("delete collection: discarded-" + collection + " from mongo");
449
					}
450
				}
451
			}
452
		}
453

    
454
		log.info("Complete garbage collection of MdStore, total store deleted: " + totalDeleted);
455
	}
456

    
457
	private DBObject getMetadataObjectForCollections(final String collectionName) {
458
		if (collectionName == null) return null;
459
		final String postfix = "_TURTdG9yZURTUmVzb3VyY2VzL01EU3RvcmVEU1Jlc291cmNlVHlwZQ==";
460
		final String tmp = collectionName.contains("discarded-") == true ? StringUtils.substringAfter(collectionName, "discarded-") : collectionName;
461
		final String collectionNameCleaned = StringUtils.substringBefore(tmp, "::") + postfix;
462

    
463
		//DBObject query = QueryBuilder.start("mdId").is(collectionNameCleaned).get();
464
		Bson query = Filters.eq("mdId", collectionNameCleaned);
465
		return this.managerTable.find(query).first();
466

    
467
	}
468

    
469
	private boolean shouldDelete(final String collectionName, final DBObject metadataManagerInstance) {
470
		log.debug("should delete instance "+metadataManagerInstance);
471
		if((metadataManagerInstance== null) || (metadataManagerInstance.get("currentId")== null)){
472
			log.debug("the instance has not currentID");
473
			return true;
474
		}
475
		String currentId = (String) metadataManagerInstance.get("currentId");
476
		if (collectionName.equals(currentId)) return false;
477
		BasicDBList expiringList = (BasicDBList) metadataManagerInstance.get("expiring");
478
		if (findInList(expiringList, collectionName, "id") == true) return false;
479
		BasicDBList transactionsList = (BasicDBList) metadataManagerInstance.get("transactions");
480
		return findInList(transactionsList, collectionName, "id") != true;
481
	}
482

    
483
	private boolean findInList(final BasicDBList list, final String object, final String tagname) {
484
		if (list == null) return false;
485
		for (int i = 0; i < list.size(); i++) {
486
			DBObject currentObject = (DBObject) list.get(i);
487
			final String id = (String) currentObject.get(tagname);
488
			if (id.equals(object)) return true;
489
		}
490
		return false;
491
	}
492

    
493
	/**
494
	 * Delete.
495
	 *
496
	 * @param list
497
	 *            the list
498
	 * @param toRemove
499
	 *            the to remove
500
	 */
501
	private void delete(final BasicDBList list, final List<DBObject> toRemove) {
502

    
503
		for (final DBObject obj : toRemove) {
504
			if (log.isDebugEnabled()) {
505
				log.debug("deleting " + obj);
506
			}
507
			list.remove(obj);
508
		}
509
	}
510

    
511
	/**
512
	 * Garbage transactions.
513
	 *
514
	 * @param currentObject
515
	 *            the current object
516
	 * @param currentId
517
	 *            the current id
518
	 */
519
	private void garbageTransactions(final DBObject currentObject, final String currentId) {
520
		if (log.isDebugEnabled()) {
521
			log.debug("Start garbage transactions ");
522
		}
523

    
524
		final BasicDBList expiring = (BasicDBList) currentObject.get("transactions");
525
		if ((expiring == null) || (expiring.size() <= getMaxTransactions())) return;
526

    
527
		List<DBObject> expiringList = Lists.newArrayList();
528

    
529
		for (int i = 0; i < expiring.size(); i++) {
530
			final DBObject cobj = (DBObject) expiring.get(i);
531
			if (cobj != null) {
532
				expiringList.add((DBObject) expiring.get(i));
533
			}
534

    
535
		}
536

    
537
		Collections.sort(expiringList, MDStoreUtils.getComparatorOnDate());
538

    
539
		List<DBObject> toRemove = Lists.newArrayList();
540
		int i = 0;
541

    
542
		// We should remove the k item less recent
543
		// where k = numberOftotalTransaction - maxNumberOfTransaction
544
		// k = numberOfItemToRemove
545

    
546
		while (((expiringList.size() - toRemove.size()) > getMaxTransactions()) || (i < expiringList.size())) {
547
			DBObject currentObj = expiringList.get(i++);
548
			String objectId = (String) currentObj.get("id");
549
			if (!objectId.equals(currentId)) {
550
				if (log.isDebugEnabled()) {
551
					log.debug("delete collection: " + objectId + " from mongo");
552
				}
553
				db.getCollection(objectId).drop();
554
				db.getCollection("discarded-" + objectId).drop();
555
				if (log.isDebugEnabled()) {
556
					log.debug("delete collection: discarded-" + objectId + " from mongo");
557
				}
558
				toRemove.add(currentObj);
559
			} else {
560
				if (log.isDebugEnabled()) {
561
					log.debug("Cannot remove transaction " + objectId + " because is the currentId: " + currentId);
562
				}
563
			}
564
		}
565

    
566
		delete(expiring, toRemove);
567
		log.info("Deleted " + toRemove.size() + " transactions, mdStore Id:" + currentObject.get("mdId"));
568
	}
569

    
570
	/**
571
	 * Garbage expiring.
572
	 *
573
	 * @param currentObject
574
	 *            the current object
575
	 * @param currentId
576
	 *            the current id
577
	 */
578
	private void garbageExpiring(final DBObject currentObject, final String currentId) {
579
		if (log.isDebugEnabled()) {
580
			log.debug("Start to search expiring mdstores for id: " + currentObject.get("mdId"));
581
		}
582
		final BasicDBList expiring = (BasicDBList) currentObject.get("expiring");
583
		final List<DBObject> toRemove = Lists.newArrayList();
584
		if (log.isDebugEnabled()) {
585
			if (expiring == null) {
586
				log.debug("expiring list is null");
587
			} else {
588
				log.debug("expiring list size is :" + expiring.size());
589
			}
590
		}
591
		if ((expiring == null) || (expiring.size() == 0)) {
592
			log.debug("Deleted  0  expired  collections, mdStore Id:" + currentObject.get("mdId"));
593
			return;
594
		}
595
		for (int i = 0; i < expiring.size(); i++) {
596
			final DBObject currentExpiredStore = (DBObject) expiring.get(i);
597
			final String currentUsedId = (String) currentExpiredStore.get("id");
598
			final Days d = getExpiringDays(currentExpiredStore, "lastRead");
599
			if (log.isDebugEnabled()) {
600
				log.debug("the store :" + currentId + " expired since " + d.getDays() + "days ");
601
			}
602
			// DELETE the collection where the last time they was read
603
			// is more than 3 days ago
604
			if (d.getDays() > getExpiredDays()) {
605
				if (currentUsedId.equals(currentId) == false) {
606
					db.getCollection(currentUsedId).drop();
607
					db.getCollection("discarded-" + currentUsedId).drop();
608
					log.debug("deleted collection " + currentUsedId);
609
				}
610
				toRemove.add(currentExpiredStore);
611
			}
612
		}
613
		delete(expiring, toRemove);
614
		log.debug("Deleted expired " + toRemove.size() + "collections, mdStore Id:" + currentObject.get("mdId"));
615
	}
616

    
617
	/**
618
	 * Gets the expiring days.
619
	 *
620
	 * @param value
621
	 *            the value
622
	 * @param paramName
623
	 *            the param name
624
	 * @return the expiring days
625
	 */
626
	private Days getExpiringDays(final DBObject value, final String paramName) {
627
		final Date lastRead = (Date) value.get(paramName);
628
		final DateTime last = new DateTime(lastRead);
629
		final DateTime today = new DateTime();
630
		final Days d = Days.daysBetween(last, today);
631
		return d;
632
	}
633

    
634
	/**
635
	 * Gets the expired days.
636
	 *
637
	 * @return the expiredDays
638
	 */
639
	public int getExpiredDays() {
640
		if (this.expiredDays == 0) return 3;
641
		return expiredDays;
642
	}
643

    
644
	/**
645
	 * Sets the expired days.
646
	 *
647
	 * @param expiredDays
648
	 *            the expiredDays to set
649
	 */
650
	public void setExpiredDays(final int expiredDays) {
651
		this.expiredDays = expiredDays;
652
	}
653

    
654
	/**
655
	 * Update incremental.
656
	 *
657
	 * @param transactionId
658
	 *            the transaction id
659
	 * @param currentId
660
	 *            the current id
661
	 */
662
	private void updateIncremental(final String transactionId, final String currentId) {
663
		final MongoCollection<DBObject> transaction = db.getCollection(transactionId, DBObject.class);
664
		final MongoCollection<DBObject> mdstore = db.getCollection(currentId, DBObject.class);
665
		final FindIterable<DBObject> it = transaction.find().noCursorTimeout(true);
666
		for (DBObject currentObj : it) {
667

    
668
			BasicDBObject newObj = new BasicDBObject();
669

    
670
			final String id = (String) currentObj.get("id");
671
			final String body = (String) currentObj.get("body");
672
			newObj.put("id", id);
673
			newObj.put("body", body);
674
			if (StringUtils.isNotBlank(id)) {
675
				//setting to journaled write concern to be sure that when the write returns everything has been flushed to disk (https://docs.mongodb.org/manual/faq/developers/#when-does-mongodb-write-updates-to-disk)
676
				//the explicit fsync command can't be run anymore: 'Command failed with error 13: 'fsync may only be run against the admin database.'
677
				final MongoCollection<DBObject> mdstoreWrite = mdstore.withWriteConcern(WriteConcern.JOURNALED);
678
				mdstoreWrite.replaceOne(new BasicDBObject("id", id), newObj, new UpdateOptions().upsert(true));
679
			}
680
		}
681
	}
682

    
683
	/*
684
	 * (non-Javadoc)
685
	 *
686
	 * @see eu.dnetlib.data.mdstore.modular.connector.MDStoreTransactionManager#dropTransaction(java.lang.String, java.lang.String)
687
	 */
688
	@Override
689
	public Boolean dropTransaction(final String mdId, final String idToDrop) throws MDStoreServiceException {
690
		verifyConsistency();
691
		final DBObject mdstoreInfo = getMDStoreAsDBObject(mdId);
692
		if (mdstoreInfo == null) throw new MDStoreServiceException("Error, unable to find Mdstore with Id " + mdId);
693
		return dropStore(mdstoreInfo, idToDrop, "transactions");
694
	}
695

    
696
	public void garbageTransactionsOnStart() throws MDStoreServiceException {
697
		verifyConsistency();
698
		FindIterable<DBObject> it = this.managerTable.find();
699

    
700
		final List<String> currentMdStoreId = Lists.newArrayList();
701
		for (DBObject currentObject : it){
702
			currentMdStoreId.add((String) currentObject.get("currentId"));
703
			final BasicDBList transactions = (BasicDBList) currentObject.get("transactions");
704
			if ((transactions != null) && (transactions.size() > 0)) {
705
				for (int i = 0; i < transactions.size(); i++) {
706
					final DBObject currentTransactions = (DBObject) transactions.get(i);
707
					final String id = (String) currentTransactions.get("id");
708
					db.getCollection(id).drop();
709
					db.getCollection("discarded-" + id).drop();
710
					log.debug("deleted collection " + id);
711
				}
712
				currentObject.put("transactions", new BasicDBList());
713
				this.getManagerTable().findOneAndReplace(new BasicDBObject("_id", currentObject.get("_id")), currentObject);
714
			}
715
		}
716

    
717
		//DELETING ALL THE DISCARDED COLLECTION THAT DISCARDED COLLECTION OF THE CURRENT MDSTORE
718
		final ArrayList<String> collectionsNames = Lists.newArrayList(db.listCollectionNames());
719

    
720
		for (String item : collectionsNames) {
721
			if (item.startsWith("discarded-")) {
722
				final String currentCollection = StringUtils.substringAfter(item, "discarded-");
723
				if (!currentMdStoreId.contains(currentCollection)) {
724
					log.info("Deleting discarded collection :" + item);
725
					this.db.getCollection(item).drop();
726
				}
727
			}
728
		}
729

    
730

    
731

    
732

    
733
	}
734

    
735
	/**
736
	 * Gets the max transactions.
737
	 *
738
	 * @return the maxTransactions
739
	 */
740
	public int getMaxTransactions() {
741
		return maxTransactions;
742
	}
743

    
744
	/**
745
	 * Sets the max transactions.
746
	 *
747
	 * @param maxTransactions
748
	 *            the maxTransactions to set
749
	 */
750
	public void setMaxTransactions(final int maxTransactions) {
751
		this.maxTransactions = maxTransactions;
752
	}
753
}
(2-2/5)