Merge "Restapi-call-node: Fix setting truststore, should not set system properties"
[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 import java.io.BufferedReader;
29 import java.io.File;
30 import java.io.FileInputStream;
31 import java.io.IOException;
32 import java.io.InputStreamReader;
33 import java.io.OutputStream;
34 import java.net.HttpURLConnection;
35 import java.net.ProtocolException;
36 import java.net.SocketException;
37 import java.net.URI;
38 import java.net.URL;
39 import java.nio.file.Files;
40 import java.nio.file.Paths;
41 import java.util.ArrayList;
42 import java.util.Base64;
43 import java.util.Collections;
44 import java.util.HashMap;
45 import java.util.HashSet;
46 import java.util.Iterator;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.Map.Entry;
50 import java.util.Properties;
51 import java.util.Set;
52 import java.util.regex.Matcher;
53 import java.util.regex.Pattern;
54 import javax.net.ssl.HttpsURLConnection;
55 import javax.ws.rs.ProcessingException;
56 import javax.ws.rs.client.Client;
57 import javax.ws.rs.client.ClientBuilder;
58 import javax.ws.rs.client.Entity;
59 import javax.ws.rs.client.Invocation;
60 import javax.ws.rs.client.WebTarget;
61 import javax.ws.rs.core.EntityTag;
62 import javax.ws.rs.core.Feature;
63 import javax.ws.rs.core.MediaType;
64 import javax.ws.rs.core.MultivaluedMap;
65 import javax.ws.rs.core.Response;
66 import javax.ws.rs.core.UriBuilder;
67 import org.apache.commons.lang3.StringUtils;
68 import org.codehaus.jettison.json.JSONException;
69 import org.codehaus.jettison.json.JSONObject;
70 import org.glassfish.jersey.client.ClientProperties;
71 import org.glassfish.jersey.client.HttpUrlConnectorProvider;
72 import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
73 import org.glassfish.jersey.client.oauth1.ConsumerCredentials;
74 import org.glassfish.jersey.client.oauth1.OAuth1ClientSupport;
75 import org.glassfish.jersey.media.multipart.MultiPart;
76 import org.glassfish.jersey.media.multipart.MultiPartFeature;
77 import org.glassfish.jersey.media.multipart.file.FileDataBodyPart;
78 import org.onap.ccsdk.sli.core.sli.SvcLogicContext;
79 import org.onap.ccsdk.sli.core.sli.SvcLogicException;
80 import org.onap.ccsdk.sli.core.sli.SvcLogicJavaPlugin;
81 import org.onap.logging.filter.base.HttpURLConnectionMetricUtil;
82 import org.onap.logging.filter.base.MetricLogClientFilter;
83 import org.onap.logging.filter.base.ONAPComponents;
84 import org.onap.logging.ref.slf4j.ONAPLogConstants;
85 import org.slf4j.Logger;
86 import org.slf4j.LoggerFactory;
87 import org.slf4j.MDC;
88
89 public class RestapiCallNode implements SvcLogicJavaPlugin {
90
91     protected static final String PARTNERS_FILE_NAME = "partners.json";
92     protected static final String UEB_PROPERTIES_FILE_NAME = "ueb.properties";
93     protected static final String DEFAULT_PROPERTIES_DIR = "/opt/onap/ccsdk/data/properties";
94     protected static final String PROPERTIES_DIR_KEY = "SDNC_CONFIG_DIR";
95     protected static final int DEFAULT_HTTP_CONNECT_TIMEOUT_MS = 30000; // 30 seconds
96     protected static final int DEFAULT_HTTP_READ_TIMEOUT_MS = 600000; // 10 minutes
97
98     private static final Logger log = LoggerFactory.getLogger(RestapiCallNode.class);
99     private String uebServers;
100     private String defaultUebTemplateFileName = "/opt/bvc/restapi/templates/default-ueb-message.json";
101
102     private String responseReceivedMessage = "Response received. Time: {}";
103     private String responseHttpCodeMessage = "HTTP response code: {}";
104     private String requestPostingException = "Exception while posting http request to client ";
105     protected static final String skipSendingMessage = "skipSending";
106     protected static final String responsePrefix = "responsePrefix";
107     protected static final String restapiUrlString = "restapiUrl";
108     protected static final String restapiUserKey = "restapiUser";
109     protected static final String restapiPasswordKey = "restapiPassword";
110     protected Integer httpConnectTimeout;
111     protected Integer httpReadTimeout;
112
113     protected HashMap<String, PartnerDetails> partnerStore;
114     private static final Pattern retryPattern = Pattern.compile(".*,(http|https):.*");
115
116     public RestapiCallNode() {
117         String configDir = System.getProperty(PROPERTIES_DIR_KEY, DEFAULT_PROPERTIES_DIR);
118         try {
119             String jsonString = readFile(configDir + "/" + PARTNERS_FILE_NAME);
120             JSONObject partners = new JSONObject(jsonString);
121             partnerStore = new HashMap<>();
122             loadPartners(partners);
123             log.info("Partners support enabled");
124         } catch (Exception e) {
125             log.warn("Partners file could not be read, Partner support will not be enabled.", e);
126         }
127
128         try (FileInputStream in = new FileInputStream(configDir + "/" + UEB_PROPERTIES_FILE_NAME)) {
129             Properties props = new Properties();
130             props.load(in);
131             uebServers = props.getProperty("servers");
132             log.info("UEB support enabled");
133         } catch (Exception e) {
134             log.warn("UEB properties could not be read, UEB support will not be enabled.", e);
135         }
136         httpConnectTimeout = readOptionalInteger("HTTP_CONNECT_TIMEOUT_MS",DEFAULT_HTTP_CONNECT_TIMEOUT_MS);
137         httpReadTimeout = readOptionalInteger("HTTP_READ_TIMEOUT_MS",DEFAULT_HTTP_READ_TIMEOUT_MS);
138     }
139
140     @SuppressWarnings("unchecked")
141     protected void loadPartners(JSONObject partners) {
142         Iterator<String> keys = partners.keys();
143         String partnerUserKey = "user";
144         String partnerPasswordKey = "password";
145         String partnerUrlKey = "url";
146
147         while (keys.hasNext()) {
148             String partnerKey = keys.next();
149             try {
150                 JSONObject partnerObject = (JSONObject) partners.get(partnerKey);
151                 if (partnerObject.has(partnerUserKey) && partnerObject.has(partnerPasswordKey)) {
152                     String url = null;
153                     if (partnerObject.has(partnerUrlKey)) {
154                         url = partnerObject.getString(partnerUrlKey);
155                     }
156                     String userName = partnerObject.getString(partnerUserKey);
157                     String password = partnerObject.getString(partnerPasswordKey);
158                     PartnerDetails details = new PartnerDetails(userName, getObfuscatedVal(password), url);
159                     partnerStore.put(partnerKey, details);
160                     log.info("mapped partner using partner key " + partnerKey);
161                 } else {
162                     log.info("Partner " + partnerKey + " is missing required keys, it won't be mapped");
163                 }
164             } catch (JSONException e) {
165                 log.info("Couldn't map the partner using partner key " + partnerKey, e);
166             }
167         }
168     }
169
170     /* Unobfuscate param value */
171     private static String getObfuscatedVal(String paramValue) {
172         String resValue = paramValue;
173         if (paramValue != null && paramValue.startsWith("${") && paramValue.endsWith("}"))
174         {
175             String paramStr = paramValue.substring(2, paramValue.length()-1);
176             if (paramStr  != null && paramStr.length() > 0)
177             {
178                 String val = System.getenv(paramStr);
179                 if (val != null && val.length() > 0)
180                 {
181                     resValue=val;
182                     log.info("Obfuscated value RESET for param value:" + paramValue);
183                 }
184             }
185         }
186         return resValue;
187     }
188
189     /**
190      * Returns parameters from the parameter map.
191      *
192      * @param paramMap parameter map
193      * @param p parameters instance
194      * @return parameters filed instance
195      * @throws SvcLogicException when svc logic exception occurs
196      */
197     public static Parameters getParameters(Map<String, String> paramMap, Parameters p) throws SvcLogicException {
198
199         p.templateFileName = parseParam(paramMap, "templateFileName", false, null);
200         p.requestBody = parseParam(paramMap, "requestBody", false, null);
201         p.restapiUrl = parseParam(paramMap, restapiUrlString, true, null);
202         p.restapiUrlSuffix = parseParam(paramMap, "restapiUrlSuffix", false, null);
203         if (p.restapiUrlSuffix != null) {
204             p.restapiUrl = p.restapiUrl + p.restapiUrlSuffix;
205         }
206
207         p.restapiUrl = UriBuilder.fromUri(p.restapiUrl).toTemplate();
208         validateUrl(p.restapiUrl);
209
210         p.restapiUser = parseParam(paramMap, restapiUserKey, false, null);
211         p.restapiPassword = parseParam(paramMap, restapiPasswordKey, false, null);
212         p.oAuthConsumerKey = parseParam(paramMap, "oAuthConsumerKey", false, null);
213         p.oAuthConsumerSecret = parseParam(paramMap, "oAuthConsumerSecret", false, null);
214         p.oAuthSignatureMethod = parseParam(paramMap, "oAuthSignatureMethod", false, null);
215         p.oAuthVersion = parseParam(paramMap, "oAuthVersion", false, null);
216         p.contentType = parseParam(paramMap, "contentType", false, null);
217         p.format = Format.fromString(parseParam(paramMap, "format", false, "json"));
218         p.authtype = fromString(parseParam(paramMap, "authType", false, "unspecified"));
219         p.httpMethod = HttpMethod.fromString(parseParam(paramMap, "httpMethod", false, "post"));
220         p.responsePrefix = parseParam(paramMap, responsePrefix, false, null);
221         p.listNameList = getListNameList(paramMap);
222         String skipSendingStr = paramMap.get(skipSendingMessage);
223         p.skipSending = "true".equalsIgnoreCase(skipSendingStr);
224         p.convertResponse = valueOf(parseParam(paramMap, "convertResponse", false, "true"));
225         p.customHttpHeaders = parseParam(paramMap, "customHttpHeaders", false, null);
226         p.partner = parseParam(paramMap, "partner", false, null);
227         p.dumpHeaders = valueOf(parseParam(paramMap, "dumpHeaders", false, null));
228         p.returnRequestPayload = valueOf(parseParam(paramMap, "returnRequestPayload", false, null));
229         p.accept = parseParam(paramMap, "accept", false, null);
230         p.multipartFormData = valueOf(parseParam(paramMap, "multipartFormData", false, "false"));
231         p.multipartFile = parseParam(paramMap, "multipartFile", false, null);
232         p.targetEntity = parseParam(paramMap, "targetEntity", false, null);
233         return p;
234     }
235
236     /**
237      * Validates the given URL in the parameters.
238      *
239      * @param restapiUrl rest api URL
240      * @throws SvcLogicException when URL validation fails
241      */
242     private static void validateUrl(String restapiUrl) throws SvcLogicException {
243         if (containsMultipleUrls(restapiUrl)) {
244             String[] urls = getMultipleUrls(restapiUrl);
245             for (String url : urls) {
246                 validateUrl(url);
247             }
248         } else {
249             try {
250                 URI.create(restapiUrl);
251             } catch (IllegalArgumentException e) {
252                 throw new SvcLogicException("Invalid input of url " + e.getLocalizedMessage(), e);
253             }
254         }
255     }
256
257     /**
258      * Returns the list of list name.
259      *
260      * @param paramMap parameters map
261      * @return list of list name
262      */
263     private static Set<String> getListNameList(Map<String, String> paramMap) {
264         Set<String> ll = new HashSet<>();
265         for (Map.Entry<String, String> entry : paramMap.entrySet()) {
266             if (entry.getKey().startsWith("listName")) {
267                 ll.add(entry.getValue());
268             }
269         }
270         return ll;
271     }
272
273     /**
274      * Parses the parameter string map of property, validates if required, assigns default value if
275      * present and returns the value.
276      *
277      * @param paramMap string param map
278      * @param name name of the property
279      * @param required if value required
280      * @param def default value
281      * @return value of the property
282      * @throws SvcLogicException if required parameter value is empty
283      */
284     public static String parseParam(Map<String, String> paramMap, String name, boolean required, String def)
285         throws SvcLogicException {
286         String s = paramMap.get(name);
287
288         if (s == null || s.trim().length() == 0) {
289             if (!required) {
290                 return def;
291             }
292             throw new SvcLogicException("Parameter " + name + " is required in RestapiCallNode");
293         }
294
295         s = s.trim();
296         StringBuilder value = new StringBuilder();
297         int i = 0;
298         int i1 = s.indexOf('%');
299         while (i1 >= 0) {
300             int i2 = s.indexOf('%', i1 + 1);
301             if (i2 < 0) {
302                 break;
303             }
304
305             String varName = s.substring(i1 + 1, i2);
306             String varValue = System.getenv(varName);
307             if (varValue == null) {
308                 varValue = "%" + varName + "%";
309             }
310
311             value.append(s.substring(i, i1));
312             value.append(varValue);
313
314             i = i2 + 1;
315             i1 = s.indexOf('%', i);
316         }
317         value.append(s.substring(i));
318
319         log.info("Parameter {}: [{}]", name, maskPassword(name, value));
320
321         return value.toString();
322     }
323
324     private static Object maskPassword(String name, Object value) {
325         String[] pwdNames = {"pwd", "passwd", "password", "Pwd", "Passwd", "Password"};
326         for (String pwdName : pwdNames) {
327             if (name.contains(pwdName)) {
328                 return "**********";
329             }
330         }
331         return value;
332     }
333
334     /**
335      * Allows Directed Graphs the ability to interact with REST APIs.
336      *
337      * @param paramMap HashMap<String,String> of parameters passed by the DG to this function
338      *        <table border="1">
339      *        <thead>
340      *        <th>parameter</th>
341      *        <th>Mandatory/Optional</th>
342      *        <th>description</th>
343      *        <th>example values</th></thead> <tbody>
344      *        <tr>
345      *        <td>templateFileName</td>
346      *        <td>Optional</td>
347      *        <td>full path to template file that can be used to build a request</td>
348      *        <td>/sdncopt/bvc/restapi/templates/vnf_service-configuration-operation_minimal.json</td>
349      *        </tr>
350      *        <tr>
351      *        <td>restapiUrl</td>
352      *        <td>Mandatory</td>
353      *        <td>url to send the request to</td>
354      *        <td>https://sdncodl:8543/restconf/operations/L3VNF-API:create-update-vnf-request</td>
355      *        </tr>
356      *        <tr>
357      *        <td>restapiUser</td>
358      *        <td>Optional</td>
359      *        <td>user name to use for http basic authentication</td>
360      *        <td>sdnc_ws</td>
361      *        </tr>
362      *        <tr>
363      *        <td>restapiPassword</td>
364      *        <td>Optional</td>
365      *        <td>unencrypted password to use for http basic authentication</td>
366      *        <td>plain_password</td>
367      *        </tr>
368      *        <tr>
369      *        <td>oAuthConsumerKey</td>
370      *        <td>Optional</td>
371      *        <td>Consumer key to use for http oAuth authentication</td>
372      *        <td>plain_key</td>
373      *        </tr>
374      *        <tr>
375      *        <td>oAuthConsumerSecret</td>
376      *        <td>Optional</td>
377      *        <td>Consumer secret to use for http oAuth authentication</td>
378      *        <td>plain_secret</td>
379      *        </tr>
380      *        <tr>
381      *        <td>oAuthSignatureMethod</td>
382      *        <td>Optional</td>
383      *        <td>Consumer method to use for http oAuth authentication</td>
384      *        <td>method</td>
385      *        </tr>
386      *        <tr>
387      *        <td>oAuthVersion</td>
388      *        <td>Optional</td>
389      *        <td>Version http oAuth authentication</td>
390      *        <td>version</td>
391      *        </tr>
392      *        <tr>
393      *        <td>contentType</td>
394      *        <td>Optional</td>
395      *        <td>http content type to set in the http header</td>
396      *        <td>usually application/json or application/xml</td>
397      *        </tr>
398      *        <tr>
399      *        <td>format</td>
400      *        <td>Optional</td>
401      *        <td>should match request body format</td>
402      *        <td>json or xml</td>
403      *        </tr>
404      *        <tr>
405      *        <td>httpMethod</td>
406      *        <td>Optional</td>
407      *        <td>http method to use when sending the request</td>
408      *        <td>get post put delete patch</td>
409      *        </tr>
410      *        <tr>
411      *        <td>responsePrefix</td>
412      *        <td>Optional</td>
413      *        <td>location the response will be written to in context memory</td>
414      *        <td>tmp.restapi.result</td>
415      *        </tr>
416      *        <tr>
417      *        <td>listName[i]</td>
418      *        <td>Optional</td>
419      *        <td>Used for processing XML responses with repeating
420      *        elements.</td>vpn-information.vrf-details
421      *        <td></td>
422      *        </tr>
423      *        <tr>
424      *        <td>skipSending</td>
425      *        <td>Optional</td>
426      *        <td></td>
427      *        <td>true or false</td>
428      *        </tr>
429      *        <tr>
430      *        <td>convertResponse</td>
431      *        <td>Optional</td>
432      *        <td>whether the response should be converted</td>
433      *        <td>true or false</td>
434      *        </tr>
435      *        <tr>
436      *        <td>customHttpHeaders</td>
437      *        <td>Optional</td>
438      *        <td>a list additional http headers to be passed in, follow the format in the example</td>
439      *        <td>X-CSI-MessageId=messageId,headerFieldName=headerFieldValue</td>
440      *        </tr>
441      *        <tr>
442      *        <td>dumpHeaders</td>
443      *        <td>Optional</td>
444      *        <td>when true writes http header content to context memory</td>
445      *        <td>true or false</td>
446      *        </tr>
447      *        <tr>
448      *        <td>partner</td>
449      *        <td>Optional</td>
450      *        <td>used to retrieve username, password and url if partner store exists</td>
451      *        <td>aaf</td>
452      *        </tr>
453      *        <tr>
454      *        <td>returnRequestPayload</td>
455      *        <td>Optional</td>
456      *        <td>used to return payload built in the request</td>
457      *        <td>true or false</td>
458      *        </tr>
459      *        </tbody>
460      *        </table>
461      * @param ctx Reference to context memory
462      * @throws SvcLogicException
463      * @since 11.0.2
464      * @see String#split(String, int)
465      */
466     public void sendRequest(Map<String, String> paramMap, SvcLogicContext ctx) throws SvcLogicException {
467         sendRequest(paramMap, ctx, null);
468     }
469
470     protected void sendRequest(Map<String, String> paramMap, SvcLogicContext ctx, RetryPolicy retryPolicy)
471         throws SvcLogicException {
472
473         HttpResponse r = new HttpResponse();
474         try {
475             handlePartner(paramMap);
476             Parameters p = getParameters(paramMap, new Parameters());
477             if(p.targetEntity != null && !p.targetEntity.isEmpty()) {
478                 MDC.put(ONAPLogConstants.MDCs.TARGET_ENTITY, p.targetEntity);
479             }
480             if (containsMultipleUrls(p.restapiUrl) && retryPolicy == null) {
481                 String[] urls = getMultipleUrls(p.restapiUrl);
482                 retryPolicy = new RetryPolicy(urls, urls.length * 2);
483                 p.restapiUrl = urls[0];
484             }
485             String pp = p.responsePrefix != null ? p.responsePrefix + '.' : "";
486
487             String req = null;
488             if (p.templateFileName != null) {
489                 String reqTemplate = readFile(p.templateFileName);
490                 req = buildXmlJsonRequest(ctx, reqTemplate, p.format);
491             } else if (p.requestBody != null) {
492                 req = p.requestBody;
493             }
494             r = sendHttpRequest(req, p);
495             setResponseStatus(ctx, p.responsePrefix, r);
496
497             if (p.dumpHeaders && r.headers != null) {
498                 for (Entry<String, List<String>> a : r.headers.entrySet()) {
499                     ctx.setAttribute(pp + "header." + a.getKey(), StringUtils.join(a.getValue(), ","));
500                 }
501             }
502
503             if (p.returnRequestPayload && req != null) {
504                 ctx.setAttribute(pp + "httpRequest", req);
505             }
506
507             if (r.body != null && r.body.trim().length() > 0) {
508                 ctx.setAttribute(pp + "httpResponse", r.body);
509
510                 if (p.convertResponse) {
511                     Map<String, String> mm = null;
512                     if (p.format == Format.XML) {
513                         mm = XmlParser.convertToProperties(r.body, p.listNameList);
514                     } else if (p.format == Format.JSON) {
515                         mm = JsonParser.convertToProperties(r.body);
516                     }
517
518                     if (mm != null) {
519                         for (Map.Entry<String, String> entry : mm.entrySet()) {
520                             ctx.setAttribute(pp + entry.getKey(), entry.getValue());
521                         }
522                     }
523                 }
524             }
525         } catch (SvcLogicException e) {
526             boolean shouldRetry = false;
527             if (e.getCause().getCause() instanceof SocketException) {
528                 shouldRetry = true;
529             }
530
531             log.error("Error sending the request: " + e.getMessage(), e);
532             String prefix = parseParam(paramMap, responsePrefix, false, null);
533             if (retryPolicy == null || !shouldRetry) {
534                 setFailureResponseStatus(ctx, prefix, e.getMessage(), r);
535             } else {
536                 log.debug(retryPolicy.getRetryMessage());
537                 try {
538                     // calling getNextHostName increments the retry count so it should be called before shouldRetry
539                     String retryString = retryPolicy.getNextHostName();
540                     if (retryPolicy.shouldRetry()) {
541                         paramMap.put(restapiUrlString, retryString);
542                         log.debug("retry attempt {} will use the retry url {}", retryPolicy.getRetryCount(),
543                             retryString);
544                         sendRequest(paramMap, ctx, retryPolicy);
545                     } else {
546                         log.debug("Maximum retries reached, won't attempt to retry. Calling setFailureResponseStatus.");
547                         setFailureResponseStatus(ctx, prefix, e.getMessage(), r);
548                     }
549                 } catch (Exception ex) {
550                     String retryErrorMessage = "Retry attempt " + retryPolicy.getRetryCount()
551                         + "has failed with error message " + ex.getMessage();
552                     setFailureResponseStatus(ctx, prefix, retryErrorMessage, r);
553                 }
554             }
555         }
556
557         if (r != null && r.code >= 300) {
558             throw new SvcLogicException(String.valueOf(r.code) + ": " + r.message);
559         }
560     }
561
562     protected void handlePartner(Map<String, String> paramMap) {
563         String partner = paramMap.get("partner");
564         if (partner != null && partner.length() > 0) {
565             PartnerDetails details = partnerStore.get(partner);
566             paramMap.put(restapiUserKey, details.username);
567             paramMap.put(restapiPasswordKey, details.password);
568             if (paramMap.get(restapiUrlString) == null) {
569                 paramMap.put(restapiUrlString, details.url);
570             }
571         }
572     }
573
574     protected String buildXmlJsonRequest(SvcLogicContext ctx, String template, Format format) throws SvcLogicException {
575         log.info("Building {} started", format);
576         long t1 = System.currentTimeMillis();
577         String originalTemplate = template;
578
579         template = expandRepeats(ctx, template, 1);
580
581         Map<String, String> mm = new HashMap<>();
582         for (String s : ctx.getAttributeKeySet()) {
583             mm.put(s, ctx.getAttribute(s));
584         }
585
586         StringBuilder ss = new StringBuilder();
587         int i = 0;
588         while (i < template.length()) {
589             int i1 = template.indexOf("${", i);
590             if (i1 < 0) {
591                 ss.append(template.substring(i));
592                 break;
593             }
594
595             int i2 = template.indexOf('}', i1 + 2);
596             if (i2 < 0) {
597                 throw new SvcLogicException("Template error: Matching } not found");
598             }
599
600             String var1 = template.substring(i1 + 2, i2);
601             String value1 = format == Format.XML ? XmlJsonUtil.getXml(mm, var1) : XmlJsonUtil.getJson(mm, var1);
602             if (value1 == null || value1.trim().length() == 0) {
603                 // delete the whole element (line)
604                 int i3 = template.lastIndexOf('\n', i1);
605                 if (i3 < 0) {
606                     i3 = 0;
607                 }
608                 int i4 = template.indexOf('\n', i1);
609                 if (i4 < 0) {
610                     i4 = template.length();
611                 }
612
613                 if (i < i3) {
614                     ss.append(template.substring(i, i3));
615                 }
616                 i = i4;
617             } else {
618                 ss.append(template.substring(i, i1)).append(value1);
619                 i = i2 + 1;
620             }
621         }
622
623         String req = format == Format.XML ? XmlJsonUtil.removeEmptyStructXml(ss.toString())
624             : XmlJsonUtil.removeEmptyStructJson(originalTemplate, ss.toString());
625
626         if (format == Format.JSON) {
627             req = XmlJsonUtil.removeLastCommaJson(req);
628         }
629
630         long t2 = System.currentTimeMillis();
631         log.info("Building {} completed. Time: {}", format, t2 - t1);
632
633         return req;
634     }
635
636     protected String expandRepeats(SvcLogicContext ctx, String template, int level) throws SvcLogicException {
637         StringBuilder newTemplate = new StringBuilder();
638         int k = 0;
639         while (k < template.length()) {
640             int i1 = template.indexOf("${repeat:", k);
641             if (i1 < 0) {
642                 newTemplate.append(template.substring(k));
643                 break;
644             }
645
646             int i2 = template.indexOf(':', i1 + 9);
647             if (i2 < 0) {
648                 throw new SvcLogicException(
649                     "Template error: Context variable name followed by : is required after repeat");
650             }
651
652             // Find the closing }, store in i3
653             int nn = 1;
654             int i3 = -1;
655             int i = i2;
656             while (nn > 0 && i < template.length()) {
657                 i3 = template.indexOf('}', i);
658                 if (i3 < 0) {
659                     throw new SvcLogicException("Template error: Matching } not found");
660                 }
661                 int i32 = template.indexOf('{', i);
662                 if (i32 >= 0 && i32 < i3) {
663                     nn++;
664                     i = i32 + 1;
665                 } else {
666                     nn--;
667                     i = i3 + 1;
668                 }
669             }
670
671             String var1 = template.substring(i1 + 9, i2);
672             String value1 = ctx.getAttribute(var1);
673             log.info("     {}:{}", var1, value1);
674             int n = 0;
675             try {
676                 n = Integer.parseInt(value1);
677             } catch (NumberFormatException e) {
678                 log.info("value1 not set or not a number, n will remain set at zero");
679             }
680
681             newTemplate.append(template.substring(k, i1));
682
683             String rpt = template.substring(i2 + 1, i3);
684
685             for (int ii = 0; ii < n; ii++) {
686                 String ss = rpt.replaceAll("\\[\\$\\{" + level + "\\}\\]", "[" + ii + "]");
687                 if (ii == n - 1 && ss.trim().endsWith(",")) {
688                     int i4 = ss.lastIndexOf(',');
689                     if (i4 > 0) {
690                         ss = ss.substring(0, i4) + ss.substring(i4 + 1);
691                     }
692                 }
693                 newTemplate.append(ss);
694             }
695
696             k = i3 + 1;
697         }
698
699         if (k == 0) {
700             return newTemplate.toString();
701         }
702
703         return expandRepeats(ctx, newTemplate.toString(), level + 1);
704     }
705
706     protected String readFile(String fileName) throws SvcLogicException {
707         try {
708             byte[] encoded = Files.readAllBytes(Paths.get(fileName));
709             return new String(encoded, "UTF-8");
710         } catch (IOException | SecurityException e) {
711             throw new SvcLogicException("Unable to read file " + fileName + e.getLocalizedMessage(), e);
712         }
713     }
714
715     protected Client addAuthType(Client c, FileParam fp) throws SvcLogicException {
716         Parameters p = new Parameters();
717         p.restapiUser = fp.user;
718         p.restapiPassword = fp.password;
719         p.oAuthConsumerKey = fp.oAuthConsumerKey;
720         p.oAuthVersion = fp.oAuthVersion;
721         p.oAuthConsumerSecret = fp.oAuthConsumerSecret;
722         p.oAuthSignatureMethod = fp.oAuthSignatureMethod;
723         p.authtype = fp.authtype;
724         return addAuthType(c, p);
725     }
726
727     public Client addAuthType(Client client, Parameters p) throws SvcLogicException {
728         if (p.authtype == AuthType.Unspecified) {
729             if (p.restapiUser != null && p.restapiPassword != null) {
730                 client.register(HttpAuthenticationFeature.basic(p.restapiUser, p.restapiPassword));
731             } else if (p.oAuthConsumerKey != null && p.oAuthConsumerSecret != null && p.oAuthSignatureMethod != null) {
732                 Feature oAuth1Feature =
733                     OAuth1ClientSupport.builder(new ConsumerCredentials(p.oAuthConsumerKey, p.oAuthConsumerSecret))
734                         .version(p.oAuthVersion).signatureMethod(p.oAuthSignatureMethod).feature().build();
735                 client.register(oAuth1Feature);
736
737             }
738         } else {
739             if (p.authtype == AuthType.DIGEST) {
740                 if (p.restapiUser != null && p.restapiPassword != null) {
741                     client.register(HttpAuthenticationFeature.digest(p.restapiUser, p.restapiPassword));
742                 } else {
743                     throw new SvcLogicException(
744                         "oAUTH authentication type selected but all restapiUser and restapiPassword "
745                             + "parameters doesn't exist",
746                         new Throwable());
747                 }
748             } else if (p.authtype == AuthType.BASIC) {
749                 if (p.restapiUser != null && p.restapiPassword != null) {
750                     client.register(HttpAuthenticationFeature.basic(p.restapiUser, p.restapiPassword));
751                 } else {
752                     throw new SvcLogicException(
753                         "oAUTH authentication type selected but all restapiUser and restapiPassword "
754                             + "parameters doesn't exist",
755                         new Throwable());
756                 }
757             } else if (p.authtype == AuthType.OAUTH) {
758                 if (p.oAuthConsumerKey != null && p.oAuthConsumerSecret != null && p.oAuthSignatureMethod != null) {
759                     Feature oAuth1Feature = OAuth1ClientSupport
760                         .builder(new ConsumerCredentials(p.oAuthConsumerKey, p.oAuthConsumerSecret))
761                         .version(p.oAuthVersion).signatureMethod(p.oAuthSignatureMethod).feature().build();
762                     client.register(oAuth1Feature);
763                 } else {
764                     throw new SvcLogicException(
765                         "oAUTH authentication type selected but all oAuthConsumerKey, oAuthConsumerSecret "
766                             + "and oAuthSignatureMethod parameters doesn't exist",
767                         new Throwable());
768                 }
769             }
770         }
771         return client;
772     }
773
774     /**
775      * Receives the http response for the http request sent.
776      *
777      * @param request request msg
778      * @param p parameters
779      * @return HTTP response
780      * @throws SvcLogicException when sending http request fails
781      */
782     public HttpResponse sendHttpRequest(String request, Parameters p) throws SvcLogicException {
783
784         HttpsURLConnection.setDefaultHostnameVerifier((string, ssls) -> true);
785
786         Client client = ClientBuilder.newBuilder().hostnameVerifier((s, sslSession) -> true).build();
787         setClientTimeouts(client);
788         // Needed to support additional HTTP methods such as PATCH
789         client.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true);
790         client.register(new MetricLogClientFilter());
791         WebTarget webTarget = addAuthType(client, p).target(p.restapiUrl);
792
793         long t1 = System.currentTimeMillis();
794
795         HttpResponse r = new HttpResponse();
796         r.code = 200;
797         String accept = p.accept;
798         if (accept == null) {
799             accept = p.format == Format.XML ? "application/xml" : "application/json";
800         }
801
802         String contentType = p.contentType;
803         if (contentType == null) {
804             contentType = accept + ";charset=UTF-8";
805         }
806
807         if (!p.skipSending && !p.multipartFormData) {
808
809             Invocation.Builder invocationBuilder = webTarget.request(contentType).accept(accept);
810
811             if (p.format == Format.NONE) {
812                 invocationBuilder.header("", "");
813             }
814
815             if (p.customHttpHeaders != null && p.customHttpHeaders.length() > 0) {
816                 String[] keyValuePairs = p.customHttpHeaders.split(",");
817                 for (String singlePair : keyValuePairs) {
818                     int equalPosition = singlePair.indexOf('=');
819                     invocationBuilder.header(singlePair.substring(0, equalPosition),
820                         singlePair.substring(equalPosition + 1, singlePair.length()));
821                 }
822             }
823
824             invocationBuilder.property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true);
825
826             Response response;
827
828             try {
829                 // When the HTTP operation has no body do not set the content-type
830                 //setting content-type has caused errors with some servers when no body is present
831                 if (request == null) {
832                     response = invocationBuilder.method(p.httpMethod.toString());
833                 } else {
834                     log.info("Sending request below to url " + p.restapiUrl);
835                     log.info(request);
836                     response = invocationBuilder.method(p.httpMethod.toString(), entity(request, contentType));
837                 }
838             } catch (ProcessingException | IllegalStateException e) {
839                 throw new SvcLogicException(requestPostingException + e.getLocalizedMessage(), e);
840             }
841
842             r.code = response.getStatus();
843             r.headers = response.getStringHeaders();
844             EntityTag etag = response.getEntityTag();
845             if (etag != null) {
846                 r.message = etag.getValue();
847             }
848             if (response.hasEntity() && r.code != 204) {
849                 r.body = response.readEntity(String.class);
850             }
851         } else if (!p.skipSending && p.multipartFormData) {
852
853             WebTarget wt = client.register(MultiPartFeature.class).target(p.restapiUrl);
854
855             MultiPart multiPart = new MultiPart();
856             multiPart.setMediaType(MediaType.MULTIPART_FORM_DATA_TYPE);
857
858             FileDataBodyPart fileDataBodyPart =
859                 new FileDataBodyPart("file", new File(p.multipartFile), MediaType.APPLICATION_OCTET_STREAM_TYPE);
860             multiPart.bodyPart(fileDataBodyPart);
861
862
863             Invocation.Builder invocationBuilder = wt.request(contentType).accept(accept);
864
865             if (p.format == Format.NONE) {
866                 invocationBuilder.header("", "");
867             }
868
869             if (p.customHttpHeaders != null && p.customHttpHeaders.length() > 0) {
870                 String[] keyValuePairs = p.customHttpHeaders.split(",");
871                 for (String singlePair : keyValuePairs) {
872                     int equalPosition = singlePair.indexOf('=');
873                     invocationBuilder.header(singlePair.substring(0, equalPosition),
874                         singlePair.substring(equalPosition + 1, singlePair.length()));
875                 }
876             }
877
878             Response response;
879
880             try {
881                 response =
882                     invocationBuilder.method(p.httpMethod.toString(), entity(multiPart, multiPart.getMediaType()));
883             } catch (ProcessingException | IllegalStateException e) {
884                 throw new SvcLogicException(requestPostingException + e.getLocalizedMessage(), e);
885             }
886
887             r.code = response.getStatus();
888             r.headers = response.getStringHeaders();
889             EntityTag etag = response.getEntityTag();
890             if (etag != null) {
891                 r.message = etag.getValue();
892             }
893             if (response.hasEntity() && r.code != 204) {
894                 r.body = response.readEntity(String.class);
895             }
896
897         }
898
899         long t2 = System.currentTimeMillis();
900         log.info(responseReceivedMessage, t2 - t1);
901         log.info(responseHttpCodeMessage, r.code);
902         log.info("HTTP response message: {}", r.message);
903         logHeaders(r.headers);
904         log.info("HTTP response: {}", r.body);
905
906         return r;
907     }
908
909     protected void setFailureResponseStatus(SvcLogicContext ctx, String prefix, String errorMessage,
910         HttpResponse resp) {
911         resp.code = 500;
912         resp.message = errorMessage;
913         String pp = prefix != null ? prefix + '.' : "";
914         ctx.setAttribute(pp + "response-code", String.valueOf(resp.code));
915         ctx.setAttribute(pp + "response-message", resp.message);
916     }
917
918     protected void setResponseStatus(SvcLogicContext ctx, String prefix, HttpResponse r) {
919         String pp = prefix != null ? prefix + '.' : "";
920         ctx.setAttribute(pp + "response-code", String.valueOf(r.code));
921         ctx.setAttribute(pp + "response-message", r.message);
922     }
923
924     public void sendFile(Map<String, String> paramMap, SvcLogicContext ctx) throws SvcLogicException {
925         HttpResponse r = null;
926         try {
927             FileParam p = getFileParameters(paramMap);
928             byte[] data = Files.readAllBytes(Paths.get(p.fileName));
929
930             r = sendHttpData(data, p);
931
932             for (int i = 0; i < 10 && r.code == 301; i++) {
933                 String newUrl = r.headers2.get("Location").get(0);
934
935                 log.info("Got response code 301. Sending same request to URL: " + newUrl);
936
937                 p.url = newUrl;
938                 r = sendHttpData(data, p);
939             }
940
941             setResponseStatus(ctx, p.responsePrefix, r);
942
943         } catch (SvcLogicException | IOException e) {
944             log.error("Error sending the request: {}", e.getMessage(), e);
945
946             r = new HttpResponse();
947             r.code = 500;
948             r.message = e.getMessage();
949             String prefix = parseParam(paramMap, responsePrefix, false, null);
950             setResponseStatus(ctx, prefix, r);
951         }
952
953         if (r != null && r.code >= 300) {
954             throw new SvcLogicException(String.valueOf(r.code) + ": " + r.message);
955         }
956     }
957
958     private FileParam getFileParameters(Map<String, String> paramMap) throws SvcLogicException {
959         FileParam p = new FileParam();
960         p.fileName = parseParam(paramMap, "fileName", true, null);
961         p.url = parseParam(paramMap, "url", true, null);
962         p.user = parseParam(paramMap, "user", false, null);
963         p.password = parseParam(paramMap, "password", false, null);
964         p.httpMethod = HttpMethod.fromString(parseParam(paramMap, "httpMethod", false, "post"));
965         p.responsePrefix = parseParam(paramMap, responsePrefix, false, null);
966         String skipSendingStr = paramMap.get(skipSendingMessage);
967         p.skipSending = "true".equalsIgnoreCase(skipSendingStr);
968         p.oAuthConsumerKey = parseParam(paramMap, "oAuthConsumerKey", false, null);
969         p.oAuthVersion = parseParam(paramMap, "oAuthVersion", false, null);
970         p.oAuthConsumerSecret = parseParam(paramMap, "oAuthConsumerSecret", false, null);
971         p.oAuthSignatureMethod = parseParam(paramMap, "oAuthSignatureMethod", false, null);
972         p.authtype = fromString(parseParam(paramMap, "authType", false, "unspecified"));
973         return p;
974     }
975
976     public void postMessageOnUeb(Map<String, String> paramMap, SvcLogicContext ctx) throws SvcLogicException {
977         HttpResponse r;
978         try {
979             UebParam p = getUebParameters(paramMap);
980
981             String pp = p.responsePrefix != null ? p.responsePrefix + '.' : "";
982
983             String req;
984
985             if (p.templateFileName == null) {
986                 log.info("No template file name specified. Using default UEB template: {}", defaultUebTemplateFileName);
987                 p.templateFileName = defaultUebTemplateFileName;
988             }
989
990             String reqTemplate = readFile(p.templateFileName);
991             reqTemplate = reqTemplate.replaceAll("rootVarName", p.rootVarName);
992             req = buildXmlJsonRequest(ctx, reqTemplate, Format.JSON);
993
994             r = postOnUeb(req, p);
995             setResponseStatus(ctx, p.responsePrefix, r);
996             if (r.body != null) {
997                 ctx.setAttribute(pp + "httpResponse", r.body);
998             }
999
1000         } catch (SvcLogicException e) {
1001             log.error("Error sending the request: {}", e.getMessage(), e);
1002
1003             r = new HttpResponse();
1004             r.code = 500;
1005             r.message = e.getMessage();
1006             String prefix = parseParam(paramMap, responsePrefix, false, null);
1007             setResponseStatus(ctx, prefix, r);
1008         }
1009
1010         if (r.code >= 300) {
1011             throw new SvcLogicException(String.valueOf(r.code) + ": " + r.message);
1012         }
1013     }
1014
1015     protected HttpResponse sendHttpData(byte[] data, FileParam p) throws IOException {
1016         URL url = new URL(p.url);
1017         HttpURLConnection con = (HttpURLConnection) url.openConnection();
1018
1019         log.info("Connection: " + con.getClass().getName());
1020
1021         con.setRequestMethod(p.httpMethod.toString());
1022         con.setRequestProperty("Content-Type", "application/octet-stream");
1023         con.setRequestProperty("Accept", "*/*");
1024         con.setRequestProperty("Expect", "100-continue");
1025         con.setFixedLengthStreamingMode(data.length);
1026         con.setInstanceFollowRedirects(false);
1027
1028         if (p.user != null && p.password != null) {
1029             String authString = p.user + ":" + p.password;
1030             String authStringEnc = Base64.getEncoder().encodeToString(authString.getBytes());
1031             con.setRequestProperty("Authorization", "Basic " + authStringEnc);
1032         }
1033
1034         con.setDoInput(true);
1035         con.setDoOutput(true);
1036
1037         log.info("Sending file");
1038         long t1 = System.currentTimeMillis();
1039
1040         HttpResponse r = new HttpResponse();
1041         r.code = 200;
1042
1043         if (!p.skipSending) {
1044             HttpURLConnectionMetricUtil util = new HttpURLConnectionMetricUtil();
1045             util.logBefore(con, ONAPComponents.DMAAP);
1046
1047             con.connect();
1048
1049             boolean continue100failed = false;
1050             try {
1051                 OutputStream os = con.getOutputStream();
1052                 os.write(data);
1053                 os.flush();
1054                 os.close();
1055             } catch (ProtocolException e) {
1056                 continue100failed = true;
1057             }
1058
1059             r.code = con.getResponseCode();
1060             r.headers2 = con.getHeaderFields();
1061
1062             if (r.code != 204 && !continue100failed) {
1063                 BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
1064                 String inputLine;
1065                 StringBuffer response = new StringBuffer();
1066                 while ((inputLine = in.readLine()) != null) {
1067                     response.append(inputLine);
1068                 }
1069                 in.close();
1070
1071                 r.body = response.toString();
1072             }
1073
1074             util.logAfter(con);
1075
1076             con.disconnect();
1077         }
1078
1079         long t2 = System.currentTimeMillis();
1080         log.info("Response received. Time: {}", t2 - t1);
1081         log.info("HTTP response code: {}", r.code);
1082         log.info("HTTP response message: {}", r.message);
1083         logHeaders(r.headers2);
1084         log.info("HTTP response: {}", r.body);
1085
1086         return r;
1087     }
1088
1089     private UebParam getUebParameters(Map<String, String> paramMap) throws SvcLogicException {
1090         UebParam p = new UebParam();
1091         p.topic = parseParam(paramMap, "topic", true, null);
1092         p.templateFileName = parseParam(paramMap, "templateFileName", false, null);
1093         p.rootVarName = parseParam(paramMap, "rootVarName", false, null);
1094         p.responsePrefix = parseParam(paramMap, responsePrefix, false, null);
1095         String skipSendingStr = paramMap.get(skipSendingMessage);
1096         p.skipSending = "true".equalsIgnoreCase(skipSendingStr);
1097         return p;
1098     }
1099
1100     protected void logProperties(Map<String, Object> mm) {
1101         List<String> ll = new ArrayList<>();
1102         for (Object o : mm.keySet()) {
1103             ll.add((String) o);
1104         }
1105         Collections.sort(ll);
1106
1107         log.info("Properties:");
1108         for (String name : ll) {
1109             log.info("--- {}:{}", name, String.valueOf(mm.get(name)));
1110         }
1111     }
1112
1113     protected void logHeaders(MultivaluedMap<String, String> mm) {
1114         log.info("HTTP response headers:");
1115
1116         if (mm == null) {
1117             return;
1118         }
1119
1120         List<String> ll = new ArrayList<>();
1121         for (Object o : mm.keySet()) {
1122             ll.add((String) o);
1123         }
1124         Collections.sort(ll);
1125
1126         for (String name : ll) {
1127             log.info("--- {}:{}", name, String.valueOf(mm.get(name)));
1128         }
1129     }
1130
1131     private void logHeaders(Map<String, List<String>> mm) {
1132         if (mm == null || mm.isEmpty()) {
1133             return;
1134         }
1135
1136         List<String> ll = new ArrayList<>();
1137         for (String s : mm.keySet()) {
1138             if (s != null) {
1139                 ll.add(s);
1140             }
1141         }
1142         Collections.sort(ll);
1143
1144         for (String name : ll) {
1145             List<String> v = mm.get(name);
1146             log.info("--- {}:{}", name, String.valueOf(mm.get(name)));
1147             log.info("--- " + name + ": " + (v.size() == 1 ? v.get(0) : v));
1148         }
1149     }
1150
1151     protected HttpResponse postOnUeb(String request, UebParam p) throws SvcLogicException {
1152         String[] urls = uebServers.split(" ");
1153         for (int i = 0; i < urls.length; i++) {
1154             if (!urls[i].endsWith("/")) {
1155                 urls[i] += "/";
1156             }
1157             urls[i] += "events/" + p.topic;
1158         }
1159
1160         Client client = ClientBuilder.newBuilder().build();
1161         setClientTimeouts(client);
1162         WebTarget webTarget = client.target(urls[0]);
1163
1164         log.info("UEB URL: {}", urls[0]);
1165         log.info("Sending request:");
1166         log.info(request);
1167         long t1 = System.currentTimeMillis();
1168
1169         HttpResponse r = new HttpResponse();
1170         r.code = 200;
1171
1172         if (!p.skipSending) {
1173             String tt = "application/json";
1174             String tt1 = tt + ";charset=UTF-8";
1175
1176             Response response;
1177             Invocation.Builder invocationBuilder = webTarget.request(tt1).accept(tt);
1178
1179             try {
1180                 response = invocationBuilder.post(Entity.entity(request, tt1));
1181             } catch (ProcessingException e) {
1182                 throw new SvcLogicException(requestPostingException + e.getLocalizedMessage(), e);
1183             }
1184             r.code = response.getStatus();
1185             r.headers = response.getStringHeaders();
1186             if (response.hasEntity()) {
1187                 r.body = response.readEntity(String.class);
1188             }
1189         }
1190
1191         long t2 = System.currentTimeMillis();
1192         log.info(responseReceivedMessage, t2 - t1);
1193         log.info(responseHttpCodeMessage, r.code);
1194         logHeaders(r.headers);
1195         log.info("HTTP response:\n {}", r.body);
1196
1197         return r;
1198     }
1199
1200     public void setUebServers(String uebServers) {
1201         this.uebServers = uebServers;
1202     }
1203
1204     public void setDefaultUebTemplateFileName(String defaultUebTemplateFileName) {
1205         this.defaultUebTemplateFileName = defaultUebTemplateFileName;
1206     }
1207
1208     protected void setClientTimeouts(Client client) {
1209         client.property(ClientProperties.CONNECT_TIMEOUT, httpConnectTimeout);
1210         client.property(ClientProperties.READ_TIMEOUT, httpReadTimeout);
1211     }
1212
1213     protected Integer readOptionalInteger(String propertyName, Integer defaultValue) {
1214         String stringValue = System.getProperty(propertyName);
1215         if (stringValue != null && stringValue.length() > 0) {
1216             try {
1217                 return Integer.valueOf(stringValue);
1218             } catch (NumberFormatException e) {
1219                 log.warn("property " + propertyName + " had the value " + stringValue + " that could not be converted to an Integer, default " + defaultValue + " will be used instead", e);
1220             }
1221         }
1222         return defaultValue;
1223     }
1224
1225     protected static String[] getMultipleUrls(String restapiUrl) {
1226         List<String> urls = new ArrayList<>();
1227         int start = 0;
1228         for (int i = 0; i < restapiUrl.length(); i++) {
1229             if (restapiUrl.charAt(i) == ',') {
1230                 if (i + 9 < restapiUrl.length()) {
1231                     String part = restapiUrl.substring(i + 1, i + 9);
1232                     if (part.equals("https://") || part.startsWith("http://")) {
1233                         urls.add(restapiUrl.substring(start, i));
1234                         start = i + 1;
1235                     }
1236                 }
1237             } else if (i == restapiUrl.length() - 1) {
1238                 urls.add(restapiUrl.substring(start, i + 1));
1239             }
1240         }
1241         String[] arr = new String[urls.size()];
1242         return urls.toArray(arr);
1243     }
1244
1245     protected static boolean containsMultipleUrls(String restapiUrl) {
1246         Matcher m = retryPattern.matcher(restapiUrl);
1247         return m.matches();
1248     }
1249
1250     private static class FileParam {
1251
1252         public String fileName;
1253         public String url;
1254         public String user;
1255         public String password;
1256         public HttpMethod httpMethod;
1257         public String responsePrefix;
1258         public boolean skipSending;
1259         public String oAuthConsumerKey;
1260         public String oAuthConsumerSecret;
1261         public String oAuthSignatureMethod;
1262         public String oAuthVersion;
1263         public AuthType authtype;
1264     }
1265
1266     private static class UebParam {
1267
1268         public String topic;
1269         public String templateFileName;
1270         public String rootVarName;
1271         public String responsePrefix;
1272         public boolean skipSending;
1273     }
1274 }