Add support for multi part form data
[ccsdk/sli/plugins.git] / restapi-call-node / provider / src / main / java / org / onap / ccsdk / sli / plugins / restapicall / RestapiCallNode.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * openECOMP : SDN-C
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights
6  *                      reserved.
7  * Modifications Copyright © 2018 IBM.
8  * ================================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * ============LICENSE_END=========================================================
21  */
22
23 package org.onap.ccsdk.sli.plugins.restapicall;
24
25 import static java.lang.Boolean.valueOf;
26 import static javax.ws.rs.client.Entity.entity;
27 import static org.onap.ccsdk.sli.plugins.restapicall.AuthType.fromString;
28
29 import java.io.File;
30 import java.io.FileInputStream;
31 import java.io.IOException;
32 import java.net.SocketException;
33 import java.net.URI;
34 import java.nio.file.Files;
35 import java.nio.file.Paths;
36 import java.security.KeyStore;
37 import java.util.ArrayList;
38 import java.util.Collections;
39 import java.util.HashMap;
40 import java.util.HashSet;
41 import java.util.List;
42 import java.util.Map;
43 import java.util.Map.Entry;
44 import java.util.Properties;
45 import java.util.Set;
46 import javax.net.ssl.HttpsURLConnection;
47 import javax.net.ssl.KeyManagerFactory;
48 import javax.net.ssl.SSLContext;
49 import javax.ws.rs.ProcessingException;
50 import javax.ws.rs.client.Client;
51 import javax.ws.rs.client.ClientBuilder;
52 import javax.ws.rs.client.Entity;
53 import javax.ws.rs.client.Invocation;
54 import javax.ws.rs.client.WebTarget;
55 import javax.ws.rs.core.*;
56
57 import org.apache.commons.lang3.StringUtils;
58 import org.glassfish.jersey.client.ClientProperties;
59 import org.glassfish.jersey.client.HttpUrlConnectorProvider;
60 import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
61 import org.glassfish.jersey.client.oauth1.ConsumerCredentials;
62 import org.glassfish.jersey.client.oauth1.OAuth1ClientSupport;
63 import org.glassfish.jersey.media.multipart.MultiPart;
64 import org.glassfish.jersey.media.multipart.MultiPartFeature;
65 import org.glassfish.jersey.media.multipart.file.FileDataBodyPart;
66 import org.onap.ccsdk.sli.core.sli.SvcLogicContext;
67 import org.onap.ccsdk.sli.core.sli.SvcLogicException;
68 import org.onap.ccsdk.sli.core.sli.SvcLogicJavaPlugin;
69 import org.slf4j.Logger;
70 import org.slf4j.LoggerFactory;
71
72 public class RestapiCallNode implements SvcLogicJavaPlugin {
73
74     protected static final String DME2_PROPERTIES_FILE_NAME = "dme2.properties";
75     protected static final String UEB_PROPERTIES_FILE_NAME = "ueb.properties";
76     protected static final String DEFAULT_PROPERTIES_DIR = "/opt/onap/ccsdk/data/properties";
77     protected static final String PROPERTIES_DIR_KEY = "SDNC_CONFIG_DIR";
78
79     private static final Logger log = LoggerFactory.getLogger(RestapiCallNode.class);
80     protected RetryPolicyStore retryPolicyStore;
81     private String uebServers;
82     private String defaultUebTemplateFileName = "/opt/bvc/restapi/templates/default-ueb-message.json";
83
84     private String responseReceivedMessage = "Response received. Time: {}";
85     private String responseHttpCodeMessage = "HTTP response code: {}";
86     private String requestPostingException = "Exception while posting http request to client ";
87     private static String skipSendingMessage = "skipSending";
88     private static String responsePrefix = "responsePrefix";
89     private static String restapiUrlString = "restapiUrl";
90
91     public RestapiCallNode() {
92         String configDir = System.getProperty(PROPERTIES_DIR_KEY, DEFAULT_PROPERTIES_DIR);
93
94         try (FileInputStream in = new FileInputStream(configDir + "/" + DME2_PROPERTIES_FILE_NAME)) {
95             Properties props = new Properties();
96             props.load(in);
97             this.retryPolicyStore = new RetryPolicyStore();
98             this.retryPolicyStore.setProxyServers(props.getProperty("proxyUrl"));
99             log.info("DME2 support enabled");
100         } catch (Exception e) {
101             log.warn("DME2 properties could not be read, DME2 support will not be enabled.", e);
102         }
103
104         try (FileInputStream in = new FileInputStream(configDir + "/" + UEB_PROPERTIES_FILE_NAME)) {
105             Properties props = new Properties();
106             props.load(in);
107             this.uebServers = props.getProperty("servers");
108             log.info("UEB support enabled");
109         } catch (Exception e) {
110             log.warn("UEB properties could not be read, UEB support will not be enabled.", e);
111         }
112     }
113
114     /**
115      * Returns parameters from the parameter map.
116      *
117      * @param paramMap parameter map
118      * @param p        parameters instance
119      * @return parameters filed instance
120      * @throws SvcLogicException when svc logic exception occurs
121      */
122     public static Parameters getParameters(Map<String, String> paramMap,
123         Parameters p)
124         throws SvcLogicException {
125         p.templateFileName = parseParam(paramMap, "templateFileName",
126             false, null);
127         p.requestBody = parseParam(paramMap, "requestBody", false, null);
128         p.restapiUrl = parseParam(paramMap, restapiUrlString, true, null);
129         validateUrl(p.restapiUrl);
130         p.restapiUser = parseParam(paramMap, "restapiUser", false, null);
131         p.restapiPassword = parseParam(paramMap, "restapiPassword", false,
132             null);
133         p.oAuthConsumerKey = parseParam(paramMap, "oAuthConsumerKey",
134             false, null);
135         p.oAuthConsumerSecret = parseParam(paramMap, "oAuthConsumerSecret",
136             false, null);
137         p.oAuthSignatureMethod = parseParam(paramMap, "oAuthSignatureMethod",
138             false, null);
139         p.oAuthVersion = parseParam(paramMap, "oAuthVersion", false, null);
140         p.contentType = parseParam(paramMap, "contentType", false, null);
141         p.format = Format.fromString(parseParam(paramMap, "format", false,
142             "json"));
143         p.authtype = fromString(parseParam(paramMap, "authType", false,
144             "unspecified"));
145         p.httpMethod = HttpMethod.fromString(parseParam(paramMap, "httpMethod",
146             false, "post"));
147         p.responsePrefix = parseParam(paramMap, responsePrefix, false, null);
148         p.listNameList = getListNameList(paramMap);
149         String skipSendingStr = paramMap.get(skipSendingMessage);
150         p.skipSending = "true".equalsIgnoreCase(skipSendingStr);
151         p.convertResponse = valueOf(parseParam(paramMap, "convertResponse",
152             false, "true"));
153         p.trustStoreFileName = parseParam(paramMap, "trustStoreFileName",
154             false, null);
155         p.trustStorePassword = parseParam(paramMap, "trustStorePassword",
156             false, null);
157         p.keyStoreFileName = parseParam(paramMap, "keyStoreFileName",
158             false, null);
159         p.keyStorePassword = parseParam(paramMap, "keyStorePassword",
160             false, null);
161         p.ssl = p.trustStoreFileName != null && p.trustStorePassword != null
162             && p.keyStoreFileName != null && p.keyStorePassword != null;
163         p.customHttpHeaders = parseParam(paramMap, "customHttpHeaders",
164             false, null);
165         p.partner = parseParam(paramMap, "partner", false, null);
166         p.dumpHeaders = valueOf(parseParam(paramMap, "dumpHeaders",
167             false, null));
168         p.returnRequestPayload = valueOf(parseParam(
169             paramMap, "returnRequestPayload", false, null));
170         p.accept = parseParam(paramMap, "accept",
171                 false, null);
172         p.multipartFormData = valueOf(parseParam(paramMap, "multipartFormData",
173                 false, "false"));
174         p.multipartFile = parseParam(paramMap, "multipartFile",
175                 false, null);
176         return p;
177     }
178
179     /**
180      * Validates the given URL in the parameters.
181      *
182      * @param restapiUrl rest api URL
183      * @throws SvcLogicException when URL validation fails
184      */
185     private static void validateUrl(String restapiUrl)
186         throws SvcLogicException {
187         try {
188             URI.create(restapiUrl);
189         } catch (IllegalArgumentException e) {
190             throw new SvcLogicException("Invalid input of url "
191                 + e.getLocalizedMessage(), e);
192         }
193     }
194
195     /**
196      * Returns the list of list name.
197      *
198      * @param paramMap parameters map
199      * @return list of list name
200      */
201     private static Set<String> getListNameList(Map<String, String> paramMap) {
202         Set<String> ll = new HashSet<>();
203         for (Map.Entry<String, String> entry : paramMap.entrySet()) {
204             if (entry.getKey().startsWith("listName")) {
205                 ll.add(entry.getValue());
206             }
207         }
208         return ll;
209     }
210
211     /**
212      * Parses the parameter string map of property, validates if required,
213      * assigns default value if present and returns the value.
214      *
215      * @param paramMap string param map
216      * @param name     name of the property
217      * @param required if value required
218      * @param def      default value
219      * @return value of the property
220      * @throws SvcLogicException if required parameter value is empty
221      */
222     public static String parseParam(Map<String, String> paramMap, String name,
223         boolean required, String def)
224         throws SvcLogicException {
225         String s = paramMap.get(name);
226
227         if (s == null || s.trim().length() == 0) {
228             if (!required) {
229                 return def;
230             }
231             throw new SvcLogicException("Parameter " + name + " is required in RestapiCallNode");
232         }
233
234         s = s.trim();
235         StringBuilder value = new StringBuilder();
236         int i = 0;
237         int i1 = s.indexOf('%');
238         while (i1 >= 0) {
239             int i2 = s.indexOf('%', i1 + 1);
240             if (i2 < 0) {
241                 break;
242             }
243
244             String varName = s.substring(i1 + 1, i2);
245             String varValue = System.getenv(varName);
246             if (varValue == null) {
247                 varValue = "%" + varName + "%";
248             }
249
250             value.append(s.substring(i, i1));
251             value.append(varValue);
252
253             i = i2 + 1;
254             i1 = s.indexOf('%', i);
255         }
256         value.append(s.substring(i));
257
258         log.info("Parameter {}: [{}]", name, value);
259         return value.toString();
260     }
261
262     public RetryPolicyStore getRetryPolicyStore() {
263         return retryPolicyStore;
264     }
265
266     public void setRetryPolicyStore(RetryPolicyStore retryPolicyStore) {
267         this.retryPolicyStore = retryPolicyStore;
268     }
269
270     /**
271      * Allows Directed Graphs  the ability to interact with REST APIs.
272      * @param paramMap HashMap<String,String> of parameters passed by the DG to this function
273      * <table border="1">
274      *  <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th><th>example values</th></thead>
275      *  <tbody>
276      *      <tr><td>templateFileName</td><td>Optional</td><td>full path to template file that can be used to build a request</td><td>/sdncopt/bvc/restapi/templates/vnf_service-configuration-operation_minimal.json</td></tr>
277      *      <tr><td>restapiUrl</td><td>Mandatory</td><td>url to send the request to</td><td>https://sdncodl:8543/restconf/operations/L3VNF-API:create-update-vnf-request</td></tr>
278      *      <tr><td>restapiUser</td><td>Optional</td><td>user name to use for http basic authentication</td><td>sdnc_ws</td></tr>
279      *      <tr><td>restapiPassword</td><td>Optional</td><td>unencrypted password to use for http basic authentication</td><td>plain_password</td></tr>
280      *      <tr><td>oAuthConsumerKey</td><td>Optional</td><td>Consumer key to use for http oAuth authentication</td><td>plain_key</td></tr>
281      *      <tr><td>oAuthConsumerSecret</td><td>Optional</td><td>Consumer secret to use for http oAuth authentication</td><td>plain_secret</td></tr>
282      *      <tr><td>oAuthSignatureMethod</td><td>Optional</td><td>Consumer method to use for http oAuth authentication</td><td>method</td></tr>
283      *      <tr><td>oAuthVersion</td><td>Optional</td><td>Version http oAuth authentication</td><td>version</td></tr>
284      *      <tr><td>contentType</td><td>Optional</td><td>http content type to set in the http header</td><td>usually application/json or application/xml</td></tr>
285      *      <tr><td>format</td><td>Optional</td><td>should match request body format</td><td>json or xml</td></tr>
286      *      <tr><td>httpMethod</td><td>Optional</td><td>http method to use when sending the request</td><td>get post put delete patch</td></tr>
287      *      <tr><td>responsePrefix</td><td>Optional</td><td>location the response will be written to in context memory</td><td>tmp.restapi.result</td></tr>
288      *      <tr><td>listName[i]</td><td>Optional</td><td>Used for processing XML responses with repeating elements.</td>vpn-information.vrf-details<td></td></tr>
289      *      <tr><td>skipSending</td><td>Optional</td><td></td><td>true or false</td></tr>
290      *      <tr><td>convertResponse </td><td>Optional</td><td>whether the response should be converted</td><td>true or false</td></tr>
291      *      <tr><td>customHttpHeaders</td><td>Optional</td><td>a list additional http headers to be passed in, follow the format in the example</td><td>X-CSI-MessageId=messageId,headerFieldName=headerFieldValue</td></tr>
292      *      <tr><td>dumpHeaders</td><td>Optional</td><td>when true writes http header content to context memory</td><td>true or false</td></tr>
293      *      <tr><td>partner</td><td>Optional</td><td>needed for DME2 calls</td><td>dme2proxy</td></tr>
294      *      <tr><td>returnRequestPayload</td><td>Optional</td><td>used to return payload built in the request</td><td>true or false</td></tr>
295      *  </tbody>
296      * </table>
297      * @param ctx Reference to context memory
298      * @throws SvcLogicException
299      * @since 11.0.2
300      * @see String#split(String, int)
301      */
302     public void sendRequest(Map<String, String> paramMap, SvcLogicContext ctx) throws SvcLogicException {
303         sendRequest(paramMap, ctx, null);
304     }
305
306     public void sendRequest(Map<String, String> paramMap, SvcLogicContext ctx, Integer retryCount)
307         throws SvcLogicException {
308
309         RetryPolicy retryPolicy = null;
310         HttpResponse r = new HttpResponse();
311         try {
312             Parameters p = getParameters(paramMap, new Parameters());
313             if (p.partner != null) {
314                 retryPolicy = retryPolicyStore.getRetryPolicy(p.partner);
315             }
316             String pp = p.responsePrefix != null ? p.responsePrefix + '.' : "";
317
318             String req = null;
319             if (p.templateFileName != null) {
320                 String reqTemplate = readFile(p.templateFileName);
321                 req = buildXmlJsonRequest(ctx, reqTemplate, p.format);
322             } else if (p.requestBody != null) {
323                 req = p.requestBody;
324             }
325             r = sendHttpRequest(req, p);
326             setResponseStatus(ctx, p.responsePrefix, r);
327
328             if (p.dumpHeaders && r.headers != null) {
329                 for (Entry<String, List<String>> a : r.headers.entrySet()) {
330                     ctx.setAttribute(pp + "header." + a.getKey(), StringUtils.join(a.getValue(), ","));
331                 }
332             }
333
334             if (p.returnRequestPayload && req != null) {
335                 ctx.setAttribute(pp + "httpRequest", req);
336             }
337
338             if (r.body != null && r.body.trim().length() > 0) {
339                 ctx.setAttribute(pp + "httpResponse", r.body);
340
341                 if (p.convertResponse) {
342                     Map<String, String> mm = null;
343                     if (p.format == Format.XML) {
344                         mm = XmlParser.convertToProperties(r.body, p.listNameList);
345                     } else if (p.format == Format.JSON) {
346                         mm = JsonParser.convertToProperties(r.body);
347                     }
348
349                     if (mm != null) {
350                         for (Map.Entry<String, String> entry : mm.entrySet()) {
351                             ctx.setAttribute(pp + entry.getKey(), entry.getValue());
352                         }
353                     }
354                 }
355             }
356         } catch (SvcLogicException e) {
357             boolean shouldRetry = false;
358             if (e.getCause().getCause() instanceof SocketException) {
359                 shouldRetry = true;
360             }
361
362             log.error("Error sending the request: " + e.getMessage(), e);
363             String prefix = parseParam(paramMap, responsePrefix, false, null);
364             if (retryPolicy == null || !shouldRetry) {
365                 setFailureResponseStatus(ctx, prefix, e.getMessage(), r);
366             } else {
367                 if (retryCount == null) {
368                     retryCount = 0;
369                 }
370                 String retryMessage = retryCount + " attempts were made out of " + retryPolicy.getMaximumRetries() +
371                     " maximum retries.";
372                 log.debug(retryMessage);
373                 try {
374                     retryCount = retryCount + 1;
375                     if (retryCount < retryPolicy.getMaximumRetries() + 1) {
376                         URI uri = new URI(paramMap.get(restapiUrlString));
377                         String hostname = uri.getHost();
378                         String retryString = retryPolicy.getNextHostName(uri.toString());
379                         URI uriTwo = new URI(retryString);
380                         URI retryUri = UriBuilder.fromUri(uri).host(uriTwo.getHost()).port(uriTwo.getPort()).scheme(
381                             uriTwo.getScheme()).build();
382                         paramMap.put(restapiUrlString, retryUri.toString());
383                         log.debug("URL was set to {}", retryUri.toString());
384                         log.debug("Failed to communicate with host {}. Request will be re-attempted using the host {}.",
385                             hostname, retryString);
386                         log.debug("This is retry attempt {} out of {}", retryCount, retryPolicy.getMaximumRetries());
387                         sendRequest(paramMap, ctx, retryCount);
388                     } else {
389                         log.debug("Maximum retries reached, calling setFailureResponseStatus.");
390                         setFailureResponseStatus(ctx, prefix, e.getMessage(), r);
391                     }
392                 } catch (Exception ex) {
393                     log.error("Could not attempt retry.", ex);
394                     String retryErrorMessage =
395                         "Retry attempt has failed. No further retry shall be attempted, calling " +
396                             "setFailureResponseStatus.";
397                     setFailureResponseStatus(ctx, prefix, retryErrorMessage, r);
398                 }
399             }
400         }
401
402         if (r != null && r.code >= 300) {
403             throw new SvcLogicException(String.valueOf(r.code) + ": " + r.message);
404         }
405     }
406
407     protected String buildXmlJsonRequest(SvcLogicContext ctx, String template, Format format)
408         throws SvcLogicException {
409         log.info("Building {} started", format);
410         long t1 = System.currentTimeMillis();
411         String originalTemplate = template;
412
413         template = expandRepeats(ctx, template, 1);
414
415         Map<String, String> mm = new HashMap<>();
416         for (String s : ctx.getAttributeKeySet()) {
417             mm.put(s, ctx.getAttribute(s));
418         }
419
420         StringBuilder ss = new StringBuilder();
421         int i = 0;
422         while (i < template.length()) {
423             int i1 = template.indexOf("${", i);
424             if (i1 < 0) {
425                 ss.append(template.substring(i));
426                 break;
427             }
428
429             int i2 = template.indexOf('}', i1 + 2);
430             if (i2 < 0) {
431                 throw new SvcLogicException("Template error: Matching } not found");
432             }
433
434             String var1 = template.substring(i1 + 2, i2);
435             String value1 = format == Format.XML ? XmlJsonUtil.getXml(mm, var1) : XmlJsonUtil.getJson(mm, var1);
436             if (value1 == null || value1.trim().length() == 0) {
437                 // delete the whole element (line)
438                 int i3 = template.lastIndexOf('\n', i1);
439                 if (i3 < 0) {
440                     i3 = 0;
441                 }
442                 int i4 = template.indexOf('\n', i1);
443                 if (i4 < 0) {
444                     i4 = template.length();
445                 }
446
447                 if (i < i3) {
448                     ss.append(template.substring(i, i3));
449                 }
450                 i = i4;
451             } else {
452                 ss.append(template.substring(i, i1)).append(value1);
453                 i = i2 + 1;
454             }
455         }
456
457         String req = format == Format.XML
458             ? XmlJsonUtil.removeEmptyStructXml(ss.toString()) : XmlJsonUtil.removeEmptyStructJson(originalTemplate, ss.toString());
459
460         if (format == Format.JSON) {
461             req = XmlJsonUtil.removeLastCommaJson(req);
462         }
463
464         long t2 = System.currentTimeMillis();
465         log.info("Building {} completed. Time: {}", format, (t2 - t1));
466
467         return req;
468     }
469
470     protected String expandRepeats(SvcLogicContext ctx, String template, int level) throws SvcLogicException {
471         StringBuilder newTemplate = new StringBuilder();
472         int k = 0;
473         while (k < template.length()) {
474             int i1 = template.indexOf("${repeat:", k);
475             if (i1 < 0) {
476                 newTemplate.append(template.substring(k));
477                 break;
478             }
479
480             int i2 = template.indexOf(':', i1 + 9);
481             if (i2 < 0) {
482                 throw new SvcLogicException(
483                     "Template error: Context variable name followed by : is required after repeat");
484             }
485
486             // Find the closing }, store in i3
487             int nn = 1;
488             int i3 = -1;
489             int i = i2;
490             while (nn > 0 && i < template.length()) {
491                 i3 = template.indexOf('}', i);
492                 if (i3 < 0) {
493                     throw new SvcLogicException("Template error: Matching } not found");
494                 }
495                 int i32 = template.indexOf('{', i);
496                 if (i32 >= 0 && i32 < i3) {
497                     nn++;
498                     i = i32 + 1;
499                 } else {
500                     nn--;
501                     i = i3 + 1;
502                 }
503             }
504
505             String var1 = template.substring(i1 + 9, i2);
506             String value1 = ctx.getAttribute(var1);
507             log.info("     {}:{}", var1, value1);
508             int n = 0;
509             try {
510                 n = Integer.parseInt(value1);
511             } catch (NumberFormatException e) {
512                 log.info("value1 not set or not a number, n will remain set at zero");
513             }
514
515             newTemplate.append(template.substring(k, i1));
516
517             String rpt = template.substring(i2 + 1, i3);
518
519             for (int ii = 0; ii < n; ii++) {
520                 String ss = rpt.replaceAll("\\[\\$\\{" + level + "\\}\\]", "[" + ii + "]");
521                 if (ii == n - 1 && ss.trim().endsWith(",")) {
522                     int i4 = ss.lastIndexOf(',');
523                     if (i4 > 0) {
524                         ss = ss.substring(0, i4) + ss.substring(i4 + 1);
525                     }
526                 }
527                 newTemplate.append(ss);
528             }
529
530             k = i3 + 1;
531         }
532
533         if (k == 0) {
534             return newTemplate.toString();
535         }
536
537         return expandRepeats(ctx, newTemplate.toString(), level + 1);
538     }
539
540     protected String readFile(String fileName) throws SvcLogicException {
541         try {
542             byte[] encoded = Files.readAllBytes(Paths.get(fileName));
543             return new String(encoded, "UTF-8");
544         } catch (IOException | SecurityException e) {
545             throw new SvcLogicException("Unable to read file " + fileName + e.getLocalizedMessage(), e);
546         }
547     }
548
549     protected Client addAuthType(Client c, FileParam fp) throws SvcLogicException {
550         Parameters p = new Parameters();
551         p.restapiUser = fp.user;
552         p.restapiPassword = fp.password;
553         p.oAuthConsumerKey = fp.oAuthConsumerKey;
554         p.oAuthVersion = fp.oAuthVersion;
555         p.oAuthConsumerSecret = fp.oAuthConsumerSecret;
556         p.oAuthSignatureMethod = fp.oAuthSignatureMethod;
557         p.authtype = fp.authtype;
558         return addAuthType(c, p);
559     }
560
561     public Client addAuthType(Client client, Parameters p) throws SvcLogicException {
562         if (p.authtype == AuthType.Unspecified) {
563             if (p.restapiUser != null && p.restapiPassword != null) {
564                 client.register(HttpAuthenticationFeature.basic(p.restapiUser, p.restapiPassword));
565             } else if (p.oAuthConsumerKey != null && p.oAuthConsumerSecret != null
566                 && p.oAuthSignatureMethod != null) {
567                 Feature oAuth1Feature = OAuth1ClientSupport
568                     .builder(new ConsumerCredentials(p.oAuthConsumerKey, p.oAuthConsumerSecret))
569                     .version(p.oAuthVersion).signatureMethod(p.oAuthSignatureMethod).feature().build();
570                 client.register(oAuth1Feature);
571             }
572         } else {
573             if (p.authtype == AuthType.DIGEST) {
574                 if (p.restapiUser != null && p.restapiPassword != null) {
575                     client.register(HttpAuthenticationFeature.digest(p.restapiUser, p.restapiPassword));
576                 } else {
577                     throw new SvcLogicException(
578                         "oAUTH authentication type selected but all restapiUser and restapiPassword " +
579                             "parameters doesn't exist", new Throwable());
580                 }
581             } else if (p.authtype == AuthType.BASIC) {
582                 if (p.restapiUser != null && p.restapiPassword != null) {
583                     client.register(HttpAuthenticationFeature.basic(p.restapiUser, p.restapiPassword));
584                 } else {
585                     throw new SvcLogicException(
586                         "oAUTH authentication type selected but all restapiUser and restapiPassword " +
587                             "parameters doesn't exist", new Throwable());
588                 }
589             } else if (p.authtype == AuthType.OAUTH) {
590                 if (p.oAuthConsumerKey != null && p.oAuthConsumerSecret != null && p.oAuthSignatureMethod != null) {
591                     Feature oAuth1Feature = OAuth1ClientSupport
592                         .builder(new ConsumerCredentials(p.oAuthConsumerKey, p.oAuthConsumerSecret))
593                         .version(p.oAuthVersion).signatureMethod(p.oAuthSignatureMethod).feature().build();
594                     client.register(oAuth1Feature);
595                 } else {
596                     throw new SvcLogicException(
597                         "oAUTH authentication type selected but all oAuthConsumerKey, oAuthConsumerSecret " +
598                             "and oAuthSignatureMethod parameters doesn't exist", new Throwable());
599                 }
600             }
601         }
602         return client;
603     }
604
605     /**
606      * Receives the http response for the http request sent.
607      *
608      * @param request request msg
609      * @param p       parameters
610      * @return HTTP response
611      * @throws SvcLogicException when sending http request fails
612      */
613     public HttpResponse sendHttpRequest(String request, Parameters p)
614         throws SvcLogicException {
615
616         SSLContext ssl = null;
617         if (p.ssl && p.restapiUrl.startsWith("https")) {
618             ssl = createSSLContext(p);
619         }
620         Client client;
621
622         if (ssl != null) {
623             HttpsURLConnection.setDefaultSSLSocketFactory(ssl.getSocketFactory());
624             client = ClientBuilder.newBuilder().sslContext(ssl).hostnameVerifier((s, sslSession) -> true)
625                 .build();
626         } else {
627             client = ClientBuilder.newBuilder().hostnameVerifier((s, sslSession) -> true)
628                 .build();
629         }
630         client.property(ClientProperties.CONNECT_TIMEOUT, 5000);
631         // Needed to support additional HTTP methods such as PATCH
632         client.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true);
633
634         WebTarget webTarget = addAuthType(client, p).target(p.restapiUrl);
635
636         log.info("Sending request:");
637         log.info(request);
638         long t1 = System.currentTimeMillis();
639
640         HttpResponse r = new HttpResponse();
641         r.code = 200;
642
643         String accept = p.accept;
644         if(accept == null) {
645             accept = p.format == Format.XML ? "application/xml" : "application/json";
646         }
647
648         String contentType = p.contentType;
649         if(contentType == null) {
650             contentType = accept + ";charset=UTF-8";
651         }
652
653         if (!p.skipSending && !p.multipartFormData) {
654
655             Invocation.Builder invocationBuilder = webTarget.request(contentType).accept(accept);
656
657             if (p.format == Format.NONE) {
658                 invocationBuilder.header("", "");
659             }
660
661             if (p.customHttpHeaders != null && p.customHttpHeaders.length() > 0) {
662                 String[] keyValuePairs = p.customHttpHeaders.split(",");
663                 for (String singlePair : keyValuePairs) {
664                     int equalPosition = singlePair.indexOf('=');
665                     invocationBuilder.header(singlePair.substring(0, equalPosition),
666                         singlePair.substring(equalPosition + 1, singlePair.length()));
667                 }
668             }
669
670             invocationBuilder.header("X-ECOMP-RequestID", org.slf4j.MDC.get("X-ECOMP-RequestID"));
671
672             Response response;
673
674             try {
675                 response = invocationBuilder.method(p.httpMethod.toString(), entity(request, contentType));
676             } catch (ProcessingException | IllegalStateException e) {
677                 throw new SvcLogicException(requestPostingException +
678                     e.getLocalizedMessage(), e);
679             }
680
681             r.code = response.getStatus();
682             r.headers = response.getStringHeaders();
683             EntityTag etag = response.getEntityTag();
684             if (etag != null) {
685                 r.message = etag.getValue();
686             }
687             if (response.hasEntity() && r.code != 204) {
688                 r.body = response.readEntity(String.class);
689             }
690         } else if (!p.skipSending && p.multipartFormData) {
691
692             WebTarget wt = client.register(MultiPartFeature.class).target(p.restapiUrl);
693
694             MultiPart multiPart = new MultiPart();
695             multiPart.setMediaType(MediaType.MULTIPART_FORM_DATA_TYPE);
696
697             FileDataBodyPart fileDataBodyPart = new FileDataBodyPart("file",
698                     new File(p.multipartFile),
699                     MediaType.APPLICATION_OCTET_STREAM_TYPE);
700             multiPart.bodyPart(fileDataBodyPart);
701
702
703             Invocation.Builder invocationBuilder = wt.request(contentType).accept(accept);
704
705             if (p.format == Format.NONE) {
706                 invocationBuilder.header("", "");
707             }
708
709             if (p.customHttpHeaders != null && p.customHttpHeaders.length() > 0) {
710                 String[] keyValuePairs = p.customHttpHeaders.split(",");
711                 for (String singlePair : keyValuePairs) {
712                     int equalPosition = singlePair.indexOf('=');
713                     invocationBuilder.header(singlePair.substring(0, equalPosition),
714                             singlePair.substring(equalPosition + 1, singlePair.length()));
715                 }
716             }
717
718             invocationBuilder.header("X-ECOMP-RequestID", org.slf4j.MDC.get("X-ECOMP-RequestID"));
719
720             Response response;
721
722             try {
723                 response = invocationBuilder.method(p.httpMethod.toString(), entity(multiPart, multiPart.getMediaType()));
724             } catch (ProcessingException | IllegalStateException e) {
725                 throw new SvcLogicException(requestPostingException +
726                         e.getLocalizedMessage(), e);
727             }
728
729             r.code = response.getStatus();
730             r.headers = response.getStringHeaders();
731             EntityTag etag = response.getEntityTag();
732             if (etag != null) {
733                 r.message = etag.getValue();
734             }
735             if (response.hasEntity() && r.code != 204) {
736                 r.body = response.readEntity(String.class);
737             }
738
739         }
740
741         long t2 = System.currentTimeMillis();
742         log.info(responseReceivedMessage, (t2 - t1));
743         log.info(responseHttpCodeMessage, r.code);
744         log.info("HTTP response message: {}", r.message);
745         logHeaders(r.headers);
746         log.info("HTTP response: {}", r.body);
747
748         return r;
749     }
750
751     protected SSLContext createSSLContext(Parameters p) {
752         try (FileInputStream in = new FileInputStream(p.keyStoreFileName)) {
753             System.setProperty("jsse.enableSNIExtension", "false");
754             System.setProperty("javax.net.ssl.trustStore", p.trustStoreFileName);
755             System.setProperty("javax.net.ssl.trustStorePassword", p.trustStorePassword);
756
757             HttpsURLConnection.setDefaultHostnameVerifier((string, ssls) -> true);
758
759             KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
760             KeyStore ks = KeyStore.getInstance("PKCS12");
761             char[] pwd = p.keyStorePassword.toCharArray();
762             ks.load(in, pwd);
763             kmf.init(ks, pwd);
764
765             SSLContext ctx = SSLContext.getInstance("TLS");
766             ctx.init(kmf.getKeyManagers(), null, null);
767             return ctx;
768         } catch (Exception e) {
769             log.error("Error creating SSLContext: {}", e.getMessage(), e);
770         }
771         return null;
772     }
773
774     protected void setFailureResponseStatus(SvcLogicContext ctx, String prefix, String errorMessage,
775         HttpResponse resp) {
776         resp.code = 500;
777         resp.message = errorMessage;
778         String pp = prefix != null ? prefix + '.' : "";
779         ctx.setAttribute(pp + "response-code", String.valueOf(resp.code));
780         ctx.setAttribute(pp + "response-message", resp.message);
781     }
782
783     protected void setResponseStatus(SvcLogicContext ctx, String prefix, HttpResponse r) {
784         String pp = prefix != null ? prefix + '.' : "";
785         ctx.setAttribute(pp + "response-code", String.valueOf(r.code));
786         ctx.setAttribute(pp + "response-message", r.message);
787     }
788
789     public void sendFile(Map<String, String> paramMap, SvcLogicContext ctx) throws SvcLogicException {
790         HttpResponse r = null;
791         try {
792             FileParam p = getFileParameters(paramMap);
793             byte[] data = Files.readAllBytes(Paths.get(p.fileName));
794
795             r = sendHttpData(data, p);
796             setResponseStatus(ctx, p.responsePrefix, r);
797
798         } catch (SvcLogicException | IOException e) {
799             log.error("Error sending the request: {}", e.getMessage(), e);
800
801             r = new HttpResponse();
802             r.code = 500;
803             r.message = e.getMessage();
804             String prefix = parseParam(paramMap, responsePrefix, false, null);
805             setResponseStatus(ctx, prefix, r);
806         }
807
808         if (r != null && r.code >= 300) {
809             throw new SvcLogicException(String.valueOf(r.code) + ": " + r.message);
810         }
811     }
812
813     private FileParam getFileParameters(Map<String, String> paramMap) throws SvcLogicException {
814         FileParam p = new FileParam();
815         p.fileName = parseParam(paramMap, "fileName", true, null);
816         p.url = parseParam(paramMap, "url", true, null);
817         p.user = parseParam(paramMap, "user", false, null);
818         p.password = parseParam(paramMap, "password", false, null);
819         p.httpMethod = HttpMethod.fromString(parseParam(paramMap, "httpMethod", false, "post"));
820         p.responsePrefix = parseParam(paramMap, responsePrefix, false, null);
821         String skipSendingStr = paramMap.get(skipSendingMessage);
822         p.skipSending = "true".equalsIgnoreCase(skipSendingStr);
823         p.oAuthConsumerKey = parseParam(paramMap, "oAuthConsumerKey", false, null);
824         p.oAuthVersion = parseParam(paramMap, "oAuthVersion", false, null);
825         p.oAuthConsumerSecret = parseParam(paramMap, "oAuthConsumerSecret", false, null);
826         p.oAuthSignatureMethod = parseParam(paramMap, "oAuthSignatureMethod", false, null);
827         p.authtype = fromString(parseParam(paramMap, "authType", false, "unspecified"));
828         return p;
829     }
830
831     public void postMessageOnUeb(Map<String, String> paramMap, SvcLogicContext ctx) throws SvcLogicException {
832         HttpResponse r;
833         try {
834             UebParam p = getUebParameters(paramMap);
835
836             String pp = p.responsePrefix != null ? p.responsePrefix + '.' : "";
837
838             String req;
839
840             if (p.templateFileName == null) {
841                 log.info("No template file name specified. Using default UEB template: {}", defaultUebTemplateFileName);
842                 p.templateFileName = defaultUebTemplateFileName;
843             }
844
845             String reqTemplate = readFile(p.templateFileName);
846             reqTemplate = reqTemplate.replaceAll("rootVarName", p.rootVarName);
847             req = buildXmlJsonRequest(ctx, reqTemplate, Format.JSON);
848
849             r = postOnUeb(req, p);
850             setResponseStatus(ctx, p.responsePrefix, r);
851             if (r.body != null) {
852                 ctx.setAttribute(pp + "httpResponse", r.body);
853             }
854
855         } catch (SvcLogicException e) {
856             log.error("Error sending the request: {}", e.getMessage(), e);
857
858             r = new HttpResponse();
859             r.code = 500;
860             r.message = e.getMessage();
861             String prefix = parseParam(paramMap, responsePrefix, false, null);
862             setResponseStatus(ctx, prefix, r);
863         }
864
865         if (r.code >= 300) {
866             throw new SvcLogicException(String.valueOf(r.code) + ": " + r.message);
867         }
868     }
869
870     protected HttpResponse sendHttpData(byte[] data, FileParam p) throws SvcLogicException {
871
872         Client client = ClientBuilder.newBuilder().build();
873         client.property(ClientProperties.CONNECT_TIMEOUT, 5000);
874         client.property(ClientProperties.FOLLOW_REDIRECTS, true);
875         WebTarget webTarget = addAuthType(client, p).target(p.url);
876
877         log.info("Sending file");
878         long t1 = System.currentTimeMillis();
879
880         HttpResponse r = new HttpResponse();
881         r.code = 200;
882
883         if (!p.skipSending) {
884             String tt = "application/octet-stream";
885             Invocation.Builder invocationBuilder = webTarget.request(tt).accept(tt);
886
887             Response response;
888
889             try {
890                 if (p.httpMethod == HttpMethod.POST) {
891                     response = invocationBuilder.post(Entity.entity(data, tt));
892                 } else if (p.httpMethod == HttpMethod.PUT) {
893                     response = invocationBuilder.put(Entity.entity(data, tt));
894                 } else {
895                     throw new SvcLogicException("Http operation" + p.httpMethod + "not supported");
896                 }
897             } catch (ProcessingException e) {
898                 throw new SvcLogicException(requestPostingException +
899                     e.getLocalizedMessage(), e);
900             }
901
902             r.code = response.getStatus();
903             r.headers = response.getStringHeaders();
904             EntityTag etag = response.getEntityTag();
905             if (etag != null) {
906                 r.message = etag.getValue();
907             }
908             if (response.hasEntity() && r.code != 204) {
909                 r.body = response.readEntity(String.class);
910             }
911
912             if (r.code == 301) {
913                 String newUrl = response.getStringHeaders().getFirst("Location");
914
915                 log.info("Got response code 301. Sending same request to URL: {}", newUrl);
916
917                     webTarget = client.target(newUrl);
918                 invocationBuilder = webTarget.request(tt).accept(tt);
919
920                 try {
921                     if (p.httpMethod == HttpMethod.POST) {
922                         response = invocationBuilder.post(Entity.entity(data, tt));
923                     } else if (p.httpMethod == HttpMethod.PUT) {
924                         response = invocationBuilder.put(Entity.entity(data, tt));
925                     } else {
926                         throw new SvcLogicException("Http operation" + p.httpMethod + "not supported");
927                     }
928                 } catch (ProcessingException e) {
929                     throw new SvcLogicException(requestPostingException +
930                         e.getLocalizedMessage(), e);
931                 }
932
933                 r.code = response.getStatus();
934                 etag = response.getEntityTag();
935                 if (etag != null) {
936                     r.message = etag.getValue();
937                 }
938                 if (response.hasEntity() && r.code != 204) {
939                     r.body = response.readEntity(String.class);
940                 }
941             }
942         }
943
944         long t2 = System.currentTimeMillis();
945         log.info(responseReceivedMessage, (t2 - t1));
946         log.info(responseHttpCodeMessage, r.code);
947         log.info("HTTP response message: {}", r.message);
948         logHeaders(r.headers);
949         log.info("HTTP response: {}", r.body);
950
951         return r;
952     }
953
954     private UebParam getUebParameters(Map<String, String> paramMap) throws SvcLogicException {
955         UebParam p = new UebParam();
956         p.topic = parseParam(paramMap, "topic", true, null);
957         p.templateFileName = parseParam(paramMap, "templateFileName", false, null);
958         p.rootVarName = parseParam(paramMap, "rootVarName", false, null);
959         p.responsePrefix = parseParam(paramMap, responsePrefix, false, null);
960         String skipSendingStr = paramMap.get(skipSendingMessage);
961         p.skipSending = "true".equalsIgnoreCase(skipSendingStr);
962         return p;
963     }
964
965     protected void logProperties(Map<String, Object> mm) {
966         List<String> ll = new ArrayList<>();
967         for (Object o : mm.keySet()) {
968             ll.add((String) o);
969         }
970         Collections.sort(ll);
971
972         log.info("Properties:");
973         for (String name : ll) {
974             log.info("--- {}:{}", name, String.valueOf(mm.get(name)));
975         }
976     }
977
978     protected void logHeaders(MultivaluedMap<String, String> mm) {
979         log.info("HTTP response headers:");
980
981         if (mm == null) {
982             return;
983         }
984
985         List<String> ll = new ArrayList<>();
986         for (Object o : mm.keySet()) {
987             ll.add((String) o);
988         }
989         Collections.sort(ll);
990
991         for (String name : ll) {
992             log.info("--- {}:{}", name, String.valueOf(mm.get(name)));
993         }
994     }
995
996     protected HttpResponse postOnUeb(String request, UebParam p) throws SvcLogicException {
997         String[] urls = uebServers.split(" ");
998         for (int i = 0; i < urls.length; i++) {
999             if (!urls[i].endsWith("/")) {
1000                 urls[i] += "/";
1001             }
1002             urls[i] += "events/" + p.topic;
1003         }
1004
1005         Client client = ClientBuilder.newBuilder().build();
1006         client.property(ClientProperties.CONNECT_TIMEOUT, 5000);
1007         WebTarget webTarget = client.target(urls[0]);
1008
1009         log.info("UEB URL: {}", urls[0]);
1010         log.info("Sending request:");
1011         log.info(request);
1012         long t1 = System.currentTimeMillis();
1013
1014         HttpResponse r = new HttpResponse();
1015         r.code = 200;
1016
1017         if (!p.skipSending) {
1018             String tt = "application/json";
1019             String tt1 = tt + ";charset=UTF-8";
1020
1021             Response response;
1022             Invocation.Builder invocationBuilder = webTarget.request(tt1).accept(tt);
1023
1024             try {
1025                 response = invocationBuilder.post(Entity.entity(request, tt1));
1026             } catch (ProcessingException e) {
1027                 throw new SvcLogicException(requestPostingException +
1028                     e.getLocalizedMessage(), e);
1029             }
1030             r.code = response.getStatus();
1031             r.headers = response.getStringHeaders();
1032             if (response.hasEntity()) {
1033                 r.body = response.readEntity(String.class);
1034             }
1035         }
1036
1037         long t2 = System.currentTimeMillis();
1038         log.info(responseReceivedMessage, (t2 - t1));
1039         log.info(responseHttpCodeMessage, r.code);
1040         logHeaders(r.headers);
1041         log.info("HTTP response:\n {}", r.body);
1042
1043         return r;
1044     }
1045
1046     public void setUebServers(String uebServers) {
1047         this.uebServers = uebServers;
1048     }
1049
1050     public void setDefaultUebTemplateFileName(String defaultUebTemplateFileName) {
1051         this.defaultUebTemplateFileName = defaultUebTemplateFileName;
1052     }
1053
1054     private static class FileParam {
1055
1056         public String fileName;
1057         public String url;
1058         public String user;
1059         public String password;
1060         public HttpMethod httpMethod;
1061         public String responsePrefix;
1062         public boolean skipSending;
1063         public String oAuthConsumerKey;
1064         public String oAuthConsumerSecret;
1065         public String oAuthSignatureMethod;
1066         public String oAuthVersion;
1067         public AuthType authtype;
1068     }
1069
1070     private static class UebParam {
1071
1072         public String topic;
1073         public String templateFileName;
1074         public String rootVarName;
1075         public String responsePrefix;
1076         public boolean skipSending;
1077     }
1078 }