blob: ba2a2241fe01630f26a03a106886b2f9c5fbaf98 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2007 Boeing.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Boeing - initial API and implementation
*******************************************************************************/
package org.eclipse.osee.framework.skynet.core.revision;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.osee.framework.core.client.ClientSessionManager;
import org.eclipse.osee.framework.core.enums.ConflictStatus;
import org.eclipse.osee.framework.core.enums.ModificationType;
import org.eclipse.osee.framework.core.exception.BranchMergeException;
import org.eclipse.osee.framework.core.exception.OseeArgumentException;
import org.eclipse.osee.framework.core.exception.OseeCoreException;
import org.eclipse.osee.framework.core.exception.OseeDataStoreException;
import org.eclipse.osee.framework.core.exception.TransactionDoesNotExist;
import org.eclipse.osee.framework.core.model.Branch;
import org.eclipse.osee.framework.core.model.TransactionRecord;
import org.eclipse.osee.framework.database.core.ConnectionHandler;
import org.eclipse.osee.framework.database.core.IOseeStatement;
import org.eclipse.osee.framework.database.core.OseeSql;
import org.eclipse.osee.framework.database.core.SQL3DataType;
import org.eclipse.osee.framework.jdk.core.util.time.GlobalTime;
import org.eclipse.osee.framework.skynet.core.artifact.Artifact;
import org.eclipse.osee.framework.skynet.core.artifact.ArtifactLoad;
import org.eclipse.osee.framework.skynet.core.artifact.ArtifactLoader;
import org.eclipse.osee.framework.skynet.core.artifact.BranchManager;
import org.eclipse.osee.framework.skynet.core.conflict.ArtifactConflictBuilder;
import org.eclipse.osee.framework.skynet.core.conflict.AttributeConflict;
import org.eclipse.osee.framework.skynet.core.conflict.AttributeConflictBuilder;
import org.eclipse.osee.framework.skynet.core.conflict.Conflict;
import org.eclipse.osee.framework.skynet.core.conflict.ConflictBuilder;
import org.eclipse.osee.framework.skynet.core.transaction.TransactionManager;
/**
* @author Theron Virgin
* @author Jeff C. Phillips
*/
public class ConflictManagerInternal {
private static final String CONFLICT_CLEANUP =
"DELETE FROM osee_conflict t1 WHERE merge_branch_id = ? and NOT EXISTS (SELECT 'X' FROM osee_join_artifact WHERE query_id = ? and t1.conflict_id = art_id)";
private static final String GET_DESTINATION_BRANCHES =
"SELECT dest_branch_id FROM osee_merge WHERE source_branch_id = ?";
private static final String GET_MERGE_DATA =
"SELECT commit_transaction_id, merge_branch_id FROM osee_merge WHERE source_branch_id = ? AND dest_branch_id = ?";
private static final String GET_COMMIT_TRANSACTION_COMMENT =
"SELECT transaction_id FROM osee_tx_details WHERE osee_comment = ? AND branch_id = ?";
private ConflictManagerInternal() {
}
public static List<Conflict> getConflictsPerBranch(TransactionRecord commitTransaction, IProgressMonitor monitor) throws OseeCoreException {
monitor.beginTask(String.format("Loading Merge Manager for Transaction %d", commitTransaction.getId()), 100);
monitor.subTask("Finding Database stored conflicts");
ArrayList<Conflict> conflicts = new ArrayList<Conflict>();
IOseeStatement chStmt = ConnectionHandler.getStatement();
try {
chStmt.runPreparedQuery(ClientSessionManager.getSql(OseeSql.CONFLICT_GET_HISTORICAL_ATTRIBUTES),
commitTransaction.getId());
while (chStmt.next()) {
AttributeConflict attributeConflict =
new AttributeConflict(chStmt.getInt("source_gamma_id"), chStmt.getInt("dest_gamma_id"),
chStmt.getInt("art_id"), commitTransaction, chStmt.getString("source_value"),
chStmt.getInt("attr_id"), chStmt.getInt("attr_type_id"),
BranchManager.getBranch(chStmt.getInt("merge_branch_id")),
BranchManager.getBranch(chStmt.getInt("dest_branch_id")));
attributeConflict.setStatus(ConflictStatus.valueOf(chStmt.getInt("status")));
conflicts.add(attributeConflict);
}
} finally {
chStmt.close();
monitor.done();
}
return conflicts;
}
public static List<Conflict> getConflictsPerBranch(Branch sourceBranch, Branch destinationBranch, TransactionRecord baselineTransaction, IProgressMonitor monitor) throws OseeCoreException {
@SuppressWarnings("unused")
// This is for bulk loading so we do not lose our references
Collection<Artifact> bulkLoadedArtifacts;
List<ConflictBuilder> conflictBuilders = new ArrayList<ConflictBuilder>();
List<Conflict> conflicts = new ArrayList<Conflict>();
Set<Integer> artIdSet = new HashSet<Integer>();
Set<Integer> artIdSetDontShow = new HashSet<Integer>();
Set<Integer> artIdSetDontAdd = new HashSet<Integer>();
//Check to see if the branch has already been committed, then use the transaction version
int commitTransactionId = getCommitTransaction(sourceBranch, destinationBranch);
if (commitTransactionId > 0) {
try {
return getConflictsPerBranch(TransactionManager.getTransactionId(commitTransactionId), monitor);
} catch (TransactionDoesNotExist ex) {
}
}
if (sourceBranch != null && destinationBranch != null) {
monitor.beginTask(String.format("Loading Merge Manager for Branch %d into Branch %d", sourceBranch.getId(),
destinationBranch.getId()), 100);
monitor.subTask("Finding Database stored conflicts");
}
if (sourceBranch == null || destinationBranch == null) {
throw new OseeArgumentException(String.format("Source Branch = %s Destination Branch = %s",
sourceBranch == null ? "NULL" : sourceBranch.getId(),
destinationBranch == null ? "NULL" : destinationBranch.getId()));
}
TransactionRecord commonTransaction = findCommonTransaction(sourceBranch, destinationBranch);
loadArtifactVersionConflicts(ClientSessionManager.getSql(OseeSql.CONFLICT_GET_ARTIFACTS_DEST), sourceBranch,
destinationBranch, baselineTransaction, conflictBuilders, artIdSet, artIdSetDontShow, artIdSetDontAdd,
monitor, commonTransaction);
loadArtifactVersionConflicts(ClientSessionManager.getSql(OseeSql.CONFLICT_GET_ARTIFACTS_SRC), sourceBranch,
destinationBranch, baselineTransaction, conflictBuilders, artIdSet, artIdSetDontShow, artIdSetDontAdd,
monitor, commonTransaction);
loadAttributeConflictsNew(sourceBranch, destinationBranch, baselineTransaction, conflictBuilders, artIdSet,
monitor, commonTransaction);
for (Integer integer : artIdSetDontAdd) {
artIdSet.remove(integer);
}
if (artIdSet.isEmpty()) {
return conflicts;
}
monitor.subTask("Creating and/or maintaining the Merge Branch");
Branch mergeBranch =
BranchManager.getOrCreateMergeBranch(sourceBranch, destinationBranch, new ArrayList<Integer>(artIdSet));
if (mergeBranch == null) {
throw new BranchMergeException("Could not create the Merge Branch.");
}
monitor.worked(15);
bulkLoadedArtifacts = preloadConflictArtifacts(sourceBranch, destinationBranch, mergeBranch, artIdSet, monitor);
System.currentTimeMillis();
//Don't create the conflicts for attributes on an artifact that is deleted etc.
for (ConflictBuilder conflictBuilder : conflictBuilders) {
Conflict conflict = getConflict(conflictBuilder, mergeBranch, artIdSetDontShow);
if (conflict != null) {
conflicts.add(conflict);
}
}
cleanUpConflictDB(conflicts, mergeBranch.getId(), monitor);
return conflicts;
}
private static Conflict getConflict(ConflictBuilder conflictBuilder, Branch mergeBranch, Set<Integer> artIdSetDontShow) throws OseeCoreException {
Conflict conflict = conflictBuilder.getConflict(mergeBranch, artIdSetDontShow);
if (conflict != null) {
try {
conflict.computeStatus();
} catch (OseeCoreException ex) {
throw ex;
}
}
return conflict;
}
private static Collection<Artifact> preloadConflictArtifacts(Branch sourceBranch, Branch destinationBranch, Branch mergeBranch, Collection<Integer> artIdSet, IProgressMonitor monitor) throws OseeCoreException {
Collection<Artifact> artifacts = null;
monitor.subTask("Preloading Artifacts Associated with the Conflicts");
if (artIdSet != null && !artIdSet.isEmpty()) {
int queryId = ArtifactLoader.getNewQueryId();
Timestamp insertTime = GlobalTime.GreenwichMeanTimestamp();
List<Object[]> insertParameters = new LinkedList<Object[]>();
for (int artId : artIdSet) {
insertParameters.add(new Object[] {queryId, insertTime, artId, sourceBranch.getId(), SQL3DataType.INTEGER});
insertParameters.add(new Object[] {queryId, insertTime, artId, destinationBranch.getId(),
SQL3DataType.INTEGER});
insertParameters.add(new Object[] {queryId, insertTime, artId, mergeBranch.getId(), SQL3DataType.INTEGER});
}
artifacts =
ArtifactLoader.loadArtifacts(queryId, ArtifactLoad.FULL, null, insertParameters, true, false, true);
}
monitor.worked(25);
return artifacts;
}
/**
* @param sourceBranch
* @param destinationBranch
* @param baselineTransaction
* @param conflictBuilders
* @param artIdSet
* @throws OseeCoreException
*/
private static void loadArtifactVersionConflicts(String sql, Branch sourceBranch, Branch destinationBranch, TransactionRecord baselineTransaction, Collection<ConflictBuilder> conflictBuilders, Set<Integer> artIdSet, Set<Integer> artIdSetDontShow, Set<Integer> artIdSetDontAdd, IProgressMonitor monitor, TransactionRecord transactionId) throws OseeCoreException {
boolean hadEntries = false;
monitor.subTask("Finding Artifact Version Conflicts");
IOseeStatement chStmt = ConnectionHandler.getStatement();
try {
chStmt.runPreparedQuery(sql, sourceBranch.getId(), sourceBranch.getBaseTransaction().getId(),
destinationBranch.getId(), transactionId != null ? transactionId.getId() : 0);
ArtifactConflictBuilder artifactConflictBuilder;
int artId = 0;
while (chStmt.next()) {
hadEntries = true;
int nextArtId = chStmt.getInt("art_id");
int sourceGamma = chStmt.getInt("source_gamma");
int destGamma = chStmt.getInt("dest_gamma");
int sourceModType = chStmt.getInt("source_mod_type");
int destModType = chStmt.getInt("dest_mod_type");
int artTypeId = chStmt.getInt("art_type_id");
if (artId != nextArtId) {
artId = nextArtId;
if (destModType == ModificationType.DELETED.getValue() && sourceModType == ModificationType.MODIFIED.getValue() || destModType == ModificationType.MODIFIED.getValue() && sourceModType == ModificationType.DELETED.getValue()) {
artifactConflictBuilder =
new ArtifactConflictBuilder(sourceGamma, destGamma, artId, baselineTransaction, sourceBranch,
destinationBranch, sourceModType, destModType, artTypeId);
conflictBuilders.add(artifactConflictBuilder);
artIdSet.add(artId);
} else if (destModType == ModificationType.DELETED.getValue() && sourceModType == ModificationType.DELETED.getValue()) {
artIdSetDontShow.add(artId);
artIdSetDontAdd.add(artId);
}
}
}
} finally {
chStmt.close();
}
if (hadEntries) {
monitor.worked(20);
for (Integer integer : artIdSet) {
artIdSetDontShow.add(integer);
}
}
}
/**
* @param sourceBranch
* @param destinationBranch
* @param baselineTransaction
* @param conflicts
* @throws OseeCoreException
*/
private static void loadAttributeConflictsNew(Branch sourceBranch, Branch destinationBranch, TransactionRecord baselineTransaction, Collection<ConflictBuilder> conflictBuilders, Set<Integer> artIdSet, IProgressMonitor monitor, TransactionRecord transactionId) throws OseeCoreException {
monitor.subTask("Finding the Attribute Conflicts");
IOseeStatement chStmt = ConnectionHandler.getStatement();
AttributeConflictBuilder attributeConflictBuilder;
try {
chStmt.runPreparedQuery(ClientSessionManager.getSql(OseeSql.CONFLICT_GET_ATTRIBUTES), sourceBranch.getId(),
sourceBranch.getBaseTransaction().getId(), destinationBranch.getId(),
transactionId != null ? transactionId.getId() : 0);
int attrId = 0;
if (chStmt.next()) {
do {
int nextAttrId = chStmt.getInt("attr_id");
int artId = chStmt.getInt("art_id");
int sourceGamma = chStmt.getInt("source_gamma");
int destGamma = chStmt.getInt("dest_gamma");
int attrTypeId = chStmt.getInt("attr_type_id");
String sourceValue =
chStmt.getString("source_value") != null ? chStmt.getString("source_value") : chStmt.getString("dest_value");
if (attrId != nextAttrId && isAttributeConflictValid(destGamma, sourceBranch)) {
attrId = nextAttrId;
attributeConflictBuilder =
new AttributeConflictBuilder(sourceGamma, destGamma, artId, baselineTransaction, sourceBranch,
destinationBranch, sourceValue, attrId, attrTypeId);
conflictBuilders.add(attributeConflictBuilder);
artIdSet.add(artId);
}
} while (chStmt.next());
}
} finally {
chStmt.close();
}
monitor.worked(30);
}
/**
* Checks source branch hierarchy to see if the conflict gamma exists. If it does, its not a real conflict because
* the source branch has already seen this change.
*
* @return Returns True if the AttributeConflict candidate is really a conflict.
* @throws OseeCoreException
*/
private static boolean isAttributeConflictValid(int destinationGammaId, Branch sourceBranch) throws OseeCoreException {
boolean isValidConflict = true;
//We just need the largest value at first so the complete source branch will be searched
int parentTransactionNumber = Integer.MAX_VALUE;
for (Branch branch : sourceBranch.getAncestors()) {
if (!branch.getBranchType().isSystemRootBranch() && !branch.getParentBranch().getBranchType().isSystemRootBranch()) {
isValidConflict &= isAttributeConflictValidOnBranch(destinationGammaId, branch, parentTransactionNumber);
parentTransactionNumber = branch.getSourceTransaction().getId();
}
if (!isValidConflict) {
break;
}
}
return isValidConflict;
}
/**
* @return Returns True if the destination gamma does not exist on a branch else false if it does.
* @throws OseeDataStoreException
*/
private static boolean isAttributeConflictValidOnBranch(int destinationGammaId, Branch branch, int endTransactionNumber) throws OseeDataStoreException {
String sql =
"select count(1) from osee_txs txs where txs.gamma_id = ? and txs.branch_id = ? and txs.transaction_id <= ?";
return ConnectionHandler.runPreparedQueryFetchInt(0, sql, destinationGammaId, branch.getId(),
endTransactionNumber) == 0;
}
private static void cleanUpConflictDB(Collection<Conflict> conflicts, int branchId, IProgressMonitor monitor) throws OseeCoreException {
monitor.subTask("Cleaning up old conflict data");
int queryId = ArtifactLoader.getNewQueryId();
try {
if (conflicts != null && conflicts.size() != 0 && branchId != 0) {
Timestamp insertTime = GlobalTime.GreenwichMeanTimestamp();
List<Object[]> insertParameters = new LinkedList<Object[]>();
for (Conflict conflict : conflicts) {
insertParameters.add(new Object[] {queryId, insertTime, conflict.getObjectId(), branchId,
SQL3DataType.INTEGER});
}
ArtifactLoader.insertIntoArtifactJoin(insertParameters);
ConnectionHandler.runPreparedUpdate(CONFLICT_CLEANUP, branchId, queryId);
}
} finally {
ArtifactLoader.clearQuery(queryId);
}
monitor.worked(10);
}
public static Collection<Integer> getDestinationBranchesMerged(int sourceBranchId) throws OseeCoreException {
List<Integer> destinationBranches = new LinkedList<Integer>();
IOseeStatement chStmt = ConnectionHandler.getStatement();
try {
chStmt.runPreparedQuery(GET_DESTINATION_BRANCHES, sourceBranchId);
while (chStmt.next()) {
destinationBranches.add(chStmt.getInt("dest_branch_id"));
}
} finally {
chStmt.close();
}
Collections.sort(destinationBranches);
return destinationBranches;
}
private static int getCommitTransaction(Branch sourceBranch, Branch destBranch) throws OseeCoreException {
int transactionId = 0;
IOseeStatement chStmt = ConnectionHandler.getStatement();
try {
if (sourceBranch != null && destBranch != null) {
chStmt.runPreparedQuery(GET_MERGE_DATA, sourceBranch.getId(), destBranch.getId());
if (chStmt.next()) {
transactionId = chStmt.getInt("commit_transaction_id");
}
if (transactionId == 0) {
chStmt.runPreparedQuery(GET_COMMIT_TRANSACTION_COMMENT,
BranchManager.COMMIT_COMMENT + sourceBranch.getName(), destBranch.getId());
if (chStmt.next()) {
transactionId = chStmt.getInt("transaction_id");
}
}
}
} finally {
chStmt.close();
}
return transactionId;
}
/**
* The purpose of this function is find the transaction (Branch Create) that holds the last common values for two
* branches that share a common history. If two branches share the same history than the point at which they diverged
* should provide the reference for detecting conflicts based on the gamma at that point.
*/
private static TransactionRecord findCommonTransaction(Branch sourceBranch, Branch destBranch) throws OseeCoreException {
Collection<Branch> sourceBranches = sourceBranch.getAncestors();
Collection<Branch> destBranches = destBranch.getAncestors();
Branch commonBranch = null;
for (Branch branch : sourceBranches) {
if (destBranches.contains(branch)) {
commonBranch = branch;
break;
}
}
if (commonBranch == null) {
throw new OseeCoreException(String.format("Cannot find a common ancestor for Branch %s and Branch %s",
sourceBranch.getShortName(), destBranch.getShortName()));
}
TransactionRecord sourceTransaction = null;
TransactionRecord destTransaction = null;
if (commonBranch.equals(destBranch)) {
destTransaction = TransactionManager.getHeadTransaction(commonBranch);
} else {
for (Branch branch : destBranches) {
if (branch.getParentBranch().equals(commonBranch)) {
destTransaction = branch.getBaseTransaction();
break;
}
}
}
for (Branch branch : sourceBranches) {
if (branch.getParentBranch().equals(commonBranch)) {
sourceTransaction = branch.getBaseTransaction();
break;
}
}
TransactionRecord toReturn = null;
if (sourceTransaction == null && destTransaction != null) {
toReturn = destTransaction;
} else if (sourceTransaction != null && destTransaction == null) {
toReturn = sourceTransaction;
} else {
toReturn = sourceTransaction.getId() <= destTransaction.getId() ? sourceTransaction : destTransaction;
}
return toReturn;
}
}