import org.sonar.api.Extension;
import org.sonar.api.Properties;
import org.sonar.api.Property;
+import org.sonar.api.PropertyField;
import org.sonar.api.PropertyType;
import org.sonar.api.SonarPlugin;
import org.sonar.api.checks.NoSonarFilter;
project = false,
global = true,
category = CoreProperties.CATEGORY_GENERAL),
+ @Property(
+ key = CoreProperties.LINKS_HOME_PAGE,
+ defaultValue = "",
+ name = "Project Home Page",
+ description = "HTTP URL of the home page of the project.",
+ project = false,
+ global = false,
+ category = CoreProperties.CATEGORY_GENERAL),
+ @Property(
+ key = CoreProperties.LINKS_CI,
+ defaultValue = "",
+ name = "CI server",
+ description = "HTTP URL of the continuous integration server.",
+ project = false,
+ global = false,
+ category = CoreProperties.CATEGORY_GENERAL),
+ @Property(
+ key = CoreProperties.LINKS_ISSUE_TRACKER,
+ defaultValue = "",
+ name = "Issue Tracker",
+ description = "HTTP URL of the issue tracker.",
+ project = false,
+ global = false,
+ category = CoreProperties.CATEGORY_GENERAL),
+ @Property(
+ key = CoreProperties.LINKS_SOURCES,
+ defaultValue = "",
+ name = "SCM server",
+ description = "HTTP URL of the server which hosts the sources of the project.",
+ project = false,
+ global = false,
+ category = CoreProperties.CATEGORY_GENERAL),
+ @Property(
+ key = CoreProperties.LINKS_SOURCES_DEV,
+ defaultValue = "",
+ name = "SCM connection for developers",
+ description = "HTTP URL used by developers to connect to the SCM server for the project.",
+ project = false,
+ global = false,
+ category = CoreProperties.CATEGORY_GENERAL),
+ @Property(
+ key = "sonar.test.jira.servers",
+ name = "Jira Servers",
+ description = "List of jira server definitions",
+ global = true,
+ project = true,
+ category = "DEV",
+ fields = {
+ @PropertyField(
+ key = "key",
+ name = "Key",
+ type = PropertyType.STRING,
+ indicativeSize = 10),
+ @PropertyField(
+ key = "url",
+ name = "Url",
+ description = "l'url du serveur jira",
+ type = PropertyType.STRING,
+ indicativeSize = 20),
+ @PropertyField(
+ key = "port",
+ name = "Port",
+ type = PropertyType.INTEGER,
+ indicativeSize = 5)}),
+ @Property(
+ key = "sonar.demo",
+ name = "Demo",
+ global = true,
+ project = true,
+ category = "DEV",
+ fields = {
+ @PropertyField(
+ key = "text",
+ name = "text",
+ type = PropertyType.TEXT),
+ @PropertyField(
+ key = "boolean",
+ name = "boolean",
+ type = PropertyType.BOOLEAN),
+ @PropertyField(
+ key = "float",
+ name = "float",
+ type = PropertyType.FLOAT),
+ @PropertyField(
+ key = "license",
+ name = "license",
+ type = PropertyType.LICENSE),
+ @PropertyField(
+ key = "metric",
+ name = "metric",
+ type = PropertyType.METRIC),
+ @PropertyField(
+ key = "password",
+ name = "password",
+ type = PropertyType.PASSWORD),
+ @PropertyField(
+ key = "regexp",
+ name = "regexp",
+ type = PropertyType.REGULAR_EXPRESSION),
+ @PropertyField(
+ key = "list",
+ name = "list",
+ type = PropertyType.SINGLE_SELECT_LIST,
+ options = {"AAA", "BBB"})}),
+ @Property(
+ key = "sonar.test.jira",
+ name = "Jira",
+ project = true,
+ category = "DEV",
+ type = PropertyType.PROPERTY_SET,
+ propertySetKey = "sonar.test.jira.servers"),
@Property(
key = CoreProperties.PROJECT_LANGUAGE_PROPERTY,
defaultValue = Java.KEY,
package org.sonar.plugins.core.sensors;
import org.apache.commons.lang.StringUtils;
-import org.apache.maven.model.CiManagement;
-import org.apache.maven.model.IssueManagement;
-import org.apache.maven.model.Scm;
-import org.apache.maven.project.MavenProject;
+import org.sonar.api.CoreProperties;
import org.sonar.api.batch.Sensor;
import org.sonar.api.batch.SensorContext;
-import org.sonar.api.batch.SupportedEnvironment;
+import org.sonar.api.config.Settings;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.ProjectLink;
+import org.sonar.core.i18n.I18nManager;
-@SupportedEnvironment("maven")
-public class ProjectLinksSensor implements Sensor {
+import java.util.Locale;
- public static final String KEY_HOME = "homepage";
- public static final String KEY_CONTINUOUS_INTEGRATION = "ci";
- public static final String KEY_ISSUE_TRACKER = "issue";
- public static final String KEY_SCM = "scm";
- public static final String KEY_SCM_DEVELOPER_CONNECTION = "scm_dev";
+public class ProjectLinksSensor implements Sensor {
- private MavenProject pom;
+ private Settings settings;
+ private I18nManager i18nManager;
- public ProjectLinksSensor(MavenProject pom) {
- this.pom = pom;
+ public ProjectLinksSensor(Settings settings, I18nManager i18nManager) {
+ this.settings = settings;
+ this.i18nManager = i18nManager;
}
public boolean shouldExecuteOnProject(Project project) {
}
public void analyse(Project project, SensorContext context) {
- updateLink(context, KEY_HOME, "Home", pom.getUrl());
-
- Scm scm = pom.getScm();
- if (scm == null) {
- scm = new Scm();
- }
- updateLink(context, KEY_SCM, "Sources", scm.getUrl());
- updateLink(context, KEY_SCM_DEVELOPER_CONNECTION, "Developer connection", scm.getDeveloperConnection());
-
- CiManagement ci = pom.getCiManagement();
- if (ci == null) {
- ci = new CiManagement();
- }
- updateLink(context, KEY_CONTINUOUS_INTEGRATION, "Continuous integration", ci.getUrl());
+ handleLink(context, CoreProperties.LINKS_HOME_PAGE);
+ handleLink(context, CoreProperties.LINKS_CI);
+ handleLink(context, CoreProperties.LINKS_ISSUE_TRACKER);
+ handleLink(context, CoreProperties.LINKS_SOURCES);
+ handleLink(context, CoreProperties.LINKS_SOURCES_DEV);
+ }
- IssueManagement issues = pom.getIssueManagement();
- if (issues == null) {
- issues = new IssueManagement();
- }
- updateLink(context, KEY_ISSUE_TRACKER, "Issues", issues.getUrl());
+ private void handleLink(SensorContext context, String linkProperty) {
+ String home = settings.getString(linkProperty);
+ String linkType = StringUtils.substringAfterLast(linkProperty, ".");
+ String name = i18nManager.message(Locale.getDefault(), "project_links." + linkType, linkProperty);
+ updateLink(context, linkType, name, home);
}
private void updateLink(SensorContext context, String key, String name, String url) {
package org.sonar.plugins.core.sensors;
import org.apache.commons.lang.StringUtils;
-import org.apache.maven.project.MavenProject;
import org.junit.Test;
import org.mockito.ArgumentMatcher;
+import org.sonar.api.CoreProperties;
import org.sonar.api.batch.SensorContext;
+import org.sonar.api.config.Settings;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.ProjectLink;
-import org.sonar.api.test.MavenTestUtils;
+import org.sonar.core.i18n.I18nManager;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
+import java.util.Locale;
+
+import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
public class ProjectLinksSensorTest {
+ @Test
+ public void testToString() {
+ assertThat(new ProjectLinksSensor(null, null).toString()).isEqualTo("ProjectLinksSensor");
+ }
+
@Test
public void shouldExecuteOnlyForLatestAnalysis() {
- MavenProject pom = mock(MavenProject.class);
Project project = mock(Project.class);
when(project.isLatestAnalysis()).thenReturn(true).thenReturn(false);
- assertThat(new ProjectLinksSensor(pom).shouldExecuteOnProject(project), is(true));
- assertThat(new ProjectLinksSensor(pom).shouldExecuteOnProject(project), is(false));
- verify(project, times(2)).isLatestAnalysis();
- verifyNoMoreInteractions(project);
+ assertThat(new ProjectLinksSensor(null, null).shouldExecuteOnProject(project)).isTrue();
+ assertThat(new ProjectLinksSensor(null, null).shouldExecuteOnProject(project)).isFalse();
}
@Test
public void shouldSaveLinks() {
- SensorContext context = mock(SensorContext.class);
- MavenProject pom = MavenTestUtils.loadPom("/org/sonar/plugins/core/sensors/ProjectLinksSensorTest/shouldSaveLinks.xml");
+ Settings settings = new Settings();
+ settings.setProperty(CoreProperties.LINKS_HOME_PAGE, "http://home");
+ I18nManager i18nManager = mock(I18nManager.class);
+ when(i18nManager.message(Locale.getDefault(), "project_links.homepage", CoreProperties.LINKS_HOME_PAGE)).thenReturn("HOME");
Project project = mock(Project.class);
+ SensorContext context = mock(SensorContext.class);
- new ProjectLinksSensor(pom).analyse(project, context);
+ new ProjectLinksSensor(settings, i18nManager).analyse(project, context);
- verify(context).saveLink(argThat(new MatchLink(ProjectLinksSensor.KEY_HOME, "Home", "http://sonar.codehaus.org")));
- verify(context).saveLink(argThat(new MatchLink(ProjectLinksSensor.KEY_ISSUE_TRACKER, "Issues", "http://jira.codehaus.org/browse/SONAR")));
- verify(context).saveLink(argThat(new MatchLink(ProjectLinksSensor.KEY_CONTINUOUS_INTEGRATION, "Continuous integration", "http://bamboo.ci.codehaus.org/browse/SONAR/")));
- verify(context).saveLink(argThat(new MatchLink(ProjectLinksSensor.KEY_SCM, "Sources", "http://svn.sonar.codehaus.org")));
- verify(context).saveLink(argThat(new MatchLink(ProjectLinksSensor.KEY_SCM_DEVELOPER_CONNECTION, "Developer connection", "scm:svn:https://svn.codehaus.org/sonar/trunk")));
+ verify(context).saveLink(argThat(new MatchLink("homepage", "HOME", "http://home")));
}
@Test
- public void shouldDeleteMissingLinks() {
- SensorContext context = mock(SensorContext.class);
- MavenProject pom = MavenTestUtils.loadPom("/org/sonar/plugins/core/sensors/ProjectLinksSensorTest/shouldDeleteMissingLinks.xml");
+ public void shouldDeleteLink() {
+ Settings settings = new Settings();
+ settings.setProperty(CoreProperties.LINKS_HOME_PAGE, "");
+ I18nManager i18nManager = mock(I18nManager.class);
+ when(i18nManager.message(Locale.getDefault(), "project_links.homepage", CoreProperties.LINKS_HOME_PAGE)).thenReturn("HOME");
Project project = mock(Project.class);
+ SensorContext context = mock(SensorContext.class);
- new ProjectLinksSensor(pom).analyse(project, context);
+ new ProjectLinksSensor(settings, i18nManager).analyse(project, context);
- verify(context).deleteLink(ProjectLinksSensor.KEY_HOME);
- verify(context).deleteLink(ProjectLinksSensor.KEY_ISSUE_TRACKER);
- verify(context).deleteLink(ProjectLinksSensor.KEY_CONTINUOUS_INTEGRATION);
- verify(context).deleteLink(ProjectLinksSensor.KEY_SCM);
- verify(context).deleteLink(ProjectLinksSensor.KEY_SCM_DEVELOPER_CONNECTION);
+ verify(context).deleteLink("homepage");
}
private class MatchLink extends ArgumentMatcher<ProjectLink> {
*/
package org.sonar.batch;
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-import java.util.Map;
-
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Maps;
+import org.apache.commons.lang.StringUtils;
+import org.apache.maven.model.CiManagement;
+import org.apache.maven.model.IssueManagement;
+import org.apache.maven.model.Scm;
import org.apache.maven.project.MavenProject;
+import org.sonar.api.CoreProperties;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.utils.SonarException;
-import com.google.common.collect.Maps;
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
public final class MavenProjectConverter {
private static final String UNABLE_TO_DETERMINE_PROJECT_STRUCTURE_EXCEPTION_MESSAGE = "Unable to determine structure of project." +
- " Probably you use Maven Advanced Reactor Options, which is not supported by Sonar and should not be used.";
+ " Probably you use Maven Advanced Reactor Options, which is not supported by Sonar and should not be used.";
public static ProjectDefinition convert(List<MavenProject> poms, MavenProject root) {
Map<String, MavenProject> paths = Maps.newHashMap(); // projects by canonical path to pom.xml
return rootProject;
}
- /**
- * Visibility has been relaxed for tests.
- */
+ @VisibleForTesting
static ProjectDefinition convert(MavenProject pom) {
String key = new StringBuilder().append(pom.getGroupId()).append(":").append(pom.getArtifactId()).toString();
ProjectDefinition definition = ProjectDefinition.create();
// IMPORTANT NOTE : reference on properties from POM model must not be saved, instead they should be copied explicitly - see SONAR-2896
+ Properties properties = pom.getModel().getProperties();
+ convertMavenLinksToProperties(pom, properties);
definition
- .setProperties(pom.getModel().getProperties())
+ .setProperties(properties)
.setKey(key)
.setVersion(pom.getVersion())
.setName(pom.getName())
.setDescription(pom.getDescription())
.addContainerExtension(pom);
synchronizeFileSystem(pom, definition);
+
return definition;
}
+ /**
+ * For SONAR-3676
+ */
+ private static void convertMavenLinksToProperties(MavenProject pom, Properties properties) {
+ setPropertyIfNotAlreadyExists(properties, CoreProperties.LINKS_HOME_PAGE, pom.getUrl());
+
+ Scm scm = pom.getScm();
+ if (scm == null) {
+ scm = new Scm();
+ }
+ setPropertyIfNotAlreadyExists(properties, CoreProperties.LINKS_SOURCES, scm.getUrl());
+ setPropertyIfNotAlreadyExists(properties, CoreProperties.LINKS_SOURCES_DEV, scm.getDeveloperConnection());
+
+ CiManagement ci = pom.getCiManagement();
+ if (ci == null) {
+ ci = new CiManagement();
+ }
+ setPropertyIfNotAlreadyExists(properties, CoreProperties.LINKS_CI, ci.getUrl());
+
+ IssueManagement issues = pom.getIssueManagement();
+ if (issues == null) {
+ issues = new IssueManagement();
+ }
+ setPropertyIfNotAlreadyExists(properties, CoreProperties.LINKS_ISSUE_TRACKER, issues.getUrl());
+ }
+
+ private static void setPropertyIfNotAlreadyExists(Properties properties, String propertyKey, String propertyValue) {
+ if (StringUtils.isBlank(properties.getProperty(propertyKey))) {
+ properties.setProperty(propertyKey, StringUtils.defaultString(propertyValue));
+ }
+ }
+
public static void synchronizeFileSystem(MavenProject pom, ProjectDefinition into) {
into.setBaseDir(pom.getBasedir());
into.setWorkDir(new File(resolvePath(pom.getBuild().getDirectory(), pom.getBasedir()), "sonar"));
import java.util.Arrays;
import java.util.Properties;
+import static org.fest.assertions.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertNull;
assertThat(rootDef.getBaseDir(), is(rootDir));
}
+ @Test
+ public void shouldConvertLinksToProperties() throws Exception {
+ MavenProject pom = loadPom("/org/sonar/batch/MavenProjectConverterTest/projectWithLinks/pom.xml", true);
+
+ ProjectDefinition rootDef = MavenProjectConverter.convert(Arrays.asList(pom), pom);
+
+ Properties props = rootDef.getProperties();
+ assertThat(props.getProperty(CoreProperties.LINKS_HOME_PAGE)).isEqualTo("http://home.com");
+ assertThat(props.getProperty(CoreProperties.LINKS_CI)).isEqualTo("http://ci.com");
+ assertThat(props.getProperty(CoreProperties.LINKS_ISSUE_TRACKER)).isEqualTo("http://issues.com");
+ assertThat(props.getProperty(CoreProperties.LINKS_SOURCES)).isEqualTo("http://sources.com");
+ assertThat(props.getProperty(CoreProperties.LINKS_SOURCES_DEV)).isEqualTo("http://sources-dev.com");
+ }
+
+ @Test
+ public void shouldNotConvertLinksToPropertiesIfPropertyAlreadyDefined() throws Exception {
+ MavenProject pom = loadPom("/org/sonar/batch/MavenProjectConverterTest/projectWithLinksAndProperties/pom.xml", true);
+
+ ProjectDefinition rootDef = MavenProjectConverter.convert(Arrays.asList(pom), pom);
+
+ Properties props = rootDef.getProperties();
+
+ // Those properties have been fed by the POM elements <ciManagement>, <issueManagement>, ...
+ assertThat(props.getProperty(CoreProperties.LINKS_CI)).isEqualTo("http://ci.com");
+ assertThat(props.getProperty(CoreProperties.LINKS_ISSUE_TRACKER)).isEqualTo("http://issues.com");
+ assertThat(props.getProperty(CoreProperties.LINKS_SOURCES_DEV)).isEqualTo("http://sources-dev.com");
+
+ // ... but those ones have been overridden by <properties> in the POM
+ assertThat(props.getProperty(CoreProperties.LINKS_SOURCES)).isEqualTo("http://sources.com-OVERRIDEN-BY-PROPS");
+ assertThat(props.getProperty(CoreProperties.LINKS_HOME_PAGE)).isEqualTo("http://home.com-OVERRIDEN-BY-PROPS");
+ }
+
private MavenProject loadPom(String pomPath, boolean isRoot) throws URISyntaxException, IOException, XmlPullParserException {
File pomFile = new File(getClass().getResource(pomPath).toURI());
Model model = new MavenXpp3Reader().read(new StringReader(FileUtils.readFileToString(pomFile)));
--- /dev/null
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.test</groupId>
+ <artifactId>parent</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ <packaging>pom</packaging>
+ <url>http://home.com</url>
+ <ciManagement>
+ <url>http://ci.com</url>
+ </ciManagement>
+ <issueManagement>
+ <url>http://issues.com</url>
+ </issueManagement>
+ <scm>
+ <url>http://sources.com</url>
+ <developerConnection>http://sources-dev.com</developerConnection>
+ </scm>
+</project>
\ No newline at end of file
--- /dev/null
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.test</groupId>
+ <artifactId>parent</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ <packaging>pom</packaging>
+ <url>http://home.com</url>
+ <ciManagement>
+ <url>http://ci.com</url>
+ </ciManagement>
+ <issueManagement>
+ <url>http://issues.com</url>
+ </issueManagement>
+ <scm>
+ <url>http://sources.com</url>
+ <developerConnection>http://sources-dev.com</developerConnection>
+ </scm>
+
+
+
+ <properties>
+ <sonar.links.homepage>http://home.com-OVERRIDEN-BY-PROPS</sonar.links.homepage>
+ <sonar.links.scm>http://sources.com-OVERRIDEN-BY-PROPS</sonar.links.scm>
+ </properties>
+
+
+</project>
\ No newline at end of file
* @since 2.11
*/
String SERVER_ID_IP_ADDRESS = "sonar.server_id.ip_address";
+
+ /**
+ * @since 3.3
+ */
+ String LINKS_HOME_PAGE = "sonar.links.homepage";
+
+ /**
+ * @since 3.3
+ */
+ String LINKS_CI = "sonar.links.ci";
+
+ /**
+ * @since 3.3
+ */
+ String LINKS_ISSUE_TRACKER = "sonar.links.issue";
+
+ /**
+ * @since 3.3
+ */
+ String LINKS_SOURCES = "sonar.links.scm";
+
+ /**
+ * @since 3.3
+ */
+ String LINKS_SOURCES_DEV = "sonar.links.scm_dev";
}