update sparky with configurable features
[aai/sparky-be.git] / sparkybe-onap-service / src / main / java / org / onap / aai / sparky / dal / GizmoAdapter.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.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30
31 import javax.ws.rs.core.MediaType;
32 import javax.ws.rs.core.UriBuilder;
33
34 import org.onap.aai.cl.api.Logger;
35 import org.onap.aai.cl.eelf.LoggerFactory;
36 import org.onap.aai.restclient.client.OperationResult;
37 import org.onap.aai.restclient.client.RestClient;
38 import org.onap.aai.restclient.enums.RestAuthenticationMode;
39 import org.onap.aai.sparky.config.oxm.OxmModelLoader;
40 import org.onap.aai.sparky.dal.exception.ElasticSearchOperationException;
41 import org.onap.aai.sparky.dal.rest.RestClientConstructionException;
42 import org.onap.aai.sparky.dal.rest.RestClientFactory;
43 import org.onap.aai.sparky.dal.rest.config.RestEndpointConfig;
44 import org.onap.aai.sparky.logging.AaiUiMsgs;
45 import org.onap.aai.sparky.util.NodeUtils;
46
47 /**
48  * The Class GizmoAdapter.
49  */
50
51 public class GizmoAdapter {
52
53   private static final Logger LOG = LoggerFactory.getInstance().getLogger(GizmoAdapter.class);
54
55   private static final String HEADER_TRANS_ID = "X-TransactionId";
56   private static final String HEADER_FROM_APP_ID = "X-FromAppId";
57   private static final String HEADER_AUTHORIZATION = "Authorization";
58
59   private static final String HTTP_SCHEME = "http";
60   private static final String HTTPS_SCHEME = "https";
61
62   private static final String TRANSACTION_ID_PREFIX = "txnId-";
63   private static final String UI_APP_NAME = "AAI-UI";
64
65   private OxmModelLoader oxmModelLoader;
66
67   private RestEndpointConfig endpointConfig;
68
69   private RestClient restClient;
70
71   private String inventoryBasePath;
72   private String relationshipsBasePath;
73   private String appPartnerName = "";
74
75   /**
76    * Instantiates a new active inventory adapter.
77    * 
78    * @throws RestClientConstructionException
79    *
80    */
81
82   public GizmoAdapter(OxmModelLoader oxmModelLoader, RestEndpointConfig endpointConfig)
83       throws ElasticSearchOperationException, IOException, RestClientConstructionException {
84
85     this.oxmModelLoader = oxmModelLoader;
86     this.endpointConfig = endpointConfig;
87     this.restClient = RestClientFactory.buildClient(endpointConfig);
88
89   }
90
91   public String getAppPartnerName() {
92     return appPartnerName;
93   }
94
95   public void setAppPartnerName(String appPartnerName) {
96     this.appPartnerName = appPartnerName;
97   }
98
99
100   public String getRelationshipsBasePath() {
101     return relationshipsBasePath;
102   }
103
104   public void setRelationshipsBasePath(String relationshipsBasePath) {
105     this.relationshipsBasePath = relationshipsBasePath;
106   }
107
108   public String getInventoryBasePath() {
109     return inventoryBasePath;
110   }
111
112   public void setInventoryBasePath(String inventoryBasePath) {
113     this.inventoryBasePath = inventoryBasePath;
114   }
115
116   public String getFullInventoryUrl(String resourceUrl) throws Exception {
117     final String host = endpointConfig.getEndpointIpAddress();
118     final String port = endpointConfig.getEndpointServerPort();
119     final String basePath = getInventoryBasePath();
120     return String.format("https://%s:%s%s%s", host, port, basePath, resourceUrl);
121   }
122
123   public String addServerDetailsToUrl(String resourceUrl) throws Exception {
124     final String host = endpointConfig.getEndpointIpAddress();
125     final String port = endpointConfig.getEndpointServerPort();
126     return String.format("https://%s:%s/%s", host, port, resourceUrl);
127   }
128
129   public String getFullRelationshipUrl(String resourceUrl) throws Exception {
130     final String host = endpointConfig.getEndpointIpAddress();
131     final String port = endpointConfig.getEndpointServerPort();
132     final String basePath = getRelationshipsBasePath();
133     return String.format("https://%s:%s%s%s", host, port, basePath, resourceUrl);
134   }
135
136   protected Map<String, List<String>> getMessageHeaders() {
137
138     Map<String, List<String>> headers = new HashMap<String, List<String>>();
139
140     headers.putIfAbsent(HEADER_FROM_APP_ID, new ArrayList<String>());
141     headers.get(HEADER_FROM_APP_ID).add(appPartnerName);
142
143     headers.putIfAbsent(HEADER_TRANS_ID, new ArrayList<String>());
144     headers.get(HEADER_TRANS_ID).add(TRANSACTION_ID_PREFIX + NodeUtils.getRandomTxnId());
145
146     if (endpointConfig.getRestAuthenticationMode() == RestAuthenticationMode.SSL_BASIC) {
147
148       headers.putIfAbsent(HEADER_AUTHORIZATION, new ArrayList<String>());
149       headers.get(HEADER_AUTHORIZATION).add(getBasicAuthenticationCredentials());
150
151     }
152
153     return headers;
154   }
155
156   protected String getBasicAuthenticationCredentials() {
157     String usernameAndPassword = String.join(":", endpointConfig.getBasicAuthUserName(),
158         endpointConfig.getBasicAuthPassword());
159     return "Basic " + java.util.Base64.getEncoder().encodeToString(usernameAndPassword.getBytes());
160   }
161
162   /**
163    * Our retry conditions should be very specific.
164    *
165    * @param r the r
166    * @return true, if successful
167    */
168   private boolean shouldRetryRequest(OperationResult r) {
169
170     if (r == null) {
171       return true;
172     }
173
174     int rc = r.getResultCode();
175
176     if (rc == 200) {
177       return false;
178     }
179
180     if (rc == 404) {
181       return false;
182     }
183
184     return true;
185
186   }
187
188   /**
189    * Query active inventory.
190    *
191    * @param url the url
192    * @param acceptContentType the accept content type
193    * @return the operation result
194    */
195   OperationResult queryGizmo(String url, String acceptContentType) {
196
197     return restClient.get(url, getMessageHeaders(), MediaType.APPLICATION_JSON_TYPE);
198
199   }
200
201   public RestEndpointConfig getEndpointConfig() {
202     return endpointConfig;
203   }
204
205   public void setEndpointConfig(RestEndpointConfig endpointConfig) {
206     this.endpointConfig = endpointConfig;
207   }
208
209   public OperationResult queryGizmoWithRetries(String url, String responseType, int numRetries) {
210
211     OperationResult result = null;
212
213     for (int retryCount = 0; retryCount < numRetries; retryCount++) {
214
215       LOG.debug(AaiUiMsgs.QUERY_AAI_RETRY_SEQ, url, String.valueOf(retryCount + 1));
216
217       result = queryGizmo(url, responseType);
218
219       /**
220        * Record number of times we have attempted the request to later summarize how many times we
221        * are generally retrying over thousands of messages in a sync.
222        * 
223        * If the number of retries is surprisingly high, then we need to understand why that is as
224        * the number of retries is also causing a heavier load on AAI beyond the throttling controls
225        * we already have in place in term of the transaction rate controller and number of
226        * parallelized threads per task processor.
227        */
228
229       result.setNumRetries(retryCount);
230
231       if (!shouldRetryRequest(result)) {
232
233         result.setFromCache(false);
234         LOG.debug(AaiUiMsgs.QUERY_AAI_RETRY_DONE_SEQ, url, String.valueOf(retryCount + 1));
235
236         return result;
237       }
238
239       try {
240         /*
241          * Sleep between re-tries to be nice to the target system.
242          */
243         Thread.sleep(50);
244       } catch (InterruptedException exc) {
245         LOG.error(AaiUiMsgs.QUERY_AAI_WAIT_INTERRUPTION, exc.getLocalizedMessage());
246         Thread.currentThread().interrupt();
247         break;
248       }
249       LOG.error(AaiUiMsgs.QUERY_AAI_RETRY_FAILURE_WITH_SEQ, url, String.valueOf(retryCount + 1));
250
251     }
252
253     LOG.info(AaiUiMsgs.QUERY_AAI_RETRY_MAXED_OUT, url);
254
255     return result;
256
257   }
258
259   /**
260    * This method adds a scheme, host and port (if missing) to the passed-in URI. If these parts of
261    * the URI are already present, they will not be duplicated.
262    * 
263    * @param selflink The URI to repair
264    * @param queryParams The query parameters as a single string
265    * @return The corrected URI (i.e. includes a scheme/host/port)
266    */
267
268   private String repairGizmoSelfLink(String baseUrlPath, String selfLink, String queryParams) {
269
270     if (selfLink == null) {
271       return selfLink;
272     }
273
274     if (selfLink.startsWith("http") || selfLink.startsWith("https")) {
275       return selfLink;
276     }
277
278     UriBuilder builder = UriBuilder.fromPath(baseUrlPath + "/" + selfLink)
279         .host(endpointConfig.getEndpointIpAddress())
280         .port(Integer.parseInt(endpointConfig.getEndpointServerPort()));
281
282     switch (endpointConfig.getRestAuthenticationMode()) {
283
284       case SSL_BASIC:
285       case SSL_CERT: {
286         builder.scheme(HTTPS_SCHEME);
287         break;
288       }
289
290       default: {
291         builder.scheme(HTTP_SCHEME);
292       }
293     }
294
295     boolean includeQueryParams = ((null != queryParams) && (!"".equals(queryParams)));
296
297     /*
298      * builder.build().toString() will encode special characters to hexadecimal pairs prefixed with
299      * a '%' so we're adding the query parameters separately, in their UTF-8 representations, so
300      * that characters such as '?', '&', etc. remain intact as needed by the synchronizer
301      */
302     return (builder.build().toString() + (includeQueryParams ? queryParams : ""));
303
304   }
305
306   public String repairRelationshipSelfLink(String selflink, String queryParams) {
307     return repairGizmoSelfLink(relationshipsBasePath, selflink, queryParams);
308   }
309
310   public String repairInventorySelfLink(String selflink, String queryParams) {
311     return repairGizmoSelfLink(inventoryBasePath, selflink, queryParams);
312   }
313
314   public OperationResult getSelfLinksByEntityType(String entityType) throws Exception {
315
316     if (entityType == null) {
317       throw new NullPointerException(
318           "Failed to getSelfLinksByEntityType() because entityType is null");
319     }
320
321     String link = getFullInventoryUrl(entityType);
322
323     return queryGizmoWithRetries(link, "application/json", endpointConfig.getNumRequestRetries());
324
325   }
326
327   public static String extractResourcePath(String selflink) {
328     try {
329       return new URI(selflink).getRawPath();
330     } catch (URISyntaxException uriSyntaxException) {
331       LOG.error(AaiUiMsgs.ERROR_EXTRACTING_RESOURCE_PATH_FROM_LINK,
332           uriSyntaxException.getMessage());
333       return selflink;
334     }
335   }
336
337 }