fc6266d917df8e5c14a803e4dbf12e3554ce64e2
[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      * Alias for httpGet().
355      *
356      * @see RESTClient#httpGet()
357      */
358     public APIResponse get() throws RESTException {
359         return httpGet();
360     }
361
362     /**
363      * Sends an http GET request using the parameters and headers previously
364      * set.
365      *
366      * @return api response
367      *
368      * @throws RESTException if request was unsuccessful
369      */
370     public APIResponse httpGet() throws RESTException {
371         HttpResponse response = null;
372
373         try (CloseableHttpClient httpClient = createClient()) {
374             String query = "";
375             if (!buildQuery().equals("")) {
376                 query = "?" + buildQuery();
377             }
378             HttpGet httpGet = new HttpGet(this.getURL() + query);
379             addInternalHeaders(httpGet);
380
381             LOGGER.debug("Executing GET to url: " + this.getURL() + query);
382
383             response = httpClient.execute(httpGet);
384
385             return buildResponse(response);
386         } catch (IOException ioe) {
387             throw new RESTException(ioe);
388         } finally {
389             if (response != null) {
390                 this.releaseConnection(response);
391             }
392         }
393     }
394
395     /**
396      * Alias for httpPost()
397      *
398      * @see RESTClient#httpPost()
399      */
400     public APIResponse post() throws RESTException {
401         return httpPost();
402     }
403
404     /**
405      * Sends an http POST request.
406      * <p>
407      * POST body will be set to the values set using add/setParameter()
408      *
409      * @return api response
410      *
411      * @throws RESTException if POST was unsuccessful
412      */
413     public APIResponse httpPost() throws RESTException {
414         return httpPost(buildQuery());
415     }
416
417     /**
418      * Sends an http POST request using the specified body.
419      *
420      * @return api response
421      *
422      * @throws RESTException if POST was unsuccessful
423      */
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));
432             }
433             LOGGER.debug("Executing POST to url: " + this.getURL());
434
435             response = httpClient.execute(httpPost);
436
437             return buildResponse(response);
438
439         } catch (IOException e) {
440             throw new RESTException(e);
441         } finally {
442             if (response != null) {
443                 this.releaseConnection(response);
444             }
445         }
446     }
447
448     /**
449      *
450      * @param body Data to PUT
451      * @return API response
452      * @throws RESTException
453      */
454     public APIResponse httpPut(String body) throws RESTException {
455         HttpResponse response = null;
456         try (CloseableHttpClient httpClient = createClient()) {
457
458             String query = "";
459             if (!buildQuery().equals("")) {
460                 query = "?" + buildQuery();
461             }
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);
467             }
468             LOGGER.debug("Executing PUT to url: " + this.getURL() + query);
469
470             response = httpClient.execute(httpPut);
471
472             return buildResponse(response);
473         } catch (IOException e) {
474             throw new RESTException(e);
475         } finally {
476             if (response != null) {
477                 this.releaseConnection(response);
478             }
479         }
480     }
481
482     /**
483      * Alias for httpPatch().
484      *
485      * @see RESTClient#httpPatch()
486      */
487     public APIResponse patch(String body) throws RESTException {
488         return httpPatch(body);
489     }
490
491     /**
492      *
493      * @param body Data to PATCH
494      * @return API response
495      * @throws RESTException
496      */
497     public APIResponse httpPatch(String body) throws RESTException {
498         HttpResponse response = null;
499         try (CloseableHttpClient httpClient = createClient()) {
500             String query = "";
501             if (!buildQuery().equals("")) {
502                 query = "?" + buildQuery();
503             }
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);
509             }
510             LOGGER.debug("Executing PATCH to url: " + this.getURL() + query);
511
512             response = httpClient.execute(httpPatch);
513
514             return buildResponse(response);
515         } catch (IOException e) {
516             throw new RESTException(e);
517         } finally {
518             if (response != null) {
519                 this.releaseConnection(response);
520             }
521         }
522     }
523
524     /**
525      * Alias for httpDelete().
526      *
527      * @see RESTClient#httpDelete()
528      */
529     public APIResponse delete() throws RESTException {
530         return httpDelete();
531     }
532
533     /**
534      * Sends an http DELETE request using the parameters and headers previously
535      * set.
536      *
537      * @return api response
538      *
539      * @throws RESTException if request was unsuccessful
540      */
541     public APIResponse httpDelete() throws RESTException {
542         return httpDelete(null);
543     }
544
545     /**
546      * Sends an http DELETE request with a body, using the parameters and headers
547      * previously set.
548      *
549      * @return api response
550      *
551      * @throws RESTException if request was unsuccessful
552      */
553     public APIResponse httpDelete(String body) throws RESTException {
554         HttpResponse response = null;
555
556         try (CloseableHttpClient httpClient = createClient()){
557
558             String query = "";
559             if (!buildQuery().equals("")) {
560                 query = "?" + buildQuery();
561             }
562             HttpDeleteWithBody httpDelete = new HttpDeleteWithBody(this.getURL() + query);
563             addInternalHeaders(httpDelete);
564
565             if (body != null && !body.equals("")) {
566                 httpEntity = new StringEntity(body);
567                 httpDelete.setEntity(httpEntity);
568             }
569
570             LOGGER.debug("Executing DELETE to url: " + this.getURL() + query);
571
572             response = httpClient.execute(httpDelete);
573
574             return buildResponse(response);
575         } catch (IOException ioe) {
576             throw new RESTException(ioe);
577         } finally {
578             if (response != null) {
579                 this.releaseConnection(response);
580             }
581         }
582     }
583
584     public String getURL() {
585         return url;
586     }
587     public Map<String,List<String>> getHeaders() {
588         return headers;
589     }
590     public Map<String,List<String>> getParameters() {
591         return parameters;
592     }
593     public HttpEntity getHttpEntity() {
594         return httpEntity;
595     }
596
597
598         /**
599          * Allows inclusion of a request body with DELETE.
600          */
601         private class HttpDeleteWithBody extends HttpEntityEnclosingRequestBase {
602             public static final String METHOD_NAME = "DELETE";
603
604             @Override
605                 public String getMethod() {
606                 return METHOD_NAME;
607             }
608
609             public HttpDeleteWithBody(final String uri) {
610                 super();
611                 setURI(URI.create(uri));
612             }
613
614             public HttpDeleteWithBody(final URI uri) {
615                 super();
616                 setURI(uri);
617             }
618
619             public HttpDeleteWithBody() {
620                 super();
621             }
622         }
623 }