import org.sonar.server.notification.NotificationService;
import org.sonar.server.notification.email.AlertsEmailTemplate;
import org.sonar.server.notification.email.EmailNotificationChannel;
+import org.sonar.server.organization.DefaultOrganizationProviderImpl;
import org.sonar.server.platform.DatabaseServerCompatibility;
import org.sonar.server.platform.DefaultServerUpgradeStatus;
import org.sonar.server.platform.ServerFileSystemImpl;
new StartupMetadataProvider(),
ServerIdManager.class,
UriReader.class,
- ServerImpl.class
+ ServerImpl.class,
+ DefaultOrganizationProviderImpl.class
};
}
);
assertThat(picoContainer.getParent().getComponentAdapters()).hasSize(
CONTAINER_ITSELF
- + 4 // level 3
+ + 5 // level 3
);
assertThat(picoContainer.getParent().getParent().getComponentAdapters()).hasSize(
CONTAINER_ITSELF
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.organization;
+
+import org.picocontainer.Startable;
+import org.sonar.api.ce.ComputeEngineSide;
+import org.sonar.server.computation.task.container.EagerStart;
+import org.sonar.server.organization.DefaultOrganizationCache;
+
+@EagerStart
+@ComputeEngineSide
+public class DefaultOrganizationLoader implements Startable {
+ private final DefaultOrganizationCache defaultOrganizationCache;
+
+ public DefaultOrganizationLoader(DefaultOrganizationCache defaultOrganizationCache) {
+ this.defaultOrganizationCache = defaultOrganizationCache;
+ }
+
+ @Override
+ public void start() {
+ defaultOrganizationCache.load();
+ }
+
+ @Override
+ public void stop() {
+ defaultOrganizationCache.unload();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.ce.organization;
+
+import javax.annotation.ParametersAreNonnullByDefault;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Nullable;
+import org.sonar.ce.organization.DefaultOrganizationLoader;
import org.sonar.ce.queue.CeTask;
import org.sonar.ce.settings.SettingsLoader;
import org.sonar.core.issue.tracking.Tracker;
import org.sonar.core.platform.ContainerPopulator;
import org.sonar.plugin.ce.ReportAnalysisComponentProvider;
+import org.sonar.server.computation.task.container.TaskContainer;
import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolderImpl;
+import org.sonar.server.computation.task.projectanalysis.api.posttask.PostProjectAnalysisTasksExecutor;
import org.sonar.server.computation.task.projectanalysis.batch.BatchReportDirectoryHolderImpl;
import org.sonar.server.computation.task.projectanalysis.batch.BatchReportReaderImpl;
import org.sonar.server.computation.task.projectanalysis.component.DbIdsRepositoryImpl;
import org.sonar.server.computation.task.projectanalysis.component.DisabledComponentsHolderImpl;
import org.sonar.server.computation.task.projectanalysis.component.SettingsRepositoryImpl;
import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolderImpl;
-import org.sonar.server.computation.task.container.TaskContainer;
import org.sonar.server.computation.task.projectanalysis.duplication.CrossProjectDuplicationStatusHolderImpl;
import org.sonar.server.computation.task.projectanalysis.duplication.DuplicationRepositoryImpl;
import org.sonar.server.computation.task.projectanalysis.duplication.IntegrateCrossProjectDuplications;
import org.sonar.server.computation.task.projectanalysis.measure.MeasureToMeasureDto;
import org.sonar.server.computation.task.projectanalysis.metric.MetricModule;
import org.sonar.server.computation.task.projectanalysis.period.PeriodsHolderImpl;
-import org.sonar.server.computation.task.projectanalysis.api.posttask.PostProjectAnalysisTasksExecutor;
import org.sonar.server.computation.task.projectanalysis.qualitygate.EvaluationResultTextConverterImpl;
import org.sonar.server.computation.task.projectanalysis.qualitygate.QualityGateHolderImpl;
import org.sonar.server.computation.task.projectanalysis.qualitygate.QualityGateServiceImpl;
import org.sonar.server.computation.task.projectanalysis.source.LastCommitVisitor;
import org.sonar.server.computation.task.projectanalysis.source.SourceHashRepositoryImpl;
import org.sonar.server.computation.task.projectanalysis.source.SourceLinesRepositoryImpl;
+import org.sonar.server.computation.task.projectanalysis.step.ReportComputationSteps;
import org.sonar.server.computation.task.step.ComputationStepExecutor;
import org.sonar.server.computation.task.step.ComputationSteps;
-import org.sonar.server.computation.task.projectanalysis.step.ReportComputationSteps;
import org.sonar.server.computation.taskprocessor.MutableTaskResultHolderImpl;
import org.sonar.server.view.index.ViewIndex;
public void populateContainer(TaskContainer container) {
ComputationSteps steps = new ReportComputationSteps(container);
container.add(SettingsLoader.class);
+ container.add(DefaultOrganizationLoader.class);
container.add(task);
container.add(steps);
container.addSingletons(componentClasses());
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.organization;
+
+import static java.util.Objects.requireNonNull;
+
+public class DefaultOrganization {
+ private final String uuid;
+ private final String key;
+ private final String name;
+ private final long createdAt;
+ private final long updatedAt;
+
+ private DefaultOrganization(Builder builder) {
+ this.uuid = requireNonNull(builder.uuid, "uuid can't be null");
+ this.key = requireNonNull(builder.key, "key can't be null");
+ this.name = requireNonNull(builder.name, "name can't be null");
+ this.createdAt = requireNonNull(builder.createdAt, "createdAt can't be null");
+ this.updatedAt = requireNonNull(builder.updatedAt, "updatedAt can't be null");
+ }
+
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public long getCreatedAt() {
+ return createdAt;
+ }
+
+ public long getUpdatedAt() {
+ return updatedAt;
+ }
+
+ @Override
+ public String toString() {
+ return "DefaultOrganization{" +
+ "uuid='" + uuid + '\'' +
+ ", key='" + key + '\'' +
+ ", name='" + name + '\'' +
+ ", createdAt=" + createdAt +
+ ", updatedAt=" + updatedAt +
+ '}';
+ }
+
+ public static final class Builder {
+ private String uuid;
+ private String key;
+ private String name;
+ private Long createdAt;
+ private Long updatedAt;
+
+ public Builder setUuid(String uuid) {
+ this.uuid = uuid;
+ return this;
+ }
+
+ public Builder setKey(String key) {
+ this.key = key;
+ return this;
+ }
+
+ public Builder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public Builder setCreatedAt(long createdAt) {
+ this.createdAt = createdAt;
+ return this;
+ }
+
+ public Builder setUpdatedAt(long updatedAt) {
+ this.updatedAt = updatedAt;
+ return this;
+ }
+
+ public DefaultOrganization build() {
+ return new DefaultOrganization(this);
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.organization;
+
+public interface DefaultOrganizationCache {
+
+ /**
+ * Loads {@link DefaultOrganization} in cache.
+ */
+ void load();
+
+ /**
+ * Unloads {@link DefaultOrganization} from cache.
+ */
+ void unload();
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.organization;
+
+public interface DefaultOrganizationProvider {
+ /**
+ * @throws IllegalStateException if there is no default organization
+ */
+ DefaultOrganization get();
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.organization;
+
+import java.util.Optional;
+import java.util.function.Supplier;
+import javax.annotation.CheckForNull;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.server.property.InternalProperties;
+
+import static com.google.common.base.Preconditions.checkState;
+
+public class DefaultOrganizationProviderImpl implements DefaultOrganizationProvider, DefaultOrganizationCache {
+ private static final ThreadLocal<Cache> CACHE = new ThreadLocal<>();
+
+ private final DbClient dbClient;
+
+ public DefaultOrganizationProviderImpl(DbClient dbClient) {
+ this.dbClient = dbClient;
+ }
+
+ @Override
+ public DefaultOrganization get() {
+ Cache cache = CACHE.get();
+ if (cache != null) {
+ return cache.get(() -> getDefaultOrganization(dbClient));
+ }
+
+ return getDefaultOrganization(dbClient);
+ }
+
+ private static DefaultOrganization getDefaultOrganization(DbClient dbClient) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ Optional<String> uuid = dbClient.internalPropertiesDao().selectByKey(dbSession, InternalProperties.DEFAULT_ORGANIZATION);
+ checkState(uuid.isPresent() && !uuid.get().isEmpty(), "No Default organization uuid configured");
+ Optional<OrganizationDto> dto = dbClient.organizationDao().selectByUuid(dbSession, uuid.get());
+ checkState(dto.isPresent(), "Default organization with uuid '%s' does not exist", uuid.get());
+ return toDefaultOrganization(dto.get());
+ }
+ }
+
+ private static DefaultOrganization toDefaultOrganization(OrganizationDto organizationDto) {
+ return DefaultOrganization.newBuilder()
+ .setUuid(organizationDto.getUuid())
+ .setKey(organizationDto.getKey())
+ .setName(organizationDto.getName())
+ .setCreatedAt(organizationDto.getCreatedAt())
+ .setUpdatedAt(organizationDto.getUpdatedAt())
+ .build();
+ }
+
+ @Override
+ public void load() {
+ checkState(
+ CACHE.get() == null,
+ "load called twice for thread '%s' or state wasn't cleared last time it was used",
+ Thread.currentThread().getName());
+ CACHE.set(new Cache());
+ }
+
+ @Override
+ public void unload() {
+ CACHE.remove();
+ }
+
+ private static final class Cache {
+ @CheckForNull
+ private DefaultOrganization defaultOrganization;
+
+ public DefaultOrganization get(Supplier<DefaultOrganization> supplier) {
+ if (defaultOrganization == null) {
+ defaultOrganization = supplier.get();
+ }
+ return defaultOrganization;
+ }
+
+ }
+}
import org.sonar.api.utils.UriReader;
import org.sonar.core.util.DefaultHttpDownloader;
+import org.sonar.server.organization.DefaultOrganizationProviderImpl;
import org.sonar.server.platform.ServerIdGenerator;
import org.sonar.server.platform.ServerIdLoader;
import org.sonar.server.platform.ServerIdManager;
ServerIdLoader.class,
ServerIdGenerator.class,
LogServerId.class,
- DefaultHttpDownloader.class);
+ DefaultHttpDownloader.class,
+ DefaultOrganizationProviderImpl.class);
}
}
import com.google.common.annotations.VisibleForTesting;
import java.io.IOException;
+import javax.annotation.Nullable;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.sonar.server.authentication.UserSessionInitializer;
+import org.sonar.server.organization.DefaultOrganizationCache;
import org.sonar.server.platform.Platform;
import org.sonar.server.setting.ThreadLocalSettings;
public class UserSessionFilter implements Filter {
-
private final Platform platform;
public UserSessionFilter() {
HttpServletResponse response = (HttpServletResponse) servletResponse;
ThreadLocalSettings settings = platform.getContainer().getComponentByType(ThreadLocalSettings.class);
+ DefaultOrganizationCache defaultOrganizationCache = platform.getContainer().getComponentByType(DefaultOrganizationCache.class);
UserSessionInitializer userSessionInitializer = platform.getContainer().getComponentByType(UserSessionInitializer.class);
- settings.load();
+ defaultOrganizationCache.load();
+ try {
+ settings.load();
+ try {
+ doFilter(request, response, chain, userSessionInitializer);
+ } finally {
+ settings.unload();
+ }
+ } finally {
+ defaultOrganizationCache.unload();
+ }
+ }
+
+ private static void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
+ @Nullable UserSessionInitializer userSessionInitializer) throws IOException, ServletException {
try {
if (userSessionInitializer == null || userSessionInitializer.initUserSession(request, response)) {
- chain.doFilter(servletRequest, servletResponse);
+ chain.doFilter(request, response);
}
} finally {
if (userSessionInitializer != null) {
userSessionInitializer.removeUserSession();
}
- settings.unload();
}
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.organization;
+
+import org.junit.Test;
+import org.sonar.server.organization.DefaultOrganizationCache;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+public class DefaultOrganizationLoaderTest {
+ private DefaultOrganizationCache defaultOrganizationCache = mock(DefaultOrganizationCache.class);
+ private DefaultOrganizationLoader underTest = new DefaultOrganizationLoader(defaultOrganizationCache);
+
+ @Test
+ public void start_calls_cache_load_method() {
+ underTest.start();
+
+ verify(defaultOrganizationCache).load();
+ verifyNoMoreInteractions(defaultOrganizationCache);
+ }
+
+ @Test
+ public void stop_calls_cache_unload_method() {
+ underTest.stop();
+
+ verify(defaultOrganizationCache).unload();
+ verifyNoMoreInteractions(defaultOrganizationCache);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.organization;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.organization.OrganizationDto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.server.property.InternalProperties.DEFAULT_ORGANIZATION;
+
+public class DefaultOrganizationProviderImplTest {
+ private static final OrganizationDto ORGANIZATION_DTO_1 = new OrganizationDto()
+ .setUuid("uuid1")
+ .setName("the name of 1")
+ .setKey("the key 1");
+ private static final long DATE_1 = 1_999_888L;
+
+ private System2 system2 = mock(System2.class);
+
+ @Rule
+ public DbTester dbTester = DbTester.create(system2);
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private DbClient dbClient = dbTester.getDbClient();
+ private DbSession dbSession = dbTester.getSession();
+
+ private DefaultOrganizationProviderImpl underTest = new DefaultOrganizationProviderImpl(dbClient);
+
+ @Test
+ public void get_fails_with_ISE_if_default_organization_internal_property_does_not_exist() {
+ expectISENoDefaultOrganizationUuid();
+
+ underTest.get();
+ }
+
+ @Test
+ public void get_fails_with_ISE_if_default_organization_internal_property_is_empty() {
+ dbClient.internalPropertiesDao().saveAsEmpty(dbSession, DEFAULT_ORGANIZATION);
+ dbSession.commit();
+
+ expectISENoDefaultOrganizationUuid();
+
+ underTest.get();
+ }
+
+ private void expectISENoDefaultOrganizationUuid() {
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("No Default organization uuid configured");
+ }
+
+ @Test
+ public void get_fails_with_ISE_if_default_organization_does_not_exist() {
+ dbClient.internalPropertiesDao().save(dbSession, DEFAULT_ORGANIZATION, "bla");
+ dbSession.commit();
+
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("Default organization with uuid 'bla' does not exist");
+
+ underTest.get();
+ }
+
+ @Test
+ public void get_returns_DefaultOrganization_populated_from_DB() {
+ insertOrganization(ORGANIZATION_DTO_1, DATE_1);
+ dbClient.internalPropertiesDao().save(dbSession, DEFAULT_ORGANIZATION, ORGANIZATION_DTO_1.getUuid());
+ dbSession.commit();
+
+ DefaultOrganization defaultOrganization = underTest.get();
+ assertThat(defaultOrganization.getUuid()).isEqualTo(ORGANIZATION_DTO_1.getUuid());
+ assertThat(defaultOrganization.getKey()).isEqualTo(ORGANIZATION_DTO_1.getKey());
+ assertThat(defaultOrganization.getName()).isEqualTo(ORGANIZATION_DTO_1.getName());
+ assertThat(defaultOrganization.getCreatedAt()).isEqualTo(DATE_1);
+ assertThat(defaultOrganization.getUpdatedAt()).isEqualTo(DATE_1);
+ }
+
+ @Test
+ public void get_returns_new_DefaultOrganization_with_each_call_when_cache_is_not_loaded() {
+ insertOrganization(ORGANIZATION_DTO_1, DATE_1);
+ dbClient.internalPropertiesDao().save(dbSession, DEFAULT_ORGANIZATION, ORGANIZATION_DTO_1.getUuid());
+ dbSession.commit();
+
+ assertThat(underTest.get()).isNotSameAs(underTest.get());
+ }
+
+ @Test
+ public void unload_does_not_fail_if_load_has_not_been_called() {
+ underTest.unload();
+ }
+
+ @Test
+ public void load_fails_with_ISE_when_called_twice_without_unload_in_between() {
+ underTest.load();
+
+ try {
+ underTest.load();
+ fail("A IllegalStateException should have been raised");
+ } catch (IllegalStateException e) {
+ assertThat(e).hasMessage("load called twice for thread '" + Thread.currentThread().getName() + "' or state wasn't cleared last time it was used");
+ } finally {
+ underTest.unload();
+ }
+ }
+
+ @Test
+ public void load_and_unload_cache_DefaultOrganization_object_by_thread() throws InterruptedException {
+ insertOrganization(ORGANIZATION_DTO_1, DATE_1);
+ dbClient.internalPropertiesDao().save(dbSession, DEFAULT_ORGANIZATION, ORGANIZATION_DTO_1.getUuid());
+ dbSession.commit();
+
+ try {
+ underTest.load();
+
+ DefaultOrganization cachedForThread1 = underTest.get();
+ assertThat(cachedForThread1).isSameAs(underTest.get());
+
+ Thread otherThread = new Thread(() -> {
+ try {
+ underTest.load();
+
+ assertThat(underTest.get())
+ .isNotSameAs(cachedForThread1)
+ .isSameAs(underTest.get());
+ } finally {
+ underTest.unload();
+ }
+ });
+ otherThread.start();
+ otherThread.join();
+ } finally {
+ underTest.unload();
+ }
+ }
+
+ @Test
+ public void get_returns_new_instance_for_each_call_once_unload_has_been_called() {
+ insertOrganization(ORGANIZATION_DTO_1, DATE_1);
+ dbClient.internalPropertiesDao().save(dbSession, DEFAULT_ORGANIZATION, ORGANIZATION_DTO_1.getUuid());
+ dbSession.commit();
+
+ try {
+ underTest.load();
+ DefaultOrganization cached = underTest.get();
+ assertThat(cached).isSameAs(underTest.get());
+
+ underTest.unload();
+ assertThat(underTest.get()).isNotSameAs(underTest.get()).isNotSameAs(cached);
+ } finally {
+ // fail safe
+ underTest.unload();
+ }
+ }
+
+ private void insertOrganization(OrganizationDto dto, long createdAt) {
+ when(system2.now()).thenReturn(createdAt);
+ dbClient.organizationDao().insert(dbSession, dto);
+ dbSession.commit();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.organization;
+
+import com.google.common.base.Preconditions;
+import org.junit.rules.ExternalResource;
+import org.sonar.core.util.UuidFactoryImpl;
+
+public class DefaultOrganizationProviderRule extends ExternalResource implements DefaultOrganizationProvider {
+ private DefaultOrganization defaultOrganization;
+
+ private DefaultOrganizationProviderRule(DefaultOrganization defaultOrganization) {
+ this.defaultOrganization = defaultOrganization;
+ }
+
+ /**
+ * <p>
+ * This method is meant to be statically imported.
+ * </p>
+ */
+ public static DefaultOrganizationProviderRule someDefaultOrganization() {
+ String uuid = UuidFactoryImpl.INSTANCE.create();
+ return new DefaultOrganizationProviderRule(DefaultOrganization.newBuilder()
+ .setUuid(uuid)
+ .setName("Default organization " + uuid)
+ .setKey(uuid + "_key")
+ .setCreatedAt(uuid.hashCode())
+ .setUpdatedAt(uuid.hashCode())
+ .build());
+ }
+
+ /**
+ * <p>
+ * This method is meant to be statically imported.
+ * </p>
+ */
+ public static DefaultOrganizationProviderRule defaultOrganizationWithName(String name) {
+ String uuid = UuidFactoryImpl.INSTANCE.create();
+ return new DefaultOrganizationProviderRule(DefaultOrganization.newBuilder()
+ .setUuid(uuid)
+ .setName(name)
+ .setKey(uuid + "_key")
+ .setCreatedAt(uuid.hashCode())
+ .setUpdatedAt(uuid.hashCode())
+ .build());
+ }
+
+ @Override
+ public DefaultOrganization get() {
+ Preconditions.checkState(defaultOrganization != null, "No default organization is set");
+ return defaultOrganization;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.organization;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class DefaultOrganizationTest {
+ private static final long DATE_2 = 2_000_000L;
+ private static final long DATE_1 = 1_000_000L;
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private DefaultOrganization.Builder populatedBuilder = new DefaultOrganization.Builder()
+ .setUuid("uuid")
+ .setKey("key")
+ .setName("name")
+ .setCreatedAt(DATE_1)
+ .setUpdatedAt(DATE_2);
+
+ @Test
+ public void build_fails_if_uuid_is_null() {
+ populatedBuilder.setUuid(null);
+
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("uuid can't be null");
+
+ populatedBuilder.build();
+ }
+
+ @Test
+ public void build_fails_if_key_is_null() {
+ populatedBuilder.setKey(null);
+
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("key can't be null");
+
+ populatedBuilder.build();
+ }
+
+ @Test
+ public void build_fails_if_name_is_null() {
+ populatedBuilder.setName(null);
+
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("name can't be null");
+
+ populatedBuilder.build();
+ }
+
+ @Test
+ public void build_fails_if_createdAt_not_set() {
+ DefaultOrganization.Builder underTest = new DefaultOrganization.Builder()
+ .setUuid("uuid")
+ .setKey("key")
+ .setName("name")
+ .setUpdatedAt(DATE_2);
+
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("createdAt can't be null");
+
+ underTest.build();
+ }
+
+ @Test
+ public void build_fails_if_updatedAt_not_set() {
+ DefaultOrganization.Builder underTest = new DefaultOrganization.Builder()
+ .setUuid("uuid")
+ .setKey("key")
+ .setName("name")
+ .setCreatedAt(DATE_1);
+
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("updatedAt can't be null");
+
+ underTest.build();
+ }
+
+ @Test
+ public void verify_toString() {
+ assertThat(populatedBuilder.build().toString())
+ .isEqualTo("DefaultOrganization{uuid='uuid', key='key', name='name', createdAt=1000000, updatedAt=2000000}");
+ }
+
+ @Test
+ public void verify_getters() {
+ DefaultOrganization underTest = populatedBuilder.build();
+
+ assertThat(underTest.getUuid()).isEqualTo("uuid");
+ assertThat(underTest.getKey()).isEqualTo("key");
+ assertThat(underTest.getName()).isEqualTo("name");
+ assertThat(underTest.getCreatedAt()).isEqualTo(DATE_1);
+ assertThat(underTest.getUpdatedAt()).isEqualTo(DATE_2);
+ }
+}
import javax.servlet.http.HttpServletResponse;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.InOrder;
import org.mockito.Mockito;
import org.sonar.server.authentication.UserSessionInitializer;
+import org.sonar.server.organization.DefaultOrganizationCache;
import org.sonar.server.platform.Platform;
import org.sonar.server.setting.ThreadLocalSettings;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.fail;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
private HttpServletResponse response = mock(HttpServletResponse.class);
private FilterChain chain = mock(FilterChain.class);
private ThreadLocalSettings settings = mock(ThreadLocalSettings.class);
+ private DefaultOrganizationCache defaultOrganizationCache = mock(DefaultOrganizationCache.class);
private UserSessionFilter underTest = new UserSessionFilter(platform);
@Before
public void setUp() {
when(platform.getContainer().getComponentByType(ThreadLocalSettings.class)).thenReturn(settings);
+ when(platform.getContainer().getComponentByType(DefaultOrganizationCache.class)).thenReturn(defaultOrganizationCache);
}
@Test
public void cleanup_user_session_after_request_handling() throws IOException, ServletException {
- when(platform.getContainer().getComponentByType(UserSessionInitializer.class)).thenReturn(userSessionInitializer);
- when(userSessionInitializer.initUserSession(request, response)).thenReturn(true);
+ mockUserSessionInitializer(true);
underTest.doFilter(request, response, chain);
@Test
public void stop_when_user_session_return_false() throws Exception {
- when(platform.getContainer().getComponentByType(UserSessionInitializer.class)).thenReturn(userSessionInitializer);
- when(userSessionInitializer.initUserSession(request, response)).thenReturn(false);
+ mockUserSessionInitializer(false);
underTest.doFilter(request, response, chain);
@Test
public void does_nothing_when_not_initialized() throws Exception {
- when(platform.getContainer().getComponentByType(UserSessionInitializer.class)).thenReturn(null);
+ mockNoUserSessionInitializer();
underTest.doFilter(request, response, chain);
verifyZeroInteractions(userSessionInitializer);
}
+ @Test
+ public void doFilter_loads_and_unloads_settings() throws Exception {
+ mockNoUserSessionInitializer();
+
+ underTest.doFilter(request, response, chain);
+
+ InOrder inOrder = inOrder(settings);
+ inOrder.verify(settings).load();
+ inOrder.verify(settings).unload();
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void doFilter_unloads_Settings_even_if_chain_throws_exception() throws Exception {
+ mockNoUserSessionInitializer();
+ RuntimeException thrown = mockChainDoFilterError();
+
+ try {
+ underTest.doFilter(request, response, chain);
+ fail("A RuntimeException should have been thrown");
+ } catch (RuntimeException e) {
+ assertThat(e).isSameAs(thrown);
+ verify(settings).unload();
+ }
+ }
+
+ @Test
+ public void doFilter_unloads_Settings_even_if_DefaultOrganizationCache_unload_fails() throws Exception {
+ mockNoUserSessionInitializer();
+ RuntimeException thrown = new RuntimeException("Faking DefaultOrganizationCache.unload failing");
+ doThrow(thrown)
+ .when(defaultOrganizationCache)
+ .unload();
+
+ try {
+ underTest.doFilter(request, response, chain);
+ fail("A RuntimeException should have been thrown");
+ } catch (RuntimeException e) {
+ assertThat(e).isSameAs(thrown);
+ verify(settings).unload();
+ }
+ }
+
+ @Test
+ public void doFilter_unloads_Settings_even_if_UserSessionInitializer_removeUserSession_fails() throws Exception {
+ RuntimeException thrown = mockUserSessionInitializerRemoveUserSessionFailing();
+
+ try {
+ underTest.doFilter(request, response, chain);
+ fail("A RuntimeException should have been thrown");
+ } catch (RuntimeException e) {
+ assertThat(e).isSameAs(thrown);
+ verify(settings).unload();
+ }
+ }
+
+ @Test
+ public void doFilter_loads_and_unloads_DefaultOrganizationCache() throws Exception {
+ mockNoUserSessionInitializer();
+
+ underTest.doFilter(request, response, chain);
+
+ InOrder inOrder = inOrder(defaultOrganizationCache);
+ inOrder.verify(defaultOrganizationCache).load();
+ inOrder.verify(defaultOrganizationCache).unload();
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void doFilter_unloads_DefaultOrganizationCache_even_if_chain_throws_exception() throws Exception {
+ mockNoUserSessionInitializer();
+ RuntimeException thrown = mockChainDoFilterError();
+
+ try {
+ underTest.doFilter(request, response, chain);
+ fail("A RuntimeException should have been thrown");
+ } catch (RuntimeException e) {
+ assertThat(e).isSameAs(thrown);
+ verify(defaultOrganizationCache).unload();
+ }
+ }
+
+ @Test
+ public void doFilter_unloads_DefaultOrganizationCache_even_if_Settings_unload_fails() throws Exception {
+ mockNoUserSessionInitializer();
+ RuntimeException thrown = new RuntimeException("Faking Settings.unload failing");
+ doThrow(thrown)
+ .when(settings)
+ .unload();
+
+ try {
+ underTest.doFilter(request, response, chain);
+ fail("A RuntimeException should have been thrown");
+ } catch (RuntimeException e) {
+ assertThat(e).isSameAs(thrown);
+ verify(defaultOrganizationCache).unload();
+ }
+ }
+
+ @Test
+ public void doFilter_unloads_DefaultOrganizationCache_even_if_UserSessionInitializer_removeUserSession_fails() throws Exception {
+ RuntimeException thrown = mockUserSessionInitializerRemoveUserSessionFailing();
+
+ try {
+ underTest.doFilter(request, response, chain);
+ fail("A RuntimeException should have been thrown");
+ } catch (RuntimeException e) {
+ assertThat(e).isSameAs(thrown);
+ verify(defaultOrganizationCache).unload();
+ }
+ }
+
@Test
public void just_for_fun_and_coverage() throws ServletException {
UserSessionFilter filter = new UserSessionFilter();
filter.destroy();
// do not fail
}
+
+ private void mockNoUserSessionInitializer() {
+ when(platform.getContainer().getComponentByType(UserSessionInitializer.class)).thenReturn(null);
+ }
+
+ private void mockUserSessionInitializer(boolean value) {
+ when(platform.getContainer().getComponentByType(UserSessionInitializer.class)).thenReturn(userSessionInitializer);
+ when(userSessionInitializer.initUserSession(request, response)).thenReturn(value);
+ }
+
+ private RuntimeException mockUserSessionInitializerRemoveUserSessionFailing() {
+ when(platform.getContainer().getComponentByType(UserSessionInitializer.class)).thenReturn(userSessionInitializer);
+ RuntimeException thrown = new RuntimeException("Faking UserSessionInitializer.removeUserSession failing");
+ doThrow(thrown)
+ .when(userSessionInitializer)
+ .removeUserSession();
+ return thrown;
+ }
+
+ private RuntimeException mockChainDoFilterError() throws IOException, ServletException {
+ RuntimeException thrown = new RuntimeException("Faking chain.doFilter failing");
+ doThrow(thrown)
+ .when(chain)
+ .doFilter(request, response);
+ return thrown;
+ }
}