aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-server
diff options
context:
space:
mode:
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>2017-10-18 18:09:16 +0200
committerGrégoire Aubert <gregoire.aubert@sonarsource.com>2017-10-23 08:01:13 -0700
commit88bccc2a748c04602940dd41c9c8fbe09dacbc7c (patch)
tree02dd9eb95c74d282364d2d5aa30757ad59c4e352 /server/sonar-server
parent9c48d41cee8baa7ae557e77faf22afce0c4a0b85 (diff)
downloadsonarqube-88bccc2a748c04602940dd41c9c8fbe09dacbc7c.tar.gz
sonarqube-88bccc2a748c04602940dd41c9c8fbe09dacbc7c.zip
SONAR-10002 make StandaloneEditionManagementStateImpl thread safe
and no state not persisted to DB can be read
Diffstat (limited to 'server/sonar-server')
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/edition/StandaloneEditionManagementStateImpl.java283
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/edition/StandaloneEditionManagementStateImplTest.java2
2 files changed, 202 insertions, 83 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/edition/StandaloneEditionManagementStateImpl.java b/server/sonar-server/src/main/java/org/sonar/server/edition/StandaloneEditionManagementStateImpl.java
index 25b68072782..475ed421c36 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/edition/StandaloneEditionManagementStateImpl.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/edition/StandaloneEditionManagementStateImpl.java
@@ -23,7 +23,9 @@ import com.google.common.collect.ImmutableSet;
import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
+import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
import org.picocontainer.Startable;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
@@ -47,11 +49,8 @@ public class StandaloneEditionManagementStateImpl implements MutableEditionManag
private static final String INSTALL_ERROR_MESSAGE = "installError";
private final DbClient dbClient;
- private String currentEditionKey;
- private PendingStatus pendingInstallationStatus;
- private String pendingEditionKey;
- private String pendingLicense;
- private String installErrorMessage;
+ @CheckForNull
+ private State state;
public StandaloneEditionManagementStateImpl(DbClient dbClient) {
this.dbClient = dbClient;
@@ -63,22 +62,26 @@ public class StandaloneEditionManagementStateImpl implements MutableEditionManag
// load current state value
Map<String, Optional<String>> internalPropertyValues = dbClient.internalPropertiesDao().selectByKeys(dbSession,
ImmutableSet.of(CURRENT_EDITION_KEY, PENDING_INSTALLATION_STATUS, PENDING_EDITION_KEY, PENDING_LICENSE, INSTALL_ERROR_MESSAGE));
- this.currentEditionKey = internalPropertyValues.getOrDefault(CURRENT_EDITION_KEY, empty())
- .map(StandaloneEditionManagementStateImpl::emptyToNull)
- .orElse(null);
- this.pendingInstallationStatus = internalPropertyValues.getOrDefault(PENDING_INSTALLATION_STATUS, empty())
+
+ PendingStatus pendingInstallationStatus = internalPropertyValues.getOrDefault(PENDING_INSTALLATION_STATUS, empty())
.map(StandaloneEditionManagementStateImpl::emptyToNull)
.map(PendingStatus::valueOf)
.orElse(NONE);
- this.pendingEditionKey = internalPropertyValues.getOrDefault(PENDING_EDITION_KEY, empty())
- .map(StandaloneEditionManagementStateImpl::emptyToNull)
- .orElse(null);
- this.pendingLicense = internalPropertyValues.getOrDefault(PENDING_LICENSE, empty())
- .map(StandaloneEditionManagementStateImpl::emptyToNull)
- .orElse(null);
- this.installErrorMessage = internalPropertyValues.getOrDefault(INSTALL_ERROR_MESSAGE, empty())
- .map(StandaloneEditionManagementStateImpl::emptyToNull)
- .orElse(null);
+ State.Builder builder = State.newBuilder(pendingInstallationStatus);
+ builder
+ .setCurrentEditionKey(internalPropertyValues.getOrDefault(CURRENT_EDITION_KEY, empty())
+ .map(StandaloneEditionManagementStateImpl::emptyToNull)
+ .orElse(null))
+ .setPendingEditionKey(internalPropertyValues.getOrDefault(PENDING_EDITION_KEY, empty())
+ .map(StandaloneEditionManagementStateImpl::emptyToNull)
+ .orElse(null))
+ .setPendingLicense(internalPropertyValues.getOrDefault(PENDING_LICENSE, empty())
+ .map(StandaloneEditionManagementStateImpl::emptyToNull)
+ .orElse(null))
+ .setInstallErrorMessage(internalPropertyValues.getOrDefault(INSTALL_ERROR_MESSAGE, empty())
+ .map(StandaloneEditionManagementStateImpl::emptyToNull)
+ .orElse(null));
+ state = builder.build();
}
}
@@ -90,56 +93,57 @@ public class StandaloneEditionManagementStateImpl implements MutableEditionManag
@Override
public Optional<String> getCurrentEditionKey() {
ensureStarted();
- return Optional.ofNullable(currentEditionKey);
+ return Optional.ofNullable(state.getCurrentEditionKey());
}
@Override
public PendingStatus getPendingInstallationStatus() {
ensureStarted();
- return pendingInstallationStatus;
+ return state.getPendingInstallationStatus();
}
@Override
public Optional<String> getPendingEditionKey() {
ensureStarted();
- return Optional.ofNullable(pendingEditionKey);
+ return Optional.ofNullable(state.getPendingEditionKey());
}
@Override
public Optional<String> getPendingLicense() {
ensureStarted();
- return Optional.ofNullable(pendingLicense);
+ return Optional.ofNullable(state.getPendingLicense());
}
@Override
public Optional<String> getInstallErrorMessage() {
ensureStarted();
- return Optional.ofNullable(installErrorMessage);
+ return Optional.ofNullable(state.getInstallErrorMessage());
}
@Override
public synchronized PendingStatus startAutomaticInstall(License license) {
ensureStarted();
checkLicense(license);
- changeStatusToFrom(AUTOMATIC_IN_PROGRESS, NONE);
- this.pendingLicense = license.getContent();
- this.pendingEditionKey = license.getEditionKey();
- this.installErrorMessage = null;
- persistProperties();
- return this.pendingInstallationStatus;
+ State newState = changeStatusToFrom(AUTOMATIC_IN_PROGRESS, NONE)
+ .setPendingLicense(license.getContent())
+ .setPendingEditionKey(license.getEditionKey())
+ .clearAutomaticInstallErrorMessage()
+ .build();
+ persistProperties(newState);
+ return newState.getPendingInstallationStatus();
}
@Override
public synchronized PendingStatus startManualInstall(License license) {
ensureStarted();
checkLicense(license);
- changeStatusToFrom(MANUAL_IN_PROGRESS, NONE);
- this.pendingLicense = license.getContent();
- this.pendingEditionKey = license.getEditionKey();
- this.pendingInstallationStatus = MANUAL_IN_PROGRESS;
- this.installErrorMessage = null;
- persistProperties();
- return this.pendingInstallationStatus;
+ State newState = changeStatusToFrom(MANUAL_IN_PROGRESS, NONE)
+ .setPendingLicense(license.getContent())
+ .setPendingEditionKey(license.getEditionKey())
+ .clearAutomaticInstallErrorMessage()
+ .build();
+ persistProperties(newState);
+ return newState.getPendingInstallationStatus();
}
@Override
@@ -147,89 +151,94 @@ public class StandaloneEditionManagementStateImpl implements MutableEditionManag
ensureStarted();
requireNonNull(newEditionKey, "newEditionKey can't be null");
checkArgument(!newEditionKey.isEmpty(), "newEditionKey can't be empty");
- changeStatusToFrom(NONE, NONE);
- this.currentEditionKey = newEditionKey;
- this.installErrorMessage = null;
- persistProperties();
- return this.pendingInstallationStatus;
+ State newState = changeStatusToFrom(NONE, NONE)
+ .setCurrentEditionKey(newEditionKey)
+ .clearAutomaticInstallErrorMessage()
+ .build();
+ persistProperties(newState);
+ return newState.getPendingInstallationStatus();
}
@Override
public synchronized PendingStatus automaticInstallReady() {
ensureStarted();
- changeStatusToFrom(AUTOMATIC_READY, AUTOMATIC_IN_PROGRESS);
- this.installErrorMessage = null;
- persistProperties();
- return this.pendingInstallationStatus;
+ State newState = changeStatusToFrom(AUTOMATIC_READY, AUTOMATIC_IN_PROGRESS)
+ .clearAutomaticInstallErrorMessage()
+ .build();
+ persistProperties(newState);
+ return newState.getPendingInstallationStatus();
}
@Override
public synchronized PendingStatus installFailed(@Nullable String errorMessage) {
ensureStarted();
- changeStatusToFrom(NONE, AUTOMATIC_IN_PROGRESS, AUTOMATIC_READY, MANUAL_IN_PROGRESS);
- this.installErrorMessage = nullableTrimmedEmptyToNull(errorMessage);
- this.pendingEditionKey = null;
- this.pendingLicense = null;
- persistProperties();
- return this.pendingInstallationStatus;
+ State newState = changeStatusToFrom(NONE, AUTOMATIC_IN_PROGRESS, AUTOMATIC_READY, MANUAL_IN_PROGRESS)
+ .setInstallErrorMessage(nullableTrimmedEmptyToNull(errorMessage))
+ .clearPendingFields()
+ .build();
+ persistProperties(newState);
+ return newState.getPendingInstallationStatus();
}
@Override
public synchronized void clearInstallErrorMessage() {
ensureStarted();
- if (this.installErrorMessage != null) {
- this.installErrorMessage = null;
- persistProperties();
+ State currentState = this.state;
+ if (currentState.getInstallErrorMessage() != null) {
+ State newState = State.newBuilder(currentState)
+ .clearAutomaticInstallErrorMessage()
+ .build();
+ persistProperties(newState);
}
}
@Override
public synchronized PendingStatus finalizeInstallation() {
ensureStarted();
- changeStatusToFrom(NONE, AUTOMATIC_READY, MANUAL_IN_PROGRESS, UNINSTALL_IN_PROGRESS);
-
- this.pendingInstallationStatus = NONE;
- this.currentEditionKey = this.pendingEditionKey;
- this.pendingEditionKey = null;
- this.pendingLicense = null;
- persistProperties();
- return this.pendingInstallationStatus;
+ State newState = changeStatusToFrom(NONE, AUTOMATIC_READY, MANUAL_IN_PROGRESS, UNINSTALL_IN_PROGRESS)
+ .commitPendingEditionKey()
+ .clearPendingFields()
+ .build();
+ persistProperties(newState);
+ return newState.getPendingInstallationStatus();
}
@Override
public synchronized PendingStatus uninstall() {
ensureStarted();
- changeStatusToFrom(UNINSTALL_IN_PROGRESS, NONE);
- checkState(currentEditionKey != null, "There is no edition currently installed");
-
- this.pendingInstallationStatus = UNINSTALL_IN_PROGRESS;
- this.pendingEditionKey = null;
- this.pendingLicense = null;
- this.currentEditionKey = null;
- persistProperties();
- return this.pendingInstallationStatus;
+ State.Builder builder = changeStatusToFrom(UNINSTALL_IN_PROGRESS, NONE);
+ checkState(state.currentEditionKey != null, "There is no edition currently installed");
+ State newState = builder
+ .clearPendingFields()
+ .clearCurrentEditionKey()
+ .clearAutomaticInstallErrorMessage()
+ .build();
+ persistProperties(newState);
+ return newState.getPendingInstallationStatus();
}
private void ensureStarted() {
- checkState(pendingInstallationStatus != null, "%s is not started", getClass().getSimpleName());
+ checkState(state != null, "%s is not started", getClass().getSimpleName());
}
- private void changeStatusToFrom(PendingStatus newStatus, PendingStatus... validPendingStatuses) {
- checkState(Arrays.stream(validPendingStatuses).anyMatch(s -> s == pendingInstallationStatus),
+ private State.Builder changeStatusToFrom(PendingStatus newStatus, PendingStatus... validPendingStatuses) {
+ State currentState = this.state;
+ checkState(Arrays.stream(validPendingStatuses).anyMatch(s -> s == currentState.getPendingInstallationStatus()),
"Can't move to %s when status is %s (should be any of %s)",
- newStatus, pendingInstallationStatus, Arrays.toString(validPendingStatuses));
- this.pendingInstallationStatus = newStatus;
+ newStatus, currentState.getPendingInstallationStatus(), Arrays.toString(validPendingStatuses));
+ return State.newBuilder(currentState, newStatus);
}
- private void persistProperties() {
+ private void persistProperties(State newState) {
try (DbSession dbSession = dbClient.openSession(false)) {
InternalPropertiesDao internalPropertiesDao = dbClient.internalPropertiesDao();
- saveInternalProperty(internalPropertiesDao, dbSession, PENDING_EDITION_KEY, pendingEditionKey);
- saveInternalProperty(internalPropertiesDao, dbSession, PENDING_LICENSE, pendingLicense);
- saveInternalProperty(internalPropertiesDao, dbSession, INSTALL_ERROR_MESSAGE, installErrorMessage);
- saveInternalProperty(internalPropertiesDao, dbSession, CURRENT_EDITION_KEY, currentEditionKey);
- saveInternalProperty(internalPropertiesDao, dbSession, PENDING_INSTALLATION_STATUS, pendingInstallationStatus.name());
+ saveInternalProperty(internalPropertiesDao, dbSession, PENDING_EDITION_KEY, newState.getPendingEditionKey());
+ saveInternalProperty(internalPropertiesDao, dbSession, PENDING_LICENSE, newState.getPendingLicense());
+ saveInternalProperty(internalPropertiesDao, dbSession, INSTALL_ERROR_MESSAGE, newState.getInstallErrorMessage());
+ saveInternalProperty(internalPropertiesDao, dbSession, CURRENT_EDITION_KEY, newState.getCurrentEditionKey());
+ saveInternalProperty(internalPropertiesDao, dbSession, PENDING_INSTALLATION_STATUS, newState.getPendingInstallationStatus().name());
dbSession.commit();
+ this.state = newState;
}
}
@@ -256,4 +265,114 @@ public class StandaloneEditionManagementStateImpl implements MutableEditionManag
private static String emptyToNull(String s) {
return s.isEmpty() ? null : s;
}
+
+ @Immutable
+ private static final class State {
+ private final String currentEditionKey;
+ private final PendingStatus pendingInstallationStatus;
+ private final String pendingEditionKey;
+ private final String pendingLicense;
+ private final String installErrorMessage;
+
+ public State(Builder builder) {
+ this.currentEditionKey = builder.currentEditionKey;
+ this.pendingInstallationStatus = builder.pendingInstallationStatus;
+ this.pendingEditionKey = builder.pendingEditionKey;
+ this.pendingLicense = builder.pendingLicense;
+ this.installErrorMessage = builder.installErrorMessage;
+ }
+
+ public String getCurrentEditionKey() {
+ return currentEditionKey;
+ }
+
+ public PendingStatus getPendingInstallationStatus() {
+ return pendingInstallationStatus;
+ }
+
+ public String getPendingEditionKey() {
+ return pendingEditionKey;
+ }
+
+ public String getPendingLicense() {
+ return pendingLicense;
+ }
+
+ public String getInstallErrorMessage() {
+ return installErrorMessage;
+ }
+
+ public static Builder newBuilder(PendingStatus pendingInstallationStatus) {
+ return new Builder(pendingInstallationStatus);
+ }
+
+ public static Builder newBuilder(State from) {
+ return newBuilder(from, from.getPendingInstallationStatus());
+ }
+
+ public static Builder newBuilder(State from, PendingStatus newStatus) {
+ return new Builder(newStatus)
+ .setCurrentEditionKey(from.currentEditionKey)
+ .setPendingEditionKey(from.pendingEditionKey)
+ .setPendingLicense(from.pendingLicense)
+ .setInstallErrorMessage(from.installErrorMessage);
+ }
+
+ private static class Builder {
+ private PendingStatus pendingInstallationStatus;
+ private String currentEditionKey;
+ private String pendingEditionKey;
+ private String pendingLicense;
+ private String installErrorMessage;
+
+ private Builder(PendingStatus pendingInstallationStatus) {
+ this.pendingInstallationStatus = requireNonNull(pendingInstallationStatus);
+ }
+
+ public Builder setCurrentEditionKey(@Nullable String currentEditionKey) {
+ this.currentEditionKey = currentEditionKey;
+ return this;
+ }
+
+ public Builder setPendingEditionKey(@Nullable String pendingEditionKey) {
+ this.pendingEditionKey = pendingEditionKey;
+ return this;
+ }
+
+ public Builder setPendingLicense(@Nullable String pendingLicense) {
+ this.pendingLicense = pendingLicense;
+ return this;
+ }
+
+ public Builder setInstallErrorMessage(@Nullable String installErrorMessage) {
+ this.installErrorMessage = installErrorMessage;
+ return this;
+ }
+
+ public Builder commitPendingEditionKey() {
+ this.currentEditionKey = pendingEditionKey;
+ return this;
+ }
+
+ public Builder clearCurrentEditionKey() {
+ this.currentEditionKey = null;
+ return this;
+ }
+
+ public Builder clearPendingFields() {
+ this.pendingEditionKey = null;
+ this.pendingLicense = null;
+ return this;
+ }
+
+ public Builder clearAutomaticInstallErrorMessage() {
+ this.installErrorMessage = null;
+ return this;
+ }
+
+ public State build() {
+ return new State(this);
+ }
+ }
+ }
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/edition/StandaloneEditionManagementStateImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/edition/StandaloneEditionManagementStateImplTest.java
index ea16c7da7e1..f999efc09e4 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/edition/StandaloneEditionManagementStateImplTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/edition/StandaloneEditionManagementStateImplTest.java
@@ -1070,7 +1070,7 @@ public class StandaloneEditionManagementStateImplTest {
}
@Test
- public void installFailed_after_installFailed() {
+ public void installFailed_fails_with_ISE_after_installFailed() {
underTest.start();
underTest.startAutomaticInstall(LICENSE_WITHOUT_PLUGINS);
underTest.installFailed(nullableErrorMessage);