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    }