/*************************GO-LICENSE-START*********************************
* Copyright 2014 ThoughtWorks, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*************************GO-LICENSE-END***********************************/
package com.thoughtworks.go.server.dao;
import com.ibatis.sqlmap.client.SqlMapClient;
import com.opensymphony.oscache.base.Cache;
import com.opensymphony.oscache.base.CacheEntry;
import com.opensymphony.oscache.base.NeedsRefreshException;
import com.rits.cloning.Cloner;
import com.thoughtworks.go.config.CaseInsensitiveString;
import com.thoughtworks.go.config.StageConfig;
import com.thoughtworks.go.database.Database;
import com.thoughtworks.go.domain.*;
import com.thoughtworks.go.domain.feed.stage.StageFeedEntry;
import com.thoughtworks.go.presentation.pipelinehistory.StageHistoryEntry;
import com.thoughtworks.go.presentation.pipelinehistory.StageHistoryPage;
import com.thoughtworks.go.presentation.pipelinehistory.StageInstanceModel;
import com.thoughtworks.go.presentation.pipelinehistory.StageInstanceModels;
import com.thoughtworks.go.server.cache.GoCache;
import com.thoughtworks.go.server.domain.JobStatusListener;
import com.thoughtworks.go.server.domain.StageIdentity;
import com.thoughtworks.go.server.domain.StageStatusListener;
import com.thoughtworks.go.server.transaction.SqlMapClientDaoSupport;
import com.thoughtworks.go.server.transaction.TransactionSynchronizationManager;
import com.thoughtworks.go.server.transaction.TransactionTemplate;
import com.thoughtworks.go.server.util.Pagination;
import com.thoughtworks.go.util.DynamicReadWriteLock;
import com.thoughtworks.go.util.FuncVarArg;
import com.thoughtworks.go.util.IBatisUtil;
import com.thoughtworks.go.util.SystemEnvironment;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.thoughtworks.go.util.IBatisUtil.arguments;
@Component
public class StageSqlMapDao extends SqlMapClientDaoSupport implements StageDao, StageStatusListener, JobStatusListener {
private static final Logger LOGGER = Logger.getLogger(SqlMapClientDaoSupport.class);
private TransactionTemplate transactionTemplate;
private GoCache goCache;
private JobInstanceSqlMapDao buildInstanceDao;
private Cache cache;
private TransactionSynchronizationManager transactionSynchronizationManager;
private Cloner cloner = new Cloner();
private DynamicReadWriteLock readWriteLock = new DynamicReadWriteLock();
@Autowired
public StageSqlMapDao(JobInstanceSqlMapDao buildInstanceDao, Cache cache, TransactionTemplate transactionTemplate, SqlMapClient sqlMapClient, GoCache goCache,
TransactionSynchronizationManager transactionSynchronizationManager, SystemEnvironment systemEnvironment, Database database) {
super(goCache, sqlMapClient, systemEnvironment, database);
this.buildInstanceDao = buildInstanceDao;
this.cache = cache;
this.transactionTemplate = transactionTemplate;
this.goCache = goCache;
this.transactionSynchronizationManager = transactionSynchronizationManager;
}
public Stages scheduledStages() {
Stages scheduledStages = new Stages(
(List<Stage>) getSqlMapClientTemplate().queryForList("getScheduledStages"));
return new Stages(scheduledStages);
}
public Stage save(final Pipeline pipeline, final Stage stage) {
return (Stage) transactionTemplate.execute(new TransactionCallback() {
public Object doInTransaction(TransactionStatus status) {
transactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
String pipelineName = pipeline.getName();
String stageName = stage.getName();
clearStageHistoryPageCaches(stage, pipelineName, false);
clearCachedStage(stage.getIdentifier());
clearCachedAllStages(pipelineName, pipeline.getCounter(), stageName);
removeFromCache(cacheKeyForStageCountForGraph(pipelineName, stageName));
}
});
stage.setPipelineId(pipeline.getId());
int maxStageCounter = getMaxStageCounter(pipeline.getId(), stage.getName());
stage.setCounter(maxStageCounter + 1);
getSqlMapClientTemplate().update("markPreviousStageRunsAsNotLatest", arguments("stageName", stage.getName()).and("pipelineId", pipeline.getId()).asMap());
getSqlMapClientTemplate().insert("insertStage", stage);
stage.setIdentifier(new StageIdentifier(pipeline, stage));
return stage;
}
});
}
private void clearStageHistoryPageCaches(Stage stage, String pipelineName, boolean clearOnlyHistoryPages) {
String mutex = mutexForStageHistory(pipelineName, stage.getName());
readWriteLock.acquireWriteLock(mutex);
try {
if (!clearOnlyHistoryPages) {
goCache.remove(cacheKeyForStageCount(pipelineName, stage.getName()));
goCache.remove(cacheKeyForStageOffset(stage));
}
goCache.remove(cacheKeyForStageHistories(pipelineName, stage.getName()));
goCache.remove(cacheKeyForDetailedStageHistories(pipelineName, stage.getName()));
} finally {
readWriteLock.releaseWriteLock(mutex);
}
}
@Deprecated
// This is only used in test for legacy purpose.
// Please call pipelineService.save(aPipeline) instead
public Stage saveWithJobs(Pipeline pipeline, Stage stage) {
if (stage.getState() == null) {
stage.building();
}
stage = save(pipeline, stage);
clearCachedStage(new StageIdentifier(pipeline, stage));//This is bad because it should be done in after-commit, but this is test-ONLY-code, so ignore!
JobInstances jobInstances = stage.getJobInstances();
for (JobInstance job : jobInstances) {
buildInstanceDao.save(stage.getId(), job);
}
for (JobInstance jobInstance : jobInstances) {
jobInstance.setIdentifier(new JobIdentifier(pipeline, stage, jobInstance));
}
return stage;
}
public int getMaxStageCounter(long pipelineId, String stageName) {
Map<String, Object> toGet = arguments("pipelineId", pipelineId).and("name", stageName).asMap();
Integer maxCounter = (Integer) getSqlMapClientTemplate().queryForObject("getMaxStageCounter", toGet);
return maxCounter == null ? 0 : maxCounter;
}
public int getCount(String pipelineName, String stageName) {
Map<String, Object> toGet = arguments("pipelineName", pipelineName).and("stageName", stageName).asMap();
String key = cacheKeyForStageCount(pipelineName, stageName);
Integer count = (Integer) goCache.get(key);
if (count == null) {
count = (Integer) getSqlMapClientTemplate().queryForObject("getStageHistoryCount", toGet);
goCache.put(key, count);
}
return count;
}
private String cacheKeyForStageCount(String pipelineName, String stageName) {
return String.format("%s_numberOfStages_%s_<>_%s", getClass().getName(), pipelineName, stageName).intern();
}
public Stages getStagesByPipelineId(long pipelineId) {
Stages stageHistory = new Stages(
(List<Stage>) getSqlMapClientTemplate().queryForList("getStagesByPipelineId", pipelineId));
return new Stages(stageHistory);
}
public int findLatestStageCounter(PipelineIdentifier pipeline, String stageName) {
Map<String, Object> toGet = arguments("pipelineName", pipeline.getName()).and("pipelineCounter",
pipeline.getCounter()).and("pipelineLabel", pipeline.getLabel()).and(
"stageName", stageName).asMap();
Integer maxCounter = (Integer) getSqlMapClientTemplate().queryForObject(
"findMaxStageCounter", toGet);
return maxCounter == null ? 0 : maxCounter;
}
public Stage findStageWithIdentifier(StageIdentifier identifier) {
String cachekey = cacheKeyForStageIdentifier(identifier);
String cacheKeyForIdentifiers = cacheKeyForListOfStageIdentifiers(identifier);
synchronized (cacheKeyForIdentifiers) {
Stage stage = (Stage) goCache.get(cacheKeyForIdentifiers, cachekey);
if (stage == null) {
IBatisUtil.IBatisArgument argument = IBatisUtil.arguments("pipelineName", identifier.getPipelineName())
.and("pipelineCounter", identifier.getPipelineCounter())
.and("stageName", identifier.getStageName())
.and("stageCounter", Integer.parseInt(identifier.getStageCounter()));
if (identifier.getPipelineLabel() != null) {
argument = argument.and("pipelineLabel", identifier.getPipelineLabel());
}
Map arguments = argument.asMap();
stage = (Stage) getSqlMapClientTemplate().queryForObject("findStageWithJobsByIdentifier", arguments);
if (stage == null) {
return new NullStage(identifier.getStageName());
}
goCache.put(cacheKeyForIdentifiers, cachekey, stage);
}
return cloner.deepClone(stage);
}
}
private String cacheKeyForListOfStageIdentifiers(StageIdentifier stageIdentifier) {
return String.format("%s_stageRunIdentifier_%s_%s_%s", getClass().getName(), stageIdentifier.getPipelineName(), stageIdentifier.getPipelineCounter(),
stageIdentifier.getStageName()).intern();
}
private String cacheKeyForStageIdentifier(StageIdentifier stageIdentifier) {
return String.format("%s_stageIdentifier_%s_%s_%s_%s", getClass().getName(), stageIdentifier.getPipelineName(), stageIdentifier.getPipelineCounter(),
stageIdentifier.getStageName(), stageIdentifier.getStageCounter()).intern();
}
public long getExpectedDurationMillis(String pipelineName, String stageName, JobInstance job) {
Long duration = getDurationOfLastSuccessfulOnAgent(pipelineName, stageName, job);
return duration == null ? 0L : duration * 1000L;
}
public Long getDurationOfLastSuccessfulOnAgent(String pipelineName, String stageName, JobInstance job) {
String key = job.getBuildDurationKey(pipelineName, stageName);
Long duration;
try {
duration = (Long) cache.getFromCache(key, CacheEntry.INDEFINITE_EXPIRY);
} catch (NeedsRefreshException nre) {
boolean updated = false;
try {
duration = recalculateBuildDuration(pipelineName, stageName, job);
cache.putInCache(key, duration);
updated = true;
} finally {
if (!updated) {
// It is essential that cancelUpdate is called if the
// cached content could not be rebuilt
cache.cancelUpdate(key);
LOGGER.warn("refresh cancelled for " + key);
}
}
}
return duration;
}
private Long recalculateBuildDuration(String pipelineName, String stageName, JobInstance job) {
Map<String, Object> toGet =
arguments("buildName", job.getName())
.and("agentUuid", job.getAgentUuid())
.and("stageName", stageName)
.and("pipelineName", pipelineName)
.asMap();
// Return a list of job id's and stage names, entries sorted by id.
Long buildId =
(Long) getSqlMapClientTemplate().queryForObject("getLastSuccessfulBuildIdOnAgent", toGet);
if (buildId == null) {
return null;
}
JobInstance mostRecent = buildInstanceDao.buildByIdWithTransitions(buildId);
return mostRecent.durationOfCompletedBuildInSeconds();
}
public int getMaxStageOrder(long pipelineId) {
Integer order = (Integer) getSqlMapClientTemplate().queryForObject("getMaxStageOrder", pipelineId);
return (order == null ? 0 : order);
}
public Integer getStageOrderInPipeline(long pipelineId, String stageName) {
Map<String, Object> params = arguments("pipelineId", pipelineId).and("stageName", stageName).asMap();
return (Integer) getSqlMapClientTemplate().queryForObject("getStageOrderInPipeline", params);
}
public void updateResult(final Stage stage, final StageResult result) {
transactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
StageIdentifier identifier = stage.getIdentifier();
clearStageHistoryPageCaches(stage, identifier.getPipelineName(), true);
clearJobStatusDependentCaches(stage.getId(), identifier);
removeFromCache(cacheKeyForStageCountForGraph(identifier.getPipelineName(), identifier.getStageName()));
}
});
getSqlMapClientTemplate().update("updateStageStatus", arguments("stageId", stage.getId())
.and("result", result.toString())
.and("state", stage.getState())
.and("completedByTransitionId", stage.getCompletedByTransitionId()).asMap());
}
public Stage mostRecentCompleted(StageConfigIdentifier identifier) {
Map<String, Object> toGet = arguments("pipelineName", identifier.getPipelineName())
.and("stageName", identifier.getStageName()).asMap();
return (Stage) getSqlMapClientTemplate().queryForObject("getMostRecentCompletedStage", toGet);
}
public Stage mostRecentStage(StageConfigIdentifier identifier) {
Map<String, Object> toGet = arguments("pipelineName", identifier.getPipelineName())
.and("stageName", identifier.getStageName()).asMap();
return (Stage) getSqlMapClientTemplate().queryForObject("getMostRecentStage", toGet);
}
public List<JobInstance> mostRecentJobsForStage(String pipelineName, String stageName) {
Long mostRecentId = mostRecentId(pipelineName, stageName);
Stage stage = stageByIdWithBuildsWithNoAssociations(mostRecentId);
List<JobInstance> jobInstances = new ArrayList<>();
jobInstances.addAll(stage.getJobInstances());
return jobInstances;
}
public Stages getPassedStagesByName(String pipelineName, String stageName, int limit, int offset) {
Map<String, Object> toGet = arguments("pipelineName", pipelineName).and("stageName", stageName)
.and("limit", limit).and("offset", offset).asMap();
return new Stages((List<Stage>) getSqlMapClientTemplate().queryForList("allPassedStagesByName", toGet));
}
public List<StageAsDMR> getPassedStagesAfter(StageIdentifier stageIdentifier, int limit, int offset) {
Stage laterThan = findStageWithIdentifier(stageIdentifier);
Map<String, Object> toGet = arguments("pipelineName", stageIdentifier.getPipelineName()).and("stageName", stageIdentifier.getStageName())
.and("laterThan", laterThan.getId()).and("limit", limit).and("offset", offset).asMap();
return (List<StageAsDMR>) getSqlMapClientTemplate().queryForList("allPassedStageAsDMRsAfter", toGet);
}
public Stages getAllRunsOfStageForPipelineInstance(String pipelineName, Integer pipelineCounter, String stageName) {
String cacheKeyForAllStages = cacheKeyForAllStageOfPipeline(pipelineName, pipelineCounter, stageName);
synchronized (cacheKeyForAllStages) {
List<Stage> stages = (List<Stage>) goCache.get(cacheKeyForAllStages);
if (stages == null) {
Map<String, Object> toGet = arguments("pipelineName", pipelineName).and("pipelineCounter", pipelineCounter).and("stageName", stageName).asMap();
stages = (List<Stage>) getSqlMapClientTemplate().queryForList("getAllRunsOfStageForPipelineInstance", toGet);
goCache.put(cacheKeyForAllStages, stages);
}
return new Stages(cloner.deepClone(stages));
}
}
private String cacheKeyForAllStageOfPipeline(String pipelineName, Integer pipelineCounter, String stageName) {
return String.format(getClass().getName() + "_allStageOfPipeline_%s_%s_%s", pipelineName, pipelineCounter, stageName).intern();
}
public List<Stage> findStageHistoryForChart(String pipelineName, String stageName, int pageSize, int offset) {
Map<String, Object> args =
arguments("pipelineName", pipelineName).
and("stageName", stageName).
and("offset", offset).
and("limit", pageSize).asMap();
return new Stages((List<Stage>) getSqlMapClientTemplate().queryForList("findStageHistoryForChartPerPipeline", args));
}
public int getTotalStageCountForChart(String pipelineName, String stageName) {
String key = cacheKeyForStageCountForGraph(pipelineName, stageName);
Integer total = (Integer) goCache.get(key);
if (total == null) {
synchronized (key) {
if (total == null) {
Map<String, Object> toGet = arguments("pipelineName", pipelineName).and("stageName", stageName).asMap();
total = (Integer) getSqlMapClientTemplate().queryForObject("getTotalStageCountForChart", toGet);
goCache.put(key, total);
}
}
}
return total;
}
@Override
public List<StageIdentity> findLatestStageInstances() {
String key = cacheKeyForLatestStageInstances();
List<StageIdentity> stageIdentities = (List<StageIdentity>) goCache.get(key);
if (stageIdentities == null) {
synchronized (key) {
stageIdentities = (List<StageIdentity>) goCache.get(key);
if (stageIdentities == null) {
stageIdentities = (List<StageIdentity>) getSqlMapClientTemplate().queryForList("latestStageInstances");
goCache.put(key, stageIdentities);
}
}
}
return stageIdentities;
}
public StageHistoryPage findStageHistoryPageByNumber(final String pipelineName, final String stageName, final int pageNumber, final int pageSize) {
return findStageHistoryPage(pipelineName, stageName, new FuncVarArg<Pagination, Object>() {
public Pagination call(Object... ignore) {
int total = getCount(pipelineName, stageName);
return Pagination.pageByNumber(pageNumber, total, pageSize);
}
});
}
public StageInstanceModels findDetailedStageHistoryByOffset(String pipelineName, String stageName, final Pagination pagination) {
String mutex = mutexForStageHistory(pipelineName, stageName);
readWriteLock.acquireReadLock(mutex);
try {
String subKey = String.format("%s-%s", pagination.getOffset(), pagination.getPageSize());
String key = cacheKeyForDetailedStageHistories(pipelineName, stageName);
StageInstanceModels stageInstanceModels = (StageInstanceModels) goCache.get(key, subKey);
if (stageInstanceModels == null) {
stageInstanceModels = findDetailedStageHistory(pipelineName, stageName, pagination);
goCache.put(key, subKey, stageInstanceModels);
}
return cloner.deepClone(stageInstanceModels);
} finally {
readWriteLock.releaseReadLock(mutex);
}
}
public StageHistoryPage findStageHistoryPage(final Stage stage, final int pageSize) {
final StageIdentifier id = stage.getIdentifier();
return findStageHistoryPage(id.getPipelineName(), id.getStageName(), new FuncVarArg<Pagination, Object>() {
public Pagination call(Object... ignore) {
int total = getCount(id.getPipelineName(), id.getStageName());
int offset = findOffsetForStage(stage);
return Pagination.pageFor(offset, total, pageSize);
}
});
}
public StageHistoryPage findStageHistoryPage(String pipelineName, String stageName, FuncVarArg<Pagination, Object> function) {
//IMPORTANT: wire cache clearing on job-state-change for me, the day StageHistoryEntry gets jobs - Sachin & JJ
String mutex = mutexForStageHistory(pipelineName, stageName);
readWriteLock.acquireReadLock(mutex);
try {
Pagination pagination = function.call();
String subKey = String.format("%s-%s", pagination.getCurrentPage(), pagination.getPageSize());
String key = cacheKeyForStageHistories(pipelineName, stageName);
StageHistoryPage stageHistoryPage = (StageHistoryPage) goCache.get(key, subKey);
if (stageHistoryPage == null) {
List<StageHistoryEntry> stageHistoryEntries = findStages(pagination, pipelineName, stageName);
stageHistoryPage = new StageHistoryPage(stageHistoryEntries, pagination, findImmediateChronologicallyForwardStageHistoryEntry(stageHistoryEntries.get(0)));
goCache.put(key, subKey, stageHistoryPage);
}
return cloner.deepClone(stageHistoryPage);
} finally {
readWriteLock.releaseReadLock(mutex);
}
}
public StageHistoryEntry findImmediateChronologicallyForwardStageHistoryEntry(StageHistoryEntry stageHistoryEntry) {
StageIdentifier stageIdentifier = stageHistoryEntry.getIdentifier();
Map<String, Object> args = arguments("pipelineName", stageIdentifier.getPipelineName()).
and("stageName", stageIdentifier.getStageName()).
and("id", stageHistoryEntry.getId()).
and("limit", 1).asMap();
return (StageHistoryEntry) getSqlMapClientTemplate().queryForObject("findStageHistoryEntryBefore", args);
}
private String mutexForStageHistory(String pipelineName, String stageName) {
return String.format("%s_stageHistoryMutex_%s_<>_%s", getClass().getName(), pipelineName, stageName).intern();
}
private String cacheKeyForStageHistories(String pipelineName, String stageName) {
return String.format("%s_stageHistories_%s_<>_%s", getClass().getName(), pipelineName, stageName).intern();
}
private String cacheKeyForDetailedStageHistories(String pipelineName, String stageName) {
return String.format("%s_detailedStageHistories_%s_<>_%s", getClass().getName(), pipelineName, stageName).intern();
}
public Long findStageIdByPipelineAndStageNameAndCounter(long pipelineId, String name, String counter) {
Map<String, Object> toGet = arguments("pipelineId", pipelineId)
.and("stageName", name)
.and("stageCounter", counter)
.asMap();
return (Long) getSqlMapClientTemplate().queryForObject("findStageIdByPipelineAndStageNameAndCounter", toGet);
}
public List<StageIdentifier> findFailedStagesBetween(String pipelineName, String stageName, double fromNaturalOrder, double toNaturalOrder) {
Map<String, Object> args = arguments("pipelineName", pipelineName).
and("stageName", stageName).
and("fromNaturalOrder", fromNaturalOrder).
and("toNaturalOrder", toNaturalOrder).asMap();
return (List<StageIdentifier>) getSqlMapClientTemplate().queryForList("findFailedStagesBetween", args);
}
List<StageHistoryEntry> findStages(Pagination pagination, String pipelineName, String stageName) {
Map<String, Object> args = arguments("pipelineName", pipelineName).
and("stageName", stageName).
and("limit", pagination.getPageSize()).
and("offset", pagination.getOffset()).asMap();
return (List<StageHistoryEntry>) getSqlMapClientTemplate().queryForList("findStageHistoryPage", args);
}
StageInstanceModels findDetailedStageHistory(String pipelineName, String stageName, Pagination pagination) {
Map<String, Object> args = arguments("pipelineName", pipelineName).
and("stageName", stageName).
and("limit", pagination.getPageSize()).
and("offset", pagination.getOffset()).asMap();
List<StageInstanceModel> detailedStageHistory = (List<StageInstanceModel>) getSqlMapClientTemplate().queryForList("getDetailedStageHistory", args);
StageInstanceModels stageInstanceModels = new StageInstanceModels();
stageInstanceModels.addAll(detailedStageHistory);
return stageInstanceModels;
}
private int findOffsetForStage(Stage stage) {
String key = cacheKeyForStageOffset(stage);
Integer offset = (Integer) goCache.get(key, String.valueOf(stage.getId()));
if (offset == null) {
Map<String, Object> args =
arguments("stageId", stage.getId()).
and("stageName", stage.getIdentifier().getStageName()).
and("pipelineName", stage.getIdentifier().getPipelineName())
.asMap();
offset = (Integer) getSqlMapClientTemplate().queryForObject("findOffsetForStage", args);
goCache.put(key, String.valueOf(stage.getId()), offset);
}
return offset;
}
private String cacheKeyForStageOffset(Stage stage) {
return String.format("%s_stageOffsetMap_%s_<>_%s", getClass().getName(), stage.getIdentifier().getPipelineName(), stage.getIdentifier().getStageName()).intern();
}
private List<StageFeedEntry> findForFeed(String baseQuery, FeedModifier modifier, long transitionId, int pageSize) {
Map parameters = new HashMap();
parameters.put("value", transitionId);
parameters.put("pageLimit", pageSize);
return getSqlMapClientTemplate().queryForList(baseQuery + modifier.suffix(), parameters);
}
public List<StageFeedEntry> findAllCompletedStages(FeedModifier modifier, long id, int pageSize) {
return findForFeed("allCompletedStages", modifier, id, pageSize);
}
public List<StageFeedEntry> findCompletedStagesFor(String pipelineName, FeedModifier feedModifier, long transitionId, long pageSize) {
return _findCompletedStagesFor(pipelineName, feedModifier, transitionId, pageSize);
}
private List<StageFeedEntry> _findCompletedStagesFor(String pipelineName, FeedModifier feedModifier, long transitionId, long pageSize) {
//IMPORTANT: wire cache clearing on job-state-change for me, the day FeedEntry gets jobs - Sachin & JJ
Map parameters = new HashMap();
parameters.put("value", transitionId);
parameters.put("pageLimit", pageSize);
parameters.put("pipelineName", pipelineName);
return (List<StageFeedEntry>) getSqlMapClientTemplate().queryForList("allCompletedStagesForPipeline" + feedModifier.suffix(), parameters);
}
public Stage mostRecentWithBuilds(String pipelineName, StageConfig stageConfig) {
Long mostRecentId = mostRecentId(pipelineName, CaseInsensitiveString.str(stageConfig.name()));
return mostRecentId == null ? NullStage.createNullStage(stageConfig) : stageByIdWithBuildsWithNoAssociations(mostRecentId);
}
Long mostRecentId(String pipelineName, String stageName) {
String key = cacheKeyForMostRecentId(pipelineName, stageName);
Long id = (Long) goCache.get(key);
if (id != null) {
return id;
}
synchronized (key) {
id = (Long) goCache.get(key);
if (id != null) {
return id;
}
Map<String, Object> toGet = arguments("pipelineName", pipelineName).and("stageName", stageName).asMap();
id = (Long) getSqlMapClientTemplate().queryForObject("getMostRecentId", toGet);
goCache.put(key, id);
}
return id;
}
public void stageStatusChanged(Stage stage) {
String pipelineName = stage.getIdentifier().getPipelineName();
String stageName = stage.getName();
removeFromCache(cacheKeyForMostRecentId(pipelineName, stageName));
removeFromCache(cacheKeyForPipelineAndStage(pipelineName, stageName));
removeFromCache(cacheKeyForPipelineAndCounter(pipelineName, stage.getIdentifier().getPipelineCounter()));
removeFromCache(cacheKeyForStageById(stage.getId()));
removeFromCache(cacheKeyForLatestStageInstances());
}
public Stage stageByIdWithBuilds(long id) {
return stageById(id);
}
public Stage stageById(long id) {
String key = cacheKeyForStageById(id);
Stage stage = (Stage) goCache.get(key);
if (stage == null) {
synchronized (key) {
stage = (Stage) goCache.get(key);
if (stage == null) {
stage = (Stage) getSqlMapClientTemplate().queryForObject("getStageById", id);
if (stage == null) {
throw new DataRetrievalFailureException("Unable to load related stage data for id " + id);
}
goCache.put(key, stage);
}
}
}
return cloner.deepClone(stage);
}
private String cacheKeyForStageById(long id) {
return String.format("%s_stageById_%s", getClass().getName(), id).intern();
}
public Stage mostRecentPassed(String pipelineName, String stageName) {
Map<String, Object> toGet = arguments("pipelineName", pipelineName).and("stageName", stageName).asMap();
Long mostRecentId = (Long) getSqlMapClientTemplate().queryForObject("getMostRecentPassedStageId", toGet);
return mostRecentId == null ? null : stageByIdWithBuilds(mostRecentId);
}
public boolean isStageActive(String pipelineName, String stageName) {
String cacheKey = cacheKeyForPipelineAndStage(pipelineName, stageName);
synchronized (cacheKey) {
Boolean isActive = (Boolean) goCache.get(cacheKey);
if (isActive == null) {
final Map<String, Object> toGet = arguments("pipelineName", pipelineName).and("stageName", stageName).asMap();
isActive = !getSqlMapClientTemplate().queryForObject("isStageActive", toGet).equals(0);
goCache.put(cacheKey, isActive);
}
return isActive;
}
}
public Stage getStageByBuild(long buildId) {
Stage stage = (Stage) getSqlMapClientTemplate().queryForObject("getStageByBuildId", buildId);
if (stage == null) {
throw new DataRetrievalFailureException("Unable to load related stage data for buildId " + buildId);
}
return stage;
}
public long getStageIdFromBuildId(long buildId) {
Long stageId = (Long) getSqlMapClientTemplate().queryForObject("getStageIdFromBuildId", buildId);
if (stageId == null) {
throw new DataRetrievalFailureException("No Stage found containing buildId " + buildId);
}
return stageId;
}
public String stageNameByStageId(long stageId) {
return getSqlMapClientTemplate().queryForObject("getStageNameByStageId", stageId).toString();
}
public void setBuildInstanceDao(JobInstanceSqlMapDao buildInstanceDao) {
this.buildInstanceDao = buildInstanceDao;
}
public void setCache(Cache cache) {
this.cache = cache;
}
public void clearCachedStage(StageIdentifier identifier) {
removeFromCache(cacheKeyForListOfStageIdentifiers(identifier));
}
public void clearCachedAllStages(String pipelineName, int pipelineCounter, String stageName) {
String cacheForAllStagesOfPipeline = cacheKeyForAllStageOfPipeline(pipelineName, pipelineCounter, stageName);
removeFromCache(cacheForAllStagesOfPipeline);
}
private String cacheKeyForPipelineAndStage(String pipelineName, String stageName) {
return String.format("%s_isStageActive_%s_%s", StageSqlMapDao.class.getName(), pipelineName, stageName).intern();
}
public Stages findAllStagesFor(String pipelineName, int counter) {
String key = cacheKeyForPipelineAndCounter(pipelineName, counter);
List<Stage> stages = (List<Stage>) goCache.get(key);
if (stages == null) {
synchronized (key) {
stages = (List<Stage>) goCache.get(key);
if (stages == null) {
Map<String, Object> params = arguments("pipelineName", pipelineName).and("pipelineCounter", counter).asMap();
stages = getSqlMapClientTemplate().queryForList("getStagesByPipelineNameAndCounter", params);
goCache.put(key, stages);
}
}
}
return new Stages(stages);
}
public List<Stage> oldestStagesHavingArtifacts() {
return getSqlMapClientTemplate().queryForList("oldestStagesHavingArtifacts");
}
public void markArtifactsDeletedFor(Stage stage) {
getSqlMapClientTemplate().update("markStageArtifactDeleted", arguments("stageId", stage.getId()).asMap());
}
private String cacheKeyForPipelineAndCounter(String pipelineName, int counter) {
String key = StageSqlMapDao.class.getName() + "_allStagesOfPipelineInstance_" + pipelineName + "_" + counter;
return key.intern();
}
public void jobStatusChanged(JobInstance job) {
clearJobStatusDependentCaches(job.getStageId(), job.getIdentifier().getStageIdentifier());
}
private void clearJobStatusDependentCaches(long stageId, StageIdentifier stageIdentifier) {
removeFromCache(cacheKeyForStageById(stageId));
clearCachedStage(stageIdentifier);
clearCachedAllStages(stageIdentifier.getPipelineName(), stageIdentifier.getPipelineCounter(), stageIdentifier.getStageName());
}
String cacheKeyForLatestStageInstances() {
return String.format("%s_latestStageInstances", getClass().getName());
}
private String cacheKeyForStageCountForGraph(String pipelineName, String stageName) {
return String.format("%s_totalStageCountForChart_%s_%s", getClass().getName(), pipelineName, stageName).intern();
}
private void removeFromCache(String key) {
synchronized (key) {
goCache.remove(key);
}
}
String cacheKeyForMostRecentId(String pipelineName, String stageName) {
return String.format("%s_mostRecentId_%s_%s", getClass().getName(), pipelineName, stageName).intern();
}
private Stage stageByIdWithBuildsWithNoAssociations(long id) {
return stageById(id);
}
}