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