001 /*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2012 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.batch;
021
022 import com.google.common.annotations.VisibleForTesting;
023 import com.google.common.collect.Maps;
024 import org.apache.commons.lang.StringUtils;
025 import org.apache.maven.model.CiManagement;
026 import org.apache.maven.model.IssueManagement;
027 import org.apache.maven.model.Scm;
028 import org.apache.maven.project.MavenProject;
029 import org.sonar.api.CoreProperties;
030 import org.sonar.api.batch.bootstrap.ProjectDefinition;
031 import org.sonar.api.utils.SonarException;
032
033 import java.io.File;
034 import java.io.IOException;
035 import java.util.List;
036 import java.util.Map;
037
038 public final class MavenProjectConverter {
039
040 private static final String UNABLE_TO_DETERMINE_PROJECT_STRUCTURE_EXCEPTION_MESSAGE = "Unable to determine structure of project." +
041 " Probably you use Maven Advanced Reactor Options, which is not supported by Sonar and should not be used.";
042
043 public static ProjectDefinition convert(List<MavenProject> poms, MavenProject root) {
044 Map<String, MavenProject> paths = Maps.newHashMap(); // projects by canonical path to pom.xml
045 Map<MavenProject, ProjectDefinition> defs = Maps.newHashMap();
046
047 try {
048 for (MavenProject pom : poms) {
049 paths.put(pom.getFile().getCanonicalPath(), pom);
050 defs.put(pom, convert(pom));
051 }
052
053 for (Map.Entry<String, MavenProject> entry : paths.entrySet()) {
054 MavenProject pom = entry.getValue();
055 for (Object m : pom.getModules()) {
056 String moduleId = (String) m;
057 File modulePath = new File(pom.getBasedir(), moduleId);
058 if (modulePath.exists() && modulePath.isDirectory()) {
059 modulePath = new File(modulePath, "pom.xml");
060 }
061 MavenProject module = paths.get(modulePath.getCanonicalPath());
062
063 ProjectDefinition parentProject = defs.get(pom);
064 ProjectDefinition subProject = defs.get(module);
065 if (parentProject == null) {
066 throw new IllegalStateException(UNABLE_TO_DETERMINE_PROJECT_STRUCTURE_EXCEPTION_MESSAGE);
067 }
068 if (subProject == null) {
069 throw new IllegalStateException(UNABLE_TO_DETERMINE_PROJECT_STRUCTURE_EXCEPTION_MESSAGE);
070 }
071 parentProject.addSubProject(subProject);
072 }
073 }
074 } catch (IOException e) {
075 throw new SonarException(e);
076 }
077
078 ProjectDefinition rootProject = defs.get(root);
079 if (rootProject == null) {
080 throw new IllegalStateException(UNABLE_TO_DETERMINE_PROJECT_STRUCTURE_EXCEPTION_MESSAGE);
081 }
082
083 return rootProject;
084 }
085
086 @VisibleForTesting
087 static ProjectDefinition convert(MavenProject pom) {
088 String key = new StringBuilder().append(pom.getGroupId()).append(":").append(pom.getArtifactId()).toString();
089 ProjectDefinition definition = ProjectDefinition.create();
090 // IMPORTANT NOTE : reference on properties from POM model must not be saved, instead they should be copied explicitly - see SONAR-2896
091 definition
092 .setProperties(pom.getModel().getProperties())
093 .setKey(key)
094 .setVersion(pom.getVersion())
095 .setName(pom.getName())
096 .setDescription(pom.getDescription())
097 .addContainerExtension(pom);
098
099 convertMavenLinksToProperties(definition, pom);
100
101 synchronizeFileSystem(pom, definition);
102
103 return definition;
104 }
105
106 /**
107 * For SONAR-3676
108 */
109 private static void convertMavenLinksToProperties(ProjectDefinition definition, MavenProject pom) {
110 setPropertyIfNotAlreadyExists(definition, CoreProperties.LINKS_HOME_PAGE, pom.getUrl());
111
112 Scm scm = pom.getScm();
113 if (scm == null) {
114 scm = new Scm();
115 }
116 setPropertyIfNotAlreadyExists(definition, CoreProperties.LINKS_SOURCES, scm.getUrl());
117 setPropertyIfNotAlreadyExists(definition, CoreProperties.LINKS_SOURCES_DEV, scm.getDeveloperConnection());
118
119 CiManagement ci = pom.getCiManagement();
120 if (ci == null) {
121 ci = new CiManagement();
122 }
123 setPropertyIfNotAlreadyExists(definition, CoreProperties.LINKS_CI, ci.getUrl());
124
125 IssueManagement issues = pom.getIssueManagement();
126 if (issues == null) {
127 issues = new IssueManagement();
128 }
129 setPropertyIfNotAlreadyExists(definition, CoreProperties.LINKS_ISSUE_TRACKER, issues.getUrl());
130 }
131
132 private static void setPropertyIfNotAlreadyExists(ProjectDefinition definition, String propertyKey, String propertyValue) {
133 if (StringUtils.isBlank(definition.getProperties().getProperty(propertyKey))) {
134 definition.setProperty(propertyKey, StringUtils.defaultString(propertyValue));
135 }
136 }
137
138 public static void synchronizeFileSystem(MavenProject pom, ProjectDefinition into) {
139 into.setBaseDir(pom.getBasedir());
140 into.setWorkDir(new File(resolvePath(pom.getBuild().getDirectory(), pom.getBasedir()), "sonar"));
141 into.setSourceDirs((String[]) pom.getCompileSourceRoots().toArray(new String[pom.getCompileSourceRoots().size()]));
142 into.setTestDirs((String[]) pom.getTestCompileSourceRoots().toArray(new String[pom.getTestCompileSourceRoots().size()]));
143 }
144
145 static File resolvePath(String path, File basedir) {
146 File file = new File(path);
147 if (!file.isAbsolute()) {
148 try {
149 file = new File(basedir, path).getCanonicalFile();
150 } catch (IOException e) {
151 throw new SonarException("Unable to resolve path '" + path + "'", e);
152 }
153 }
154 return file;
155 }
156
157 private MavenProjectConverter() {
158 // only static methods
159 }
160 }