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 List<Attribute> toAttributeList(String xml, ONAPLogAdapter adapter) throws SAXException, IOException {
136 Logger log = adapter.unwrap();
137 Document doc = this.parser.parse(new InputSource(new StringReader(xml)));
138 NodeList children = doc.getDocumentElement().getChildNodes();
139 List<Attribute> result = new ArrayList<>();
140 for (int i = 0; i < children.getLength(); i++) {
141 Node child = children.item(i);
142 if (child.getNodeType() == Node.ELEMENT_NODE) {
144 // remove white space before conversion
145 String attributeName = ((Element) child).getTagName().replaceAll("\\s", "");
147 // If the incoming attribute name is not listed in the
148 // attributeNameMapping, then this attribute will be removed.
149 String newName = enricherAttributeNameMapping.get(attributeName);
150 if (newName != null) {
151 Attribute attr = new Attribute();
152 attr.setName(newName);
153 attr.setValue(((Element) child).getTextContent());
154 attr.setDataQuality(DataQuality.ok());
157 log.debug("[" + ((Element) child).getTagName()
158 + "] was removed due to not listed in enricherAttributeNameMapping.");
165 private void sendNotification(String url, String authorization, Object notification, ONAPLogAdapter adapter) {
167 Invocation.Builder request = this.callbackClient.target(url).request().accept(MediaType.TEXT_PLAIN_TYPE);
169 if (authorization != null) {
170 request.header(HttpHeaders.AUTHORIZATION, authorization);
172 Logger log = adapter.unwrap();
173 adapter.invoke(new RequestBuilderWrapper(request), InvocationMode.SYNCHRONOUS);
175 // native client marshaller doesn't skip null fields
176 // so manually marshal notification to json
178 if (log.isDebugEnabled()) {
179 StringBuilder debugRequest = new StringBuilder("REQUEST:\n");
180 debugRequest.append("URL: ").append(url).append("\n");
181 debugRequest.append("Payload: ").append(notification).append("\n");
182 // if (headers != null) {
183 // debugRequest.append("Headers: ");
184 // for (Entry<String, List<String>> header : headers.entrySet())
186 // debugRequest.append("\n\t").append(header.getKey()).append(":");
187 // for (String headerEntry : header.getValue()) {
188 // debugRequest.append("\"").append(headerEntry).append("\" ");
192 log.debug(debugRequest.toString());
195 Response result = request.post(Entity.json(notification));
197 adapter.unwrap().info("request at url = {} resulted in http response: {}", url,
198 result.getStatusInfo().getStatusCode() + " " + result.getStatusInfo().getReasonPhrase());
200 if (log.isDebugEnabled()) {
201 StringBuilder debugResponse = new StringBuilder("RESPONSE:\n");
202 debugResponse.append("Result: ").append(result.getStatus()).append("\n");
203 String content = result.hasEntity() ? result.getEntity().toString() : null;
204 if (result.getStatus() >= 300) {
205 debugResponse.append("Failure Cause: ").append(content).append("\n");
207 debugResponse.append("Payload: ").append(content).append("\n");
209 if (result.getHeaders() != null) {
210 debugResponse.append("Headers: ");
211 for (Entry<String, List<Object>> header : result.getHeaders().entrySet()) {
212 debugResponse.append("\n\t").append(header.getKey()).append(":");
213 for (Object headerEntry : header.getValue()) {
214 debugResponse.append("\"").append(headerEntry).append("\" ");
218 log.debug(debugResponse.toString());
221 } catch (Exception x) {
222 log.error("Error during {} operation to endpoint at url = {} with error = {}", "POST", url,
223 x.getLocalizedMessage());
227 private class ResourceTask implements Runnable {
228 private final String transactionId;
229 private final String requestId;
230 private final String resourceType;
231 private final List<String> resourceIds;
232 private final String notificationURL;
233 private final String notificationAuthorization;
234 private final String resourceURL;
235 private final ONAPLogAdapter adapter;
237 public ResourceTask(String transactionId,
240 List<String> resourceIds,
241 String notificationURL,
242 String notificationAuthorization,
244 ONAPLogAdapter adapter) {
245 this.transactionId = transactionId;
246 this.requestId = requestId;
247 this.resourceType = resourceType;
248 this.resourceIds = resourceIds;
249 this.notificationURL = notificationURL;
250 this.notificationAuthorization = notificationAuthorization;
251 this.resourceURL = resourceURL;
252 this.adapter = adapter;
257 NetworkDiscoveryNotification notification = new NetworkDiscoveryNotification();
258 notification.setRequestId(this.requestId);
260 List<Resource> resources = null;
261 MessageFormat format = new MessageFormat(SpringServiceImpl.this.enricherBaseUrl + this.resourceURL);
262 MultivaluedMap<String, String> headers = new MultivaluedHashMap<>();
263 headers.add(ENRICHER_HEADER_APPLICATION, RestService.SERVICE_NAME);
264 headers.add(ENRICHER_HEADER_TRANSACTION, this.transactionId);
266 for (String resourceId : this.resourceIds) {
267 String url = format.format(new Object[] { resourceId });
268 OperationResult result = SpringServiceImpl.this.enricherClient.get(url, headers,
269 MediaType.APPLICATION_XML_TYPE);
271 Resource resource = new Resource();
272 resource.setType(this.resourceType);
273 resource.setId(resourceId);
274 if (resources == null) {
275 resources = new ArrayList<>();
276 notification.setResources(resources);
278 resources.add(resource);
280 if (result.wasSuccessful()) {
281 resource.setDataQuality(DataQuality.ok());
283 List<Attribute> attributeList = toAttributeList(result.getResult(), adapter);
284 resource.setAttributeList(attributeList);
285 } catch (Exception x) {
286 resource.setDataQuality(DataQuality.error(x.getMessage()));
289 resource.setDataQuality(DataQuality.error(result.getFailureCause()));
292 notification.setCode(Status.OK.getStatusCode());
293 notification.setMessage(Status.OK.getReasonPhrase());
294 notification.setAckFinalIndicator(true);
296 // call client back with resource details
297 sendNotification(this.notificationURL, this.notificationAuthorization, notification, adapter);
301 private static class RequestBuilderWrapper implements RequestBuilder<RequestBuilderWrapper> {
302 private Invocation.Builder builder;
304 private RequestBuilderWrapper(Invocation.Builder builder) {
305 this.builder = builder;
309 public RequestBuilderWrapper setHeader(String name, String value) {
310 this.builder.header(name, value);