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 }