3 * Copyright (C) 2009-2017 SonarSource SA
4 * mailto:info AT sonarsource DOT com
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 package org.sonar.server.edition;
22 import com.google.common.collect.ImmutableSet;
23 import java.util.Arrays;
25 import java.util.Optional;
26 import javax.annotation.Nullable;
27 import org.picocontainer.Startable;
28 import org.sonar.db.DbClient;
29 import org.sonar.db.DbSession;
30 import org.sonar.db.property.InternalPropertiesDao;
32 import static com.google.common.base.Preconditions.checkArgument;
33 import static com.google.common.base.Preconditions.checkState;
34 import static java.util.Objects.requireNonNull;
35 import static java.util.Optional.empty;
36 import static org.sonar.server.edition.EditionManagementState.PendingStatus.AUTOMATIC_IN_PROGRESS;
37 import static org.sonar.server.edition.EditionManagementState.PendingStatus.AUTOMATIC_READY;
38 import static org.sonar.server.edition.EditionManagementState.PendingStatus.MANUAL_IN_PROGRESS;
39 import static org.sonar.server.edition.EditionManagementState.PendingStatus.NONE;
40 import static org.sonar.server.edition.EditionManagementState.PendingStatus.UNINSTALL_IN_PROGRESS;
42 public class StandaloneEditionManagementStateImpl implements MutableEditionManagementState, Startable {
43 private static final String CURRENT_EDITION_KEY = "currentEditionKey";
44 private static final String PENDING_INSTALLATION_STATUS = "pendingInstallStatus";
45 private static final String PENDING_EDITION_KEY = "pendingEditionKey";
46 private static final String PENDING_LICENSE = "pendingLicense";
47 private static final String INSTALL_ERROR_MESSAGE = "installError";
49 private final DbClient dbClient;
50 private String currentEditionKey;
51 private PendingStatus pendingInstallationStatus;
52 private String pendingEditionKey;
53 private String pendingLicense;
54 private String installErrorMessage;
56 public StandaloneEditionManagementStateImpl(DbClient dbClient) {
57 this.dbClient = dbClient;
62 try (DbSession dbSession = dbClient.openSession(false)) {
63 // load current state value
64 Map<String, Optional<String>> internalPropertyValues = dbClient.internalPropertiesDao().selectByKeys(dbSession,
65 ImmutableSet.of(CURRENT_EDITION_KEY, PENDING_INSTALLATION_STATUS, PENDING_EDITION_KEY, PENDING_LICENSE, INSTALL_ERROR_MESSAGE));
66 this.currentEditionKey = internalPropertyValues.getOrDefault(CURRENT_EDITION_KEY, empty())
67 .map(StandaloneEditionManagementStateImpl::emptyToNull)
69 this.pendingInstallationStatus = internalPropertyValues.getOrDefault(PENDING_INSTALLATION_STATUS, empty())
70 .map(StandaloneEditionManagementStateImpl::emptyToNull)
71 .map(PendingStatus::valueOf)
73 this.pendingEditionKey = internalPropertyValues.getOrDefault(PENDING_EDITION_KEY, empty())
74 .map(StandaloneEditionManagementStateImpl::emptyToNull)
76 this.pendingLicense = internalPropertyValues.getOrDefault(PENDING_LICENSE, empty())
77 .map(StandaloneEditionManagementStateImpl::emptyToNull)
79 this.installErrorMessage = internalPropertyValues.getOrDefault(INSTALL_ERROR_MESSAGE, empty())
80 .map(StandaloneEditionManagementStateImpl::emptyToNull)
91 public Optional<String> getCurrentEditionKey() {
93 return Optional.ofNullable(currentEditionKey);
97 public PendingStatus getPendingInstallationStatus() {
99 return pendingInstallationStatus;
103 public Optional<String> getPendingEditionKey() {
105 return Optional.ofNullable(pendingEditionKey);
109 public Optional<String> getPendingLicense() {
111 return Optional.ofNullable(pendingLicense);
115 public Optional<String> getInstallErrorMessage() {
117 return Optional.ofNullable(installErrorMessage);
121 public synchronized PendingStatus startAutomaticInstall(License license) {
123 checkLicense(license);
124 changeStatusToFrom(AUTOMATIC_IN_PROGRESS, NONE);
125 this.pendingLicense = license.getContent();
126 this.pendingEditionKey = license.getEditionKey();
127 this.installErrorMessage = null;
129 return this.pendingInstallationStatus;
133 public synchronized PendingStatus startManualInstall(License license) {
135 checkLicense(license);
136 changeStatusToFrom(MANUAL_IN_PROGRESS, NONE);
137 this.pendingLicense = license.getContent();
138 this.pendingEditionKey = license.getEditionKey();
139 this.pendingInstallationStatus = MANUAL_IN_PROGRESS;
140 this.installErrorMessage = null;
142 return this.pendingInstallationStatus;
146 public synchronized PendingStatus newEditionWithoutInstall(String newEditionKey) {
148 requireNonNull(newEditionKey, "newEditionKey can't be null");
149 checkArgument(!newEditionKey.isEmpty(), "newEditionKey can't be empty");
150 changeStatusToFrom(NONE, NONE);
151 this.currentEditionKey = newEditionKey;
152 this.installErrorMessage = null;
154 return this.pendingInstallationStatus;
158 public synchronized PendingStatus automaticInstallReady() {
160 changeStatusToFrom(AUTOMATIC_READY, AUTOMATIC_IN_PROGRESS);
161 this.installErrorMessage = null;
163 return this.pendingInstallationStatus;
167 public synchronized PendingStatus installFailed(@Nullable String errorMessage) {
169 changeStatusToFrom(NONE, AUTOMATIC_IN_PROGRESS, AUTOMATIC_READY, MANUAL_IN_PROGRESS);
170 this.installErrorMessage = nullableTrimmedEmptyToNull(errorMessage);
171 this.pendingEditionKey = null;
172 this.pendingLicense = null;
174 return this.pendingInstallationStatus;
178 public synchronized PendingStatus finalizeInstallation() {
180 changeStatusToFrom(NONE, AUTOMATIC_READY, MANUAL_IN_PROGRESS, UNINSTALL_IN_PROGRESS);
182 this.pendingInstallationStatus = NONE;
183 this.currentEditionKey = this.pendingEditionKey;
184 this.pendingEditionKey = null;
185 this.pendingLicense = null;
187 return this.pendingInstallationStatus;
191 public synchronized PendingStatus uninstall() {
193 changeStatusToFrom(UNINSTALL_IN_PROGRESS, NONE);
194 checkState(currentEditionKey != null, "There is no edition currently installed");
196 this.pendingInstallationStatus = UNINSTALL_IN_PROGRESS;
197 this.pendingEditionKey = null;
198 this.pendingLicense = null;
199 this.currentEditionKey = null;
201 return this.pendingInstallationStatus;
204 private void ensureStarted() {
205 checkState(pendingInstallationStatus != null, "%s is not started", getClass().getSimpleName());
208 private void changeStatusToFrom(PendingStatus newStatus, PendingStatus... validPendingStatuses) {
209 checkState(Arrays.stream(validPendingStatuses).anyMatch(s -> s == pendingInstallationStatus),
210 "Can't move to %s when status is %s (should be any of %s)",
211 newStatus, pendingInstallationStatus, Arrays.toString(validPendingStatuses));
212 this.pendingInstallationStatus = newStatus;
215 private void persistProperties() {
216 try (DbSession dbSession = dbClient.openSession(false)) {
217 InternalPropertiesDao internalPropertiesDao = dbClient.internalPropertiesDao();
218 saveInternalProperty(internalPropertiesDao, dbSession, PENDING_EDITION_KEY, pendingEditionKey);
219 saveInternalProperty(internalPropertiesDao, dbSession, PENDING_LICENSE, pendingLicense);
220 saveInternalProperty(internalPropertiesDao, dbSession, INSTALL_ERROR_MESSAGE, installErrorMessage);
221 saveInternalProperty(internalPropertiesDao, dbSession, CURRENT_EDITION_KEY, currentEditionKey);
222 saveInternalProperty(internalPropertiesDao, dbSession, PENDING_INSTALLATION_STATUS, pendingInstallationStatus.name());
227 private static void saveInternalProperty(InternalPropertiesDao dao, DbSession dbSession, String key, @Nullable String value) {
229 dao.saveAsEmpty(dbSession, key);
231 dao.save(dbSession, key, value);
235 private static void checkLicense(License license) {
236 requireNonNull(license, "license can't be null");
239 private static String nullableTrimmedEmptyToNull(@Nullable String s) {
244 return v.isEmpty() ? null : v;
247 private static String emptyToNull(String s) {
248 return s.isEmpty() ? null : s;