001/* 002 * SonarQube 003 * Copyright (C) 2009-2017 SonarSource SA 004 * mailto:info AT sonarsource DOT com 005 * 006 * This program is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * This program is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public License 017 * along with this program; if not, write to the Free Software Foundation, 018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 019 */ 020package org.sonar.api.config; 021 022import com.google.common.base.Splitter; 023import java.util.ArrayList; 024import java.util.Date; 025import java.util.List; 026import java.util.Map; 027import java.util.Optional; 028import java.util.Properties; 029import java.util.stream.Collectors; 030import javax.annotation.CheckForNull; 031import javax.annotation.Nullable; 032import org.apache.commons.lang.ArrayUtils; 033import org.apache.commons.lang.StringUtils; 034import org.sonar.api.batch.ScannerSide; 035import org.sonar.api.ce.ComputeEngineSide; 036import org.sonar.api.server.ServerSide; 037import org.sonar.api.utils.DateUtils; 038import org.sonarsource.api.sonarlint.SonarLintSide; 039 040import static java.util.Objects.requireNonNull; 041import static org.apache.commons.lang.StringUtils.trim; 042 043/** 044 * @deprecated since 6.5 use {@link Configuration} 045 */ 046@ServerSide 047@ComputeEngineSide 048@ScannerSide 049@SonarLintSide 050@Deprecated 051public abstract class Settings { 052 053 private final PropertyDefinitions definitions; 054 private final Encryption encryption; 055 056 protected Settings(PropertyDefinitions definitions, Encryption encryption) { 057 this.definitions = requireNonNull(definitions); 058 this.encryption = requireNonNull(encryption); 059 } 060 061 protected abstract Optional<String> get(String key); 062 063 /** 064 * Add the settings with the specified key and value, both are trimmed and neither can be null. 065 * 066 * @throws NullPointerException if {@code key} and/or {@code value} is {@code null}. 067 */ 068 protected abstract void set(String key, String value); 069 070 protected abstract void remove(String key); 071 072 /** 073 * Immutable map of the properties that have non-default values. 074 * The default values defined by {@link PropertyDefinitions} are ignored, 075 * so the returned values are not the effective values. Basically only 076 * the non-empty results of {@link #getRawString(String)} are returned. 077 * <p> 078 * Values are not decrypted if they are encrypted with a secret key. 079 * </p> 080 */ 081 public abstract Map<String, String> getProperties(); 082 083 public Encryption getEncryption() { 084 return encryption; 085 } 086 087 /** 088 * The value that overrides the default value. It 089 * may be encrypted with a secret key. Use {@link #getString(String)} to get 090 * the effective and decrypted value. 091 * 092 * @since 6.1 093 */ 094 public Optional<String> getRawString(String key) { 095 return get(definitions.validKey(requireNonNull(key))); 096 } 097 098 /** 099 * All the property definitions declared by core and plugins. 100 */ 101 public PropertyDefinitions getDefinitions() { 102 return definitions; 103 } 104 105 /** 106 * The definition related to the specified property. It may 107 * be empty. 108 * 109 * @since 6.1 110 */ 111 public Optional<PropertyDefinition> getDefinition(String key) { 112 return Optional.ofNullable(definitions.get(key)); 113 } 114 115 /** 116 * @return {@code true} if the property has a non-default value, else {@code false}. 117 */ 118 public boolean hasKey(String key) { 119 return getRawString(key).isPresent(); 120 } 121 122 @CheckForNull 123 public String getDefaultValue(String key) { 124 return definitions.getDefaultValue(key); 125 } 126 127 public boolean hasDefaultValue(String key) { 128 return StringUtils.isNotEmpty(getDefaultValue(key)); 129 } 130 131 /** 132 * The effective value of the specified property. Can return 133 * {@code null} if the property is not set and has no 134 * defined default value. 135 * <p> 136 * If the property is encrypted with a secret key, 137 * then the returned value is decrypted. 138 * </p> 139 * 140 * @throws IllegalStateException if value is encrypted but fails to be decrypted. 141 */ 142 @CheckForNull 143 public String getString(String key) { 144 String effectiveKey = definitions.validKey(key); 145 Optional<String> value = getRawString(effectiveKey); 146 if (!value.isPresent()) { 147 // default values cannot be encrypted, so return value as-is. 148 return getDefaultValue(effectiveKey); 149 } 150 if (encryption.isEncrypted(value.get())) { 151 try { 152 return encryption.decrypt(value.get()); 153 } catch (Exception e) { 154 throw new IllegalStateException("Fail to decrypt the property " + effectiveKey + ". Please check your secret key.", e); 155 } 156 } 157 return value.get(); 158 } 159 160 /** 161 * Effective value as boolean. It is {@code false} if {@link #getString(String)} 162 * does not return {@code "true"}, even if it's not a boolean representation. 163 * @return {@code true} if the effective value is {@code "true"}, else {@code false}. 164 */ 165 public boolean getBoolean(String key) { 166 String value = getString(key); 167 return StringUtils.isNotEmpty(value) && Boolean.parseBoolean(value); 168 } 169 170 /** 171 * Effective value as {@code int}. 172 * @return the value as {@code int}. If the property does not have value nor default value, then {@code 0} is returned. 173 * @throws NumberFormatException if value is not empty and is not a parsable integer 174 */ 175 public int getInt(String key) { 176 String value = getString(key); 177 if (StringUtils.isNotEmpty(value)) { 178 return Integer.parseInt(value); 179 } 180 return 0; 181 } 182 183 /** 184 * Effective value as {@code long}. 185 * @return the value as {@code long}. If the property does not have value nor default value, then {@code 0L} is returned. 186 * @throws NumberFormatException if value is not empty and is not a parsable {@code long} 187 */ 188 public long getLong(String key) { 189 String value = getString(key); 190 if (StringUtils.isNotEmpty(value)) { 191 return Long.parseLong(value); 192 } 193 return 0L; 194 } 195 196 /** 197 * Effective value as {@link Date}, without time fields. Format is {@link DateUtils#DATE_FORMAT}. 198 * 199 * @return the value as a {@link Date}. If the property does not have value nor default value, then {@code null} is returned. 200 * @throws RuntimeException if value is not empty and is not in accordance with {@link DateUtils#DATE_FORMAT}. 201 */ 202 @CheckForNull 203 public Date getDate(String key) { 204 String value = getString(key); 205 if (StringUtils.isNotEmpty(value)) { 206 return DateUtils.parseDate(value); 207 } 208 return null; 209 } 210 211 /** 212 * Effective value as {@link Date}, with time fields. Format is {@link DateUtils#DATETIME_FORMAT}. 213 * 214 * @return the value as a {@link Date}. If the property does not have value nor default value, then {@code null} is returned. 215 * @throws RuntimeException if value is not empty and is not in accordance with {@link DateUtils#DATETIME_FORMAT}. 216 */ 217 @CheckForNull 218 public Date getDateTime(String key) { 219 String value = getString(key); 220 if (StringUtils.isNotEmpty(value)) { 221 return DateUtils.parseDateTime(value); 222 } 223 return null; 224 } 225 226 /** 227 * Effective value as {@code Float}. 228 * @return the value as {@code Float}. If the property does not have value nor default value, then {@code null} is returned. 229 * @throws NumberFormatException if value is not empty and is not a parsable number 230 */ 231 @CheckForNull 232 public Float getFloat(String key) { 233 String value = getString(key); 234 if (StringUtils.isNotEmpty(value)) { 235 try { 236 return Float.valueOf(value); 237 } catch (NumberFormatException e) { 238 throw new IllegalStateException(String.format("The property '%s' is not a float value", key)); 239 } 240 } 241 return null; 242 } 243 244 /** 245 * Effective value as {@code Double}. 246 * @return the value as {@code Double}. If the property does not have value nor default value, then {@code null} is returned. 247 * @throws NumberFormatException if value is not empty and is not a parsable number 248 */ 249 @CheckForNull 250 public Double getDouble(String key) { 251 String value = getString(key); 252 if (StringUtils.isNotEmpty(value)) { 253 try { 254 return Double.valueOf(value); 255 } catch (NumberFormatException e) { 256 throw new IllegalStateException(String.format("The property '%s' is not a double value", key)); 257 } 258 } 259 return null; 260 } 261 262 /** 263 * Value is split by comma and trimmed. Never returns null. 264 * <br> 265 * Examples : 266 * <ul> 267 * <li>"one,two,three " -> ["one", "two", "three"]</li> 268 * <li>" one, two, three " -> ["one", "two", "three"]</li> 269 * <li>"one, , three" -> ["one", "", "three"]</li> 270 * </ul> 271 */ 272 public String[] getStringArray(String key) { 273 String effectiveKey = definitions.validKey(key); 274 Optional<PropertyDefinition> def = getDefinition(effectiveKey); 275 if ((def.isPresent()) && (def.get().multiValues())) { 276 String value = getString(key); 277 if (value == null) { 278 return ArrayUtils.EMPTY_STRING_ARRAY; 279 } 280 281 List<String> values = new ArrayList<>(); 282 for (String v : Splitter.on(",").trimResults().split(value)) { 283 values.add(v.replace("%2C", ",")); 284 } 285 return values.toArray(new String[values.size()]); 286 } 287 288 return getStringArrayBySeparator(key, ","); 289 } 290 291 /** 292 * Value is split by carriage returns. 293 * 294 * @return non-null array of lines. The line termination characters are excluded. 295 * @since 3.2 296 */ 297 public String[] getStringLines(String key) { 298 String value = getString(key); 299 if (StringUtils.isEmpty(value)) { 300 return new String[0]; 301 } 302 return value.split("\r?\n|\r", -1); 303 } 304 305 /** 306 * Value is split and trimmed. 307 */ 308 public String[] getStringArrayBySeparator(String key, String separator) { 309 String value = getString(key); 310 if (value != null) { 311 String[] strings = StringUtils.splitByWholeSeparator(value, separator); 312 String[] result = new String[strings.length]; 313 for (int index = 0; index < strings.length; index++) { 314 result[index] = trim(strings[index]); 315 } 316 return result; 317 } 318 return ArrayUtils.EMPTY_STRING_ARRAY; 319 } 320 321 public Settings appendProperty(String key, @Nullable String value) { 322 Optional<String> existingValue = getRawString(definitions.validKey(key)); 323 String newValue; 324 if (!existingValue.isPresent()) { 325 newValue = trim(value); 326 } else { 327 newValue = existingValue.get() + "," + trim(value); 328 } 329 return setProperty(key, newValue); 330 } 331 332 public Settings setProperty(String key, @Nullable String[] values) { 333 requireNonNull(key, "key can't be null"); 334 String effectiveKey = key.trim(); 335 Optional<PropertyDefinition> def = getDefinition(effectiveKey); 336 if (!def.isPresent() || (!def.get().multiValues())) { 337 throw new IllegalStateException("Fail to set multiple values on a single value property " + key); 338 } 339 340 String text = null; 341 if (values != null) { 342 List<String> escaped = new ArrayList<>(); 343 for (String value : values) { 344 if (null != value) { 345 escaped.add(value.replace(",", "%2C")); 346 } else { 347 escaped.add(""); 348 } 349 } 350 351 String escapedValue = escaped.stream().collect(Collectors.joining(",")); 352 text = trim(escapedValue); 353 } 354 return setProperty(key, text); 355 } 356 357 /** 358 * Change a property value in a restricted scope only, depending on execution context. New value 359 * is <b>never</b> persisted. New value is ephemeral and kept in memory only: 360 * <ul> 361 * <li>during current analysis in the case of scanner stack</li> 362 * <li>during processing of current HTTP request in the case of web server stack</li> 363 * <li>during execution of current task in the case of Compute Engine stack</li> 364 * </ul> 365 * 366 * Property is temporarily removed if the parameter {@code value} is {@code null} 367 */ 368 public Settings setProperty(String key, @Nullable String value) { 369 String validKey = definitions.validKey(key); 370 if (value == null) { 371 removeProperty(validKey); 372 } else { 373 set(validKey, trim(value)); 374 } 375 return this; 376 } 377 378 /** 379 * @see #setProperty(String, String) 380 */ 381 public Settings setProperty(String key, @Nullable Boolean value) { 382 return setProperty(key, value == null ? null : String.valueOf(value)); 383 } 384 385 /** 386 * @see #setProperty(String, String) 387 */ 388 public Settings setProperty(String key, @Nullable Integer value) { 389 return setProperty(key, value == null ? null : String.valueOf(value)); 390 } 391 392 /** 393 * @see #setProperty(String, String) 394 */ 395 public Settings setProperty(String key, @Nullable Long value) { 396 return setProperty(key, value == null ? null : String.valueOf(value)); 397 } 398 399 /** 400 * @see #setProperty(String, String) 401 */ 402 public Settings setProperty(String key, @Nullable Double value) { 403 return setProperty(key, value == null ? null : String.valueOf(value)); 404 } 405 406 /** 407 * @see #setProperty(String, String) 408 */ 409 public Settings setProperty(String key, @Nullable Float value) { 410 return setProperty(key, value == null ? null : String.valueOf(value)); 411 } 412 413 /** 414 * @see #setProperty(String, String) 415 */ 416 public Settings setProperty(String key, @Nullable Date date) { 417 return setProperty(key, date, false); 418 } 419 420 public Settings addProperties(Map<String, String> props) { 421 for (Map.Entry<String, String> entry : props.entrySet()) { 422 setProperty(entry.getKey(), entry.getValue()); 423 } 424 return this; 425 } 426 427 public Settings addProperties(Properties props) { 428 for (Map.Entry<Object, Object> entry : props.entrySet()) { 429 setProperty(entry.getKey().toString(), entry.getValue().toString()); 430 } 431 return this; 432 } 433 434 /** 435 * @see #setProperty(String, String) 436 */ 437 public Settings setProperty(String key, @Nullable Date date, boolean includeTime) { 438 if (date == null) { 439 return removeProperty(key); 440 } 441 return setProperty(key, includeTime ? DateUtils.formatDateTime(date) : DateUtils.formatDate(date)); 442 } 443 444 public Settings removeProperty(String key) { 445 remove(key); 446 return this; 447 } 448 449 public List<String> getKeysStartingWith(String prefix) { 450 return getProperties().keySet().stream() 451 .filter(key -> StringUtils.startsWith(key, prefix)) 452 .collect(Collectors.toList()); 453 } 454 455}