Dynamic Cloud Owner Support
[so.git] / bpmn / MSORESTClient / src / main / java / org / onap / so / rest / RESTClient.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP - SO
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
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
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=========================================================
20  */
21
22 package org.onap.so.rest;
23
24 import java.io.IOException;
25 import java.io.UnsupportedEncodingException;
26 import java.net.URI;
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;
32 import java.util.Map;
33 import java.util.Set;
34
35 import javax.net.ssl.SSLSocketFactory;
36
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;
58 /**
59  * Client used to send RESTFul requests.
60  * <p>
61  * Many of the methods return a reference to the 'this,' thereby allowing
62  * method chaining.
63  * <br>
64  * An example of usage can be found below:
65  * <pre>
66  * RESTClient client;
67  * try {
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!");
76  *     }
77  *  } catch (RESTException re) {
78  *      // Handle Exception
79  *  }
80  * </pre>
81  *
82  * @version 1.0
83  * @since 1.0
84  */
85 public class RESTClient {
86
87         private static final MsoLogger LOGGER = MsoLogger.getMsoLogger(MsoLogger.Catalog.BPEL,RESTClient.class);
88     private final String proxyHost;
89     private final int proxyPort;
90
91     private final String url;
92
93     private final Map<String, List<String>> headers;
94     private final Map<String, List<String>> parameters;
95
96
97
98     private HttpEntity httpEntity;
99
100     /**
101      * Internal method used to build an APIResponse using the specified
102      * HttpResponse object.
103      *
104      * @param response response wrapped inside an APIResponse object
105      * @return api response
106      */
107     private APIResponse buildResponse(HttpResponse response)
108             throws RESTException {
109
110         return new APIResponse(response);
111     }
112
113     /**
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
117      *
118      */
119     private void releaseConnection(HttpResponse response) throws RESTException {
120         try {
121             EntityUtils.consume(response.getEntity());
122         } catch (IOException ioe) {
123             throw new RESTException(ioe);
124         }
125     }
126
127     /**
128      * Sets headers to the http message.
129      *
130      * @param httpMsg http message to set headers for
131      */
132     private void addInternalHeaders(AbstractHttpMessage httpMsg) {
133         if (headers.isEmpty()) {
134             return;
135         }
136
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);
142             }
143         }
144     }
145
146     /**
147      * Builds the query part of a URL.
148      *
149      * @return query
150      */
151     private String buildQuery() {
152         if (this.parameters.size() == 0) {
153             return "";
154         }
155
156         StringBuilder sb = new StringBuilder();
157         String charSet = "UTF-8";
158         try {
159             Iterator<String> keyitr = this.parameters.keySet().iterator();
160             for (int i = 0; keyitr.hasNext(); ++i) {
161                 if (i > 0) {
162                     sb.append("&");
163                 }
164
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));
169                     sb.append("=");
170                     sb.append(URLEncoder.encode(value, charSet));
171                 }
172             }
173         } catch (UnsupportedEncodingException e) {
174             LOGGER.debug("Exception :", e);
175         }
176         return sb.toString();
177     }
178
179     /**
180      * Creates an http client that can be used for sending http requests.
181      *
182      * @return created http client
183      *
184      * @throws RESTException if unable to create http client.
185      */
186     private CloseableHttpClient createClient() throws RESTException {
187        HttpClientBuilder clientBuilder;
188        try {
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());
201                 }
202                 clientBuilder.disableRedirectHandling();
203
204                 if ((this.proxyHost != null) && (this.proxyPort != -1)) {
205                         HttpHost proxy = new HttpHost(this.proxyHost, this.proxyPort);
206                         clientBuilder.setProxy(proxy);
207                 }
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();
214     }
215
216
217
218
219
220     /**
221      * Creates a RESTClient with the specified URL, proxy host, and proxy port.
222      *
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
226      *
227      * @throws RESTException if unable to create a RESTClient
228      */
229     public RESTClient(String url, String proxyHost, int proxyPort) {
230         this(new RESTConfig(url, proxyHost, proxyPort));
231     }
232
233     /**
234      * Creates a RESTClient with the specified URL. No proxy host nor port will
235      * be used.
236      *
237      * @param url URL to send request to
238      *
239      * @throws RESTException if unable to create a RESTClient
240      */
241     public RESTClient(String url) {
242         this(new RESTConfig(url));
243     }
244
245     /**
246      * Creates a RESTClient with the RESTConfig object.
247      *
248      * @param restConfig config to use for sending request
249      *
250      * @throws RESTException if unable to create a RESTClient
251      */
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();
258     }
259
260     /**
261      * Adds parameter to be sent during http request.
262      * <p>
263      * Does not remove any parameters with the same name, thus allowing
264      * duplicates.
265      *
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
269      */
270     public RESTClient addParameter(String name, String value) {
271         if (!parameters.containsKey(name)) {
272             parameters.put(name, new ArrayList<>());
273         }
274
275         List<String> values = parameters.get(name);
276         values.add(value);
277
278         return this;
279     }
280
281     /**
282      * Sets parameter to be sent during http request.
283      * <p>
284      * Removes any parameters with the same name, thus disallowing duplicates.
285      *
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
289      */
290     public RESTClient setParameter(String name, String value) {
291         if (parameters.containsKey(name)) {
292             parameters.get(name).clear();
293         }
294
295         addParameter(name, value);
296
297         return this;
298     }
299
300     /**
301      * Adds http header to be sent during http request.
302      * <p>
303      * Does not remove any headers with the same name, thus allowing
304      * duplicates.
305      *
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
309      */
310     public RESTClient addHeader(String name, String value) {
311         if (!headers.containsKey(name)) {
312             headers.put(name, new ArrayList<>());
313         }
314
315         List<String> values = headers.get(name);
316         values.add(value);
317
318         return this;
319     }
320
321     /**
322      * Sets http header to be sent during http request.
323      * <p>
324      * Does not remove any headers with the same name, thus allowing
325      * duplicates.
326      *
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
330      */
331     public RESTClient setHeader(String name, String value) {
332         if (headers.containsKey(name)) {
333             headers.get(name).clear();
334         }
335
336         addHeader(name, value);
337
338         return this;
339     }
340
341     /**
342      * Convenience method for adding the authorization header using the
343      * specified OAuthToken object.
344      *
345      * @param token token to use for setting authorization
346      * @return a reference to 'this,' which can be used for method chaining
347      */
348     public RESTClient addAuthorizationHeader(String token) {
349         this.addHeader("Authorization", token);
350         return this;
351     }
352
353     /**
354      * Sends an http GET request using the parameters and headers previously
355      * set.
356      *
357      * @return api response
358      *
359      * @throws RESTException if request was unsuccessful
360      */
361     public APIResponse httpGet() throws RESTException {
362         HttpResponse response = null;
363
364         try (CloseableHttpClient httpClient = createClient()) {
365             String query = "";
366             if (!buildQuery().equals("")) {
367                 query = "?" + buildQuery();
368             }
369             HttpGet httpGet = new HttpGet(this.getURL() + query);
370             addInternalHeaders(httpGet);
371
372             LOGGER.debug("Executing GET to url: " + this.getURL() + query);
373
374             response = httpClient.execute(httpGet);
375
376             return buildResponse(response);
377         } catch (IOException ioe) {
378             throw new RESTException(ioe);
379         } finally {
380             if (response != null) {
381                 this.releaseConnection(response);
382             }
383         }
384     }
385
386     /**
387      * Alias for httpPost()
388      *
389      * @see RESTClient#httpPost()
390      */
391     public APIResponse post() throws RESTException {
392         return httpPost();
393     }
394
395     /**
396      * Sends an http POST request.
397      * <p>
398      * POST body will be set to the values set using add/setParameter()
399      *
400      * @return api response
401      *
402      * @throws RESTException if POST was unsuccessful
403      */
404     public APIResponse httpPost() throws RESTException {
405         return httpPost(buildQuery());
406     }
407
408     /**
409      * Sends an http POST request using the specified body.
410      *
411      * @return api response
412      *
413      * @throws RESTException if POST was unsuccessful
414      */
415     public APIResponse httpPost(String body) throws RESTException {
416         HttpResponse response = null;
417         try (CloseableHttpClient httpClient = createClient()) {
418             HttpPost httpPost = new HttpPost(this.getURL());
419             addInternalHeaders(httpPost);
420             if (body != null && !body.equals("")) {
421                 httpEntity = new StringEntity(body);
422                 httpPost.setEntity(new StringEntity(body));
423             }
424             LOGGER.debug("Executing POST to url: " + this.getURL());
425
426             response = httpClient.execute(httpPost);
427
428             return buildResponse(response);
429
430         } catch (IOException e) {
431             throw new RESTException(e);
432         } finally {
433             if (response != null) {
434                 this.releaseConnection(response);
435             }
436         }
437     }
438
439     /**
440      *
441      * @param body Data to PUT
442      * @return API response
443      * @throws RESTException
444      */
445     public APIResponse httpPut(String body) throws RESTException {
446         HttpResponse response = null;
447         try (CloseableHttpClient httpClient = createClient()) {
448
449             String query = "";
450             if (!buildQuery().equals("")) {
451                 query = "?" + buildQuery();
452             }
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);
458             }
459             LOGGER.debug("Executing PUT to url: " + this.getURL() + query);
460
461             response = httpClient.execute(httpPut);
462
463             return buildResponse(response);
464         } catch (IOException e) {
465             throw new RESTException(e);
466         } finally {
467             if (response != null) {
468                 this.releaseConnection(response);
469             }
470         }
471     }
472
473     /**
474      * Alias for httpPatch().
475      *
476      * @see RESTClient#httpPatch()
477      */
478     public APIResponse patch(String body) throws RESTException {
479         return httpPatch(body);
480     }
481
482     /**
483      *
484      * @param body Data to PATCH
485      * @return API response
486      * @throws RESTException
487      */
488     public APIResponse httpPatch(String body) throws RESTException {
489         HttpResponse response = null;
490         try (CloseableHttpClient httpClient = createClient()) {
491             String query = "";
492             if (!buildQuery().equals("")) {
493                 query = "?" + buildQuery();
494             }
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);
500             }
501             LOGGER.debug("Executing PATCH to url: " + this.getURL() + query);
502
503             response = httpClient.execute(httpPatch);
504
505             return buildResponse(response);
506         } catch (IOException e) {
507             throw new RESTException(e);
508         } finally {
509             if (response != null) {
510                 this.releaseConnection(response);
511             }
512         }
513     }
514
515     /**
516      * Alias for httpDelete().
517      *
518      * @see RESTClient#httpDelete()
519      */
520     public APIResponse delete() throws RESTException {
521         return httpDelete();
522     }
523
524     /**
525      * Sends an http DELETE request using the parameters and headers previously
526      * set.
527      *
528      * @return api response
529      *
530      * @throws RESTException if request was unsuccessful
531      */
532     public APIResponse httpDelete() throws RESTException {
533         return httpDelete(null);
534     }
535
536     /**
537      * Sends an http DELETE request with a body, using the parameters and headers
538      * previously set.
539      *
540      * @return api response
541      *
542      * @throws RESTException if request was unsuccessful
543      */
544     public APIResponse httpDelete(String body) throws RESTException {
545         HttpResponse response = null;
546
547         try (CloseableHttpClient httpClient = createClient()){
548
549             String query = "";
550             if (!buildQuery().equals("")) {
551                 query = "?" + buildQuery();
552             }
553             HttpDeleteWithBody httpDelete = new HttpDeleteWithBody(this.getURL() + query);
554             addInternalHeaders(httpDelete);
555
556             if (body != null && !body.equals("")) {
557                 httpEntity = new StringEntity(body);
558                 httpDelete.setEntity(httpEntity);
559             }
560
561             LOGGER.debug("Executing DELETE to url: " + this.getURL() + query);
562
563             response = httpClient.execute(httpDelete);
564
565             return buildResponse(response);
566         } catch (IOException ioe) {
567             throw new RESTException(ioe);
568         } finally {
569             if (response != null) {
570                 this.releaseConnection(response);
571             }
572         }
573     }
574
575     public String getURL() {
576         return url;
577     }
578     public Map<String,List<String>> getHeaders() {
579         return headers;
580     }
581     public Map<String,List<String>> getParameters() {
582         return parameters;
583     }
584     public HttpEntity getHttpEntity() {
585         return httpEntity;
586     }
587
588
589         /**
590          * Allows inclusion of a request body with DELETE.
591          */
592         private class HttpDeleteWithBody extends HttpEntityEnclosingRequestBase {
593             public static final String METHOD_NAME = "DELETE";
594
595             @Override
596                 public String getMethod() {
597                 return METHOD_NAME;
598             }
599
600             public HttpDeleteWithBody(final String uri) {
601                 super();
602                 setURI(URI.create(uri));
603             }
604
605             public HttpDeleteWithBody(final URI uri) {
606                 super();
607                 setURI(uri);
608             }
609
610             public HttpDeleteWithBody() {
611                 super();
612             }
613         }
614 }