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;
96 * Internal method used to build an APIResponse using the specified
97 * HttpResponse object.
99 * @param response response wrapped inside an APIResponse object
100 * @return api response
102 private APIResponse buildResponse(HttpResponse response)
103 throws RESTException {
105 return new APIResponse(response);
109 * Used to release any resources used by the connection.
110 * @param response HttpResponse object used for releasing the connection
111 * @throws RESTException if unable to release connection
114 private void releaseConnection(HttpResponse response) throws RESTException {
116 EntityUtils.consume(response.getEntity());
117 } catch (IOException ioe) {
118 throw new RESTException(ioe);
123 * Sets headers to the http message.
125 * @param httpMsg http message to set headers for
127 private void addInternalHeaders(AbstractHttpMessage httpMsg) {
128 if (headers.isEmpty()) {
132 final Set<String> keySet = headers.keySet();
133 for (final String key : keySet) {
134 final List<String> values = headers.get(key);
135 for (final String value : values) {
136 httpMsg.addHeader(key, value);
142 * Builds the query part of a URL.
146 private String buildQuery() {
147 if (this.parameters.size() == 0) {
151 StringBuilder sb = new StringBuilder();
152 String charSet = "UTF-8";
154 Iterator<String> keyitr = this.parameters.keySet().iterator();
155 for (int i = 0; keyitr.hasNext(); ++i) {
160 final String name = keyitr.next();
161 final List<String> values = this.parameters.get(name);
162 for(final String value : values) {
163 sb.append(URLEncoder.encode(name, charSet));
165 sb.append(URLEncoder.encode(value, charSet));
168 } catch (UnsupportedEncodingException e) {
172 return sb.toString();
176 * Creates an http client that can be used for sending http requests.
178 * @return created http client
180 * @throws RESTException if unable to create http client.
182 private CloseableHttpClient createClient() throws RESTException {
183 //TODO - we may want to trust self signed certificate at some point - add implementation here
184 HttpClientBuilder clientBuilder;
187 SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
188 (SSLSocketFactory) SSLSocketFactory.getDefault(),
189 new HostNameVerifier());
190 Registry<ConnectionSocketFactory> registry = RegistryBuilder
191 .<ConnectionSocketFactory> create()
193 PlainConnectionSocketFactory.getSocketFactory())
194 .register("https", sslSocketFactory).build();
195 PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager(
197 clientBuilder = HttpClientBuilder.create().setConnectionManager(
199 } catch (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);
209 return clientBuilder.build();
213 * Creates a RESTClient with the specified URL, proxy host, and proxy port.
215 * @param URL URL to send request to
216 * @param proxyHost proxy host to use for sending request
217 * @param proxyPort proxy port to use for sendin request
219 * @throws RESTException if unable to create a RESTClient
221 public RESTClient(String URL, String proxyHost, int proxyPort)
222 throws RESTException {
223 this(new RESTConfig(URL, proxyHost, proxyPort));
227 * Creates a RESTClient with the specified URL. No proxy host nor port will
230 * @param URL URL to send request to
232 * @throws RESTException if unable to create a RESTClient
234 public RESTClient(String URL) throws RESTException {
235 this(new RESTConfig(URL));
239 * Creates a RESTClient with the RESTConfig object.
241 * @param RESTConfig config to use for sending request
243 * @throws RESTException if unable to create a RESTClient
245 public RESTClient(RESTConfig cfg) throws RESTException {
246 this.headers = new LinkedHashMap<String, List<String>>();
247 this.parameters = new LinkedHashMap<String, List<String>>();
248 this.URL = cfg.getURL();
249 this.proxyHost = cfg.getProxyHost();
250 this.proxyPort = cfg.getProxyPort();
254 * Adds parameter to be sent during http request.
256 * Does not remove any parameters with the same name, thus allowing
259 * @param name name of parameter
260 * @param value value of parametr
261 * @return a reference to 'this', which can be used for method chaining
263 public RESTClient addParameter(String name, String value) {
264 if (!parameters.containsKey(name)) {
265 parameters.put(name, new ArrayList<String>());
268 List<String> values = parameters.get(name);
275 * Sets parameter to be sent during http request.
277 * Removes any parameters with the same name, thus disallowing duplicates.
279 * @param name name of parameter
280 * @param value value of parametr
281 * @return a reference to 'this', which can be used for method chaining
283 public RESTClient setParameter(String name, String value) {
284 if (parameters.containsKey(name)) {
285 parameters.get(name).clear();
288 addParameter(name, value);
294 * Adds http header to be sent during http request.
296 * Does not remove any headers with the same name, thus allowing
299 * @param name name of header
300 * @param value value of header
301 * @return a reference to 'this', which can be used for method chaining
303 public RESTClient addHeader(String name, String value) {
304 if (!headers.containsKey(name)) {
305 headers.put(name, new ArrayList<String>());
308 List<String> values = headers.get(name);
315 * Sets http header to be sent during http request.
317 * Does not remove any headers with the same name, thus allowing
320 * @param name name of header
321 * @param value value of header
322 * @return a reference to 'this', which can be used for method chaining
324 public RESTClient setHeader(String name, String value) {
325 if (headers.containsKey(name)) {
326 headers.get(name).clear();
329 addHeader(name, value);
335 * Convenience method for adding the authorization header using the
336 * specified OAuthToken object.
338 * @param token token to use for setting authorization
339 * @return a reference to 'this,' which can be used for method chaining
341 public RESTClient addAuthorizationHeader(String token) {
342 this.addHeader("Authorization", token);
347 * Alias for httpGet().
349 * @see RESTClient#httpGet()
351 public APIResponse get() throws RESTException {
356 * Sends an http GET request using the parameters and headers previously
359 * @return api response
361 * @throws RESTException if request was unsuccessful
363 public APIResponse httpGet() throws RESTException {
364 HttpResponse response = null;
366 try (CloseableHttpClient httpClient = createClient()) {
368 if (!buildQuery().equals("")) {
369 query = "?" + buildQuery();
371 HttpGet httpGet = new HttpGet(this.getURL() + query);
372 addInternalHeaders(httpGet);
374 response = httpClient.execute(httpGet);
376 APIResponse apiResponse = buildResponse(response);
378 } catch (IOException ioe) {
379 throw new RESTException(ioe);
381 if (response != null) {
382 this.releaseConnection(response);
388 * Alias for httpPost()
390 * @see RESTClient#httpPost()
392 public APIResponse post() throws RESTException {
397 * Sends an http POST request.
399 * POST body will be set to the values set using add/setParameter()
401 * @return api response
403 * @throws RESTException if POST was unsuccessful
405 public APIResponse httpPost() throws RESTException {
406 APIResponse response = httpPost(buildQuery());
411 * Sends an http POST request using the specified body.
413 * @return api response
415 * @throws RESTException if POST was unsuccessful
417 public APIResponse httpPost(String body) throws RESTException {
418 HttpResponse response = null;
419 try (CloseableHttpClient httpClient = createClient()) {
420 HttpPost httpPost = new HttpPost(this.getURL());
421 addInternalHeaders(httpPost);
422 if (body != null && !body.equals("")) {
423 httpEntity = new StringEntity(body);
424 httpPost.setEntity(new StringEntity(body));
427 response = httpClient.execute(httpPost);
429 return buildResponse(response);
430 } catch (IOException e) {
431 throw new RESTException(e);
433 if (response != null) {
434 this.releaseConnection(response);
441 * @param body Data to PUT
442 * @return API response
443 * @throws RESTException
445 public APIResponse httpPut(String body) throws RESTException {
446 HttpResponse response = null;
447 try (CloseableHttpClient httpClient = createClient()) {
450 if (!buildQuery().equals("")) {
451 query = "?" + buildQuery();
453 HttpPut httpPut = new HttpPut(this.getURL() + query);
454 addInternalHeaders(httpPut);
455 if (body != null && !body.equals("")) {
456 httpEntity = new StringEntity(body);
457 httpPut.setEntity(httpEntity);
460 response = httpClient.execute(httpPut);
462 return buildResponse(response);
463 } catch (IOException e) {
464 throw new RESTException(e);
466 if (response != null) {
467 this.releaseConnection(response);
473 * Alias for httpPatch().
475 * @see RESTClient#httpPatch()
477 public APIResponse patch(String body) throws RESTException {
478 return httpPatch(body);
483 * @param body Data to PATCH
484 * @return API response
485 * @throws RESTException
487 public APIResponse httpPatch(String body) throws RESTException {
488 HttpResponse response = null;
489 try (CloseableHttpClient httpClient = createClient()) {
491 if (!buildQuery().equals("")) {
492 query = "?" + buildQuery();
494 HttpPatch httpPatch = new HttpPatch(this.getURL() + query);
495 addInternalHeaders(httpPatch);
496 if (body != null && !body.equals("")) {
497 httpEntity = new StringEntity(body);
498 httpPatch.setEntity(httpEntity);
501 response = httpClient.execute(httpPatch);
503 return buildResponse(response);
504 } catch (IOException e) {
505 throw new RESTException(e);
507 if (response != null) {
508 this.releaseConnection(response);
514 * Alias for httpDelete().
516 * @see RESTClient#httpDelete()
518 public APIResponse delete() throws RESTException {
523 * Sends an http DELETE request using the parameters and headers previously
526 * @return api response
528 * @throws RESTException if request was unsuccessful
530 public APIResponse httpDelete() throws RESTException {
531 return httpDelete(null);
535 * Sends an http DELETE request with a body, using the parameters and headers
538 * @return api response
540 * @throws RESTException if request was unsuccessful
542 public APIResponse httpDelete(String body) throws RESTException {
543 HttpResponse response = null;
545 try (CloseableHttpClient httpClient = createClient()){
548 if (!buildQuery().equals("")) {
549 query = "?" + buildQuery();
551 HttpDeleteWithBody httpDelete = new HttpDeleteWithBody(this.getURL() + query);
552 addInternalHeaders(httpDelete);
554 if (body != null && !body.equals("")) {
555 httpEntity = new StringEntity(body);
556 httpDelete.setEntity(httpEntity);
559 response = httpClient.execute(httpDelete);
561 APIResponse apiResponse = buildResponse(response);
563 } catch (IOException ioe) {
564 throw new RESTException(ioe);
566 if (response != null) {
567 this.releaseConnection(response);
572 public String getURL() {
575 public LinkedHashMap<String,List<String>> getHeaders() {
578 public LinkedHashMap<String,List<String>> getParameters() {
581 public HttpEntity getHttpEntity() {
587 * Allows inclusion of a request body with DELETE.
589 private class HttpDeleteWithBody extends HttpEntityEnclosingRequestBase {
590 public static final String METHOD_NAME = "DELETE";
592 public String getMethod() {
596 public HttpDeleteWithBody(final String uri) {
598 setURI(URI.create(uri));
601 public HttpDeleteWithBody(final URI uri) {
606 public HttpDeleteWithBody() {