Fix all blocker sonar issues and some checkstyle
[aai/aai-common.git] / aai-core / src / main / java / org / onap / aai / restcore / RESTAPI.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *    http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.aai.restcore;
22
23 import java.io.UnsupportedEncodingException;
24 import java.net.URI;
25 import java.util.ArrayList;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.concurrent.Callable;
30 import java.util.concurrent.ExecutorService;
31 import java.util.concurrent.Executors;
32 import java.util.concurrent.Future;
33 import java.util.concurrent.TimeUnit;
34 import java.util.concurrent.TimeoutException;
35 import javax.ws.rs.core.HttpHeaders;
36 import javax.ws.rs.core.MediaType;
37 import javax.ws.rs.core.Response;
38 import javax.ws.rs.core.UriInfo;
39 import org.onap.aai.db.props.AAIProperties;
40 import org.onap.aai.exceptions.AAIException;
41 import org.onap.aai.introspection.Introspector;
42 import org.onap.aai.introspection.Loader;
43 import org.onap.aai.introspection.tools.CreateUUID;
44 import org.onap.aai.introspection.tools.DefaultFields;
45 import org.onap.aai.introspection.tools.InjectKeysFromURI;
46 import org.onap.aai.introspection.tools.IntrospectorValidator;
47 import org.onap.aai.introspection.tools.Issue;
48 import org.onap.aai.introspection.tools.RemoveNonVisibleProperty;
49 import org.onap.aai.logging.ErrorLogHelper;
50 import org.onap.aai.util.AAIConfig;
51 import org.onap.aai.util.FormatDate;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55 /**
56  * Base class for AAI REST API classes.
57  * Provides method to validate header information
58  * TODO should authenticate caller and authorize them for the API they are calling
59  * TODO should store the transaction
60  *
61  *
62  */
63 public class RESTAPI {
64
65     private static final Logger LOGGER = LoggerFactory.getLogger(RESTAPI.class);
66
67     /**
68      * The Enum Action.
69      */
70     public enum Action {
71         GET, PUT, POST, DELETE
72     }
73
74     /**
75      * Gets the from app id.
76      *
77      * @param headers the headers
78      * @return the from app id
79      * @throws AAIException the AAI exception
80      */
81     protected String getFromAppId(HttpHeaders headers) throws AAIException {
82         String fromAppId = null;
83         if (headers != null) {
84             List<String> fromAppIdHeader = headers.getRequestHeader("X-FromAppId");
85             if (fromAppIdHeader != null) {
86                 for (String fromAppIdValue : fromAppIdHeader) {
87                     fromAppId = fromAppIdValue;
88                 }
89             }
90         }
91
92         if (fromAppId == null) {
93             throw new AAIException("AAI_4009");
94         }
95
96         return fromAppId;
97     }
98
99     /**
100      * Gets the trans id.
101      *
102      * @param headers the headers
103      * @return the trans id
104      * @throws AAIException the AAI exception
105      */
106     protected String getTransId(HttpHeaders headers) throws AAIException {
107         String transId = null;
108         if (headers != null) {
109             List<String> transIdHeader = headers.getRequestHeader("X-TransactionId");
110             if (transIdHeader != null) {
111                 for (String transIdValue : transIdHeader) {
112                     transId = transIdValue;
113                 }
114             }
115         }
116
117         if (transId == null) {
118             throw new AAIException("AAI_4010");
119         }
120
121         return transId;
122     }
123
124     /**
125      * Gen date.
126      *
127      * @return the string
128      */
129     protected String genDate() {
130         FormatDate fd = new FormatDate("YYMMdd-HH:mm:ss:SSS");
131
132         return fd.getDateTime();
133     }
134
135     /**
136      * Gets the media type.
137      *
138      * @param mediaTypeList the media type list
139      * @return the media type
140      */
141     protected String getMediaType(List<MediaType> mediaTypeList) {
142         String mediaType = MediaType.APPLICATION_JSON; // json is the default
143         for (MediaType mt : mediaTypeList) {
144             if (MediaType.APPLICATION_XML_TYPE.isCompatible(mt)) {
145                 mediaType = MediaType.APPLICATION_XML;
146             }
147         }
148         return mediaType;
149     }
150
151     /* ----------helpers for common consumer actions ----------- */
152
153     /**
154      * Sets the depth.
155      *
156      * @param depthParam the depth param
157      * @return the int
158      * @throws AAIException the AAI exception
159      */
160     protected int setDepth(String depthParam) throws AAIException {
161         int depth = AAIProperties.MAXIMUM_DEPTH; // default
162         if (depthParam != null && depthParam.length() > 0 && !depthParam.equals("all")) {
163             try {
164                 depth = Integer.parseInt(depthParam);
165             } catch (Exception e) {
166                 throw new AAIException("AAI_4016");
167             }
168         }
169         return depth;
170     }
171
172     /**
173      * Consumer exception response generator.
174      *
175      * @param headers the headers
176      * @param info the info
177      * @param templateAction the template action
178      * @param e the e
179      * @return the response
180      */
181     protected Response consumerExceptionResponseGenerator(HttpHeaders headers, UriInfo info, HttpMethod templateAction,
182             AAIException e) {
183         ArrayList<String> templateVars = new ArrayList<>();
184         templateVars.add(templateAction.toString()); // GET, PUT, etc
185         templateVars.add(info.getPath());
186         templateVars.addAll(e.getTemplateVars());
187
188         ErrorLogHelper.logException(e);
189         return Response
190                 .status(e.getErrorObject().getHTTPResponseCode()).entity(ErrorLogHelper
191                         .getRESTAPIErrorResponseWithLogging(headers.getAcceptableMediaTypes(), e, templateVars))
192                 .build();
193     }
194
195     /**
196      * Validate introspector.
197      *
198      * @param obj the obj
199      * @param loader the loader
200      * @param uri the uri
201      * @throws AAIException the AAI exception
202      * @throws UnsupportedEncodingException the unsupported encoding exception
203      */
204     protected void validateIntrospector(Introspector obj, Loader loader, URI uri, HttpMethod method)
205             throws AAIException, UnsupportedEncodingException {
206
207         int maximumDepth = AAIProperties.MAXIMUM_DEPTH;
208         boolean validateRequired = true;
209         if (method.equals(HttpMethod.MERGE_PATCH)) {
210             validateRequired = false;
211             maximumDepth = 0;
212         }
213         IntrospectorValidator validator = new IntrospectorValidator.Builder().validateRequired(validateRequired)
214                 .restrictDepth(maximumDepth).addResolver(new RemoveNonVisibleProperty()).addResolver(new CreateUUID())
215                 .addResolver(new DefaultFields()).addResolver(new InjectKeysFromURI(loader, uri)).build();
216         boolean result = validator.validate(obj);
217         if (!result) {
218             result = validator.resolveIssues();
219         }
220         if (!result) {
221             List<String> messages = new ArrayList<>();
222             for (Issue issue : validator.getIssues()) {
223                 if (!issue.isResolved()) {
224                     messages.add(issue.getDetail());
225                 }
226             }
227             String errors = String.join(",", messages);
228             throw new AAIException("AAI_3000", errors);
229         }
230         // check that key in payload and key in request uri are the same
231         String objURI = obj.getURI();
232         // if requested object is a parent objURI will have a leading slash the input uri will lack
233         // this adds that leading slash for the comparison
234         String testURI = "/" + uri.getRawPath();
235         if (!testURI.endsWith(objURI)) {
236             throw new AAIException("AAI_3000", "uri and payload keys don't match");
237         }
238     }
239
240     /**
241      * Gets the input media type.
242      *
243      * @param mediaType the media type
244      * @return the input media type
245      */
246     protected String getInputMediaType(MediaType mediaType) {
247         return mediaType.getType() + "/" + mediaType.getSubtype();
248     }
249
250     /**
251      * Returns the app specific timeout in milliseconds, -1 overrides the timeout for an app
252      *
253      * @param sot
254      * @param appTimeouts
255      * @param defaultTimeout
256      * @return integer timeout in or -1 to bypass
257      * @throws AAIException
258      */
259
260     public int getTimeoutLimit(String sot, String appTimeouts, String defaultTimeout) {
261         String[] ignoreAppIds = (appTimeouts).split("\\|");
262         int appLimit = Integer.parseInt(defaultTimeout);
263         final Map<String, Integer> m = new HashMap<>();
264         for (int i = 0; i < ignoreAppIds.length; i++) {
265             String[] vals = ignoreAppIds[i].split(",");
266             m.put(vals[0], Integer.parseInt(vals[1]));
267         }
268         if (m.get(sot) != null) {
269             appLimit = m.get(sot);
270         }
271         return appLimit;
272     }
273
274     /**
275      * Returns whether time out is enabled
276      *
277      * @param sot
278      * @param isEnabled
279      * @param appTimeouts
280      * @param defaultTimeout
281      * @return boolean of whether the timeout is enabled
282      * @throws AAIException
283      */
284     public boolean isTimeoutEnabled(String sot, String isEnabled, String appTimeouts, String defaultTimeout) {
285         boolean isTimeoutEnabled = Boolean.parseBoolean(isEnabled);
286         int ata = -1;
287         if (isTimeoutEnabled) {
288             ata = getTimeoutLimit(sot, appTimeouts, defaultTimeout);
289         }
290         return isTimeoutEnabled && (ata > -1);
291     }
292
293     /**
294      * Executes the process thread and watches the future for the timeout
295      *
296      * @param handler
297      * @param sourceOfTruth
298      * @param appTimeoutLimit
299      * @param defaultTimeoutLimit
300      * @param method
301      * @param headers
302      * @param info
303      * @return the response
304      */
305
306     public Response executeProcess(Future<Response> handler, String sourceOfTruth, String appTimeoutLimit,
307             String defaultTimeoutLimit, HttpMethod method, HttpHeaders headers, UriInfo info) {
308         Response response = null;
309         int timeoutLimit = 0;
310         try {
311             timeoutLimit = getTimeoutLimit(sourceOfTruth, appTimeoutLimit, defaultTimeoutLimit);
312             response = handler.get(timeoutLimit, TimeUnit.MILLISECONDS);
313         } catch (TimeoutException e) {
314             AAIException ex = new AAIException("AAI_7406",
315                     String.format("Timeout limit of %s seconds reached.", timeoutLimit / 1000));
316             response = consumerExceptionResponseGenerator(headers, info, method, ex);
317             handler.cancel(true);
318         } catch (InterruptedException e) {
319             AAIException ex = new AAIException("AAI_4000", e);
320             response = consumerExceptionResponseGenerator(headers, info, method, ex);
321             Thread.currentThread().interrupt();
322         } catch (Exception e) {
323             AAIException ex = new AAIException("AAI_4000", e);
324             response = consumerExceptionResponseGenerator(headers, info, method, ex);
325         }
326         return response;
327     }
328
329     /**
330      * runner sets up the timer logic and invokes it
331      *
332      * @param toe
333      * @param tba
334      * @param tdl
335      * @param headers
336      * @param info
337      * @param httpMethod
338      * @param c
339      * @return the response
340      */
341     public Response runner(String toe, String tba, String tdl, HttpHeaders headers, UriInfo info, HttpMethod httpMethod,
342             Callable c) {
343         Response response = null;
344         Future<Response> handler = null;
345         ExecutorService executor = null;
346         try {
347             String timeoutEnabled = AAIConfig.get(toe);
348             String timeoutByApp = AAIConfig.get(tba);
349             String timeoutDefaultLimit = AAIConfig.get(tdl);
350             String sourceOfTruth = headers.getRequestHeaders().getFirst("X-FromAppId");
351             if (isTimeoutEnabled(sourceOfTruth, timeoutEnabled, timeoutByApp, timeoutDefaultLimit)) {
352                 executor = Executors.newSingleThreadExecutor();
353                 handler = executor.submit(c);
354                 response = executeProcess(handler, sourceOfTruth, timeoutByApp, timeoutDefaultLimit, httpMethod,
355                         headers, info);
356             } else {
357                 response = (Response) c.call();
358             }
359         } catch (Exception e) {
360             AAIException ex = new AAIException("AAI_4000", e);
361             response = consumerExceptionResponseGenerator(headers, info, httpMethod, ex);
362         } finally {
363             if (executor != null && handler != null) {
364                 executor.shutdownNow();
365             }
366         }
367         return response;
368     }
369
370 }