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 org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25 import org.onap.aai.db.props.AAIProperties;
26 import org.onap.aai.exceptions.AAIException;
27 import org.onap.aai.introspection.Introspector;
28 import org.onap.aai.introspection.Loader;
29 import org.onap.aai.introspection.tools.*;
30 import org.onap.aai.logging.ErrorLogHelper;
31 import org.onap.aai.util.AAIConfig;
32 import org.onap.aai.util.FormatDate;
34 import javax.ws.rs.core.HttpHeaders;
35 import javax.ws.rs.core.MediaType;
36 import javax.ws.rs.core.Response;
37 import javax.ws.rs.core.UriInfo;
38 import java.io.UnsupportedEncodingException;
40 import java.util.ArrayList;
41 import java.util.HashMap;
42 import java.util.List;
44 import java.util.concurrent.*;
47 * Base class for AAI REST API classes.
48 * Provides method to validate header information
49 * TODO should authenticate caller and authorize them for the API they are calling
50 * TODO should store the transaction
54 public class RESTAPI {
56 private static final Logger LOGGER = LoggerFactory.getLogger(RESTAPI.class);
62 GET, PUT, POST, DELETE
66 * Gets the from app id.
68 * @param headers the headers
69 * @return the from app id
70 * @throws AAIException the AAI exception
72 protected String getFromAppId(HttpHeaders headers) throws AAIException {
73 String fromAppId = null;
74 if (headers != null) {
75 List<String> fromAppIdHeader = headers.getRequestHeader("X-FromAppId");
76 if (fromAppIdHeader != null) {
77 for (String fromAppIdValue : fromAppIdHeader) {
78 fromAppId = fromAppIdValue;
83 if (fromAppId == null) {
84 throw new AAIException("AAI_4009");
93 * @param headers the headers
94 * @return the trans id
95 * @throws AAIException the AAI exception
97 protected String getTransId(HttpHeaders headers) throws AAIException {
98 String transId = null;
99 if (headers != null) {
100 List<String> transIdHeader = headers.getRequestHeader("X-TransactionId");
101 if (transIdHeader != null) {
102 for (String transIdValue : transIdHeader) {
103 transId = transIdValue;
108 if (transId == null) {
109 throw new AAIException("AAI_4010");
120 protected String genDate() {
121 FormatDate fd = new FormatDate("YYMMdd-HH:mm:ss:SSS");
123 return fd.getDateTime();
127 * Gets the media type.
129 * @param mediaTypeList the media type list
130 * @return the media type
132 protected String getMediaType(List<MediaType> mediaTypeList) {
133 String mediaType = MediaType.APPLICATION_JSON; // json is the default
134 for (MediaType mt : mediaTypeList) {
135 if (MediaType.APPLICATION_XML_TYPE.isCompatible(mt)) {
136 mediaType = MediaType.APPLICATION_XML;
142 /* ----------helpers for common consumer actions ----------- */
147 * @param depthParam the depth param
149 * @throws AAIException the AAI exception
151 protected int setDepth(String depthParam) throws AAIException {
152 int depth = AAIProperties.MAXIMUM_DEPTH; // default
153 if (depthParam != null && depthParam.length() > 0 && !depthParam.equals("all")) {
155 depth = Integer.parseInt(depthParam);
156 } catch (Exception e) {
157 throw new AAIException("AAI_4016");
164 * Consumer exception response generator.
166 * @param headers the headers
167 * @param info the info
168 * @param templateAction the template action
170 * @return the response
172 protected Response consumerExceptionResponseGenerator(HttpHeaders headers, UriInfo info, HttpMethod templateAction,
174 ArrayList<String> templateVars = new ArrayList<>();
175 templateVars.add(templateAction.toString()); // GET, PUT, etc
176 templateVars.add(info.getPath());
177 templateVars.addAll(e.getTemplateVars());
179 ErrorLogHelper.logException(e);
181 .status(e.getErrorObject().getHTTPResponseCode()).entity(ErrorLogHelper
182 .getRESTAPIErrorResponseWithLogging(headers.getAcceptableMediaTypes(), e, templateVars))
187 * Validate introspector.
190 * @param loader the loader
192 * @throws AAIException the AAI exception
193 * @throws UnsupportedEncodingException the unsupported encoding exception
195 protected void validateIntrospector(Introspector obj, Loader loader, URI uri, HttpMethod method)
196 throws AAIException, UnsupportedEncodingException {
198 int maximumDepth = AAIProperties.MAXIMUM_DEPTH;
199 boolean validateRequired = true;
200 if (method.equals(HttpMethod.MERGE_PATCH)) {
201 validateRequired = false;
204 IntrospectorValidator validator = new IntrospectorValidator.Builder().validateRequired(validateRequired)
205 .restrictDepth(maximumDepth).addResolver(new RemoveNonVisibleProperty()).addResolver(new CreateUUID())
206 .addResolver(new DefaultFields()).addResolver(new InjectKeysFromURI(loader, uri)).build();
207 boolean result = validator.validate(obj);
209 result = validator.resolveIssues();
212 List<String> messages = new ArrayList<>();
213 for (Issue issue : validator.getIssues()) {
214 if (!issue.isResolved()) {
215 messages.add(issue.getDetail());
218 String errors = String.join(",", messages);
219 throw new AAIException("AAI_3000", errors);
221 // check that key in payload and key in request uri are the same
222 String objURI = obj.getURI();
223 // if requested object is a parent objURI will have a leading slash the input uri will lack
224 // this adds that leading slash for the comparison
225 String testURI = "/" + uri.getRawPath();
226 if (!testURI.endsWith(objURI)) {
227 throw new AAIException("AAI_3000", "uri and payload keys don't match");
232 * Gets the input media type.
234 * @param mediaType the media type
235 * @return the input media type
237 protected String getInputMediaType(MediaType mediaType) {
238 return mediaType.getType() + "/" + mediaType.getSubtype();
242 * Returns the app specific timeout in milliseconds, -1 overrides the timeout for an app
246 * @param defaultTimeout
247 * @return integer timeout in or -1 to bypass
248 * @throws AAIException
251 public int getTimeoutLimit(String sot, String appTimeouts, String defaultTimeout) {
252 String[] ignoreAppIds = (appTimeouts).split("\\|");
253 int appLimit = Integer.parseInt(defaultTimeout);
254 final Map<String, Integer> m = new HashMap<>();
255 for (int i = 0; i < ignoreAppIds.length; i++) {
256 String[] vals = ignoreAppIds[i].split(",");
257 m.put(vals[0], Integer.parseInt(vals[1]));
259 if (m.get(sot) != null) {
260 appLimit = m.get(sot);
266 * Returns whether time out is enabled
271 * @param defaultTimeout
272 * @return boolean of whether the timeout is enabled
273 * @throws AAIException
275 public boolean isTimeoutEnabled(String sot, String isEnabled, String appTimeouts, String defaultTimeout) {
276 boolean isTimeoutEnabled = Boolean.parseBoolean(isEnabled);
278 if (isTimeoutEnabled) {
279 ata = getTimeoutLimit(sot, appTimeouts, defaultTimeout);
281 return isTimeoutEnabled && (ata > -1);
285 * Executes the process thread and watches the future for the timeout
288 * @param sourceOfTruth
289 * @param appTimeoutLimit
290 * @param defaultTimeoutLimit
294 * @return the response
297 public Response executeProcess(Future<Response> handler, String sourceOfTruth, String appTimeoutLimit,
298 String defaultTimeoutLimit, HttpMethod method, HttpHeaders headers, UriInfo info) {
299 Response response = null;
300 int timeoutLimit = 0;
302 timeoutLimit = getTimeoutLimit(sourceOfTruth, appTimeoutLimit, defaultTimeoutLimit);
303 response = handler.get(timeoutLimit, TimeUnit.MILLISECONDS);
304 } catch (TimeoutException e) {
305 AAIException ex = new AAIException("AAI_7406",
306 String.format("Timeout limit of %s seconds reached.", timeoutLimit / 1000));
307 response = consumerExceptionResponseGenerator(headers, info, method, ex);
308 handler.cancel(true);
309 } catch (Exception e) {
310 AAIException ex = new AAIException("AAI_4000", e);
311 response = consumerExceptionResponseGenerator(headers, info, method, ex);
317 * runner sets up the timer logic and invokes it
326 * @return the response
328 public Response runner(String toe, String tba, String tdl, HttpHeaders headers, UriInfo info, HttpMethod httpMethod,
330 Response response = null;
331 Future<Response> handler = null;
332 ExecutorService executor = null;
334 String timeoutEnabled = AAIConfig.get(toe);
335 String timeoutByApp = AAIConfig.get(tba);
336 String timeoutDefaultLimit = AAIConfig.get(tdl);
337 String sourceOfTruth = headers.getRequestHeaders().getFirst("X-FromAppId");
338 if (isTimeoutEnabled(sourceOfTruth, timeoutEnabled, timeoutByApp, timeoutDefaultLimit)) {
339 executor = Executors.newSingleThreadExecutor();
340 handler = executor.submit(c);
341 response = executeProcess(handler, sourceOfTruth, timeoutByApp, timeoutDefaultLimit, httpMethod,
344 response = (Response) c.call();
346 } catch (Exception e) {
347 AAIException ex = new AAIException("AAI_4000", e);
348 response = consumerExceptionResponseGenerator(headers, info, httpMethod, ex);
350 if (executor != null && handler != null) {
351 executor.shutdownNow();