001/* 002 * SonarQube 003 * Copyright (C) 2009-2018 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 java.util.Optional; 023import org.sonar.api.batch.ScannerSide; 024import org.sonar.api.ce.ComputeEngineSide; 025import org.sonar.api.config.internal.MapSettings; 026import org.sonar.api.server.ServerSide; 027import org.sonarsource.api.sonarlint.SonarLintSide; 028 029/** 030 * Component to get effective configuration. Values of properties depend on the runtime environment: 031 * <ul> 032 * <li>immutable project or module configuration in scanner.</li> 033 * <li>global configuration in web server. It does not allow to get the settings overridden on projects.</li> 034 * <li>project configuration in Compute Engine.</li> 035 * </ul> 036 * 037 * <h3>Usage</h3> 038 * <pre> 039 * public class MyExtension { 040 * 041 * private final Configuration config; 042 * 043 * public MyExtension(Configuration config) { 044 * this.config = config; 045 * } 046 * public void doSomething() { 047 * String fooValue = config.get("sonar.foo").orElse(null); 048 * // .. 049 * } 050 * } 051 * </pre> 052 * 053 * <h3>Scanner example</h3> 054 * Scanner sensor can get the reference on Configuration directly through SensorContext, 055 * without injecting the component into constructor. 056 * 057 * <pre> 058 * public class MySensor implements Sensor { 059 * {@literal @}Override 060 * public void execute(SensorContext context) { 061 * String fooValue = context.config().get("sonar.foo").orElse(null); 062 * // .. 063 * } 064 * } 065 * </pre> 066 * 067 * <p> 068 * For testing, and only for testing, the in-memory implementation {@link MapSettings} can be used. 069 * <pre> 070 * {@literal @}Test 071 * public void my_test() { 072 * MapSettings settings = new MapSettings(); 073 * settings.setProperty("foo", "bar"); 074 * MyExtension underTest = new MyExtension(settings.asConfig()); 075 * // ... 076 * } 077 * </pre> 078 * 079 * @see MapSettings 080 * @see PropertyDefinition 081 * @since 6.5 082 */ 083@ScannerSide 084@ServerSide 085@ComputeEngineSide 086@SonarLintSide 087public interface Configuration { 088 089 /** 090 * The effective value of the specified property. Can return {@code Optional#empty()} if the property is not set and has no defined default value. 091 * <p> 092 * If the property is encrypted with a secret key, then the returned value is decrypted. 093 * </p> 094 * 095 * @throws IllegalStateException if value is encrypted but fails to be decrypted. 096 */ 097 Optional<String> get(String key); 098 099 /** 100 * @return {@code true} if the property has a non-default value, else {@code false}. 101 */ 102 boolean hasKey(String key); 103 104 /** 105 * Used to read multi-valued properties. 106 * <p> 107 * See {@link PropertyDefinition.Builder#multiValues(boolean)} 108 * Multi-valued properties coming from scanner are parsed as CSV lines (ie comma separator and optional double quotes to escape values). 109 * Non quoted values are trimmed. 110 * <br> 111 * Examples : 112 * <ul> 113 * <li>"one,two,three " -> ["one", "two", "three"]</li> 114 * <li>" one, two, three " -> ["one", "two", "three"]</li> 115 * <li>"one, , three" -> ["one", "", "three"]</li> 116 * <li>"one,\"two,three\",\" four \"" -> ["one", "two,three", " four "]</li> 117 * </ul> 118 */ 119 String[] getStringArray(String key); 120 121 /** 122 * Effective value as boolean. It is {@code empty} if {@link #get(String)} is empty or if it 123 * does not return {@code "true"}, even if it's not a boolean representation. 124 * @return {@code true} if the effective value is {@code "true"}, {@code false} for any other non empty value. 125 * If the property does not have value nor default value, then {@code empty} is returned. 126 */ 127 default Optional<Boolean> getBoolean(String key) { 128 return get(key).map(String::trim).map(Boolean::parseBoolean); 129 } 130 131 /** 132 * Effective value as {@code int}. 133 * @return the value as {@code int}. If the property does not have value nor default value, then {@code empty} is returned. 134 * @throws NumberFormatException if value is not empty and is not a parsable integer 135 */ 136 default Optional<Integer> getInt(String key) { 137 try { 138 return get(key).map(String::trim).map(Integer::parseInt); 139 } catch (NumberFormatException e) { 140 throw new IllegalStateException(String.format("The property '%s' is not an int value: %s", key, e.getMessage())); 141 } 142 } 143 144 /** 145 * Effective value as {@code long}. 146 * @return the value as {@code long}. If the property does not have value nor default value, then {@code empty} is returned. 147 * @throws NumberFormatException if value is not empty and is not a parsable {@code long} 148 */ 149 default Optional<Long> getLong(String key) { 150 try { 151 return get(key).map(String::trim).map(Long::parseLong); 152 } catch (NumberFormatException e) { 153 throw new IllegalStateException(String.format("The property '%s' is not an long value: %s", key, e.getMessage())); 154 } 155 } 156 157 /** 158 * Effective value as {@code Float}. 159 * @return the value as {@code Float}. If the property does not have value nor default value, then {@code empty} is returned. 160 * @throws NumberFormatException if value is not empty and is not a parsable number 161 */ 162 default Optional<Float> getFloat(String key) { 163 try { 164 return get(key).map(String::trim).map(Float::valueOf); 165 } catch (NumberFormatException e) { 166 throw new IllegalStateException(String.format("The property '%s' is not an float value: %s", key, e.getMessage())); 167 } 168 } 169 170 /** 171 * Effective value as {@code Double}. 172 * @return the value as {@code Double}. If the property does not have value nor default value, then {@code empty} is returned. 173 * @throws NumberFormatException if value is not empty and is not a parsable number 174 */ 175 default Optional<Double> getDouble(String key) { 176 try { 177 return get(key).map(String::trim).map(Double::valueOf); 178 } catch (NumberFormatException e) { 179 throw new IllegalStateException(String.format("The property '%s' is not an double value: %s", key, e.getMessage())); 180 } 181 } 182 183}