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.ArrayUtils;
023    import org.apache.commons.lang.StringUtils;
024    import org.slf4j.Logger;
025    import org.slf4j.LoggerFactory;
026    import org.sonar.api.CoreProperties;
027    import org.sonar.api.batch.bootstrap.ProjectBuilder;
028    import org.sonar.api.batch.bootstrap.ProjectDefinition;
029    import org.sonar.api.batch.bootstrap.ProjectReactor;
030    import org.sonar.api.config.Settings;
031    import org.sonar.api.task.TaskComponent;
032    
033    import javax.annotation.Nullable;
034    
035    /**
036     * Exclude the sub-projects as defined by the properties sonar.skippedModules and sonar.includedModules
037     *
038     * @since 2.12
039     */
040    public class ProjectExclusions implements TaskComponent {
041    
042      private static final Logger LOG = LoggerFactory.getLogger(ProjectExclusions.class);
043    
044      private Settings settings;
045      private ProjectReactor reactor;
046    
047      public ProjectExclusions(Settings settings, ProjectReactor reactor,
048        // exclusions are applied when the project is completely defined by extensions
049        @Nullable ProjectBuilder[] projectBuilders) {
050        this.settings = settings;
051        this.reactor = reactor;
052      }
053    
054      public ProjectExclusions(Settings settings, ProjectReactor reactor) {
055        this(settings, reactor, new ProjectBuilder[0]);
056      }
057    
058      public void apply() {
059        if (!reactor.getProjects().isEmpty() && StringUtils.isNotBlank(reactor.getProjects().get(0).getKey())) {
060          LOG.info("Apply project exclusions");
061    
062          if (settings.hasKey(CoreProperties.CORE_INCLUDED_MODULES_PROPERTY)) {
063            LOG.warn("'sonar.includedModules' property is deprecated since version 4.3 and should not be used anymore.");
064          }
065          if (settings.hasKey(CoreProperties.CORE_SKIPPED_MODULES_PROPERTY)) {
066            LOG.warn("'sonar.skippedModules' property is deprecated since version 4.3 and should not be used anymore.");
067          }
068    
069          for (ProjectDefinition project : reactor.getProjects()) {
070            if (isExcluded(key(project), project == reactor.getRoot())) {
071              exclude(project);
072            }
073          }
074        }
075      }
076    
077      private boolean isExcluded(String projectKey, boolean isRoot) {
078        String[] includedKeys = settings.getStringArray(CoreProperties.CORE_INCLUDED_MODULES_PROPERTY);
079        boolean excluded = false;
080        if (!isRoot && includedKeys.length > 0) {
081          excluded = !ArrayUtils.contains(includedKeys, projectKey);
082        }
083        String skippedModulesProperty = CoreProperties.CORE_SKIPPED_MODULES_PROPERTY;
084        if (!excluded) {
085          String[] excludedKeys = settings.getStringArray(skippedModulesProperty);
086          excluded = ArrayUtils.contains(excludedKeys, projectKey);
087        }
088        if (excluded && isRoot) {
089          throw new IllegalArgumentException("The root project can't be excluded. Please check the parameters " + skippedModulesProperty + " and sonar.includedModules.");
090        }
091        return excluded;
092      }
093    
094      private void exclude(ProjectDefinition project) {
095        LOG.info(String.format("Exclude project: %s [%s]", project.getName(), project.getKey()));
096        project.remove();
097      }
098    
099      static String key(ProjectDefinition project) {
100        String key = project.getKey();
101        if (key.contains(":")) {
102          return StringUtils.substringAfter(key, ":");
103        }
104        return key;
105      }
106    }