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.sdnc.apps.pomba.networkdiscovery.ApplicationException;
49 import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.Attribute;
50 import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.DataQuality;
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;
91 public SpringServiceImpl() throws ParserConfigurationException {
92 this.parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
96 public NetworkDiscoveryResponse findbyResourceIdAndType(String transactionId,
99 List<String> resourceIds,
100 String notificationURL,
101 String notificationAuthorization,
102 ONAPLogAdapter adapter) throws ApplicationException {
104 NetworkDiscoveryResponse response = new NetworkDiscoveryResponse();
105 response.setRequestId(requestId);
107 String enricherURL = this.enricherTypeURLs.get(resourceType);
108 // check if resourceType is supported
109 if (enricherURL == null) {
110 // don't know what to do with this - return empty response
111 response.setCode(Status.NO_CONTENT.getStatusCode());
112 response.setMessage("Unsupported resourceType " + resourceType);
113 response.setAckFinalIndicator(true);
117 // schedule discovery of specified resources
118 Runnable task = new ResourceTask(transactionId, requestId, resourceType, resourceIds,
119 notificationURL, notificationAuthorization, enricherURL, adapter);
120 this.executor.submit(task);
122 response.setCode(Status.ACCEPTED.getStatusCode());
123 response.setMessage(Status.ACCEPTED.getReasonPhrase());
124 response.setAckFinalIndicator(false);
129 public void shutdown() {
130 this.executor.shutdown();
133 private List<Attribute> toAttributeList(String xml) throws SAXException, IOException {
134 // TODO don't return raw A&AI attributes but coerce to swagger-defined enums
135 Document doc = this.parser.parse(new InputSource(new StringReader(xml)));
136 NodeList children = doc.getDocumentElement().getChildNodes();
137 List<Attribute> result = new ArrayList<>();
138 for (int i = 0; i < children.getLength(); i++) {
139 Node child = children.item(i);
140 if (child.getNodeType() == Node.ELEMENT_NODE) {
141 Attribute attr = new Attribute();
142 attr.setName(((Element)child).getTagName());
143 attr.setValue(((Element)child).getTextContent());
144 attr.setDataQuality(DataQuality.ok());
151 private void sendNotification(String url, String authorization, Object notification, ONAPLogAdapter adapter) {
153 Invocation.Builder request = this.callbackClient
156 .accept(MediaType.TEXT_PLAIN_TYPE);
158 if (authorization != null) {
159 request.header(HttpHeaders.AUTHORIZATION, authorization);
161 Logger log = adapter.unwrap();
162 adapter.invoke(new RequestBuilderWrapper(request), InvocationMode.SYNCHRONOUS);
164 // native client marshaller doesn't skip null fields
165 // so manually marshal notification to json
167 if (log.isDebugEnabled()) {
168 StringBuilder debugRequest = new StringBuilder("REQUEST:\n");
169 debugRequest.append("URL: ").append(url).append("\n");
170 debugRequest.append("Payload: ").append(notification).append("\n");
171 // if (headers != null) {
172 // debugRequest.append("Headers: ");
173 // for (Entry<String, List<String>> header : headers.entrySet()) {
174 // debugRequest.append("\n\t").append(header.getKey()).append(":");
175 // for (String headerEntry : header.getValue()) {
176 // debugRequest.append("\"").append(headerEntry).append("\" ");
180 log.debug(debugRequest.toString());
183 Response result = request.post(Entity.json(notification));
185 adapter.unwrap().info("request at url = {} resulted in http response: {}", url, result.getStatusInfo().getStatusCode() + " " + result.getStatusInfo().getReasonPhrase());
187 if (log.isDebugEnabled()) {
188 StringBuilder debugResponse = new StringBuilder("RESPONSE:\n");
189 debugResponse.append("Result: ").append(result.getStatus()).append("\n");
190 String content = result.hasEntity() ? result.getEntity().toString() : null;
191 if (result.getStatus() >= 300) {
192 debugResponse.append("Failure Cause: ").append(content).append("\n");
194 debugResponse.append("Payload: ").append(content).append("\n");
196 if (result.getHeaders() != null) {
197 debugResponse.append("Headers: ");
198 for (Entry<String, List<Object>> header : result.getHeaders().entrySet()) {
199 debugResponse.append("\n\t").append(header.getKey()).append(":");
200 for (Object headerEntry : header.getValue()) {
201 debugResponse.append("\"").append(headerEntry).append("\" ");
205 log.debug(debugResponse.toString());
208 } catch (Exception x) {
209 log.error("Error during {} operation to endpoint at url = {} with error = {}",
210 "POST", url, x.getLocalizedMessage());
214 private class ResourceTask implements Runnable {
215 private final String transactionId;
216 private final String requestId;
217 private final String resourceType;
218 private final List<String> resourceIds;
219 private final String notificationURL;
220 private final String notificationAuthorization;
221 private final String resourceURL;
222 private final ONAPLogAdapter adapter;
224 public ResourceTask(String transactionId,
227 List<String> resourceIds,
228 String notificationURL,
229 String notificationAuthorization,
231 ONAPLogAdapter adapter) {
232 this.transactionId = transactionId;
233 this.requestId = requestId;
234 this.resourceType = resourceType;
235 this.resourceIds = resourceIds;
236 this.notificationURL = notificationURL;
237 this.notificationAuthorization = notificationAuthorization;
238 this.resourceURL = resourceURL;
239 this.adapter = adapter;
244 NetworkDiscoveryNotification notification = new NetworkDiscoveryNotification();
245 notification.setRequestId(this.requestId);
247 List<Resource> resources = null;
248 MessageFormat format = new MessageFormat(SpringServiceImpl.this.enricherBaseUrl + this.resourceURL);
249 MultivaluedMap<String, String> headers = new MultivaluedHashMap<>();
250 headers.add(ENRICHER_HEADER_APPLICATION, RestService.SERVICE_NAME);
251 headers.add(ENRICHER_HEADER_TRANSACTION, this.transactionId);
252 for (String resourceId : this.resourceIds) {
253 String url = format.format(new Object[] { resourceId });
254 OperationResult result = SpringServiceImpl.this.enricherClient.get(url, headers, MediaType.APPLICATION_XML_TYPE);
256 Resource resource = new Resource();
257 resource.setType(this.resourceType);
258 resource.setId(resourceId);
259 if (resources == null) {
260 resources = new ArrayList<>();
261 notification.setResources(resources);
263 resources.add(resource);
265 if (result.wasSuccessful()) {
266 resource.setDataQuality(DataQuality.ok());
268 resource.setAttributeList(toAttributeList(result.getResult()));
269 } catch (Exception x) {
270 resource.setDataQuality(DataQuality.error(x.getMessage()));
274 resource.setDataQuality(DataQuality.error(result.getFailureCause()));
277 notification.setCode(Status.OK.getStatusCode());
278 notification.setMessage(Status.OK.getReasonPhrase());
279 notification.setAckFinalIndicator(true);
281 // call client back with resource details
282 sendNotification(this.notificationURL, this.notificationAuthorization, notification, adapter);
286 private static class RequestBuilderWrapper implements RequestBuilder<RequestBuilderWrapper> {
287 private Invocation.Builder builder;
288 private RequestBuilderWrapper(Invocation.Builder builder) {
289 this.builder = builder;
293 public RequestBuilderWrapper setHeader(String name, String value) {
294 this.builder.header(name, value);