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();
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),
348 Math.min((this.paginationBucket * this.paginationIndex), vertTemp.size()));
350 vertices = query.getQueryBuilder().toList();
352 boolean isNewVertex = false;
353 String outputMediaType = getMediaType(request.getHeaders().getAcceptableMediaTypes());
354 String result = null;
355 params = request.getInfo().getQueryParameters(false);
356 depth = setDepth(obj, params.getFirst("depth"));
357 if (params.containsKey("format")) {
358 format = Format.getFormat(params.getFirst("format"));
360 String cleanUp = params.getFirst("cleanup");
361 String requestContext = "";
362 List<String> requestContextList = request.getHeaders().getRequestHeader("aai-request-context");
363 if (requestContextList != null) {
364 requestContext = requestContextList.get(0);
367 if (cleanUp == null) {
370 if (vertices.size() > 1 && processSingle
371 && !(method.equals(HttpMethod.GET) || method.equals(HttpMethod.GET_RELATIONSHIP))) {
372 if (method.equals(HttpMethod.DELETE)) {
373 LoggingContext.restoreIfPossible();
374 throw new AAIException("AAI_6138");
376 LoggingContext.restoreIfPossible();
377 throw new AAIException("AAI_6137");
380 if (method.equals(HttpMethod.PUT)) {
381 String resourceVersion = (String) obj.getValue("resource-version");
382 if (vertices.isEmpty()) {
383 if (enableResourceVersion) {
384 serializer.verifyResourceVersion("create", query.getResultType(), "",
385 resourceVersion, obj.getURI());
389 if (enableResourceVersion) {
390 serializer.verifyResourceVersion("update", query.getResultType(),
391 vertices.get(0).<String>property("resource-version").orElse(null),
392 resourceVersion, obj.getURI());
397 if (vertices.isEmpty()) {
398 String msg = createNotFoundMessage(query.getResultType(), request.getUri());
399 throw new AAIException("AAI_6114", msg);
408 HashMap<String, Introspector> relatedObjects = new HashMap<>();
409 String nodeOnly = params.getFirst("nodes-only");
410 boolean isNodeOnly = nodeOnly != null;
414 if (format == null) {
415 obj = this.getObjectFromDb(vertices, serializer, query, obj, request.getUri(),
416 depth, isNodeOnly, cleanUp);
418 LoggingContext.elapsedTime((long) serializer.getDBTimeMsecs(),
419 TimeUnit.MILLISECONDS);
420 LOGGER.info("Completed");
421 LoggingContext.restoreIfPossible();
425 MarshallerProperties properties;
426 if (!request.getMarshallerProperties().isPresent()) {
427 properties = new MarshallerProperties.Builder(
428 org.onap.aai.restcore.MediaType.getEnum(outputMediaType)).build();
430 properties = request.getMarshallerProperties().get();
432 result = obj.marshal(properties);
436 new FormatFactory(loader, serializer, schemaVersions, basePath + "/");
437 Formatter formatter = ff.get(format, params);
438 result = formatter.output(vertices.stream().map(vertex -> (Object) vertex)
439 .collect(Collectors.toList())).toString();
444 case GET_RELATIONSHIP:
445 if (format == null) {
446 obj = this.getRelationshipObjectFromDb(vertices, serializer, query,
447 request.getInfo().getRequestUri());
449 LoggingContext.elapsedTime((long) serializer.getDBTimeMsecs(),
450 TimeUnit.MILLISECONDS);
451 LOGGER.info("Completed");
452 LoggingContext.restoreIfPossible();
456 MarshallerProperties properties;
457 if (!request.getMarshallerProperties().isPresent()) {
458 properties = new MarshallerProperties.Builder(
459 org.onap.aai.restcore.MediaType.getEnum(outputMediaType)).build();
461 properties = request.getMarshallerProperties().get();
463 result = obj.marshal(properties);
465 String msg = createRelationshipNotFoundMessage(query.getResultType(),
467 throw new AAIException("AAI_6149", msg);
471 new FormatFactory(loader, serializer, schemaVersions, basePath + "/");
472 Formatter formatter = ff.get(format, params);
473 result = formatter.output(vertices.stream().map(vertex -> (Object) vertex)
474 .collect(Collectors.toList())).toString();
479 response = this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request,
480 sourceOfTruth, version, loader, obj, uri, true);
482 v = serializer.createNewVertex(obj);
484 serializer.serializeToDb(obj, v, query, uri.getRawPath(), requestContext);
485 this.invokeExtension(dbEngine, this.dbEngine.tx(), HttpMethod.PUT, request,
486 sourceOfTruth, version, loader, obj, uri, false);
489 status = Status.CREATED;
491 obj = serializer.getLatestVersionView(v);
492 if (query.isDependent()) {
494 this.getRelatedObjects(serializer, queryEngine, v, obj, this.loader);
496 LoggingContext.elapsedTime(
497 (long) serializer.getDBTimeMsecs() + (long) queryEngine.getDBTimeMsecs(),
498 TimeUnit.MILLISECONDS);
499 LOGGER.info("Completed ");
500 LoggingContext.restoreIfPossible();
501 notification.createNotificationEvent(transactionId, sourceOfTruth, status, uri, obj,
502 relatedObjects, basePath);
506 serializer.touchStandardVertexProperties(v, false);
507 this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request, sourceOfTruth,
508 version, loader, obj, uri, true);
509 serializer.createEdge(obj, v);
511 LoggingContext.elapsedTime((long) serializer.getDBTimeMsecs(), TimeUnit.MILLISECONDS);
512 LOGGER.info("Completed");
513 LoggingContext.restoreIfPossible();
515 notification.createNotificationEvent(transactionId, sourceOfTruth, status,
516 new URI(uri.toString().replace("/relationship-list/relationship", "")),
517 serializer.getLatestVersionView(v), relatedObjects, basePath);
520 Introspector existingObj = loader.introspectorFromName(obj.getDbName());
521 existingObj = this.getObjectFromDb(vertices, serializer, query, existingObj,
522 request.getUri(), 0, false, cleanUp);
523 String existingJson = existingObj.marshal(false);
526 if (request.getRawRequestContent().isPresent()) {
527 newJson = request.getRawRequestContent().get();
531 Object relationshipList = request.getIntrospector().getValue("relationship-list");
532 ObjectMapper mapper = new ObjectMapper();
534 JsonNode existingNode = mapper.readTree(existingJson);
535 JsonNode newNode = mapper.readTree(newJson);
536 JsonMergePatch patch = JsonMergePatch.fromJson(newNode);
537 JsonNode completed = patch.apply(existingNode);
538 String patched = mapper.writeValueAsString(completed);
539 Introspector patchedObj = loader.unmarshal(existingObj.getName(), patched);
540 if (relationshipList == null) {
541 // if the caller didn't touch the relationship-list, we shouldn't either
542 patchedObj.setValue("relationship-list", null);
544 serializer.serializeToDb(patchedObj, v, query, uri.getRawPath(), requestContext);
546 patchedObj = serializer.getLatestVersionView(v);
547 if (query.isDependent()) {
548 relatedObjects = this.getRelatedObjects(serializer, queryEngine, v, patchedObj,
551 LoggingContext.elapsedTime(
552 (long) serializer.getDBTimeMsecs() + (long) queryEngine.getDBTimeMsecs(),
553 TimeUnit.MILLISECONDS);
554 LOGGER.info("Completed");
555 LoggingContext.restoreIfPossible();
556 notification.createNotificationEvent(transactionId, sourceOfTruth, status, uri,
557 patchedObj, relatedObjects, basePath);
558 } catch (IOException | JsonPatchException e) {
560 LOGGER.info("Caught exception: " + e.getMessage());
561 LoggingContext.restoreIfPossible();
562 throw new AAIException("AAI_3000", "could not perform patch operation");
566 String resourceVersion = params.getFirst("resource-version");
567 obj = serializer.getLatestVersionView(v);
568 if (query.isDependent()) {
570 this.getRelatedObjects(serializer, queryEngine, v, obj, this.loader);
573 * Find all Delete-other-vertex vertices and create structure for notify
574 * findDeleatble also returns the startVertex v and we dont want to create
575 * duplicate notification events for the same
576 * So remove the startvertex first
579 List<Vertex> deletableVertices = dbEngine.getQueryEngine().findDeletable(v);
580 Long vId = (Long) v.id();
583 * I am assuming vertexId cant be null
585 deletableVertices.removeIf(s -> vId.equals(s.id()));
586 boolean isDelVerticesPresent = !deletableVertices.isEmpty();
587 Map<Vertex, Introspector> deleteObjects = new HashMap<>();
588 Map<String, URI> uriMap = new HashMap<>();
589 Map<String, HashMap<String, Introspector>> deleteRelatedObjects = new HashMap<>();
591 if (isDelVerticesPresent) {
592 deleteObjects = this.buildIntrospectorObjects(serializer, deletableVertices);
594 uriMap = this.buildURIMap(serializer, deleteObjects);
595 deleteRelatedObjects =
596 this.buildRelatedObjects(serializer, queryEngine, deleteObjects);
599 this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request, sourceOfTruth,
600 version, loader, obj, uri, true);
601 serializer.delete(v, deletableVertices, resourceVersion, enableResourceVersion);
602 this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request, sourceOfTruth,
603 version, loader, obj, uri, false);
605 LoggingContext.elapsedTime(
606 (long) serializer.getDBTimeMsecs() + (long) queryEngine.getDBTimeMsecs(),
607 TimeUnit.MILLISECONDS);
608 LOGGER.info("Completed");
609 LoggingContext.restoreIfPossible();
610 status = Status.NO_CONTENT;
611 notification.createNotificationEvent(transactionId, sourceOfTruth, status, uri, obj,
612 relatedObjects, basePath);
615 * Notify delete-other-v candidates
618 if (isDelVerticesPresent) {
619 this.buildNotificationEvent(sourceOfTruth, status, transactionId, notification,
620 deleteObjects, uriMap, deleteRelatedObjects, basePath);
625 serializer.touchStandardVertexProperties(v, false);
626 serializer.deleteEdge(obj, v);
628 LoggingContext.elapsedTime((long) serializer.getDBTimeMsecs(), TimeUnit.MILLISECONDS);
629 LOGGER.info("Completed");
630 LoggingContext.restoreIfPossible();
631 status = Status.NO_CONTENT;
632 notification.createNotificationEvent(transactionId, sourceOfTruth, Status.OK,
633 new URI(uri.toString().replace("/relationship-list/relationship", "")),
634 serializer.getLatestVersionView(v), relatedObjects, basePath);
641 * temporarily adding vertex id to the headers
642 * to be able to use for testing the vertex id endpoint functionality
643 * since we presently have no other way of generating those id urls
645 if (response == null && v != null
646 && (method.equals(HttpMethod.PUT) || method.equals(HttpMethod.GET)
647 || method.equals(HttpMethod.MERGE_PATCH)
648 || method.equals(HttpMethod.GET_RELATIONSHIP))
651 String myvertid = v.id().toString();
652 if (this.isPaginated()) {
653 response = Response.status(status).header("vertex-id", myvertid)
654 .header("total-results", this.getTotalVertices())
655 .header("total-pages", this.getTotalPaginationBuckets()).entity(result)
656 .type(outputMediaType).build();
658 response = Response.status(status).header("vertex-id", myvertid).entity(result)
659 .type(outputMediaType).build();
661 } else if (response == null) {
662 response = Response.status(status).type(outputMediaType).build();
664 // response already set to something
666 Pair<URI, Response> pairedResp = Pair.with(request.getUri(), response);
667 responses.add(pairedResp);
668 // break out of retry loop
670 } catch (JanusGraphException e) {
671 this.dbEngine.rollback();
673 LOGGER.info("Caught exception: " + e.getMessage());
674 LoggingContext.restoreIfPossible();
675 AAIException ex = new AAIException("AAI_6142", e);
676 ErrorLogHelper.logException(ex);
677 Thread.sleep((retry + 1) * 20L);
678 this.dbEngine.startTransaction();
679 queryEngine = dbEngine.getQueryEngine();
680 serializer = new DBSerializer(version, dbEngine, introspectorFactoryType, sourceOfTruth);
682 if (retry == maxRetries) {
683 throw new AAIException("AAI_6134");
686 } catch (AAIException e) {
688 ArrayList<String> templateVars = new ArrayList<>();
689 templateVars.add(request.getMethod().toString()); // GET, PUT, etc
690 templateVars.add(request.getUri().getPath());
691 templateVars.addAll(e.getTemplateVars());
692 ErrorLogHelper.logException(e);
693 response = Response.status(e.getErrorObject().getHTTPResponseCode()).entity(ErrorLogHelper
694 .getRESTAPIErrorResponse(request.getHeaders().getAcceptableMediaTypes(), e, templateVars))
696 Pair<URI, Response> pairedResp = Pair.with(request.getUri(), response);
697 responses.add(pairedResp);
699 } catch (Exception e) {
702 AAIException ex = new AAIException("AAI_4000", e);
703 ArrayList<String> templateVars = new ArrayList<String>();
704 templateVars.add(request.getMethod().toString()); // GET, PUT, etc
705 templateVars.add(request.getUri().getPath().toString());
706 ErrorLogHelper.logException(ex);
707 response = Response.status(ex.getErrorObject().getHTTPResponseCode()).entity(ErrorLogHelper
708 .getRESTAPIErrorResponse(request.getHeaders().getAcceptableMediaTypes(), ex, templateVars))
710 Pair<URI, Response> pairedResp = Pair.with(request.getUri(), response);
711 responses.add(pairedResp);
715 notification.triggerEvents();
716 return Pair.with(success, responses);
720 * Gets the media type.
722 * @param mediaTypeList the media type list
723 * @return the media type
725 private String getMediaType(List<MediaType> mediaTypeList) {
726 String mediaType = MediaType.APPLICATION_JSON; // json is the default
727 for (MediaType mt : mediaTypeList) {
728 if (MediaType.APPLICATION_XML_TYPE.isCompatible(mt)) {
729 mediaType = MediaType.APPLICATION_XML;
736 * Gets the object from db.
738 * @param serializer the serializer
739 * @param query the query
742 * @param depth the depth
743 * @param cleanUp the clean up
744 * @return the object from db
745 * @throws AAIException the AAI exception
746 * @throws IllegalAccessException the illegal access exception
747 * @throws IllegalArgumentException the illegal argument exception
748 * @throws InvocationTargetException the invocation target exception
749 * @throws SecurityException the security exception
750 * @throws InstantiationException the instantiation exception
751 * @throws NoSuchMethodException the no such method exception
752 * @throws UnsupportedEncodingException the unsupported encoding exception
753 * @throws MalformedURLException the malformed URL exception
754 * @throws AAIUnknownObjectException
755 * @throws URISyntaxException
757 private Introspector getObjectFromDb(List<Vertex> results, DBSerializer serializer, QueryParser query,
758 Introspector obj, URI uri, int depth, boolean nodeOnly, String cleanUp)
759 throws AAIException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
760 SecurityException, InstantiationException, NoSuchMethodException, UnsupportedEncodingException,
761 AAIUnknownObjectException, URISyntaxException {
764 if (results.isEmpty()) {
765 String msg = createNotFoundMessage(query.getResultType(), uri);
766 throw new AAIException("AAI_6114", msg);
769 return serializer.dbToObject(results, obj, depth, nodeOnly, cleanUp);
774 * Gets the object from db.
776 * @param serializer the serializer
777 * @param query the query
780 * @param depth the depth
781 * @param cleanUp the clean up
782 * @return the object from db
783 * @throws AAIException the AAI exception
784 * @throws IllegalAccessException the illegal access exception
785 * @throws IllegalArgumentException the illegal argument exception
786 * @throws InvocationTargetException the invocation target exception
787 * @throws SecurityException the security exception
788 * @throws InstantiationException the instantiation exception
789 * @throws NoSuchMethodException the no such method exception
790 * @throws UnsupportedEncodingException the unsupported encoding exception
791 * @throws MalformedURLException the malformed URL exception
792 * @throws AAIUnknownObjectException
793 * @throws URISyntaxException
795 private Introspector getRelationshipObjectFromDb(List<Vertex> results, DBSerializer serializer, QueryParser query,
796 URI uri) throws AAIException, IllegalArgumentException, SecurityException, UnsupportedEncodingException,
797 AAIUnknownObjectException {
800 if (results.isEmpty()) {
801 String msg = createNotFoundMessage(query.getResultType(), uri);
802 throw new AAIException("AAI_6114", msg);
805 if (results.size() > 1) {
806 throw new AAIException("AAI_6148", uri.getPath());
809 Vertex v = results.get(0);
810 return serializer.dbToRelationshipObject(v);
816 * @param dbEngine the db engine
818 * @param httpMethod the http method
819 * @param fromAppId the from app id
820 * @param apiVersion the api version
821 * @param loader the loader
824 * @param isPreprocess the is preprocess
825 * @return the response
826 * @throws IllegalArgumentException the illegal argument exception
827 * @throws UnsupportedEncodingException the unsupported encoding exception
828 * @throws AAIException the AAI exception
830 private Response invokeExtension(TransactionalGraphEngine dbEngine, Graph g, HttpMethod httpMethod,
831 DBRequest request, String fromAppId, SchemaVersion apiVersion, Loader loader, Introspector obj, URI uri,
832 boolean isPreprocess) throws IllegalArgumentException, UnsupportedEncodingException, AAIException {
833 AAIExtensionMap aaiExtMap = new AAIExtensionMap();
834 // ModelInjestor injestor = ModelInjestor.getInstance();
835 Response response = null;
836 URIToExtensionInformation extensionInformation = new URIToExtensionInformation(loader, uri);
837 aaiExtMap.setHttpEntry(this);
838 aaiExtMap.setDbRequest(request);
839 aaiExtMap.setTransId(request.getTransactionId());
840 aaiExtMap.setFromAppId(fromAppId);
841 aaiExtMap.setGraph(g);
842 aaiExtMap.setApiVersion(apiVersion.toString());
843 aaiExtMap.setObjectFromRequest(obj);
844 aaiExtMap.setObjectFromRequestType(obj.getJavaClassName());
845 aaiExtMap.setObjectFromResponse(obj);
846 aaiExtMap.setObjectFromResponseType(obj.getJavaClassName());
847 aaiExtMap.setJaxbContext(nodeIngestor.getContextForVersion(apiVersion));
848 aaiExtMap.setUri(uri.getRawPath());
849 aaiExtMap.setTransactionalGraphEngine(dbEngine);
850 aaiExtMap.setLoader(loader);
851 aaiExtMap.setNamespace(extensionInformation.getNamespace());
853 ExtensionController ext = new ExtensionController();
854 ext.runExtension(aaiExtMap.getApiVersion(), extensionInformation.getNamespace(),
855 extensionInformation.getTopObject(), extensionInformation.getMethodName(httpMethod, isPreprocess),
856 aaiExtMap, isPreprocess);
858 if (aaiExtMap.getPrecheckAddedList().size() > 0) {
859 response = notifyOnSkeletonCreation(aaiExtMap, obj, request.getHeaders());
866 * Notify on skeleton creation.
868 * @param aaiExtMap the aai ext map
869 * @param input the input
870 * @param headers the headers
871 * @return the response
874 private Response notifyOnSkeletonCreation(AAIExtensionMap aaiExtMap, Introspector input, HttpHeaders headers) {
875 Response response = null;
876 HashMap<AAIException, ArrayList<String>> exceptionList = new HashMap<AAIException, ArrayList<String>>();
878 StringBuilder keyString = new StringBuilder();
880 Set<String> resourceKeys = input.getKeys();
881 for (String key : resourceKeys) {
882 keyString.append(key).append("=").append(input.getValue(key).toString()).append(" ");
885 for (AAIResponseMessage msg : aaiExtMap.getPrecheckResponseMessages().getAAIResponseMessage()) {
886 ArrayList<String> templateVars = new ArrayList<>();
888 templateVars.add("PUT " + input.getDbName());
889 templateVars.add(keyString.toString());
890 List<String> keys = new ArrayList<>();
891 templateVars.add(msg.getAaiResponseMessageResourceType());
892 for (AAIResponseMessageDatum dat : msg.getAaiResponseMessageData().getAAIResponseMessageDatum()) {
893 keys.add(dat.getAaiResponseMessageDatumKey() + "=" + dat.getAaiResponseMessageDatumValue());
895 templateVars.add(StringUtils.join(keys, ", "));
896 exceptionList.put(new AAIException("AAI_0004", msg.getAaiResponseMessageResourceType()), templateVars);
898 response = Response.status(Status.ACCEPTED)
899 .entity(ErrorLogHelper.getRESTAPIInfoResponse(headers.getAcceptableMediaTypes(), exceptionList))
906 * Creates the not found message.
908 * @param resultType the result type
912 private String createNotFoundMessage(String resultType, URI uri) {
914 String msg = "No Node of type " + resultType + " found at: " + uri.getPath();
920 * Creates the not found message.
922 * @param resultType the result type
926 private String createRelationshipNotFoundMessage(String resultType, URI uri) {
928 String msg = "No relationship found of type " + resultType + " at the given URI: " + uri.getPath()
929 + "/relationship-list";
937 * @param depthParam the depth param
939 * @throws AAIException the AAI exception
941 protected int setDepth(Introspector obj, String depthParam) throws AAIException {
942 int depth = AAIProperties.MAXIMUM_DEPTH;
944 String getAllRandomStr = AAIConfig.get("aai.rest.getall.depthparam", "");
945 if (depthParam != null && getAllRandomStr != null && !getAllRandomStr.isEmpty()
946 && getAllRandomStr.equals(depthParam)) {
950 if (depthParam == null) {
951 if (this.version.compareTo(schemaVersions.getDepthVersion()) >= 0) {
954 depth = AAIProperties.MAXIMUM_DEPTH;
957 if (!depthParam.isEmpty() && !"all".equals(depthParam)) {
959 depth = Integer.parseInt(depthParam);
960 } catch (Exception e) {
961 throw new AAIException("AAI_4016");
966 String maxDepth = obj.getMetadata(ObjectMetadata.MAXIMUM_DEPTH);
968 int maximumDepth = AAIProperties.MAXIMUM_DEPTH;
970 if (maxDepth != null) {
972 maximumDepth = Integer.parseInt(maxDepth);
973 } catch (Exception ex) {
974 throw new AAIException("AAI_4018");
978 if (depth > maximumDepth) {
979 throw new AAIException("AAI_3303");
986 * Checks if is modification method.
988 * @param method the method
989 * @return true, if is modification method
991 private boolean isModificationMethod(HttpMethod method) {
992 boolean result = false;
994 if (method.equals(HttpMethod.PUT) || method.equals(HttpMethod.PUT_EDGE) || method.equals(HttpMethod.DELETE_EDGE)
995 || method.equals(HttpMethod.MERGE_PATCH)) {
1004 * Given an uri, introspector object and loader object
1005 * it will check if the obj is top level object if it is,
1006 * it will return immediately returning the uri passed in
1007 * If it isn't, it will go through, get the uriTemplate
1008 * from the introspector object and get the count of "/"s
1009 * and remove that part of the uri using substring
1010 * and keep doing that until the current object is top level
1011 * Also added the max depth just so worst case scenario
1012 * Then keep adding aai-uri to the list include the aai-uri passed in
1013 * Convert that list into an array and return it
1020 * /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1/vservers/vserver/v1
1022 * Given the uriTemplate vserver -> /vservers/vserver/{vserver-id}
1023 * it converts to /vservers/vserver
1025 * lastIndexOf /vservers/vserver in
1026 * /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1/vservers/vserver/v1
1031 * Use substring to get the string from 0 to that lastIndexOf
1032 * aai-uri -> /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1
1034 * From this new aai-uri, generate a introspector from the URITOObject class
1035 * and keep doing this until you
1039 * @param aaiUri - aai-uri of the vertex representating the unique id of a given vertex
1040 * @param obj - introspector object of the given starting vertex
1041 * @param loader - Type of loader which will always be MoxyLoader to support model driven
1042 * @return an array of strings which can be used to get the vertexes of parent and grand parents from a given vertex
1043 * @throws UnsupportedEncodingException
1044 * @throws AAIException
1046 String[] convertIntrospectorToUriList(String aaiUri, Introspector obj, Loader loader)
1047 throws UnsupportedEncodingException, AAIException {
1049 List<String> uriList = new ArrayList<>();
1050 String template = StringUtils.EMPTY;
1051 String truncatedUri = aaiUri;
1052 int depth = AAIProperties.MAXIMUM_DEPTH;
1053 uriList.add(truncatedUri);
1055 while (depth >= 0 && !obj.isTopLevel()) {
1056 template = obj.getMetadata(ObjectMetadata.URI_TEMPLATE);
1058 if (template == null) {
1059 LOGGER.warn("Unable to find the uriTemplate for the object {}", obj.getDbName());
1063 int templateCount = StringUtils.countMatches(template, "/");
1064 int truncatedUriCount = StringUtils.countMatches(truncatedUri, "/");
1066 if (templateCount > truncatedUriCount) {
1067 LOGGER.warn("Template uri {} contains more slashes than truncatedUri {}", template, truncatedUri);
1071 int cutIndex = StringUtils.ordinalIndexOf(truncatedUri, "/", truncatedUriCount - templateCount + 1);
1072 truncatedUri = StringUtils.substring(truncatedUri, 0, cutIndex);
1073 uriList.add(truncatedUri);
1074 obj = new URIToObject(loader, UriBuilder.fromPath(truncatedUri).build()).getEntity();
1078 return uriList.toArray(new String[uriList.size()]);
1084 * @param queryEngine
1089 * @throws IllegalAccessException
1090 * @throws IllegalArgumentException
1091 * @throws InvocationTargetException
1092 * @throws SecurityException
1093 * @throws InstantiationException
1094 * @throws NoSuchMethodException
1095 * @throws UnsupportedEncodingException
1096 * @throws AAIException
1097 * @throws URISyntaxException
1099 private HashMap<String, Introspector> getRelatedObjects(DBSerializer serializer, QueryEngine queryEngine, Vertex v,
1100 Introspector obj, Loader loader) throws IllegalAccessException, IllegalArgumentException,
1101 InvocationTargetException, SecurityException, InstantiationException, NoSuchMethodException,
1102 UnsupportedEncodingException, AAIException, URISyntaxException {
1104 HashMap<String, Introspector> relatedVertices = new HashMap<>();
1105 VertexProperty aaiUriProperty = v.property(AAIProperties.AAI_URI);
1107 if (!aaiUriProperty.isPresent()) {
1108 if (LOGGER.isDebugEnabled()) {
1109 LOGGER.debug("For the given vertex {}, it seems aai-uri is not present so not getting related objects",
1113 "It seems aai-uri is not present in vertex, so not getting related objects, for more info enable debug log");
1115 return relatedVertices;
1118 String aaiUri = aaiUriProperty.value().toString();
1120 if (!obj.isTopLevel()) {
1121 String[] uriList = convertIntrospectorToUriList(aaiUri, obj, loader);
1122 List<Vertex> vertexChain = null;
1123 // If the uriList is null then there is something wrong with converting the uri
1124 // into a list of aai-uris so falling back to the old mechanism for finding parents
1125 if (uriList == null) {
1127 "Falling back to the old mechanism due to unable to convert aai-uri to list of uris but this is not optimal");
1128 vertexChain = queryEngine.findParents(v);
1130 vertexChain = queryEngine.findParents(uriList);
1132 for (Vertex vertex : vertexChain) {
1134 final Introspector vertexObj = serializer.getVertexProperties(vertex);
1135 relatedVertices.put(vertexObj.getObjectId(), vertexObj);
1136 } catch (AAIUnknownObjectException e) {
1137 LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned");
1142 final Introspector vertexObj = serializer.getVertexProperties(v);
1143 relatedVertices.put(vertexObj.getObjectId(), vertexObj);
1144 } catch (AAIUnknownObjectException e) {
1145 LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned");
1149 return relatedVertices;
1152 private Map<Vertex, Introspector> buildIntrospectorObjects(DBSerializer serializer, Iterable<Vertex> vertices) {
1153 Map<Vertex, Introspector> deleteObjectMap = new HashMap<>();
1154 for (Vertex vertex : vertices) {
1156 // deleteObjectMap.computeIfAbsent(vertex, s ->
1157 // serializer.getLatestVersionView(vertex));
1158 Introspector deleteObj = serializer.getLatestVersionView(vertex);
1159 deleteObjectMap.put(vertex, deleteObj);
1160 } catch (UnsupportedEncodingException | AAIException e) {
1161 LOGGER.warn("Unable to get Introspctor Objects, Just continue");
1167 return deleteObjectMap;
1171 private Map<String, URI> buildURIMap(DBSerializer serializer, Map<Vertex, Introspector> introSpector) {
1172 Map<String, URI> uriMap = new HashMap<>();
1173 for (Map.Entry<Vertex, Introspector> entry : introSpector.entrySet()) {
1176 uri = serializer.getURIForVertex(entry.getKey());
1177 if (null != entry.getValue()) {
1178 uriMap.put(entry.getValue().getObjectId(), uri);
1180 } catch (UnsupportedEncodingException e) {
1181 LOGGER.warn("Unable to get URIs, Just continue");
1191 private Map<String, HashMap<String, Introspector>> buildRelatedObjects(DBSerializer serializer,
1192 QueryEngine queryEngine, Map<Vertex, Introspector> introSpector) {
1194 Map<String, HashMap<String, Introspector>> relatedObjectsMap = new HashMap<>();
1195 for (Map.Entry<Vertex, Introspector> entry : introSpector.entrySet()) {
1197 HashMap<String, Introspector> relatedObjects =
1198 this.getRelatedObjects(serializer, queryEngine, entry.getKey(), entry.getValue(), this.loader);
1199 if (null != entry.getValue()) {
1200 relatedObjectsMap.put(entry.getValue().getObjectId(), relatedObjects);
1202 } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | SecurityException
1203 | InstantiationException | NoSuchMethodException | UnsupportedEncodingException | AAIException
1204 | URISyntaxException e) {
1205 LOGGER.warn("Unable to get realted Objects, Just continue");
1211 return relatedObjectsMap;
1215 private void buildNotificationEvent(String sourceOfTruth, Status status, String transactionId,
1216 UEBNotification notification, Map<Vertex, Introspector> deleteObjects, Map<String, URI> uriMap,
1217 Map<String, HashMap<String, Introspector>> deleteRelatedObjects, String basePath) {
1218 for (Map.Entry<Vertex, Introspector> entry : deleteObjects.entrySet()) {
1220 String vertexObjectId = "";
1222 if (null != entry.getValue()) {
1223 vertexObjectId = entry.getValue().getObjectId();
1225 if (uriMap.containsKey(vertexObjectId) && deleteRelatedObjects.containsKey(vertexObjectId)) {
1226 notification.createNotificationEvent(transactionId, sourceOfTruth, status,
1227 uriMap.get(vertexObjectId), entry.getValue(), deleteRelatedObjects.get(vertexObjectId),
1231 } catch (UnsupportedEncodingException | AAIException e) {
1233 LOGGER.warn("Error in sending notification");
1238 public void setPaginationParameters(String resultIndex, String resultSize) {
1239 if (resultIndex != null && resultIndex != "-1" && resultSize != null && resultSize != "-1") {
1240 this.setPaginationIndex(Integer.parseInt(resultIndex));
1241 this.setPaginationBucket(Integer.parseInt(resultSize));
1245 public List<Object> getPaginatedVertexList(List<Object> vertexList) throws AAIException {
1246 List<Object> vertices;
1247 if (this.isPaginated()) {
1248 this.setTotalsForPaging(vertexList.size(), this.getPaginationBucket());
1249 int startIndex = (this.getPaginationIndex() - 1) * this.getPaginationBucket();
1250 int endIndex = Math.min((this.getPaginationBucket() * this.getPaginationIndex()), vertexList.size());
1251 if (startIndex > endIndex) {
1252 throw new AAIException("AAI_6150",
1253 " ResultIndex is not appropriate for the result set, Needs to be <= " + endIndex);
1255 vertices = vertexList.subList(startIndex, endIndex);
1257 vertices = vertexList;