001 /* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2008-2011 SonarSource 004 * mailto:contact AT sonarsource DOT com 005 * 006 * Sonar is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * Sonar is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with Sonar; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 019 */ 020 package org.sonar.plugins.core.sensors; 021 022 import java.util.Date; 023 import java.util.List; 024 import java.util.Map; 025 026 import javax.persistence.Query; 027 028 import org.sonar.api.batch.Decorator; 029 import org.sonar.api.batch.DecoratorContext; 030 import org.sonar.api.batch.DependsUpon; 031 import org.sonar.api.database.DatabaseSession; 032 import org.sonar.api.database.model.RuleFailureModel; 033 import org.sonar.api.database.model.Snapshot; 034 import org.sonar.api.resources.Project; 035 import org.sonar.api.resources.Resource; 036 import org.sonar.api.rules.Violation; 037 import org.sonar.batch.index.ResourcePersister; 038 import org.sonar.core.NotDryRun; 039 import org.sonar.jpa.entity.Review; 040 import org.sonar.plugins.core.timemachine.ViolationTrackingDecorator; 041 042 import com.google.common.collect.Maps; 043 044 /** 045 * Decorator that updates reviews that are linked to violations for which the message and the line number have changed. In this case, the 046 * message of the review and its corresponding line number must change. 047 */ 048 @NotDryRun 049 @DependsUpon(CloseReviewsDecorator.REVIEW_LIFECYCLE_BARRIER) 050 public class UpdateReviewsDecorator implements Decorator { 051 052 private ResourcePersister resourcePersister; 053 private DatabaseSession databaseSession; 054 private ViolationTrackingDecorator violationTrackingDecorator; 055 private Query updateReviewQuery; 056 private Query updateReviewQueryForNullLine; 057 private Map<Integer, Violation> violationsPerPermanentId; 058 059 public UpdateReviewsDecorator(ResourcePersister resourcePersister, DatabaseSession databaseSession, 060 ViolationTrackingDecorator violationTrackingDecorator) { 061 this.resourcePersister = resourcePersister; 062 this.databaseSession = databaseSession; 063 this.violationTrackingDecorator = violationTrackingDecorator; 064 violationsPerPermanentId = Maps.newHashMap(); 065 } 066 067 public boolean shouldExecuteOnProject(Project project) { 068 return project.isLatestAnalysis(); 069 } 070 071 @SuppressWarnings({ "rawtypes" }) 072 public void decorate(Resource resource, DecoratorContext context) { 073 Snapshot currentSnapshot = resourcePersister.getSnapshot(resource); 074 if (currentSnapshot != null) { 075 Date currentDate = new Date(); 076 // prepare the map of rule failures by permanent_id 077 for (Violation violation : context.getViolations()) { 078 RuleFailureModel ruleFailure = violationTrackingDecorator.getReferenceViolation(violation); 079 if (ruleFailure != null) { 080 violationsPerPermanentId.put(ruleFailure.getPermanentId(), violation); 081 } 082 } 083 // and run the update 084 updateReviews(currentSnapshot.getResourceId(), currentDate); 085 086 databaseSession.commit(); 087 } 088 } 089 090 @SuppressWarnings({ "unchecked" }) 091 protected void updateReviews(int resourceId, Date currentDate) { 092 // prepare the DB native queries 093 updateReviewQuery = databaseSession 094 .createNativeQuery("UPDATE reviews SET title=?, resource_line=?, updated_at=CURRENT_TIMESTAMP WHERE id=?"); 095 updateReviewQueryForNullLine = databaseSession 096 .createNativeQuery("UPDATE reviews SET title=?, resource_line=NULL, updated_at=CURRENT_TIMESTAMP WHERE id=?"); 097 Query searchReviewsQuery = databaseSession.getEntityManager().createNativeQuery( 098 "SELECT * FROM reviews WHERE status!='CLOSED' AND resource_id=?", Review.class); 099 // and iterate over the reviews that we find 100 List<Review> reviews = searchReviewsQuery.setParameter(1, resourceId).getResultList(); 101 for (Review review : reviews) { 102 checkReviewForUpdate(review, currentDate); 103 } 104 } 105 106 protected void checkReviewForUpdate(Review review, Date currentDate) { 107 Violation violation = violationsPerPermanentId.get(review.getRuleFailurePermamentId()); 108 if (violation != null) { 109 String message = violation.getMessage(); 110 Integer line = violation.getLineId(); 111 if ( !review.getTitle().equals(message) || (review.getResourceLine() == null && line != null) 112 || (review.getResourceLine() != null && !review.getResourceLine().equals(line))) { 113 if (line == null) { 114 updateReviewQueryForNullLine.setParameter(1, message).setParameter(2, review.getId()).executeUpdate(); 115 } else { 116 updateReviewQuery.setParameter(1, message).setParameter(2, line).setParameter(3, review.getId()).executeUpdate(); 117 } 118 } 119 } 120 } 121 122 }