--- /dev/null
+description = 'SonarQube WebServer internal APIs, used by other Web Server modules or Core Extensions'
+
+sonarqube {
+ properties {
+ property 'sonar.projectName', "${projectTitle} :: WebServer :: API"
+ }
+}
+
+sourceSets {
+ test {
+ resources {
+ srcDirs += ['src/test/projects']
+ }
+ }
+}
+
+configurations {
+ tests
+
+ testCompile.extendsFrom tests
+}
+
+dependencies {
+ // please keep the list grouped by configuration and ordered by name
+
+ compile 'com.google.guava:guava'
+ compile 'io.jsonwebtoken:jjwt-api'
+ compile 'io.jsonwebtoken:jjwt-impl'
+ compile project(':sonar-core')
+ compile project(':server:sonar-db-dao')
+ compile project(':server:sonar-process')
+ compile project(':server:sonar-server-common')
+ compile project(path: ':sonar-plugin-api', configuration: 'shadow')
+ compile project(':sonar-plugin-api-impl')
+ compile 'org.mindrot:jbcrypt'
+
+ compileOnly 'com.google.code.findbugs:jsr305'
+ compileOnly 'javax.servlet:javax.servlet-api'
+
+ testCompile 'org.assertj:assertj-guava'
+ testCompile 'com.google.code.findbugs:jsr305'
+ testCompile 'com.tngtech.java:junit-dataprovider'
+ testCompile 'javax.servlet:javax.servlet-api'
+ testCompile 'org.mockito:mockito-core'
+ testCompile project(':server:sonar-db-testing')
+ testCompile project(path: ":server:sonar-server-common", configuration: "tests")
+ testCompile project(path: ":server:sonar-webserver-ws", configuration: "tests")
+ testCompile project(':sonar-testing-harness')
+}
+
+task testJar(type: Jar) {
+ classifier = 'tests'
+ from sourceSets.test.output
+}
+
+artifacts {
+ tests testJar
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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;
+
+public interface ProcessCommandWrapper {
+ /**
+ * Requests to the main process that SQ be restarted.
+ */
+ void requestSQRestart();
+
+ /**
+ * Requests to the main process that the WebServer is stopped.
+ */
+ void requestHardStop();
+
+ /**
+ * Notifies any listening process that the WebServer is operational.
+ */
+ void notifyOperational();
+
+ /**
+ * Checks whether the Compute Engine is operational.
+ */
+ boolean isCeOperational();
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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 java.io.File;
+import org.sonar.api.config.Configuration;
+import org.sonar.process.ProcessId;
+import org.sonar.process.sharedmemoryfile.DefaultProcessCommands;
+import org.sonar.process.sharedmemoryfile.ProcessCommands;
+
+import static org.sonar.process.ProcessEntryPoint.PROPERTY_PROCESS_INDEX;
+import static org.sonar.process.ProcessEntryPoint.PROPERTY_SHARED_PATH;
+
+public class ProcessCommandWrapperImpl implements ProcessCommandWrapper {
+
+ private static final ProcessMethod<Void> SET_OPERATIONAL = processCommands -> {
+ processCommands.setOperational();
+ return null;
+ };
+ private static final ProcessMethod<Void> ASK_FOR_RESTART = processCommands -> {
+ processCommands.askForRestart();
+ return null;
+ };
+ private static final ProcessMethod<Void> ASK_FOR_HARD_STOP = processCommands -> {
+ processCommands.askForHardStop();
+ return null;
+ };
+ private static final ProcessMethod<Boolean> IS_OPERATIONAL = ProcessCommands::isOperational;
+
+ private final Configuration config;
+
+ public ProcessCommandWrapperImpl(Configuration config) {
+ this.config = config;
+ }
+
+ @Override
+ public void requestSQRestart() {
+ call(ASK_FOR_RESTART, selfProcessNumber());
+ }
+
+ @Override
+ public void requestHardStop() {
+ call(ASK_FOR_HARD_STOP, selfProcessNumber());
+ }
+
+ @Override
+ public void notifyOperational() {
+ call(SET_OPERATIONAL, selfProcessNumber());
+ }
+
+ @Override
+ public boolean isCeOperational() {
+ return call(IS_OPERATIONAL, ProcessId.COMPUTE_ENGINE.getIpcIndex());
+ }
+
+ private int selfProcessNumber() {
+ return nonNullAsInt(PROPERTY_PROCESS_INDEX);
+ }
+
+ private <T> T call(ProcessMethod<T> command, int processNumber) {
+ File shareDir = nonNullValueAsFile(PROPERTY_SHARED_PATH);
+ try (DefaultProcessCommands commands = DefaultProcessCommands.secondary(shareDir, processNumber)) {
+ return command.callOn(commands);
+ }
+ }
+
+ private interface ProcessMethod<T> {
+ T callOn(ProcessCommands processCommands);
+ }
+
+ private int nonNullAsInt(String key) {
+ return config.getInt(key).orElseThrow(() -> new IllegalArgumentException(String.format("Property %s is not set", key)));
+ }
+
+ private File nonNullValueAsFile(String key) {
+ return new File(config.get(key).orElseThrow(() -> new IllegalArgumentException(String.format("Property %s is not set", key))));
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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;
+
+/**
+ * Holds a boolean flag representing the restarting status of the WebServer.
+ * This boolean is {@code false} by default and can safely be changed concurrently using methods {@link #set()} and
+ * {@link #unset()}.
+ */
+public interface RestartFlagHolder {
+ /**
+ * @return whether restarting flag has been set or not.
+ */
+ boolean isRestarting();
+
+ /**
+ * Sets the restarting flag to {@code true}, no matter it already is or not.
+ */
+ void set();
+
+ /**
+ * Sets the restarting flag to {@code false}, no matter it already is or not.
+ */
+ void unset();
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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 java.util.concurrent.atomic.AtomicBoolean;
+
+public class RestartFlagHolderImpl implements RestartFlagHolder {
+ private final AtomicBoolean restarting = new AtomicBoolean(false);
+
+ @Override
+ public boolean isRestarting() {
+ return restarting.get();
+ }
+
+ @Override
+ public void set() {
+ restarting.set(true);
+ }
+
+ @Override
+ public void unset() {
+ restarting.set(false);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.app;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.branch;
+
+interface BranchFeature {
+
+ boolean isEnabled();
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.branch;
+
+import org.sonar.api.server.ServerSide;
+
+/**
+ * The branch plugin needs to implement this in order to know that the branch feature is supported
+ */
+@ServerSide
+public interface BranchFeatureExtension extends BranchFeature {
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.branch;
+
+/**
+ * The goal of this class is to handle the 2 different use case :
+ * - The branch plugin exists, the proxy will redirect method calls to the plugin
+ * - No branch plugin, feature is disabled
+ */
+public interface BranchFeatureProxy extends BranchFeature {
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.branch;
+
+public class BranchFeatureProxyImpl implements BranchFeatureProxy {
+
+ private final BranchFeatureExtension branchFeatureExtension;
+
+ public BranchFeatureProxyImpl() {
+ this.branchFeatureExtension = null;
+ }
+
+ public BranchFeatureProxyImpl(BranchFeatureExtension branchFeatureExtension) {
+ this.branchFeatureExtension = branchFeatureExtension;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return branchFeatureExtension != null && branchFeatureExtension.isEnabled();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.branch;
+
+import javax.annotation.ParametersAreNonnullByDefault;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.http;
+
+import java.util.Optional;
+import org.sonar.api.utils.log.LoggerLevel;
+import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
+
+public interface CeHttpClient {
+ Optional<ProtobufSystemInfo.SystemInfo> retrieveSystemInfo();
+
+ void changeLogLevel(LoggerLevel level);
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.http;
+
+import javax.annotation.ParametersAreNonnullByDefault;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.exceptions;
+
+import com.google.common.base.MoreObjects;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.lang.String.format;
+import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
+import static java.util.Arrays.asList;
+
+/**
+ * Request is not valid and can not be processed.
+ */
+public class BadRequestException extends ServerException {
+
+ private final transient List<String> errors;
+
+ private BadRequestException(List<String> errors) {
+ super(HTTP_BAD_REQUEST, errors.get(0));
+ this.errors = errors;
+ }
+
+ public static void checkRequest(boolean expression, String message, Object... messageArguments) {
+ if (!expression) {
+ throw create(format(message, messageArguments));
+ }
+ }
+
+ public static void checkRequest(boolean expression, List<String> messages) {
+ if (!expression) {
+ throw create(messages);
+ }
+ }
+
+ public static BadRequestException create(List<String> errorMessages) {
+ checkArgument(!errorMessages.isEmpty(), "At least one error message is required");
+ checkArgument(errorMessages.stream().noneMatch(message -> message == null || message.isEmpty()), "Message cannot be empty");
+ return new BadRequestException(errorMessages);
+ }
+
+ public static BadRequestException create(String... errorMessages) {
+ return create(asList(errorMessages));
+ }
+
+ public List<String> errors() {
+ return errors;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("errors", errors)
+ .toString();
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.exceptions;
+
+import com.google.common.base.Preconditions;
+
+import static java.net.HttpURLConnection.HTTP_FORBIDDEN;
+
+/**
+ * Permission denied. User does not have the required permissions.
+ */
+public class ForbiddenException extends ServerException {
+
+ public ForbiddenException(String message) {
+ super(HTTP_FORBIDDEN, Preconditions.checkNotNull(message));
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.exceptions;
+
+import com.google.common.base.Preconditions;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static java.lang.String.format;
+
+public class Message {
+
+ private final String msg;
+
+ private Message(String format, Object... params) {
+ Preconditions.checkArgument(!isNullOrEmpty(format), "Message cannot be empty");
+ this.msg = format(format, params);
+ }
+
+ public String getMessage() {
+ return msg;
+ }
+
+ public static Message of(String msg, Object... arguments) {
+ return new Message(msg, arguments);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Message other = (Message) o;
+ return this.msg.equals(other.msg);
+ }
+
+ @Override
+ public int hashCode() {
+ return msg.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return msg;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.exceptions;
+
+import com.google.common.base.Optional;
+import javax.annotation.Nullable;
+
+import static java.lang.String.format;
+import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
+
+public class NotFoundException extends ServerException {
+
+ public NotFoundException(String message) {
+ super(HTTP_NOT_FOUND, message);
+ }
+
+ /**
+ * @throws NotFoundException if the value if null
+ * @return the value
+ */
+ public static <T> T checkFound(@Nullable T value, String message, Object... messageArguments) {
+ if (value == null) {
+ throw new NotFoundException(format(message, messageArguments));
+ }
+
+ return value;
+ }
+
+ /**
+ * @throws NotFoundException if the value is not present
+ * @return the value
+ */
+ public static <T> T checkFoundWithOptional(Optional<T> value, String message, Object... messageArguments) {
+ if (!value.isPresent()) {
+ throw new NotFoundException(format(message, messageArguments));
+ }
+
+ return value.get();
+ }
+
+ public static <T> T checkFoundWithOptional(java.util.Optional<T> value, String message, Object... messageArguments) {
+ if (!value.isPresent()) {
+ throw new NotFoundException(format(message, messageArguments));
+ }
+
+ return value.get();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.exceptions;
+
+import static java.util.Objects.requireNonNull;
+
+public class ServerException extends RuntimeException {
+ private final int httpCode;
+
+ public ServerException(int httpCode, String message) {
+ super(requireNonNull(message, "Error message cannot be null"));
+ this.httpCode = httpCode;
+ }
+
+ public int httpCode() {
+ return httpCode;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.exceptions;
+
+import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
+
+/**
+ * User needs to be authenticated. HTTP request is generally redirected to login form.
+ */
+public class UnauthorizedException extends ServerException {
+
+ public UnauthorizedException(String message) {
+ super(HTTP_UNAUTHORIZED, message);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.exceptions;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.health;
+
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import org.sonar.process.cluster.health.NodeHealth;
+
+import static com.google.common.collect.ImmutableSet.copyOf;
+import static java.util.Objects.requireNonNull;
+
+public class ClusterHealth {
+ private final Health health;
+ private final Set<NodeHealth> nodes;
+
+ public ClusterHealth(Health health, Set<NodeHealth> nodes) {
+ this.health = requireNonNull(health, "health can't be null");
+ this.nodes = copyOf(requireNonNull(nodes, "nodes can't be null"));
+ }
+
+ public Health getHealth() {
+ return health;
+ }
+
+ public Set<NodeHealth> getNodes() {
+ return nodes;
+ }
+
+ public Optional<NodeHealth> getNodeHealth(String nodeName) {
+ return nodes.stream()
+ .filter(node -> nodeName.equals(node.getDetails().getName()))
+ .findFirst();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ClusterHealth that = (ClusterHealth) o;
+ return Objects.equals(health, that.health) &&
+ Objects.equals(nodes, that.nodes);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(health, nodes);
+ }
+
+ @Override
+ public String toString() {
+ return "ClusterHealth{" +
+ "health=" + health +
+ ", nodes=" + nodes +
+ '}';
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.health;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+public class Health {
+ /**
+ * The GREEN status without any cause as a constant, for convenience and optimisation.
+ */
+ public static final Health GREEN = newHealthCheckBuilder()
+ .setStatus(Status.GREEN)
+ .build();
+
+ private final Status status;
+ private final Set<String> causes;
+
+ public Health(Builder builder) {
+ this.status = builder.status;
+ this.causes = ImmutableSet.copyOf(builder.causes);
+ }
+
+ public Status getStatus() {
+ return status;
+ }
+
+ public Set<String> getCauses() {
+ return causes;
+ }
+
+ public static Builder newHealthCheckBuilder() {
+ return new Builder();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Health health = (Health) o;
+ return status == health.status &&
+ Objects.equals(causes, health.causes);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(status, causes);
+ }
+
+ @Override
+ public String toString() {
+ return "Health{" + status +
+ ", causes=" + causes +
+ '}';
+ }
+
+ /**
+ * Builder of {@link Health} which supports being reused for optimization.
+ */
+ public static class Builder {
+ private Status status;
+ private Set<String> causes = new HashSet<>(0);
+
+ private Builder() {
+ // use static factory method
+ }
+
+ public Builder clear() {
+ this.status = null;
+ this.causes.clear();
+ return this;
+ }
+
+ public Builder setStatus(Status status) {
+ this.status = checkStatus(status);
+ return this;
+ }
+
+ public Builder addCause(String cause) {
+ requireNonNull(cause, "cause can't be null");
+ checkArgument(!cause.trim().isEmpty(), "cause can't be empty");
+ causes.add(cause);
+ return this;
+ }
+
+ public Health build() {
+ checkStatus(this.status);
+ return new Health(this);
+ }
+
+ private static Status checkStatus(Status status) {
+ return requireNonNull(status, "status can't be null");
+ }
+ }
+
+ public enum Status {
+ /**
+ * Fully working
+ */
+ GREEN,
+ /**
+ * Yellow: Working but something must be fixed to make SQ fully operational
+ */
+ YELLOW,
+ /**
+ * Red: Not working
+ */
+ RED
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.health;
+
+public interface HealthChecker {
+ /**
+ * Perform a check of the health of the current SonarQube node, either as a standalone node or as a member
+ * of a cluster.
+ */
+ Health checkNode();
+
+ /**
+ * Perform a check of the health of the SonarQube cluster.
+ *
+ * @throws IllegalStateException if clustering is not enabled.
+ */
+ ClusterHealth checkCluster();
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.health;
+
+import javax.annotation.ParametersAreNonnullByDefault;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.platform;
+
+import org.sonar.api.ExtensionPoint;
+import org.sonar.api.server.ServerSide;
+
+@ServerSide
+@ExtensionPoint
+public interface ClusterFeature {
+
+ boolean isEnabled();
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.platform;
+
+import org.sonar.core.platform.ComponentContainer;
+
+public interface Platform {
+ void doStart();
+
+ Status status();
+
+ ComponentContainer getContainer();
+
+ enum Status {
+ BOOTING, SAFEMODE, STARTING, UP
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.platform;
+
+import org.sonar.api.utils.text.JsonWriter;
+
+public interface SystemInfoWriter {
+ void write(JsonWriter json) throws Exception;
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.platform;
+
+import javax.annotation.ParametersAreNonnullByDefault;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.plugins;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.io.FileUtils;
+import org.sonar.core.platform.PluginInfo;
+
+import static java.util.Objects.requireNonNull;
+
+@Immutable
+public class InstalledPlugin {
+ private final PluginInfo plugin;
+ private final FileAndMd5 loadedJar;
+ @Nullable
+ private final FileAndMd5 compressedJar;
+
+ public InstalledPlugin(PluginInfo plugin, FileAndMd5 loadedJar, @Nullable FileAndMd5 compressedJar) {
+ this.plugin = requireNonNull(plugin);
+ this.loadedJar = requireNonNull(loadedJar);
+ this.compressedJar = compressedJar;
+ }
+
+ public PluginInfo getPluginInfo() {
+ return plugin;
+ }
+
+ public FileAndMd5 getLoadedJar() {
+ return loadedJar;
+ }
+
+ @Nullable
+ public FileAndMd5 getCompressedJar() {
+ return compressedJar;
+ }
+
+ @Immutable
+ public static final class FileAndMd5 {
+ private final File file;
+ private final String md5;
+
+ public FileAndMd5(File file) {
+ try (InputStream fis = FileUtils.openInputStream(file)) {
+ this.file = file;
+ this.md5 = DigestUtils.md5Hex(fis);
+ } catch (IOException e) {
+ throw new IllegalStateException("Fail to compute md5 of " + file, e);
+ }
+ }
+
+ public File getFile() {
+ return file;
+ }
+
+ public String getMd5() {
+ return md5;
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.plugins;
+
+import com.google.common.base.Optional;
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.apache.commons.io.FileUtils;
+import org.picocontainer.Startable;
+import org.sonar.api.utils.HttpDownloader;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.core.platform.PluginInfo;
+import org.sonar.core.util.stream.MoreCollectors;
+import org.sonar.server.platform.ServerFileSystem;
+import org.sonar.updatecenter.common.Release;
+import org.sonar.updatecenter.common.UpdateCenter;
+import org.sonar.updatecenter.common.Version;
+
+import static org.apache.commons.io.FileUtils.copyFile;
+import static org.apache.commons.io.FileUtils.copyFileToDirectory;
+import static org.apache.commons.io.FileUtils.forceMkdir;
+import static org.apache.commons.io.FileUtils.toFile;
+import static org.apache.commons.lang.StringUtils.substringAfterLast;
+import static org.sonar.core.util.FileUtils.deleteQuietly;
+import static org.sonar.server.exceptions.BadRequestException.checkRequest;
+
+/**
+ * Downloads plugins from update center. Files are copied in the directory extensions/downloads and then
+ * moved to extensions/plugins after server restart.
+ */
+public class PluginDownloader implements Startable {
+
+ private static final Logger LOG = Loggers.get(PluginDownloader.class);
+ private static final String TMP_SUFFIX = "tmp";
+ private static final String PLUGIN_EXTENSION = "jar";
+
+ private final UpdateCenterMatrixFactory updateCenterMatrixFactory;
+ private final HttpDownloader downloader;
+ private final File downloadDir;
+
+ public PluginDownloader(UpdateCenterMatrixFactory updateCenterMatrixFactory, HttpDownloader downloader,
+ ServerFileSystem fileSystem) {
+ this.updateCenterMatrixFactory = updateCenterMatrixFactory;
+ this.downloader = downloader;
+ this.downloadDir = fileSystem.getDownloadedPluginsDir();
+ }
+
+ /**
+ * Deletes the temporary files remaining from previous downloads
+ */
+ @Override
+ public void start() {
+ try {
+ forceMkdir(downloadDir);
+ for (File tempFile : listTempFile(this.downloadDir)) {
+ deleteQuietly(tempFile);
+ }
+ } catch (IOException e) {
+ throw new IllegalStateException("Fail to create the directory: " + downloadDir, e);
+ }
+ }
+
+ @Override
+ public void stop() {
+ // Nothing to do
+ }
+
+ public void cancelDownloads() {
+ try {
+ if (downloadDir.exists()) {
+ org.sonar.core.util.FileUtils.cleanDirectory(downloadDir);
+ }
+ } catch (IOException e) {
+ throw new IllegalStateException("Fail to clean the plugin downloads directory: " + downloadDir, e);
+ }
+ }
+
+ public List<String> getDownloadedPluginFilenames() {
+ List<String> names = new ArrayList<>();
+ for (File file : listPlugins(this.downloadDir)) {
+ names.add(file.getName());
+ }
+ return names;
+ }
+
+ /**
+ * @return the list of download plugins as {@link PluginInfo} instances
+ */
+ public Collection<PluginInfo> getDownloadedPlugins() {
+ return listPlugins(this.downloadDir)
+ .stream()
+ .map(PluginInfo::create)
+ .collect(MoreCollectors.toList());
+ }
+
+ public void download(String pluginKey, Version version) {
+ Optional<UpdateCenter> updateCenter = updateCenterMatrixFactory.getUpdateCenter(true);
+ if (updateCenter.isPresent()) {
+ List<Release> installablePlugins = updateCenter.get().findInstallablePlugins(pluginKey, version);
+ checkRequest(!installablePlugins.isEmpty(), "Error while downloading plugin '%s' with version '%s'. No compatible plugin found.", pluginKey, version.getName());
+ for (Release release : installablePlugins) {
+ try {
+ downloadRelease(release);
+ } catch (Exception e) {
+ String message = String.format("Fail to download the plugin (%s, version %s) from %s (error is : %s)",
+ release.getArtifact().getKey(), release.getVersion().getName(), release.getDownloadUrl(), e.getMessage());
+ LOG.debug(message, e);
+ throw new IllegalStateException(message, e);
+ }
+ }
+ }
+ }
+
+ private void downloadRelease(Release release) throws URISyntaxException, IOException {
+ String url = release.getDownloadUrl();
+
+ URI uri = new URI(url);
+ if (url.startsWith("file:")) {
+ // used for tests
+ File file = toFile(uri.toURL());
+ copyFileToDirectory(file, downloadDir);
+ } else {
+ String filename = substringAfterLast(uri.getPath(), "/");
+ if (!filename.endsWith("." + PLUGIN_EXTENSION)) {
+ filename = release.getKey() + "-" + release.getVersion() + "." + PLUGIN_EXTENSION;
+ }
+ File targetFile = new File(downloadDir, filename);
+ File tempFile = new File(downloadDir, filename + "." + TMP_SUFFIX);
+ downloader.download(uri, tempFile);
+ copyFile(tempFile, targetFile);
+ deleteQuietly(tempFile);
+ }
+ }
+
+ private static Collection<File> listTempFile(File dir) {
+ return FileUtils.listFiles(dir, new String[] {TMP_SUFFIX}, false);
+ }
+
+ private static Collection<File> listPlugins(File dir) {
+ return FileUtils.listFiles(dir, new String[] {PLUGIN_EXTENSION}, false);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.plugins;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.jar.JarInputStream;
+import java.util.jar.Pack200;
+import java.util.zip.GZIPOutputStream;
+import org.sonar.api.config.Configuration;
+import org.sonar.api.server.ServerSide;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.api.utils.log.Profiler;
+import org.sonar.core.platform.PluginInfo;
+import org.sonar.server.plugins.InstalledPlugin.FileAndMd5;
+
+import static com.google.common.base.Preconditions.checkState;
+
+@ServerSide
+public class PluginFileSystem {
+
+ public static final String PROPERTY_PLUGIN_COMPRESSION_ENABLE = "sonar.pluginsCompression.enable";
+ private static final Logger LOG = Loggers.get(PluginFileSystem.class);
+
+ private final Configuration configuration;
+ private final Map<String, InstalledPlugin> installedFiles = new HashMap<>();
+
+ public PluginFileSystem(Configuration configuration) {
+ this.configuration = configuration;
+ }
+
+ /**
+ * @param plugin
+ * @param loadedJar the JAR loaded by classloaders. It differs from {@code plugin.getJarFile()}
+ * which is the initial location of JAR as seen by users
+ */
+ public void addInstalledPlugin(PluginInfo plugin, File loadedJar) {
+ checkState(!installedFiles.containsKey(plugin.getKey()), "Plugin %s is already loaded", plugin.getKey());
+ checkState(loadedJar.exists(), "loadedJar does not exist: %s", loadedJar);
+
+ Optional<File> compressed = compressJar(plugin, loadedJar);
+ InstalledPlugin installedFile = new InstalledPlugin(
+ plugin,
+ new FileAndMd5(loadedJar),
+ compressed.map(FileAndMd5::new).orElse(null));
+ installedFiles.put(plugin.getKey(), installedFile);
+ }
+
+ public Optional<InstalledPlugin> getInstalledPlugin(String pluginKey) {
+ return Optional.ofNullable(installedFiles.get(pluginKey));
+ }
+
+ public Collection<InstalledPlugin> getInstalledFiles() {
+ return installedFiles.values();
+ }
+
+ private Optional<File> compressJar(PluginInfo plugin, File jar) {
+ if (!configuration.getBoolean(PROPERTY_PLUGIN_COMPRESSION_ENABLE).orElse(false)) {
+ return Optional.empty();
+ }
+
+ Path targetPack200 = getPack200Path(jar.toPath());
+ Path sourcePack200Path = getPack200Path(plugin.getNonNullJarFile().toPath());
+
+ // check if packed file was deployed alongside the jar. If that's the case, use it instead of generating it (SONAR-10395).
+ if (sourcePack200Path.toFile().exists()) {
+ try {
+ LOG.debug("Found pack200: " + sourcePack200Path);
+ Files.copy(sourcePack200Path, targetPack200);
+ } catch (IOException e) {
+ throw new IllegalStateException("Failed to copy pack200 file from " + sourcePack200Path + " to " + targetPack200, e);
+ }
+ } else {
+ pack200(jar.toPath(), targetPack200, plugin.getKey());
+ }
+ return Optional.of(targetPack200.toFile());
+ }
+
+ private static void pack200(Path jarPath, Path toPack200Path, String pluginKey) {
+ Profiler profiler = Profiler.create(LOG);
+ profiler.startInfo("Compressing plugin " + pluginKey + " [pack200]");
+
+ try (JarInputStream in = new JarInputStream(new BufferedInputStream(Files.newInputStream(jarPath)));
+ OutputStream out = new GZIPOutputStream(new BufferedOutputStream(Files.newOutputStream(toPack200Path)))) {
+ Pack200.newPacker().pack(in, out);
+ } catch (IOException e) {
+ throw new IllegalStateException(String.format("Fail to pack200 plugin [%s] '%s' to '%s'", pluginKey, jarPath, toPack200Path), e);
+ }
+ profiler.stopInfo();
+ }
+
+ private static Path getPack200Path(Path jar) {
+ String jarFileName = jar.getFileName().toString();
+ String filename = jarFileName.substring(0, jarFileName.length() - 3) + "pack.gz";
+ return jar.resolveSibling(filename);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.plugins;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+import org.apache.commons.io.FileUtils;
+import org.picocontainer.Startable;
+import org.sonar.core.platform.PluginInfo;
+import org.sonar.core.util.stream.MoreCollectors;
+import org.sonar.server.platform.ServerFileSystem;
+
+import static java.lang.String.format;
+import static org.apache.commons.io.FileUtils.forceMkdir;
+
+public class PluginUninstaller implements Startable {
+ private static final String PLUGIN_EXTENSION = "jar";
+ private final ServerPluginRepository serverPluginRepository;
+ private final File uninstallDir;
+
+ public PluginUninstaller(ServerPluginRepository serverPluginRepository, ServerFileSystem fs) {
+ this.serverPluginRepository = serverPluginRepository;
+ this.uninstallDir = fs.getUninstalledPluginsDir();
+ }
+
+ private static Collection<File> listJarFiles(File dir) {
+ if (dir.exists()) {
+ return FileUtils.listFiles(dir, new String[] {PLUGIN_EXTENSION}, false);
+ }
+ return Collections.emptyList();
+ }
+
+ @Override
+ public void start() {
+ try {
+ forceMkdir(uninstallDir);
+ } catch (IOException e) {
+ throw new IllegalStateException("Fail to create the directory: " + uninstallDir, e);
+ }
+ }
+
+ @Override
+ public void stop() {
+ // Nothing to do
+ }
+
+ public void uninstall(String pluginKey) {
+ ensurePluginIsInstalled(pluginKey);
+ serverPluginRepository.uninstall(pluginKey, uninstallDir);
+ }
+
+ public void cancelUninstalls() {
+ serverPluginRepository.cancelUninstalls(uninstallDir);
+ }
+
+ /**
+ * @return the list of plugins to be uninstalled as {@link PluginInfo} instances
+ */
+ public Collection<PluginInfo> getUninstalledPlugins() {
+ return listJarFiles(uninstallDir)
+ .stream()
+ .map(PluginInfo::create)
+ .collect(MoreCollectors.toList());
+ }
+
+ private void ensurePluginIsInstalled(String key) {
+ if (!serverPluginRepository.hasPlugin(key)) {
+ throw new IllegalArgumentException(format("Plugin [%s] is not installed", key));
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.plugins;
+
+import java.io.File;
+import org.apache.commons.io.FileUtils;
+import org.sonar.api.server.ServerSide;
+import org.sonar.api.utils.ZipUtils;
+import org.sonar.core.platform.ExplodedPlugin;
+import org.sonar.core.platform.PluginInfo;
+import org.sonar.core.platform.PluginJarExploder;
+import org.sonar.server.platform.ServerFileSystem;
+
+import static org.apache.commons.io.FileUtils.forceMkdir;
+
+@ServerSide
+public class ServerPluginJarExploder extends PluginJarExploder {
+ private final ServerFileSystem fs;
+ private final PluginFileSystem pluginFileSystem;
+
+ public ServerPluginJarExploder(ServerFileSystem fs, PluginFileSystem pluginFileSystem) {
+ this.fs = fs;
+ this.pluginFileSystem = pluginFileSystem;
+ }
+
+ /**
+ * JAR files of directory extensions/plugins can be moved when server is up and plugins are uninstalled.
+ * For this reason these files must not be locked by classloaders. They are copied to the directory
+ * web/deploy/plugins in order to be loaded by {@link org.sonar.core.platform.PluginLoader}.
+ */
+ @Override
+ public ExplodedPlugin explode(PluginInfo pluginInfo) {
+ File toDir = new File(fs.getDeployedPluginsDir(), pluginInfo.getKey());
+ try {
+ forceMkdir(toDir);
+ org.sonar.core.util.FileUtils.cleanDirectory(toDir);
+
+ File jarSource = pluginInfo.getNonNullJarFile();
+ File jarTarget = new File(toDir, jarSource.getName());
+
+ FileUtils.copyFile(jarSource, jarTarget);
+ ZipUtils.unzip(jarSource, toDir, newLibFilter());
+ ExplodedPlugin explodedPlugin = explodeFromUnzippedDir(pluginInfo.getKey(), jarTarget, toDir);
+ pluginFileSystem.addInstalledPlugin(pluginInfo, jarTarget);
+ return explodedPlugin;
+ } catch (Exception e) {
+ throw new IllegalStateException(String.format(
+ "Fail to unzip plugin [%s] %s to %s", pluginInfo.getKey(), pluginInfo.getNonNullJarFile().getAbsolutePath(), toDir.getAbsolutePath()), e);
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.plugins;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Joiner;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Ordering;
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import javax.annotation.CheckForNull;
+import org.apache.commons.io.FileUtils;
+import org.picocontainer.Startable;
+import org.sonar.api.Plugin;
+import org.sonar.api.SonarRuntime;
+import org.sonar.api.utils.MessageException;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.core.platform.PluginInfo;
+import org.sonar.core.platform.PluginLoader;
+import org.sonar.core.platform.PluginRepository;
+import org.sonar.server.platform.ServerFileSystem;
+import org.sonar.updatecenter.common.Version;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static java.lang.String.format;
+import static org.apache.commons.io.FileUtils.moveFile;
+import static org.apache.commons.io.FileUtils.moveFileToDirectory;
+import static org.sonar.core.util.FileUtils.deleteQuietly;
+
+/**
+ * Entry point to install and load plugins on server startup. It manages
+ * <ul>
+ * <li>installation of new plugins (effective after server startup)</li>
+ * <li>un-installation of plugins (effective after server startup)</li>
+ * <li>cancel pending installations/un-installations</li>
+ * <li>instantiation of plugin entry-points</li>
+ * </ul>
+ */
+public class ServerPluginRepository implements PluginRepository, Startable {
+
+ private static final Logger LOG = Loggers.get(ServerPluginRepository.class);
+ private static final String[] JAR_FILE_EXTENSIONS = new String[] {"jar"};
+ // List of plugins that are silently removed if installed
+ private static final Set<String> DEFAULT_BLACKLISTED_PLUGINS = ImmutableSet.of("scmactivity", "issuesreport", "genericcoverage");
+ // List of plugins that should prevent the server to finish its startup
+ private static final Set<String> FORBIDDEN_COMPATIBLE_PLUGINS = ImmutableSet.of("sqale", "report", "views");
+ private static final Joiner SLASH_JOINER = Joiner.on(" / ").skipNulls();
+ private static final String NOT_STARTED_YET = "not started yet";
+
+ private final SonarRuntime runtime;
+ private final ServerFileSystem fs;
+ private final PluginLoader loader;
+ private final AtomicBoolean started = new AtomicBoolean(false);
+ private Set<String> blacklistedPluginKeys = DEFAULT_BLACKLISTED_PLUGINS;
+
+ // following fields are available after startup
+ private final Map<String, PluginInfo> pluginInfosByKeys = new HashMap<>();
+ private final Map<String, Plugin> pluginInstancesByKeys = new HashMap<>();
+ private final Map<ClassLoader, String> keysByClassLoader = new HashMap<>();
+
+ public ServerPluginRepository(SonarRuntime runtime, ServerFileSystem fs, PluginLoader loader) {
+ this.runtime = runtime;
+ this.fs = fs;
+ this.loader = loader;
+ }
+
+ @VisibleForTesting
+ void setBlacklistedPluginKeys(Set<String> keys) {
+ this.blacklistedPluginKeys = keys;
+ }
+
+ @Override
+ public void start() {
+ loadPreInstalledPlugins();
+ moveDownloadedPlugins();
+ unloadIncompatiblePlugins();
+ logInstalledPlugins();
+ loadInstances();
+ started.set(true);
+ }
+
+ @Override
+ public void stop() {
+ // close classloaders
+ loader.unload(pluginInstancesByKeys.values());
+ pluginInstancesByKeys.clear();
+ pluginInfosByKeys.clear();
+ keysByClassLoader.clear();
+ started.set(true);
+ }
+
+ /**
+ * Return the key of the plugin the extension (in the sense of {@link Plugin.Context#addExtension(Object)} is coming from.
+ */
+ @CheckForNull
+ public String getPluginKey(Object extension) {
+ return keysByClassLoader.get(extension.getClass().getClassLoader());
+ }
+
+ /**
+ * Load the plugins that are located in extensions/plugins. Blacklisted plugins are
+ * deleted.
+ */
+ private void loadPreInstalledPlugins() {
+ for (File file : listJarFiles(fs.getInstalledPluginsDir())) {
+ PluginInfo info = PluginInfo.create(file);
+ registerPluginInfo(info);
+ }
+ }
+
+ /**
+ * Move the plugins recently downloaded to extensions/plugins.
+ */
+ private void moveDownloadedPlugins() {
+ if (fs.getDownloadedPluginsDir().exists()) {
+ for (File sourceFile : listJarFiles(fs.getDownloadedPluginsDir())) {
+ overrideAndRegisterPlugin(sourceFile);
+ }
+ }
+ }
+
+ private void registerPluginInfo(PluginInfo info) {
+ String pluginKey = info.getKey();
+ if (blacklistedPluginKeys.contains(pluginKey)) {
+ LOG.warn("Plugin {} [{}] is blacklisted and is being uninstalled", info.getName(), pluginKey);
+ deleteQuietly(info.getNonNullJarFile());
+ return;
+ }
+ if (FORBIDDEN_COMPATIBLE_PLUGINS.contains(pluginKey)) {
+ throw MessageException.of(String.format("Plugin '%s' is no longer compatible with this version of SonarQube", pluginKey));
+ }
+ PluginInfo existing = pluginInfosByKeys.put(pluginKey, info);
+ if (existing != null) {
+ throw MessageException.of(format("Found two versions of the plugin %s [%s] in the directory extensions/plugins. Please remove one of %s or %s.",
+ info.getName(), pluginKey, info.getNonNullJarFile().getName(), existing.getNonNullJarFile().getName()));
+ }
+
+ }
+
+ /**
+ * Move or copy plugin to directory extensions/plugins. If a version of this plugin
+ * already exists then it's deleted.
+ */
+ private void overrideAndRegisterPlugin(File sourceFile) {
+ File destDir = fs.getInstalledPluginsDir();
+ File destFile = new File(destDir, sourceFile.getName());
+ if (destFile.exists()) {
+ // plugin with same filename already installed
+ deleteQuietly(destFile);
+ }
+
+ try {
+ moveFile(sourceFile, destFile);
+
+ } catch (IOException e) {
+ throw new IllegalStateException(format("Fail to move plugin: %s to %s",
+ sourceFile.getAbsolutePath(), destFile.getAbsolutePath()), e);
+ }
+
+ PluginInfo info = PluginInfo.create(destFile);
+ PluginInfo existing = pluginInfosByKeys.put(info.getKey(), info);
+ if (existing != null) {
+ if (!existing.getNonNullJarFile().getName().equals(destFile.getName())) {
+ deleteQuietly(existing.getNonNullJarFile());
+ }
+ LOG.info("Plugin {} [{}] updated to version {}", info.getName(), info.getKey(), info.getVersion());
+ } else {
+ LOG.info("Plugin {} [{}] installed", info.getName(), info.getKey());
+ }
+ }
+
+ /**
+ * Removes the plugins that are not compatible with current environment.
+ */
+ private void unloadIncompatiblePlugins() {
+ // loop as long as the previous loop ignored some plugins. That allows to support dependencies
+ // on many levels, for example D extends C, which extends B, which requires A. If A is not installed,
+ // then B, C and D must be ignored. That's not possible to achieve this algorithm with a single
+ // iteration over plugins.
+ Set<String> removedKeys = new HashSet<>();
+ do {
+ removedKeys.clear();
+ for (PluginInfo plugin : pluginInfosByKeys.values()) {
+ if (!isCompatible(plugin, runtime, pluginInfosByKeys)) {
+ removedKeys.add(plugin.getKey());
+ }
+ }
+ for (String removedKey : removedKeys) {
+ pluginInfosByKeys.remove(removedKey);
+ }
+ } while (!removedKeys.isEmpty());
+ }
+
+ @VisibleForTesting
+ static boolean isCompatible(PluginInfo plugin, SonarRuntime runtime, Map<String, PluginInfo> allPluginsByKeys) {
+ if (Strings.isNullOrEmpty(plugin.getMainClass()) && Strings.isNullOrEmpty(plugin.getBasePlugin())) {
+ LOG.warn("Plugin {} [{}] is ignored because entry point class is not defined", plugin.getName(), plugin.getKey());
+ return false;
+ }
+
+ if (!plugin.isCompatibleWith(runtime.getApiVersion().toString())) {
+ throw MessageException.of(format(
+ "Plugin %s [%s] requires at least SonarQube %s", plugin.getName(), plugin.getKey(), plugin.getMinimalSqVersion()));
+ }
+
+ if (!Strings.isNullOrEmpty(plugin.getBasePlugin()) && !allPluginsByKeys.containsKey(plugin.getBasePlugin())) {
+ // it extends a plugin that is not installed
+ LOG.warn("Plugin {} [{}] is ignored because its base plugin [{}] is not installed", plugin.getName(), plugin.getKey(), plugin.getBasePlugin());
+ return false;
+ }
+
+ for (PluginInfo.RequiredPlugin requiredPlugin : plugin.getRequiredPlugins()) {
+ PluginInfo installedRequirement = allPluginsByKeys.get(requiredPlugin.getKey());
+ if (installedRequirement == null) {
+ // it requires a plugin that is not installed
+ LOG.warn("Plugin {} [{}] is ignored because the required plugin [{}] is not installed", plugin.getName(), plugin.getKey(), requiredPlugin.getKey());
+ return false;
+ }
+ Version installedRequirementVersion = installedRequirement.getVersion();
+ if (installedRequirementVersion != null && requiredPlugin.getMinimalVersion().compareToIgnoreQualifier(installedRequirementVersion) > 0) {
+ // it requires a more recent version
+ LOG.warn("Plugin {} [{}] is ignored because the version {} of required plugin [{}] is not supported", plugin.getName(), plugin.getKey(),
+ requiredPlugin.getKey(), requiredPlugin.getMinimalVersion());
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void logInstalledPlugins() {
+ List<PluginInfo> orderedPlugins = Ordering.natural().sortedCopy(pluginInfosByKeys.values());
+ for (PluginInfo plugin : orderedPlugins) {
+ LOG.info("Deploy plugin {}", SLASH_JOINER.join(plugin.getName(), plugin.getVersion(), plugin.getImplementationBuild()));
+ }
+ }
+
+ private void loadInstances() {
+ pluginInstancesByKeys.putAll(loader.load(pluginInfosByKeys));
+
+ for (Map.Entry<String, Plugin> e : pluginInstancesByKeys.entrySet()) {
+ keysByClassLoader.put(e.getValue().getClass().getClassLoader(), e.getKey());
+ }
+ }
+
+ /**
+ * Uninstall a plugin and its dependents
+ */
+ public void uninstall(String pluginKey, File uninstallDir) {
+ Set<String> uninstallKeys = new HashSet<>();
+ uninstallKeys.add(pluginKey);
+ appendDependentPluginKeys(pluginKey, uninstallKeys);
+
+ for (String uninstallKey : uninstallKeys) {
+ PluginInfo info = getPluginInfo(uninstallKey);
+
+ try {
+ if (!getPluginFile(info).exists()) {
+ LOG.info("Plugin already uninstalled: {} [{}]", info.getName(), info.getKey());
+ continue;
+ }
+
+ LOG.info("Uninstalling plugin {} [{}]", info.getName(), info.getKey());
+
+ File masterFile = getPluginFile(info);
+ moveFileToDirectory(masterFile, uninstallDir, true);
+ } catch (IOException e) {
+ throw new IllegalStateException(format("Fail to uninstall plugin %s [%s]", info.getName(), info.getKey()), e);
+ }
+ }
+ }
+
+ public void cancelUninstalls(File uninstallDir) {
+ for (File file : listJarFiles(uninstallDir)) {
+ try {
+ moveFileToDirectory(file, fs.getInstalledPluginsDir(), false);
+ } catch (IOException e) {
+ throw new IllegalStateException("Fail to cancel plugin uninstalls", e);
+ }
+ }
+ }
+
+ /**
+ * Appends dependent plugins, only the ones that still exist in the plugins folder.
+ */
+ private void appendDependentPluginKeys(String pluginKey, Set<String> appendTo) {
+ for (PluginInfo otherPlugin : getPluginInfos()) {
+ if (!otherPlugin.getKey().equals(pluginKey)) {
+ for (PluginInfo.RequiredPlugin requirement : otherPlugin.getRequiredPlugins()) {
+ if (requirement.getKey().equals(pluginKey)) {
+ appendTo.add(otherPlugin.getKey());
+ appendDependentPluginKeys(otherPlugin.getKey(), appendTo);
+ }
+ }
+ }
+ }
+ }
+
+ private File getPluginFile(PluginInfo info) {
+ // we don't reuse info.getFile() just to be sure that file is located in from extensions/plugins
+ return new File(fs.getInstalledPluginsDir(), info.getNonNullJarFile().getName());
+ }
+
+ public Map<String, PluginInfo> getPluginInfosByKeys() {
+ return pluginInfosByKeys;
+ }
+
+ @Override
+ public Collection<PluginInfo> getPluginInfos() {
+ checkState(started.get(), NOT_STARTED_YET);
+ return ImmutableList.copyOf(pluginInfosByKeys.values());
+ }
+
+ @Override
+ public PluginInfo getPluginInfo(String key) {
+ checkState(started.get(), NOT_STARTED_YET);
+ PluginInfo info = pluginInfosByKeys.get(key);
+ if (info == null) {
+ throw new IllegalArgumentException(format("Plugin [%s] does not exist", key));
+ }
+ return info;
+ }
+
+ @Override
+ public Plugin getPluginInstance(String key) {
+ checkState(started.get(), NOT_STARTED_YET);
+ Plugin plugin = pluginInstancesByKeys.get(key);
+ checkArgument(plugin != null, "Plugin [%s] does not exist", key);
+ return plugin;
+ }
+
+ @Override
+ public boolean hasPlugin(String key) {
+ checkState(started.get(), NOT_STARTED_YET);
+ return pluginInfosByKeys.containsKey(key);
+ }
+
+ private static Collection<File> listJarFiles(File dir) {
+ if (dir.exists()) {
+ return FileUtils.listFiles(dir, JAR_FILE_EXTENSIONS, false);
+ }
+ return Collections.emptyList();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.plugins;
+
+import com.google.common.base.Optional;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+import org.apache.commons.io.IOUtils;
+import org.sonar.api.Properties;
+import org.sonar.api.Property;
+import org.sonar.api.config.Configuration;
+import org.sonar.api.utils.UriReader;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.process.ProcessProperties;
+import org.sonar.updatecenter.common.UpdateCenter;
+import org.sonar.updatecenter.common.UpdateCenterDeserializer;
+import org.sonar.updatecenter.common.UpdateCenterDeserializer.Mode;
+
+/**
+ * HTTP client to load data from the remote update center hosted at https://update.sonarsource.org.
+ *
+ * @since 2.4
+ */
+@Properties({
+ @Property(
+ key = UpdateCenterClient.URL_PROPERTY,
+ defaultValue = "https://update.sonarsource.org/update-center.properties",
+ name = "Update Center URL",
+ category = "Update Center",
+ project = false,
+ // hidden from UI
+ global = false)
+})
+public class UpdateCenterClient {
+
+ public static final String URL_PROPERTY = "sonar.updatecenter.url";
+ public static final int PERIOD_IN_MILLISECONDS = 60 * 60 * 1000;
+
+ private final URI uri;
+ private final UriReader uriReader;
+ private final boolean isActivated;
+ private UpdateCenter pluginCenter = null;
+ private long lastRefreshDate = 0;
+
+ public UpdateCenterClient(UriReader uriReader, Configuration config) throws URISyntaxException {
+ this.uriReader = uriReader;
+ this.uri = new URI(config.get(URL_PROPERTY).get());
+ this.isActivated = config.getBoolean(ProcessProperties.Property.SONAR_UPDATECENTER_ACTIVATE.getKey()).get();
+ Loggers.get(getClass()).info("Update center: " + uriReader.description(uri));
+ }
+
+ public Optional<UpdateCenter> getUpdateCenter() {
+ return getUpdateCenter(false);
+ }
+
+ public Optional<UpdateCenter> getUpdateCenter(boolean forceRefresh) {
+ if (!isActivated) {
+ return Optional.absent();
+ }
+
+ if (pluginCenter == null || forceRefresh || needsRefresh()) {
+ pluginCenter = init();
+ lastRefreshDate = System.currentTimeMillis();
+ }
+ return Optional.fromNullable(pluginCenter);
+ }
+
+ public Date getLastRefreshDate() {
+ return lastRefreshDate > 0 ? new Date(lastRefreshDate) : null;
+ }
+
+ private boolean needsRefresh() {
+ return lastRefreshDate + PERIOD_IN_MILLISECONDS < System.currentTimeMillis();
+ }
+
+ private UpdateCenter init() {
+ InputStream input = null;
+ try {
+ String content = uriReader.readString(uri, StandardCharsets.UTF_8);
+ java.util.Properties properties = new java.util.Properties();
+ input = IOUtils.toInputStream(content, StandardCharsets.UTF_8);
+ properties.load(input);
+ return new UpdateCenterDeserializer(Mode.PROD, true).fromProperties(properties);
+
+ } catch (Exception e) {
+ Loggers.get(getClass()).error("Fail to connect to update center", e);
+ return null;
+
+ } finally {
+ IOUtils.closeQuietly(input);
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.plugins;
+
+import com.google.common.base.Optional;
+import org.sonar.api.SonarRuntime;
+import org.sonar.updatecenter.common.UpdateCenter;
+import org.sonar.updatecenter.common.Version;
+
+/**
+ * @since 2.4
+ */
+public class UpdateCenterMatrixFactory {
+
+ private final UpdateCenterClient centerClient;
+ private final SonarRuntime sonarRuntime;
+ private final InstalledPluginReferentialFactory installedPluginReferentialFactory;
+
+ public UpdateCenterMatrixFactory(UpdateCenterClient centerClient, SonarRuntime runtime,
+ InstalledPluginReferentialFactory installedPluginReferentialFactory) {
+ this.centerClient = centerClient;
+ this.sonarRuntime = runtime;
+ this.installedPluginReferentialFactory = installedPluginReferentialFactory;
+ }
+
+ public Optional<UpdateCenter> getUpdateCenter(boolean refreshUpdateCenter) {
+ Optional<UpdateCenter> updateCenter = centerClient.getUpdateCenter(refreshUpdateCenter);
+ if (updateCenter.isPresent()) {
+ org.sonar.api.utils.Version fullVersion = sonarRuntime.getApiVersion();
+ org.sonar.api.utils.Version semanticVersion = org.sonar.api.utils.Version.create(fullVersion.major(), fullVersion.minor(), fullVersion.patch());
+
+ return Optional.of(updateCenter.get().setInstalledSonarVersion(Version.create(semanticVersion.toString())).registerInstalledPlugins(
+ installedPluginReferentialFactory.getInstalledPluginReferential())
+ .setDate(centerClient.getLastRefreshDate()));
+ }
+ return Optional.absent();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.plugins;
+
+import org.sonar.api.SonarRuntime;
+import org.sonar.api.server.ServerSide;
+import org.sonar.core.platform.PluginRepository;
+
+import static java.util.Collections.singleton;
+
+@ServerSide
+public class WebServerExtensionInstaller extends ServerExtensionInstaller {
+ public WebServerExtensionInstaller(SonarRuntime sonarRuntime, PluginRepository pluginRepository) {
+ super(sonarRuntime, pluginRepository, singleton(ServerSide.class));
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.plugins.edition;
+
+import java.util.Arrays;
+import org.sonar.core.platform.PluginInfo;
+import org.sonar.updatecenter.common.Plugin;
+
+public final class EditionBundledPlugins {
+
+ private static final String SONARSOURCE_ORGANIZATION = "SonarSource";
+ private static final String[] SONARSOURCE_COMMERCIAL_LICENSES = {"SonarSource", "Commercial"};
+
+ private EditionBundledPlugins() {
+ // prevents instantiation
+ }
+
+ public static boolean isEditionBundled(Plugin plugin) {
+ return SONARSOURCE_ORGANIZATION.equalsIgnoreCase(plugin.getOrganization())
+ && Arrays.stream(SONARSOURCE_COMMERCIAL_LICENSES).anyMatch(s -> s.equalsIgnoreCase(plugin.getLicense()));
+ }
+
+ public static boolean isEditionBundled(PluginInfo pluginInfo) {
+ return SONARSOURCE_ORGANIZATION.equalsIgnoreCase(pluginInfo.getOrganizationName())
+ && Arrays.stream(SONARSOURCE_COMMERCIAL_LICENSES).anyMatch(s -> s.equalsIgnoreCase(pluginInfo.getLicense()));
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.plugins.edition;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.plugins;
+
+import javax.annotation.ParametersAreNonnullByDefault;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.project;
+
+import java.util.Set;
+import org.sonar.api.server.ServerSide;
+
+@ServerSide
+public interface ProjectLifeCycleListener {
+ /**
+ * This method is called after the specified projects have been deleted.
+ */
+ void onProjectsDeleted(Set<Project> projects);
+
+ /**
+ * This method is called after the specified projects have been deleted.
+ */
+ void onProjectBranchesDeleted(Set<Project> projects);
+
+ /**
+ * This method is called after the specified projects' keys have been modified.
+ */
+ void onProjectsRekeyed(Set<RekeyedProject> rekeyedProjects);
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.project;
+
+import java.util.Set;
+
+public interface ProjectLifeCycleListeners {
+ /**
+ * This method is called after the specified projects have been deleted and will call method
+ * {@link ProjectLifeCycleListener#onProjectsDeleted(Set) onProjectsDeleted(Set)} of all known
+ * {@link ProjectLifeCycleListener} implementations.
+ * <p>
+ * This method ensures all {@link ProjectLifeCycleListener} implementations are called, even if one or more of
+ * them fail with an exception.
+ */
+ void onProjectsDeleted(Set<Project> projects);
+
+ /**
+ * This method is called after the specified project branches have been deleted and will call method
+ * {@link ProjectLifeCycleListener#onProjectBranchesDeleted(Set)} of all known
+ * {@link ProjectLifeCycleListener} implementations.
+ * <p>
+ * This method ensures all {@link ProjectLifeCycleListener} implementations are called, even if one or more of
+ * them fail with an exception.
+ */
+ void onProjectBranchesDeleted(Set<Project> projects);
+
+ /**
+ * This method is called after the specified project's key has been changed and will call method
+ * {@link ProjectLifeCycleListener#onProjectsRekeyed(Set) onProjectsRekeyed(Set)} of all known
+ * {@link ProjectLifeCycleListener} implementations.
+ * <p>
+ * This method ensures all {@link ProjectLifeCycleListener} implementations are called, even if one or more of
+ * them fail with an exception.
+ */
+ void onProjectsRekeyed(Set<RekeyedProject> rekeyedProjects);
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.project;
+
+import java.util.Arrays;
+import java.util.Set;
+import java.util.function.Consumer;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class ProjectLifeCycleListenersImpl implements ProjectLifeCycleListeners {
+ private static final Logger LOG = Loggers.get(ProjectLifeCycleListenersImpl.class);
+
+ private final ProjectLifeCycleListener[] listeners;
+
+ /**
+ * Used by Pico when there is no ProjectLifeCycleListener implementation in container.
+ */
+ public ProjectLifeCycleListenersImpl() {
+ this.listeners = new ProjectLifeCycleListener[0];
+ }
+
+ /**
+ * Used by Pico when there is at least one ProjectLifeCycleListener implementation in container.
+ */
+ public ProjectLifeCycleListenersImpl(ProjectLifeCycleListener[] listeners) {
+ this.listeners = listeners;
+ }
+
+ @Override
+ public void onProjectsDeleted(Set<Project> projects) {
+ checkNotNull(projects, "projects can't be null");
+ if (projects.isEmpty()) {
+ return;
+ }
+
+ Arrays.stream(listeners)
+ .forEach(safelyCallListener(listener -> listener.onProjectsDeleted(projects)));
+ }
+
+ @Override
+ public void onProjectBranchesDeleted(Set<Project> projects) {
+ checkNotNull(projects, "projects can't be null");
+ if (projects.isEmpty()) {
+ return;
+ }
+
+ Arrays.stream(listeners)
+ .forEach(safelyCallListener(listener -> listener.onProjectBranchesDeleted(projects)));
+ }
+
+ @Override
+ public void onProjectsRekeyed(Set<RekeyedProject> rekeyedProjects) {
+ checkNotNull(rekeyedProjects, "rekeyedProjects can't be null");
+ if (rekeyedProjects.isEmpty()) {
+ return;
+ }
+
+ Arrays.stream(listeners)
+ .forEach(safelyCallListener(listener -> listener.onProjectsRekeyed(rekeyedProjects)));
+ }
+
+ private static Consumer<ProjectLifeCycleListener> safelyCallListener(Consumer<ProjectLifeCycleListener> task) {
+ return listener -> {
+ try {
+ task.accept(listener);
+ } catch (Error | Exception e) {
+ LOG.error("Call on ProjectLifeCycleListener \"{}\" failed", listener.getClass(), e);
+ }
+ };
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.project;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public final class RekeyedProject {
+ private final Project project;
+ private final String previousKey;
+
+ public RekeyedProject(Project project, String previousKey) {
+ this.project = checkNotNull(project, "project can't be null");
+ this.previousKey = checkNotNull(previousKey, "previousKey can't be null");
+ }
+
+ public Project getProject() {
+ return project;
+ }
+
+ public String getPreviousKey() {
+ return previousKey;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ RekeyedProject that = (RekeyedProject) o;
+ return Objects.equals(project, that.project) &&
+ Objects.equals(previousKey, that.previousKey);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(project, previousKey);
+ }
+
+ @Override
+ public String toString() {
+ return "RekeyedProject{" +
+ "project=" + project +
+ ", previousKey='" + previousKey + '\'' +
+ '}';
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.project;
+
+import java.util.List;
+
+import static java.util.Arrays.stream;
+import static org.sonar.core.util.stream.MoreCollectors.toList;
+
+public enum Visibility {
+
+ PRIVATE(true, "private"),
+ PUBLIC(false, "public");
+
+ private static final List<String> LABELS = stream(values()).map(Visibility::getLabel).collect(toList(values().length));
+
+ private final boolean isPrivate;
+ private final String label;
+
+ Visibility(boolean isPrivate, String label) {
+ this.isPrivate = isPrivate;
+ this.label = label;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ boolean isPrivate() {
+ return isPrivate;
+ }
+
+ public static String getLabel(boolean isPrivate) {
+ return stream(values())
+ .filter(v -> v.isPrivate == isPrivate)
+ .map(Visibility::getLabel)
+ .findAny()
+ .orElseThrow(() -> new IllegalStateException("Invalid visibility boolean '" + isPrivate + "'"));
+ }
+
+ public static boolean isPrivate(String label) {
+ return parseVisibility(label).isPrivate();
+ }
+
+ public static Visibility parseVisibility(String label) {
+ return stream(values())
+ .filter(v -> v.label.equals(label))
+ .findAny()
+ .orElseThrow(() -> new IllegalStateException("Invalid visibility label '" + label + "'"));
+ }
+
+ public static List<String> getLabels() {
+ return LABELS;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.project;
+
+import javax.annotation.ParametersAreNonnullByDefault;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.qualitygate;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Store number of projects in warning in order for the web service api/components/search to know if warning value should be return in the quality gate facet.
+ * The value is updated each time the daemon {@link ProjectsInWarningDaemon} is executed
+ */
+public class ProjectsInWarning {
+
+ private Long projectsInWarning;
+
+ public void update(long projectsInWarning) {
+ this.projectsInWarning = projectsInWarning;
+ }
+
+ public long count() {
+ checkArgument(isInitialized(), "Initialization has not be done");
+ return projectsInWarning;
+ }
+
+ boolean isInitialized() {
+ return projectsInWarning != null;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.qualitygate.changeevent;
+
+import java.util.Optional;
+import java.util.function.Supplier;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+import org.sonar.api.config.Configuration;
+import org.sonar.api.measures.Metric;
+import org.sonar.db.component.BranchDto;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.SnapshotDto;
+import org.sonar.server.qualitygate.EvaluatedQualityGate;
+
+import static java.util.Objects.requireNonNull;
+
+@Immutable
+public class QGChangeEvent {
+ private final ComponentDto project;
+ private final BranchDto branch;
+ private final SnapshotDto analysis;
+ private final Configuration projectConfiguration;
+ private final Metric.Level previousStatus;
+ private final Supplier<Optional<EvaluatedQualityGate>> qualityGateSupplier;
+
+ public QGChangeEvent(ComponentDto project, BranchDto branch, SnapshotDto analysis, Configuration projectConfiguration,
+ @Nullable Metric.Level previousStatus, Supplier<Optional<EvaluatedQualityGate>> qualityGateSupplier) {
+ this.project = requireNonNull(project, "project can't be null");
+ this.branch = requireNonNull(branch, "branch can't be null");
+ this.analysis = requireNonNull(analysis, "analysis can't be null");
+ this.projectConfiguration = requireNonNull(projectConfiguration, "projectConfiguration can't be null");
+ this.previousStatus = previousStatus;
+ this.qualityGateSupplier = requireNonNull(qualityGateSupplier, "qualityGateSupplier can't be null");
+ }
+
+ public BranchDto getBranch() {
+ return branch;
+ }
+
+ public ComponentDto getProject() {
+ return project;
+ }
+
+ public SnapshotDto getAnalysis() {
+ return analysis;
+ }
+
+ public Configuration getProjectConfiguration() {
+ return projectConfiguration;
+ }
+
+ public Optional<Metric.Level> getPreviousStatus() {
+ return Optional.ofNullable(previousStatus);
+ }
+
+ public Supplier<Optional<EvaluatedQualityGate>> getQualityGateSupplier() {
+ return qualityGateSupplier;
+ }
+
+ @Override
+ public String toString() {
+ return "QGChangeEvent{" +
+ "project=" + toString(project) +
+ ", branch=" + toString(branch) +
+ ", analysis=" + toString(analysis) +
+ ", projectConfiguration=" + projectConfiguration +
+ ", previousStatus=" + previousStatus +
+ ", qualityGateSupplier=" + qualityGateSupplier +
+ '}';
+ }
+
+ private static String toString(ComponentDto project) {
+ return project.uuid() + ":" + project.getKey();
+ }
+
+ private static String toString(BranchDto branch) {
+ return branch.getBranchType() + ":" + branch.getUuid() + ":" + branch.getProjectUuid() + ":" + branch.getMergeBranchUuid();
+ }
+
+ private static String toString(SnapshotDto analysis) {
+ return analysis.getUuid() + ":" + analysis.getCreatedAt();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.qualitygate.changeevent;
+
+import java.util.EnumSet;
+import java.util.Set;
+import org.sonar.api.rules.RuleType;
+import org.sonar.api.server.ServerSide;
+
+@ServerSide
+public interface QGChangeEventListener {
+ /**
+ * Called consequently to a change done on one or more issue of a given project.
+ *
+ * @param qualityGateEvent can not be {@code null}
+ * @param changedIssues can not be {@code null} nor empty
+ */
+ void onIssueChanges(QGChangeEvent qualityGateEvent, Set<ChangedIssue> changedIssues);
+
+ interface ChangedIssue {
+
+ String getKey();
+
+ Status getStatus();
+
+ RuleType getType();
+
+ String getSeverity();
+
+ default boolean isNotClosed() {
+ return !Status.CLOSED_STATUSES.contains(getStatus());
+ }
+ }
+
+ enum Status {
+ OPEN,
+ CONFIRMED,
+ REOPENED,
+ RESOLVED_FP,
+ RESOLVED_WF,
+ RESOLVED_FIXED,
+ TO_REVIEW,
+ IN_REVIEW,
+ REVIEWED;
+
+ protected static final Set<Status> CLOSED_STATUSES = EnumSet.of(CONFIRMED, RESOLVED_FIXED, RESOLVED_FP, RESOLVED_WF);
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.qualitygate.changeevent;
+
+import java.util.Collection;
+import java.util.List;
+import org.sonar.core.issue.DefaultIssue;
+
+public interface QGChangeEventListeners {
+
+ void broadcastOnIssueChange(List<DefaultIssue> changedIssues, Collection<QGChangeEvent> qgChangeEvents);
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.qualitygate.changeevent;
+
+import com.google.common.collect.Multimap;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.rules.RuleType;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.util.stream.MoreCollectors;
+import org.sonar.server.qualitygate.changeevent.QGChangeEventListener.ChangedIssue;
+
+import static java.lang.String.format;
+import static org.sonar.core.util.stream.MoreCollectors.toSet;
+
+/**
+ * Broadcast a given collection of {@link QGChangeEvent} for a specific trigger to all the registered
+ * {@link QGChangeEventListener} in Pico.
+ *
+ * This class ensures that an {@link Exception} occurring calling one of the {@link QGChangeEventListener} doesn't
+ * prevent from calling the others.
+ */
+public class QGChangeEventListenersImpl implements QGChangeEventListeners {
+ private static final Logger LOG = Loggers.get(QGChangeEventListenersImpl.class);
+
+ private final QGChangeEventListener[] listeners;
+
+ /**
+ * Used by Pico when there is no QGChangeEventListener instance in container.
+ */
+ public QGChangeEventListenersImpl() {
+ this.listeners = new QGChangeEventListener[0];
+ }
+
+ public QGChangeEventListenersImpl(QGChangeEventListener[] listeners) {
+ this.listeners = listeners;
+ }
+
+ @Override
+ public void broadcastOnIssueChange(List<DefaultIssue> issues, Collection<QGChangeEvent> changeEvents) {
+ if (listeners.length == 0 || issues.isEmpty() || changeEvents.isEmpty()) {
+ return;
+ }
+
+ try {
+ Multimap<String, QGChangeEvent> eventsByComponentUuid = changeEvents.stream()
+ .collect(MoreCollectors.index(t -> t.getProject().uuid()));
+ Multimap<String, DefaultIssue> issueByComponentUuid = issues.stream()
+ .collect(MoreCollectors.index(DefaultIssue::projectUuid));
+
+ issueByComponentUuid.asMap()
+ .forEach((componentUuid, value) -> {
+ Collection<QGChangeEvent> qgChangeEvents = eventsByComponentUuid.get(componentUuid);
+ if (!qgChangeEvents.isEmpty()) {
+ Set<ChangedIssue> changedIssues = value.stream()
+ .map(ChangedIssueImpl::new)
+ .collect(toSet());
+ qgChangeEvents
+ .forEach(changeEvent -> Arrays.stream(listeners)
+ .forEach(listener -> broadcastTo(changedIssues, changeEvent, listener)));
+ }
+ });
+ } catch (Error e) {
+ LOG.warn(format("Broadcasting to listeners failed for %s events", changeEvents.size()), e);
+ }
+ }
+
+ private static void broadcastTo(Set<ChangedIssue> changedIssues, QGChangeEvent changeEvent, QGChangeEventListener listener) {
+ try {
+ LOG.trace("calling onChange() on listener {} for events {}...", listener.getClass().getName(), changeEvent);
+ listener.onIssueChanges(changeEvent, changedIssues);
+ } catch (Exception e) {
+ LOG.warn(format("onChange() call failed on listener %s for events %s", listener.getClass().getName(), changeEvent), e);
+ }
+ }
+
+ static class ChangedIssueImpl implements ChangedIssue {
+ private final String key;
+ private final QGChangeEventListener.Status status;
+ private final RuleType type;
+ private final String severity;
+
+ ChangedIssueImpl(DefaultIssue issue) {
+ this.key = issue.key();
+ this.status = statusOf(issue);
+ this.type = issue.type();
+ this.severity = issue.severity();
+ }
+
+ static QGChangeEventListener.Status statusOf(DefaultIssue issue) {
+ switch (issue.status()) {
+ case Issue.STATUS_OPEN:
+ return QGChangeEventListener.Status.OPEN;
+ case Issue.STATUS_CONFIRMED:
+ return QGChangeEventListener.Status.CONFIRMED;
+ case Issue.STATUS_REOPENED:
+ return QGChangeEventListener.Status.REOPENED;
+ case Issue.STATUS_TO_REVIEW:
+ return QGChangeEventListener.Status.TO_REVIEW;
+ case Issue.STATUS_IN_REVIEW:
+ return QGChangeEventListener.Status.IN_REVIEW;
+ case Issue.STATUS_REVIEWED:
+ return QGChangeEventListener.Status.REVIEWED;
+ case Issue.STATUS_RESOLVED:
+ return statusOfResolved(issue);
+ default:
+ throw new IllegalStateException("Unexpected status: " + issue.status());
+ }
+ }
+
+ private static QGChangeEventListener.Status statusOfResolved(DefaultIssue issue) {
+ String resolution = issue.resolution();
+ Objects.requireNonNull(resolution, "A resolved issue should have a resolution");
+ switch (resolution) {
+ case Issue.RESOLUTION_FALSE_POSITIVE:
+ return QGChangeEventListener.Status.RESOLVED_FP;
+ case Issue.RESOLUTION_WONT_FIX:
+ return QGChangeEventListener.Status.RESOLVED_WF;
+ case Issue.RESOLUTION_FIXED:
+ return QGChangeEventListener.Status.RESOLVED_FIXED;
+ default:
+ throw new IllegalStateException("Unexpected resolution for a resolved issue: " + resolution);
+ }
+ }
+
+ @Override
+ public String getKey() {
+ return key;
+ }
+
+ @Override
+ public QGChangeEventListener.Status getStatus() {
+ return status;
+ }
+
+ @Override
+ public RuleType getType() {
+ return type;
+ }
+
+ @Override
+ public String getSeverity() {
+ return severity;
+ }
+
+ @Override
+ public String toString() {
+ return "ChangedIssueImpl{" +
+ "key='" + key + '\'' +
+ ", status=" + status +
+ ", type=" + type +
+ ", severity=" + severity +
+ '}';
+ }
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.qualitygate.changeevent;
+
+public enum Trigger {
+ ISSUE_CHANGE
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.qualitygate.changeevent;
+
+import javax.annotation.ParametersAreNonnullByDefault;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.qualityprofile;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class BulkChangeResult {
+
+ private final List<String> errors = new ArrayList<>();
+ private int succeeded = 0;
+ private int failed = 0;
+ private final List<ActiveRuleChange> changes = new ArrayList<>();
+
+ public List<String> getErrors() {
+ return errors;
+ }
+
+ public int countSucceeded() {
+ return succeeded;
+ }
+
+ public int countFailed() {
+ return failed;
+ }
+
+ void incrementSucceeded() {
+ succeeded++;
+ }
+
+ void incrementFailed() {
+ failed++;
+ }
+
+ void addChanges(Collection<ActiveRuleChange> c) {
+ this.changes.addAll(c);
+ }
+
+ public List<ActiveRuleChange> getChanges() {
+ return changes;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.qualityprofile;
+
+import java.util.Collection;
+import java.util.List;
+import javax.annotation.Nullable;
+import org.sonar.api.server.ServerSide;
+import org.sonar.db.DbSession;
+import org.sonar.db.qualityprofile.QProfileDto;
+import org.sonar.db.rule.RuleDefinitionDto;
+import org.sonar.server.rule.index.RuleQuery;
+
+/**
+ * Operations related to activation and deactivation of rules on user profiles.
+ */
+@ServerSide
+public interface QProfileRules {
+
+ /**
+ * Activate multiple rules at once on a Quality profile.
+ * Db session is committed and Elasticsearch indices are updated.
+ * If an activation fails to be executed, then all others are
+ * canceled, db session is not committed and an exception is
+ * thrown.
+ */
+ List<ActiveRuleChange> activateAndCommit(DbSession dbSession, QProfileDto profile, Collection<RuleActivation> activations);
+
+ /**
+ * Same as {@link #activateAndCommit(DbSession, QProfileDto, Collection)} except
+ * that:
+ * - rules are loaded from search engine
+ * - rules are activated with default parameters
+ * - an activation failure does not break others. No exception is thrown.
+ */
+ BulkChangeResult bulkActivateAndCommit(DbSession dbSession, QProfileDto profile, RuleQuery ruleQuery, @Nullable String severity);
+
+ List<ActiveRuleChange> deactivateAndCommit(DbSession dbSession, QProfileDto profile, Collection<Integer> ruleIds);
+
+ BulkChangeResult bulkDeactivateAndCommit(DbSession dbSession, QProfileDto profile, RuleQuery ruleQuery);
+
+ /**
+ * Delete a rule from all Quality profiles. Db session is not committed. As a
+ * consequence Elasticsearch indices are NOT updated.
+ */
+ List<ActiveRuleChange> deleteRule(DbSession dbSession, RuleDefinitionDto rule);
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.qualityprofile;
+
+import com.google.common.base.Strings;
+import java.util.HashMap;
+import java.util.Map;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+import org.sonar.api.rule.Severity;
+
+/**
+ * The request for activation.
+ */
+@Immutable
+public class RuleActivation {
+
+ private final int ruleId;
+ private final boolean reset;
+ private final String severity;
+ private final Map<String, String> parameters = new HashMap<>();
+
+ private RuleActivation(int ruleId, boolean reset, @Nullable String severity, @Nullable Map<String, String> parameters) {
+ this.ruleId = ruleId;
+ this.reset = reset;
+ this.severity = severity;
+ if (severity != null && !Severity.ALL.contains(severity)) {
+ throw new IllegalArgumentException("Unknown severity: " + severity);
+ }
+ if (parameters != null) {
+ for (Map.Entry<String, String> entry : parameters.entrySet()) {
+ this.parameters.put(entry.getKey(), Strings.emptyToNull(entry.getValue()));
+ }
+ }
+ }
+
+ public static RuleActivation createReset(int ruleId) {
+ return new RuleActivation(ruleId, true, null, null);
+ }
+
+ public static RuleActivation create(int ruleId, @Nullable String severity, @Nullable Map<String, String> parameters) {
+ return new RuleActivation(ruleId, false, severity, parameters);
+ }
+
+ public static RuleActivation create(int ruleId) {
+ return create(ruleId, null, null);
+ }
+
+ /**
+ * Optional severity. Use the parent severity or default rule severity if null.
+ */
+ @CheckForNull
+ public String getSeverity() {
+ return severity;
+ }
+
+ public int getRuleId() {
+ return ruleId;
+ }
+
+ @CheckForNull
+ public String getParameter(String key) {
+ return parameters.get(key);
+ }
+
+ public boolean hasParameter(String key) {
+ return parameters.containsKey(key);
+ }
+
+ public boolean isReset() {
+ return reset;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.qualityprofile;
+
+import javax.annotation.ParametersAreNonnullByDefault;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.setting;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import org.sonar.api.config.Configuration;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentDto;
+
+import static java.lang.String.format;
+import static java.util.Objects.requireNonNull;
+
+public interface ProjectConfigurationLoader {
+ /**
+ * Loads configuration for the specified components.
+ *
+ * <p>
+ * Returns the applicable component configuration with most specific configuration overriding more global ones
+ * (eg. global > project > branch).
+ *
+ * <p>
+ * Any component is accepted but SQ only supports specific properties for projects and branches.
+ */
+ Map<String, Configuration> loadProjectConfigurations(DbSession dbSession, Set<ComponentDto> projects);
+
+ default Configuration loadProjectConfiguration(DbSession dbSession, ComponentDto project) {
+ Map<String, Configuration> configurations = loadProjectConfigurations(dbSession, Collections.singleton(project));
+ return requireNonNull(configurations.get(project.uuid()), () -> format("Configuration for project '%s' is not found", project.getKey()));
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.setting;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import org.sonar.api.config.Configuration;
+import org.sonar.api.config.Settings;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.property.PropertyDto;
+
+import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
+
+public class ProjectConfigurationLoaderImpl implements ProjectConfigurationLoader {
+ private final Settings globalSettings;
+ private final DbClient dbClient;
+
+ public ProjectConfigurationLoaderImpl(Settings globalSettings, DbClient dbClient) {
+ this.globalSettings = globalSettings;
+ this.dbClient = dbClient;
+ }
+
+ @Override
+ public Map<String, Configuration> loadProjectConfigurations(DbSession dbSession, Set<ComponentDto> projects) {
+ Set<String> mainBranchDbKeys = projects.stream().map(ComponentDto::getKey).collect(Collectors.toSet());
+ Map<String, ChildSettings> mainBranchSettingsByDbKey = loadMainBranchConfigurations(dbSession, mainBranchDbKeys);
+ return projects.stream()
+ .collect(uniqueIndex(ComponentDto::uuid, component -> {
+ if (component.getDbKey().equals(component.getKey())) {
+ return mainBranchSettingsByDbKey.get(component.getKey()).asConfiguration();
+ }
+
+ ChildSettings settings = new ChildSettings(mainBranchSettingsByDbKey.get(component.getKey()));
+ dbClient.propertiesDao()
+ .selectProjectProperties(dbSession, component.getDbKey())
+ .forEach(property -> settings.setProperty(property.getKey(), property.getValue()));
+ return settings.asConfiguration();
+ }));
+ }
+
+ private Map<String, ChildSettings> loadMainBranchConfigurations(DbSession dbSession, Set<String> dbKeys) {
+ return dbKeys.stream().collect(uniqueIndex(Function.identity(), dbKey -> {
+ ChildSettings settings = new ChildSettings(globalSettings);
+ List<PropertyDto> propertyDtos = dbClient.propertiesDao()
+ .selectProjectProperties(dbSession, dbKey);
+ propertyDtos
+ .forEach(property -> settings.setProperty(property.getKey(), property.getValue()));
+ return settings;
+ }));
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.setting;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.sonar.api.config.GlobalPropertyChangeHandler;
+
+import javax.annotation.Nullable;
+
+public class SettingsChangeNotifier {
+
+ @VisibleForTesting
+ GlobalPropertyChangeHandler[] changeHandlers;
+
+ public SettingsChangeNotifier(GlobalPropertyChangeHandler[] changeHandlers) {
+ this.changeHandlers = changeHandlers;
+ }
+
+ public SettingsChangeNotifier() {
+ this(new GlobalPropertyChangeHandler[0]);
+ }
+
+ public void onGlobalPropertyChange(String key, @Nullable String value) {
+ GlobalPropertyChangeHandler.PropertyChange change = GlobalPropertyChangeHandler.PropertyChange.create(key, value);
+ for (GlobalPropertyChangeHandler changeHandler : changeHandlers) {
+ changeHandler.onChange(change);
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.setting;
+
+import javax.annotation.ParametersAreNonnullByDefault;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.telemetry;
+
+import java.util.Optional;
+import org.sonar.api.server.ServerSide;
+
+@ServerSide
+public interface LicenseReader {
+ Optional<License> read();
+
+ interface License {
+ String getType();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.telemetry;
+
+import javax.annotation.ParametersAreNonnullByDefault;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.util;
+
+import java.util.List;
+import javax.annotation.Nullable;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.PropertyType;
+
+import static org.sonar.server.exceptions.BadRequestException.checkRequest;
+
+public class BooleanTypeValidation implements TypeValidation {
+
+ @Override
+ public String key() {
+ return PropertyType.BOOLEAN.name();
+ }
+
+ @Override
+ public void validate(String value, @Nullable List<String> options) {
+ checkRequest(StringUtils.equalsIgnoreCase(value, "true") || StringUtils.equalsIgnoreCase(value, "false"),
+ "Value '%s' must be one of \"true\" or \"false\".", value);
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.util;
+
+import java.util.List;
+import javax.annotation.Nullable;
+import org.sonar.api.PropertyType;
+import org.sonar.server.exceptions.BadRequestException;
+
+import static java.lang.String.format;
+
+public class FloatTypeValidation implements TypeValidation {
+
+ @Override
+ public String key() {
+ return PropertyType.FLOAT.name();
+ }
+
+ @Override
+ public void validate(String value, @Nullable List<String> options) {
+ try {
+ Double.parseDouble(value);
+ } catch (NumberFormatException e) {
+ throw BadRequestException.create(format("Value '%s' must be an floating point number.", value));
+ }
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.util;
+
+import org.sonar.api.ce.ComputeEngineSide;
+import org.sonar.api.server.ServerSide;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+
+/**
+ * Provide a simple mechanism to manage global locks across multiple nodes running in a cluster.
+ * In the target use case multiple nodes try to execute something at around the same time,
+ * and only the first should succeed, and the rest do nothing.
+ */
+@ComputeEngineSide
+@ServerSide
+public class GlobalLockManager {
+
+ static final int DEFAULT_LOCK_DURATION_SECONDS = 180;
+
+ private final DbClient dbClient;
+
+ public GlobalLockManager(DbClient dbClient) {
+ this.dbClient = dbClient;
+ }
+
+ /**
+ * Try to acquire a lock on the given name in the default namespace,
+ * using the generic locking mechanism of {@see org.sonar.db.property.InternalPropertiesDao}.
+ */
+ public boolean tryLock(String name) {
+ return tryLock(name, DEFAULT_LOCK_DURATION_SECONDS);
+ }
+
+ public boolean tryLock(String name, int durationSecond) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ boolean success = dbClient.internalPropertiesDao().tryLock(dbSession, name, durationSecond);
+ dbSession.commit();
+ return success;
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.util;
+
+import java.util.List;
+import javax.annotation.Nullable;
+import org.sonar.api.PropertyType;
+import org.sonar.server.exceptions.BadRequestException;
+
+import static java.lang.String.format;
+
+public class IntegerTypeValidation implements TypeValidation {
+
+ @Override
+ public String key() {
+ return PropertyType.INTEGER.name();
+ }
+
+ @Override
+ public void validate(String value, @Nullable List<String> options) {
+ try {
+ Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ throw BadRequestException.create(format("Value '%s' must be an integer.", value));
+ }
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.util;
+
+import java.util.List;
+import javax.annotation.Nullable;
+import org.sonar.api.PropertyType;
+import org.sonar.server.exceptions.BadRequestException;
+
+import static java.lang.String.format;
+
+public class LongTypeValidation implements TypeValidation {
+ @Override
+ public String key() {
+ return PropertyType.LONG.name();
+ }
+
+ @Override
+ public void validate(String value, @Nullable List<String> options) {
+ try {
+ Long.parseLong(value);
+ } catch (NumberFormatException e) {
+ throw BadRequestException.create(format("Value '%s' must be a long.", value));
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.util;
+
+import java.util.List;
+import javax.annotation.Nullable;
+import org.sonar.api.PropertyType;
+import org.sonar.api.measures.Metric;
+import org.sonar.server.exceptions.BadRequestException;
+
+import static java.lang.String.format;
+
+public class MetricLevelTypeValidation implements TypeValidation {
+ @Override
+ public String key() {
+ return PropertyType.METRIC_LEVEL.name();
+ }
+
+ @Override
+ public void validate(String value, @Nullable List<String> options) {
+ try {
+ Metric.Level.valueOf(value);
+ } catch (IllegalArgumentException e) {
+ throw BadRequestException.create(format("Value '%s' must be one of \"OK\", \"ERROR\".", value));
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.util;
+
+import java.util.List;
+import javax.annotation.Nullable;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.PropertyType;
+
+import static org.sonar.server.exceptions.BadRequestException.checkRequest;
+
+public class StringListTypeValidation implements TypeValidation {
+
+ @Override
+ public String key() {
+ return PropertyType.SINGLE_SELECT_LIST.name();
+ }
+
+ @Override
+ public void validate(String value, @Nullable List<String> options) {
+ checkRequest(options == null || options.contains(value), "Value '%s' must be one of : %s.", value, StringUtils.join(options, ", "));
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.util;
+
+import org.sonar.api.PropertyType;
+
+import javax.annotation.Nullable;
+
+import java.util.List;
+
+public class StringTypeValidation implements TypeValidation {
+
+ @Override
+ public String key() {
+ return PropertyType.STRING.name();
+ }
+
+ @Override
+ public void validate(String value, @Nullable List<String> options) {
+ // Nothing to do
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.util;
+
+import org.sonar.api.PropertyType;
+
+import javax.annotation.Nullable;
+
+import java.util.List;
+
+public class TextTypeValidation implements TypeValidation {
+
+ @Override
+ public String key() {
+ return PropertyType.TEXT.name();
+ }
+
+ @Override
+ public void validate(String value, @Nullable List<String> options) {
+ // Nothing to do
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.util;
+
+import org.sonar.api.server.ServerSide;
+
+import javax.annotation.Nullable;
+
+import java.util.List;
+
+@ServerSide
+public interface TypeValidation {
+
+ String key();
+
+ void validate(String value, @Nullable List<String> options);
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.util;
+
+import org.sonar.core.platform.Module;
+
+public class TypeValidationModule extends Module {
+ @Override
+ protected void configureModule() {
+ add(
+ TypeValidations.class,
+ IntegerTypeValidation.class,
+ FloatTypeValidation.class,
+ BooleanTypeValidation.class,
+ TextTypeValidation.class,
+ StringTypeValidation.class,
+ StringListTypeValidation.class,
+ LongTypeValidation.class,
+ MetricLevelTypeValidation.class
+ );
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.util;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import java.util.List;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.sonar.api.server.ServerSide;
+
+import static org.sonar.server.exceptions.BadRequestException.checkRequest;
+
+@ServerSide
+public class TypeValidations {
+
+ private final List<TypeValidation> typeValidationList;
+
+ public TypeValidations(List<TypeValidation> typeValidationList) {
+ this.typeValidationList = typeValidationList;
+ }
+
+ public void validate(List<String> values, String type, List<String> options) {
+ TypeValidation typeValidation = findByKey(type);
+ for (String value : values) {
+ typeValidation.validate(value, options);
+ }
+ }
+
+ public void validate(String value, String type, @Nullable List<String> options) {
+ TypeValidation typeValidation = findByKey(type);
+ typeValidation.validate(value, options);
+ }
+
+ private TypeValidation findByKey(String key) {
+ TypeValidation typeValidation = Iterables.find(typeValidationList, new TypeValidationMatchKey(key), null);
+ checkRequest(typeValidation != null, "Type '%s' is not valid.", key);
+ return typeValidation;
+ }
+
+ private static class TypeValidationMatchKey implements Predicate<TypeValidation> {
+ private final String key;
+
+ public TypeValidationMatchKey(String key) {
+ this.key = key;
+ }
+
+ @Override
+ public boolean apply(@Nonnull TypeValidation input) {
+ return input.key().equals(key);
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.util;
+
+public class Validation {
+
+ public static final String CANT_BE_EMPTY_MESSAGE = "%s can't be empty";
+ public static final String IS_TOO_SHORT_MESSAGE = "%s is too short (minimum is %s characters)";
+ public static final String IS_TOO_LONG_MESSAGE = "%s is too long (maximum is %s characters)";
+ public static final String IS_ALREADY_USED_MESSAGE = "%s has already been taken";
+
+ private Validation() {
+ // only static methods
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.util;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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 java.io.File;
+import java.io.IOException;
+import java.util.Random;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.config.internal.MapSettings;
+import org.sonar.process.sharedmemoryfile.DefaultProcessCommands;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.process.ProcessEntryPoint.PROPERTY_PROCESS_INDEX;
+import static org.sonar.process.ProcessEntryPoint.PROPERTY_SHARED_PATH;
+
+public class ProcessCommandWrapperImplTest {
+ private static final int PROCESS_NUMBER = 2;
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private MapSettings settings = new MapSettings();
+
+ @Test
+ public void requestSQRestart_throws_IAE_if_process_index_property_not_set() {
+ ProcessCommandWrapperImpl processCommandWrapper = new ProcessCommandWrapperImpl(settings.asConfig());
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Property process.index is not set");
+
+ processCommandWrapper.requestSQRestart();
+ }
+
+ @Test
+ public void requestSQRestart_throws_IAE_if_process_shared_path_property_not_set() {
+ settings.setProperty(PROPERTY_PROCESS_INDEX, 1);
+ ProcessCommandWrapperImpl processCommandWrapper = new ProcessCommandWrapperImpl(settings.asConfig());
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Property process.sharedDir is not set");
+
+ processCommandWrapper.requestSQRestart();
+ }
+
+ @Test
+ public void requestSQRestart_updates_shareMemory_file() throws IOException {
+ File tmpDir = temp.newFolder().getAbsoluteFile();
+ settings.setProperty(PROPERTY_SHARED_PATH, tmpDir.getAbsolutePath());
+ settings.setProperty(PROPERTY_PROCESS_INDEX, PROCESS_NUMBER);
+
+ ProcessCommandWrapperImpl underTest = new ProcessCommandWrapperImpl(settings.asConfig());
+ underTest.requestSQRestart();
+
+ try (DefaultProcessCommands processCommands = DefaultProcessCommands.secondary(tmpDir, PROCESS_NUMBER)) {
+ assertThat(processCommands.askedForRestart()).isTrue();
+ }
+ }
+
+ @Test
+ public void requestSQStop_throws_IAE_if_process_shared_path_property_not_set() {
+ settings.setProperty(PROPERTY_PROCESS_INDEX, 1);
+ ProcessCommandWrapperImpl processCommandWrapper = new ProcessCommandWrapperImpl(settings.asConfig());
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Property process.sharedDir is not set");
+
+ processCommandWrapper.requestHardStop();
+ }
+
+ @Test
+ public void requestSQStop_updates_shareMemory_file() throws IOException {
+ File tmpDir = temp.newFolder().getAbsoluteFile();
+ settings.setProperty(PROPERTY_SHARED_PATH, tmpDir.getAbsolutePath());
+ settings.setProperty(PROPERTY_PROCESS_INDEX, PROCESS_NUMBER);
+
+ ProcessCommandWrapperImpl underTest = new ProcessCommandWrapperImpl(settings.asConfig());
+ underTest.requestHardStop();
+
+ try (DefaultProcessCommands processCommands = DefaultProcessCommands.secondary(tmpDir, PROCESS_NUMBER)) {
+ assertThat(processCommands.askedForHardStop()).isTrue();
+ }
+ }
+
+ @Test
+ public void notifyOperational_throws_IAE_if_process_sharedDir_property_not_set() {
+ settings.setProperty(PROPERTY_PROCESS_INDEX, 1);
+ ProcessCommandWrapperImpl processCommandWrapper = new ProcessCommandWrapperImpl(settings.asConfig());
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Property process.sharedDir is not set");
+
+ processCommandWrapper.notifyOperational();
+ }
+
+ @Test
+ public void notifyOperational_throws_IAE_if_process_index_property_not_set() {
+ ProcessCommandWrapperImpl processCommandWrapper = new ProcessCommandWrapperImpl(settings.asConfig());
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Property process.index is not set");
+
+ processCommandWrapper.notifyOperational();
+ }
+
+ @Test
+ public void notifyOperational_updates_shareMemory_file() throws IOException {
+ File tmpDir = temp.newFolder().getAbsoluteFile();
+ settings.setProperty(PROPERTY_SHARED_PATH, tmpDir.getAbsolutePath());
+ settings.setProperty(PROPERTY_PROCESS_INDEX, PROCESS_NUMBER);
+
+ ProcessCommandWrapperImpl underTest = new ProcessCommandWrapperImpl(settings.asConfig());
+ underTest.notifyOperational();
+
+ try (DefaultProcessCommands processCommands = DefaultProcessCommands.secondary(tmpDir, PROCESS_NUMBER)) {
+ assertThat(processCommands.isOperational()).isTrue();
+ }
+ }
+
+ @Test
+ public void isCeOperational_reads_shared_memory_operational_flag_in_location_3() throws IOException {
+ File tmpDir = temp.newFolder().getAbsoluteFile();
+ settings.setProperty(PROPERTY_SHARED_PATH, tmpDir.getAbsolutePath());
+
+ boolean expected = new Random().nextBoolean();
+ if (expected) {
+ try (DefaultProcessCommands processCommands = DefaultProcessCommands.secondary(tmpDir, 3)) {
+ processCommands.setOperational();
+ }
+ }
+
+ ProcessCommandWrapperImpl underTest = new ProcessCommandWrapperImpl(settings.asConfig());
+
+ assertThat(underTest.isCeOperational()).isEqualTo(expected);
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.branch;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class BranchFeatureProxyImplTest {
+
+ private BranchFeatureExtension branchFeatureExtension = mock(BranchFeatureExtension.class);
+
+ @Test
+ public void return_false_when_no_extension() {
+ assertThat(new BranchFeatureProxyImpl().isEnabled()).isFalse();
+ }
+
+ @Test
+ public void return_false_when_extension_returns_false() {
+ when(branchFeatureExtension.isEnabled()).thenReturn(false);
+ assertThat(new BranchFeatureProxyImpl(branchFeatureExtension).isEnabled()).isFalse();
+ }
+
+ @Test
+ public void return_true_when_extension_returns_ftrue() {
+ when(branchFeatureExtension.isEnabled()).thenReturn(true);
+ assertThat(new BranchFeatureProxyImpl(branchFeatureExtension).isEnabled()).isTrue();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.branch;
+
+import org.junit.rules.ExternalResource;
+
+public class BranchFeatureRule extends ExternalResource implements BranchFeatureProxy {
+
+ private boolean enabled;
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ @Override
+ protected void after() {
+ reset();
+ }
+
+ public void reset() {
+ this.enabled = false;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.exceptions;
+
+import java.util.Collections;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static java.util.Arrays.asList;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class BadRequestExceptionTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void text_error() {
+ BadRequestException exception = BadRequestException.create("error");
+ assertThat(exception.getMessage()).isEqualTo("error");
+ }
+
+ @Test
+ public void create_exception_from_list() {
+ BadRequestException underTest = BadRequestException.create(asList("error1", "error2"));
+
+ assertThat(underTest.errors()).containsOnly("error1", "error2");
+ }
+
+ @Test
+ public void create_exception_from_var_args() {
+ BadRequestException underTest = BadRequestException.create("error1", "error2");
+
+ assertThat(underTest.errors()).containsOnly("error1", "error2");
+ }
+
+ @Test
+ public void getMessage_return_first_error() {
+ BadRequestException underTest = BadRequestException.create(asList("error1", "error2"));
+
+ assertThat(underTest.getMessage()).isEqualTo("error1");
+ }
+
+ @Test
+ public void fail_when_creating_exception_with_empty_list() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("At least one error message is required");
+
+ BadRequestException.create(Collections.emptyList());
+ }
+
+ @Test
+ public void fail_when_creating_exception_with_one_empty_element() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Message cannot be empty");
+
+ BadRequestException.create(asList("error", ""));
+ }
+
+ @Test
+ public void fail_when_creating_exception_with_one_null_element() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Message cannot be empty");
+
+ BadRequestException.create(asList("error", null));
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.exceptions;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class MessageTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void create_message() {
+ Message message = Message.of("key1 %s", "param1");
+ assertThat(message.getMessage()).isEqualTo("key1 param1");
+ }
+
+ @Test
+ public void create_message_without_params() {
+ Message message = Message.of("key1");
+ assertThat(message.getMessage()).isEqualTo("key1");
+ }
+
+ @Test
+ public void fail_when_message_is_null() {
+ expectedException.expect(IllegalArgumentException.class);
+
+ Message.of(null);
+ }
+
+ @Test
+ public void fail_when_message_is_empty() {
+ expectedException.expect(IllegalArgumentException.class);
+
+ Message.of("");
+ }
+
+ @Test
+ public void test_equals_and_hashcode() {
+ Message message1 = Message.of("key1%s", "param1");
+ Message message2 = Message.of("key2%s", "param2");
+ Message message3 = Message.of("key1");
+ Message message4 = Message.of("key1%s", "param2");
+ Message sameAsMessage1 = Message.of("key1%s", "param1");
+
+ assertThat(message1).isEqualTo(message1);
+ assertThat(message1).isNotEqualTo(message2);
+ assertThat(message1).isNotEqualTo(message3);
+ assertThat(message1).isNotEqualTo(message4);
+ assertThat(message1).isEqualTo(sameAsMessage1);
+ assertThat(message1).isNotEqualTo(null);
+ assertThat(message1).isNotEqualTo(new Object());
+
+ assertThat(message1.hashCode()).isEqualTo(message1.hashCode());
+ assertThat(message1.hashCode()).isNotEqualTo(message2.hashCode());
+ assertThat(message1.hashCode()).isNotEqualTo(message3.hashCode());
+ assertThat(message1.hashCode()).isNotEqualTo(message4.hashCode());
+ assertThat(message1.hashCode()).isEqualTo(sameAsMessage1.hashCode());
+ }
+
+ @Test
+ public void to_string() {
+ assertThat(Message.of("key1 %s", "param1").toString()).isEqualTo("key1 param1");
+ assertThat(Message.of("key1").toString()).isEqualTo("key1");
+ assertThat(Message.of("key1", (Object[])null).toString()).isEqualTo("key1");
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.exceptions;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ServerExceptionTest {
+
+ @Test
+ public void should_create_exception_with_status() {
+ ServerException exception = new ServerException(400, "error!");
+ assertThat(exception.httpCode()).isEqualTo(400);
+ }
+
+ @Test
+ public void should_create_exception_with_status_and_message() {
+ ServerException exception = new ServerException(404, "Not found");
+ assertThat(exception.httpCode()).isEqualTo(404);
+ assertThat(exception.getMessage()).isEqualTo("Not found");
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.health;
+
+public class TestStandaloneHealthChecker implements HealthChecker {
+
+ private Health health = Health.newHealthCheckBuilder().setStatus(Health.Status.GREEN).build();
+
+ public void setHealth(Health h) {
+ this.health = h;
+ }
+
+ @Override
+ public Health checkNode() {
+ return health;
+ }
+
+ @Override
+ public ClusterHealth checkCluster() {
+ throw new IllegalStateException();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.plugins;
+
+import com.google.common.base.Optional;
+import java.io.File;
+import java.net.URI;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.mockito.ArgumentMatcher;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.sonar.api.utils.HttpDownloader;
+import org.sonar.core.platform.PluginInfo;
+import org.sonar.server.exceptions.BadRequestException;
+import org.sonar.server.platform.ServerFileSystem;
+import org.sonar.updatecenter.common.Plugin;
+import org.sonar.updatecenter.common.Release;
+import org.sonar.updatecenter.common.UpdateCenter;
+import org.sonar.updatecenter.common.Version;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.apache.commons.io.FileUtils.copyFileToDirectory;
+import static org.apache.commons.io.FileUtils.touch;
+import static org.apache.commons.io.FilenameUtils.separatorsToUnix;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.sonar.updatecenter.common.Version.create;
+
+public class PluginDownloaderTest {
+
+ @Rule
+ public TemporaryFolder testFolder = new TemporaryFolder();
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+ private File downloadDir;
+ private UpdateCenterMatrixFactory updateCenterMatrixFactory;
+ private UpdateCenter updateCenter;
+ private HttpDownloader httpDownloader;
+ private PluginDownloader pluginDownloader;
+
+ @Before
+ public void before() throws Exception {
+ updateCenterMatrixFactory = mock(UpdateCenterMatrixFactory.class);
+ updateCenter = mock(UpdateCenter.class);
+ when(updateCenterMatrixFactory.getUpdateCenter(anyBoolean())).thenReturn(Optional.of(updateCenter));
+
+ httpDownloader = mock(HttpDownloader.class);
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock inv) throws Throwable {
+ File toFile = (File) inv.getArguments()[1];
+ touch(toFile);
+ return null;
+ }
+ }).when(httpDownloader).download(any(URI.class), any(File.class));
+
+ ServerFileSystem fs = mock(ServerFileSystem.class);
+ downloadDir = testFolder.newFolder("downloads");
+ when(fs.getDownloadedPluginsDir()).thenReturn(downloadDir);
+
+ pluginDownloader = new PluginDownloader(updateCenterMatrixFactory, httpDownloader, fs);
+ }
+
+ @After
+ public void stop() {
+ pluginDownloader.stop();
+ }
+
+ @Test
+ public void clean_temporary_files_at_startup() throws Exception {
+ touch(new File(downloadDir, "sonar-php.jar"));
+ touch(new File(downloadDir, "sonar-js.jar.tmp"));
+ assertThat(downloadDir.listFiles()).hasSize(2);
+ pluginDownloader.start();
+
+ File[] files = downloadDir.listFiles();
+ assertThat(files).hasSize(1);
+ assertThat(files[0].getName()).isEqualTo("sonar-php.jar");
+ }
+
+ @Test
+ public void download_from_url() {
+ Plugin test = Plugin.factory("test");
+ Release test10 = new Release(test, "1.0").setDownloadUrl("http://server/test-1.0.jar");
+ test.addRelease(test10);
+
+ when(updateCenter.findInstallablePlugins("foo", create("1.0"))).thenReturn(newArrayList(test10));
+
+ pluginDownloader.start();
+ pluginDownloader.download("foo", create("1.0"));
+
+ // SONAR-4523: do not corrupt JAR files when restarting the server while a plugin is being downloaded.
+ // The JAR file is downloaded in a temp file
+ verify(httpDownloader).download(any(URI.class), argThat(new HasFileName("test-1.0.jar.tmp")));
+ assertThat(new File(downloadDir, "test-1.0.jar")).exists();
+ assertThat(new File(downloadDir, "test-1.0.jar.tmp")).doesNotExist();
+ }
+
+ @Test
+ public void download_when_update_center_is_unavailable_with_no_exception_thrown() {
+ when(updateCenterMatrixFactory.getUpdateCenter(anyBoolean())).thenReturn(Optional.absent());
+
+ Plugin test = Plugin.factory("test");
+ Release test10 = new Release(test, "1.0").setDownloadUrl("http://server/test-1.0.jar");
+ test.addRelease(test10);
+
+ pluginDownloader.start();
+ pluginDownloader.download("foo", create("1.0"));
+ }
+
+ /**
+ * SONAR-4685
+ */
+ @Test
+ public void download_from_redirect_url() {
+ Plugin test = Plugin.factory("plugintest");
+ Release test10 = new Release(test, "1.0").setDownloadUrl("http://server/redirect?r=release&g=test&a=test&v=1.0&e=jar");
+ test.addRelease(test10);
+
+ when(updateCenter.findInstallablePlugins("foo", create("1.0"))).thenReturn(newArrayList(test10));
+
+ pluginDownloader.start();
+ pluginDownloader.download("foo", create("1.0"));
+
+ // SONAR-4523: do not corrupt JAR files when restarting the server while a plugin is being downloaded.
+ // The JAR file is downloaded in a temp file
+ verify(httpDownloader).download(any(URI.class), argThat(new HasFileName("plugintest-1.0.jar.tmp")));
+ assertThat(new File(downloadDir, "plugintest-1.0.jar")).exists();
+ assertThat(new File(downloadDir, "plugintest-1.0.jar.tmp")).doesNotExist();
+ }
+
+ @Test
+ public void throw_exception_if_download_dir_is_invalid() throws Exception {
+ ServerFileSystem fs = mock(ServerFileSystem.class);
+ // download dir is a file instead of being a directory
+ File downloadDir = testFolder.newFile();
+ when(fs.getDownloadedPluginsDir()).thenReturn(downloadDir);
+
+ pluginDownloader = new PluginDownloader(updateCenterMatrixFactory, httpDownloader, fs);
+ try {
+ pluginDownloader.start();
+ fail();
+ } catch (IllegalStateException e) {
+ // ok
+ }
+ }
+
+ @Test
+ public void fail_if_no_compatible_plugin_found() {
+ expectedException.expect(BadRequestException.class);
+
+ pluginDownloader.download("foo", create("1.0"));
+ }
+
+ @Test
+ public void download_from_file() throws Exception {
+ Plugin test = Plugin.factory("test");
+ File file = testFolder.newFile("test-1.0.jar");
+ file.createNewFile();
+ Release test10 = new Release(test, "1.0").setDownloadUrl("file://" + separatorsToUnix(file.getCanonicalPath()));
+ test.addRelease(test10);
+
+ when(updateCenter.findInstallablePlugins("foo", create("1.0"))).thenReturn(newArrayList(test10));
+
+ pluginDownloader.start();
+ pluginDownloader.download("foo", create("1.0"));
+ verify(httpDownloader, never()).download(any(URI.class), any(File.class));
+ assertThat(noDownloadedFiles()).isGreaterThan(0);
+ }
+
+ @Test
+ public void throw_exception_if_could_not_download() {
+ Plugin test = Plugin.factory("test");
+ Release test10 = new Release(test, "1.0").setDownloadUrl("file://not_found");
+ test.addRelease(test10);
+
+ when(updateCenter.findInstallablePlugins("foo", create("1.0"))).thenReturn(newArrayList(test10));
+
+ pluginDownloader.start();
+ try {
+ pluginDownloader.download("foo", create("1.0"));
+ fail();
+ } catch (IllegalStateException e) {
+ // ok
+ }
+ }
+
+ @Test
+ public void throw_exception_if_download_fail() {
+ Plugin test = Plugin.factory("test");
+ Release test10 = new Release(test, "1.0").setDownloadUrl("http://server/test-1.0.jar");
+ test.addRelease(test10);
+ when(updateCenter.findInstallablePlugins("foo", create("1.0"))).thenReturn(newArrayList(test10));
+
+ doThrow(new RuntimeException()).when(httpDownloader).download(any(URI.class), any(File.class));
+
+ pluginDownloader.start();
+ try {
+ pluginDownloader.download("foo", create("1.0"));
+ fail();
+ } catch (IllegalStateException e) {
+ // ok
+ }
+ }
+
+ @Test
+ public void read_download_folder() throws Exception {
+ pluginDownloader.start();
+ assertThat(noDownloadedFiles()).isZero();
+
+ copyFileToDirectory(TestProjectUtils.jarOf("test-base-plugin"), downloadDir);
+
+ assertThat(pluginDownloader.getDownloadedPlugins()).hasSize(1);
+ PluginInfo info = pluginDownloader.getDownloadedPlugins().iterator().next();
+ assertThat(info.getKey()).isEqualTo("testbase");
+ assertThat(info.getName()).isEqualTo("Base Plugin");
+ assertThat(info.getVersion()).isEqualTo(Version.create("0.1-SNAPSHOT"));
+ assertThat(info.getMainClass()).isEqualTo("BasePlugin");
+ }
+
+ @Test
+ public void getDownloadedPluginFilenames_reads_plugin_info_of_files_in_download_folder() throws Exception {
+ pluginDownloader.start();
+ assertThat(pluginDownloader.getDownloadedPlugins()).hasSize(0);
+
+ File file1 = new File(downloadDir, "file1.jar");
+ file1.createNewFile();
+ File file2 = new File(downloadDir, "file2.jar");
+ file2.createNewFile();
+
+ assertThat(noDownloadedFiles()).isEqualTo(2);
+ }
+
+ @Test
+ public void cancel_downloads() throws Exception {
+ File file1 = new File(downloadDir, "file1.jar");
+ file1.createNewFile();
+ File file2 = new File(downloadDir, "file2.jar");
+ file2.createNewFile();
+
+ pluginDownloader.start();
+ assertThat(noDownloadedFiles()).isGreaterThan(0);
+ pluginDownloader.cancelDownloads();
+ assertThat(noDownloadedFiles()).isZero();
+ }
+
+ private int noDownloadedFiles() {
+ return downloadDir.listFiles((file, name) -> name.endsWith(".jar")).length;
+ }
+
+ // SONAR-5011
+ @Test
+ public void download_common_transitive_dependency() {
+ Plugin test1 = Plugin.factory("test1");
+ Release test1R = new Release(test1, "1.0").setDownloadUrl("http://server/test1-1.0.jar");
+ test1.addRelease(test1R);
+
+ Plugin test2 = Plugin.factory("test2");
+ Release test2R = new Release(test2, "1.0").setDownloadUrl("http://server/test2-1.0.jar");
+ test2.addRelease(test2R);
+
+ Plugin testDep = Plugin.factory("testdep");
+ Release testDepR = new Release(testDep, "1.0").setDownloadUrl("http://server/testdep-1.0.jar");
+ testDep.addRelease(testDepR);
+
+ when(updateCenter.findInstallablePlugins("test1", create("1.0"))).thenReturn(newArrayList(test1R, testDepR));
+ when(updateCenter.findInstallablePlugins("test2", create("1.0"))).thenReturn(newArrayList(test2R, testDepR));
+
+ pluginDownloader.start();
+ pluginDownloader.download("test1", create("1.0"));
+ pluginDownloader.download("test2", create("1.0"));
+
+ assertThat(new File(downloadDir, "test1-1.0.jar")).exists();
+ assertThat(new File(downloadDir, "test2-1.0.jar")).exists();
+ assertThat(new File(downloadDir, "testdep-1.0.jar")).exists();
+ }
+
+ class HasFileName implements ArgumentMatcher<File> {
+ private final String name;
+
+ HasFileName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public boolean matches(File file) {
+ return file.getName().equals(name);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.plugins;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.RandomStringUtils;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.config.internal.MapSettings;
+import org.sonar.core.platform.PluginInfo;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.server.plugins.PluginFileSystem.PROPERTY_PLUGIN_COMPRESSION_ENABLE;
+
+public class PluginFileSystemTest {
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ private MapSettings settings = new MapSettings();
+ private Path targetJarPath;
+ private Path targetFolder;
+ private Path sourceFolder;
+
+ @Before
+ public void setUp() throws IOException {
+ sourceFolder = temp.newFolder("source").toPath();
+ targetFolder = temp.newFolder("target").toPath();
+ targetJarPath = targetFolder.resolve("test.jar");
+ Files.createFile(targetJarPath);
+ }
+
+ @Test
+ public void add_plugin_to_list_of_installed_plugins() throws IOException {
+ File jar = touch(temp.newFolder(), "sonar-foo-plugin.jar");
+ PluginInfo info = new PluginInfo("foo");
+
+ PluginFileSystem underTest = new PluginFileSystem(settings.asConfig());
+ underTest.addInstalledPlugin(info, jar);
+
+ assertThat(underTest.getInstalledFiles()).hasSize(1);
+ InstalledPlugin installedPlugin = underTest.getInstalledPlugin("foo").get();
+ assertThat(installedPlugin.getCompressedJar()).isNull();
+ assertThat(installedPlugin.getLoadedJar().getFile().toPath()).isEqualTo(jar.toPath());
+ assertThat(installedPlugin.getPluginInfo()).isSameAs(info);
+ }
+
+ @Test
+ public void compress_jar_if_compression_enabled() throws IOException {
+ File jar = touch(temp.newFolder(), "sonar-foo-plugin.jar");
+ PluginInfo info = new PluginInfo("foo").setJarFile(jar);
+ // the JAR is copied somewhere else in order to be loaded by classloaders
+ File loadedJar = touch(temp.newFolder(), "sonar-foo-plugin.jar");
+
+ settings.setProperty(PROPERTY_PLUGIN_COMPRESSION_ENABLE, true);
+ PluginFileSystem underTest = new PluginFileSystem(settings.asConfig());
+ underTest.addInstalledPlugin(info, loadedJar);
+
+ assertThat(underTest.getInstalledFiles()).hasSize(1);
+
+ InstalledPlugin installedPlugin = underTest.getInstalledPlugin("foo").get();
+ assertThat(installedPlugin.getPluginInfo()).isSameAs(info);
+ assertThat(installedPlugin.getLoadedJar().getFile().toPath()).isEqualTo(loadedJar.toPath());
+ assertThat(installedPlugin.getCompressedJar().getFile())
+ .exists()
+ .isFile()
+ .hasName("sonar-foo-plugin.pack.gz")
+ .hasParent(loadedJar.getParentFile());
+ }
+
+ @Test
+ public void copy_and_use_existing_packed_jar_if_compression_enabled() throws IOException {
+ File jar = touch(temp.newFolder(), "sonar-foo-plugin.jar");
+ File packedJar = touch(jar.getParentFile(), "sonar-foo-plugin.pack.gz");
+ PluginInfo info = new PluginInfo("foo").setJarFile(jar);
+ // the JAR is copied somewhere else in order to be loaded by classloaders
+ File loadedJar = touch(temp.newFolder(), "sonar-foo-plugin.jar");
+
+ settings.setProperty(PROPERTY_PLUGIN_COMPRESSION_ENABLE, true);
+ PluginFileSystem underTest = new PluginFileSystem(settings.asConfig());
+ underTest.addInstalledPlugin(info, loadedJar);
+
+ assertThat(underTest.getInstalledFiles()).hasSize(1);
+
+ InstalledPlugin installedPlugin = underTest.getInstalledPlugin("foo").get();
+ assertThat(installedPlugin.getPluginInfo()).isSameAs(info);
+ assertThat(installedPlugin.getLoadedJar().getFile().toPath()).isEqualTo(loadedJar.toPath());
+ assertThat(installedPlugin.getCompressedJar().getFile())
+ .exists()
+ .isFile()
+ .hasName(packedJar.getName())
+ .hasParent(loadedJar.getParentFile())
+ .hasSameContentAs(packedJar);
+ }
+
+ private static File touch(File dir, String filename) throws IOException {
+ File file = new File(dir, filename);
+ FileUtils.write(file, RandomStringUtils.random(10));
+ return file;
+ }
+
+ //
+ // @Test
+ // public void should_use_deployed_packed_file() throws IOException {
+ // Path packedPath = sourceFolder.resolve("test.pack.gz");
+ // Files.write(packedPath, new byte[] {1, 2, 3});
+ //
+ // settings.setProperty(PROPERTY_PLUGIN_COMPRESSION_ENABLE, true);
+ // underTest = new PluginFileSystem(settings.asConfig());
+ // underTest.compressJar("key", sourceFolder, targetJarPath);
+ //
+ // assertThat(Files.list(targetFolder)).containsOnly(targetJarPath, targetFolder.resolve("test.pack.gz"));
+ // assertThat(underTest.getPlugins()).hasSize(1);
+ // assertThat(underTest.getPlugins().get("key").getFilename()).isEqualTo("test.pack.gz");
+ //
+ // // check that the file was copied, not generated
+ // assertThat(targetFolder.resolve("test.pack.gz")).hasSameContentAs(packedPath);
+ // }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.plugins;
+
+import java.io.File;
+import java.io.IOException;
+import org.apache.commons.io.FileUtils;
+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.server.platform.ServerFileSystem;
+
+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.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+public class PluginUninstallerTest {
+ @Rule
+ public TemporaryFolder testFolder = new TemporaryFolder();
+
+ @Rule
+ public ExpectedException exception = ExpectedException.none();
+
+ private File uninstallDir;
+ private PluginUninstaller underTest;
+ private ServerPluginRepository serverPluginRepository;
+ private ServerFileSystem fs;
+
+ @Before
+ public void setUp() throws IOException {
+ serverPluginRepository = mock(ServerPluginRepository.class);
+ uninstallDir = testFolder.newFolder("uninstall");
+ fs = mock(ServerFileSystem.class);
+ when(fs.getUninstalledPluginsDir()).thenReturn(uninstallDir);
+ underTest = new PluginUninstaller(serverPluginRepository, fs);
+ }
+
+ @Test
+ public void uninstall() {
+ when(serverPluginRepository.hasPlugin("plugin")).thenReturn(true);
+ underTest.uninstall("plugin");
+ verify(serverPluginRepository).uninstall("plugin", uninstallDir);
+ }
+
+ @Test
+ public void fail_uninstall_if_plugin_not_installed() {
+ when(serverPluginRepository.hasPlugin("plugin")).thenReturn(false);
+ exception.expect(IllegalArgumentException.class);
+ exception.expectMessage("Plugin [plugin] is not installed");
+ underTest.uninstall("plugin");
+ verifyZeroInteractions(serverPluginRepository);
+ }
+
+ @Test
+ public void create_uninstall_dir() {
+ File dir = new File(testFolder.getRoot(), "dir");
+ when(fs.getUninstalledPluginsDir()).thenReturn(dir);
+ underTest = new PluginUninstaller(serverPluginRepository, fs);
+ underTest.start();
+ assertThat(dir).isDirectory();
+ }
+
+ @Test
+ public void cancel() {
+ underTest.cancelUninstalls();
+ verify(serverPluginRepository).cancelUninstalls(uninstallDir);
+ verifyNoMoreInteractions(serverPluginRepository);
+ }
+
+ @Test
+ public void list_uninstalled_plugins() throws IOException {
+ new File(uninstallDir, "file1").createNewFile();
+ copyTestPluginTo("test-base-plugin", uninstallDir);
+ assertThat(underTest.getUninstalledPlugins()).extracting("key").containsOnly("testbase");
+ }
+
+ private File copyTestPluginTo(String testPluginName, File toDir) throws IOException {
+ File jar = TestProjectUtils.jarOf(testPluginName);
+ // file is copied because it's supposed to be moved by the test
+ FileUtils.copyFileToDirectory(jar, toDir);
+ return new File(toDir, jar.getName());
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.plugins;
+
+import java.io.File;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.core.platform.ExplodedPlugin;
+import org.sonar.core.platform.PluginInfo;
+import org.sonar.server.platform.ServerFileSystem;
+
+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 ServerPluginJarExploderTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ private ServerFileSystem fs = mock(ServerFileSystem.class);
+ private PluginFileSystem pluginFileSystem = mock(PluginFileSystem.class);
+ private ServerPluginJarExploder underTest = new ServerPluginJarExploder(fs, pluginFileSystem);
+
+ @Test
+ public void copy_all_classloader_files_to_dedicated_directory() throws Exception {
+ File deployDir = temp.newFolder();
+ when(fs.getDeployedPluginsDir()).thenReturn(deployDir);
+ File sourceJar = TestProjectUtils.jarOf("test-libs-plugin");
+ PluginInfo info = PluginInfo.create(sourceJar);
+
+ ExplodedPlugin exploded = underTest.explode(info);
+
+ // all the files loaded by classloaders (JAR + META-INF/libs/*.jar) are copied to the dedicated directory
+ // web/deploy/{pluginKey}
+ File pluginDeployDir = new File(deployDir, "testlibs");
+
+ assertThat(exploded.getKey()).isEqualTo("testlibs");
+ assertThat(exploded.getMain()).isFile().exists().hasParent(pluginDeployDir);
+ assertThat(exploded.getLibs()).extracting("name").containsOnly("commons-daemon-1.0.15.jar", "commons-email-20030310.165926.jar");
+ for (File lib : exploded.getLibs()) {
+ assertThat(lib).exists().isFile();
+ assertThat(lib.getCanonicalPath()).startsWith(pluginDeployDir.getCanonicalPath());
+ }
+ File targetJar = new File(fs.getDeployedPluginsDir(), "testlibs/test-libs-plugin-0.1-SNAPSHOT.jar");
+ verify(pluginFileSystem).addInstalledPlugin(info, targetJar);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.plugins;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+import org.apache.commons.io.FileUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.mockito.Mockito;
+import org.sonar.api.SonarRuntime;
+import org.sonar.api.utils.MessageException;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.core.platform.PluginInfo;
+import org.sonar.core.platform.PluginLoader;
+import org.sonar.server.platform.ServerFileSystem;
+import org.sonar.updatecenter.common.Version;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ServerPluginRepositoryTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Rule
+ public LogTester logs = new LogTester();
+
+ private SonarRuntime runtime = mock(SonarRuntime.class);
+ private ServerFileSystem fs = mock(ServerFileSystem.class, Mockito.RETURNS_DEEP_STUBS);
+ private PluginLoader pluginLoader = mock(PluginLoader.class);
+ private ServerPluginRepository underTest = new ServerPluginRepository(runtime, fs, pluginLoader);
+
+ @Before
+ public void setUp() throws IOException {
+ when(fs.getDeployedPluginsDir()).thenReturn(temp.newFolder());
+ when(fs.getDownloadedPluginsDir()).thenReturn(temp.newFolder());
+ when(fs.getHomeDir()).thenReturn(temp.newFolder());
+ when(fs.getInstalledPluginsDir()).thenReturn(temp.newFolder());
+ when(fs.getTempDir()).thenReturn(temp.newFolder());
+ when(runtime.getApiVersion()).thenReturn(org.sonar.api.utils.Version.parse("5.2"));
+ }
+
+ @After
+ public void tearDown() {
+ underTest.stop();
+ }
+
+ @Test
+ public void standard_startup_loads_installed_plugins() throws Exception {
+ copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir());
+
+ underTest.start();
+
+ assertThat(underTest.getPluginInfosByKeys()).containsOnlyKeys("testbase");
+ }
+
+ @Test
+ public void no_plugins_at_all_on_startup() {
+ underTest.start();
+
+ assertThat(underTest.getPluginInfos()).isEmpty();
+ assertThat(underTest.getPluginInfosByKeys()).isEmpty();
+ assertThat(underTest.hasPlugin("testbase")).isFalse();
+ }
+
+ @Test
+ public void fail_if_multiple_jars_for_same_installed_plugin_on_startup() throws Exception {
+ copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir());
+ copyTestPluginTo("test-base-plugin-v2", fs.getInstalledPluginsDir());
+
+ try {
+ underTest.start();
+ fail();
+ } catch (MessageException e) {
+ assertThat(e)
+ .hasMessageStartingWith("Found two versions of the plugin Base Plugin [testbase] in the directory extensions/plugins. Please remove one of ")
+ // order is not guaranteed, so assertion is split
+ .hasMessageContaining("test-base-plugin-0.1-SNAPSHOT.jar")
+ .hasMessageContaining("test-base-plugin-0.2-SNAPSHOT.jar");
+ }
+ }
+
+ @Test
+ public void install_downloaded_plugins_on_startup() throws Exception {
+ File downloadedJar = copyTestPluginTo("test-base-plugin", fs.getDownloadedPluginsDir());
+
+ underTest.start();
+
+ // plugin is moved to extensions/plugins then loaded
+ assertThat(downloadedJar).doesNotExist();
+ assertThat(new File(fs.getInstalledPluginsDir(), downloadedJar.getName())).isFile().exists();
+ assertThat(underTest.getPluginInfosByKeys()).containsOnlyKeys("testbase");
+ }
+
+ @Test
+ public void downloaded_file_overrides_existing_installed_file_on_startup() throws Exception {
+ File installedV1 = copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir());
+ File downloadedV2 = copyTestPluginTo("test-base-plugin-v2", fs.getDownloadedPluginsDir());
+
+ underTest.start();
+
+ // plugin is moved to extensions/plugins and replaces v1
+ assertThat(downloadedV2).doesNotExist();
+ assertThat(installedV1).doesNotExist();
+ assertThat(new File(fs.getInstalledPluginsDir(), downloadedV2.getName())).exists();
+ assertThat(underTest.getPluginInfosByKeys()).containsOnlyKeys("testbase");
+ assertThat(underTest.getPluginInfo("testbase").getVersion()).isEqualTo(Version.create("0.2-SNAPSHOT"));
+ }
+
+ @Test
+ public void blacklisted_plugin_is_automatically_uninstalled_on_startup() throws Exception {
+ underTest.setBlacklistedPluginKeys(ImmutableSet.of("testbase", "issuesreport"));
+ File jar = copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir());
+
+ underTest.start();
+
+ // plugin is not installed and file is deleted
+ assertThat(underTest.getPluginInfos()).isEmpty();
+ assertThat(jar).doesNotExist();
+ }
+
+ @Test
+ public void test_plugin_requirements_at_startup() throws Exception {
+ copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir());
+ copyTestPluginTo("test-require-plugin", fs.getInstalledPluginsDir());
+
+ underTest.start();
+
+ // both plugins are installed
+ assertThat(underTest.getPluginInfosByKeys()).containsOnlyKeys("testbase", "testrequire");
+ }
+
+ @Test
+ public void plugin_is_ignored_if_required_plugin_is_missing_at_startup() throws Exception {
+ copyTestPluginTo("test-require-plugin", fs.getInstalledPluginsDir());
+
+ underTest.start();
+
+ // plugin is not installed as test-base-plugin is missing
+ assertThat(underTest.getPluginInfosByKeys()).isEmpty();
+ }
+
+ @Test
+ public void plugin_is_ignored_if_required_plugin_is_too_old_at_startup() throws Exception {
+ copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir());
+ copyTestPluginTo("test-requirenew-plugin", fs.getInstalledPluginsDir());
+
+ underTest.start();
+
+ // the plugin "requirenew" is not installed as it requires base 0.2+ to be installed.
+ assertThat(underTest.getPluginInfosByKeys()).containsOnlyKeys("testbase");
+ }
+
+ @Test
+ public void fail_if_plugin_does_not_support_sq_version() throws Exception {
+ when(runtime.getApiVersion()).thenReturn(org.sonar.api.utils.Version.parse("1.0"));
+ copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir());
+
+ try {
+ underTest.start();
+ fail();
+ } catch (MessageException e) {
+ assertThat(e).hasMessage("Plugin Base Plugin [testbase] requires at least SonarQube 4.5.4");
+ }
+ }
+
+ @Test
+ public void uninstall() throws Exception {
+ File installedJar = copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir());
+ File uninstallDir = temp.newFolder("uninstallDir");
+
+ underTest.start();
+ assertThat(underTest.getPluginInfosByKeys()).containsOnlyKeys("testbase");
+ underTest.uninstall("testbase", uninstallDir);
+
+ assertThat(installedJar).doesNotExist();
+ // still up. Will be dropped after next startup
+ assertThat(underTest.getPluginInfosByKeys()).containsOnlyKeys("testbase");
+ assertThat(uninstallDir.list()).containsOnly(installedJar.getName());
+ }
+
+ @Test
+ public void uninstall_dependents() throws Exception {
+ File base = copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir());
+ File extension = copyTestPluginTo("test-require-plugin", fs.getInstalledPluginsDir());
+ File uninstallDir = temp.newFolder("uninstallDir");
+
+ underTest.start();
+ assertThat(underTest.getPluginInfos()).hasSize(2);
+ underTest.uninstall("testbase", uninstallDir);
+ assertThat(base).doesNotExist();
+ assertThat(extension).doesNotExist();
+ assertThat(uninstallDir.list()).containsOnly(base.getName(), extension.getName());
+ }
+
+ @Test
+ public void dont_uninstall_non_existing_dependents() throws IOException {
+ File base = copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir());
+ File extension = copyTestPluginTo("test-require-plugin", fs.getInstalledPluginsDir());
+ File uninstallDir = temp.newFolder("uninstallDir");
+
+ underTest.start();
+ assertThat(underTest.getPluginInfos()).hasSize(2);
+ underTest.uninstall("testrequire", uninstallDir);
+ assertThat(underTest.getPluginInfos()).hasSize(2);
+
+ underTest.uninstall("testbase", uninstallDir);
+ assertThat(base).doesNotExist();
+ assertThat(extension).doesNotExist();
+ assertThat(uninstallDir.list()).containsOnly(base.getName(), extension.getName());
+ }
+
+ @Test
+ public void dont_uninstall_non_existing_files() throws IOException {
+ File base = copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir());
+ File extension = copyTestPluginTo("test-require-plugin", fs.getInstalledPluginsDir());
+ File uninstallDir = temp.newFolder("uninstallDir");
+
+ underTest.start();
+ assertThat(underTest.getPluginInfos()).hasSize(2);
+ underTest.uninstall("testbase", uninstallDir);
+ assertThat(underTest.getPluginInfos()).hasSize(2);
+
+ underTest.uninstall("testbase", uninstallDir);
+ assertThat(base).doesNotExist();
+ assertThat(extension).doesNotExist();
+ assertThat(uninstallDir.list()).containsOnly(base.getName(), extension.getName());
+ }
+
+ @Test
+ public void install_plugin_and_its_extension_plugins_at_startup() throws Exception {
+ copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir());
+ copyTestPluginTo("test-extend-plugin", fs.getInstalledPluginsDir());
+
+ underTest.start();
+
+ // both plugins are installed
+ assertThat(underTest.getPluginInfosByKeys()).containsOnlyKeys("testbase", "testextend");
+ }
+
+ @Test
+ public void extension_plugin_is_ignored_if_base_plugin_is_missing_at_startup() throws Exception {
+ copyTestPluginTo("test-extend-plugin", fs.getInstalledPluginsDir());
+
+ underTest.start();
+
+ // plugin is not installed as its base plugin is not installed
+ assertThat(underTest.getPluginInfos()).isEmpty();
+ }
+
+ @Test
+ public void fail_to_get_missing_plugins() {
+ underTest.start();
+ try {
+ underTest.getPluginInfo("unknown");
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertThat(e).hasMessage("Plugin [unknown] does not exist");
+ }
+
+ try {
+ underTest.getPluginInstance("unknown");
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertThat(e).hasMessage("Plugin [unknown] does not exist");
+ }
+ }
+
+ @Test
+ public void plugin_is_incompatible_if_no_entry_point_class() {
+ PluginInfo plugin = new PluginInfo("foo").setName("Foo");
+ assertThat(ServerPluginRepository.isCompatible(plugin, runtime, Collections.emptyMap())).isFalse();
+ assertThat(logs.logs()).contains("Plugin Foo [foo] is ignored because entry point class is not defined");
+ }
+
+ @Test
+ public void fail_when_views_is_installed() throws Exception {
+ copyTestPluginTo("fake-views-plugin", fs.getInstalledPluginsDir());
+
+ expectedException.expect(MessageException.class);
+ expectedException.expectMessage("Plugin 'views' is no longer compatible with this version of SonarQube");
+ underTest.start();
+ }
+
+ @Test
+ public void fail_when_sqale_plugin_is_installed() throws Exception {
+ copyTestPluginTo("fake-sqale-plugin", fs.getInstalledPluginsDir());
+
+ expectedException.expect(MessageException.class);
+ expectedException.expectMessage("Plugin 'sqale' is no longer compatible with this version of SonarQube");
+ underTest.start();
+ }
+
+ @Test
+ public void fail_when_report_is_installed() throws Exception {
+ copyTestPluginTo("fake-report-plugin", fs.getInstalledPluginsDir());
+
+ expectedException.expect(MessageException.class);
+ expectedException.expectMessage("Plugin 'report' is no longer compatible with this version of SonarQube");
+ underTest.start();
+ }
+
+ /**
+ * Some plugins can only extend the classloader of base plugin, without declaring new extensions.
+ */
+ @Test
+ public void plugin_is_compatible_if_no_entry_point_class_but_extend_other_plugin() {
+ PluginInfo basePlugin = new PluginInfo("base").setMainClass("org.bar.Bar");
+ PluginInfo plugin = new PluginInfo("foo").setBasePlugin("base");
+ Map<String, PluginInfo> plugins = ImmutableMap.of("base", basePlugin, "foo", plugin);
+
+ assertThat(ServerPluginRepository.isCompatible(plugin, runtime, plugins)).isTrue();
+ }
+
+ @Test
+ public void getPluginInstance_throws_ISE_if_repo_is_not_started() {
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("not started yet");
+
+ underTest.getPluginInstance("foo");
+ }
+
+ @Test
+ public void getPluginInfo_throws_ISE_if_repo_is_not_started() {
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("not started yet");
+
+ underTest.getPluginInfo("foo");
+ }
+
+ @Test
+ public void hasPlugin_throws_ISE_if_repo_is_not_started() {
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("not started yet");
+
+ underTest.hasPlugin("foo");
+ }
+
+ @Test
+ public void getPluginInfos_throws_ISE_if_repo_is_not_started() {
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("not started yet");
+
+ underTest.getPluginInfos();
+ }
+
+ private File copyTestPluginTo(String testPluginName, File toDir) throws IOException {
+ File jar = TestProjectUtils.jarOf(testPluginName);
+ // file is copied because it's supposed to be moved by the test
+ FileUtils.copyFileToDirectory(jar, toDir);
+ return new File(toDir, jar.getName());
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.plugins;
+
+import org.sonar.api.Plugin;
+
+public class TestPluginA implements Plugin {
+ @Override
+ public void define(Context context) {
+
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.plugins;
+
+import org.apache.commons.io.FileUtils;
+
+import java.io.File;
+import java.util.Collection;
+
+public class TestProjectUtils {
+
+ /**
+ * Get the artifact of plugins stored in src/test/projects
+ */
+ public static File jarOf(String dirName) {
+ File target = FileUtils.toFile(TestProjectUtils.class.getResource(String.format("/%s/target/", dirName)));
+ Collection<File> jars = FileUtils.listFiles(target, new String[] {"jar"}, false);
+ if (jars == null || jars.size() != 1) {
+ throw new IllegalArgumentException("Test project is badly defined: " + dirName);
+ }
+ return jars.iterator().next();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.plugins;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.config.internal.MapSettings;
+import org.sonar.api.utils.SonarException;
+import org.sonar.api.utils.UriReader;
+import org.sonar.process.ProcessProperties;
+import org.sonar.updatecenter.common.UpdateCenter;
+import org.sonar.updatecenter.common.Version;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.guava.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class UpdateCenterClientTest {
+
+ private static final String BASE_URL = "https://update.sonarsource.org";
+ private UriReader reader = mock(UriReader.class);
+ private MapSettings settings = new MapSettings();
+ private UpdateCenterClient underTest;
+
+ @Before
+ public void startServer() throws Exception {
+ reader = mock(UriReader.class);
+ settings.setProperty(UpdateCenterClient.URL_PROPERTY, BASE_URL);
+ settings.setProperty(ProcessProperties.Property.SONAR_UPDATECENTER_ACTIVATE.getKey(), true);
+ underTest = new UpdateCenterClient(reader, settings.asConfig());
+ }
+
+ @Test
+ public void downloadUpdateCenter() throws URISyntaxException {
+ when(reader.readString(new URI(BASE_URL), StandardCharsets.UTF_8)).thenReturn("publicVersions=2.2,2.3");
+ UpdateCenter plugins = underTest.getUpdateCenter().get();
+ verify(reader, times(1)).readString(new URI(BASE_URL), StandardCharsets.UTF_8);
+ assertThat(plugins.getSonar().getVersions()).containsOnly(Version.create("2.2"), Version.create("2.3"));
+ assertThat(underTest.getLastRefreshDate()).isNotNull();
+ }
+
+ @Test
+ public void not_available_before_initialization() {
+ assertThat(underTest.getLastRefreshDate()).isNull();
+ }
+
+ @Test
+ public void ignore_connection_errors() {
+ when(reader.readString(any(URI.class), eq(StandardCharsets.UTF_8))).thenThrow(new SonarException());
+ assertThat(underTest.getUpdateCenter()).isAbsent();
+ }
+
+ @Test
+ public void cache_data() throws Exception {
+ when(reader.readString(new URI(BASE_URL), StandardCharsets.UTF_8)).thenReturn("sonar.versions=2.2,2.3");
+
+ underTest.getUpdateCenter();
+ underTest.getUpdateCenter();
+
+ verify(reader, times(1)).readString(new URI(BASE_URL), StandardCharsets.UTF_8);
+ }
+
+ @Test
+ public void forceRefresh() throws Exception {
+ when(reader.readString(new URI(BASE_URL), StandardCharsets.UTF_8)).thenReturn("sonar.versions=2.2,2.3");
+
+ underTest.getUpdateCenter();
+ underTest.getUpdateCenter(true);
+
+ verify(reader, times(2)).readString(new URI(BASE_URL), StandardCharsets.UTF_8);
+ }
+
+ @Test
+ public void update_center_is_null_when_property_is_false() {
+ settings.setProperty(ProcessProperties.Property.SONAR_UPDATECENTER_ACTIVATE.getKey(), false);
+
+ assertThat(underTest.getUpdateCenter()).isAbsent();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.plugins;
+
+import com.google.common.base.Optional;
+import org.junit.Test;
+import org.sonar.api.SonarRuntime;
+import org.sonar.updatecenter.common.UpdateCenter;
+
+import static org.assertj.guava.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class UpdateCenterMatrixFactoryTest {
+
+ private UpdateCenterMatrixFactory underTest;
+
+ @Test
+ public void return_absent_update_center() {
+ UpdateCenterClient updateCenterClient = mock(UpdateCenterClient.class);
+ when(updateCenterClient.getUpdateCenter(anyBoolean())).thenReturn(Optional.absent());
+
+ underTest = new UpdateCenterMatrixFactory(updateCenterClient, mock(SonarRuntime.class), mock(InstalledPluginReferentialFactory.class));
+
+ Optional<UpdateCenter> updateCenter = underTest.getUpdateCenter(false);
+
+ assertThat(updateCenter).isAbsent();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.plugins;
+
+import javax.servlet.GenericServlet;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.util.Properties;
+
+public class UpdateCenterServlet extends GenericServlet {
+
+ int count = 0;
+
+ @Override
+ public void service(ServletRequest request, ServletResponse response) throws IOException {
+ count++;
+ Properties props = new Properties();
+ props.setProperty("count", String.valueOf(count));
+ props.setProperty("agent", ((HttpServletRequest)request).getHeader("User-Agent"));
+ props.store(response.getOutputStream(), null);
+ }
+}
+
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.plugins.edition;
+
+import java.util.Random;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.core.platform.PluginInfo;
+import org.sonar.updatecenter.common.Plugin;
+
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class EditionBundledPluginsTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private final Random random = new Random();
+
+ @Test
+ public void isEditionBundled_on_Plugin_fails_with_NPE_if_arg_is_null() {
+ expectedException.expect(NullPointerException.class);
+
+ EditionBundledPlugins.isEditionBundled((Plugin) null);
+ }
+
+ @Test
+ public void isEditionBundled_on_Plugin_returns_false_for_SonarSource_and_non_commercial_license() {
+ Plugin plugin = newPlugin(randomizeCase("SonarSource"), randomAlphanumeric(3));
+
+ assertThat(EditionBundledPlugins.isEditionBundled(plugin)).isFalse();
+ }
+
+ @Test
+ public void isEditionBundled_on_Plugin_returns_false_for_license_SonarSource_and_non_SonarSource_organization() {
+ Plugin plugin = newPlugin(randomAlphanumeric(3), randomizeCase("SonarSource"));
+
+ assertThat(EditionBundledPlugins.isEditionBundled(plugin)).isFalse();
+ }
+
+ @Test
+ public void isEditionBundled_on_Plugin_returns_false_for_license_Commercial_and_non_SonarSource_organization() {
+ Plugin plugin = newPlugin(randomAlphanumeric(3), randomizeCase("Commercial"));
+
+ assertThat(EditionBundledPlugins.isEditionBundled(plugin)).isFalse();
+ }
+
+ @Test
+ public void isEditionBundled_on_Plugin_returns_true_for_organization_SonarSource_and_license_SonarSource_case_insensitive() {
+ Plugin plugin = newPlugin(randomizeCase("SonarSource"), randomizeCase("SonarSource"));
+
+ assertThat(EditionBundledPlugins.isEditionBundled(plugin)).isTrue();
+ }
+
+ @Test
+ public void isEditionBundled_on_Plugin_returns_true_for_organization_SonarSource_and_license_Commercial_case_insensitive() {
+ Plugin plugin = newPlugin(randomizeCase("SonarSource"), randomizeCase("Commercial"));
+
+ assertThat(EditionBundledPlugins.isEditionBundled(plugin)).isTrue();
+ }
+
+ @Test
+ public void isEditionBundled_on_PluginInfo_fails_with_NPE_if_arg_is_null() {
+ expectedException.expect(NullPointerException.class);
+
+ EditionBundledPlugins.isEditionBundled((PluginInfo) null);
+ }
+
+ @Test
+ public void isEditionBundled_on_PluginInfo_returns_false_for_SonarSource_and_non_commercial_license() {
+ PluginInfo pluginInfo = newPluginInfo(randomizeCase("SonarSource"), randomAlphanumeric(3));
+
+ assertThat(EditionBundledPlugins.isEditionBundled(pluginInfo)).isFalse();
+ }
+
+ @Test
+ public void isEditionBundled_on_PluginInfo_returns_false_for_license_SonarSource_and_non_SonarSource_organization() {
+ PluginInfo pluginInfo = newPluginInfo(randomAlphanumeric(3), randomizeCase("SonarSource"));
+
+ assertThat(EditionBundledPlugins.isEditionBundled(pluginInfo)).isFalse();
+ }
+
+ @Test
+ public void isEditionBundled_on_PluginInfo_returns_false_for_license_Commercial_and_non_SonarSource_organization() {
+ PluginInfo pluginInfo = newPluginInfo(randomAlphanumeric(3), randomizeCase("Commercial"));
+
+ assertThat(EditionBundledPlugins.isEditionBundled(pluginInfo)).isFalse();
+ }
+
+ @Test
+ public void isEditionBundled_on_PluginInfo_returns_true_for_organization_SonarSource_and_license_SonarSource_case_insensitive() {
+ PluginInfo pluginInfo = newPluginInfo(randomizeCase("SonarSource"), randomizeCase("SonarSource"));
+
+ assertThat(EditionBundledPlugins.isEditionBundled(pluginInfo)).isTrue();
+ }
+
+ @Test
+ public void isEditionBundled_on_PluginINfo_returns_true_for_organization_SonarSource_and_license_Commercial_case_insensitive() {
+ PluginInfo pluginInfo = newPluginInfo(randomizeCase("SonarSource"), randomizeCase("Commercial"));
+
+ assertThat(EditionBundledPlugins.isEditionBundled(pluginInfo)).isTrue();
+ }
+
+ private String randomizeCase(String s) {
+ return s.chars()
+ .map(c -> random.nextBoolean() ? Character.toUpperCase(c) : Character.toLowerCase(c))
+ .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
+ .toString();
+ }
+
+ private PluginInfo newPluginInfo(String organization, String license) {
+ PluginInfo pluginInfo = new PluginInfo(randomAlphanumeric(2));
+ if (random.nextBoolean()) {
+ pluginInfo.setName(randomAlphanumeric(3));
+ }
+ if (random.nextBoolean()) {
+ pluginInfo.setOrganizationUrl(randomAlphanumeric(4));
+ }
+ if (random.nextBoolean()) {
+ pluginInfo.setIssueTrackerUrl(randomAlphanumeric(5));
+ }
+ if (random.nextBoolean()) {
+ pluginInfo.setIssueTrackerUrl(randomAlphanumeric(6));
+ }
+ if (random.nextBoolean()) {
+ pluginInfo.setBasePlugin(randomAlphanumeric(7));
+ }
+ if (random.nextBoolean()) {
+ pluginInfo.setHomepageUrl(randomAlphanumeric(8));
+ }
+ return pluginInfo
+ .setOrganizationName(organization)
+ .setLicense(license);
+ }
+
+ private Plugin newPlugin(String organization, String license) {
+ Plugin plugin = Plugin.factory(randomAlphanumeric(2));
+ if (random.nextBoolean()) {
+ plugin.setName(randomAlphanumeric(3));
+ }
+ if (random.nextBoolean()) {
+ plugin.setOrganizationUrl(randomAlphanumeric(4));
+ }
+ if (random.nextBoolean()) {
+ plugin.setTermsConditionsUrl(randomAlphanumeric(5));
+ }
+ if (random.nextBoolean()) {
+ plugin.setIssueTrackerUrl(randomAlphanumeric(6));
+ }
+ if (random.nextBoolean()) {
+ plugin.setCategory(randomAlphanumeric(7));
+ }
+ if (random.nextBoolean()) {
+ plugin.setHomepageUrl(randomAlphanumeric(8));
+ }
+ return plugin
+ .setLicense(license)
+ .setOrganization(organization);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.project;
+
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import java.util.Collections;
+import java.util.Random;
+import java.util.Set;
+import java.util.stream.IntStream;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mockito;
+import org.sonar.core.util.stream.MoreCollectors;
+
+import static java.util.Collections.singleton;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.same;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
+import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto;
+
+@RunWith(DataProviderRunner.class)
+public class ProjectLifeCycleListenersImplTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private ProjectLifeCycleListener listener1 = mock(ProjectLifeCycleListener.class);
+ private ProjectLifeCycleListener listener2 = mock(ProjectLifeCycleListener.class);
+ private ProjectLifeCycleListener listener3 = mock(ProjectLifeCycleListener.class);
+ private ProjectLifeCycleListenersImpl underTestNoListeners = new ProjectLifeCycleListenersImpl();
+ private ProjectLifeCycleListenersImpl underTestWithListeners = new ProjectLifeCycleListenersImpl(
+ new ProjectLifeCycleListener[] {listener1, listener2, listener3});
+
+ @Test
+ public void onProjectsDeleted_throws_NPE_if_set_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("projects can't be null");
+
+ underTestWithListeners.onProjectsDeleted(null);
+ }
+
+ @Test
+ public void onProjectsDeleted_throws_NPE_if_set_is_null_even_if_no_listeners() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("projects can't be null");
+
+ underTestNoListeners.onProjectsDeleted(null);
+ }
+
+ @Test
+ public void onProjectsDeleted_has_no_effect_if_set_is_empty() {
+ underTestNoListeners.onProjectsDeleted(Collections.emptySet());
+
+ underTestWithListeners.onProjectsDeleted(Collections.emptySet());
+ verifyZeroInteractions(listener1, listener2, listener3);
+ }
+
+ @Test
+ @UseDataProvider("oneOrManyProjects")
+ public void onProjectsDeleted_does_not_fail_if_there_is_no_listener(Set<Project> projects) {
+ underTestNoListeners.onProjectsDeleted(projects);
+ }
+
+ @Test
+ @UseDataProvider("oneOrManyProjects")
+ public void onProjectsDeleted_calls_all_listeners_in_order_of_addition_to_constructor(Set<Project> projects) {
+ InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
+
+ underTestWithListeners.onProjectsDeleted(projects);
+
+ inOrder.verify(listener1).onProjectsDeleted(same(projects));
+ inOrder.verify(listener2).onProjectsDeleted(same(projects));
+ inOrder.verify(listener3).onProjectsDeleted(same(projects));
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ @UseDataProvider("oneOrManyProjects")
+ public void onProjectsDeleted_calls_all_listeners_even_if_one_throws_an_Exception(Set<Project> projects) {
+ InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
+ doThrow(new RuntimeException("Faking listener2 throwing an exception"))
+ .when(listener2)
+ .onProjectsDeleted(any());
+
+ underTestWithListeners.onProjectsDeleted(projects);
+
+ inOrder.verify(listener1).onProjectsDeleted(same(projects));
+ inOrder.verify(listener2).onProjectsDeleted(same(projects));
+ inOrder.verify(listener3).onProjectsDeleted(same(projects));
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ @UseDataProvider("oneOrManyProjects")
+ public void onProjectsDeleted_calls_all_listeners_even_if_one_throws_an_Error(Set<Project> projects) {
+ InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
+ doThrow(new Error("Faking listener2 throwing an Error"))
+ .when(listener2)
+ .onProjectsDeleted(any());
+
+ underTestWithListeners.onProjectsDeleted(projects);
+
+ inOrder.verify(listener1).onProjectsDeleted(same(projects));
+ inOrder.verify(listener2).onProjectsDeleted(same(projects));
+ inOrder.verify(listener3).onProjectsDeleted(same(projects));
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void onProjectBranchesDeleted_throws_NPE_if_set_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("projects can't be null");
+
+ underTestWithListeners.onProjectBranchesDeleted(null);
+ }
+
+ @Test
+ public void onProjectBranchesDeleted_throws_NPE_if_set_is_null_even_if_no_listeners() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("projects can't be null");
+
+ underTestNoListeners.onProjectBranchesDeleted(null);
+ }
+
+ @Test
+ public void onProjectBranchesDeleted_has_no_effect_if_set_is_empty() {
+ underTestNoListeners.onProjectBranchesDeleted(Collections.emptySet());
+
+ underTestWithListeners.onProjectBranchesDeleted(Collections.emptySet());
+ verifyZeroInteractions(listener1, listener2, listener3);
+ }
+
+ @Test
+ @UseDataProvider("oneOrManyProjects")
+ public void onProjectBranchesDeleted_does_not_fail_if_there_is_no_listener(Set<Project> projects) {
+ underTestNoListeners.onProjectBranchesDeleted(projects);
+ }
+
+ @Test
+ @UseDataProvider("oneOrManyProjects")
+ public void onProjectBranchesDeleted_calls_all_listeners_in_order_of_addition_to_constructor(Set<Project> projects) {
+ InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
+
+ underTestWithListeners.onProjectBranchesDeleted(projects);
+
+ inOrder.verify(listener1).onProjectBranchesDeleted(same(projects));
+ inOrder.verify(listener2).onProjectBranchesDeleted(same(projects));
+ inOrder.verify(listener3).onProjectBranchesDeleted(same(projects));
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ @UseDataProvider("oneOrManyProjects")
+ public void onProjectBranchesDeleted_calls_all_listeners_even_if_one_throws_an_Exception(Set<Project> projects) {
+ InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
+ doThrow(new RuntimeException("Faking listener2 throwing an exception"))
+ .when(listener2)
+ .onProjectBranchesDeleted(any());
+
+ underTestWithListeners.onProjectBranchesDeleted(projects);
+
+ inOrder.verify(listener1).onProjectBranchesDeleted(same(projects));
+ inOrder.verify(listener2).onProjectBranchesDeleted(same(projects));
+ inOrder.verify(listener3).onProjectBranchesDeleted(same(projects));
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ @UseDataProvider("oneOrManyProjects")
+ public void onProjectBranchesDeleted_calls_all_listeners_even_if_one_throws_an_Error(Set<Project> projects) {
+ InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
+ doThrow(new Error("Faking listener2 throwing an Error"))
+ .when(listener2)
+ .onProjectBranchesDeleted(any());
+
+ underTestWithListeners.onProjectBranchesDeleted(projects);
+
+ inOrder.verify(listener1).onProjectBranchesDeleted(same(projects));
+ inOrder.verify(listener2).onProjectBranchesDeleted(same(projects));
+ inOrder.verify(listener3).onProjectBranchesDeleted(same(projects));
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @DataProvider
+ public static Object[][] oneOrManyProjects() {
+ return new Object[][] {
+ {singleton(newUniqueProject())},
+ {IntStream.range(0, 1 + new Random().nextInt(10)).mapToObj(i -> newUniqueProject()).collect(MoreCollectors.toSet())}
+ };
+ }
+ // SDSDS
+
+ @Test
+ public void onProjectsRekeyed_throws_NPE_if_set_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("rekeyedProjects can't be null");
+
+ underTestWithListeners.onProjectsRekeyed(null);
+ }
+
+ @Test
+ public void onProjectsRekeyed_throws_NPE_if_set_is_null_even_if_no_listeners() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("rekeyedProjects can't be null");
+
+ underTestNoListeners.onProjectsRekeyed(null);
+ }
+
+ @Test
+ public void onProjectsRekeyed_has_no_effect_if_set_is_empty() {
+ underTestNoListeners.onProjectsRekeyed(Collections.emptySet());
+
+ underTestWithListeners.onProjectsRekeyed(Collections.emptySet());
+ verifyZeroInteractions(listener1, listener2, listener3);
+ }
+
+ @Test
+ @UseDataProvider("oneOrManyRekeyedProjects")
+ public void onProjectsRekeyed_does_not_fail_if_there_is_no_listener(Set<RekeyedProject> projects) {
+ underTestNoListeners.onProjectsRekeyed(projects);
+ }
+
+ @Test
+ @UseDataProvider("oneOrManyRekeyedProjects")
+ public void onProjectsRekeyed_calls_all_listeners_in_order_of_addition_to_constructor(Set<RekeyedProject> projects) {
+ InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
+
+ underTestWithListeners.onProjectsRekeyed(projects);
+
+ inOrder.verify(listener1).onProjectsRekeyed(same(projects));
+ inOrder.verify(listener2).onProjectsRekeyed(same(projects));
+ inOrder.verify(listener3).onProjectsRekeyed(same(projects));
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ @UseDataProvider("oneOrManyRekeyedProjects")
+ public void onProjectsRekeyed_calls_all_listeners_even_if_one_throws_an_Exception(Set<RekeyedProject> projects) {
+ InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
+ doThrow(new RuntimeException("Faking listener2 throwing an exception"))
+ .when(listener2)
+ .onProjectsRekeyed(any());
+
+ underTestWithListeners.onProjectsRekeyed(projects);
+
+ inOrder.verify(listener1).onProjectsRekeyed(same(projects));
+ inOrder.verify(listener2).onProjectsRekeyed(same(projects));
+ inOrder.verify(listener3).onProjectsRekeyed(same(projects));
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ @UseDataProvider("oneOrManyRekeyedProjects")
+ public void onProjectsRekeyed_calls_all_listeners_even_if_one_throws_an_Error(Set<RekeyedProject> projects) {
+ InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
+ doThrow(new Error("Faking listener2 throwing an Error"))
+ .when(listener2)
+ .onProjectsRekeyed(any());
+
+ underTestWithListeners.onProjectsRekeyed(projects);
+
+ inOrder.verify(listener1).onProjectsRekeyed(same(projects));
+ inOrder.verify(listener2).onProjectsRekeyed(same(projects));
+ inOrder.verify(listener3).onProjectsRekeyed(same(projects));
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @DataProvider
+ public static Object[][] oneOrManyRekeyedProjects() {
+ return new Object[][] {
+ {singleton(newUniqueRekeyedProject())},
+ {IntStream.range(0, 1 + new Random().nextInt(10)).mapToObj(i -> newUniqueRekeyedProject()).collect(MoreCollectors.toSet())}
+ };
+ }
+
+ private static Project newUniqueProject() {
+ return Project.from(newPrivateProjectDto(newOrganizationDto()));
+ }
+
+ private static int counter = 3_989;
+
+ private static RekeyedProject newUniqueRekeyedProject() {
+ int base = counter++;
+ Project project = Project.from(newPrivateProjectDto(newOrganizationDto()));
+ return new RekeyedProject(project, base + "_old_key");
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.project;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static java.util.Collections.emptyList;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
+import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto;
+
+public class RekeyedProjectTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void constructor_throws_NPE_if_project_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("project can't be null");
+
+ new RekeyedProject(null, randomAlphanumeric(3));
+ }
+
+ @Test
+ public void constructor_throws_NPE_if_previousKey_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("previousKey can't be null");
+
+ new RekeyedProject(newRandomProject(), null);
+ }
+
+ @Test
+ public void verify_getters() {
+ Project project = newRandomProject();
+ String previousKey = randomAlphanumeric(6);
+ RekeyedProject underTest = new RekeyedProject(project, previousKey);
+
+ assertThat(underTest.getProject()).isSameAs(project);
+ assertThat(underTest.getPreviousKey()).isEqualTo(previousKey);
+ }
+
+ @Test
+ public void equals_is_based_on_project_and_previousKey() {
+ Project project = newRandomProject();
+ String previousKey = randomAlphanumeric(6);
+ RekeyedProject underTest = new RekeyedProject(project, previousKey);
+
+ assertThat(underTest).isEqualTo(underTest);
+ assertThat(underTest).isEqualTo(new RekeyedProject(project, previousKey));
+ assertThat(underTest).isNotEqualTo(new RekeyedProject(project, randomAlphanumeric(11)));
+ assertThat(underTest).isNotEqualTo(new RekeyedProject(newRandomProject(), previousKey));
+ assertThat(underTest).isNotEqualTo(new Object());
+ assertThat(underTest).isNotEqualTo(null);
+ }
+
+ @Test
+ public void hashCode_is_based_on_project_and_previousKey() {
+ Project project = newRandomProject();
+ String previousKey = randomAlphanumeric(6);
+ RekeyedProject underTest = new RekeyedProject(project, previousKey);
+
+ assertThat(underTest.hashCode()).isEqualTo(underTest.hashCode());
+ assertThat(underTest.hashCode()).isEqualTo(new RekeyedProject(project, previousKey).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new RekeyedProject(project, randomAlphanumeric(11)).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new RekeyedProject(newRandomProject(), previousKey).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new Object().hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(null);
+ }
+
+ @Test
+ public void verify_toString() {
+ Project project = new Project("A", "B", "C", "D", emptyList());
+ String previousKey = "E";
+ RekeyedProject underTest = new RekeyedProject(project, previousKey);
+
+ assertThat(underTest.toString()).isEqualTo("RekeyedProject{project=Project{uuid='A', key='B', name='C', description='D'}, previousKey='E'}");
+ }
+
+ private static Project newRandomProject() {
+ return Project.from(newPrivateProjectDto(newOrganizationDto()));
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.qualitygate.changeevent;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.apache.commons.lang.RandomStringUtils;
+import org.assertj.core.groups.Tuple;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.Mockito;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.rules.RuleType;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.api.utils.log.LoggerLevel;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.server.qualitygate.changeevent.QGChangeEventListener.ChangedIssue;
+import org.sonar.server.qualitygate.changeevent.QGChangeEventListenersImpl.ChangedIssueImpl;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
+import static java.util.Collections.emptySet;
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.same;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+public class QGChangeEventListenersImplTest {
+ @Rule
+ public LogTester logTester = new LogTester();
+
+ private QGChangeEventListener listener1 = mock(QGChangeEventListener.class);
+ private QGChangeEventListener listener2 = mock(QGChangeEventListener.class);
+ private QGChangeEventListener listener3 = mock(QGChangeEventListener.class);
+ private List<QGChangeEventListener> listeners = Arrays.asList(listener1, listener2, listener3);
+
+ private String component1Uuid = RandomStringUtils.randomAlphabetic(6);
+ private ComponentDto component1 = newComponentDto(component1Uuid);
+ private DefaultIssue component1Issue = newDefaultIssue(component1Uuid);
+ private List<DefaultIssue> oneIssueOnComponent1 = singletonList(component1Issue);
+ private QGChangeEvent component1QGChangeEvent = newQGChangeEvent(component1);
+
+ private InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
+
+ private QGChangeEventListenersImpl underTest = new QGChangeEventListenersImpl(new QGChangeEventListener[] {listener1, listener2, listener3});
+
+ @Test
+ public void broadcastOnIssueChange_has_no_effect_when_issues_are_empty() {
+ underTest.broadcastOnIssueChange(emptyList(), singletonList(component1QGChangeEvent));
+
+ verifyZeroInteractions(listener1, listener2, listener3);
+ }
+
+ @Test
+ public void broadcastOnIssueChange_has_no_effect_when_no_changeEvent() {
+ underTest.broadcastOnIssueChange(oneIssueOnComponent1, emptySet());
+
+ verifyZeroInteractions(listener1, listener2, listener3);
+ }
+
+ @Test
+ public void broadcastOnIssueChange_passes_same_arguments_to_all_listeners_in_order_of_addition_to_constructor() {
+ underTest.broadcastOnIssueChange(oneIssueOnComponent1, singletonList(component1QGChangeEvent));
+
+ ArgumentCaptor<Set<ChangedIssue>> changedIssuesCaptor = newSetCaptor();
+ inOrder.verify(listener1).onIssueChanges(same(component1QGChangeEvent), changedIssuesCaptor.capture());
+ Set<ChangedIssue> changedIssues = changedIssuesCaptor.getValue();
+ inOrder.verify(listener2).onIssueChanges(same(component1QGChangeEvent), same(changedIssues));
+ inOrder.verify(listener3).onIssueChanges(same(component1QGChangeEvent), same(changedIssues));
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void broadcastOnIssueChange_calls_all_listeners_even_if_one_throws_an_exception() {
+ QGChangeEventListener failingListener = new QGChangeEventListener[] {listener1, listener2, listener3}[new Random().nextInt(3)];
+ doThrow(new RuntimeException("Faking an exception thrown by onChanges"))
+ .when(failingListener)
+ .onIssueChanges(any(), any());
+
+ underTest.broadcastOnIssueChange(oneIssueOnComponent1, singletonList(component1QGChangeEvent));
+
+ ArgumentCaptor<Set<ChangedIssue>> changedIssuesCaptor = newSetCaptor();
+ inOrder.verify(listener1).onIssueChanges(same(component1QGChangeEvent), changedIssuesCaptor.capture());
+ Set<ChangedIssue> changedIssues = changedIssuesCaptor.getValue();
+ inOrder.verify(listener2).onIssueChanges(same(component1QGChangeEvent), same(changedIssues));
+ inOrder.verify(listener3).onIssueChanges(same(component1QGChangeEvent), same(changedIssues));
+ inOrder.verifyNoMoreInteractions();
+ assertThat(logTester.logs()).hasSize(4);
+ assertThat(logTester.logs(LoggerLevel.WARN)).hasSize(1);
+ }
+
+ @Test
+ public void broadcastOnIssueChange_stops_calling_listeners_when_one_throws_an_ERROR() {
+ doThrow(new Error("Faking an error thrown by a listener"))
+ .when(listener2)
+ .onIssueChanges(any(), any());
+
+ underTest.broadcastOnIssueChange(oneIssueOnComponent1, singletonList(component1QGChangeEvent));
+
+ ArgumentCaptor<Set<ChangedIssue>> changedIssuesCaptor = newSetCaptor();
+ inOrder.verify(listener1).onIssueChanges(same(component1QGChangeEvent), changedIssuesCaptor.capture());
+ Set<ChangedIssue> changedIssues = changedIssuesCaptor.getValue();
+ inOrder.verify(listener2).onIssueChanges(same(component1QGChangeEvent), same(changedIssues));
+ inOrder.verifyNoMoreInteractions();
+ assertThat(logTester.logs()).hasSize(3);
+ assertThat(logTester.logs(LoggerLevel.WARN)).hasSize(1);
+ }
+
+ @Test
+ public void broadcastOnIssueChange_logs_each_listener_call_at_TRACE_level() {
+ underTest.broadcastOnIssueChange(oneIssueOnComponent1, singletonList(component1QGChangeEvent));
+
+ assertThat(logTester.logs()).hasSize(3);
+ List<String> traceLogs = logTester.logs(LoggerLevel.TRACE);
+ assertThat(traceLogs).hasSize(3)
+ .containsOnly(
+ "calling onChange() on listener " + listener1.getClass().getName() + " for events " + component1QGChangeEvent.toString() + "...",
+ "calling onChange() on listener " + listener2.getClass().getName() + " for events " + component1QGChangeEvent.toString() + "...",
+ "calling onChange() on listener " + listener3.getClass().getName() + " for events " + component1QGChangeEvent.toString() + "...");
+ }
+
+ @Test
+ public void broadcastOnIssueChange_passes_immutable_set_of_ChangedIssues() {
+ QGChangeEventListenersImpl underTest = new QGChangeEventListenersImpl(new QGChangeEventListener[] {listener1});
+
+ underTest.broadcastOnIssueChange(oneIssueOnComponent1, singletonList(component1QGChangeEvent));
+
+ ArgumentCaptor<Set<ChangedIssue>> changedIssuesCaptor = newSetCaptor();
+ inOrder.verify(listener1).onIssueChanges(same(component1QGChangeEvent), changedIssuesCaptor.capture());
+ assertThat(changedIssuesCaptor.getValue()).isInstanceOf(ImmutableSet.class);
+ }
+
+ @Test
+ public void broadcastOnIssueChange_has_no_effect_when_no_listener() {
+ QGChangeEventListenersImpl underTest = new QGChangeEventListenersImpl();
+
+ underTest.broadcastOnIssueChange(oneIssueOnComponent1, singletonList(component1QGChangeEvent));
+
+ verifyZeroInteractions(listener1, listener2, listener3);
+ }
+
+ @Test
+ public void broadcastOnIssueChange_calls_listener_for_each_component_uuid_with_at_least_one_QGChangeEvent() {
+ // component2 has multiple issues
+ ComponentDto component2 = newComponentDto(component1Uuid + "2");
+ DefaultIssue[] component2Issues = {newDefaultIssue(component2.uuid()), newDefaultIssue(component2.uuid())};
+ QGChangeEvent component2QGChangeEvent = newQGChangeEvent(component2);
+
+ // component 3 has multiple QGChangeEvent and only one issue
+ ComponentDto component3 = newComponentDto(component1Uuid + "3");
+ DefaultIssue component3Issue = newDefaultIssue(component3.uuid());
+ QGChangeEvent[] component3QGChangeEvents = {newQGChangeEvent(component3), newQGChangeEvent(component3)};
+
+ // component 4 has multiple QGChangeEvent and multiples issues
+ ComponentDto component4 = newComponentDto(component1Uuid + "4");
+ DefaultIssue[] component4Issues = {newDefaultIssue(component4.uuid()), newDefaultIssue(component4.uuid())};
+ QGChangeEvent[] component4QGChangeEvents = {newQGChangeEvent(component4), newQGChangeEvent(component4)};
+
+ // component 5 has no QGChangeEvent but one issue
+ ComponentDto component5 = newComponentDto(component1Uuid + "5");
+ DefaultIssue component5Issue = newDefaultIssue(component5.uuid());
+
+ List<DefaultIssue> issues = Stream.of(
+ Stream.of(component1Issue),
+ Arrays.stream(component2Issues),
+ Stream.of(component3Issue),
+ Arrays.stream(component4Issues),
+ Stream.of(component5Issue))
+ .flatMap(s -> s)
+ .collect(Collectors.toList());
+
+ List<DefaultIssue> changedIssues = randomizedList(issues);
+ List<QGChangeEvent> qgChangeEvents = Stream.of(
+ Stream.of(component1QGChangeEvent),
+ Stream.of(component2QGChangeEvent),
+ Arrays.stream(component3QGChangeEvents),
+ Arrays.stream(component4QGChangeEvents))
+ .flatMap(s -> s)
+ .collect(Collectors.toList());
+
+ underTest.broadcastOnIssueChange(changedIssues, randomizedList(qgChangeEvents));
+
+ listeners.forEach(listener -> {
+ verifyListenerCalled(listener, component1QGChangeEvent, component1Issue);
+ verifyListenerCalled(listener, component2QGChangeEvent, component2Issues);
+ Arrays.stream(component3QGChangeEvents)
+ .forEach(component3QGChangeEvent -> verifyListenerCalled(listener, component3QGChangeEvent, component3Issue));
+ Arrays.stream(component4QGChangeEvents)
+ .forEach(component4QGChangeEvent -> verifyListenerCalled(listener, component4QGChangeEvent, component4Issues));
+ });
+ verifyNoMoreInteractions(listener1, listener2, listener3);
+ }
+
+ @Test
+ public void isNotClosed_returns_true_if_issue_in_one_of_opened_states() {
+ DefaultIssue defaultIssue = new DefaultIssue();
+ defaultIssue.setStatus(Issue.STATUS_REOPENED);
+ defaultIssue.setKey("abc");
+ defaultIssue.setType(RuleType.BUG);
+ defaultIssue.setSeverity("BLOCKER");
+
+ ChangedIssue changedIssue = new ChangedIssueImpl(defaultIssue);
+
+ assertThat(changedIssue.isNotClosed()).isTrue();
+ }
+
+ @Test
+ public void isNotClosed_returns_false_if_issue_in_one_of_closed_states() {
+ DefaultIssue defaultIssue = new DefaultIssue();
+ defaultIssue.setStatus(Issue.STATUS_CONFIRMED);
+ defaultIssue.setKey("abc");
+ defaultIssue.setType(RuleType.BUG);
+ defaultIssue.setSeverity("BLOCKER");
+
+ ChangedIssue changedIssue = new ChangedIssueImpl(defaultIssue);
+
+ assertThat(changedIssue.isNotClosed()).isFalse();
+ }
+
+ @Test
+ public void test_status_mapping() {
+ assertThat(ChangedIssueImpl.statusOf(new DefaultIssue().setStatus(Issue.STATUS_OPEN))).isEqualTo(QGChangeEventListener.Status.OPEN);
+ assertThat(ChangedIssueImpl.statusOf(new DefaultIssue().setStatus(Issue.STATUS_REOPENED))).isEqualTo(QGChangeEventListener.Status.REOPENED);
+ assertThat(ChangedIssueImpl.statusOf(new DefaultIssue().setStatus(Issue.STATUS_CONFIRMED))).isEqualTo(QGChangeEventListener.Status.CONFIRMED);
+ assertThat(ChangedIssueImpl.statusOf(new DefaultIssue().setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_FALSE_POSITIVE)))
+ .isEqualTo(QGChangeEventListener.Status.RESOLVED_FP);
+ assertThat(ChangedIssueImpl.statusOf(new DefaultIssue().setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_WONT_FIX)))
+ .isEqualTo(QGChangeEventListener.Status.RESOLVED_WF);
+ assertThat(ChangedIssueImpl.statusOf(new DefaultIssue().setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_FIXED)))
+ .isEqualTo(QGChangeEventListener.Status.RESOLVED_FIXED);
+ try {
+ ChangedIssueImpl.statusOf(new DefaultIssue().setStatus(Issue.STATUS_CLOSED));
+ fail("Expected exception");
+ } catch (Exception e) {
+ assertThat(e).hasMessage("Unexpected status: CLOSED");
+ }
+ try {
+ ChangedIssueImpl.statusOf(new DefaultIssue().setStatus(Issue.STATUS_RESOLVED));
+ fail("Expected exception");
+ } catch (Exception e) {
+ assertThat(e).hasMessage("A resolved issue should have a resolution");
+ }
+ try {
+ ChangedIssueImpl.statusOf(new DefaultIssue().setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_REMOVED));
+ fail("Expected exception");
+ } catch (Exception e) {
+ assertThat(e).hasMessage("Unexpected resolution for a resolved issue: REMOVED");
+ }
+ }
+
+ @Test
+ public void test_status_mapping_on_security_hotspots() {
+ assertThat(ChangedIssueImpl.statusOf(new DefaultIssue().setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW)))
+ .isEqualTo(QGChangeEventListener.Status.TO_REVIEW);
+ assertThat(ChangedIssueImpl.statusOf(new DefaultIssue().setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_IN_REVIEW)))
+ .isEqualTo(QGChangeEventListener.Status.IN_REVIEW);
+ assertThat(ChangedIssueImpl.statusOf(new DefaultIssue().setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)))
+ .isEqualTo(QGChangeEventListener.Status.REVIEWED);
+ }
+
+ private void verifyListenerCalled(QGChangeEventListener listener, QGChangeEvent changeEvent, DefaultIssue... issues) {
+ ArgumentCaptor<Set<ChangedIssue>> changedIssuesCaptor = newSetCaptor();
+ verify(listener).onIssueChanges(same(changeEvent), changedIssuesCaptor.capture());
+ Set<ChangedIssue> changedIssues = changedIssuesCaptor.getValue();
+ Tuple[] expected = Arrays.stream(issues)
+ .map(issue -> tuple(issue.key(), ChangedIssueImpl.statusOf(issue), issue.type()))
+ .toArray(Tuple[]::new);
+ assertThat(changedIssues)
+ .hasSize(issues.length)
+ .extracting(ChangedIssue::getKey, ChangedIssue::getStatus, ChangedIssue::getType)
+ .containsOnly(expected);
+ }
+
+ private static final String[] POSSIBLE_STATUSES = asList(Issue.STATUS_CONFIRMED, Issue.STATUS_REOPENED, Issue.STATUS_RESOLVED).stream().toArray(String[]::new);
+ private static int issueIdCounter = 0;
+
+ private static DefaultIssue newDefaultIssue(String projectUuid) {
+ DefaultIssue defaultIssue = new DefaultIssue();
+ defaultIssue.setKey("issue_" + issueIdCounter++);
+ defaultIssue.setProjectUuid(projectUuid);
+ defaultIssue.setType(RuleType.values()[new Random().nextInt(RuleType.values().length)]);
+ defaultIssue.setStatus(POSSIBLE_STATUSES[new Random().nextInt(POSSIBLE_STATUSES.length)]);
+ String[] possibleResolutions = possibleResolutions(defaultIssue.getStatus());
+ if (possibleResolutions.length > 0) {
+ defaultIssue.setResolution(possibleResolutions[new Random().nextInt(possibleResolutions.length)]);
+ }
+ return defaultIssue;
+ }
+
+ private static String[] possibleResolutions(String status) {
+ switch (status) {
+ case Issue.STATUS_RESOLVED:
+ return new String[] {Issue.RESOLUTION_FALSE_POSITIVE, Issue.RESOLUTION_WONT_FIX};
+ default:
+ return new String[0];
+ }
+ }
+
+ private static ComponentDto newComponentDto(String uuid) {
+ ComponentDto componentDto = new ComponentDto();
+ componentDto.setUuid(uuid);
+ return componentDto;
+ }
+
+ private static QGChangeEvent newQGChangeEvent(ComponentDto componentDto) {
+ QGChangeEvent res = mock(QGChangeEvent.class);
+ when(res.getProject()).thenReturn(componentDto);
+ return res;
+ }
+
+ private static <T> ArgumentCaptor<Set<T>> newSetCaptor() {
+ Class<Set<T>> clazz = (Class<Set<T>>) (Class) Set.class;
+ return ArgumentCaptor.forClass(clazz);
+ }
+
+ private static <T> List<T> randomizedList(List<T> issues) {
+ ArrayList<T> res = new ArrayList<>(issues);
+ Collections.shuffle(res);
+ return ImmutableList.copyOf(res);
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.qualitygate.changeevent;
+
+import java.util.Optional;
+import java.util.Random;
+import java.util.function.Supplier;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.mockito.Mockito;
+import org.sonar.api.config.Configuration;
+import org.sonar.api.measures.Metric;
+import org.sonar.db.component.BranchDto;
+import org.sonar.db.component.BranchType;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.SnapshotDto;
+import org.sonar.server.qualitygate.EvaluatedQualityGate;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class QGChangeEventTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private ComponentDto project = new ComponentDto()
+ .setDbKey("foo")
+ .setUuid("bar");
+ private BranchDto branch = new BranchDto()
+ .setBranchType(BranchType.SHORT)
+ .setUuid("bar")
+ .setProjectUuid("doh")
+ .setMergeBranchUuid("zop");
+ private SnapshotDto analysis = new SnapshotDto()
+ .setUuid("pto")
+ .setCreatedAt(8_999_999_765L);
+ private Configuration configuration = Mockito.mock(Configuration.class);
+ private Metric.Level previousStatus = Metric.Level.values()[new Random().nextInt(Metric.Level.values().length)];
+ private Supplier<Optional<EvaluatedQualityGate>> supplier = Optional::empty;
+
+ @Test
+ public void constructor_fails_with_NPE_if_project_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("project can't be null");
+
+ new QGChangeEvent(null, branch, analysis, configuration, previousStatus, supplier);
+ }
+
+ @Test
+ public void constructor_fails_with_NPE_if_branch_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("branch can't be null");
+
+ new QGChangeEvent(project, null, analysis, configuration, previousStatus, supplier);
+ }
+
+ @Test
+ public void constructor_fails_with_NPE_if_analysis_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("analysis can't be null");
+
+ new QGChangeEvent(project, branch, null, configuration, previousStatus, supplier);
+ }
+
+ @Test
+ public void constructor_fails_with_NPE_if_configuration_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("projectConfiguration can't be null");
+
+ new QGChangeEvent(project, branch, analysis, null, previousStatus, supplier);
+ }
+
+ @Test
+ public void constructor_does_not_fail_with_NPE_if_previousStatus_is_null() {
+ new QGChangeEvent(project, branch, analysis, configuration, null, supplier);
+ }
+
+ @Test
+ public void constructor_fails_with_NPE_if_supplier_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("qualityGateSupplier can't be null");
+
+ new QGChangeEvent(project, branch, analysis, configuration, previousStatus, null);
+ }
+
+ @Test
+ public void verify_getters() {
+ QGChangeEvent underTest = new QGChangeEvent(project, branch, analysis, configuration, previousStatus, supplier);
+
+ assertThat(underTest.getProject()).isSameAs(project);
+ assertThat(underTest.getBranch()).isSameAs(branch);
+ assertThat(underTest.getAnalysis()).isSameAs(analysis);
+ assertThat(underTest.getProjectConfiguration()).isSameAs(configuration);
+ assertThat(underTest.getPreviousStatus()).contains(previousStatus);
+ assertThat(underTest.getQualityGateSupplier()).isSameAs(supplier);
+ }
+
+ @Test
+ public void getPreviousStatus_returns_empty_when_previousStatus_is_null() {
+ QGChangeEvent underTest = new QGChangeEvent(project, branch, analysis, configuration, previousStatus, supplier);
+
+ assertThat(underTest.getPreviousStatus()).contains(previousStatus);
+ }
+
+ @Test
+ public void overrides_toString() {
+ QGChangeEvent underTest = new QGChangeEvent(project, branch, analysis, configuration, previousStatus, supplier);
+
+ assertThat(underTest.toString())
+ .isEqualTo("QGChangeEvent{project=bar:foo, branch=SHORT:bar:doh:zop, analysis=pto:8999999765" +
+ ", projectConfiguration=" + configuration.toString() +
+ ", previousStatus=" + previousStatus +
+ ", qualityGateSupplier=" + supplier + "}");
+
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.setting;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.util.Collections;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.config.Configuration;
+import org.sonar.api.config.internal.MapSettings;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.property.PropertiesDao;
+import org.sonar.db.property.PropertyDto;
+
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singleton;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+public class ProjectConfigurationLoaderImplTest {
+ private DbClient dbClient = mock(DbClient.class);
+ private DbSession dbSession = mock(DbSession.class);
+ private PropertiesDao propertiesDao = mock(PropertiesDao.class);
+ private MapSettings globalSettings = new MapSettings();
+ private ProjectConfigurationLoaderImpl underTest = new ProjectConfigurationLoaderImpl(globalSettings, dbClient);
+
+ @Before
+ public void setUp() throws Exception {
+ when(dbClient.openSession(anyBoolean()))
+ .thenThrow(new IllegalStateException("ProjectConfigurationLoaderImpl should not open DB session"));
+ when(dbClient.propertiesDao()).thenReturn(propertiesDao);
+ }
+
+ @Test
+ public void returns_empty_map_when_no_component() {
+ assertThat(underTest.loadProjectConfigurations(dbSession, Collections.emptySet()))
+ .isEmpty();
+
+ verifyZeroInteractions(propertiesDao);
+ }
+
+ @Test
+ public void return_configuration_with_just_global_settings_when_no_component_settings() {
+ String key = randomAlphanumeric(3);
+ String value = randomAlphanumeric(4);
+ String componentDbKey = randomAlphanumeric(5);
+ String componentUuid = randomAlphanumeric(6);
+ globalSettings.setProperty(key, value);
+ when(propertiesDao.selectProjectProperties(dbSession, componentDbKey))
+ .thenReturn(emptyList());
+ ComponentDto component = newComponentDto(componentDbKey, componentUuid);
+
+ Map<String, Configuration> configurations = underTest.loadProjectConfigurations(dbSession, singleton(component));
+
+ assertThat(configurations)
+ .containsOnlyKeys(componentUuid);
+ assertThat(configurations.get(componentUuid).get(key)).contains(value);
+ }
+
+ @Test
+ public void return_configuration_with_global_settings_and_component_settings() {
+ String globalKey = randomAlphanumeric(3);
+ String globalValue = randomAlphanumeric(4);
+ String componentDbKey = randomAlphanumeric(5);
+ String componentUuid = randomAlphanumeric(6);
+ String projectPropKey1 = randomAlphanumeric(7);
+ String projectPropValue1 = randomAlphanumeric(8);
+ String projectPropKey2 = randomAlphanumeric(9);
+ String projectPropValue2 = randomAlphanumeric(10);
+ globalSettings.setProperty(globalKey, globalValue);
+ when(propertiesDao.selectProjectProperties(dbSession, componentDbKey))
+ .thenReturn(ImmutableList.of(newPropertyDto(projectPropKey1, projectPropValue1), newPropertyDto(projectPropKey2, projectPropValue2)));
+ ComponentDto component = newComponentDto(componentDbKey, componentUuid);
+
+ Map<String, Configuration> configurations = underTest.loadProjectConfigurations(dbSession, singleton(component));
+
+ assertThat(configurations)
+ .containsOnlyKeys(componentUuid);
+ assertThat(configurations.get(componentUuid).get(globalKey)).contains(globalValue);
+ assertThat(configurations.get(componentUuid).get(projectPropKey1)).contains(projectPropValue1);
+ assertThat(configurations.get(componentUuid).get(projectPropKey2)).contains(projectPropValue2);
+ }
+
+ @Test
+ public void return_configuration_with_global_settings_main_branch_settings_and_branch_settings() {
+ String globalKey = randomAlphanumeric(3);
+ String globalValue = randomAlphanumeric(4);
+ String mainBranchDbKey = randomAlphanumeric(5);
+ String branchDbKey = mainBranchDbKey + ComponentDto.BRANCH_KEY_SEPARATOR + randomAlphabetic(5);
+ String branchUuid = randomAlphanumeric(6);
+ String mainBranchPropKey = randomAlphanumeric(7);
+ String mainBranchPropValue = randomAlphanumeric(8);
+ String branchPropKey = randomAlphanumeric(9);
+ String branchPropValue = randomAlphanumeric(10);
+ globalSettings.setProperty(globalKey, globalValue);
+ when(propertiesDao.selectProjectProperties(dbSession, mainBranchDbKey))
+ .thenReturn(ImmutableList.of(newPropertyDto(mainBranchPropKey, mainBranchPropValue)));
+ when(propertiesDao.selectProjectProperties(dbSession, branchDbKey))
+ .thenReturn(ImmutableList.of(newPropertyDto(branchPropKey, branchPropValue)));
+ ComponentDto component = newComponentDto(branchDbKey, branchUuid);
+
+ Map<String, Configuration> configurations = underTest.loadProjectConfigurations(dbSession, singleton(component));
+
+ assertThat(configurations)
+ .containsOnlyKeys(branchUuid);
+ assertThat(configurations.get(branchUuid).get(globalKey)).contains(globalValue);
+ assertThat(configurations.get(branchUuid).get(mainBranchPropKey)).contains(mainBranchPropValue);
+ assertThat(configurations.get(branchUuid).get(branchPropKey)).contains(branchPropValue);
+ }
+
+ @Test
+ public void loads_configuration_of_any_given_component_only_once() {
+ String mainBranch1DbKey = randomAlphanumeric(4);
+ String mainBranch1Uuid = randomAlphanumeric(5);
+ String branch1DbKey = mainBranch1DbKey + ComponentDto.BRANCH_KEY_SEPARATOR + randomAlphabetic(5);
+ String branch1Uuid = randomAlphanumeric(6);
+ String branch2DbKey = mainBranch1DbKey + ComponentDto.BRANCH_KEY_SEPARATOR + randomAlphabetic(7);
+ String branch2Uuid = randomAlphanumeric(8);
+ String mainBranch2DbKey = randomAlphanumeric(14);
+ String mainBranch2Uuid = randomAlphanumeric(15);
+ String branch3DbKey = mainBranch2DbKey + ComponentDto.BRANCH_KEY_SEPARATOR + randomAlphabetic(5);
+ String branch3Uuid = randomAlphanumeric(16);
+
+ ComponentDto mainBranch1 = newComponentDto(mainBranch1DbKey, mainBranch1Uuid);
+ ComponentDto branch1 = newComponentDto(branch1DbKey, branch1Uuid);
+ ComponentDto branch2 = newComponentDto(branch2DbKey, branch2Uuid);
+ ComponentDto mainBranch2 = newComponentDto(mainBranch2DbKey, mainBranch2Uuid);
+ ComponentDto branch3 = newComponentDto(branch3DbKey, branch3Uuid);
+
+ underTest.loadProjectConfigurations(dbSession, ImmutableSet.of(mainBranch1, mainBranch2, branch1, branch2, branch3));
+
+ verify(propertiesDao, times(1)).selectProjectProperties(dbSession, mainBranch1DbKey);
+ verify(propertiesDao, times(1)).selectProjectProperties(dbSession, mainBranch2DbKey);
+ verify(propertiesDao, times(1)).selectProjectProperties(dbSession, branch1DbKey);
+ verify(propertiesDao, times(1)).selectProjectProperties(dbSession, branch2DbKey);
+ verify(propertiesDao, times(1)).selectProjectProperties(dbSession, branch3DbKey);
+ verifyNoMoreInteractions(propertiesDao);
+ }
+
+ private ComponentDto newComponentDto(String componentDbKey, String componentUuid) {
+ return new ComponentDto().setDbKey(componentDbKey).setUuid(componentUuid);
+ }
+
+ private PropertyDto newPropertyDto(String projectKey1, String projectValue1) {
+ return new PropertyDto()
+ .setKey(projectKey1)
+ .setValue(projectValue1);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.setting;
+
+import org.junit.Test;
+import org.sonar.api.config.GlobalPropertyChangeHandler;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class SettingsChangeNotifierTest {
+ @Test
+ public void onGlobalPropertyChange() {
+ GlobalPropertyChangeHandler handler = mock(GlobalPropertyChangeHandler.class);
+ SettingsChangeNotifier notifier = new SettingsChangeNotifier(new GlobalPropertyChangeHandler[] {handler});
+
+ notifier.onGlobalPropertyChange("foo", "bar");
+
+ verify(handler).onChange(argThat(change -> change.getKey().equals("foo") && change.getNewValue().equals("bar")));
+ }
+
+ @Test
+ public void no_handlers() {
+ SettingsChangeNotifier notifier = new SettingsChangeNotifier();
+
+ assertThat(notifier.changeHandlers).isEmpty();
+
+ // does not fail
+ notifier.onGlobalPropertyChange("foo", "bar");
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.setting;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import org.sonar.api.config.Configuration;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentDto;
+
+public class TestProjectConfigurationLoader implements ProjectConfigurationLoader {
+
+ private final Configuration config;
+
+ public TestProjectConfigurationLoader(Configuration config) {
+ this.config = config;
+ }
+
+ @Override
+ public Map<String, Configuration> loadProjectConfigurations(DbSession dbSession, Set<ComponentDto> projects) {
+ Map<String, Configuration> map = new HashMap<>();
+ for (ComponentDto project : projects) {
+ map.put(project.uuid(), config);
+ }
+ return map;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.util;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.server.exceptions.BadRequestException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class BooleanTypeValidationTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private BooleanTypeValidation underTest = new BooleanTypeValidation();
+
+ @Test
+ public void key() {
+ assertThat(underTest.key()).isEqualTo("BOOLEAN");
+ }
+
+ @Test
+ public void not_fail_on_valid_boolean() {
+ underTest.validate("true", null);
+ underTest.validate("True", null);
+ underTest.validate("false", null);
+ underTest.validate("FALSE", null);
+ }
+
+ @Test
+ public void fail_on_invalid_boolean() {
+ expectedException.expect(BadRequestException.class);
+ expectedException.expectMessage("Value 'abc' must be one of \"true\" or \"false\".");
+
+ underTest.validate("abc", null);
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.util;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.server.exceptions.BadRequestException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class FloatTypeValidationTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private FloatTypeValidation validation = new FloatTypeValidation();
+
+ @Test
+ public void key() {
+ assertThat(validation.key()).isEqualTo("FLOAT");
+ }
+
+ @Test
+ public void not_fail_on_valid_float() {
+ validation.validate("10.2", null);
+ validation.validate("10", null);
+ validation.validate("-10.3", null);
+ }
+
+ @Test
+ public void fail_on_invalid_float() {
+ expectedException.expect(BadRequestException.class);
+ expectedException.expectMessage("Value 'abc' must be an floating point number.");
+
+ validation.validate("abc", null);
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.util;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.server.util.GlobalLockManager.DEFAULT_LOCK_DURATION_SECONDS;
+
+public class GlobalLockManagerTest {
+
+ private final System2 system2 = mock(System2.class);
+
+ @Rule
+ public final DbTester dbTester = DbTester.create(system2);
+
+ private final GlobalLockManager underTest = new GlobalLockManager(dbTester.getDbClient());
+
+ @Test
+ public void tryLock_succeeds_when_created_for_the_first_time() {
+ assertThat(underTest.tryLock("newName")).isTrue();
+ }
+
+ @Test
+ public void tryLock_fails_when_previous_lock_is_too_recent() {
+ String name = "newName";
+ assertThat(underTest.tryLock(name)).isTrue();
+ assertThat(underTest.tryLock(name)).isFalse();
+ }
+
+ @Test
+ public void tryLock_succeeds_when_previous_lock_is_old_enough() {
+ String name = "newName";
+ long firstLock = 0;
+ long longEnoughAfterFirstLock = firstLock + DEFAULT_LOCK_DURATION_SECONDS * 1000;
+ long notLongEnoughAfterFirstLock = longEnoughAfterFirstLock - 1;
+
+ when(system2.now()).thenReturn(firstLock);
+ assertThat(underTest.tryLock(name)).isTrue();
+
+ when(system2.now()).thenReturn(notLongEnoughAfterFirstLock);
+ assertThat(underTest.tryLock(name)).isFalse();
+
+ when(system2.now()).thenReturn(longEnoughAfterFirstLock);
+ assertThat(underTest.tryLock(name)).isTrue();
+ }
+
+ @Test
+ public void locks_with_different_name_are_independent() {
+ assertThat(underTest.tryLock("newName1")).isTrue();
+ assertThat(underTest.tryLock("newName2")).isTrue();
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.util;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.server.exceptions.BadRequestException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class IntegerTypeValidationTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private IntegerTypeValidation validation = new IntegerTypeValidation();
+
+ @Test
+ public void key() {
+ assertThat(validation.key()).isEqualTo("INTEGER");
+ }
+
+ @Test
+ public void not_fail_on_valid_integer() {
+ validation.validate("10", null);
+ validation.validate("-10", null);
+ }
+
+ @Test
+ public void fail_on_string() {
+ expectedException.expect(BadRequestException.class);
+ expectedException.expectMessage("Value 'abc' must be an integer.");
+
+ validation.validate("abc", null);
+ }
+
+ @Test
+ public void fail_on_float() {
+ expectedException.expect(BadRequestException.class);
+ expectedException.expectMessage("Value '10.1' must be an integer.");
+
+ validation.validate("10.1", null);
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.util;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.PropertyType;
+import org.sonar.server.exceptions.BadRequestException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class LongTypeValidationTest {
+
+ LongTypeValidation underTest = new LongTypeValidation();
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void key_is_long_type_name() {
+ assertThat(underTest.key()).isEqualTo(PropertyType.LONG.name());
+ }
+
+ @Test
+ public void do_not_fail_with_long_values() {
+ underTest.validate("1984", null);
+ underTest.validate("-1984", null);
+ }
+
+ @Test
+ public void fail_when_float() {
+ expectedException.expect(BadRequestException.class);
+ expectedException.expectMessage("Value '3.14' must be a long.");
+
+ underTest.validate("3.14", null);
+ }
+
+ @Test
+ public void fail_when_string() {
+ expectedException.expect(BadRequestException.class);
+ expectedException.expectMessage("Value 'original string' must be a long.");
+
+ underTest.validate("original string", null);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.util;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.server.exceptions.BadRequestException;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class StringListTypeValidationTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private StringListTypeValidation validation = new StringListTypeValidation();
+
+ @Test
+ public void key() {
+ assertThat(validation.key()).isEqualTo("SINGLE_SELECT_LIST");
+ }
+
+ @Test
+ public void not_fail_on_valid_option() {
+ validation.validate("a", newArrayList("a", "b", "c"));
+ validation.validate("a", null);
+ }
+
+ @Test
+ public void fail_on_invalid_option() {
+ expectedException.expect(BadRequestException.class);
+ expectedException.expectMessage("Value 'abc' must be one of : a, b, c.");
+
+ validation.validate("abc", newArrayList("a", "b", "c"));
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.util;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class StringTypeValidationTest {
+
+ StringTypeValidation validation;
+
+ @Before
+ public void setUp() {
+ validation = new StringTypeValidation();
+ }
+
+ @Test
+ public void key() {
+ assertThat(validation.key()).isEqualTo("STRING");
+ }
+
+ @Test
+ public void not_fail_on_valid_string() {
+ validation.validate("10", null);
+ validation.validate("abc", null);
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.util;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class TextTypeValidationTest {
+
+ TextTypeValidation validation;
+
+ @Before
+ public void setUp() {
+ validation = new TextTypeValidation();
+ }
+
+ @Test
+ public void key() {
+ assertThat(validation.key()).isEqualTo("TEXT");
+ }
+
+ @Test
+ public void not_fail_on_valid_text() {
+ validation.validate("10", null);
+ validation.validate("abc", null);
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.util;
+
+import org.junit.Test;
+import org.sonar.core.platform.ComponentContainer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class TypeValidationModuleTest {
+ @Test
+ public void verify_count_of_added_components() {
+ ComponentContainer container = new ComponentContainer();
+ new TypeValidationModule().configure(container);
+ assertThat(container.size()).isEqualTo(11);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.util;
+
+import org.junit.Test;
+import org.sonar.server.exceptions.BadRequestException;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class TypeValidationsTest {
+
+ @Test
+ public void validate() {
+ TypeValidation fakeTypeValidation = mock(TypeValidation.class);
+ when(fakeTypeValidation.key()).thenReturn("Fake");
+
+ TypeValidations typeValidations = new TypeValidations(newArrayList(fakeTypeValidation));
+ typeValidations.validate("10", "Fake", newArrayList("a"));
+
+ verify(fakeTypeValidation).validate("10", newArrayList("a"));
+ }
+
+ @Test
+ public void validate__multiple_values() {
+ TypeValidation fakeTypeValidation = mock(TypeValidation.class);
+ when(fakeTypeValidation.key()).thenReturn("Fake");
+
+ TypeValidations typeValidations = new TypeValidations(newArrayList(fakeTypeValidation));
+ typeValidations.validate(newArrayList("10", "11", "12"), "Fake", newArrayList("11"));
+
+ verify(fakeTypeValidation).validate("10", newArrayList("11"));
+ }
+
+ @Test
+ public void fail_on_unknown_type() {
+ TypeValidation fakeTypeValidation = mock(TypeValidation.class);
+ when(fakeTypeValidation.key()).thenReturn("Fake");
+
+ try {
+ TypeValidations typeValidations = new TypeValidations(newArrayList(fakeTypeValidation));
+ typeValidations.validate("10", "Unknown", null);
+ fail();
+ } catch (Exception e) {
+ assertThat(e).isInstanceOf(BadRequestException.class);
+ BadRequestException badRequestException = (BadRequestException) e;
+ assertThat(badRequestException.getMessage()).isEqualTo("Type 'Unknown' is not valid.");
+ }
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.util;
+
+import java.util.Arrays;
+
+public class TypeValidationsTesting {
+ private TypeValidationsTesting() {
+ // utility class
+ }
+
+ public static TypeValidations newFullTypeValidations() {
+ return new TypeValidations(Arrays.asList(
+ new BooleanTypeValidation(),
+ new IntegerTypeValidation(),
+ new LongTypeValidation(),
+ new FloatTypeValidation(),
+ new StringTypeValidation(),
+ new StringListTypeValidation(),
+ new MetricLevelTypeValidation()
+ ));
+ }
+}
--- /dev/null
+# see README.txt
+!*/target/
+*/target/classes/
+*/target/maven-archiver/
+*/target/maven-status/
+*/target/test-*/
+
--- /dev/null
+This directory provides the fake plugins used by tests. These tests are rarely changed, so generated
+artifacts are stored in Git repository (see .gitignore). It avoids from adding unnecessary modules
+to build.
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.sonarsource.sonarqube.tests</groupId>
+ <artifactId>fake-report-plugin</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ <packaging>sonar-plugin</packaging>
+ <name>Fake Report Plugin</name>
+ <description>Fake Report Plugin</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-plugin-api</artifactId>
+ <version>4.5.4</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <sourceDirectory>src</sourceDirectory>
+ <plugins>
+ <plugin>
+ <groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId>
+ <artifactId>sonar-packaging-maven-plugin</artifactId>
+ <version>1.15</version>
+ <extensions>true</extensions>
+ <configuration>
+ <pluginKey>report</pluginKey>
+ <pluginClass>BasePlugin</pluginClass>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.
+ */
+import org.sonar.api.Plugin;
+
+import java.util.Collections;
+import java.util.List;
+
+public class BasePlugin extends Plugin {
+
+ public void define(Plugin.Context context) {
+
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.plugins.testbase.api;
+
+public class BaseApi {
+ public void doNothing() {
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.sonarsource.sonarqube.tests</groupId>
+ <artifactId>fake-sqale-plugin</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ <packaging>sonar-plugin</packaging>
+ <name>Fake SQALE Plugin</name>
+ <description>Fake SQALE Plugin</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-plugin-api</artifactId>
+ <version>4.5.4</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <sourceDirectory>src</sourceDirectory>
+ <plugins>
+ <plugin>
+ <groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId>
+ <artifactId>sonar-packaging-maven-plugin</artifactId>
+ <version>1.15</version>
+ <extensions>true</extensions>
+ <configuration>
+ <pluginKey>sqale</pluginKey>
+ <pluginClass>BasePlugin</pluginClass>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.
+ */
+import org.sonar.api.Plugin;
+
+import java.util.Collections;
+import java.util.List;
+
+public class BasePlugin extends Plugin {
+
+ public void define(Plugin.Context context) {
+
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.plugins.testbase.api;
+
+public class BaseApi {
+ public void doNothing() {
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.sonarsource.sonarqube.tests</groupId>
+ <artifactId>fake-views-plugin</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ <packaging>sonar-plugin</packaging>
+ <name>Fake Views Plugin</name>
+ <description>Fake Views Plugin</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-plugin-api</artifactId>
+ <version>4.5.4</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <sourceDirectory>src</sourceDirectory>
+ <plugins>
+ <plugin>
+ <groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId>
+ <artifactId>sonar-packaging-maven-plugin</artifactId>
+ <version>1.15</version>
+ <extensions>true</extensions>
+ <configuration>
+ <pluginKey>views</pluginKey>
+ <pluginClass>BasePlugin</pluginClass>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.
+ */
+import org.sonar.api.Plugin;
+
+import java.util.Collections;
+import java.util.List;
+
+public class BasePlugin extends Plugin {
+
+ public void define(Plugin.Context context) {
+
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.plugins.testbase.api;
+
+public class BaseApi {
+ public void doNothing() {
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.sonarsource.sonarqube.tests</groupId>
+ <artifactId>parent</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ <packaging>pom</packaging>
+ <modules>
+ <module>test-base-plugin</module>
+ <module>test-base-plugin-v2</module>
+ <module>test-core-plugin</module>
+ <module>test-extend-plugin</module>
+ <module>test-libs-plugin</module>
+ <module>test-require-plugin</module>
+ <module>test-requirenew-plugin</module>
+ <module>fake-report-plugin</module>
+ <module>fake-sqale-plugin</module>
+ <module>fake-views-plugin</module>
+ </modules>
+
+</project>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.sonarsource.sonarqube.tests</groupId>
+ <artifactId>test-base-plugin</artifactId>
+ <version>0.2-SNAPSHOT</version>
+ <packaging>sonar-plugin</packaging>
+ <name>Base Plugin</name>
+ <description>Simple standalone plugin. Used by other fake plugins.</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-plugin-api</artifactId>
+ <version>4.5.4</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <sourceDirectory>src</sourceDirectory>
+ <plugins>
+ <plugin>
+ <groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId>
+ <artifactId>sonar-packaging-maven-plugin</artifactId>
+ <version>1.15</version>
+ <extensions>true</extensions>
+ <configuration>
+ <pluginKey>testbase</pluginKey>
+ <pluginClass>BasePlugin</pluginClass>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.
+ */
+import org.sonar.api.Plugin;
+
+import java.util.Collections;
+import java.util.List;
+
+public class BasePlugin extends Plugin {
+
+ public void define(Plugin.Context context) {
+
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.plugins.testbase.api;
+
+public class BaseApi {
+ public void doNothing() {
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.sonarsource.sonarqube.tests</groupId>
+ <artifactId>test-base-plugin</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ <packaging>sonar-plugin</packaging>
+ <name>Base Plugin</name>
+ <description>Simple standalone plugin. Used by other fake plugins.</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-plugin-api</artifactId>
+ <version>4.5.4</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <sourceDirectory>src</sourceDirectory>
+ <plugins>
+ <plugin>
+ <groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId>
+ <artifactId>sonar-packaging-maven-plugin</artifactId>
+ <version>1.15</version>
+ <extensions>true</extensions>
+ <configuration>
+ <pluginKey>testbase</pluginKey>
+ <pluginClass>BasePlugin</pluginClass>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.
+ */
+import org.sonar.api.Plugin;
+
+import java.util.Collections;
+import java.util.List;
+
+public class BasePlugin extends Plugin {
+
+ public void define(Plugin.Context context) {
+
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.plugins.testbase.api;
+
+public class BaseApi {
+ public void doNothing() {
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.sonarsource.sonarqube.tests</groupId>
+ <artifactId>test-extend-plugin</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ <packaging>sonar-plugin</packaging>
+ <name>Test Extend Plugin</name>
+ <description>Fake plugin that extends the plugin with key "base"</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-plugin-api</artifactId>
+ <version>4.5.4</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <sourceDirectory>src</sourceDirectory>
+ <plugins>
+ <plugin>
+ <groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId>
+ <artifactId>sonar-packaging-maven-plugin</artifactId>
+ <version>1.15</version>
+ <extensions>true</extensions>
+ <configuration>
+ <pluginKey>testextend</pluginKey>
+ <pluginClass>ExtendPlugin</pluginClass>
+ <basePlugin>testbase</basePlugin>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.
+ */
+import org.sonar.api.Plugin;
+
+import java.util.Collections;
+import java.util.List;
+
+public class ExtendPlugin extends Plugin {
+
+ public void define(Plugin.Context context) {
+
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.sonarsource.sonarqube.tests</groupId>
+ <artifactId>test-libs-plugin</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ <packaging>sonar-plugin</packaging>
+ <name>Test Libs Plugin</name>
+ <description>Fake plugin that embeds some libraries</description>
+
+ <dependencies>
+ <!-- embedded libs. Chosen because small ! -->
+ <dependency>
+ <groupId>commons-email</groupId>
+ <artifactId>commons-email</artifactId>
+ <version>20030310.165926</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-daemon</groupId>
+ <artifactId>commons-daemon</artifactId>
+ <version>1.0.15</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-plugin-api</artifactId>
+ <version>4.5.4</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <sourceDirectory>src</sourceDirectory>
+ <plugins>
+ <plugin>
+ <groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId>
+ <artifactId>sonar-packaging-maven-plugin</artifactId>
+ <version>1.15</version>
+ <extensions>true</extensions>
+ <configuration>
+ <pluginKey>testlibs</pluginKey>
+ <pluginClass>LibsPlugin</pluginClass>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.
+ */
+import org.sonar.api.Plugin;
+
+import java.util.Collections;
+import java.util.List;
+
+public class LibsPlugin extends Plugin {
+
+ public void define(Plugin.Context context) {
+
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.sonarsource.sonarqube.tests</groupId>
+ <artifactId>test-require-plugin</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ <packaging>sonar-plugin</packaging>
+ <name>Test Require Plugin</name>
+ <description>This fake plugin depends on test-base-plugin</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-plugin-api</artifactId>
+ <version>4.5.4</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.sonarsource.sonarqube.tests</groupId>
+ <artifactId>test-base-plugin</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ <type>sonar-plugin</type>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <sourceDirectory>src</sourceDirectory>
+ <plugins>
+ <plugin>
+ <groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId>
+ <artifactId>sonar-packaging-maven-plugin</artifactId>
+ <version>1.15</version>
+ <extensions>true</extensions>
+ <configuration>
+ <pluginKey>testrequire</pluginKey>
+ <pluginClass>RequirePlugin</pluginClass>
+ <requirePlugins>testbase:0.1</requirePlugins>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.
+ */
+import org.sonar.api.Plugin;
+
+import java.util.Collections;
+import java.util.List;
+
+public class RequirePlugin extends Plugin {
+
+ public RequirePlugin() {
+ // call a class that is in the api published by the base plugin
+ new org.sonar.plugins.testbase.api.BaseApi().doNothing();
+ }
+
+ public void define(Plugin.Context context) {
+
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.sonarsource.sonarqube.tests</groupId>
+ <artifactId>test-requirenew-plugin</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ <packaging>sonar-plugin</packaging>
+ <name>Test Require New Plugin</name>
+ <description>This fake plugin requires a version of test-base-plugin that is not installed</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-plugin-api</artifactId>
+ <version>4.5.4</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.sonarsource.sonarqube.tests</groupId>
+ <artifactId>test-base-plugin</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ <type>sonar-plugin</type>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <sourceDirectory>src</sourceDirectory>
+ <plugins>
+ <plugin>
+ <groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId>
+ <artifactId>sonar-packaging-maven-plugin</artifactId>
+ <version>1.15</version>
+ <extensions>true</extensions>
+ <configuration>
+ <pluginKey>testrequire</pluginKey>
+ <pluginClass>RequirePlugin</pluginClass>
+ <requirePlugins>testbase:0.2</requirePlugins>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.
+ */
+import org.sonar.api.Plugin;
+
+import java.util.Collections;
+import java.util.List;
+
+public class RequirePlugin extends Plugin {
+
+ public RequirePlugin() {
+ // call a class that is in the api published by the base plugin
+ new org.sonar.plugins.testbase.api.BaseApi().doNothing();
+ }
+
+ public void define(Plugin.Context context) {
+
+ }
+
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>
+<configuration debug="false">
+ <contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator"/>
+
+ <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+ <pattern>
+ %d{yyyy.MM.dd HH:mm:ss} %-5level %msg%n
+ </pattern>
+ </encoder>
+ </appender>
+
+ <root>
+ <level value="INFO"/>
+ <appender-ref ref="CONSOLE"/>
+ </root>
+
+ <logger name="ch.qos.logback">
+ <level value="WARN"/>
+ </logger>
+
+ <logger name="okhttp3.mockwebserver">
+ <level value="WARN"/>
+ </logger>
+
+</configuration>
compile project(':server:sonar-db-dao')
compile project(':server:sonar-process')
compile project(':server:sonar-server-common')
- compile project(':server:sonar-webserver-common')
+ compile project(':server:sonar-webserver-api')
compile project(path: ':sonar-plugin-api', configuration: 'shadow')
compile project(':sonar-plugin-api-impl')
compile 'org.mindrot:jbcrypt'
+++ /dev/null
-sonarqube {
- properties {
- property 'sonar.projectName', "${projectTitle} :: WebServer :: Common"
- }
-}
-
-sourceSets {
- test {
- resources {
- srcDirs += ['src/test/projects']
- }
- }
-}
-
-configurations {
- tests
-
- testCompile.extendsFrom tests
-}
-
-dependencies {
- // please keep the list grouped by configuration and ordered by name
-
- compile 'com.google.guava:guava'
- compile 'io.jsonwebtoken:jjwt-api'
- compile 'io.jsonwebtoken:jjwt-impl'
- compile project(':sonar-core')
- compile project(':server:sonar-db-dao')
- compile project(':server:sonar-process')
- compile project(':server:sonar-server-common')
- compile project(path: ':sonar-plugin-api', configuration: 'shadow')
- compile project(':sonar-plugin-api-impl')
- compile 'org.mindrot:jbcrypt'
-
- compileOnly 'com.google.code.findbugs:jsr305'
- compileOnly 'javax.servlet:javax.servlet-api'
-
- testCompile 'org.assertj:assertj-guava'
- testCompile 'com.google.code.findbugs:jsr305'
- testCompile 'com.tngtech.java:junit-dataprovider'
- testCompile 'javax.servlet:javax.servlet-api'
- testCompile 'org.mockito:mockito-core'
- testCompile project(':server:sonar-db-testing')
- testCompile project(path: ":server:sonar-server-common", configuration: "tests")
- testCompile project(path: ":server:sonar-webserver-ws", configuration: "tests")
- testCompile project(':sonar-testing-harness')
-}
-
-task testJar(type: Jar) {
- classifier = 'tests'
- from sourceSets.test.output
-}
-
-artifacts {
- tests testJar
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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;
-
-public interface ProcessCommandWrapper {
- /**
- * Requests to the main process that SQ be restarted.
- */
- void requestSQRestart();
-
- /**
- * Requests to the main process that the WebServer is stopped.
- */
- void requestHardStop();
-
- /**
- * Notifies any listening process that the WebServer is operational.
- */
- void notifyOperational();
-
- /**
- * Checks whether the Compute Engine is operational.
- */
- boolean isCeOperational();
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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 java.io.File;
-import org.sonar.api.config.Configuration;
-import org.sonar.process.ProcessId;
-import org.sonar.process.sharedmemoryfile.DefaultProcessCommands;
-import org.sonar.process.sharedmemoryfile.ProcessCommands;
-
-import static org.sonar.process.ProcessEntryPoint.PROPERTY_PROCESS_INDEX;
-import static org.sonar.process.ProcessEntryPoint.PROPERTY_SHARED_PATH;
-
-public class ProcessCommandWrapperImpl implements ProcessCommandWrapper {
-
- private static final ProcessMethod<Void> SET_OPERATIONAL = processCommands -> {
- processCommands.setOperational();
- return null;
- };
- private static final ProcessMethod<Void> ASK_FOR_RESTART = processCommands -> {
- processCommands.askForRestart();
- return null;
- };
- private static final ProcessMethod<Void> ASK_FOR_HARD_STOP = processCommands -> {
- processCommands.askForHardStop();
- return null;
- };
- private static final ProcessMethod<Boolean> IS_OPERATIONAL = ProcessCommands::isOperational;
-
- private final Configuration config;
-
- public ProcessCommandWrapperImpl(Configuration config) {
- this.config = config;
- }
-
- @Override
- public void requestSQRestart() {
- call(ASK_FOR_RESTART, selfProcessNumber());
- }
-
- @Override
- public void requestHardStop() {
- call(ASK_FOR_HARD_STOP, selfProcessNumber());
- }
-
- @Override
- public void notifyOperational() {
- call(SET_OPERATIONAL, selfProcessNumber());
- }
-
- @Override
- public boolean isCeOperational() {
- return call(IS_OPERATIONAL, ProcessId.COMPUTE_ENGINE.getIpcIndex());
- }
-
- private int selfProcessNumber() {
- return nonNullAsInt(PROPERTY_PROCESS_INDEX);
- }
-
- private <T> T call(ProcessMethod<T> command, int processNumber) {
- File shareDir = nonNullValueAsFile(PROPERTY_SHARED_PATH);
- try (DefaultProcessCommands commands = DefaultProcessCommands.secondary(shareDir, processNumber)) {
- return command.callOn(commands);
- }
- }
-
- private interface ProcessMethod<T> {
- T callOn(ProcessCommands processCommands);
- }
-
- private int nonNullAsInt(String key) {
- return config.getInt(key).orElseThrow(() -> new IllegalArgumentException(String.format("Property %s is not set", key)));
- }
-
- private File nonNullValueAsFile(String key) {
- return new File(config.get(key).orElseThrow(() -> new IllegalArgumentException(String.format("Property %s is not set", key))));
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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;
-
-/**
- * Holds a boolean flag representing the restarting status of the WebServer.
- * This boolean is {@code false} by default and can safely be changed concurrently using methods {@link #set()} and
- * {@link #unset()}.
- */
-public interface RestartFlagHolder {
- /**
- * @return whether restarting flag has been set or not.
- */
- boolean isRestarting();
-
- /**
- * Sets the restarting flag to {@code true}, no matter it already is or not.
- */
- void set();
-
- /**
- * Sets the restarting flag to {@code false}, no matter it already is or not.
- */
- void unset();
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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 java.util.concurrent.atomic.AtomicBoolean;
-
-public class RestartFlagHolderImpl implements RestartFlagHolder {
- private final AtomicBoolean restarting = new AtomicBoolean(false);
-
- @Override
- public boolean isRestarting() {
- return restarting.get();
- }
-
- @Override
- public void set() {
- restarting.set(true);
- }
-
- @Override
- public void unset() {
- restarting.set(false);
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.app;
-
-import javax.annotation.ParametersAreNonnullByDefault;
-
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.branch;
-
-interface BranchFeature {
-
- boolean isEnabled();
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.branch;
-
-import org.sonar.api.server.ServerSide;
-
-/**
- * The branch plugin needs to implement this in order to know that the branch feature is supported
- */
-@ServerSide
-public interface BranchFeatureExtension extends BranchFeature {
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.branch;
-
-/**
- * The goal of this class is to handle the 2 different use case :
- * - The branch plugin exists, the proxy will redirect method calls to the plugin
- * - No branch plugin, feature is disabled
- */
-public interface BranchFeatureProxy extends BranchFeature {
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.branch;
-
-public class BranchFeatureProxyImpl implements BranchFeatureProxy {
-
- private final BranchFeatureExtension branchFeatureExtension;
-
- public BranchFeatureProxyImpl() {
- this.branchFeatureExtension = null;
- }
-
- public BranchFeatureProxyImpl(BranchFeatureExtension branchFeatureExtension) {
- this.branchFeatureExtension = branchFeatureExtension;
- }
-
- @Override
- public boolean isEnabled() {
- return branchFeatureExtension != null && branchFeatureExtension.isEnabled();
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.branch;
-
-import javax.annotation.ParametersAreNonnullByDefault;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.http;
-
-import java.util.Optional;
-import org.sonar.api.utils.log.LoggerLevel;
-import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
-
-public interface CeHttpClient {
- Optional<ProtobufSystemInfo.SystemInfo> retrieveSystemInfo();
-
- void changeLogLevel(LoggerLevel level);
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.http;
-
-import javax.annotation.ParametersAreNonnullByDefault;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.exceptions;
-
-import com.google.common.base.MoreObjects;
-import java.util.List;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static java.lang.String.format;
-import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
-import static java.util.Arrays.asList;
-
-/**
- * Request is not valid and can not be processed.
- */
-public class BadRequestException extends ServerException {
-
- private final transient List<String> errors;
-
- private BadRequestException(List<String> errors) {
- super(HTTP_BAD_REQUEST, errors.get(0));
- this.errors = errors;
- }
-
- public static void checkRequest(boolean expression, String message, Object... messageArguments) {
- if (!expression) {
- throw create(format(message, messageArguments));
- }
- }
-
- public static void checkRequest(boolean expression, List<String> messages) {
- if (!expression) {
- throw create(messages);
- }
- }
-
- public static BadRequestException create(List<String> errorMessages) {
- checkArgument(!errorMessages.isEmpty(), "At least one error message is required");
- checkArgument(errorMessages.stream().noneMatch(message -> message == null || message.isEmpty()), "Message cannot be empty");
- return new BadRequestException(errorMessages);
- }
-
- public static BadRequestException create(String... errorMessages) {
- return create(asList(errorMessages));
- }
-
- public List<String> errors() {
- return errors;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(this)
- .add("errors", errors)
- .toString();
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.exceptions;
-
-import com.google.common.base.Preconditions;
-
-import static java.net.HttpURLConnection.HTTP_FORBIDDEN;
-
-/**
- * Permission denied. User does not have the required permissions.
- */
-public class ForbiddenException extends ServerException {
-
- public ForbiddenException(String message) {
- super(HTTP_FORBIDDEN, Preconditions.checkNotNull(message));
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.exceptions;
-
-import com.google.common.base.Preconditions;
-
-import static com.google.common.base.Strings.isNullOrEmpty;
-import static java.lang.String.format;
-
-public class Message {
-
- private final String msg;
-
- private Message(String format, Object... params) {
- Preconditions.checkArgument(!isNullOrEmpty(format), "Message cannot be empty");
- this.msg = format(format, params);
- }
-
- public String getMessage() {
- return msg;
- }
-
- public static Message of(String msg, Object... arguments) {
- return new Message(msg, arguments);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
-
- Message other = (Message) o;
- return this.msg.equals(other.msg);
- }
-
- @Override
- public int hashCode() {
- return msg.hashCode();
- }
-
- @Override
- public String toString() {
- return msg;
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.exceptions;
-
-import com.google.common.base.Optional;
-import javax.annotation.Nullable;
-
-import static java.lang.String.format;
-import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
-
-public class NotFoundException extends ServerException {
-
- public NotFoundException(String message) {
- super(HTTP_NOT_FOUND, message);
- }
-
- /**
- * @throws NotFoundException if the value if null
- * @return the value
- */
- public static <T> T checkFound(@Nullable T value, String message, Object... messageArguments) {
- if (value == null) {
- throw new NotFoundException(format(message, messageArguments));
- }
-
- return value;
- }
-
- /**
- * @throws NotFoundException if the value is not present
- * @return the value
- */
- public static <T> T checkFoundWithOptional(Optional<T> value, String message, Object... messageArguments) {
- if (!value.isPresent()) {
- throw new NotFoundException(format(message, messageArguments));
- }
-
- return value.get();
- }
-
- public static <T> T checkFoundWithOptional(java.util.Optional<T> value, String message, Object... messageArguments) {
- if (!value.isPresent()) {
- throw new NotFoundException(format(message, messageArguments));
- }
-
- return value.get();
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.exceptions;
-
-import static java.util.Objects.requireNonNull;
-
-public class ServerException extends RuntimeException {
- private final int httpCode;
-
- public ServerException(int httpCode, String message) {
- super(requireNonNull(message, "Error message cannot be null"));
- this.httpCode = httpCode;
- }
-
- public int httpCode() {
- return httpCode;
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.exceptions;
-
-import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
-
-/**
- * User needs to be authenticated. HTTP request is generally redirected to login form.
- */
-public class UnauthorizedException extends ServerException {
-
- public UnauthorizedException(String message) {
- super(HTTP_UNAUTHORIZED, message);
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.exceptions;
-
-import javax.annotation.ParametersAreNonnullByDefault;
-
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.health;
-
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import org.sonar.process.cluster.health.NodeHealth;
-
-import static com.google.common.collect.ImmutableSet.copyOf;
-import static java.util.Objects.requireNonNull;
-
-public class ClusterHealth {
- private final Health health;
- private final Set<NodeHealth> nodes;
-
- public ClusterHealth(Health health, Set<NodeHealth> nodes) {
- this.health = requireNonNull(health, "health can't be null");
- this.nodes = copyOf(requireNonNull(nodes, "nodes can't be null"));
- }
-
- public Health getHealth() {
- return health;
- }
-
- public Set<NodeHealth> getNodes() {
- return nodes;
- }
-
- public Optional<NodeHealth> getNodeHealth(String nodeName) {
- return nodes.stream()
- .filter(node -> nodeName.equals(node.getDetails().getName()))
- .findFirst();
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- ClusterHealth that = (ClusterHealth) o;
- return Objects.equals(health, that.health) &&
- Objects.equals(nodes, that.nodes);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(health, nodes);
- }
-
- @Override
- public String toString() {
- return "ClusterHealth{" +
- "health=" + health +
- ", nodes=" + nodes +
- '}';
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.health;
-
-import com.google.common.collect.ImmutableSet;
-import java.util.HashSet;
-import java.util.Objects;
-import java.util.Set;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static java.util.Objects.requireNonNull;
-
-public class Health {
- /**
- * The GREEN status without any cause as a constant, for convenience and optimisation.
- */
- public static final Health GREEN = newHealthCheckBuilder()
- .setStatus(Status.GREEN)
- .build();
-
- private final Status status;
- private final Set<String> causes;
-
- public Health(Builder builder) {
- this.status = builder.status;
- this.causes = ImmutableSet.copyOf(builder.causes);
- }
-
- public Status getStatus() {
- return status;
- }
-
- public Set<String> getCauses() {
- return causes;
- }
-
- public static Builder newHealthCheckBuilder() {
- return new Builder();
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- Health health = (Health) o;
- return status == health.status &&
- Objects.equals(causes, health.causes);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(status, causes);
- }
-
- @Override
- public String toString() {
- return "Health{" + status +
- ", causes=" + causes +
- '}';
- }
-
- /**
- * Builder of {@link Health} which supports being reused for optimization.
- */
- public static class Builder {
- private Status status;
- private Set<String> causes = new HashSet<>(0);
-
- private Builder() {
- // use static factory method
- }
-
- public Builder clear() {
- this.status = null;
- this.causes.clear();
- return this;
- }
-
- public Builder setStatus(Status status) {
- this.status = checkStatus(status);
- return this;
- }
-
- public Builder addCause(String cause) {
- requireNonNull(cause, "cause can't be null");
- checkArgument(!cause.trim().isEmpty(), "cause can't be empty");
- causes.add(cause);
- return this;
- }
-
- public Health build() {
- checkStatus(this.status);
- return new Health(this);
- }
-
- private static Status checkStatus(Status status) {
- return requireNonNull(status, "status can't be null");
- }
- }
-
- public enum Status {
- /**
- * Fully working
- */
- GREEN,
- /**
- * Yellow: Working but something must be fixed to make SQ fully operational
- */
- YELLOW,
- /**
- * Red: Not working
- */
- RED
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.health;
-
-public interface HealthChecker {
- /**
- * Perform a check of the health of the current SonarQube node, either as a standalone node or as a member
- * of a cluster.
- */
- Health checkNode();
-
- /**
- * Perform a check of the health of the SonarQube cluster.
- *
- * @throws IllegalStateException if clustering is not enabled.
- */
- ClusterHealth checkCluster();
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.health;
-
-import javax.annotation.ParametersAreNonnullByDefault;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.platform;
-
-import org.sonar.api.ExtensionPoint;
-import org.sonar.api.server.ServerSide;
-
-@ServerSide
-@ExtensionPoint
-public interface ClusterFeature {
-
- boolean isEnabled();
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.platform;
-
-import org.sonar.core.platform.ComponentContainer;
-
-public interface Platform {
- void doStart();
-
- Status status();
-
- ComponentContainer getContainer();
-
- enum Status {
- BOOTING, SAFEMODE, STARTING, UP
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.platform;
-
-import org.sonar.api.utils.text.JsonWriter;
-
-public interface SystemInfoWriter {
- void write(JsonWriter json) throws Exception;
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.platform;
-
-import javax.annotation.ParametersAreNonnullByDefault;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.plugins;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import javax.annotation.Nullable;
-import javax.annotation.concurrent.Immutable;
-import org.apache.commons.codec.digest.DigestUtils;
-import org.apache.commons.io.FileUtils;
-import org.sonar.core.platform.PluginInfo;
-
-import static java.util.Objects.requireNonNull;
-
-@Immutable
-public class InstalledPlugin {
- private final PluginInfo plugin;
- private final FileAndMd5 loadedJar;
- @Nullable
- private final FileAndMd5 compressedJar;
-
- public InstalledPlugin(PluginInfo plugin, FileAndMd5 loadedJar, @Nullable FileAndMd5 compressedJar) {
- this.plugin = requireNonNull(plugin);
- this.loadedJar = requireNonNull(loadedJar);
- this.compressedJar = compressedJar;
- }
-
- public PluginInfo getPluginInfo() {
- return plugin;
- }
-
- public FileAndMd5 getLoadedJar() {
- return loadedJar;
- }
-
- @Nullable
- public FileAndMd5 getCompressedJar() {
- return compressedJar;
- }
-
- @Immutable
- public static final class FileAndMd5 {
- private final File file;
- private final String md5;
-
- public FileAndMd5(File file) {
- try (InputStream fis = FileUtils.openInputStream(file)) {
- this.file = file;
- this.md5 = DigestUtils.md5Hex(fis);
- } catch (IOException e) {
- throw new IllegalStateException("Fail to compute md5 of " + file, e);
- }
- }
-
- public File getFile() {
- return file;
- }
-
- public String getMd5() {
- return md5;
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.plugins;
-
-import com.google.common.base.Optional;
-import java.io.File;
-import java.io.IOException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import org.apache.commons.io.FileUtils;
-import org.picocontainer.Startable;
-import org.sonar.api.utils.HttpDownloader;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.core.platform.PluginInfo;
-import org.sonar.core.util.stream.MoreCollectors;
-import org.sonar.server.platform.ServerFileSystem;
-import org.sonar.updatecenter.common.Release;
-import org.sonar.updatecenter.common.UpdateCenter;
-import org.sonar.updatecenter.common.Version;
-
-import static org.apache.commons.io.FileUtils.copyFile;
-import static org.apache.commons.io.FileUtils.copyFileToDirectory;
-import static org.apache.commons.io.FileUtils.forceMkdir;
-import static org.apache.commons.io.FileUtils.toFile;
-import static org.apache.commons.lang.StringUtils.substringAfterLast;
-import static org.sonar.core.util.FileUtils.deleteQuietly;
-import static org.sonar.server.exceptions.BadRequestException.checkRequest;
-
-/**
- * Downloads plugins from update center. Files are copied in the directory extensions/downloads and then
- * moved to extensions/plugins after server restart.
- */
-public class PluginDownloader implements Startable {
-
- private static final Logger LOG = Loggers.get(PluginDownloader.class);
- private static final String TMP_SUFFIX = "tmp";
- private static final String PLUGIN_EXTENSION = "jar";
-
- private final UpdateCenterMatrixFactory updateCenterMatrixFactory;
- private final HttpDownloader downloader;
- private final File downloadDir;
-
- public PluginDownloader(UpdateCenterMatrixFactory updateCenterMatrixFactory, HttpDownloader downloader,
- ServerFileSystem fileSystem) {
- this.updateCenterMatrixFactory = updateCenterMatrixFactory;
- this.downloader = downloader;
- this.downloadDir = fileSystem.getDownloadedPluginsDir();
- }
-
- /**
- * Deletes the temporary files remaining from previous downloads
- */
- @Override
- public void start() {
- try {
- forceMkdir(downloadDir);
- for (File tempFile : listTempFile(this.downloadDir)) {
- deleteQuietly(tempFile);
- }
- } catch (IOException e) {
- throw new IllegalStateException("Fail to create the directory: " + downloadDir, e);
- }
- }
-
- @Override
- public void stop() {
- // Nothing to do
- }
-
- public void cancelDownloads() {
- try {
- if (downloadDir.exists()) {
- org.sonar.core.util.FileUtils.cleanDirectory(downloadDir);
- }
- } catch (IOException e) {
- throw new IllegalStateException("Fail to clean the plugin downloads directory: " + downloadDir, e);
- }
- }
-
- public List<String> getDownloadedPluginFilenames() {
- List<String> names = new ArrayList<>();
- for (File file : listPlugins(this.downloadDir)) {
- names.add(file.getName());
- }
- return names;
- }
-
- /**
- * @return the list of download plugins as {@link PluginInfo} instances
- */
- public Collection<PluginInfo> getDownloadedPlugins() {
- return listPlugins(this.downloadDir)
- .stream()
- .map(PluginInfo::create)
- .collect(MoreCollectors.toList());
- }
-
- public void download(String pluginKey, Version version) {
- Optional<UpdateCenter> updateCenter = updateCenterMatrixFactory.getUpdateCenter(true);
- if (updateCenter.isPresent()) {
- List<Release> installablePlugins = updateCenter.get().findInstallablePlugins(pluginKey, version);
- checkRequest(!installablePlugins.isEmpty(), "Error while downloading plugin '%s' with version '%s'. No compatible plugin found.", pluginKey, version.getName());
- for (Release release : installablePlugins) {
- try {
- downloadRelease(release);
- } catch (Exception e) {
- String message = String.format("Fail to download the plugin (%s, version %s) from %s (error is : %s)",
- release.getArtifact().getKey(), release.getVersion().getName(), release.getDownloadUrl(), e.getMessage());
- LOG.debug(message, e);
- throw new IllegalStateException(message, e);
- }
- }
- }
- }
-
- private void downloadRelease(Release release) throws URISyntaxException, IOException {
- String url = release.getDownloadUrl();
-
- URI uri = new URI(url);
- if (url.startsWith("file:")) {
- // used for tests
- File file = toFile(uri.toURL());
- copyFileToDirectory(file, downloadDir);
- } else {
- String filename = substringAfterLast(uri.getPath(), "/");
- if (!filename.endsWith("." + PLUGIN_EXTENSION)) {
- filename = release.getKey() + "-" + release.getVersion() + "." + PLUGIN_EXTENSION;
- }
- File targetFile = new File(downloadDir, filename);
- File tempFile = new File(downloadDir, filename + "." + TMP_SUFFIX);
- downloader.download(uri, tempFile);
- copyFile(tempFile, targetFile);
- deleteQuietly(tempFile);
- }
- }
-
- private static Collection<File> listTempFile(File dir) {
- return FileUtils.listFiles(dir, new String[] {TMP_SUFFIX}, false);
- }
-
- private static Collection<File> listPlugins(File dir) {
- return FileUtils.listFiles(dir, new String[] {PLUGIN_EXTENSION}, false);
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.plugins;
-
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-import java.util.jar.JarInputStream;
-import java.util.jar.Pack200;
-import java.util.zip.GZIPOutputStream;
-import org.sonar.api.config.Configuration;
-import org.sonar.api.server.ServerSide;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.api.utils.log.Profiler;
-import org.sonar.core.platform.PluginInfo;
-import org.sonar.server.plugins.InstalledPlugin.FileAndMd5;
-
-import static com.google.common.base.Preconditions.checkState;
-
-@ServerSide
-public class PluginFileSystem {
-
- public static final String PROPERTY_PLUGIN_COMPRESSION_ENABLE = "sonar.pluginsCompression.enable";
- private static final Logger LOG = Loggers.get(PluginFileSystem.class);
-
- private final Configuration configuration;
- private final Map<String, InstalledPlugin> installedFiles = new HashMap<>();
-
- public PluginFileSystem(Configuration configuration) {
- this.configuration = configuration;
- }
-
- /**
- * @param plugin
- * @param loadedJar the JAR loaded by classloaders. It differs from {@code plugin.getJarFile()}
- * which is the initial location of JAR as seen by users
- */
- public void addInstalledPlugin(PluginInfo plugin, File loadedJar) {
- checkState(!installedFiles.containsKey(plugin.getKey()), "Plugin %s is already loaded", plugin.getKey());
- checkState(loadedJar.exists(), "loadedJar does not exist: %s", loadedJar);
-
- Optional<File> compressed = compressJar(plugin, loadedJar);
- InstalledPlugin installedFile = new InstalledPlugin(
- plugin,
- new FileAndMd5(loadedJar),
- compressed.map(FileAndMd5::new).orElse(null));
- installedFiles.put(plugin.getKey(), installedFile);
- }
-
- public Optional<InstalledPlugin> getInstalledPlugin(String pluginKey) {
- return Optional.ofNullable(installedFiles.get(pluginKey));
- }
-
- public Collection<InstalledPlugin> getInstalledFiles() {
- return installedFiles.values();
- }
-
- private Optional<File> compressJar(PluginInfo plugin, File jar) {
- if (!configuration.getBoolean(PROPERTY_PLUGIN_COMPRESSION_ENABLE).orElse(false)) {
- return Optional.empty();
- }
-
- Path targetPack200 = getPack200Path(jar.toPath());
- Path sourcePack200Path = getPack200Path(plugin.getNonNullJarFile().toPath());
-
- // check if packed file was deployed alongside the jar. If that's the case, use it instead of generating it (SONAR-10395).
- if (sourcePack200Path.toFile().exists()) {
- try {
- LOG.debug("Found pack200: " + sourcePack200Path);
- Files.copy(sourcePack200Path, targetPack200);
- } catch (IOException e) {
- throw new IllegalStateException("Failed to copy pack200 file from " + sourcePack200Path + " to " + targetPack200, e);
- }
- } else {
- pack200(jar.toPath(), targetPack200, plugin.getKey());
- }
- return Optional.of(targetPack200.toFile());
- }
-
- private static void pack200(Path jarPath, Path toPack200Path, String pluginKey) {
- Profiler profiler = Profiler.create(LOG);
- profiler.startInfo("Compressing plugin " + pluginKey + " [pack200]");
-
- try (JarInputStream in = new JarInputStream(new BufferedInputStream(Files.newInputStream(jarPath)));
- OutputStream out = new GZIPOutputStream(new BufferedOutputStream(Files.newOutputStream(toPack200Path)))) {
- Pack200.newPacker().pack(in, out);
- } catch (IOException e) {
- throw new IllegalStateException(String.format("Fail to pack200 plugin [%s] '%s' to '%s'", pluginKey, jarPath, toPack200Path), e);
- }
- profiler.stopInfo();
- }
-
- private static Path getPack200Path(Path jar) {
- String jarFileName = jar.getFileName().toString();
- String filename = jarFileName.substring(0, jarFileName.length() - 3) + "pack.gz";
- return jar.resolveSibling(filename);
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.plugins;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Collection;
-import java.util.Collections;
-import org.apache.commons.io.FileUtils;
-import org.picocontainer.Startable;
-import org.sonar.core.platform.PluginInfo;
-import org.sonar.core.util.stream.MoreCollectors;
-import org.sonar.server.platform.ServerFileSystem;
-
-import static java.lang.String.format;
-import static org.apache.commons.io.FileUtils.forceMkdir;
-
-public class PluginUninstaller implements Startable {
- private static final String PLUGIN_EXTENSION = "jar";
- private final ServerPluginRepository serverPluginRepository;
- private final File uninstallDir;
-
- public PluginUninstaller(ServerPluginRepository serverPluginRepository, ServerFileSystem fs) {
- this.serverPluginRepository = serverPluginRepository;
- this.uninstallDir = fs.getUninstalledPluginsDir();
- }
-
- private static Collection<File> listJarFiles(File dir) {
- if (dir.exists()) {
- return FileUtils.listFiles(dir, new String[] {PLUGIN_EXTENSION}, false);
- }
- return Collections.emptyList();
- }
-
- @Override
- public void start() {
- try {
- forceMkdir(uninstallDir);
- } catch (IOException e) {
- throw new IllegalStateException("Fail to create the directory: " + uninstallDir, e);
- }
- }
-
- @Override
- public void stop() {
- // Nothing to do
- }
-
- public void uninstall(String pluginKey) {
- ensurePluginIsInstalled(pluginKey);
- serverPluginRepository.uninstall(pluginKey, uninstallDir);
- }
-
- public void cancelUninstalls() {
- serverPluginRepository.cancelUninstalls(uninstallDir);
- }
-
- /**
- * @return the list of plugins to be uninstalled as {@link PluginInfo} instances
- */
- public Collection<PluginInfo> getUninstalledPlugins() {
- return listJarFiles(uninstallDir)
- .stream()
- .map(PluginInfo::create)
- .collect(MoreCollectors.toList());
- }
-
- private void ensurePluginIsInstalled(String key) {
- if (!serverPluginRepository.hasPlugin(key)) {
- throw new IllegalArgumentException(format("Plugin [%s] is not installed", key));
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.plugins;
-
-import java.io.File;
-import org.apache.commons.io.FileUtils;
-import org.sonar.api.server.ServerSide;
-import org.sonar.api.utils.ZipUtils;
-import org.sonar.core.platform.ExplodedPlugin;
-import org.sonar.core.platform.PluginInfo;
-import org.sonar.core.platform.PluginJarExploder;
-import org.sonar.server.platform.ServerFileSystem;
-
-import static org.apache.commons.io.FileUtils.forceMkdir;
-
-@ServerSide
-public class ServerPluginJarExploder extends PluginJarExploder {
- private final ServerFileSystem fs;
- private final PluginFileSystem pluginFileSystem;
-
- public ServerPluginJarExploder(ServerFileSystem fs, PluginFileSystem pluginFileSystem) {
- this.fs = fs;
- this.pluginFileSystem = pluginFileSystem;
- }
-
- /**
- * JAR files of directory extensions/plugins can be moved when server is up and plugins are uninstalled.
- * For this reason these files must not be locked by classloaders. They are copied to the directory
- * web/deploy/plugins in order to be loaded by {@link org.sonar.core.platform.PluginLoader}.
- */
- @Override
- public ExplodedPlugin explode(PluginInfo pluginInfo) {
- File toDir = new File(fs.getDeployedPluginsDir(), pluginInfo.getKey());
- try {
- forceMkdir(toDir);
- org.sonar.core.util.FileUtils.cleanDirectory(toDir);
-
- File jarSource = pluginInfo.getNonNullJarFile();
- File jarTarget = new File(toDir, jarSource.getName());
-
- FileUtils.copyFile(jarSource, jarTarget);
- ZipUtils.unzip(jarSource, toDir, newLibFilter());
- ExplodedPlugin explodedPlugin = explodeFromUnzippedDir(pluginInfo.getKey(), jarTarget, toDir);
- pluginFileSystem.addInstalledPlugin(pluginInfo, jarTarget);
- return explodedPlugin;
- } catch (Exception e) {
- throw new IllegalStateException(String.format(
- "Fail to unzip plugin [%s] %s to %s", pluginInfo.getKey(), pluginInfo.getNonNullJarFile().getAbsolutePath(), toDir.getAbsolutePath()), e);
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.plugins;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Joiner;
-import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Ordering;
-import java.io.File;
-import java.io.IOException;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
-import javax.annotation.CheckForNull;
-import org.apache.commons.io.FileUtils;
-import org.picocontainer.Startable;
-import org.sonar.api.Plugin;
-import org.sonar.api.SonarRuntime;
-import org.sonar.api.utils.MessageException;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.core.platform.PluginInfo;
-import org.sonar.core.platform.PluginLoader;
-import org.sonar.core.platform.PluginRepository;
-import org.sonar.server.platform.ServerFileSystem;
-import org.sonar.updatecenter.common.Version;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
-import static java.lang.String.format;
-import static org.apache.commons.io.FileUtils.moveFile;
-import static org.apache.commons.io.FileUtils.moveFileToDirectory;
-import static org.sonar.core.util.FileUtils.deleteQuietly;
-
-/**
- * Entry point to install and load plugins on server startup. It manages
- * <ul>
- * <li>installation of new plugins (effective after server startup)</li>
- * <li>un-installation of plugins (effective after server startup)</li>
- * <li>cancel pending installations/un-installations</li>
- * <li>instantiation of plugin entry-points</li>
- * </ul>
- */
-public class ServerPluginRepository implements PluginRepository, Startable {
-
- private static final Logger LOG = Loggers.get(ServerPluginRepository.class);
- private static final String[] JAR_FILE_EXTENSIONS = new String[] {"jar"};
- // List of plugins that are silently removed if installed
- private static final Set<String> DEFAULT_BLACKLISTED_PLUGINS = ImmutableSet.of("scmactivity", "issuesreport", "genericcoverage");
- // List of plugins that should prevent the server to finish its startup
- private static final Set<String> FORBIDDEN_COMPATIBLE_PLUGINS = ImmutableSet.of("sqale", "report", "views");
- private static final Joiner SLASH_JOINER = Joiner.on(" / ").skipNulls();
- private static final String NOT_STARTED_YET = "not started yet";
-
- private final SonarRuntime runtime;
- private final ServerFileSystem fs;
- private final PluginLoader loader;
- private final AtomicBoolean started = new AtomicBoolean(false);
- private Set<String> blacklistedPluginKeys = DEFAULT_BLACKLISTED_PLUGINS;
-
- // following fields are available after startup
- private final Map<String, PluginInfo> pluginInfosByKeys = new HashMap<>();
- private final Map<String, Plugin> pluginInstancesByKeys = new HashMap<>();
- private final Map<ClassLoader, String> keysByClassLoader = new HashMap<>();
-
- public ServerPluginRepository(SonarRuntime runtime, ServerFileSystem fs, PluginLoader loader) {
- this.runtime = runtime;
- this.fs = fs;
- this.loader = loader;
- }
-
- @VisibleForTesting
- void setBlacklistedPluginKeys(Set<String> keys) {
- this.blacklistedPluginKeys = keys;
- }
-
- @Override
- public void start() {
- loadPreInstalledPlugins();
- moveDownloadedPlugins();
- unloadIncompatiblePlugins();
- logInstalledPlugins();
- loadInstances();
- started.set(true);
- }
-
- @Override
- public void stop() {
- // close classloaders
- loader.unload(pluginInstancesByKeys.values());
- pluginInstancesByKeys.clear();
- pluginInfosByKeys.clear();
- keysByClassLoader.clear();
- started.set(true);
- }
-
- /**
- * Return the key of the plugin the extension (in the sense of {@link Plugin.Context#addExtension(Object)} is coming from.
- */
- @CheckForNull
- public String getPluginKey(Object extension) {
- return keysByClassLoader.get(extension.getClass().getClassLoader());
- }
-
- /**
- * Load the plugins that are located in extensions/plugins. Blacklisted plugins are
- * deleted.
- */
- private void loadPreInstalledPlugins() {
- for (File file : listJarFiles(fs.getInstalledPluginsDir())) {
- PluginInfo info = PluginInfo.create(file);
- registerPluginInfo(info);
- }
- }
-
- /**
- * Move the plugins recently downloaded to extensions/plugins.
- */
- private void moveDownloadedPlugins() {
- if (fs.getDownloadedPluginsDir().exists()) {
- for (File sourceFile : listJarFiles(fs.getDownloadedPluginsDir())) {
- overrideAndRegisterPlugin(sourceFile);
- }
- }
- }
-
- private void registerPluginInfo(PluginInfo info) {
- String pluginKey = info.getKey();
- if (blacklistedPluginKeys.contains(pluginKey)) {
- LOG.warn("Plugin {} [{}] is blacklisted and is being uninstalled", info.getName(), pluginKey);
- deleteQuietly(info.getNonNullJarFile());
- return;
- }
- if (FORBIDDEN_COMPATIBLE_PLUGINS.contains(pluginKey)) {
- throw MessageException.of(String.format("Plugin '%s' is no longer compatible with this version of SonarQube", pluginKey));
- }
- PluginInfo existing = pluginInfosByKeys.put(pluginKey, info);
- if (existing != null) {
- throw MessageException.of(format("Found two versions of the plugin %s [%s] in the directory extensions/plugins. Please remove one of %s or %s.",
- info.getName(), pluginKey, info.getNonNullJarFile().getName(), existing.getNonNullJarFile().getName()));
- }
-
- }
-
- /**
- * Move or copy plugin to directory extensions/plugins. If a version of this plugin
- * already exists then it's deleted.
- */
- private void overrideAndRegisterPlugin(File sourceFile) {
- File destDir = fs.getInstalledPluginsDir();
- File destFile = new File(destDir, sourceFile.getName());
- if (destFile.exists()) {
- // plugin with same filename already installed
- deleteQuietly(destFile);
- }
-
- try {
- moveFile(sourceFile, destFile);
-
- } catch (IOException e) {
- throw new IllegalStateException(format("Fail to move plugin: %s to %s",
- sourceFile.getAbsolutePath(), destFile.getAbsolutePath()), e);
- }
-
- PluginInfo info = PluginInfo.create(destFile);
- PluginInfo existing = pluginInfosByKeys.put(info.getKey(), info);
- if (existing != null) {
- if (!existing.getNonNullJarFile().getName().equals(destFile.getName())) {
- deleteQuietly(existing.getNonNullJarFile());
- }
- LOG.info("Plugin {} [{}] updated to version {}", info.getName(), info.getKey(), info.getVersion());
- } else {
- LOG.info("Plugin {} [{}] installed", info.getName(), info.getKey());
- }
- }
-
- /**
- * Removes the plugins that are not compatible with current environment.
- */
- private void unloadIncompatiblePlugins() {
- // loop as long as the previous loop ignored some plugins. That allows to support dependencies
- // on many levels, for example D extends C, which extends B, which requires A. If A is not installed,
- // then B, C and D must be ignored. That's not possible to achieve this algorithm with a single
- // iteration over plugins.
- Set<String> removedKeys = new HashSet<>();
- do {
- removedKeys.clear();
- for (PluginInfo plugin : pluginInfosByKeys.values()) {
- if (!isCompatible(plugin, runtime, pluginInfosByKeys)) {
- removedKeys.add(plugin.getKey());
- }
- }
- for (String removedKey : removedKeys) {
- pluginInfosByKeys.remove(removedKey);
- }
- } while (!removedKeys.isEmpty());
- }
-
- @VisibleForTesting
- static boolean isCompatible(PluginInfo plugin, SonarRuntime runtime, Map<String, PluginInfo> allPluginsByKeys) {
- if (Strings.isNullOrEmpty(plugin.getMainClass()) && Strings.isNullOrEmpty(plugin.getBasePlugin())) {
- LOG.warn("Plugin {} [{}] is ignored because entry point class is not defined", plugin.getName(), plugin.getKey());
- return false;
- }
-
- if (!plugin.isCompatibleWith(runtime.getApiVersion().toString())) {
- throw MessageException.of(format(
- "Plugin %s [%s] requires at least SonarQube %s", plugin.getName(), plugin.getKey(), plugin.getMinimalSqVersion()));
- }
-
- if (!Strings.isNullOrEmpty(plugin.getBasePlugin()) && !allPluginsByKeys.containsKey(plugin.getBasePlugin())) {
- // it extends a plugin that is not installed
- LOG.warn("Plugin {} [{}] is ignored because its base plugin [{}] is not installed", plugin.getName(), plugin.getKey(), plugin.getBasePlugin());
- return false;
- }
-
- for (PluginInfo.RequiredPlugin requiredPlugin : plugin.getRequiredPlugins()) {
- PluginInfo installedRequirement = allPluginsByKeys.get(requiredPlugin.getKey());
- if (installedRequirement == null) {
- // it requires a plugin that is not installed
- LOG.warn("Plugin {} [{}] is ignored because the required plugin [{}] is not installed", plugin.getName(), plugin.getKey(), requiredPlugin.getKey());
- return false;
- }
- Version installedRequirementVersion = installedRequirement.getVersion();
- if (installedRequirementVersion != null && requiredPlugin.getMinimalVersion().compareToIgnoreQualifier(installedRequirementVersion) > 0) {
- // it requires a more recent version
- LOG.warn("Plugin {} [{}] is ignored because the version {} of required plugin [{}] is not supported", plugin.getName(), plugin.getKey(),
- requiredPlugin.getKey(), requiredPlugin.getMinimalVersion());
- return false;
- }
- }
- return true;
- }
-
- private void logInstalledPlugins() {
- List<PluginInfo> orderedPlugins = Ordering.natural().sortedCopy(pluginInfosByKeys.values());
- for (PluginInfo plugin : orderedPlugins) {
- LOG.info("Deploy plugin {}", SLASH_JOINER.join(plugin.getName(), plugin.getVersion(), plugin.getImplementationBuild()));
- }
- }
-
- private void loadInstances() {
- pluginInstancesByKeys.putAll(loader.load(pluginInfosByKeys));
-
- for (Map.Entry<String, Plugin> e : pluginInstancesByKeys.entrySet()) {
- keysByClassLoader.put(e.getValue().getClass().getClassLoader(), e.getKey());
- }
- }
-
- /**
- * Uninstall a plugin and its dependents
- */
- public void uninstall(String pluginKey, File uninstallDir) {
- Set<String> uninstallKeys = new HashSet<>();
- uninstallKeys.add(pluginKey);
- appendDependentPluginKeys(pluginKey, uninstallKeys);
-
- for (String uninstallKey : uninstallKeys) {
- PluginInfo info = getPluginInfo(uninstallKey);
-
- try {
- if (!getPluginFile(info).exists()) {
- LOG.info("Plugin already uninstalled: {} [{}]", info.getName(), info.getKey());
- continue;
- }
-
- LOG.info("Uninstalling plugin {} [{}]", info.getName(), info.getKey());
-
- File masterFile = getPluginFile(info);
- moveFileToDirectory(masterFile, uninstallDir, true);
- } catch (IOException e) {
- throw new IllegalStateException(format("Fail to uninstall plugin %s [%s]", info.getName(), info.getKey()), e);
- }
- }
- }
-
- public void cancelUninstalls(File uninstallDir) {
- for (File file : listJarFiles(uninstallDir)) {
- try {
- moveFileToDirectory(file, fs.getInstalledPluginsDir(), false);
- } catch (IOException e) {
- throw new IllegalStateException("Fail to cancel plugin uninstalls", e);
- }
- }
- }
-
- /**
- * Appends dependent plugins, only the ones that still exist in the plugins folder.
- */
- private void appendDependentPluginKeys(String pluginKey, Set<String> appendTo) {
- for (PluginInfo otherPlugin : getPluginInfos()) {
- if (!otherPlugin.getKey().equals(pluginKey)) {
- for (PluginInfo.RequiredPlugin requirement : otherPlugin.getRequiredPlugins()) {
- if (requirement.getKey().equals(pluginKey)) {
- appendTo.add(otherPlugin.getKey());
- appendDependentPluginKeys(otherPlugin.getKey(), appendTo);
- }
- }
- }
- }
- }
-
- private File getPluginFile(PluginInfo info) {
- // we don't reuse info.getFile() just to be sure that file is located in from extensions/plugins
- return new File(fs.getInstalledPluginsDir(), info.getNonNullJarFile().getName());
- }
-
- public Map<String, PluginInfo> getPluginInfosByKeys() {
- return pluginInfosByKeys;
- }
-
- @Override
- public Collection<PluginInfo> getPluginInfos() {
- checkState(started.get(), NOT_STARTED_YET);
- return ImmutableList.copyOf(pluginInfosByKeys.values());
- }
-
- @Override
- public PluginInfo getPluginInfo(String key) {
- checkState(started.get(), NOT_STARTED_YET);
- PluginInfo info = pluginInfosByKeys.get(key);
- if (info == null) {
- throw new IllegalArgumentException(format("Plugin [%s] does not exist", key));
- }
- return info;
- }
-
- @Override
- public Plugin getPluginInstance(String key) {
- checkState(started.get(), NOT_STARTED_YET);
- Plugin plugin = pluginInstancesByKeys.get(key);
- checkArgument(plugin != null, "Plugin [%s] does not exist", key);
- return plugin;
- }
-
- @Override
- public boolean hasPlugin(String key) {
- checkState(started.get(), NOT_STARTED_YET);
- return pluginInfosByKeys.containsKey(key);
- }
-
- private static Collection<File> listJarFiles(File dir) {
- if (dir.exists()) {
- return FileUtils.listFiles(dir, JAR_FILE_EXTENSIONS, false);
- }
- return Collections.emptyList();
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.plugins;
-
-import com.google.common.base.Optional;
-import java.io.InputStream;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.nio.charset.StandardCharsets;
-import java.util.Date;
-import org.apache.commons.io.IOUtils;
-import org.sonar.api.Properties;
-import org.sonar.api.Property;
-import org.sonar.api.config.Configuration;
-import org.sonar.api.utils.UriReader;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.process.ProcessProperties;
-import org.sonar.updatecenter.common.UpdateCenter;
-import org.sonar.updatecenter.common.UpdateCenterDeserializer;
-import org.sonar.updatecenter.common.UpdateCenterDeserializer.Mode;
-
-/**
- * HTTP client to load data from the remote update center hosted at https://update.sonarsource.org.
- *
- * @since 2.4
- */
-@Properties({
- @Property(
- key = UpdateCenterClient.URL_PROPERTY,
- defaultValue = "https://update.sonarsource.org/update-center.properties",
- name = "Update Center URL",
- category = "Update Center",
- project = false,
- // hidden from UI
- global = false)
-})
-public class UpdateCenterClient {
-
- public static final String URL_PROPERTY = "sonar.updatecenter.url";
- public static final int PERIOD_IN_MILLISECONDS = 60 * 60 * 1000;
-
- private final URI uri;
- private final UriReader uriReader;
- private final boolean isActivated;
- private UpdateCenter pluginCenter = null;
- private long lastRefreshDate = 0;
-
- public UpdateCenterClient(UriReader uriReader, Configuration config) throws URISyntaxException {
- this.uriReader = uriReader;
- this.uri = new URI(config.get(URL_PROPERTY).get());
- this.isActivated = config.getBoolean(ProcessProperties.Property.SONAR_UPDATECENTER_ACTIVATE.getKey()).get();
- Loggers.get(getClass()).info("Update center: " + uriReader.description(uri));
- }
-
- public Optional<UpdateCenter> getUpdateCenter() {
- return getUpdateCenter(false);
- }
-
- public Optional<UpdateCenter> getUpdateCenter(boolean forceRefresh) {
- if (!isActivated) {
- return Optional.absent();
- }
-
- if (pluginCenter == null || forceRefresh || needsRefresh()) {
- pluginCenter = init();
- lastRefreshDate = System.currentTimeMillis();
- }
- return Optional.fromNullable(pluginCenter);
- }
-
- public Date getLastRefreshDate() {
- return lastRefreshDate > 0 ? new Date(lastRefreshDate) : null;
- }
-
- private boolean needsRefresh() {
- return lastRefreshDate + PERIOD_IN_MILLISECONDS < System.currentTimeMillis();
- }
-
- private UpdateCenter init() {
- InputStream input = null;
- try {
- String content = uriReader.readString(uri, StandardCharsets.UTF_8);
- java.util.Properties properties = new java.util.Properties();
- input = IOUtils.toInputStream(content, StandardCharsets.UTF_8);
- properties.load(input);
- return new UpdateCenterDeserializer(Mode.PROD, true).fromProperties(properties);
-
- } catch (Exception e) {
- Loggers.get(getClass()).error("Fail to connect to update center", e);
- return null;
-
- } finally {
- IOUtils.closeQuietly(input);
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.plugins;
-
-import com.google.common.base.Optional;
-import org.sonar.api.SonarRuntime;
-import org.sonar.updatecenter.common.UpdateCenter;
-import org.sonar.updatecenter.common.Version;
-
-/**
- * @since 2.4
- */
-public class UpdateCenterMatrixFactory {
-
- private final UpdateCenterClient centerClient;
- private final SonarRuntime sonarRuntime;
- private final InstalledPluginReferentialFactory installedPluginReferentialFactory;
-
- public UpdateCenterMatrixFactory(UpdateCenterClient centerClient, SonarRuntime runtime,
- InstalledPluginReferentialFactory installedPluginReferentialFactory) {
- this.centerClient = centerClient;
- this.sonarRuntime = runtime;
- this.installedPluginReferentialFactory = installedPluginReferentialFactory;
- }
-
- public Optional<UpdateCenter> getUpdateCenter(boolean refreshUpdateCenter) {
- Optional<UpdateCenter> updateCenter = centerClient.getUpdateCenter(refreshUpdateCenter);
- if (updateCenter.isPresent()) {
- org.sonar.api.utils.Version fullVersion = sonarRuntime.getApiVersion();
- org.sonar.api.utils.Version semanticVersion = org.sonar.api.utils.Version.create(fullVersion.major(), fullVersion.minor(), fullVersion.patch());
-
- return Optional.of(updateCenter.get().setInstalledSonarVersion(Version.create(semanticVersion.toString())).registerInstalledPlugins(
- installedPluginReferentialFactory.getInstalledPluginReferential())
- .setDate(centerClient.getLastRefreshDate()));
- }
- return Optional.absent();
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.plugins;
-
-import org.sonar.api.SonarRuntime;
-import org.sonar.api.server.ServerSide;
-import org.sonar.core.platform.PluginRepository;
-
-import static java.util.Collections.singleton;
-
-@ServerSide
-public class WebServerExtensionInstaller extends ServerExtensionInstaller {
- public WebServerExtensionInstaller(SonarRuntime sonarRuntime, PluginRepository pluginRepository) {
- super(sonarRuntime, pluginRepository, singleton(ServerSide.class));
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.plugins.edition;
-
-import java.util.Arrays;
-import org.sonar.core.platform.PluginInfo;
-import org.sonar.updatecenter.common.Plugin;
-
-public final class EditionBundledPlugins {
-
- private static final String SONARSOURCE_ORGANIZATION = "SonarSource";
- private static final String[] SONARSOURCE_COMMERCIAL_LICENSES = {"SonarSource", "Commercial"};
-
- private EditionBundledPlugins() {
- // prevents instantiation
- }
-
- public static boolean isEditionBundled(Plugin plugin) {
- return SONARSOURCE_ORGANIZATION.equalsIgnoreCase(plugin.getOrganization())
- && Arrays.stream(SONARSOURCE_COMMERCIAL_LICENSES).anyMatch(s -> s.equalsIgnoreCase(plugin.getLicense()));
- }
-
- public static boolean isEditionBundled(PluginInfo pluginInfo) {
- return SONARSOURCE_ORGANIZATION.equalsIgnoreCase(pluginInfo.getOrganizationName())
- && Arrays.stream(SONARSOURCE_COMMERCIAL_LICENSES).anyMatch(s -> s.equalsIgnoreCase(pluginInfo.getLicense()));
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.plugins.edition;
-
-import javax.annotation.ParametersAreNonnullByDefault;
-
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.plugins;
-
-import javax.annotation.ParametersAreNonnullByDefault;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.project;
-
-import java.util.Set;
-import org.sonar.api.server.ServerSide;
-
-@ServerSide
-public interface ProjectLifeCycleListener {
- /**
- * This method is called after the specified projects have been deleted.
- */
- void onProjectsDeleted(Set<Project> projects);
-
- /**
- * This method is called after the specified projects have been deleted.
- */
- void onProjectBranchesDeleted(Set<Project> projects);
-
- /**
- * This method is called after the specified projects' keys have been modified.
- */
- void onProjectsRekeyed(Set<RekeyedProject> rekeyedProjects);
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.project;
-
-import java.util.Set;
-
-public interface ProjectLifeCycleListeners {
- /**
- * This method is called after the specified projects have been deleted and will call method
- * {@link ProjectLifeCycleListener#onProjectsDeleted(Set) onProjectsDeleted(Set)} of all known
- * {@link ProjectLifeCycleListener} implementations.
- * <p>
- * This method ensures all {@link ProjectLifeCycleListener} implementations are called, even if one or more of
- * them fail with an exception.
- */
- void onProjectsDeleted(Set<Project> projects);
-
- /**
- * This method is called after the specified project branches have been deleted and will call method
- * {@link ProjectLifeCycleListener#onProjectBranchesDeleted(Set)} of all known
- * {@link ProjectLifeCycleListener} implementations.
- * <p>
- * This method ensures all {@link ProjectLifeCycleListener} implementations are called, even if one or more of
- * them fail with an exception.
- */
- void onProjectBranchesDeleted(Set<Project> projects);
-
- /**
- * This method is called after the specified project's key has been changed and will call method
- * {@link ProjectLifeCycleListener#onProjectsRekeyed(Set) onProjectsRekeyed(Set)} of all known
- * {@link ProjectLifeCycleListener} implementations.
- * <p>
- * This method ensures all {@link ProjectLifeCycleListener} implementations are called, even if one or more of
- * them fail with an exception.
- */
- void onProjectsRekeyed(Set<RekeyedProject> rekeyedProjects);
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.project;
-
-import java.util.Arrays;
-import java.util.Set;
-import java.util.function.Consumer;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-public class ProjectLifeCycleListenersImpl implements ProjectLifeCycleListeners {
- private static final Logger LOG = Loggers.get(ProjectLifeCycleListenersImpl.class);
-
- private final ProjectLifeCycleListener[] listeners;
-
- /**
- * Used by Pico when there is no ProjectLifeCycleListener implementation in container.
- */
- public ProjectLifeCycleListenersImpl() {
- this.listeners = new ProjectLifeCycleListener[0];
- }
-
- /**
- * Used by Pico when there is at least one ProjectLifeCycleListener implementation in container.
- */
- public ProjectLifeCycleListenersImpl(ProjectLifeCycleListener[] listeners) {
- this.listeners = listeners;
- }
-
- @Override
- public void onProjectsDeleted(Set<Project> projects) {
- checkNotNull(projects, "projects can't be null");
- if (projects.isEmpty()) {
- return;
- }
-
- Arrays.stream(listeners)
- .forEach(safelyCallListener(listener -> listener.onProjectsDeleted(projects)));
- }
-
- @Override
- public void onProjectBranchesDeleted(Set<Project> projects) {
- checkNotNull(projects, "projects can't be null");
- if (projects.isEmpty()) {
- return;
- }
-
- Arrays.stream(listeners)
- .forEach(safelyCallListener(listener -> listener.onProjectBranchesDeleted(projects)));
- }
-
- @Override
- public void onProjectsRekeyed(Set<RekeyedProject> rekeyedProjects) {
- checkNotNull(rekeyedProjects, "rekeyedProjects can't be null");
- if (rekeyedProjects.isEmpty()) {
- return;
- }
-
- Arrays.stream(listeners)
- .forEach(safelyCallListener(listener -> listener.onProjectsRekeyed(rekeyedProjects)));
- }
-
- private static Consumer<ProjectLifeCycleListener> safelyCallListener(Consumer<ProjectLifeCycleListener> task) {
- return listener -> {
- try {
- task.accept(listener);
- } catch (Error | Exception e) {
- LOG.error("Call on ProjectLifeCycleListener \"{}\" failed", listener.getClass(), e);
- }
- };
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.project;
-
-import java.util.Objects;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-public final class RekeyedProject {
- private final Project project;
- private final String previousKey;
-
- public RekeyedProject(Project project, String previousKey) {
- this.project = checkNotNull(project, "project can't be null");
- this.previousKey = checkNotNull(previousKey, "previousKey can't be null");
- }
-
- public Project getProject() {
- return project;
- }
-
- public String getPreviousKey() {
- return previousKey;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- RekeyedProject that = (RekeyedProject) o;
- return Objects.equals(project, that.project) &&
- Objects.equals(previousKey, that.previousKey);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(project, previousKey);
- }
-
- @Override
- public String toString() {
- return "RekeyedProject{" +
- "project=" + project +
- ", previousKey='" + previousKey + '\'' +
- '}';
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.project;
-
-import java.util.List;
-
-import static java.util.Arrays.stream;
-import static org.sonar.core.util.stream.MoreCollectors.toList;
-
-public enum Visibility {
-
- PRIVATE(true, "private"),
- PUBLIC(false, "public");
-
- private static final List<String> LABELS = stream(values()).map(Visibility::getLabel).collect(toList(values().length));
-
- private final boolean isPrivate;
- private final String label;
-
- Visibility(boolean isPrivate, String label) {
- this.isPrivate = isPrivate;
- this.label = label;
- }
-
- public String getLabel() {
- return label;
- }
-
- boolean isPrivate() {
- return isPrivate;
- }
-
- public static String getLabel(boolean isPrivate) {
- return stream(values())
- .filter(v -> v.isPrivate == isPrivate)
- .map(Visibility::getLabel)
- .findAny()
- .orElseThrow(() -> new IllegalStateException("Invalid visibility boolean '" + isPrivate + "'"));
- }
-
- public static boolean isPrivate(String label) {
- return parseVisibility(label).isPrivate();
- }
-
- public static Visibility parseVisibility(String label) {
- return stream(values())
- .filter(v -> v.label.equals(label))
- .findAny()
- .orElseThrow(() -> new IllegalStateException("Invalid visibility label '" + label + "'"));
- }
-
- public static List<String> getLabels() {
- return LABELS;
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.project;
-
-import javax.annotation.ParametersAreNonnullByDefault;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.qualitygate;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-/**
- * Store number of projects in warning in order for the web service api/components/search to know if warning value should be return in the quality gate facet.
- * The value is updated each time the daemon {@link ProjectsInWarningDaemon} is executed
- */
-public class ProjectsInWarning {
-
- private Long projectsInWarning;
-
- public void update(long projectsInWarning) {
- this.projectsInWarning = projectsInWarning;
- }
-
- public long count() {
- checkArgument(isInitialized(), "Initialization has not be done");
- return projectsInWarning;
- }
-
- boolean isInitialized() {
- return projectsInWarning != null;
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.qualitygate.changeevent;
-
-import java.util.Optional;
-import java.util.function.Supplier;
-import javax.annotation.Nullable;
-import javax.annotation.concurrent.Immutable;
-import org.sonar.api.config.Configuration;
-import org.sonar.api.measures.Metric;
-import org.sonar.db.component.BranchDto;
-import org.sonar.db.component.ComponentDto;
-import org.sonar.db.component.SnapshotDto;
-import org.sonar.server.qualitygate.EvaluatedQualityGate;
-
-import static java.util.Objects.requireNonNull;
-
-@Immutable
-public class QGChangeEvent {
- private final ComponentDto project;
- private final BranchDto branch;
- private final SnapshotDto analysis;
- private final Configuration projectConfiguration;
- private final Metric.Level previousStatus;
- private final Supplier<Optional<EvaluatedQualityGate>> qualityGateSupplier;
-
- public QGChangeEvent(ComponentDto project, BranchDto branch, SnapshotDto analysis, Configuration projectConfiguration,
- @Nullable Metric.Level previousStatus, Supplier<Optional<EvaluatedQualityGate>> qualityGateSupplier) {
- this.project = requireNonNull(project, "project can't be null");
- this.branch = requireNonNull(branch, "branch can't be null");
- this.analysis = requireNonNull(analysis, "analysis can't be null");
- this.projectConfiguration = requireNonNull(projectConfiguration, "projectConfiguration can't be null");
- this.previousStatus = previousStatus;
- this.qualityGateSupplier = requireNonNull(qualityGateSupplier, "qualityGateSupplier can't be null");
- }
-
- public BranchDto getBranch() {
- return branch;
- }
-
- public ComponentDto getProject() {
- return project;
- }
-
- public SnapshotDto getAnalysis() {
- return analysis;
- }
-
- public Configuration getProjectConfiguration() {
- return projectConfiguration;
- }
-
- public Optional<Metric.Level> getPreviousStatus() {
- return Optional.ofNullable(previousStatus);
- }
-
- public Supplier<Optional<EvaluatedQualityGate>> getQualityGateSupplier() {
- return qualityGateSupplier;
- }
-
- @Override
- public String toString() {
- return "QGChangeEvent{" +
- "project=" + toString(project) +
- ", branch=" + toString(branch) +
- ", analysis=" + toString(analysis) +
- ", projectConfiguration=" + projectConfiguration +
- ", previousStatus=" + previousStatus +
- ", qualityGateSupplier=" + qualityGateSupplier +
- '}';
- }
-
- private static String toString(ComponentDto project) {
- return project.uuid() + ":" + project.getKey();
- }
-
- private static String toString(BranchDto branch) {
- return branch.getBranchType() + ":" + branch.getUuid() + ":" + branch.getProjectUuid() + ":" + branch.getMergeBranchUuid();
- }
-
- private static String toString(SnapshotDto analysis) {
- return analysis.getUuid() + ":" + analysis.getCreatedAt();
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.qualitygate.changeevent;
-
-import java.util.EnumSet;
-import java.util.Set;
-import org.sonar.api.rules.RuleType;
-import org.sonar.api.server.ServerSide;
-
-@ServerSide
-public interface QGChangeEventListener {
- /**
- * Called consequently to a change done on one or more issue of a given project.
- *
- * @param qualityGateEvent can not be {@code null}
- * @param changedIssues can not be {@code null} nor empty
- */
- void onIssueChanges(QGChangeEvent qualityGateEvent, Set<ChangedIssue> changedIssues);
-
- interface ChangedIssue {
-
- String getKey();
-
- Status getStatus();
-
- RuleType getType();
-
- String getSeverity();
-
- default boolean isNotClosed() {
- return !Status.CLOSED_STATUSES.contains(getStatus());
- }
- }
-
- enum Status {
- OPEN,
- CONFIRMED,
- REOPENED,
- RESOLVED_FP,
- RESOLVED_WF,
- RESOLVED_FIXED,
- TO_REVIEW,
- IN_REVIEW,
- REVIEWED;
-
- protected static final Set<Status> CLOSED_STATUSES = EnumSet.of(CONFIRMED, RESOLVED_FIXED, RESOLVED_FP, RESOLVED_WF);
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.qualitygate.changeevent;
-
-import java.util.Collection;
-import java.util.List;
-import org.sonar.core.issue.DefaultIssue;
-
-public interface QGChangeEventListeners {
-
- void broadcastOnIssueChange(List<DefaultIssue> changedIssues, Collection<QGChangeEvent> qgChangeEvents);
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.qualitygate.changeevent;
-
-import com.google.common.collect.Multimap;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-import org.sonar.api.issue.Issue;
-import org.sonar.api.rules.RuleType;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.core.issue.DefaultIssue;
-import org.sonar.core.util.stream.MoreCollectors;
-import org.sonar.server.qualitygate.changeevent.QGChangeEventListener.ChangedIssue;
-
-import static java.lang.String.format;
-import static org.sonar.core.util.stream.MoreCollectors.toSet;
-
-/**
- * Broadcast a given collection of {@link QGChangeEvent} for a specific trigger to all the registered
- * {@link QGChangeEventListener} in Pico.
- *
- * This class ensures that an {@link Exception} occurring calling one of the {@link QGChangeEventListener} doesn't
- * prevent from calling the others.
- */
-public class QGChangeEventListenersImpl implements QGChangeEventListeners {
- private static final Logger LOG = Loggers.get(QGChangeEventListenersImpl.class);
-
- private final QGChangeEventListener[] listeners;
-
- /**
- * Used by Pico when there is no QGChangeEventListener instance in container.
- */
- public QGChangeEventListenersImpl() {
- this.listeners = new QGChangeEventListener[0];
- }
-
- public QGChangeEventListenersImpl(QGChangeEventListener[] listeners) {
- this.listeners = listeners;
- }
-
- @Override
- public void broadcastOnIssueChange(List<DefaultIssue> issues, Collection<QGChangeEvent> changeEvents) {
- if (listeners.length == 0 || issues.isEmpty() || changeEvents.isEmpty()) {
- return;
- }
-
- try {
- Multimap<String, QGChangeEvent> eventsByComponentUuid = changeEvents.stream()
- .collect(MoreCollectors.index(t -> t.getProject().uuid()));
- Multimap<String, DefaultIssue> issueByComponentUuid = issues.stream()
- .collect(MoreCollectors.index(DefaultIssue::projectUuid));
-
- issueByComponentUuid.asMap()
- .forEach((componentUuid, value) -> {
- Collection<QGChangeEvent> qgChangeEvents = eventsByComponentUuid.get(componentUuid);
- if (!qgChangeEvents.isEmpty()) {
- Set<ChangedIssue> changedIssues = value.stream()
- .map(ChangedIssueImpl::new)
- .collect(toSet());
- qgChangeEvents
- .forEach(changeEvent -> Arrays.stream(listeners)
- .forEach(listener -> broadcastTo(changedIssues, changeEvent, listener)));
- }
- });
- } catch (Error e) {
- LOG.warn(format("Broadcasting to listeners failed for %s events", changeEvents.size()), e);
- }
- }
-
- private static void broadcastTo(Set<ChangedIssue> changedIssues, QGChangeEvent changeEvent, QGChangeEventListener listener) {
- try {
- LOG.trace("calling onChange() on listener {} for events {}...", listener.getClass().getName(), changeEvent);
- listener.onIssueChanges(changeEvent, changedIssues);
- } catch (Exception e) {
- LOG.warn(format("onChange() call failed on listener %s for events %s", listener.getClass().getName(), changeEvent), e);
- }
- }
-
- static class ChangedIssueImpl implements ChangedIssue {
- private final String key;
- private final QGChangeEventListener.Status status;
- private final RuleType type;
- private final String severity;
-
- ChangedIssueImpl(DefaultIssue issue) {
- this.key = issue.key();
- this.status = statusOf(issue);
- this.type = issue.type();
- this.severity = issue.severity();
- }
-
- static QGChangeEventListener.Status statusOf(DefaultIssue issue) {
- switch (issue.status()) {
- case Issue.STATUS_OPEN:
- return QGChangeEventListener.Status.OPEN;
- case Issue.STATUS_CONFIRMED:
- return QGChangeEventListener.Status.CONFIRMED;
- case Issue.STATUS_REOPENED:
- return QGChangeEventListener.Status.REOPENED;
- case Issue.STATUS_TO_REVIEW:
- return QGChangeEventListener.Status.TO_REVIEW;
- case Issue.STATUS_IN_REVIEW:
- return QGChangeEventListener.Status.IN_REVIEW;
- case Issue.STATUS_REVIEWED:
- return QGChangeEventListener.Status.REVIEWED;
- case Issue.STATUS_RESOLVED:
- return statusOfResolved(issue);
- default:
- throw new IllegalStateException("Unexpected status: " + issue.status());
- }
- }
-
- private static QGChangeEventListener.Status statusOfResolved(DefaultIssue issue) {
- String resolution = issue.resolution();
- Objects.requireNonNull(resolution, "A resolved issue should have a resolution");
- switch (resolution) {
- case Issue.RESOLUTION_FALSE_POSITIVE:
- return QGChangeEventListener.Status.RESOLVED_FP;
- case Issue.RESOLUTION_WONT_FIX:
- return QGChangeEventListener.Status.RESOLVED_WF;
- case Issue.RESOLUTION_FIXED:
- return QGChangeEventListener.Status.RESOLVED_FIXED;
- default:
- throw new IllegalStateException("Unexpected resolution for a resolved issue: " + resolution);
- }
- }
-
- @Override
- public String getKey() {
- return key;
- }
-
- @Override
- public QGChangeEventListener.Status getStatus() {
- return status;
- }
-
- @Override
- public RuleType getType() {
- return type;
- }
-
- @Override
- public String getSeverity() {
- return severity;
- }
-
- @Override
- public String toString() {
- return "ChangedIssueImpl{" +
- "key='" + key + '\'' +
- ", status=" + status +
- ", type=" + type +
- ", severity=" + severity +
- '}';
- }
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.qualitygate.changeevent;
-
-public enum Trigger {
- ISSUE_CHANGE
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.qualitygate.changeevent;
-
-import javax.annotation.ParametersAreNonnullByDefault;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.qualityprofile;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-public class BulkChangeResult {
-
- private final List<String> errors = new ArrayList<>();
- private int succeeded = 0;
- private int failed = 0;
- private final List<ActiveRuleChange> changes = new ArrayList<>();
-
- public List<String> getErrors() {
- return errors;
- }
-
- public int countSucceeded() {
- return succeeded;
- }
-
- public int countFailed() {
- return failed;
- }
-
- void incrementSucceeded() {
- succeeded++;
- }
-
- void incrementFailed() {
- failed++;
- }
-
- void addChanges(Collection<ActiveRuleChange> c) {
- this.changes.addAll(c);
- }
-
- public List<ActiveRuleChange> getChanges() {
- return changes;
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.qualityprofile;
-
-import java.util.Collection;
-import java.util.List;
-import javax.annotation.Nullable;
-import org.sonar.api.server.ServerSide;
-import org.sonar.db.DbSession;
-import org.sonar.db.qualityprofile.QProfileDto;
-import org.sonar.db.rule.RuleDefinitionDto;
-import org.sonar.server.rule.index.RuleQuery;
-
-/**
- * Operations related to activation and deactivation of rules on user profiles.
- */
-@ServerSide
-public interface QProfileRules {
-
- /**
- * Activate multiple rules at once on a Quality profile.
- * Db session is committed and Elasticsearch indices are updated.
- * If an activation fails to be executed, then all others are
- * canceled, db session is not committed and an exception is
- * thrown.
- */
- List<ActiveRuleChange> activateAndCommit(DbSession dbSession, QProfileDto profile, Collection<RuleActivation> activations);
-
- /**
- * Same as {@link #activateAndCommit(DbSession, QProfileDto, Collection)} except
- * that:
- * - rules are loaded from search engine
- * - rules are activated with default parameters
- * - an activation failure does not break others. No exception is thrown.
- */
- BulkChangeResult bulkActivateAndCommit(DbSession dbSession, QProfileDto profile, RuleQuery ruleQuery, @Nullable String severity);
-
- List<ActiveRuleChange> deactivateAndCommit(DbSession dbSession, QProfileDto profile, Collection<Integer> ruleIds);
-
- BulkChangeResult bulkDeactivateAndCommit(DbSession dbSession, QProfileDto profile, RuleQuery ruleQuery);
-
- /**
- * Delete a rule from all Quality profiles. Db session is not committed. As a
- * consequence Elasticsearch indices are NOT updated.
- */
- List<ActiveRuleChange> deleteRule(DbSession dbSession, RuleDefinitionDto rule);
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.qualityprofile;
-
-import com.google.common.base.Strings;
-import java.util.HashMap;
-import java.util.Map;
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-import javax.annotation.concurrent.Immutable;
-import org.sonar.api.rule.Severity;
-
-/**
- * The request for activation.
- */
-@Immutable
-public class RuleActivation {
-
- private final int ruleId;
- private final boolean reset;
- private final String severity;
- private final Map<String, String> parameters = new HashMap<>();
-
- private RuleActivation(int ruleId, boolean reset, @Nullable String severity, @Nullable Map<String, String> parameters) {
- this.ruleId = ruleId;
- this.reset = reset;
- this.severity = severity;
- if (severity != null && !Severity.ALL.contains(severity)) {
- throw new IllegalArgumentException("Unknown severity: " + severity);
- }
- if (parameters != null) {
- for (Map.Entry<String, String> entry : parameters.entrySet()) {
- this.parameters.put(entry.getKey(), Strings.emptyToNull(entry.getValue()));
- }
- }
- }
-
- public static RuleActivation createReset(int ruleId) {
- return new RuleActivation(ruleId, true, null, null);
- }
-
- public static RuleActivation create(int ruleId, @Nullable String severity, @Nullable Map<String, String> parameters) {
- return new RuleActivation(ruleId, false, severity, parameters);
- }
-
- public static RuleActivation create(int ruleId) {
- return create(ruleId, null, null);
- }
-
- /**
- * Optional severity. Use the parent severity or default rule severity if null.
- */
- @CheckForNull
- public String getSeverity() {
- return severity;
- }
-
- public int getRuleId() {
- return ruleId;
- }
-
- @CheckForNull
- public String getParameter(String key) {
- return parameters.get(key);
- }
-
- public boolean hasParameter(String key) {
- return parameters.containsKey(key);
- }
-
- public boolean isReset() {
- return reset;
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.qualityprofile;
-
-import javax.annotation.ParametersAreNonnullByDefault;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.setting;
-
-import java.util.Collections;
-import java.util.Map;
-import java.util.Set;
-import org.sonar.api.config.Configuration;
-import org.sonar.db.DbSession;
-import org.sonar.db.component.ComponentDto;
-
-import static java.lang.String.format;
-import static java.util.Objects.requireNonNull;
-
-public interface ProjectConfigurationLoader {
- /**
- * Loads configuration for the specified components.
- *
- * <p>
- * Returns the applicable component configuration with most specific configuration overriding more global ones
- * (eg. global > project > branch).
- *
- * <p>
- * Any component is accepted but SQ only supports specific properties for projects and branches.
- */
- Map<String, Configuration> loadProjectConfigurations(DbSession dbSession, Set<ComponentDto> projects);
-
- default Configuration loadProjectConfiguration(DbSession dbSession, ComponentDto project) {
- Map<String, Configuration> configurations = loadProjectConfigurations(dbSession, Collections.singleton(project));
- return requireNonNull(configurations.get(project.uuid()), () -> format("Configuration for project '%s' is not found", project.getKey()));
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.setting;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-import org.sonar.api.config.Configuration;
-import org.sonar.api.config.Settings;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.component.ComponentDto;
-import org.sonar.db.property.PropertyDto;
-
-import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
-
-public class ProjectConfigurationLoaderImpl implements ProjectConfigurationLoader {
- private final Settings globalSettings;
- private final DbClient dbClient;
-
- public ProjectConfigurationLoaderImpl(Settings globalSettings, DbClient dbClient) {
- this.globalSettings = globalSettings;
- this.dbClient = dbClient;
- }
-
- @Override
- public Map<String, Configuration> loadProjectConfigurations(DbSession dbSession, Set<ComponentDto> projects) {
- Set<String> mainBranchDbKeys = projects.stream().map(ComponentDto::getKey).collect(Collectors.toSet());
- Map<String, ChildSettings> mainBranchSettingsByDbKey = loadMainBranchConfigurations(dbSession, mainBranchDbKeys);
- return projects.stream()
- .collect(uniqueIndex(ComponentDto::uuid, component -> {
- if (component.getDbKey().equals(component.getKey())) {
- return mainBranchSettingsByDbKey.get(component.getKey()).asConfiguration();
- }
-
- ChildSettings settings = new ChildSettings(mainBranchSettingsByDbKey.get(component.getKey()));
- dbClient.propertiesDao()
- .selectProjectProperties(dbSession, component.getDbKey())
- .forEach(property -> settings.setProperty(property.getKey(), property.getValue()));
- return settings.asConfiguration();
- }));
- }
-
- private Map<String, ChildSettings> loadMainBranchConfigurations(DbSession dbSession, Set<String> dbKeys) {
- return dbKeys.stream().collect(uniqueIndex(Function.identity(), dbKey -> {
- ChildSettings settings = new ChildSettings(globalSettings);
- List<PropertyDto> propertyDtos = dbClient.propertiesDao()
- .selectProjectProperties(dbSession, dbKey);
- propertyDtos
- .forEach(property -> settings.setProperty(property.getKey(), property.getValue()));
- return settings;
- }));
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.setting;
-
-import com.google.common.annotations.VisibleForTesting;
-import org.sonar.api.config.GlobalPropertyChangeHandler;
-
-import javax.annotation.Nullable;
-
-public class SettingsChangeNotifier {
-
- @VisibleForTesting
- GlobalPropertyChangeHandler[] changeHandlers;
-
- public SettingsChangeNotifier(GlobalPropertyChangeHandler[] changeHandlers) {
- this.changeHandlers = changeHandlers;
- }
-
- public SettingsChangeNotifier() {
- this(new GlobalPropertyChangeHandler[0]);
- }
-
- public void onGlobalPropertyChange(String key, @Nullable String value) {
- GlobalPropertyChangeHandler.PropertyChange change = GlobalPropertyChangeHandler.PropertyChange.create(key, value);
- for (GlobalPropertyChangeHandler changeHandler : changeHandlers) {
- changeHandler.onChange(change);
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.setting;
-
-import javax.annotation.ParametersAreNonnullByDefault;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.telemetry;
-
-import java.util.Optional;
-import org.sonar.api.server.ServerSide;
-
-@ServerSide
-public interface LicenseReader {
- Optional<License> read();
-
- interface License {
- String getType();
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.telemetry;
-
-import javax.annotation.ParametersAreNonnullByDefault;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.util;
-
-import java.util.List;
-import javax.annotation.Nullable;
-import org.apache.commons.lang.StringUtils;
-import org.sonar.api.PropertyType;
-
-import static org.sonar.server.exceptions.BadRequestException.checkRequest;
-
-public class BooleanTypeValidation implements TypeValidation {
-
- @Override
- public String key() {
- return PropertyType.BOOLEAN.name();
- }
-
- @Override
- public void validate(String value, @Nullable List<String> options) {
- checkRequest(StringUtils.equalsIgnoreCase(value, "true") || StringUtils.equalsIgnoreCase(value, "false"),
- "Value '%s' must be one of \"true\" or \"false\".", value);
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.util;
-
-import java.util.List;
-import javax.annotation.Nullable;
-import org.sonar.api.PropertyType;
-import org.sonar.server.exceptions.BadRequestException;
-
-import static java.lang.String.format;
-
-public class FloatTypeValidation implements TypeValidation {
-
- @Override
- public String key() {
- return PropertyType.FLOAT.name();
- }
-
- @Override
- public void validate(String value, @Nullable List<String> options) {
- try {
- Double.parseDouble(value);
- } catch (NumberFormatException e) {
- throw BadRequestException.create(format("Value '%s' must be an floating point number.", value));
- }
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.util;
-
-import org.sonar.api.ce.ComputeEngineSide;
-import org.sonar.api.server.ServerSide;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-
-/**
- * Provide a simple mechanism to manage global locks across multiple nodes running in a cluster.
- * In the target use case multiple nodes try to execute something at around the same time,
- * and only the first should succeed, and the rest do nothing.
- */
-@ComputeEngineSide
-@ServerSide
-public class GlobalLockManager {
-
- static final int DEFAULT_LOCK_DURATION_SECONDS = 180;
-
- private final DbClient dbClient;
-
- public GlobalLockManager(DbClient dbClient) {
- this.dbClient = dbClient;
- }
-
- /**
- * Try to acquire a lock on the given name in the default namespace,
- * using the generic locking mechanism of {@see org.sonar.db.property.InternalPropertiesDao}.
- */
- public boolean tryLock(String name) {
- return tryLock(name, DEFAULT_LOCK_DURATION_SECONDS);
- }
-
- public boolean tryLock(String name, int durationSecond) {
- try (DbSession dbSession = dbClient.openSession(false)) {
- boolean success = dbClient.internalPropertiesDao().tryLock(dbSession, name, durationSecond);
- dbSession.commit();
- return success;
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.util;
-
-import java.util.List;
-import javax.annotation.Nullable;
-import org.sonar.api.PropertyType;
-import org.sonar.server.exceptions.BadRequestException;
-
-import static java.lang.String.format;
-
-public class IntegerTypeValidation implements TypeValidation {
-
- @Override
- public String key() {
- return PropertyType.INTEGER.name();
- }
-
- @Override
- public void validate(String value, @Nullable List<String> options) {
- try {
- Integer.parseInt(value);
- } catch (NumberFormatException e) {
- throw BadRequestException.create(format("Value '%s' must be an integer.", value));
- }
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.util;
-
-import java.util.List;
-import javax.annotation.Nullable;
-import org.sonar.api.PropertyType;
-import org.sonar.server.exceptions.BadRequestException;
-
-import static java.lang.String.format;
-
-public class LongTypeValidation implements TypeValidation {
- @Override
- public String key() {
- return PropertyType.LONG.name();
- }
-
- @Override
- public void validate(String value, @Nullable List<String> options) {
- try {
- Long.parseLong(value);
- } catch (NumberFormatException e) {
- throw BadRequestException.create(format("Value '%s' must be a long.", value));
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.util;
-
-import java.util.List;
-import javax.annotation.Nullable;
-import org.sonar.api.PropertyType;
-import org.sonar.api.measures.Metric;
-import org.sonar.server.exceptions.BadRequestException;
-
-import static java.lang.String.format;
-
-public class MetricLevelTypeValidation implements TypeValidation {
- @Override
- public String key() {
- return PropertyType.METRIC_LEVEL.name();
- }
-
- @Override
- public void validate(String value, @Nullable List<String> options) {
- try {
- Metric.Level.valueOf(value);
- } catch (IllegalArgumentException e) {
- throw BadRequestException.create(format("Value '%s' must be one of \"OK\", \"ERROR\".", value));
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.util;
-
-import java.util.List;
-import javax.annotation.Nullable;
-import org.apache.commons.lang.StringUtils;
-import org.sonar.api.PropertyType;
-
-import static org.sonar.server.exceptions.BadRequestException.checkRequest;
-
-public class StringListTypeValidation implements TypeValidation {
-
- @Override
- public String key() {
- return PropertyType.SINGLE_SELECT_LIST.name();
- }
-
- @Override
- public void validate(String value, @Nullable List<String> options) {
- checkRequest(options == null || options.contains(value), "Value '%s' must be one of : %s.", value, StringUtils.join(options, ", "));
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.util;
-
-import org.sonar.api.PropertyType;
-
-import javax.annotation.Nullable;
-
-import java.util.List;
-
-public class StringTypeValidation implements TypeValidation {
-
- @Override
- public String key() {
- return PropertyType.STRING.name();
- }
-
- @Override
- public void validate(String value, @Nullable List<String> options) {
- // Nothing to do
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.util;
-
-import org.sonar.api.PropertyType;
-
-import javax.annotation.Nullable;
-
-import java.util.List;
-
-public class TextTypeValidation implements TypeValidation {
-
- @Override
- public String key() {
- return PropertyType.TEXT.name();
- }
-
- @Override
- public void validate(String value, @Nullable List<String> options) {
- // Nothing to do
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.util;
-
-import org.sonar.api.server.ServerSide;
-
-import javax.annotation.Nullable;
-
-import java.util.List;
-
-@ServerSide
-public interface TypeValidation {
-
- String key();
-
- void validate(String value, @Nullable List<String> options);
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.util;
-
-import org.sonar.core.platform.Module;
-
-public class TypeValidationModule extends Module {
- @Override
- protected void configureModule() {
- add(
- TypeValidations.class,
- IntegerTypeValidation.class,
- FloatTypeValidation.class,
- BooleanTypeValidation.class,
- TextTypeValidation.class,
- StringTypeValidation.class,
- StringListTypeValidation.class,
- LongTypeValidation.class,
- MetricLevelTypeValidation.class
- );
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.util;
-
-import com.google.common.base.Predicate;
-import com.google.common.collect.Iterables;
-import java.util.List;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import org.sonar.api.server.ServerSide;
-
-import static org.sonar.server.exceptions.BadRequestException.checkRequest;
-
-@ServerSide
-public class TypeValidations {
-
- private final List<TypeValidation> typeValidationList;
-
- public TypeValidations(List<TypeValidation> typeValidationList) {
- this.typeValidationList = typeValidationList;
- }
-
- public void validate(List<String> values, String type, List<String> options) {
- TypeValidation typeValidation = findByKey(type);
- for (String value : values) {
- typeValidation.validate(value, options);
- }
- }
-
- public void validate(String value, String type, @Nullable List<String> options) {
- TypeValidation typeValidation = findByKey(type);
- typeValidation.validate(value, options);
- }
-
- private TypeValidation findByKey(String key) {
- TypeValidation typeValidation = Iterables.find(typeValidationList, new TypeValidationMatchKey(key), null);
- checkRequest(typeValidation != null, "Type '%s' is not valid.", key);
- return typeValidation;
- }
-
- private static class TypeValidationMatchKey implements Predicate<TypeValidation> {
- private final String key;
-
- public TypeValidationMatchKey(String key) {
- this.key = key;
- }
-
- @Override
- public boolean apply(@Nonnull TypeValidation input) {
- return input.key().equals(key);
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.util;
-
-public class Validation {
-
- public static final String CANT_BE_EMPTY_MESSAGE = "%s can't be empty";
- public static final String IS_TOO_SHORT_MESSAGE = "%s is too short (minimum is %s characters)";
- public static final String IS_TOO_LONG_MESSAGE = "%s is too long (maximum is %s characters)";
- public static final String IS_ALREADY_USED_MESSAGE = "%s has already been taken";
-
- private Validation() {
- // only static methods
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.util;
-
-import javax.annotation.ParametersAreNonnullByDefault;
-
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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 java.io.File;
-import java.io.IOException;
-import java.util.Random;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.api.config.internal.MapSettings;
-import org.sonar.process.sharedmemoryfile.DefaultProcessCommands;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonar.process.ProcessEntryPoint.PROPERTY_PROCESS_INDEX;
-import static org.sonar.process.ProcessEntryPoint.PROPERTY_SHARED_PATH;
-
-public class ProcessCommandWrapperImplTest {
- private static final int PROCESS_NUMBER = 2;
-
- @Rule
- public TemporaryFolder temp = new TemporaryFolder();
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private MapSettings settings = new MapSettings();
-
- @Test
- public void requestSQRestart_throws_IAE_if_process_index_property_not_set() {
- ProcessCommandWrapperImpl processCommandWrapper = new ProcessCommandWrapperImpl(settings.asConfig());
-
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("Property process.index is not set");
-
- processCommandWrapper.requestSQRestart();
- }
-
- @Test
- public void requestSQRestart_throws_IAE_if_process_shared_path_property_not_set() {
- settings.setProperty(PROPERTY_PROCESS_INDEX, 1);
- ProcessCommandWrapperImpl processCommandWrapper = new ProcessCommandWrapperImpl(settings.asConfig());
-
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("Property process.sharedDir is not set");
-
- processCommandWrapper.requestSQRestart();
- }
-
- @Test
- public void requestSQRestart_updates_shareMemory_file() throws IOException {
- File tmpDir = temp.newFolder().getAbsoluteFile();
- settings.setProperty(PROPERTY_SHARED_PATH, tmpDir.getAbsolutePath());
- settings.setProperty(PROPERTY_PROCESS_INDEX, PROCESS_NUMBER);
-
- ProcessCommandWrapperImpl underTest = new ProcessCommandWrapperImpl(settings.asConfig());
- underTest.requestSQRestart();
-
- try (DefaultProcessCommands processCommands = DefaultProcessCommands.secondary(tmpDir, PROCESS_NUMBER)) {
- assertThat(processCommands.askedForRestart()).isTrue();
- }
- }
-
- @Test
- public void requestSQStop_throws_IAE_if_process_shared_path_property_not_set() {
- settings.setProperty(PROPERTY_PROCESS_INDEX, 1);
- ProcessCommandWrapperImpl processCommandWrapper = new ProcessCommandWrapperImpl(settings.asConfig());
-
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("Property process.sharedDir is not set");
-
- processCommandWrapper.requestHardStop();
- }
-
- @Test
- public void requestSQStop_updates_shareMemory_file() throws IOException {
- File tmpDir = temp.newFolder().getAbsoluteFile();
- settings.setProperty(PROPERTY_SHARED_PATH, tmpDir.getAbsolutePath());
- settings.setProperty(PROPERTY_PROCESS_INDEX, PROCESS_NUMBER);
-
- ProcessCommandWrapperImpl underTest = new ProcessCommandWrapperImpl(settings.asConfig());
- underTest.requestHardStop();
-
- try (DefaultProcessCommands processCommands = DefaultProcessCommands.secondary(tmpDir, PROCESS_NUMBER)) {
- assertThat(processCommands.askedForHardStop()).isTrue();
- }
- }
-
- @Test
- public void notifyOperational_throws_IAE_if_process_sharedDir_property_not_set() {
- settings.setProperty(PROPERTY_PROCESS_INDEX, 1);
- ProcessCommandWrapperImpl processCommandWrapper = new ProcessCommandWrapperImpl(settings.asConfig());
-
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("Property process.sharedDir is not set");
-
- processCommandWrapper.notifyOperational();
- }
-
- @Test
- public void notifyOperational_throws_IAE_if_process_index_property_not_set() {
- ProcessCommandWrapperImpl processCommandWrapper = new ProcessCommandWrapperImpl(settings.asConfig());
-
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("Property process.index is not set");
-
- processCommandWrapper.notifyOperational();
- }
-
- @Test
- public void notifyOperational_updates_shareMemory_file() throws IOException {
- File tmpDir = temp.newFolder().getAbsoluteFile();
- settings.setProperty(PROPERTY_SHARED_PATH, tmpDir.getAbsolutePath());
- settings.setProperty(PROPERTY_PROCESS_INDEX, PROCESS_NUMBER);
-
- ProcessCommandWrapperImpl underTest = new ProcessCommandWrapperImpl(settings.asConfig());
- underTest.notifyOperational();
-
- try (DefaultProcessCommands processCommands = DefaultProcessCommands.secondary(tmpDir, PROCESS_NUMBER)) {
- assertThat(processCommands.isOperational()).isTrue();
- }
- }
-
- @Test
- public void isCeOperational_reads_shared_memory_operational_flag_in_location_3() throws IOException {
- File tmpDir = temp.newFolder().getAbsoluteFile();
- settings.setProperty(PROPERTY_SHARED_PATH, tmpDir.getAbsolutePath());
-
- boolean expected = new Random().nextBoolean();
- if (expected) {
- try (DefaultProcessCommands processCommands = DefaultProcessCommands.secondary(tmpDir, 3)) {
- processCommands.setOperational();
- }
- }
-
- ProcessCommandWrapperImpl underTest = new ProcessCommandWrapperImpl(settings.asConfig());
-
- assertThat(underTest.isCeOperational()).isEqualTo(expected);
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.branch;
-
-import org.junit.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class BranchFeatureProxyImplTest {
-
- private BranchFeatureExtension branchFeatureExtension = mock(BranchFeatureExtension.class);
-
- @Test
- public void return_false_when_no_extension() {
- assertThat(new BranchFeatureProxyImpl().isEnabled()).isFalse();
- }
-
- @Test
- public void return_false_when_extension_returns_false() {
- when(branchFeatureExtension.isEnabled()).thenReturn(false);
- assertThat(new BranchFeatureProxyImpl(branchFeatureExtension).isEnabled()).isFalse();
- }
-
- @Test
- public void return_true_when_extension_returns_ftrue() {
- when(branchFeatureExtension.isEnabled()).thenReturn(true);
- assertThat(new BranchFeatureProxyImpl(branchFeatureExtension).isEnabled()).isTrue();
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.branch;
-
-import org.junit.rules.ExternalResource;
-
-public class BranchFeatureRule extends ExternalResource implements BranchFeatureProxy {
-
- private boolean enabled;
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
- @Override
- protected void after() {
- reset();
- }
-
- public void reset() {
- this.enabled = false;
- }
-
- @Override
- public boolean isEnabled() {
- return enabled;
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.exceptions;
-
-import java.util.Collections;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-import static java.util.Arrays.asList;
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class BadRequestExceptionTest {
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- @Test
- public void text_error() {
- BadRequestException exception = BadRequestException.create("error");
- assertThat(exception.getMessage()).isEqualTo("error");
- }
-
- @Test
- public void create_exception_from_list() {
- BadRequestException underTest = BadRequestException.create(asList("error1", "error2"));
-
- assertThat(underTest.errors()).containsOnly("error1", "error2");
- }
-
- @Test
- public void create_exception_from_var_args() {
- BadRequestException underTest = BadRequestException.create("error1", "error2");
-
- assertThat(underTest.errors()).containsOnly("error1", "error2");
- }
-
- @Test
- public void getMessage_return_first_error() {
- BadRequestException underTest = BadRequestException.create(asList("error1", "error2"));
-
- assertThat(underTest.getMessage()).isEqualTo("error1");
- }
-
- @Test
- public void fail_when_creating_exception_with_empty_list() {
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("At least one error message is required");
-
- BadRequestException.create(Collections.emptyList());
- }
-
- @Test
- public void fail_when_creating_exception_with_one_empty_element() {
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("Message cannot be empty");
-
- BadRequestException.create(asList("error", ""));
- }
-
- @Test
- public void fail_when_creating_exception_with_one_null_element() {
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("Message cannot be empty");
-
- BadRequestException.create(asList("error", null));
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.exceptions;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class MessageTest {
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- @Test
- public void create_message() {
- Message message = Message.of("key1 %s", "param1");
- assertThat(message.getMessage()).isEqualTo("key1 param1");
- }
-
- @Test
- public void create_message_without_params() {
- Message message = Message.of("key1");
- assertThat(message.getMessage()).isEqualTo("key1");
- }
-
- @Test
- public void fail_when_message_is_null() {
- expectedException.expect(IllegalArgumentException.class);
-
- Message.of(null);
- }
-
- @Test
- public void fail_when_message_is_empty() {
- expectedException.expect(IllegalArgumentException.class);
-
- Message.of("");
- }
-
- @Test
- public void test_equals_and_hashcode() {
- Message message1 = Message.of("key1%s", "param1");
- Message message2 = Message.of("key2%s", "param2");
- Message message3 = Message.of("key1");
- Message message4 = Message.of("key1%s", "param2");
- Message sameAsMessage1 = Message.of("key1%s", "param1");
-
- assertThat(message1).isEqualTo(message1);
- assertThat(message1).isNotEqualTo(message2);
- assertThat(message1).isNotEqualTo(message3);
- assertThat(message1).isNotEqualTo(message4);
- assertThat(message1).isEqualTo(sameAsMessage1);
- assertThat(message1).isNotEqualTo(null);
- assertThat(message1).isNotEqualTo(new Object());
-
- assertThat(message1.hashCode()).isEqualTo(message1.hashCode());
- assertThat(message1.hashCode()).isNotEqualTo(message2.hashCode());
- assertThat(message1.hashCode()).isNotEqualTo(message3.hashCode());
- assertThat(message1.hashCode()).isNotEqualTo(message4.hashCode());
- assertThat(message1.hashCode()).isEqualTo(sameAsMessage1.hashCode());
- }
-
- @Test
- public void to_string() {
- assertThat(Message.of("key1 %s", "param1").toString()).isEqualTo("key1 param1");
- assertThat(Message.of("key1").toString()).isEqualTo("key1");
- assertThat(Message.of("key1", (Object[])null).toString()).isEqualTo("key1");
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.exceptions;
-
-import org.junit.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class ServerExceptionTest {
-
- @Test
- public void should_create_exception_with_status() {
- ServerException exception = new ServerException(400, "error!");
- assertThat(exception.httpCode()).isEqualTo(400);
- }
-
- @Test
- public void should_create_exception_with_status_and_message() {
- ServerException exception = new ServerException(404, "Not found");
- assertThat(exception.httpCode()).isEqualTo(404);
- assertThat(exception.getMessage()).isEqualTo("Not found");
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.health;
-
-public class TestStandaloneHealthChecker implements HealthChecker {
-
- private Health health = Health.newHealthCheckBuilder().setStatus(Health.Status.GREEN).build();
-
- public void setHealth(Health h) {
- this.health = h;
- }
-
- @Override
- public Health checkNode() {
- return health;
- }
-
- @Override
- public ClusterHealth checkCluster() {
- throw new IllegalStateException();
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.plugins;
-
-import com.google.common.base.Optional;
-import java.io.File;
-import java.net.URI;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.rules.TemporaryFolder;
-import org.mockito.ArgumentMatcher;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-import org.sonar.api.utils.HttpDownloader;
-import org.sonar.core.platform.PluginInfo;
-import org.sonar.server.exceptions.BadRequestException;
-import org.sonar.server.platform.ServerFileSystem;
-import org.sonar.updatecenter.common.Plugin;
-import org.sonar.updatecenter.common.Release;
-import org.sonar.updatecenter.common.UpdateCenter;
-import org.sonar.updatecenter.common.Version;
-
-import static com.google.common.collect.Lists.newArrayList;
-import static org.apache.commons.io.FileUtils.copyFileToDirectory;
-import static org.apache.commons.io.FileUtils.touch;
-import static org.apache.commons.io.FilenameUtils.separatorsToUnix;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.sonar.updatecenter.common.Version.create;
-
-public class PluginDownloaderTest {
-
- @Rule
- public TemporaryFolder testFolder = new TemporaryFolder();
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
- private File downloadDir;
- private UpdateCenterMatrixFactory updateCenterMatrixFactory;
- private UpdateCenter updateCenter;
- private HttpDownloader httpDownloader;
- private PluginDownloader pluginDownloader;
-
- @Before
- public void before() throws Exception {
- updateCenterMatrixFactory = mock(UpdateCenterMatrixFactory.class);
- updateCenter = mock(UpdateCenter.class);
- when(updateCenterMatrixFactory.getUpdateCenter(anyBoolean())).thenReturn(Optional.of(updateCenter));
-
- httpDownloader = mock(HttpDownloader.class);
- doAnswer(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock inv) throws Throwable {
- File toFile = (File) inv.getArguments()[1];
- touch(toFile);
- return null;
- }
- }).when(httpDownloader).download(any(URI.class), any(File.class));
-
- ServerFileSystem fs = mock(ServerFileSystem.class);
- downloadDir = testFolder.newFolder("downloads");
- when(fs.getDownloadedPluginsDir()).thenReturn(downloadDir);
-
- pluginDownloader = new PluginDownloader(updateCenterMatrixFactory, httpDownloader, fs);
- }
-
- @After
- public void stop() {
- pluginDownloader.stop();
- }
-
- @Test
- public void clean_temporary_files_at_startup() throws Exception {
- touch(new File(downloadDir, "sonar-php.jar"));
- touch(new File(downloadDir, "sonar-js.jar.tmp"));
- assertThat(downloadDir.listFiles()).hasSize(2);
- pluginDownloader.start();
-
- File[] files = downloadDir.listFiles();
- assertThat(files).hasSize(1);
- assertThat(files[0].getName()).isEqualTo("sonar-php.jar");
- }
-
- @Test
- public void download_from_url() {
- Plugin test = Plugin.factory("test");
- Release test10 = new Release(test, "1.0").setDownloadUrl("http://server/test-1.0.jar");
- test.addRelease(test10);
-
- when(updateCenter.findInstallablePlugins("foo", create("1.0"))).thenReturn(newArrayList(test10));
-
- pluginDownloader.start();
- pluginDownloader.download("foo", create("1.0"));
-
- // SONAR-4523: do not corrupt JAR files when restarting the server while a plugin is being downloaded.
- // The JAR file is downloaded in a temp file
- verify(httpDownloader).download(any(URI.class), argThat(new HasFileName("test-1.0.jar.tmp")));
- assertThat(new File(downloadDir, "test-1.0.jar")).exists();
- assertThat(new File(downloadDir, "test-1.0.jar.tmp")).doesNotExist();
- }
-
- @Test
- public void download_when_update_center_is_unavailable_with_no_exception_thrown() {
- when(updateCenterMatrixFactory.getUpdateCenter(anyBoolean())).thenReturn(Optional.absent());
-
- Plugin test = Plugin.factory("test");
- Release test10 = new Release(test, "1.0").setDownloadUrl("http://server/test-1.0.jar");
- test.addRelease(test10);
-
- pluginDownloader.start();
- pluginDownloader.download("foo", create("1.0"));
- }
-
- /**
- * SONAR-4685
- */
- @Test
- public void download_from_redirect_url() {
- Plugin test = Plugin.factory("plugintest");
- Release test10 = new Release(test, "1.0").setDownloadUrl("http://server/redirect?r=release&g=test&a=test&v=1.0&e=jar");
- test.addRelease(test10);
-
- when(updateCenter.findInstallablePlugins("foo", create("1.0"))).thenReturn(newArrayList(test10));
-
- pluginDownloader.start();
- pluginDownloader.download("foo", create("1.0"));
-
- // SONAR-4523: do not corrupt JAR files when restarting the server while a plugin is being downloaded.
- // The JAR file is downloaded in a temp file
- verify(httpDownloader).download(any(URI.class), argThat(new HasFileName("plugintest-1.0.jar.tmp")));
- assertThat(new File(downloadDir, "plugintest-1.0.jar")).exists();
- assertThat(new File(downloadDir, "plugintest-1.0.jar.tmp")).doesNotExist();
- }
-
- @Test
- public void throw_exception_if_download_dir_is_invalid() throws Exception {
- ServerFileSystem fs = mock(ServerFileSystem.class);
- // download dir is a file instead of being a directory
- File downloadDir = testFolder.newFile();
- when(fs.getDownloadedPluginsDir()).thenReturn(downloadDir);
-
- pluginDownloader = new PluginDownloader(updateCenterMatrixFactory, httpDownloader, fs);
- try {
- pluginDownloader.start();
- fail();
- } catch (IllegalStateException e) {
- // ok
- }
- }
-
- @Test
- public void fail_if_no_compatible_plugin_found() {
- expectedException.expect(BadRequestException.class);
-
- pluginDownloader.download("foo", create("1.0"));
- }
-
- @Test
- public void download_from_file() throws Exception {
- Plugin test = Plugin.factory("test");
- File file = testFolder.newFile("test-1.0.jar");
- file.createNewFile();
- Release test10 = new Release(test, "1.0").setDownloadUrl("file://" + separatorsToUnix(file.getCanonicalPath()));
- test.addRelease(test10);
-
- when(updateCenter.findInstallablePlugins("foo", create("1.0"))).thenReturn(newArrayList(test10));
-
- pluginDownloader.start();
- pluginDownloader.download("foo", create("1.0"));
- verify(httpDownloader, never()).download(any(URI.class), any(File.class));
- assertThat(noDownloadedFiles()).isGreaterThan(0);
- }
-
- @Test
- public void throw_exception_if_could_not_download() {
- Plugin test = Plugin.factory("test");
- Release test10 = new Release(test, "1.0").setDownloadUrl("file://not_found");
- test.addRelease(test10);
-
- when(updateCenter.findInstallablePlugins("foo", create("1.0"))).thenReturn(newArrayList(test10));
-
- pluginDownloader.start();
- try {
- pluginDownloader.download("foo", create("1.0"));
- fail();
- } catch (IllegalStateException e) {
- // ok
- }
- }
-
- @Test
- public void throw_exception_if_download_fail() {
- Plugin test = Plugin.factory("test");
- Release test10 = new Release(test, "1.0").setDownloadUrl("http://server/test-1.0.jar");
- test.addRelease(test10);
- when(updateCenter.findInstallablePlugins("foo", create("1.0"))).thenReturn(newArrayList(test10));
-
- doThrow(new RuntimeException()).when(httpDownloader).download(any(URI.class), any(File.class));
-
- pluginDownloader.start();
- try {
- pluginDownloader.download("foo", create("1.0"));
- fail();
- } catch (IllegalStateException e) {
- // ok
- }
- }
-
- @Test
- public void read_download_folder() throws Exception {
- pluginDownloader.start();
- assertThat(noDownloadedFiles()).isZero();
-
- copyFileToDirectory(TestProjectUtils.jarOf("test-base-plugin"), downloadDir);
-
- assertThat(pluginDownloader.getDownloadedPlugins()).hasSize(1);
- PluginInfo info = pluginDownloader.getDownloadedPlugins().iterator().next();
- assertThat(info.getKey()).isEqualTo("testbase");
- assertThat(info.getName()).isEqualTo("Base Plugin");
- assertThat(info.getVersion()).isEqualTo(Version.create("0.1-SNAPSHOT"));
- assertThat(info.getMainClass()).isEqualTo("BasePlugin");
- }
-
- @Test
- public void getDownloadedPluginFilenames_reads_plugin_info_of_files_in_download_folder() throws Exception {
- pluginDownloader.start();
- assertThat(pluginDownloader.getDownloadedPlugins()).hasSize(0);
-
- File file1 = new File(downloadDir, "file1.jar");
- file1.createNewFile();
- File file2 = new File(downloadDir, "file2.jar");
- file2.createNewFile();
-
- assertThat(noDownloadedFiles()).isEqualTo(2);
- }
-
- @Test
- public void cancel_downloads() throws Exception {
- File file1 = new File(downloadDir, "file1.jar");
- file1.createNewFile();
- File file2 = new File(downloadDir, "file2.jar");
- file2.createNewFile();
-
- pluginDownloader.start();
- assertThat(noDownloadedFiles()).isGreaterThan(0);
- pluginDownloader.cancelDownloads();
- assertThat(noDownloadedFiles()).isZero();
- }
-
- private int noDownloadedFiles() {
- return downloadDir.listFiles((file, name) -> name.endsWith(".jar")).length;
- }
-
- // SONAR-5011
- @Test
- public void download_common_transitive_dependency() {
- Plugin test1 = Plugin.factory("test1");
- Release test1R = new Release(test1, "1.0").setDownloadUrl("http://server/test1-1.0.jar");
- test1.addRelease(test1R);
-
- Plugin test2 = Plugin.factory("test2");
- Release test2R = new Release(test2, "1.0").setDownloadUrl("http://server/test2-1.0.jar");
- test2.addRelease(test2R);
-
- Plugin testDep = Plugin.factory("testdep");
- Release testDepR = new Release(testDep, "1.0").setDownloadUrl("http://server/testdep-1.0.jar");
- testDep.addRelease(testDepR);
-
- when(updateCenter.findInstallablePlugins("test1", create("1.0"))).thenReturn(newArrayList(test1R, testDepR));
- when(updateCenter.findInstallablePlugins("test2", create("1.0"))).thenReturn(newArrayList(test2R, testDepR));
-
- pluginDownloader.start();
- pluginDownloader.download("test1", create("1.0"));
- pluginDownloader.download("test2", create("1.0"));
-
- assertThat(new File(downloadDir, "test1-1.0.jar")).exists();
- assertThat(new File(downloadDir, "test2-1.0.jar")).exists();
- assertThat(new File(downloadDir, "testdep-1.0.jar")).exists();
- }
-
- class HasFileName implements ArgumentMatcher<File> {
- private final String name;
-
- HasFileName(String name) {
- this.name = name;
- }
-
- @Override
- public boolean matches(File file) {
- return file.getName().equals(name);
- }
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.plugins;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.lang.RandomStringUtils;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.api.config.internal.MapSettings;
-import org.sonar.core.platform.PluginInfo;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonar.server.plugins.PluginFileSystem.PROPERTY_PLUGIN_COMPRESSION_ENABLE;
-
-public class PluginFileSystemTest {
- @Rule
- public TemporaryFolder temp = new TemporaryFolder();
-
- private MapSettings settings = new MapSettings();
- private Path targetJarPath;
- private Path targetFolder;
- private Path sourceFolder;
-
- @Before
- public void setUp() throws IOException {
- sourceFolder = temp.newFolder("source").toPath();
- targetFolder = temp.newFolder("target").toPath();
- targetJarPath = targetFolder.resolve("test.jar");
- Files.createFile(targetJarPath);
- }
-
- @Test
- public void add_plugin_to_list_of_installed_plugins() throws IOException {
- File jar = touch(temp.newFolder(), "sonar-foo-plugin.jar");
- PluginInfo info = new PluginInfo("foo");
-
- PluginFileSystem underTest = new PluginFileSystem(settings.asConfig());
- underTest.addInstalledPlugin(info, jar);
-
- assertThat(underTest.getInstalledFiles()).hasSize(1);
- InstalledPlugin installedPlugin = underTest.getInstalledPlugin("foo").get();
- assertThat(installedPlugin.getCompressedJar()).isNull();
- assertThat(installedPlugin.getLoadedJar().getFile().toPath()).isEqualTo(jar.toPath());
- assertThat(installedPlugin.getPluginInfo()).isSameAs(info);
- }
-
- @Test
- public void compress_jar_if_compression_enabled() throws IOException {
- File jar = touch(temp.newFolder(), "sonar-foo-plugin.jar");
- PluginInfo info = new PluginInfo("foo").setJarFile(jar);
- // the JAR is copied somewhere else in order to be loaded by classloaders
- File loadedJar = touch(temp.newFolder(), "sonar-foo-plugin.jar");
-
- settings.setProperty(PROPERTY_PLUGIN_COMPRESSION_ENABLE, true);
- PluginFileSystem underTest = new PluginFileSystem(settings.asConfig());
- underTest.addInstalledPlugin(info, loadedJar);
-
- assertThat(underTest.getInstalledFiles()).hasSize(1);
-
- InstalledPlugin installedPlugin = underTest.getInstalledPlugin("foo").get();
- assertThat(installedPlugin.getPluginInfo()).isSameAs(info);
- assertThat(installedPlugin.getLoadedJar().getFile().toPath()).isEqualTo(loadedJar.toPath());
- assertThat(installedPlugin.getCompressedJar().getFile())
- .exists()
- .isFile()
- .hasName("sonar-foo-plugin.pack.gz")
- .hasParent(loadedJar.getParentFile());
- }
-
- @Test
- public void copy_and_use_existing_packed_jar_if_compression_enabled() throws IOException {
- File jar = touch(temp.newFolder(), "sonar-foo-plugin.jar");
- File packedJar = touch(jar.getParentFile(), "sonar-foo-plugin.pack.gz");
- PluginInfo info = new PluginInfo("foo").setJarFile(jar);
- // the JAR is copied somewhere else in order to be loaded by classloaders
- File loadedJar = touch(temp.newFolder(), "sonar-foo-plugin.jar");
-
- settings.setProperty(PROPERTY_PLUGIN_COMPRESSION_ENABLE, true);
- PluginFileSystem underTest = new PluginFileSystem(settings.asConfig());
- underTest.addInstalledPlugin(info, loadedJar);
-
- assertThat(underTest.getInstalledFiles()).hasSize(1);
-
- InstalledPlugin installedPlugin = underTest.getInstalledPlugin("foo").get();
- assertThat(installedPlugin.getPluginInfo()).isSameAs(info);
- assertThat(installedPlugin.getLoadedJar().getFile().toPath()).isEqualTo(loadedJar.toPath());
- assertThat(installedPlugin.getCompressedJar().getFile())
- .exists()
- .isFile()
- .hasName(packedJar.getName())
- .hasParent(loadedJar.getParentFile())
- .hasSameContentAs(packedJar);
- }
-
- private static File touch(File dir, String filename) throws IOException {
- File file = new File(dir, filename);
- FileUtils.write(file, RandomStringUtils.random(10));
- return file;
- }
-
- //
- // @Test
- // public void should_use_deployed_packed_file() throws IOException {
- // Path packedPath = sourceFolder.resolve("test.pack.gz");
- // Files.write(packedPath, new byte[] {1, 2, 3});
- //
- // settings.setProperty(PROPERTY_PLUGIN_COMPRESSION_ENABLE, true);
- // underTest = new PluginFileSystem(settings.asConfig());
- // underTest.compressJar("key", sourceFolder, targetJarPath);
- //
- // assertThat(Files.list(targetFolder)).containsOnly(targetJarPath, targetFolder.resolve("test.pack.gz"));
- // assertThat(underTest.getPlugins()).hasSize(1);
- // assertThat(underTest.getPlugins().get("key").getFilename()).isEqualTo("test.pack.gz");
- //
- // // check that the file was copied, not generated
- // assertThat(targetFolder.resolve("test.pack.gz")).hasSameContentAs(packedPath);
- // }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.plugins;
-
-import java.io.File;
-import java.io.IOException;
-import org.apache.commons.io.FileUtils;
-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.server.platform.ServerFileSystem;
-
-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.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-
-public class PluginUninstallerTest {
- @Rule
- public TemporaryFolder testFolder = new TemporaryFolder();
-
- @Rule
- public ExpectedException exception = ExpectedException.none();
-
- private File uninstallDir;
- private PluginUninstaller underTest;
- private ServerPluginRepository serverPluginRepository;
- private ServerFileSystem fs;
-
- @Before
- public void setUp() throws IOException {
- serverPluginRepository = mock(ServerPluginRepository.class);
- uninstallDir = testFolder.newFolder("uninstall");
- fs = mock(ServerFileSystem.class);
- when(fs.getUninstalledPluginsDir()).thenReturn(uninstallDir);
- underTest = new PluginUninstaller(serverPluginRepository, fs);
- }
-
- @Test
- public void uninstall() {
- when(serverPluginRepository.hasPlugin("plugin")).thenReturn(true);
- underTest.uninstall("plugin");
- verify(serverPluginRepository).uninstall("plugin", uninstallDir);
- }
-
- @Test
- public void fail_uninstall_if_plugin_not_installed() {
- when(serverPluginRepository.hasPlugin("plugin")).thenReturn(false);
- exception.expect(IllegalArgumentException.class);
- exception.expectMessage("Plugin [plugin] is not installed");
- underTest.uninstall("plugin");
- verifyZeroInteractions(serverPluginRepository);
- }
-
- @Test
- public void create_uninstall_dir() {
- File dir = new File(testFolder.getRoot(), "dir");
- when(fs.getUninstalledPluginsDir()).thenReturn(dir);
- underTest = new PluginUninstaller(serverPluginRepository, fs);
- underTest.start();
- assertThat(dir).isDirectory();
- }
-
- @Test
- public void cancel() {
- underTest.cancelUninstalls();
- verify(serverPluginRepository).cancelUninstalls(uninstallDir);
- verifyNoMoreInteractions(serverPluginRepository);
- }
-
- @Test
- public void list_uninstalled_plugins() throws IOException {
- new File(uninstallDir, "file1").createNewFile();
- copyTestPluginTo("test-base-plugin", uninstallDir);
- assertThat(underTest.getUninstalledPlugins()).extracting("key").containsOnly("testbase");
- }
-
- private File copyTestPluginTo(String testPluginName, File toDir) throws IOException {
- File jar = TestProjectUtils.jarOf(testPluginName);
- // file is copied because it's supposed to be moved by the test
- FileUtils.copyFileToDirectory(jar, toDir);
- return new File(toDir, jar.getName());
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.plugins;
-
-import java.io.File;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.core.platform.ExplodedPlugin;
-import org.sonar.core.platform.PluginInfo;
-import org.sonar.server.platform.ServerFileSystem;
-
-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 ServerPluginJarExploderTest {
-
- @Rule
- public TemporaryFolder temp = new TemporaryFolder();
-
- private ServerFileSystem fs = mock(ServerFileSystem.class);
- private PluginFileSystem pluginFileSystem = mock(PluginFileSystem.class);
- private ServerPluginJarExploder underTest = new ServerPluginJarExploder(fs, pluginFileSystem);
-
- @Test
- public void copy_all_classloader_files_to_dedicated_directory() throws Exception {
- File deployDir = temp.newFolder();
- when(fs.getDeployedPluginsDir()).thenReturn(deployDir);
- File sourceJar = TestProjectUtils.jarOf("test-libs-plugin");
- PluginInfo info = PluginInfo.create(sourceJar);
-
- ExplodedPlugin exploded = underTest.explode(info);
-
- // all the files loaded by classloaders (JAR + META-INF/libs/*.jar) are copied to the dedicated directory
- // web/deploy/{pluginKey}
- File pluginDeployDir = new File(deployDir, "testlibs");
-
- assertThat(exploded.getKey()).isEqualTo("testlibs");
- assertThat(exploded.getMain()).isFile().exists().hasParent(pluginDeployDir);
- assertThat(exploded.getLibs()).extracting("name").containsOnly("commons-daemon-1.0.15.jar", "commons-email-20030310.165926.jar");
- for (File lib : exploded.getLibs()) {
- assertThat(lib).exists().isFile();
- assertThat(lib.getCanonicalPath()).startsWith(pluginDeployDir.getCanonicalPath());
- }
- File targetJar = new File(fs.getDeployedPluginsDir(), "testlibs/test-libs-plugin-0.1-SNAPSHOT.jar");
- verify(pluginFileSystem).addInstalledPlugin(info, targetJar);
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.plugins;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import java.io.File;
-import java.io.IOException;
-import java.util.Collections;
-import java.util.Map;
-import org.apache.commons.io.FileUtils;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.rules.TemporaryFolder;
-import org.mockito.Mockito;
-import org.sonar.api.SonarRuntime;
-import org.sonar.api.utils.MessageException;
-import org.sonar.api.utils.log.LogTester;
-import org.sonar.core.platform.PluginInfo;
-import org.sonar.core.platform.PluginLoader;
-import org.sonar.server.platform.ServerFileSystem;
-import org.sonar.updatecenter.common.Version;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class ServerPluginRepositoryTest {
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- @Rule
- public TemporaryFolder temp = new TemporaryFolder();
-
- @Rule
- public LogTester logs = new LogTester();
-
- private SonarRuntime runtime = mock(SonarRuntime.class);
- private ServerFileSystem fs = mock(ServerFileSystem.class, Mockito.RETURNS_DEEP_STUBS);
- private PluginLoader pluginLoader = mock(PluginLoader.class);
- private ServerPluginRepository underTest = new ServerPluginRepository(runtime, fs, pluginLoader);
-
- @Before
- public void setUp() throws IOException {
- when(fs.getDeployedPluginsDir()).thenReturn(temp.newFolder());
- when(fs.getDownloadedPluginsDir()).thenReturn(temp.newFolder());
- when(fs.getHomeDir()).thenReturn(temp.newFolder());
- when(fs.getInstalledPluginsDir()).thenReturn(temp.newFolder());
- when(fs.getTempDir()).thenReturn(temp.newFolder());
- when(runtime.getApiVersion()).thenReturn(org.sonar.api.utils.Version.parse("5.2"));
- }
-
- @After
- public void tearDown() {
- underTest.stop();
- }
-
- @Test
- public void standard_startup_loads_installed_plugins() throws Exception {
- copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir());
-
- underTest.start();
-
- assertThat(underTest.getPluginInfosByKeys()).containsOnlyKeys("testbase");
- }
-
- @Test
- public void no_plugins_at_all_on_startup() {
- underTest.start();
-
- assertThat(underTest.getPluginInfos()).isEmpty();
- assertThat(underTest.getPluginInfosByKeys()).isEmpty();
- assertThat(underTest.hasPlugin("testbase")).isFalse();
- }
-
- @Test
- public void fail_if_multiple_jars_for_same_installed_plugin_on_startup() throws Exception {
- copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir());
- copyTestPluginTo("test-base-plugin-v2", fs.getInstalledPluginsDir());
-
- try {
- underTest.start();
- fail();
- } catch (MessageException e) {
- assertThat(e)
- .hasMessageStartingWith("Found two versions of the plugin Base Plugin [testbase] in the directory extensions/plugins. Please remove one of ")
- // order is not guaranteed, so assertion is split
- .hasMessageContaining("test-base-plugin-0.1-SNAPSHOT.jar")
- .hasMessageContaining("test-base-plugin-0.2-SNAPSHOT.jar");
- }
- }
-
- @Test
- public void install_downloaded_plugins_on_startup() throws Exception {
- File downloadedJar = copyTestPluginTo("test-base-plugin", fs.getDownloadedPluginsDir());
-
- underTest.start();
-
- // plugin is moved to extensions/plugins then loaded
- assertThat(downloadedJar).doesNotExist();
- assertThat(new File(fs.getInstalledPluginsDir(), downloadedJar.getName())).isFile().exists();
- assertThat(underTest.getPluginInfosByKeys()).containsOnlyKeys("testbase");
- }
-
- @Test
- public void downloaded_file_overrides_existing_installed_file_on_startup() throws Exception {
- File installedV1 = copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir());
- File downloadedV2 = copyTestPluginTo("test-base-plugin-v2", fs.getDownloadedPluginsDir());
-
- underTest.start();
-
- // plugin is moved to extensions/plugins and replaces v1
- assertThat(downloadedV2).doesNotExist();
- assertThat(installedV1).doesNotExist();
- assertThat(new File(fs.getInstalledPluginsDir(), downloadedV2.getName())).exists();
- assertThat(underTest.getPluginInfosByKeys()).containsOnlyKeys("testbase");
- assertThat(underTest.getPluginInfo("testbase").getVersion()).isEqualTo(Version.create("0.2-SNAPSHOT"));
- }
-
- @Test
- public void blacklisted_plugin_is_automatically_uninstalled_on_startup() throws Exception {
- underTest.setBlacklistedPluginKeys(ImmutableSet.of("testbase", "issuesreport"));
- File jar = copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir());
-
- underTest.start();
-
- // plugin is not installed and file is deleted
- assertThat(underTest.getPluginInfos()).isEmpty();
- assertThat(jar).doesNotExist();
- }
-
- @Test
- public void test_plugin_requirements_at_startup() throws Exception {
- copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir());
- copyTestPluginTo("test-require-plugin", fs.getInstalledPluginsDir());
-
- underTest.start();
-
- // both plugins are installed
- assertThat(underTest.getPluginInfosByKeys()).containsOnlyKeys("testbase", "testrequire");
- }
-
- @Test
- public void plugin_is_ignored_if_required_plugin_is_missing_at_startup() throws Exception {
- copyTestPluginTo("test-require-plugin", fs.getInstalledPluginsDir());
-
- underTest.start();
-
- // plugin is not installed as test-base-plugin is missing
- assertThat(underTest.getPluginInfosByKeys()).isEmpty();
- }
-
- @Test
- public void plugin_is_ignored_if_required_plugin_is_too_old_at_startup() throws Exception {
- copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir());
- copyTestPluginTo("test-requirenew-plugin", fs.getInstalledPluginsDir());
-
- underTest.start();
-
- // the plugin "requirenew" is not installed as it requires base 0.2+ to be installed.
- assertThat(underTest.getPluginInfosByKeys()).containsOnlyKeys("testbase");
- }
-
- @Test
- public void fail_if_plugin_does_not_support_sq_version() throws Exception {
- when(runtime.getApiVersion()).thenReturn(org.sonar.api.utils.Version.parse("1.0"));
- copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir());
-
- try {
- underTest.start();
- fail();
- } catch (MessageException e) {
- assertThat(e).hasMessage("Plugin Base Plugin [testbase] requires at least SonarQube 4.5.4");
- }
- }
-
- @Test
- public void uninstall() throws Exception {
- File installedJar = copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir());
- File uninstallDir = temp.newFolder("uninstallDir");
-
- underTest.start();
- assertThat(underTest.getPluginInfosByKeys()).containsOnlyKeys("testbase");
- underTest.uninstall("testbase", uninstallDir);
-
- assertThat(installedJar).doesNotExist();
- // still up. Will be dropped after next startup
- assertThat(underTest.getPluginInfosByKeys()).containsOnlyKeys("testbase");
- assertThat(uninstallDir.list()).containsOnly(installedJar.getName());
- }
-
- @Test
- public void uninstall_dependents() throws Exception {
- File base = copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir());
- File extension = copyTestPluginTo("test-require-plugin", fs.getInstalledPluginsDir());
- File uninstallDir = temp.newFolder("uninstallDir");
-
- underTest.start();
- assertThat(underTest.getPluginInfos()).hasSize(2);
- underTest.uninstall("testbase", uninstallDir);
- assertThat(base).doesNotExist();
- assertThat(extension).doesNotExist();
- assertThat(uninstallDir.list()).containsOnly(base.getName(), extension.getName());
- }
-
- @Test
- public void dont_uninstall_non_existing_dependents() throws IOException {
- File base = copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir());
- File extension = copyTestPluginTo("test-require-plugin", fs.getInstalledPluginsDir());
- File uninstallDir = temp.newFolder("uninstallDir");
-
- underTest.start();
- assertThat(underTest.getPluginInfos()).hasSize(2);
- underTest.uninstall("testrequire", uninstallDir);
- assertThat(underTest.getPluginInfos()).hasSize(2);
-
- underTest.uninstall("testbase", uninstallDir);
- assertThat(base).doesNotExist();
- assertThat(extension).doesNotExist();
- assertThat(uninstallDir.list()).containsOnly(base.getName(), extension.getName());
- }
-
- @Test
- public void dont_uninstall_non_existing_files() throws IOException {
- File base = copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir());
- File extension = copyTestPluginTo("test-require-plugin", fs.getInstalledPluginsDir());
- File uninstallDir = temp.newFolder("uninstallDir");
-
- underTest.start();
- assertThat(underTest.getPluginInfos()).hasSize(2);
- underTest.uninstall("testbase", uninstallDir);
- assertThat(underTest.getPluginInfos()).hasSize(2);
-
- underTest.uninstall("testbase", uninstallDir);
- assertThat(base).doesNotExist();
- assertThat(extension).doesNotExist();
- assertThat(uninstallDir.list()).containsOnly(base.getName(), extension.getName());
- }
-
- @Test
- public void install_plugin_and_its_extension_plugins_at_startup() throws Exception {
- copyTestPluginTo("test-base-plugin", fs.getInstalledPluginsDir());
- copyTestPluginTo("test-extend-plugin", fs.getInstalledPluginsDir());
-
- underTest.start();
-
- // both plugins are installed
- assertThat(underTest.getPluginInfosByKeys()).containsOnlyKeys("testbase", "testextend");
- }
-
- @Test
- public void extension_plugin_is_ignored_if_base_plugin_is_missing_at_startup() throws Exception {
- copyTestPluginTo("test-extend-plugin", fs.getInstalledPluginsDir());
-
- underTest.start();
-
- // plugin is not installed as its base plugin is not installed
- assertThat(underTest.getPluginInfos()).isEmpty();
- }
-
- @Test
- public void fail_to_get_missing_plugins() {
- underTest.start();
- try {
- underTest.getPluginInfo("unknown");
- fail();
- } catch (IllegalArgumentException e) {
- assertThat(e).hasMessage("Plugin [unknown] does not exist");
- }
-
- try {
- underTest.getPluginInstance("unknown");
- fail();
- } catch (IllegalArgumentException e) {
- assertThat(e).hasMessage("Plugin [unknown] does not exist");
- }
- }
-
- @Test
- public void plugin_is_incompatible_if_no_entry_point_class() {
- PluginInfo plugin = new PluginInfo("foo").setName("Foo");
- assertThat(ServerPluginRepository.isCompatible(plugin, runtime, Collections.emptyMap())).isFalse();
- assertThat(logs.logs()).contains("Plugin Foo [foo] is ignored because entry point class is not defined");
- }
-
- @Test
- public void fail_when_views_is_installed() throws Exception {
- copyTestPluginTo("fake-views-plugin", fs.getInstalledPluginsDir());
-
- expectedException.expect(MessageException.class);
- expectedException.expectMessage("Plugin 'views' is no longer compatible with this version of SonarQube");
- underTest.start();
- }
-
- @Test
- public void fail_when_sqale_plugin_is_installed() throws Exception {
- copyTestPluginTo("fake-sqale-plugin", fs.getInstalledPluginsDir());
-
- expectedException.expect(MessageException.class);
- expectedException.expectMessage("Plugin 'sqale' is no longer compatible with this version of SonarQube");
- underTest.start();
- }
-
- @Test
- public void fail_when_report_is_installed() throws Exception {
- copyTestPluginTo("fake-report-plugin", fs.getInstalledPluginsDir());
-
- expectedException.expect(MessageException.class);
- expectedException.expectMessage("Plugin 'report' is no longer compatible with this version of SonarQube");
- underTest.start();
- }
-
- /**
- * Some plugins can only extend the classloader of base plugin, without declaring new extensions.
- */
- @Test
- public void plugin_is_compatible_if_no_entry_point_class_but_extend_other_plugin() {
- PluginInfo basePlugin = new PluginInfo("base").setMainClass("org.bar.Bar");
- PluginInfo plugin = new PluginInfo("foo").setBasePlugin("base");
- Map<String, PluginInfo> plugins = ImmutableMap.of("base", basePlugin, "foo", plugin);
-
- assertThat(ServerPluginRepository.isCompatible(plugin, runtime, plugins)).isTrue();
- }
-
- @Test
- public void getPluginInstance_throws_ISE_if_repo_is_not_started() {
- expectedException.expect(IllegalStateException.class);
- expectedException.expectMessage("not started yet");
-
- underTest.getPluginInstance("foo");
- }
-
- @Test
- public void getPluginInfo_throws_ISE_if_repo_is_not_started() {
- expectedException.expect(IllegalStateException.class);
- expectedException.expectMessage("not started yet");
-
- underTest.getPluginInfo("foo");
- }
-
- @Test
- public void hasPlugin_throws_ISE_if_repo_is_not_started() {
- expectedException.expect(IllegalStateException.class);
- expectedException.expectMessage("not started yet");
-
- underTest.hasPlugin("foo");
- }
-
- @Test
- public void getPluginInfos_throws_ISE_if_repo_is_not_started() {
- expectedException.expect(IllegalStateException.class);
- expectedException.expectMessage("not started yet");
-
- underTest.getPluginInfos();
- }
-
- private File copyTestPluginTo(String testPluginName, File toDir) throws IOException {
- File jar = TestProjectUtils.jarOf(testPluginName);
- // file is copied because it's supposed to be moved by the test
- FileUtils.copyFileToDirectory(jar, toDir);
- return new File(toDir, jar.getName());
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.plugins;
-
-import org.sonar.api.Plugin;
-
-public class TestPluginA implements Plugin {
- @Override
- public void define(Context context) {
-
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.plugins;
-
-import org.apache.commons.io.FileUtils;
-
-import java.io.File;
-import java.util.Collection;
-
-public class TestProjectUtils {
-
- /**
- * Get the artifact of plugins stored in src/test/projects
- */
- public static File jarOf(String dirName) {
- File target = FileUtils.toFile(TestProjectUtils.class.getResource(String.format("/%s/target/", dirName)));
- Collection<File> jars = FileUtils.listFiles(target, new String[] {"jar"}, false);
- if (jars == null || jars.size() != 1) {
- throw new IllegalArgumentException("Test project is badly defined: " + dirName);
- }
- return jars.iterator().next();
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.plugins;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.nio.charset.StandardCharsets;
-import org.junit.Before;
-import org.junit.Test;
-import org.sonar.api.config.internal.MapSettings;
-import org.sonar.api.utils.SonarException;
-import org.sonar.api.utils.UriReader;
-import org.sonar.process.ProcessProperties;
-import org.sonar.updatecenter.common.UpdateCenter;
-import org.sonar.updatecenter.common.Version;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.guava.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-public class UpdateCenterClientTest {
-
- private static final String BASE_URL = "https://update.sonarsource.org";
- private UriReader reader = mock(UriReader.class);
- private MapSettings settings = new MapSettings();
- private UpdateCenterClient underTest;
-
- @Before
- public void startServer() throws Exception {
- reader = mock(UriReader.class);
- settings.setProperty(UpdateCenterClient.URL_PROPERTY, BASE_URL);
- settings.setProperty(ProcessProperties.Property.SONAR_UPDATECENTER_ACTIVATE.getKey(), true);
- underTest = new UpdateCenterClient(reader, settings.asConfig());
- }
-
- @Test
- public void downloadUpdateCenter() throws URISyntaxException {
- when(reader.readString(new URI(BASE_URL), StandardCharsets.UTF_8)).thenReturn("publicVersions=2.2,2.3");
- UpdateCenter plugins = underTest.getUpdateCenter().get();
- verify(reader, times(1)).readString(new URI(BASE_URL), StandardCharsets.UTF_8);
- assertThat(plugins.getSonar().getVersions()).containsOnly(Version.create("2.2"), Version.create("2.3"));
- assertThat(underTest.getLastRefreshDate()).isNotNull();
- }
-
- @Test
- public void not_available_before_initialization() {
- assertThat(underTest.getLastRefreshDate()).isNull();
- }
-
- @Test
- public void ignore_connection_errors() {
- when(reader.readString(any(URI.class), eq(StandardCharsets.UTF_8))).thenThrow(new SonarException());
- assertThat(underTest.getUpdateCenter()).isAbsent();
- }
-
- @Test
- public void cache_data() throws Exception {
- when(reader.readString(new URI(BASE_URL), StandardCharsets.UTF_8)).thenReturn("sonar.versions=2.2,2.3");
-
- underTest.getUpdateCenter();
- underTest.getUpdateCenter();
-
- verify(reader, times(1)).readString(new URI(BASE_URL), StandardCharsets.UTF_8);
- }
-
- @Test
- public void forceRefresh() throws Exception {
- when(reader.readString(new URI(BASE_URL), StandardCharsets.UTF_8)).thenReturn("sonar.versions=2.2,2.3");
-
- underTest.getUpdateCenter();
- underTest.getUpdateCenter(true);
-
- verify(reader, times(2)).readString(new URI(BASE_URL), StandardCharsets.UTF_8);
- }
-
- @Test
- public void update_center_is_null_when_property_is_false() {
- settings.setProperty(ProcessProperties.Property.SONAR_UPDATECENTER_ACTIVATE.getKey(), false);
-
- assertThat(underTest.getUpdateCenter()).isAbsent();
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.plugins;
-
-import com.google.common.base.Optional;
-import org.junit.Test;
-import org.sonar.api.SonarRuntime;
-import org.sonar.updatecenter.common.UpdateCenter;
-
-import static org.assertj.guava.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class UpdateCenterMatrixFactoryTest {
-
- private UpdateCenterMatrixFactory underTest;
-
- @Test
- public void return_absent_update_center() {
- UpdateCenterClient updateCenterClient = mock(UpdateCenterClient.class);
- when(updateCenterClient.getUpdateCenter(anyBoolean())).thenReturn(Optional.absent());
-
- underTest = new UpdateCenterMatrixFactory(updateCenterClient, mock(SonarRuntime.class), mock(InstalledPluginReferentialFactory.class));
-
- Optional<UpdateCenter> updateCenter = underTest.getUpdateCenter(false);
-
- assertThat(updateCenter).isAbsent();
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.plugins;
-
-import javax.servlet.GenericServlet;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import java.io.IOException;
-import java.util.Properties;
-
-public class UpdateCenterServlet extends GenericServlet {
-
- int count = 0;
-
- @Override
- public void service(ServletRequest request, ServletResponse response) throws IOException {
- count++;
- Properties props = new Properties();
- props.setProperty("count", String.valueOf(count));
- props.setProperty("agent", ((HttpServletRequest)request).getHeader("User-Agent"));
- props.store(response.getOutputStream(), null);
- }
-}
-
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.plugins.edition;
-
-import java.util.Random;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.core.platform.PluginInfo;
-import org.sonar.updatecenter.common.Plugin;
-
-import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class EditionBundledPluginsTest {
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private final Random random = new Random();
-
- @Test
- public void isEditionBundled_on_Plugin_fails_with_NPE_if_arg_is_null() {
- expectedException.expect(NullPointerException.class);
-
- EditionBundledPlugins.isEditionBundled((Plugin) null);
- }
-
- @Test
- public void isEditionBundled_on_Plugin_returns_false_for_SonarSource_and_non_commercial_license() {
- Plugin plugin = newPlugin(randomizeCase("SonarSource"), randomAlphanumeric(3));
-
- assertThat(EditionBundledPlugins.isEditionBundled(plugin)).isFalse();
- }
-
- @Test
- public void isEditionBundled_on_Plugin_returns_false_for_license_SonarSource_and_non_SonarSource_organization() {
- Plugin plugin = newPlugin(randomAlphanumeric(3), randomizeCase("SonarSource"));
-
- assertThat(EditionBundledPlugins.isEditionBundled(plugin)).isFalse();
- }
-
- @Test
- public void isEditionBundled_on_Plugin_returns_false_for_license_Commercial_and_non_SonarSource_organization() {
- Plugin plugin = newPlugin(randomAlphanumeric(3), randomizeCase("Commercial"));
-
- assertThat(EditionBundledPlugins.isEditionBundled(plugin)).isFalse();
- }
-
- @Test
- public void isEditionBundled_on_Plugin_returns_true_for_organization_SonarSource_and_license_SonarSource_case_insensitive() {
- Plugin plugin = newPlugin(randomizeCase("SonarSource"), randomizeCase("SonarSource"));
-
- assertThat(EditionBundledPlugins.isEditionBundled(plugin)).isTrue();
- }
-
- @Test
- public void isEditionBundled_on_Plugin_returns_true_for_organization_SonarSource_and_license_Commercial_case_insensitive() {
- Plugin plugin = newPlugin(randomizeCase("SonarSource"), randomizeCase("Commercial"));
-
- assertThat(EditionBundledPlugins.isEditionBundled(plugin)).isTrue();
- }
-
- @Test
- public void isEditionBundled_on_PluginInfo_fails_with_NPE_if_arg_is_null() {
- expectedException.expect(NullPointerException.class);
-
- EditionBundledPlugins.isEditionBundled((PluginInfo) null);
- }
-
- @Test
- public void isEditionBundled_on_PluginInfo_returns_false_for_SonarSource_and_non_commercial_license() {
- PluginInfo pluginInfo = newPluginInfo(randomizeCase("SonarSource"), randomAlphanumeric(3));
-
- assertThat(EditionBundledPlugins.isEditionBundled(pluginInfo)).isFalse();
- }
-
- @Test
- public void isEditionBundled_on_PluginInfo_returns_false_for_license_SonarSource_and_non_SonarSource_organization() {
- PluginInfo pluginInfo = newPluginInfo(randomAlphanumeric(3), randomizeCase("SonarSource"));
-
- assertThat(EditionBundledPlugins.isEditionBundled(pluginInfo)).isFalse();
- }
-
- @Test
- public void isEditionBundled_on_PluginInfo_returns_false_for_license_Commercial_and_non_SonarSource_organization() {
- PluginInfo pluginInfo = newPluginInfo(randomAlphanumeric(3), randomizeCase("Commercial"));
-
- assertThat(EditionBundledPlugins.isEditionBundled(pluginInfo)).isFalse();
- }
-
- @Test
- public void isEditionBundled_on_PluginInfo_returns_true_for_organization_SonarSource_and_license_SonarSource_case_insensitive() {
- PluginInfo pluginInfo = newPluginInfo(randomizeCase("SonarSource"), randomizeCase("SonarSource"));
-
- assertThat(EditionBundledPlugins.isEditionBundled(pluginInfo)).isTrue();
- }
-
- @Test
- public void isEditionBundled_on_PluginINfo_returns_true_for_organization_SonarSource_and_license_Commercial_case_insensitive() {
- PluginInfo pluginInfo = newPluginInfo(randomizeCase("SonarSource"), randomizeCase("Commercial"));
-
- assertThat(EditionBundledPlugins.isEditionBundled(pluginInfo)).isTrue();
- }
-
- private String randomizeCase(String s) {
- return s.chars()
- .map(c -> random.nextBoolean() ? Character.toUpperCase(c) : Character.toLowerCase(c))
- .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
- .toString();
- }
-
- private PluginInfo newPluginInfo(String organization, String license) {
- PluginInfo pluginInfo = new PluginInfo(randomAlphanumeric(2));
- if (random.nextBoolean()) {
- pluginInfo.setName(randomAlphanumeric(3));
- }
- if (random.nextBoolean()) {
- pluginInfo.setOrganizationUrl(randomAlphanumeric(4));
- }
- if (random.nextBoolean()) {
- pluginInfo.setIssueTrackerUrl(randomAlphanumeric(5));
- }
- if (random.nextBoolean()) {
- pluginInfo.setIssueTrackerUrl(randomAlphanumeric(6));
- }
- if (random.nextBoolean()) {
- pluginInfo.setBasePlugin(randomAlphanumeric(7));
- }
- if (random.nextBoolean()) {
- pluginInfo.setHomepageUrl(randomAlphanumeric(8));
- }
- return pluginInfo
- .setOrganizationName(organization)
- .setLicense(license);
- }
-
- private Plugin newPlugin(String organization, String license) {
- Plugin plugin = Plugin.factory(randomAlphanumeric(2));
- if (random.nextBoolean()) {
- plugin.setName(randomAlphanumeric(3));
- }
- if (random.nextBoolean()) {
- plugin.setOrganizationUrl(randomAlphanumeric(4));
- }
- if (random.nextBoolean()) {
- plugin.setTermsConditionsUrl(randomAlphanumeric(5));
- }
- if (random.nextBoolean()) {
- plugin.setIssueTrackerUrl(randomAlphanumeric(6));
- }
- if (random.nextBoolean()) {
- plugin.setCategory(randomAlphanumeric(7));
- }
- if (random.nextBoolean()) {
- plugin.setHomepageUrl(randomAlphanumeric(8));
- }
- return plugin
- .setLicense(license)
- .setOrganization(organization);
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.project;
-
-import com.tngtech.java.junit.dataprovider.DataProvider;
-import com.tngtech.java.junit.dataprovider.DataProviderRunner;
-import com.tngtech.java.junit.dataprovider.UseDataProvider;
-import java.util.Collections;
-import java.util.Random;
-import java.util.Set;
-import java.util.stream.IntStream;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.runner.RunWith;
-import org.mockito.InOrder;
-import org.mockito.Mockito;
-import org.sonar.core.util.stream.MoreCollectors;
-
-import static java.util.Collections.singleton;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.same;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
-import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto;
-
-@RunWith(DataProviderRunner.class)
-public class ProjectLifeCycleListenersImplTest {
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private ProjectLifeCycleListener listener1 = mock(ProjectLifeCycleListener.class);
- private ProjectLifeCycleListener listener2 = mock(ProjectLifeCycleListener.class);
- private ProjectLifeCycleListener listener3 = mock(ProjectLifeCycleListener.class);
- private ProjectLifeCycleListenersImpl underTestNoListeners = new ProjectLifeCycleListenersImpl();
- private ProjectLifeCycleListenersImpl underTestWithListeners = new ProjectLifeCycleListenersImpl(
- new ProjectLifeCycleListener[] {listener1, listener2, listener3});
-
- @Test
- public void onProjectsDeleted_throws_NPE_if_set_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("projects can't be null");
-
- underTestWithListeners.onProjectsDeleted(null);
- }
-
- @Test
- public void onProjectsDeleted_throws_NPE_if_set_is_null_even_if_no_listeners() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("projects can't be null");
-
- underTestNoListeners.onProjectsDeleted(null);
- }
-
- @Test
- public void onProjectsDeleted_has_no_effect_if_set_is_empty() {
- underTestNoListeners.onProjectsDeleted(Collections.emptySet());
-
- underTestWithListeners.onProjectsDeleted(Collections.emptySet());
- verifyZeroInteractions(listener1, listener2, listener3);
- }
-
- @Test
- @UseDataProvider("oneOrManyProjects")
- public void onProjectsDeleted_does_not_fail_if_there_is_no_listener(Set<Project> projects) {
- underTestNoListeners.onProjectsDeleted(projects);
- }
-
- @Test
- @UseDataProvider("oneOrManyProjects")
- public void onProjectsDeleted_calls_all_listeners_in_order_of_addition_to_constructor(Set<Project> projects) {
- InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
-
- underTestWithListeners.onProjectsDeleted(projects);
-
- inOrder.verify(listener1).onProjectsDeleted(same(projects));
- inOrder.verify(listener2).onProjectsDeleted(same(projects));
- inOrder.verify(listener3).onProjectsDeleted(same(projects));
- inOrder.verifyNoMoreInteractions();
- }
-
- @Test
- @UseDataProvider("oneOrManyProjects")
- public void onProjectsDeleted_calls_all_listeners_even_if_one_throws_an_Exception(Set<Project> projects) {
- InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
- doThrow(new RuntimeException("Faking listener2 throwing an exception"))
- .when(listener2)
- .onProjectsDeleted(any());
-
- underTestWithListeners.onProjectsDeleted(projects);
-
- inOrder.verify(listener1).onProjectsDeleted(same(projects));
- inOrder.verify(listener2).onProjectsDeleted(same(projects));
- inOrder.verify(listener3).onProjectsDeleted(same(projects));
- inOrder.verifyNoMoreInteractions();
- }
-
- @Test
- @UseDataProvider("oneOrManyProjects")
- public void onProjectsDeleted_calls_all_listeners_even_if_one_throws_an_Error(Set<Project> projects) {
- InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
- doThrow(new Error("Faking listener2 throwing an Error"))
- .when(listener2)
- .onProjectsDeleted(any());
-
- underTestWithListeners.onProjectsDeleted(projects);
-
- inOrder.verify(listener1).onProjectsDeleted(same(projects));
- inOrder.verify(listener2).onProjectsDeleted(same(projects));
- inOrder.verify(listener3).onProjectsDeleted(same(projects));
- inOrder.verifyNoMoreInteractions();
- }
-
- @Test
- public void onProjectBranchesDeleted_throws_NPE_if_set_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("projects can't be null");
-
- underTestWithListeners.onProjectBranchesDeleted(null);
- }
-
- @Test
- public void onProjectBranchesDeleted_throws_NPE_if_set_is_null_even_if_no_listeners() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("projects can't be null");
-
- underTestNoListeners.onProjectBranchesDeleted(null);
- }
-
- @Test
- public void onProjectBranchesDeleted_has_no_effect_if_set_is_empty() {
- underTestNoListeners.onProjectBranchesDeleted(Collections.emptySet());
-
- underTestWithListeners.onProjectBranchesDeleted(Collections.emptySet());
- verifyZeroInteractions(listener1, listener2, listener3);
- }
-
- @Test
- @UseDataProvider("oneOrManyProjects")
- public void onProjectBranchesDeleted_does_not_fail_if_there_is_no_listener(Set<Project> projects) {
- underTestNoListeners.onProjectBranchesDeleted(projects);
- }
-
- @Test
- @UseDataProvider("oneOrManyProjects")
- public void onProjectBranchesDeleted_calls_all_listeners_in_order_of_addition_to_constructor(Set<Project> projects) {
- InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
-
- underTestWithListeners.onProjectBranchesDeleted(projects);
-
- inOrder.verify(listener1).onProjectBranchesDeleted(same(projects));
- inOrder.verify(listener2).onProjectBranchesDeleted(same(projects));
- inOrder.verify(listener3).onProjectBranchesDeleted(same(projects));
- inOrder.verifyNoMoreInteractions();
- }
-
- @Test
- @UseDataProvider("oneOrManyProjects")
- public void onProjectBranchesDeleted_calls_all_listeners_even_if_one_throws_an_Exception(Set<Project> projects) {
- InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
- doThrow(new RuntimeException("Faking listener2 throwing an exception"))
- .when(listener2)
- .onProjectBranchesDeleted(any());
-
- underTestWithListeners.onProjectBranchesDeleted(projects);
-
- inOrder.verify(listener1).onProjectBranchesDeleted(same(projects));
- inOrder.verify(listener2).onProjectBranchesDeleted(same(projects));
- inOrder.verify(listener3).onProjectBranchesDeleted(same(projects));
- inOrder.verifyNoMoreInteractions();
- }
-
- @Test
- @UseDataProvider("oneOrManyProjects")
- public void onProjectBranchesDeleted_calls_all_listeners_even_if_one_throws_an_Error(Set<Project> projects) {
- InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
- doThrow(new Error("Faking listener2 throwing an Error"))
- .when(listener2)
- .onProjectBranchesDeleted(any());
-
- underTestWithListeners.onProjectBranchesDeleted(projects);
-
- inOrder.verify(listener1).onProjectBranchesDeleted(same(projects));
- inOrder.verify(listener2).onProjectBranchesDeleted(same(projects));
- inOrder.verify(listener3).onProjectBranchesDeleted(same(projects));
- inOrder.verifyNoMoreInteractions();
- }
-
- @DataProvider
- public static Object[][] oneOrManyProjects() {
- return new Object[][] {
- {singleton(newUniqueProject())},
- {IntStream.range(0, 1 + new Random().nextInt(10)).mapToObj(i -> newUniqueProject()).collect(MoreCollectors.toSet())}
- };
- }
- // SDSDS
-
- @Test
- public void onProjectsRekeyed_throws_NPE_if_set_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("rekeyedProjects can't be null");
-
- underTestWithListeners.onProjectsRekeyed(null);
- }
-
- @Test
- public void onProjectsRekeyed_throws_NPE_if_set_is_null_even_if_no_listeners() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("rekeyedProjects can't be null");
-
- underTestNoListeners.onProjectsRekeyed(null);
- }
-
- @Test
- public void onProjectsRekeyed_has_no_effect_if_set_is_empty() {
- underTestNoListeners.onProjectsRekeyed(Collections.emptySet());
-
- underTestWithListeners.onProjectsRekeyed(Collections.emptySet());
- verifyZeroInteractions(listener1, listener2, listener3);
- }
-
- @Test
- @UseDataProvider("oneOrManyRekeyedProjects")
- public void onProjectsRekeyed_does_not_fail_if_there_is_no_listener(Set<RekeyedProject> projects) {
- underTestNoListeners.onProjectsRekeyed(projects);
- }
-
- @Test
- @UseDataProvider("oneOrManyRekeyedProjects")
- public void onProjectsRekeyed_calls_all_listeners_in_order_of_addition_to_constructor(Set<RekeyedProject> projects) {
- InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
-
- underTestWithListeners.onProjectsRekeyed(projects);
-
- inOrder.verify(listener1).onProjectsRekeyed(same(projects));
- inOrder.verify(listener2).onProjectsRekeyed(same(projects));
- inOrder.verify(listener3).onProjectsRekeyed(same(projects));
- inOrder.verifyNoMoreInteractions();
- }
-
- @Test
- @UseDataProvider("oneOrManyRekeyedProjects")
- public void onProjectsRekeyed_calls_all_listeners_even_if_one_throws_an_Exception(Set<RekeyedProject> projects) {
- InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
- doThrow(new RuntimeException("Faking listener2 throwing an exception"))
- .when(listener2)
- .onProjectsRekeyed(any());
-
- underTestWithListeners.onProjectsRekeyed(projects);
-
- inOrder.verify(listener1).onProjectsRekeyed(same(projects));
- inOrder.verify(listener2).onProjectsRekeyed(same(projects));
- inOrder.verify(listener3).onProjectsRekeyed(same(projects));
- inOrder.verifyNoMoreInteractions();
- }
-
- @Test
- @UseDataProvider("oneOrManyRekeyedProjects")
- public void onProjectsRekeyed_calls_all_listeners_even_if_one_throws_an_Error(Set<RekeyedProject> projects) {
- InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
- doThrow(new Error("Faking listener2 throwing an Error"))
- .when(listener2)
- .onProjectsRekeyed(any());
-
- underTestWithListeners.onProjectsRekeyed(projects);
-
- inOrder.verify(listener1).onProjectsRekeyed(same(projects));
- inOrder.verify(listener2).onProjectsRekeyed(same(projects));
- inOrder.verify(listener3).onProjectsRekeyed(same(projects));
- inOrder.verifyNoMoreInteractions();
- }
-
- @DataProvider
- public static Object[][] oneOrManyRekeyedProjects() {
- return new Object[][] {
- {singleton(newUniqueRekeyedProject())},
- {IntStream.range(0, 1 + new Random().nextInt(10)).mapToObj(i -> newUniqueRekeyedProject()).collect(MoreCollectors.toSet())}
- };
- }
-
- private static Project newUniqueProject() {
- return Project.from(newPrivateProjectDto(newOrganizationDto()));
- }
-
- private static int counter = 3_989;
-
- private static RekeyedProject newUniqueRekeyedProject() {
- int base = counter++;
- Project project = Project.from(newPrivateProjectDto(newOrganizationDto()));
- return new RekeyedProject(project, base + "_old_key");
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.project;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-import static java.util.Collections.emptyList;
-import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
-import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto;
-
-public class RekeyedProjectTest {
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- @Test
- public void constructor_throws_NPE_if_project_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("project can't be null");
-
- new RekeyedProject(null, randomAlphanumeric(3));
- }
-
- @Test
- public void constructor_throws_NPE_if_previousKey_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("previousKey can't be null");
-
- new RekeyedProject(newRandomProject(), null);
- }
-
- @Test
- public void verify_getters() {
- Project project = newRandomProject();
- String previousKey = randomAlphanumeric(6);
- RekeyedProject underTest = new RekeyedProject(project, previousKey);
-
- assertThat(underTest.getProject()).isSameAs(project);
- assertThat(underTest.getPreviousKey()).isEqualTo(previousKey);
- }
-
- @Test
- public void equals_is_based_on_project_and_previousKey() {
- Project project = newRandomProject();
- String previousKey = randomAlphanumeric(6);
- RekeyedProject underTest = new RekeyedProject(project, previousKey);
-
- assertThat(underTest).isEqualTo(underTest);
- assertThat(underTest).isEqualTo(new RekeyedProject(project, previousKey));
- assertThat(underTest).isNotEqualTo(new RekeyedProject(project, randomAlphanumeric(11)));
- assertThat(underTest).isNotEqualTo(new RekeyedProject(newRandomProject(), previousKey));
- assertThat(underTest).isNotEqualTo(new Object());
- assertThat(underTest).isNotEqualTo(null);
- }
-
- @Test
- public void hashCode_is_based_on_project_and_previousKey() {
- Project project = newRandomProject();
- String previousKey = randomAlphanumeric(6);
- RekeyedProject underTest = new RekeyedProject(project, previousKey);
-
- assertThat(underTest.hashCode()).isEqualTo(underTest.hashCode());
- assertThat(underTest.hashCode()).isEqualTo(new RekeyedProject(project, previousKey).hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new RekeyedProject(project, randomAlphanumeric(11)).hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new RekeyedProject(newRandomProject(), previousKey).hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new Object().hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(null);
- }
-
- @Test
- public void verify_toString() {
- Project project = new Project("A", "B", "C", "D", emptyList());
- String previousKey = "E";
- RekeyedProject underTest = new RekeyedProject(project, previousKey);
-
- assertThat(underTest.toString()).isEqualTo("RekeyedProject{project=Project{uuid='A', key='B', name='C', description='D'}, previousKey='E'}");
- }
-
- private static Project newRandomProject() {
- return Project.from(newPrivateProjectDto(newOrganizationDto()));
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.qualitygate.changeevent;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Random;
-import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import org.apache.commons.lang.RandomStringUtils;
-import org.assertj.core.groups.Tuple;
-import org.junit.Rule;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.InOrder;
-import org.mockito.Mockito;
-import org.sonar.api.issue.Issue;
-import org.sonar.api.rules.RuleType;
-import org.sonar.api.utils.log.LogTester;
-import org.sonar.api.utils.log.LoggerLevel;
-import org.sonar.core.issue.DefaultIssue;
-import org.sonar.db.component.ComponentDto;
-import org.sonar.server.qualitygate.changeevent.QGChangeEventListener.ChangedIssue;
-import org.sonar.server.qualitygate.changeevent.QGChangeEventListenersImpl.ChangedIssueImpl;
-
-import static java.util.Arrays.asList;
-import static java.util.Collections.emptyList;
-import static java.util.Collections.emptySet;
-import static java.util.Collections.singletonList;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.tuple;
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.same;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-
-public class QGChangeEventListenersImplTest {
- @Rule
- public LogTester logTester = new LogTester();
-
- private QGChangeEventListener listener1 = mock(QGChangeEventListener.class);
- private QGChangeEventListener listener2 = mock(QGChangeEventListener.class);
- private QGChangeEventListener listener3 = mock(QGChangeEventListener.class);
- private List<QGChangeEventListener> listeners = Arrays.asList(listener1, listener2, listener3);
-
- private String component1Uuid = RandomStringUtils.randomAlphabetic(6);
- private ComponentDto component1 = newComponentDto(component1Uuid);
- private DefaultIssue component1Issue = newDefaultIssue(component1Uuid);
- private List<DefaultIssue> oneIssueOnComponent1 = singletonList(component1Issue);
- private QGChangeEvent component1QGChangeEvent = newQGChangeEvent(component1);
-
- private InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
-
- private QGChangeEventListenersImpl underTest = new QGChangeEventListenersImpl(new QGChangeEventListener[] {listener1, listener2, listener3});
-
- @Test
- public void broadcastOnIssueChange_has_no_effect_when_issues_are_empty() {
- underTest.broadcastOnIssueChange(emptyList(), singletonList(component1QGChangeEvent));
-
- verifyZeroInteractions(listener1, listener2, listener3);
- }
-
- @Test
- public void broadcastOnIssueChange_has_no_effect_when_no_changeEvent() {
- underTest.broadcastOnIssueChange(oneIssueOnComponent1, emptySet());
-
- verifyZeroInteractions(listener1, listener2, listener3);
- }
-
- @Test
- public void broadcastOnIssueChange_passes_same_arguments_to_all_listeners_in_order_of_addition_to_constructor() {
- underTest.broadcastOnIssueChange(oneIssueOnComponent1, singletonList(component1QGChangeEvent));
-
- ArgumentCaptor<Set<ChangedIssue>> changedIssuesCaptor = newSetCaptor();
- inOrder.verify(listener1).onIssueChanges(same(component1QGChangeEvent), changedIssuesCaptor.capture());
- Set<ChangedIssue> changedIssues = changedIssuesCaptor.getValue();
- inOrder.verify(listener2).onIssueChanges(same(component1QGChangeEvent), same(changedIssues));
- inOrder.verify(listener3).onIssueChanges(same(component1QGChangeEvent), same(changedIssues));
- inOrder.verifyNoMoreInteractions();
- }
-
- @Test
- public void broadcastOnIssueChange_calls_all_listeners_even_if_one_throws_an_exception() {
- QGChangeEventListener failingListener = new QGChangeEventListener[] {listener1, listener2, listener3}[new Random().nextInt(3)];
- doThrow(new RuntimeException("Faking an exception thrown by onChanges"))
- .when(failingListener)
- .onIssueChanges(any(), any());
-
- underTest.broadcastOnIssueChange(oneIssueOnComponent1, singletonList(component1QGChangeEvent));
-
- ArgumentCaptor<Set<ChangedIssue>> changedIssuesCaptor = newSetCaptor();
- inOrder.verify(listener1).onIssueChanges(same(component1QGChangeEvent), changedIssuesCaptor.capture());
- Set<ChangedIssue> changedIssues = changedIssuesCaptor.getValue();
- inOrder.verify(listener2).onIssueChanges(same(component1QGChangeEvent), same(changedIssues));
- inOrder.verify(listener3).onIssueChanges(same(component1QGChangeEvent), same(changedIssues));
- inOrder.verifyNoMoreInteractions();
- assertThat(logTester.logs()).hasSize(4);
- assertThat(logTester.logs(LoggerLevel.WARN)).hasSize(1);
- }
-
- @Test
- public void broadcastOnIssueChange_stops_calling_listeners_when_one_throws_an_ERROR() {
- doThrow(new Error("Faking an error thrown by a listener"))
- .when(listener2)
- .onIssueChanges(any(), any());
-
- underTest.broadcastOnIssueChange(oneIssueOnComponent1, singletonList(component1QGChangeEvent));
-
- ArgumentCaptor<Set<ChangedIssue>> changedIssuesCaptor = newSetCaptor();
- inOrder.verify(listener1).onIssueChanges(same(component1QGChangeEvent), changedIssuesCaptor.capture());
- Set<ChangedIssue> changedIssues = changedIssuesCaptor.getValue();
- inOrder.verify(listener2).onIssueChanges(same(component1QGChangeEvent), same(changedIssues));
- inOrder.verifyNoMoreInteractions();
- assertThat(logTester.logs()).hasSize(3);
- assertThat(logTester.logs(LoggerLevel.WARN)).hasSize(1);
- }
-
- @Test
- public void broadcastOnIssueChange_logs_each_listener_call_at_TRACE_level() {
- underTest.broadcastOnIssueChange(oneIssueOnComponent1, singletonList(component1QGChangeEvent));
-
- assertThat(logTester.logs()).hasSize(3);
- List<String> traceLogs = logTester.logs(LoggerLevel.TRACE);
- assertThat(traceLogs).hasSize(3)
- .containsOnly(
- "calling onChange() on listener " + listener1.getClass().getName() + " for events " + component1QGChangeEvent.toString() + "...",
- "calling onChange() on listener " + listener2.getClass().getName() + " for events " + component1QGChangeEvent.toString() + "...",
- "calling onChange() on listener " + listener3.getClass().getName() + " for events " + component1QGChangeEvent.toString() + "...");
- }
-
- @Test
- public void broadcastOnIssueChange_passes_immutable_set_of_ChangedIssues() {
- QGChangeEventListenersImpl underTest = new QGChangeEventListenersImpl(new QGChangeEventListener[] {listener1});
-
- underTest.broadcastOnIssueChange(oneIssueOnComponent1, singletonList(component1QGChangeEvent));
-
- ArgumentCaptor<Set<ChangedIssue>> changedIssuesCaptor = newSetCaptor();
- inOrder.verify(listener1).onIssueChanges(same(component1QGChangeEvent), changedIssuesCaptor.capture());
- assertThat(changedIssuesCaptor.getValue()).isInstanceOf(ImmutableSet.class);
- }
-
- @Test
- public void broadcastOnIssueChange_has_no_effect_when_no_listener() {
- QGChangeEventListenersImpl underTest = new QGChangeEventListenersImpl();
-
- underTest.broadcastOnIssueChange(oneIssueOnComponent1, singletonList(component1QGChangeEvent));
-
- verifyZeroInteractions(listener1, listener2, listener3);
- }
-
- @Test
- public void broadcastOnIssueChange_calls_listener_for_each_component_uuid_with_at_least_one_QGChangeEvent() {
- // component2 has multiple issues
- ComponentDto component2 = newComponentDto(component1Uuid + "2");
- DefaultIssue[] component2Issues = {newDefaultIssue(component2.uuid()), newDefaultIssue(component2.uuid())};
- QGChangeEvent component2QGChangeEvent = newQGChangeEvent(component2);
-
- // component 3 has multiple QGChangeEvent and only one issue
- ComponentDto component3 = newComponentDto(component1Uuid + "3");
- DefaultIssue component3Issue = newDefaultIssue(component3.uuid());
- QGChangeEvent[] component3QGChangeEvents = {newQGChangeEvent(component3), newQGChangeEvent(component3)};
-
- // component 4 has multiple QGChangeEvent and multiples issues
- ComponentDto component4 = newComponentDto(component1Uuid + "4");
- DefaultIssue[] component4Issues = {newDefaultIssue(component4.uuid()), newDefaultIssue(component4.uuid())};
- QGChangeEvent[] component4QGChangeEvents = {newQGChangeEvent(component4), newQGChangeEvent(component4)};
-
- // component 5 has no QGChangeEvent but one issue
- ComponentDto component5 = newComponentDto(component1Uuid + "5");
- DefaultIssue component5Issue = newDefaultIssue(component5.uuid());
-
- List<DefaultIssue> issues = Stream.of(
- Stream.of(component1Issue),
- Arrays.stream(component2Issues),
- Stream.of(component3Issue),
- Arrays.stream(component4Issues),
- Stream.of(component5Issue))
- .flatMap(s -> s)
- .collect(Collectors.toList());
-
- List<DefaultIssue> changedIssues = randomizedList(issues);
- List<QGChangeEvent> qgChangeEvents = Stream.of(
- Stream.of(component1QGChangeEvent),
- Stream.of(component2QGChangeEvent),
- Arrays.stream(component3QGChangeEvents),
- Arrays.stream(component4QGChangeEvents))
- .flatMap(s -> s)
- .collect(Collectors.toList());
-
- underTest.broadcastOnIssueChange(changedIssues, randomizedList(qgChangeEvents));
-
- listeners.forEach(listener -> {
- verifyListenerCalled(listener, component1QGChangeEvent, component1Issue);
- verifyListenerCalled(listener, component2QGChangeEvent, component2Issues);
- Arrays.stream(component3QGChangeEvents)
- .forEach(component3QGChangeEvent -> verifyListenerCalled(listener, component3QGChangeEvent, component3Issue));
- Arrays.stream(component4QGChangeEvents)
- .forEach(component4QGChangeEvent -> verifyListenerCalled(listener, component4QGChangeEvent, component4Issues));
- });
- verifyNoMoreInteractions(listener1, listener2, listener3);
- }
-
- @Test
- public void isNotClosed_returns_true_if_issue_in_one_of_opened_states() {
- DefaultIssue defaultIssue = new DefaultIssue();
- defaultIssue.setStatus(Issue.STATUS_REOPENED);
- defaultIssue.setKey("abc");
- defaultIssue.setType(RuleType.BUG);
- defaultIssue.setSeverity("BLOCKER");
-
- ChangedIssue changedIssue = new ChangedIssueImpl(defaultIssue);
-
- assertThat(changedIssue.isNotClosed()).isTrue();
- }
-
- @Test
- public void isNotClosed_returns_false_if_issue_in_one_of_closed_states() {
- DefaultIssue defaultIssue = new DefaultIssue();
- defaultIssue.setStatus(Issue.STATUS_CONFIRMED);
- defaultIssue.setKey("abc");
- defaultIssue.setType(RuleType.BUG);
- defaultIssue.setSeverity("BLOCKER");
-
- ChangedIssue changedIssue = new ChangedIssueImpl(defaultIssue);
-
- assertThat(changedIssue.isNotClosed()).isFalse();
- }
-
- @Test
- public void test_status_mapping() {
- assertThat(ChangedIssueImpl.statusOf(new DefaultIssue().setStatus(Issue.STATUS_OPEN))).isEqualTo(QGChangeEventListener.Status.OPEN);
- assertThat(ChangedIssueImpl.statusOf(new DefaultIssue().setStatus(Issue.STATUS_REOPENED))).isEqualTo(QGChangeEventListener.Status.REOPENED);
- assertThat(ChangedIssueImpl.statusOf(new DefaultIssue().setStatus(Issue.STATUS_CONFIRMED))).isEqualTo(QGChangeEventListener.Status.CONFIRMED);
- assertThat(ChangedIssueImpl.statusOf(new DefaultIssue().setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_FALSE_POSITIVE)))
- .isEqualTo(QGChangeEventListener.Status.RESOLVED_FP);
- assertThat(ChangedIssueImpl.statusOf(new DefaultIssue().setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_WONT_FIX)))
- .isEqualTo(QGChangeEventListener.Status.RESOLVED_WF);
- assertThat(ChangedIssueImpl.statusOf(new DefaultIssue().setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_FIXED)))
- .isEqualTo(QGChangeEventListener.Status.RESOLVED_FIXED);
- try {
- ChangedIssueImpl.statusOf(new DefaultIssue().setStatus(Issue.STATUS_CLOSED));
- fail("Expected exception");
- } catch (Exception e) {
- assertThat(e).hasMessage("Unexpected status: CLOSED");
- }
- try {
- ChangedIssueImpl.statusOf(new DefaultIssue().setStatus(Issue.STATUS_RESOLVED));
- fail("Expected exception");
- } catch (Exception e) {
- assertThat(e).hasMessage("A resolved issue should have a resolution");
- }
- try {
- ChangedIssueImpl.statusOf(new DefaultIssue().setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_REMOVED));
- fail("Expected exception");
- } catch (Exception e) {
- assertThat(e).hasMessage("Unexpected resolution for a resolved issue: REMOVED");
- }
- }
-
- @Test
- public void test_status_mapping_on_security_hotspots() {
- assertThat(ChangedIssueImpl.statusOf(new DefaultIssue().setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW)))
- .isEqualTo(QGChangeEventListener.Status.TO_REVIEW);
- assertThat(ChangedIssueImpl.statusOf(new DefaultIssue().setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_IN_REVIEW)))
- .isEqualTo(QGChangeEventListener.Status.IN_REVIEW);
- assertThat(ChangedIssueImpl.statusOf(new DefaultIssue().setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)))
- .isEqualTo(QGChangeEventListener.Status.REVIEWED);
- }
-
- private void verifyListenerCalled(QGChangeEventListener listener, QGChangeEvent changeEvent, DefaultIssue... issues) {
- ArgumentCaptor<Set<ChangedIssue>> changedIssuesCaptor = newSetCaptor();
- verify(listener).onIssueChanges(same(changeEvent), changedIssuesCaptor.capture());
- Set<ChangedIssue> changedIssues = changedIssuesCaptor.getValue();
- Tuple[] expected = Arrays.stream(issues)
- .map(issue -> tuple(issue.key(), ChangedIssueImpl.statusOf(issue), issue.type()))
- .toArray(Tuple[]::new);
- assertThat(changedIssues)
- .hasSize(issues.length)
- .extracting(ChangedIssue::getKey, ChangedIssue::getStatus, ChangedIssue::getType)
- .containsOnly(expected);
- }
-
- private static final String[] POSSIBLE_STATUSES = asList(Issue.STATUS_CONFIRMED, Issue.STATUS_REOPENED, Issue.STATUS_RESOLVED).stream().toArray(String[]::new);
- private static int issueIdCounter = 0;
-
- private static DefaultIssue newDefaultIssue(String projectUuid) {
- DefaultIssue defaultIssue = new DefaultIssue();
- defaultIssue.setKey("issue_" + issueIdCounter++);
- defaultIssue.setProjectUuid(projectUuid);
- defaultIssue.setType(RuleType.values()[new Random().nextInt(RuleType.values().length)]);
- defaultIssue.setStatus(POSSIBLE_STATUSES[new Random().nextInt(POSSIBLE_STATUSES.length)]);
- String[] possibleResolutions = possibleResolutions(defaultIssue.getStatus());
- if (possibleResolutions.length > 0) {
- defaultIssue.setResolution(possibleResolutions[new Random().nextInt(possibleResolutions.length)]);
- }
- return defaultIssue;
- }
-
- private static String[] possibleResolutions(String status) {
- switch (status) {
- case Issue.STATUS_RESOLVED:
- return new String[] {Issue.RESOLUTION_FALSE_POSITIVE, Issue.RESOLUTION_WONT_FIX};
- default:
- return new String[0];
- }
- }
-
- private static ComponentDto newComponentDto(String uuid) {
- ComponentDto componentDto = new ComponentDto();
- componentDto.setUuid(uuid);
- return componentDto;
- }
-
- private static QGChangeEvent newQGChangeEvent(ComponentDto componentDto) {
- QGChangeEvent res = mock(QGChangeEvent.class);
- when(res.getProject()).thenReturn(componentDto);
- return res;
- }
-
- private static <T> ArgumentCaptor<Set<T>> newSetCaptor() {
- Class<Set<T>> clazz = (Class<Set<T>>) (Class) Set.class;
- return ArgumentCaptor.forClass(clazz);
- }
-
- private static <T> List<T> randomizedList(List<T> issues) {
- ArrayList<T> res = new ArrayList<>(issues);
- Collections.shuffle(res);
- return ImmutableList.copyOf(res);
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.qualitygate.changeevent;
-
-import java.util.Optional;
-import java.util.Random;
-import java.util.function.Supplier;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.mockito.Mockito;
-import org.sonar.api.config.Configuration;
-import org.sonar.api.measures.Metric;
-import org.sonar.db.component.BranchDto;
-import org.sonar.db.component.BranchType;
-import org.sonar.db.component.ComponentDto;
-import org.sonar.db.component.SnapshotDto;
-import org.sonar.server.qualitygate.EvaluatedQualityGate;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class QGChangeEventTest {
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private ComponentDto project = new ComponentDto()
- .setDbKey("foo")
- .setUuid("bar");
- private BranchDto branch = new BranchDto()
- .setBranchType(BranchType.SHORT)
- .setUuid("bar")
- .setProjectUuid("doh")
- .setMergeBranchUuid("zop");
- private SnapshotDto analysis = new SnapshotDto()
- .setUuid("pto")
- .setCreatedAt(8_999_999_765L);
- private Configuration configuration = Mockito.mock(Configuration.class);
- private Metric.Level previousStatus = Metric.Level.values()[new Random().nextInt(Metric.Level.values().length)];
- private Supplier<Optional<EvaluatedQualityGate>> supplier = Optional::empty;
-
- @Test
- public void constructor_fails_with_NPE_if_project_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("project can't be null");
-
- new QGChangeEvent(null, branch, analysis, configuration, previousStatus, supplier);
- }
-
- @Test
- public void constructor_fails_with_NPE_if_branch_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("branch can't be null");
-
- new QGChangeEvent(project, null, analysis, configuration, previousStatus, supplier);
- }
-
- @Test
- public void constructor_fails_with_NPE_if_analysis_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("analysis can't be null");
-
- new QGChangeEvent(project, branch, null, configuration, previousStatus, supplier);
- }
-
- @Test
- public void constructor_fails_with_NPE_if_configuration_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("projectConfiguration can't be null");
-
- new QGChangeEvent(project, branch, analysis, null, previousStatus, supplier);
- }
-
- @Test
- public void constructor_does_not_fail_with_NPE_if_previousStatus_is_null() {
- new QGChangeEvent(project, branch, analysis, configuration, null, supplier);
- }
-
- @Test
- public void constructor_fails_with_NPE_if_supplier_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("qualityGateSupplier can't be null");
-
- new QGChangeEvent(project, branch, analysis, configuration, previousStatus, null);
- }
-
- @Test
- public void verify_getters() {
- QGChangeEvent underTest = new QGChangeEvent(project, branch, analysis, configuration, previousStatus, supplier);
-
- assertThat(underTest.getProject()).isSameAs(project);
- assertThat(underTest.getBranch()).isSameAs(branch);
- assertThat(underTest.getAnalysis()).isSameAs(analysis);
- assertThat(underTest.getProjectConfiguration()).isSameAs(configuration);
- assertThat(underTest.getPreviousStatus()).contains(previousStatus);
- assertThat(underTest.getQualityGateSupplier()).isSameAs(supplier);
- }
-
- @Test
- public void getPreviousStatus_returns_empty_when_previousStatus_is_null() {
- QGChangeEvent underTest = new QGChangeEvent(project, branch, analysis, configuration, previousStatus, supplier);
-
- assertThat(underTest.getPreviousStatus()).contains(previousStatus);
- }
-
- @Test
- public void overrides_toString() {
- QGChangeEvent underTest = new QGChangeEvent(project, branch, analysis, configuration, previousStatus, supplier);
-
- assertThat(underTest.toString())
- .isEqualTo("QGChangeEvent{project=bar:foo, branch=SHORT:bar:doh:zop, analysis=pto:8999999765" +
- ", projectConfiguration=" + configuration.toString() +
- ", previousStatus=" + previousStatus +
- ", qualityGateSupplier=" + supplier + "}");
-
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.setting;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import java.util.Collections;
-import java.util.Map;
-import org.junit.Before;
-import org.junit.Test;
-import org.sonar.api.config.Configuration;
-import org.sonar.api.config.internal.MapSettings;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.component.ComponentDto;
-import org.sonar.db.property.PropertiesDao;
-import org.sonar.db.property.PropertyDto;
-
-import static java.util.Collections.emptyList;
-import static java.util.Collections.singleton;
-import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
-import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-
-public class ProjectConfigurationLoaderImplTest {
- private DbClient dbClient = mock(DbClient.class);
- private DbSession dbSession = mock(DbSession.class);
- private PropertiesDao propertiesDao = mock(PropertiesDao.class);
- private MapSettings globalSettings = new MapSettings();
- private ProjectConfigurationLoaderImpl underTest = new ProjectConfigurationLoaderImpl(globalSettings, dbClient);
-
- @Before
- public void setUp() throws Exception {
- when(dbClient.openSession(anyBoolean()))
- .thenThrow(new IllegalStateException("ProjectConfigurationLoaderImpl should not open DB session"));
- when(dbClient.propertiesDao()).thenReturn(propertiesDao);
- }
-
- @Test
- public void returns_empty_map_when_no_component() {
- assertThat(underTest.loadProjectConfigurations(dbSession, Collections.emptySet()))
- .isEmpty();
-
- verifyZeroInteractions(propertiesDao);
- }
-
- @Test
- public void return_configuration_with_just_global_settings_when_no_component_settings() {
- String key = randomAlphanumeric(3);
- String value = randomAlphanumeric(4);
- String componentDbKey = randomAlphanumeric(5);
- String componentUuid = randomAlphanumeric(6);
- globalSettings.setProperty(key, value);
- when(propertiesDao.selectProjectProperties(dbSession, componentDbKey))
- .thenReturn(emptyList());
- ComponentDto component = newComponentDto(componentDbKey, componentUuid);
-
- Map<String, Configuration> configurations = underTest.loadProjectConfigurations(dbSession, singleton(component));
-
- assertThat(configurations)
- .containsOnlyKeys(componentUuid);
- assertThat(configurations.get(componentUuid).get(key)).contains(value);
- }
-
- @Test
- public void return_configuration_with_global_settings_and_component_settings() {
- String globalKey = randomAlphanumeric(3);
- String globalValue = randomAlphanumeric(4);
- String componentDbKey = randomAlphanumeric(5);
- String componentUuid = randomAlphanumeric(6);
- String projectPropKey1 = randomAlphanumeric(7);
- String projectPropValue1 = randomAlphanumeric(8);
- String projectPropKey2 = randomAlphanumeric(9);
- String projectPropValue2 = randomAlphanumeric(10);
- globalSettings.setProperty(globalKey, globalValue);
- when(propertiesDao.selectProjectProperties(dbSession, componentDbKey))
- .thenReturn(ImmutableList.of(newPropertyDto(projectPropKey1, projectPropValue1), newPropertyDto(projectPropKey2, projectPropValue2)));
- ComponentDto component = newComponentDto(componentDbKey, componentUuid);
-
- Map<String, Configuration> configurations = underTest.loadProjectConfigurations(dbSession, singleton(component));
-
- assertThat(configurations)
- .containsOnlyKeys(componentUuid);
- assertThat(configurations.get(componentUuid).get(globalKey)).contains(globalValue);
- assertThat(configurations.get(componentUuid).get(projectPropKey1)).contains(projectPropValue1);
- assertThat(configurations.get(componentUuid).get(projectPropKey2)).contains(projectPropValue2);
- }
-
- @Test
- public void return_configuration_with_global_settings_main_branch_settings_and_branch_settings() {
- String globalKey = randomAlphanumeric(3);
- String globalValue = randomAlphanumeric(4);
- String mainBranchDbKey = randomAlphanumeric(5);
- String branchDbKey = mainBranchDbKey + ComponentDto.BRANCH_KEY_SEPARATOR + randomAlphabetic(5);
- String branchUuid = randomAlphanumeric(6);
- String mainBranchPropKey = randomAlphanumeric(7);
- String mainBranchPropValue = randomAlphanumeric(8);
- String branchPropKey = randomAlphanumeric(9);
- String branchPropValue = randomAlphanumeric(10);
- globalSettings.setProperty(globalKey, globalValue);
- when(propertiesDao.selectProjectProperties(dbSession, mainBranchDbKey))
- .thenReturn(ImmutableList.of(newPropertyDto(mainBranchPropKey, mainBranchPropValue)));
- when(propertiesDao.selectProjectProperties(dbSession, branchDbKey))
- .thenReturn(ImmutableList.of(newPropertyDto(branchPropKey, branchPropValue)));
- ComponentDto component = newComponentDto(branchDbKey, branchUuid);
-
- Map<String, Configuration> configurations = underTest.loadProjectConfigurations(dbSession, singleton(component));
-
- assertThat(configurations)
- .containsOnlyKeys(branchUuid);
- assertThat(configurations.get(branchUuid).get(globalKey)).contains(globalValue);
- assertThat(configurations.get(branchUuid).get(mainBranchPropKey)).contains(mainBranchPropValue);
- assertThat(configurations.get(branchUuid).get(branchPropKey)).contains(branchPropValue);
- }
-
- @Test
- public void loads_configuration_of_any_given_component_only_once() {
- String mainBranch1DbKey = randomAlphanumeric(4);
- String mainBranch1Uuid = randomAlphanumeric(5);
- String branch1DbKey = mainBranch1DbKey + ComponentDto.BRANCH_KEY_SEPARATOR + randomAlphabetic(5);
- String branch1Uuid = randomAlphanumeric(6);
- String branch2DbKey = mainBranch1DbKey + ComponentDto.BRANCH_KEY_SEPARATOR + randomAlphabetic(7);
- String branch2Uuid = randomAlphanumeric(8);
- String mainBranch2DbKey = randomAlphanumeric(14);
- String mainBranch2Uuid = randomAlphanumeric(15);
- String branch3DbKey = mainBranch2DbKey + ComponentDto.BRANCH_KEY_SEPARATOR + randomAlphabetic(5);
- String branch3Uuid = randomAlphanumeric(16);
-
- ComponentDto mainBranch1 = newComponentDto(mainBranch1DbKey, mainBranch1Uuid);
- ComponentDto branch1 = newComponentDto(branch1DbKey, branch1Uuid);
- ComponentDto branch2 = newComponentDto(branch2DbKey, branch2Uuid);
- ComponentDto mainBranch2 = newComponentDto(mainBranch2DbKey, mainBranch2Uuid);
- ComponentDto branch3 = newComponentDto(branch3DbKey, branch3Uuid);
-
- underTest.loadProjectConfigurations(dbSession, ImmutableSet.of(mainBranch1, mainBranch2, branch1, branch2, branch3));
-
- verify(propertiesDao, times(1)).selectProjectProperties(dbSession, mainBranch1DbKey);
- verify(propertiesDao, times(1)).selectProjectProperties(dbSession, mainBranch2DbKey);
- verify(propertiesDao, times(1)).selectProjectProperties(dbSession, branch1DbKey);
- verify(propertiesDao, times(1)).selectProjectProperties(dbSession, branch2DbKey);
- verify(propertiesDao, times(1)).selectProjectProperties(dbSession, branch3DbKey);
- verifyNoMoreInteractions(propertiesDao);
- }
-
- private ComponentDto newComponentDto(String componentDbKey, String componentUuid) {
- return new ComponentDto().setDbKey(componentDbKey).setUuid(componentUuid);
- }
-
- private PropertyDto newPropertyDto(String projectKey1, String projectValue1) {
- return new PropertyDto()
- .setKey(projectKey1)
- .setValue(projectValue1);
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.setting;
-
-import org.junit.Test;
-import org.sonar.api.config.GlobalPropertyChangeHandler;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-public class SettingsChangeNotifierTest {
- @Test
- public void onGlobalPropertyChange() {
- GlobalPropertyChangeHandler handler = mock(GlobalPropertyChangeHandler.class);
- SettingsChangeNotifier notifier = new SettingsChangeNotifier(new GlobalPropertyChangeHandler[] {handler});
-
- notifier.onGlobalPropertyChange("foo", "bar");
-
- verify(handler).onChange(argThat(change -> change.getKey().equals("foo") && change.getNewValue().equals("bar")));
- }
-
- @Test
- public void no_handlers() {
- SettingsChangeNotifier notifier = new SettingsChangeNotifier();
-
- assertThat(notifier.changeHandlers).isEmpty();
-
- // does not fail
- notifier.onGlobalPropertyChange("foo", "bar");
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.setting;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import org.sonar.api.config.Configuration;
-import org.sonar.db.DbSession;
-import org.sonar.db.component.ComponentDto;
-
-public class TestProjectConfigurationLoader implements ProjectConfigurationLoader {
-
- private final Configuration config;
-
- public TestProjectConfigurationLoader(Configuration config) {
- this.config = config;
- }
-
- @Override
- public Map<String, Configuration> loadProjectConfigurations(DbSession dbSession, Set<ComponentDto> projects) {
- Map<String, Configuration> map = new HashMap<>();
- for (ComponentDto project : projects) {
- map.put(project.uuid(), config);
- }
- return map;
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.util;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.server.exceptions.BadRequestException;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class BooleanTypeValidationTest {
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private BooleanTypeValidation underTest = new BooleanTypeValidation();
-
- @Test
- public void key() {
- assertThat(underTest.key()).isEqualTo("BOOLEAN");
- }
-
- @Test
- public void not_fail_on_valid_boolean() {
- underTest.validate("true", null);
- underTest.validate("True", null);
- underTest.validate("false", null);
- underTest.validate("FALSE", null);
- }
-
- @Test
- public void fail_on_invalid_boolean() {
- expectedException.expect(BadRequestException.class);
- expectedException.expectMessage("Value 'abc' must be one of \"true\" or \"false\".");
-
- underTest.validate("abc", null);
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.util;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.server.exceptions.BadRequestException;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class FloatTypeValidationTest {
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private FloatTypeValidation validation = new FloatTypeValidation();
-
- @Test
- public void key() {
- assertThat(validation.key()).isEqualTo("FLOAT");
- }
-
- @Test
- public void not_fail_on_valid_float() {
- validation.validate("10.2", null);
- validation.validate("10", null);
- validation.validate("-10.3", null);
- }
-
- @Test
- public void fail_on_invalid_float() {
- expectedException.expect(BadRequestException.class);
- expectedException.expectMessage("Value 'abc' must be an floating point number.");
-
- validation.validate("abc", null);
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.util;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.utils.System2;
-import org.sonar.db.DbTester;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.server.util.GlobalLockManager.DEFAULT_LOCK_DURATION_SECONDS;
-
-public class GlobalLockManagerTest {
-
- private final System2 system2 = mock(System2.class);
-
- @Rule
- public final DbTester dbTester = DbTester.create(system2);
-
- private final GlobalLockManager underTest = new GlobalLockManager(dbTester.getDbClient());
-
- @Test
- public void tryLock_succeeds_when_created_for_the_first_time() {
- assertThat(underTest.tryLock("newName")).isTrue();
- }
-
- @Test
- public void tryLock_fails_when_previous_lock_is_too_recent() {
- String name = "newName";
- assertThat(underTest.tryLock(name)).isTrue();
- assertThat(underTest.tryLock(name)).isFalse();
- }
-
- @Test
- public void tryLock_succeeds_when_previous_lock_is_old_enough() {
- String name = "newName";
- long firstLock = 0;
- long longEnoughAfterFirstLock = firstLock + DEFAULT_LOCK_DURATION_SECONDS * 1000;
- long notLongEnoughAfterFirstLock = longEnoughAfterFirstLock - 1;
-
- when(system2.now()).thenReturn(firstLock);
- assertThat(underTest.tryLock(name)).isTrue();
-
- when(system2.now()).thenReturn(notLongEnoughAfterFirstLock);
- assertThat(underTest.tryLock(name)).isFalse();
-
- when(system2.now()).thenReturn(longEnoughAfterFirstLock);
- assertThat(underTest.tryLock(name)).isTrue();
- }
-
- @Test
- public void locks_with_different_name_are_independent() {
- assertThat(underTest.tryLock("newName1")).isTrue();
- assertThat(underTest.tryLock("newName2")).isTrue();
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.util;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.server.exceptions.BadRequestException;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class IntegerTypeValidationTest {
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private IntegerTypeValidation validation = new IntegerTypeValidation();
-
- @Test
- public void key() {
- assertThat(validation.key()).isEqualTo("INTEGER");
- }
-
- @Test
- public void not_fail_on_valid_integer() {
- validation.validate("10", null);
- validation.validate("-10", null);
- }
-
- @Test
- public void fail_on_string() {
- expectedException.expect(BadRequestException.class);
- expectedException.expectMessage("Value 'abc' must be an integer.");
-
- validation.validate("abc", null);
- }
-
- @Test
- public void fail_on_float() {
- expectedException.expect(BadRequestException.class);
- expectedException.expectMessage("Value '10.1' must be an integer.");
-
- validation.validate("10.1", null);
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.util;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.api.PropertyType;
-import org.sonar.server.exceptions.BadRequestException;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class LongTypeValidationTest {
-
- LongTypeValidation underTest = new LongTypeValidation();
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- @Test
- public void key_is_long_type_name() {
- assertThat(underTest.key()).isEqualTo(PropertyType.LONG.name());
- }
-
- @Test
- public void do_not_fail_with_long_values() {
- underTest.validate("1984", null);
- underTest.validate("-1984", null);
- }
-
- @Test
- public void fail_when_float() {
- expectedException.expect(BadRequestException.class);
- expectedException.expectMessage("Value '3.14' must be a long.");
-
- underTest.validate("3.14", null);
- }
-
- @Test
- public void fail_when_string() {
- expectedException.expect(BadRequestException.class);
- expectedException.expectMessage("Value 'original string' must be a long.");
-
- underTest.validate("original string", null);
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.util;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.server.exceptions.BadRequestException;
-
-import static com.google.common.collect.Lists.newArrayList;
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class StringListTypeValidationTest {
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private StringListTypeValidation validation = new StringListTypeValidation();
-
- @Test
- public void key() {
- assertThat(validation.key()).isEqualTo("SINGLE_SELECT_LIST");
- }
-
- @Test
- public void not_fail_on_valid_option() {
- validation.validate("a", newArrayList("a", "b", "c"));
- validation.validate("a", null);
- }
-
- @Test
- public void fail_on_invalid_option() {
- expectedException.expect(BadRequestException.class);
- expectedException.expectMessage("Value 'abc' must be one of : a, b, c.");
-
- validation.validate("abc", newArrayList("a", "b", "c"));
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.util;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class StringTypeValidationTest {
-
- StringTypeValidation validation;
-
- @Before
- public void setUp() {
- validation = new StringTypeValidation();
- }
-
- @Test
- public void key() {
- assertThat(validation.key()).isEqualTo("STRING");
- }
-
- @Test
- public void not_fail_on_valid_string() {
- validation.validate("10", null);
- validation.validate("abc", null);
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.util;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class TextTypeValidationTest {
-
- TextTypeValidation validation;
-
- @Before
- public void setUp() {
- validation = new TextTypeValidation();
- }
-
- @Test
- public void key() {
- assertThat(validation.key()).isEqualTo("TEXT");
- }
-
- @Test
- public void not_fail_on_valid_text() {
- validation.validate("10", null);
- validation.validate("abc", null);
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.util;
-
-import org.junit.Test;
-import org.sonar.core.platform.ComponentContainer;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class TypeValidationModuleTest {
- @Test
- public void verify_count_of_added_components() {
- ComponentContainer container = new ComponentContainer();
- new TypeValidationModule().configure(container);
- assertThat(container.size()).isEqualTo(11);
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.util;
-
-import org.junit.Test;
-import org.sonar.server.exceptions.BadRequestException;
-
-import static com.google.common.collect.Lists.newArrayList;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-public class TypeValidationsTest {
-
- @Test
- public void validate() {
- TypeValidation fakeTypeValidation = mock(TypeValidation.class);
- when(fakeTypeValidation.key()).thenReturn("Fake");
-
- TypeValidations typeValidations = new TypeValidations(newArrayList(fakeTypeValidation));
- typeValidations.validate("10", "Fake", newArrayList("a"));
-
- verify(fakeTypeValidation).validate("10", newArrayList("a"));
- }
-
- @Test
- public void validate__multiple_values() {
- TypeValidation fakeTypeValidation = mock(TypeValidation.class);
- when(fakeTypeValidation.key()).thenReturn("Fake");
-
- TypeValidations typeValidations = new TypeValidations(newArrayList(fakeTypeValidation));
- typeValidations.validate(newArrayList("10", "11", "12"), "Fake", newArrayList("11"));
-
- verify(fakeTypeValidation).validate("10", newArrayList("11"));
- }
-
- @Test
- public void fail_on_unknown_type() {
- TypeValidation fakeTypeValidation = mock(TypeValidation.class);
- when(fakeTypeValidation.key()).thenReturn("Fake");
-
- try {
- TypeValidations typeValidations = new TypeValidations(newArrayList(fakeTypeValidation));
- typeValidations.validate("10", "Unknown", null);
- fail();
- } catch (Exception e) {
- assertThat(e).isInstanceOf(BadRequestException.class);
- BadRequestException badRequestException = (BadRequestException) e;
- assertThat(badRequestException.getMessage()).isEqualTo("Type 'Unknown' is not valid.");
- }
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.util;
-
-import java.util.Arrays;
-
-public class TypeValidationsTesting {
- private TypeValidationsTesting() {
- // utility class
- }
-
- public static TypeValidations newFullTypeValidations() {
- return new TypeValidations(Arrays.asList(
- new BooleanTypeValidation(),
- new IntegerTypeValidation(),
- new LongTypeValidation(),
- new FloatTypeValidation(),
- new StringTypeValidation(),
- new StringListTypeValidation(),
- new MetricLevelTypeValidation()
- ));
- }
-}
+++ /dev/null
-# see README.txt
-!*/target/
-*/target/classes/
-*/target/maven-archiver/
-*/target/maven-status/
-*/target/test-*/
-
+++ /dev/null
-This directory provides the fake plugins used by tests. These tests are rarely changed, so generated
-artifacts are stored in Git repository (see .gitignore). It avoids from adding unnecessary modules
-to build.
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.sonarsource.sonarqube.tests</groupId>
- <artifactId>fake-report-plugin</artifactId>
- <version>0.1-SNAPSHOT</version>
- <packaging>sonar-plugin</packaging>
- <name>Fake Report Plugin</name>
- <description>Fake Report Plugin</description>
-
- <dependencies>
- <dependency>
- <groupId>org.codehaus.sonar</groupId>
- <artifactId>sonar-plugin-api</artifactId>
- <version>4.5.4</version>
- <scope>provided</scope>
- </dependency>
- </dependencies>
- <build>
- <sourceDirectory>src</sourceDirectory>
- <plugins>
- <plugin>
- <groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId>
- <artifactId>sonar-packaging-maven-plugin</artifactId>
- <version>1.15</version>
- <extensions>true</extensions>
- <configuration>
- <pluginKey>report</pluginKey>
- <pluginClass>BasePlugin</pluginClass>
- </configuration>
- </plugin>
- </plugins>
- </build>
-
-</project>
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.
- */
-import org.sonar.api.Plugin;
-
-import java.util.Collections;
-import java.util.List;
-
-public class BasePlugin extends Plugin {
-
- public void define(Plugin.Context context) {
-
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.plugins.testbase.api;
-
-public class BaseApi {
- public void doNothing() {
- }
-}
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.sonarsource.sonarqube.tests</groupId>
- <artifactId>fake-sqale-plugin</artifactId>
- <version>0.1-SNAPSHOT</version>
- <packaging>sonar-plugin</packaging>
- <name>Fake SQALE Plugin</name>
- <description>Fake SQALE Plugin</description>
-
- <dependencies>
- <dependency>
- <groupId>org.codehaus.sonar</groupId>
- <artifactId>sonar-plugin-api</artifactId>
- <version>4.5.4</version>
- <scope>provided</scope>
- </dependency>
- </dependencies>
- <build>
- <sourceDirectory>src</sourceDirectory>
- <plugins>
- <plugin>
- <groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId>
- <artifactId>sonar-packaging-maven-plugin</artifactId>
- <version>1.15</version>
- <extensions>true</extensions>
- <configuration>
- <pluginKey>sqale</pluginKey>
- <pluginClass>BasePlugin</pluginClass>
- </configuration>
- </plugin>
- </plugins>
- </build>
-
-</project>
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.
- */
-import org.sonar.api.Plugin;
-
-import java.util.Collections;
-import java.util.List;
-
-public class BasePlugin extends Plugin {
-
- public void define(Plugin.Context context) {
-
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.plugins.testbase.api;
-
-public class BaseApi {
- public void doNothing() {
- }
-}
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.sonarsource.sonarqube.tests</groupId>
- <artifactId>fake-views-plugin</artifactId>
- <version>0.1-SNAPSHOT</version>
- <packaging>sonar-plugin</packaging>
- <name>Fake Views Plugin</name>
- <description>Fake Views Plugin</description>
-
- <dependencies>
- <dependency>
- <groupId>org.codehaus.sonar</groupId>
- <artifactId>sonar-plugin-api</artifactId>
- <version>4.5.4</version>
- <scope>provided</scope>
- </dependency>
- </dependencies>
- <build>
- <sourceDirectory>src</sourceDirectory>
- <plugins>
- <plugin>
- <groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId>
- <artifactId>sonar-packaging-maven-plugin</artifactId>
- <version>1.15</version>
- <extensions>true</extensions>
- <configuration>
- <pluginKey>views</pluginKey>
- <pluginClass>BasePlugin</pluginClass>
- </configuration>
- </plugin>
- </plugins>
- </build>
-
-</project>
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.
- */
-import org.sonar.api.Plugin;
-
-import java.util.Collections;
-import java.util.List;
-
-public class BasePlugin extends Plugin {
-
- public void define(Plugin.Context context) {
-
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.plugins.testbase.api;
-
-public class BaseApi {
- public void doNothing() {
- }
-}
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.sonarsource.sonarqube.tests</groupId>
- <artifactId>parent</artifactId>
- <version>0.1-SNAPSHOT</version>
- <packaging>pom</packaging>
- <modules>
- <module>test-base-plugin</module>
- <module>test-base-plugin-v2</module>
- <module>test-core-plugin</module>
- <module>test-extend-plugin</module>
- <module>test-libs-plugin</module>
- <module>test-require-plugin</module>
- <module>test-requirenew-plugin</module>
- <module>fake-report-plugin</module>
- <module>fake-sqale-plugin</module>
- <module>fake-views-plugin</module>
- </modules>
-
-</project>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.sonarsource.sonarqube.tests</groupId>
- <artifactId>test-base-plugin</artifactId>
- <version>0.2-SNAPSHOT</version>
- <packaging>sonar-plugin</packaging>
- <name>Base Plugin</name>
- <description>Simple standalone plugin. Used by other fake plugins.</description>
-
- <dependencies>
- <dependency>
- <groupId>org.codehaus.sonar</groupId>
- <artifactId>sonar-plugin-api</artifactId>
- <version>4.5.4</version>
- <scope>provided</scope>
- </dependency>
- </dependencies>
- <build>
- <sourceDirectory>src</sourceDirectory>
- <plugins>
- <plugin>
- <groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId>
- <artifactId>sonar-packaging-maven-plugin</artifactId>
- <version>1.15</version>
- <extensions>true</extensions>
- <configuration>
- <pluginKey>testbase</pluginKey>
- <pluginClass>BasePlugin</pluginClass>
- </configuration>
- </plugin>
- </plugins>
- </build>
-
-</project>
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.
- */
-import org.sonar.api.Plugin;
-
-import java.util.Collections;
-import java.util.List;
-
-public class BasePlugin extends Plugin {
-
- public void define(Plugin.Context context) {
-
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.plugins.testbase.api;
-
-public class BaseApi {
- public void doNothing() {
- }
-}
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.sonarsource.sonarqube.tests</groupId>
- <artifactId>test-base-plugin</artifactId>
- <version>0.1-SNAPSHOT</version>
- <packaging>sonar-plugin</packaging>
- <name>Base Plugin</name>
- <description>Simple standalone plugin. Used by other fake plugins.</description>
-
- <dependencies>
- <dependency>
- <groupId>org.codehaus.sonar</groupId>
- <artifactId>sonar-plugin-api</artifactId>
- <version>4.5.4</version>
- <scope>provided</scope>
- </dependency>
- </dependencies>
- <build>
- <sourceDirectory>src</sourceDirectory>
- <plugins>
- <plugin>
- <groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId>
- <artifactId>sonar-packaging-maven-plugin</artifactId>
- <version>1.15</version>
- <extensions>true</extensions>
- <configuration>
- <pluginKey>testbase</pluginKey>
- <pluginClass>BasePlugin</pluginClass>
- </configuration>
- </plugin>
- </plugins>
- </build>
-
-</project>
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.
- */
-import org.sonar.api.Plugin;
-
-import java.util.Collections;
-import java.util.List;
-
-public class BasePlugin extends Plugin {
-
- public void define(Plugin.Context context) {
-
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.plugins.testbase.api;
-
-public class BaseApi {
- public void doNothing() {
- }
-}
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.sonarsource.sonarqube.tests</groupId>
- <artifactId>test-extend-plugin</artifactId>
- <version>0.1-SNAPSHOT</version>
- <packaging>sonar-plugin</packaging>
- <name>Test Extend Plugin</name>
- <description>Fake plugin that extends the plugin with key "base"</description>
-
- <dependencies>
- <dependency>
- <groupId>org.codehaus.sonar</groupId>
- <artifactId>sonar-plugin-api</artifactId>
- <version>4.5.4</version>
- <scope>provided</scope>
- </dependency>
- </dependencies>
- <build>
- <sourceDirectory>src</sourceDirectory>
- <plugins>
- <plugin>
- <groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId>
- <artifactId>sonar-packaging-maven-plugin</artifactId>
- <version>1.15</version>
- <extensions>true</extensions>
- <configuration>
- <pluginKey>testextend</pluginKey>
- <pluginClass>ExtendPlugin</pluginClass>
- <basePlugin>testbase</basePlugin>
- </configuration>
- </plugin>
- </plugins>
- </build>
-
-</project>
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.
- */
-import org.sonar.api.Plugin;
-
-import java.util.Collections;
-import java.util.List;
-
-public class ExtendPlugin extends Plugin {
-
- public void define(Plugin.Context context) {
-
- }
-}
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.sonarsource.sonarqube.tests</groupId>
- <artifactId>test-libs-plugin</artifactId>
- <version>0.1-SNAPSHOT</version>
- <packaging>sonar-plugin</packaging>
- <name>Test Libs Plugin</name>
- <description>Fake plugin that embeds some libraries</description>
-
- <dependencies>
- <!-- embedded libs. Chosen because small ! -->
- <dependency>
- <groupId>commons-email</groupId>
- <artifactId>commons-email</artifactId>
- <version>20030310.165926</version>
- </dependency>
- <dependency>
- <groupId>commons-daemon</groupId>
- <artifactId>commons-daemon</artifactId>
- <version>1.0.15</version>
- </dependency>
-
- <dependency>
- <groupId>org.codehaus.sonar</groupId>
- <artifactId>sonar-plugin-api</artifactId>
- <version>4.5.4</version>
- <scope>provided</scope>
- </dependency>
- </dependencies>
-
- <build>
- <sourceDirectory>src</sourceDirectory>
- <plugins>
- <plugin>
- <groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId>
- <artifactId>sonar-packaging-maven-plugin</artifactId>
- <version>1.15</version>
- <extensions>true</extensions>
- <configuration>
- <pluginKey>testlibs</pluginKey>
- <pluginClass>LibsPlugin</pluginClass>
- </configuration>
- </plugin>
- </plugins>
- </build>
-
-</project>
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.
- */
-import org.sonar.api.Plugin;
-
-import java.util.Collections;
-import java.util.List;
-
-public class LibsPlugin extends Plugin {
-
- public void define(Plugin.Context context) {
-
- }
-}
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.sonarsource.sonarqube.tests</groupId>
- <artifactId>test-require-plugin</artifactId>
- <version>0.1-SNAPSHOT</version>
- <packaging>sonar-plugin</packaging>
- <name>Test Require Plugin</name>
- <description>This fake plugin depends on test-base-plugin</description>
-
- <dependencies>
- <dependency>
- <groupId>org.codehaus.sonar</groupId>
- <artifactId>sonar-plugin-api</artifactId>
- <version>4.5.4</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.sonarsource.sonarqube.tests</groupId>
- <artifactId>test-base-plugin</artifactId>
- <version>0.1-SNAPSHOT</version>
- <type>sonar-plugin</type>
- <scope>provided</scope>
- </dependency>
- </dependencies>
- <build>
- <sourceDirectory>src</sourceDirectory>
- <plugins>
- <plugin>
- <groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId>
- <artifactId>sonar-packaging-maven-plugin</artifactId>
- <version>1.15</version>
- <extensions>true</extensions>
- <configuration>
- <pluginKey>testrequire</pluginKey>
- <pluginClass>RequirePlugin</pluginClass>
- <requirePlugins>testbase:0.1</requirePlugins>
- </configuration>
- </plugin>
- </plugins>
- </build>
-
-</project>
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.
- */
-import org.sonar.api.Plugin;
-
-import java.util.Collections;
-import java.util.List;
-
-public class RequirePlugin extends Plugin {
-
- public RequirePlugin() {
- // call a class that is in the api published by the base plugin
- new org.sonar.plugins.testbase.api.BaseApi().doNothing();
- }
-
- public void define(Plugin.Context context) {
-
- }
-}
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.sonarsource.sonarqube.tests</groupId>
- <artifactId>test-requirenew-plugin</artifactId>
- <version>0.1-SNAPSHOT</version>
- <packaging>sonar-plugin</packaging>
- <name>Test Require New Plugin</name>
- <description>This fake plugin requires a version of test-base-plugin that is not installed</description>
-
- <dependencies>
- <dependency>
- <groupId>org.codehaus.sonar</groupId>
- <artifactId>sonar-plugin-api</artifactId>
- <version>4.5.4</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.sonarsource.sonarqube.tests</groupId>
- <artifactId>test-base-plugin</artifactId>
- <version>0.1-SNAPSHOT</version>
- <type>sonar-plugin</type>
- <scope>provided</scope>
- </dependency>
- </dependencies>
- <build>
- <sourceDirectory>src</sourceDirectory>
- <plugins>
- <plugin>
- <groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId>
- <artifactId>sonar-packaging-maven-plugin</artifactId>
- <version>1.15</version>
- <extensions>true</extensions>
- <configuration>
- <pluginKey>testrequire</pluginKey>
- <pluginClass>RequirePlugin</pluginClass>
- <requirePlugins>testbase:0.2</requirePlugins>
- </configuration>
- </plugin>
- </plugins>
- </build>
-
-</project>
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.
- */
-import org.sonar.api.Plugin;
-
-import java.util.Collections;
-import java.util.List;
-
-public class RequirePlugin extends Plugin {
-
- public RequirePlugin() {
- // call a class that is in the api published by the base plugin
- new org.sonar.plugins.testbase.api.BaseApi().doNothing();
- }
-
- public void define(Plugin.Context context) {
-
- }
-
-}
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8" ?>
-<configuration debug="false">
- <contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator"/>
-
- <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
- <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
- <pattern>
- %d{yyyy.MM.dd HH:mm:ss} %-5level %msg%n
- </pattern>
- </encoder>
- </appender>
-
- <root>
- <level value="INFO"/>
- <appender-ref ref="CONSOLE"/>
- </root>
-
- <logger name="ch.qos.logback">
- <level value="WARN"/>
- </logger>
-
- <logger name="okhttp3.mockwebserver">
- <level value="WARN"/>
- </logger>
-
-</configuration>
compile project(':server:sonar-db-migration')
compile project(':server:sonar-process')
compile project(':server:sonar-server-common')
+ compile project(':server:sonar-webserver-api')
compile project(':server:sonar-webserver-auth')
- compile project(':server:sonar-webserver-common')
compile project(':server:sonar-webserver-es')
compile project(':sonar-core')
compile project(':sonar-duplications')
testCompile 'org.subethamail:subethasmtp'
testCompile project(':server:sonar-db-testing')
testCompile project(path: ":server:sonar-server-common", configuration: "tests")
+ testCompile project(path: ":server:sonar-webserver-api", configuration: "tests")
testCompile project(path: ":server:sonar-webserver-auth", configuration: "tests")
- testCompile project(path: ":server:sonar-webserver-common", configuration: "tests")
testCompile project(path: ":server:sonar-webserver-es", configuration: "tests")
testCompile project(path: ":server:sonar-webserver-ws", configuration: "tests")
testCompile project(':sonar-testing-harness')
testCompile 'org.mockito:mockito-core'
testCompile project(':server:sonar-db-testing')
testCompile project(path: ":server:sonar-server-common", configuration: "tests")
+ testCompile project(path: ":server:sonar-webserver-api", configuration: "tests")
testCompile project(path: ":server:sonar-webserver-auth", configuration: "tests")
- testCompile project(path: ":server:sonar-webserver-common", configuration: "tests")
testCompile project(path: ":server:sonar-webserver-es", configuration: "tests")
testCompile project(path: ":server:sonar-webserver-ws", configuration: "tests")
testCompile project(':sonar-testing-harness')
compile 'com.google.guava:guava'
compile project(':sonar-core')
- compile project(':server:sonar-webserver-common')
+ compile project(':server:sonar-webserver-api')
compile project(path: ':sonar-plugin-api', configuration: 'shadow')
compile project(':sonar-plugin-api-impl')
compile project(':sonar-ws')
testCompile 'org.eclipse.jetty:jetty-servlet'
testCompile project(':server:sonar-db-testing')
testCompile project(path: ":server:sonar-server-common", configuration: "tests")
+ testCompile project(path: ":server:sonar-webserver-api", configuration: "tests")
testCompile project(path: ":server:sonar-webserver-auth", configuration: "tests")
- testCompile project(path: ":server:sonar-webserver-common", configuration: "tests")
testCompile project(path: ":server:sonar-webserver-es", configuration: "tests")
testCompile project(':sonar-testing-harness')
}
include 'server:sonar-vsts'
include 'server:sonar-web'
include 'server:sonar-webserver'
+include 'server:sonar-webserver-api'
include 'server:sonar-webserver-auth'
-include 'server:sonar-webserver-common'
include 'server:sonar-webserver-core'
include 'server:sonar-webserver-es'
include 'server:sonar-webserver-webapi'