Merge "Reorder modifiers"
[so.git] / adapters / mso-sdnc-adapter / src / main / java / org / openecomp / mso / adapters / sdnc / sdncrest / SDNCConnector.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 package org.openecomp.mso.adapters.sdnc.sdncrest;
22
23 import org.openecomp.mso.adapters.sdnc.impl.Constants;
24 import org.openecomp.mso.adapters.sdncrest.SDNCErrorCommon;
25 import org.openecomp.mso.adapters.sdncrest.SDNCResponseCommon;
26 import org.openecomp.mso.logger.MessageEnum;
27 import org.openecomp.mso.logger.MsoAlarmLogger;
28 import org.openecomp.mso.logger.MsoLogger;
29 import org.apache.http.HttpResponse;
30 import org.apache.http.client.HttpClient;
31 import org.apache.http.client.config.RequestConfig;
32 import org.apache.http.client.methods.*;
33 import org.apache.http.conn.ConnectTimeoutException;
34 import org.apache.http.entity.ContentType;
35 import org.apache.http.entity.StringEntity;
36 import org.apache.http.impl.client.HttpClientBuilder;
37 import org.apache.http.util.EntityUtils;
38 import org.w3c.dom.Document;
39 import org.w3c.dom.Element;
40 import org.w3c.dom.NodeList;
41 import org.xml.sax.InputSource;
42
43 import javax.xml.XMLConstants;
44 import javax.xml.bind.DatatypeConverter;
45 import javax.xml.parsers.DocumentBuilderFactory;
46 import javax.xml.xpath.XPath;
47 import javax.xml.xpath.XPathConstants;
48 import javax.xml.xpath.XPathExpressionException;
49 import javax.xml.xpath.XPathFactory;
50 import java.io.StringReader;
51 import java.net.HttpURLConnection;
52 import java.net.SocketTimeoutException;
53
54 /**
55  * Sends requests to SDNC and processes the responses.
56  */
57 public abstract class SDNCConnector {
58         private static final MsoLogger LOGGER = MsoLogger.getMsoLogger(MsoLogger.Catalog.RA);
59         private static final MsoAlarmLogger ALARMLOGGER = new MsoAlarmLogger();
60
61         public SDNCResponseCommon send(String content, TypedRequestTunables rt) {
62                 LOGGER.info(MessageEnum.RA_SEND_REQUEST_SDNC, rt.toString(), "SDNC", "");
63                 LOGGER.debug("SDNC Request Body:\n" + content);
64
65                 HttpRequestBase method = null;
66                 HttpResponse httpResponse = null;
67
68                 try {
69                         int timeout = Integer.parseInt(rt.getTimeout());
70
71                         RequestConfig requestConfig = RequestConfig.custom()
72                                 .setSocketTimeout(timeout)
73                                 .setConnectTimeout(timeout)
74                                 .setConnectionRequestTimeout(timeout)
75                                 .build();
76
77                         HttpClient client = HttpClientBuilder.create().build();
78
79                         if ("POST".equals(rt.getReqMethod())) {
80                                 HttpPost httpPost = new HttpPost(rt.getSdncUrl());
81                                 httpPost.setConfig(requestConfig);
82                                 httpPost.setEntity(new StringEntity(content, ContentType.APPLICATION_XML));
83                                 method =  httpPost;
84                         } else if ("PUT".equals(rt.getReqMethod())) {
85                                 HttpPut httpPut = new HttpPut(rt.getSdncUrl());
86                                 httpPut.setConfig(requestConfig);
87                                 httpPut.setEntity(new StringEntity(content, ContentType.APPLICATION_XML));
88                                 method =  httpPut;
89                         } else if ("GET".equals(rt.getReqMethod())) {
90                                 HttpGet httpGet = new HttpGet(rt.getSdncUrl());
91                                 httpGet.setConfig(requestConfig);
92                                 method =  httpGet;
93                         } else if ("DELETE".equals(rt.getReqMethod())) {
94                                 HttpDelete httpDelete = new HttpDelete(rt.getSdncUrl());
95                                 httpDelete.setConfig(requestConfig);
96                                 method =  httpDelete;
97                         }
98
99                         // AAF Integration, disabled for now due to the constrains from other party
100                         // String userCredentials = CredentialConstants.getSecurityProperties().getEncryptedProperty(CredentialConstants.DEFAULT_AUTH, "", CredentialConstants.getEncryptionKey());
101             // if (userCredentials == null) {
102             //          userCredentials = "";
103             //}
104                         String userCredentials = SDNCAdapterProperties.getEncryptedProperty(Constants.SDNC_AUTH_PROP,
105                                 Constants.DEFAULT_SDNC_AUTH, Constants.ENCRYPTION_KEY);
106                         String authorization = "Basic " + DatatypeConverter.printBase64Binary(userCredentials.getBytes());
107                         if(null != method) {
108                             method.setHeader("Authorization", authorization);
109                             method.setHeader("Accept", "application/yang.data+xml");
110                         }
111                         else {
112                             LOGGER.debug("method is NULL:");
113                         }
114
115                         
116
117                         httpResponse = client.execute(method);
118
119                         String responseContent = null;
120                         if (httpResponse.getEntity() != null) {
121                                 responseContent = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");
122                         }
123
124                         int statusCode = httpResponse.getStatusLine().getStatusCode();
125                         String statusMessage = httpResponse.getStatusLine().getReasonPhrase();
126
127                         LOGGER.debug("SDNC Response: " + statusCode + " " + statusMessage
128                                 + (responseContent == null ? "" : System.lineSeparator() + responseContent));
129
130                         if (httpResponse.getStatusLine().getStatusCode() >= 300) {
131                                 String errMsg = "SDNC returned " + statusCode + " " + statusMessage;
132
133                                 String errors = analyzeErrors(responseContent);
134                                 if (errors != null) {
135                                         errMsg += " " + errors;
136                                 }
137
138                                 logError(errMsg);
139                                 ALARMLOGGER.sendAlarm("MsoInternalError", MsoAlarmLogger.CRITICAL, errMsg);
140                                 return createErrorResponse(statusCode, errMsg, rt);
141                         }
142
143                         httpResponse = null;
144                         
145                         if(null != method) {
146                     method.reset();
147                         }
148             else {
149                 LOGGER.debug("method is NULL:");
150             }
151
152                         method = null;
153
154                         LOGGER.info(MessageEnum.RA_RESPONSE_FROM_SDNC, responseContent, "SDNC", "");
155                         return createResponseFromContent(statusCode, statusMessage, responseContent, rt);
156
157                 } catch (SocketTimeoutException | ConnectTimeoutException e) {
158                         String errMsg = "Request to SDNC timed out";
159                         logError(errMsg, e);
160                         return createErrorResponse(HttpURLConnection.HTTP_CLIENT_TIMEOUT, errMsg, rt);
161
162                 } catch (Exception e) {
163                         String errMsg = "Error processing request to SDNC";
164                         logError(errMsg, e);
165                         return createErrorResponse(HttpURLConnection.HTTP_INTERNAL_ERROR, errMsg, rt);
166
167                 } finally {
168                         if (httpResponse != null) {
169                                 try {
170                                         EntityUtils.consume(httpResponse.getEntity());
171                                 } catch (Exception e) {
172                                     LOGGER.debug("Exception:", e);
173                                 }
174                         }
175
176                         if (method != null) {
177                                 try {
178                                         method.reset();
179                                 } catch (Exception e) {
180                                     LOGGER.debug("Exception:", e);
181                                 }
182                         }
183                 }
184         }
185
186         protected void logError(String errMsg) {
187                 LOGGER.error(MessageEnum.RA_EXCEPTION_COMMUNICATE_SDNC, "SDNC", "",
188                         MsoLogger.ErrorCode.AvailabilityError, errMsg);
189                 ALARMLOGGER.sendAlarm("MsoInternalError", MsoAlarmLogger.CRITICAL, errMsg);
190         }
191
192         protected void logError(String errMsg, Throwable t) {
193                 LOGGER.error(MessageEnum.RA_EXCEPTION_COMMUNICATE_SDNC, "SDNC", "",
194                         MsoLogger.ErrorCode.AvailabilityError, errMsg, t);
195                 ALARMLOGGER.sendAlarm("MsoInternalError", MsoAlarmLogger.CRITICAL, errMsg);
196         }
197
198         /**
199          * Generates a response object from content received from SDNC.  The response
200          * object may be a success response object or an error response object. This
201          * method must be overridden by the subclass to return the correct object type.
202          * @param statusCode the response status code from SDNC (e.g. 200)
203          * @param statusMessage the response status message from SDNC (e.g. "OK")
204          * @param responseContent the body of the response from SDNC (possibly null)
205          * @param rt request tunables
206          * @return a response object
207          */
208         protected abstract SDNCResponseCommon createResponseFromContent(int statusCode,
209                         String statusMessage, String responseContent, TypedRequestTunables rt);
210
211         /**
212          * Generates an error response object. This method must be overridden by the
213          * subclass to return the correct object type.
214          * @param statusCode the response status code (from SDNC, or internally generated)
215          * @param errMsg the error message (normally a verbose explanation of the error)
216          * @param rt request tunables
217          * @return an error response object
218          */
219         protected abstract SDNCErrorCommon createErrorResponse(int statusCode,
220                         String errMsg, TypedRequestTunables rt);
221
222         /**
223          * Called by the send() method to analyze errors that may be encoded in the
224          * content of non-2XX responses.  By default, this method tries to parse the
225          * content as a restconf error.
226          * <pre>
227          *     xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf"
228          * </pre>
229          * If an error (or errors) can be obtained from the content, then the result
230          * is a string in this format:
231          * <pre>
232          * [error-type:TYPE, error-tag:TAG, error-message:MESSAGE] ...
233          * </pre>
234          * If no error could be obtained from the content, then the result is null.
235          * <p>
236          * The subclass can override this method to provide another implementation.
237          */
238         protected String analyzeErrors(String content) {
239                 if (content == null || content.isEmpty()) {
240                         return null;
241                 }
242
243                 // Confirmed with Andrew Shen on 11/1/16 that SDNC will send content like
244                 // this in error responses (non-2XX response codes) to "agnostic" service
245                 // requests.
246                 //
247                 // <errors xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf">
248                 //   <error>
249                 //     <error-type>protocol</error-type>
250                 //     <error-tag>malformed-message</error-tag>
251                 //     <error-message>Error parsing input: The element type "input" must be terminated by the matching end-tag "&lt;/input&gt;".</error-message>
252                 //   </error>
253                 // </errors>
254
255                 StringBuilder output = null;
256
257                 try {
258                         XPathFactory xpathFactory = XPathFactory.newInstance();
259                         XPath xpath = xpathFactory.newXPath();
260                         DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
261                         documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
262                         InputSource source = new InputSource(new StringReader(content));
263                         Document doc = documentBuilderFactory.newDocumentBuilder().parse(source);
264                         NodeList errors = (NodeList) xpath.evaluate("errors/error", doc, XPathConstants.NODESET);
265
266                         for (int i = 0; i < errors.getLength(); i++)
267                         {
268                                 Element error = (Element) errors.item(i);
269
270                                 String info = "";
271
272                                 try {
273                                         String errorType = xpath.evaluate("error-type", error);
274                                         info += "error-type:" + errorType;
275                                 } catch (XPathExpressionException e) {
276                                     LOGGER.error(MessageEnum.RA_EVALUATE_XPATH_ERROR, "error-type", error.toString(), "SDNC", "",
277                                         MsoLogger.ErrorCode.DataError, "XPath Exception", e);
278                                 }
279
280                                 try {
281                                         String errorTag = xpath.evaluate( "error-tag", error);
282                                         if (!info.isEmpty()) {
283                                                 info += ", ";
284                                         }
285                                         info += "error-tag:" + errorTag;
286                                 } catch (XPathExpressionException e) {
287                                         LOGGER.error(MessageEnum.RA_EVALUATE_XPATH_ERROR, "error-tag", error.toString(), "SDNC", "",
288                                                 MsoLogger.ErrorCode.DataError, "XPath Exception", e);
289                                 }
290
291                                 try {
292                                         String errorMessage = xpath.evaluate("error-message", error);
293                                         if (!info.isEmpty()) {
294                                                 info += ", ";
295                                         }
296                                         info += "error-message:" + errorMessage;
297                                 } catch (Exception e) {
298                                         LOGGER.error(MessageEnum.RA_EVALUATE_XPATH_ERROR, "error-message", error.toString(), "SDNC", "",
299                                                 MsoLogger.ErrorCode.DataError, "XPath Exception", e);
300                                 }
301
302                                 if (!info.isEmpty()) {
303                                         if (output == null) {
304                                                 output = new StringBuilder("[" + info + "]");
305                                         } else {
306                                                 output.append(" [").append(info).append("]");
307                                         }
308                                 }
309                         }
310                 } catch (Exception e) {
311                         LOGGER.error (MessageEnum.RA_ANALYZE_ERROR_EXC, "SDNC", "",
312                                 MsoLogger.ErrorCode.DataError, "Exception while analyzing errors", e);
313                 }
314
315                 return output.toString();
316         }
317 }