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.dbcleaner.runner; 021 022 import org.hibernate.HibernateException; 023 import org.slf4j.Logger; 024 import org.slf4j.LoggerFactory; 025 import org.sonar.api.batch.PostJob; 026 import org.sonar.api.batch.SensorContext; 027 import org.sonar.api.database.DatabaseSession; 028 import org.sonar.api.database.model.Snapshot; 029 import org.sonar.api.resources.Project; 030 import org.sonar.api.utils.TimeProfiler; 031 import org.sonar.core.NotDryRun; 032 import org.sonar.plugins.dbcleaner.api.Purge; 033 034 import javax.persistence.Query; 035 036 @NotDryRun 037 public final class PurgeRunner implements PostJob { 038 039 private DatabaseSession session; 040 private Snapshot snapshot; 041 private Purge[] purges; 042 private org.sonar.api.batch.Purge[] deprecatedPurges; 043 private Project project; 044 private static final Logger LOG = LoggerFactory.getLogger(PurgeRunner.class); 045 046 public PurgeRunner(DatabaseSession session, Project project, Snapshot snapshot, Purge[] purges) { 047 this.session = session; 048 this.project = project; 049 this.snapshot = snapshot; 050 this.purges = purges.clone(); 051 this.deprecatedPurges = new org.sonar.api.batch.Purge[0]; 052 } 053 054 public PurgeRunner(DatabaseSession session, Project project, Snapshot snapshot, Purge[] purges, org.sonar.api.batch.Purge[] deprecatedPurges) { 055 this.session = session; 056 this.project = project; 057 this.snapshot = snapshot; 058 this.purges = purges.clone(); 059 this.deprecatedPurges = deprecatedPurges.clone(); 060 } 061 062 public void executeOn(Project project, SensorContext context) { 063 if (shouldExecuteOn(project)) { 064 purge(); 065 } 066 } 067 068 static boolean shouldExecuteOn(Project project) { 069 return project.isRoot(); 070 } 071 072 public void purge() { 073 TimeProfiler profiler = new TimeProfiler(LOG).start("Database optimization"); 074 DefaultPurgeContext context = newContext(); 075 LOG.debug("Snapshots to purge: " + context); 076 executeDeprecatedPurges(context); 077 executePurges(context); 078 profiler.stop(); 079 } 080 081 private void executeDeprecatedPurges(DefaultPurgeContext context) { 082 TimeProfiler profiler = new TimeProfiler(); 083 for (org.sonar.api.batch.Purge purge : deprecatedPurges) { 084 try { 085 profiler.start("Purge " + purge.getClass().getName()); 086 purge.purge(context); 087 session.commit();// force hibernate to commit, so we're sure that the potential raised exception comes from this purge 088 profiler.stop(); 089 090 } catch (javax.persistence.PersistenceException e) { 091 // Temporary workaround for MySQL deadlocks. The exception must not fail the build 092 // See https://jira.codehaus.org/browse/SONAR-2961 and https://jira.codehaus.org/browse/SONAR-2190 093 LOG.warn("Fail to execute purge: " + purge, e); 094 095 } catch (HibernateException e) { 096 // Temporary workaround for MySQL deadlocks. The exception must not fail the build 097 // See https://jira.codehaus.org/browse/SONAR-2961 and https://jira.codehaus.org/browse/SONAR-2190 098 LOG.warn("Fail to execute purge: " + purge, e); 099 } 100 } 101 } 102 103 private void executePurges(DefaultPurgeContext context) { 104 TimeProfiler profiler = new TimeProfiler(); 105 for (Purge purge : purges) { 106 try { 107 profiler.start("Purge " + purge.getClass().getName()); 108 purge.purge(context); 109 session.commit(); // force hibernate to commit, so we're sure that the potential raised exception comes from this purge 110 profiler.stop(); 111 } catch (javax.persistence.PersistenceException e) { 112 // Temporary workaround for MySQL deadlocks. The exception must not fail the build 113 // See https://jira.codehaus.org/browse/SONAR-2961 and https://jira.codehaus.org/browse/SONAR-2190 114 LOG.warn("Fail to execute purge: " + purge, e); 115 116 } catch (HibernateException e) { 117 // Temporary workaround for MySQL deadlocks. The exception must not fail the build 118 // See https://jira.codehaus.org/browse/SONAR-2961 and https://jira.codehaus.org/browse/SONAR-2190 119 LOG.warn("Fail to execute purge: " + purge, e); 120 } 121 } 122 } 123 124 private DefaultPurgeContext newContext() { 125 DefaultPurgeContext context = new DefaultPurgeContext(project, snapshot); 126 Snapshot previousLastSnapshot = getPreviousLastSnapshot(); 127 if (previousLastSnapshot != null && previousLastSnapshot.getCreatedAt().before(snapshot.getCreatedAt())) { 128 context.setLastSnapshotId(previousLastSnapshot.getId()); 129 } 130 return context; 131 } 132 133 private Snapshot getPreviousLastSnapshot() { 134 Query query = session.createQuery( 135 "SELECT s FROM " + Snapshot.class.getSimpleName() + " s " + 136 "WHERE s.status=:status AND s.resourceId=:resourceId AND s.createdAt<:date AND s.id <> :sid ORDER BY s.createdAt DESC"); 137 query.setParameter("status", Snapshot.STATUS_PROCESSED); 138 query.setParameter("resourceId", snapshot.getResourceId()); 139 query.setParameter("date", snapshot.getCreatedAt()); 140 query.setParameter("sid", snapshot.getId()); 141 query.setMaxResults(1); 142 return session.getSingleResult(query, null); 143 } 144 }