001    /*
002     * SonarQube, open source software quality management tool.
003     * Copyright (C) 2008-2014 SonarSource
004     * mailto:contact AT sonarsource DOT com
005     *
006     * SonarQube 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     * SonarQube 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 License
017     * along with this program; if not, write to the Free Software Foundation,
018     * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
019     */
020    package org.sonar.batch.scan;
021    
022    import org.apache.commons.lang.StringUtils;
023    import org.slf4j.Logger;
024    import org.slf4j.LoggerFactory;
025    import org.sonar.api.i18n.I18n;
026    import org.sonar.api.resources.Project;
027    import org.sonar.api.utils.Semaphores;
028    import org.sonar.api.utils.SonarException;
029    import org.sonar.batch.ProjectTree;
030    import org.sonar.batch.bootstrap.AnalysisMode;
031    
032    import java.util.Locale;
033    
034    public class ProjectLock {
035    
036      private static final Logger LOG = LoggerFactory.getLogger(ProjectLock.class);
037    
038      private final Semaphores semaphores;
039      private final ProjectTree projectTree;
040      private final AnalysisMode analysisMode;
041      private final I18n i18n;
042    
043      public ProjectLock(Semaphores semaphores, ProjectTree projectTree, AnalysisMode analysisMode, I18n i18n) {
044        this.semaphores = semaphores;
045        this.projectTree = projectTree;
046        this.analysisMode = analysisMode;
047        this.i18n = i18n;
048      }
049    
050      public void start() {
051        if (!analysisMode.isPreview() && StringUtils.isNotBlank(getProject().getKey())) {
052          Semaphores.Semaphore semaphore = acquire();
053          if (!semaphore.isLocked()) {
054            LOG.error(getErrorMessage(semaphore));
055            throw new SonarException("The project is already being analysed.");
056          }
057        }
058      }
059    
060      private String getErrorMessage(Semaphores.Semaphore semaphore) {
061        long duration = semaphore.getDurationSinceLocked();
062        String durationDisplay = i18n.age(Locale.ENGLISH, duration);
063    
064        return "It looks like an analysis of '" + getProject().getName() + "' is already running (started " + durationDisplay + " ago).";
065      }
066    
067      public void stop() {
068        if (!analysisMode.isPreview()) {
069          release();
070        }
071      }
072    
073      private Semaphores.Semaphore acquire() {
074        LOG.debug("Acquire semaphore on project : {}, with key {}", getProject(), getSemaphoreKey());
075        return semaphores.acquire(getSemaphoreKey(), 15, 10);
076      }
077    
078      private void release() {
079        LOG.debug("Release semaphore on project : {}, with key {}", getProject(), getSemaphoreKey());
080        semaphores.release(getSemaphoreKey());
081      }
082    
083      private String getSemaphoreKey() {
084        return "batch-" + getProject().getKey();
085      }
086    
087      private Project getProject() {
088        return projectTree.getRootProject();
089      }
090    }