try {
okHttpClient.newCall(request.build()).execute();
} catch (IOException e) {
- LOG.debug("Error when sending opt-out usage statistics: %s", e.getMessage());
+ LOG.debug("Error when sending opt-out usage statistics: {}", e.getMessage());
}
}
import java.util.concurrent.TimeUnit;
import org.picocontainer.Startable;
import org.sonar.api.config.Configuration;
-import org.sonar.api.platform.Server;
import org.sonar.api.server.ServerSide;
import org.sonar.api.utils.System2;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.api.utils.text.JsonWriter;
-import org.sonar.core.platform.PluginRepository;
-import org.sonar.server.es.SearchOptions;
-import org.sonar.server.measure.index.ProjectMeasuresIndex;
-import org.sonar.server.measure.index.ProjectMeasuresStatistics;
import org.sonar.server.property.InternalProperties;
-import org.sonar.server.user.index.UserIndex;
-import org.sonar.server.user.index.UserQuery;
import static org.sonar.api.measures.CoreMetrics.LINES_KEY;
import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY;
static final String I_PROP_OPT_OUT = "telemetry.optOut";
private static final Logger LOG = Loggers.get(TelemetryDaemon.class);
+ private final TelemetryDataLoader dataLoader;
private final TelemetryClient telemetryClient;
private final Configuration config;
private final InternalProperties internalProperties;
- private final Server server;
- private final PluginRepository pluginRepository;
private final System2 system2;
- private final UserIndex userIndex;
- private final ProjectMeasuresIndex projectMeasuresIndex;
private ScheduledExecutorService executorService;
- public TelemetryDaemon(TelemetryClient telemetryClient, Configuration config, InternalProperties internalProperties, Server server, PluginRepository pluginRepository,
- System2 system2, UserIndex userIndex, ProjectMeasuresIndex projectMeasuresIndex) {
+ public TelemetryDaemon(TelemetryDataLoader dataLoader, TelemetryClient telemetryClient, Configuration config, InternalProperties internalProperties, System2 system2) {
+ this.dataLoader = dataLoader;
this.telemetryClient = telemetryClient;
this.config = config;
this.internalProperties = internalProperties;
- this.server = server;
- this.pluginRepository = pluginRepository;
this.system2 = system2;
- this.userIndex = userIndex;
- this.projectMeasuresIndex = projectMeasuresIndex;
}
@Override
StringWriter json = new StringWriter();
try (JsonWriter writer = JsonWriter.of(json)) {
writer.beginObject();
- writer.prop("id", server.getId());
+ writer.prop("id", dataLoader.loadServerId());
writer.endObject();
}
telemetryClient.optOut(json.toString());
}
private void uploadStatistics() throws IOException {
+ TelemetryData statistics = dataLoader.load();
StringWriter json = new StringWriter();
try (JsonWriter writer = JsonWriter.of(json)) {
writer.beginObject();
- writer.prop("id", server.getId());
- writer.prop("version", server.getVersion());
+ writer.prop("id", statistics.getServerId());
+ writer.prop("version", statistics.getVersion());
writer.name("plugins");
- writer.beginObject();
- pluginRepository.getPluginInfos().forEach(plugin -> {
- String version = plugin.getVersion() == null ? "undefined" : plugin.getVersion().getName();
- writer.prop(plugin.getKey(), version);
- });
- writer.endObject();
- long userCount = userIndex.search(UserQuery.builder().build(), new SearchOptions().setLimit(1)).getTotal();
- writer.prop("userCount", userCount);
- ProjectMeasuresStatistics statistics = projectMeasuresIndex.searchTelemetryStatistics();
+ writer.valueObject(statistics.getPlugins());
+ writer.prop("userCount", statistics.getUserCount());
writer.prop("projectCount", statistics.getProjectCount());
writer.prop(LINES_KEY, statistics.getLines());
writer.prop(NCLOC_KEY, statistics.getNcloc());
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.telemetry;
+
+import java.util.Map;
+import org.sonar.server.measure.index.ProjectMeasuresStatistics;
+
+import static java.util.Objects.requireNonNull;
+
+public class TelemetryData {
+ private final String serverId;
+ private final String version;
+ private final Map<String, String> plugins;
+ private final long lines;
+ private final long ncloc;
+ private final long userCount;
+ private final long projectCount;
+ private final Map<String, Long> projectCountByLanguage;
+ private final Map<String, Long> nclocByLanguage;
+
+ public TelemetryData(Builder builder) {
+ serverId = builder.serverId;
+ version = builder.version;
+ plugins = builder.plugins;
+ lines = builder.projectMeasuresStatistics.getLines();
+ ncloc = builder.projectMeasuresStatistics.getNcloc();
+ userCount = builder.userCount;
+ projectCount = builder.projectMeasuresStatistics.getProjectCount();
+ projectCountByLanguage = builder.projectMeasuresStatistics.getProjectCountByLanguage();
+ nclocByLanguage = builder.projectMeasuresStatistics.getNclocByLanguage();
+ }
+
+ public String getServerId() {
+ return serverId;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public Map<String, String> getPlugins() {
+ return plugins;
+ }
+
+ public long getLines() {
+ return lines;
+ }
+
+ public long getNcloc() {
+ return ncloc;
+ }
+
+ public long getUserCount() {
+ return userCount;
+ }
+
+ public long getProjectCount() {
+ return projectCount;
+ }
+
+ public Map<String, Long> getProjectCountByLanguage() {
+ return projectCountByLanguage;
+ }
+
+ public Map<String, Long> getNclocByLanguage() {
+ return nclocByLanguage;
+ }
+
+ static Builder builder() {
+ return new Builder();
+ }
+
+ static class Builder {
+ private String serverId;
+ private String version;
+ private long userCount;
+ private Map<String, String> plugins;
+ private ProjectMeasuresStatistics projectMeasuresStatistics;
+
+ private Builder() {
+ // enforce static factory method
+ }
+
+ Builder setServerId(String serverId) {
+ this.serverId = serverId;
+ return this;
+ }
+
+ Builder setVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ void setUserCount(long userCount) {
+ this.userCount = userCount;
+ }
+
+ void setPlugins(Map<String, String> plugins) {
+ this.plugins = plugins;
+ }
+
+ void setProjectMeasuresStatistics(ProjectMeasuresStatistics projectMeasuresStatistics) {
+ this.projectMeasuresStatistics = projectMeasuresStatistics;
+ }
+
+ TelemetryData build() {
+ requireNonNull(serverId);
+ requireNonNull(version);
+ requireNonNull(plugins);
+ requireNonNull(projectMeasuresStatistics);
+
+ return new TelemetryData(this);
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.telemetry;
+
+import java.util.Map;
+import java.util.function.Function;
+import org.sonar.api.platform.Server;
+import org.sonar.api.server.ServerSide;
+import org.sonar.core.platform.PluginInfo;
+import org.sonar.core.platform.PluginRepository;
+import org.sonar.core.util.stream.MoreCollectors;
+import org.sonar.server.es.SearchOptions;
+import org.sonar.server.measure.index.ProjectMeasuresIndex;
+import org.sonar.server.measure.index.ProjectMeasuresStatistics;
+import org.sonar.server.user.index.UserIndex;
+import org.sonar.server.user.index.UserQuery;
+
+@ServerSide
+public class TelemetryDataLoader {
+ private final Server server;
+ private final PluginRepository pluginRepository;
+ private final UserIndex userIndex;
+ private final ProjectMeasuresIndex projectMeasuresIndex;
+
+ public TelemetryDataLoader(Server server, PluginRepository pluginRepository, UserIndex userIndex, ProjectMeasuresIndex projectMeasuresIndex) {
+ this.server = server;
+ this.pluginRepository = pluginRepository;
+ this.userIndex = userIndex;
+ this.projectMeasuresIndex = projectMeasuresIndex;
+ }
+
+ public TelemetryData load() {
+ TelemetryData.Builder data = TelemetryData.builder();
+
+ data.setServerId(server.getId());
+ data.setVersion(server.getVersion());
+ Function<PluginInfo, String> getVersion = plugin -> plugin.getVersion() == null ? "undefined" : plugin.getVersion().getName();
+ Map<String, String> plugins = pluginRepository.getPluginInfos().stream().collect(MoreCollectors.uniqueIndex(PluginInfo::getKey, getVersion));
+ data.setPlugins(plugins);
+ long userCount = userIndex.search(UserQuery.builder().build(), new SearchOptions().setLimit(1)).getTotal();
+ data.setUserCount(userCount);
+ ProjectMeasuresStatistics projectMeasuresStatistics = projectMeasuresIndex.searchTelemetryStatistics();
+ data.setProjectMeasuresStatistics(projectMeasuresStatistics);
+
+ return data.build();
+ }
+
+ String loadServerId() {
+ return server.getId();
+ }
+}
@Override
protected void configureModule() {
add(
+ TelemetryDataLoader.class,
TelemetryDaemon.class,
TelemetryClient.class);
}
import javax.annotation.CheckForNull;
import org.sonar.api.platform.Server;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+
class FakeServer extends Server {
private String id;
private String version;
+ public FakeServer() {
+ this.id = randomAlphanumeric(20);
+ this.version = randomAlphanumeric(10);
+ }
+
@Override
public String getId() {
return id;
import org.sonar.api.config.PropertyDefinitions;
import org.sonar.api.config.internal.MapSettings;
import org.sonar.api.utils.internal.TestSystem2;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.api.utils.log.LoggerLevel;
import org.sonar.core.config.TelemetryProperties;
import org.sonar.core.platform.PluginInfo;
import org.sonar.core.platform.PluginRepository;
public UserSessionRule userSession = UserSessionRule.standalone();
@Rule
public EsTester es = new EsTester(new UserIndexDefinition(emptyConfig), new ProjectMeasuresIndexDefinition(emptyConfig));
+ @Rule
+ public LogTester logger = new LogTester().setLevel(LoggerLevel.DEBUG);
private TelemetryClient client = mock(TelemetryClient.class);
private InternalProperties internalProperties = new MapInternalProperties();
settings = new MapSettings(new PropertyDefinitions(TelemetryProperties.all()));
system2.setNow(System.currentTimeMillis());
- underTest = new TelemetryDaemon(client, settings.asConfig(), internalProperties, server, pluginRepository, system2, new UserIndex(es.client()),
- new ProjectMeasuresIndex(es.client(), null));
+ underTest = new TelemetryDaemon(new TelemetryDataLoader(server, pluginRepository, new UserIndex(es.client()), new ProjectMeasuresIndex(es.client(), null)), client,
+ settings.asConfig(), internalProperties, system2);
}
@Test
String json = jsonCaptor.getValue();
assertJson(json).isSimilarTo(getClass().getResource("telemetry-example.json"));
assertJson(getClass().getResource("telemetry-example.json")).isSimilarTo(json);
+ assertThat(logger.logs(LoggerLevel.INFO)).contains("Sharing of SonarQube statistics is enabled.");
}
@Test
verify(client, timeout(1_000).never()).upload(anyString());
verify(client, timeout(1_000).times(1)).optOut(anyString());
+ assertThat(logger.logs(LoggerLevel.INFO)).contains("Sharing of SonarQube statistics is disabled.");
}
private PluginInfo newPlugin(String key, String version) {
public void verify_count_of_added_components() {
ComponentContainer container = new ComponentContainer();
new TelemetryModule().configure(container);
- assertThat(container.size()).isEqualTo(2 + 2);
+ assertThat(container.size()).isEqualTo(3 + 2);
}
}