strolch/src/main/java/ch/eitchnet/xmlpers/impl/DefaultPersistenceTransacti...

321 lines
9.4 KiB
Java
Raw Normal View History

2013-10-06 12:32:57 +02:00
/*
* Copyright (c) 2012, Robert von Burg
*
* All rights reserved.
*
* This file is part of the XXX.
*
* XXX is free software: you can redistribute
* it and/or modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* XXX 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 XXX. If not, see
* <http://www.gnu.org/licenses/>.
*/
package ch.eitchnet.xmlpers.impl;
2013-10-06 12:32:57 +02:00
import java.text.MessageFormat;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
2013-10-06 12:32:57 +02:00
import java.util.List;
import java.util.Map;
2013-10-06 12:32:57 +02:00
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.eitchnet.utils.helper.StringHelper;
import ch.eitchnet.utils.objectfilter.ObjectFilter;
import ch.eitchnet.xmlpers.api.FileDao;
import ch.eitchnet.xmlpers.api.IoMode;
import ch.eitchnet.xmlpers.api.MetadataDao;
import ch.eitchnet.xmlpers.api.ModificationResult;
import ch.eitchnet.xmlpers.api.ObjectDao;
2013-10-06 12:32:57 +02:00
import ch.eitchnet.xmlpers.api.PersistenceContext;
import ch.eitchnet.xmlpers.api.PersistenceRealm;
import ch.eitchnet.xmlpers.api.PersistenceTransaction;
import ch.eitchnet.xmlpers.api.TransactionCloseStrategy;
import ch.eitchnet.xmlpers.api.TransactionResult;
import ch.eitchnet.xmlpers.api.TransactionState;
import ch.eitchnet.xmlpers.api.XmlPersistenceException;
import ch.eitchnet.xmlpers.objref.ObjectReferenceCache;
2013-10-06 12:32:57 +02:00
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
public class DefaultPersistenceTransaction implements PersistenceTransaction {
private static final Logger logger = LoggerFactory.getLogger(DefaultPersistenceTransaction.class);
private final DefaultPersistenceRealm realm;
private final boolean verbose;
private final ObjectFilter objectFilter;
2013-10-06 12:32:57 +02:00
private final ObjectDao objectDao;
private final MetadataDao metadataDao;
private FileDao fileDao;
private IoMode ioMode;
2013-10-06 18:42:55 +02:00
private TransactionCloseStrategy closeStrategy;
private TransactionState state;
private long startTime;
private Date startTimeDate;
private TransactionResult txResult;
public DefaultPersistenceTransaction(DefaultPersistenceRealm realm, boolean verbose) {
this.startTime = System.nanoTime();
this.startTimeDate = new Date();
this.realm = realm;
2013-10-06 12:32:57 +02:00
this.verbose = verbose;
this.objectFilter = new ObjectFilter();
this.fileDao = new FileDao(this, realm.getPathBuilder(), verbose);
2013-10-06 12:32:57 +02:00
this.objectDao = new ObjectDao(this, this.fileDao, this.objectFilter);
this.metadataDao = new MetadataDao(realm.getPathBuilder(), this, verbose);
this.closeStrategy = TransactionCloseStrategy.COMMIT;
this.state = TransactionState.OPEN;
}
@Override
public void setTransactionResult(TransactionResult txResult) throws IllegalStateException {
if (this.txResult != null) {
String msg = "The transaction already has a result set!"; //$NON-NLS-1$
throw new IllegalStateException(msg);
}
this.txResult = txResult;
}
@Override
public TransactionResult getTransactionResult() throws IllegalStateException {
if (isOpen()) {
String msg = "The transaction is still open thus has no result yet! Either commit or rollback before calling this method"; //$NON-NLS-1$
throw new IllegalStateException(msg);
}
return this.txResult;
}
@Override
public PersistenceRealm getRealm() {
return this.realm;
2013-10-06 12:32:57 +02:00
}
@Override
public ObjectDao getObjectDao() {
return this.objectDao;
}
@Override
public MetadataDao getMetadataDao() {
return this.metadataDao;
}
@Override
public ObjectReferenceCache getObjectRefCache() {
return this.realm.getObjectRefCache();
}
@Override
public void setCloseStrategy(TransactionCloseStrategy closeStrategy) {
this.closeStrategy = closeStrategy;
}
@Override
public void close() throws XmlPersistenceException {
this.closeStrategy.close(this);
}
@Override
public void autoCloseableRollback() {
long start = System.nanoTime();
if (this.state == TransactionState.COMMITTED)
2013-10-06 18:42:55 +02:00
throw new IllegalStateException("Transaction has already been committed!"); //$NON-NLS-1$
if (this.state != TransactionState.ROLLED_BACK) {
unlockObjectRefs();
this.state = TransactionState.ROLLED_BACK;
2013-10-06 18:42:55 +02:00
this.objectFilter.clearCache();
long end = System.nanoTime();
long txDuration = end - this.startTime;
long closeDuration = end - start;
this.txResult.clear();
this.txResult.setState(this.state);
this.txResult.setStartTime(this.startTimeDate);
this.txResult.setTxDuration(txDuration);
this.txResult.setCloseDuration(closeDuration);
this.txResult.setRealm(this.realm.getRealmName());
this.txResult.setModificationByKey(Collections.<String, ModificationResult> emptyMap());
2013-10-06 18:42:55 +02:00
}
2013-10-06 12:32:57 +02:00
}
@Override
public void autoCloseableCommit() throws XmlPersistenceException {
2013-10-06 12:32:57 +02:00
long start = System.nanoTime();
2013-10-06 12:32:57 +02:00
try {
2013-10-06 12:32:57 +02:00
if (this.verbose) {
String msg = "Committing {0} operations in TX...";//$NON-NLS-1$
logger.info(MessageFormat.format(msg, this.objectFilter.sizeCache()));
}
Set<String> keySet = this.objectFilter.keySet();
Map<String, ModificationResult> modifications;
if (this.txResult == null)
modifications = null;
else
modifications = new HashMap<>(keySet.size());
2013-10-06 12:32:57 +02:00
for (String key : keySet) {
List<Object> removed = this.objectFilter.getRemoved(key);
if (removed.isEmpty()) {
if (this.verbose)
logger.info("No objects removed in this tx."); //$NON-NLS-1$
} else {
if (this.verbose)
logger.info(removed.size() + " objects removed in this tx."); //$NON-NLS-1$
for (Object object : removed) {
@SuppressWarnings("unchecked")
PersistenceContext<Object> ctx = (PersistenceContext<Object>) object;
this.fileDao.performDelete(ctx);
2013-10-06 12:32:57 +02:00
}
}
List<Object> updated = this.objectFilter.getUpdated(key);
if (updated.isEmpty()) {
if (this.verbose)
logger.info("No objects updated in this tx."); //$NON-NLS-1$
} else {
if (this.verbose)
logger.info(updated.size() + " objects updated in this tx."); //$NON-NLS-1$
for (Object object : updated) {
@SuppressWarnings("unchecked")
PersistenceContext<Object> ctx = (PersistenceContext<Object>) object;
this.fileDao.performUpdate(ctx);
2013-10-06 12:32:57 +02:00
}
}
List<Object> added = this.objectFilter.getAdded(key);
if (added.isEmpty()) {
if (this.verbose)
logger.info("No objects added in this tx."); //$NON-NLS-1$
} else {
if (this.verbose)
logger.info(added.size() + " objects added in this tx."); //$NON-NLS-1$
for (Object object : added) {
@SuppressWarnings("unchecked")
PersistenceContext<Object> ctx = (PersistenceContext<Object>) object;
this.fileDao.performCreate(ctx);
2013-10-06 12:32:57 +02:00
}
}
if (modifications != null) {
ModificationResult result = new ModificationResult(key, added, updated, removed);
modifications.put(key, result);
}
2013-10-06 12:32:57 +02:00
}
if (this.txResult != null) {
this.txResult.clear();
this.txResult.setState(TransactionState.COMMITTED);
this.txResult.setModificationByKey(modifications);
}
} catch (Exception e) {
if (this.txResult == null) {
long end = System.nanoTime();
long txDuration = end - this.startTime;
long closeDuration = end - start;
StringBuilder sb = new StringBuilder();
sb.append("TX has failed after "); //$NON-NLS-1$
sb.append(StringHelper.formatNanoDuration(txDuration));
sb.append(" with close operation taking "); //$NON-NLS-1$
sb.append(StringHelper.formatNanoDuration(closeDuration));
logger.info(sb.toString());
throw e;
}
this.txResult.clear();
this.txResult.setState(TransactionState.FAILED);
this.txResult.setModificationByKey(Collections.<String, ModificationResult> emptyMap());
2013-10-06 12:32:57 +02:00
} finally {
2013-10-06 12:32:57 +02:00
// clean up
unlockObjectRefs();
2013-10-06 12:32:57 +02:00
this.objectFilter.clearCache();
}
long end = System.nanoTime();
long txDuration = end - this.startTime;
long closeDuration = end - start;
if (this.txResult == null) {
StringBuilder sb = new StringBuilder();
sb.append("TX was completed after "); //$NON-NLS-1$
sb.append(StringHelper.formatNanoDuration(txDuration));
sb.append(" with close operation taking "); //$NON-NLS-1$
sb.append(StringHelper.formatNanoDuration(closeDuration));
logger.info(sb.toString());
} else {
this.txResult.setStartTime(this.startTimeDate);
this.txResult.setTxDuration(txDuration);
this.txResult.setCloseDuration(closeDuration);
this.txResult.setRealm(this.realm.getRealmName());
if (this.txResult.getState() == TransactionState.FAILED) {
String msg = "Failed to commit TX due to underlying exception: {0}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, this.txResult.getFailCause().getMessage());
throw new XmlPersistenceException(msg, this.txResult.getFailCause());
}
2013-10-06 12:32:57 +02:00
}
}
@SuppressWarnings("rawtypes")
private void unlockObjectRefs() {
List<PersistenceContext> lockedObjects = this.objectFilter.getAll(PersistenceContext.class);
for (PersistenceContext lockedObject : lockedObjects) {
lockedObject.getObjectRef().unlock();
}
}
2013-10-06 12:32:57 +02:00
@Override
2013-10-06 18:42:55 +02:00
public boolean isOpen() {
return this.state == TransactionState.OPEN;
2013-10-06 18:42:55 +02:00
}
@Override
public void setIoMode(IoMode ioMode) {
2013-10-06 18:42:55 +02:00
this.ioMode = ioMode;
}
@Override
public IoMode getIoMode() {
2013-10-06 18:42:55 +02:00
return this.ioMode;
2013-10-06 12:32:57 +02:00
}
}