package org.javers.spring.jpa;
import java.util.Optional;
import org.javers.common.validation.Validate;
import org.javers.core.Javers;
import org.javers.core.changelog.ChangeProcessor;
import org.javers.core.commit.Commit;
import org.javers.core.diff.Change;
import org.javers.core.diff.Diff;
import org.javers.core.diff.changetype.PropertyChange;
import org.javers.core.json.JsonConverter;
import org.javers.core.metamodel.object.CdoSnapshot;
import org.javers.core.metamodel.property.Property;
import org.javers.core.metamodel.type.JaversType;
import org.javers.repository.jql.GlobalIdDTO;
import org.javers.repository.jql.JqlQuery;
import org.javers.shadow.Shadow;
import org.javers.repository.sql.JaversSqlRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.*;
import javax.annotation.PostConstruct;
import javax.transaction.Transactional;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* Transactional wrapper for core JaVers instance.
* Provides integration with Spring JPA TransactionManager
*
* @author bartosz walacik
*/
public class JaversTransactionalDecorator implements Javers {
private static final Logger logger = LoggerFactory.getLogger(JaversTransactionalDecorator.class);
private final Javers delegate;
private final JaversSqlRepository javersSqlRepository;
private final PlatformTransactionManager txManager;
JaversTransactionalDecorator(Javers delegate, JaversSqlRepository javersSqlRepository, PlatformTransactionManager txManager) {
Validate.argumentsAreNotNull(delegate, javersSqlRepository, txManager);
this.delegate = delegate;
this.javersSqlRepository = javersSqlRepository;
this.txManager = txManager;
}
@Override
@Transactional
public Commit commit(String author, Object currentVersion) {
registerRollbackListener();
return delegate.commit(author, currentVersion);
}
@Override
@Transactional
public Commit commit(String author, Object currentVersion, Map<String, String> commitProperties) {
registerRollbackListener();
return delegate.commit(author, currentVersion, commitProperties);
}
@Override
@Transactional
public Commit commitShallowDelete(String author, Object deleted) {
return delegate.commitShallowDelete(author, deleted);
}
@Override
@Transactional
public Commit commitShallowDelete(String author, Object deleted, Map<String, String> properties) {
return delegate.commitShallowDelete(author, deleted, properties);
}
@Override
@Transactional
public Commit commitShallowDeleteById(String author, GlobalIdDTO globalId) {
return delegate.commitShallowDeleteById(author, globalId);
}
@Override
@Transactional
public Commit commitShallowDeleteById(String author, GlobalIdDTO globalId, Map<String, String> properties) {
return delegate.commitShallowDeleteById(author, globalId, properties);
}
@Override
public Diff compare(Object oldVersion, Object currentVersion) {
return delegate.compare(oldVersion, currentVersion);
}
@Override
public <T> Diff compareCollections(Collection<T> oldVersion, Collection<T> currentVersion, Class<T> itemClass) {
return delegate.compareCollections(oldVersion, currentVersion, itemClass);
}
@Override
public Diff initial(Object newDomainObject) {
return delegate.initial(newDomainObject);
}
@Transactional
@Override
public Optional<CdoSnapshot> getLatestSnapshot(Object localId, Class entityClass) {
return delegate.getLatestSnapshot(localId, entityClass);
}
@Transactional
@Override
public <T> List<Shadow<T>> findShadows(JqlQuery query) {
return delegate.findShadows(query);
}
@Transactional
@Override
public List<CdoSnapshot> findSnapshots(JqlQuery query) {
return delegate.findSnapshots(query);
}
@Transactional
@Override
public List<Change> findChanges(JqlQuery query) {
return delegate.findChanges(query);
}
@Override
public JsonConverter getJsonConverter() {
return delegate.getJsonConverter();
}
@Override
public <T> T processChangeList(List<Change> changes, ChangeProcessor<T> changeProcessor) {
return delegate.processChangeList(changes, changeProcessor);
}
@Override
public <T extends JaversType> T getTypeMapping(Type clientsType) {
return delegate.getTypeMapping(clientsType);
}
@PostConstruct
public void ensureSchema() {
TransactionTemplate tmpl = new TransactionTemplate(txManager);
tmpl.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
javersSqlRepository.ensureSchema();
}
});
}
@Override
public Property getProperty(PropertyChange propertyChange) {
return delegate.getProperty(propertyChange);
}
private void registerRollbackListener() {
if (javersSqlRepository.getConfiguration().isGlobalIdCacheDisabled()) {
return;
}
if(TransactionSynchronizationManager.isSynchronizationActive() &&
TransactionSynchronizationManager.isActualTransactionActive()) {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter(){
@Override
public void afterCompletion(int status) {
if (TransactionSynchronization.STATUS_ROLLED_BACK == status) {
logger.info("evicting javersSqlRepository local cache due to transaction rollback");
javersSqlRepository.evictCache();
}
}
});
}
}
}