blob: bd250005a99c7b9074b957729ec02a4daf6982bd [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.artifact;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import org.eclipse.osee.framework.core.client.ClientSessionManager;
import org.eclipse.osee.framework.core.data.IArtifactType;
import org.eclipse.osee.framework.core.data.IOseeBranch;
import org.eclipse.osee.framework.core.enums.DeletionFlag;
import org.eclipse.osee.framework.core.enums.LoadLevel;
import org.eclipse.osee.framework.core.enums.ModificationType;
import org.eclipse.osee.framework.core.exception.OseeDataStoreException;
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.OseeConnection;
import org.eclipse.osee.framework.database.core.OseeSql;
import org.eclipse.osee.framework.database.core.SQL3DataType;
import org.eclipse.osee.framework.jdk.core.type.CompositeKeyHashMap;
import org.eclipse.osee.framework.jdk.core.type.OseeCoreException;
import org.eclipse.osee.framework.jdk.core.type.Pair;
import org.eclipse.osee.framework.jdk.core.util.Lib;
import org.eclipse.osee.framework.jdk.core.util.time.GlobalTime;
import org.eclipse.osee.framework.logging.OseeLog;
import org.eclipse.osee.framework.skynet.core.internal.Activator;
/**
* @author Ryan D. Brooks
*/
public final class ArtifactLoader {
private static final String INSERT_JOIN_ARTIFACT =
"INSERT INTO osee_join_artifact (query_id, insert_time, art_id, branch_id, transaction_id) VALUES (?, ?, ?, ?, ?)";
private static final String DELETE_FROM_JOIN_ARTIFACT = "DELETE FROM osee_join_artifact WHERE query_id = ?";
private static final CompositeKeyHashMap<Integer, Long, ReentrantLock> loadingActiveMap =
new CompositeKeyHashMap<Integer, Long, ReentrantLock>(1000, true);
private static final Random queryRandom = new Random();
/**
* (re)loads the artifacts selected by sql and then returns them in a list
*/
public static List<Artifact> getArtifacts(String sql, Object[] queryParameters, int artifactCountEstimate, LoadLevel loadLevel, LoadType reload, ISearchConfirmer confirmer, TransactionRecord transactionId, DeletionFlag allowDeleted) throws OseeCoreException {
List<Pair<Integer, Long>> toLoad = selectArtifacts(sql, queryParameters, artifactCountEstimate);
List<Artifact> artifacts = loadSelectedArtifacts(toLoad, loadLevel, reload, allowDeleted, transactionId);
if (confirmer != null) {
confirmer.canProceed(artifacts.size());
}
return new LinkedList<Artifact>(artifacts);
}
public static List<Artifact> loadArtifacts(Collection<Integer> artIds, IOseeBranch branch, LoadLevel loadLevel, LoadType reload, DeletionFlag allowDeleted, TransactionRecord transactionId) throws OseeCoreException {
List<Pair<Integer, Long>> toLoad = new LinkedList<Pair<Integer, Long>>();
Long branchId = BranchManager.getBranchId(branch);
for (Integer artId : new HashSet<Integer>(artIds)) {
toLoad.add(new Pair<Integer, Long>(artId, branchId));
}
List<Artifact> artifacts = loadSelectedArtifacts(toLoad, loadLevel, reload, allowDeleted, transactionId);
return artifacts;
}
public static List<Artifact> loadArtifacts(Collection<Integer> artIds, IOseeBranch branch, LoadLevel loadLevel, LoadType reload, DeletionFlag allowDeleted) throws OseeCoreException {
return loadArtifacts(artIds, branch, loadLevel, reload, allowDeleted, null);
}
private static List<Artifact> loadSelectedArtifacts(List<Pair<Integer, Long>> toLoad, LoadLevel loadLevel, LoadType reload, DeletionFlag allowDeleted, TransactionRecord transactionId) throws OseeCoreException {
Set<Artifact> artifacts = new LinkedHashSet<Artifact>();
if (transactionId == null) {
loadActiveArtifacts(toLoad, artifacts, loadLevel, reload, allowDeleted);
} else {
loadArtifacts(toLoad, loadLevel, transactionId, reload, allowDeleted, artifacts);
}
return new LinkedList<Artifact>(artifacts);
}
private static void loadActiveArtifacts(List<Pair<Integer, Long>> toLoad, Set<Artifact> artifacts, LoadLevel loadLevel, LoadType reload, DeletionFlag allowDeleted) throws OseeCoreException {
if (!toLoad.isEmpty()) {
int numRequested = toLoad.size();
Iterator<Pair<Integer, Long>> iterator = toLoad.iterator();
CompositeKeyHashMap<Integer, Long, ReentrantLock> locks =
new CompositeKeyHashMap<Integer, Long, ReentrantLock>();
while (iterator.hasNext()) {
Pair<Integer, Long> next = iterator.next();
Integer artId = next.getFirst();
Long branchId = next.getSecond();
Artifact active = null;
if (reload == LoadType.INCLUDE_CACHE) {
synchronized (ArtifactCache.class) {
active = ArtifactCache.getActive(artId, branchId);
}
}
boolean doNotLoad = determineIfIShouldLoad(artifacts, allowDeleted, locks, artId, branchId, active);
if (doNotLoad) {
iterator.remove();
}
}
// load arts that are not in the cache
try {
loadArtifacts(toLoad, loadLevel, null, reload, allowDeleted, artifacts);
} finally {
// remove and unlock locks this thread created but didn't load
if (artifacts.size() != numRequested) {
for (Pair<Integer, Long> loadPair : toLoad) {
Integer artId = loadPair.getFirst();
Long branchId = loadPair.getSecond();
removeAndUnlock(artId, branchId);
locks.remove(artId, branchId);
}
}
}
processLocks(locks, artifacts);
}
}
private static void removeAndUnlock(Integer artId, Long branchId) {
ReentrantLock lock = null;
synchronized (loadingActiveMap) {
lock = loadingActiveMap.remove(artId, branchId);
}
if (lock != null && lock.isLocked()) {
lock.unlock();
}
}
private static boolean determineIfIShouldLoad(Set<Artifact> artifacts, DeletionFlag allowDeleted, CompositeKeyHashMap<Integer, Long, ReentrantLock> locks, Integer artId, Long branchId, Artifact active) {
boolean doNotLoad = false;
//not in the cache
if (active == null) {
synchronized (loadingActiveMap) {
ReentrantLock lock = loadingActiveMap.get(artId, branchId);
// this thread should load the artifact
if (lock == null) {
lock = new ReentrantLock();
lock.lock();
loadingActiveMap.put(artId, branchId, lock);
} else {
// another thread is loading the artifact, do not load it
locks.put(artId, branchId, lock);
doNotLoad = true;
}
}
} else {
// artifact is in the cache, do not load it
if (!active.isDeleted() || (active.isDeleted() && allowDeleted == DeletionFlag.INCLUDE_DELETED)) {
artifacts.add(active);
}
doNotLoad = true;
}
return doNotLoad;
}
private static void processLocks(CompositeKeyHashMap<Integer, Long, ReentrantLock> locks, Set<Artifact> artifacts) {
Iterator<Entry<Pair<Integer, Long>, ReentrantLock>> iterator = locks.entrySet().iterator();
while (iterator.hasNext()) {
Entry<Pair<Integer, Long>, ReentrantLock> entry = iterator.next();
Integer artId = entry.getKey().getFirst();
Long branchId = entry.getKey().getSecond();
entry.getValue().lock();
entry.getValue().unlock();
Artifact active = ArtifactCache.getActive(artId, branchId);
if (active != null) {
artifacts.add(active);
}
}
}
private static void loadArtifactsFromQueryId(Collection<Artifact> loadedItems, int queryId, LoadLevel loadLevel, ISearchConfirmer confirmer, int fetchSize, LoadType reload, TransactionRecord transactionId, DeletionFlag allowDeleted) throws OseeCoreException {
try {
OseeSql sqlKey;
boolean historical = transactionId != null;
if (historical) {
sqlKey = OseeSql.LOAD_HISTORICAL_ARTIFACTS;
} else if (allowDeleted == DeletionFlag.INCLUDE_DELETED) {
sqlKey = OseeSql.LOAD_CURRENT_ARTIFACTS_WITH_DELETED;
} else {
sqlKey = OseeSql.LOAD_CURRENT_ARTIFACTS;
}
IOseeStatement chStmt = ConnectionHandler.getStatement();
String sql = null;
try {
sql = ClientSessionManager.getSql(sqlKey);
chStmt.runPreparedQuery(fetchSize, sql, queryId);
int previousArtId = -1;
int previousBranchId = -1;
while (chStmt.next()) {
int artId = chStmt.getInt("art_id");
int branchId = chStmt.getInt("branch_id");
// assumption: sql is returning rows ordered by branch_id, art_id, transaction_id in descending order
if (previousArtId != artId || previousBranchId != branchId) {
// assumption: sql is returning unwanted deleted artifacts only in the historical case
if (!historical || allowDeleted == DeletionFlag.INCLUDE_DELETED || ModificationType.getMod(chStmt.getInt("mod_type")) != ModificationType.DELETED) {
Artifact shallowArtifact = retrieveShallowArtifact(chStmt, reload, historical);
loadedItems.add(shallowArtifact);
}
}
previousArtId = artId;
previousBranchId = branchId;
}
} catch (OseeDataStoreException ex) {
OseeLog.logf(Activator.class, Level.SEVERE, ex, "%s - %s", sqlKey, sql == null ? "SQL unknown" : sql);
throw ex;
} finally {
chStmt.close();
}
if (confirmer == null || confirmer.canProceed(loadedItems.size())) {
loadArtifactsData(queryId, loadedItems, loadLevel, reload, transactionId, allowDeleted);
}
} finally {
clearQuery(queryId);
}
}
/**
* loads or reloads artifacts based on artifact ids and branch ids
*
* @param artifacts
* @param locks
*/
private static void loadArtifacts(List<Pair<Integer, Long>> toLoad, LoadLevel loadLevel, TransactionRecord transactionId, LoadType reload, DeletionFlag allowDeleted, Set<Artifact> artifacts) throws OseeCoreException {
if (toLoad != null && !toLoad.isEmpty()) {
int queryId = ArtifactLoader.getNewQueryId();
Timestamp insertTime = GlobalTime.GreenwichMeanTimestamp();
boolean historical = transactionId != null;
List<Object[]> insertParameters = new LinkedList<Object[]>();
for (Pair<Integer, Long> pair : toLoad) {
insertParameters.add(new Object[] {
queryId,
insertTime,
pair.getFirst(),
pair.getSecond(),
historical ? transactionId.getId() : SQL3DataType.INTEGER});
}
loadArtifacts(artifacts, queryId, loadLevel, null, insertParameters, reload, transactionId, allowDeleted);
}
}
private static void loadArtifacts(Collection<Artifact> loadedItems, int queryId, LoadLevel loadLevel, ISearchConfirmer confirmer, List<Object[]> insertParameters, LoadType reload, TransactionRecord transactionId, DeletionFlag allowDeleted) throws OseeCoreException {
if (!insertParameters.isEmpty()) {
Collection<Artifact> data;
if (loadedItems.isEmpty()) {
data = loadedItems;
} else {
// Use a new list if loaded items already contains data to prevent artifact overwrites during loading
data = new ArrayList<Artifact>(insertParameters.size());
}
long time = System.currentTimeMillis();
try {
insertIntoArtifactJoin(insertParameters);
loadArtifactsFromQueryId(data, queryId, loadLevel, confirmer, insertParameters.size(), reload,
transactionId, allowDeleted);
} finally {
if (data != loadedItems) {
loadedItems.addAll(data);
}
OseeLog.logf(Activator.class, Level.FINE, new Exception("Artifact Load Time"),
"Artifact Load Time [%s] for [%d] artifacts. ", Lib.getElapseString(time), loadedItems.size());
clearQuery(queryId);
}
}
}
/**
* must be call in a try block with a finally clause which calls clearQuery()
*/
public static int insertIntoArtifactJoin(OseeConnection connection, List<Object[]> insertParameters) throws OseeCoreException {
return ConnectionHandler.runBatchUpdate(connection, INSERT_JOIN_ARTIFACT, insertParameters);
}
/**
* must be call in a try block with a finally clause which calls clearQuery()
*/
public static int insertIntoArtifactJoin(List<Object[]> insertParameters) throws OseeCoreException {
return insertIntoArtifactJoin(null, insertParameters);
}
/**
* should only be used in tandem with with selectArtifacts()
*
* @param queryId value gotten from call to getNewQueryId and used in populating the insert parameters for
* selectArtifacts
*/
public static void clearQuery(int queryId) throws OseeCoreException {
ConnectionHandler.runPreparedUpdate(DELETE_FROM_JOIN_ARTIFACT, queryId);
}
/**
* should only be used in tandem with with selectArtifacts()
*
* @param queryId value gotten from call to getNewQueryId and used in populating the insert parameters for
* selectArtifacts
*/
public static void clearQuery(OseeConnection connection, int queryId) throws OseeCoreException {
if (connection != null) {
ConnectionHandler.runPreparedUpdate(connection, DELETE_FROM_JOIN_ARTIFACT, queryId);
} else {
ConnectionHandler.runPreparedUpdate(DELETE_FROM_JOIN_ARTIFACT, queryId);
}
}
/**
* Determines the artIds and branchIds of artifacts to load based on sql and queryParameters
*/
private static List<Pair<Integer, Long>> selectArtifacts(String sql, Object[] queryParameters, int artifactCountEstimate) throws OseeCoreException {
IOseeStatement chStmt = ConnectionHandler.getStatement();
long time = System.currentTimeMillis();
List<Pair<Integer, Long>> toLoad = new LinkedList<Pair<Integer, Long>>();
try {
chStmt.runPreparedQuery(artifactCountEstimate, sql, queryParameters);
while (chStmt.next()) {
int artId = chStmt.getInt("art_id");
long branchId = chStmt.getLong("branch_id");
toLoad.add(new Pair<Integer, Long>(artId, branchId));
}
} finally {
chStmt.close();
}
OseeLog.logf(Activator.class, Level.FINE, new Exception("Artifact Selection Time"),
"Artifact Selection Time [%s], [%d] artifacts selected", Lib.getElapseString(time), toLoad.size());
return toLoad;
// processList(queryId, toLoad, artifacts, insertParameters, transactionId, reload, locks);
}
/**
* This method is called only after the cache has been checked
*/
private static Artifact retrieveShallowArtifact(IOseeStatement chStmt, LoadType reload, boolean historical) throws OseeCoreException {
int artifactId = chStmt.getInt("art_id");
Branch branch = BranchManager.getBranch(chStmt.getLong("branch_id"));
int transactionId = Artifact.TRANSACTION_SENTINEL;
if (historical) {
int stripeTransactionNumber = chStmt.getInt("stripe_transaction_id");
transactionId = stripeTransactionNumber;
}
Artifact artifact = historical ? null : ArtifactCache.getActive(artifactId, branch);
if (artifact == null) {
IArtifactType artifactType = ArtifactTypeManager.getTypeByGuid(chStmt.getLong("art_type_id"));
ArtifactFactory factory = ArtifactTypeManager.getFactory(artifactType);
artifact =
factory.loadExisitingArtifact(artifactId, chStmt.getString("guid"), artifactType,
chStmt.getInt("gamma_id"), branch, transactionId, ModificationType.getMod(chStmt.getInt("mod_type")),
historical);
}
if (reload == LoadType.RELOAD_CACHE) {
artifact.internalSetPersistenceData(chStmt.getInt("gamma_id"), transactionId,
ModificationType.getMod(chStmt.getInt("mod_type")), historical, false);
}
return artifact;
}
@SuppressWarnings("unchecked")
static void loadArtifactData(Artifact artifact, LoadLevel loadLevel) throws OseeCoreException {
int queryId = getNewQueryId();
Timestamp insertTime = GlobalTime.GreenwichMeanTimestamp();
try {
ConnectionHandler.runPreparedUpdate(INSERT_JOIN_ARTIFACT, queryId, insertTime, artifact.getArtId(),
artifact.getFullBranch().getId(), SQL3DataType.INTEGER);
List<Artifact> artifacts = new ArrayList<Artifact>(1);
artifacts.add(artifact);
loadArtifactsData(queryId, artifacts, loadLevel, LoadType.INCLUDE_CACHE, null,
artifact.isDeleted() ? DeletionFlag.INCLUDE_DELETED : DeletionFlag.EXCLUDE_DELETED);
} finally {
clearQuery(queryId);
}
}
private static void loadArtifactsData(int queryId, Collection<Artifact> artifacts, LoadLevel loadLevel, LoadType reload, TransactionRecord transactionId, DeletionFlag allowDeleted) throws OseeCoreException {
if (reload == LoadType.RELOAD_CACHE) {
for (Artifact artifact : artifacts) {
artifact.prepareForReload();
}
}
boolean historical = transactionId != null;
long key2;
CompositeKeyHashMap<Integer, Long, Artifact> tempCache =
new CompositeKeyHashMap<Integer, Long, Artifact>(artifacts.size(), true);
for (Artifact artifact : artifacts) {
key2 = historical ? transactionId.getId() : BranchManager.getBranchId(artifact.getBranch());
tempCache.put(artifact.getArtId(), key2, artifact);
}
AttributeLoader.loadAttributeData(queryId, tempCache, historical, allowDeleted, loadLevel);
RelationLoader.loadRelationData(queryId, artifacts, historical, loadLevel);
if (!historical) {
for (Artifact artifact : artifacts) {
key2 = artifact.getFullBranch().getId();
removeAndUnlock(artifact.getArtId(), key2);
}
}
}
public static int getNewQueryId() {
return queryRandom.nextInt(Integer.MAX_VALUE);
}
}