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