2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.onap.aai.restcore;
23 import java.io.UnsupportedEncodingException;
25 import java.util.ArrayList;
26 import java.util.HashMap;
27 import java.util.List;
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;
36 import javax.ws.rs.core.HttpHeaders;
37 import javax.ws.rs.core.MediaType;
38 import javax.ws.rs.core.Response;
39 import javax.ws.rs.core.UriInfo;
41 import org.onap.aai.db.props.AAIProperties;
42 import org.onap.aai.exceptions.AAIException;
43 import org.onap.aai.introspection.Introspector;
44 import org.onap.aai.introspection.Loader;
45 import org.onap.aai.introspection.tools.CreateUUID;
46 import org.onap.aai.introspection.tools.DefaultFields;
47 import org.onap.aai.introspection.tools.InjectKeysFromURI;
48 import org.onap.aai.introspection.tools.IntrospectorValidator;
49 import org.onap.aai.introspection.tools.Issue;
50 import org.onap.aai.introspection.tools.RemoveNonVisibleProperty;
51 import org.onap.aai.logging.ErrorLogHelper;
52 import org.onap.aai.util.AAIConfig;
53 import org.onap.aai.util.FormatDate;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
58 * Base class for AAI REST API classes.
59 * Provides method to validate header information
60 * TODO should authenticate caller and authorize them for the API they are calling
61 * TODO should store the transaction
65 public class RESTAPI {
67 private static final Logger LOGGER = LoggerFactory.getLogger(RESTAPI.class);
73 GET, PUT, POST, DELETE
77 * Gets the from app id.
79 * @param headers the headers
80 * @return the from app id
81 * @throws AAIException the AAI exception
83 protected String getFromAppId(HttpHeaders headers) throws AAIException {
84 String fromAppId = null;
85 if (headers != null) {
86 List<String> fromAppIdHeader = headers.getRequestHeader("X-FromAppId");
87 if (fromAppIdHeader != null) {
88 for (String fromAppIdValue : fromAppIdHeader) {
89 fromAppId = fromAppIdValue;
94 if (fromAppId == null) {
95 throw new AAIException("AAI_4009");
104 * @param headers the headers
105 * @return the trans id
106 * @throws AAIException the AAI exception
108 protected String getTransId(HttpHeaders headers) throws AAIException {
109 String transId = null;
110 if (headers != null) {
111 List<String> transIdHeader = headers.getRequestHeader("X-TransactionId");
112 if (transIdHeader != null) {
113 for (String transIdValue : transIdHeader) {
114 transId = transIdValue;
119 if (transId == null) {
120 throw new AAIException("AAI_4010");
131 protected String genDate() {
132 FormatDate fd = new FormatDate("YYMMdd-HH:mm:ss:SSS");
134 return fd.getDateTime();
138 * Gets the media type.
140 * @param mediaTypeList the media type list
141 * @return the media type
143 protected String getMediaType(List<MediaType> mediaTypeList) {
144 String mediaType = MediaType.APPLICATION_JSON; // json is the default
145 for (MediaType mt : mediaTypeList) {
146 if (MediaType.APPLICATION_XML_TYPE.isCompatible(mt)) {
147 mediaType = MediaType.APPLICATION_XML;
153 /* ----------helpers for common consumer actions ----------- */
158 * @param depthParam the depth param
160 * @throws AAIException the AAI exception
162 protected int setDepth(String depthParam) throws AAIException {
163 int depth = AAIProperties.MAXIMUM_DEPTH; // default
164 if (depthParam != null && depthParam.length() > 0 && !depthParam.equals("all")) {
166 depth = Integer.parseInt(depthParam);
167 } catch (Exception e) {
168 throw new AAIException("AAI_4016");
175 * Consumer exception response generator.
177 * @param headers the headers
178 * @param info the info
179 * @param templateAction the template action
181 * @return the response
183 protected Response consumerExceptionResponseGenerator(HttpHeaders headers, UriInfo info, HttpMethod templateAction,
185 ArrayList<String> templateVars = new ArrayList<>();
186 templateVars.add(templateAction.toString()); // GET, PUT, etc
187 templateVars.add(info.getPath());
188 templateVars.addAll(e.getTemplateVars());
190 ErrorLogHelper.logException(e);
192 .status(e.getErrorObject().getHTTPResponseCode()).entity(ErrorLogHelper
193 .getRESTAPIErrorResponseWithLogging(headers.getAcceptableMediaTypes(), e, templateVars))
198 * Validate introspector.
201 * @param loader the loader
203 * @throws AAIException the AAI exception
204 * @throws UnsupportedEncodingException the unsupported encoding exception
206 protected void validateIntrospector(Introspector obj, Loader loader, URI uri, HttpMethod method)
207 throws AAIException, UnsupportedEncodingException {
209 int maximumDepth = AAIProperties.MAXIMUM_DEPTH;
210 boolean validateRequired = true;
211 if (method.equals(HttpMethod.MERGE_PATCH)) {
212 validateRequired = false;
215 IntrospectorValidator validator = new IntrospectorValidator.Builder().validateRequired(validateRequired)
216 .restrictDepth(maximumDepth).addResolver(new RemoveNonVisibleProperty()).addResolver(new CreateUUID())
217 .addResolver(new DefaultFields()).addResolver(new InjectKeysFromURI(loader, uri)).build();
218 boolean result = validator.validate(obj);
220 result = validator.resolveIssues();
223 List<String> messages = new ArrayList<>();
224 for (Issue issue : validator.getIssues()) {
225 if (!issue.isResolved()) {
226 messages.add(issue.getDetail());
229 String errors = String.join(",", messages);
230 throw new AAIException("AAI_3000", errors);
232 // check that key in payload and key in request uri are the same
233 String objURI = obj.getURI();
234 // if requested object is a parent objURI will have a leading slash the input uri will lack
235 // this adds that leading slash for the comparison
236 String testURI = "/" + uri.getRawPath();
237 if (!testURI.endsWith(objURI)) {
238 throw new AAIException("AAI_3000", "uri and payload keys don't match");
243 * Gets the input media type.
245 * @param mediaType the media type
246 * @return the input media type
248 protected String getInputMediaType(MediaType mediaType) {
249 return mediaType.getType() + "/" + mediaType.getSubtype();
253 * Returns the app specific timeout in milliseconds, -1 overrides the timeout for an app
257 * @param defaultTimeout
258 * @return integer timeout in or -1 to bypass
259 * @throws AAIException
262 public int getTimeoutLimit(String sot, String appTimeouts, String defaultTimeout) {
263 String[] ignoreAppIds = (appTimeouts).split("\\|");
264 int appLimit = Integer.parseInt(defaultTimeout);
265 final Map<String, Integer> m = new HashMap<>();
266 for (int i = 0; i < ignoreAppIds.length; i++) {
267 String[] vals = ignoreAppIds[i].split(",");
268 m.put(vals[0], Integer.parseInt(vals[1]));
270 if (m.get(sot) != null) {
271 appLimit = m.get(sot);
277 * Returns whether time out is enabled
282 * @param defaultTimeout
283 * @return boolean of whether the timeout is enabled
284 * @throws AAIException
286 public boolean isTimeoutEnabled(String sot, String isEnabled, String appTimeouts, String defaultTimeout) {
287 boolean isTimeoutEnabled = Boolean.parseBoolean(isEnabled);
289 if (isTimeoutEnabled) {
290 ata = getTimeoutLimit(sot, appTimeouts, defaultTimeout);
292 return isTimeoutEnabled && (ata > -1);
296 * Executes the process thread and watches the future for the timeout
299 * @param sourceOfTruth
300 * @param appTimeoutLimit
301 * @param defaultTimeoutLimit
305 * @return the response
308 public Response executeProcess(Future<Response> handler, String sourceOfTruth, String appTimeoutLimit,
309 String defaultTimeoutLimit, HttpMethod method, HttpHeaders headers, UriInfo info) {
310 Response response = null;
311 int timeoutLimit = 0;
313 timeoutLimit = getTimeoutLimit(sourceOfTruth, appTimeoutLimit, defaultTimeoutLimit);
314 response = handler.get(timeoutLimit, TimeUnit.MILLISECONDS);
315 } catch (TimeoutException e) {
316 AAIException ex = new AAIException("AAI_7406",
317 String.format("Timeout limit of %s seconds reached.", timeoutLimit / 1000));
318 response = consumerExceptionResponseGenerator(headers, info, method, ex);
319 handler.cancel(true);
320 } catch (InterruptedException e) {
321 AAIException ex = new AAIException("AAI_4000", e);
322 response = consumerExceptionResponseGenerator(headers, info, method, ex);
323 Thread.currentThread().interrupt();
324 } catch (Exception e) {
325 AAIException ex = new AAIException("AAI_4000", e);
326 response = consumerExceptionResponseGenerator(headers, info, method, ex);
332 * runner sets up the timer logic and invokes it
341 * @return the response
343 public Response runner(String toe, String tba, String tdl, HttpHeaders headers, UriInfo info, HttpMethod httpMethod,
344 Callable<Response> c) {
345 Response response = null;
346 Future<Response> handler = null;
347 ExecutorService executor = null;
349 String timeoutEnabled = AAIConfig.get(toe);
350 String timeoutByApp = AAIConfig.get(tba);
351 String timeoutDefaultLimit = AAIConfig.get(tdl);
352 String sourceOfTruth = headers.getRequestHeaders().getFirst("X-FromAppId");
353 if (isTimeoutEnabled(sourceOfTruth, timeoutEnabled, timeoutByApp, timeoutDefaultLimit)) {
354 executor = Executors.newSingleThreadExecutor();
355 handler = executor.submit(c);
356 response = executeProcess(handler, sourceOfTruth, timeoutByApp, timeoutDefaultLimit, httpMethod,
359 response = (Response) c.call();
361 } catch (Exception e) {
362 AAIException ex = new AAIException("AAI_4000", e);
363 response = consumerExceptionResponseGenerator(headers, info, httpMethod, ex);
365 if (executor != null && handler != null) {
366 executor.shutdownNow();