]> source.dussan.org Git - sonarqube.git/blob
475ed421c3695b32cf0ddd1563f015ad2d48557d
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2017 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
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.
10  *
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.
15  *
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.
19  */
20 package org.sonar.server.edition;
21
22 import com.google.common.collect.ImmutableSet;
23 import java.util.Arrays;
24 import java.util.Map;
25 import java.util.Optional;
26 import javax.annotation.CheckForNull;
27 import javax.annotation.Nullable;
28 import javax.annotation.concurrent.Immutable;
29 import org.picocontainer.Startable;
30 import org.sonar.db.DbClient;
31 import org.sonar.db.DbSession;
32 import org.sonar.db.property.InternalPropertiesDao;
33
34 import static com.google.common.base.Preconditions.checkArgument;
35 import static com.google.common.base.Preconditions.checkState;
36 import static java.util.Objects.requireNonNull;
37 import static java.util.Optional.empty;
38 import static org.sonar.server.edition.EditionManagementState.PendingStatus.AUTOMATIC_IN_PROGRESS;
39 import static org.sonar.server.edition.EditionManagementState.PendingStatus.AUTOMATIC_READY;
40 import static org.sonar.server.edition.EditionManagementState.PendingStatus.MANUAL_IN_PROGRESS;
41 import static org.sonar.server.edition.EditionManagementState.PendingStatus.NONE;
42 import static org.sonar.server.edition.EditionManagementState.PendingStatus.UNINSTALL_IN_PROGRESS;
43
44 public class StandaloneEditionManagementStateImpl implements MutableEditionManagementState, Startable {
45   private static final String CURRENT_EDITION_KEY = "currentEditionKey";
46   private static final String PENDING_INSTALLATION_STATUS = "pendingInstallStatus";
47   private static final String PENDING_EDITION_KEY = "pendingEditionKey";
48   private static final String PENDING_LICENSE = "pendingLicense";
49   private static final String INSTALL_ERROR_MESSAGE = "installError";
50
51   private final DbClient dbClient;
52   @CheckForNull
53   private State state;
54
55   public StandaloneEditionManagementStateImpl(DbClient dbClient) {
56     this.dbClient = dbClient;
57   }
58
59   @Override
60   public void start() {
61     try (DbSession dbSession = dbClient.openSession(false)) {
62       // load current state value
63       Map<String, Optional<String>> internalPropertyValues = dbClient.internalPropertiesDao().selectByKeys(dbSession,
64         ImmutableSet.of(CURRENT_EDITION_KEY, PENDING_INSTALLATION_STATUS, PENDING_EDITION_KEY, PENDING_LICENSE, INSTALL_ERROR_MESSAGE));
65
66       PendingStatus pendingInstallationStatus = internalPropertyValues.getOrDefault(PENDING_INSTALLATION_STATUS, empty())
67         .map(StandaloneEditionManagementStateImpl::emptyToNull)
68         .map(PendingStatus::valueOf)
69         .orElse(NONE);
70       State.Builder builder = State.newBuilder(pendingInstallationStatus);
71       builder
72         .setCurrentEditionKey(internalPropertyValues.getOrDefault(CURRENT_EDITION_KEY, empty())
73           .map(StandaloneEditionManagementStateImpl::emptyToNull)
74           .orElse(null))
75         .setPendingEditionKey(internalPropertyValues.getOrDefault(PENDING_EDITION_KEY, empty())
76           .map(StandaloneEditionManagementStateImpl::emptyToNull)
77           .orElse(null))
78         .setPendingLicense(internalPropertyValues.getOrDefault(PENDING_LICENSE, empty())
79           .map(StandaloneEditionManagementStateImpl::emptyToNull)
80           .orElse(null))
81         .setInstallErrorMessage(internalPropertyValues.getOrDefault(INSTALL_ERROR_MESSAGE, empty())
82           .map(StandaloneEditionManagementStateImpl::emptyToNull)
83           .orElse(null));
84       state = builder.build();
85     }
86   }
87
88   @Override
89   public void stop() {
90     // nothing to do
91   }
92
93   @Override
94   public Optional<String> getCurrentEditionKey() {
95     ensureStarted();
96     return Optional.ofNullable(state.getCurrentEditionKey());
97   }
98
99   @Override
100   public PendingStatus getPendingInstallationStatus() {
101     ensureStarted();
102     return state.getPendingInstallationStatus();
103   }
104
105   @Override
106   public Optional<String> getPendingEditionKey() {
107     ensureStarted();
108     return Optional.ofNullable(state.getPendingEditionKey());
109   }
110
111   @Override
112   public Optional<String> getPendingLicense() {
113     ensureStarted();
114     return Optional.ofNullable(state.getPendingLicense());
115   }
116
117   @Override
118   public Optional<String> getInstallErrorMessage() {
119     ensureStarted();
120     return Optional.ofNullable(state.getInstallErrorMessage());
121   }
122
123   @Override
124   public synchronized PendingStatus startAutomaticInstall(License license) {
125     ensureStarted();
126     checkLicense(license);
127     State newState = changeStatusToFrom(AUTOMATIC_IN_PROGRESS, NONE)
128       .setPendingLicense(license.getContent())
129       .setPendingEditionKey(license.getEditionKey())
130       .clearAutomaticInstallErrorMessage()
131       .build();
132     persistProperties(newState);
133     return newState.getPendingInstallationStatus();
134   }
135
136   @Override
137   public synchronized PendingStatus startManualInstall(License license) {
138     ensureStarted();
139     checkLicense(license);
140     State newState = changeStatusToFrom(MANUAL_IN_PROGRESS, NONE)
141       .setPendingLicense(license.getContent())
142       .setPendingEditionKey(license.getEditionKey())
143       .clearAutomaticInstallErrorMessage()
144       .build();
145     persistProperties(newState);
146     return newState.getPendingInstallationStatus();
147   }
148
149   @Override
150   public synchronized PendingStatus newEditionWithoutInstall(String newEditionKey) {
151     ensureStarted();
152     requireNonNull(newEditionKey, "newEditionKey can't be null");
153     checkArgument(!newEditionKey.isEmpty(), "newEditionKey can't be empty");
154     State newState = changeStatusToFrom(NONE, NONE)
155       .setCurrentEditionKey(newEditionKey)
156       .clearAutomaticInstallErrorMessage()
157       .build();
158     persistProperties(newState);
159     return newState.getPendingInstallationStatus();
160   }
161
162   @Override
163   public synchronized PendingStatus automaticInstallReady() {
164     ensureStarted();
165     State newState = changeStatusToFrom(AUTOMATIC_READY, AUTOMATIC_IN_PROGRESS)
166       .clearAutomaticInstallErrorMessage()
167       .build();
168     persistProperties(newState);
169     return newState.getPendingInstallationStatus();
170   }
171
172   @Override
173   public synchronized PendingStatus installFailed(@Nullable String errorMessage) {
174     ensureStarted();
175     State newState = changeStatusToFrom(NONE, AUTOMATIC_IN_PROGRESS, AUTOMATIC_READY, MANUAL_IN_PROGRESS)
176       .setInstallErrorMessage(nullableTrimmedEmptyToNull(errorMessage))
177       .clearPendingFields()
178       .build();
179     persistProperties(newState);
180     return newState.getPendingInstallationStatus();
181   }
182
183   @Override
184   public synchronized void clearInstallErrorMessage() {
185     ensureStarted();
186     State currentState = this.state;
187     if (currentState.getInstallErrorMessage() != null) {
188       State newState = State.newBuilder(currentState)
189         .clearAutomaticInstallErrorMessage()
190         .build();
191       persistProperties(newState);
192     }
193   }
194
195   @Override
196   public synchronized PendingStatus finalizeInstallation() {
197     ensureStarted();
198     State newState = changeStatusToFrom(NONE, AUTOMATIC_READY, MANUAL_IN_PROGRESS, UNINSTALL_IN_PROGRESS)
199       .commitPendingEditionKey()
200       .clearPendingFields()
201       .build();
202     persistProperties(newState);
203     return newState.getPendingInstallationStatus();
204   }
205
206   @Override
207   public synchronized PendingStatus uninstall() {
208     ensureStarted();
209     State.Builder builder = changeStatusToFrom(UNINSTALL_IN_PROGRESS, NONE);
210     checkState(state.currentEditionKey != null, "There is no edition currently installed");
211     State newState = builder
212       .clearPendingFields()
213       .clearCurrentEditionKey()
214       .clearAutomaticInstallErrorMessage()
215       .build();
216     persistProperties(newState);
217     return newState.getPendingInstallationStatus();
218   }
219
220   private void ensureStarted() {
221     checkState(state != null, "%s is not started", getClass().getSimpleName());
222   }
223
224   private State.Builder changeStatusToFrom(PendingStatus newStatus, PendingStatus... validPendingStatuses) {
225     State currentState = this.state;
226     checkState(Arrays.stream(validPendingStatuses).anyMatch(s -> s == currentState.getPendingInstallationStatus()),
227       "Can't move to %s when status is %s (should be any of %s)",
228       newStatus, currentState.getPendingInstallationStatus(), Arrays.toString(validPendingStatuses));
229     return State.newBuilder(currentState, newStatus);
230   }
231
232   private void persistProperties(State newState) {
233     try (DbSession dbSession = dbClient.openSession(false)) {
234       InternalPropertiesDao internalPropertiesDao = dbClient.internalPropertiesDao();
235       saveInternalProperty(internalPropertiesDao, dbSession, PENDING_EDITION_KEY, newState.getPendingEditionKey());
236       saveInternalProperty(internalPropertiesDao, dbSession, PENDING_LICENSE, newState.getPendingLicense());
237       saveInternalProperty(internalPropertiesDao, dbSession, INSTALL_ERROR_MESSAGE, newState.getInstallErrorMessage());
238       saveInternalProperty(internalPropertiesDao, dbSession, CURRENT_EDITION_KEY, newState.getCurrentEditionKey());
239       saveInternalProperty(internalPropertiesDao, dbSession, PENDING_INSTALLATION_STATUS, newState.getPendingInstallationStatus().name());
240       dbSession.commit();
241       this.state = newState;
242     }
243   }
244
245   private static void saveInternalProperty(InternalPropertiesDao dao, DbSession dbSession, String key, @Nullable String value) {
246     if (value == null) {
247       dao.saveAsEmpty(dbSession, key);
248     } else {
249       dao.save(dbSession, key, value);
250     }
251   }
252
253   private static void checkLicense(License license) {
254     requireNonNull(license, "license can't be null");
255   }
256
257   private static String nullableTrimmedEmptyToNull(@Nullable String s) {
258     if (s == null) {
259       return null;
260     }
261     String v = s.trim();
262     return v.isEmpty() ? null : v;
263   }
264
265   private static String emptyToNull(String s) {
266     return s.isEmpty() ? null : s;
267   }
268
269   @Immutable
270   private static final class State {
271     private final String currentEditionKey;
272     private final PendingStatus pendingInstallationStatus;
273     private final String pendingEditionKey;
274     private final String pendingLicense;
275     private final String installErrorMessage;
276
277     public State(Builder builder) {
278       this.currentEditionKey = builder.currentEditionKey;
279       this.pendingInstallationStatus = builder.pendingInstallationStatus;
280       this.pendingEditionKey = builder.pendingEditionKey;
281       this.pendingLicense = builder.pendingLicense;
282       this.installErrorMessage = builder.installErrorMessage;
283     }
284
285     public String getCurrentEditionKey() {
286       return currentEditionKey;
287     }
288
289     public PendingStatus getPendingInstallationStatus() {
290       return pendingInstallationStatus;
291     }
292
293     public String getPendingEditionKey() {
294       return pendingEditionKey;
295     }
296
297     public String getPendingLicense() {
298       return pendingLicense;
299     }
300
301     public String getInstallErrorMessage() {
302       return installErrorMessage;
303     }
304
305     public static Builder newBuilder(PendingStatus pendingInstallationStatus) {
306       return new Builder(pendingInstallationStatus);
307     }
308
309     public static Builder newBuilder(State from) {
310       return newBuilder(from, from.getPendingInstallationStatus());
311     }
312
313     public static Builder newBuilder(State from, PendingStatus newStatus) {
314       return new Builder(newStatus)
315         .setCurrentEditionKey(from.currentEditionKey)
316         .setPendingEditionKey(from.pendingEditionKey)
317         .setPendingLicense(from.pendingLicense)
318         .setInstallErrorMessage(from.installErrorMessage);
319     }
320
321     private static class Builder {
322       private PendingStatus pendingInstallationStatus;
323       private String currentEditionKey;
324       private String pendingEditionKey;
325       private String pendingLicense;
326       private String installErrorMessage;
327
328       private Builder(PendingStatus pendingInstallationStatus) {
329         this.pendingInstallationStatus = requireNonNull(pendingInstallationStatus);
330       }
331
332       public Builder setCurrentEditionKey(@Nullable String currentEditionKey) {
333         this.currentEditionKey = currentEditionKey;
334         return this;
335       }
336
337       public Builder setPendingEditionKey(@Nullable String pendingEditionKey) {
338         this.pendingEditionKey = pendingEditionKey;
339         return this;
340       }
341
342       public Builder setPendingLicense(@Nullable String pendingLicense) {
343         this.pendingLicense = pendingLicense;
344         return this;
345       }
346
347       public Builder setInstallErrorMessage(@Nullable String installErrorMessage) {
348         this.installErrorMessage = installErrorMessage;
349         return this;
350       }
351
352       public Builder commitPendingEditionKey() {
353         this.currentEditionKey = pendingEditionKey;
354         return this;
355       }
356
357       public Builder clearCurrentEditionKey() {
358         this.currentEditionKey = null;
359         return this;
360       }
361
362       public Builder clearPendingFields() {
363         this.pendingEditionKey = null;
364         this.pendingLicense = null;
365         return this;
366       }
367
368       public Builder clearAutomaticInstallErrorMessage() {
369         this.installErrorMessage = null;
370         return this;
371       }
372
373       public State build() {
374         return new State(this);
375       }
376     }
377   }
378 }