001/* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2008-2012 SonarSource 004 * mailto:contact AT sonarsource DOT com 005 * 006 * Sonar 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 * Sonar 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 017 * License along with Sonar; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 019 */ 020package org.sonar.api.config; 021 022import com.google.common.base.Strings; 023import com.google.common.collect.Lists; 024import com.google.common.collect.Maps; 025import org.apache.commons.lang.ArrayUtils; 026import org.apache.commons.lang.StringUtils; 027import org.sonar.api.BatchComponent; 028import org.sonar.api.ServerComponent; 029import org.sonar.api.utils.DateUtils; 030 031import javax.annotation.Nullable; 032import java.util.*; 033 034/** 035 * Project Settings on batch side, Global Settings on server side. This component does not access to database, so 036 * property changed via setter methods are not persisted. 037 * <p/> 038 * <p> 039 * This component replaces the deprecated org.apache.commons.configuration.Configuration 040 * </p> 041 * 042 * @since 2.12 043 */ 044public class Settings implements BatchComponent, ServerComponent { 045 046 protected final Map<String, String> properties; 047 protected final PropertyDefinitions definitions; 048 private final Encryption encryption; 049 050 public Settings() { 051 this(new PropertyDefinitions()); 052 } 053 054 public Settings(PropertyDefinitions definitions) { 055 this.properties = Maps.newHashMap(); 056 this.definitions = definitions; 057 this.encryption = new Encryption(this); 058 } 059 060 /** 061 * Clone settings. Actions are not propagated to cloned settings. 062 * 063 * @since 3.1 064 */ 065 public Settings(Settings other) { 066 this.properties = Maps.newHashMap(other.properties); 067 this.definitions = other.definitions; 068 this.encryption = other.encryption; 069 } 070 071 public final Encryption getEncryption() { 072 return encryption; 073 } 074 075 public final String getDefaultValue(String key) { 076 return definitions.getDefaultValue(key); 077 } 078 079 public final boolean hasKey(String key) { 080 return properties.containsKey(key); 081 } 082 083 public final boolean hasDefaultValue(String key) { 084 return StringUtils.isNotEmpty(getDefaultValue(key)); 085 } 086 087 public final String getString(String key) { 088 String value = properties.get(key); 089 if (value == null) { 090 value = getDefaultValue(key); 091 } else if (encryption.isEncrypted(value)) { 092 try { 093 value = encryption.decrypt(value); 094 } catch (Exception e) { 095 throw new IllegalStateException("Fail to decrypt the property " + key + ". Please check your secret key.", e); 096 } 097 } 098 return value; 099 } 100 101 /** 102 * Does not decrypt value. 103 */ 104 protected String getClearString(String key) { 105 String value = properties.get(key); 106 if (value == null) { 107 value = getDefaultValue(key); 108 } 109 return value; 110 } 111 112 public final boolean getBoolean(String key) { 113 String value = getString(key); 114 return StringUtils.isNotEmpty(value) && Boolean.parseBoolean(value); 115 } 116 117 /** 118 * @return the value as int. If the property does not exist and has no default value, then 0 is returned. 119 */ 120 public final int getInt(String key) { 121 String value = getString(key); 122 if (StringUtils.isNotEmpty(value)) { 123 return Integer.parseInt(value); 124 } 125 return 0; 126 } 127 128 public final long getLong(String key) { 129 String value = getString(key); 130 if (StringUtils.isNotEmpty(value)) { 131 return Long.parseLong(value); 132 } 133 return 0L; 134 } 135 136 public final Date getDate(String key) { 137 String value = getString(key); 138 if (StringUtils.isNotEmpty(value)) { 139 return DateUtils.parseDate(value); 140 } 141 return null; 142 } 143 144 public final Date getDateTime(String key) { 145 String value = getString(key); 146 if (StringUtils.isNotEmpty(value)) { 147 return DateUtils.parseDateTime(value); 148 } 149 return null; 150 } 151 152 /** 153 * Value is split by comma and trimmed. 154 * <p/> 155 * Examples : 156 * <ul> 157 * <li>"one,two,three " -> ["one", "two", "three"]</li> 158 * <li>" one, two, three " -> ["one", "two", "three"]</li> 159 * <li>"one, , three" -> ["one", "", "three"]</li> 160 * </ul> 161 */ 162 public final String[] getStringArray(String key) { 163 return getStringArrayBySeparator(key, ","); 164 } 165 166 /** 167 * Value is split by carriage returns. 168 * 169 * @return non-null array of lines. The line termination characters are excluded. 170 * @since 3.2 171 */ 172 public final String[] getStringLines(String key) { 173 String value = getString(key); 174 if (Strings.isNullOrEmpty(value)) { 175 return ArrayUtils.EMPTY_STRING_ARRAY; 176 } 177 return value.split("\r?\n|\r", -1); 178 } 179 180 /** 181 * Value is splitted and trimmed. 182 */ 183 public final String[] getStringArrayBySeparator(String key, String separator) { 184 String value = getString(key); 185 if (value != null) { 186 String[] strings = StringUtils.splitByWholeSeparator(value, separator); 187 String[] result = new String[strings.length]; 188 for (int index = 0; index < strings.length; index++) { 189 result[index] = StringUtils.trim(strings[index]); 190 } 191 return result; 192 } 193 return ArrayUtils.EMPTY_STRING_ARRAY; 194 } 195 196 public final List<String> getKeysStartingWith(String prefix) { 197 List<String> result = Lists.newArrayList(); 198 for (String key : properties.keySet()) { 199 if (StringUtils.startsWith(key, prefix)) { 200 result.add(key); 201 } 202 } 203 return result; 204 } 205 206 public final Settings appendProperty(String key, String value) { 207 String newValue = properties.get(key); 208 if (StringUtils.isEmpty(newValue)) { 209 newValue = StringUtils.trim(value); 210 } else { 211 newValue += "," + StringUtils.trim(value); 212 } 213 return setProperty(key, newValue); 214 } 215 216 public final Settings setProperty(String key, @Nullable String value) { 217 if (value == null) { 218 properties.remove(key); 219 doOnRemoveProperty(key); 220 } else { 221 properties.put(key, StringUtils.trim(value)); 222 doOnSetProperty(key, value); 223 } 224 return this; 225 } 226 227 public final Settings setProperty(String key, @Nullable Boolean value) { 228 return setProperty(key, String.valueOf(value)); 229 } 230 231 public final Settings setProperty(String key, @Nullable Integer value) { 232 return setProperty(key, String.valueOf(value)); 233 } 234 235 public final Settings setProperty(String key, @Nullable Long value) { 236 return setProperty(key, String.valueOf(value)); 237 } 238 239 public final Settings setProperty(String key, @Nullable Double value) { 240 return setProperty(key, String.valueOf(value)); 241 } 242 243 public final Settings setProperty(String key, @Nullable Date date) { 244 return setProperty(key, date, false); 245 } 246 247 public final Settings addProperties(Map<String, String> props) { 248 for (Map.Entry<String, String> entry : props.entrySet()) { 249 setProperty(entry.getKey(), entry.getValue()); 250 } 251 return this; 252 } 253 254 public final Settings addProperties(Properties props) { 255 for (Map.Entry<Object, Object> entry : props.entrySet()) { 256 setProperty(entry.getKey().toString(), entry.getValue().toString()); 257 } 258 return this; 259 } 260 261 public final Settings addSystemProperties() { 262 return addProperties(System.getProperties()); 263 } 264 265 public final Settings addEnvironmentVariables() { 266 return addProperties(System.getenv()); 267 } 268 269 public final Settings setProperties(Map<String, String> props) { 270 clear(); 271 return addProperties(props); 272 } 273 274 public final Settings setProperty(String key, @Nullable Date date, boolean includeTime) { 275 return setProperty(key, includeTime ? DateUtils.formatDateTime(date) : DateUtils.formatDate(date)); 276 } 277 278 public final Settings removeProperty(String key) { 279 return setProperty(key, (String) null); 280 } 281 282 public final Settings clear() { 283 properties.clear(); 284 doOnClearProperties(); 285 return this; 286 } 287 288 /** 289 * @return unmodifiable properties 290 */ 291 public final Map<String, String> getProperties() { 292 return Collections.unmodifiableMap(properties); 293 } 294 295 public final PropertyDefinitions getDefinitions() { 296 return definitions; 297 } 298 299 /** 300 * Create empty settings. Definition of available properties is loaded from the given annotated class. 301 * This method is usually used by unit tests. 302 */ 303 public static Settings createForComponent(Object component) { 304 return new Settings(new PropertyDefinitions(component)); 305 } 306 307 protected void doOnSetProperty(String key, @Nullable String value) { 308 } 309 310 protected void doOnRemoveProperty(String key) { 311 } 312 313 protected void doOnClearProperties() { 314 } 315}