Replaced all tabs with spaces in java and pom.xml
[so.git] / adapters / mso-openstack-adapters / src / main / java / org / onap / so / adapters / vnf / BpelRestClient.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP - SO
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Modifications Copyright (c) 2019 Samsung
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.so.adapters.vnf;
24
25
26 import java.security.GeneralSecurityException;
27 import java.util.Set;
28 import java.util.TreeSet;
29 import javax.annotation.PostConstruct;
30 import javax.xml.bind.DatatypeConverter;
31 import org.apache.http.HttpEntity;
32 import org.apache.http.client.config.RequestConfig;
33 import org.apache.http.client.methods.CloseableHttpResponse;
34 import org.apache.http.client.methods.HttpPost;
35 import org.apache.http.entity.ContentType;
36 import org.apache.http.entity.StringEntity;
37 import org.apache.http.impl.client.CloseableHttpClient;
38 import org.apache.http.impl.client.HttpClients;
39 import org.apache.http.util.EntityUtils;
40 import org.onap.so.logger.ErrorCode;
41 import org.onap.so.logger.MessageEnum;
42 import org.onap.so.utils.CryptoUtils;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45 import org.springframework.beans.factory.annotation.Autowired;
46 import org.springframework.context.annotation.Scope;
47 import org.springframework.core.env.Environment;
48 import org.springframework.stereotype.Component;
49
50 /**
51  * This is the class that is used to POST replies from the MSO adapters to the BPEL engine. It can be configured via
52  * property file, or modified using the member methods. The properties to use are: org.onap.so.adapters.vnf.bpelauth
53  * encrypted authorization string to send to BEPL engine org.onap.so.adapters.vnf.sockettimeout socket timeout value
54  * org.onap.so.adapters.vnf.connecttimeout connect timeout value org.onap.so.adapters.vnf.retrycount number of times to
55  * retry failed connections org.onap.so.adapters.vnf.retryinterval interval (in seconds) between retries
56  * org.onap.so.adapters.vnf.retrylist list of response codes that will trigger a retry (the special code 900 means
57  * "connection was not established")
58  */
59 @Component
60 @Scope("prototype")
61 public class BpelRestClient {
62     public static final String MSO_PROP_VNF_ADAPTER = "MSO_PROP_VNF_ADAPTER";
63     private static final String PROPERTY_DOMAIN = "org.onap.so.adapters.vnf";
64     private static final String BPEL_AUTH_PROPERTY = PROPERTY_DOMAIN + ".bpelauth";
65     private static final String SOCKET_TIMEOUT_PROPERTY = PROPERTY_DOMAIN + ".sockettimeout";
66     private static final String CONN_TIMEOUT_PROPERTY = PROPERTY_DOMAIN + ".connecttimeout";
67     private static final String RETRY_COUNT_PROPERTY = PROPERTY_DOMAIN + ".retrycount";
68     private static final String RETRY_INTERVAL_PROPERTY = PROPERTY_DOMAIN + ".retryinterval";
69     private static final String RETRY_LIST_PROPERTY = PROPERTY_DOMAIN + ".retrylist";
70     private static final String ENCRYPTION_KEY_PROP = "org.onap.so.adapters.network.encryptionKey";
71     private static final Logger logger = LoggerFactory.getLogger(BpelRestClient.class);
72
73     /** Default socket timeout (in seconds) */
74     public static final int DEFAULT_SOCKET_TIMEOUT = 5;
75     /** Default connect timeout (in seconds) */
76     public static final int DEFAULT_CONNECT_TIMEOUT = 5;
77     /** By default, retry up to five times */
78     public static final int DEFAULT_RETRY_COUNT = 5;
79     /** Default interval to wait between retries (in seconds), negative means use backoff algorithm */
80     public static final int DEFAULT_RETRY_INTERVAL = -15;
81     /** Default list of response codes to trigger a retry */
82     public static final String DEFAULT_RETRY_LIST = "408,429,500,502,503,504,900"; // 900 is "connection failed"
83     /** Default credentials */
84     public static final String DEFAULT_CREDENTIALS = "";
85
86     @Autowired
87     private Environment env;
88     // Properties of the BPEL client -- all are configurable
89     private int socketTimeout;
90     private int connectTimeout;
91     private int retryCount;
92     private int retryInterval;
93     private Set<Integer> retryList;
94     private String credentials;
95
96     // last response from BPEL engine
97     private int lastResponseCode;
98     private String lastResponse;
99
100     /**
101      * Create a client to send results to the BPEL engine, using configuration from the MSO_PROP_VNF_ADAPTER properties.
102      */
103     public BpelRestClient() {
104         socketTimeout = DEFAULT_SOCKET_TIMEOUT;
105         connectTimeout = DEFAULT_CONNECT_TIMEOUT;
106         retryCount = DEFAULT_RETRY_COUNT;
107         retryInterval = DEFAULT_RETRY_INTERVAL;
108         setRetryList(DEFAULT_RETRY_LIST);
109         credentials = DEFAULT_CREDENTIALS;
110         lastResponseCode = 0;
111         lastResponse = "";
112
113     }
114
115     @PostConstruct
116     protected void init() {
117
118         socketTimeout = env.getProperty(SOCKET_TIMEOUT_PROPERTY, Integer.class, DEFAULT_SOCKET_TIMEOUT);
119         connectTimeout = env.getProperty(CONN_TIMEOUT_PROPERTY, Integer.class, DEFAULT_CONNECT_TIMEOUT);
120         retryCount = env.getProperty(RETRY_COUNT_PROPERTY, Integer.class, DEFAULT_RETRY_COUNT);
121         retryInterval = env.getProperty(RETRY_INTERVAL_PROPERTY, Integer.class, DEFAULT_RETRY_INTERVAL);
122         setRetryList(env.getProperty(RETRY_LIST_PROPERTY, DEFAULT_RETRY_LIST));
123         credentials = getEncryptedProperty(BPEL_AUTH_PROPERTY, DEFAULT_CREDENTIALS, ENCRYPTION_KEY_PROP);
124     }
125
126     public int getSocketTimeout() {
127         return socketTimeout;
128     }
129
130     public void setSocketTimeout(int socketTimeout) {
131         this.socketTimeout = socketTimeout;
132     }
133
134     public int getConnectTimeout() {
135         return connectTimeout;
136     }
137
138     public void setConnectTimeout(int connectTimeout) {
139         this.connectTimeout = connectTimeout;
140     }
141
142     public int getRetryCount() {
143         return retryCount;
144     }
145
146     public void setRetryCount(int retryCount) {
147         int newRetryCount = retryCount;
148         if (newRetryCount < 0)
149             newRetryCount = DEFAULT_RETRY_COUNT;
150         this.retryCount = newRetryCount;
151     }
152
153     public int getRetryInterval() {
154         return retryInterval;
155     }
156
157     public void setRetryInterval(int retryInterval) {
158         this.retryInterval = retryInterval;
159     }
160
161     public String getCredentials() {
162         return credentials;
163     }
164
165     public void setCredentials(String credentials) {
166         this.credentials = credentials;
167     }
168
169     public String getRetryList() {
170         if (retryList.isEmpty())
171             return "";
172         String t = retryList.toString();
173         return t.substring(1, t.length() - 1);
174     }
175
176     public void setRetryList(String retryList) {
177         Set<Integer> s = new TreeSet<>();
178         for (String t : retryList.split("[, ]")) {
179             try {
180                 s.add(Integer.parseInt(t));
181             } catch (NumberFormatException x) {
182                 // ignore
183             }
184         }
185         this.retryList = s;
186     }
187
188     public int getLastResponseCode() {
189         return lastResponseCode;
190     }
191
192     public String getLastResponse() {
193         return lastResponse;
194     }
195
196     /**
197      * Post a response to the URL of the BPEL engine. As long as the response code is one of those in the retryList, the
198      * post will be retried up to "retrycount" times with an interval (in seconds) of "retryInterval". If retryInterval
199      * is negative, then each successive retry interval will be double the previous one.
200      * 
201      * @param toBpelStr the content (XML or JSON) to post
202      * @param bpelUrl the URL to post to
203      * @param isxml true if the content is XML, otherwise assumed to be JSON
204      * @return true if the post succeeded, false if all retries failed
205      */
206     public boolean bpelPost(final String toBpelStr, final String bpelUrl, final boolean isxml) {
207         debug("Sending response to BPEL: " + toBpelStr);
208         int totalretries = 0;
209         int retryint = retryInterval;
210         while (true) {
211             sendOne(toBpelStr, bpelUrl, isxml);
212             // Note: really should handle response code 415 by switching between content types if needed
213             if (!retryList.contains(lastResponseCode)) {
214                 debug("Got response code: " + lastResponseCode + ": returning.");
215                 return true;
216             }
217             if (totalretries >= retryCount) {
218                 debug("Retried " + totalretries + " times, giving up.");
219                 logger.error("{} {} Could not deliver response to BPEL after {} tries: {}",
220                         MessageEnum.RA_SEND_VNF_NOTIF_ERR, ErrorCode.BusinessProcesssError.getValue(), totalretries,
221                         toBpelStr);
222                 return false;
223             }
224             totalretries++;
225             int sleepinterval = retryint;
226             if (retryint < 0) {
227                 // if retry interval is negative double the retry on each pass
228                 sleepinterval = -retryint;
229                 retryint *= 2;
230             }
231             debug("Sleeping for " + sleepinterval + " seconds.");
232             try {
233                 Thread.sleep(sleepinterval * 1000L);
234             } catch (InterruptedException e) {
235                 logger.debug("Exception while Thread sleep", e);
236                 Thread.currentThread().interrupt();
237             }
238         }
239     }
240
241     private void debug(String m) {
242         logger.debug(m);
243     }
244
245     private void sendOne(final String toBpelStr, final String bpelUrl, final boolean isxml) {
246         logger.debug("Sending to BPEL server: {}", bpelUrl);
247         logger.debug("Content is: {}", toBpelStr);
248
249         // POST
250         HttpPost post = new HttpPost(bpelUrl);
251         if (credentials != null && !credentials.isEmpty())
252             post.addHeader("Authorization", "Basic " + DatatypeConverter.printBase64Binary(credentials.getBytes()));
253
254         logger.debug("HTTPPost Headers: {}", post.getAllHeaders());
255
256         // ContentType
257         ContentType ctype = isxml ? ContentType.APPLICATION_XML : ContentType.APPLICATION_JSON;
258         post.setEntity(new StringEntity(toBpelStr, ctype));
259
260         // Timeouts
261         RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout * 1000)
262                 .setConnectTimeout(connectTimeout * 1000).build();
263         post.setConfig(requestConfig);
264
265         try (CloseableHttpClient client = HttpClients.createDefault()) {
266             CloseableHttpResponse response = client.execute(post);
267             if (response != null) {
268                 lastResponseCode = response.getStatusLine().getStatusCode();
269                 HttpEntity entity = response.getEntity();
270                 lastResponse = (entity != null) ? EntityUtils.toString(entity) : "";
271             } else {
272                 lastResponseCode = 900;
273                 lastResponse = "";
274             }
275         } catch (Exception e) {
276             logger.error("{} {} Exception - Error sending Bpel notification: {} ", MessageEnum.RA_SEND_VNF_NOTIF_ERR,
277                     ErrorCode.BusinessProcesssError.getValue(), toBpelStr, e);
278             lastResponseCode = 900;
279             lastResponse = "";
280         }
281
282         logger.debug("Response code from BPEL server: {}", lastResponseCode);
283         logger.debug("Response body is: {}", lastResponse);
284     }
285
286     private String getEncryptedProperty(String key, String defaultValue, String encryptionKey) {
287         if (env.getProperty(key) != null) {
288             try {
289                 return CryptoUtils.decrypt(env.getProperty(key), env.getProperty(encryptionKey));
290             } catch (GeneralSecurityException e) {
291                 logger.debug("Exception while decrypting property: {} ", env.getProperty(key), e);
292             }
293         }
294         return defaultValue;
295     }
296
297 }