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;
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;
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
63 public class RESTAPI {
65 private static final Logger LOGGER = LoggerFactory.getLogger(RESTAPI.class);
71 GET, PUT, POST, DELETE
75 * Gets the from app id.
77 * @param headers the headers
78 * @return the from app id
79 * @throws AAIException the AAI exception
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;
92 if (fromAppId == null) {
93 throw new AAIException("AAI_4009");
102 * @param headers the headers
103 * @return the trans id
104 * @throws AAIException the AAI exception
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;
117 if (transId == null) {
118 throw new AAIException("AAI_4010");
129 protected String genDate() {
130 FormatDate fd = new FormatDate("YYMMdd-HH:mm:ss:SSS");
132 return fd.getDateTime();
136 * Gets the media type.
138 * @param mediaTypeList the media type list
139 * @return the media type
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;
151 /* ----------helpers for common consumer actions ----------- */
156 * @param depthParam the depth param
158 * @throws AAIException the AAI exception
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")) {
164 depth = Integer.parseInt(depthParam);
165 } catch (Exception e) {
166 throw new AAIException("AAI_4016");
173 * Consumer exception response generator.
175 * @param headers the headers
176 * @param info the info
177 * @param templateAction the template action
179 * @return the response
181 protected Response consumerExceptionResponseGenerator(HttpHeaders headers, UriInfo info, HttpMethod templateAction,
183 ArrayList<String> templateVars = new ArrayList<>();
184 templateVars.add(templateAction.toString()); // GET, PUT, etc
185 templateVars.add(info.getPath());
186 templateVars.addAll(e.getTemplateVars());
188 ErrorLogHelper.logException(e);
190 .status(e.getErrorObject().getHTTPResponseCode()).entity(ErrorLogHelper
191 .getRESTAPIErrorResponseWithLogging(headers.getAcceptableMediaTypes(), e, templateVars))
196 * Validate introspector.
199 * @param loader the loader
201 * @throws AAIException the AAI exception
202 * @throws UnsupportedEncodingException the unsupported encoding exception
204 protected void validateIntrospector(Introspector obj, Loader loader, URI uri, HttpMethod method)
205 throws AAIException, UnsupportedEncodingException {
207 int maximumDepth = AAIProperties.MAXIMUM_DEPTH;
208 boolean validateRequired = true;
209 if (method.equals(HttpMethod.MERGE_PATCH)) {
210 validateRequired = false;
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);
218 result = validator.resolveIssues();
221 List<String> messages = new ArrayList<>();
222 for (Issue issue : validator.getIssues()) {
223 if (!issue.isResolved()) {
224 messages.add(issue.getDetail());
227 String errors = String.join(",", messages);
228 throw new AAIException("AAI_3000", errors);
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");
241 * Gets the input media type.
243 * @param mediaType the media type
244 * @return the input media type
246 protected String getInputMediaType(MediaType mediaType) {
247 return mediaType.getType() + "/" + mediaType.getSubtype();
251 * Returns the app specific timeout in milliseconds, -1 overrides the timeout for an app
255 * @param defaultTimeout
256 * @return integer timeout in or -1 to bypass
257 * @throws AAIException
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]));
268 if (m.get(sot) != null) {
269 appLimit = m.get(sot);
275 * Returns whether time out is enabled
280 * @param defaultTimeout
281 * @return boolean of whether the timeout is enabled
282 * @throws AAIException
284 public boolean isTimeoutEnabled(String sot, String isEnabled, String appTimeouts, String defaultTimeout) {
285 boolean isTimeoutEnabled = Boolean.parseBoolean(isEnabled);
287 if (isTimeoutEnabled) {
288 ata = getTimeoutLimit(sot, appTimeouts, defaultTimeout);
290 return isTimeoutEnabled && (ata > -1);
294 * Executes the process thread and watches the future for the timeout
297 * @param sourceOfTruth
298 * @param appTimeoutLimit
299 * @param defaultTimeoutLimit
303 * @return the response
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;
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);
330 * runner sets up the timer logic and invokes it
339 * @return the response
341 public Response runner(String toe, String tba, String tdl, HttpHeaders headers, UriInfo info, HttpMethod httpMethod,
343 Response response = null;
344 Future<Response> handler = null;
345 ExecutorService executor = null;
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,
357 response = (Response) c.call();
359 } catch (Exception e) {
360 AAIException ex = new AAIException("AAI_4000", e);
361 response = consumerExceptionResponseGenerator(headers, info, httpMethod, ex);
363 if (executor != null && handler != null) {
364 executor.shutdownNow();