diff options
author | 2018-07-12 14:26:26 -0400 | |
---|---|---|
committer | 2018-07-23 15:02:36 -0400 | |
commit | 92c73de0f57aee2ada5f5c85b960c4c8ce7adf85 (patch) | |
tree | 66038237e070b13a82b1ceb7e7b0d98ba15fe576 /pomba/network-discovery/src/main/java | |
parent | 17ac3b089df1cc33ced623ab4150edcacd4381c6 (diff) |
Initial code for network discovery microservice
Issue-ID: SDNC-317
Change-Id: I09d44de02283079471de0084da07793cb3d7820c
Signed-off-by: Brad Benesch <brad.benesch@amdocs.com>
Diffstat (limited to 'pomba/network-discovery/src/main/java')
11 files changed, 1009 insertions, 0 deletions
diff --git a/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/Application.java b/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/Application.java new file mode 100644 index 0000000..eb2d9fd --- /dev/null +++ b/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/Application.java @@ -0,0 +1,44 @@ +/* + * ============LICENSE_START=================================================== + * Copyright (c) 2018 Amdocs + * ============================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END===================================================== + */ +package org.onap.sdnc.apps.pomba.networkdiscovery; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.support.SpringBootServletInitializer; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.scheduling.annotation.EnableAsync; + +@SpringBootApplication +@ComponentScan(basePackages = {"org.onap.sdnc.apps.pomba.networkdiscovery"}) +@EnableAsync +@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class}) +public class Application extends SpringBootServletInitializer { + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(Application.class); + } + + public static void main(String[] args) throws Exception { + SpringApplication.run(Application.class, args); + } + +} diff --git a/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/ApplicationException.java b/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/ApplicationException.java new file mode 100644 index 0000000..6cc3592 --- /dev/null +++ b/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/ApplicationException.java @@ -0,0 +1,85 @@ +/* + * ============LICENSE_START=================================================== + * Copyright (c) 2018 Amdocs + * ============================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END===================================================== + */ +package org.onap.sdnc.apps.pomba.networkdiscovery; + +import javax.ws.rs.core.Response.Status; + +public class ApplicationException extends Exception { + public static enum Error { + GENERAL_FAILURE("NET.0001", "An error occurred: %s"), + MISSING_PARAM("NET.0002", "Missing required parameter %s"), + UNAUTHORIZED("NET.0003", "Unauthorized"); + + private final String responseCode; + private final String message; + + private Error(String responseCode, String message) { + this.responseCode = responseCode; + this.message = message; + } + + public String getMessage(Object... args) { + return String.format(this.message, args); + } + + public String getResponseCode() { + return this.responseCode; + } + } + + private static final long serialVersionUID = -4874149714911165454L; + + private final Status httpStatus; + private String responseCode; + + public ApplicationException(String message) { + this(message, Status.INTERNAL_SERVER_ERROR); + } + + public ApplicationException(Error errorCode, Status httpStatus, Object... args) { + super(errorCode.getMessage(args)); + if (httpStatus == null) { + throw new NullPointerException("httpStatus"); + } + + this.responseCode = errorCode.getResponseCode(); + this.httpStatus = httpStatus; + } + + public ApplicationException(String message, Status httpStatus) { + super(message); + if (httpStatus == null) { + throw new NullPointerException("httpStatus"); + } + this.httpStatus = httpStatus; + } + + public ApplicationException(String message, Exception cause) { + super(message, cause); + this.httpStatus = Status.INTERNAL_SERVER_ERROR; + } + + public Status getHttpStatus() { + return this.httpStatus; + } + + public String getResponseCode() { + return this.responseCode; + } + +} diff --git a/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/AuthorizationConfiguration.java b/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/AuthorizationConfiguration.java new file mode 100644 index 0000000..5204a48 --- /dev/null +++ b/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/AuthorizationConfiguration.java @@ -0,0 +1,41 @@ +/* + * ============LICENSE_START=================================================== + * Copyright (c) 2018 Amdocs + * ============================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END===================================================== + */ + +package org.onap.sdnc.apps.pomba.networkdiscovery; + +import java.util.Base64; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; + +@Component +public class AuthorizationConfiguration { + + @Value("${basicAuth.username:admin}") + private String username; + + @Value("${basicAuth.password:admin}") + private String password; + + @Bean(name="basicAuthHeader") + public String getBasicAuthHeader() { + return "Basic " + Base64.getEncoder().encodeToString((this.username + ":" + this.password).getBytes()); + } + + +} diff --git a/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/CallbackConfiguration.java b/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/CallbackConfiguration.java new file mode 100644 index 0000000..30d872f --- /dev/null +++ b/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/CallbackConfiguration.java @@ -0,0 +1,74 @@ +/* + * ============LICENSE_START=================================================== + * Copyright (c) 2018 Amdocs + * ============================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END===================================================== + */ +package org.onap.sdnc.apps.pomba.networkdiscovery; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.client.ClientProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; + +@Component +public class CallbackConfiguration { + /** Use same object mapper as embedded tomcat server */ + @Autowired + private ObjectMapper objectMapper; + + @Value("${callback.keyStorePath:}") + private String keyStorePath; + + @Value("${callback.keyStorePassword:}") + private String keyStorePassword; + + @Value("${callback.connectionTimeout:5000}") + private int connectionTimeout; + + @Value("${callback.readTimeout:60000}") + private int readTimeout; + + @Value("${callback.username:}") + private String basicAuthUsername; + + @Value("${callback.password:}") + private String basicAuthPassword; + + @Bean(name="callbackClient") + public Client getClient() throws Exception { + ClientConfig configuration = new ClientConfig() + .property(ClientProperties.CONNECT_TIMEOUT, this.connectionTimeout) + .property(ClientProperties.READ_TIMEOUT, this.readTimeout) + .register(new JacksonJaxbJsonProvider(objectMapper, null)); + + // TODO set up SSL if configured +// if (useSsl) { +// setupSecureSocketLayerClientConfig(clientConfig); +// } + // Finally, create and initialize our client... + Client client = ClientBuilder.newClient(configuration); + + // ...and return it to the caller. + return client; + } + + +} diff --git a/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/EnricherConfiguration.java b/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/EnricherConfiguration.java new file mode 100644 index 0000000..0a08577 --- /dev/null +++ b/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/EnricherConfiguration.java @@ -0,0 +1,86 @@ +/* + * ============LICENSE_START=================================================== + * Copyright (c) 2018 Amdocs + * ============================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END===================================================== + */ +package org.onap.sdnc.apps.pomba.networkdiscovery; + +import java.util.HashMap; +import java.util.Map; +import java.util.StringTokenizer; +import org.onap.aai.restclient.client.RestClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +@Configuration +public class EnricherConfiguration { + @Autowired + private Environment env; + + @Value("${enricher.url}") + private String url; + + @Value("${enricher.keyStorePath}") + private String keyStorePath; + + @Value("${enricher.keyStorePassword}") + private String keyStorePassword; + + @Value("${enricher.connectionTimeout:5000}") + private int connectionTimeout; + + @Value("${enricher.readTimeout:60000}") + private int readTimeout; + + @Bean(name="enricherClient") + public RestClient restClient() { + return new RestClient() + .validateServerHostname(false) + .validateServerCertChain(false) + .connectTimeoutMs(this.connectionTimeout) + .readTimeoutMs(this.readTimeout) + .clientCertFile(this.keyStorePath) + .clientCertPassword(this.keyStorePassword); + } + + @Bean(name="enricherBaseUrl") + public String getURL() { + return this.url; + } + + @Bean(name="enricherTypeURLs") + public Map<String, String> enricherTypeURLs() { + + Map<String, String> result = new HashMap<>(); + String types = this.env.getProperty("enricher.types"); + if (types == null) { + return result; + } + + StringTokenizer tokenizer = new StringTokenizer(types, ", "); + while (tokenizer.hasMoreTokens()) { + String type = tokenizer.nextToken(); + String url = this.env.getProperty("enricher.type." + type + ".url"); + result.put(type, url); + } + + return result; + } + + +} diff --git a/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/JerseyConfiguration.java b/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/JerseyConfiguration.java new file mode 100644 index 0000000..5d93f64 --- /dev/null +++ b/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/JerseyConfiguration.java @@ -0,0 +1,66 @@ +/* + * ============LICENSE_START=================================================== + * Copyright (c) 2018 Amdocs + * ============================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END===================================================== + */ +package org.onap.sdnc.apps.pomba.networkdiscovery; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.MapperFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import java.util.logging.Logger; +import javax.ws.rs.ApplicationPath; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.filter.LoggingFilter; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.servlet.ServletProperties; +import org.onap.sdnc.apps.pomba.networkdiscovery.service.rs.RestServiceImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; + +@Component +@ApplicationPath("/") +public class JerseyConfiguration extends ResourceConfig { + private static final Logger log = Logger.getLogger(JerseyConfiguration.class.getName()); + + @Bean + @Primary + public ObjectMapper objectMapper() { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); + objectMapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES); + objectMapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING); + return objectMapper; + } + + @Autowired + public JerseyConfiguration() { + register(RestServiceImpl.class); + property(ServletProperties.FILTER_FORWARD_ON_404, true); + register(new LoggingFilter(log, true)); + } + + @Bean + public Client jerseyClient() { + return ClientBuilder.newClient(new ClientConfig()); + } +} diff --git a/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/WebConfiguration.java b/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/WebConfiguration.java new file mode 100644 index 0000000..7a7bbd9 --- /dev/null +++ b/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/WebConfiguration.java @@ -0,0 +1,44 @@ +/* + * ============LICENSE_START=================================================== + * Copyright (c) 2018 Amdocs + * ============================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END===================================================== + */ +package org.onap.sdnc.apps.pomba.networkdiscovery; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; + +@Configuration +public class WebConfiguration { + + @Bean + public WebMvcConfigurerAdapter forwardToIndex() { + return new WebMvcConfigurerAdapter() { + @Override + public void addViewControllers(ViewControllerRegistry registry) { + registry.addViewController("/swagger").setViewName( + "redirect:/swagger/index.html"); + registry.addViewController("/swagger/").setViewName( + "redirect:/swagger/index.html"); + registry.addViewController("/docs").setViewName( + "redirect:/docs/html/index.html"); + registry.addViewController("/docs/").setViewName( + "redirect:/docs/html/index.html"); + } + }; + } +}
\ No newline at end of file diff --git a/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/service/SpringService.java b/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/service/SpringService.java new file mode 100644 index 0000000..e0f50f6 --- /dev/null +++ b/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/service/SpringService.java @@ -0,0 +1,35 @@ +/* + * ============LICENSE_START=================================================== + * Copyright (c) 2018 Amdocs + * ============================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END===================================================== + */ +package org.onap.sdnc.apps.pomba.networkdiscovery.service; + +import java.util.List; +import org.onap.logging.ref.slf4j.ONAPLogAdapter; +import org.onap.sdnc.apps.pomba.networkdiscovery.ApplicationException; +import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.NetworkDiscoveryResponse; + +public interface SpringService { + + public NetworkDiscoveryResponse findbyResourceIdAndType(String transactionId, + String requestId, + String resourceType, + List<String> resourceIds, + String notificationURL, + String notificationAuthorization, + ONAPLogAdapter adapter) + throws ApplicationException; +} diff --git a/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/service/SpringServiceImpl.java b/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/service/SpringServiceImpl.java new file mode 100644 index 0000000..3230a52 --- /dev/null +++ b/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/service/SpringServiceImpl.java @@ -0,0 +1,299 @@ +/* + * ============LICENSE_START=================================================== + * Copyright (c) 2018 Amdocs + * ============================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END===================================================== + */ +package org.onap.sdnc.apps.pomba.networkdiscovery.service; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import java.io.IOException; +import java.io.StringReader; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import javax.annotation.PreDestroy; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.Invocation; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import org.onap.aai.restclient.client.OperationResult; +import org.onap.aai.restclient.client.RestClient; +import org.onap.logging.ref.slf4j.ONAPLogAdapter; +import org.onap.logging.ref.slf4j.ONAPLogAdapter.RequestBuilder; +import org.onap.logging.ref.slf4j.ONAPLogConstants.InvocationMode; +import org.onap.sdnc.apps.pomba.networkdiscovery.ApplicationException; +import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.Attribute; +import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.DataQuality; +import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.NetworkDiscoveryNotification; +import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.NetworkDiscoveryResponse; +import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.Resource; +import org.onap.sdnc.apps.pomba.networkdiscovery.service.rs.RestService; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +@Service +public class SpringServiceImpl implements SpringService { + private static final String ENRICHER_HEADER_APPLICATION = "X-FromAppId"; + private static final String ENRICHER_HEADER_TRANSACTION = "X-TransactionId"; + + private static final int DEFAULT_WORKER_THREADS = 3; + + private ExecutorService executor = Executors.newFixedThreadPool( + Integer.getInteger("discovery.threads", DEFAULT_WORKER_THREADS), + new ThreadFactoryBuilder().setNameFormat("discovery-worker-%d").build()); + + @Autowired + private RestClient enricherClient; + + @Autowired + private String enricherBaseUrl; + + @javax.annotation.Resource + private Client callbackClient; + + @javax.annotation.Resource + private Map<String, String> enricherTypeURLs; + + private DocumentBuilder parser; + + + public SpringServiceImpl() throws ParserConfigurationException { + this.parser = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + } + + @Override + public NetworkDiscoveryResponse findbyResourceIdAndType(String transactionId, + String requestId, + String resourceType, + List<String> resourceIds, + String notificationURL, + String notificationAuthorization, + ONAPLogAdapter adapter) throws ApplicationException { + + NetworkDiscoveryResponse response = new NetworkDiscoveryResponse(); + response.setRequestId(requestId); + + String enricherURL = this.enricherTypeURLs.get(resourceType); + // check if resourceType is supported + if (enricherURL == null) { + // don't know what to do with this - return empty response + response.setCode(Status.NO_CONTENT.getStatusCode()); + response.setMessage("Unsupported resourceType " + resourceType); + response.setAckFinalIndicator(true); + return response; + } + + // schedule discovery of specified resources + Runnable task = new ResourceTask(transactionId, requestId, resourceType, resourceIds, + notificationURL, notificationAuthorization, enricherURL, adapter); + this.executor.submit(task); + + response.setCode(Status.ACCEPTED.getStatusCode()); + response.setMessage(Status.ACCEPTED.getReasonPhrase()); + response.setAckFinalIndicator(false); + return response; + } + + @PreDestroy + public void shutdown() { + this.executor.shutdown(); + } + + private List<Attribute> toAttributeList(String xml) throws SAXException, IOException { + // TODO don't return raw A&AI attributes but coerce to swagger-defined enums + Document doc = this.parser.parse(new InputSource(new StringReader(xml))); + NodeList children = doc.getDocumentElement().getChildNodes(); + List<Attribute> result = new ArrayList<>(); + for (int i = 0; i < children.getLength(); i++) { + Node child = children.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + Attribute attr = new Attribute(); + attr.setName(((Element)child).getTagName()); + attr.setValue(((Element)child).getTextContent()); + attr.setDataQuality(DataQuality.ok()); + result.add(attr); + } + } + return result; + } + + private void sendNotification(String url, String authorization, Object notification, ONAPLogAdapter adapter) { + + Invocation.Builder request = this.callbackClient + .target(url) + .request() + .accept(MediaType.TEXT_PLAIN_TYPE); + + if (authorization != null) { + request.header(HttpHeaders.AUTHORIZATION, authorization); + } + Logger log = adapter.unwrap(); + adapter.invoke(new RequestBuilderWrapper(request), InvocationMode.SYNCHRONOUS); + try { + // native client marshaller doesn't skip null fields + // so manually marshal notification to json + + if (log.isDebugEnabled()) { + StringBuilder debugRequest = new StringBuilder("REQUEST:\n"); + debugRequest.append("URL: ").append(url).append("\n"); + debugRequest.append("Payload: ").append(notification).append("\n"); +// if (headers != null) { +// debugRequest.append("Headers: "); +// for (Entry<String, List<String>> header : headers.entrySet()) { +// debugRequest.append("\n\t").append(header.getKey()).append(":"); +// for (String headerEntry : header.getValue()) { +// debugRequest.append("\"").append(headerEntry).append("\" "); +// } +// } +// } + log.debug(debugRequest.toString()); + } + + Response result = request.post(Entity.json(notification)); + + adapter.unwrap().info("request at url = {} resulted in http response: {}", url, result.getStatusInfo().getStatusCode() + " " + result.getStatusInfo().getReasonPhrase()); + + if (log.isDebugEnabled()) { + StringBuilder debugResponse = new StringBuilder("RESPONSE:\n"); + debugResponse.append("Result: ").append(result.getStatus()).append("\n"); + String content = result.hasEntity() ? result.getEntity().toString() : null; + if (result.getStatus() >= 300) { + debugResponse.append("Failure Cause: ").append(content).append("\n"); + } else { + debugResponse.append("Payload: ").append(content).append("\n"); + } + if (result.getHeaders() != null) { + debugResponse.append("Headers: "); + for (Entry<String, List<Object>> header : result.getHeaders().entrySet()) { + debugResponse.append("\n\t").append(header.getKey()).append(":"); + for (Object headerEntry : header.getValue()) { + debugResponse.append("\"").append(headerEntry).append("\" "); + } + } + } + log.debug(debugResponse.toString()); + } + + } catch (Exception x) { + log.error("Error during {} operation to endpoint at url = {} with error = {}", + "POST", url, x.getLocalizedMessage()); + } + } + + private class ResourceTask implements Runnable { + private final String transactionId; + private final String requestId; + private final String resourceType; + private final List<String> resourceIds; + private final String notificationURL; + private final String notificationAuthorization; + private final String resourceURL; + private final ONAPLogAdapter adapter; + + public ResourceTask(String transactionId, + String requestId, + String resourceType, + List<String> resourceIds, + String notificationURL, + String notificationAuthorization, + String resourceURL, + ONAPLogAdapter adapter) { + this.transactionId = transactionId; + this.requestId = requestId; + this.resourceType = resourceType; + this.resourceIds = resourceIds; + this.notificationURL = notificationURL; + this.notificationAuthorization = notificationAuthorization; + this.resourceURL = resourceURL; + this.adapter = adapter; + } + + @Override + public void run() { + NetworkDiscoveryNotification notification = new NetworkDiscoveryNotification(); + notification.setRequestId(this.requestId); + + List<Resource> resources = null; + MessageFormat format = new MessageFormat(SpringServiceImpl.this.enricherBaseUrl + this.resourceURL); + MultivaluedMap<String, String> headers = new MultivaluedHashMap<>(); + headers.add(ENRICHER_HEADER_APPLICATION, RestService.SERVICE_NAME); + headers.add(ENRICHER_HEADER_TRANSACTION, this.transactionId); + for (String resourceId : this.resourceIds) { + String url = format.format(new Object[] { resourceId }); + OperationResult result = SpringServiceImpl.this.enricherClient.get(url, headers, MediaType.APPLICATION_XML_TYPE); + + Resource resource = new Resource(); + resource.setType(this.resourceType); + resource.setId(resourceId); + if (resources == null) { + resources = new ArrayList<>(); + notification.setResources(resources); + } + resources.add(resource); + + if (result.wasSuccessful()) { + resource.setDataQuality(DataQuality.ok()); + try { + resource.setAttributeList(toAttributeList(result.getResult())); + } catch (Exception x) { + resource.setDataQuality(DataQuality.error(x.getMessage())); + } + } + else { + resource.setDataQuality(DataQuality.error(result.getFailureCause())); + } + } + notification.setCode(Status.OK.getStatusCode()); + notification.setMessage(Status.OK.getReasonPhrase()); + notification.setAckFinalIndicator(true); + + // call client back with resource details + sendNotification(this.notificationURL, this.notificationAuthorization, notification, adapter); + } + } + + private static class RequestBuilderWrapper implements RequestBuilder<RequestBuilderWrapper> { + private Invocation.Builder builder; + private RequestBuilderWrapper(Invocation.Builder builder) { + this.builder = builder; + } + + @Override + public RequestBuilderWrapper setHeader(String name, String value) { + this.builder.header(name, value); + return this; + } + + } +}
\ No newline at end of file diff --git a/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/service/rs/RestService.java b/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/service/rs/RestService.java new file mode 100644 index 0000000..57fbb53 --- /dev/null +++ b/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/service/rs/RestService.java @@ -0,0 +1,103 @@ +/* + * ============LICENSE_START=================================================== + * Copyright (c) 2018 Amdocs + * ============================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END===================================================== + */ +package org.onap.sdnc.apps.pomba.networkdiscovery.service.rs; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import io.swagger.annotations.Authorization; +import java.util.List; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import org.onap.logging.ref.slf4j.ONAPLogConstants; +import org.onap.sdnc.apps.pomba.networkdiscovery.ApplicationException; +import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.NetworkDiscoveryResponse; + + +@Api(protocols="http", tags= {"resource"}) +@Path("{version: v1}/network") +@Produces(MediaType.APPLICATION_JSON) +public interface RestService { + + public static final String SERVICE_NAME = "network-discovery"; + + @GET + @Path("/resource") + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + @ApiOperation( + value = "Get Network Information", + notes = "Retrieve information from primary data sources", + response = NetworkDiscoveryResponse.class, + authorizations = @Authorization("basicAuth") + ) + @ApiResponses( + value = { + @ApiResponse(code = 200, message = "Request has completed and no more information is forthcoming."), + @ApiResponse(code = 202, message = "Request has been accepted and more information will be posted to notificationURL."), + @ApiResponse(code = 400, message = "Missing mandatory field in the request or HTTP header."), + @ApiResponse(code = 404, message = "Requested resource was not found."), + @ApiResponse(code = 500, message = "Request failed due to internal error") + }) + public Response findbyResourceIdAndType(@Context + HttpServletRequest request, + + @PathParam("version") + @ApiParam(required=true, defaultValue="v1", allowableValues="v1") + String version, + + @HeaderParam(HttpHeaders.AUTHORIZATION) + @ApiParam(hidden=true) + String authorization, + + @HeaderParam(ONAPLogConstants.Headers.PARTNER_NAME) + @ApiParam(required=true) + String xFromAppId, + + @HeaderParam(ONAPLogConstants.Headers.REQUEST_ID) + String xTransactionId, + + @QueryParam("requestId") + @ApiParam(required=true) + String requestId, + + @QueryParam("resourceType") + @ApiParam(required=true) + String resourceType, + + @QueryParam("resourceId") + @ApiParam(required=true) + List<String> resourceIds, + + @QueryParam("notificationURL") + @ApiParam(required=true) + String notificationURL) throws ApplicationException; + +}
\ No newline at end of file diff --git a/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/service/rs/RestServiceImpl.java b/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/service/rs/RestServiceImpl.java new file mode 100644 index 0000000..94c7492 --- /dev/null +++ b/pomba/network-discovery/src/main/java/org/onap/sdnc/apps/pomba/networkdiscovery/service/rs/RestServiceImpl.java @@ -0,0 +1,132 @@ +/* + * ============LICENSE_START=================================================== + * Copyright (c) 2018 Amdocs + * ============================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END===================================================== + */ +package org.onap.sdnc.apps.pomba.networkdiscovery.service.rs; + +import static org.onap.sdnc.apps.pomba.networkdiscovery.ApplicationException.Error.GENERAL_FAILURE; +import static org.onap.sdnc.apps.pomba.networkdiscovery.ApplicationException.Error.MISSING_PARAM; +import static org.onap.sdnc.apps.pomba.networkdiscovery.ApplicationException.Error.UNAUTHORIZED; + +import java.util.List; +import java.util.UUID; +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.ResponseBuilder; +import javax.ws.rs.core.Response.Status; +import org.onap.logging.ref.slf4j.ONAPLogAdapter; +import org.onap.logging.ref.slf4j.ONAPLogConstants; +import org.onap.logging.ref.slf4j.ONAPLogConstants.ResponseStatus; +import org.onap.sdnc.apps.pomba.networkdiscovery.ApplicationException; +import org.onap.sdnc.apps.pomba.networkdiscovery.datamodel.NetworkDiscoveryResponse; +import org.onap.sdnc.apps.pomba.networkdiscovery.service.SpringService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class RestServiceImpl implements RestService { + + private static Logger log = LoggerFactory.getLogger(RestService.class); + + @Autowired + private SpringService service; + + @Resource(name="basicAuthHeader") + private String basicAuthHeader; + + public RestServiceImpl() { + } + + @Override + public Response findbyResourceIdAndType(HttpServletRequest request, + String version, + String authorization, + String fromAppId, + String transactionId, + String requestId, + String resourceType, + List<String> resourceIds, + String notificationURL) throws ApplicationException { + + ONAPLogAdapter adapter = new ONAPLogAdapter(log); + adapter.getServiceDescriptor().setServiceName(SERVICE_NAME); + adapter.entering(request); + try { + + if (version == null) { + // only unit tests can pass null here + // url matching will guarantee non-null in real server + version = "v1"; + } + + if (!this.basicAuthHeader.equals(authorization)) { + throw new ApplicationException(UNAUTHORIZED, Status.UNAUTHORIZED); + } + if ((fromAppId == null) || fromAppId.trim().isEmpty()) { + throw new ApplicationException(MISSING_PARAM, Status.BAD_REQUEST, ONAPLogConstants.Headers.PARTNER_NAME); + } + if (requestId == null || requestId.isEmpty()) { + throw new ApplicationException(MISSING_PARAM, Status.BAD_REQUEST, "requestId"); + } + if (notificationURL == null) { + throw new ApplicationException(MISSING_PARAM, Status.BAD_REQUEST, "notificationURL"); + } + if (resourceType == null || resourceType.isEmpty()) { + throw new ApplicationException(MISSING_PARAM, Status.BAD_REQUEST, "resourceType"); + } + if (resourceIds == null || resourceIds.isEmpty()) { + throw new ApplicationException(MISSING_PARAM, Status.BAD_REQUEST, "resourceIds"); + } + + if (transactionId == null || transactionId.isEmpty()) { + transactionId = UUID.randomUUID().toString(); + log.debug("transactionId is missing; using newly generated value: " + transactionId); + } + + // just reuse received Authorization header in callback + NetworkDiscoveryResponse response = this.service.findbyResourceIdAndType(transactionId, + requestId, resourceType, resourceIds, notificationURL, authorization, adapter); + adapter.getResponseDescriptor().setResponseStatus(ResponseStatus.COMPLETED); + return Response.ok(response).build(); + + } catch (ApplicationException x) { + adapter.getResponseDescriptor() + .setResponseCode(x.getResponseCode()) + .setResponseStatus(ResponseStatus.ERROR); + log.error(x.getMessage(), x); + ResponseBuilder builder = Response.status(x.getHttpStatus()).entity(x.getMessage()); + if (authorization == null) { + builder.header(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"network-discovery\""); + } + return builder.build(); + + } catch (Exception x) { + adapter.getResponseDescriptor() + .setResponseCode(GENERAL_FAILURE.getResponseCode()) + .setResponseStatus(ResponseStatus.ERROR); + log.error(GENERAL_FAILURE.getMessage(x), x); + return Response.status(Status.INTERNAL_SERVER_ERROR).entity(x.getMessage()).build(); + } finally { + adapter.exiting(); + } + + } + +} |