fix constructor ref to a non existing bean
[aai/sparky-be.git] / sparkybe-onap-service / src / main / java / org / onap / aai / sparky / dal / ActiveInventoryAdapter.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * Copyright © 2017-2018 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
11  *
12  *       http://www.apache.org/licenses/LICENSE-2.0
13  *
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=========================================================
20  */
21 package org.onap.aai.sparky.dal;
22
23 import java.io.IOException;
24 import java.net.URI;
25 import java.net.URISyntaxException;
26 import java.net.URLEncoder;
27 import java.util.ArrayList;
28 import java.util.HashMap;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.NoSuchElementException;
32
33 import javax.ws.rs.core.MediaType;
34 import javax.ws.rs.core.UriBuilder;
35
36 import org.apache.http.client.utils.URIBuilder;
37 import org.onap.aai.cl.api.Logger;
38 import org.onap.aai.cl.eelf.LoggerFactory;
39 import org.onap.aai.restclient.client.OperationResult;
40 import org.onap.aai.restclient.client.RestClient;
41 import org.onap.aai.restclient.enums.RestAuthenticationMode;
42 import org.onap.aai.sparky.config.oxm.OxmEntityDescriptor;
43 import org.onap.aai.sparky.config.oxm.OxmEntityLookup;
44 import org.onap.aai.sparky.config.oxm.OxmModelLoader;
45 import org.onap.aai.sparky.dal.exception.ElasticSearchOperationException;
46 import org.onap.aai.sparky.dal.rest.RestClientConstructionException;
47 import org.onap.aai.sparky.dal.rest.RestClientFactory;
48 import org.onap.aai.sparky.dal.rest.config.RestEndpointConfig;
49 import org.onap.aai.sparky.logging.AaiUiMsgs;
50 import org.onap.aai.sparky.util.Encryptor;
51 import org.onap.aai.sparky.util.NodeUtils;
52 import org.onap.aai.sparky.viewandinspect.config.SparkyConstants;
53
54 /**
55  * The Class ActiveInventoryAdapter.
56  */
57
58 public class ActiveInventoryAdapter {
59
60   private static final Logger LOG =
61       LoggerFactory.getInstance().getLogger(ActiveInventoryAdapter.class);
62
63   private static final String HEADER_TRANS_ID = "X-TransactionId";
64   private static final String HEADER_FROM_APP_ID = "X-FromAppId";
65   private static final String HEADER_AUTHORIZATION = "Authorization";
66
67   private static final String HTTP_SCHEME = "http";
68   private static final String HTTPS_SCHEME = "https";
69   
70   private static final String TRANSACTION_ID_PREFIX = "txnId-";
71   private static final String UI_APP_NAME = "AAI-UI";
72
73   private OxmModelLoader oxmModelLoader;
74   private OxmEntityLookup oxmEntityLookup;
75   private RestEndpointConfig endpointConfig; 
76
77   private RestClient restClient;
78
79   /**
80    * Instantiates a new active inventory adapter.
81    * @throws RestClientConstructionException 
82    *
83    */
84
85   public ActiveInventoryAdapter(OxmModelLoader oxmModelLoader, OxmEntityLookup oxmEntityLookup,
86       RestEndpointConfig endpointConfig)
87       throws ElasticSearchOperationException, IOException, RestClientConstructionException {
88
89     this.oxmModelLoader = oxmModelLoader;
90     this.oxmEntityLookup = oxmEntityLookup;
91     this.endpointConfig = endpointConfig;
92     
93     /*
94      * Add support for de-obfuscating basic auth password (if obfuscated)
95      */
96
97     if (endpointConfig.getRestAuthenticationMode() == RestAuthenticationMode.SSL_BASIC) {
98       String basicAuthPassword = endpointConfig.getBasicAuthPassword();
99
100       if (basicAuthPassword != null
101           && basicAuthPassword.startsWith(SparkyConstants.OBFUSCATION_PREFIX)) {
102         Encryptor enc = new Encryptor();
103         endpointConfig.setBasicAuthPassword(enc.decryptValue(basicAuthPassword));
104       }
105     }
106
107     this.restClient = RestClientFactory.buildClient(endpointConfig);
108
109   }
110
111   protected Map<String, List<String>> getMessageHeaders() {
112
113     Map<String, List<String>> headers = new HashMap<String, List<String>>();
114
115     headers.putIfAbsent(HEADER_FROM_APP_ID, new ArrayList<String>());
116     headers.get(HEADER_FROM_APP_ID).add(UI_APP_NAME);
117
118     headers.putIfAbsent(HEADER_TRANS_ID, new ArrayList<String>());
119     headers.get(HEADER_TRANS_ID).add(TRANSACTION_ID_PREFIX + NodeUtils.getRandomTxnId());
120
121     if (endpointConfig.getRestAuthenticationMode() == RestAuthenticationMode.SSL_BASIC) {
122       headers.putIfAbsent(HEADER_AUTHORIZATION, new ArrayList<String>());
123       headers.get(HEADER_AUTHORIZATION).add(getBasicAuthenticationCredentials());
124     }
125
126     return headers;
127   }
128
129   protected String getBasicAuthenticationCredentials() {
130
131     String usernameAndPassword = String.join(":", endpointConfig.getBasicAuthUserName(),
132         endpointConfig.getBasicAuthPassword());
133     return "Basic " + java.util.Base64.getEncoder().encodeToString(usernameAndPassword.getBytes());
134   }
135
136   public OxmEntityLookup getOxmEntityLookup() {
137     return oxmEntityLookup;
138   }
139
140   public void setOxmEntityLookup(OxmEntityLookup oxmEntityLookup) {
141     this.oxmEntityLookup = oxmEntityLookup;
142   }
143
144   protected String getResourceBasePath() {
145
146     String versionStr;
147     if (oxmModelLoader != null) {
148       versionStr = String.valueOf(oxmModelLoader.getLatestVersionNum());
149     } else {
150       throw new RuntimeException("Unable to resolve aai version.");
151     }
152
153     return "/aai/" + versionStr.toLowerCase();
154
155   }
156   
157   public static String extractResourcePath(String selflink) {
158     try {
159       return new URI(selflink).getRawPath();
160     } catch (URISyntaxException uriSyntaxException) {
161       LOG.error(AaiUiMsgs.ERROR_EXTRACTING_RESOURCE_PATH_FROM_LINK,
162           uriSyntaxException.getMessage());
163       return selflink;
164     }
165   }
166
167   
168   /**
169    * Gets the full url.
170    *
171    * @param resourceUrl the resource url
172    * @return the full url
173    * @throws Exception the exception
174    */
175   private String getFullUrl(String resourceUrl) throws Exception {
176     final String basePath = getResourceBasePath();
177     return String.format("https://%s:%s%s%s", endpointConfig.getEndpointIpAddress(),
178         endpointConfig.getEndpointServerPort(), basePath, resourceUrl);
179   }
180
181   public String getGenericQueryForSelfLink(String startNodeType, List<String> queryParams)
182       throws Exception {
183
184     URIBuilder urlBuilder = new URIBuilder(getFullUrl("/search/generic-query"));
185
186     for (String queryParam : queryParams) {
187       urlBuilder.addParameter("key", queryParam);
188     }
189
190     urlBuilder.addParameter("start-node-type", startNodeType);
191     urlBuilder.addParameter("include", startNodeType);
192
193     final String constructedLink = urlBuilder.toString();
194
195     return constructedLink;
196
197   }
198
199
200   public OperationResult getSelfLinksByEntityType(String entityType) throws Exception {
201
202     /*
203      * For this one, I want to dynamically construct the nodes-query for self-link discovery as a
204      * utility method that will use the OXM model entity data to drive the query as well.
205      */
206
207     if (entityType == null) {
208       throw new NullPointerException(
209           "Failed to getSelfLinksByEntityType() because entityType is null");
210     }
211
212     OxmEntityDescriptor entityDescriptor = oxmEntityLookup.getEntityDescriptors().get(entityType);
213
214     if (entityDescriptor == null) {
215       throw new NoSuchElementException("Failed to getSelfLinksByEntityType() because could"
216           + " not find entity descriptor from OXM with type = " + entityType);
217     }
218
219     String link = null;
220     final String primaryKeyStr =
221         NodeUtils.concatArray(entityDescriptor.getPrimaryKeyAttributeNames(), "/");
222
223     link = getFullUrl("/search/nodes-query?search-node-type=" + entityType + "&filter="
224         + primaryKeyStr + ":EXISTS");
225
226
227     return restClient.get(link, getMessageHeaders(), MediaType.APPLICATION_JSON_TYPE);
228
229   }
230
231   public OperationResult getSelfLinkForEntity(String entityType, String primaryKeyName,
232       String primaryKeyValue) throws Exception {
233
234     if (entityType == null) {
235       throw new NullPointerException("Failed to getSelfLinkForEntity() because entityType is null");
236     }
237
238     if (primaryKeyName == null) {
239       throw new NullPointerException(
240           "Failed to getSelfLinkForEntity() because primaryKeyName is null");
241     }
242
243     if (primaryKeyValue == null) {
244       throw new NullPointerException(
245           "Failed to getSelfLinkForEntity() because primaryKeyValue is null");
246     }
247
248     /*
249      * Try to protect ourselves from illegal URI formatting exceptions caused by characters that
250      * aren't natively supported in a URI, but can be escaped to make them legal.
251      */
252
253     String encodedEntityType = URLEncoder.encode(entityType, "UTF-8");
254     String encodedPrimaryKeyName = URLEncoder.encode(primaryKeyName, "UTF-8");
255     String encodedPrimaryKeyValue = URLEncoder.encode(primaryKeyValue, "UTF-8");
256
257     String link = null;
258
259     if ("service-instance".equals(entityType)) {
260
261       link = getFullUrl("/search/generic-query?key=" + encodedEntityType + "."
262           + encodedPrimaryKeyName + ":" + encodedPrimaryKeyValue + "&start-node-type="
263           + encodedEntityType + "&include=customer&depth=2");
264
265     } else {
266
267       link =
268           getFullUrl("/search/generic-query?key=" + encodedEntityType + "." + encodedPrimaryKeyName
269               + ":" + encodedPrimaryKeyValue + "&start-node-type=" + encodedEntityType);
270
271     }
272
273     return queryActiveInventoryWithRetries(link, "application/json",
274         endpointConfig.getNumRequestRetries());
275
276   }
277
278
279   /**
280    * Our retry conditions should be very specific.
281    *
282    * @param r the r
283    * @return true, if successful
284    */
285   private boolean shouldRetryRequest(OperationResult r) {
286
287     if (r == null) {
288       return true;
289     }
290
291     int rc = r.getResultCode();
292
293     if (rc == 200) {
294       return false;
295     }
296
297     if (rc == 404) {
298       return false;
299     }
300
301     return true;
302
303   }
304
305   /**
306    * Query active inventory.
307    *
308    * @param url the url
309    * @param acceptContentType the accept content type
310    * @return the operation result
311    */
312   // package protected for test classes instead of private
313   OperationResult queryActiveInventory(String url, String acceptContentType) {
314
315     return restClient.get(url, getMessageHeaders(), MediaType.APPLICATION_JSON_TYPE);
316
317   }
318
319   public RestEndpointConfig getEndpointConfig() {
320     return endpointConfig;
321   }
322
323   public void setEndpointConfig(RestEndpointConfig endpointConfig) {
324     this.endpointConfig = endpointConfig;
325   }
326
327   public OperationResult queryActiveInventoryWithRetries(String url, String responseType,
328       int numRetries) {
329
330     OperationResult result = null;
331
332     for (int retryCount = 0; retryCount < numRetries; retryCount++) {
333
334       LOG.debug(AaiUiMsgs.QUERY_AAI_RETRY_SEQ, url, String.valueOf(retryCount + 1));
335
336       result = queryActiveInventory(url, responseType);
337
338       /**
339        * Record number of times we have attempted the request to later summarize how many times we
340        * are generally retrying over thousands of messages in a sync.
341        * 
342        * If the number of retries is surprisingly high, then we need to understand why that is as
343        * the number of retries is also causing a heavier load on AAI beyond the throttling controls
344        * we already have in place in term of the transaction rate controller and number of
345        * parallelized threads per task processor.
346        */
347
348       result.setNumRetries(retryCount);
349
350       if (!shouldRetryRequest(result)) {
351
352         result.setFromCache(false);
353         LOG.debug(AaiUiMsgs.QUERY_AAI_RETRY_DONE_SEQ, url, String.valueOf(retryCount + 1));
354
355         return result;
356       }
357
358       try {
359         /*
360          * Sleep between re-tries to be nice to the target system.
361          */
362         Thread.sleep(50);
363       } catch (InterruptedException exc) {
364         LOG.error(AaiUiMsgs.QUERY_AAI_WAIT_INTERRUPTION, exc.getLocalizedMessage());
365         Thread.currentThread().interrupt();
366         break;
367       }
368       LOG.error(AaiUiMsgs.QUERY_AAI_RETRY_FAILURE_WITH_SEQ, url, String.valueOf(retryCount + 1));
369
370     }
371
372     LOG.info(AaiUiMsgs.QUERY_AAI_RETRY_MAXED_OUT, url);
373
374     return result;
375
376   }
377   
378   public String repairSelfLink(String selfLink) {
379     return repairSelfLink(selfLink, null);
380   }
381
382   /**
383    * This method adds a scheme, host and port (if missing) to the passed-in URI.
384    * If these parts of the URI are already present, they will not be duplicated.
385    * 
386    * @param selflink The URI to repair
387    * @param queryParams The query parameters as a single string
388    * @return The corrected URI (i.e. includes a scheme/host/port)
389    */
390   public String repairSelfLink(String selflink, String queryParams) {
391     if (selflink == null) {
392       return selflink;
393     }
394
395     UriBuilder builder = UriBuilder.fromPath(selflink).host(endpointConfig.getEndpointIpAddress())
396         .port(Integer.parseInt(endpointConfig.getEndpointServerPort()));
397
398     switch (endpointConfig.getRestAuthenticationMode()) {
399
400       case SSL_BASIC:
401       case SSL_CERT: {
402         builder.scheme(HTTPS_SCHEME);
403         break;
404       }
405
406       default: {
407         builder.scheme(HTTP_SCHEME);
408       }
409     }
410
411     boolean includeQueryParams = ( (null != queryParams) && (!"".equals(queryParams)) );
412
413     /* builder.build().toString() will encode special characters to hexadecimal pairs prefixed with a '%'
414        so we're adding the query parameters separately, in their UTF-8 representations, so that
415        characters such as '?', '&', etc. remain intact as needed by the synchronizer */
416     return (builder.build().toString() + (includeQueryParams ? queryParams : ""));
417   }
418
419 }