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