2 * ============LICENSE_START===================================================
3 * Copyright (c) 2018 Amdocs
4 * ============================================================================
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 * ============LICENSE_END=====================================================
18 package org.onap.sdnc.apps.pomba.networkdiscovery.service;
20 import com.google.common.util.concurrent.ThreadFactoryBuilder;
21 import java.io.IOException;
22 import java.io.StringReader;
23 import java.text.MessageFormat;
24 import java.util.ArrayList;
25 import java.util.List;
27 import java.util.Map.Entry;
28 import java.util.concurrent.ExecutorService;
29 import java.util.concurrent.Executors;
30 import javax.annotation.PreDestroy;
31 import javax.ws.rs.client.Client;
32 import javax.ws.rs.client.Entity;
33 import javax.ws.rs.client.Invocation;
34 import javax.ws.rs.core.HttpHeaders;
35 import javax.ws.rs.core.MediaType;
36 import javax.ws.rs.core.MultivaluedHashMap;
37 import javax.ws.rs.core.MultivaluedMap;
38 import javax.ws.rs.core.Response;
39 import javax.ws.rs.core.Response.Status;
40 import javax.xml.parsers.DocumentBuilder;
41 import javax.xml.parsers.DocumentBuilderFactory;
42 import javax.xml.parsers.ParserConfigurationException;
43 import org.onap.aai.restclient.client.OperationResult;
44 import org.onap.aai.restclient.client.RestClient;
45 import org.onap.logging.ref.slf4j.ONAPLogAdapter;
46 import org.onap.logging.ref.slf4j.ONAPLogAdapter.RequestBuilder;
47 import org.onap.logging.ref.slf4j.ONAPLogConstants.InvocationMode;
48 import org.onap.pomba.common.datatypes.DataQuality;
49 import org.onap.sdnc.apps.pomba.networkdiscovery.ApplicationException;
50 import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.Attribute;
51 import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.NetworkDiscoveryNotification;
52 import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.NetworkDiscoveryResponse;
53 import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.Resource;
54 import org.onap.sdnc.apps.pomba.networkdiscovery.service.rs.RestService;
55 import org.slf4j.Logger;
56 import org.springframework.beans.factory.annotation.Autowired;
57 import org.springframework.stereotype.Service;
58 import org.w3c.dom.Document;
59 import org.w3c.dom.Element;
60 import org.w3c.dom.Node;
61 import org.w3c.dom.NodeList;
62 import org.xml.sax.InputSource;
63 import org.xml.sax.SAXException;
66 public class SpringServiceImpl implements SpringService {
67 private static final String ENRICHER_HEADER_APPLICATION = "X-FromAppId";
68 private static final String ENRICHER_HEADER_TRANSACTION = "X-TransactionId";
70 private static final int DEFAULT_WORKER_THREADS = 3;
72 private ExecutorService executor = Executors.newFixedThreadPool(
73 Integer.getInteger("discovery.threads", DEFAULT_WORKER_THREADS),
74 new ThreadFactoryBuilder().setNameFormat("discovery-worker-%d").build());
77 private RestClient enricherClient;
80 private String enricherBaseUrl;
82 @javax.annotation.Resource
83 private Client callbackClient;
85 @javax.annotation.Resource
86 private Map<String, String> enricherTypeURLs;
88 private DocumentBuilder parser;
90 @javax.annotation.Resource
91 private Map<String, String> enricherAttributeNameMapping;
93 public SpringServiceImpl() throws ParserConfigurationException {
94 this.parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
98 public NetworkDiscoveryResponse findbyResourceIdAndType(String transactionId,
101 List<String> resourceIds,
102 String notificationURL,
103 String notificationAuthorization,
104 ONAPLogAdapter adapter) throws ApplicationException {
106 NetworkDiscoveryResponse response = new NetworkDiscoveryResponse();
107 response.setRequestId(requestId);
109 String enricherURL = this.enricherTypeURLs.get(resourceType);
110 // check if resourceType is supported
111 if (enricherURL == null) {
112 // don't know what to do with this - return empty response
113 response.setCode(Status.NO_CONTENT.getStatusCode());
114 response.setMessage("Unsupported resourceType " + resourceType);
115 response.setAckFinalIndicator(true);
119 // schedule discovery of specified resources
120 Runnable task = new ResourceTask(transactionId, requestId, resourceType, resourceIds, notificationURL,
121 notificationAuthorization, enricherURL, adapter);
122 this.executor.submit(task);
124 response.setCode(Status.ACCEPTED.getStatusCode());
125 response.setMessage(Status.ACCEPTED.getReasonPhrase());
126 response.setAckFinalIndicator(false);
131 public void shutdown() {
132 this.executor.shutdown();
135 private void sendNotification(String url, String authorization, Object notification, ONAPLogAdapter adapter) {
137 Invocation.Builder request = this.callbackClient.target(url).request().accept(MediaType.TEXT_PLAIN_TYPE);
139 if (authorization != null) {
140 request.header(HttpHeaders.AUTHORIZATION, authorization);
142 Logger log = adapter.unwrap();
143 adapter.invoke(new RequestBuilderWrapper(request), InvocationMode.SYNCHRONOUS);
145 // native client marshaller doesn't skip null fields
146 // so manually marshal notification to json
148 if (log.isDebugEnabled()) {
149 StringBuilder debugRequest = new StringBuilder("REQUEST:\n");
150 debugRequest.append("URL: ").append(url).append("\n");
151 debugRequest.append("Payload: ").append(notification).append("\n");
152 // if (headers != null) {
153 // debugRequest.append("Headers: ");
154 // for (Entry<String, List<String>> header : headers.entrySet())
156 // debugRequest.append("\n\t").append(header.getKey()).append(":");
157 // for (String headerEntry : header.getValue()) {
158 // debugRequest.append("\"").append(headerEntry).append("\" ");
162 log.debug(debugRequest.toString());
165 Response result = request.post(Entity.json(notification));
167 adapter.unwrap().info("request at url = {} resulted in http response: {}", url,
168 result.getStatusInfo().getStatusCode() + " " + result.getStatusInfo().getReasonPhrase());
170 if (log.isDebugEnabled()) {
171 StringBuilder debugResponse = new StringBuilder("RESPONSE:\n");
172 debugResponse.append("Result: ").append(result.getStatus()).append("\n");
173 String content = result.hasEntity() ? result.getEntity().toString() : null;
174 if (result.getStatus() >= 300) {
175 debugResponse.append("Failure Cause: ").append(content).append("\n");
177 debugResponse.append("Payload: ").append(content).append("\n");
179 if (result.getHeaders() != null) {
180 debugResponse.append("Headers: ");
181 for (Entry<String, List<Object>> header : result.getHeaders().entrySet()) {
182 debugResponse.append("\n\t").append(header.getKey()).append(":");
183 for (Object headerEntry : header.getValue()) {
184 debugResponse.append("\"").append(headerEntry).append("\" ");
188 log.debug(debugResponse.toString());
191 } catch (Exception x) {
192 log.error("Error during {} operation to endpoint at url = {} with error = {}", "POST", url,
193 x.getLocalizedMessage());
197 private class ResourceTask implements Runnable {
198 private final String transactionId;
199 private final String requestId;
200 private final String resourceType;
201 private final List<String> resourceIds;
202 private final String notificationURL;
203 private final String notificationAuthorization;
204 private final String resourceURL;
205 private final ONAPLogAdapter adapter;
207 public ResourceTask(String transactionId,
210 List<String> resourceIds,
211 String notificationURL,
212 String notificationAuthorization,
214 ONAPLogAdapter adapter) {
215 this.transactionId = transactionId;
216 this.requestId = requestId;
217 this.resourceType = resourceType;
218 this.resourceIds = resourceIds;
219 this.notificationURL = notificationURL;
220 this.notificationAuthorization = notificationAuthorization;
221 this.resourceURL = resourceURL;
222 this.adapter = adapter;
227 NetworkDiscoveryNotification notification = new NetworkDiscoveryNotification();
228 notification.setRequestId(this.requestId);
230 List<Resource> resources = null;
231 MessageFormat format = new MessageFormat(SpringServiceImpl.this.enricherBaseUrl + this.resourceURL);
232 MultivaluedMap<String, String> headers = new MultivaluedHashMap<>();
233 headers.add(ENRICHER_HEADER_APPLICATION, RestService.SERVICE_NAME);
234 headers.add(ENRICHER_HEADER_TRANSACTION, this.transactionId);
236 for (String resourceId : this.resourceIds) {
237 String url = format.format(new Object[] { resourceId });
238 OperationResult result = SpringServiceImpl.this.enricherClient.get(url, headers,
239 MediaType.APPLICATION_XML_TYPE);
241 Resource resource = new Resource();
242 resource.setType(this.resourceType);
243 resource.setId(resourceId);
244 if (resources == null) {
245 resources = new ArrayList<>();
246 notification.setResources(resources);
248 resources.add(resource);
250 if (result.wasSuccessful()) {
251 resource.setDataQuality(DataQuality.ok());
253 List<Attribute> attributeList = toAttributeList(result.getResult(), adapter);
254 resource.setAttributeList(attributeList);
255 } catch (Exception x) {
256 resource.setDataQuality(DataQuality.error(x.getMessage()));
259 resource.setDataQuality(DataQuality.error(result.getFailureCause()));
262 notification.setCode(Status.OK.getStatusCode());
263 notification.setMessage(Status.OK.getReasonPhrase());
264 notification.setAckFinalIndicator(true);
266 // call client back with resource details
267 sendNotification(this.notificationURL, this.notificationAuthorization, notification, adapter);
270 private List<Attribute> toAttributeList(String xml, ONAPLogAdapter adapter) throws SAXException, IOException {
271 Logger log = adapter.unwrap();
272 Document doc = parser.parse(new InputSource(new StringReader(xml)));
273 NodeList children = doc.getDocumentElement().getChildNodes();
274 List<Attribute> result = new ArrayList<>();
275 for (int i = 0; i < children.getLength(); i++) {
276 Node child = children.item(i);
277 if (child.getNodeType() == Node.ELEMENT_NODE) {
279 // remove white space before conversion
280 String attributeName = ((Element) child).getTagName().replaceAll("\\s", "");
282 // If the incoming attribute name is not listed in the
283 // attributeNameMapping, then this attribute will be removed.
284 String newName = enricherAttributeNameMapping.get(attributeName);
285 if (newName != null) {
286 Attribute attr = new Attribute();
287 attr.setName(newName);
288 attr.setValue(((Element) child).getTextContent());
289 attr.setDataQuality(DataQuality.ok());
292 log.debug("[" + ((Element) child).getTagName()
293 + "] was removed due to not listed in enricherAttributeNameMapping.");
302 private static class RequestBuilderWrapper implements RequestBuilder<RequestBuilderWrapper> {
303 private Invocation.Builder builder;
305 private RequestBuilderWrapper(Invocation.Builder builder) {
306 this.builder = builder;
310 public RequestBuilderWrapper setHeader(String name, String value) {
311 this.builder.header(name, value);