2 * ============LICENSE_START===================================================
3 * SPARKY (AAI UI service)
4 * ============================================================================
5 * Copyright © 2017 AT&T Intellectual Property.
6 * Copyright © 2017 Amdocs
8 * ============================================================================
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 * ============LICENSE_END=====================================================
22 * ECOMP and OpenECOMP are trademarks
23 * and service marks of AT&T Intellectual Property.
25 package org.onap.aai.sparky.dal;
27 import java.io.IOException;
29 import java.net.URISyntaxException;
30 import java.net.URLEncoder;
31 import java.util.ArrayList;
32 import java.util.HashMap;
33 import java.util.List;
35 import java.util.NoSuchElementException;
37 import javax.ws.rs.core.MediaType;
38 import javax.ws.rs.core.UriBuilder;
40 import org.apache.http.client.utils.URIBuilder;
41 import org.onap.aai.cl.api.Logger;
42 import org.onap.aai.cl.eelf.LoggerFactory;
43 import org.onap.aai.restclient.client.OperationResult;
44 import org.onap.aai.restclient.client.RestClient;
45 import org.onap.aai.restclient.enums.RestAuthenticationMode;
46 import org.onap.aai.sparky.config.oxm.OxmEntityDescriptor;
47 import org.onap.aai.sparky.config.oxm.OxmEntityLookup;
48 import org.onap.aai.sparky.config.oxm.OxmModelLoader;
49 import org.onap.aai.sparky.dal.exception.ElasticSearchOperationException;
50 import org.onap.aai.sparky.dal.rest.RestClientConstructionException;
51 import org.onap.aai.sparky.dal.rest.RestClientFactory;
52 import org.onap.aai.sparky.dal.rest.config.RestEndpointConfig;
53 import org.onap.aai.sparky.logging.AaiUiMsgs;
54 import org.onap.aai.sparky.util.NodeUtils;
57 * The Class ActiveInventoryAdapter.
60 public class ActiveInventoryAdapter {
62 private static final Logger LOG =
63 LoggerFactory.getInstance().getLogger(ActiveInventoryAdapter.class);
65 private static final String HEADER_TRANS_ID = "X-TransactionId";
66 private static final String HEADER_FROM_APP_ID = "X-FromAppId";
67 private static final String HEADER_AUTHORIZATION = "Authorization";
69 private static final String HTTP_SCHEME = "http";
70 private static final String HTTPS_SCHEME = "https";
72 private static final String TRANSACTION_ID_PREFIX = "txnId-";
73 private static final String UI_APP_NAME = "AAI-UI";
75 private OxmModelLoader oxmModelLoader;
76 private OxmEntityLookup oxmEntityLookup;
77 private RestEndpointConfig endpointConfig;
79 private RestClient restClient;
82 * Instantiates a new active inventory adapter.
83 * @throws RestClientConstructionException
87 public ActiveInventoryAdapter(OxmModelLoader oxmModelLoader, OxmEntityLookup oxmEntityLookup,
88 RestEndpointConfig endpointConfig)
89 throws ElasticSearchOperationException, IOException, RestClientConstructionException {
91 this.oxmModelLoader = oxmModelLoader;
92 this.oxmEntityLookup = oxmEntityLookup;
93 this.endpointConfig = endpointConfig;
94 this.restClient = RestClientFactory.buildClient(endpointConfig);
98 protected Map<String, List<String>> getMessageHeaders() {
100 Map<String, List<String>> headers = new HashMap<String, List<String>>();
102 headers.putIfAbsent(HEADER_FROM_APP_ID, new ArrayList<String>());
103 headers.get(HEADER_FROM_APP_ID).add(UI_APP_NAME);
105 headers.putIfAbsent(HEADER_TRANS_ID, new ArrayList<String>());
106 headers.get(HEADER_TRANS_ID).add(TRANSACTION_ID_PREFIX + NodeUtils.getRandomTxnId());
108 if (endpointConfig.getRestAuthenticationMode() == RestAuthenticationMode.SSL_BASIC) {
110 headers.putIfAbsent(HEADER_AUTHORIZATION, new ArrayList<String>());
111 headers.get(HEADER_AUTHORIZATION).add(getBasicAuthenticationCredentials());
118 protected String getBasicAuthenticationCredentials() {
119 String usernameAndPassword = String.join(":", endpointConfig.getBasicAuthUserName(),
120 endpointConfig.getBasicAuthPassword());
121 return "Basic " + java.util.Base64.getEncoder().encodeToString(usernameAndPassword.getBytes());
124 public OxmEntityLookup getOxmEntityLookup() {
125 return oxmEntityLookup;
128 public void setOxmEntityLookup(OxmEntityLookup oxmEntityLookup) {
129 this.oxmEntityLookup = oxmEntityLookup;
132 protected String getResourceBasePath() {
134 String versionStr = null;
135 if (oxmModelLoader != null) {
136 versionStr = String.valueOf(oxmModelLoader.getLatestVersionNum());
139 return "/aai/v" + versionStr;
143 public static String extractResourcePath(String selflink) {
145 return new URI(selflink).getRawPath();
146 } catch (URISyntaxException uriSyntaxException) {
147 LOG.error(AaiUiMsgs.ERROR_EXTRACTING_RESOURCE_PATH_FROM_LINK,
148 uriSyntaxException.getMessage());
157 * @param resourceUrl the resource url
158 * @return the full url
159 * @throws Exception the exception
161 private String getFullUrl(String resourceUrl) throws Exception {
162 final String basePath = getResourceBasePath();
163 return String.format("https://%s:%s%s%s", endpointConfig.getEndpointIpAddress(),
164 endpointConfig.getEndpointServerPort(), basePath, resourceUrl);
167 public String getGenericQueryForSelfLink(String startNodeType, List<String> queryParams)
170 URIBuilder urlBuilder = new URIBuilder(getFullUrl("/search/generic-query"));
172 for (String queryParam : queryParams) {
173 urlBuilder.addParameter("key", queryParam);
176 urlBuilder.addParameter("start-node-type", startNodeType);
177 urlBuilder.addParameter("include", startNodeType);
179 final String constructedLink = urlBuilder.toString();
181 return constructedLink;
186 public OperationResult getSelfLinksByEntityType(String entityType) throws Exception {
189 * For this one, I want to dynamically construct the nodes-query for self-link discovery as a
190 * utility method that will use the OXM model entity data to drive the query as well.
193 if (entityType == null) {
194 throw new NullPointerException(
195 "Failed to getSelfLinksByEntityType() because entityType is null");
198 OxmEntityDescriptor entityDescriptor = oxmEntityLookup.getEntityDescriptors().get(entityType);
200 if (entityDescriptor == null) {
201 throw new NoSuchElementException("Failed to getSelfLinksByEntityType() because could"
202 + " not find entity descriptor from OXM with type = " + entityType);
206 final String primaryKeyStr =
207 NodeUtils.concatArray(entityDescriptor.getPrimaryKeyAttributeNames(), "/");
209 link = getFullUrl("/search/nodes-query?search-node-type=" + entityType + "&filter="
210 + primaryKeyStr + ":EXISTS");
213 return restClient.get(link, getMessageHeaders(), MediaType.APPLICATION_JSON_TYPE);
217 public OperationResult getSelfLinkForEntity(String entityType, String primaryKeyName,
218 String primaryKeyValue) throws Exception {
220 if (entityType == null) {
221 throw new NullPointerException("Failed to getSelfLinkForEntity() because entityType is null");
224 if (primaryKeyName == null) {
225 throw new NullPointerException(
226 "Failed to getSelfLinkForEntity() because primaryKeyName is null");
229 if (primaryKeyValue == null) {
230 throw new NullPointerException(
231 "Failed to getSelfLinkForEntity() because primaryKeyValue is null");
235 * Try to protect ourselves from illegal URI formatting exceptions caused by characters that
236 * aren't natively supported in a URI, but can be escaped to make them legal.
239 String encodedEntityType = URLEncoder.encode(entityType, "UTF-8");
240 String encodedPrimaryKeyName = URLEncoder.encode(primaryKeyName, "UTF-8");
241 String encodedPrimaryKeyValue = URLEncoder.encode(primaryKeyValue, "UTF-8");
245 if ("service-instance".equals(entityType)) {
247 link = getFullUrl("/search/generic-query?key=" + encodedEntityType + "."
248 + encodedPrimaryKeyName + ":" + encodedPrimaryKeyValue + "&start-node-type="
249 + encodedEntityType + "&include=customer&depth=2");
254 getFullUrl("/search/generic-query?key=" + encodedEntityType + "." + encodedPrimaryKeyName
255 + ":" + encodedPrimaryKeyValue + "&start-node-type=" + encodedEntityType);
259 return queryActiveInventoryWithRetries(link, "application/json",
260 endpointConfig.getNumRequestRetries());
266 * Our retry conditions should be very specific.
269 * @return true, if successful
271 private boolean shouldRetryRequest(OperationResult r) {
277 int rc = r.getResultCode();
292 * Query active inventory.
295 * @param acceptContentType the accept content type
296 * @return the operation result
298 // package protected for test classes instead of private
299 OperationResult queryActiveInventory(String url, String acceptContentType) {
301 return restClient.get(url, getMessageHeaders(), MediaType.APPLICATION_JSON_TYPE);
305 public RestEndpointConfig getEndpointConfig() {
306 return endpointConfig;
309 public void setEndpointConfig(RestEndpointConfig endpointConfig) {
310 this.endpointConfig = endpointConfig;
313 public OperationResult queryActiveInventoryWithRetries(String url, String responseType,
316 OperationResult result = null;
318 for (int retryCount = 0; retryCount < numRetries; retryCount++) {
320 LOG.debug(AaiUiMsgs.QUERY_AAI_RETRY_SEQ, url, String.valueOf(retryCount + 1));
322 result = queryActiveInventory(url, responseType);
325 * Record number of times we have attempted the request to later summarize how many times we
326 * are generally retrying over thousands of messages in a sync.
328 * If the number of retries is surprisingly high, then we need to understand why that is as
329 * the number of retries is also causing a heavier load on AAI beyond the throttling controls
330 * we already have in place in term of the transaction rate controller and number of
331 * parallelized threads per task processor.
334 result.setNumRetries(retryCount);
336 if (!shouldRetryRequest(result)) {
338 result.setFromCache(false);
339 LOG.debug(AaiUiMsgs.QUERY_AAI_RETRY_DONE_SEQ, url, String.valueOf(retryCount + 1));
346 * Sleep between re-tries to be nice to the target system.
349 } catch (InterruptedException exc) {
350 LOG.error(AaiUiMsgs.QUERY_AAI_WAIT_INTERRUPTION, exc.getLocalizedMessage());
353 LOG.error(AaiUiMsgs.QUERY_AAI_RETRY_FAILURE_WITH_SEQ, url, String.valueOf(retryCount + 1));
357 LOG.info(AaiUiMsgs.QUERY_AAI_RETRY_MAXED_OUT, url);
363 public String repairSelfLink(String selfLink) {
364 return repairSelfLink(selfLink, null);
368 * This method adds a scheme, host and port (if missing) to the passed-in URI.
369 * If these parts of the URI are already present, they will not be duplicated.
371 * @param selflink The URI to repair
372 * @param queryParams The query parameters as a single string
373 * @return The corrected URI (i.e. includes a scheme/host/port)
375 public String repairSelfLink(String selflink, String queryParams) {
376 if (selflink == null) {
380 UriBuilder builder = UriBuilder.fromPath(selflink).host(endpointConfig.getEndpointIpAddress())
381 .port(Integer.parseInt(endpointConfig.getEndpointServerPort()));
383 switch (endpointConfig.getRestAuthenticationMode()) {
387 builder.scheme(HTTPS_SCHEME);
392 builder.scheme(HTTP_SCHEME);
396 boolean includeQueryParams = ( (null != queryParams) && (!"".equals(queryParams)) );
398 /* builder.build().toString() will encode special characters to hexadecimal pairs prefixed with a '%'
399 so we're adding the query parameters separately, in their UTF-8 representations, so that
400 characters such as '?', '&', etc. remain intact as needed by the synchronizer */
401 return (builder.build().toString() + (includeQueryParams ? queryParams : ""));