/*
* This program is copyright (c) 2007 Hortis-GRC SA.
*
* This file is part of Sonar.
* Sonar is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Sonar is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package ch.hortis.sonar.core.batch;
import ch.hortis.sonar.core.service.AbstractService;
import ch.hortis.sonar.core.service.AvgCalculator;
import ch.hortis.sonar.core.service.CodeCoverageCalculator;
import ch.hortis.sonar.core.service.ErrorRciCalculator;
import ch.hortis.sonar.core.service.MissingUnitTestsService;
import ch.hortis.sonar.core.service.RuleErrorsCountCalculator;
import ch.hortis.sonar.core.service.RuleWarningsCountCalculator;
import ch.hortis.sonar.core.service.Service;
import ch.hortis.sonar.core.service.SnapshotProcessor;
import ch.hortis.sonar.core.service.SourcesHighlighterService;
import ch.hortis.sonar.core.service.SumCalculator;
import ch.hortis.sonar.core.service.TendencyCalculator;
import ch.hortis.sonar.core.service.TransactionalSnapshotProcessor;
import ch.hortis.sonar.core.service.WarningRciCalculator;
import ch.hortis.sonar.model.JdbcData;
import ch.hortis.sonar.model.Metrics;
import ch.hortis.sonar.model.SnapshotGroup;
import ch.hortis.sonar.service.MavenProjectService;
import ch.hortis.sonar.service.SnapshotGroupService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import javax.persistence.FlushModeType;
import javax.persistence.Query;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
public class ProjectAnalyserTask extends DatabaseTask {
private static final Logger LOG = LoggerFactory.getLogger(ProjectAnalyserTask.class);
public ProjectAnalyserTask(JdbcData jdbcData) {
super(jdbcData);
}
protected List<Service> getTaskServices() {
List<ch.hortis.sonar.service.Service> dbServices =
Collections.unmodifiableList(ch.hortis.sonar.service.Service.getAllServices(getEntityManager()));
List<Service> services = new ArrayList<Service>();
services.add(new MissingUnitTestsService());
services.add(new SumCalculator(Metrics.CHANGELOG_COMMITS));
services.add(new SumCalculator(Metrics.CHANGELOG_FILE_COMMITS));
services.add(new SumCalculator(Metrics.NCSS_CLASSES));
services.add(new SumCalculator(Metrics.CYCLOMATIC_COMPLEXITY));
services.add(new SumCalculator(Metrics.NCSS_FUNCTIONS));
services.add(new SumCalculator(Metrics.NCSS_NCSS));
services.add(new SumCalculator(Metrics.NCSS_PACKAGES));
services.add(new SumCalculator(Metrics.PMD_DUPLICATED_LINES));
services.add(new SumCalculator(Metrics.PMD_DUPLICATED_TOKENS));
services.add(new SumCalculator(Metrics.PMD_DUPLICATION));
services.add(new SumCalculator(Metrics.SUREFIRE_TESTS));
services.add(new SumCalculator(Metrics.SUREFIRE_TIME));
services.add(new AvgCalculator(Metrics.CYCLOMATIC_COMPLEXITY_AVG_CLASS, Metrics.CYCLOMATIC_COMPLEXITY, Metrics.NCSS_CLASSES));
services.add(new AvgCalculator(Metrics.CYCLOMATIC_COMPLEXITY_AVG_FUNCTION, Metrics.CYCLOMATIC_COMPLEXITY, Metrics.NCSS_FUNCTIONS));
services.add(new CodeCoverageCalculator());
services.add(new RuleErrorsCountCalculator());
services.add(new RuleWarningsCountCalculator());
services.add(new ErrorRciCalculator());
services.add(new WarningRciCalculator());
services.add(new SourcesHighlighterService());
services.add(new TendencyCalculator(Metrics.tendencyableMetrics()));
for (Service service : services) {
if (service instanceof AbstractService) {
((AbstractService) service).setDatabaseServices(dbServices);
}
}
return services;
}
public void execute() {
long startTime = System.currentTimeMillis();
Collection<SnapshotGroup> groups = getGroupsToProcess();
int processed = 0;
MavenProjectService projectService = new MavenProjectService(getEntityManager());
SnapshotGroupService snapshotGroupService = new SnapshotGroupService(getEntityManager());
for (Iterator<SnapshotGroup> i = groups.iterator(); i.hasNext();) {
SnapshotGroup group = i.next();
if (!snapshotGroupService.isReadyToCalculateMeasures(group, projectService)) {
i.remove();
}
}
// removing all references to snaphsots from the entitymanager VERY important to clean memory
createNewEntityManager();
LOG.info(groups.size() + " snapshots to process");
for (Iterator<SnapshotGroup> i = groups.iterator(); i.hasNext(); ) {
SnapshotGroup group = i.next();
// since SnapshotGroup are now detached must reload them to put them under the entitymanager control
LOG.info("Reloading snapshot group " + group.getId());
group = getEntityManager().find(SnapshotGroup.class, group.getId());
try {
long snapshotGroupStartTime = System.currentTimeMillis();
MDC.put("group", group.getId().toString());
LOG.info("Process snapshot group");
processGroup(group);
long timeTaken = System.currentTimeMillis() - snapshotGroupStartTime;
LOG.info("Processed snapshot group in " + (timeTaken) + " ms");
processed++;
} catch (Throwable e) {
LOG.error("Cannot process the snapshot group", e);
removeInvalidGroup(group);
} finally {
MDC.clear();
}
// cleaning memory
i.remove();
createNewEntityManager();
}
if (processed > 0) {
long timeTaken = System.currentTimeMillis() - startTime;
LOG.info(processed + " snapshots processed in " + (timeTaken) + " ms");
}
}
protected Collection<SnapshotGroup> getGroupsToProcess() {
Query query = getEntityManager().createQuery("SELECT g FROM SnapshotGroup g WHERE g.processed=false ORDER BY g.createdAt asc");
query.setFlushMode( FlushModeType.COMMIT );
return query.getResultList();
}
protected void processGroup(SnapshotGroup group) throws Throwable {
// each snapshots of the group will be processed within their own transaction
SnapshotProcessor snapshotProcessor = new TransactionalSnapshotProcessor(getEntityManager(), getTaskServices());
snapshotProcessor.process(group.getRootSnapshot());
getEntityManager().getTransaction().begin();
try {
group.setProcessed(true);
Query query = getEntityManager().createQuery("SELECT g FROM SnapshotGroup g WHERE g.mavenProject = :project AND g.last=true AND g.processed=true");
query.setParameter("project", group.getMavenProject());
Collection<SnapshotGroup> lastGroups = query.getResultList();
for (SnapshotGroup lastGroup : lastGroups) {
lastGroup.setLast(Boolean.FALSE);
getEntityManager().merge(lastGroup);
}
group.setLast(true);
getEntityManager().merge(group);
getEntityManager().getTransaction().commit();
} catch ( Throwable t ) {
getEntityManager().getTransaction().rollback();
throw t;
}
}
private void removeInvalidGroup(SnapshotGroup group) {
//Remove invalid groups
getEntityManager().clear();
getEntityManager().getTransaction().begin();
Query query = getEntityManager().createQuery("DELETE FROM Snapshot WHERE snapshotGroup.id=:groupId");
query.setParameter("groupId", group.getId());
query.executeUpdate();
query = getEntityManager().createQuery("DELETE FROM SnapshotGroup WHERE id=:groupId");
query.setParameter("groupId", group.getId());
query.executeUpdate();
LOG.info("Remove SnapshotGroup with id=" + group.getId());
getEntityManager().getTransaction().commit();
}
}