2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 package org.openecomp.mso.rest;
23 import java.io.Closeable;
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;
34 import javax.net.ssl.SSLSocketFactory;
36 import org.apache.http.HttpEntity;
37 import org.apache.http.HttpHost;
38 import org.apache.http.HttpResponse;
39 import org.apache.http.client.HttpClient;
40 import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
41 import org.apache.http.client.methods.HttpGet;
42 import org.apache.http.client.methods.HttpPatch;
43 import org.apache.http.client.methods.HttpPost;
44 import org.apache.http.client.methods.HttpPut;
45 import org.apache.http.config.Registry;
46 import org.apache.http.config.RegistryBuilder;
47 import org.apache.http.conn.socket.ConnectionSocketFactory;
48 import org.apache.http.conn.socket.PlainConnectionSocketFactory;
49 import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
50 import org.apache.http.entity.StringEntity;
51 import org.apache.http.impl.client.CloseableHttpClient;
52 import org.apache.http.impl.client.HttpClientBuilder;
53 import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
54 import org.apache.http.message.AbstractHttpMessage;
55 import org.apache.http.util.EntityUtils;
58 * Client used to send RESTFul requests.
60 * Many of the methods return a reference to the 'this,' thereby allowing
63 * An example of usage can be found below:
67 * client = new RESTClient("http://www.openecomp.org");
68 * APIResponse response = client
69 * .setHeader("Accept", "application/json")
70 * .setHeader("Clientid", "clientid")
71 * .setHeader("header", "value")
72 * .httpPost("postbody");
73 * if (response.getStatusCode() == 200) {
74 * System.out.println("Success!");
76 * } catch (RESTException re) {
84 public class RESTClient {
85 private final String proxyHost;
86 private final int proxyPort;
88 private final String URL;
90 private final LinkedHashMap<String, List<String>> headers;
91 private final LinkedHashMap<String, List<String>> parameters;
93 private HttpEntity httpEntity;
94 private HttpClient unitTestClient;
97 * Internal method used to build an APIResponse using the specified
98 * HttpResponse object.
100 * @param response response wrapped inside an APIResponse object
101 * @return api response
103 private APIResponse buildResponse(HttpResponse response)
104 throws RESTException {
106 return new APIResponse(response);
110 * Used to release any resources used by the connection.
111 * @param response HttpResponse object used for releasing the connection
112 * @throws RESTException if unable to release connection
115 private void releaseConnection(HttpResponse response) throws RESTException {
117 EntityUtils.consume(response.getEntity());
118 } catch (IOException ioe) {
119 throw new RESTException(ioe);
124 * Sets headers to the http message.
126 * @param httpMsg http message to set headers for
128 private void addInternalHeaders(AbstractHttpMessage httpMsg) {
129 if (headers.isEmpty()) {
133 final Set<String> keySet = headers.keySet();
134 for (final String key : keySet) {
135 final List<String> values = headers.get(key);
136 for (final String value : values) {
137 httpMsg.addHeader(key, value);
143 * Builds the query part of a URL.
147 private String buildQuery() {
148 if (this.parameters.size() == 0) {
152 StringBuilder sb = new StringBuilder();
153 String charSet = "UTF-8";
155 Iterator<String> keyitr = this.parameters.keySet().iterator();
156 for (int i = 0; keyitr.hasNext(); ++i) {
161 final String name = keyitr.next();
162 final List<String> values = this.parameters.get(name);
163 for(final String value : values) {
164 sb.append(URLEncoder.encode(name, charSet));
166 sb.append(URLEncoder.encode(value, charSet));
169 } catch (UnsupportedEncodingException e) {
173 return sb.toString();
177 * Creates an http client that can be used for sending http requests.
179 * @return created http client
181 * @throws RESTException if unable to create http client.
183 private CloseableHttpClient createClient() throws RESTException {
184 //TODO - we may want to trust self signed certificate at some point - add implementation here
185 HttpClientBuilder clientBuilder;
188 SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
189 (SSLSocketFactory) SSLSocketFactory.getDefault(),
190 new HostNameVerifier());
191 Registry<ConnectionSocketFactory> registry = RegistryBuilder
192 .<ConnectionSocketFactory> create()
194 PlainConnectionSocketFactory.getSocketFactory())
195 .register("https", sslSocketFactory).build();
196 PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager(
198 clientBuilder = HttpClientBuilder.create().setConnectionManager(
200 } catch (Exception ex) {
201 throw new RESTException(ex.getMessage());
203 clientBuilder.disableRedirectHandling();
205 if ((this.proxyHost != null) && (this.proxyPort != -1)) {
206 HttpHost proxy = new HttpHost(this.proxyHost, this.proxyPort);
207 clientBuilder.setProxy(proxy);
210 return clientBuilder.build();
214 * Creates a RESTClient with the specified URL, proxy host, and proxy port.
216 * @param URL URL to send request to
217 * @param proxyHost proxy host to use for sending request
218 * @param proxyPort proxy port to use for sendin request
220 * @throws RESTException if unable to create a RESTClient
222 public RESTClient(String URL, String proxyHost, int proxyPort)
223 throws RESTException {
224 this(new RESTConfig(URL, proxyHost, proxyPort));
228 * Creates a RESTClient with the specified URL. No proxy host nor port will
231 * @param URL URL to send request to
233 * @throws RESTException if unable to create a RESTClient
235 public RESTClient(String URL) throws RESTException {
236 this(new RESTConfig(URL));
240 * Creates a RESTClient with the RESTConfig object.
242 * @param RESTConfig config to use for sending request
244 * @throws RESTException if unable to create a RESTClient
246 public RESTClient(RESTConfig cfg) throws RESTException {
247 this.headers = new LinkedHashMap<String, List<String>>();
248 this.parameters = new LinkedHashMap<String, List<String>>();
249 this.URL = cfg.getURL();
250 this.proxyHost = cfg.getProxyHost();
251 this.proxyPort = cfg.getProxyPort();
255 * Adds parameter to be sent during http request.
257 * Does not remove any parameters with the same name, thus allowing
260 * @param name name of parameter
261 * @param value value of parametr
262 * @return a reference to 'this', which can be used for method chaining
264 public RESTClient addParameter(String name, String value) {
265 if (!parameters.containsKey(name)) {
266 parameters.put(name, new ArrayList<String>());
269 List<String> values = parameters.get(name);
276 * Sets parameter to be sent during http request.
278 * Removes any parameters with the same name, thus disallowing duplicates.
280 * @param name name of parameter
281 * @param value value of parametr
282 * @return a reference to 'this', which can be used for method chaining
284 public RESTClient setParameter(String name, String value) {
285 if (parameters.containsKey(name)) {
286 parameters.get(name).clear();
289 addParameter(name, value);
295 * Adds http header to be sent during http request.
297 * Does not remove any headers with the same name, thus allowing
300 * @param name name of header
301 * @param value value of header
302 * @return a reference to 'this', which can be used for method chaining
304 public RESTClient addHeader(String name, String value) {
305 if (!headers.containsKey(name)) {
306 headers.put(name, new ArrayList<String>());
309 List<String> values = headers.get(name);
316 * Sets http header to be sent during http request.
318 * Does not remove any headers with the same name, thus allowing
321 * @param name name of header
322 * @param value value of header
323 * @return a reference to 'this', which can be used for method chaining
325 public RESTClient setHeader(String name, String value) {
326 if (headers.containsKey(name)) {
327 headers.get(name).clear();
330 addHeader(name, value);
336 * Convenience method for adding the authorization header using the
337 * specified OAuthToken object.
339 * @param token token to use for setting authorization
340 * @return a reference to 'this,' which can be used for method chaining
342 public RESTClient addAuthorizationHeader(String token) {
343 this.addHeader("Authorization", token);
348 * Alias for httpGet().
350 * @see RESTClient#httpGet()
352 public APIResponse get() throws RESTException {
357 * Sends an http GET request using the parameters and headers previously
360 * @return api response
362 * @throws RESTException if request was unsuccessful
364 public APIResponse httpGet() throws RESTException {
365 HttpResponse response = null;
367 try (CloseableHttpClient httpClient = createClient()) {
369 if (!buildQuery().equals("")) {
370 query = "?" + buildQuery();
372 HttpGet httpGet = new HttpGet(this.getURL() + query);
373 addInternalHeaders(httpGet);
375 response = httpClient.execute(httpGet);
377 APIResponse apiResponse = buildResponse(response);
379 } catch (IOException ioe) {
380 throw new RESTException(ioe);
382 if (response != null) {
383 this.releaseConnection(response);
389 * Alias for httpPost()
391 * @see RESTClient#httpPost()
393 public APIResponse post() throws RESTException {
398 * Sends an http POST request.
400 * POST body will be set to the values set using add/setParameter()
402 * @return api response
404 * @throws RESTException if POST was unsuccessful
406 public APIResponse httpPost() throws RESTException {
407 APIResponse response = httpPost(buildQuery());
412 * Sends an http POST request using the specified body.
414 * @return api response
416 * @throws RESTException if POST was unsuccessful
418 public APIResponse httpPost(String body) throws RESTException {
419 HttpResponse response = null;
420 try (CloseableHttpClient httpClient = createClient()) {
421 HttpPost httpPost = new HttpPost(this.getURL());
422 addInternalHeaders(httpPost);
423 if (body != null && !body.equals("")) {
424 httpEntity = new StringEntity(body);
425 httpPost.setEntity(new StringEntity(body));
428 response = httpClient.execute(httpPost);
430 return buildResponse(response);
431 } catch (IOException e) {
432 throw new RESTException(e);
434 if (response != null) {
435 this.releaseConnection(response);
442 * @param body Data to PUT
443 * @return API response
444 * @throws RESTException
446 public APIResponse httpPut(String body) throws RESTException {
447 HttpResponse response = null;
448 try (CloseableHttpClient httpClient = createClient()) {
451 if (!buildQuery().equals("")) {
452 query = "?" + buildQuery();
454 HttpPut httpPut = new HttpPut(this.getURL() + query);
455 addInternalHeaders(httpPut);
456 if (body != null && !body.equals("")) {
457 httpEntity = new StringEntity(body);
458 httpPut.setEntity(httpEntity);
461 response = httpClient.execute(httpPut);
463 return buildResponse(response);
464 } catch (IOException e) {
465 throw new RESTException(e);
467 if (response != null) {
468 this.releaseConnection(response);
474 * Alias for httpPatch().
476 * @see RESTClient#httpPatch()
478 public APIResponse patch(String body) throws RESTException {
479 return httpPatch(body);
484 * @param body Data to PATCH
485 * @return API response
486 * @throws RESTException
488 public APIResponse httpPatch(String body) throws RESTException {
489 HttpResponse response = null;
490 try (CloseableHttpClient httpClient = createClient()) {
492 if (!buildQuery().equals("")) {
493 query = "?" + buildQuery();
495 HttpPatch httpPatch = new HttpPatch(this.getURL() + query);
496 addInternalHeaders(httpPatch);
497 if (body != null && !body.equals("")) {
498 httpEntity = new StringEntity(body);
499 httpPatch.setEntity(httpEntity);
502 response = httpClient.execute(httpPatch);
504 return buildResponse(response);
505 } catch (IOException e) {
506 throw new RESTException(e);
508 if (response != null) {
509 this.releaseConnection(response);
515 * Alias for httpDelete().
517 * @see RESTClient#httpDelete()
519 public APIResponse delete() throws RESTException {
524 * Sends an http DELETE request using the parameters and headers previously
527 * @return api response
529 * @throws RESTException if request was unsuccessful
531 public APIResponse httpDelete() throws RESTException {
532 return httpDelete(null);
536 * Sends an http DELETE request with a body, using the parameters and headers
539 * @return api response
541 * @throws RESTException if request was unsuccessful
543 public APIResponse httpDelete(String body) throws RESTException {
544 HttpResponse response = null;
546 try (CloseableHttpClient httpClient = createClient()){
549 if (!buildQuery().equals("")) {
550 query = "?" + buildQuery();
552 HttpDeleteWithBody httpDelete = new HttpDeleteWithBody(this.getURL() + query);
553 addInternalHeaders(httpDelete);
555 if (body != null && !body.equals("")) {
556 httpEntity = new StringEntity(body);
557 httpDelete.setEntity(httpEntity);
560 response = httpClient.execute(httpDelete);
562 APIResponse apiResponse = buildResponse(response);
564 } catch (IOException ioe) {
565 throw new RESTException(ioe);
567 if (response != null) {
568 this.releaseConnection(response);
573 public String getURL() {
576 public LinkedHashMap<String,List<String>> getHeaders() {
579 public LinkedHashMap<String,List<String>> getParameters() {
582 public HttpEntity getHttpEntity() {
586 public HttpClient getUnitTestClient() {
587 return unitTestClient;
590 public void setUnitTestClient(HttpClient unitTestClient) {
591 this.unitTestClient = unitTestClient;
595 * Allows inclusion of a request body with DELETE.
597 private class HttpDeleteWithBody extends HttpEntityEnclosingRequestBase {
598 public static final String METHOD_NAME = "DELETE";
600 public String getMethod() {
604 public HttpDeleteWithBody(final String uri) {
606 setURI(URI.create(uri));
609 public HttpDeleteWithBody(final URI uri) {
614 public HttpDeleteWithBody() {