bmpn sonar issue fix
[so.git] / bpmn / MSORESTClient / src / main / java / org / openecomp / mso / 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.openecomp.mso.rest;
23
24 import java.io.Closeable;
25 import java.io.IOException;
26 import java.io.UnsupportedEncodingException;
27 import java.net.URI;
28 import java.net.URLEncoder;
29 import java.util.ArrayList;
30 import java.util.Iterator;
31 import java.util.LinkedHashMap;
32 import java.util.List;
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.HttpClient;
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
58 import org.openecomp.mso.logger.MsoLogger;
59 /**
60  * Client used to send RESTFul requests.
61  * <p>
62  * Many of the methods return a reference to the 'this,' thereby allowing 
63  * method chaining. 
64  * <br>
65  * An example of usage can be found below:
66  * <pre>
67  * RESTClient client;
68  * try {
69  *     client = new RESTClient("http://www.openecomp.org");
70  *     APIResponse response = client
71  *         .setHeader("Accept", "application/json")
72  *         .setHeader("Clientid", "clientid")
73  *         .setHeader("header", "value")
74  *         .httpPost("postbody");
75  *     if (response.getStatusCode() == 200) {
76  *         System.out.println("Success!");
77  *     }
78  *  } catch (RESTException re) {
79  *      // Handle Exception
80  *  }
81  * </pre>
82  *
83  * @version 1.0
84  * @since 1.0
85  */
86 public class RESTClient {
87         
88         private static final MsoLogger LOGGER = MsoLogger.getMsoLogger(MsoLogger.Catalog.BPEL);
89     private final String proxyHost;
90     private final int proxyPort;
91
92     private final String URL;
93
94     private final LinkedHashMap<String, List<String>> headers;
95     private final LinkedHashMap<String, List<String>> parameters;
96     
97     private HttpEntity httpEntity;
98
99     /**
100      * Internal method used to build an APIResponse using the specified 
101      * HttpResponse object.
102      *
103      * @param response response wrapped inside an APIResponse object
104      * @return api response
105      */
106     private APIResponse buildResponse(HttpResponse response) 
107             throws RESTException {
108
109         return new APIResponse(response);
110     }
111
112     /**
113      * Used to release any resources used by the connection.
114      * @param response HttpResponse object used for releasing the connection
115      * @throws RESTException if unable to release connection
116      *
117      */
118     private void releaseConnection(HttpResponse response) throws RESTException {
119         try {
120             EntityUtils.consume(response.getEntity());
121         } catch (IOException ioe) {
122             throw new RESTException(ioe);
123         }
124     }
125
126     /**
127      * Sets headers to the http message.
128      *
129      * @param httpMsg http message to set headers for
130      */
131     private void addInternalHeaders(AbstractHttpMessage httpMsg) {
132         if (headers.isEmpty()) {
133             return;
134         }
135
136         final Set<String> keySet = headers.keySet();
137         for (final String key : keySet) {
138             final List<String> values = headers.get(key);
139             for (final String value : values) {
140                 httpMsg.addHeader(key, value);
141             }
142         }
143     }
144
145     /**
146      * Builds the query part of a URL.
147      *
148      * @return query
149      */
150     private String buildQuery() {
151         if (this.parameters.size() == 0) {
152             return "";
153         }
154
155         StringBuilder sb = new StringBuilder();
156         String charSet = "UTF-8";
157         try {
158             Iterator<String> keyitr = this.parameters.keySet().iterator();
159             for (int i = 0; keyitr.hasNext(); ++i) {
160                 if (i > 0) {
161                     sb.append("&");
162                 }
163
164                 final String name = keyitr.next();
165                 final List<String> values = this.parameters.get(name);
166                 for(final String value : values) {
167                     sb.append(URLEncoder.encode(name, charSet));
168                     sb.append("=");
169                     sb.append(URLEncoder.encode(value, charSet));
170                 }
171             }
172         } catch (UnsupportedEncodingException e) {
173             LOGGER.debug("Exception :", e);
174         }
175         return sb.toString();
176     }
177
178     /**
179      * Creates an http client that can be used for sending http requests.
180      *
181      * @return created http client
182      *
183      * @throws RESTException if unable to create http client.
184      */
185     private CloseableHttpClient createClient() throws RESTException {
186         //TODO - we may want to trust self signed certificate at some point - add implementation here
187         HttpClientBuilder clientBuilder;
188
189                 try {
190                         SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
191                                         (SSLSocketFactory) SSLSocketFactory.getDefault(),
192                                         new HostNameVerifier());
193                         Registry<ConnectionSocketFactory> registry = RegistryBuilder
194                                         .<ConnectionSocketFactory> create()
195                                         .register("http",
196                                                         PlainConnectionSocketFactory.getSocketFactory())
197                                         .register("https", sslSocketFactory).build();
198                         PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager(
199                                         registry);
200                         clientBuilder = HttpClientBuilder.create().setConnectionManager(
201                                         manager);
202                 } catch (Exception ex) {
203                         LOGGER.debug("Exception :", ex);
204                         throw new RESTException(ex.getMessage());
205                 }
206                 clientBuilder.disableRedirectHandling();
207
208                 if ((this.proxyHost != null) && (this.proxyPort != -1)) {
209                         HttpHost proxy = new HttpHost(this.proxyHost, this.proxyPort);
210                         clientBuilder.setProxy(proxy);
211                 }
212
213                 return clientBuilder.build();
214     }
215
216     /**
217      * Creates a RESTClient with the specified URL, proxy host, and proxy port.
218      *
219      * @param URL URL to send request to
220      * @param proxyHost proxy host to use for sending request
221      * @param proxyPort proxy port to use for sendin request
222      *
223      * @throws RESTException if unable to create a RESTClient
224      */
225     public RESTClient(String URL, String proxyHost, int proxyPort)
226             throws RESTException {
227         this(new RESTConfig(URL, proxyHost, proxyPort));
228     }
229
230     /**
231      * Creates a RESTClient with the specified URL. No proxy host nor port will
232      * be used. 
233      *
234      * @param URL URL to send request to
235      *
236      * @throws RESTException if unable to create a RESTClient
237      */
238     public RESTClient(String URL) throws RESTException {
239         this(new RESTConfig(URL));
240     }
241     
242     /**
243      * Creates a RESTClient with the RESTConfig object.
244      *
245      * @param RESTConfig config to use for sending request
246      *
247      * @throws RESTException if unable to create a RESTClient
248      */
249     public RESTClient(RESTConfig cfg) throws RESTException {
250         this.headers = new LinkedHashMap<String, List<String>>();
251         this.parameters = new LinkedHashMap<String, List<String>>();
252         this.URL = cfg.getURL();
253         this.proxyHost = cfg.getProxyHost();
254         this.proxyPort = cfg.getProxyPort();
255     }
256
257     /**
258      * Adds parameter to be sent during http request.
259      * <p>
260      * Does not remove any parameters with the same name, thus allowing 
261      * duplicates.
262      *
263      * @param name name of parameter
264      * @param value value of parametr
265      * @return a reference to 'this', which can be used for method chaining
266      */
267     public RESTClient addParameter(String name, String value) {
268         if (!parameters.containsKey(name)) {
269             parameters.put(name, new ArrayList<String>());
270         }
271
272         List<String> values = parameters.get(name);
273         values.add(value);
274
275         return this;
276     }
277
278     /**
279      * Sets parameter to be sent during http request.
280      * <p>
281      * Removes any parameters with the same name, thus disallowing duplicates.
282      *
283      * @param name name of parameter
284      * @param value value of parametr
285      * @return a reference to 'this', which can be used for method chaining
286      */
287     public RESTClient setParameter(String name, String value) {
288         if (parameters.containsKey(name)) {
289             parameters.get(name).clear();
290         }
291
292         addParameter(name, value);
293
294         return this;
295     }
296
297     /**
298      * Adds http header to be sent during http request.
299      * <p>
300      * Does not remove any headers with the same name, thus allowing 
301      * duplicates.
302      *
303      * @param name name of header 
304      * @param value value of header 
305      * @return a reference to 'this', which can be used for method chaining
306      */
307     public RESTClient addHeader(String name, String value) {
308         if (!headers.containsKey(name)) {
309             headers.put(name, new ArrayList<String>());
310         }
311
312         List<String> values = headers.get(name);
313         values.add(value);
314
315         return this;
316     }
317
318     /**
319      * Sets http header to be sent during http request.
320      * <p>
321      * Does not remove any headers with the same name, thus allowing 
322      * duplicates.
323      *
324      * @param name name of header 
325      * @param value value of header 
326      * @return a reference to 'this', which can be used for method chaining
327      */
328     public RESTClient setHeader(String name, String value) {
329         if (headers.containsKey(name)) {
330             headers.get(name).clear();
331         }
332
333         addHeader(name, value);
334
335         return this;
336     }
337     
338     /**
339      * Convenience method for adding the authorization header using the 
340      * specified OAuthToken object.
341      *
342      * @param token token to use for setting authorization
343      * @return a reference to 'this,' which can be used for method chaining
344      */
345     public RESTClient addAuthorizationHeader(String token) {
346         this.addHeader("Authorization", token);
347         return this;
348     }
349
350     /**
351      * Alias for httpGet().
352      *
353      * @see RESTClient#httpGet()
354      */
355     public APIResponse get() throws RESTException {
356         return httpGet();
357     }
358
359     /**
360      * Sends an http GET request using the parameters and headers previously
361      * set.
362      *
363      * @return api response
364      *
365      * @throws RESTException if request was unsuccessful
366      */
367     public APIResponse httpGet() throws RESTException {
368         HttpResponse response = null;
369
370         try (CloseableHttpClient httpClient = createClient()) {
371             String query = "";
372             if (!buildQuery().equals("")) {
373                 query = "?" + buildQuery();
374             }
375             HttpGet httpGet = new HttpGet(this.getURL() + query);
376             addInternalHeaders(httpGet);
377
378             response = httpClient.execute(httpGet);
379
380             APIResponse apiResponse = buildResponse(response);
381             return apiResponse;
382         } catch (IOException ioe) {
383             throw new RESTException(ioe);
384         } finally {
385             if (response != null) {
386                 this.releaseConnection(response);
387             }
388         }
389     }
390
391     /**
392      * Alias for httpPost()
393      *
394      * @see RESTClient#httpPost()
395      */
396     public APIResponse post() throws RESTException {
397         return httpPost();
398     }
399
400     /**
401      * Sends an http POST request.
402      * <p>
403      * POST body will be set to the values set using add/setParameter()
404      *
405      * @return api response
406      *
407      * @throws RESTException if POST was unsuccessful
408      */
409     public APIResponse httpPost() throws RESTException {
410             APIResponse response = httpPost(buildQuery()); 
411             return response;
412     }
413
414     /**
415      * Sends an http POST request using the specified body.
416      *
417      * @return api response
418      *
419      * @throws RESTException if POST was unsuccessful
420      */
421     public APIResponse httpPost(String body) throws RESTException {
422         HttpResponse response = null;
423         try (CloseableHttpClient httpClient = createClient()) {
424             HttpPost httpPost = new HttpPost(this.getURL());
425             addInternalHeaders(httpPost);
426             if (body != null && !body.equals("")) {
427                 httpEntity = new StringEntity(body);
428                 httpPost.setEntity(new StringEntity(body));
429             }
430
431             response = httpClient.execute(httpPost);
432
433             return buildResponse(response);
434         } catch (IOException e) {
435             throw new RESTException(e);
436         } finally {
437             if (response != null) {
438                 this.releaseConnection(response);
439             }
440         }
441     }
442
443     /**
444      * 
445      * @param body Data to PUT
446      * @return API response
447      * @throws RESTException 
448      */
449     public APIResponse httpPut(String body) throws RESTException {
450         HttpResponse response = null;
451         try (CloseableHttpClient httpClient = createClient()) {
452
453             String query = "";
454             if (!buildQuery().equals("")) {
455                 query = "?" + buildQuery();
456             }
457             HttpPut httpPut = new HttpPut(this.getURL() + query);
458             addInternalHeaders(httpPut);
459             if (body != null && !body.equals("")) {
460                 httpEntity = new StringEntity(body);
461                 httpPut.setEntity(httpEntity);
462             }
463
464             response = httpClient.execute(httpPut);
465
466             return buildResponse(response);
467         } catch (IOException e) {
468             throw new RESTException(e);
469         } finally {
470             if (response != null) {
471                 this.releaseConnection(response);
472             }
473         }
474     }
475
476     /**
477      * Alias for httpPatch().
478      *
479      * @see RESTClient#httpPatch()
480      */
481     public APIResponse patch(String body) throws RESTException {
482         return httpPatch(body);
483     }
484
485     /**
486      * 
487      * @param body Data to PATCH
488      * @return API response
489      * @throws RESTException 
490      */
491     public APIResponse httpPatch(String body) throws RESTException {
492         HttpResponse response = null;
493         try (CloseableHttpClient httpClient = createClient()) {
494             String query = "";
495             if (!buildQuery().equals("")) {
496                 query = "?" + buildQuery();
497             }
498             HttpPatch httpPatch = new HttpPatch(this.getURL() + query);
499             addInternalHeaders(httpPatch);
500             if (body != null && !body.equals("")) {
501                 httpEntity = new StringEntity(body);
502                 httpPatch.setEntity(httpEntity);
503             }
504
505             response = httpClient.execute(httpPatch);
506
507             return buildResponse(response);
508         } catch (IOException e) {
509             throw new RESTException(e);
510         } finally {
511             if (response != null) {
512                 this.releaseConnection(response);
513             }
514         }
515     }
516
517     /**
518      * Alias for httpDelete().
519      *
520      * @see RESTClient#httpDelete()
521      */
522     public APIResponse delete() throws RESTException {
523         return httpDelete();
524     }
525
526     /**
527      * Sends an http DELETE request using the parameters and headers previously
528      * set.
529      *
530      * @return api response
531      *
532      * @throws RESTException if request was unsuccessful
533      */
534     public APIResponse httpDelete() throws RESTException {
535         return httpDelete(null);
536     }
537
538     /**
539      * Sends an http DELETE request with a body, using the parameters and headers
540      * previously set.
541      *
542      * @return api response
543      *
544      * @throws RESTException if request was unsuccessful
545      */
546     public APIResponse httpDelete(String body) throws RESTException {
547         HttpResponse response = null;
548
549         try (CloseableHttpClient httpClient = createClient()){
550
551             String query = "";
552             if (!buildQuery().equals("")) {
553                 query = "?" + buildQuery();
554             }
555             HttpDeleteWithBody httpDelete = new HttpDeleteWithBody(this.getURL() + query);
556             addInternalHeaders(httpDelete);
557  
558             if (body != null && !body.equals("")) {
559                 httpEntity = new StringEntity(body);
560                 httpDelete.setEntity(httpEntity);
561             }
562
563             response = httpClient.execute(httpDelete);
564
565             APIResponse apiResponse = buildResponse(response);
566             return apiResponse;
567         } catch (IOException ioe) {
568             throw new RESTException(ioe);
569         } finally {
570             if (response != null) {
571                 this.releaseConnection(response);
572             }
573         }
574     }
575
576     public String getURL() {
577         return URL;
578     }
579     public LinkedHashMap<String,List<String>> getHeaders() {
580         return headers;
581     }
582     public LinkedHashMap<String,List<String>> getParameters() {
583         return parameters;
584     }
585     public HttpEntity getHttpEntity() {
586         return httpEntity;
587     }
588
589         
590         /**
591          * Allows inclusion of a request body with DELETE.
592          */
593         private class HttpDeleteWithBody extends HttpEntityEnclosingRequestBase {
594             public static final String METHOD_NAME = "DELETE";
595          
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 }