001 /*
002 * SonarQube, open source software quality management tool.
003 * Copyright (C) 2008-2014 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * SonarQube 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 * SonarQube 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 */
020 package org.sonar.wsclient;
021
022 import org.sonar.wsclient.internal.HttpRequestFactory;
023 import org.sonar.wsclient.issue.ActionPlanClient;
024 import org.sonar.wsclient.issue.IssueClient;
025 import org.sonar.wsclient.issue.internal.DefaultActionPlanClient;
026 import org.sonar.wsclient.issue.internal.DefaultIssueClient;
027 import org.sonar.wsclient.permissions.PermissionClient;
028 import org.sonar.wsclient.permissions.internal.DefaultPermissionClient;
029 import org.sonar.wsclient.project.ProjectClient;
030 import org.sonar.wsclient.project.internal.DefaultProjectClient;
031 import org.sonar.wsclient.qprofile.QProfileClient;
032 import org.sonar.wsclient.qprofile.internal.DefaultQProfileClient;
033 import org.sonar.wsclient.qualitygate.QualityGateClient;
034 import org.sonar.wsclient.qualitygate.internal.DefaultQualityGateClient;
035 import org.sonar.wsclient.system.SystemClient;
036 import org.sonar.wsclient.system.internal.DefaultSystemClient;
037 import org.sonar.wsclient.user.UserClient;
038 import org.sonar.wsclient.user.internal.DefaultUserClient;
039
040 import javax.annotation.Nullable;
041
042 import java.util.Arrays;
043 import java.util.HashMap;
044 import java.util.Map;
045
046 /**
047 * Entry point of the Java Client for Sonar Web Services. It does not support all web services yet.
048 * <p/>
049 * Example:
050 * <pre>
051 * SonarClient client = SonarClient.create("http://localhost:9000");
052 * IssueClient issueClient = client.issueClient();
053 * </pre>
054 *
055 * @since 3.6
056 */
057 public class SonarClient {
058
059 public static final int DEFAULT_CONNECT_TIMEOUT_MILLISECONDS = 30000;
060 public static final int DEFAULT_READ_TIMEOUT_MILLISECONDS = 60000;
061
062 /**
063 * Visibility relaxed for unit tests
064 */
065 final HttpRequestFactory requestFactory;
066
067 private SonarClient(Builder builder) {
068 this(new HttpRequestFactory(builder.url)
069 .setLogin(builder.login)
070 .setPassword(builder.password)
071 .setProxyHost(builder.proxyHost)
072 .setProxyPort(builder.proxyPort)
073 .setProxyLogin(builder.proxyLogin)
074 .setProxyPassword(builder.proxyPassword)
075 .setConnectTimeoutInMilliseconds(builder.connectTimeoutMs)
076 .setReadTimeoutInMilliseconds(builder.readTimeoutMs));
077 }
078
079 /**
080 * Visible for testing
081 */
082 SonarClient(HttpRequestFactory requestFactory) {
083 this.requestFactory = requestFactory;
084 }
085
086 /**
087 * New client to interact with web services related to issues
088 */
089 public IssueClient issueClient() {
090 return new DefaultIssueClient(requestFactory);
091 }
092
093 /**
094 * New client to interact with web services related to issue action plans
095 */
096 public ActionPlanClient actionPlanClient() {
097 return new DefaultActionPlanClient(requestFactory);
098 }
099
100 /**
101 * New client to interact with web services related to users
102 */
103 public UserClient userClient() {
104 return new DefaultUserClient(requestFactory);
105 }
106
107 /**
108 * New client to interact with web services related to users and groups permissions
109 */
110 public PermissionClient permissionClient() {
111 return new DefaultPermissionClient(requestFactory);
112 }
113
114 /**
115 * New client to interact with web services related to projects
116 */
117 public ProjectClient projectClient() {
118 return new DefaultProjectClient(requestFactory);
119 }
120
121 /**
122 * New client to interact with web services related to quality gates
123 */
124 public QualityGateClient qualityGateClient() {
125 return new DefaultQualityGateClient(requestFactory);
126 }
127
128 /**
129 * New client to interact with web services related to quality profiles
130 */
131 public QProfileClient qProfileClient() {
132 return new DefaultQProfileClient(requestFactory);
133 }
134
135 public SystemClient systemClient() {
136 return new DefaultSystemClient(requestFactory);
137 }
138
139 /**
140 * Create a builder of {@link SonarClient}s.
141 */
142 public static Builder builder() {
143 return new Builder();
144 }
145
146 /**
147 * Create a client with default configuration. Use {@link #builder()} to define
148 * a custom configuration (credentials, HTTP proxy, HTTP timeouts).
149 */
150 public static SonarClient create(String serverUrl) {
151 return builder().url(serverUrl).build();
152 }
153
154 /**
155 * Send a POST request on the given relativeUrl, with provided parameters (can be empty).
156 * The beginning slash (/) of relativeUrl is supported but not mandatory.
157 * <p/>
158 * Example:
159 * <pre> {@code
160 * Map<String,Object> params = new HashMap<>();
161 * params.put("name", "My Quality Gate");
162 * client.post("api/qualitygates/create", params);
163 * }</pre>
164 * @since 4.5
165 * @return the response body
166 */
167 public String post(String relativeUrl, Map<String, Object> params) {
168 return requestFactory.post(relativeUrl, params);
169 }
170
171 /**
172 * Same as {@link #post(String, java.util.Map)} but parameters are defined as an array
173 * of even number of elements (key1, value1, key, value2, ...). Keys must not be null.
174 */
175 public String post(String relativeUrl, Object... params) {
176 return post(relativeUrl, paramsAsMap(params));
177 }
178
179 /**
180 * Send a GET request on the given relativeUrl, with provided parameters (can be empty).
181 * The beginning slash (/) of relativeUrl is supported but not mandatory.
182 * @since 4.5
183 * @return the response body
184 */
185 public String get(String relativeUrl, Map<String, Object> params) {
186 return requestFactory.get(relativeUrl, params);
187 }
188
189 /**
190 * Same as {@link #get(String, java.util.Map)} but parameters are defined as an array
191 * of even number of elements (key1, value1, key, value2, ...). Keys must not be null.
192 */
193 public String get(String relativeUrl, Object... params) {
194 return get(relativeUrl, paramsAsMap(params));
195 }
196
197 private Map<String, Object> paramsAsMap(Object[] params) {
198 if (params.length % 2 == 1) {
199 throw new IllegalArgumentException(String.format(
200 "Expecting even number of elements. Got %s", Arrays.toString(params)));
201 }
202 Map<String, Object> map = new HashMap<String, Object>();
203 for (int index = 0; index < params.length; index++) {
204 if (params[index] == null) {
205 throw new IllegalArgumentException(String.format(
206 "Parameter key can't be null at index %d of %s", index, Arrays.toString(params)));
207 }
208 map.put(params[index].toString(), params[index + 1]);
209 index++;
210 }
211 return map;
212 }
213
214 public static class Builder {
215 private String login, password, url, proxyHost, proxyLogin, proxyPassword;
216 private int proxyPort = 0;
217 private int connectTimeoutMs = DEFAULT_CONNECT_TIMEOUT_MILLISECONDS, readTimeoutMs = DEFAULT_READ_TIMEOUT_MILLISECONDS;
218
219 private Builder() {
220 }
221
222 /**
223 * Mandatory HTTP server URL, eg "http://localhost:9000"
224 */
225 public Builder url(String url) {
226 this.url = url;
227 return this;
228 }
229
230 /**
231 * Optional login, for example "admin"
232 */
233 public Builder login(@Nullable String login) {
234 this.login = login;
235 return this;
236 }
237
238 /**
239 * Optional password related to {@link #login(String)}, for example "admin"
240 */
241 public Builder password(@Nullable String password) {
242 this.password = password;
243 return this;
244 }
245
246 /**
247 * Host and port of the optional HTTP proxy
248 */
249 public Builder proxy(@Nullable String proxyHost, int proxyPort) {
250 this.proxyHost = proxyHost;
251 this.proxyPort = proxyPort;
252 return this;
253 }
254
255 public Builder proxyLogin(@Nullable String proxyLogin) {
256 this.proxyLogin = proxyLogin;
257 return this;
258 }
259
260 public Builder proxyPassword(@Nullable String proxyPassword) {
261 this.proxyPassword = proxyPassword;
262 return this;
263 }
264
265 /**
266 * Sets a specified timeout value, in milliseconds, to be used when opening HTTP connection.
267 * A timeout of zero is interpreted as an infinite timeout. Default value is {@link SonarClient#DEFAULT_CONNECT_TIMEOUT_MILLISECONDS}
268 */
269 public Builder connectTimeoutMilliseconds(int i) {
270 this.connectTimeoutMs = i;
271 return this;
272 }
273
274 /**
275 * Sets the read timeout to a specified timeout, in milliseconds.
276 * A timeout of zero is interpreted as an infinite timeout. Default value is {@link SonarClient#DEFAULT_READ_TIMEOUT_MILLISECONDS}
277 */
278 public Builder readTimeoutMilliseconds(int i) {
279 this.readTimeoutMs = i;
280 return this;
281 }
282
283 /**
284 * Build a new client
285 */
286 public SonarClient build() {
287 if (url == null || "".equals(url)) {
288 throw new IllegalStateException("Server URL must be set");
289 }
290 return new SonarClient(this);
291 }
292 }
293 }