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=========================================================
20 package org.onap.aai.rest.db;
23 import java.io.IOException;
24 import java.io.UnsupportedEncodingException;
25 import java.lang.reflect.InvocationTargetException;
26 import java.net.MalformedURLException;
28 import java.net.URISyntaxException;
29 import java.util.ArrayList;
30 import java.util.HashMap;
31 import java.util.List;
34 import java.util.concurrent.TimeUnit;
35 import java.util.stream.Collectors;
37 import javax.ws.rs.core.HttpHeaders;
38 import javax.ws.rs.core.MediaType;
39 import javax.ws.rs.core.MultivaluedMap;
40 import javax.ws.rs.core.Response;
41 import javax.ws.rs.core.Response.Status;
42 import javax.ws.rs.core.UriBuilder;
44 import com.att.eelf.configuration.EELFLogger;
45 import com.att.eelf.configuration.EELFManager;
46 import com.fasterxml.jackson.databind.JsonNode;
47 import com.fasterxml.jackson.databind.ObjectMapper;
48 import com.github.fge.jsonpatch.JsonPatchException;
49 import com.github.fge.jsonpatch.mergepatch.JsonMergePatch;
50 import org.apache.commons.lang.StringUtils;
51 import org.apache.tinkerpop.gremlin.structure.Graph;
52 import org.apache.tinkerpop.gremlin.structure.Vertex;
53 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
54 import org.janusgraph.core.JanusGraphException;
55 import org.javatuples.Pair;
56 import org.json.JSONException;
57 import org.json.JSONObject;
58 import org.onap.aai.db.props.AAIProperties;
59 import org.onap.aai.dbmap.DBConnectionType;
60 import org.onap.aai.domain.responseMessage.AAIResponseMessage;
61 import org.onap.aai.domain.responseMessage.AAIResponseMessageDatum;
62 import org.onap.aai.exceptions.AAIException;
63 import org.onap.aai.extensions.AAIExtensionMap;
64 import org.onap.aai.extensions.ExtensionController;
65 import org.onap.aai.introspection.*;
66 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
67 import org.onap.aai.logging.ErrorLogHelper;
68 import org.onap.aai.logging.LoggingContext;
69 import org.onap.aai.nodes.NodeIngestor;
70 import org.onap.aai.parsers.query.QueryParser;
71 import org.onap.aai.parsers.uri.URIToExtensionInformation;
73 import org.onap.aai.parsers.uri.URIToObject;
74 import org.onap.aai.rest.ueb.UEBNotification;
75 import org.onap.aai.restcore.HttpMethod;
76 import org.onap.aai.schema.enums.ObjectMetadata;
77 import org.onap.aai.serialization.db.DBSerializer;
78 import org.onap.aai.serialization.engines.JanusGraphDBEngine;
79 import org.onap.aai.serialization.engines.QueryStyle;
80 import org.onap.aai.serialization.engines.TransactionalGraphEngine;
81 import org.onap.aai.serialization.engines.query.QueryEngine;
82 import org.onap.aai.serialization.queryformats.Format;
83 import org.onap.aai.serialization.queryformats.FormatFactory;
84 import org.onap.aai.serialization.queryformats.Formatter;
85 import org.onap.aai.setup.SchemaVersion;
86 import org.onap.aai.setup.SchemaVersions;
87 import org.onap.aai.util.AAIConfig;
88 import org.springframework.beans.factory.annotation.Autowired;
89 import org.springframework.beans.factory.annotation.Value;
92 * The Class HttpEntry.
94 public class HttpEntry {
96 private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(HttpEntry.class);
97 private static final String TARGET_ENTITY = "DB";
99 private ModelType introspectorFactoryType;
101 private QueryStyle queryStyle;
103 private SchemaVersion version;
105 private Loader loader;
107 private TransactionalGraphEngine dbEngine;
109 private boolean processSingle = true;
111 private int paginationBucket = -1;
112 private int paginationIndex = -1;
113 private int totalVertices = 0;
114 private int totalPaginationBuckets = 0;
117 private NodeIngestor nodeIngestor;
120 private LoaderFactory loaderFactory;
123 private SchemaVersions schemaVersions;
125 @Value("${schema.uri.base.path}")
126 private String basePath;
128 private UEBNotification notification;
131 * Instantiates a new http entry.
133 * @param modelType the model type
134 * @param queryStyle the query style
136 public HttpEntry(ModelType modelType, QueryStyle queryStyle) {
137 this.introspectorFactoryType = modelType;
138 this.queryStyle = queryStyle;
141 public HttpEntry setHttpEntryProperties(SchemaVersion version, DBConnectionType connectionType) {
142 this.version = version;
143 this.loader = loaderFactory.createLoaderForVersion(introspectorFactoryType, version);
144 this.dbEngine = new JanusGraphDBEngine(
149 getDbEngine().startTransaction();
150 this.notification = new UEBNotification(loader, loaderFactory, schemaVersions);
155 public HttpEntry setHttpEntryProperties(SchemaVersion version, DBConnectionType connectionType, UEBNotification notification) {
156 this.version = version;
157 this.loader = loaderFactory.createLoaderForVersion(introspectorFactoryType, version);
158 this.dbEngine = new JanusGraphDBEngine(
163 this.notification = notification;
164 //start transaction on creation
165 getDbEngine().startTransaction();
171 * Gets the introspector factory type.
173 * @return the introspector factory type
175 public ModelType getIntrospectorFactoryType() {
176 return introspectorFactoryType;
180 * Gets the query style.
182 * @return the query style
184 public QueryStyle getQueryStyle() {
191 * @return the version
193 public SchemaVersion getVersion() {
202 public Loader getLoader() {
207 * Gets the db engine.
209 * @return the db engine
211 public TransactionalGraphEngine getDbEngine() {
215 public Pair<Boolean, List<Pair<URI, Response>>> process(List<DBRequest> requests, String sourceOfTruth) throws AAIException {
216 return this.process(requests, sourceOfTruth, true);
221 * Checks the pagination bucket and pagination index variables to determine whether or not the user
222 * requested paginated results
224 * @return a boolean true/false of whether the user requested paginated results
226 public boolean isPaginated() {
227 return this.paginationBucket > -1 && this.paginationIndex > -1;
231 * Returns the pagination size
232 * @return integer of the size of results to be returned when paginated
234 public int getPaginationBucket() {
235 return this.paginationBucket;
239 * Setter for the pagination bucket variable which stores in this object the size of results to return
242 public void setPaginationBucket(int pb) {
243 this.paginationBucket = pb;
247 * Getter to return the pagination index requested by the user when requesting paginated results
250 public int getPaginationIndex() {
251 return this.paginationIndex;
255 * Sets the pagination index that was passed in by the user, to determine which index or results to retrieve when
259 public void setPaginationIndex(int pi) {
263 this.paginationIndex = pi;
267 * Sets the total vertices variables and calculates the amount of pages based on size and total vertices
268 * @param totalVertices
269 * @param paginationBucketSize
271 public void setTotalsForPaging(int totalVertices, int paginationBucketSize) {
272 this.totalVertices = totalVertices;
273 //set total number of buckets equal to full pages
274 this.totalPaginationBuckets = totalVertices / paginationBucketSize;
275 //conditionally add a page for the remainder
276 if (totalVertices % paginationBucketSize > 0) {
277 this.totalPaginationBuckets++;
282 * @return the total amount of pages
284 public int getTotalPaginationBuckets() {
285 return this.totalPaginationBuckets;
290 * @return the total number of vertices when paginated
292 public int getTotalVertices() {
293 return this.totalVertices;
298 * @param requests the requests
299 * @param sourceOfTruth the source of truth
302 * @throws AAIException the AAI exception
304 public Pair<Boolean, List<Pair<URI, Response>>> process(List<DBRequest> requests, String sourceOfTruth, boolean enableResourceVersion) throws AAIException {
306 DBSerializer serializer = new DBSerializer(version, dbEngine, introspectorFactoryType, sourceOfTruth);
307 String methodName = "process";
309 Introspector obj = null;
310 QueryParser query = null;
312 String transactionId = null;
313 int depth = AAIProperties.MAXIMUM_DEPTH;
314 Format format = null;
315 List<Pair<URI, Response>> responses = new ArrayList<>();
316 MultivaluedMap<String, String> params = null;
317 HttpMethod method = null;
319 Boolean success = true;
320 QueryEngine queryEngine = dbEngine.getQueryEngine();
324 LoggingContext.save();
325 for (DBRequest request : requests) {
327 Status status = Status.NOT_FOUND;
328 method = request.getMethod();
330 for (retry = 0; retry < maxRetries; ++retry) {
333 LoggingContext.targetEntity(TARGET_ENTITY);
334 LoggingContext.targetServiceName(methodName + " " + method);
336 obj = request.getIntrospector();
337 query = request.getParser();
338 transactionId = request.getTransactionId();
339 uriTemp = request.getUri().getRawPath().replaceFirst("^v\\d+/", "");
340 uri = UriBuilder.fromPath(uriTemp).build();
341 LoggingContext.startTime();
342 List<Vertex> vertTemp;
343 List<Vertex> vertices;
344 if (this.isPaginated()) {
345 vertTemp = query.getQueryBuilder().toList();
346 this.setTotalsForPaging(vertTemp.size(), this.paginationBucket);
347 vertices = vertTemp.subList(((this.paginationIndex - 1) * this.paginationBucket), Math.min((this.paginationBucket * this.paginationIndex), vertTemp.size()));
349 vertices = query.getQueryBuilder().toList();
351 boolean isNewVertex = false;
352 String outputMediaType = getMediaType(request.getHeaders().getAcceptableMediaTypes());
353 String result = null;
354 params = request.getInfo().getQueryParameters(false);
355 depth = setDepth(obj, params.getFirst("depth"));
356 if (params.containsKey("format")) {
357 format = Format.getFormat(params.getFirst("format"));
359 String cleanUp = params.getFirst("cleanup");
360 String requestContext = "";
361 List<String> requestContextList = request.getHeaders().getRequestHeader("aai-request-context");
362 if (requestContextList != null) {
363 requestContext = requestContextList.get(0);
366 if (cleanUp == null) {
369 if (vertices.size() > 1 && processSingle && !(method.equals(HttpMethod.GET) || method.equals(HttpMethod.GET_RELATIONSHIP))) {
370 if (method.equals(HttpMethod.DELETE)) {
371 LoggingContext.restoreIfPossible();
372 throw new AAIException("AAI_6138");
374 LoggingContext.restoreIfPossible();
375 throw new AAIException("AAI_6137");
378 if (method.equals(HttpMethod.PUT)) {
379 String resourceVersion = (String) obj.getValue("resource-version");
380 if (vertices.isEmpty()) {
381 if (enableResourceVersion) {
382 serializer.verifyResourceVersion("create", query.getResultType(), "", resourceVersion, obj.getURI());
386 if (enableResourceVersion) {
387 serializer.verifyResourceVersion("update", query.getResultType(), vertices.get(0).<String>property("resource-version").orElse(null), resourceVersion, obj.getURI());
392 if (vertices.isEmpty()) {
393 String msg = createNotFoundMessage(query.getResultType(), request.getUri());
394 throw new AAIException("AAI_6114", msg);
403 HashMap<String, Introspector> relatedObjects = new HashMap<>();
404 String nodeOnly = params.getFirst("nodes-only");
405 boolean isNodeOnly = nodeOnly != null;
409 if (format == null) {
410 obj = this.getObjectFromDb(vertices, serializer, query, obj, request.getUri(), depth, isNodeOnly, cleanUp);
413 LoggingContext.elapsedTime((long) serializer.getDBTimeMsecs(), TimeUnit.MILLISECONDS);
414 LOGGER.info("Completed");
415 LoggingContext.restoreIfPossible();
419 MarshallerProperties properties;
420 if (!request.getMarshallerProperties().isPresent()) {
421 properties = new MarshallerProperties.Builder(org.onap.aai.restcore.MediaType.getEnum(outputMediaType)).build();
423 properties = request.getMarshallerProperties().get();
425 result = obj.marshal(properties);
428 FormatFactory ff = new FormatFactory(loader, serializer, schemaVersions, basePath + "/");
429 Formatter formatter = ff.get(format, params);
430 result = formatter.output(vertices.stream().map(vertex -> (Object) vertex).collect(Collectors.toList())).toString();
435 case GET_RELATIONSHIP:
436 if (format == null) {
437 obj = this.getRelationshipObjectFromDb(vertices, serializer, query, request.getInfo().getRequestUri());
439 LoggingContext.elapsedTime((long) serializer.getDBTimeMsecs(), TimeUnit.MILLISECONDS);
440 LOGGER.info("Completed");
441 LoggingContext.restoreIfPossible();
445 MarshallerProperties properties;
446 if (!request.getMarshallerProperties().isPresent()) {
447 properties = new MarshallerProperties.Builder(org.onap.aai.restcore.MediaType.getEnum(outputMediaType)).build();
449 properties = request.getMarshallerProperties().get();
451 result = obj.marshal(properties);
453 String msg = createRelationshipNotFoundMessage(query.getResultType(), request.getUri());
454 throw new AAIException("AAI_6149", msg);
457 FormatFactory ff = new FormatFactory(loader, serializer, schemaVersions, basePath + "/");
458 Formatter formatter = ff.get(format, params);
459 result = formatter.output(vertices.stream().map(vertex -> (Object) vertex).collect(Collectors.toList())).toString();
464 response = this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request, sourceOfTruth, version, loader, obj, uri, true);
466 v = serializer.createNewVertex(obj);
468 serializer.serializeToDb(obj, v, query, uri.getRawPath(), requestContext);
469 this.invokeExtension(dbEngine, this.dbEngine.tx(), HttpMethod.PUT, request, sourceOfTruth, version, loader, obj, uri, false);
472 status = Status.CREATED;
474 obj = serializer.getLatestVersionView(v);
475 if (query.isDependent()) {
476 relatedObjects = this.getRelatedObjects(serializer, queryEngine, v, obj, this.loader);
478 LoggingContext.elapsedTime((long) serializer.getDBTimeMsecs() +
479 (long) queryEngine.getDBTimeMsecs(), TimeUnit.MILLISECONDS);
480 LOGGER.info("Completed ");
481 LoggingContext.restoreIfPossible();
482 notification.createNotificationEvent(transactionId, sourceOfTruth, status, uri, obj, relatedObjects, basePath);
486 serializer.touchStandardVertexProperties(v, false);
487 this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request, sourceOfTruth, version, loader, obj, uri, true);
488 serializer.createEdge(obj, v);
490 LoggingContext.elapsedTime((long) serializer.getDBTimeMsecs(), TimeUnit.MILLISECONDS);
491 LOGGER.info("Completed");
492 LoggingContext.restoreIfPossible();
494 notification.createNotificationEvent(transactionId, sourceOfTruth, status, new URI(uri.toString().replace("/relationship-list/relationship", "")), serializer.getLatestVersionView(v), relatedObjects, basePath);
497 Introspector existingObj = loader.introspectorFromName(obj.getDbName());
498 existingObj = this.getObjectFromDb(vertices, serializer, query, existingObj, request.getUri(), 0, false, cleanUp);
499 String existingJson = existingObj.marshal(false);
502 if (request.getRawRequestContent().isPresent()) {
503 newJson = request.getRawRequestContent().get();
507 Object relationshipList = request.getIntrospector().getValue("relationship-list");
508 ObjectMapper mapper = new ObjectMapper();
510 JsonNode existingNode = mapper.readTree(existingJson);
511 JsonNode newNode = mapper.readTree(newJson);
512 JsonMergePatch patch = JsonMergePatch.fromJson(newNode);
513 JsonNode completed = patch.apply(existingNode);
514 String patched = mapper.writeValueAsString(completed);
515 Introspector patchedObj = loader.unmarshal(existingObj.getName(), patched);
516 if (relationshipList == null) {
517 //if the caller didn't touch the relationship-list, we shouldn't either
518 patchedObj.setValue("relationship-list", null);
520 serializer.serializeToDb(patchedObj, v, query, uri.getRawPath(), requestContext);
522 patchedObj = serializer.getLatestVersionView(v);
523 if (query.isDependent()) {
524 relatedObjects = this.getRelatedObjects(serializer, queryEngine, v, patchedObj, this.loader);
526 LoggingContext.elapsedTime((long) serializer.getDBTimeMsecs() +
527 (long) queryEngine.getDBTimeMsecs(), TimeUnit.MILLISECONDS);
528 LOGGER.info("Completed");
529 LoggingContext.restoreIfPossible();
530 notification.createNotificationEvent(transactionId, sourceOfTruth, status, uri, patchedObj, relatedObjects, basePath);
531 } catch (IOException | JsonPatchException e) {
533 LOGGER.info("Caught exception: " + e.getMessage());
534 LoggingContext.restoreIfPossible();
535 throw new AAIException("AAI_3000", "could not perform patch operation");
539 String resourceVersion = params.getFirst("resource-version");
540 obj = serializer.getLatestVersionView(v);
541 if (query.isDependent()) {
542 relatedObjects = this.getRelatedObjects(serializer, queryEngine, v, obj, this.loader);
545 * Find all Delete-other-vertex vertices and create structure for notify
546 * findDeleatble also returns the startVertex v and we dont want to create
547 * duplicate notification events for the same
548 * So remove the startvertex first
551 List<Vertex> deletableVertices = dbEngine.getQueryEngine().findDeletable(v);
552 Long vId = (Long) v.id();
555 * I am assuming vertexId cant be null
557 deletableVertices.removeIf(s -> vId.equals(s.id()));
558 boolean isDelVerticesPresent = !deletableVertices.isEmpty();
559 Map<Vertex, Introspector> deleteObjects = new HashMap<>();
560 Map<String, URI> uriMap = new HashMap<>();
561 Map<String, HashMap<String, Introspector>> deleteRelatedObjects = new HashMap<>();
563 if (isDelVerticesPresent) {
564 deleteObjects = this.buildIntrospectorObjects(serializer, deletableVertices);
566 uriMap = this.buildURIMap(serializer, deleteObjects);
567 deleteRelatedObjects = this.buildRelatedObjects(serializer, queryEngine, deleteObjects);
570 this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request, sourceOfTruth, version, loader, obj, uri, true);
571 serializer.delete(v, deletableVertices, resourceVersion, enableResourceVersion);
572 this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request, sourceOfTruth, version, loader, obj, uri, false);
574 LoggingContext.elapsedTime((long) serializer.getDBTimeMsecs() +
575 (long) queryEngine.getDBTimeMsecs(), TimeUnit.MILLISECONDS);
576 LOGGER.info("Completed");
577 LoggingContext.restoreIfPossible();
578 status = Status.NO_CONTENT;
579 notification.createNotificationEvent(transactionId, sourceOfTruth, status, uri, obj, relatedObjects, basePath);
582 * Notify delete-other-v candidates
585 if (isDelVerticesPresent) {
586 this.buildNotificationEvent(sourceOfTruth, status, transactionId, notification, deleteObjects,
587 uriMap, deleteRelatedObjects, basePath);
592 serializer.touchStandardVertexProperties(v, false);
593 serializer.deleteEdge(obj, v);
595 LoggingContext.elapsedTime((long) serializer.getDBTimeMsecs(), TimeUnit.MILLISECONDS);
596 LOGGER.info("Completed");
597 LoggingContext.restoreIfPossible();
598 status = Status.NO_CONTENT;
599 notification.createNotificationEvent(transactionId, sourceOfTruth, Status.OK, new URI(uri.toString().replace("/relationship-list/relationship", "")), serializer.getLatestVersionView(v), relatedObjects, basePath);
606 /* temporarily adding vertex id to the headers
607 * to be able to use for testing the vertex id endpoint functionality
608 * since we presently have no other way of generating those id urls
610 if (response == null && v != null && (
611 method.equals(HttpMethod.PUT)
612 || method.equals(HttpMethod.GET)
613 || method.equals(HttpMethod.MERGE_PATCH)
614 || method.equals(HttpMethod.GET_RELATIONSHIP))
617 String myvertid = v.id().toString();
618 if (this.isPaginated()) {
619 response = Response.status(status)
620 .header("vertex-id", myvertid)
621 .header("total-results", this.getTotalVertices())
622 .header("total-pages", this.getTotalPaginationBuckets())
624 .type(outputMediaType).build();
626 response = Response.status(status)
627 .header("vertex-id", myvertid)
629 .type(outputMediaType).build();
631 } else if (response == null) {
632 response = Response.status(status)
633 .type(outputMediaType).build();
635 //response already set to something
637 Pair<URI, Response> pairedResp = Pair.with(request.getUri(), response);
638 responses.add(pairedResp);
639 //break out of retry loop
641 } catch (JanusGraphException e) {
642 this.dbEngine.rollback();
644 LOGGER.info("Caught exception: " + e.getMessage());
645 LoggingContext.restoreIfPossible();
646 AAIException ex = new AAIException("AAI_6142", e);
647 ErrorLogHelper.logException(ex);
648 Thread.sleep((retry + 1) * 20L);
649 this.dbEngine.startTransaction();
650 queryEngine = dbEngine.getQueryEngine();
651 serializer = new DBSerializer(version, dbEngine, introspectorFactoryType, sourceOfTruth);
653 if (retry == maxRetries) {
654 throw new AAIException("AAI_6134");
657 } catch (AAIException e) {
659 ArrayList<String> templateVars = new ArrayList<>();
660 templateVars.add(request.getMethod().toString()); //GET, PUT, etc
661 templateVars.add(request.getUri().getPath());
662 templateVars.addAll(e.getTemplateVars());
663 ErrorLogHelper.logException(e);
665 .status(e.getErrorObject().getHTTPResponseCode())
666 .entity(ErrorLogHelper.getRESTAPIErrorResponse(request.getHeaders().getAcceptableMediaTypes(), e, templateVars))
668 Pair<URI, Response> pairedResp = Pair.with(request.getUri(), response);
669 responses.add(pairedResp);
671 } catch (Exception e) {
674 AAIException ex = new AAIException("AAI_4000", e);
675 ArrayList<String> templateVars = new ArrayList<String>();
676 templateVars.add(request.getMethod().toString()); //GET, PUT, etc
677 templateVars.add(request.getUri().getPath().toString());
678 ErrorLogHelper.logException(ex);
680 .status(ex.getErrorObject().getHTTPResponseCode())
681 .entity(ErrorLogHelper.getRESTAPIErrorResponse(request.getHeaders().getAcceptableMediaTypes(), ex, templateVars))
683 Pair<URI, Response> pairedResp = Pair.with(request.getUri(), response);
684 responses.add(pairedResp);
688 notification.triggerEvents();
689 return Pair.with(success, responses);
694 * Gets the media type.
696 * @param mediaTypeList the media type list
697 * @return the media type
699 private String getMediaType(List<MediaType> mediaTypeList) {
700 String mediaType = MediaType.APPLICATION_JSON; // json is the default
701 for (MediaType mt : mediaTypeList) {
702 if (MediaType.APPLICATION_XML_TYPE.isCompatible(mt)) {
703 mediaType = MediaType.APPLICATION_XML;
710 * Gets the object from db.
712 * @param serializer the serializer
713 * @param query the query
716 * @param depth the depth
717 * @param cleanUp the clean up
718 * @return the object from db
719 * @throws AAIException the AAI exception
720 * @throws IllegalAccessException the illegal access exception
721 * @throws IllegalArgumentException the illegal argument exception
722 * @throws InvocationTargetException the invocation target exception
723 * @throws SecurityException the security exception
724 * @throws InstantiationException the instantiation exception
725 * @throws NoSuchMethodException the no such method exception
726 * @throws UnsupportedEncodingException the unsupported encoding exception
727 * @throws MalformedURLException the malformed URL exception
728 * @throws AAIUnknownObjectException
729 * @throws URISyntaxException
731 private Introspector getObjectFromDb(List<Vertex> results, DBSerializer serializer, QueryParser query, Introspector obj, URI uri, int depth, boolean nodeOnly, String cleanUp) throws AAIException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, InstantiationException, NoSuchMethodException, UnsupportedEncodingException, AAIUnknownObjectException, URISyntaxException {
734 if (results.isEmpty()) {
735 String msg = createNotFoundMessage(query.getResultType(), uri);
736 throw new AAIException("AAI_6114", msg);
739 return serializer.dbToObject(results, obj, depth, nodeOnly, cleanUp);
744 * Gets the object from db.
746 * @param serializer the serializer
747 * @param query the query
750 * @param depth the depth
751 * @param cleanUp the clean up
752 * @return the object from db
753 * @throws AAIException the AAI exception
754 * @throws IllegalAccessException the illegal access exception
755 * @throws IllegalArgumentException the illegal argument exception
756 * @throws InvocationTargetException the invocation target exception
757 * @throws SecurityException the security exception
758 * @throws InstantiationException the instantiation exception
759 * @throws NoSuchMethodException the no such method exception
760 * @throws UnsupportedEncodingException the unsupported encoding exception
761 * @throws MalformedURLException the malformed URL exception
762 * @throws AAIUnknownObjectException
763 * @throws URISyntaxException
765 private Introspector getRelationshipObjectFromDb(List<Vertex> results, DBSerializer serializer, QueryParser query, URI uri) throws AAIException, IllegalArgumentException, SecurityException, UnsupportedEncodingException, AAIUnknownObjectException {
768 if (results.isEmpty()) {
769 String msg = createNotFoundMessage(query.getResultType(), uri);
770 throw new AAIException("AAI_6114", msg);
773 if (results.size() > 1) {
774 throw new AAIException("AAI_6148", uri.getPath());
777 Vertex v = results.get(0);
778 return serializer.dbToRelationshipObject(v);
784 * @param dbEngine the db engine
786 * @param httpMethod the http method
787 * @param fromAppId the from app id
788 * @param apiVersion the api version
789 * @param loader the loader
792 * @param isPreprocess the is preprocess
793 * @return the response
794 * @throws IllegalArgumentException the illegal argument exception
795 * @throws UnsupportedEncodingException the unsupported encoding exception
796 * @throws AAIException the AAI exception
798 private Response invokeExtension(TransactionalGraphEngine dbEngine, Graph g, HttpMethod httpMethod, DBRequest request, String fromAppId, SchemaVersion apiVersion, Loader loader, Introspector obj, URI uri, boolean isPreprocess) throws IllegalArgumentException, UnsupportedEncodingException, AAIException {
799 AAIExtensionMap aaiExtMap = new AAIExtensionMap();
800 //ModelInjestor injestor = ModelInjestor.getInstance();
801 Response response = null;
802 URIToExtensionInformation extensionInformation = new URIToExtensionInformation(loader, uri);
803 aaiExtMap.setHttpEntry(this);
804 aaiExtMap.setDbRequest(request);
805 aaiExtMap.setTransId(request.getTransactionId());
806 aaiExtMap.setFromAppId(fromAppId);
807 aaiExtMap.setGraph(g);
808 aaiExtMap.setApiVersion(apiVersion.toString());
809 aaiExtMap.setObjectFromRequest(obj);
810 aaiExtMap.setObjectFromRequestType(obj.getJavaClassName());
811 aaiExtMap.setObjectFromResponse(obj);
812 aaiExtMap.setObjectFromResponseType(obj.getJavaClassName());
813 aaiExtMap.setJaxbContext(nodeIngestor.getContextForVersion(apiVersion));
814 aaiExtMap.setUri(uri.getRawPath());
815 aaiExtMap.setTransactionalGraphEngine(dbEngine);
816 aaiExtMap.setLoader(loader);
817 aaiExtMap.setNamespace(extensionInformation.getNamespace());
819 ExtensionController ext = new ExtensionController();
820 ext.runExtension(aaiExtMap.getApiVersion(),
821 extensionInformation.getNamespace(),
822 extensionInformation.getTopObject(),
823 extensionInformation.getMethodName(httpMethod, isPreprocess),
827 if (aaiExtMap.getPrecheckAddedList().size() > 0) {
828 response = notifyOnSkeletonCreation(aaiExtMap, obj, request.getHeaders());
835 * Notify on skeleton creation.
837 * @param aaiExtMap the aai ext map
838 * @param input the input
839 * @param headers the headers
840 * @return the response
843 private Response notifyOnSkeletonCreation(AAIExtensionMap aaiExtMap, Introspector input, HttpHeaders headers) {
844 Response response = null;
845 HashMap<AAIException, ArrayList<String>> exceptionList = new HashMap<AAIException, ArrayList<String>>();
847 StringBuilder keyString = new StringBuilder();
849 Set<String> resourceKeys = input.getKeys();
850 for (String key : resourceKeys) {
851 keyString.append(key).append("=").append(input.getValue(key).toString()).append(" ");
854 for (AAIResponseMessage msg : aaiExtMap.getPrecheckResponseMessages().getAAIResponseMessage()) {
855 ArrayList<String> templateVars = new ArrayList<>();
857 templateVars.add("PUT " + input.getDbName());
858 templateVars.add(keyString.toString());
859 List<String> keys = new ArrayList<>();
860 templateVars.add(msg.getAaiResponseMessageResourceType());
861 for (AAIResponseMessageDatum dat : msg.getAaiResponseMessageData().getAAIResponseMessageDatum()) {
862 keys.add(dat.getAaiResponseMessageDatumKey() + "=" + dat.getAaiResponseMessageDatumValue());
864 templateVars.add(StringUtils.join(keys, ", "));
865 exceptionList.put(new AAIException("AAI_0004", msg.getAaiResponseMessageResourceType()),
869 .status(Status.ACCEPTED).entity(ErrorLogHelper
870 .getRESTAPIInfoResponse(headers.getAcceptableMediaTypes(), exceptionList))
877 * Creates the not found message.
879 * @param resultType the result type
883 private String createNotFoundMessage(String resultType, URI uri) {
885 String msg = "No Node of type " + resultType + " found at: " + uri.getPath();
891 * Creates the not found message.
893 * @param resultType the result type
897 private String createRelationshipNotFoundMessage(String resultType, URI uri) {
899 String msg = "No relationship found of type " + resultType + " at the given URI: " + uri.getPath() + "/relationship-list";
907 * @param depthParam the depth param
909 * @throws AAIException the AAI exception
911 protected int setDepth(Introspector obj, String depthParam) throws AAIException {
912 int depth = AAIProperties.MAXIMUM_DEPTH;
914 String getAllRandomStr = AAIConfig.get("aai.rest.getall.depthparam", "");
915 if (depthParam != null && getAllRandomStr != null && !getAllRandomStr.isEmpty()
916 && getAllRandomStr.equals(depthParam)) {
920 if (depthParam == null) {
921 if (this.version.compareTo(schemaVersions.getDepthVersion()) >= 0) {
924 depth = AAIProperties.MAXIMUM_DEPTH;
927 if (!depthParam.isEmpty() && !"all".equals(depthParam)) {
929 depth = Integer.parseInt(depthParam);
930 } catch (Exception e) {
931 throw new AAIException("AAI_4016");
936 String maxDepth = obj.getMetadata(ObjectMetadata.MAXIMUM_DEPTH);
938 int maximumDepth = AAIProperties.MAXIMUM_DEPTH;
940 if (maxDepth != null) {
942 maximumDepth = Integer.parseInt(maxDepth);
943 } catch (Exception ex) {
944 throw new AAIException("AAI_4018");
948 if (depth > maximumDepth) {
949 throw new AAIException("AAI_3303");
956 * Checks if is modification method.
958 * @param method the method
959 * @return true, if is modification method
961 private boolean isModificationMethod(HttpMethod method) {
962 boolean result = false;
964 if (method.equals(HttpMethod.PUT) || method.equals(HttpMethod.PUT_EDGE) || method.equals(HttpMethod.DELETE_EDGE) || method.equals(HttpMethod.MERGE_PATCH)) {
973 * Given an uri, introspector object and loader object
974 * it will check if the obj is top level object if it is,
975 * it will return immediately returning the uri passed in
976 * If it isn't, it will go through, get the uriTemplate
977 * from the introspector object and get the count of "/"s
978 * and remove that part of the uri using substring
979 * and keep doing that until the current object is top level
980 * Also added the max depth just so worst case scenario
981 * Then keep adding aai-uri to the list include the aai-uri passed in
982 * Convert that list into an array and return it
988 * aai-uri -> /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1/vservers/vserver/v1
990 * Given the uriTemplate vserver -> /vservers/vserver/{vserver-id}
991 * it converts to /vservers/vserver
993 * lastIndexOf /vservers/vserver in /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1/vservers/vserver/v1
998 * Use substring to get the string from 0 to that lastIndexOf
999 * aai-uri -> /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1
1001 * From this new aai-uri, generate a introspector from the URITOObject class
1002 * and keep doing this until you
1006 * @param aaiUri - aai-uri of the vertex representating the unique id of a given vertex
1007 * @param obj - introspector object of the given starting vertex
1008 * @param loader - Type of loader which will always be MoxyLoader to support model driven
1009 * @return an array of strings which can be used to get the vertexes of parent and grand parents from a given vertex
1010 * @throws UnsupportedEncodingException
1011 * @throws AAIException
1013 String[] convertIntrospectorToUriList(String aaiUri, Introspector obj, Loader loader) throws UnsupportedEncodingException, AAIException {
1015 List<String> uriList = new ArrayList<>();
1016 String template = StringUtils.EMPTY;
1017 String truncatedUri = aaiUri;
1018 int depth = AAIProperties.MAXIMUM_DEPTH;
1019 uriList.add(truncatedUri);
1021 while (depth >= 0 && !obj.isTopLevel()) {
1022 template = obj.getMetadata(ObjectMetadata.URI_TEMPLATE);
1024 if (template == null) {
1025 LOGGER.warn("Unable to find the uriTemplate for the object {}", obj.getDbName());
1029 int templateCount = StringUtils.countMatches(template, "/");
1030 int truncatedUriCount = StringUtils.countMatches(truncatedUri, "/");
1032 if (templateCount > truncatedUriCount) {
1033 LOGGER.warn("Template uri {} contains more slashes than truncatedUri {}", template, truncatedUri);
1037 int cutIndex = StringUtils.ordinalIndexOf(truncatedUri, "/", truncatedUriCount - templateCount + 1);
1038 truncatedUri = StringUtils.substring(truncatedUri, 0, cutIndex);
1039 uriList.add(truncatedUri);
1040 obj = new URIToObject(loader, UriBuilder.fromPath(truncatedUri).build()).getEntity();
1044 return uriList.toArray(new String[uriList.size()]);
1050 * @param queryEngine
1055 * @throws IllegalAccessException
1056 * @throws IllegalArgumentException
1057 * @throws InvocationTargetException
1058 * @throws SecurityException
1059 * @throws InstantiationException
1060 * @throws NoSuchMethodException
1061 * @throws UnsupportedEncodingException
1062 * @throws AAIException
1063 * @throws URISyntaxException
1065 private HashMap<String, Introspector> getRelatedObjects(DBSerializer serializer, QueryEngine queryEngine, Vertex v, Introspector obj, Loader loader) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, InstantiationException, NoSuchMethodException, UnsupportedEncodingException, AAIException, URISyntaxException {
1067 HashMap<String, Introspector> relatedVertices = new HashMap<>();
1068 VertexProperty aaiUriProperty = v.property(AAIProperties.AAI_URI);
1070 if (!aaiUriProperty.isPresent()) {
1071 if (LOGGER.isDebugEnabled()) {
1072 LOGGER.debug("For the given vertex {}, it seems aai-uri is not present so not getting related objects", v.id().toString());
1074 LOGGER.info("It seems aai-uri is not present in vertex, so not getting related objects, for more info enable debug log");
1076 return relatedVertices;
1079 String aaiUri = aaiUriProperty.value().toString();
1081 if (!obj.isTopLevel()) {
1082 String[] uriList = convertIntrospectorToUriList(aaiUri, obj, loader);
1083 List<Vertex> vertexChain = null;
1084 // If the uriList is null then there is something wrong with converting the uri
1085 // into a list of aai-uris so falling back to the old mechanism for finding parents
1086 if (uriList == null) {
1087 LOGGER.info("Falling back to the old mechanism due to unable to convert aai-uri to list of uris but this is not optimal");
1088 vertexChain = queryEngine.findParents(v);
1090 vertexChain = queryEngine.findParents(uriList);
1092 for (Vertex vertex : vertexChain) {
1094 final Introspector vertexObj = serializer.getVertexProperties(vertex);
1095 relatedVertices.put(vertexObj.getObjectId(), vertexObj);
1096 } catch (AAIUnknownObjectException e) {
1097 LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned");
1102 final Introspector vertexObj = serializer.getVertexProperties(v);
1103 relatedVertices.put(vertexObj.getObjectId(), vertexObj);
1104 } catch (AAIUnknownObjectException e) {
1105 LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned");
1109 return relatedVertices;
1112 private Map<Vertex, Introspector> buildIntrospectorObjects(DBSerializer serializer, Iterable<Vertex> vertices) {
1113 Map<Vertex, Introspector> deleteObjectMap = new HashMap<>();
1114 for (Vertex vertex : vertices) {
1116 // deleteObjectMap.computeIfAbsent(vertex, s ->
1117 // serializer.getLatestVersionView(vertex));
1118 Introspector deleteObj = serializer.getLatestVersionView(vertex);
1119 deleteObjectMap.put(vertex, deleteObj);
1120 } catch (UnsupportedEncodingException | AAIException e) {
1121 LOGGER.warn("Unable to get Introspctor Objects, Just continue");
1127 return deleteObjectMap;
1131 private Map<String, URI> buildURIMap(DBSerializer serializer, Map<Vertex, Introspector> introSpector) {
1132 Map<String, URI> uriMap = new HashMap<>();
1133 for (Map.Entry<Vertex, Introspector> entry : introSpector.entrySet()) {
1136 uri = serializer.getURIForVertex(entry.getKey());
1137 if (null != entry.getValue()) {
1138 uriMap.put(entry.getValue().getObjectId(), uri);
1140 } catch (UnsupportedEncodingException e) {
1141 LOGGER.warn("Unable to get URIs, Just continue");
1151 private Map<String, HashMap<String, Introspector>> buildRelatedObjects(DBSerializer serializer,
1152 QueryEngine queryEngine, Map<Vertex, Introspector> introSpector) {
1154 Map<String, HashMap<String, Introspector>> relatedObjectsMap = new HashMap<>();
1155 for (Map.Entry<Vertex, Introspector> entry : introSpector.entrySet()) {
1157 HashMap<String, Introspector> relatedObjects = this.getRelatedObjects(serializer, queryEngine,
1158 entry.getKey(), entry.getValue(), this.loader);
1159 if (null != entry.getValue()) {
1160 relatedObjectsMap.put(entry.getValue().getObjectId(), relatedObjects);
1162 } catch (IllegalAccessException | IllegalArgumentException
1163 | InvocationTargetException | SecurityException | InstantiationException | NoSuchMethodException
1164 | UnsupportedEncodingException | AAIException | URISyntaxException e) {
1165 LOGGER.warn("Unable to get realted Objects, Just continue");
1171 return relatedObjectsMap;
1175 private void buildNotificationEvent(String sourceOfTruth, Status status, String transactionId,
1176 UEBNotification notification, Map<Vertex, Introspector> deleteObjects, Map<String, URI> uriMap,
1177 Map<String, HashMap<String, Introspector>> deleteRelatedObjects, String basePath) {
1178 for (Map.Entry<Vertex, Introspector> entry : deleteObjects.entrySet()) {
1180 String vertexObjectId = "";
1182 if (null != entry.getValue()) {
1183 vertexObjectId = entry.getValue().getObjectId();
1185 if (uriMap.containsKey(vertexObjectId) && deleteRelatedObjects.containsKey(vertexObjectId)) {
1186 notification.createNotificationEvent(transactionId, sourceOfTruth, status,
1187 uriMap.get(vertexObjectId), entry.getValue(), deleteRelatedObjects.get(vertexObjectId), basePath);
1190 } catch (UnsupportedEncodingException | AAIException e) {
1192 LOGGER.warn("Error in sending notification");
1197 public void setPaginationParameters(String resultIndex, String resultSize) {
1198 if (resultIndex != null && resultIndex != "-1" && resultSize != null && resultSize != "-1") {
1199 this.setPaginationIndex(Integer.parseInt(resultIndex));
1200 this.setPaginationBucket(Integer.parseInt(resultSize));
1204 public List<Object> getPaginatedVertexList(List<Object> vertexList) throws AAIException {
1205 List<Object> vertices;
1206 if (this.isPaginated()) {
1207 this.setTotalsForPaging(vertexList.size(), this.getPaginationBucket());
1208 int startIndex = (this.getPaginationIndex() - 1) * this.getPaginationBucket();
1209 int endIndex = Math.min((this.getPaginationBucket() * this.getPaginationIndex()), vertexList.size());
1210 if (startIndex > endIndex) {
1211 throw new AAIException("AAI_6150", " ResultIndex is not appropriate for the result set, Needs to be <= " + endIndex);
1213 vertices = vertexList.subList(startIndex, endIndex);
1215 vertices = vertexList;