
rapture.repo.NVersionedRepo Maven / Gradle / Ivy
/**
* The MIT License (MIT)
*
* Copyright (c) 2011-2016 Incapture Technologies LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package rapture.repo;
import rapture.common.LockHandle;
import rapture.common.MessageFormat;
import rapture.common.exception.RaptureExceptionFactory;
import rapture.common.impl.jackson.JsonContent;
import rapture.common.model.DocumentMetadata;
import rapture.common.model.DocumentWithMeta;
import rapture.dsl.dparse.AbsoluteVersion;
import rapture.dsl.dparse.AsOfTimeDirective;
import rapture.dsl.dparse.BaseDirective;
import rapture.dsl.dparse.VersionDirective;
import rapture.lock.ILockingHandler;
import rapture.repo.meta.AbstractMetaBasedRepo;
import rapture.repo.meta.handler.VersionedMetaHandler;
import java.net.HttpURLConnection;
import java.util.Map;
import org.apache.log4j.Logger;
/**
* A repo that manages a "latest" version and a history of versions
*
* Also stores meta data about the current and historical versions
*
* @author amkimian
*/
public class NVersionedRepo extends AbstractMetaBasedRepo {
private static final Logger log = Logger.getLogger(NVersionedRepo.class);
public NVersionedRepo(Map config, KeyStore store, KeyStore versionKeyStore, KeyStore metaKeyStore, KeyStore attributeStore,
ILockingHandler lockHandler) {
super(store, lockHandler);
metaHandler = new VersionedMetaHandler(store, versionKeyStore, metaKeyStore, attributeStore);
}
@Override
public DocumentWithMeta revertDoc(String disp, BaseDirective directive) {
return metaHandler.revertDoc(disp, producer);
}
@Override
public boolean isVersioned() {
return true;
}
/**
* Archive old versions of the entire repo, including all docs.
*
* @param authority
* @param versionLimit
* @param timeLimit
* @param ensureVersionLimit
* @param user
* @return
*/
public boolean archiveRepoVersions(final String authority, final int versionLimit, final long timeLimit, final boolean ensureVersionLimit,
final String user) {
log.info("Archive repo " + authority);
RepoVisitor visitor = new RepoVisitor() {
@Override
public boolean visit(String name, JsonContent content, boolean isFolder) {
archiveDocumentVersions(name, versionLimit, timeLimit, ensureVersionLimit, user);
return true;
}
};
visitAll("", null, visitor);
return true;
}
/**
* Archives old versions
*
* @param docPath doc path
* @param versionLimit number of versions to retain
* @param timeLimit commits older than timeLimit will be archived
* @param ensureVersionLimit ensure number of versions to retain even if commit is older
* than timeLimit
* @param user
* @return
*/
public boolean archiveDocumentVersions(String docPath, int versionLimit, long timeLimit, boolean ensureVersionLimit, String user) {
if (versionLimit <= 0) {
log.error("versionLimit should > 0");
return false;
}
if (metaHandler.supportsVersionLookupByTime()) {
return archiveDocumentVersionsWithTimestampRepo(docPath, versionLimit, timeLimit, ensureVersionLimit, user);
}
else {
return archiveDocumentVersionsWithStandardRepo(docPath, versionLimit, timeLimit, ensureVersionLimit, user);
}
}
private boolean archiveDocumentVersionsWithStandardRepo(String docPath, int versionLimit, long timeLimit, boolean ensureVersionLimit, String user) {
try {
int latestVersion = metaHandler.getLatestMeta(docPath).getVersion();
int cutoffVersion = getCutoffVersion(docPath, versionLimit, timeLimit, ensureVersionLimit);
log.info(String.format("Archive doc %s: latestVersion=%d, cutoffVersion=%d", docPath, latestVersion, cutoffVersion));
// no version to archive
if (cutoffVersion < 1) {
return true;
}
// cutoff version is the latest version, delete the document
if (cutoffVersion == latestVersion) {
return removeDocument(docPath, user, "Archive old versions");
}
return metaHandler.deleteOldVersions(docPath, cutoffVersion);
} catch (Exception e) {
log.error("Failed to archive " + docPath, e);
return false;
}
}
private int getCutoffVersion(String docPath, int versionLimit, long timeLimit, boolean ensureVersionLimit) {
int versionsRemaining = versionLimit;
int cutoffVersion = -1;
DocumentMetadata metadata = metaHandler.getLatestMeta(docPath);
while (metadata != null) {
Long time = metadata.getModifiedTimestamp();
if (time == null && metadata.getWriteTime() != null) {
time = metadata.getWriteTime().getTime();
}
if (time == null || (time < timeLimit && !ensureVersionLimit)) {
// reached the cut off version
cutoffVersion = metadata.getVersion();
break;
} else {
versionsRemaining--;
if (versionsRemaining <= 0) {
cutoffVersion = metadata.getVersion() - 1;
break;
} else {
// move on to previous version
metadata = metaHandler.getVersionMeta(docPath, metadata.getVersion() - 1);
}
}
}
return cutoffVersion;
}
private boolean archiveDocumentVersionsWithTimestampRepo(String docPath, int versionLimit, long timeLimit, boolean ensureVersionLimit, String user) {
try {
DocumentMetadata latestMeta = metaHandler.getLatestMeta(docPath);
long latestTimestamp = latestMeta.getModifiedTimestamp();
long cutoffTimestamp = getCutoffTimestamp(docPath, versionLimit, timeLimit, ensureVersionLimit, latestMeta);
log.info(String.format("Archive doc %s: latestTimestamp=%d, cutoffTimestamp=%d", docPath, latestTimestamp, cutoffTimestamp));
// no version to archive
if (cutoffTimestamp < 1) {
return true;
}
// cutoff version is the latest version, delete the document
if (cutoffTimestamp == latestTimestamp) {
return removeDocument(docPath, user, "Archive old versions");
}
return metaHandler.deleteOldVersions(docPath, cutoffTimestamp);
} catch (Exception e) {
log.error("Failed to archive " + docPath, e);
return false;
}
}
private long getCutoffTimestamp(String docPath, int versionLimit, long timeLimit, boolean ensureVersionLimit, DocumentMetadata latestMeta) {
Long cutoffTimestamp = -1L;
int cutoffVersion = latestMeta.getVersion() - versionLimit;
DocumentMetadata metadata = latestMeta;
if (timeLimit > 0) {
// Jump right to the timestamp we're looking for, then iterate if we need to to keep versionLimit versions.
metadata = metaHandler.getMetaAtTimestamp(docPath, timeLimit);
}
while (metadata != null) {
cutoffTimestamp = metadata.getModifiedTimestamp();
if (cutoffTimestamp == null && metadata.getWriteTime() != null) {
cutoffTimestamp = metadata.getWriteTime().getTime();
}
if (cutoffTimestamp == null || (cutoffTimestamp < timeLimit && !ensureVersionLimit)) {
// reached the cut off version
break;
} else {
if (metadata.getVersion() <= cutoffVersion) {
break;
} else {
// move on to previous version
metadata = metaHandler.getMetaAtTimestamp(docPath, cutoffTimestamp - 1);
}
}
}
if (cutoffTimestamp == null) {
cutoffTimestamp = -1L;
}
return cutoffTimestamp.longValue();
}
@Override
protected DocumentWithMeta getVersionedDocumentWithMeta(String docPath, VersionDirective directive) {
if (directive instanceof AsOfTimeDirective) {
return getVersionedDocumentWithMetaByAsOfTime(docPath, (AsOfTimeDirective) directive);
}
else {
return metaHandler.getDocumentWithMeta(docPath, ((AbsoluteVersion) directive).getVersion());
}
}
private DocumentWithMeta getVersionedDocumentWithMetaByAsOfTime(String docPath, AsOfTimeDirective directive) {
if (metaHandler.supportsVersionLookupByTime()) {
DocumentWithMeta documentWithMeta = metaHandler.getDocumentWithMeta(docPath, directive.getAsOfTimeMillis());
if (documentWithMeta == null) {
// Document didn't exist at that time.
String[] parameters = {docPath, directive.getAsOfTime()};
throw RaptureExceptionFactory.create(HttpURLConnection.HTTP_NOT_FOUND,
new MessageFormat(Messages.getString("InvalidAsOfTime"), parameters));
}
return documentWithMeta;
}
else {
String[] parameters = {directive.getClass().getName()};
throw RaptureExceptionFactory.create(HttpURLConnection.HTTP_INTERNAL_ERROR,
new MessageFormat(Messages.getString("NVersionedRepository.InvalidDirective"), parameters));
}
}
@Override
public boolean addDocumentWithVersion(String docPath, String content, String user, String comment, boolean mustBeNew, int expectedVersion) {
String lockHolder = repoLockHandler.generateLockHolder();
boolean ret = false;
LockHandle lockHandle = repoLockHandler.acquireLock(lockHolder);
if (lockHandle != null) {
try {
ret = metaHandler.addDocumentWithExpectedVersion(docPath, content, user, comment, expectedVersion, producer);
} finally {
repoLockHandler.releaseLock(lockHolder, lockHandle);
}
} else {
throw RaptureExceptionFactory.create(HttpURLConnection.HTTP_INTERNAL_ERROR, Messages.getString("NVersionedRepo.nolockWrite")); //$NON-NLS-1$
}
return ret;
}
protected BaseDirective getAppropriateDirectiveFromAsOfTimeDirective(String docPath, AsOfTimeDirective directive) {
if (metaHandler.supportsVersionLookupByTime()) {
return directive;
}
Integer version = metaHandler.getVersionNumberAsOfTime(docPath, directive.getAsOfTime());
if (version == null) {
// Document didn't exist at that time.
String[] parameters = {docPath, directive.getAsOfTime()};
throw RaptureExceptionFactory.create(HttpURLConnection.HTTP_NOT_FOUND,
new MessageFormat(Messages.getString("InvalidAsOfTime"), parameters));
}
AbsoluteVersion newDirective = new AbsoluteVersion();
newDirective.setVersion(version);
return newDirective;
}
@Override
public DocumentWithMeta getDocAndMeta(String docPath, BaseDirective directive) {
if (directive instanceof AsOfTimeDirective) {
directive = getAppropriateDirectiveFromAsOfTimeDirective(docPath, (AsOfTimeDirective) directive);
}
return super.getDocAndMeta(docPath, directive);
}
@Override
public DocumentMetadata getMeta(String docPath, BaseDirective directive) {
if (directive instanceof AsOfTimeDirective) {
directive = getAppropriateDirectiveFromAsOfTimeDirective(docPath, (AsOfTimeDirective) directive);
}
return super.getMeta(docPath, directive);
}
@Override
public String getDocument(String docPath, BaseDirective directive) {
if (directive instanceof AsOfTimeDirective) {
directive = getAppropriateDirectiveFromAsOfTimeDirective(docPath, (AsOfTimeDirective) directive);
}
return super.getDocument(docPath, directive);
}
}