2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
6 * Copyright © 2017 Amdocs
7 * ================================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 * ============LICENSE_END=========================================================
21 * ECOMP is a trademark and service mark of AT&T Intellectual Property.
23 package org.onap.aai.sparky.dal;
25 import java.io.IOException;
26 import java.net.URISyntaxException;
27 import java.net.URLEncoder;
28 import java.util.ArrayList;
29 import java.util.HashMap;
30 import java.util.List;
32 import java.util.NoSuchElementException;
34 import javax.ws.rs.core.MediaType;
35 import javax.ws.rs.core.UriBuilder;
37 import org.apache.http.NameValuePair;
38 import org.apache.http.client.utils.URIBuilder;
39 import org.onap.aai.cl.api.Logger;
40 import org.onap.aai.cl.eelf.LoggerFactory;
41 import org.onap.aai.restclient.client.OperationResult;
42 import org.onap.aai.restclient.client.RestClient;
43 import org.onap.aai.restclient.enums.RestAuthenticationMode;
44 import org.onap.aai.sparky.config.oxm.OxmEntityDescriptor;
45 import org.onap.aai.sparky.config.oxm.OxmEntityLookup;
46 import org.onap.aai.sparky.config.oxm.OxmModelLoader;
47 import org.onap.aai.sparky.dal.exception.ElasticSearchOperationException;
48 import org.onap.aai.sparky.logging.AaiUiMsgs;
49 import org.onap.aai.sparky.util.Encryptor;
50 import org.onap.aai.sparky.util.NodeUtils;
51 import org.onap.aai.sparky.viewandinspect.config.TierSupportUiConstants;
56 * The Class ActiveInventoryAdapter.
59 public class ActiveInventoryAdapter {
61 private static final Logger LOG =
62 LoggerFactory.getInstance().getLogger(ActiveInventoryAdapter.class);
64 private static final String HEADER_TRANS_ID = "X-TransactionId";
65 private static final String HEADER_FROM_APP_ID = "X-FromAppId";
66 private static final String HEADER_AUTHORIZATION = "Authorization";
68 private static final String HTTP_SCHEME = "http";
69 private static final String HTTPS_SCHEME = "https";
71 private static final String TRANSACTION_ID_PREFIX = "txnId-";
72 private static final String UI_APP_NAME = "AAI-UI";
74 private OxmModelLoader oxmModelLoader;
75 private OxmEntityLookup oxmEntityLookup;
77 private RestClient restClient;
79 private String activeInventoryIpAddress;
80 private String activeInventoryServerPort;
81 private int numRequestRetries;
82 private String basicAuthUserName;
83 private String basicAuthPassword;
84 private RestAuthenticationMode restAuthenticationMode;
85 private int connectTimeoutInMs;
86 private int readTimeoutInMs;
89 * Instantiates a new active inventory adapter.
93 public ActiveInventoryAdapter(OxmModelLoader oxmModelLoader,
94 RestAuthenticationMode authenticationMode, boolean validateServerHostname,
95 boolean validateServerCertChain, String certFileName, String certPassword,
96 String truststoreFileName, int connectTimeoutInMs, int readTimeoutInMs)
97 throws ElasticSearchOperationException, IOException {
99 this.oxmModelLoader = oxmModelLoader;
100 this.restAuthenticationMode = authenticationMode;
101 this.connectTimeoutInMs = connectTimeoutInMs;
102 this.readTimeoutInMs = readTimeoutInMs;
105 Encryptor enc = new Encryptor();
106 String certFileNameFullPath = TierSupportUiConstants.CONFIG_AUTH_LOCATION + certFileName;
107 String decryptedCertPassword = enc.decryptValue(certPassword);
108 String truststoreFileNameFullPath =
109 TierSupportUiConstants.CONFIG_AUTH_LOCATION + truststoreFileName;
111 this.restClient = new RestClient().authenticationMode(authenticationMode)
112 .validateServerCertChain(validateServerCertChain)
113 .validateServerHostname(validateServerHostname).clientCertFile(certFileNameFullPath)
114 .clientCertPassword(decryptedCertPassword).trustStore(truststoreFileNameFullPath)
115 .connectTimeoutMs(connectTimeoutInMs).readTimeoutMs(readTimeoutInMs);
119 public ActiveInventoryAdapter(OxmModelLoader oxmModelLoader,
120 RestAuthenticationMode authenticationMode, boolean validateServerHostname,
121 boolean validateServerCertChain, String basicAuthUserName, String basicAuthPassword,
122 int connectTimeoutInMs, int readTimeoutInMs)
123 throws ElasticSearchOperationException, IOException {
125 this.oxmModelLoader = oxmModelLoader;
126 this.restAuthenticationMode = authenticationMode;
128 this.restClient = new RestClient().authenticationMode(authenticationMode)
129 .validateServerCertChain(validateServerCertChain)
130 .validateServerHostname(validateServerHostname).connectTimeoutMs(connectTimeoutInMs)
131 .readTimeoutMs(readTimeoutInMs);
133 this.basicAuthUserName = basicAuthUserName;
134 this.basicAuthPassword = basicAuthPassword;
139 protected Map<String, List<String>> getMessageHeaders() {
141 Map<String, List<String>> headers = new HashMap<String, List<String>>();
143 headers.putIfAbsent(HEADER_FROM_APP_ID, new ArrayList<String>());
144 headers.get(HEADER_FROM_APP_ID).add(UI_APP_NAME);
146 headers.putIfAbsent(HEADER_TRANS_ID, new ArrayList<String>());
147 headers.get(HEADER_TRANS_ID).add(TRANSACTION_ID_PREFIX + NodeUtils.getRandomTxnId());
149 if (restAuthenticationMode == RestAuthenticationMode.SSL_BASIC) {
151 headers.putIfAbsent(HEADER_AUTHORIZATION, new ArrayList<String>());
152 headers.get(HEADER_AUTHORIZATION).add(getBasicAuthenticationCredentials());
159 protected String getBasicAuthenticationCredentials() {
160 String usernameAndPassword = String.join(":", basicAuthUserName, basicAuthPassword);
161 return "Basic " + java.util.Base64.getEncoder().encodeToString(usernameAndPassword.getBytes());
164 public int getNumRequestRetries() {
165 return numRequestRetries;
170 public void setNumRequestRetries(int numRequestRetries) {
171 this.numRequestRetries = numRequestRetries;
174 public OxmEntityLookup getOxmEntityLookup() {
175 return oxmEntityLookup;
178 public void setOxmEntityLookup(OxmEntityLookup oxmEntityLookup) {
179 this.oxmEntityLookup = oxmEntityLookup;
182 public String getActiveInventoryIpAddress() {
183 return activeInventoryIpAddress;
186 public void setActiveInventoryIpAddress(String activeInventoryIpAddress) {
187 this.activeInventoryIpAddress = activeInventoryIpAddress;
190 public String getActiveInventoryServerPort() {
191 return activeInventoryServerPort;
194 public void setActiveInventoryServerPort(String activeInventoryServerPort) {
195 this.activeInventoryServerPort = activeInventoryServerPort;
198 protected String getResourceBasePath() {
200 String versionStr = null;
201 if (oxmModelLoader != null) {
202 versionStr = String.valueOf(oxmModelLoader.getLatestVersionNum());
205 return "/aai/v" + versionStr;
209 public int getConnectTimeoutInMs() {
210 return this.connectTimeoutInMs;
213 public int getReadTimeoutInMs() {
214 return this.readTimeoutInMs;
220 * @param resourceUrl the resource url
221 * @return the full url
222 * @throws Exception the exception
224 private String getFullUrl(String resourceUrl) throws Exception {
225 final String basePath = getResourceBasePath();
226 return String.format("https://%s:%s%s%s", activeInventoryIpAddress, activeInventoryServerPort,
227 basePath, resourceUrl);
230 public String getGenericQueryForSelfLink(String startNodeType, List<String> queryParams)
233 URIBuilder urlBuilder = new URIBuilder(getFullUrl("/search/generic-query"));
235 for (String queryParam : queryParams) {
236 urlBuilder.addParameter("key", queryParam);
239 urlBuilder.addParameter("start-node-type", startNodeType);
240 urlBuilder.addParameter("include", startNodeType);
242 final String constructedLink = urlBuilder.toString();
244 return constructedLink;
249 public OperationResult getSelfLinksByEntityType(String entityType) throws Exception {
252 * For this one, I want to dynamically construct the nodes-query for self-link discovery as a
253 * utility method that will use the OXM model entity data to drive the query as well.
256 if (entityType == null) {
257 throw new NullPointerException(
258 "Failed to getSelfLinksByEntityType() because entityType is null");
261 OxmEntityDescriptor entityDescriptor = oxmEntityLookup.getEntityDescriptors().get(entityType);
263 if (entityDescriptor == null) {
264 throw new NoSuchElementException("Failed to getSelfLinksByEntityType() because could"
265 + " not find entity descriptor from OXM with type = " + entityType);
269 final String primaryKeyStr =
270 NodeUtils.concatArray(entityDescriptor.getPrimaryKeyAttributeNames(), "/");
272 link = getFullUrl("/search/nodes-query?search-node-type=" + entityType + "&filter="
273 + primaryKeyStr + ":EXISTS");
276 return restClient.get(link, getMessageHeaders(), MediaType.APPLICATION_JSON_TYPE);
280 public OperationResult getSelfLinkForEntity(String entityType, String primaryKeyName,
281 String primaryKeyValue) throws Exception {
283 if (entityType == null) {
284 throw new NullPointerException("Failed to getSelfLinkForEntity() because entityType is null");
287 if (primaryKeyName == null) {
288 throw new NullPointerException(
289 "Failed to getSelfLinkForEntity() because primaryKeyName is null");
292 if (primaryKeyValue == null) {
293 throw new NullPointerException(
294 "Failed to getSelfLinkForEntity() because primaryKeyValue is null");
298 * Try to protect ourselves from illegal URI formatting exceptions caused by characters that
299 * aren't natively supported in a URI, but can be escaped to make them legal.
302 String encodedEntityType = URLEncoder.encode(entityType, "UTF-8");
303 String encodedPrimaryKeyName = URLEncoder.encode(primaryKeyName, "UTF-8");
304 String encodedPrimaryKeyValue = URLEncoder.encode(primaryKeyValue, "UTF-8");
308 if ("service-instance".equals(entityType)) {
310 link = getFullUrl("/search/generic-query?key=" + encodedEntityType + "."
311 + encodedPrimaryKeyName + ":" + encodedPrimaryKeyValue + "&start-node-type="
312 + encodedEntityType + "&include=customer&depth=2");
317 getFullUrl("/search/generic-query?key=" + encodedEntityType + "." + encodedPrimaryKeyName
318 + ":" + encodedPrimaryKeyValue + "&start-node-type=" + encodedEntityType);
322 return queryActiveInventoryWithRetries(link, "application/json", numRequestRetries);
328 * Our retry conditions should be very specific.
331 * @return true, if successful
333 private boolean shouldRetryRequest(OperationResult r) {
339 int rc = r.getResultCode();
354 * Query active inventory.
357 * @param acceptContentType the accept content type
358 * @return the operation result
360 // package protected for test classes instead of private
361 OperationResult queryActiveInventory(String url, String acceptContentType) {
363 return restClient.get(url, getMessageHeaders(), MediaType.APPLICATION_JSON_TYPE);
367 public OperationResult queryActiveInventoryWithRetries(String url, String responseType,
370 OperationResult result = null;
372 for (int retryCount = 0; retryCount < numRetries; retryCount++) {
374 LOG.debug(AaiUiMsgs.QUERY_AAI_RETRY_SEQ, url, String.valueOf(retryCount + 1));
376 result = queryActiveInventory(url, responseType);
379 * Record number of times we have attempted the request to later summarize how many times we
380 * are generally retrying over thousands of messages in a sync.
382 * If the number of retries is surprisingly high, then we need to understand why that is as
383 * the number of retries is also causing a heavier load on AAI beyond the throttling controls
384 * we already have in place in term of the transaction rate controller and number of
385 * parallelized threads per task processor.
388 result.setNumRetries(retryCount);
390 if (!shouldRetryRequest(result)) {
392 result.setFromCache(false);
393 LOG.debug(AaiUiMsgs.QUERY_AAI_RETRY_DONE_SEQ, url, String.valueOf(retryCount + 1));
400 * Sleep between re-tries to be nice to the target system.
403 } catch (InterruptedException exc) {
404 LOG.error(AaiUiMsgs.QUERY_AAI_WAIT_INTERRUPTION, exc.getLocalizedMessage());
407 LOG.error(AaiUiMsgs.QUERY_AAI_RETRY_FAILURE_WITH_SEQ, url, String.valueOf(retryCount + 1));
411 LOG.info(AaiUiMsgs.QUERY_AAI_RETRY_MAXED_OUT, url);
417 public String repairSelfLink(String selfLink) {
418 return repairSelfLink(selfLink, null);
422 * This method adds a scheme, host and port (if missing) to the passed-in URI. If these parts of
423 * the URI are already present, they will not be duplicated.
425 * @param selflink The URI to repair
426 * @param queryParams The query parameters as a single string
427 * @return The corrected URI (i.e. includes a scheme/host/port)
429 public String repairSelfLink(String selflink, String queryParams) {
430 if (selflink == null) {
434 UriBuilder builder = UriBuilder.fromPath(selflink).host(activeInventoryIpAddress)
435 .port(Integer.parseInt(activeInventoryServerPort));
437 switch (restAuthenticationMode) {
441 builder.scheme(HTTPS_SCHEME);
446 builder.scheme(HTTP_SCHEME);
450 boolean includeQueryParams = ((null != queryParams) && (!"".equals(queryParams)));
453 * builder.build().toString() will encode special characters to hexadecimal pairs prefixed with
454 * a '%' so we're adding the query parameters separately, in their UTF-8 representations, so
455 * that characters such as '?', '&', etc. remain intact as needed by the synchronizer
457 return (builder.build().toString() + (includeQueryParams ? queryParams : ""));