2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * Copyright (C) 2017 Huawei Technologies Co., Ltd. All rights reserved.
7 * ================================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 * ============LICENSE_END=========================================================
22 package org.onap.so.rest;
24 import java.io.IOException;
25 import java.io.UnsupportedEncodingException;
27 import java.net.URLEncoder;
28 import java.util.ArrayList;
29 import java.util.Iterator;
30 import java.util.LinkedHashMap;
31 import java.util.List;
35 import javax.net.ssl.SSLSocketFactory;
37 import org.apache.http.HttpEntity;
38 import org.apache.http.HttpHost;
39 import org.apache.http.HttpResponse;
40 import org.apache.http.client.config.RequestConfig;
41 import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
42 import org.apache.http.client.methods.HttpGet;
43 import org.apache.http.client.methods.HttpPatch;
44 import org.apache.http.client.methods.HttpPost;
45 import org.apache.http.client.methods.HttpPut;
46 import org.apache.http.config.Registry;
47 import org.apache.http.config.RegistryBuilder;
48 import org.apache.http.conn.socket.ConnectionSocketFactory;
49 import org.apache.http.conn.socket.PlainConnectionSocketFactory;
50 import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
51 import org.apache.http.entity.StringEntity;
52 import org.apache.http.impl.client.CloseableHttpClient;
53 import org.apache.http.impl.client.HttpClientBuilder;
54 import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
55 import org.apache.http.message.AbstractHttpMessage;
56 import org.apache.http.util.EntityUtils;
57 import org.onap.so.logger.MsoLogger;
59 * Client used to send RESTFul requests.
61 * Many of the methods return a reference to the 'this,' thereby allowing
64 * An example of usage can be found below:
68 * client = new RESTClient("http://www.openecomp.org");
69 * APIResponse response = client
70 * .setHeader("Accept", "application/json")
71 * .setHeader("Clientid", "clientid")
72 * .setHeader("header", "value")
73 * .httpPost("postbody");
74 * if (response.getStatusCode() == 200) {
75 * System.out.println("Success!");
77 * } catch (RESTException re) {
85 public class RESTClient {
87 private static final MsoLogger LOGGER = MsoLogger.getMsoLogger(MsoLogger.Catalog.BPEL,RESTClient.class);
88 private final String proxyHost;
89 private final int proxyPort;
91 private final String url;
93 private final Map<String, List<String>> headers;
94 private final Map<String, List<String>> parameters;
98 private HttpEntity httpEntity;
101 * Internal method used to build an APIResponse using the specified
102 * HttpResponse object.
104 * @param response response wrapped inside an APIResponse object
105 * @return api response
107 private APIResponse buildResponse(HttpResponse response)
108 throws RESTException {
110 return new APIResponse(response);
114 * Used to release any resources used by the connection.
115 * @param response HttpResponse object used for releasing the connection
116 * @throws RESTException if unable to release connection
119 private void releaseConnection(HttpResponse response) throws RESTException {
121 EntityUtils.consume(response.getEntity());
122 } catch (IOException ioe) {
123 throw new RESTException(ioe);
128 * Sets headers to the http message.
130 * @param httpMsg http message to set headers for
132 private void addInternalHeaders(AbstractHttpMessage httpMsg) {
133 if (headers.isEmpty()) {
137 final Set<String> keySet = headers.keySet();
138 for (final String key : keySet) {
139 final List<String> values = headers.get(key);
140 for (final String value : values) {
141 httpMsg.addHeader(key, value);
147 * Builds the query part of a URL.
151 private String buildQuery() {
152 if (this.parameters.size() == 0) {
156 StringBuilder sb = new StringBuilder();
157 String charSet = "UTF-8";
159 Iterator<String> keyitr = this.parameters.keySet().iterator();
160 for (int i = 0; keyitr.hasNext(); ++i) {
165 final String name = keyitr.next();
166 final List<String> values = this.parameters.get(name);
167 for(final String value : values) {
168 sb.append(URLEncoder.encode(name, charSet));
170 sb.append(URLEncoder.encode(value, charSet));
173 } catch (UnsupportedEncodingException e) {
174 LOGGER.debug("Exception :", e);
176 return sb.toString();
180 * Creates an http client that can be used for sending http requests.
182 * @return created http client
184 * @throws RESTException if unable to create http client.
186 private CloseableHttpClient createClient() throws RESTException {
187 HttpClientBuilder clientBuilder;
189 SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
190 (SSLSocketFactory) SSLSocketFactory.getDefault(),
191 new HostNameVerifier());
192 Registry<ConnectionSocketFactory> registry = RegistryBuilder
193 .<ConnectionSocketFactory> create()
194 .register("http", PlainConnectionSocketFactory.getSocketFactory())
195 .register("https", sslSocketFactory).build();
196 PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager(registry);
197 clientBuilder = HttpClientBuilder.create().setConnectionManager(manager);
198 } catch (Exception ex) {
199 LOGGER.debug("Exception :", ex);
200 throw new RESTException(ex.getMessage());
202 clientBuilder.disableRedirectHandling();
204 if ((this.proxyHost != null) && (this.proxyPort != -1)) {
205 HttpHost proxy = new HttpHost(this.proxyHost, this.proxyPort);
206 clientBuilder.setProxy(proxy);
208 int timeoutInSeconds = 300;
209 RequestConfig requestConfig = RequestConfig.custom()
210 .setConnectTimeout(timeoutInSeconds * 1000)
211 .setConnectionRequestTimeout(timeoutInSeconds * 1000)
212 .setSocketTimeout(timeoutInSeconds * 1000).build();
213 return clientBuilder.setDefaultRequestConfig(requestConfig).build();
221 * Creates a RESTClient with the specified URL, proxy host, and proxy port.
223 * @param url URL to send request to
224 * @param proxyHost proxy host to use for sending request
225 * @param proxyPort proxy port to use for sendin request
227 * @throws RESTException if unable to create a RESTClient
229 public RESTClient(String url, String proxyHost, int proxyPort) {
230 this(new RESTConfig(url, proxyHost, proxyPort));
234 * Creates a RESTClient with the specified URL. No proxy host nor port will
237 * @param url URL to send request to
239 * @throws RESTException if unable to create a RESTClient
241 public RESTClient(String url) {
242 this(new RESTConfig(url));
246 * Creates a RESTClient with the RESTConfig object.
248 * @param restConfig config to use for sending request
250 * @throws RESTException if unable to create a RESTClient
252 public RESTClient(RESTConfig restConfig) {
253 this.headers = new LinkedHashMap<>();
254 this.parameters = new LinkedHashMap<>();
255 this.url = restConfig.getURL();
256 this.proxyHost = restConfig.getProxyHost();
257 this.proxyPort = restConfig.getProxyPort();
261 * Adds parameter to be sent during http request.
263 * Does not remove any parameters with the same name, thus allowing
266 * @param name name of parameter
267 * @param value value of parametr
268 * @return a reference to 'this', which can be used for method chaining
270 public RESTClient addParameter(String name, String value) {
271 if (!parameters.containsKey(name)) {
272 parameters.put(name, new ArrayList<>());
275 List<String> values = parameters.get(name);
282 * Sets parameter to be sent during http request.
284 * Removes any parameters with the same name, thus disallowing duplicates.
286 * @param name name of parameter
287 * @param value value of parametr
288 * @return a reference to 'this', which can be used for method chaining
290 public RESTClient setParameter(String name, String value) {
291 if (parameters.containsKey(name)) {
292 parameters.get(name).clear();
295 addParameter(name, value);
301 * Adds http header to be sent during http request.
303 * Does not remove any headers with the same name, thus allowing
306 * @param name name of header
307 * @param value value of header
308 * @return a reference to 'this', which can be used for method chaining
310 public RESTClient addHeader(String name, String value) {
311 if (!headers.containsKey(name)) {
312 headers.put(name, new ArrayList<>());
315 List<String> values = headers.get(name);
322 * Sets http header to be sent during http request.
324 * Does not remove any headers with the same name, thus allowing
327 * @param name name of header
328 * @param value value of header
329 * @return a reference to 'this', which can be used for method chaining
331 public RESTClient setHeader(String name, String value) {
332 if (headers.containsKey(name)) {
333 headers.get(name).clear();
336 addHeader(name, value);
342 * Convenience method for adding the authorization header using the
343 * specified OAuthToken object.
345 * @param token token to use for setting authorization
346 * @return a reference to 'this,' which can be used for method chaining
348 public RESTClient addAuthorizationHeader(String token) {
349 this.addHeader("Authorization", token);
354 * Alias for httpGet().
356 * @see RESTClient#httpGet()
358 public APIResponse get() throws RESTException {
363 * Sends an http GET request using the parameters and headers previously
366 * @return api response
368 * @throws RESTException if request was unsuccessful
370 public APIResponse httpGet() throws RESTException {
371 HttpResponse response = null;
373 try (CloseableHttpClient httpClient = createClient()) {
375 if (!buildQuery().equals("")) {
376 query = "?" + buildQuery();
378 HttpGet httpGet = new HttpGet(this.getURL() + query);
379 addInternalHeaders(httpGet);
381 LOGGER.debug("Executing GET to url: " + this.getURL() + query);
383 response = httpClient.execute(httpGet);
385 return buildResponse(response);
386 } catch (IOException ioe) {
387 throw new RESTException(ioe);
389 if (response != null) {
390 this.releaseConnection(response);
396 * Alias for httpPost()
398 * @see RESTClient#httpPost()
400 public APIResponse post() throws RESTException {
405 * Sends an http POST request.
407 * POST body will be set to the values set using add/setParameter()
409 * @return api response
411 * @throws RESTException if POST was unsuccessful
413 public APIResponse httpPost() throws RESTException {
414 return httpPost(buildQuery());
418 * Sends an http POST request using the specified body.
420 * @return api response
422 * @throws RESTException if POST was unsuccessful
424 public APIResponse httpPost(String body) throws RESTException {
425 HttpResponse response = null;
426 try (CloseableHttpClient httpClient = createClient()) {
427 HttpPost httpPost = new HttpPost(this.getURL());
428 addInternalHeaders(httpPost);
429 if (body != null && !body.equals("")) {
430 httpEntity = new StringEntity(body);
431 httpPost.setEntity(new StringEntity(body));
433 LOGGER.debug("Executing POST to url: " + this.getURL());
435 response = httpClient.execute(httpPost);
437 return buildResponse(response);
439 } catch (IOException e) {
440 throw new RESTException(e);
442 if (response != null) {
443 this.releaseConnection(response);
450 * @param body Data to PUT
451 * @return API response
452 * @throws RESTException
454 public APIResponse httpPut(String body) throws RESTException {
455 HttpResponse response = null;
456 try (CloseableHttpClient httpClient = createClient()) {
459 if (!buildQuery().equals("")) {
460 query = "?" + buildQuery();
462 HttpPut httpPut = new HttpPut(this.getURL() + query);
463 addInternalHeaders(httpPut);
464 if (body != null && !body.equals("")) {
465 httpEntity = new StringEntity(body);
466 httpPut.setEntity(httpEntity);
468 LOGGER.debug("Executing PUT to url: " + this.getURL() + query);
470 response = httpClient.execute(httpPut);
472 return buildResponse(response);
473 } catch (IOException e) {
474 throw new RESTException(e);
476 if (response != null) {
477 this.releaseConnection(response);
483 * Alias for httpPatch().
485 * @see RESTClient#httpPatch()
487 public APIResponse patch(String body) throws RESTException {
488 return httpPatch(body);
493 * @param body Data to PATCH
494 * @return API response
495 * @throws RESTException
497 public APIResponse httpPatch(String body) throws RESTException {
498 HttpResponse response = null;
499 try (CloseableHttpClient httpClient = createClient()) {
501 if (!buildQuery().equals("")) {
502 query = "?" + buildQuery();
504 HttpPatch httpPatch = new HttpPatch(this.getURL() + query);
505 addInternalHeaders(httpPatch);
506 if (body != null && !body.equals("")) {
507 httpEntity = new StringEntity(body);
508 httpPatch.setEntity(httpEntity);
510 LOGGER.debug("Executing PATCH to url: " + this.getURL() + query);
512 response = httpClient.execute(httpPatch);
514 return buildResponse(response);
515 } catch (IOException e) {
516 throw new RESTException(e);
518 if (response != null) {
519 this.releaseConnection(response);
525 * Alias for httpDelete().
527 * @see RESTClient#httpDelete()
529 public APIResponse delete() throws RESTException {
534 * Sends an http DELETE request using the parameters and headers previously
537 * @return api response
539 * @throws RESTException if request was unsuccessful
541 public APIResponse httpDelete() throws RESTException {
542 return httpDelete(null);
546 * Sends an http DELETE request with a body, using the parameters and headers
549 * @return api response
551 * @throws RESTException if request was unsuccessful
553 public APIResponse httpDelete(String body) throws RESTException {
554 HttpResponse response = null;
556 try (CloseableHttpClient httpClient = createClient()){
559 if (!buildQuery().equals("")) {
560 query = "?" + buildQuery();
562 HttpDeleteWithBody httpDelete = new HttpDeleteWithBody(this.getURL() + query);
563 addInternalHeaders(httpDelete);
565 if (body != null && !body.equals("")) {
566 httpEntity = new StringEntity(body);
567 httpDelete.setEntity(httpEntity);
570 LOGGER.debug("Executing DELETE to url: " + this.getURL() + query);
572 response = httpClient.execute(httpDelete);
574 return buildResponse(response);
575 } catch (IOException ioe) {
576 throw new RESTException(ioe);
578 if (response != null) {
579 this.releaseConnection(response);
584 public String getURL() {
587 public Map<String,List<String>> getHeaders() {
590 public Map<String,List<String>> getParameters() {
593 public HttpEntity getHttpEntity() {
599 * Allows inclusion of a request body with DELETE.
601 private class HttpDeleteWithBody extends HttpEntityEnclosingRequestBase {
602 public static final String METHOD_NAME = "DELETE";
605 public String getMethod() {
609 public HttpDeleteWithBody(final String uri) {
611 setURI(URI.create(uri));
614 public HttpDeleteWithBody(final URI uri) {
619 public HttpDeleteWithBody() {