2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright © 2017 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=========================================================
20 * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22 package org.onap.aai.restcore;
24 import java.io.UnsupportedEncodingException;
26 import java.util.ArrayList;
27 import java.util.List;
29 import java.util.HashMap;
30 import java.util.concurrent.Executors;
31 import java.util.concurrent.Future;
32 import java.util.concurrent.ExecutorService;
33 import java.util.concurrent.TimeUnit;
34 import java.util.concurrent.Callable;
35 import java.util.concurrent.TimeoutException;
37 import javax.ws.rs.core.HttpHeaders;
38 import javax.ws.rs.core.MediaType;
39 import javax.ws.rs.core.Response;
40 import javax.ws.rs.core.UriInfo;
42 import org.onap.aai.db.props.AAIProperties;
43 import org.onap.aai.dbmap.DBConnectionType;
44 import org.onap.aai.exceptions.AAIException;
45 import org.onap.aai.introspection.Introspector;
46 import org.onap.aai.introspection.Loader;
47 import org.onap.aai.introspection.tools.CreateUUID;
48 import org.onap.aai.introspection.tools.DefaultFields;
49 import org.onap.aai.introspection.tools.InjectKeysFromURI;
50 import org.onap.aai.introspection.tools.IntrospectorValidator;
51 import org.onap.aai.introspection.tools.Issue;
52 import org.onap.aai.introspection.tools.RemoveNonVisibleProperty;
53 import org.onap.aai.logging.ErrorLogHelper;
54 import org.onap.aai.logging.LoggingContext;
55 import org.onap.aai.util.AAIConfig;
56 import org.onap.aai.util.FormatDate;
58 import com.att.eelf.configuration.EELFLogger;
59 import com.att.eelf.configuration.EELFManager;
60 import com.google.common.base.Joiner;
64 * Base class for AAI REST API classes.
65 * Provides method to validate header information
66 * TODO should authenticate caller and authorize them for the API they are calling
67 * TODO should store the transaction
71 public class RESTAPI {
73 private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(RESTAPI.class);
75 protected final String COMPONENT = "aairest";
81 GET, PUT, POST, DELETE
85 * Gets the from app id.
87 * @param headers the headers
88 * @return the from app id
89 * @throws AAIException the AAI exception
91 protected String getFromAppId(HttpHeaders headers) throws AAIException {
92 String fromAppId = null;
93 if (headers != null) {
94 List<String> fromAppIdHeader = headers.getRequestHeader("X-FromAppId");
95 if (fromAppIdHeader != null) {
96 for (String fromAppIdValue : fromAppIdHeader) {
97 fromAppId = fromAppIdValue;
102 if (fromAppId == null) {
103 throw new AAIException("AAI_4009");
106 LoggingContext.partnerName(fromAppId);
114 * @param headers the headers
115 * @return the trans id
116 * @throws AAIException the AAI exception
118 protected String getTransId(HttpHeaders headers) throws AAIException {
119 String transId = null;
120 if (headers != null) {
121 List<String> transIdHeader = headers.getRequestHeader("X-TransactionId");
122 if (transIdHeader != null) {
123 for (String transIdValue : transIdHeader) {
124 transId = transIdValue;
129 if (transId == null) {
130 throw new AAIException("AAI_4010");
133 LoggingContext.requestId(transId);
144 protected String genDate() {
145 FormatDate fd = new FormatDate( "YYMMdd-HH:mm:ss:SSS");
147 return fd.getDateTime();
151 * Gets the media type.
153 * @param mediaTypeList the media type list
154 * @return the media type
156 protected String getMediaType(List <MediaType> mediaTypeList) {
157 String mediaType = MediaType.APPLICATION_JSON; // json is the default
158 for (MediaType mt : mediaTypeList) {
159 if (MediaType.APPLICATION_XML_TYPE.isCompatible(mt)) {
160 mediaType = MediaType.APPLICATION_XML;
167 /* ----------helpers for common consumer actions ----------- */
172 * @param depthParam the depth param
174 * @throws AAIException the AAI exception
176 protected int setDepth(String depthParam) throws AAIException {
177 int depth = AAIProperties.MAXIMUM_DEPTH; //default
178 if (depthParam != null && depthParam.length() > 0 && !depthParam.equals("all")){
180 depth = Integer.valueOf(depthParam);
181 } catch (Exception e) {
182 throw new AAIException("AAI_4016");
189 * Consumer exception response generator.
191 * @param headers the headers
192 * @param info the info
193 * @param templateAction the template action
195 * @return the response
197 protected Response consumerExceptionResponseGenerator(HttpHeaders headers, UriInfo info, HttpMethod templateAction, AAIException e) {
198 ArrayList<String> templateVars = new ArrayList<String>();
199 templateVars.add(templateAction.toString()); //GET, PUT, etc
200 templateVars.add(info.getPath().toString());
201 templateVars.addAll(e.getTemplateVars());
203 ErrorLogHelper.logException(e);
205 .status(e.getErrorObject().getHTTPResponseCode())
206 .entity(ErrorLogHelper.getRESTAPIErrorResponseWithLogging(headers.getAcceptableMediaTypes(), e, templateVars))
211 * Validate introspector.
214 * @param loader the loader
216 * @throws AAIException the AAI exception
217 * @throws UnsupportedEncodingException the unsupported encoding exception
219 protected void validateIntrospector(Introspector obj, Loader loader, URI uri, HttpMethod method) throws AAIException, UnsupportedEncodingException {
221 int maximumDepth = AAIProperties.MAXIMUM_DEPTH;
222 boolean validateRequired = true;
223 if (method.equals(HttpMethod.MERGE_PATCH)) {
224 validateRequired = false;
227 IntrospectorValidator validator = new IntrospectorValidator.Builder()
228 .validateRequired(validateRequired)
229 .restrictDepth(maximumDepth)
230 .addResolver(new RemoveNonVisibleProperty())
231 .addResolver(new CreateUUID())
232 .addResolver(new DefaultFields())
233 .addResolver(new InjectKeysFromURI(loader, uri))
235 boolean result = validator.validate(obj);
237 result = validator.resolveIssues();
240 List<String> messages = new ArrayList<>();
241 for (Issue issue : validator.getIssues()) {
242 if (!issue.isResolved()) {
243 messages.add(issue.getDetail());
246 String errors = Joiner.on(",").join(messages);
247 throw new AAIException("AAI_3000", errors);
249 //check that key in payload and key in request uri are the same
250 String objURI = obj.getURI();
251 //if requested object is a parent objURI will have a leading slash the input uri will lack
252 //this adds that leading slash for the comparison
253 String testURI = "/" + uri.getRawPath();
254 if (!testURI.endsWith(objURI)) {
255 throw new AAIException("AAI_3000", "uri and payload keys don't match");
259 protected DBConnectionType determineConnectionType(String fromAppId, String realTime) throws AAIException {
260 if (fromAppId == null) {
261 throw new AAIException("AAI_4009", "X-FromAppId is not set");
264 DBConnectionType type = DBConnectionType.REALTIME;
265 boolean isRealTimeClient = AAIConfig.get("aai.realtime.clients", "").contains(fromAppId);
266 if (isRealTimeClient || realTime != null) {
267 type = DBConnectionType.REALTIME;
269 type = DBConnectionType.CACHED;
276 * Gets the input media type.
278 * @param mediaType the media type
279 * @return the input media type
281 protected String getInputMediaType(MediaType mediaType) {
282 String result = mediaType.getType() + "/" + mediaType.getSubtype();
289 * Returns the app specific timeout in milliseconds, -1 overrides the timeout for an app
293 * @param defaultTimeout
294 * @return integer timeout in or -1 to bypass
295 * @throws AAIException
298 public int getTimeoutLimit(String sot, String appTimeouts, String defaultTimeout) throws AAIException{
299 String[] ignoreAppIds = (appTimeouts).split("\\|");
300 int appLimit = Integer.parseInt(defaultTimeout);
301 final Map<String, Integer> m = new HashMap<String, Integer>();
302 if(ignoreAppIds != null) {
303 for (int i = 0; i < ignoreAppIds.length; i++) {
304 String[] vals = ignoreAppIds[i].split(",");
305 m.put(vals[0], Integer.parseInt(vals[1]));
307 if (m.get(sot) != null) {
308 appLimit = m.get(sot);
315 * Returns whether time out is enabled
319 * @param defaultTimeout
320 * @return boolean of whether the timeout is enabled
321 * @throws AAIException
323 public boolean isTimeoutEnabled(String sot, String isEnabled, String appTimeouts, String defaultTimeout) throws AAIException{
324 Boolean isTimeoutEnabled = Boolean.parseBoolean(isEnabled);
326 if(isTimeoutEnabled) {
327 ata = getTimeoutLimit(sot, appTimeouts, defaultTimeout);
329 return isTimeoutEnabled && (ata > -1);
333 * Executes the process thread and watches the future for the timeout
335 * @param sourceOfTruth
336 * @param appTimeoutLimit
337 * @param defaultTimeoutLimit
341 * @return the response
344 public Response executeProcess(Future<Response> handler, String sourceOfTruth, String appTimeoutLimit, String defaultTimeoutLimit, HttpMethod method, HttpHeaders headers, UriInfo info){
345 Response response = null;
346 int timeoutLimit = 0;
348 timeoutLimit = getTimeoutLimit(sourceOfTruth, appTimeoutLimit, defaultTimeoutLimit);
349 response = handler.get(timeoutLimit, TimeUnit.MILLISECONDS);
350 } catch (TimeoutException e) {
351 AAIException ex = new AAIException("AAI_7406", String.format("Timeout limit of %s seconds reached.", timeoutLimit/1000));
352 response = consumerExceptionResponseGenerator(headers, info, method, ex);
353 handler.cancel(true);
354 } catch (Exception e) {
355 AAIException ex = new AAIException("AAI_4000", e);
356 response = consumerExceptionResponseGenerator(headers, info, method, ex);
362 * runner sets up the timer logic and invokes it
370 * @return the response
372 public Response runner(String toe, String tba, String tdl, HttpHeaders headers, UriInfo info, HttpMethod httpMethod, Callable c){
373 Response response = null;
374 Future<Response> handler = null;
375 ExecutorService executor = null;
377 String timeoutEnabled = AAIConfig.get(toe);
378 String timeoutByApp = AAIConfig.get(tba);
379 String timeoutDefaultLimit = AAIConfig.get(tdl);
380 String sourceOfTruth = headers.getRequestHeaders().getFirst("X-FromAppId");
381 if (isTimeoutEnabled(sourceOfTruth, timeoutEnabled, timeoutByApp, timeoutDefaultLimit)) {
382 executor = Executors.newSingleThreadExecutor();
383 handler = executor.submit(c);
384 response = executeProcess(handler, sourceOfTruth, timeoutByApp, timeoutDefaultLimit, httpMethod, headers, info);
386 response = (Response) c.call();
389 AAIException ex = new AAIException("AAI_4000", e);
390 response = consumerExceptionResponseGenerator(headers, info, httpMethod, ex);
392 if(executor != null && handler != null){
393 executor.shutdownNow();