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