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