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