import org.sonar.ce.task.container.TaskContainer;
import org.sonar.ce.task.projectanalysis.container.ContainerFactory;
import org.sonar.ce.task.step.ComputationStepExecutor;
-import org.sonar.ce.taskprocessor.CeTaskProcessor;
+import org.sonar.ce.task.taskprocessor.CeTaskProcessor;
import org.sonar.ce.taskprocessor.TaskResultHolder;
import org.sonar.core.platform.ComponentContainer;
import org.sonar.db.ce.CeTaskTypes;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.ce.task.taskprocessor;
+
+import java.util.Set;
+import javax.annotation.CheckForNull;
+import org.sonar.api.ce.ComputeEngineSide;
+import org.sonar.api.server.ServerSide;
+import org.sonar.ce.task.CeTask;
+import org.sonar.ce.task.CeTaskResult;
+
+/**
+ * This interface is used to provide the processing code for {@link CeTask}s of one or more type to be called by the
+ * Compute Engine.
+ */
+@ComputeEngineSide
+@ServerSide
+public interface CeTaskProcessor {
+
+ /**
+ * The {@link CeTask#getType()} for which this {@link CeTaskProcessor} provides the processing code.
+ * <p>
+ * The match of type is done using {@link String#equals(Object)} and if more than one {@link CeTaskProcessor} declares
+ * itself had handler for the same {@link CeTask#getType()}, an error will be raised at startup and startup will
+ * fail.
+ * </p>
+ * <p>
+ * If an empty {@link Set} is returned, the {@link CeTaskProcessor} will be ignored.
+ * </p>
+ */
+ Set<String> getHandledCeTaskTypes();
+
+ /**
+ * Calls the processing code for a specific {@link CeTask} which will optionally return a {@link CeTaskResult}
+ * holding information to be persisted in the processing history of the Compute Engine (currently the {@code CE_ACTIVITY} table).
+ * <p>
+ * The specified is guaranteed to be non {@code null} and its {@link CeTask#getType()} to be one of the values
+ * of {@link #getHandledCeTaskTypes()}.
+ * </p>
+ *
+ * @throws RuntimeException when thrown, it will be caught and logged by the Compute Engine and the processing of the
+ * specified {@link CeTask} will be flagged as failed.
+ */
+ @CheckForNull
+ CeTaskResult process(CeTask task);
+}
import org.sonar.ce.ComputeEngine;
import org.sonar.ce.ComputeEngineImpl;
import org.sonar.ce.container.ComputeEngineContainerImpl;
-import org.sonar.ce.log.CeProcessLogging;
+import org.sonar.ce.logging.CeProcessLogging;
import org.sonar.process.MinimumViableSystem;
import org.sonar.process.Monitored;
import org.sonar.process.ProcessEntryPoint;
import org.sonar.ce.async.SynchronousAsyncExecution;
import org.sonar.ce.cleaning.CeCleaningModule;
import org.sonar.ce.db.ReadOnlyPropertiesDao;
-import org.sonar.ce.log.CeProcessLogging;
+import org.sonar.ce.logging.CeProcessLogging;
import org.sonar.ce.notification.ReportAnalysisFailureNotificationModule;
import org.sonar.ce.platform.CECoreExtensionsInstaller;
import org.sonar.ce.platform.ComputeEngineExtensionInstaller;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.ce.logging;
+
+import ch.qos.logback.classic.Level;
+import org.sonar.process.ProcessId;
+import org.sonar.process.logging.LogDomain;
+import org.sonar.process.logging.LogLevelConfig;
+import org.sonar.server.log.ServerProcessLogging;
+
+import static org.sonar.ce.task.log.CeTaskLogging.MDC_CE_TASK_UUID;
+
+/**
+ * Configure logback for the Compute Engine process. Logs are written to file "ce.log" in SQ's log directory.
+ */
+public class CeProcessLogging extends ServerProcessLogging {
+
+ public CeProcessLogging() {
+ super(ProcessId.COMPUTE_ENGINE, "%X{" + MDC_CE_TASK_UUID + "}");
+ }
+
+ @Override
+ protected void extendLogLevelConfiguration(LogLevelConfig.Builder logLevelConfigBuilder) {
+ logLevelConfigBuilder.levelByDomain("sql", ProcessId.COMPUTE_ENGINE, LogDomain.SQL);
+ logLevelConfigBuilder.levelByDomain("es", ProcessId.COMPUTE_ENGINE, LogDomain.ES);
+ JMX_RMI_LOGGER_NAMES.forEach(loggerName -> logLevelConfigBuilder.levelByDomain(loggerName, ProcessId.COMPUTE_ENGINE, LogDomain.JMX));
+ LOGGER_NAMES_TO_TURN_OFF.forEach(loggerName -> logLevelConfigBuilder.immutableLevel(loggerName, Level.OFF));
+ }
+
+ @Override
+ protected void extendConfigure() {
+ // nothing to do
+ }
+}
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
@ParametersAreNonnullByDefault
-package org.sonar.ce.notification;
+package org.sonar.ce.task.projectanalysis.notification;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.Optional;
import org.sonar.ce.task.CeTask;
+import org.sonar.ce.task.taskprocessor.CeTaskProcessor;
public interface CeTaskProcessorRepository {
import java.util.Optional;
import javax.annotation.Nonnull;
import org.sonar.ce.task.CeTask;
+import org.sonar.ce.task.taskprocessor.CeTaskProcessor;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.FluentIterable.from;
import org.sonar.ce.queue.InternalCeQueue;
import org.sonar.ce.task.CeTask;
import org.sonar.ce.task.CeTaskResult;
+import org.sonar.ce.task.taskprocessor.CeTaskProcessor;
import org.sonar.core.util.logs.Profiler;
import org.sonar.db.ce.CeActivityDto;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.ce.logging;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.Appender;
+import ch.qos.logback.core.ConsoleAppender;
+import ch.qos.logback.core.FileAppender;
+import ch.qos.logback.core.joran.spi.JoranException;
+import java.io.File;
+import java.io.IOException;
+import java.util.Properties;
+import java.util.stream.Stream;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.process.Props;
+import org.sonar.process.logging.LogbackHelper;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.slf4j.Logger.ROOT_LOGGER_NAME;
+import static org.sonar.process.ProcessProperties.Property.PATH_LOGS;
+
+public class CeProcessLoggingTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private File logDir;
+ private Props props = new Props(new Properties());
+ private CeProcessLogging underTest = new CeProcessLogging();
+
+ @Before
+ public void setUp() throws IOException {
+ logDir = temp.newFolder();
+ props.set(PATH_LOGS.getKey(), logDir.getAbsolutePath());
+ }
+
+ @AfterClass
+ public static void resetLogback() throws JoranException {
+ new LogbackHelper().resetFromXml("/logback-test.xml");
+ }
+
+ @Test
+ public void do_not_log_to_console() {
+ LoggerContext ctx = underTest.configure(props);
+
+ Logger root = ctx.getLogger(Logger.ROOT_LOGGER_NAME);
+ Appender appender = root.getAppender("CONSOLE");
+ assertThat(appender).isNull();
+ }
+
+ @Test
+ public void startup_logger_prints_to_only_to_system_out() {
+ LoggerContext ctx = underTest.configure(props);
+
+ Logger startup = ctx.getLogger("startup");
+ assertThat(startup.isAdditive()).isFalse();
+ Appender appender = startup.getAppender("CONSOLE");
+ assertThat(appender).isInstanceOf(ConsoleAppender.class);
+ ConsoleAppender<ILoggingEvent> consoleAppender = (ConsoleAppender<ILoggingEvent>) appender;
+ assertThat(consoleAppender.getTarget()).isEqualTo("System.out");
+ assertThat(consoleAppender.getEncoder()).isInstanceOf(PatternLayoutEncoder.class);
+ PatternLayoutEncoder patternEncoder = (PatternLayoutEncoder) consoleAppender.getEncoder();
+ assertThat(patternEncoder.getPattern()).isEqualTo("%d{yyyy.MM.dd HH:mm:ss} %-5level app[][%logger{20}] %msg%n");
+ }
+
+ @Test
+ public void log_to_ce_file() {
+ LoggerContext ctx = underTest.configure(props);
+
+ Logger root = ctx.getLogger(Logger.ROOT_LOGGER_NAME);
+ Appender<ILoggingEvent> appender = root.getAppender("file_ce");
+ assertThat(appender).isInstanceOf(FileAppender.class);
+ FileAppender fileAppender = (FileAppender) appender;
+ assertThat(fileAppender.getFile()).isEqualTo(new File(logDir, "ce.log").getAbsolutePath());
+ assertThat(fileAppender.getEncoder()).isInstanceOf(PatternLayoutEncoder.class);
+ PatternLayoutEncoder encoder = (PatternLayoutEncoder) fileAppender.getEncoder();
+ assertThat(encoder.getPattern()).isEqualTo("%d{yyyy.MM.dd HH:mm:ss} %-5level ce[%X{ceTaskUuid}][%logger{20}] %msg%n");
+ }
+
+ @Test
+ public void default_level_for_root_logger_is_INFO() {
+ LoggerContext ctx = underTest.configure(props);
+
+ verifyRootLogLevel(ctx, Level.INFO);
+ }
+
+ @Test
+ public void root_logger_level_changes_with_global_property() {
+ props.set("sonar.log.level", "TRACE");
+
+ LoggerContext ctx = underTest.configure(props);
+
+ verifyRootLogLevel(ctx, Level.TRACE);
+ }
+
+ @Test
+ public void root_logger_level_changes_with_ce_property() {
+ props.set("sonar.log.level.ce", "TRACE");
+
+ LoggerContext ctx = underTest.configure(props);
+
+ verifyRootLogLevel(ctx, Level.TRACE);
+ }
+
+ @Test
+ public void root_logger_level_is_configured_from_ce_property_over_global_property() {
+ props.set("sonar.log.level", "TRACE");
+ props.set("sonar.log.level.ce", "DEBUG");
+
+ LoggerContext ctx = underTest.configure(props);
+
+ verifyRootLogLevel(ctx, Level.DEBUG);
+ }
+
+ @Test
+ public void root_logger_level_changes_with_ce_property_and_is_case_insensitive() {
+ props.set("sonar.log.level.ce", "debug");
+
+ LoggerContext ctx = underTest.configure(props);
+
+ verifyRootLogLevel(ctx, Level.DEBUG);
+ }
+
+ @Test
+ public void sql_logger_level_changes_with_global_property_and_is_case_insensitive() {
+ props.set("sonar.log.level", "InFO");
+
+ LoggerContext ctx = underTest.configure(props);
+
+ verifySqlLogLevel(ctx, Level.INFO);
+ }
+
+ @Test
+ public void sql_logger_level_changes_with_ce_property_and_is_case_insensitive() {
+ props.set("sonar.log.level.ce", "TrACe");
+
+ LoggerContext ctx = underTest.configure(props);
+
+ verifySqlLogLevel(ctx, Level.TRACE);
+ }
+
+ @Test
+ public void sql_logger_level_changes_with_ce_sql_property_and_is_case_insensitive() {
+ props.set("sonar.log.level.ce.sql", "debug");
+
+ LoggerContext ctx = underTest.configure(props);
+
+ verifySqlLogLevel(ctx, Level.DEBUG);
+ }
+
+ @Test
+ public void sql_logger_level_is_configured_from_ce_sql_property_over_ce_property() {
+ props.set("sonar.log.level.ce.sql", "debug");
+ props.set("sonar.log.level.ce", "TRACE");
+
+ LoggerContext ctx = underTest.configure(props);
+
+ verifySqlLogLevel(ctx, Level.DEBUG);
+ }
+
+ @Test
+ public void sql_logger_level_is_configured_from_ce_sql_property_over_global_property() {
+ props.set("sonar.log.level.ce.sql", "debug");
+ props.set("sonar.log.level", "TRACE");
+
+ LoggerContext ctx = underTest.configure(props);
+
+ verifySqlLogLevel(ctx, Level.DEBUG);
+ }
+
+ @Test
+ public void sql_logger_level_is_configured_from_ce_property_over_global_property() {
+ props.set("sonar.log.level.ce", "debug");
+ props.set("sonar.log.level", "TRACE");
+
+ LoggerContext ctx = underTest.configure(props);
+
+ verifySqlLogLevel(ctx, Level.DEBUG);
+ }
+
+ @Test
+ public void es_logger_level_changes_with_global_property_and_is_case_insensitive() {
+ props.set("sonar.log.level", "InFO");
+
+ LoggerContext ctx = underTest.configure(props);
+
+ verifyEsLogLevel(ctx, Level.INFO);
+ }
+
+ @Test
+ public void es_logger_level_changes_with_ce_property_and_is_case_insensitive() {
+ props.set("sonar.log.level.ce", "TrACe");
+
+ LoggerContext ctx = underTest.configure(props);
+
+ verifyEsLogLevel(ctx, Level.TRACE);
+ }
+
+ @Test
+ public void es_logger_level_changes_with_ce_es_property_and_is_case_insensitive() {
+ props.set("sonar.log.level.ce.es", "debug");
+
+ LoggerContext ctx = underTest.configure(props);
+
+ verifyEsLogLevel(ctx, Level.DEBUG);
+ }
+
+ @Test
+ public void es_logger_level_is_configured_from_ce_es_property_over_ce_property() {
+ props.set("sonar.log.level.ce.es", "debug");
+ props.set("sonar.log.level.ce", "TRACE");
+
+ LoggerContext ctx = underTest.configure(props);
+
+ verifyEsLogLevel(ctx, Level.DEBUG);
+ }
+
+ @Test
+ public void es_logger_level_is_configured_from_ce_es_property_over_global_property() {
+ props.set("sonar.log.level.ce.es", "debug");
+ props.set("sonar.log.level", "TRACE");
+
+ LoggerContext ctx = underTest.configure(props);
+
+ verifyEsLogLevel(ctx, Level.DEBUG);
+ }
+
+ @Test
+ public void es_logger_level_is_configured_from_ce_property_over_global_property() {
+ props.set("sonar.log.level.ce", "debug");
+ props.set("sonar.log.level", "TRACE");
+
+ LoggerContext ctx = underTest.configure(props);
+
+ verifyEsLogLevel(ctx, Level.DEBUG);
+ }
+
+ @Test
+ public void jmx_logger_level_changes_with_global_property_and_is_case_insensitive() {
+ props.set("sonar.log.level", "InFO");
+
+ LoggerContext ctx = underTest.configure(props);
+
+ verifyJmxLogLevel(ctx, Level.INFO);
+ }
+
+ @Test
+ public void jmx_logger_level_changes_with_jmx_property_and_is_case_insensitive() {
+ props.set("sonar.log.level.ce", "TrACe");
+
+ LoggerContext ctx = underTest.configure(props);
+
+ verifyJmxLogLevel(ctx, Level.TRACE);
+ }
+
+ @Test
+ public void jmx_logger_level_changes_with_ce_jmx_property_and_is_case_insensitive() {
+ props.set("sonar.log.level.ce.jmx", "debug");
+
+ LoggerContext ctx = underTest.configure(props);
+
+ verifyJmxLogLevel(ctx, Level.DEBUG);
+ }
+
+ @Test
+ public void jmx_logger_level_is_configured_from_ce_jmx_property_over_ce_property() {
+ props.set("sonar.log.level.ce.jmx", "debug");
+ props.set("sonar.log.level.ce", "TRACE");
+
+ LoggerContext ctx = underTest.configure(props);
+
+ verifyJmxLogLevel(ctx, Level.DEBUG);
+ }
+
+ @Test
+ public void jmx_logger_level_is_configured_from_ce_jmx_property_over_global_property() {
+ props.set("sonar.log.level.ce.jmx", "debug");
+ props.set("sonar.log.level", "TRACE");
+
+ LoggerContext ctx = underTest.configure(props);
+
+ verifyJmxLogLevel(ctx, Level.DEBUG);
+ }
+
+ @Test
+ public void jmx_logger_level_is_configured_from_ce_property_over_global_property() {
+ props.set("sonar.log.level.ce", "debug");
+ props.set("sonar.log.level", "TRACE");
+
+ LoggerContext ctx = underTest.configure(props);
+
+ verifyJmxLogLevel(ctx, Level.DEBUG);
+ }
+
+ @Test
+ public void root_logger_level_defaults_to_INFO_if_ce_property_has_invalid_value() {
+ props.set("sonar.log.level.ce", "DodoDouh!");
+
+ LoggerContext ctx = underTest.configure(props);
+ verifyRootLogLevel(ctx, Level.INFO);
+ }
+
+ @Test
+ public void sql_logger_level_defaults_to_INFO_if_ce_sql_property_has_invalid_value() {
+ props.set("sonar.log.level.ce.sql", "DodoDouh!");
+
+ LoggerContext ctx = underTest.configure(props);
+ verifySqlLogLevel(ctx, Level.INFO);
+ }
+
+ @Test
+ public void es_logger_level_defaults_to_INFO_if_ce_es_property_has_invalid_value() {
+ props.set("sonar.log.level.ce.es", "DodoDouh!");
+
+ LoggerContext ctx = underTest.configure(props);
+ verifyEsLogLevel(ctx, Level.INFO);
+ }
+
+ @Test
+ public void jmx_loggers_level_defaults_to_INFO_if_ce_jmx_property_has_invalid_value() {
+ props.set("sonar.log.level.ce.jmx", "DodoDouh!");
+
+ LoggerContext ctx = underTest.configure(props);
+ verifyJmxLogLevel(ctx, Level.INFO);
+ }
+
+ @Test
+ public void fail_with_IAE_if_global_property_unsupported_level() {
+ props.set("sonar.log.level", "ERROR");
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("log level ERROR in property sonar.log.level is not a supported value (allowed levels are [TRACE, DEBUG, INFO])");
+
+ underTest.configure(props);
+ }
+
+ @Test
+ public void fail_with_IAE_if_ce_property_unsupported_level() {
+ props.set("sonar.log.level.ce", "ERROR");
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("log level ERROR in property sonar.log.level.ce is not a supported value (allowed levels are [TRACE, DEBUG, INFO])");
+
+ underTest.configure(props);
+ }
+
+ @Test
+ public void fail_with_IAE_if_ce_sql_property_unsupported_level() {
+ props.set("sonar.log.level.ce.sql", "ERROR");
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("log level ERROR in property sonar.log.level.ce.sql is not a supported value (allowed levels are [TRACE, DEBUG, INFO])");
+
+ underTest.configure(props);
+ }
+
+ @Test
+ public void fail_with_IAE_if_ce_es_property_unsupported_level() {
+ props.set("sonar.log.level.ce.es", "ERROR");
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("log level ERROR in property sonar.log.level.ce.es is not a supported value (allowed levels are [TRACE, DEBUG, INFO])");
+
+ underTest.configure(props);
+ }
+
+ @Test
+ public void fail_with_IAE_if_ce_jmx_property_unsupported_level() {
+ props.set("sonar.log.level.ce.jmx", "ERROR");
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("log level ERROR in property sonar.log.level.ce.jmx is not a supported value (allowed levels are [TRACE, DEBUG, INFO])");
+
+ underTest.configure(props);
+ }
+
+ @Test
+ public void configure_defines_hardcoded_levels() {
+ LoggerContext context = underTest.configure(props);
+
+ verifyImmutableLogLevels(context);
+ }
+
+ @Test
+ public void configure_defines_hardcoded_levels_unchanged_by_global_property() {
+ props.set("sonar.log.level", "TRACE");
+
+ LoggerContext context = underTest.configure(props);
+
+ verifyImmutableLogLevels(context);
+ }
+
+ @Test
+ public void configure_defines_hardcoded_levels_unchanged_by_ce_property() {
+ props.set("sonar.log.level.ce", "TRACE");
+
+ LoggerContext context = underTest.configure(props);
+
+ verifyImmutableLogLevels(context);
+ }
+
+ @Test
+ public void configure_turns_off_some_MsSQL_driver_logger() {
+ LoggerContext context = underTest.configure(props);
+
+ Stream.of("com.microsoft.sqlserver.jdbc.internals",
+ "com.microsoft.sqlserver.jdbc.ResultSet",
+ "com.microsoft.sqlserver.jdbc.Statement",
+ "com.microsoft.sqlserver.jdbc.Connection")
+ .forEach(loggerName -> assertThat(context.getLogger(loggerName).getLevel()).isEqualTo(Level.OFF));
+ }
+
+ private void verifyRootLogLevel(LoggerContext ctx, Level expected) {
+ assertThat(ctx.getLogger(ROOT_LOGGER_NAME).getLevel()).isEqualTo(expected);
+ }
+
+ private void verifySqlLogLevel(LoggerContext ctx, Level expected) {
+ assertThat(ctx.getLogger("sql").getLevel()).isEqualTo(expected);
+ }
+
+ private void verifyEsLogLevel(LoggerContext ctx, Level expected) {
+ assertThat(ctx.getLogger("es").getLevel()).isEqualTo(expected);
+ }
+
+ private void verifyJmxLogLevel(LoggerContext ctx, Level expected) {
+ assertThat(ctx.getLogger("javax.management.remote.timeout").getLevel()).isEqualTo(expected);
+ assertThat(ctx.getLogger("javax.management.remote.misc").getLevel()).isEqualTo(expected);
+ assertThat(ctx.getLogger("javax.management.remote.rmi").getLevel()).isEqualTo(expected);
+ assertThat(ctx.getLogger("javax.management.mbeanserver").getLevel()).isEqualTo(expected);
+ assertThat(ctx.getLogger("sun.rmi.loader").getLevel()).isEqualTo(expected);
+ assertThat(ctx.getLogger("sun.rmi.transport.tcp").getLevel()).isEqualTo(expected);
+ assertThat(ctx.getLogger("sun.rmi.transport.misc").getLevel()).isEqualTo(expected);
+ assertThat(ctx.getLogger("sun.rmi.server.call").getLevel()).isEqualTo(expected);
+ assertThat(ctx.getLogger("sun.rmi.dgc").getLevel()).isEqualTo(expected);
+ }
+
+ private void verifyImmutableLogLevels(LoggerContext ctx) {
+ assertThat(ctx.getLogger("org.apache.ibatis").getLevel()).isEqualTo(Level.WARN);
+ assertThat(ctx.getLogger("java.sql").getLevel()).isEqualTo(Level.WARN);
+ assertThat(ctx.getLogger("java.sql.ResultSet").getLevel()).isEqualTo(Level.WARN);
+ assertThat(ctx.getLogger("org.sonar.MEASURE_FILTER").getLevel()).isEqualTo(Level.WARN);
+ assertThat(ctx.getLogger("org.elasticsearch").getLevel()).isEqualTo(Level.INFO);
+ assertThat(ctx.getLogger("org.elasticsearch.node").getLevel()).isEqualTo(Level.INFO);
+ assertThat(ctx.getLogger("org.elasticsearch.http").getLevel()).isEqualTo(Level.INFO);
+ assertThat(ctx.getLogger("ch.qos.logback").getLevel()).isEqualTo(Level.WARN);
+ assertThat(ctx.getLogger("org.apache.catalina").getLevel()).isEqualTo(Level.INFO);
+ assertThat(ctx.getLogger("org.apache.coyote").getLevel()).isEqualTo(Level.INFO);
+ assertThat(ctx.getLogger("org.apache.jasper").getLevel()).isEqualTo(Level.INFO);
+ assertThat(ctx.getLogger("org.apache.tomcat").getLevel()).isEqualTo(Level.INFO);
+ }
+}
import org.junit.rules.ExpectedException;
import org.sonar.ce.task.CeTask;
import org.sonar.ce.task.CeTaskResult;
+import org.sonar.ce.task.taskprocessor.CeTaskProcessor;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.rules.ExternalResource;
import org.sonar.ce.task.CeTask;
import org.sonar.ce.task.CeTaskResult;
+import org.sonar.ce.task.taskprocessor.CeTaskProcessor;
import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.requireNonNull;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.log;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.ConsoleAppender;
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
+import org.sonar.process.ProcessId;
+import org.sonar.process.Props;
+import org.sonar.process.logging.LogLevelConfig;
+import org.sonar.process.logging.LogbackHelper;
+import org.sonar.process.logging.RootLoggerConfig;
+
+import static org.sonar.process.logging.RootLoggerConfig.newRootLoggerConfigBuilder;
+
+public abstract class ServerProcessLogging {
+
+ public static final String STARTUP_LOGGER_NAME = "startup";
+ protected static final Set<String> JMX_RMI_LOGGER_NAMES = ImmutableSet.of(
+ "javax.management.remote.timeout",
+ "javax.management.remote.misc",
+ "javax.management.remote.rmi",
+ "javax.management.mbeanserver",
+ "sun.rmi.loader",
+ "sun.rmi.transport.tcp",
+ "sun.rmi.transport.misc",
+ "sun.rmi.server.call",
+ "sun.rmi.dgc");
+ protected static final Set<String> LOGGER_NAMES_TO_TURN_OFF = ImmutableSet.of(
+ // mssql driver
+ "com.microsoft.sqlserver.jdbc.internals",
+ "com.microsoft.sqlserver.jdbc.ResultSet",
+ "com.microsoft.sqlserver.jdbc.Statement",
+ "com.microsoft.sqlserver.jdbc.Connection");
+ private final ProcessId processId;
+ private final String threadIdFieldPattern;
+ private final LogbackHelper helper = new LogbackHelper();
+ private final LogLevelConfig logLevelConfig;
+
+ protected ServerProcessLogging(ProcessId processId, String threadIdFieldPattern) {
+ this.processId = processId;
+ this.threadIdFieldPattern = threadIdFieldPattern;
+ this.logLevelConfig = createLogLevelConfiguration(processId);
+ }
+
+ private LogLevelConfig createLogLevelConfiguration(ProcessId processId) {
+ LogLevelConfig.Builder builder = LogLevelConfig.newBuilder(helper.getRootLoggerName());
+ builder.rootLevelFor(processId);
+ builder.immutableLevel("org.apache.ibatis", Level.WARN);
+ builder.immutableLevel("java.sql", Level.WARN);
+ builder.immutableLevel("java.sql.ResultSet", Level.WARN);
+ builder.immutableLevel("org.sonar.MEASURE_FILTER", Level.WARN);
+ builder.immutableLevel("org.elasticsearch", Level.INFO);
+ builder.immutableLevel("org.elasticsearch.node", Level.INFO);
+ builder.immutableLevel("org.elasticsearch.http", Level.INFO);
+ builder.immutableLevel("ch.qos.logback", Level.WARN);
+ builder.immutableLevel("org.apache.catalina", Level.INFO);
+ builder.immutableLevel("org.apache.coyote", Level.INFO);
+ builder.immutableLevel("org.apache.jasper", Level.INFO);
+ builder.immutableLevel("org.apache.tomcat", Level.INFO);
+ builder.immutableLevel("org.postgresql.core.v3.QueryExecutorImpl", Level.INFO);
+ builder.immutableLevel("org.postgresql.jdbc.PgConnection", Level.INFO);
+
+ extendLogLevelConfiguration(builder);
+
+ return builder.build();
+ }
+
+ public LoggerContext configure(Props props) {
+ LoggerContext ctx = helper.getRootContext();
+ ctx.reset();
+
+ configureRootLogger(props);
+ helper.apply(logLevelConfig, props);
+ configureDirectToConsoleLoggers(ctx, STARTUP_LOGGER_NAME);
+ extendConfigure();
+
+ helper.enableJulChangePropagation(ctx);
+
+ return ctx;
+ }
+
+ public LogLevelConfig getLogLevelConfig() {
+ return this.logLevelConfig;
+ }
+
+ protected abstract void extendLogLevelConfiguration(LogLevelConfig.Builder logLevelConfigBuilder);
+
+ protected abstract void extendConfigure();
+
+ private void configureRootLogger(Props props) {
+ RootLoggerConfig config = newRootLoggerConfigBuilder()
+ .setProcessId(processId)
+ .setThreadIdFieldPattern(threadIdFieldPattern)
+ .build();
+ String logPattern = helper.buildLogPattern(config);
+
+ helper.configureGlobalFileLog(props, config, logPattern);
+ helper.configureForSubprocessGobbler(props, logPattern);
+ }
+
+ /**
+ * Setup one or more specified loggers to be non additive and to print to System.out which will be caught by the Main
+ * Process and written to sonar.log.
+ */
+ private void configureDirectToConsoleLoggers(LoggerContext context, String... loggerNames) {
+ RootLoggerConfig config = newRootLoggerConfigBuilder()
+ .setProcessId(ProcessId.APP)
+ .setThreadIdFieldPattern("")
+ .build();
+ String logPattern = helper.buildLogPattern(config);
+ ConsoleAppender<ILoggingEvent> consoleAppender = helper.newConsoleAppender(context, "CONSOLE", logPattern);
+
+ for (String loggerName : loggerNames) {
+ Logger consoleLogger = context.getLogger(loggerName);
+ consoleLogger.setAdditive(false);
+ consoleLogger.addAppender(consoleAppender);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.log;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.notification;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Multimap;
+import java.io.IOException;
+import java.io.InvalidClassException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import javax.annotation.Nullable;
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.notifications.NotificationChannel;
+import org.sonar.api.utils.SonarException;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.notification.NotificationQueueDto;
+import org.sonar.db.property.Subscriber;
+
+import static java.util.Collections.singletonList;
+import static java.util.Objects.requireNonNull;
+
+public class DefaultNotificationManager implements NotificationManager {
+
+ private static final Logger LOG = Loggers.get(DefaultNotificationManager.class);
+
+ private static final String UNABLE_TO_READ_NOTIFICATION = "Unable to read notification";
+
+ private NotificationChannel[] notificationChannels;
+ private final DbClient dbClient;
+
+ private boolean alreadyLoggedDeserializationIssue = false;
+
+ /**
+ * Default constructor used by Pico
+ */
+ public DefaultNotificationManager(NotificationChannel[] channels,
+ DbClient dbClient) {
+ this.notificationChannels = channels;
+ this.dbClient = dbClient;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void scheduleForSending(Notification notification) {
+ NotificationQueueDto dto = NotificationQueueDto.toNotificationQueueDto(notification);
+ dbClient.notificationQueueDao().insert(singletonList(dto));
+ }
+
+ /**
+ * Give the notification queue so that it can be processed
+ */
+ public Notification getFromQueue() {
+ int batchSize = 1;
+ List<NotificationQueueDto> notificationDtos = dbClient.notificationQueueDao().selectOldest(batchSize);
+ if (notificationDtos.isEmpty()) {
+ return null;
+ }
+ dbClient.notificationQueueDao().delete(notificationDtos);
+
+ return convertToNotification(notificationDtos);
+ }
+
+ private Notification convertToNotification(List<NotificationQueueDto> notifications) {
+ try {
+ // If batchSize is increased then we should return a list instead of a single element
+ return notifications.get(0).toNotification();
+ } catch (InvalidClassException e) {
+ // SONAR-4739
+ if (!alreadyLoggedDeserializationIssue) {
+ logDeserializationIssue();
+ alreadyLoggedDeserializationIssue = true;
+ }
+ return null;
+ } catch (IOException | ClassNotFoundException e) {
+ throw new SonarException(UNABLE_TO_READ_NOTIFICATION, e);
+ }
+ }
+
+ @VisibleForTesting
+ void logDeserializationIssue() {
+ LOG.warn("It is impossible to send pending notifications which existed prior to the upgrade of SonarQube. They will be ignored.");
+ }
+
+ public long count() {
+ return dbClient.notificationQueueDao().count();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Multimap<String, NotificationChannel> findSubscribedRecipientsForDispatcher(NotificationDispatcher dispatcher,
+ String projectKey, SubscriberPermissionsOnProject subscriberPermissionsOnProject) {
+ requireNonNull(projectKey, "projectKey is mandatory");
+ String dispatcherKey = dispatcher.getKey();
+
+ Set<SubscriberAndChannel> subscriberAndChannels = Arrays.stream(notificationChannels)
+ .flatMap(notificationChannel -> toSubscriberAndChannels(dispatcherKey, projectKey, notificationChannel))
+ .collect(Collectors.toSet());
+
+ if (subscriberAndChannels.isEmpty()) {
+ return ImmutableMultimap.of();
+ }
+
+ ImmutableSetMultimap.Builder<String, NotificationChannel> builder = ImmutableSetMultimap.builder();
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ Set<String> authorizedLogins = keepAuthorizedLogins(dbSession, projectKey, subscriberAndChannels, subscriberPermissionsOnProject);
+ subscriberAndChannels.stream()
+ .filter(subscriberAndChannel -> authorizedLogins.contains(subscriberAndChannel.getSubscriber().getLogin()))
+ .forEach(subscriberAndChannel -> builder.put(subscriberAndChannel.getSubscriber().getLogin(), subscriberAndChannel.getChannel()));
+ }
+ return builder.build();
+ }
+
+ private Stream<SubscriberAndChannel> toSubscriberAndChannels(String dispatcherKey, String projectKey, NotificationChannel notificationChannel) {
+ Set<Subscriber> usersForNotification = dbClient.propertiesDao().findUsersForNotification(dispatcherKey, notificationChannel.getKey(), projectKey);
+ return usersForNotification
+ .stream()
+ .map(login -> new SubscriberAndChannel(login, notificationChannel));
+ }
+
+ private Set<String> keepAuthorizedLogins(DbSession dbSession, String projectKey, Set<SubscriberAndChannel> subscriberAndChannels,
+ SubscriberPermissionsOnProject requiredPermissions) {
+ if (requiredPermissions.getGlobalSubscribers().equals(requiredPermissions.getProjectSubscribers())) {
+ return keepAuthorizedLogins(dbSession, projectKey, subscriberAndChannels, null, requiredPermissions.getGlobalSubscribers());
+ } else {
+ return Stream
+ .concat(
+ keepAuthorizedLogins(dbSession, projectKey, subscriberAndChannels, true, requiredPermissions.getGlobalSubscribers()).stream(),
+ keepAuthorizedLogins(dbSession, projectKey, subscriberAndChannels, false, requiredPermissions.getProjectSubscribers()).stream())
+ .collect(Collectors.toSet());
+ }
+ }
+
+ private Set<String> keepAuthorizedLogins(DbSession dbSession, String projectKey, Set<SubscriberAndChannel> subscriberAndChannels,
+ @Nullable Boolean global, String permission) {
+ Set<String> logins = subscriberAndChannels.stream()
+ .filter(s -> global == null || s.getSubscriber().isGlobal() == global)
+ .map(s -> s.getSubscriber().getLogin())
+ .collect(Collectors.toSet());
+ if (logins.isEmpty()) {
+ return Collections.emptySet();
+ }
+ return dbClient.authorizationDao().keepAuthorizedLoginsOnProject(dbSession, logins, projectKey, permission);
+ }
+
+ private static final class SubscriberAndChannel {
+ private final Subscriber subscriber;
+ private final NotificationChannel channel;
+
+ private SubscriberAndChannel(Subscriber subscriber, NotificationChannel channel) {
+ this.subscriber = subscriber;
+ this.channel = channel;
+ }
+
+ Subscriber getSubscriber() {
+ return subscriber;
+ }
+
+ NotificationChannel getChannel() {
+ return channel;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ SubscriberAndChannel that = (SubscriberAndChannel) o;
+ return Objects.equals(subscriber, that.subscriber) &&
+ Objects.equals(channel, that.channel);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(subscriber, channel);
+ }
+ }
+
+ @VisibleForTesting
+ protected List<NotificationChannel> getChannels() {
+ return Arrays.asList(notificationChannels);
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.notification;
+
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.ExtensionPoint;
+import org.sonar.api.ce.ComputeEngineSide;
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.notifications.NotificationChannel;
+import org.sonar.api.server.ServerSide;
+
+/**
+ * <p>
+ * Plugins should extend this class to provide logic to determine which users are interested in receiving notifications,
+ * along with which delivery channels they selected.
+ * </p>
+ * For example:
+ * <ul>
+ * <li>notify me by email when someone comments an issue reported by me</li>
+ * <li>notify me by twitter when someone comments an issue assigned to me</li>
+ * <li>notify me by Jabber when someone mentions me in an issue comment</li>
+ * <li>send me by SMS when there are system notifications (like password reset, account creation, ...)</li>
+ * </ul>
+ */
+@ServerSide
+@ComputeEngineSide
+@ExtensionPoint
+public abstract class NotificationDispatcher {
+
+ private final String notificationType;
+
+ /**
+ * Additional information related to the notification, which will be used
+ * to know who should receive the notification.
+ */
+ public interface Context {
+ /**
+ * Adds a user that will be notified through the given notification channel.
+ *
+ * @param userLogin the user login
+ * @param notificationChannel the notification channel to use for this user
+ */
+ void addUser(String userLogin, NotificationChannel notificationChannel);
+ }
+
+ /**
+ * Creates a new dispatcher for notifications of the given type.
+ *
+ * @param notificationType the type of notifications handled by this dispatcher
+ */
+ public NotificationDispatcher(String notificationType) {
+ this.notificationType = notificationType;
+ }
+
+ /**
+ * Creates a new generic dispatcher, used for any kind of notification.
+ * <p/>
+ * Should be avoided and replaced by the other constructor - as it is easier to understand that a
+ * dispatcher listens for a specific type of notification.
+ */
+ public NotificationDispatcher() {
+ this("");
+ }
+
+ /**
+ * The unique key of this dispatcher. By default it's the class name without the package prefix.
+ * <p/>
+ * The related label in l10n bundles is 'notification.dispatcher.<key>', for example 'notification.dispatcher.NewFalsePositive'.
+ */
+ public String getKey() {
+ return getClass().getSimpleName();
+ }
+
+ /**
+ * @since 5.1
+ */
+ public String getType() {
+ return notificationType;
+ }
+
+ /**
+ * <p>
+ * Performs the dispatch.
+ * </p>
+ */
+ public final void performDispatch(Notification notification, Context context) {
+ if (StringUtils.equals(notification.getType(), notificationType) || StringUtils.equals("", notificationType)) {
+ dispatch(notification, context);
+ }
+ }
+
+ /**
+ * <p>
+ * Implements the logic that defines which users will receive the notification.
+ * </p>
+ * The purpose of this method is to populate the context object with users, based on the type of notification and the content of the notification.
+ */
+ public abstract void dispatch(Notification notification, Context context);
+
+ @Override
+ public String toString() {
+ return getKey();
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.notification;
+
+import com.google.common.collect.Maps;
+import java.util.Map;
+import org.sonar.api.ce.ComputeEngineSide;
+import org.sonar.api.server.ServerSide;
+
+/**
+ * Notification dispatchers (see {@link NotificationDispatcher}) can define their own metadata class in order
+ * to tell more about them.
+ * <p>
+ * Instances of these classes must be declared by {@link org.sonar.api.Plugin}.
+ * </p>
+ */
+@ServerSide
+@ComputeEngineSide
+public final class NotificationDispatcherMetadata {
+
+ public static final String GLOBAL_NOTIFICATION = "globalNotification";
+ public static final String PER_PROJECT_NOTIFICATION = "perProjectNotification";
+
+ private String dispatcherKey;
+ private Map<String, String> properties;
+
+ private NotificationDispatcherMetadata(String dispatcherKey) {
+ this.dispatcherKey = dispatcherKey;
+ this.properties = Maps.newHashMap();
+ }
+
+ /**
+ * Creates a new metadata instance for the given dispatcher.
+ * <p/>
+ * By default the key is the class name without package. It can be changed by overriding
+ * {@link NotificationDispatcher#getKey()}.
+ */
+ public static NotificationDispatcherMetadata create(String dispatcherKey) {
+ return new NotificationDispatcherMetadata(dispatcherKey);
+ }
+
+ /**
+ * Sets a property on this metadata object.
+ */
+ public NotificationDispatcherMetadata setProperty(String key, String value) {
+ properties.put(key, value);
+ return this;
+ }
+
+ /**
+ * Gives the property for the given key.
+ */
+ public String getProperty(String key) {
+ return properties.get(key);
+ }
+
+ /**
+ * Returns the unique key of the dispatcher.
+ */
+ public String getDispatcherKey() {
+ return dispatcherKey;
+ }
+
+ @Override
+ public String toString() {
+ return dispatcherKey;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ NotificationDispatcherMetadata that = (NotificationDispatcherMetadata) o;
+ return dispatcherKey.equals(that.dispatcherKey);
+ }
+
+ @Override
+ public int hashCode() {
+ return dispatcherKey.hashCode();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.notification;
+
+import com.google.common.collect.Multimap;
+import java.util.Objects;
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.notifications.NotificationChannel;
+import org.sonar.api.web.UserRole;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * The notification manager receives notifications and is in charge of storing them so that they are processed by the notification service.
+ * <p>
+ * Pico provides an instance of this class, and plugins just need to create notifications and pass them to this manager with
+ * the {@link NotificationManager#scheduleForSending(Notification)} method.
+ * </p>
+ */
+public interface NotificationManager {
+
+ /**
+ * Receives a notification and stores it so that it is processed by the notification service.
+ *
+ * @param notification the notification.
+ */
+ void scheduleForSending(Notification notification);
+
+ /**
+ * <p>
+ * Returns the list of users who subscribed to the given dispatcher, along with the notification channels (email, twitter, ...) that they choose
+ * for this dispatcher.
+ * </p>
+ * <p>
+ * The resource ID can be null in case of notifications that have nothing to do with a specific project (like system notifications).
+ * </p>
+ *
+ * @param dispatcher the dispatcher for which this list of users is requested
+ * @param projectUuid UUID of the project
+ * @param subscriberPermissionsOnProject the required permission for global and project subscribers
+ *
+ * @return the list of user login along with the subscribed channels
+ */
+ Multimap<String, NotificationChannel> findSubscribedRecipientsForDispatcher(NotificationDispatcher dispatcher, String projectUuid,
+ SubscriberPermissionsOnProject subscriberPermissionsOnProject);
+
+ final class SubscriberPermissionsOnProject {
+ public static final SubscriberPermissionsOnProject ALL_MUST_HAVE_ROLE_USER = new SubscriberPermissionsOnProject(UserRole.USER);
+
+ private final String globalSubscribers;
+ private final String projectSubscribers;
+
+ public SubscriberPermissionsOnProject(String globalAndProjectSubscribers) {
+ this(globalAndProjectSubscribers, globalAndProjectSubscribers);
+ }
+
+ public SubscriberPermissionsOnProject(String globalSubscribers, String projectSubscribers) {
+ this.globalSubscribers = requireNonNull(globalSubscribers, "global subscribers's required permission can't be null");
+ this.projectSubscribers = requireNonNull(projectSubscribers, "project subscribers's required permission can't be null");
+ }
+
+ public String getGlobalSubscribers() {
+ return globalSubscribers;
+ }
+
+ public String getProjectSubscribers() {
+ return projectSubscribers;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ SubscriberPermissionsOnProject that = (SubscriberPermissionsOnProject) o;
+ return globalSubscribers.equals(that.globalSubscribers) && projectSubscribers.equals(that.projectSubscribers);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(globalSubscribers, projectSubscribers);
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.notification;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.organization;
+
+import static java.util.Objects.requireNonNull;
+
+public class DefaultOrganization {
+ private final String uuid;
+ private final String key;
+ private final String name;
+ private final long createdAt;
+ private final long updatedAt;
+
+ private DefaultOrganization(Builder builder) {
+ this.uuid = requireNonNull(builder.uuid, "uuid can't be null");
+ this.key = requireNonNull(builder.key, "key can't be null");
+ this.name = requireNonNull(builder.name, "name can't be null");
+ this.createdAt = requireNonNull(builder.createdAt, "createdAt can't be null");
+ this.updatedAt = requireNonNull(builder.updatedAt, "updatedAt can't be null");
+ }
+
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public long getCreatedAt() {
+ return createdAt;
+ }
+
+ public long getUpdatedAt() {
+ return updatedAt;
+ }
+
+ @Override
+ public String toString() {
+ return "DefaultOrganization{" +
+ "uuid='" + uuid + '\'' +
+ ", key='" + key + '\'' +
+ ", name='" + name + '\'' +
+ ", createdAt=" + createdAt +
+ ", updatedAt=" + updatedAt +
+ '}';
+ }
+
+ public static final class Builder {
+ private String uuid;
+ private String key;
+ private String name;
+ private Long createdAt;
+ private Long updatedAt;
+
+ public Builder setUuid(String uuid) {
+ this.uuid = uuid;
+ return this;
+ }
+
+ public Builder setKey(String key) {
+ this.key = key;
+ return this;
+ }
+
+ public Builder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public Builder setCreatedAt(long createdAt) {
+ this.createdAt = createdAt;
+ return this;
+ }
+
+ public Builder setUpdatedAt(long updatedAt) {
+ this.updatedAt = updatedAt;
+ return this;
+ }
+
+ public DefaultOrganization build() {
+ return new DefaultOrganization(this);
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.organization;
+
+public interface DefaultOrganizationCache {
+
+ /**
+ * Loads {@link DefaultOrganization} in cache.
+ */
+ void load();
+
+ /**
+ * Unloads {@link DefaultOrganization} from cache.
+ */
+ void unload();
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.organization;
+
+public interface DefaultOrganizationProvider {
+ /**
+ * @throws IllegalStateException if there is no default organization
+ */
+ DefaultOrganization get();
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.organization;
+
+import java.util.Optional;
+import java.util.function.Supplier;
+import javax.annotation.CheckForNull;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.server.property.InternalProperties;
+
+import static com.google.common.base.Preconditions.checkState;
+
+public class DefaultOrganizationProviderImpl implements DefaultOrganizationProvider, DefaultOrganizationCache {
+ private static final ThreadLocal<Cache> CACHE = new ThreadLocal<>();
+
+ private final DbClient dbClient;
+
+ public DefaultOrganizationProviderImpl(DbClient dbClient) {
+ this.dbClient = dbClient;
+ }
+
+ @Override
+ public DefaultOrganization get() {
+ Cache cache = CACHE.get();
+ if (cache != null) {
+ return cache.get(() -> getDefaultOrganization(dbClient));
+ }
+
+ return getDefaultOrganization(dbClient);
+ }
+
+ private static DefaultOrganization getDefaultOrganization(DbClient dbClient) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ Optional<String> uuid = dbClient.internalPropertiesDao().selectByKey(dbSession, InternalProperties.DEFAULT_ORGANIZATION);
+ checkState(uuid.isPresent() && !uuid.get().isEmpty(), "No Default organization uuid configured");
+ Optional<OrganizationDto> dto = dbClient.organizationDao().selectByUuid(dbSession, uuid.get());
+ checkState(dto.isPresent(), "Default organization with uuid '%s' does not exist", uuid.get());
+ return toDefaultOrganization(dto.get());
+ }
+ }
+
+ private static DefaultOrganization toDefaultOrganization(OrganizationDto organizationDto) {
+ return DefaultOrganization.newBuilder()
+ .setUuid(organizationDto.getUuid())
+ .setKey(organizationDto.getKey())
+ .setName(organizationDto.getName())
+ .setCreatedAt(organizationDto.getCreatedAt())
+ .setUpdatedAt(organizationDto.getUpdatedAt())
+ .build();
+ }
+
+ @Override
+ public void load() {
+ checkState(
+ CACHE.get() == null,
+ "load called twice for thread '%s' or state wasn't cleared last time it was used",
+ Thread.currentThread().getName());
+ CACHE.set(new Cache());
+ }
+
+ @Override
+ public void unload() {
+ CACHE.remove();
+ }
+
+ private static final class Cache {
+ @CheckForNull
+ private DefaultOrganization defaultOrganization;
+
+ public DefaultOrganization get(Supplier<DefaultOrganization> supplier) {
+ if (defaultOrganization == null) {
+ defaultOrganization = supplier.get();
+ }
+ return defaultOrganization;
+ }
+
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.organization;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.property;
+
+import java.util.Optional;
+import javax.annotation.Nullable;
+
+/**
+ * Allows to read and write internal properties.
+ */
+public interface InternalProperties {
+ /**
+ * The UUID of the default organization.
+ * Can't be null unless SQ is strongly corrupted.
+ */
+ String DEFAULT_ORGANIZATION = "organization.default";
+
+ String ORGANIZATION_ENABLED = "organization.enabled";
+
+ String SERVER_ID_CHECKSUM = "server.idChecksum";
+
+ /**
+ * Compute Engine is pausing/paused if property value is "true".
+ */
+ String COMPUTE_ENGINE_PAUSE = "ce.pause";
+
+ String BITBUCKETCLOUD_APP_SHAREDSECRET = "bbc.app.sharedSecret";
+ /**
+ * Read the value of the specified property.
+ *
+ * @return {@link Optional#empty()} if the property does not exist, an empty string if the property is empty,
+ * otherwise the value of the property as a String.
+ *
+ * @throws IllegalArgumentException if {@code propertyKey} is {@code null} or empty
+ */
+ Optional<String> read(String propertyKey);
+
+ /**
+ * Write the value of the specified property.
+ * <p>
+ * {@code null} and empty string are valid values which will persist the specified property as empty.
+ * </p>
+ *
+ * @throws IllegalArgumentException if {@code propertyKey} is {@code null} or empty
+ */
+ void write(String propertyKey, @Nullable String value);
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.property;
+
+import java.util.Optional;
+import javax.annotation.Nullable;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * A cache-less implementation of {@link InternalProperties} reading and writing to DB table INTERNAL_PROPERTIES.
+ */
+public class InternalPropertiesImpl implements InternalProperties {
+ private final DbClient dbClient;
+
+ public InternalPropertiesImpl(DbClient dbClient) {
+ this.dbClient = dbClient;
+ }
+
+ @Override
+ public Optional<String> read(String propertyKey) {
+ checkPropertyKey(propertyKey);
+
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ return dbClient.internalPropertiesDao().selectByKey(dbSession, propertyKey);
+ }
+ }
+
+ @Override
+ public void write(String propertyKey, @Nullable String value) {
+ checkPropertyKey(propertyKey);
+
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ if (value == null || value.isEmpty()) {
+ dbClient.internalPropertiesDao().saveAsEmpty(dbSession, propertyKey);
+ } else {
+ dbClient.internalPropertiesDao().save(dbSession, propertyKey, value);
+ }
+ dbSession.commit();
+ }
+ }
+
+ private static void checkPropertyKey(@Nullable String propertyKey) {
+ checkArgument(propertyKey != null && !propertyKey.isEmpty(), "property key can't be null nor empty");
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.property;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import javax.annotation.Nullable;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Map based implementation of {@link InternalProperties} to be used for unit testing.
+ */
+public class MapInternalProperties implements InternalProperties {
+ private final Map<String, String> values = new HashMap<>(1);
+
+ @Override
+ public Optional<String> read(String propertyKey) {
+ checkPropertyKey(propertyKey);
+ return Optional.ofNullable(values.get(propertyKey));
+ }
+
+ @Override
+ public void write(String propertyKey, @Nullable String value) {
+ checkPropertyKey(propertyKey);
+ values.put(propertyKey, value);
+ }
+
+ private static void checkPropertyKey(@Nullable String propertyKey) {
+ checkArgument(propertyKey != null && !propertyKey.isEmpty(), "property key can't be null nor empty");
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.property;
+
+import javax.annotation.ParametersAreNonnullByDefault;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.notification;
+
+import com.google.common.collect.Multimap;
+import java.io.InvalidClassException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.lang.RandomStringUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InOrder;
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.notifications.NotificationChannel;
+import org.sonar.api.web.UserRole;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.notification.NotificationQueueDao;
+import org.sonar.db.notification.NotificationQueueDto;
+import org.sonar.db.permission.AuthorizationDao;
+import org.sonar.db.property.PropertiesDao;
+import org.sonar.db.property.Subscriber;
+import org.sonar.server.notification.NotificationManager.SubscriberPermissionsOnProject;
+
+import static com.google.common.collect.Sets.newHashSet;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anySet;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.only;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.internal.verification.VerificationModeFactory.times;
+
+public class DefaultNotificationManagerTest {
+
+ private DefaultNotificationManager underTest;
+
+ private PropertiesDao propertiesDao = mock(PropertiesDao.class);
+ private NotificationDispatcher dispatcher = mock(NotificationDispatcher.class);
+ private NotificationChannel emailChannel = mock(NotificationChannel.class);
+ private NotificationChannel twitterChannel = mock(NotificationChannel.class);
+ private NotificationQueueDao notificationQueueDao = mock(NotificationQueueDao.class);
+ private AuthorizationDao authorizationDao = mock(AuthorizationDao.class);
+ private DbClient dbClient = mock(DbClient.class);
+ private DbSession dbSession = mock(DbSession.class);
+
+ @Before
+ public void setUp() {
+ when(dispatcher.getKey()).thenReturn("NewViolations");
+ when(emailChannel.getKey()).thenReturn("Email");
+ when(twitterChannel.getKey()).thenReturn("Twitter");
+ when(dbClient.openSession(anyBoolean())).thenReturn(dbSession);
+ when(dbClient.propertiesDao()).thenReturn(propertiesDao);
+ when(dbClient.notificationQueueDao()).thenReturn(notificationQueueDao);
+ when(dbClient.authorizationDao()).thenReturn(authorizationDao);
+
+ underTest = new DefaultNotificationManager(new NotificationChannel[] {emailChannel, twitterChannel}, dbClient);
+ }
+
+ @Test
+ public void shouldProvideChannelList() {
+ assertThat(underTest.getChannels()).containsOnly(emailChannel, twitterChannel);
+
+ underTest = new DefaultNotificationManager(new NotificationChannel[] {}, dbClient);
+ assertThat(underTest.getChannels()).hasSize(0);
+ }
+
+ @Test
+ public void shouldPersist() {
+ Notification notification = new Notification("test");
+ underTest.scheduleForSending(notification);
+
+ verify(notificationQueueDao, only()).insert(any(List.class));
+ }
+
+ @Test
+ public void shouldGetFromQueueAndDelete() {
+ Notification notification = new Notification("test");
+ NotificationQueueDto dto = NotificationQueueDto.toNotificationQueueDto(notification);
+ List<NotificationQueueDto> dtos = Arrays.asList(dto);
+ when(notificationQueueDao.selectOldest(1)).thenReturn(dtos);
+
+ assertThat(underTest.getFromQueue()).isNotNull();
+
+ InOrder inOrder = inOrder(notificationQueueDao);
+ inOrder.verify(notificationQueueDao).selectOldest(1);
+ inOrder.verify(notificationQueueDao).delete(dtos);
+ }
+
+ // SONAR-4739
+ @Test
+ public void shouldNotFailWhenUnableToDeserialize() throws Exception {
+ NotificationQueueDto dto1 = mock(NotificationQueueDto.class);
+ when(dto1.toNotification()).thenThrow(new InvalidClassException("Pouet"));
+ List<NotificationQueueDto> dtos = Arrays.asList(dto1);
+ when(notificationQueueDao.selectOldest(1)).thenReturn(dtos);
+
+ underTest = spy(underTest);
+ assertThat(underTest.getFromQueue()).isNull();
+ assertThat(underTest.getFromQueue()).isNull();
+
+ verify(underTest, times(1)).logDeserializationIssue();
+ }
+
+ @Test
+ public void shouldFindNoRecipient() {
+ assertThat(underTest.findSubscribedRecipientsForDispatcher(dispatcher, "uuid_45", new SubscriberPermissionsOnProject(UserRole.USER)).asMap().entrySet())
+ .hasSize(0);
+ }
+
+ @Test
+ public void shouldFindSubscribedRecipientForGivenResource() {
+ String projectUuid = "uuid_45";
+ when(propertiesDao.findUsersForNotification("NewViolations", "Email", projectUuid))
+ .thenReturn(newHashSet(new Subscriber("user1", false), new Subscriber("user3", false), new Subscriber("user3", true)));
+ when(propertiesDao.findUsersForNotification("NewViolations", "Twitter", "uuid_56"))
+ .thenReturn(newHashSet(new Subscriber("user2", false)));
+ when(propertiesDao.findUsersForNotification("NewViolations", "Twitter", projectUuid))
+ .thenReturn(newHashSet(new Subscriber("user3", true)));
+ when(propertiesDao.findUsersForNotification("NewAlerts", "Twitter", projectUuid))
+ .thenReturn(newHashSet(new Subscriber("user4", false)));
+
+ when(authorizationDao.keepAuthorizedLoginsOnProject(dbSession, newHashSet("user1", "user3"), projectUuid, "user"))
+ .thenReturn(newHashSet("user1", "user3"));
+
+ Multimap<String, NotificationChannel> multiMap = underTest.findSubscribedRecipientsForDispatcher(dispatcher, projectUuid,
+ SubscriberPermissionsOnProject.ALL_MUST_HAVE_ROLE_USER);
+ assertThat(multiMap.entries()).hasSize(3);
+
+ Map<String, Collection<NotificationChannel>> map = multiMap.asMap();
+ assertThat(map.get("user1")).containsOnly(emailChannel);
+ assertThat(map.get("user2")).isNull();
+ assertThat(map.get("user3")).containsOnly(emailChannel, twitterChannel);
+ assertThat(map.get("user4")).isNull();
+
+ // code is optimized to perform only 1 SQL requests for all channels
+ verify(authorizationDao, times(1)).keepAuthorizedLoginsOnProject(eq(dbSession), anySet(), anyString(), anyString());
+ }
+
+ @Test
+ public void should_apply_distinct_permission_filtering_global_or_project_subscribers() {
+ String globalPermission = RandomStringUtils.randomAlphanumeric(4);
+ String projectPermission = RandomStringUtils.randomAlphanumeric(5);
+ String projectUuid = "uuid_45";
+ when(propertiesDao.findUsersForNotification("NewViolations", "Email", projectUuid))
+ .thenReturn(newHashSet(new Subscriber("user1", false), new Subscriber("user3", false), new Subscriber("user3", true)));
+ when(propertiesDao.findUsersForNotification("NewViolations", "Twitter", "uuid_56"))
+ .thenReturn(newHashSet(new Subscriber("user2", false)));
+ when(propertiesDao.findUsersForNotification("NewViolations", "Twitter", projectUuid))
+ .thenReturn(newHashSet(new Subscriber("user3", true)));
+ when(propertiesDao.findUsersForNotification("NewAlerts", "Twitter", projectUuid))
+ .thenReturn(newHashSet(new Subscriber("user4", false)));
+
+ when(authorizationDao.keepAuthorizedLoginsOnProject(dbSession, newHashSet("user3", "user4"), projectUuid, globalPermission))
+ .thenReturn(newHashSet("user3"));
+ when(authorizationDao.keepAuthorizedLoginsOnProject(dbSession, newHashSet("user1", "user3"), projectUuid, projectPermission))
+ .thenReturn(newHashSet("user1", "user3"));
+
+ Multimap<String, NotificationChannel> multiMap = underTest.findSubscribedRecipientsForDispatcher(dispatcher, projectUuid,
+ new SubscriberPermissionsOnProject(globalPermission, projectPermission));
+ assertThat(multiMap.entries()).hasSize(3);
+
+ Map<String, Collection<NotificationChannel>> map = multiMap.asMap();
+ assertThat(map.get("user1")).containsOnly(emailChannel);
+ assertThat(map.get("user2")).isNull();
+ assertThat(map.get("user3")).containsOnly(emailChannel, twitterChannel);
+ assertThat(map.get("user4")).isNull();
+
+ // code is optimized to perform only 2 SQL requests for all channels
+ verify(authorizationDao, times(1)).keepAuthorizedLoginsOnProject(eq(dbSession), anySet(), anyString(), eq(globalPermission));
+ verify(authorizationDao, times(1)).keepAuthorizedLoginsOnProject(eq(dbSession), anySet(), anyString(), eq(projectPermission));
+ }
+
+ @Test
+ public void do_not_call_db_for_project_permission_filtering_if_there_is_no_project_subscriber() {
+ String globalPermission = RandomStringUtils.randomAlphanumeric(4);
+ String projectPermission = RandomStringUtils.randomAlphanumeric(5);
+ String projectUuid = "uuid_45";
+ when(propertiesDao.findUsersForNotification("NewViolations", "Email", projectUuid))
+ .thenReturn(newHashSet(new Subscriber("user3", true)));
+ when(propertiesDao.findUsersForNotification("NewViolations", "Twitter", projectUuid))
+ .thenReturn(newHashSet(new Subscriber("user3", true)));
+
+ when(authorizationDao.keepAuthorizedLoginsOnProject(dbSession, newHashSet("user3"), projectUuid, globalPermission))
+ .thenReturn(newHashSet("user3"));
+
+ Multimap<String, NotificationChannel> multiMap = underTest.findSubscribedRecipientsForDispatcher(dispatcher, projectUuid,
+ new SubscriberPermissionsOnProject(globalPermission, projectPermission));
+ assertThat(multiMap.entries()).hasSize(2);
+
+ Map<String, Collection<NotificationChannel>> map = multiMap.asMap();
+ assertThat(map.get("user3")).containsOnly(emailChannel, twitterChannel);
+
+ verify(authorizationDao, times(1)).keepAuthorizedLoginsOnProject(eq(dbSession), anySet(), anyString(), eq(globalPermission));
+ verify(authorizationDao, times(0)).keepAuthorizedLoginsOnProject(eq(dbSession), anySet(), anyString(), eq(projectPermission));
+ }
+
+ @Test
+ public void do_not_call_db_for_project_permission_filtering_if_there_is_no_global_subscriber() {
+ String globalPermission = RandomStringUtils.randomAlphanumeric(4);
+ String projectPermission = RandomStringUtils.randomAlphanumeric(5);
+ String projectUuid = "uuid_45";
+ when(propertiesDao.findUsersForNotification("NewViolations", "Email", projectUuid))
+ .thenReturn(newHashSet(new Subscriber("user3", false)));
+ when(propertiesDao.findUsersForNotification("NewViolations", "Twitter", projectUuid))
+ .thenReturn(newHashSet(new Subscriber("user3", false)));
+
+ when(authorizationDao.keepAuthorizedLoginsOnProject(dbSession, newHashSet("user3"), projectUuid, projectPermission))
+ .thenReturn(newHashSet("user3"));
+
+ Multimap<String, NotificationChannel> multiMap = underTest.findSubscribedRecipientsForDispatcher(dispatcher, projectUuid,
+ new SubscriberPermissionsOnProject(globalPermission, projectPermission));
+ assertThat(multiMap.entries()).hasSize(2);
+
+ Map<String, Collection<NotificationChannel>> map = multiMap.asMap();
+ assertThat(map.get("user3")).containsOnly(emailChannel, twitterChannel);
+
+ verify(authorizationDao, times(0)).keepAuthorizedLoginsOnProject(eq(dbSession), anySet(), anyString(), eq(globalPermission));
+ verify(authorizationDao, times(1)).keepAuthorizedLoginsOnProject(eq(dbSession), anySet(), anyString(), eq(projectPermission));
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.notification;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class NotificationDispatcherMetadataTest {
+
+ private NotificationDispatcherMetadata metadata;
+
+ @Before
+ public void init() {
+ metadata = NotificationDispatcherMetadata.create("NewViolations").setProperty("global", "true");
+ }
+
+ @Test
+ public void shouldReturnDispatcherKey() {
+ assertThat(metadata.getDispatcherKey()).isEqualTo("NewViolations");
+ }
+
+ @Test
+ public void shouldReturnProperty() {
+ assertThat(metadata.getProperty("global")).isEqualTo("true");
+ assertThat(metadata.getProperty("per-project")).isNull();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.notification;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.notifications.NotificationChannel;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class NotificationDispatcherTest {
+
+ @Mock
+ private NotificationChannel channel;
+
+ @Mock
+ private Notification notification;
+
+ @Mock
+ private NotificationDispatcher.Context context;
+
+ @Before
+ public void init() {
+ MockitoAnnotations.initMocks(this);
+ when(notification.getType()).thenReturn("event1");
+ }
+
+ @Test
+ public void defaultMethods() {
+ NotificationDispatcher dispatcher = new FakeGenericNotificationDispatcher();
+ assertThat(dispatcher.getKey(), is("FakeGenericNotificationDispatcher"));
+ assertThat(dispatcher.toString(), is("FakeGenericNotificationDispatcher"));
+ }
+
+ @Test
+ public void shouldAlwaysRunDispatchForGenericDispatcher() {
+ NotificationDispatcher dispatcher = new FakeGenericNotificationDispatcher();
+ dispatcher.performDispatch(notification, context);
+
+ verify(context, times(1)).addUser("user1", channel);
+ }
+
+ @Test
+ public void shouldNotAlwaysRunDispatchForSpecificDispatcher() {
+ NotificationDispatcher dispatcher = new FakeSpecificNotificationDispatcher();
+
+ // a "event1" notif is sent
+ dispatcher.performDispatch(notification, context);
+ verify(context, never()).addUser("user1", channel);
+
+ // now, a "specific-event" notif is sent
+ when(notification.getType()).thenReturn("specific-event");
+ dispatcher.performDispatch(notification, context);
+ verify(context, times(1)).addUser("user1", channel);
+ }
+
+ class FakeGenericNotificationDispatcher extends NotificationDispatcher {
+ @Override
+ public void dispatch(Notification notification, Context context) {
+ context.addUser("user1", channel);
+ }
+ }
+
+ class FakeSpecificNotificationDispatcher extends NotificationDispatcher {
+
+ public FakeSpecificNotificationDispatcher() {
+ super("specific-event");
+ }
+
+ @Override
+ public void dispatch(Notification notification, Context context) {
+ context.addUser("user1", channel);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.organization;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.organization.OrganizationDto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto;
+import static org.sonar.server.property.InternalProperties.DEFAULT_ORGANIZATION;
+
+public class DefaultOrganizationProviderImplTest {
+ private static final OrganizationDto ORGANIZATION_DTO_1 = newOrganizationDto()
+ .setUuid("uuid1")
+ .setName("the name of 1")
+ .setKey("the key 1");
+ private static final long DATE_1 = 1_999_888L;
+
+ private System2 system2 = mock(System2.class);
+
+ @Rule
+ public DbTester dbTester = DbTester.create(system2).setDisableDefaultOrganization(true);
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private DbClient dbClient = dbTester.getDbClient();
+ private DbSession dbSession = dbTester.getSession();
+
+ private DefaultOrganizationProviderImpl underTest = new DefaultOrganizationProviderImpl(dbClient);
+
+ @Test
+ public void get_fails_with_ISE_if_default_organization_internal_property_does_not_exist() {
+ expectISENoDefaultOrganizationUuid();
+
+ underTest.get();
+ }
+
+ @Test
+ public void get_fails_with_ISE_if_default_organization_internal_property_is_empty() {
+ dbClient.internalPropertiesDao().saveAsEmpty(dbSession, DEFAULT_ORGANIZATION);
+ dbSession.commit();
+
+ expectISENoDefaultOrganizationUuid();
+
+ underTest.get();
+ }
+
+ private void expectISENoDefaultOrganizationUuid() {
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("No Default organization uuid configured");
+ }
+
+ @Test
+ public void get_fails_with_ISE_if_default_organization_does_not_exist() {
+ dbClient.internalPropertiesDao().save(dbSession, DEFAULT_ORGANIZATION, "bla");
+ dbSession.commit();
+
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("Default organization with uuid 'bla' does not exist");
+
+ underTest.get();
+ }
+
+ @Test
+ public void get_returns_DefaultOrganization_populated_from_DB() {
+ insertOrganization(ORGANIZATION_DTO_1, DATE_1);
+ dbClient.internalPropertiesDao().save(dbSession, DEFAULT_ORGANIZATION, ORGANIZATION_DTO_1.getUuid());
+ dbSession.commit();
+
+ DefaultOrganization defaultOrganization = underTest.get();
+ assertThat(defaultOrganization.getUuid()).isEqualTo(ORGANIZATION_DTO_1.getUuid());
+ assertThat(defaultOrganization.getKey()).isEqualTo(ORGANIZATION_DTO_1.getKey());
+ assertThat(defaultOrganization.getName()).isEqualTo(ORGANIZATION_DTO_1.getName());
+ assertThat(defaultOrganization.getCreatedAt()).isEqualTo(DATE_1);
+ assertThat(defaultOrganization.getUpdatedAt()).isEqualTo(DATE_1);
+ }
+
+ @Test
+ public void get_returns_new_DefaultOrganization_with_each_call_when_cache_is_not_loaded() {
+ insertOrganization(ORGANIZATION_DTO_1, DATE_1);
+ dbClient.internalPropertiesDao().save(dbSession, DEFAULT_ORGANIZATION, ORGANIZATION_DTO_1.getUuid());
+ dbSession.commit();
+
+ assertThat(underTest.get()).isNotSameAs(underTest.get());
+ }
+
+ @Test
+ public void unload_does_not_fail_if_load_has_not_been_called() {
+ underTest.unload();
+ }
+
+ @Test
+ public void load_fails_with_ISE_when_called_twice_without_unload_in_between() {
+ underTest.load();
+
+ try {
+ underTest.load();
+ fail("A IllegalStateException should have been raised");
+ } catch (IllegalStateException e) {
+ assertThat(e).hasMessage("load called twice for thread '" + Thread.currentThread().getName() + "' or state wasn't cleared last time it was used");
+ } finally {
+ underTest.unload();
+ }
+ }
+
+ @Test
+ public void load_and_unload_cache_DefaultOrganization_object_by_thread() throws InterruptedException {
+ insertOrganization(ORGANIZATION_DTO_1, DATE_1);
+ dbClient.internalPropertiesDao().save(dbSession, DEFAULT_ORGANIZATION, ORGANIZATION_DTO_1.getUuid());
+ dbSession.commit();
+
+ try {
+ underTest.load();
+
+ DefaultOrganization cachedForThread1 = underTest.get();
+ assertThat(cachedForThread1).isSameAs(underTest.get());
+
+ Thread otherThread = new Thread(() -> {
+ try {
+ underTest.load();
+
+ assertThat(underTest.get())
+ .isNotSameAs(cachedForThread1)
+ .isSameAs(underTest.get());
+ } finally {
+ underTest.unload();
+ }
+ });
+ otherThread.start();
+ otherThread.join();
+ } finally {
+ underTest.unload();
+ }
+ }
+
+ @Test
+ public void get_returns_new_instance_for_each_call_once_unload_has_been_called() {
+ insertOrganization(ORGANIZATION_DTO_1, DATE_1);
+ dbClient.internalPropertiesDao().save(dbSession, DEFAULT_ORGANIZATION, ORGANIZATION_DTO_1.getUuid());
+ dbSession.commit();
+
+ try {
+ underTest.load();
+ DefaultOrganization cached = underTest.get();
+ assertThat(cached).isSameAs(underTest.get());
+
+ underTest.unload();
+ assertThat(underTest.get()).isNotSameAs(underTest.get()).isNotSameAs(cached);
+ } finally {
+ // fail safe
+ underTest.unload();
+ }
+ }
+
+ private void insertOrganization(OrganizationDto dto, long createdAt) {
+ when(system2.now()).thenReturn(createdAt);
+ dbClient.organizationDao().insert(dbSession, dto, false);
+ dbSession.commit();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.organization;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class DefaultOrganizationTest {
+ private static final long DATE_2 = 2_000_000L;
+ private static final long DATE_1 = 1_000_000L;
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private DefaultOrganization.Builder populatedBuilder = new DefaultOrganization.Builder()
+ .setUuid("uuid")
+ .setKey("key")
+ .setName("name")
+ .setCreatedAt(DATE_1)
+ .setUpdatedAt(DATE_2);
+
+ @Test
+ public void build_fails_if_uuid_is_null() {
+ populatedBuilder.setUuid(null);
+
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("uuid can't be null");
+
+ populatedBuilder.build();
+ }
+
+ @Test
+ public void build_fails_if_key_is_null() {
+ populatedBuilder.setKey(null);
+
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("key can't be null");
+
+ populatedBuilder.build();
+ }
+
+ @Test
+ public void build_fails_if_name_is_null() {
+ populatedBuilder.setName(null);
+
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("name can't be null");
+
+ populatedBuilder.build();
+ }
+
+ @Test
+ public void build_fails_if_createdAt_not_set() {
+ DefaultOrganization.Builder underTest = new DefaultOrganization.Builder()
+ .setUuid("uuid")
+ .setKey("key")
+ .setName("name")
+ .setUpdatedAt(DATE_2);
+
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("createdAt can't be null");
+
+ underTest.build();
+ }
+
+ @Test
+ public void build_fails_if_updatedAt_not_set() {
+ DefaultOrganization.Builder underTest = new DefaultOrganization.Builder()
+ .setUuid("uuid")
+ .setKey("key")
+ .setName("name")
+ .setCreatedAt(DATE_1);
+
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("updatedAt can't be null");
+
+ underTest.build();
+ }
+
+ @Test
+ public void verify_toString() {
+ assertThat(populatedBuilder.build().toString())
+ .isEqualTo("DefaultOrganization{uuid='uuid', key='key', name='name', createdAt=1000000, updatedAt=2000000}");
+ }
+
+ @Test
+ public void verify_getters() {
+ DefaultOrganization underTest = populatedBuilder.build();
+
+ assertThat(underTest.getUuid()).isEqualTo("uuid");
+ assertThat(underTest.getKey()).isEqualTo("key");
+ assertThat(underTest.getName()).isEqualTo("name");
+ assertThat(underTest.getCreatedAt()).isEqualTo(DATE_1);
+ assertThat(underTest.getUpdatedAt()).isEqualTo(DATE_2);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.property;
+
+import java.util.Optional;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.property.InternalPropertiesDao;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class InternalPropertiesImplTest {
+ private static final String EMPTY_STRING = "";
+ public static final String SOME_VALUE = "a value";
+ public static final String SOME_KEY = "some key";
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private DbClient dbClient = mock(DbClient.class);
+ private DbSession dbSession = mock(DbSession.class);
+ private InternalPropertiesDao internalPropertiesDao = mock(InternalPropertiesDao.class);
+ private InternalPropertiesImpl underTest = new InternalPropertiesImpl(dbClient);
+
+ @Before
+ public void setUp() throws Exception {
+ when(dbClient.openSession(false)).thenReturn(dbSession);
+ when(dbClient.internalPropertiesDao()).thenReturn(internalPropertiesDao);
+ }
+
+ @Test
+ public void reads_throws_IAE_if_key_is_null() {
+ expectKeyNullOrEmptyIAE();
+
+ underTest.read(null);
+ }
+
+ @Test
+ public void reads_throws_IAE_if_key_is_empty() {
+ expectKeyNullOrEmptyIAE();
+
+ underTest.read(EMPTY_STRING);
+ }
+
+ @Test
+ public void reads_returns_optional_from_DAO() {
+ Optional<String> value = Optional.of("bablabla");
+
+ when(internalPropertiesDao.selectByKey(dbSession, SOME_KEY)).thenReturn(value);
+
+ assertThat(underTest.read(SOME_KEY)).isSameAs(value);
+ }
+
+ @Test
+ public void write_throws_IAE_if_key_is_null() {
+ expectKeyNullOrEmptyIAE();
+
+ underTest.write(null, SOME_VALUE);
+ }
+
+ @Test
+ public void writes_throws_IAE_if_key_is_empty() {
+ expectKeyNullOrEmptyIAE();
+
+ underTest.write(EMPTY_STRING, SOME_VALUE);
+ }
+
+ @Test
+ public void write_calls_dao_saveAsEmpty_when_value_is_null() {
+ underTest.write(SOME_KEY, null);
+
+ verify(internalPropertiesDao).saveAsEmpty(dbSession, SOME_KEY);
+ verify(dbSession).commit();
+ }
+
+ @Test
+ public void write_calls_dao_saveAsEmpty_when_value_is_empty() {
+ underTest.write(SOME_KEY, EMPTY_STRING);
+
+ verify(internalPropertiesDao).saveAsEmpty(dbSession, SOME_KEY);
+ verify(dbSession).commit();
+ }
+
+ @Test
+ public void write_calls_dao_save_when_value_is_neither_null_nor_empty() {
+ underTest.write(SOME_KEY, SOME_VALUE);
+
+ verify(internalPropertiesDao).save(dbSession, SOME_KEY, SOME_VALUE);
+ verify(dbSession).commit();
+ }
+
+ private void expectKeyNullOrEmptyIAE() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("key can't be null nor empty");
+ }
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.ce;
-
-import org.sonar.ce.http.CeHttpClientImpl;
-import org.sonar.ce.queue.CeQueueImpl;
-import org.sonar.ce.task.log.CeTaskLogging;
-import org.sonar.ce.taskprocessor.ReportTaskProcessorDeclaration;
-import org.sonar.core.platform.Module;
-import org.sonar.server.ce.queue.ReportSubmitter;
-
-public class CeModule extends Module {
- @Override
- protected void configureModule() {
- add(CeTaskLogging.class,
- CeHttpClientImpl.class,
-
- // Queue
- CeQueueImpl.class,
- ReportSubmitter.class,
-
- // Core tasks processors
- ReportTaskProcessorDeclaration.class);
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.ce.log;
-
-import ch.qos.logback.classic.Level;
-import org.sonar.process.ProcessId;
-import org.sonar.process.logging.LogDomain;
-import org.sonar.process.logging.LogLevelConfig;
-import org.sonar.server.app.ServerProcessLogging;
-
-import static org.sonar.ce.task.log.CeTaskLogging.MDC_CE_TASK_UUID;
-
-/**
- * Configure logback for the Compute Engine process. Logs are written to file "ce.log" in SQ's log directory.
- */
-public class CeProcessLogging extends ServerProcessLogging {
-
- public CeProcessLogging() {
- super(ProcessId.COMPUTE_ENGINE, "%X{" + MDC_CE_TASK_UUID + "}");
- }
-
- @Override
- protected void extendLogLevelConfiguration(LogLevelConfig.Builder logLevelConfigBuilder) {
- logLevelConfigBuilder.levelByDomain("sql", ProcessId.COMPUTE_ENGINE, LogDomain.SQL);
- logLevelConfigBuilder.levelByDomain("es", ProcessId.COMPUTE_ENGINE, LogDomain.ES);
- JMX_RMI_LOGGER_NAMES.forEach(loggerName -> logLevelConfigBuilder.levelByDomain(loggerName, ProcessId.COMPUTE_ENGINE, LogDomain.JMX));
- LOGGER_NAMES_TO_TURN_OFF.forEach(loggerName -> logLevelConfigBuilder.immutableLevel(loggerName, Level.OFF));
- }
-
- @Override
- protected void extendConfigure() {
- // nothing to do
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.
- */
-@ParametersAreNonnullByDefault
-package org.sonar.ce.log;
-
-import javax.annotation.ParametersAreNonnullByDefault;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.ce.notification;
-
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-
-import static java.util.Objects.requireNonNull;
-
-public class ReportAnalysisFailureNotification {
- public static final String TYPE = "ce-report-task-failure";
-
- private final Project project;
- private final Task task;
- private final String errorMessage;
-
- public ReportAnalysisFailureNotification(Project project, Task task, @Nullable String errorMessage) {
- this.project = requireNonNull(project, "project can't be null");
- this.task = requireNonNull(task, "task can't be null");
- this.errorMessage = errorMessage;
- }
-
- public Project getProject() {
- return project;
- }
-
- public Task getTask() {
- return task;
- }
-
- @CheckForNull
- public String getErrorMessage() {
- return errorMessage;
- }
-
- public static final class Project {
- private final String uuid;
- private final String key;
- private final String name;
- private final String branchName;
-
- public Project(String uuid, String key, String name, @Nullable String branchName) {
- this.uuid = requireNonNull(uuid, "uuid can't be null");
- this.key = requireNonNull(key, "key can't be null");
- this.name = requireNonNull(name, "name can't be null");
- this.branchName = branchName;
- }
-
- public String getUuid() {
- return uuid;
- }
-
- public String getKey() {
- return key;
- }
-
- public String getName() {
- return name;
- }
-
- @CheckForNull
- public String getBranchName() {
- return branchName;
- }
- }
-
- public static final class Task {
- private final String uuid;
- private final long createdAt;
- private final long failedAt;
-
- public Task(String uuid, long createdAt, long failedAt) {
- this.uuid = requireNonNull(uuid, "uuid can't be null");
- this.createdAt = createdAt;
- this.failedAt = failedAt;
- }
-
- public String getUuid() {
- return uuid;
- }
-
- public long getCreatedAt() {
- return createdAt;
- }
-
- public long getFailedAt() {
- return failedAt;
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.ce.notification;
-
-import com.google.common.collect.Multimap;
-import java.util.Collection;
-import java.util.Map;
-import org.sonar.api.notifications.Notification;
-import org.sonar.api.notifications.NotificationChannel;
-import org.sonar.api.web.UserRole;
-import org.sonar.server.notification.NotificationDispatcher;
-import org.sonar.server.notification.NotificationDispatcherMetadata;
-import org.sonar.server.notification.NotificationManager;
-import org.sonar.server.notification.NotificationManager.SubscriberPermissionsOnProject;
-
-public class ReportAnalysisFailureNotificationDispatcher extends NotificationDispatcher {
-
- public static final String KEY = "CeReportTaskFailure";
- private static final SubscriberPermissionsOnProject REQUIRED_SUBSCRIBER_PERMISSIONS = new SubscriberPermissionsOnProject(UserRole.ADMIN, UserRole.USER);
-
- private final NotificationManager manager;
-
- public ReportAnalysisFailureNotificationDispatcher(NotificationManager manager) {
- super(ReportAnalysisFailureNotification.TYPE);
- this.manager = manager;
- }
-
- public static NotificationDispatcherMetadata newMetadata() {
- return NotificationDispatcherMetadata.create(KEY)
- .setProperty(NotificationDispatcherMetadata.GLOBAL_NOTIFICATION, String.valueOf(true))
- .setProperty(NotificationDispatcherMetadata.PER_PROJECT_NOTIFICATION, String.valueOf(true));
- }
-
- @Override
- public String getKey() {
- return KEY;
- }
-
- @Override
- public void dispatch(Notification notification, Context context) {
- String projectKey = notification.getFieldValue("project.key");
- Multimap<String, NotificationChannel> subscribedRecipients = manager
- .findSubscribedRecipientsForDispatcher(this, projectKey, REQUIRED_SUBSCRIBER_PERMISSIONS);
-
- for (Map.Entry<String, Collection<NotificationChannel>> channelsByRecipients : subscribedRecipients.asMap().entrySet()) {
- String userLogin = channelsByRecipients.getKey();
- for (NotificationChannel channel : channelsByRecipients.getValue()) {
- context.addUser(userLogin, channel);
- }
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.ce.notification;
-
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
-import org.sonar.api.config.EmailSettings;
-import org.sonar.api.notifications.Notification;
-import org.sonar.plugins.emailnotifications.api.EmailMessage;
-import org.sonar.plugins.emailnotifications.api.EmailTemplate;
-
-import static org.sonar.api.utils.DateUtils.formatDateTime;
-
-public class ReportAnalysisFailureNotificationEmailTemplate extends EmailTemplate {
- private static final char LINE_RETURN = '\n';
- private static final char TAB = '\t';
-
- private final ReportAnalysisFailureNotificationSerializer serializer;
- protected final EmailSettings settings;
-
- public ReportAnalysisFailureNotificationEmailTemplate(ReportAnalysisFailureNotificationSerializer serializer, EmailSettings settings) {
- this.serializer = serializer;
- this.settings = settings;
- }
-
- @Override
- public EmailMessage format(Notification notification) {
- if (!ReportAnalysisFailureNotification.TYPE.equals(notification.getType())) {
- return null;
- }
-
- ReportAnalysisFailureNotification taskFailureNotification = serializer.fromNotification(notification);
- String projectUuid = taskFailureNotification.getProject().getUuid();
- String projectFullName = computeProjectFullName(taskFailureNotification.getProject());
-
- return new EmailMessage()
- .setMessageId(notification.getType() + "/" + projectUuid)
- .setSubject(subject(projectFullName))
- .setMessage(message(projectFullName, taskFailureNotification));
- }
-
- private static String computeProjectFullName(ReportAnalysisFailureNotification.Project project) {
- String branchName = project.getBranchName();
- if (branchName != null) {
- return String.format("%s (%s)", project.getName(), branchName);
- }
- return project.getName();
- }
-
- private static String subject(String projectFullName) {
- return String.format("%s: Background task in failure", projectFullName);
- }
-
- private String message(String projectFullName, ReportAnalysisFailureNotification taskFailureNotification) {
- ReportAnalysisFailureNotification.Project project = taskFailureNotification.getProject();
- ReportAnalysisFailureNotification.Task task = taskFailureNotification.getTask();
-
- StringBuilder res = new StringBuilder();
- res.append("Project:").append(TAB).append(projectFullName).append(LINE_RETURN);
- res.append("Background task:").append(TAB).append(task.getUuid()).append(LINE_RETURN);
- res.append("Submission time:").append(TAB).append(formatDateTime(task.getCreatedAt())).append(LINE_RETURN);
- res.append("Failure time:").append(TAB).append(formatDateTime(task.getFailedAt())).append(LINE_RETURN);
-
- String errorMessage = taskFailureNotification.getErrorMessage();
- if (errorMessage != null) {
- res.append(LINE_RETURN);
- res.append("Error message:").append(TAB).append(errorMessage).append(LINE_RETURN);
- }
-
- res.append(LINE_RETURN);
- res.append("More details at: ").append(String.format("%s/project/background_tasks?id=%s", settings.getServerBaseURL(), encode(project.getKey())));
-
- return res.toString();
- }
-
- private static String encode(String toEncode) {
- try {
- return URLEncoder.encode(toEncode, "UTF-8");
- } catch (UnsupportedEncodingException e) {
- throw new IllegalStateException("Encoding not supported", e);
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.ce.notification;
-
-import org.sonar.core.platform.Module;
-
-public class ReportAnalysisFailureNotificationModule extends Module {
- @Override
- protected void configureModule() {
- add(
- ReportAnalysisFailureNotificationDispatcher.class,
- ReportAnalysisFailureNotificationDispatcher.newMetadata(),
- ReportAnalysisFailureNotificationSerializerImpl.class,
- ReportAnalysisFailureNotificationEmailTemplate.class);
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.ce.notification;
-
-import org.sonar.api.notifications.Notification;
-
-public interface ReportAnalysisFailureNotificationSerializer {
- Notification toNotification(ReportAnalysisFailureNotification reportAnalysisFailureNotification);
-
- ReportAnalysisFailureNotification fromNotification(Notification notification);
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.ce.notification;
-
-import org.sonar.api.notifications.Notification;
-
-import static java.lang.String.valueOf;
-
-public class ReportAnalysisFailureNotificationSerializerImpl implements ReportAnalysisFailureNotificationSerializer {
- private static final String FIELD_PROJECT_UUID = "project.uuid";
- private static final String FIELD_PROJECT_KEY = "project.key";
- private static final String FIELD_PROJECT_NAME = "project.name";
- private static final String FIELD_PROJECT_BRANCH = "project.branchName";
- private static final String FIELD_TASK_UUID = "task.uuid";
- private static final String FIELD_TASK_CREATED_AT = "task.createdAt";
- private static final String FIELD_TASK_FAILED_AT = "task.failedAt";
- private static final String FIELD_ERROR_MESSAGE = "error.message";
-
- @Override
- public Notification toNotification(ReportAnalysisFailureNotification reportAnalysisFailureNotification) {
- return new Notification(ReportAnalysisFailureNotification.TYPE)
- .setFieldValue(FIELD_PROJECT_UUID, reportAnalysisFailureNotification.getProject().getUuid())
- .setFieldValue(FIELD_PROJECT_KEY, reportAnalysisFailureNotification.getProject().getKey())
- .setFieldValue(FIELD_PROJECT_NAME, reportAnalysisFailureNotification.getProject().getName())
- .setFieldValue(FIELD_PROJECT_BRANCH, reportAnalysisFailureNotification.getProject().getBranchName())
- .setFieldValue(FIELD_TASK_UUID, reportAnalysisFailureNotification.getTask().getUuid())
- .setFieldValue(FIELD_TASK_CREATED_AT, valueOf(reportAnalysisFailureNotification.getTask().getCreatedAt()))
- .setFieldValue(FIELD_TASK_FAILED_AT, valueOf(reportAnalysisFailureNotification.getTask().getFailedAt()))
- .setFieldValue(FIELD_ERROR_MESSAGE, reportAnalysisFailureNotification.getErrorMessage());
- }
-
- @Override
- public ReportAnalysisFailureNotification fromNotification(Notification notification) {
- return new ReportAnalysisFailureNotification(
- new ReportAnalysisFailureNotification.Project(
- notification.getFieldValue(FIELD_PROJECT_UUID),
- notification.getFieldValue(FIELD_PROJECT_KEY),
- notification.getFieldValue(FIELD_PROJECT_NAME),
- notification.getFieldValue(FIELD_PROJECT_BRANCH)),
- new ReportAnalysisFailureNotification.Task(
- notification.getFieldValue(FIELD_TASK_UUID),
- Long.valueOf(notification.getFieldValue(FIELD_TASK_CREATED_AT)),
- Long.valueOf(notification.getFieldValue(FIELD_TASK_FAILED_AT))),
- notification.getFieldValue(FIELD_ERROR_MESSAGE));
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.
- */
-@ParametersAreNonnullByDefault
-package org.sonar.ce.notification;
-
-import javax.annotation.ParametersAreNonnullByDefault;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.
- */
-@ParametersAreNonnullByDefault
-package org.sonar.ce;
-
-import javax.annotation.ParametersAreNonnullByDefault;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.ce.task.projectanalysis;
+
+import java.util.Collections;
+import java.util.Set;
+import org.sonar.ce.task.CeTask;
+import org.sonar.ce.task.CeTaskResult;
+import org.sonar.ce.task.taskprocessor.CeTaskProcessor;
+import org.sonar.db.ce.CeTaskTypes;
+
+/**
+ * CeTaskProcessor without any real implementation used to declare the CeTask type to the WebServer only.
+ */
+public class ReportTaskProcessorDeclaration implements CeTaskProcessor {
+
+ private static final Set<String> HANDLED_TYPES = Collections.singleton(CeTaskTypes.REPORT);
+
+ @Override
+ public Set<String> getHandledCeTaskTypes() {
+ return HANDLED_TYPES;
+ }
+
+ @Override
+ public CeTaskResult process(CeTask task) {
+ throw new UnsupportedOperationException("process must not be called in WebServer");
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.ce.notification;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+import static java.util.Objects.requireNonNull;
+
+public class ReportAnalysisFailureNotification {
+ public static final String TYPE = "ce-report-task-failure";
+
+ private final Project project;
+ private final Task task;
+ private final String errorMessage;
+
+ public ReportAnalysisFailureNotification(Project project, Task task, @Nullable String errorMessage) {
+ this.project = requireNonNull(project, "project can't be null");
+ this.task = requireNonNull(task, "task can't be null");
+ this.errorMessage = errorMessage;
+ }
+
+ public Project getProject() {
+ return project;
+ }
+
+ public Task getTask() {
+ return task;
+ }
+
+ @CheckForNull
+ public String getErrorMessage() {
+ return errorMessage;
+ }
+
+ public static final class Project {
+ private final String uuid;
+ private final String key;
+ private final String name;
+ private final String branchName;
+
+ public Project(String uuid, String key, String name, @Nullable String branchName) {
+ this.uuid = requireNonNull(uuid, "uuid can't be null");
+ this.key = requireNonNull(key, "key can't be null");
+ this.name = requireNonNull(name, "name can't be null");
+ this.branchName = branchName;
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @CheckForNull
+ public String getBranchName() {
+ return branchName;
+ }
+ }
+
+ public static final class Task {
+ private final String uuid;
+ private final long createdAt;
+ private final long failedAt;
+
+ public Task(String uuid, long createdAt, long failedAt) {
+ this.uuid = requireNonNull(uuid, "uuid can't be null");
+ this.createdAt = createdAt;
+ this.failedAt = failedAt;
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public long getCreatedAt() {
+ return createdAt;
+ }
+
+ public long getFailedAt() {
+ return failedAt;
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.ce.notification;
+
+import com.google.common.collect.Multimap;
+import java.util.Collection;
+import java.util.Map;
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.notifications.NotificationChannel;
+import org.sonar.api.web.UserRole;
+import org.sonar.server.notification.NotificationDispatcher;
+import org.sonar.server.notification.NotificationDispatcherMetadata;
+import org.sonar.server.notification.NotificationManager;
+import org.sonar.server.notification.NotificationManager.SubscriberPermissionsOnProject;
+
+public class ReportAnalysisFailureNotificationDispatcher extends NotificationDispatcher {
+
+ public static final String KEY = "CeReportTaskFailure";
+ private static final SubscriberPermissionsOnProject REQUIRED_SUBSCRIBER_PERMISSIONS = new SubscriberPermissionsOnProject(UserRole.ADMIN, UserRole.USER);
+
+ private final NotificationManager manager;
+
+ public ReportAnalysisFailureNotificationDispatcher(NotificationManager manager) {
+ super(ReportAnalysisFailureNotification.TYPE);
+ this.manager = manager;
+ }
+
+ public static NotificationDispatcherMetadata newMetadata() {
+ return NotificationDispatcherMetadata.create(KEY)
+ .setProperty(NotificationDispatcherMetadata.GLOBAL_NOTIFICATION, String.valueOf(true))
+ .setProperty(NotificationDispatcherMetadata.PER_PROJECT_NOTIFICATION, String.valueOf(true));
+ }
+
+ @Override
+ public String getKey() {
+ return KEY;
+ }
+
+ @Override
+ public void dispatch(Notification notification, Context context) {
+ String projectKey = notification.getFieldValue("project.key");
+ Multimap<String, NotificationChannel> subscribedRecipients = manager
+ .findSubscribedRecipientsForDispatcher(this, projectKey, REQUIRED_SUBSCRIBER_PERMISSIONS);
+
+ for (Map.Entry<String, Collection<NotificationChannel>> channelsByRecipients : subscribedRecipients.asMap().entrySet()) {
+ String userLogin = channelsByRecipients.getKey();
+ for (NotificationChannel channel : channelsByRecipients.getValue()) {
+ context.addUser(userLogin, channel);
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.ce.notification;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import org.sonar.api.config.EmailSettings;
+import org.sonar.api.notifications.Notification;
+import org.sonar.plugins.emailnotifications.api.EmailMessage;
+import org.sonar.plugins.emailnotifications.api.EmailTemplate;
+
+import static org.sonar.api.utils.DateUtils.formatDateTime;
+
+public class ReportAnalysisFailureNotificationEmailTemplate extends EmailTemplate {
+ private static final char LINE_RETURN = '\n';
+ private static final char TAB = '\t';
+
+ private final ReportAnalysisFailureNotificationSerializer serializer;
+ protected final EmailSettings settings;
+
+ public ReportAnalysisFailureNotificationEmailTemplate(ReportAnalysisFailureNotificationSerializer serializer, EmailSettings settings) {
+ this.serializer = serializer;
+ this.settings = settings;
+ }
+
+ @Override
+ public EmailMessage format(Notification notification) {
+ if (!ReportAnalysisFailureNotification.TYPE.equals(notification.getType())) {
+ return null;
+ }
+
+ ReportAnalysisFailureNotification taskFailureNotification = serializer.fromNotification(notification);
+ String projectUuid = taskFailureNotification.getProject().getUuid();
+ String projectFullName = computeProjectFullName(taskFailureNotification.getProject());
+
+ return new EmailMessage()
+ .setMessageId(notification.getType() + "/" + projectUuid)
+ .setSubject(subject(projectFullName))
+ .setMessage(message(projectFullName, taskFailureNotification));
+ }
+
+ private static String computeProjectFullName(ReportAnalysisFailureNotification.Project project) {
+ String branchName = project.getBranchName();
+ if (branchName != null) {
+ return String.format("%s (%s)", project.getName(), branchName);
+ }
+ return project.getName();
+ }
+
+ private static String subject(String projectFullName) {
+ return String.format("%s: Background task in failure", projectFullName);
+ }
+
+ private String message(String projectFullName, ReportAnalysisFailureNotification taskFailureNotification) {
+ ReportAnalysisFailureNotification.Project project = taskFailureNotification.getProject();
+ ReportAnalysisFailureNotification.Task task = taskFailureNotification.getTask();
+
+ StringBuilder res = new StringBuilder();
+ res.append("Project:").append(TAB).append(projectFullName).append(LINE_RETURN);
+ res.append("Background task:").append(TAB).append(task.getUuid()).append(LINE_RETURN);
+ res.append("Submission time:").append(TAB).append(formatDateTime(task.getCreatedAt())).append(LINE_RETURN);
+ res.append("Failure time:").append(TAB).append(formatDateTime(task.getFailedAt())).append(LINE_RETURN);
+
+ String errorMessage = taskFailureNotification.getErrorMessage();
+ if (errorMessage != null) {
+ res.append(LINE_RETURN);
+ res.append("Error message:").append(TAB).append(errorMessage).append(LINE_RETURN);
+ }
+
+ res.append(LINE_RETURN);
+ res.append("More details at: ").append(String.format("%s/project/background_tasks?id=%s", settings.getServerBaseURL(), encode(project.getKey())));
+
+ return res.toString();
+ }
+
+ private static String encode(String toEncode) {
+ try {
+ return URLEncoder.encode(toEncode, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new IllegalStateException("Encoding not supported", e);
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.ce.notification;
+
+import org.sonar.core.platform.Module;
+
+public class ReportAnalysisFailureNotificationModule extends Module {
+ @Override
+ protected void configureModule() {
+ add(
+ ReportAnalysisFailureNotificationDispatcher.class,
+ ReportAnalysisFailureNotificationDispatcher.newMetadata(),
+ ReportAnalysisFailureNotificationSerializerImpl.class,
+ ReportAnalysisFailureNotificationEmailTemplate.class);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.ce.notification;
+
+import org.sonar.api.notifications.Notification;
+
+public interface ReportAnalysisFailureNotificationSerializer {
+ Notification toNotification(ReportAnalysisFailureNotification reportAnalysisFailureNotification);
+
+ ReportAnalysisFailureNotification fromNotification(Notification notification);
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.ce.notification;
+
+import org.sonar.api.notifications.Notification;
+
+import static java.lang.String.valueOf;
+
+public class ReportAnalysisFailureNotificationSerializerImpl implements ReportAnalysisFailureNotificationSerializer {
+ private static final String FIELD_PROJECT_UUID = "project.uuid";
+ private static final String FIELD_PROJECT_KEY = "project.key";
+ private static final String FIELD_PROJECT_NAME = "project.name";
+ private static final String FIELD_PROJECT_BRANCH = "project.branchName";
+ private static final String FIELD_TASK_UUID = "task.uuid";
+ private static final String FIELD_TASK_CREATED_AT = "task.createdAt";
+ private static final String FIELD_TASK_FAILED_AT = "task.failedAt";
+ private static final String FIELD_ERROR_MESSAGE = "error.message";
+
+ @Override
+ public Notification toNotification(ReportAnalysisFailureNotification reportAnalysisFailureNotification) {
+ return new Notification(ReportAnalysisFailureNotification.TYPE)
+ .setFieldValue(FIELD_PROJECT_UUID, reportAnalysisFailureNotification.getProject().getUuid())
+ .setFieldValue(FIELD_PROJECT_KEY, reportAnalysisFailureNotification.getProject().getKey())
+ .setFieldValue(FIELD_PROJECT_NAME, reportAnalysisFailureNotification.getProject().getName())
+ .setFieldValue(FIELD_PROJECT_BRANCH, reportAnalysisFailureNotification.getProject().getBranchName())
+ .setFieldValue(FIELD_TASK_UUID, reportAnalysisFailureNotification.getTask().getUuid())
+ .setFieldValue(FIELD_TASK_CREATED_AT, valueOf(reportAnalysisFailureNotification.getTask().getCreatedAt()))
+ .setFieldValue(FIELD_TASK_FAILED_AT, valueOf(reportAnalysisFailureNotification.getTask().getFailedAt()))
+ .setFieldValue(FIELD_ERROR_MESSAGE, reportAnalysisFailureNotification.getErrorMessage());
+ }
+
+ @Override
+ public ReportAnalysisFailureNotification fromNotification(Notification notification) {
+ return new ReportAnalysisFailureNotification(
+ new ReportAnalysisFailureNotification.Project(
+ notification.getFieldValue(FIELD_PROJECT_UUID),
+ notification.getFieldValue(FIELD_PROJECT_KEY),
+ notification.getFieldValue(FIELD_PROJECT_NAME),
+ notification.getFieldValue(FIELD_PROJECT_BRANCH)),
+ new ReportAnalysisFailureNotification.Task(
+ notification.getFieldValue(FIELD_TASK_UUID),
+ Long.valueOf(notification.getFieldValue(FIELD_TASK_CREATED_AT)),
+ Long.valueOf(notification.getFieldValue(FIELD_TASK_FAILED_AT))),
+ notification.getFieldValue(FIELD_ERROR_MESSAGE));
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.ce.task.projectanalysis;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.ce.taskprocessor;
-
-import java.util.Set;
-import javax.annotation.CheckForNull;
-import org.sonar.api.ce.ComputeEngineSide;
-import org.sonar.api.server.ServerSide;
-import org.sonar.ce.task.CeTask;
-import org.sonar.ce.task.CeTaskResult;
-
-/**
- * This interface is used to provide the processing code for {@link CeTask}s of one or more type to be called by the
- * Compute Engine.
- */
-@ComputeEngineSide
-@ServerSide
-public interface CeTaskProcessor {
-
- /**
- * The {@link CeTask#getType()} for which this {@link CeTaskProcessor} provides the processing code.
- * <p>
- * The match of type is done using {@link String#equals(Object)} and if more than one {@link CeTaskProcessor} declares
- * itself had handler for the same {@link CeTask#getType()}, an error will be raised at startup and startup will
- * fail.
- * </p>
- * <p>
- * If an empty {@link Set} is returned, the {@link CeTaskProcessor} will be ignored.
- * </p>
- */
- Set<String> getHandledCeTaskTypes();
-
- /**
- * Calls the processing code for a specific {@link CeTask} which will optionally return a {@link CeTaskResult}
- * holding information to be persisted in the processing history of the Compute Engine (currently the {@code CE_ACTIVITY} table).
- * <p>
- * The specified is guaranteed to be non {@code null} and its {@link CeTask#getType()} to be one of the values
- * of {@link #getHandledCeTaskTypes()}.
- * </p>
- *
- * @throws RuntimeException when thrown, it will be caught and logged by the Compute Engine and the processing of the
- * specified {@link CeTask} will be flagged as failed.
- */
- @CheckForNull
- CeTaskResult process(CeTask task);
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.ce.taskprocessor;
-
-import java.util.Collections;
-import java.util.Set;
-import org.sonar.ce.task.CeTask;
-import org.sonar.ce.task.CeTaskResult;
-import org.sonar.db.ce.CeTaskTypes;
-
-/**
- * CeTaskProcessor without any real implementation used to declare the CeTask type to the WebServer only.
- */
-public class ReportTaskProcessorDeclaration implements CeTaskProcessor {
-
- private static final Set<String> HANDLED_TYPES = Collections.singleton(CeTaskTypes.REPORT);
-
- @Override
- public Set<String> getHandledCeTaskTypes() {
- return HANDLED_TYPES;
- }
-
- @Override
- public CeTaskResult process(CeTask task) {
- throw new UnsupportedOperationException("process must not be called in WebServer");
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.
- */
-@ParametersAreNonnullByDefault
-package org.sonar.ce.taskprocessor;
-
-import javax.annotation.ParametersAreNonnullByDefault;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.app;
-
-import ch.qos.logback.classic.Level;
-import ch.qos.logback.classic.Logger;
-import ch.qos.logback.classic.LoggerContext;
-import ch.qos.logback.classic.spi.ILoggingEvent;
-import ch.qos.logback.core.ConsoleAppender;
-import com.google.common.collect.ImmutableSet;
-import java.util.Set;
-import org.sonar.process.ProcessId;
-import org.sonar.process.Props;
-import org.sonar.process.logging.LogLevelConfig;
-import org.sonar.process.logging.LogbackHelper;
-import org.sonar.process.logging.RootLoggerConfig;
-
-import static org.sonar.process.logging.RootLoggerConfig.newRootLoggerConfigBuilder;
-
-public abstract class ServerProcessLogging {
-
- public static final String STARTUP_LOGGER_NAME = "startup";
- protected static final Set<String> JMX_RMI_LOGGER_NAMES = ImmutableSet.of(
- "javax.management.remote.timeout",
- "javax.management.remote.misc",
- "javax.management.remote.rmi",
- "javax.management.mbeanserver",
- "sun.rmi.loader",
- "sun.rmi.transport.tcp",
- "sun.rmi.transport.misc",
- "sun.rmi.server.call",
- "sun.rmi.dgc");
- protected static final Set<String> LOGGER_NAMES_TO_TURN_OFF = ImmutableSet.of(
- // mssql driver
- "com.microsoft.sqlserver.jdbc.internals",
- "com.microsoft.sqlserver.jdbc.ResultSet",
- "com.microsoft.sqlserver.jdbc.Statement",
- "com.microsoft.sqlserver.jdbc.Connection");
- private final ProcessId processId;
- private final String threadIdFieldPattern;
- private final LogbackHelper helper = new LogbackHelper();
- private final LogLevelConfig logLevelConfig;
-
- protected ServerProcessLogging(ProcessId processId, String threadIdFieldPattern) {
- this.processId = processId;
- this.threadIdFieldPattern = threadIdFieldPattern;
- this.logLevelConfig = createLogLevelConfiguration(processId);
- }
-
- private LogLevelConfig createLogLevelConfiguration(ProcessId processId) {
- LogLevelConfig.Builder builder = LogLevelConfig.newBuilder(helper.getRootLoggerName());
- builder.rootLevelFor(processId);
- builder.immutableLevel("org.apache.ibatis", Level.WARN);
- builder.immutableLevel("java.sql", Level.WARN);
- builder.immutableLevel("java.sql.ResultSet", Level.WARN);
- builder.immutableLevel("org.sonar.MEASURE_FILTER", Level.WARN);
- builder.immutableLevel("org.elasticsearch", Level.INFO);
- builder.immutableLevel("org.elasticsearch.node", Level.INFO);
- builder.immutableLevel("org.elasticsearch.http", Level.INFO);
- builder.immutableLevel("ch.qos.logback", Level.WARN);
- builder.immutableLevel("org.apache.catalina", Level.INFO);
- builder.immutableLevel("org.apache.coyote", Level.INFO);
- builder.immutableLevel("org.apache.jasper", Level.INFO);
- builder.immutableLevel("org.apache.tomcat", Level.INFO);
- builder.immutableLevel("org.postgresql.core.v3.QueryExecutorImpl", Level.INFO);
- builder.immutableLevel("org.postgresql.jdbc.PgConnection", Level.INFO);
-
- extendLogLevelConfiguration(builder);
-
- return builder.build();
- }
-
- public LoggerContext configure(Props props) {
- LoggerContext ctx = helper.getRootContext();
- ctx.reset();
-
- configureRootLogger(props);
- helper.apply(logLevelConfig, props);
- configureDirectToConsoleLoggers(ctx, STARTUP_LOGGER_NAME);
- extendConfigure();
-
- helper.enableJulChangePropagation(ctx);
-
- return ctx;
- }
-
- public LogLevelConfig getLogLevelConfig() {
- return this.logLevelConfig;
- }
-
- protected abstract void extendLogLevelConfiguration(LogLevelConfig.Builder logLevelConfigBuilder);
-
- protected abstract void extendConfigure();
-
- private void configureRootLogger(Props props) {
- RootLoggerConfig config = newRootLoggerConfigBuilder()
- .setProcessId(processId)
- .setThreadIdFieldPattern(threadIdFieldPattern)
- .build();
- String logPattern = helper.buildLogPattern(config);
-
- helper.configureGlobalFileLog(props, config, logPattern);
- helper.configureForSubprocessGobbler(props, logPattern);
- }
-
- /**
- * Setup one or more specified loggers to be non additive and to print to System.out which will be caught by the Main
- * Process and written to sonar.log.
- */
- private void configureDirectToConsoleLoggers(LoggerContext context, String... loggerNames) {
- RootLoggerConfig config = newRootLoggerConfigBuilder()
- .setProcessId(ProcessId.APP)
- .setThreadIdFieldPattern("")
- .build();
- String logPattern = helper.buildLogPattern(config);
- ConsoleAppender<ILoggingEvent> consoleAppender = helper.newConsoleAppender(context, "CONSOLE", logPattern);
-
- for (String loggerName : loggerNames) {
- Logger consoleLogger = context.getLogger(loggerName);
- consoleLogger.setAdditive(false);
- consoleLogger.addAppender(consoleAppender);
- }
- }
-
-}
import org.sonar.process.ProcessId;
import org.sonar.process.logging.LogDomain;
import org.sonar.process.logging.LogLevelConfig;
+import org.sonar.server.log.ServerProcessLogging;
import static org.sonar.server.platform.web.requestid.RequestIdMDCStorage.HTTP_REQUEST_ID_MDC_KEY;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.ce;
+
+import org.sonar.ce.http.CeHttpClientImpl;
+import org.sonar.ce.queue.CeQueueImpl;
+import org.sonar.ce.task.log.CeTaskLogging;
+import org.sonar.ce.task.projectanalysis.ReportTaskProcessorDeclaration;
+import org.sonar.core.platform.Module;
+import org.sonar.server.ce.queue.ReportSubmitter;
+
+public class CeModule extends Module {
+ @Override
+ protected void configureModule() {
+ add(CeTaskLogging.class,
+ CeHttpClientImpl.class,
+
+ // Queue
+ CeQueueImpl.class,
+ ReportSubmitter.class,
+
+ // Core tasks processors
+ ReportTaskProcessorDeclaration.class);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.ce;
+
+import javax.annotation.ParametersAreNonnullByDefault;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.server.ws.WebService.Param;
import org.sonar.api.web.UserRole;
-import org.sonar.ce.taskprocessor.CeTaskProcessor;
+import org.sonar.ce.task.taskprocessor.CeTaskProcessor;
import org.sonar.core.util.Uuids;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
-import org.sonar.ce.taskprocessor.CeTaskProcessor;
+import org.sonar.ce.task.taskprocessor.CeTaskProcessor;
import org.sonarqube.ws.Ce;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.notification;
-
-import com.google.common.collect.Multimap;
-import java.util.Objects;
-import org.sonar.api.notifications.Notification;
-import org.sonar.api.notifications.NotificationChannel;
-import org.sonar.api.web.UserRole;
-
-import static java.util.Objects.requireNonNull;
-
-/**
- * The notification manager receives notifications and is in charge of storing them so that they are processed by the notification service.
- * <p>
- * Pico provides an instance of this class, and plugins just need to create notifications and pass them to this manager with
- * the {@link NotificationManager#scheduleForSending(Notification)} method.
- * </p>
- */
-public interface NotificationManager {
-
- /**
- * Receives a notification and stores it so that it is processed by the notification service.
- *
- * @param notification the notification.
- */
- void scheduleForSending(Notification notification);
-
- /**
- * <p>
- * Returns the list of users who subscribed to the given dispatcher, along with the notification channels (email, twitter, ...) that they choose
- * for this dispatcher.
- * </p>
- * <p>
- * The resource ID can be null in case of notifications that have nothing to do with a specific project (like system notifications).
- * </p>
- *
- * @param dispatcher the dispatcher for which this list of users is requested
- * @param projectUuid UUID of the project
- * @param subscriberPermissionsOnProject the required permission for global and project subscribers
- *
- * @return the list of user login along with the subscribed channels
- */
- Multimap<String, NotificationChannel> findSubscribedRecipientsForDispatcher(NotificationDispatcher dispatcher, String projectUuid,
- SubscriberPermissionsOnProject subscriberPermissionsOnProject);
-
- final class SubscriberPermissionsOnProject {
- public static final SubscriberPermissionsOnProject ALL_MUST_HAVE_ROLE_USER = new SubscriberPermissionsOnProject(UserRole.USER);
-
- private final String globalSubscribers;
- private final String projectSubscribers;
-
- public SubscriberPermissionsOnProject(String globalAndProjectSubscribers) {
- this(globalAndProjectSubscribers, globalAndProjectSubscribers);
- }
-
- public SubscriberPermissionsOnProject(String globalSubscribers, String projectSubscribers) {
- this.globalSubscribers = requireNonNull(globalSubscribers, "global subscribers's required permission can't be null");
- this.projectSubscribers = requireNonNull(projectSubscribers, "project subscribers's required permission can't be null");
- }
-
- public String getGlobalSubscribers() {
- return globalSubscribers;
- }
-
- public String getProjectSubscribers() {
- return projectSubscribers;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- SubscriberPermissionsOnProject that = (SubscriberPermissionsOnProject) o;
- return globalSubscribers.equals(that.globalSubscribers) && projectSubscribers.equals(that.projectSubscribers);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(globalSubscribers, projectSubscribers);
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.organization;
-
-import static java.util.Objects.requireNonNull;
-
-public class DefaultOrganization {
- private final String uuid;
- private final String key;
- private final String name;
- private final long createdAt;
- private final long updatedAt;
-
- private DefaultOrganization(Builder builder) {
- this.uuid = requireNonNull(builder.uuid, "uuid can't be null");
- this.key = requireNonNull(builder.key, "key can't be null");
- this.name = requireNonNull(builder.name, "name can't be null");
- this.createdAt = requireNonNull(builder.createdAt, "createdAt can't be null");
- this.updatedAt = requireNonNull(builder.updatedAt, "updatedAt can't be null");
- }
-
- public static Builder newBuilder() {
- return new Builder();
- }
-
- public String getUuid() {
- return uuid;
- }
-
- public String getKey() {
- return key;
- }
-
- public String getName() {
- return name;
- }
-
- public long getCreatedAt() {
- return createdAt;
- }
-
- public long getUpdatedAt() {
- return updatedAt;
- }
-
- @Override
- public String toString() {
- return "DefaultOrganization{" +
- "uuid='" + uuid + '\'' +
- ", key='" + key + '\'' +
- ", name='" + name + '\'' +
- ", createdAt=" + createdAt +
- ", updatedAt=" + updatedAt +
- '}';
- }
-
- public static final class Builder {
- private String uuid;
- private String key;
- private String name;
- private Long createdAt;
- private Long updatedAt;
-
- public Builder setUuid(String uuid) {
- this.uuid = uuid;
- return this;
- }
-
- public Builder setKey(String key) {
- this.key = key;
- return this;
- }
-
- public Builder setName(String name) {
- this.name = name;
- return this;
- }
-
- public Builder setCreatedAt(long createdAt) {
- this.createdAt = createdAt;
- return this;
- }
-
- public Builder setUpdatedAt(long updatedAt) {
- this.updatedAt = updatedAt;
- return this;
- }
-
- public DefaultOrganization build() {
- return new DefaultOrganization(this);
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.organization;
-
-public interface DefaultOrganizationCache {
-
- /**
- * Loads {@link DefaultOrganization} in cache.
- */
- void load();
-
- /**
- * Unloads {@link DefaultOrganization} from cache.
- */
- void unload();
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.organization;
-
-public interface DefaultOrganizationProvider {
- /**
- * @throws IllegalStateException if there is no default organization
- */
- DefaultOrganization get();
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.organization;
-
-import java.util.Optional;
-import java.util.function.Supplier;
-import javax.annotation.CheckForNull;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.organization.OrganizationDto;
-import org.sonar.server.property.InternalProperties;
-
-import static com.google.common.base.Preconditions.checkState;
-
-public class DefaultOrganizationProviderImpl implements DefaultOrganizationProvider, DefaultOrganizationCache {
- private static final ThreadLocal<Cache> CACHE = new ThreadLocal<>();
-
- private final DbClient dbClient;
-
- public DefaultOrganizationProviderImpl(DbClient dbClient) {
- this.dbClient = dbClient;
- }
-
- @Override
- public DefaultOrganization get() {
- Cache cache = CACHE.get();
- if (cache != null) {
- return cache.get(() -> getDefaultOrganization(dbClient));
- }
-
- return getDefaultOrganization(dbClient);
- }
-
- private static DefaultOrganization getDefaultOrganization(DbClient dbClient) {
- try (DbSession dbSession = dbClient.openSession(false)) {
- Optional<String> uuid = dbClient.internalPropertiesDao().selectByKey(dbSession, InternalProperties.DEFAULT_ORGANIZATION);
- checkState(uuid.isPresent() && !uuid.get().isEmpty(), "No Default organization uuid configured");
- Optional<OrganizationDto> dto = dbClient.organizationDao().selectByUuid(dbSession, uuid.get());
- checkState(dto.isPresent(), "Default organization with uuid '%s' does not exist", uuid.get());
- return toDefaultOrganization(dto.get());
- }
- }
-
- private static DefaultOrganization toDefaultOrganization(OrganizationDto organizationDto) {
- return DefaultOrganization.newBuilder()
- .setUuid(organizationDto.getUuid())
- .setKey(organizationDto.getKey())
- .setName(organizationDto.getName())
- .setCreatedAt(organizationDto.getCreatedAt())
- .setUpdatedAt(organizationDto.getUpdatedAt())
- .build();
- }
-
- @Override
- public void load() {
- checkState(
- CACHE.get() == null,
- "load called twice for thread '%s' or state wasn't cleared last time it was used",
- Thread.currentThread().getName());
- CACHE.set(new Cache());
- }
-
- @Override
- public void unload() {
- CACHE.remove();
- }
-
- private static final class Cache {
- @CheckForNull
- private DefaultOrganization defaultOrganization;
-
- public DefaultOrganization get(Supplier<DefaultOrganization> supplier) {
- if (defaultOrganization == null) {
- defaultOrganization = supplier.get();
- }
- return defaultOrganization;
- }
-
- }
-}
import org.sonar.process.ProcessProperties;
import org.sonar.server.platform.db.migration.version.DatabaseVersion;
-import static org.sonar.server.app.ServerProcessLogging.STARTUP_LOGGER_NAME;
+import static org.sonar.server.log.ServerProcessLogging.STARTUP_LOGGER_NAME;
public class DatabaseServerCompatibility implements Startable {
import org.sonar.api.utils.log.Loggers;
import org.sonar.db.Database;
import org.sonar.process.logging.LogbackHelper;
-import org.sonar.server.app.ServerProcessLogging;
+import org.sonar.server.log.ServerProcessLogging;
import static org.sonar.api.utils.log.LoggerLevel.TRACE;
import static org.sonar.process.ProcessProperties.Property.PATH_LOGS;
import org.sonar.api.rules.AnnotationRuleParser;
import org.sonar.api.rules.XMLRuleParser;
import org.sonar.api.server.rule.RulesDefinitionXmlLoader;
-import org.sonar.ce.CeModule;
+import org.sonar.server.ce.CeModule;
import org.sonar.ce.notification.ReportAnalysisFailureNotificationModule;
import org.sonar.core.component.DefaultResourceTypes;
import org.sonar.core.extension.CoreExtensionsInstaller;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.property;
-
-import java.util.Optional;
-import javax.annotation.Nullable;
-
-/**
- * Allows to read and write internal properties.
- */
-public interface InternalProperties {
- /**
- * The UUID of the default organization.
- * Can't be null unless SQ is strongly corrupted.
- */
- String DEFAULT_ORGANIZATION = "organization.default";
-
- String ORGANIZATION_ENABLED = "organization.enabled";
-
- String SERVER_ID_CHECKSUM = "server.idChecksum";
-
- /**
- * Compute Engine is pausing/paused if property value is "true".
- */
- String COMPUTE_ENGINE_PAUSE = "ce.pause";
-
- String BITBUCKETCLOUD_APP_SHAREDSECRET = "bbc.app.sharedSecret";
- /**
- * Read the value of the specified property.
- *
- * @return {@link Optional#empty()} if the property does not exist, an empty string if the property is empty,
- * otherwise the value of the property as a String.
- *
- * @throws IllegalArgumentException if {@code propertyKey} is {@code null} or empty
- */
- Optional<String> read(String propertyKey);
-
- /**
- * Write the value of the specified property.
- * <p>
- * {@code null} and empty string are valid values which will persist the specified property as empty.
- * </p>
- *
- * @throws IllegalArgumentException if {@code propertyKey} is {@code null} or empty
- */
- void write(String propertyKey, @Nullable String value);
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.property;
-
-import java.util.Optional;
-import javax.annotation.Nullable;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-/**
- * A cache-less implementation of {@link InternalProperties} reading and writing to DB table INTERNAL_PROPERTIES.
- */
-public class InternalPropertiesImpl implements InternalProperties {
- private final DbClient dbClient;
-
- public InternalPropertiesImpl(DbClient dbClient) {
- this.dbClient = dbClient;
- }
-
- @Override
- public Optional<String> read(String propertyKey) {
- checkPropertyKey(propertyKey);
-
- try (DbSession dbSession = dbClient.openSession(false)) {
- return dbClient.internalPropertiesDao().selectByKey(dbSession, propertyKey);
- }
- }
-
- @Override
- public void write(String propertyKey, @Nullable String value) {
- checkPropertyKey(propertyKey);
-
- try (DbSession dbSession = dbClient.openSession(false)) {
- if (value == null || value.isEmpty()) {
- dbClient.internalPropertiesDao().saveAsEmpty(dbSession, propertyKey);
- } else {
- dbClient.internalPropertiesDao().save(dbSession, propertyKey, value);
- }
- dbSession.commit();
- }
- }
-
- private static void checkPropertyKey(@Nullable String propertyKey) {
- checkArgument(propertyKey != null && !propertyKey.isEmpty(), "property key can't be null nor empty");
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.property;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-import javax.annotation.Nullable;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-/**
- * Map based implementation of {@link InternalProperties} to be used for unit testing.
- */
-public class MapInternalProperties implements InternalProperties {
- private final Map<String, String> values = new HashMap<>(1);
-
- @Override
- public Optional<String> read(String propertyKey) {
- checkPropertyKey(propertyKey);
- return Optional.ofNullable(values.get(propertyKey));
- }
-
- @Override
- public void write(String propertyKey, @Nullable String value) {
- checkPropertyKey(propertyKey);
- values.put(propertyKey, value);
- }
-
- private static void checkPropertyKey(@Nullable String propertyKey) {
- checkArgument(propertyKey != null && !propertyKey.isEmpty(), "property key can't be null nor empty");
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.
- */
-@ParametersAreNonnullByDefault
-package org.sonar.server.property;
-
-import javax.annotation.ParametersAreNonnullByDefault;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.ce.log;
-
-import ch.qos.logback.classic.Level;
-import ch.qos.logback.classic.Logger;
-import ch.qos.logback.classic.LoggerContext;
-import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
-import ch.qos.logback.classic.spi.ILoggingEvent;
-import ch.qos.logback.core.Appender;
-import ch.qos.logback.core.ConsoleAppender;
-import ch.qos.logback.core.FileAppender;
-import ch.qos.logback.core.joran.spi.JoranException;
-import java.io.File;
-import java.io.IOException;
-import java.util.Properties;
-import java.util.stream.Stream;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.process.Props;
-import org.sonar.process.logging.LogbackHelper;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.slf4j.Logger.ROOT_LOGGER_NAME;
-import static org.sonar.process.ProcessProperties.Property.PATH_LOGS;
-
-public class CeProcessLoggingTest {
-
- @Rule
- public TemporaryFolder temp = new TemporaryFolder();
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private File logDir;
- private Props props = new Props(new Properties());
- private CeProcessLogging underTest = new CeProcessLogging();
-
- @Before
- public void setUp() throws IOException {
- logDir = temp.newFolder();
- props.set(PATH_LOGS.getKey(), logDir.getAbsolutePath());
- }
-
- @AfterClass
- public static void resetLogback() throws JoranException {
- new LogbackHelper().resetFromXml("/logback-test.xml");
- }
-
- @Test
- public void do_not_log_to_console() {
- LoggerContext ctx = underTest.configure(props);
-
- Logger root = ctx.getLogger(Logger.ROOT_LOGGER_NAME);
- Appender appender = root.getAppender("CONSOLE");
- assertThat(appender).isNull();
- }
-
- @Test
- public void startup_logger_prints_to_only_to_system_out() {
- LoggerContext ctx = underTest.configure(props);
-
- Logger startup = ctx.getLogger("startup");
- assertThat(startup.isAdditive()).isFalse();
- Appender appender = startup.getAppender("CONSOLE");
- assertThat(appender).isInstanceOf(ConsoleAppender.class);
- ConsoleAppender<ILoggingEvent> consoleAppender = (ConsoleAppender<ILoggingEvent>) appender;
- assertThat(consoleAppender.getTarget()).isEqualTo("System.out");
- assertThat(consoleAppender.getEncoder()).isInstanceOf(PatternLayoutEncoder.class);
- PatternLayoutEncoder patternEncoder = (PatternLayoutEncoder) consoleAppender.getEncoder();
- assertThat(patternEncoder.getPattern()).isEqualTo("%d{yyyy.MM.dd HH:mm:ss} %-5level app[][%logger{20}] %msg%n");
- }
-
- @Test
- public void log_to_ce_file() {
- LoggerContext ctx = underTest.configure(props);
-
- Logger root = ctx.getLogger(Logger.ROOT_LOGGER_NAME);
- Appender<ILoggingEvent> appender = root.getAppender("file_ce");
- assertThat(appender).isInstanceOf(FileAppender.class);
- FileAppender fileAppender = (FileAppender) appender;
- assertThat(fileAppender.getFile()).isEqualTo(new File(logDir, "ce.log").getAbsolutePath());
- assertThat(fileAppender.getEncoder()).isInstanceOf(PatternLayoutEncoder.class);
- PatternLayoutEncoder encoder = (PatternLayoutEncoder) fileAppender.getEncoder();
- assertThat(encoder.getPattern()).isEqualTo("%d{yyyy.MM.dd HH:mm:ss} %-5level ce[%X{ceTaskUuid}][%logger{20}] %msg%n");
- }
-
- @Test
- public void default_level_for_root_logger_is_INFO() {
- LoggerContext ctx = underTest.configure(props);
-
- verifyRootLogLevel(ctx, Level.INFO);
- }
-
- @Test
- public void root_logger_level_changes_with_global_property() {
- props.set("sonar.log.level", "TRACE");
-
- LoggerContext ctx = underTest.configure(props);
-
- verifyRootLogLevel(ctx, Level.TRACE);
- }
-
- @Test
- public void root_logger_level_changes_with_ce_property() {
- props.set("sonar.log.level.ce", "TRACE");
-
- LoggerContext ctx = underTest.configure(props);
-
- verifyRootLogLevel(ctx, Level.TRACE);
- }
-
- @Test
- public void root_logger_level_is_configured_from_ce_property_over_global_property() {
- props.set("sonar.log.level", "TRACE");
- props.set("sonar.log.level.ce", "DEBUG");
-
- LoggerContext ctx = underTest.configure(props);
-
- verifyRootLogLevel(ctx, Level.DEBUG);
- }
-
- @Test
- public void root_logger_level_changes_with_ce_property_and_is_case_insensitive() {
- props.set("sonar.log.level.ce", "debug");
-
- LoggerContext ctx = underTest.configure(props);
-
- verifyRootLogLevel(ctx, Level.DEBUG);
- }
-
- @Test
- public void sql_logger_level_changes_with_global_property_and_is_case_insensitive() {
- props.set("sonar.log.level", "InFO");
-
- LoggerContext ctx = underTest.configure(props);
-
- verifySqlLogLevel(ctx, Level.INFO);
- }
-
- @Test
- public void sql_logger_level_changes_with_ce_property_and_is_case_insensitive() {
- props.set("sonar.log.level.ce", "TrACe");
-
- LoggerContext ctx = underTest.configure(props);
-
- verifySqlLogLevel(ctx, Level.TRACE);
- }
-
- @Test
- public void sql_logger_level_changes_with_ce_sql_property_and_is_case_insensitive() {
- props.set("sonar.log.level.ce.sql", "debug");
-
- LoggerContext ctx = underTest.configure(props);
-
- verifySqlLogLevel(ctx, Level.DEBUG);
- }
-
- @Test
- public void sql_logger_level_is_configured_from_ce_sql_property_over_ce_property() {
- props.set("sonar.log.level.ce.sql", "debug");
- props.set("sonar.log.level.ce", "TRACE");
-
- LoggerContext ctx = underTest.configure(props);
-
- verifySqlLogLevel(ctx, Level.DEBUG);
- }
-
- @Test
- public void sql_logger_level_is_configured_from_ce_sql_property_over_global_property() {
- props.set("sonar.log.level.ce.sql", "debug");
- props.set("sonar.log.level", "TRACE");
-
- LoggerContext ctx = underTest.configure(props);
-
- verifySqlLogLevel(ctx, Level.DEBUG);
- }
-
- @Test
- public void sql_logger_level_is_configured_from_ce_property_over_global_property() {
- props.set("sonar.log.level.ce", "debug");
- props.set("sonar.log.level", "TRACE");
-
- LoggerContext ctx = underTest.configure(props);
-
- verifySqlLogLevel(ctx, Level.DEBUG);
- }
-
- @Test
- public void es_logger_level_changes_with_global_property_and_is_case_insensitive() {
- props.set("sonar.log.level", "InFO");
-
- LoggerContext ctx = underTest.configure(props);
-
- verifyEsLogLevel(ctx, Level.INFO);
- }
-
- @Test
- public void es_logger_level_changes_with_ce_property_and_is_case_insensitive() {
- props.set("sonar.log.level.ce", "TrACe");
-
- LoggerContext ctx = underTest.configure(props);
-
- verifyEsLogLevel(ctx, Level.TRACE);
- }
-
- @Test
- public void es_logger_level_changes_with_ce_es_property_and_is_case_insensitive() {
- props.set("sonar.log.level.ce.es", "debug");
-
- LoggerContext ctx = underTest.configure(props);
-
- verifyEsLogLevel(ctx, Level.DEBUG);
- }
-
- @Test
- public void es_logger_level_is_configured_from_ce_es_property_over_ce_property() {
- props.set("sonar.log.level.ce.es", "debug");
- props.set("sonar.log.level.ce", "TRACE");
-
- LoggerContext ctx = underTest.configure(props);
-
- verifyEsLogLevel(ctx, Level.DEBUG);
- }
-
- @Test
- public void es_logger_level_is_configured_from_ce_es_property_over_global_property() {
- props.set("sonar.log.level.ce.es", "debug");
- props.set("sonar.log.level", "TRACE");
-
- LoggerContext ctx = underTest.configure(props);
-
- verifyEsLogLevel(ctx, Level.DEBUG);
- }
-
- @Test
- public void es_logger_level_is_configured_from_ce_property_over_global_property() {
- props.set("sonar.log.level.ce", "debug");
- props.set("sonar.log.level", "TRACE");
-
- LoggerContext ctx = underTest.configure(props);
-
- verifyEsLogLevel(ctx, Level.DEBUG);
- }
-
- @Test
- public void jmx_logger_level_changes_with_global_property_and_is_case_insensitive() {
- props.set("sonar.log.level", "InFO");
-
- LoggerContext ctx = underTest.configure(props);
-
- verifyJmxLogLevel(ctx, Level.INFO);
- }
-
- @Test
- public void jmx_logger_level_changes_with_jmx_property_and_is_case_insensitive() {
- props.set("sonar.log.level.ce", "TrACe");
-
- LoggerContext ctx = underTest.configure(props);
-
- verifyJmxLogLevel(ctx, Level.TRACE);
- }
-
- @Test
- public void jmx_logger_level_changes_with_ce_jmx_property_and_is_case_insensitive() {
- props.set("sonar.log.level.ce.jmx", "debug");
-
- LoggerContext ctx = underTest.configure(props);
-
- verifyJmxLogLevel(ctx, Level.DEBUG);
- }
-
- @Test
- public void jmx_logger_level_is_configured_from_ce_jmx_property_over_ce_property() {
- props.set("sonar.log.level.ce.jmx", "debug");
- props.set("sonar.log.level.ce", "TRACE");
-
- LoggerContext ctx = underTest.configure(props);
-
- verifyJmxLogLevel(ctx, Level.DEBUG);
- }
-
- @Test
- public void jmx_logger_level_is_configured_from_ce_jmx_property_over_global_property() {
- props.set("sonar.log.level.ce.jmx", "debug");
- props.set("sonar.log.level", "TRACE");
-
- LoggerContext ctx = underTest.configure(props);
-
- verifyJmxLogLevel(ctx, Level.DEBUG);
- }
-
- @Test
- public void jmx_logger_level_is_configured_from_ce_property_over_global_property() {
- props.set("sonar.log.level.ce", "debug");
- props.set("sonar.log.level", "TRACE");
-
- LoggerContext ctx = underTest.configure(props);
-
- verifyJmxLogLevel(ctx, Level.DEBUG);
- }
-
- @Test
- public void root_logger_level_defaults_to_INFO_if_ce_property_has_invalid_value() {
- props.set("sonar.log.level.ce", "DodoDouh!");
-
- LoggerContext ctx = underTest.configure(props);
- verifyRootLogLevel(ctx, Level.INFO);
- }
-
- @Test
- public void sql_logger_level_defaults_to_INFO_if_ce_sql_property_has_invalid_value() {
- props.set("sonar.log.level.ce.sql", "DodoDouh!");
-
- LoggerContext ctx = underTest.configure(props);
- verifySqlLogLevel(ctx, Level.INFO);
- }
-
- @Test
- public void es_logger_level_defaults_to_INFO_if_ce_es_property_has_invalid_value() {
- props.set("sonar.log.level.ce.es", "DodoDouh!");
-
- LoggerContext ctx = underTest.configure(props);
- verifyEsLogLevel(ctx, Level.INFO);
- }
-
- @Test
- public void jmx_loggers_level_defaults_to_INFO_if_ce_jmx_property_has_invalid_value() {
- props.set("sonar.log.level.ce.jmx", "DodoDouh!");
-
- LoggerContext ctx = underTest.configure(props);
- verifyJmxLogLevel(ctx, Level.INFO);
- }
-
- @Test
- public void fail_with_IAE_if_global_property_unsupported_level() {
- props.set("sonar.log.level", "ERROR");
-
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("log level ERROR in property sonar.log.level is not a supported value (allowed levels are [TRACE, DEBUG, INFO])");
-
- underTest.configure(props);
- }
-
- @Test
- public void fail_with_IAE_if_ce_property_unsupported_level() {
- props.set("sonar.log.level.ce", "ERROR");
-
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("log level ERROR in property sonar.log.level.ce is not a supported value (allowed levels are [TRACE, DEBUG, INFO])");
-
- underTest.configure(props);
- }
-
- @Test
- public void fail_with_IAE_if_ce_sql_property_unsupported_level() {
- props.set("sonar.log.level.ce.sql", "ERROR");
-
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("log level ERROR in property sonar.log.level.ce.sql is not a supported value (allowed levels are [TRACE, DEBUG, INFO])");
-
- underTest.configure(props);
- }
-
- @Test
- public void fail_with_IAE_if_ce_es_property_unsupported_level() {
- props.set("sonar.log.level.ce.es", "ERROR");
-
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("log level ERROR in property sonar.log.level.ce.es is not a supported value (allowed levels are [TRACE, DEBUG, INFO])");
-
- underTest.configure(props);
- }
-
- @Test
- public void fail_with_IAE_if_ce_jmx_property_unsupported_level() {
- props.set("sonar.log.level.ce.jmx", "ERROR");
-
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("log level ERROR in property sonar.log.level.ce.jmx is not a supported value (allowed levels are [TRACE, DEBUG, INFO])");
-
- underTest.configure(props);
- }
-
- @Test
- public void configure_defines_hardcoded_levels() {
- LoggerContext context = underTest.configure(props);
-
- verifyImmutableLogLevels(context);
- }
-
- @Test
- public void configure_defines_hardcoded_levels_unchanged_by_global_property() {
- props.set("sonar.log.level", "TRACE");
-
- LoggerContext context = underTest.configure(props);
-
- verifyImmutableLogLevels(context);
- }
-
- @Test
- public void configure_defines_hardcoded_levels_unchanged_by_ce_property() {
- props.set("sonar.log.level.ce", "TRACE");
-
- LoggerContext context = underTest.configure(props);
-
- verifyImmutableLogLevels(context);
- }
-
- @Test
- public void configure_turns_off_some_MsSQL_driver_logger() {
- LoggerContext context = underTest.configure(props);
-
- Stream.of("com.microsoft.sqlserver.jdbc.internals",
- "com.microsoft.sqlserver.jdbc.ResultSet",
- "com.microsoft.sqlserver.jdbc.Statement",
- "com.microsoft.sqlserver.jdbc.Connection")
- .forEach(loggerName -> assertThat(context.getLogger(loggerName).getLevel()).isEqualTo(Level.OFF));
- }
-
- private void verifyRootLogLevel(LoggerContext ctx, Level expected) {
- assertThat(ctx.getLogger(ROOT_LOGGER_NAME).getLevel()).isEqualTo(expected);
- }
-
- private void verifySqlLogLevel(LoggerContext ctx, Level expected) {
- assertThat(ctx.getLogger("sql").getLevel()).isEqualTo(expected);
- }
-
- private void verifyEsLogLevel(LoggerContext ctx, Level expected) {
- assertThat(ctx.getLogger("es").getLevel()).isEqualTo(expected);
- }
-
- private void verifyJmxLogLevel(LoggerContext ctx, Level expected) {
- assertThat(ctx.getLogger("javax.management.remote.timeout").getLevel()).isEqualTo(expected);
- assertThat(ctx.getLogger("javax.management.remote.misc").getLevel()).isEqualTo(expected);
- assertThat(ctx.getLogger("javax.management.remote.rmi").getLevel()).isEqualTo(expected);
- assertThat(ctx.getLogger("javax.management.mbeanserver").getLevel()).isEqualTo(expected);
- assertThat(ctx.getLogger("sun.rmi.loader").getLevel()).isEqualTo(expected);
- assertThat(ctx.getLogger("sun.rmi.transport.tcp").getLevel()).isEqualTo(expected);
- assertThat(ctx.getLogger("sun.rmi.transport.misc").getLevel()).isEqualTo(expected);
- assertThat(ctx.getLogger("sun.rmi.server.call").getLevel()).isEqualTo(expected);
- assertThat(ctx.getLogger("sun.rmi.dgc").getLevel()).isEqualTo(expected);
- }
-
- private void verifyImmutableLogLevels(LoggerContext ctx) {
- assertThat(ctx.getLogger("org.apache.ibatis").getLevel()).isEqualTo(Level.WARN);
- assertThat(ctx.getLogger("java.sql").getLevel()).isEqualTo(Level.WARN);
- assertThat(ctx.getLogger("java.sql.ResultSet").getLevel()).isEqualTo(Level.WARN);
- assertThat(ctx.getLogger("org.sonar.MEASURE_FILTER").getLevel()).isEqualTo(Level.WARN);
- assertThat(ctx.getLogger("org.elasticsearch").getLevel()).isEqualTo(Level.INFO);
- assertThat(ctx.getLogger("org.elasticsearch.node").getLevel()).isEqualTo(Level.INFO);
- assertThat(ctx.getLogger("org.elasticsearch.http").getLevel()).isEqualTo(Level.INFO);
- assertThat(ctx.getLogger("ch.qos.logback").getLevel()).isEqualTo(Level.WARN);
- assertThat(ctx.getLogger("org.apache.catalina").getLevel()).isEqualTo(Level.INFO);
- assertThat(ctx.getLogger("org.apache.coyote").getLevel()).isEqualTo(Level.INFO);
- assertThat(ctx.getLogger("org.apache.jasper").getLevel()).isEqualTo(Level.INFO);
- assertThat(ctx.getLogger("org.apache.tomcat").getLevel()).isEqualTo(Level.INFO);
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.ce.notification;
-
-import com.google.common.collect.HashMultimap;
-import org.junit.Test;
-import org.sonar.api.notifications.Notification;
-import org.sonar.api.notifications.NotificationChannel;
-import org.sonar.api.web.UserRole;
-import org.sonar.server.notification.NotificationDispatcher;
-import org.sonar.server.notification.NotificationDispatcherMetadata;
-import org.sonar.server.notification.NotificationManager;
-import org.sonar.server.notification.NotificationManager.SubscriberPermissionsOnProject;
-
-import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-public class ReportAnalysisFailureNotificationDispatcherTest {
- private NotificationManager notificationManager = mock(NotificationManager.class);
- private Notification notificationMock = mock(Notification.class);
- private NotificationDispatcher.Context contextMock = mock(NotificationDispatcher.Context.class);
- private ReportAnalysisFailureNotificationDispatcher underTest = new ReportAnalysisFailureNotificationDispatcher(notificationManager);
-
- @Test
- public void dispatcher_defines_key() {
- assertThat(underTest.getKey()).isNotEmpty();
- }
-
- @Test
- public void newMetadata_indicates_enabled_global_and_project_level_notifications() {
- NotificationDispatcherMetadata metadata = ReportAnalysisFailureNotificationDispatcher.newMetadata();
-
- assertThat(metadata.getProperty(NotificationDispatcherMetadata.GLOBAL_NOTIFICATION)).isEqualTo("true");
- assertThat(metadata.getProperty(NotificationDispatcherMetadata.PER_PROJECT_NOTIFICATION)).isEqualTo("true");
- }
-
- @Test
- public void performDispatch_has_no_effect_if_type_is_empty() {
- when(notificationMock.getType()).thenReturn("");
-
- underTest.performDispatch(notificationMock, contextMock);
-
- verify(notificationMock).getType();
- verifyNoMoreInteractions(notificationMock, contextMock);
- }
-
- @Test
- public void performDispatch_has_no_effect_if_type_is_not_ReportAnalysisFailureNotification_TYPE() {
- when(notificationMock.getType()).thenReturn(randomAlphanumeric(6));
-
- underTest.performDispatch(notificationMock, contextMock);
-
- verify(notificationMock).getType();
- verifyNoMoreInteractions(notificationMock, contextMock);
- }
-
- @Test
- public void performDispatch_adds_user_for_each_recipient_and_channel_for_the_component_uuid_in_the_notification() {
- when(notificationMock.getType()).thenReturn(ReportAnalysisFailureNotification.TYPE);
- String projectKey = randomAlphanumeric(9);
- when(notificationMock.getFieldValue("project.key")).thenReturn(projectKey);
- HashMultimap<String, NotificationChannel> multimap = HashMultimap.create();
- String login1 = randomAlphanumeric(3);
- String login2 = randomAlphanumeric(3);
- NotificationChannel channel1 = mock(NotificationChannel.class);
- NotificationChannel channel2 = mock(NotificationChannel.class);
- NotificationChannel channel3 = mock(NotificationChannel.class);
- multimap.put(login1, channel1);
- multimap.put(login1, channel2);
- multimap.put(login2, channel2);
- multimap.put(login2, channel3);
- when(notificationManager.findSubscribedRecipientsForDispatcher(underTest, projectKey, new SubscriberPermissionsOnProject(UserRole.ADMIN, UserRole.USER)))
- .thenReturn(multimap);
-
- underTest.performDispatch(notificationMock, contextMock);
-
- verify(contextMock).addUser(login1, channel1);
- verify(contextMock).addUser(login1, channel2);
- verify(contextMock).addUser(login2, channel2);
- verify(contextMock).addUser(login2, channel3);
- verifyNoMoreInteractions(contextMock);
- }
-
- @Test
- public void performDispatch_adds_no_user_if_notification_manager_returns_none() {
- when(notificationMock.getType()).thenReturn(ReportAnalysisFailureNotification.TYPE);
- String projectKey = randomAlphanumeric(9);
- when(notificationMock.getFieldValue("project.key")).thenReturn(projectKey);
- HashMultimap<String, NotificationChannel> multimap = HashMultimap.create();
- when(notificationManager.findSubscribedRecipientsForDispatcher(underTest, projectKey, new SubscriberPermissionsOnProject(UserRole.ADMIN, UserRole.USER)))
- .thenReturn(multimap);
-
- underTest.performDispatch(notificationMock, contextMock);
-
- verifyNoMoreInteractions(contextMock);
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.ce.notification;
-
-import java.util.Random;
-import org.apache.commons.lang.RandomStringUtils;
-import org.junit.Before;
-import org.junit.Test;
-import org.sonar.api.config.EmailSettings;
-import org.sonar.api.notifications.Notification;
-import org.sonar.api.utils.DateUtils;
-import org.sonar.plugins.emailnotifications.api.EmailMessage;
-
-import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class ReportAnalysisFailureNotificationEmailTemplateTest {
- private String serverUrl = RandomStringUtils.randomAlphanumeric(12);
- private Notification notification = new Notification(ReportAnalysisFailureNotification.TYPE);
- private Random random = new Random();
- private ReportAnalysisFailureNotification.Project projectNoBranch = new ReportAnalysisFailureNotification.Project(
- randomAlphanumeric(2),
- randomAlphanumeric(3),
- randomAlphanumeric(4),
- null);
- private ReportAnalysisFailureNotification.Project projectWithBranch = new ReportAnalysisFailureNotification.Project(
- randomAlphanumeric(6),
- randomAlphanumeric(7),
- randomAlphanumeric(8),
- randomAlphanumeric(9));
- private ReportAnalysisFailureNotification.Task task = new ReportAnalysisFailureNotification.Task(
- randomAlphanumeric(10),
- random.nextInt(99),
- random.nextInt(99));
- private String errorMessage = randomAlphanumeric(11);
-
- private ReportAnalysisFailureNotificationSerializer serializer = mock(ReportAnalysisFailureNotificationSerializer.class);
- private EmailSettings emailSettings = mock(EmailSettings.class);
- private ReportAnalysisFailureNotificationEmailTemplate underTest = new ReportAnalysisFailureNotificationEmailTemplate(serializer, emailSettings);
-
- @Before
- public void setUp() throws Exception {
- when(emailSettings.getServerBaseURL()).thenReturn(serverUrl);
- }
-
- @Test
- public void returns_null_if_notification_type_is_not_ReportAnalysisFailureNotification_TYPE() {
- Notification notification = new Notification(RandomStringUtils.randomAlphanumeric(5));
-
- EmailMessage emailMessage = underTest.format(notification);
-
- assertThat(emailMessage).isNull();
- }
-
- @Test
- public void format_returns_email_with_subject_without_branch() {
- when(serializer.fromNotification(notification)).thenReturn(new ReportAnalysisFailureNotification(
- projectNoBranch, task, errorMessage));
-
- EmailMessage emailMessage = underTest.format(notification);
-
- assertThat(emailMessage.getSubject()).isEqualTo(projectNoBranch.getName() + ": Background task in failure");
- }
-
- @Test
- public void format_returns_email_with_subject_with_branch() {
- when(serializer.fromNotification(notification)).thenReturn(new ReportAnalysisFailureNotification(
- projectWithBranch, task, errorMessage));
-
- EmailMessage emailMessage = underTest.format(notification);
-
- assertThat(emailMessage.getSubject()).isEqualTo(projectWithBranch.getName() + " (" + projectWithBranch.getBranchName() + "): Background task in failure");
- }
-
- @Test
- public void format_returns_email_with_message_without_branch() {
- when(serializer.fromNotification(notification)).thenReturn(new ReportAnalysisFailureNotification(
- projectNoBranch, task, errorMessage));
-
- EmailMessage emailMessage = underTest.format(notification);
-
- assertThat(emailMessage.getMessage())
- .contains("Project:\t" + projectNoBranch.getName() + "\n");
- }
-
- @Test
- public void format_returns_email_with_message_with_branch() {
- when(serializer.fromNotification(notification)).thenReturn(new ReportAnalysisFailureNotification(
- projectWithBranch, task, errorMessage));
-
- EmailMessage emailMessage = underTest.format(notification);
-
- assertThat(emailMessage.getMessage())
- .contains("Project:\t" + projectWithBranch.getName() + " (" + projectWithBranch.getBranchName() + ")\n");
- }
-
- @Test
- public void format_returns_email_with_message_containing_all_information() {
- when(serializer.fromNotification(notification)).thenReturn(new ReportAnalysisFailureNotification(
- projectNoBranch, task, errorMessage));
-
- EmailMessage emailMessage = underTest.format(notification);
-
- assertThat(emailMessage.getMessage())
- .isEqualTo("Project:\t" + projectNoBranch.getName() + "\n" +
- "Background task:\t" + task.getUuid() + "\n" +
- "Submission time:\t" + DateUtils.formatDateTime(task.getCreatedAt()) + "\n" +
- "Failure time:\t" + DateUtils.formatDateTime(task.getFailedAt()) + "\n" +
- "\n" +
- "Error message:\t" + errorMessage + "\n" +
- "\n" +
- "More details at: " + serverUrl + "/project/background_tasks?id=" + projectNoBranch.getKey());
- }
-
- @Test
- public void format_returns_email_with_message_with_error_message_when_null() {
- when(serializer.fromNotification(notification)).thenReturn(new ReportAnalysisFailureNotification(
- projectNoBranch, task, null));
-
- EmailMessage emailMessage = underTest.format(notification);
-
- assertThat(emailMessage.getMessage())
- .doesNotContain("Error message:\t");
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.ce.notification;
-
-import org.junit.Test;
-import org.sonar.core.platform.ComponentContainer;
-import org.sonar.server.notification.NotificationDispatcherMetadata;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class ReportAnalysisFailureNotificationModuleTest {
- private ReportAnalysisFailureNotificationModule underTest = new ReportAnalysisFailureNotificationModule();
-
- @Test
- public void adds_dispatcher_and_its_metadata() {
- ComponentContainer container = new ComponentContainer();
-
- underTest.configure(container);
-
- assertThat(container.getPicoContainer().getComponentAdapters(NotificationDispatcherMetadata.class)).isNotNull();
- assertThat(container.getPicoContainer().getComponentAdapters(ReportAnalysisFailureNotificationDispatcher.class)).isNotNull();
- }
-
- @Test
- public void adds_template_and_serializer() {
- ComponentContainer container = new ComponentContainer();
-
- underTest.configure(container);
-
- assertThat(container.getPicoContainer().getComponentAdapters(ReportAnalysisFailureNotificationEmailTemplate.class)).isNotNull();
- assertThat(container.getPicoContainer().getComponentAdapters(ReportAnalysisFailureNotificationSerializer.class)).isNotNull();
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.ce.notification;
-
-import java.util.Random;
-import org.junit.Test;
-import org.sonar.api.notifications.Notification;
-
-import static java.lang.String.valueOf;
-import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class ReportAnalysisFailureNotificationSerializerImplTest {
- private Random random = new Random();
- private ReportAnalysisFailureNotification.Project project = new ReportAnalysisFailureNotification.Project(
- randomAlphanumeric(2),
- randomAlphanumeric(3),
- randomAlphanumeric(4),
- randomAlphanumeric(5));
- private ReportAnalysisFailureNotification.Task task = new ReportAnalysisFailureNotification.Task(
- randomAlphanumeric(6),
- random.nextInt(99),
- random.nextInt(99));
- private String errorMessage = randomAlphanumeric(7);
- private ReportAnalysisFailureNotificationSerializerImpl underTest = new ReportAnalysisFailureNotificationSerializerImpl();
-
- @Test
- public void verify_toNotification() {
-
- Notification notification = underTest.toNotification(new ReportAnalysisFailureNotification(project, task, errorMessage));
-
- assertThat(notification.getFieldValue("project.uuid")).isEqualTo(project.getUuid());
- assertThat(notification.getFieldValue("project.name")).isEqualTo(project.getName());
- assertThat(notification.getFieldValue("project.key")).isEqualTo(project.getKey());
- assertThat(notification.getFieldValue("project.branchName")).isEqualTo(project.getBranchName());
- assertThat(notification.getFieldValue("task.uuid")).isEqualTo(task.getUuid());
- assertThat(notification.getFieldValue("task.createdAt")).isEqualTo(valueOf(task.getCreatedAt()));
- assertThat(notification.getFieldValue("task.failedAt")).isEqualTo(valueOf(task.getFailedAt()));
- assertThat(notification.getFieldValue("error.message")).isEqualTo(errorMessage);
- }
-
- @Test
- public void verify_fromNotification() {
- Notification notification = new Notification(randomAlphanumeric(1))
- .setFieldValue("project.uuid", project.getUuid())
- .setFieldValue("project.name", project.getName())
- .setFieldValue("project.key", project.getKey())
- .setFieldValue("project.branchName", project.getBranchName())
- .setFieldValue("task.uuid", task.getUuid())
- .setFieldValue("task.createdAt", valueOf(task.getCreatedAt()))
- .setFieldValue("task.failedAt", valueOf(task.getFailedAt()))
- .setFieldValue("error.message", errorMessage);
-
- ReportAnalysisFailureNotification reportAnalysisFailureNotification = underTest.fromNotification(notification);
-
- assertThat(reportAnalysisFailureNotification.getProject().getUuid()).isEqualTo(project.getUuid());
- assertThat(reportAnalysisFailureNotification.getProject().getKey()).isEqualTo(project.getKey());
- assertThat(reportAnalysisFailureNotification.getProject().getName()).isEqualTo(project.getName());
- assertThat(reportAnalysisFailureNotification.getProject().getBranchName()).isEqualTo(project.getBranchName());
- assertThat(reportAnalysisFailureNotification.getTask().getUuid()).isEqualTo(task.getUuid());
- assertThat(reportAnalysisFailureNotification.getTask().getCreatedAt()).isEqualTo(task.getCreatedAt());
- assertThat(reportAnalysisFailureNotification.getTask().getFailedAt()).isEqualTo(task.getFailedAt());
- assertThat(reportAnalysisFailureNotification.getErrorMessage()).isEqualTo(errorMessage);
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.ce.notification;
-
-import java.util.Random;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class ReportAnalysisFailureNotificationTest {
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private Random random = new Random();
- private ReportAnalysisFailureNotification.Task task = new ReportAnalysisFailureNotification.Task(
- randomAlphanumeric(2), random.nextInt(5_996), random.nextInt(9_635));
- private ReportAnalysisFailureNotification.Project project = new ReportAnalysisFailureNotification.Project(
- randomAlphanumeric(6), randomAlphanumeric(7), randomAlphanumeric(8), randomAlphanumeric(9));
-
- @Test
- public void project_constructor_fails_if_uuid_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("uuid can't be null");
-
- new ReportAnalysisFailureNotification.Project(null, randomAlphanumeric(2), randomAlphanumeric(3), randomAlphanumeric(4));
- }
-
- @Test
- public void project_constructor_fails_if_key_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("key can't be null");
-
- new ReportAnalysisFailureNotification.Project(randomAlphanumeric(2), null, randomAlphanumeric(3), randomAlphanumeric(4));
- }
-
- @Test
- public void project_constructor_fails_if_name_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("name can't be null");
-
- new ReportAnalysisFailureNotification.Project(randomAlphanumeric(2), randomAlphanumeric(3), null, randomAlphanumeric(4));
- }
-
- @Test
- public void verify_report_getters() {
- String uuid = randomAlphanumeric(2);
- String key = randomAlphanumeric(3);
- String name = randomAlphanumeric(4);
- String branchName = randomAlphanumeric(5);
- ReportAnalysisFailureNotification.Project underTest = new ReportAnalysisFailureNotification.Project(uuid, key, name, branchName);
-
- assertThat(underTest.getUuid()).isEqualTo(uuid);
- assertThat(underTest.getName()).isEqualTo(name);
- assertThat(underTest.getKey()).isEqualTo(key);
- assertThat(underTest.getBranchName()).isEqualTo(branchName);
- }
-
- @Test
- public void project_supports_null_branch() {
- ReportAnalysisFailureNotification.Project underTest = new ReportAnalysisFailureNotification.Project(randomAlphanumeric(2), randomAlphanumeric(3), randomAlphanumeric(4), null);
-
- assertThat(underTest.getBranchName()).isNull();
- }
-
- @Test
- public void task_constructor_fails_if_uuid_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("uuid can't be null");
-
- new ReportAnalysisFailureNotification.Task(null, random.nextInt(5_996), random.nextInt(9_635));
- }
-
- @Test
- public void verify_task_getters() {
- String uuid = randomAlphanumeric(6);
- int createdAt = random.nextInt(5_996);
- int failedAt = random.nextInt(9_635);
-
- ReportAnalysisFailureNotification.Task underTest = new ReportAnalysisFailureNotification.Task(uuid, createdAt, failedAt);
-
- assertThat(underTest.getUuid()).isEqualTo(uuid);
- assertThat(underTest.getCreatedAt()).isEqualTo(createdAt);
- assertThat(underTest.getFailedAt()).isEqualTo(failedAt);
- }
-
- @Test
- public void constructor_fails_with_NPE_if_Project_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("project can't be null");
-
- new ReportAnalysisFailureNotification(null, task, randomAlphanumeric(99));
- }
-
- @Test
- public void constructor_fails_with_NPE_if_Task_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("task can't be null");
-
- new ReportAnalysisFailureNotification(project, null, randomAlphanumeric(99));
- }
-
- @Test
- public void verify_getters() {
- String message = randomAlphanumeric(99);
- ReportAnalysisFailureNotification underTest = new ReportAnalysisFailureNotification(project, task, message);
-
- assertThat(underTest.getProject()).isSameAs(project);
- assertThat(underTest.getTask()).isSameAs(task);
- assertThat(underTest.getErrorMessage()).isSameAs(message);
- }
-
- @Test
- public void null_error_message_is_supported() {
- ReportAnalysisFailureNotification underTest = new ReportAnalysisFailureNotification(project, task, null);
-
- assertThat(underTest.getErrorMessage()).isNull();
- }
-}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.ce.task.projectanalysis;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.ce.task.CeTask;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+public class ReportTaskProcessorDeclarationTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private ReportTaskProcessorDeclaration underTest = new ReportTaskProcessorDeclaration();
+
+ @Test
+ public void getHandledCeTaskTypes_returns_REPORT() {
+ assertThat(underTest.getHandledCeTaskTypes()).containsOnly("REPORT");
+ }
+
+ @Test
+ public void process_throws_UOE() {
+ expectedException.expect(UnsupportedOperationException.class);
+ expectedException.expectMessage("process must not be called in WebServer");
+
+ underTest.process(mock(CeTask.class));
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.ce.notification;
+
+import com.google.common.collect.HashMultimap;
+import org.junit.Test;
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.notifications.NotificationChannel;
+import org.sonar.api.web.UserRole;
+import org.sonar.server.notification.NotificationDispatcher;
+import org.sonar.server.notification.NotificationDispatcherMetadata;
+import org.sonar.server.notification.NotificationManager;
+import org.sonar.server.notification.NotificationManager.SubscriberPermissionsOnProject;
+
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+public class ReportAnalysisFailureNotificationDispatcherTest {
+ private NotificationManager notificationManager = mock(NotificationManager.class);
+ private Notification notificationMock = mock(Notification.class);
+ private NotificationDispatcher.Context contextMock = mock(NotificationDispatcher.Context.class);
+ private ReportAnalysisFailureNotificationDispatcher underTest = new ReportAnalysisFailureNotificationDispatcher(notificationManager);
+
+ @Test
+ public void dispatcher_defines_key() {
+ assertThat(underTest.getKey()).isNotEmpty();
+ }
+
+ @Test
+ public void newMetadata_indicates_enabled_global_and_project_level_notifications() {
+ NotificationDispatcherMetadata metadata = ReportAnalysisFailureNotificationDispatcher.newMetadata();
+
+ assertThat(metadata.getProperty(NotificationDispatcherMetadata.GLOBAL_NOTIFICATION)).isEqualTo("true");
+ assertThat(metadata.getProperty(NotificationDispatcherMetadata.PER_PROJECT_NOTIFICATION)).isEqualTo("true");
+ }
+
+ @Test
+ public void performDispatch_has_no_effect_if_type_is_empty() {
+ when(notificationMock.getType()).thenReturn("");
+
+ underTest.performDispatch(notificationMock, contextMock);
+
+ verify(notificationMock).getType();
+ verifyNoMoreInteractions(notificationMock, contextMock);
+ }
+
+ @Test
+ public void performDispatch_has_no_effect_if_type_is_not_ReportAnalysisFailureNotification_TYPE() {
+ when(notificationMock.getType()).thenReturn(randomAlphanumeric(6));
+
+ underTest.performDispatch(notificationMock, contextMock);
+
+ verify(notificationMock).getType();
+ verifyNoMoreInteractions(notificationMock, contextMock);
+ }
+
+ @Test
+ public void performDispatch_adds_user_for_each_recipient_and_channel_for_the_component_uuid_in_the_notification() {
+ when(notificationMock.getType()).thenReturn(ReportAnalysisFailureNotification.TYPE);
+ String projectKey = randomAlphanumeric(9);
+ when(notificationMock.getFieldValue("project.key")).thenReturn(projectKey);
+ HashMultimap<String, NotificationChannel> multimap = HashMultimap.create();
+ String login1 = randomAlphanumeric(3);
+ String login2 = randomAlphanumeric(3);
+ NotificationChannel channel1 = mock(NotificationChannel.class);
+ NotificationChannel channel2 = mock(NotificationChannel.class);
+ NotificationChannel channel3 = mock(NotificationChannel.class);
+ multimap.put(login1, channel1);
+ multimap.put(login1, channel2);
+ multimap.put(login2, channel2);
+ multimap.put(login2, channel3);
+ when(notificationManager.findSubscribedRecipientsForDispatcher(underTest, projectKey, new SubscriberPermissionsOnProject(UserRole.ADMIN, UserRole.USER)))
+ .thenReturn(multimap);
+
+ underTest.performDispatch(notificationMock, contextMock);
+
+ verify(contextMock).addUser(login1, channel1);
+ verify(contextMock).addUser(login1, channel2);
+ verify(contextMock).addUser(login2, channel2);
+ verify(contextMock).addUser(login2, channel3);
+ verifyNoMoreInteractions(contextMock);
+ }
+
+ @Test
+ public void performDispatch_adds_no_user_if_notification_manager_returns_none() {
+ when(notificationMock.getType()).thenReturn(ReportAnalysisFailureNotification.TYPE);
+ String projectKey = randomAlphanumeric(9);
+ when(notificationMock.getFieldValue("project.key")).thenReturn(projectKey);
+ HashMultimap<String, NotificationChannel> multimap = HashMultimap.create();
+ when(notificationManager.findSubscribedRecipientsForDispatcher(underTest, projectKey, new SubscriberPermissionsOnProject(UserRole.ADMIN, UserRole.USER)))
+ .thenReturn(multimap);
+
+ underTest.performDispatch(notificationMock, contextMock);
+
+ verifyNoMoreInteractions(contextMock);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.ce.notification;
+
+import java.util.Random;
+import org.apache.commons.lang.RandomStringUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.config.EmailSettings;
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.plugins.emailnotifications.api.EmailMessage;
+
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ReportAnalysisFailureNotificationEmailTemplateTest {
+ private String serverUrl = RandomStringUtils.randomAlphanumeric(12);
+ private Notification notification = new Notification(ReportAnalysisFailureNotification.TYPE);
+ private Random random = new Random();
+ private ReportAnalysisFailureNotification.Project projectNoBranch = new ReportAnalysisFailureNotification.Project(
+ randomAlphanumeric(2),
+ randomAlphanumeric(3),
+ randomAlphanumeric(4),
+ null);
+ private ReportAnalysisFailureNotification.Project projectWithBranch = new ReportAnalysisFailureNotification.Project(
+ randomAlphanumeric(6),
+ randomAlphanumeric(7),
+ randomAlphanumeric(8),
+ randomAlphanumeric(9));
+ private ReportAnalysisFailureNotification.Task task = new ReportAnalysisFailureNotification.Task(
+ randomAlphanumeric(10),
+ random.nextInt(99),
+ random.nextInt(99));
+ private String errorMessage = randomAlphanumeric(11);
+
+ private ReportAnalysisFailureNotificationSerializer serializer = mock(ReportAnalysisFailureNotificationSerializer.class);
+ private EmailSettings emailSettings = mock(EmailSettings.class);
+ private ReportAnalysisFailureNotificationEmailTemplate underTest = new ReportAnalysisFailureNotificationEmailTemplate(serializer, emailSettings);
+
+ @Before
+ public void setUp() throws Exception {
+ when(emailSettings.getServerBaseURL()).thenReturn(serverUrl);
+ }
+
+ @Test
+ public void returns_null_if_notification_type_is_not_ReportAnalysisFailureNotification_TYPE() {
+ Notification notification = new Notification(RandomStringUtils.randomAlphanumeric(5));
+
+ EmailMessage emailMessage = underTest.format(notification);
+
+ assertThat(emailMessage).isNull();
+ }
+
+ @Test
+ public void format_returns_email_with_subject_without_branch() {
+ when(serializer.fromNotification(notification)).thenReturn(new ReportAnalysisFailureNotification(
+ projectNoBranch, task, errorMessage));
+
+ EmailMessage emailMessage = underTest.format(notification);
+
+ assertThat(emailMessage.getSubject()).isEqualTo(projectNoBranch.getName() + ": Background task in failure");
+ }
+
+ @Test
+ public void format_returns_email_with_subject_with_branch() {
+ when(serializer.fromNotification(notification)).thenReturn(new ReportAnalysisFailureNotification(
+ projectWithBranch, task, errorMessage));
+
+ EmailMessage emailMessage = underTest.format(notification);
+
+ assertThat(emailMessage.getSubject()).isEqualTo(projectWithBranch.getName() + " (" + projectWithBranch.getBranchName() + "): Background task in failure");
+ }
+
+ @Test
+ public void format_returns_email_with_message_without_branch() {
+ when(serializer.fromNotification(notification)).thenReturn(new ReportAnalysisFailureNotification(
+ projectNoBranch, task, errorMessage));
+
+ EmailMessage emailMessage = underTest.format(notification);
+
+ assertThat(emailMessage.getMessage())
+ .contains("Project:\t" + projectNoBranch.getName() + "\n");
+ }
+
+ @Test
+ public void format_returns_email_with_message_with_branch() {
+ when(serializer.fromNotification(notification)).thenReturn(new ReportAnalysisFailureNotification(
+ projectWithBranch, task, errorMessage));
+
+ EmailMessage emailMessage = underTest.format(notification);
+
+ assertThat(emailMessage.getMessage())
+ .contains("Project:\t" + projectWithBranch.getName() + " (" + projectWithBranch.getBranchName() + ")\n");
+ }
+
+ @Test
+ public void format_returns_email_with_message_containing_all_information() {
+ when(serializer.fromNotification(notification)).thenReturn(new ReportAnalysisFailureNotification(
+ projectNoBranch, task, errorMessage));
+
+ EmailMessage emailMessage = underTest.format(notification);
+
+ assertThat(emailMessage.getMessage())
+ .isEqualTo("Project:\t" + projectNoBranch.getName() + "\n" +
+ "Background task:\t" + task.getUuid() + "\n" +
+ "Submission time:\t" + DateUtils.formatDateTime(task.getCreatedAt()) + "\n" +
+ "Failure time:\t" + DateUtils.formatDateTime(task.getFailedAt()) + "\n" +
+ "\n" +
+ "Error message:\t" + errorMessage + "\n" +
+ "\n" +
+ "More details at: " + serverUrl + "/project/background_tasks?id=" + projectNoBranch.getKey());
+ }
+
+ @Test
+ public void format_returns_email_with_message_with_error_message_when_null() {
+ when(serializer.fromNotification(notification)).thenReturn(new ReportAnalysisFailureNotification(
+ projectNoBranch, task, null));
+
+ EmailMessage emailMessage = underTest.format(notification);
+
+ assertThat(emailMessage.getMessage())
+ .doesNotContain("Error message:\t");
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.ce.notification;
+
+import org.junit.Test;
+import org.sonar.core.platform.ComponentContainer;
+import org.sonar.server.notification.NotificationDispatcherMetadata;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ReportAnalysisFailureNotificationModuleTest {
+ private ReportAnalysisFailureNotificationModule underTest = new ReportAnalysisFailureNotificationModule();
+
+ @Test
+ public void adds_dispatcher_and_its_metadata() {
+ ComponentContainer container = new ComponentContainer();
+
+ underTest.configure(container);
+
+ assertThat(container.getPicoContainer().getComponentAdapters(NotificationDispatcherMetadata.class)).isNotNull();
+ assertThat(container.getPicoContainer().getComponentAdapters(ReportAnalysisFailureNotificationDispatcher.class)).isNotNull();
+ }
+
+ @Test
+ public void adds_template_and_serializer() {
+ ComponentContainer container = new ComponentContainer();
+
+ underTest.configure(container);
+
+ assertThat(container.getPicoContainer().getComponentAdapters(ReportAnalysisFailureNotificationEmailTemplate.class)).isNotNull();
+ assertThat(container.getPicoContainer().getComponentAdapters(ReportAnalysisFailureNotificationSerializer.class)).isNotNull();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.ce.notification;
+
+import java.util.Random;
+import org.junit.Test;
+import org.sonar.api.notifications.Notification;
+
+import static java.lang.String.valueOf;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ReportAnalysisFailureNotificationSerializerImplTest {
+ private Random random = new Random();
+ private ReportAnalysisFailureNotification.Project project = new ReportAnalysisFailureNotification.Project(
+ randomAlphanumeric(2),
+ randomAlphanumeric(3),
+ randomAlphanumeric(4),
+ randomAlphanumeric(5));
+ private ReportAnalysisFailureNotification.Task task = new ReportAnalysisFailureNotification.Task(
+ randomAlphanumeric(6),
+ random.nextInt(99),
+ random.nextInt(99));
+ private String errorMessage = randomAlphanumeric(7);
+ private ReportAnalysisFailureNotificationSerializerImpl underTest = new ReportAnalysisFailureNotificationSerializerImpl();
+
+ @Test
+ public void verify_toNotification() {
+
+ Notification notification = underTest.toNotification(new ReportAnalysisFailureNotification(project, task, errorMessage));
+
+ assertThat(notification.getFieldValue("project.uuid")).isEqualTo(project.getUuid());
+ assertThat(notification.getFieldValue("project.name")).isEqualTo(project.getName());
+ assertThat(notification.getFieldValue("project.key")).isEqualTo(project.getKey());
+ assertThat(notification.getFieldValue("project.branchName")).isEqualTo(project.getBranchName());
+ assertThat(notification.getFieldValue("task.uuid")).isEqualTo(task.getUuid());
+ assertThat(notification.getFieldValue("task.createdAt")).isEqualTo(valueOf(task.getCreatedAt()));
+ assertThat(notification.getFieldValue("task.failedAt")).isEqualTo(valueOf(task.getFailedAt()));
+ assertThat(notification.getFieldValue("error.message")).isEqualTo(errorMessage);
+ }
+
+ @Test
+ public void verify_fromNotification() {
+ Notification notification = new Notification(randomAlphanumeric(1))
+ .setFieldValue("project.uuid", project.getUuid())
+ .setFieldValue("project.name", project.getName())
+ .setFieldValue("project.key", project.getKey())
+ .setFieldValue("project.branchName", project.getBranchName())
+ .setFieldValue("task.uuid", task.getUuid())
+ .setFieldValue("task.createdAt", valueOf(task.getCreatedAt()))
+ .setFieldValue("task.failedAt", valueOf(task.getFailedAt()))
+ .setFieldValue("error.message", errorMessage);
+
+ ReportAnalysisFailureNotification reportAnalysisFailureNotification = underTest.fromNotification(notification);
+
+ assertThat(reportAnalysisFailureNotification.getProject().getUuid()).isEqualTo(project.getUuid());
+ assertThat(reportAnalysisFailureNotification.getProject().getKey()).isEqualTo(project.getKey());
+ assertThat(reportAnalysisFailureNotification.getProject().getName()).isEqualTo(project.getName());
+ assertThat(reportAnalysisFailureNotification.getProject().getBranchName()).isEqualTo(project.getBranchName());
+ assertThat(reportAnalysisFailureNotification.getTask().getUuid()).isEqualTo(task.getUuid());
+ assertThat(reportAnalysisFailureNotification.getTask().getCreatedAt()).isEqualTo(task.getCreatedAt());
+ assertThat(reportAnalysisFailureNotification.getTask().getFailedAt()).isEqualTo(task.getFailedAt());
+ assertThat(reportAnalysisFailureNotification.getErrorMessage()).isEqualTo(errorMessage);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.ce.notification;
+
+import java.util.Random;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ReportAnalysisFailureNotificationTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private Random random = new Random();
+ private ReportAnalysisFailureNotification.Task task = new ReportAnalysisFailureNotification.Task(
+ randomAlphanumeric(2), random.nextInt(5_996), random.nextInt(9_635));
+ private ReportAnalysisFailureNotification.Project project = new ReportAnalysisFailureNotification.Project(
+ randomAlphanumeric(6), randomAlphanumeric(7), randomAlphanumeric(8), randomAlphanumeric(9));
+
+ @Test
+ public void project_constructor_fails_if_uuid_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("uuid can't be null");
+
+ new ReportAnalysisFailureNotification.Project(null, randomAlphanumeric(2), randomAlphanumeric(3), randomAlphanumeric(4));
+ }
+
+ @Test
+ public void project_constructor_fails_if_key_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("key can't be null");
+
+ new ReportAnalysisFailureNotification.Project(randomAlphanumeric(2), null, randomAlphanumeric(3), randomAlphanumeric(4));
+ }
+
+ @Test
+ public void project_constructor_fails_if_name_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("name can't be null");
+
+ new ReportAnalysisFailureNotification.Project(randomAlphanumeric(2), randomAlphanumeric(3), null, randomAlphanumeric(4));
+ }
+
+ @Test
+ public void verify_report_getters() {
+ String uuid = randomAlphanumeric(2);
+ String key = randomAlphanumeric(3);
+ String name = randomAlphanumeric(4);
+ String branchName = randomAlphanumeric(5);
+ ReportAnalysisFailureNotification.Project underTest = new ReportAnalysisFailureNotification.Project(uuid, key, name, branchName);
+
+ assertThat(underTest.getUuid()).isEqualTo(uuid);
+ assertThat(underTest.getName()).isEqualTo(name);
+ assertThat(underTest.getKey()).isEqualTo(key);
+ assertThat(underTest.getBranchName()).isEqualTo(branchName);
+ }
+
+ @Test
+ public void project_supports_null_branch() {
+ ReportAnalysisFailureNotification.Project underTest = new ReportAnalysisFailureNotification.Project(randomAlphanumeric(2), randomAlphanumeric(3), randomAlphanumeric(4), null);
+
+ assertThat(underTest.getBranchName()).isNull();
+ }
+
+ @Test
+ public void task_constructor_fails_if_uuid_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("uuid can't be null");
+
+ new ReportAnalysisFailureNotification.Task(null, random.nextInt(5_996), random.nextInt(9_635));
+ }
+
+ @Test
+ public void verify_task_getters() {
+ String uuid = randomAlphanumeric(6);
+ int createdAt = random.nextInt(5_996);
+ int failedAt = random.nextInt(9_635);
+
+ ReportAnalysisFailureNotification.Task underTest = new ReportAnalysisFailureNotification.Task(uuid, createdAt, failedAt);
+
+ assertThat(underTest.getUuid()).isEqualTo(uuid);
+ assertThat(underTest.getCreatedAt()).isEqualTo(createdAt);
+ assertThat(underTest.getFailedAt()).isEqualTo(failedAt);
+ }
+
+ @Test
+ public void constructor_fails_with_NPE_if_Project_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("project can't be null");
+
+ new ReportAnalysisFailureNotification(null, task, randomAlphanumeric(99));
+ }
+
+ @Test
+ public void constructor_fails_with_NPE_if_Task_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("task can't be null");
+
+ new ReportAnalysisFailureNotification(project, null, randomAlphanumeric(99));
+ }
+
+ @Test
+ public void verify_getters() {
+ String message = randomAlphanumeric(99);
+ ReportAnalysisFailureNotification underTest = new ReportAnalysisFailureNotification(project, task, message);
+
+ assertThat(underTest.getProject()).isSameAs(project);
+ assertThat(underTest.getTask()).isSameAs(task);
+ assertThat(underTest.getErrorMessage()).isSameAs(message);
+ }
+
+ @Test
+ public void null_error_message_is_supported() {
+ ReportAnalysisFailureNotification underTest = new ReportAnalysisFailureNotification(project, task, null);
+
+ assertThat(underTest.getErrorMessage()).isNull();
+ }
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.ce.taskprocessor;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.ce.task.CeTask;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-
-public class ReportTaskProcessorDeclarationTest {
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private ReportTaskProcessorDeclaration underTest = new ReportTaskProcessorDeclaration();
-
- @Test
- public void getHandledCeTaskTypes_returns_REPORT() {
- assertThat(underTest.getHandledCeTaskTypes()).containsOnly("REPORT");
- }
-
- @Test
- public void process_throws_UOE() {
- expectedException.expect(UnsupportedOperationException.class);
- expectedException.expectMessage("process must not be called in WebServer");
-
- underTest.process(mock(CeTask.class));
- }
-}
import org.sonar.api.server.ws.WebService.Param;
import org.sonar.api.utils.System2;
import org.sonar.api.web.UserRole;
-import org.sonar.ce.taskprocessor.CeTaskProcessor;
+import org.sonar.ce.task.taskprocessor.CeTaskProcessor;
import org.sonar.core.util.Uuids;
import org.sonar.db.DbTester;
import org.sonar.db.ce.CeActivityDto;
import org.junit.Test;
import org.sonar.ce.task.CeTask;
import org.sonar.ce.task.CeTaskResult;
-import org.sonar.ce.taskprocessor.CeTaskProcessor;
+import org.sonar.ce.task.taskprocessor.CeTaskProcessor;
import org.sonar.server.ws.WsActionTester;
import static org.sonar.test.JsonAssert.assertJson;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.notification;
-
-import com.google.common.collect.Multimap;
-import java.io.InvalidClassException;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import org.apache.commons.lang.RandomStringUtils;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.InOrder;
-import org.sonar.api.notifications.Notification;
-import org.sonar.api.notifications.NotificationChannel;
-import org.sonar.api.web.UserRole;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.notification.NotificationQueueDao;
-import org.sonar.db.notification.NotificationQueueDto;
-import org.sonar.db.permission.AuthorizationDao;
-import org.sonar.db.property.PropertiesDao;
-import org.sonar.db.property.Subscriber;
-import org.sonar.server.notification.NotificationManager.SubscriberPermissionsOnProject;
-
-import static com.google.common.collect.Sets.newHashSet;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anySet;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.only;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.internal.verification.VerificationModeFactory.times;
-
-public class DefaultNotificationManagerTest {
-
- private DefaultNotificationManager underTest;
-
- private PropertiesDao propertiesDao = mock(PropertiesDao.class);
- private NotificationDispatcher dispatcher = mock(NotificationDispatcher.class);
- private NotificationChannel emailChannel = mock(NotificationChannel.class);
- private NotificationChannel twitterChannel = mock(NotificationChannel.class);
- private NotificationQueueDao notificationQueueDao = mock(NotificationQueueDao.class);
- private AuthorizationDao authorizationDao = mock(AuthorizationDao.class);
- private DbClient dbClient = mock(DbClient.class);
- private DbSession dbSession = mock(DbSession.class);
-
- @Before
- public void setUp() {
- when(dispatcher.getKey()).thenReturn("NewViolations");
- when(emailChannel.getKey()).thenReturn("Email");
- when(twitterChannel.getKey()).thenReturn("Twitter");
- when(dbClient.openSession(anyBoolean())).thenReturn(dbSession);
- when(dbClient.propertiesDao()).thenReturn(propertiesDao);
- when(dbClient.notificationQueueDao()).thenReturn(notificationQueueDao);
- when(dbClient.authorizationDao()).thenReturn(authorizationDao);
-
- underTest = new DefaultNotificationManager(new NotificationChannel[] {emailChannel, twitterChannel}, dbClient);
- }
-
- @Test
- public void shouldProvideChannelList() {
- assertThat(underTest.getChannels()).containsOnly(emailChannel, twitterChannel);
-
- underTest = new DefaultNotificationManager(new NotificationChannel[] {}, dbClient);
- assertThat(underTest.getChannels()).hasSize(0);
- }
-
- @Test
- public void shouldPersist() {
- Notification notification = new Notification("test");
- underTest.scheduleForSending(notification);
-
- verify(notificationQueueDao, only()).insert(any(List.class));
- }
-
- @Test
- public void shouldGetFromQueueAndDelete() {
- Notification notification = new Notification("test");
- NotificationQueueDto dto = NotificationQueueDto.toNotificationQueueDto(notification);
- List<NotificationQueueDto> dtos = Arrays.asList(dto);
- when(notificationQueueDao.selectOldest(1)).thenReturn(dtos);
-
- assertThat(underTest.getFromQueue()).isNotNull();
-
- InOrder inOrder = inOrder(notificationQueueDao);
- inOrder.verify(notificationQueueDao).selectOldest(1);
- inOrder.verify(notificationQueueDao).delete(dtos);
- }
-
- // SONAR-4739
- @Test
- public void shouldNotFailWhenUnableToDeserialize() throws Exception {
- NotificationQueueDto dto1 = mock(NotificationQueueDto.class);
- when(dto1.toNotification()).thenThrow(new InvalidClassException("Pouet"));
- List<NotificationQueueDto> dtos = Arrays.asList(dto1);
- when(notificationQueueDao.selectOldest(1)).thenReturn(dtos);
-
- underTest = spy(underTest);
- assertThat(underTest.getFromQueue()).isNull();
- assertThat(underTest.getFromQueue()).isNull();
-
- verify(underTest, times(1)).logDeserializationIssue();
- }
-
- @Test
- public void shouldFindNoRecipient() {
- assertThat(underTest.findSubscribedRecipientsForDispatcher(dispatcher, "uuid_45", new SubscriberPermissionsOnProject(UserRole.USER)).asMap().entrySet())
- .hasSize(0);
- }
-
- @Test
- public void shouldFindSubscribedRecipientForGivenResource() {
- String projectUuid = "uuid_45";
- when(propertiesDao.findUsersForNotification("NewViolations", "Email", projectUuid))
- .thenReturn(newHashSet(new Subscriber("user1", false), new Subscriber("user3", false), new Subscriber("user3", true)));
- when(propertiesDao.findUsersForNotification("NewViolations", "Twitter", "uuid_56"))
- .thenReturn(newHashSet(new Subscriber("user2", false)));
- when(propertiesDao.findUsersForNotification("NewViolations", "Twitter", projectUuid))
- .thenReturn(newHashSet(new Subscriber("user3", true)));
- when(propertiesDao.findUsersForNotification("NewAlerts", "Twitter", projectUuid))
- .thenReturn(newHashSet(new Subscriber("user4", false)));
-
- when(authorizationDao.keepAuthorizedLoginsOnProject(dbSession, newHashSet("user1", "user3"), projectUuid, "user"))
- .thenReturn(newHashSet("user1", "user3"));
-
- Multimap<String, NotificationChannel> multiMap = underTest.findSubscribedRecipientsForDispatcher(dispatcher, projectUuid,
- SubscriberPermissionsOnProject.ALL_MUST_HAVE_ROLE_USER);
- assertThat(multiMap.entries()).hasSize(3);
-
- Map<String, Collection<NotificationChannel>> map = multiMap.asMap();
- assertThat(map.get("user1")).containsOnly(emailChannel);
- assertThat(map.get("user2")).isNull();
- assertThat(map.get("user3")).containsOnly(emailChannel, twitterChannel);
- assertThat(map.get("user4")).isNull();
-
- // code is optimized to perform only 1 SQL requests for all channels
- verify(authorizationDao, times(1)).keepAuthorizedLoginsOnProject(eq(dbSession), anySet(), anyString(), anyString());
- }
-
- @Test
- public void should_apply_distinct_permission_filtering_global_or_project_subscribers() {
- String globalPermission = RandomStringUtils.randomAlphanumeric(4);
- String projectPermission = RandomStringUtils.randomAlphanumeric(5);
- String projectUuid = "uuid_45";
- when(propertiesDao.findUsersForNotification("NewViolations", "Email", projectUuid))
- .thenReturn(newHashSet(new Subscriber("user1", false), new Subscriber("user3", false), new Subscriber("user3", true)));
- when(propertiesDao.findUsersForNotification("NewViolations", "Twitter", "uuid_56"))
- .thenReturn(newHashSet(new Subscriber("user2", false)));
- when(propertiesDao.findUsersForNotification("NewViolations", "Twitter", projectUuid))
- .thenReturn(newHashSet(new Subscriber("user3", true)));
- when(propertiesDao.findUsersForNotification("NewAlerts", "Twitter", projectUuid))
- .thenReturn(newHashSet(new Subscriber("user4", false)));
-
- when(authorizationDao.keepAuthorizedLoginsOnProject(dbSession, newHashSet("user3", "user4"), projectUuid, globalPermission))
- .thenReturn(newHashSet("user3"));
- when(authorizationDao.keepAuthorizedLoginsOnProject(dbSession, newHashSet("user1", "user3"), projectUuid, projectPermission))
- .thenReturn(newHashSet("user1", "user3"));
-
- Multimap<String, NotificationChannel> multiMap = underTest.findSubscribedRecipientsForDispatcher(dispatcher, projectUuid,
- new SubscriberPermissionsOnProject(globalPermission, projectPermission));
- assertThat(multiMap.entries()).hasSize(3);
-
- Map<String, Collection<NotificationChannel>> map = multiMap.asMap();
- assertThat(map.get("user1")).containsOnly(emailChannel);
- assertThat(map.get("user2")).isNull();
- assertThat(map.get("user3")).containsOnly(emailChannel, twitterChannel);
- assertThat(map.get("user4")).isNull();
-
- // code is optimized to perform only 2 SQL requests for all channels
- verify(authorizationDao, times(1)).keepAuthorizedLoginsOnProject(eq(dbSession), anySet(), anyString(), eq(globalPermission));
- verify(authorizationDao, times(1)).keepAuthorizedLoginsOnProject(eq(dbSession), anySet(), anyString(), eq(projectPermission));
- }
-
- @Test
- public void do_not_call_db_for_project_permission_filtering_if_there_is_no_project_subscriber() {
- String globalPermission = RandomStringUtils.randomAlphanumeric(4);
- String projectPermission = RandomStringUtils.randomAlphanumeric(5);
- String projectUuid = "uuid_45";
- when(propertiesDao.findUsersForNotification("NewViolations", "Email", projectUuid))
- .thenReturn(newHashSet(new Subscriber("user3", true)));
- when(propertiesDao.findUsersForNotification("NewViolations", "Twitter", projectUuid))
- .thenReturn(newHashSet(new Subscriber("user3", true)));
-
- when(authorizationDao.keepAuthorizedLoginsOnProject(dbSession, newHashSet("user3"), projectUuid, globalPermission))
- .thenReturn(newHashSet("user3"));
-
- Multimap<String, NotificationChannel> multiMap = underTest.findSubscribedRecipientsForDispatcher(dispatcher, projectUuid,
- new SubscriberPermissionsOnProject(globalPermission, projectPermission));
- assertThat(multiMap.entries()).hasSize(2);
-
- Map<String, Collection<NotificationChannel>> map = multiMap.asMap();
- assertThat(map.get("user3")).containsOnly(emailChannel, twitterChannel);
-
- verify(authorizationDao, times(1)).keepAuthorizedLoginsOnProject(eq(dbSession), anySet(), anyString(), eq(globalPermission));
- verify(authorizationDao, times(0)).keepAuthorizedLoginsOnProject(eq(dbSession), anySet(), anyString(), eq(projectPermission));
- }
-
- @Test
- public void do_not_call_db_for_project_permission_filtering_if_there_is_no_global_subscriber() {
- String globalPermission = RandomStringUtils.randomAlphanumeric(4);
- String projectPermission = RandomStringUtils.randomAlphanumeric(5);
- String projectUuid = "uuid_45";
- when(propertiesDao.findUsersForNotification("NewViolations", "Email", projectUuid))
- .thenReturn(newHashSet(new Subscriber("user3", false)));
- when(propertiesDao.findUsersForNotification("NewViolations", "Twitter", projectUuid))
- .thenReturn(newHashSet(new Subscriber("user3", false)));
-
- when(authorizationDao.keepAuthorizedLoginsOnProject(dbSession, newHashSet("user3"), projectUuid, projectPermission))
- .thenReturn(newHashSet("user3"));
-
- Multimap<String, NotificationChannel> multiMap = underTest.findSubscribedRecipientsForDispatcher(dispatcher, projectUuid,
- new SubscriberPermissionsOnProject(globalPermission, projectPermission));
- assertThat(multiMap.entries()).hasSize(2);
-
- Map<String, Collection<NotificationChannel>> map = multiMap.asMap();
- assertThat(map.get("user3")).containsOnly(emailChannel, twitterChannel);
-
- verify(authorizationDao, times(0)).keepAuthorizedLoginsOnProject(eq(dbSession), anySet(), anyString(), eq(globalPermission));
- verify(authorizationDao, times(1)).keepAuthorizedLoginsOnProject(eq(dbSession), anySet(), anyString(), eq(projectPermission));
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.notification;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class NotificationDispatcherMetadataTest {
-
- private NotificationDispatcherMetadata metadata;
-
- @Before
- public void init() {
- metadata = NotificationDispatcherMetadata.create("NewViolations").setProperty("global", "true");
- }
-
- @Test
- public void shouldReturnDispatcherKey() {
- assertThat(metadata.getDispatcherKey()).isEqualTo("NewViolations");
- }
-
- @Test
- public void shouldReturnProperty() {
- assertThat(metadata.getProperty("global")).isEqualTo("true");
- assertThat(metadata.getProperty("per-project")).isNull();
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.notification;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.sonar.api.notifications.Notification;
-import org.sonar.api.notifications.NotificationChannel;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-public class NotificationDispatcherTest {
-
- @Mock
- private NotificationChannel channel;
-
- @Mock
- private Notification notification;
-
- @Mock
- private NotificationDispatcher.Context context;
-
- @Before
- public void init() {
- MockitoAnnotations.initMocks(this);
- when(notification.getType()).thenReturn("event1");
- }
-
- @Test
- public void defaultMethods() {
- NotificationDispatcher dispatcher = new FakeGenericNotificationDispatcher();
- assertThat(dispatcher.getKey(), is("FakeGenericNotificationDispatcher"));
- assertThat(dispatcher.toString(), is("FakeGenericNotificationDispatcher"));
- }
-
- @Test
- public void shouldAlwaysRunDispatchForGenericDispatcher() {
- NotificationDispatcher dispatcher = new FakeGenericNotificationDispatcher();
- dispatcher.performDispatch(notification, context);
-
- verify(context, times(1)).addUser("user1", channel);
- }
-
- @Test
- public void shouldNotAlwaysRunDispatchForSpecificDispatcher() {
- NotificationDispatcher dispatcher = new FakeSpecificNotificationDispatcher();
-
- // a "event1" notif is sent
- dispatcher.performDispatch(notification, context);
- verify(context, never()).addUser("user1", channel);
-
- // now, a "specific-event" notif is sent
- when(notification.getType()).thenReturn("specific-event");
- dispatcher.performDispatch(notification, context);
- verify(context, times(1)).addUser("user1", channel);
- }
-
- class FakeGenericNotificationDispatcher extends NotificationDispatcher {
- @Override
- public void dispatch(Notification notification, Context context) {
- context.addUser("user1", channel);
- }
- }
-
- class FakeSpecificNotificationDispatcher extends NotificationDispatcher {
-
- public FakeSpecificNotificationDispatcher() {
- super("specific-event");
- }
-
- @Override
- public void dispatch(Notification notification, Context context) {
- context.addUser("user1", channel);
- }
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.organization;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.api.utils.System2;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.DbTester;
-import org.sonar.db.organization.OrganizationDto;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.fail;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto;
-import static org.sonar.server.property.InternalProperties.DEFAULT_ORGANIZATION;
-
-public class DefaultOrganizationProviderImplTest {
- private static final OrganizationDto ORGANIZATION_DTO_1 = newOrganizationDto()
- .setUuid("uuid1")
- .setName("the name of 1")
- .setKey("the key 1");
- private static final long DATE_1 = 1_999_888L;
-
- private System2 system2 = mock(System2.class);
-
- @Rule
- public DbTester dbTester = DbTester.create(system2).setDisableDefaultOrganization(true);
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private DbClient dbClient = dbTester.getDbClient();
- private DbSession dbSession = dbTester.getSession();
-
- private DefaultOrganizationProviderImpl underTest = new DefaultOrganizationProviderImpl(dbClient);
-
- @Test
- public void get_fails_with_ISE_if_default_organization_internal_property_does_not_exist() {
- expectISENoDefaultOrganizationUuid();
-
- underTest.get();
- }
-
- @Test
- public void get_fails_with_ISE_if_default_organization_internal_property_is_empty() {
- dbClient.internalPropertiesDao().saveAsEmpty(dbSession, DEFAULT_ORGANIZATION);
- dbSession.commit();
-
- expectISENoDefaultOrganizationUuid();
-
- underTest.get();
- }
-
- private void expectISENoDefaultOrganizationUuid() {
- expectedException.expect(IllegalStateException.class);
- expectedException.expectMessage("No Default organization uuid configured");
- }
-
- @Test
- public void get_fails_with_ISE_if_default_organization_does_not_exist() {
- dbClient.internalPropertiesDao().save(dbSession, DEFAULT_ORGANIZATION, "bla");
- dbSession.commit();
-
- expectedException.expect(IllegalStateException.class);
- expectedException.expectMessage("Default organization with uuid 'bla' does not exist");
-
- underTest.get();
- }
-
- @Test
- public void get_returns_DefaultOrganization_populated_from_DB() {
- insertOrganization(ORGANIZATION_DTO_1, DATE_1);
- dbClient.internalPropertiesDao().save(dbSession, DEFAULT_ORGANIZATION, ORGANIZATION_DTO_1.getUuid());
- dbSession.commit();
-
- DefaultOrganization defaultOrganization = underTest.get();
- assertThat(defaultOrganization.getUuid()).isEqualTo(ORGANIZATION_DTO_1.getUuid());
- assertThat(defaultOrganization.getKey()).isEqualTo(ORGANIZATION_DTO_1.getKey());
- assertThat(defaultOrganization.getName()).isEqualTo(ORGANIZATION_DTO_1.getName());
- assertThat(defaultOrganization.getCreatedAt()).isEqualTo(DATE_1);
- assertThat(defaultOrganization.getUpdatedAt()).isEqualTo(DATE_1);
- }
-
- @Test
- public void get_returns_new_DefaultOrganization_with_each_call_when_cache_is_not_loaded() {
- insertOrganization(ORGANIZATION_DTO_1, DATE_1);
- dbClient.internalPropertiesDao().save(dbSession, DEFAULT_ORGANIZATION, ORGANIZATION_DTO_1.getUuid());
- dbSession.commit();
-
- assertThat(underTest.get()).isNotSameAs(underTest.get());
- }
-
- @Test
- public void unload_does_not_fail_if_load_has_not_been_called() {
- underTest.unload();
- }
-
- @Test
- public void load_fails_with_ISE_when_called_twice_without_unload_in_between() {
- underTest.load();
-
- try {
- underTest.load();
- fail("A IllegalStateException should have been raised");
- } catch (IllegalStateException e) {
- assertThat(e).hasMessage("load called twice for thread '" + Thread.currentThread().getName() + "' or state wasn't cleared last time it was used");
- } finally {
- underTest.unload();
- }
- }
-
- @Test
- public void load_and_unload_cache_DefaultOrganization_object_by_thread() throws InterruptedException {
- insertOrganization(ORGANIZATION_DTO_1, DATE_1);
- dbClient.internalPropertiesDao().save(dbSession, DEFAULT_ORGANIZATION, ORGANIZATION_DTO_1.getUuid());
- dbSession.commit();
-
- try {
- underTest.load();
-
- DefaultOrganization cachedForThread1 = underTest.get();
- assertThat(cachedForThread1).isSameAs(underTest.get());
-
- Thread otherThread = new Thread(() -> {
- try {
- underTest.load();
-
- assertThat(underTest.get())
- .isNotSameAs(cachedForThread1)
- .isSameAs(underTest.get());
- } finally {
- underTest.unload();
- }
- });
- otherThread.start();
- otherThread.join();
- } finally {
- underTest.unload();
- }
- }
-
- @Test
- public void get_returns_new_instance_for_each_call_once_unload_has_been_called() {
- insertOrganization(ORGANIZATION_DTO_1, DATE_1);
- dbClient.internalPropertiesDao().save(dbSession, DEFAULT_ORGANIZATION, ORGANIZATION_DTO_1.getUuid());
- dbSession.commit();
-
- try {
- underTest.load();
- DefaultOrganization cached = underTest.get();
- assertThat(cached).isSameAs(underTest.get());
-
- underTest.unload();
- assertThat(underTest.get()).isNotSameAs(underTest.get()).isNotSameAs(cached);
- } finally {
- // fail safe
- underTest.unload();
- }
- }
-
- private void insertOrganization(OrganizationDto dto, long createdAt) {
- when(system2.now()).thenReturn(createdAt);
- dbClient.organizationDao().insert(dbSession, dto, false);
- dbSession.commit();
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.organization;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class DefaultOrganizationTest {
- private static final long DATE_2 = 2_000_000L;
- private static final long DATE_1 = 1_000_000L;
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private DefaultOrganization.Builder populatedBuilder = new DefaultOrganization.Builder()
- .setUuid("uuid")
- .setKey("key")
- .setName("name")
- .setCreatedAt(DATE_1)
- .setUpdatedAt(DATE_2);
-
- @Test
- public void build_fails_if_uuid_is_null() {
- populatedBuilder.setUuid(null);
-
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("uuid can't be null");
-
- populatedBuilder.build();
- }
-
- @Test
- public void build_fails_if_key_is_null() {
- populatedBuilder.setKey(null);
-
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("key can't be null");
-
- populatedBuilder.build();
- }
-
- @Test
- public void build_fails_if_name_is_null() {
- populatedBuilder.setName(null);
-
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("name can't be null");
-
- populatedBuilder.build();
- }
-
- @Test
- public void build_fails_if_createdAt_not_set() {
- DefaultOrganization.Builder underTest = new DefaultOrganization.Builder()
- .setUuid("uuid")
- .setKey("key")
- .setName("name")
- .setUpdatedAt(DATE_2);
-
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("createdAt can't be null");
-
- underTest.build();
- }
-
- @Test
- public void build_fails_if_updatedAt_not_set() {
- DefaultOrganization.Builder underTest = new DefaultOrganization.Builder()
- .setUuid("uuid")
- .setKey("key")
- .setName("name")
- .setCreatedAt(DATE_1);
-
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("updatedAt can't be null");
-
- underTest.build();
- }
-
- @Test
- public void verify_toString() {
- assertThat(populatedBuilder.build().toString())
- .isEqualTo("DefaultOrganization{uuid='uuid', key='key', name='name', createdAt=1000000, updatedAt=2000000}");
- }
-
- @Test
- public void verify_getters() {
- DefaultOrganization underTest = populatedBuilder.build();
-
- assertThat(underTest.getUuid()).isEqualTo("uuid");
- assertThat(underTest.getKey()).isEqualTo("key");
- assertThat(underTest.getName()).isEqualTo("name");
- assertThat(underTest.getCreatedAt()).isEqualTo(DATE_1);
- assertThat(underTest.getUpdatedAt()).isEqualTo(DATE_2);
- }
-}
import org.sonar.db.Database;
import org.sonar.process.logging.LogLevelConfig;
import org.sonar.process.logging.LogbackHelper;
-import org.sonar.server.app.ServerProcessLogging;
+import org.sonar.server.log.ServerProcessLogging;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.property;
-
-import java.util.Optional;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.property.InternalPropertiesDao;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-public class InternalPropertiesImplTest {
- private static final String EMPTY_STRING = "";
- public static final String SOME_VALUE = "a value";
- public static final String SOME_KEY = "some key";
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private DbClient dbClient = mock(DbClient.class);
- private DbSession dbSession = mock(DbSession.class);
- private InternalPropertiesDao internalPropertiesDao = mock(InternalPropertiesDao.class);
- private InternalPropertiesImpl underTest = new InternalPropertiesImpl(dbClient);
-
- @Before
- public void setUp() throws Exception {
- when(dbClient.openSession(false)).thenReturn(dbSession);
- when(dbClient.internalPropertiesDao()).thenReturn(internalPropertiesDao);
- }
-
- @Test
- public void reads_throws_IAE_if_key_is_null() {
- expectKeyNullOrEmptyIAE();
-
- underTest.read(null);
- }
-
- @Test
- public void reads_throws_IAE_if_key_is_empty() {
- expectKeyNullOrEmptyIAE();
-
- underTest.read(EMPTY_STRING);
- }
-
- @Test
- public void reads_returns_optional_from_DAO() {
- Optional<String> value = Optional.of("bablabla");
-
- when(internalPropertiesDao.selectByKey(dbSession, SOME_KEY)).thenReturn(value);
-
- assertThat(underTest.read(SOME_KEY)).isSameAs(value);
- }
-
- @Test
- public void write_throws_IAE_if_key_is_null() {
- expectKeyNullOrEmptyIAE();
-
- underTest.write(null, SOME_VALUE);
- }
-
- @Test
- public void writes_throws_IAE_if_key_is_empty() {
- expectKeyNullOrEmptyIAE();
-
- underTest.write(EMPTY_STRING, SOME_VALUE);
- }
-
- @Test
- public void write_calls_dao_saveAsEmpty_when_value_is_null() {
- underTest.write(SOME_KEY, null);
-
- verify(internalPropertiesDao).saveAsEmpty(dbSession, SOME_KEY);
- verify(dbSession).commit();
- }
-
- @Test
- public void write_calls_dao_saveAsEmpty_when_value_is_empty() {
- underTest.write(SOME_KEY, EMPTY_STRING);
-
- verify(internalPropertiesDao).saveAsEmpty(dbSession, SOME_KEY);
- verify(dbSession).commit();
- }
-
- @Test
- public void write_calls_dao_save_when_value_is_neither_null_nor_empty() {
- underTest.write(SOME_KEY, SOME_VALUE);
-
- verify(internalPropertiesDao).save(dbSession, SOME_KEY, SOME_VALUE);
- verify(dbSession).commit();
- }
-
- private void expectKeyNullOrEmptyIAE() {
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("key can't be null nor empty");
- }
-}