blob: da7c3f241e29f3d31c026b4abac576b6421099f4 [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.Platform;
import org.eclipse.osee.framework.core.client.ClientSessionManager;
import org.eclipse.osee.framework.core.data.OseeSql;
import org.eclipse.osee.framework.core.enums.BranchState;
import org.eclipse.osee.framework.core.enums.ConflictStatus;
import org.eclipse.osee.framework.core.enums.ModificationType;
import org.eclipse.osee.framework.db.connection.ConnectionHandler;
import org.eclipse.osee.framework.db.connection.ConnectionHandlerStatement;
import org.eclipse.osee.framework.db.connection.exception.BranchMergeException;
import org.eclipse.osee.framework.db.connection.exception.OseeArgumentException;
import org.eclipse.osee.framework.db.connection.exception.OseeCoreException;
import org.eclipse.osee.framework.db.connection.exception.TransactionDoesNotExist;
import org.eclipse.osee.framework.db.connection.info.SQL3DataType;
import org.eclipse.osee.framework.jdk.core.util.Lib;
import org.eclipse.osee.framework.jdk.core.util.time.GlobalTime;
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.Branch;
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.status.EmptyMonitor;
import org.eclipse.osee.framework.skynet.core.status.IStatusMonitor;
import org.eclipse.osee.framework.skynet.core.transaction.TransactionId;
import org.eclipse.osee.framework.skynet.core.transaction.TransactionIdManager;
/**
* @author Theron Virgin
* @author Jeff C. Phillips
*/
public class ConflictManagerInternal {
private static final String CONFLICT_CLEANUP =
"DELETE FROM osee_conflict WHERE merge_branch_id = ? AND conflict_id NOT IN ";
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 static final boolean DEBUG =
"TRUE".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.osee.framework.skynet.core/debug/Merge"));
private ConflictManagerInternal() {
}
public static List<Conflict> getConflictsPerBranch(TransactionId commitTransaction, IStatusMonitor monitor) throws OseeCoreException {
long time = System.currentTimeMillis();
long totalTime = time;
if (monitor == null) {
monitor = new EmptyMonitor();
}
monitor.startJob(String.format("Loading Merge Manager for Transaction %d",
commitTransaction.getTransactionNumber()), 100);
monitor.setSubtaskName("Finding Database stored conflicts");
if (DEBUG) {
System.out.println(String.format("\nDiscovering Conflicts based on Transaction ID: %d",
commitTransaction.getTransactionNumber()));
totalTime = System.currentTimeMillis();
}
ArrayList<Conflict> conflicts = new ArrayList<Conflict>();
ConnectionHandlerStatement chStmt = new ConnectionHandlerStatement();
if (DEBUG) {
System.out.println("Running Query to find conflicts stored in the DataBase");
time = System.currentTimeMillis();
}
try {
chStmt.runPreparedQuery(ClientSessionManager.getSQL(OseeSql.Conflicts.SELECT_HISTORIC_ATTRIBUTE_CONFLICTS),
commitTransaction.getTransactionNumber());
if (DEBUG) {
System.out.println(String.format(" Query finished in %s", Lib.getElapseString(time)));
}
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")));
conflicts.add(attributeConflict);
attributeConflict.setStatus(ConflictStatus.getStatus(chStmt.getInt("status")));
}
monitor.done();
} finally {
chStmt.close();
}
if (DEBUG) {
debugDump(conflicts, totalTime);
}
return conflicts;
}
public static List<Conflict> getConflictsPerBranch(Branch sourceBranch, Branch destinationBranch, TransactionId baselineTransaction, IStatusMonitor monitor) throws OseeCoreException {
ArrayList<ConflictBuilder> conflictBuilders = new ArrayList<ConflictBuilder>();
ArrayList<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 than use the transaction version
if (monitor == null) {
monitor = new EmptyMonitor();
}
int commitTransactionId = getCommitTransaction(sourceBranch, destinationBranch);
if (commitTransactionId != 0) {
try {
return getConflictsPerBranch(TransactionIdManager.getTransactionId(commitTransactionId), monitor);
} catch (TransactionDoesNotExist ex) {
}
}
long totalTime = 0;
if (sourceBranch != null && destinationBranch != null) {
monitor.startJob(String.format("Loading Merge Manager for Branch %d into Branch %d",
sourceBranch.getBranchId(), destinationBranch.getBranchId()), 100);
monitor.setSubtaskName("Finding Database stored conflicts");
if (DEBUG) {
System.out.println(String.format(
"\nDiscovering Conflicts based on Source Branch: %d Destination Branch: %d",
sourceBranch.getBranchId(), destinationBranch.getBranchId()));
totalTime = System.currentTimeMillis();
}
}
if (sourceBranch == null || destinationBranch == null) {
throw new OseeArgumentException(String.format("Source Branch = %s Destination Branch = %s",
sourceBranch == null ? "NULL" : sourceBranch.getBranchId(),
destinationBranch == null ? "NULL" : destinationBranch.getBranchId()));
}
if (!sourceBranch.isCommitted() && !sourceBranch.isRebaselined() && !sourceBranch.isRebaselineInProgress()) {
BranchManager.setBranchState(sourceBranch, BranchState.COMMIT_IN_PROGRESS);
}
int transactionId = findCommonTransaction(sourceBranch, destinationBranch);
loadArtifactVersionConflictsNew(sourceBranch, destinationBranch, baselineTransaction, conflictBuilders, artIdSet,
artIdSetDontShow, artIdSetDontAdd, monitor, transactionId);
loadAttributeConflictsNew(sourceBranch, destinationBranch, baselineTransaction, conflictBuilders, artIdSet,
monitor, transactionId);
for (Integer integer : artIdSetDontAdd) {
artIdSet.remove(integer);
}
if (artIdSet.isEmpty()) {
return conflicts;
}
if (DEBUG) {
System.out.println(String.format(" Conflicts found in %s", Lib.getElapseString(totalTime)));
}
monitor.setSubtaskName("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.updateWork(15);
preloadConflictArtifacts(sourceBranch, destinationBranch, mergeBranch, artIdSet, monitor);
long time = System.currentTimeMillis();
//Don't create the conflicts for attributes on an artifact that is deleted etc.
for (ConflictBuilder conflictBuilder : conflictBuilders) {
Conflict conflict = conflictBuilder.getConflict(mergeBranch, artIdSetDontShow);
if (conflict != null) {
conflicts.add(conflict);
conflict.computeStatus();
}
}
if (DEBUG) {
System.out.println(String.format(" Creating conflict objects and setting theri status completed in %s",
Lib.getElapseString(time)));
}
cleanUpConflictDB(conflicts, mergeBranch.getBranchId(), monitor);
if (DEBUG) {
debugDump(conflicts, totalTime);
}
return conflicts;
}
private static void preloadConflictArtifacts(Branch sourceBranch, Branch destinationBranch, Branch mergeBranch, Collection<Integer> artIdSet, IStatusMonitor monitor) throws OseeCoreException {
long time = 0;
monitor.setSubtaskName("Preloading Artifacts Associated with the Conflicts");
if (DEBUG) {
System.out.println("Prelodaing Conflict Artifacts");
time = System.currentTimeMillis();
}
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.getBranchId(),
SQL3DataType.INTEGER});
insertParameters.add(new Object[] {queryId, insertTime, artId, destinationBranch.getBranchId(),
SQL3DataType.INTEGER});
insertParameters.add(new Object[] {queryId, insertTime, artId, mergeBranch.getBranchId(),
SQL3DataType.INTEGER});
}
ArtifactLoader.loadArtifacts(queryId, ArtifactLoad.FULL, null, insertParameters, true, false, true);
}
if (DEBUG) {
System.out.println(String.format(" Preloading took %s", Lib.getElapseString(time)));
}
monitor.updateWork(25);
}
/**
* @param sourceBranch
* @param destinationBranch
* @param baselineTransaction
* @param conflictBuilders
* @param artIdSet
* @throws OseeCoreException
*/
private static void loadArtifactVersionConflictsNew(Branch sourceBranch, Branch destinationBranch, TransactionId baselineTransaction, ArrayList<ConflictBuilder> conflictBuilders, Set<Integer> artIdSet, Set<Integer> artIdSetDontShow, Set<Integer> artIdSetDontAdd, IStatusMonitor monitor, int transactionId) throws OseeCoreException {
long time = 0;
if (DEBUG) {
System.out.println("Finding Artifact Version Conflicts");
System.out.println(" Running the Artifact Conflict Query");
time = System.currentTimeMillis();
}
monitor.setSubtaskName("Finding Artifact Version Conflicts");
ConnectionHandlerStatement chStmt = new ConnectionHandlerStatement();
try {
chStmt.runPreparedQuery(ClientSessionManager.getSQL(OseeSql.Conflicts.SELECT_ARTIFACT_CONFLICTS),
sourceBranch.getBranchId(), destinationBranch.getBranchId(), transactionId);
if (!chStmt.next()) {
return;
}
ArtifactConflictBuilder artifactConflictBuilder;
int artId = 0;
do {
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);
}
}
} while (chStmt.next());
} finally {
chStmt.close();
}
if (DEBUG) {
System.out.println(String.format(" Query completed in %s ", Lib.getElapseString(time)));
}
monitor.updateWork(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, TransactionId baselineTransaction, ArrayList<ConflictBuilder> conflictBuilders, Set<Integer> artIdSet, IStatusMonitor monitor, int transactionId) throws OseeCoreException {
long time = 0;
if (DEBUG) {
System.out.println("Finding Attribute Version Conflicts");
System.out.println(" Running the Attribute Conflict Query");
time = System.currentTimeMillis();
}
monitor.setSubtaskName("Finding the Attribute Conflicts");
ConnectionHandlerStatement chStmt = new ConnectionHandlerStatement();
AttributeConflictBuilder attributeConflictBuilder;
try {
chStmt.runPreparedQuery(ClientSessionManager.getSQL(OseeSql.Conflicts.SELECT_ATTRIBUTE_CONFLICTS),
sourceBranch.getBranchId(), destinationBranch.getBranchId(), transactionId);
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) {
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();
}
if (DEBUG) {
System.out.println(String.format(" Query completed in %s ", Lib.getElapseString(time)));
}
monitor.updateWork(30);
}
private static void debugDump(Collection<Conflict> conflicts, long time) throws OseeCoreException {
int displayCount = 1;
System.out.println(String.format("Found %d conflicts in %s", conflicts.size(), Lib.getElapseString(time)));
for (Conflict conflict : conflicts) {
System.out.println(String.format(
" %d. ArtId = %d, ChangeItem = %s, SourceGamma = %d, DestGamma = %d, Status = %s", displayCount++,
conflict.getArtId(), conflict.getChangeItem(), conflict.getSourceGamma(), conflict.getDestGamma(),
conflict.getStatus()));
}
}
private static void cleanUpConflictDB(Collection<Conflict> conflicts, int branchId, IStatusMonitor monitor) throws OseeCoreException {
int count = 0;
long time = System.currentTimeMillis();
monitor.setSubtaskName("Cleaning up old conflict data");
if (conflicts != null && conflicts.size() != 0 && branchId != 0) {
count = ConnectionHandler.runPreparedUpdate(CONFLICT_CLEANUP + createData(conflicts), branchId);
}
if (DEBUG) {
System.out.println(String.format(" Cleaned up %d conflicts that are no longer conflicting in %s ", count,
Lib.getElapseString(time)));
}
monitor.updateWork(10);
}
private static String createData(Collection<Conflict> conflicts) throws OseeCoreException {
StringBuilder builder = new StringBuilder();
builder.append("(");
boolean first = true;
for (Conflict conflict : conflicts) {
if (!first) {
builder.append(" , ");
} else {
first = false;
}
builder.append(conflict.getObjectId());
}
builder.append(")");
return builder.toString();
}
public static Collection<Integer> getDestinationBranchesMerged(int sourceBranchId) throws OseeCoreException {
List<Integer> destinationBranches = new LinkedList<Integer>();
ConnectionHandlerStatement chStmt = new ConnectionHandlerStatement();
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;
}
public static int getCommitTransaction(Branch sourceBranch, Branch destBranch) throws OseeCoreException {
int transactionId = 0;
ConnectionHandlerStatement chStmt = new ConnectionHandlerStatement();
try {
if (sourceBranch != null && destBranch != null) {
chStmt.runPreparedQuery(GET_MERGE_DATA, sourceBranch.getBranchId(), destBranch.getBranchId());
if (chStmt.next()) {
transactionId = chStmt.getInt("commit_transaction_id");
}
if (transactionId == 0) {
chStmt.runPreparedQuery(GET_COMMIT_TRANSACTION_COMMENT,
BranchManager.COMMIT_COMMENT + sourceBranch.getBranchName(), destBranch.getBranchId());
if (chStmt.next()) {
transactionId = chStmt.getInt("transaction_id");
}
}
}
} finally {
chStmt.close();
}
return transactionId;
}
public static int getMergeBranchId(int sourceBranchId, int destBranchId) throws OseeCoreException {
int mergeBranchId = 0;
ConnectionHandlerStatement chStmt = new ConnectionHandlerStatement();
try {
chStmt.runPreparedQuery(GET_MERGE_DATA, sourceBranchId, destBranchId);
if (chStmt.next()) {
mergeBranchId = chStmt.getInt("merge_branch_id");
}
} finally {
chStmt.close();
}
return mergeBranchId;
}
/**
* 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.
*/
public static int findCommonTransaction(Branch sourceBranch, Branch destBranch) throws OseeCoreException {
List<Branch> sourceBranches = sourceBranch.getBranchHierarchy();
List<Branch> destBranches = destBranch.getBranchHierarchy();
Branch commonBranch = null;
for (Branch branch : sourceBranches) {
if (destBranches.contains(branch)) {
commonBranch = branch;
break;
}
}
if (commonBranch == null) {
throw new OseeCoreException(String.format("Can not find a common ancestor for Branch %s and Branch %s",
sourceBranch.getBranchShortName(), destBranch.getBranchShortName()));
}
int sourceTransaction = 0;
int destTransaction = 0;
if (commonBranch.equals(destBranch)) {
destTransaction = Integer.MAX_VALUE;
} else {
for (Branch branch : destBranches) {
if (branch.getParentBranch().equals(commonBranch)) {
destTransaction = TransactionIdManager.getStartEndPoint(branch).getKey().getTransactionNumber();
break;
}
}
}
for (Branch branch : sourceBranches) {
if (branch.getParentBranch().equals(commonBranch)) {
sourceTransaction = TransactionIdManager.getStartEndPoint(branch).getKey().getTransactionNumber();
break;
}
}
return sourceTransaction <= destTransaction ? sourceTransaction : destTransaction;
}
}