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