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.rest.db;
23 import com.att.eelf.configuration.EELFLogger;
24 import com.att.eelf.configuration.EELFManager;
25 import com.fasterxml.jackson.databind.JsonNode;
26 import com.fasterxml.jackson.databind.ObjectMapper;
27 import com.github.fge.jsonpatch.JsonPatchException;
28 import com.github.fge.jsonpatch.mergepatch.JsonMergePatch;
30 import java.io.IOException;
31 import java.io.UnsupportedEncodingException;
32 import java.lang.reflect.InvocationTargetException;
33 import java.net.MalformedURLException;
35 import java.net.URISyntaxException;
36 import java.util.ArrayList;
37 import java.util.HashMap;
38 import java.util.List;
41 import java.util.concurrent.TimeUnit;
42 import java.util.stream.Collectors;
44 import javax.ws.rs.core.HttpHeaders;
45 import javax.ws.rs.core.MediaType;
46 import javax.ws.rs.core.MultivaluedMap;
47 import javax.ws.rs.core.Response;
48 import javax.ws.rs.core.Response.Status;
49 import javax.ws.rs.core.UriBuilder;
51 import org.apache.commons.lang.StringUtils;
52 import org.apache.tinkerpop.gremlin.structure.Graph;
53 import org.apache.tinkerpop.gremlin.structure.Vertex;
54 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
55 import org.janusgraph.core.JanusGraphException;
56 import org.javatuples.Pair;
57 import org.json.JSONException;
58 import org.json.JSONObject;
59 import org.onap.aai.db.props.AAIProperties;
60 import org.onap.aai.dbmap.DBConnectionType;
61 import org.onap.aai.domain.responseMessage.AAIResponseMessage;
62 import org.onap.aai.domain.responseMessage.AAIResponseMessageDatum;
63 import org.onap.aai.exceptions.AAIException;
64 import org.onap.aai.extensions.AAIExtensionMap;
65 import org.onap.aai.extensions.ExtensionController;
66 import org.onap.aai.introspection.*;
67 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
68 import org.onap.aai.logging.ErrorLogHelper;
69 import org.onap.aai.logging.LoggingContext;
70 import org.onap.aai.nodes.NodeIngestor;
71 import org.onap.aai.parsers.query.QueryParser;
72 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(queryStyle, connectionType, loader);
146 getDbEngine().startTransaction();
147 this.notification = new UEBNotification(loader, loaderFactory, schemaVersions);
151 public HttpEntry setHttpEntryProperties(SchemaVersion version, DBConnectionType connectionType,
152 UEBNotification notification) {
153 this.version = version;
154 this.loader = loaderFactory.createLoaderForVersion(introspectorFactoryType, version);
155 this.dbEngine = new JanusGraphDBEngine(queryStyle, connectionType, loader);
157 this.notification = notification;
158 // start transaction on creation
159 getDbEngine().startTransaction();
164 * Gets the introspector factory type.
166 * @return the introspector factory type
168 public ModelType getIntrospectorFactoryType() {
169 return introspectorFactoryType;
173 * Gets the query style.
175 * @return the query style
177 public QueryStyle getQueryStyle() {
184 * @return the version
186 public SchemaVersion getVersion() {
195 public Loader getLoader() {
200 * Gets the db engine.
202 * @return the db engine
204 public TransactionalGraphEngine getDbEngine() {
208 public Pair<Boolean, List<Pair<URI, Response>>> process(List<DBRequest> requests, String sourceOfTruth)
209 throws AAIException {
210 return this.process(requests, sourceOfTruth, true);
214 * Checks the pagination bucket and pagination index variables to determine whether or not the user
215 * requested paginated results
217 * @return a boolean true/false of whether the user requested paginated results
219 public boolean isPaginated() {
220 return this.paginationBucket > -1 && this.paginationIndex > -1;
224 * Returns the pagination size
226 * @return integer of the size of results to be returned when paginated
228 public int getPaginationBucket() {
229 return this.paginationBucket;
233 * Setter for the pagination bucket variable which stores in this object the size of results to return
237 public void setPaginationBucket(int pb) {
238 this.paginationBucket = pb;
242 * Getter to return the pagination index requested by the user when requesting paginated results
246 public int getPaginationIndex() {
247 return this.paginationIndex;
251 * Sets the pagination index that was passed in by the user, to determine which index or results to retrieve when
256 public void setPaginationIndex(int pi) {
260 this.paginationIndex = pi;
264 * Sets the total vertices variables and calculates the amount of pages based on size and total vertices
266 * @param totalVertices
267 * @param paginationBucketSize
269 public void setTotalsForPaging(int totalVertices, int paginationBucketSize) {
270 this.totalVertices = totalVertices;
271 // set total number of buckets equal to full pages
272 this.totalPaginationBuckets = totalVertices / paginationBucketSize;
273 // conditionally add a page for the remainder
274 if (totalVertices % paginationBucketSize > 0) {
275 this.totalPaginationBuckets++;
280 * @return the total amount of pages
282 public int getTotalPaginationBuckets() {
283 return this.totalPaginationBuckets;
288 * @return the total number of vertices when paginated
290 public int getTotalVertices() {
291 return this.totalVertices;
297 * @param requests the requests
298 * @param sourceOfTruth the source of truth
301 * @throws AAIException the AAI exception
303 public Pair<Boolean, List<Pair<URI, Response>>> process(List<DBRequest> requests, String sourceOfTruth,
304 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();
332 LoggingContext.targetEntity(TARGET_ENTITY);
333 LoggingContext.targetServiceName(methodName + " " + method);
335 obj = request.getIntrospector();
336 query = request.getParser();
337 transactionId = request.getTransactionId();
338 uriTemp = request.getUri().getRawPath().replaceFirst("^v\\d+/", "");
339 uri = UriBuilder.fromPath(uriTemp).build();
340 LoggingContext.startTime();
341 List<Vertex> vertTemp;
342 List<Vertex> vertices;
343 if (this.isPaginated()) {
344 vertTemp = query.getQueryBuilder().toList();
345 this.setTotalsForPaging(vertTemp.size(), this.paginationBucket);
346 vertices = vertTemp.subList(((this.paginationIndex - 1) * this.paginationBucket),
347 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
370 && !(method.equals(HttpMethod.GET) || method.equals(HttpMethod.GET_RELATIONSHIP))) {
371 if (method.equals(HttpMethod.DELETE)) {
372 LoggingContext.restoreIfPossible();
373 throw new AAIException("AAI_6138");
375 LoggingContext.restoreIfPossible();
376 throw new AAIException("AAI_6137");
379 if (method.equals(HttpMethod.PUT)) {
380 String resourceVersion = (String) obj.getValue("resource-version");
381 if (vertices.isEmpty()) {
382 if (enableResourceVersion) {
383 serializer.verifyResourceVersion("create", query.getResultType(), "",
384 resourceVersion, obj.getURI());
388 if (enableResourceVersion) {
389 serializer.verifyResourceVersion("update", query.getResultType(),
390 vertices.get(0).<String>property("resource-version").orElse(null),
391 resourceVersion, obj.getURI());
396 if (vertices.isEmpty()) {
397 String msg = createNotFoundMessage(query.getResultType(), request.getUri());
398 throw new AAIException("AAI_6114", msg);
407 HashMap<String, Introspector> relatedObjects = new HashMap<>();
408 String nodeOnly = params.getFirst("nodes-only");
409 boolean isNodeOnly = nodeOnly != null;
413 if (format == null) {
414 obj = this.getObjectFromDb(vertices, serializer, query, obj, request.getUri(),
415 depth, isNodeOnly, cleanUp);
417 LoggingContext.elapsedTime((long) serializer.getDBTimeMsecs(),
418 TimeUnit.MILLISECONDS);
419 LOGGER.info("Completed");
420 LoggingContext.restoreIfPossible();
424 MarshallerProperties properties;
425 if (!request.getMarshallerProperties().isPresent()) {
426 properties = new MarshallerProperties.Builder(
427 org.onap.aai.restcore.MediaType.getEnum(outputMediaType)).build();
429 properties = request.getMarshallerProperties().get();
431 result = obj.marshal(properties);
435 new FormatFactory(loader, serializer, schemaVersions, basePath + "/");
436 Formatter formatter = ff.get(format, params);
437 result = formatter.output(vertices.stream().map(vertex -> (Object) vertex)
438 .collect(Collectors.toList())).toString();
443 case GET_RELATIONSHIP:
444 if (format == null) {
445 obj = this.getRelationshipObjectFromDb(vertices, serializer, query,
446 request.getInfo().getRequestUri());
448 LoggingContext.elapsedTime((long) serializer.getDBTimeMsecs(),
449 TimeUnit.MILLISECONDS);
450 LOGGER.info("Completed");
451 LoggingContext.restoreIfPossible();
455 MarshallerProperties properties;
456 if (!request.getMarshallerProperties().isPresent()) {
457 properties = new MarshallerProperties.Builder(
458 org.onap.aai.restcore.MediaType.getEnum(outputMediaType)).build();
460 properties = request.getMarshallerProperties().get();
462 result = obj.marshal(properties);
464 String msg = createRelationshipNotFoundMessage(query.getResultType(),
466 throw new AAIException("AAI_6149", msg);
470 new FormatFactory(loader, serializer, schemaVersions, basePath + "/");
471 Formatter formatter = ff.get(format, params);
472 result = formatter.output(vertices.stream().map(vertex -> (Object) vertex)
473 .collect(Collectors.toList())).toString();
478 response = this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request,
479 sourceOfTruth, version, loader, obj, uri, true);
481 v = serializer.createNewVertex(obj);
483 serializer.serializeToDb(obj, v, query, uri.getRawPath(), requestContext);
484 this.invokeExtension(dbEngine, this.dbEngine.tx(), HttpMethod.PUT, request,
485 sourceOfTruth, version, loader, obj, uri, false);
488 status = Status.CREATED;
490 obj = serializer.getLatestVersionView(v);
491 if (query.isDependent()) {
493 this.getRelatedObjects(serializer, queryEngine, v, obj, this.loader);
495 LoggingContext.elapsedTime(
496 (long) serializer.getDBTimeMsecs() + (long) queryEngine.getDBTimeMsecs(),
497 TimeUnit.MILLISECONDS);
498 LOGGER.info("Completed ");
499 LoggingContext.restoreIfPossible();
500 notification.createNotificationEvent(transactionId, sourceOfTruth, status, uri, obj,
501 relatedObjects, basePath);
505 serializer.touchStandardVertexProperties(v, false);
506 this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request, sourceOfTruth,
507 version, loader, obj, uri, true);
508 serializer.createEdge(obj, v);
510 LoggingContext.elapsedTime((long) serializer.getDBTimeMsecs(), TimeUnit.MILLISECONDS);
511 LOGGER.info("Completed");
512 LoggingContext.restoreIfPossible();
514 notification.createNotificationEvent(transactionId, sourceOfTruth, status,
515 new URI(uri.toString().replace("/relationship-list/relationship", "")),
516 serializer.getLatestVersionView(v), relatedObjects, basePath);
519 Introspector existingObj = loader.introspectorFromName(obj.getDbName());
520 existingObj = this.getObjectFromDb(vertices, serializer, query, existingObj,
521 request.getUri(), 0, false, cleanUp);
522 String existingJson = existingObj.marshal(false);
525 if (request.getRawRequestContent().isPresent()) {
526 newJson = request.getRawRequestContent().get();
530 Object relationshipList = request.getIntrospector().getValue("relationship-list");
531 ObjectMapper mapper = new ObjectMapper();
533 JsonNode existingNode = mapper.readTree(existingJson);
534 JsonNode newNode = mapper.readTree(newJson);
535 JsonMergePatch patch = JsonMergePatch.fromJson(newNode);
536 JsonNode completed = patch.apply(existingNode);
537 String patched = mapper.writeValueAsString(completed);
538 Introspector patchedObj = loader.unmarshal(existingObj.getName(), patched);
539 if (relationshipList == null) {
540 // if the caller didn't touch the relationship-list, we shouldn't either
541 patchedObj.setValue("relationship-list", null);
543 serializer.serializeToDb(patchedObj, v, query, uri.getRawPath(), requestContext);
545 patchedObj = serializer.getLatestVersionView(v);
546 if (query.isDependent()) {
547 relatedObjects = this.getRelatedObjects(serializer, queryEngine, v, patchedObj,
550 LoggingContext.elapsedTime(
551 (long) serializer.getDBTimeMsecs() + (long) queryEngine.getDBTimeMsecs(),
552 TimeUnit.MILLISECONDS);
553 LOGGER.info("Completed");
554 LoggingContext.restoreIfPossible();
555 notification.createNotificationEvent(transactionId, sourceOfTruth, status, uri,
556 patchedObj, relatedObjects, basePath);
557 } catch (IOException | JsonPatchException e) {
559 LOGGER.info("Caught exception: " + e.getMessage());
560 LoggingContext.restoreIfPossible();
561 throw new AAIException("AAI_3000", "could not perform patch operation");
565 String resourceVersion = params.getFirst("resource-version");
566 obj = serializer.getLatestVersionView(v);
567 if (query.isDependent()) {
569 this.getRelatedObjects(serializer, queryEngine, v, obj, this.loader);
572 * Find all Delete-other-vertex vertices and create structure for notify
573 * findDeleatble also returns the startVertex v and we dont want to create
574 * duplicate notification events for the same
575 * So remove the startvertex first
578 List<Vertex> deletableVertices = dbEngine.getQueryEngine().findDeletable(v);
579 Long vId = (Long) v.id();
582 * I am assuming vertexId cant be null
584 deletableVertices.removeIf(s -> vId.equals(s.id()));
585 boolean isDelVerticesPresent = !deletableVertices.isEmpty();
586 Map<Vertex, Introspector> deleteObjects = new HashMap<>();
587 Map<String, URI> uriMap = new HashMap<>();
588 Map<String, HashMap<String, Introspector>> deleteRelatedObjects = new HashMap<>();
590 if (isDelVerticesPresent) {
591 deleteObjects = this.buildIntrospectorObjects(serializer, deletableVertices);
593 uriMap = this.buildURIMap(serializer, deleteObjects);
594 deleteRelatedObjects =
595 this.buildRelatedObjects(serializer, queryEngine, deleteObjects);
598 this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request, sourceOfTruth,
599 version, loader, obj, uri, true);
600 serializer.delete(v, deletableVertices, resourceVersion, enableResourceVersion);
601 this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request, sourceOfTruth,
602 version, loader, obj, uri, false);
604 LoggingContext.elapsedTime(
605 (long) serializer.getDBTimeMsecs() + (long) queryEngine.getDBTimeMsecs(),
606 TimeUnit.MILLISECONDS);
607 LOGGER.info("Completed");
608 LoggingContext.restoreIfPossible();
609 status = Status.NO_CONTENT;
610 notification.createNotificationEvent(transactionId, sourceOfTruth, status, uri, obj,
611 relatedObjects, basePath);
614 * Notify delete-other-v candidates
617 if (isDelVerticesPresent) {
618 this.buildNotificationEvent(sourceOfTruth, status, transactionId, notification,
619 deleteObjects, uriMap, deleteRelatedObjects, basePath);
624 serializer.touchStandardVertexProperties(v, false);
625 serializer.deleteEdge(obj, v);
627 LoggingContext.elapsedTime((long) serializer.getDBTimeMsecs(), TimeUnit.MILLISECONDS);
628 LOGGER.info("Completed");
629 LoggingContext.restoreIfPossible();
630 status = Status.NO_CONTENT;
631 notification.createNotificationEvent(transactionId, sourceOfTruth, Status.OK,
632 new URI(uri.toString().replace("/relationship-list/relationship", "")),
633 serializer.getLatestVersionView(v), relatedObjects, basePath);
640 * temporarily adding vertex id to the headers
641 * to be able to use for testing the vertex id endpoint functionality
642 * since we presently have no other way of generating those id urls
644 if (response == null && v != null
645 && (method.equals(HttpMethod.PUT) || method.equals(HttpMethod.GET)
646 || method.equals(HttpMethod.MERGE_PATCH)
647 || method.equals(HttpMethod.GET_RELATIONSHIP))
650 String myvertid = v.id().toString();
651 if (this.isPaginated()) {
652 response = Response.status(status).header("vertex-id", myvertid)
653 .header("total-results", this.getTotalVertices())
654 .header("total-pages", this.getTotalPaginationBuckets()).entity(result)
655 .type(outputMediaType).build();
657 response = Response.status(status).header("vertex-id", myvertid).entity(result)
658 .type(outputMediaType).build();
660 } else if (response == null) {
661 response = Response.status(status).type(outputMediaType).build();
663 // response already set to something
665 Pair<URI, Response> pairedResp = Pair.with(request.getUri(), response);
666 responses.add(pairedResp);
667 } catch (JanusGraphException e) {
668 this.dbEngine.rollback();
670 LOGGER.info("Caught exception: " + e.getMessage());
671 LoggingContext.restoreIfPossible();
672 throw new AAIException("AAI_6134", e);
674 if (retry == maxRetries) {
675 throw new AAIException("AAI_6134");
677 } catch (AAIException e) {
679 ArrayList<String> templateVars = new ArrayList<>();
680 templateVars.add(request.getMethod().toString()); // GET, PUT, etc
681 templateVars.add(request.getUri().getPath());
682 templateVars.addAll(e.getTemplateVars());
683 ErrorLogHelper.logException(e);
684 response = Response.status(e.getErrorObject().getHTTPResponseCode()).entity(ErrorLogHelper
685 .getRESTAPIErrorResponse(request.getHeaders().getAcceptableMediaTypes(), e, templateVars))
687 Pair<URI, Response> pairedResp = Pair.with(request.getUri(), response);
688 responses.add(pairedResp);
690 } catch (Exception e) {
693 AAIException ex = new AAIException("AAI_4000", e);
694 ArrayList<String> templateVars = new ArrayList<String>();
695 templateVars.add(request.getMethod().toString()); // GET, PUT, etc
696 templateVars.add(request.getUri().getPath().toString());
697 ErrorLogHelper.logException(ex);
698 response = Response.status(ex.getErrorObject().getHTTPResponseCode()).entity(ErrorLogHelper
699 .getRESTAPIErrorResponse(request.getHeaders().getAcceptableMediaTypes(), ex, templateVars))
701 Pair<URI, Response> pairedResp = Pair.with(request.getUri(), response);
702 responses.add(pairedResp);
706 notification.triggerEvents();
707 return Pair.with(success, responses);
711 * Gets the media type.
713 * @param mediaTypeList the media type list
714 * @return the media type
716 private String getMediaType(List<MediaType> mediaTypeList) {
717 String mediaType = MediaType.APPLICATION_JSON; // json is the default
718 for (MediaType mt : mediaTypeList) {
719 if (MediaType.APPLICATION_XML_TYPE.isCompatible(mt)) {
720 mediaType = MediaType.APPLICATION_XML;
727 * Gets the object from db.
729 * @param serializer the serializer
730 * @param query the query
733 * @param depth the depth
734 * @param cleanUp the clean up
735 * @return the object from db
736 * @throws AAIException the AAI exception
737 * @throws IllegalAccessException the illegal access exception
738 * @throws IllegalArgumentException the illegal argument exception
739 * @throws InvocationTargetException the invocation target exception
740 * @throws SecurityException the security exception
741 * @throws InstantiationException the instantiation exception
742 * @throws NoSuchMethodException the no such method exception
743 * @throws UnsupportedEncodingException the unsupported encoding exception
744 * @throws MalformedURLException the malformed URL exception
745 * @throws AAIUnknownObjectException
746 * @throws URISyntaxException
748 private Introspector getObjectFromDb(List<Vertex> results, DBSerializer serializer, QueryParser query,
749 Introspector obj, URI uri, int depth, boolean nodeOnly, String cleanUp)
750 throws AAIException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
751 SecurityException, InstantiationException, NoSuchMethodException, UnsupportedEncodingException,
752 AAIUnknownObjectException, URISyntaxException {
755 if (results.isEmpty()) {
756 String msg = createNotFoundMessage(query.getResultType(), uri);
757 throw new AAIException("AAI_6114", msg);
760 return serializer.dbToObject(results, obj, depth, nodeOnly, cleanUp);
765 * Gets the object from db.
767 * @param serializer the serializer
768 * @param query the query
771 * @param depth the depth
772 * @param cleanUp the clean up
773 * @return the object from db
774 * @throws AAIException the AAI exception
775 * @throws IllegalAccessException the illegal access exception
776 * @throws IllegalArgumentException the illegal argument exception
777 * @throws InvocationTargetException the invocation target exception
778 * @throws SecurityException the security exception
779 * @throws InstantiationException the instantiation exception
780 * @throws NoSuchMethodException the no such method exception
781 * @throws UnsupportedEncodingException the unsupported encoding exception
782 * @throws MalformedURLException the malformed URL exception
783 * @throws AAIUnknownObjectException
784 * @throws URISyntaxException
786 private Introspector getRelationshipObjectFromDb(List<Vertex> results, DBSerializer serializer, QueryParser query,
787 URI uri) throws AAIException, IllegalArgumentException, SecurityException, UnsupportedEncodingException,
788 AAIUnknownObjectException {
791 if (results.isEmpty()) {
792 String msg = createNotFoundMessage(query.getResultType(), uri);
793 throw new AAIException("AAI_6114", msg);
796 if (results.size() > 1) {
797 throw new AAIException("AAI_6148", uri.getPath());
800 Vertex v = results.get(0);
801 return serializer.dbToRelationshipObject(v);
807 * @param dbEngine the db engine
809 * @param httpMethod the http method
810 * @param fromAppId the from app id
811 * @param apiVersion the api version
812 * @param loader the loader
815 * @param isPreprocess the is preprocess
816 * @return the response
817 * @throws IllegalArgumentException the illegal argument exception
818 * @throws UnsupportedEncodingException the unsupported encoding exception
819 * @throws AAIException the AAI exception
821 private Response invokeExtension(TransactionalGraphEngine dbEngine, Graph g, HttpMethod httpMethod,
822 DBRequest request, String fromAppId, SchemaVersion apiVersion, Loader loader, Introspector obj, URI uri,
823 boolean isPreprocess) throws IllegalArgumentException, UnsupportedEncodingException, AAIException {
824 AAIExtensionMap aaiExtMap = new AAIExtensionMap();
825 // ModelInjestor injestor = ModelInjestor.getInstance();
826 Response response = null;
827 URIToExtensionInformation extensionInformation = new URIToExtensionInformation(loader, uri);
828 aaiExtMap.setHttpEntry(this);
829 aaiExtMap.setDbRequest(request);
830 aaiExtMap.setTransId(request.getTransactionId());
831 aaiExtMap.setFromAppId(fromAppId);
832 aaiExtMap.setGraph(g);
833 aaiExtMap.setApiVersion(apiVersion.toString());
834 aaiExtMap.setObjectFromRequest(obj);
835 aaiExtMap.setObjectFromRequestType(obj.getJavaClassName());
836 aaiExtMap.setObjectFromResponse(obj);
837 aaiExtMap.setObjectFromResponseType(obj.getJavaClassName());
838 aaiExtMap.setJaxbContext(nodeIngestor.getContextForVersion(apiVersion));
839 aaiExtMap.setUri(uri.getRawPath());
840 aaiExtMap.setTransactionalGraphEngine(dbEngine);
841 aaiExtMap.setLoader(loader);
842 aaiExtMap.setNamespace(extensionInformation.getNamespace());
844 ExtensionController ext = new ExtensionController();
845 ext.runExtension(aaiExtMap.getApiVersion(), extensionInformation.getNamespace(),
846 extensionInformation.getTopObject(), extensionInformation.getMethodName(httpMethod, isPreprocess),
847 aaiExtMap, isPreprocess);
849 if (aaiExtMap.getPrecheckAddedList().size() > 0) {
850 response = notifyOnSkeletonCreation(aaiExtMap, obj, request.getHeaders());
857 * Notify on skeleton creation.
859 * @param aaiExtMap the aai ext map
860 * @param input the input
861 * @param headers the headers
862 * @return the response
865 private Response notifyOnSkeletonCreation(AAIExtensionMap aaiExtMap, Introspector input, HttpHeaders headers) {
866 Response response = null;
867 HashMap<AAIException, ArrayList<String>> exceptionList = new HashMap<AAIException, ArrayList<String>>();
869 StringBuilder keyString = new StringBuilder();
871 Set<String> resourceKeys = input.getKeys();
872 for (String key : resourceKeys) {
873 keyString.append(key).append("=").append(input.getValue(key).toString()).append(" ");
876 for (AAIResponseMessage msg : aaiExtMap.getPrecheckResponseMessages().getAAIResponseMessage()) {
877 ArrayList<String> templateVars = new ArrayList<>();
879 templateVars.add("PUT " + input.getDbName());
880 templateVars.add(keyString.toString());
881 List<String> keys = new ArrayList<>();
882 templateVars.add(msg.getAaiResponseMessageResourceType());
883 for (AAIResponseMessageDatum dat : msg.getAaiResponseMessageData().getAAIResponseMessageDatum()) {
884 keys.add(dat.getAaiResponseMessageDatumKey() + "=" + dat.getAaiResponseMessageDatumValue());
886 templateVars.add(StringUtils.join(keys, ", "));
887 exceptionList.put(new AAIException("AAI_0004", msg.getAaiResponseMessageResourceType()), templateVars);
889 response = Response.status(Status.ACCEPTED)
890 .entity(ErrorLogHelper.getRESTAPIInfoResponse(headers.getAcceptableMediaTypes(), exceptionList))
897 * Creates the not found message.
899 * @param resultType the result type
903 private String createNotFoundMessage(String resultType, URI uri) {
905 String msg = "No Node of type " + resultType + " found at: " + uri.getPath();
911 * Creates the not found message.
913 * @param resultType the result type
917 private String createRelationshipNotFoundMessage(String resultType, URI uri) {
919 String msg = "No relationship found of type " + resultType + " at the given URI: " + uri.getPath()
920 + "/relationship-list";
928 * @param depthParam the depth param
930 * @throws AAIException the AAI exception
932 protected int setDepth(Introspector obj, String depthParam) throws AAIException {
933 int depth = AAIProperties.MAXIMUM_DEPTH;
935 String getAllRandomStr = AAIConfig.get("aai.rest.getall.depthparam", "");
936 if (depthParam != null && getAllRandomStr != null && !getAllRandomStr.isEmpty()
937 && getAllRandomStr.equals(depthParam)) {
941 if (depthParam == null) {
942 if (this.version.compareTo(schemaVersions.getDepthVersion()) >= 0) {
945 depth = AAIProperties.MAXIMUM_DEPTH;
948 if (!depthParam.isEmpty() && !"all".equals(depthParam)) {
950 depth = Integer.parseInt(depthParam);
951 } catch (Exception e) {
952 throw new AAIException("AAI_4016");
957 String maxDepth = obj.getMetadata(ObjectMetadata.MAXIMUM_DEPTH);
959 int maximumDepth = AAIProperties.MAXIMUM_DEPTH;
961 if (maxDepth != null) {
963 maximumDepth = Integer.parseInt(maxDepth);
964 } catch (Exception ex) {
965 throw new AAIException("AAI_4018");
969 if (depth > maximumDepth) {
970 throw new AAIException("AAI_3303");
977 * Checks if is modification method.
979 * @param method the method
980 * @return true, if is modification method
982 private boolean isModificationMethod(HttpMethod method) {
983 boolean result = false;
985 if (method.equals(HttpMethod.PUT) || method.equals(HttpMethod.PUT_EDGE) || method.equals(HttpMethod.DELETE_EDGE)
986 || method.equals(HttpMethod.MERGE_PATCH)) {
995 * Given an uri, introspector object and loader object
996 * it will check if the obj is top level object if it is,
997 * it will return immediately returning the uri passed in
998 * If it isn't, it will go through, get the uriTemplate
999 * from the introspector object and get the count of "/"s
1000 * and remove that part of the uri using substring
1001 * and keep doing that until the current object is top level
1002 * Also added the max depth just so worst case scenario
1003 * Then keep adding aai-uri to the list include the aai-uri passed in
1004 * Convert that list into an array and return it
1011 * /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1/vservers/vserver/v1
1013 * Given the uriTemplate vserver -> /vservers/vserver/{vserver-id}
1014 * it converts to /vservers/vserver
1016 * lastIndexOf /vservers/vserver in
1017 * /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1/vservers/vserver/v1
1022 * Use substring to get the string from 0 to that lastIndexOf
1023 * aai-uri -> /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1
1025 * From this new aai-uri, generate a introspector from the URITOObject class
1026 * and keep doing this until you
1030 * @param aaiUri - aai-uri of the vertex representating the unique id of a given vertex
1031 * @param obj - introspector object of the given starting vertex
1032 * @param loader - Type of loader which will always be MoxyLoader to support model driven
1033 * @return an array of strings which can be used to get the vertexes of parent and grand parents from a given vertex
1034 * @throws UnsupportedEncodingException
1035 * @throws AAIException
1037 String[] convertIntrospectorToUriList(String aaiUri, Introspector obj, Loader loader)
1038 throws UnsupportedEncodingException, AAIException {
1040 List<String> uriList = new ArrayList<>();
1041 String template = StringUtils.EMPTY;
1042 String truncatedUri = aaiUri;
1043 int depth = AAIProperties.MAXIMUM_DEPTH;
1044 uriList.add(truncatedUri);
1046 while (depth >= 0 && !obj.isTopLevel()) {
1047 template = obj.getMetadata(ObjectMetadata.URI_TEMPLATE);
1049 if (template == null) {
1050 LOGGER.warn("Unable to find the uriTemplate for the object {}", obj.getDbName());
1054 int templateCount = StringUtils.countMatches(template, "/");
1055 int truncatedUriCount = StringUtils.countMatches(truncatedUri, "/");
1057 if (templateCount > truncatedUriCount) {
1058 LOGGER.warn("Template uri {} contains more slashes than truncatedUri {}", template, truncatedUri);
1062 int cutIndex = StringUtils.ordinalIndexOf(truncatedUri, "/", truncatedUriCount - templateCount + 1);
1063 truncatedUri = StringUtils.substring(truncatedUri, 0, cutIndex);
1064 uriList.add(truncatedUri);
1065 obj = new URIToObject(loader, UriBuilder.fromPath(truncatedUri).build()).getEntity();
1069 return uriList.toArray(new String[uriList.size()]);
1075 * @param queryEngine
1080 * @throws IllegalAccessException
1081 * @throws IllegalArgumentException
1082 * @throws InvocationTargetException
1083 * @throws SecurityException
1084 * @throws InstantiationException
1085 * @throws NoSuchMethodException
1086 * @throws UnsupportedEncodingException
1087 * @throws AAIException
1088 * @throws URISyntaxException
1090 private HashMap<String, Introspector> getRelatedObjects(DBSerializer serializer, QueryEngine queryEngine, Vertex v,
1091 Introspector obj, Loader loader) throws IllegalAccessException, IllegalArgumentException,
1092 InvocationTargetException, SecurityException, InstantiationException, NoSuchMethodException,
1093 UnsupportedEncodingException, AAIException, URISyntaxException {
1095 HashMap<String, Introspector> relatedVertices = new HashMap<>();
1096 VertexProperty aaiUriProperty = v.property(AAIProperties.AAI_URI);
1098 if (!aaiUriProperty.isPresent()) {
1099 if (LOGGER.isDebugEnabled()) {
1100 LOGGER.debug("For the given vertex {}, it seems aai-uri is not present so not getting related objects",
1104 "It seems aai-uri is not present in vertex, so not getting related objects, for more info enable debug log");
1106 return relatedVertices;
1109 String aaiUri = aaiUriProperty.value().toString();
1111 if (!obj.isTopLevel()) {
1112 String[] uriList = convertIntrospectorToUriList(aaiUri, obj, loader);
1113 List<Vertex> vertexChain = null;
1114 // If the uriList is null then there is something wrong with converting the uri
1115 // into a list of aai-uris so falling back to the old mechanism for finding parents
1116 if (uriList == null) {
1118 "Falling back to the old mechanism due to unable to convert aai-uri to list of uris but this is not optimal");
1119 vertexChain = queryEngine.findParents(v);
1121 vertexChain = queryEngine.findParents(uriList);
1123 for (Vertex vertex : vertexChain) {
1125 final Introspector vertexObj = serializer.getVertexProperties(vertex);
1126 relatedVertices.put(vertexObj.getObjectId(), vertexObj);
1127 } catch (AAIUnknownObjectException e) {
1128 LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned");
1133 final Introspector vertexObj = serializer.getVertexProperties(v);
1134 relatedVertices.put(vertexObj.getObjectId(), vertexObj);
1135 } catch (AAIUnknownObjectException e) {
1136 LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned");
1140 return relatedVertices;
1143 private Map<Vertex, Introspector> buildIntrospectorObjects(DBSerializer serializer, Iterable<Vertex> vertices) {
1144 Map<Vertex, Introspector> deleteObjectMap = new HashMap<>();
1145 for (Vertex vertex : vertices) {
1147 // deleteObjectMap.computeIfAbsent(vertex, s ->
1148 // serializer.getLatestVersionView(vertex));
1149 Introspector deleteObj = serializer.getLatestVersionView(vertex);
1150 deleteObjectMap.put(vertex, deleteObj);
1151 } catch (UnsupportedEncodingException | AAIException e) {
1152 LOGGER.warn("Unable to get Introspctor Objects, Just continue");
1158 return deleteObjectMap;
1162 private Map<String, URI> buildURIMap(DBSerializer serializer, Map<Vertex, Introspector> introSpector) {
1163 Map<String, URI> uriMap = new HashMap<>();
1164 for (Map.Entry<Vertex, Introspector> entry : introSpector.entrySet()) {
1167 uri = serializer.getURIForVertex(entry.getKey());
1168 if (null != entry.getValue()) {
1169 uriMap.put(entry.getValue().getObjectId(), uri);
1171 } catch (UnsupportedEncodingException e) {
1172 LOGGER.warn("Unable to get URIs, Just continue");
1182 private Map<String, HashMap<String, Introspector>> buildRelatedObjects(DBSerializer serializer,
1183 QueryEngine queryEngine, Map<Vertex, Introspector> introSpector) {
1185 Map<String, HashMap<String, Introspector>> relatedObjectsMap = new HashMap<>();
1186 for (Map.Entry<Vertex, Introspector> entry : introSpector.entrySet()) {
1188 HashMap<String, Introspector> relatedObjects =
1189 this.getRelatedObjects(serializer, queryEngine, entry.getKey(), entry.getValue(), this.loader);
1190 if (null != entry.getValue()) {
1191 relatedObjectsMap.put(entry.getValue().getObjectId(), relatedObjects);
1193 } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | SecurityException
1194 | InstantiationException | NoSuchMethodException | UnsupportedEncodingException | AAIException
1195 | URISyntaxException e) {
1196 LOGGER.warn("Unable to get realted Objects, Just continue");
1202 return relatedObjectsMap;
1206 private void buildNotificationEvent(String sourceOfTruth, Status status, String transactionId,
1207 UEBNotification notification, Map<Vertex, Introspector> deleteObjects, Map<String, URI> uriMap,
1208 Map<String, HashMap<String, Introspector>> deleteRelatedObjects, String basePath) {
1209 for (Map.Entry<Vertex, Introspector> entry : deleteObjects.entrySet()) {
1211 String vertexObjectId = "";
1213 if (null != entry.getValue()) {
1214 vertexObjectId = entry.getValue().getObjectId();
1216 if (uriMap.containsKey(vertexObjectId) && deleteRelatedObjects.containsKey(vertexObjectId)) {
1217 notification.createNotificationEvent(transactionId, sourceOfTruth, status,
1218 uriMap.get(vertexObjectId), entry.getValue(), deleteRelatedObjects.get(vertexObjectId),
1222 } catch (UnsupportedEncodingException | AAIException e) {
1224 LOGGER.warn("Error in sending notification");
1229 public void setPaginationParameters(String resultIndex, String resultSize) {
1230 if (resultIndex != null && resultIndex != "-1" && resultSize != null && resultSize != "-1") {
1231 this.setPaginationIndex(Integer.parseInt(resultIndex));
1232 this.setPaginationBucket(Integer.parseInt(resultSize));
1236 public List<Object> getPaginatedVertexList(List<Object> vertexList) throws AAIException {
1237 List<Object> vertices;
1238 if (this.isPaginated()) {
1239 this.setTotalsForPaging(vertexList.size(), this.getPaginationBucket());
1240 int startIndex = (this.getPaginationIndex() - 1) * this.getPaginationBucket();
1241 int endIndex = Math.min((this.getPaginationBucket() * this.getPaginationIndex()), vertexList.size());
1242 if (startIndex > endIndex) {
1243 throw new AAIException("AAI_6150",
1244 " ResultIndex is not appropriate for the result set, Needs to be <= " + endIndex);
1246 vertices = vertexList.subList(startIndex, endIndex);
1248 vertices = vertexList;