]> source.dussan.org Git - sonarqube.git/commitdiff
rename sonar-webserver-common to sonar-webserver-api
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Wed, 14 Aug 2019 10:46:39 +0000 (12:46 +0200)
committerSonarTech <sonartech@sonarsource.com>
Wed, 14 Aug 2019 18:21:16 +0000 (20:21 +0200)
304 files changed:
server/sonar-webserver-api/build.gradle [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/app/ProcessCommandWrapper.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/app/ProcessCommandWrapperImpl.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/app/RestartFlagHolder.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/app/RestartFlagHolderImpl.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/app/package-info.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/branch/BranchFeature.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/branch/BranchFeatureExtension.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/branch/BranchFeatureProxy.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/branch/BranchFeatureProxyImpl.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/branch/package-info.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/ce/http/CeHttpClient.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/ce/http/package-info.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/exceptions/BadRequestException.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/exceptions/ForbiddenException.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/exceptions/Message.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/exceptions/NotFoundException.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/exceptions/ServerException.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/exceptions/UnauthorizedException.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/exceptions/package-info.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/health/ClusterHealth.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/health/Health.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/health/HealthChecker.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/health/package-info.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/platform/ClusterFeature.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/platform/Platform.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/platform/SystemInfoWriter.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/platform/package-info.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/InstalledPlugin.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/PluginDownloader.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/PluginFileSystem.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/PluginUninstaller.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/ServerPluginJarExploder.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/ServerPluginRepository.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/UpdateCenterClient.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/UpdateCenterMatrixFactory.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/WebServerExtensionInstaller.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/edition/EditionBundledPlugins.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/edition/package-info.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/package-info.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/project/ProjectLifeCycleListener.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/project/ProjectLifeCycleListeners.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/project/ProjectLifeCycleListenersImpl.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/project/RekeyedProject.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/project/Visibility.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/project/package-info.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/qualitygate/ProjectsInWarning.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEvent.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListener.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListeners.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImpl.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/qualitygate/changeevent/Trigger.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/qualitygate/changeevent/package-info.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/qualityprofile/BulkChangeResult.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/qualityprofile/QProfileRules.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/qualityprofile/RuleActivation.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/qualityprofile/package-info.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/setting/ProjectConfigurationLoader.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/setting/ProjectConfigurationLoaderImpl.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/setting/SettingsChangeNotifier.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/setting/package-info.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/telemetry/LicenseReader.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/telemetry/package-info.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/util/BooleanTypeValidation.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/util/FloatTypeValidation.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/util/GlobalLockManager.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/util/IntegerTypeValidation.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/util/LongTypeValidation.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/util/MetricLevelTypeValidation.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/util/StringListTypeValidation.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/util/StringTypeValidation.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/util/TextTypeValidation.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/util/TypeValidation.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/util/TypeValidationModule.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/util/TypeValidations.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/util/Validation.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/util/package-info.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/app/ProcessCommandWrapperImplTest.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/branch/BranchFeatureProxyImplTest.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/branch/BranchFeatureRule.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/exceptions/BadRequestExceptionTest.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/exceptions/MessageTest.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/exceptions/ServerExceptionTest.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/health/TestStandaloneHealthChecker.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/PluginDownloaderTest.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/PluginFileSystemTest.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/PluginUninstallerTest.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/ServerPluginJarExploderTest.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/ServerPluginRepositoryTest.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/TestPluginA.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/TestProjectUtils.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/UpdateCenterClientTest.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/UpdateCenterMatrixFactoryTest.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/UpdateCenterServlet.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/edition/EditionBundledPluginsTest.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/project/ProjectLifeCycleListenersImplTest.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/project/RekeyedProjectTest.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImplTest.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/qualitygate/changeevent/QGChangeEventTest.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/setting/ProjectConfigurationLoaderImplTest.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/setting/SettingsChangeNotifierTest.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/setting/TestProjectConfigurationLoader.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/util/BooleanTypeValidationTest.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/util/FloatTypeValidationTest.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/util/GlobalLockManagerTest.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/util/IntegerTypeValidationTest.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/util/LongTypeValidationTest.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/util/StringListTypeValidationTest.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/util/StringTypeValidationTest.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/util/TextTypeValidationTest.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/util/TypeValidationModuleTest.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/util/TypeValidationsTest.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/java/org/sonar/server/util/TypeValidationsTesting.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/.gitignore [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/README.txt [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/fake-report-plugin/pom.xml [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/fake-report-plugin/src/BasePlugin.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/fake-report-plugin/src/org/sonar/plugins/testbase/api/BaseApi.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/fake-report-plugin/target/fake-report-plugin-0.1-SNAPSHOT.jar [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/fake-sqale-plugin/pom.xml [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/fake-sqale-plugin/src/BasePlugin.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/fake-sqale-plugin/src/org/sonar/plugins/testbase/api/BaseApi.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/fake-sqale-plugin/target/fake-sqale-plugin-0.1-SNAPSHOT.jar [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/fake-views-plugin/pom.xml [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/fake-views-plugin/src/BasePlugin.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/fake-views-plugin/src/org/sonar/plugins/testbase/api/BaseApi.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/fake-views-plugin/target/fake-views-plugin-0.1-SNAPSHOT.jar [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/pom.xml [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/test-base-plugin-v2/pom.xml [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/test-base-plugin-v2/src/BasePlugin.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/test-base-plugin-v2/src/org/sonar/plugins/testbase/api/BaseApi.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/test-base-plugin-v2/target/test-base-plugin-0.2-SNAPSHOT.jar [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/test-base-plugin/pom.xml [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/test-base-plugin/src/BasePlugin.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/test-base-plugin/src/org/sonar/plugins/testbase/api/BaseApi.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/test-base-plugin/target/test-base-plugin-0.1-SNAPSHOT.jar [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/test-extend-plugin/pom.xml [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/test-extend-plugin/src/ExtendPlugin.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/test-extend-plugin/target/test-extend-plugin-0.1-SNAPSHOT.jar [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/test-libs-plugin/pom.xml [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/test-libs-plugin/src/LibsPlugin.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/test-libs-plugin/target/test-libs-plugin-0.1-SNAPSHOT.jar [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/test-require-plugin/pom.xml [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/test-require-plugin/src/RequirePlugin.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/test-require-plugin/target/test-require-plugin-0.1-SNAPSHOT.jar [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/test-requirenew-plugin/pom.xml [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/test-requirenew-plugin/src/RequirePlugin.java [new file with mode: 0644]
server/sonar-webserver-api/src/test/projects/test-requirenew-plugin/target/test-requirenew-plugin-0.1-SNAPSHOT.jar [new file with mode: 0644]
server/sonar-webserver-api/src/test/resources/logback-test.xml [new file with mode: 0644]
server/sonar-webserver-auth/build.gradle
server/sonar-webserver-common/build.gradle [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/app/ProcessCommandWrapper.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/app/ProcessCommandWrapperImpl.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/app/RestartFlagHolder.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/app/RestartFlagHolderImpl.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/app/package-info.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/branch/BranchFeature.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/branch/BranchFeatureExtension.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/branch/BranchFeatureProxy.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/branch/BranchFeatureProxyImpl.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/branch/package-info.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/ce/http/CeHttpClient.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/ce/http/package-info.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/exceptions/BadRequestException.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/exceptions/ForbiddenException.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/exceptions/Message.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/exceptions/NotFoundException.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/exceptions/ServerException.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/exceptions/UnauthorizedException.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/exceptions/package-info.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/health/ClusterHealth.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/health/Health.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/health/HealthChecker.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/health/package-info.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/platform/ClusterFeature.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/platform/Platform.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/platform/SystemInfoWriter.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/platform/package-info.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/InstalledPlugin.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/PluginDownloader.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/PluginFileSystem.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/PluginUninstaller.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/ServerPluginJarExploder.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/ServerPluginRepository.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/UpdateCenterClient.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/UpdateCenterMatrixFactory.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/WebServerExtensionInstaller.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/edition/EditionBundledPlugins.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/edition/package-info.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/package-info.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/project/ProjectLifeCycleListener.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/project/ProjectLifeCycleListeners.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/project/ProjectLifeCycleListenersImpl.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/project/RekeyedProject.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/project/Visibility.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/project/package-info.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/qualitygate/ProjectsInWarning.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEvent.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListener.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListeners.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImpl.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/qualitygate/changeevent/Trigger.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/qualitygate/changeevent/package-info.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/qualityprofile/BulkChangeResult.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/qualityprofile/QProfileRules.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/qualityprofile/RuleActivation.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/qualityprofile/package-info.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/setting/ProjectConfigurationLoader.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/setting/ProjectConfigurationLoaderImpl.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/setting/SettingsChangeNotifier.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/setting/package-info.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/telemetry/LicenseReader.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/telemetry/package-info.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/util/BooleanTypeValidation.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/util/FloatTypeValidation.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/util/GlobalLockManager.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/util/IntegerTypeValidation.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/util/LongTypeValidation.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/util/MetricLevelTypeValidation.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/util/StringListTypeValidation.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/util/StringTypeValidation.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/util/TextTypeValidation.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/util/TypeValidation.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/util/TypeValidationModule.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/util/TypeValidations.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/util/Validation.java [deleted file]
server/sonar-webserver-common/src/main/java/org/sonar/server/util/package-info.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/app/ProcessCommandWrapperImplTest.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/branch/BranchFeatureProxyImplTest.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/branch/BranchFeatureRule.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/exceptions/BadRequestExceptionTest.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/exceptions/MessageTest.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/exceptions/ServerExceptionTest.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/health/TestStandaloneHealthChecker.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/PluginDownloaderTest.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/PluginFileSystemTest.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/PluginUninstallerTest.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/ServerPluginJarExploderTest.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/ServerPluginRepositoryTest.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/TestPluginA.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/TestProjectUtils.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/UpdateCenterClientTest.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/UpdateCenterMatrixFactoryTest.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/UpdateCenterServlet.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/edition/EditionBundledPluginsTest.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/project/ProjectLifeCycleListenersImplTest.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/project/RekeyedProjectTest.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImplTest.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/qualitygate/changeevent/QGChangeEventTest.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/setting/ProjectConfigurationLoaderImplTest.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/setting/SettingsChangeNotifierTest.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/setting/TestProjectConfigurationLoader.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/util/BooleanTypeValidationTest.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/util/FloatTypeValidationTest.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/util/GlobalLockManagerTest.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/util/IntegerTypeValidationTest.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/util/LongTypeValidationTest.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/util/StringListTypeValidationTest.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/util/StringTypeValidationTest.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/util/TextTypeValidationTest.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/util/TypeValidationModuleTest.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/util/TypeValidationsTest.java [deleted file]
server/sonar-webserver-common/src/test/java/org/sonar/server/util/TypeValidationsTesting.java [deleted file]
server/sonar-webserver-common/src/test/projects/.gitignore [deleted file]
server/sonar-webserver-common/src/test/projects/README.txt [deleted file]
server/sonar-webserver-common/src/test/projects/fake-report-plugin/pom.xml [deleted file]
server/sonar-webserver-common/src/test/projects/fake-report-plugin/src/BasePlugin.java [deleted file]
server/sonar-webserver-common/src/test/projects/fake-report-plugin/src/org/sonar/plugins/testbase/api/BaseApi.java [deleted file]
server/sonar-webserver-common/src/test/projects/fake-report-plugin/target/fake-report-plugin-0.1-SNAPSHOT.jar [deleted file]
server/sonar-webserver-common/src/test/projects/fake-sqale-plugin/pom.xml [deleted file]
server/sonar-webserver-common/src/test/projects/fake-sqale-plugin/src/BasePlugin.java [deleted file]
server/sonar-webserver-common/src/test/projects/fake-sqale-plugin/src/org/sonar/plugins/testbase/api/BaseApi.java [deleted file]
server/sonar-webserver-common/src/test/projects/fake-sqale-plugin/target/fake-sqale-plugin-0.1-SNAPSHOT.jar [deleted file]
server/sonar-webserver-common/src/test/projects/fake-views-plugin/pom.xml [deleted file]
server/sonar-webserver-common/src/test/projects/fake-views-plugin/src/BasePlugin.java [deleted file]
server/sonar-webserver-common/src/test/projects/fake-views-plugin/src/org/sonar/plugins/testbase/api/BaseApi.java [deleted file]
server/sonar-webserver-common/src/test/projects/fake-views-plugin/target/fake-views-plugin-0.1-SNAPSHOT.jar [deleted file]
server/sonar-webserver-common/src/test/projects/pom.xml [deleted file]
server/sonar-webserver-common/src/test/projects/test-base-plugin-v2/pom.xml [deleted file]
server/sonar-webserver-common/src/test/projects/test-base-plugin-v2/src/BasePlugin.java [deleted file]
server/sonar-webserver-common/src/test/projects/test-base-plugin-v2/src/org/sonar/plugins/testbase/api/BaseApi.java [deleted file]
server/sonar-webserver-common/src/test/projects/test-base-plugin-v2/target/test-base-plugin-0.2-SNAPSHOT.jar [deleted file]
server/sonar-webserver-common/src/test/projects/test-base-plugin/pom.xml [deleted file]
server/sonar-webserver-common/src/test/projects/test-base-plugin/src/BasePlugin.java [deleted file]
server/sonar-webserver-common/src/test/projects/test-base-plugin/src/org/sonar/plugins/testbase/api/BaseApi.java [deleted file]
server/sonar-webserver-common/src/test/projects/test-base-plugin/target/test-base-plugin-0.1-SNAPSHOT.jar [deleted file]
server/sonar-webserver-common/src/test/projects/test-extend-plugin/pom.xml [deleted file]
server/sonar-webserver-common/src/test/projects/test-extend-plugin/src/ExtendPlugin.java [deleted file]
server/sonar-webserver-common/src/test/projects/test-extend-plugin/target/test-extend-plugin-0.1-SNAPSHOT.jar [deleted file]
server/sonar-webserver-common/src/test/projects/test-libs-plugin/pom.xml [deleted file]
server/sonar-webserver-common/src/test/projects/test-libs-plugin/src/LibsPlugin.java [deleted file]
server/sonar-webserver-common/src/test/projects/test-libs-plugin/target/test-libs-plugin-0.1-SNAPSHOT.jar [deleted file]
server/sonar-webserver-common/src/test/projects/test-require-plugin/pom.xml [deleted file]
server/sonar-webserver-common/src/test/projects/test-require-plugin/src/RequirePlugin.java [deleted file]
server/sonar-webserver-common/src/test/projects/test-require-plugin/target/test-require-plugin-0.1-SNAPSHOT.jar [deleted file]
server/sonar-webserver-common/src/test/projects/test-requirenew-plugin/pom.xml [deleted file]
server/sonar-webserver-common/src/test/projects/test-requirenew-plugin/src/RequirePlugin.java [deleted file]
server/sonar-webserver-common/src/test/projects/test-requirenew-plugin/target/test-requirenew-plugin-0.1-SNAPSHOT.jar [deleted file]
server/sonar-webserver-common/src/test/resources/logback-test.xml [deleted file]
server/sonar-webserver-core/build.gradle
server/sonar-webserver-webapi/build.gradle
server/sonar-webserver-ws/build.gradle
server/sonar-webserver/build.gradle
settings.gradle

diff --git a/server/sonar-webserver-api/build.gradle b/server/sonar-webserver-api/build.gradle
new file mode 100644 (file)
index 0000000..9c89a4b
--- /dev/null
@@ -0,0 +1,58 @@
+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
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/app/ProcessCommandWrapper.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/app/ProcessCommandWrapper.java
new file mode 100644 (file)
index 0000000..44bdc43
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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();
+
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/app/ProcessCommandWrapperImpl.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/app/ProcessCommandWrapperImpl.java
new file mode 100644 (file)
index 0000000..12b3f68
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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))));
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/app/RestartFlagHolder.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/app/RestartFlagHolder.java
new file mode 100644 (file)
index 0000000..dfc6c8c
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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();
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/app/RestartFlagHolderImpl.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/app/RestartFlagHolderImpl.java
new file mode 100644 (file)
index 0000000..98354a8
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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);
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/app/package-info.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/app/package-info.java
new file mode 100644 (file)
index 0000000..7170de2
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * 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;
+
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/branch/BranchFeature.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/branch/BranchFeature.java
new file mode 100644 (file)
index 0000000..7d60949
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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();
+
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/branch/BranchFeatureExtension.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/branch/BranchFeatureExtension.java
new file mode 100644 (file)
index 0000000..20fc40f
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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 {
+
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/branch/BranchFeatureProxy.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/branch/BranchFeatureProxy.java
new file mode 100644 (file)
index 0000000..8647d7c
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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 {
+
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/branch/BranchFeatureProxyImpl.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/branch/BranchFeatureProxyImpl.java
new file mode 100644 (file)
index 0000000..634e597
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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();
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/branch/package-info.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/branch/package-info.java
new file mode 100644 (file)
index 0000000..cb79dbc
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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;
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/ce/http/CeHttpClient.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/ce/http/CeHttpClient.java
new file mode 100644 (file)
index 0000000..f86a08e
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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);
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/ce/http/package-info.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/ce/http/package-info.java
new file mode 100644 (file)
index 0000000..17b36c5
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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;
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/exceptions/BadRequestException.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/exceptions/BadRequestException.java
new file mode 100644 (file)
index 0000000..3113d20
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * 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();
+  }
+
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/exceptions/ForbiddenException.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/exceptions/ForbiddenException.java
new file mode 100644 (file)
index 0000000..d72eefb
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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));
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/exceptions/Message.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/exceptions/Message.java
new file mode 100644 (file)
index 0000000..c069ead
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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;
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/exceptions/NotFoundException.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/exceptions/NotFoundException.java
new file mode 100644 (file)
index 0000000..f21a98b
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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();
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/exceptions/ServerException.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/exceptions/ServerException.java
new file mode 100644 (file)
index 0000000..491c17e
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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;
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/exceptions/UnauthorizedException.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/exceptions/UnauthorizedException.java
new file mode 100644 (file)
index 0000000..0b4af12
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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);
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/exceptions/package-info.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/exceptions/package-info.java
new file mode 100644 (file)
index 0000000..c1ac144
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * 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;
+
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/health/ClusterHealth.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/health/ClusterHealth.java
new file mode 100644 (file)
index 0000000..23fcfca
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * 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 +
+      '}';
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/health/Health.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/health/Health.java
new file mode 100644 (file)
index 0000000..d5b4944
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * 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
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/health/HealthChecker.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/health/HealthChecker.java
new file mode 100644 (file)
index 0000000..80fb8ae
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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();
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/health/package-info.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/health/package-info.java
new file mode 100644 (file)
index 0000000..18ea762
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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;
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/platform/ClusterFeature.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/platform/ClusterFeature.java
new file mode 100644 (file)
index 0000000..24d8617
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * 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();
+
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/platform/Platform.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/platform/Platform.java
new file mode 100644 (file)
index 0000000..22f3e45
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/platform/SystemInfoWriter.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/platform/SystemInfoWriter.java
new file mode 100644 (file)
index 0000000..3a82f47
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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;
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/platform/package-info.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/platform/package-info.java
new file mode 100644 (file)
index 0000000..71d1873
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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;
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/InstalledPlugin.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/InstalledPlugin.java
new file mode 100644 (file)
index 0000000..0fffb7b
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * 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;
+    }
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/PluginDownloader.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/PluginDownloader.java
new file mode 100644 (file)
index 0000000..d61efac
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * 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);
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/PluginFileSystem.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/PluginFileSystem.java
new file mode 100644 (file)
index 0000000..e65336a
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * 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);
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/PluginUninstaller.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/PluginUninstaller.java
new file mode 100644 (file)
index 0000000..8253dba
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * 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));
+    }
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/ServerPluginJarExploder.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/ServerPluginJarExploder.java
new file mode 100644 (file)
index 0000000..1970d44
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * 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);
+    }
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/ServerPluginRepository.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/ServerPluginRepository.java
new file mode 100644 (file)
index 0000000..ecc3abc
--- /dev/null
@@ -0,0 +1,372 @@
+/*
+ * 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();
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/UpdateCenterClient.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/UpdateCenterClient.java
new file mode 100644 (file)
index 0000000..2f5d0b2
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * 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);
+    }
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/UpdateCenterMatrixFactory.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/UpdateCenterMatrixFactory.java
new file mode 100644 (file)
index 0000000..0c0a673
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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();
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/WebServerExtensionInstaller.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/WebServerExtensionInstaller.java
new file mode 100644 (file)
index 0000000..9fa956d
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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));
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/edition/EditionBundledPlugins.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/edition/EditionBundledPlugins.java
new file mode 100644 (file)
index 0000000..f6c1db3
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * 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()));
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/edition/package-info.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/edition/package-info.java
new file mode 100644 (file)
index 0000000..e0a7b00
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * 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;
+
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/package-info.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/package-info.java
new file mode 100644 (file)
index 0000000..cc398bb
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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;
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/project/ProjectLifeCycleListener.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/project/ProjectLifeCycleListener.java
new file mode 100644 (file)
index 0000000..6afdea5
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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);
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/project/ProjectLifeCycleListeners.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/project/ProjectLifeCycleListeners.java
new file mode 100644 (file)
index 0000000..7f3d3f1
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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);
+
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/project/ProjectLifeCycleListenersImpl.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/project/ProjectLifeCycleListenersImpl.java
new file mode 100644 (file)
index 0000000..af440d6
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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);
+      }
+    };
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/project/RekeyedProject.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/project/RekeyedProject.java
new file mode 100644 (file)
index 0000000..ecab0b1
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * 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 + '\'' +
+      '}';
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/project/Visibility.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/project/Visibility.java
new file mode 100644 (file)
index 0000000..edf0518
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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;
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/project/package-info.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/project/package-info.java
new file mode 100644 (file)
index 0000000..205d705
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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;
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/qualitygate/ProjectsInWarning.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/qualitygate/ProjectsInWarning.java
new file mode 100644 (file)
index 0000000..7591c13
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * 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;
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEvent.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEvent.java
new file mode 100644 (file)
index 0000000..6953a77
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * 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();
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListener.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListener.java
new file mode 100644 (file)
index 0000000..c3baf20
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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);
+  }
+
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListeners.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListeners.java
new file mode 100644 (file)
index 0000000..e2e66b5
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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);
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImpl.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImpl.java
new file mode 100644 (file)
index 0000000..76a4c2b
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * 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 +
+        '}';
+    }
+  }
+
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/qualitygate/changeevent/Trigger.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/qualitygate/changeevent/Trigger.java
new file mode 100644 (file)
index 0000000..6ef8397
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * 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
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/qualitygate/changeevent/package-info.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/qualitygate/changeevent/package-info.java
new file mode 100644 (file)
index 0000000..ed52cd8
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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;
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/qualityprofile/BulkChangeResult.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/qualityprofile/BulkChangeResult.java
new file mode 100644 (file)
index 0000000..91a31ca
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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;
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/qualityprofile/QProfileRules.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/qualityprofile/QProfileRules.java
new file mode 100644 (file)
index 0000000..32a11c0
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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);
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/qualityprofile/RuleActivation.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/qualityprofile/RuleActivation.java
new file mode 100644 (file)
index 0000000..106fd15
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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;
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/qualityprofile/package-info.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/qualityprofile/package-info.java
new file mode 100644 (file)
index 0000000..5a3c798
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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;
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/setting/ProjectConfigurationLoader.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/setting/ProjectConfigurationLoader.java
new file mode 100644 (file)
index 0000000..e261763
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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()));
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/setting/ProjectConfigurationLoaderImpl.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/setting/ProjectConfigurationLoaderImpl.java
new file mode 100644 (file)
index 0000000..95e1124
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * 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;
+    }));
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/setting/SettingsChangeNotifier.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/setting/SettingsChangeNotifier.java
new file mode 100644 (file)
index 0000000..de074ef
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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);
+    }
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/setting/package-info.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/setting/package-info.java
new file mode 100644 (file)
index 0000000..e816bf4
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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;
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/telemetry/LicenseReader.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/telemetry/LicenseReader.java
new file mode 100644 (file)
index 0000000..22029d6
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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();
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/telemetry/package-info.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/telemetry/package-info.java
new file mode 100644 (file)
index 0000000..ce3724b
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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;
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/util/BooleanTypeValidation.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/util/BooleanTypeValidation.java
new file mode 100644 (file)
index 0000000..1b3ade3
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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);
+  }
+
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/util/FloatTypeValidation.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/util/FloatTypeValidation.java
new file mode 100644 (file)
index 0000000..fd221dc
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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));
+    }
+  }
+
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/util/GlobalLockManager.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/util/GlobalLockManager.java
new file mode 100644 (file)
index 0000000..81f13b2
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 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;
+    }
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/util/IntegerTypeValidation.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/util/IntegerTypeValidation.java
new file mode 100644 (file)
index 0000000..84502df
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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));
+    }
+  }
+
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/util/LongTypeValidation.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/util/LongTypeValidation.java
new file mode 100644 (file)
index 0000000..9ca540a
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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));
+    }
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/util/MetricLevelTypeValidation.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/util/MetricLevelTypeValidation.java
new file mode 100644 (file)
index 0000000..ba8598a
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * 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));
+    }
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/util/StringListTypeValidation.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/util/StringListTypeValidation.java
new file mode 100644 (file)
index 0000000..acb1c7d
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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, ", "));
+  }
+
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/util/StringTypeValidation.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/util/StringTypeValidation.java
new file mode 100644 (file)
index 0000000..3851ede
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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
+  }
+
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/util/TextTypeValidation.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/util/TextTypeValidation.java
new file mode 100644 (file)
index 0000000..c26ad12
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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
+  }
+
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/util/TypeValidation.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/util/TypeValidation.java
new file mode 100644 (file)
index 0000000..797a343
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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);
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/util/TypeValidationModule.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/util/TypeValidationModule.java
new file mode 100644 (file)
index 0000000..3cc4f03
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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
+    );
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/util/TypeValidations.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/util/TypeValidations.java
new file mode 100644 (file)
index 0000000..2c8883b
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * 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);
+    }
+  }
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/util/Validation.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/util/Validation.java
new file mode 100644 (file)
index 0000000..99d28d0
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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
+  }
+
+}
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/util/package-info.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/util/package-info.java
new file mode 100644 (file)
index 0000000..8fd4b5b
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * 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;
+
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/app/ProcessCommandWrapperImplTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/app/ProcessCommandWrapperImplTest.java
new file mode 100644 (file)
index 0000000..de5f243
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * 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);
+  }
+
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/branch/BranchFeatureProxyImplTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/branch/BranchFeatureProxyImplTest.java
new file mode 100644 (file)
index 0000000..6fb3d9a
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * 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();
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/branch/BranchFeatureRule.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/branch/BranchFeatureRule.java
new file mode 100644 (file)
index 0000000..2bf5646
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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;
+  }
+
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/exceptions/BadRequestExceptionTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/exceptions/BadRequestExceptionTest.java
new file mode 100644 (file)
index 0000000..0dc82c5
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * 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));
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/exceptions/MessageTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/exceptions/MessageTest.java
new file mode 100644 (file)
index 0000000..f05bb55
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * 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");
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/exceptions/ServerExceptionTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/exceptions/ServerExceptionTest.java
new file mode 100644 (file)
index 0000000..ae2ce78
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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");
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/health/TestStandaloneHealthChecker.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/health/TestStandaloneHealthChecker.java
new file mode 100644 (file)
index 0000000..27c2e46
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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();
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/PluginDownloaderTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/PluginDownloaderTest.java
new file mode 100644 (file)
index 0000000..f984cd9
--- /dev/null
@@ -0,0 +1,322 @@
+/*
+ * 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);
+    }
+  }
+
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/PluginFileSystemTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/PluginFileSystemTest.java
new file mode 100644 (file)
index 0000000..bc353f9
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * 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);
+  // }
+
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/PluginUninstallerTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/PluginUninstallerTest.java
new file mode 100644 (file)
index 0000000..20eae5f
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * 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());
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/ServerPluginJarExploderTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/ServerPluginJarExploderTest.java
new file mode 100644 (file)
index 0000000..8f4d928
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * 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);
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/ServerPluginRepositoryTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/ServerPluginRepositoryTest.java
new file mode 100644 (file)
index 0000000..e426c9e
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+ * 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());
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/TestPluginA.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/TestPluginA.java
new file mode 100644 (file)
index 0000000..7952eb5
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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) {
+
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/TestProjectUtils.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/TestProjectUtils.java
new file mode 100644 (file)
index 0000000..9710a86
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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();
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/UpdateCenterClientTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/UpdateCenterClientTest.java
new file mode 100644 (file)
index 0000000..b1e50cb
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * 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();
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/UpdateCenterMatrixFactoryTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/UpdateCenterMatrixFactoryTest.java
new file mode 100644 (file)
index 0000000..a71522a
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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();
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/UpdateCenterServlet.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/UpdateCenterServlet.java
new file mode 100644 (file)
index 0000000..d6d9904
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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);
+  }
+}
+
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/edition/EditionBundledPluginsTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/edition/EditionBundledPluginsTest.java
new file mode 100644 (file)
index 0000000..907768f
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * 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);
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/project/ProjectLifeCycleListenersImplTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/project/ProjectLifeCycleListenersImplTest.java
new file mode 100644 (file)
index 0000000..8085b64
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ * 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");
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/project/RekeyedProjectTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/project/RekeyedProjectTest.java
new file mode 100644 (file)
index 0000000..a847d91
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * 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()));
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImplTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImplTest.java
new file mode 100644 (file)
index 0000000..9646374
--- /dev/null
@@ -0,0 +1,358 @@
+/*
+ * 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);
+  }
+
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/qualitygate/changeevent/QGChangeEventTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/qualitygate/changeevent/QGChangeEventTest.java
new file mode 100644 (file)
index 0000000..eb4ce5d
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * 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 + "}");
+
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/setting/ProjectConfigurationLoaderImplTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/setting/ProjectConfigurationLoaderImplTest.java
new file mode 100644 (file)
index 0000000..0fb88ee
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * 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);
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/setting/SettingsChangeNotifierTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/setting/SettingsChangeNotifierTest.java
new file mode 100644 (file)
index 0000000..577eda8
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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");
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/setting/TestProjectConfigurationLoader.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/setting/TestProjectConfigurationLoader.java
new file mode 100644 (file)
index 0000000..e0c0b6d
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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;
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/util/BooleanTypeValidationTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/util/BooleanTypeValidationTest.java
new file mode 100644 (file)
index 0000000..d11c031
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * 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);
+  }
+
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/util/FloatTypeValidationTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/util/FloatTypeValidationTest.java
new file mode 100644 (file)
index 0000000..b88c3fb
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * 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);
+  }
+
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/util/GlobalLockManagerTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/util/GlobalLockManagerTest.java
new file mode 100644 (file)
index 0000000..8a069e1
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * 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();
+  }
+
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/util/IntegerTypeValidationTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/util/IntegerTypeValidationTest.java
new file mode 100644 (file)
index 0000000..e9b1953
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * 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);
+  }
+
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/util/LongTypeValidationTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/util/LongTypeValidationTest.java
new file mode 100644 (file)
index 0000000..2f7d7e0
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * 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);
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/util/StringListTypeValidationTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/util/StringListTypeValidationTest.java
new file mode 100644 (file)
index 0000000..9f2630f
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * 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"));
+  }
+
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/util/StringTypeValidationTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/util/StringTypeValidationTest.java
new file mode 100644 (file)
index 0000000..dde3afa
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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);
+  }
+
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/util/TextTypeValidationTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/util/TextTypeValidationTest.java
new file mode 100644 (file)
index 0000000..4261f95
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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);
+  }
+
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/util/TypeValidationModuleTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/util/TypeValidationModuleTest.java
new file mode 100644 (file)
index 0000000..3ff709c
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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);
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/util/TypeValidationsTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/util/TypeValidationsTest.java
new file mode 100644 (file)
index 0000000..4dbae47
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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.");
+    }
+  }
+
+}
diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/util/TypeValidationsTesting.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/util/TypeValidationsTesting.java
new file mode 100644 (file)
index 0000000..b237ba6
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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()
+      ));
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/projects/.gitignore b/server/sonar-webserver-api/src/test/projects/.gitignore
new file mode 100644 (file)
index 0000000..a945b85
--- /dev/null
@@ -0,0 +1,7 @@
+# see README.txt
+!*/target/
+*/target/classes/
+*/target/maven-archiver/
+*/target/maven-status/
+*/target/test-*/
+
diff --git a/server/sonar-webserver-api/src/test/projects/README.txt b/server/sonar-webserver-api/src/test/projects/README.txt
new file mode 100644 (file)
index 0000000..c53a66d
--- /dev/null
@@ -0,0 +1,3 @@
+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.
diff --git a/server/sonar-webserver-api/src/test/projects/fake-report-plugin/pom.xml b/server/sonar-webserver-api/src/test/projects/fake-report-plugin/pom.xml
new file mode 100644 (file)
index 0000000..72a04db
--- /dev/null
@@ -0,0 +1,36 @@
+<?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>
diff --git a/server/sonar-webserver-api/src/test/projects/fake-report-plugin/src/BasePlugin.java b/server/sonar-webserver-api/src/test/projects/fake-report-plugin/src/BasePlugin.java
new file mode 100644 (file)
index 0000000..d12daff
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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) {
+
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/projects/fake-report-plugin/src/org/sonar/plugins/testbase/api/BaseApi.java b/server/sonar-webserver-api/src/test/projects/fake-report-plugin/src/org/sonar/plugins/testbase/api/BaseApi.java
new file mode 100644 (file)
index 0000000..e0b5439
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * 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() {
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/projects/fake-report-plugin/target/fake-report-plugin-0.1-SNAPSHOT.jar b/server/sonar-webserver-api/src/test/projects/fake-report-plugin/target/fake-report-plugin-0.1-SNAPSHOT.jar
new file mode 100644 (file)
index 0000000..6085e44
Binary files /dev/null and b/server/sonar-webserver-api/src/test/projects/fake-report-plugin/target/fake-report-plugin-0.1-SNAPSHOT.jar differ
diff --git a/server/sonar-webserver-api/src/test/projects/fake-sqale-plugin/pom.xml b/server/sonar-webserver-api/src/test/projects/fake-sqale-plugin/pom.xml
new file mode 100644 (file)
index 0000000..e417dd9
--- /dev/null
@@ -0,0 +1,36 @@
+<?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>
diff --git a/server/sonar-webserver-api/src/test/projects/fake-sqale-plugin/src/BasePlugin.java b/server/sonar-webserver-api/src/test/projects/fake-sqale-plugin/src/BasePlugin.java
new file mode 100644 (file)
index 0000000..d12daff
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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) {
+
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/projects/fake-sqale-plugin/src/org/sonar/plugins/testbase/api/BaseApi.java b/server/sonar-webserver-api/src/test/projects/fake-sqale-plugin/src/org/sonar/plugins/testbase/api/BaseApi.java
new file mode 100644 (file)
index 0000000..e0b5439
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * 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() {
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/projects/fake-sqale-plugin/target/fake-sqale-plugin-0.1-SNAPSHOT.jar b/server/sonar-webserver-api/src/test/projects/fake-sqale-plugin/target/fake-sqale-plugin-0.1-SNAPSHOT.jar
new file mode 100644 (file)
index 0000000..b5c99f7
Binary files /dev/null and b/server/sonar-webserver-api/src/test/projects/fake-sqale-plugin/target/fake-sqale-plugin-0.1-SNAPSHOT.jar differ
diff --git a/server/sonar-webserver-api/src/test/projects/fake-views-plugin/pom.xml b/server/sonar-webserver-api/src/test/projects/fake-views-plugin/pom.xml
new file mode 100644 (file)
index 0000000..1ef73d2
--- /dev/null
@@ -0,0 +1,36 @@
+<?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>
diff --git a/server/sonar-webserver-api/src/test/projects/fake-views-plugin/src/BasePlugin.java b/server/sonar-webserver-api/src/test/projects/fake-views-plugin/src/BasePlugin.java
new file mode 100644 (file)
index 0000000..d12daff
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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) {
+
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/projects/fake-views-plugin/src/org/sonar/plugins/testbase/api/BaseApi.java b/server/sonar-webserver-api/src/test/projects/fake-views-plugin/src/org/sonar/plugins/testbase/api/BaseApi.java
new file mode 100644 (file)
index 0000000..e0b5439
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * 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() {
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/projects/fake-views-plugin/target/fake-views-plugin-0.1-SNAPSHOT.jar b/server/sonar-webserver-api/src/test/projects/fake-views-plugin/target/fake-views-plugin-0.1-SNAPSHOT.jar
new file mode 100644 (file)
index 0000000..a47d93d
Binary files /dev/null and b/server/sonar-webserver-api/src/test/projects/fake-views-plugin/target/fake-views-plugin-0.1-SNAPSHOT.jar differ
diff --git a/server/sonar-webserver-api/src/test/projects/pom.xml b/server/sonar-webserver-api/src/test/projects/pom.xml
new file mode 100644 (file)
index 0000000..3733831
--- /dev/null
@@ -0,0 +1,22 @@
+<?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>
diff --git a/server/sonar-webserver-api/src/test/projects/test-base-plugin-v2/pom.xml b/server/sonar-webserver-api/src/test/projects/test-base-plugin-v2/pom.xml
new file mode 100644 (file)
index 0000000..982be1c
--- /dev/null
@@ -0,0 +1,36 @@
+<?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>
diff --git a/server/sonar-webserver-api/src/test/projects/test-base-plugin-v2/src/BasePlugin.java b/server/sonar-webserver-api/src/test/projects/test-base-plugin-v2/src/BasePlugin.java
new file mode 100644 (file)
index 0000000..d12daff
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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) {
+
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/projects/test-base-plugin-v2/src/org/sonar/plugins/testbase/api/BaseApi.java b/server/sonar-webserver-api/src/test/projects/test-base-plugin-v2/src/org/sonar/plugins/testbase/api/BaseApi.java
new file mode 100644 (file)
index 0000000..e0b5439
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * 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() {
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/projects/test-base-plugin-v2/target/test-base-plugin-0.2-SNAPSHOT.jar b/server/sonar-webserver-api/src/test/projects/test-base-plugin-v2/target/test-base-plugin-0.2-SNAPSHOT.jar
new file mode 100644 (file)
index 0000000..1d4ef54
Binary files /dev/null and b/server/sonar-webserver-api/src/test/projects/test-base-plugin-v2/target/test-base-plugin-0.2-SNAPSHOT.jar differ
diff --git a/server/sonar-webserver-api/src/test/projects/test-base-plugin/pom.xml b/server/sonar-webserver-api/src/test/projects/test-base-plugin/pom.xml
new file mode 100644 (file)
index 0000000..c4e9593
--- /dev/null
@@ -0,0 +1,36 @@
+<?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>
diff --git a/server/sonar-webserver-api/src/test/projects/test-base-plugin/src/BasePlugin.java b/server/sonar-webserver-api/src/test/projects/test-base-plugin/src/BasePlugin.java
new file mode 100644 (file)
index 0000000..d12daff
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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) {
+
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/projects/test-base-plugin/src/org/sonar/plugins/testbase/api/BaseApi.java b/server/sonar-webserver-api/src/test/projects/test-base-plugin/src/org/sonar/plugins/testbase/api/BaseApi.java
new file mode 100644 (file)
index 0000000..e0b5439
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * 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() {
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/projects/test-base-plugin/target/test-base-plugin-0.1-SNAPSHOT.jar b/server/sonar-webserver-api/src/test/projects/test-base-plugin/target/test-base-plugin-0.1-SNAPSHOT.jar
new file mode 100644 (file)
index 0000000..739a22f
Binary files /dev/null and b/server/sonar-webserver-api/src/test/projects/test-base-plugin/target/test-base-plugin-0.1-SNAPSHOT.jar differ
diff --git a/server/sonar-webserver-api/src/test/projects/test-extend-plugin/pom.xml b/server/sonar-webserver-api/src/test/projects/test-extend-plugin/pom.xml
new file mode 100644 (file)
index 0000000..e23667e
--- /dev/null
@@ -0,0 +1,37 @@
+<?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>
diff --git a/server/sonar-webserver-api/src/test/projects/test-extend-plugin/src/ExtendPlugin.java b/server/sonar-webserver-api/src/test/projects/test-extend-plugin/src/ExtendPlugin.java
new file mode 100644 (file)
index 0000000..d364a2f
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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) {
+
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/projects/test-extend-plugin/target/test-extend-plugin-0.1-SNAPSHOT.jar b/server/sonar-webserver-api/src/test/projects/test-extend-plugin/target/test-extend-plugin-0.1-SNAPSHOT.jar
new file mode 100644 (file)
index 0000000..2f63c2e
Binary files /dev/null and b/server/sonar-webserver-api/src/test/projects/test-extend-plugin/target/test-extend-plugin-0.1-SNAPSHOT.jar differ
diff --git a/server/sonar-webserver-api/src/test/projects/test-libs-plugin/pom.xml b/server/sonar-webserver-api/src/test/projects/test-libs-plugin/pom.xml
new file mode 100644 (file)
index 0000000..2d49cca
--- /dev/null
@@ -0,0 +1,49 @@
+<?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>
diff --git a/server/sonar-webserver-api/src/test/projects/test-libs-plugin/src/LibsPlugin.java b/server/sonar-webserver-api/src/test/projects/test-libs-plugin/src/LibsPlugin.java
new file mode 100644 (file)
index 0000000..7e3ebe0
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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) {
+
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/projects/test-libs-plugin/target/test-libs-plugin-0.1-SNAPSHOT.jar b/server/sonar-webserver-api/src/test/projects/test-libs-plugin/target/test-libs-plugin-0.1-SNAPSHOT.jar
new file mode 100644 (file)
index 0000000..6ebe865
Binary files /dev/null and b/server/sonar-webserver-api/src/test/projects/test-libs-plugin/target/test-libs-plugin-0.1-SNAPSHOT.jar differ
diff --git a/server/sonar-webserver-api/src/test/projects/test-require-plugin/pom.xml b/server/sonar-webserver-api/src/test/projects/test-require-plugin/pom.xml
new file mode 100644 (file)
index 0000000..62462ff
--- /dev/null
@@ -0,0 +1,44 @@
+<?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>
diff --git a/server/sonar-webserver-api/src/test/projects/test-require-plugin/src/RequirePlugin.java b/server/sonar-webserver-api/src/test/projects/test-require-plugin/src/RequirePlugin.java
new file mode 100644 (file)
index 0000000..847ae2d
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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) {
+
+  }
+}
diff --git a/server/sonar-webserver-api/src/test/projects/test-require-plugin/target/test-require-plugin-0.1-SNAPSHOT.jar b/server/sonar-webserver-api/src/test/projects/test-require-plugin/target/test-require-plugin-0.1-SNAPSHOT.jar
new file mode 100644 (file)
index 0000000..f5fc95f
Binary files /dev/null and b/server/sonar-webserver-api/src/test/projects/test-require-plugin/target/test-require-plugin-0.1-SNAPSHOT.jar differ
diff --git a/server/sonar-webserver-api/src/test/projects/test-requirenew-plugin/pom.xml b/server/sonar-webserver-api/src/test/projects/test-requirenew-plugin/pom.xml
new file mode 100644 (file)
index 0000000..044cd94
--- /dev/null
@@ -0,0 +1,44 @@
+<?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>
diff --git a/server/sonar-webserver-api/src/test/projects/test-requirenew-plugin/src/RequirePlugin.java b/server/sonar-webserver-api/src/test/projects/test-requirenew-plugin/src/RequirePlugin.java
new file mode 100644 (file)
index 0000000..0d14cde
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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) {
+
+  }
+
+}
diff --git a/server/sonar-webserver-api/src/test/projects/test-requirenew-plugin/target/test-requirenew-plugin-0.1-SNAPSHOT.jar b/server/sonar-webserver-api/src/test/projects/test-requirenew-plugin/target/test-requirenew-plugin-0.1-SNAPSHOT.jar
new file mode 100644 (file)
index 0000000..0dd577f
Binary files /dev/null and b/server/sonar-webserver-api/src/test/projects/test-requirenew-plugin/target/test-requirenew-plugin-0.1-SNAPSHOT.jar differ
diff --git a/server/sonar-webserver-api/src/test/resources/logback-test.xml b/server/sonar-webserver-api/src/test/resources/logback-test.xml
new file mode 100644 (file)
index 0000000..3e34b0f
--- /dev/null
@@ -0,0 +1,26 @@
+<?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>
index 195dbbbe963523dcf2c29fbcdc4ec2a95c78aafb..3d50a54a0ec6b24f84f1a31a984cd29c5188b367 100644 (file)
@@ -21,7 +21,7 @@ dependencies {
   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'
diff --git a/server/sonar-webserver-common/build.gradle b/server/sonar-webserver-common/build.gradle
deleted file mode 100644 (file)
index 17637fc..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-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
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/app/ProcessCommandWrapper.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/app/ProcessCommandWrapper.java
deleted file mode 100644 (file)
index 44bdc43..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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();
-
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/app/ProcessCommandWrapperImpl.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/app/ProcessCommandWrapperImpl.java
deleted file mode 100644 (file)
index 12b3f68..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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))));
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/app/RestartFlagHolder.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/app/RestartFlagHolder.java
deleted file mode 100644 (file)
index dfc6c8c..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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();
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/app/RestartFlagHolderImpl.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/app/RestartFlagHolderImpl.java
deleted file mode 100644 (file)
index 98354a8..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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);
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/app/package-info.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/app/package-info.java
deleted file mode 100644 (file)
index 7170de2..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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;
-
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/branch/BranchFeature.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/branch/BranchFeature.java
deleted file mode 100644 (file)
index 7d60949..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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();
-
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/branch/BranchFeatureExtension.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/branch/BranchFeatureExtension.java
deleted file mode 100644 (file)
index 20fc40f..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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 {
-
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/branch/BranchFeatureProxy.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/branch/BranchFeatureProxy.java
deleted file mode 100644 (file)
index 8647d7c..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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 {
-
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/branch/BranchFeatureProxyImpl.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/branch/BranchFeatureProxyImpl.java
deleted file mode 100644 (file)
index 634e597..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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();
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/branch/package-info.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/branch/package-info.java
deleted file mode 100644 (file)
index cb79dbc..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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;
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/ce/http/CeHttpClient.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/ce/http/CeHttpClient.java
deleted file mode 100644 (file)
index f86a08e..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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);
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/ce/http/package-info.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/ce/http/package-info.java
deleted file mode 100644 (file)
index 17b36c5..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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;
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/exceptions/BadRequestException.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/exceptions/BadRequestException.java
deleted file mode 100644 (file)
index 3113d20..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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();
-  }
-
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/exceptions/ForbiddenException.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/exceptions/ForbiddenException.java
deleted file mode 100644 (file)
index d72eefb..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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));
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/exceptions/Message.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/exceptions/Message.java
deleted file mode 100644 (file)
index c069ead..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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;
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/exceptions/NotFoundException.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/exceptions/NotFoundException.java
deleted file mode 100644 (file)
index f21a98b..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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();
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/exceptions/ServerException.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/exceptions/ServerException.java
deleted file mode 100644 (file)
index 491c17e..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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;
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/exceptions/UnauthorizedException.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/exceptions/UnauthorizedException.java
deleted file mode 100644 (file)
index 0b4af12..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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);
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/exceptions/package-info.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/exceptions/package-info.java
deleted file mode 100644 (file)
index c1ac144..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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;
-
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/health/ClusterHealth.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/health/ClusterHealth.java
deleted file mode 100644 (file)
index 23fcfca..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * 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 +
-      '}';
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/health/Health.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/health/Health.java
deleted file mode 100644 (file)
index d5b4944..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * 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
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/health/HealthChecker.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/health/HealthChecker.java
deleted file mode 100644 (file)
index 80fb8ae..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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();
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/health/package-info.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/health/package-info.java
deleted file mode 100644 (file)
index 18ea762..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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;
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/platform/ClusterFeature.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/platform/ClusterFeature.java
deleted file mode 100644 (file)
index 24d8617..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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();
-
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/platform/Platform.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/platform/Platform.java
deleted file mode 100644 (file)
index 22f3e45..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/platform/SystemInfoWriter.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/platform/SystemInfoWriter.java
deleted file mode 100644 (file)
index 3a82f47..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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;
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/platform/package-info.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/platform/package-info.java
deleted file mode 100644 (file)
index 71d1873..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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;
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/InstalledPlugin.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/InstalledPlugin.java
deleted file mode 100644 (file)
index 0fffb7b..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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;
-    }
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/PluginDownloader.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/PluginDownloader.java
deleted file mode 100644 (file)
index d61efac..0000000
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * 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);
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/PluginFileSystem.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/PluginFileSystem.java
deleted file mode 100644 (file)
index e65336a..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * 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);
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/PluginUninstaller.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/PluginUninstaller.java
deleted file mode 100644 (file)
index 8253dba..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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));
-    }
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/ServerPluginJarExploder.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/ServerPluginJarExploder.java
deleted file mode 100644 (file)
index 1970d44..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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);
-    }
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/ServerPluginRepository.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/ServerPluginRepository.java
deleted file mode 100644 (file)
index ecc3abc..0000000
+++ /dev/null
@@ -1,372 +0,0 @@
-/*
- * 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();
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/UpdateCenterClient.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/UpdateCenterClient.java
deleted file mode 100644 (file)
index 2f5d0b2..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * 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);
-    }
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/UpdateCenterMatrixFactory.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/UpdateCenterMatrixFactory.java
deleted file mode 100644 (file)
index 0c0a673..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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();
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/WebServerExtensionInstaller.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/WebServerExtensionInstaller.java
deleted file mode 100644 (file)
index 9fa956d..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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));
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/edition/EditionBundledPlugins.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/edition/EditionBundledPlugins.java
deleted file mode 100644 (file)
index f6c1db3..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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()));
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/edition/package-info.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/edition/package-info.java
deleted file mode 100644 (file)
index e0a7b00..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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;
-
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/package-info.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/plugins/package-info.java
deleted file mode 100644 (file)
index cc398bb..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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;
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/project/ProjectLifeCycleListener.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/project/ProjectLifeCycleListener.java
deleted file mode 100644 (file)
index 6afdea5..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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);
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/project/ProjectLifeCycleListeners.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/project/ProjectLifeCycleListeners.java
deleted file mode 100644 (file)
index 7f3d3f1..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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);
-
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/project/ProjectLifeCycleListenersImpl.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/project/ProjectLifeCycleListenersImpl.java
deleted file mode 100644 (file)
index af440d6..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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);
-      }
-    };
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/project/RekeyedProject.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/project/RekeyedProject.java
deleted file mode 100644 (file)
index ecab0b1..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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 + '\'' +
-      '}';
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/project/Visibility.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/project/Visibility.java
deleted file mode 100644 (file)
index edf0518..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * 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;
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/project/package-info.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/project/package-info.java
deleted file mode 100644 (file)
index 205d705..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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;
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/qualitygate/ProjectsInWarning.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/qualitygate/ProjectsInWarning.java
deleted file mode 100644 (file)
index 7591c13..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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;
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEvent.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEvent.java
deleted file mode 100644 (file)
index 6953a77..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * 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();
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListener.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListener.java
deleted file mode 100644 (file)
index c3baf20..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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);
-  }
-
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListeners.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListeners.java
deleted file mode 100644 (file)
index e2e66b5..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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);
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImpl.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImpl.java
deleted file mode 100644 (file)
index 76a4c2b..0000000
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * 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 +
-        '}';
-    }
-  }
-
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/qualitygate/changeevent/Trigger.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/qualitygate/changeevent/Trigger.java
deleted file mode 100644 (file)
index 6ef8397..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/qualitygate/changeevent/package-info.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/qualitygate/changeevent/package-info.java
deleted file mode 100644 (file)
index ed52cd8..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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;
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/qualityprofile/BulkChangeResult.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/qualityprofile/BulkChangeResult.java
deleted file mode 100644 (file)
index 91a31ca..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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;
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/qualityprofile/QProfileRules.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/qualityprofile/QProfileRules.java
deleted file mode 100644 (file)
index 32a11c0..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * 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);
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/qualityprofile/RuleActivation.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/qualityprofile/RuleActivation.java
deleted file mode 100644 (file)
index 106fd15..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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;
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/qualityprofile/package-info.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/qualityprofile/package-info.java
deleted file mode 100644 (file)
index 5a3c798..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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;
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/setting/ProjectConfigurationLoader.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/setting/ProjectConfigurationLoader.java
deleted file mode 100644 (file)
index e261763..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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()));
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/setting/ProjectConfigurationLoaderImpl.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/setting/ProjectConfigurationLoaderImpl.java
deleted file mode 100644 (file)
index 95e1124..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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;
-    }));
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/setting/SettingsChangeNotifier.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/setting/SettingsChangeNotifier.java
deleted file mode 100644 (file)
index de074ef..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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);
-    }
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/setting/package-info.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/setting/package-info.java
deleted file mode 100644 (file)
index e816bf4..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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;
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/telemetry/LicenseReader.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/telemetry/LicenseReader.java
deleted file mode 100644 (file)
index 22029d6..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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();
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/telemetry/package-info.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/telemetry/package-info.java
deleted file mode 100644 (file)
index ce3724b..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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;
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/util/BooleanTypeValidation.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/util/BooleanTypeValidation.java
deleted file mode 100644 (file)
index 1b3ade3..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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);
-  }
-
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/util/FloatTypeValidation.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/util/FloatTypeValidation.java
deleted file mode 100644 (file)
index fd221dc..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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));
-    }
-  }
-
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/util/GlobalLockManager.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/util/GlobalLockManager.java
deleted file mode 100644 (file)
index 81f13b2..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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;
-    }
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/util/IntegerTypeValidation.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/util/IntegerTypeValidation.java
deleted file mode 100644 (file)
index 84502df..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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));
-    }
-  }
-
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/util/LongTypeValidation.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/util/LongTypeValidation.java
deleted file mode 100644 (file)
index 9ca540a..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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));
-    }
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/util/MetricLevelTypeValidation.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/util/MetricLevelTypeValidation.java
deleted file mode 100644 (file)
index ba8598a..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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));
-    }
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/util/StringListTypeValidation.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/util/StringListTypeValidation.java
deleted file mode 100644 (file)
index acb1c7d..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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, ", "));
-  }
-
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/util/StringTypeValidation.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/util/StringTypeValidation.java
deleted file mode 100644 (file)
index 3851ede..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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
-  }
-
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/util/TextTypeValidation.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/util/TextTypeValidation.java
deleted file mode 100644 (file)
index c26ad12..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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
-  }
-
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/util/TypeValidation.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/util/TypeValidation.java
deleted file mode 100644 (file)
index 797a343..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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);
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/util/TypeValidationModule.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/util/TypeValidationModule.java
deleted file mode 100644 (file)
index 3cc4f03..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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
-    );
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/util/TypeValidations.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/util/TypeValidations.java
deleted file mode 100644 (file)
index 2c8883b..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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);
-    }
-  }
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/util/Validation.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/util/Validation.java
deleted file mode 100644 (file)
index 99d28d0..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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
-  }
-
-}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/util/package-info.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/util/package-info.java
deleted file mode 100644 (file)
index 8fd4b5b..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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;
-
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/app/ProcessCommandWrapperImplTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/app/ProcessCommandWrapperImplTest.java
deleted file mode 100644 (file)
index de5f243..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * 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);
-  }
-
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/branch/BranchFeatureProxyImplTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/branch/BranchFeatureProxyImplTest.java
deleted file mode 100644 (file)
index 6fb3d9a..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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();
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/branch/BranchFeatureRule.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/branch/BranchFeatureRule.java
deleted file mode 100644 (file)
index 2bf5646..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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;
-  }
-
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/exceptions/BadRequestExceptionTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/exceptions/BadRequestExceptionTest.java
deleted file mode 100644 (file)
index 0dc82c5..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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));
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/exceptions/MessageTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/exceptions/MessageTest.java
deleted file mode 100644 (file)
index f05bb55..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * 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");
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/exceptions/ServerExceptionTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/exceptions/ServerExceptionTest.java
deleted file mode 100644 (file)
index ae2ce78..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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");
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/health/TestStandaloneHealthChecker.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/health/TestStandaloneHealthChecker.java
deleted file mode 100644 (file)
index 27c2e46..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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();
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/PluginDownloaderTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/PluginDownloaderTest.java
deleted file mode 100644 (file)
index f984cd9..0000000
+++ /dev/null
@@ -1,322 +0,0 @@
-/*
- * 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);
-    }
-  }
-
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/PluginFileSystemTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/PluginFileSystemTest.java
deleted file mode 100644 (file)
index bc353f9..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * 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);
-  // }
-
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/PluginUninstallerTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/PluginUninstallerTest.java
deleted file mode 100644 (file)
index 20eae5f..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * 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());
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/ServerPluginJarExploderTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/ServerPluginJarExploderTest.java
deleted file mode 100644 (file)
index 8f4d928..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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);
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/ServerPluginRepositoryTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/ServerPluginRepositoryTest.java
deleted file mode 100644 (file)
index e426c9e..0000000
+++ /dev/null
@@ -1,385 +0,0 @@
-/*
- * 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());
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/TestPluginA.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/TestPluginA.java
deleted file mode 100644 (file)
index 7952eb5..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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) {
-
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/TestProjectUtils.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/TestProjectUtils.java
deleted file mode 100644 (file)
index 9710a86..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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();
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/UpdateCenterClientTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/UpdateCenterClientTest.java
deleted file mode 100644 (file)
index b1e50cb..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * 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();
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/UpdateCenterMatrixFactoryTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/UpdateCenterMatrixFactoryTest.java
deleted file mode 100644 (file)
index a71522a..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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();
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/UpdateCenterServlet.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/UpdateCenterServlet.java
deleted file mode 100644 (file)
index d6d9904..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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);
-  }
-}
-
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/edition/EditionBundledPluginsTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/plugins/edition/EditionBundledPluginsTest.java
deleted file mode 100644 (file)
index 907768f..0000000
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * 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);
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/project/ProjectLifeCycleListenersImplTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/project/ProjectLifeCycleListenersImplTest.java
deleted file mode 100644 (file)
index 8085b64..0000000
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
- * 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");
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/project/RekeyedProjectTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/project/RekeyedProjectTest.java
deleted file mode 100644 (file)
index a847d91..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * 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()));
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImplTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImplTest.java
deleted file mode 100644 (file)
index 9646374..0000000
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * 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);
-  }
-
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/qualitygate/changeevent/QGChangeEventTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/qualitygate/changeevent/QGChangeEventTest.java
deleted file mode 100644 (file)
index eb4ce5d..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * 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 + "}");
-
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/setting/ProjectConfigurationLoaderImplTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/setting/ProjectConfigurationLoaderImplTest.java
deleted file mode 100644 (file)
index 0fb88ee..0000000
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * 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);
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/setting/SettingsChangeNotifierTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/setting/SettingsChangeNotifierTest.java
deleted file mode 100644 (file)
index 577eda8..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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");
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/setting/TestProjectConfigurationLoader.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/setting/TestProjectConfigurationLoader.java
deleted file mode 100644 (file)
index e0c0b6d..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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;
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/util/BooleanTypeValidationTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/util/BooleanTypeValidationTest.java
deleted file mode 100644 (file)
index d11c031..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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);
-  }
-
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/util/FloatTypeValidationTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/util/FloatTypeValidationTest.java
deleted file mode 100644 (file)
index b88c3fb..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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);
-  }
-
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/util/GlobalLockManagerTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/util/GlobalLockManagerTest.java
deleted file mode 100644 (file)
index 8a069e1..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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();
-  }
-
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/util/IntegerTypeValidationTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/util/IntegerTypeValidationTest.java
deleted file mode 100644 (file)
index e9b1953..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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);
-  }
-
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/util/LongTypeValidationTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/util/LongTypeValidationTest.java
deleted file mode 100644 (file)
index 2f7d7e0..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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);
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/util/StringListTypeValidationTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/util/StringListTypeValidationTest.java
deleted file mode 100644 (file)
index 9f2630f..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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"));
-  }
-
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/util/StringTypeValidationTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/util/StringTypeValidationTest.java
deleted file mode 100644 (file)
index dde3afa..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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);
-  }
-
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/util/TextTypeValidationTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/util/TextTypeValidationTest.java
deleted file mode 100644 (file)
index 4261f95..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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);
-  }
-
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/util/TypeValidationModuleTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/util/TypeValidationModuleTest.java
deleted file mode 100644 (file)
index 3ff709c..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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);
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/util/TypeValidationsTest.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/util/TypeValidationsTest.java
deleted file mode 100644 (file)
index 4dbae47..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * 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.");
-    }
-  }
-
-}
diff --git a/server/sonar-webserver-common/src/test/java/org/sonar/server/util/TypeValidationsTesting.java b/server/sonar-webserver-common/src/test/java/org/sonar/server/util/TypeValidationsTesting.java
deleted file mode 100644 (file)
index b237ba6..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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()
-      ));
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/projects/.gitignore b/server/sonar-webserver-common/src/test/projects/.gitignore
deleted file mode 100644 (file)
index a945b85..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# see README.txt
-!*/target/
-*/target/classes/
-*/target/maven-archiver/
-*/target/maven-status/
-*/target/test-*/
-
diff --git a/server/sonar-webserver-common/src/test/projects/README.txt b/server/sonar-webserver-common/src/test/projects/README.txt
deleted file mode 100644 (file)
index c53a66d..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-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.
diff --git a/server/sonar-webserver-common/src/test/projects/fake-report-plugin/pom.xml b/server/sonar-webserver-common/src/test/projects/fake-report-plugin/pom.xml
deleted file mode 100644 (file)
index 72a04db..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-<?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>
diff --git a/server/sonar-webserver-common/src/test/projects/fake-report-plugin/src/BasePlugin.java b/server/sonar-webserver-common/src/test/projects/fake-report-plugin/src/BasePlugin.java
deleted file mode 100644 (file)
index d12daff..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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) {
-
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/projects/fake-report-plugin/src/org/sonar/plugins/testbase/api/BaseApi.java b/server/sonar-webserver-common/src/test/projects/fake-report-plugin/src/org/sonar/plugins/testbase/api/BaseApi.java
deleted file mode 100644 (file)
index e0b5439..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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() {
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/projects/fake-report-plugin/target/fake-report-plugin-0.1-SNAPSHOT.jar b/server/sonar-webserver-common/src/test/projects/fake-report-plugin/target/fake-report-plugin-0.1-SNAPSHOT.jar
deleted file mode 100644 (file)
index 6085e44..0000000
Binary files a/server/sonar-webserver-common/src/test/projects/fake-report-plugin/target/fake-report-plugin-0.1-SNAPSHOT.jar and /dev/null differ
diff --git a/server/sonar-webserver-common/src/test/projects/fake-sqale-plugin/pom.xml b/server/sonar-webserver-common/src/test/projects/fake-sqale-plugin/pom.xml
deleted file mode 100644 (file)
index e417dd9..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-<?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>
diff --git a/server/sonar-webserver-common/src/test/projects/fake-sqale-plugin/src/BasePlugin.java b/server/sonar-webserver-common/src/test/projects/fake-sqale-plugin/src/BasePlugin.java
deleted file mode 100644 (file)
index d12daff..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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) {
-
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/projects/fake-sqale-plugin/src/org/sonar/plugins/testbase/api/BaseApi.java b/server/sonar-webserver-common/src/test/projects/fake-sqale-plugin/src/org/sonar/plugins/testbase/api/BaseApi.java
deleted file mode 100644 (file)
index e0b5439..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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() {
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/projects/fake-sqale-plugin/target/fake-sqale-plugin-0.1-SNAPSHOT.jar b/server/sonar-webserver-common/src/test/projects/fake-sqale-plugin/target/fake-sqale-plugin-0.1-SNAPSHOT.jar
deleted file mode 100644 (file)
index b5c99f7..0000000
Binary files a/server/sonar-webserver-common/src/test/projects/fake-sqale-plugin/target/fake-sqale-plugin-0.1-SNAPSHOT.jar and /dev/null differ
diff --git a/server/sonar-webserver-common/src/test/projects/fake-views-plugin/pom.xml b/server/sonar-webserver-common/src/test/projects/fake-views-plugin/pom.xml
deleted file mode 100644 (file)
index 1ef73d2..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-<?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>
diff --git a/server/sonar-webserver-common/src/test/projects/fake-views-plugin/src/BasePlugin.java b/server/sonar-webserver-common/src/test/projects/fake-views-plugin/src/BasePlugin.java
deleted file mode 100644 (file)
index d12daff..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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) {
-
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/projects/fake-views-plugin/src/org/sonar/plugins/testbase/api/BaseApi.java b/server/sonar-webserver-common/src/test/projects/fake-views-plugin/src/org/sonar/plugins/testbase/api/BaseApi.java
deleted file mode 100644 (file)
index e0b5439..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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() {
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/projects/fake-views-plugin/target/fake-views-plugin-0.1-SNAPSHOT.jar b/server/sonar-webserver-common/src/test/projects/fake-views-plugin/target/fake-views-plugin-0.1-SNAPSHOT.jar
deleted file mode 100644 (file)
index a47d93d..0000000
Binary files a/server/sonar-webserver-common/src/test/projects/fake-views-plugin/target/fake-views-plugin-0.1-SNAPSHOT.jar and /dev/null differ
diff --git a/server/sonar-webserver-common/src/test/projects/pom.xml b/server/sonar-webserver-common/src/test/projects/pom.xml
deleted file mode 100644 (file)
index 3733831..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-<?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>
diff --git a/server/sonar-webserver-common/src/test/projects/test-base-plugin-v2/pom.xml b/server/sonar-webserver-common/src/test/projects/test-base-plugin-v2/pom.xml
deleted file mode 100644 (file)
index 982be1c..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-<?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>
diff --git a/server/sonar-webserver-common/src/test/projects/test-base-plugin-v2/src/BasePlugin.java b/server/sonar-webserver-common/src/test/projects/test-base-plugin-v2/src/BasePlugin.java
deleted file mode 100644 (file)
index d12daff..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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) {
-
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/projects/test-base-plugin-v2/src/org/sonar/plugins/testbase/api/BaseApi.java b/server/sonar-webserver-common/src/test/projects/test-base-plugin-v2/src/org/sonar/plugins/testbase/api/BaseApi.java
deleted file mode 100644 (file)
index e0b5439..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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() {
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/projects/test-base-plugin-v2/target/test-base-plugin-0.2-SNAPSHOT.jar b/server/sonar-webserver-common/src/test/projects/test-base-plugin-v2/target/test-base-plugin-0.2-SNAPSHOT.jar
deleted file mode 100644 (file)
index 1d4ef54..0000000
Binary files a/server/sonar-webserver-common/src/test/projects/test-base-plugin-v2/target/test-base-plugin-0.2-SNAPSHOT.jar and /dev/null differ
diff --git a/server/sonar-webserver-common/src/test/projects/test-base-plugin/pom.xml b/server/sonar-webserver-common/src/test/projects/test-base-plugin/pom.xml
deleted file mode 100644 (file)
index c4e9593..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-<?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>
diff --git a/server/sonar-webserver-common/src/test/projects/test-base-plugin/src/BasePlugin.java b/server/sonar-webserver-common/src/test/projects/test-base-plugin/src/BasePlugin.java
deleted file mode 100644 (file)
index d12daff..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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) {
-
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/projects/test-base-plugin/src/org/sonar/plugins/testbase/api/BaseApi.java b/server/sonar-webserver-common/src/test/projects/test-base-plugin/src/org/sonar/plugins/testbase/api/BaseApi.java
deleted file mode 100644 (file)
index e0b5439..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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() {
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/projects/test-base-plugin/target/test-base-plugin-0.1-SNAPSHOT.jar b/server/sonar-webserver-common/src/test/projects/test-base-plugin/target/test-base-plugin-0.1-SNAPSHOT.jar
deleted file mode 100644 (file)
index 739a22f..0000000
Binary files a/server/sonar-webserver-common/src/test/projects/test-base-plugin/target/test-base-plugin-0.1-SNAPSHOT.jar and /dev/null differ
diff --git a/server/sonar-webserver-common/src/test/projects/test-extend-plugin/pom.xml b/server/sonar-webserver-common/src/test/projects/test-extend-plugin/pom.xml
deleted file mode 100644 (file)
index e23667e..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-<?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>
diff --git a/server/sonar-webserver-common/src/test/projects/test-extend-plugin/src/ExtendPlugin.java b/server/sonar-webserver-common/src/test/projects/test-extend-plugin/src/ExtendPlugin.java
deleted file mode 100644 (file)
index d364a2f..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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) {
-
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/projects/test-extend-plugin/target/test-extend-plugin-0.1-SNAPSHOT.jar b/server/sonar-webserver-common/src/test/projects/test-extend-plugin/target/test-extend-plugin-0.1-SNAPSHOT.jar
deleted file mode 100644 (file)
index 2f63c2e..0000000
Binary files a/server/sonar-webserver-common/src/test/projects/test-extend-plugin/target/test-extend-plugin-0.1-SNAPSHOT.jar and /dev/null differ
diff --git a/server/sonar-webserver-common/src/test/projects/test-libs-plugin/pom.xml b/server/sonar-webserver-common/src/test/projects/test-libs-plugin/pom.xml
deleted file mode 100644 (file)
index 2d49cca..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-<?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>
diff --git a/server/sonar-webserver-common/src/test/projects/test-libs-plugin/src/LibsPlugin.java b/server/sonar-webserver-common/src/test/projects/test-libs-plugin/src/LibsPlugin.java
deleted file mode 100644 (file)
index 7e3ebe0..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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) {
-
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/projects/test-libs-plugin/target/test-libs-plugin-0.1-SNAPSHOT.jar b/server/sonar-webserver-common/src/test/projects/test-libs-plugin/target/test-libs-plugin-0.1-SNAPSHOT.jar
deleted file mode 100644 (file)
index 6ebe865..0000000
Binary files a/server/sonar-webserver-common/src/test/projects/test-libs-plugin/target/test-libs-plugin-0.1-SNAPSHOT.jar and /dev/null differ
diff --git a/server/sonar-webserver-common/src/test/projects/test-require-plugin/pom.xml b/server/sonar-webserver-common/src/test/projects/test-require-plugin/pom.xml
deleted file mode 100644 (file)
index 62462ff..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-<?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>
diff --git a/server/sonar-webserver-common/src/test/projects/test-require-plugin/src/RequirePlugin.java b/server/sonar-webserver-common/src/test/projects/test-require-plugin/src/RequirePlugin.java
deleted file mode 100644 (file)
index 847ae2d..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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) {
-
-  }
-}
diff --git a/server/sonar-webserver-common/src/test/projects/test-require-plugin/target/test-require-plugin-0.1-SNAPSHOT.jar b/server/sonar-webserver-common/src/test/projects/test-require-plugin/target/test-require-plugin-0.1-SNAPSHOT.jar
deleted file mode 100644 (file)
index f5fc95f..0000000
Binary files a/server/sonar-webserver-common/src/test/projects/test-require-plugin/target/test-require-plugin-0.1-SNAPSHOT.jar and /dev/null differ
diff --git a/server/sonar-webserver-common/src/test/projects/test-requirenew-plugin/pom.xml b/server/sonar-webserver-common/src/test/projects/test-requirenew-plugin/pom.xml
deleted file mode 100644 (file)
index 044cd94..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-<?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>
diff --git a/server/sonar-webserver-common/src/test/projects/test-requirenew-plugin/src/RequirePlugin.java b/server/sonar-webserver-common/src/test/projects/test-requirenew-plugin/src/RequirePlugin.java
deleted file mode 100644 (file)
index 0d14cde..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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) {
-
-  }
-
-}
diff --git a/server/sonar-webserver-common/src/test/projects/test-requirenew-plugin/target/test-requirenew-plugin-0.1-SNAPSHOT.jar b/server/sonar-webserver-common/src/test/projects/test-requirenew-plugin/target/test-requirenew-plugin-0.1-SNAPSHOT.jar
deleted file mode 100644 (file)
index 0dd577f..0000000
Binary files a/server/sonar-webserver-common/src/test/projects/test-requirenew-plugin/target/test-requirenew-plugin-0.1-SNAPSHOT.jar and /dev/null differ
diff --git a/server/sonar-webserver-common/src/test/resources/logback-test.xml b/server/sonar-webserver-common/src/test/resources/logback-test.xml
deleted file mode 100644 (file)
index 3e34b0f..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-<?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>
index 8d8daa84ad98ff8c2924688c764376c72b3c021b..f8415d6f3a4702c70d2ba38ac2a23a29a8e6209a 100644 (file)
@@ -43,8 +43,8 @@ dependencies {
   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')
@@ -74,8 +74,8 @@ dependencies {
   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')
index c94534da9971b1a40088772f04fa32028b33ee06..d32bf648c4d5cbc20ac2edb0f364d58ec7a06d99 100644 (file)
@@ -36,8 +36,8 @@ dependencies {
   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')
index 61535505c5e472e0349cb78a732362d3cbef3636..7fa56ae5c0b718ec4f62366884aef2ba4d8eb31c 100644 (file)
@@ -17,7 +17,7 @@ dependencies {
 
   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')
index 47925090c2febf3c7f117789f28056bd73ea18a9..863fa2769b9c41a735108d464d7a32a2b8246bdb 100644 (file)
@@ -28,8 +28,8 @@ dependencies {
   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')
 }
index 000a441e1983a37e08d632c2ecc03adf58b6b599..7ea694bb170d463c58bc59ff2455dbfb82760e9d 100644 (file)
@@ -17,8 +17,8 @@ include 'server:sonar-server-common'
 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'