2 * ============LICENSE_START=======================================================
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 * Modifications Copyright (C) 2018 IBM.
9 * ================================================================================
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * Unless required by applicable law or agreed to in writing, software
17 * distributed under the License is distributed on an "AS IS" BASIS,
18 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 * See the License for the specific language governing permissions and
20 * limitations under the License.
21 * ============LICENSE_END=========================================================
24 package org.onap.so.adapters.sdnc.sdncrest;
26 import java.io.StringReader;
27 import java.net.HttpURLConnection;
28 import java.net.SocketTimeoutException;
30 import javax.xml.XMLConstants;
31 import javax.xml.bind.DatatypeConverter;
32 import javax.xml.parsers.DocumentBuilderFactory;
33 import javax.xml.xpath.XPath;
34 import javax.xml.xpath.XPathConstants;
35 import javax.xml.xpath.XPathExpressionException;
36 import javax.xml.xpath.XPathFactory;
38 import org.apache.http.HttpResponse;
39 import org.apache.http.client.HttpClient;
40 import org.apache.http.client.config.RequestConfig;
41 import org.apache.http.client.methods.HttpDelete;
42 import org.apache.http.client.methods.HttpGet;
43 import org.apache.http.client.methods.HttpPost;
44 import org.apache.http.client.methods.HttpPut;
45 import org.apache.http.client.methods.HttpRequestBase;
46 import org.apache.http.conn.ConnectTimeoutException;
47 import org.apache.http.entity.ContentType;
48 import org.apache.http.entity.StringEntity;
49 import org.apache.http.impl.client.HttpClientBuilder;
50 import org.apache.http.util.EntityUtils;
51 import org.onap.so.adapters.sdnc.impl.Constants;
52 import org.onap.so.adapters.sdncrest.SDNCErrorCommon;
53 import org.onap.so.adapters.sdncrest.SDNCResponseCommon;
54 import org.onap.so.logger.MessageEnum;
55 import org.onap.so.logger.MsoAlarmLogger;
56 import org.onap.so.logger.MsoLogger;
57 import org.springframework.beans.factory.annotation.Autowired;
58 import org.springframework.stereotype.Component;
59 import org.w3c.dom.Document;
60 import org.w3c.dom.Element;
61 import org.w3c.dom.NodeList;
62 import org.xml.sax.InputSource;
63 import org.onap.so.utils.CryptoUtils;
64 import org.springframework.core.env.Environment;
67 * Sends requests to SDNC and processes the responses.
70 public abstract class SDNCConnector {
71 private static final MsoLogger LOGGER = MsoLogger.getMsoLogger(MsoLogger.Catalog.RA,SDNCConnector.class);
72 private static final MsoAlarmLogger ALARMLOGGER = new MsoAlarmLogger();
73 private static final String MSO_INTERNAL_ERROR="MsoInternalError";
74 private static final String XPATH_EXCEPTION="XPath Exception";
76 private Environment env;
78 public SDNCResponseCommon send(String content, TypedRequestTunables rt) {
79 LOGGER.debug("SDNC URL: " + rt.getSdncUrl());
80 LOGGER.debug("SDNC Request Body:\n" + content);
82 HttpRequestBase method = null;
83 HttpResponse httpResponse = null;
86 int timeout = Integer.parseInt(rt.getTimeout());
88 RequestConfig requestConfig = RequestConfig.custom()
89 .setSocketTimeout(timeout)
90 .setConnectTimeout(timeout)
91 .setConnectionRequestTimeout(timeout)
94 HttpClient client = HttpClientBuilder.create().build();
96 if ("POST".equals(rt.getReqMethod())) {
97 HttpPost httpPost = new HttpPost(rt.getSdncUrl());
98 httpPost.setConfig(requestConfig);
99 httpPost.setEntity(new StringEntity(content, ContentType.APPLICATION_XML));
101 } else if ("PUT".equals(rt.getReqMethod())) {
102 HttpPut httpPut = new HttpPut(rt.getSdncUrl());
103 httpPut.setConfig(requestConfig);
104 httpPut.setEntity(new StringEntity(content, ContentType.APPLICATION_XML));
106 } else if ("GET".equals(rt.getReqMethod())) {
107 HttpGet httpGet = new HttpGet(rt.getSdncUrl());
108 httpGet.setConfig(requestConfig);
110 } else if ("DELETE".equals(rt.getReqMethod())) {
111 HttpDelete httpDelete = new HttpDelete(rt.getSdncUrl());
112 httpDelete.setConfig(requestConfig);
117 String userCredentials = CryptoUtils.decrypt(env.getProperty(Constants.SDNC_AUTH_PROP),
118 env.getProperty(Constants.ENCRYPTION_KEY_PROP));
119 String authorization = "Basic " + DatatypeConverter.printBase64Binary(userCredentials.getBytes());
121 method.setHeader("Authorization", authorization);
122 method.setHeader("Accept", "application/yang.data+xml");
125 LOGGER.debug("method is NULL:");
130 httpResponse = client.execute(method);
132 String responseContent = null;
133 if (httpResponse.getEntity() != null) {
134 responseContent = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");
137 int statusCode = httpResponse.getStatusLine().getStatusCode();
138 String statusMessage = httpResponse.getStatusLine().getReasonPhrase();
140 LOGGER.debug("SDNC Response: " + statusCode + " " + statusMessage
141 + (responseContent == null ? "" : System.lineSeparator() + responseContent));
143 if (httpResponse.getStatusLine().getStatusCode() >= 300) {
144 String errMsg = "SDNC returned " + statusCode + " " + statusMessage;
146 String errors = analyzeErrors(responseContent);
147 if (errors != null) {
148 errMsg += " " + errors;
152 ALARMLOGGER.sendAlarm(MSO_INTERNAL_ERROR, MsoAlarmLogger.CRITICAL, errMsg);
153 return createErrorResponse(statusCode, errMsg, rt);
162 LOGGER.debug("method is NULL:");
167 LOGGER.info(MessageEnum.RA_RESPONSE_FROM_SDNC, responseContent, "SDNC", "");
168 return createResponseFromContent(statusCode, statusMessage, responseContent, rt);
170 } catch (SocketTimeoutException | ConnectTimeoutException e) {
171 String errMsg = "Request to SDNC timed out";
173 return createErrorResponse(HttpURLConnection.HTTP_CLIENT_TIMEOUT, errMsg, rt);
175 } catch (Exception e) {
176 String errMsg = "Error processing request to SDNC";
178 return createErrorResponse(HttpURLConnection.HTTP_INTERNAL_ERROR, errMsg, rt);
181 if (httpResponse != null) {
183 EntityUtils.consume(httpResponse.getEntity());
184 } catch (Exception e) {
185 LOGGER.debug("Exception:", e);
189 if (method != null) {
192 } catch (Exception e) {
193 LOGGER.debug("Exception:", e);
199 protected void logError(String errMsg) {
200 LOGGER.error(MessageEnum.RA_EXCEPTION_COMMUNICATE_SDNC, "SDNC", "",
201 MsoLogger.ErrorCode.AvailabilityError, errMsg);
202 ALARMLOGGER.sendAlarm(MSO_INTERNAL_ERROR, MsoAlarmLogger.CRITICAL, errMsg);
205 protected void logError(String errMsg, Throwable t) {
206 LOGGER.error(MessageEnum.RA_EXCEPTION_COMMUNICATE_SDNC, "SDNC", "",
207 MsoLogger.ErrorCode.AvailabilityError, errMsg, t);
208 ALARMLOGGER.sendAlarm(MSO_INTERNAL_ERROR, MsoAlarmLogger.CRITICAL, errMsg);
212 * Generates a response object from content received from SDNC. The response
213 * object may be a success response object or an error response object. This
214 * method must be overridden by the subclass to return the correct object type.
215 * @param statusCode the response status code from SDNC (e.g. 200)
216 * @param statusMessage the response status message from SDNC (e.g. "OK")
217 * @param responseContent the body of the response from SDNC (possibly null)
218 * @param rt request tunables
219 * @return a response object
221 protected abstract SDNCResponseCommon createResponseFromContent(int statusCode,
222 String statusMessage, String responseContent, TypedRequestTunables rt);
225 * Generates an error response object. This method must be overridden by the
226 * subclass to return the correct object type.
227 * @param statusCode the response status code (from SDNC, or internally generated)
228 * @param errMsg the error message (normally a verbose explanation of the error)
229 * @param rt request tunables
230 * @return an error response object
232 protected abstract SDNCErrorCommon createErrorResponse(int statusCode,
233 String errMsg, TypedRequestTunables rt);
236 * Called by the send() method to analyze errors that may be encoded in the
237 * content of non-2XX responses. By default, this method tries to parse the
238 * content as a restconf error.
240 * xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf"
242 * If an error (or errors) can be obtained from the content, then the result
243 * is a string in this format:
245 * [error-type:TYPE, error-tag:TAG, error-message:MESSAGE] ...
247 * If no error could be obtained from the content, then the result is null.
249 * The subclass can override this method to provide another implementation.
251 protected String analyzeErrors(String content) {
252 if (content == null || content.isEmpty()) {
256 // Confirmed with Andrew Shen on 11/1/16 that SDNC will send content like
257 // this in error responses (non-2XX response codes) to "agnostic" service
260 // <errors xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf">
262 // <error-type>protocol</error-type>
263 // <error-tag>malformed-message</error-tag>
264 // <error-message>Error parsing input: The element type "input" must be terminated by the matching end-tag "</input>".</error-message>
268 StringBuilder output = null;
271 XPathFactory xpathFactory = XPathFactory.newInstance();
272 XPath xpath = xpathFactory.newXPath();
273 DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
274 documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
275 documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
276 documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
277 InputSource source = new InputSource(new StringReader(content));
278 Document doc = documentBuilderFactory.newDocumentBuilder().parse(source);
279 NodeList errors = (NodeList) xpath.evaluate("errors/error", doc, XPathConstants.NODESET);
281 for (int i = 0; i < errors.getLength(); i++)
283 Element error = (Element) errors.item(i);
288 String errorType = xpath.evaluate("error-type", error);
289 info += "error-type:" + errorType;
290 } catch (XPathExpressionException e) {
291 LOGGER.error(MessageEnum.RA_EVALUATE_XPATH_ERROR, "error-type", error.toString(), "SDNC", "",
292 MsoLogger.ErrorCode.DataError, XPATH_EXCEPTION, e);
296 String errorTag = xpath.evaluate( "error-tag", error);
297 if (!info.isEmpty()) {
300 info += "error-tag:" + errorTag;
301 } catch (XPathExpressionException e) {
302 LOGGER.error(MessageEnum.RA_EVALUATE_XPATH_ERROR, "error-tag", error.toString(), "SDNC", "",
303 MsoLogger.ErrorCode.DataError, XPATH_EXCEPTION, e);
307 String errorMessage = xpath.evaluate("error-message", error);
308 if (!info.isEmpty()) {
311 info += "error-message:" + errorMessage;
312 } catch (Exception e) {
313 LOGGER.error(MessageEnum.RA_EVALUATE_XPATH_ERROR, "error-message", error.toString(), "SDNC", "",
314 MsoLogger.ErrorCode.DataError, XPATH_EXCEPTION, e);
317 if (!info.isEmpty()) {
318 if (output == null) {
319 output = new StringBuilder("[" + info + "]");
321 output.append(" [").append(info).append("]");
325 } catch (Exception e) {
326 LOGGER.error (MessageEnum.RA_ANALYZE_ERROR_EXC, "SDNC", "",
327 MsoLogger.ErrorCode.DataError, "Exception while analyzing errors", e);
330 return output.toString();