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 org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
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;
29 import org.apache.tinkerpop.gremlin.structure.Vertex;
30 import org.janusgraph.core.JanusGraphException;
31 import org.javatuples.Pair;
32 import org.onap.aai.aailog.logs.AaiDBMetricLog;
33 import org.onap.aai.db.props.AAIProperties;
34 import org.onap.aai.exceptions.AAIException;
35 import org.onap.aai.introspection.*;
36 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
37 import org.onap.aai.logging.ErrorLogHelper;
38 import org.onap.aai.nodes.NodeIngestor;
39 import org.onap.aai.parsers.query.QueryParser;
40 import org.onap.aai.prevalidation.ValidationService;
41 import org.onap.aai.rest.ueb.UEBNotification;
42 import org.onap.aai.restcore.HttpMethod;
43 import org.onap.aai.schema.enums.ObjectMetadata;
44 import org.onap.aai.serialization.db.DBSerializer;
45 import org.onap.aai.serialization.engines.JanusGraphDBEngine;
46 import org.onap.aai.serialization.engines.QueryStyle;
47 import org.onap.aai.serialization.engines.TransactionalGraphEngine;
48 import org.onap.aai.serialization.engines.query.QueryEngine;
49 import org.onap.aai.serialization.queryformats.Format;
50 import org.onap.aai.serialization.queryformats.FormatFactory;
51 import org.onap.aai.serialization.queryformats.Formatter;
52 import org.onap.aai.setup.SchemaVersion;
53 import org.onap.aai.setup.SchemaVersions;
54 import org.onap.aai.transforms.XmlFormatTransformer;
55 import org.onap.aai.util.AAIConfig;
56 import org.onap.aai.util.AAIConstants;
57 import org.onap.aai.util.delta.DeltaEvents;
58 import org.springframework.beans.factory.annotation.Autowired;
59 import org.springframework.beans.factory.annotation.Value;
60 import org.springframework.http.ResponseEntity;
62 import javax.ws.rs.core.*;
63 import javax.ws.rs.core.Response.Status;
64 import java.io.IOException;
65 import java.io.UnsupportedEncodingException;
66 import java.lang.reflect.InvocationTargetException;
67 import java.net.MalformedURLException;
69 import java.net.URISyntaxException;
71 import java.util.stream.Collectors;
74 * The Class HttpEntry.
76 public class HttpEntry {
78 private static final Logger LOGGER = LoggerFactory.getLogger(HttpEntry.class);
80 private ModelType introspectorFactoryType;
82 private QueryStyle queryStyle;
84 private SchemaVersion version;
86 private Loader loader;
88 private TransactionalGraphEngine dbEngine;
90 private boolean processSingle = true;
92 private int paginationBucket = -1;
93 private int paginationIndex = -1;
94 private int totalVertices = 0;
95 private int totalPaginationBuckets = 0;
98 private NodeIngestor nodeIngestor;
101 private LoaderFactory loaderFactory;
104 private SchemaVersions schemaVersions;
106 @Value("${schema.uri.base.path}")
107 private String basePath;
109 @Value("${delta.events.enabled:false}")
110 private boolean isDeltaEventsEnabled;
112 private String serverBase;
115 private XmlFormatTransformer xmlFormatTransformer;
118 * Inject the validation service if the profile pre-valiation is enabled,
119 * Otherwise this variable will be set to null and thats why required=false
120 * so that it can continue even if pre validation isn't enabled
122 @Autowired(required = false)
123 private ValidationService validationService;
125 private UEBNotification notification;
127 private int notificationDepth;
130 * Instantiates a new http entry.
132 * @param modelType the model type
133 * @param queryStyle the query style
135 public HttpEntry(ModelType modelType, QueryStyle queryStyle) {
136 this.introspectorFactoryType = modelType;
137 this.queryStyle = queryStyle;
140 public HttpEntry setHttpEntryProperties(SchemaVersion version) {
141 this.version = version;
142 this.loader = loaderFactory.createLoaderForVersion(introspectorFactoryType, version);
143 this.dbEngine = new JanusGraphDBEngine(queryStyle, loader);
145 getDbEngine().startTransaction();
146 this.notification = new UEBNotification(loader, loaderFactory, schemaVersions);
147 if("true".equals(AAIConfig.get("aai.notification.depth.all.enabled", "true"))){
148 this.notificationDepth = AAIProperties.MAXIMUM_DEPTH;
150 this.notificationDepth = AAIProperties.MINIMUM_DEPTH;
155 public HttpEntry setHttpEntryProperties(SchemaVersion version, String serverBase) {
156 this.version = version;
157 this.loader = loaderFactory.createLoaderForVersion(introspectorFactoryType, version);
158 this.dbEngine = new JanusGraphDBEngine(queryStyle, loader);
160 getDbEngine().startTransaction();
161 this.notification = new UEBNotification(loader, loaderFactory, schemaVersions);
162 if("true".equals(AAIConfig.get("aai.notification.depth.all.enabled", "true"))){
163 this.notificationDepth = AAIProperties.MAXIMUM_DEPTH;
165 this.notificationDepth = AAIProperties.MINIMUM_DEPTH;
168 this.serverBase = serverBase;
172 public HttpEntry setHttpEntryProperties(SchemaVersion version, UEBNotification notification) {
173 this.version = version;
174 this.loader = loaderFactory.createLoaderForVersion(introspectorFactoryType, version);
175 this.dbEngine = new JanusGraphDBEngine(queryStyle, loader);
177 this.notification = notification;
179 if("true".equals(AAIConfig.get("aai.notification.depth.all.enabled", "true"))){
180 this.notificationDepth = AAIProperties.MAXIMUM_DEPTH;
182 this.notificationDepth = AAIProperties.MINIMUM_DEPTH;
184 // start transaction on creation
185 getDbEngine().startTransaction();
189 public HttpEntry setHttpEntryProperties(SchemaVersion version, UEBNotification notification, int notificationDepth) {
190 this.version = version;
191 this.loader = loaderFactory.createLoaderForVersion(introspectorFactoryType, version);
192 this.dbEngine = new JanusGraphDBEngine(queryStyle, loader);
194 this.notification = notification;
195 this.notificationDepth = notificationDepth;
196 // start transaction on creation
197 getDbEngine().startTransaction();
202 * Gets the introspector factory type.
204 * @return the introspector factory type
206 public ModelType getIntrospectorFactoryType() {
207 return introspectorFactoryType;
211 * Gets the query style.
213 * @return the query style
215 public QueryStyle getQueryStyle() {
222 * @return the version
224 public SchemaVersion getVersion() {
233 public Loader getLoader() {
238 * Gets the db engine.
240 * @return the db engine
242 public TransactionalGraphEngine getDbEngine() {
246 public Pair<Boolean, List<Pair<URI, Response>>> process(List<DBRequest> requests, String sourceOfTruth)
247 throws AAIException {
248 return this.process(requests, sourceOfTruth, true);
252 * Checks the pagination bucket and pagination index variables to determine whether or not the user
253 * requested paginated results
255 * @return a boolean true/false of whether the user requested paginated results
257 public boolean isPaginated() {
258 return this.paginationBucket > -1 && this.paginationIndex > -1;
262 * Returns the pagination size
264 * @return integer of the size of results to be returned when paginated
266 public int getPaginationBucket() {
267 return this.paginationBucket;
271 * Setter for the pagination bucket variable which stores in this object the size of results to return
275 public void setPaginationBucket(int pb) {
276 this.paginationBucket = pb;
280 * Getter to return the pagination index requested by the user when requesting paginated results
284 public int getPaginationIndex() {
285 return this.paginationIndex;
289 * Sets the pagination index that was passed in by the user, to determine which index or results to retrieve when
294 public void setPaginationIndex(int pi) {
298 this.paginationIndex = pi;
302 * Sets the total vertices variables and calculates the amount of pages based on size and total vertices
304 * @param totalVertices
305 * @param paginationBucketSize
307 public void setTotalsForPaging(int totalVertices, int paginationBucketSize) {
308 this.totalVertices = totalVertices;
309 // set total number of buckets equal to full pages
310 this.totalPaginationBuckets = totalVertices / paginationBucketSize;
311 // conditionally add a page for the remainder
312 if (totalVertices % paginationBucketSize > 0) {
313 this.totalPaginationBuckets++;
318 * @return the total amount of pages
320 public int getTotalPaginationBuckets() {
321 return this.totalPaginationBuckets;
326 * @return the total number of vertices when paginated
328 public int getTotalVertices() {
329 return this.totalVertices;
335 * @param requests the requests
336 * @param sourceOfTruth the source of truth
339 * @throws AAIException the AAI exception
341 public Pair<Boolean, List<Pair<URI, Response>>> process(List<DBRequest> requests, String sourceOfTruth,
342 boolean enableResourceVersion) throws AAIException {
344 DBSerializer serializer = null;
346 if(serverBase != null){
347 serializer = new DBSerializer(version, dbEngine, introspectorFactoryType, sourceOfTruth, notificationDepth, serverBase);
349 serializer = new DBSerializer(version, dbEngine, introspectorFactoryType, sourceOfTruth, notificationDepth);
356 String transactionId = null;
358 Format format = null;
359 List<Pair<URI, Response>> responses = new ArrayList<>();
360 MultivaluedMap<String, String> params;
363 boolean success = true;
364 QueryEngine queryEngine = dbEngine.getQueryEngine();
365 Set<Vertex> mainVertexesToNotifyOn = new LinkedHashSet<>();
367 AaiDBMetricLog metricLog = new AaiDBMetricLog(AAIConstants.AAI_RESOURCES_MS);
369 String outputMediaType = null;
371 if(requests != null && !requests.isEmpty()){
372 HttpHeaders headers = requests.get(0).getHeaders();
373 outputMediaType = getMediaType(headers.getAcceptableMediaTypes());
376 for (DBRequest request : requests) {
378 Status status = Status.NOT_FOUND;
379 method = request.getMethod();
380 metricLog.pre(request);
384 obj = request.getIntrospector();
385 query = request.getParser();
386 transactionId = request.getTransactionId();
387 uriTemp = request.getUri().getRawPath().replaceFirst("^v\\d+/", "");
388 uri = UriBuilder.fromPath(uriTemp).build();
389 List<Vertex> vertTemp;
390 List<Vertex> vertices;
391 if (this.isPaginated()) {
392 vertTemp = query.getQueryBuilder().toList();
393 this.setTotalsForPaging(vertTemp.size(), this.paginationBucket);
394 vertices = vertTemp.subList(((this.paginationIndex - 1) * this.paginationBucket),
395 Math.min((this.paginationBucket * this.paginationIndex), vertTemp.size()));
397 vertices = query.getQueryBuilder().toList();
400 HttpHeaders headers = request.getHeaders();
401 outputMediaType = getMediaType(headers.getAcceptableMediaTypes());
402 String result = null;
403 params = request.getInfo().getQueryParameters(false);
404 depth = setDepth(obj, params.getFirst("depth"));
405 if (params.containsKey("format")) {
406 format = Format.getFormat(params.getFirst("format"));
408 String cleanUp = params.getFirst("cleanup");
409 String requestContext = "";
410 List<String> requestContextList = request.getHeaders().getRequestHeader("aai-request-context");
411 if (requestContextList != null) {
412 requestContext = requestContextList.get(0);
415 if (cleanUp == null) {
418 if (vertices.size() > 1 && processSingle
419 && !(method.equals(HttpMethod.GET) || method.equals(HttpMethod.GET_RELATIONSHIP))) {
420 if (method.equals(HttpMethod.DELETE)) {
422 throw new AAIException("AAI_6138");
424 throw new AAIException("AAI_6137");
427 if (method.equals(HttpMethod.PUT)) {
428 String resourceVersion = obj.getValue(AAIProperties.RESOURCE_VERSION);
429 if (vertices.isEmpty()) {
430 if (enableResourceVersion) {
431 serializer.verifyResourceVersion("create", query.getResultType(), "",
432 resourceVersion, obj.getURI());
436 if (enableResourceVersion) {
437 serializer.verifyResourceVersion("update", query.getResultType(),
438 vertices.get(0).<String>property(AAIProperties.RESOURCE_VERSION).orElse(null),
439 resourceVersion, obj.getURI());
444 if (vertices.isEmpty()) {
445 String msg = createNotFoundMessage(query.getResultType(), request.getUri());
446 throw new AAIException("AAI_6114", msg);
457 * This skip-related-to query parameter is used to determine if the relationships object will omit the related-to-property
458 * If a GET is sent to resources without a format, if format=resource, or if format=resource_and_url with this param set to false
459 * then behavior will be keep the related-to properties. By default, set to true.
460 * Otherwise, for any other case, when the skip-related-to parameter exists, has value=true, or some unfamiliar input (e.g. skip-related-to=bogusvalue), the value is true.
462 boolean isSkipRelatedTo = true;
463 if (params.containsKey("skip-related-to")) {
464 String skipRelatedTo = params.getFirst("skip-related-to");
465 isSkipRelatedTo = !(skipRelatedTo != null && skipRelatedTo.equals("false"));
467 // if skip-related-to param is missing, then default it to false;
468 isSkipRelatedTo = false;
471 HashMap<String, Introspector> relatedObjects = new HashMap<>();
472 String nodeOnly = params.getFirst("nodes-only");
473 boolean isNodeOnly = nodeOnly != null;
477 if (format == null) {
478 obj = this.getObjectFromDb(vertices, serializer, query, obj, request.getUri(),
479 depth, isNodeOnly, cleanUp, isSkipRelatedTo);
483 MarshallerProperties properties;
484 if (!request.getMarshallerProperties().isPresent()) {
485 properties = new MarshallerProperties.Builder(
486 org.onap.aai.restcore.MediaType.getEnum(outputMediaType)).build();
488 properties = request.getMarshallerProperties().get();
490 result = obj.marshal(properties);
494 new FormatFactory(loader, serializer, schemaVersions, basePath + "/", serverBase);
495 Formatter formatter = ff.get(format, params);
496 result = formatter.output(vertices.stream().map(vertex -> (Object) vertex)
497 .collect(Collectors.toList())).toString();
499 if(outputMediaType == null){
500 outputMediaType = MediaType.APPLICATION_JSON;
503 if(MediaType.APPLICATION_XML_TYPE.isCompatible(MediaType.valueOf(outputMediaType))){
504 result = xmlFormatTransformer.transform(result);
510 case GET_RELATIONSHIP:
511 if (format == null) {
512 obj = this.getRelationshipObjectFromDb(vertices, serializer, query,
513 request.getInfo().getRequestUri(), isSkipRelatedTo);
517 MarshallerProperties properties;
518 if (!request.getMarshallerProperties().isPresent()) {
519 properties = new MarshallerProperties.Builder(
520 org.onap.aai.restcore.MediaType.getEnum(outputMediaType)).build();
522 properties = request.getMarshallerProperties().get();
524 result = obj.marshal(properties);
526 String msg = createRelationshipNotFoundMessage(query.getResultType(),
528 throw new AAIException("AAI_6149", msg);
532 new FormatFactory(loader, serializer, schemaVersions, basePath + "/", serverBase);
533 Formatter formatter = ff.get(format, params);
534 result = formatter.output(vertices.stream().map(vertex -> (Object) vertex)
535 .collect(Collectors.toList())).toString();
537 if(outputMediaType == null){
538 outputMediaType = MediaType.APPLICATION_JSON;
541 if(MediaType.APPLICATION_XML_TYPE.isCompatible(MediaType.valueOf(outputMediaType))){
542 result = xmlFormatTransformer.transform(result);
549 v = serializer.createNewVertex(obj);
551 serializer.serializeToDb(obj, v, query, uri.getRawPath(), requestContext);
554 status = Status.CREATED;
557 mainVertexesToNotifyOn.add(v);
558 if (notificationDepth == AAIProperties.MINIMUM_DEPTH) {
559 Map<String, Pair<Introspector, LinkedHashMap<String,Introspector>>> allImpliedDeleteObjs = serializer.getImpliedDeleteUriObjectPair();
561 for(Map.Entry<String, Pair<Introspector, LinkedHashMap<String,Introspector>>> entry: allImpliedDeleteObjs.entrySet()){
562 // The format is purposefully %s/%s%s due to the fact
563 // that every aai-uri will have a slash at the beginning
564 // If that assumption isn't true, then its best to change this code
565 String curUri = String.format("%s/%s%s", basePath , version , entry.getKey());
566 Introspector curObj = entry.getValue().getValue0();
567 HashMap<String, Introspector> curObjRelated = entry.getValue().getValue1();
568 notification.createNotificationEvent(transactionId, sourceOfTruth, Status.NO_CONTENT, URI.create(curUri), curObj, curObjRelated, basePath);
574 serializer.touchStandardVertexProperties(v, false);
575 Vertex relatedVertex = serializer.createEdge(obj, v);
578 mainVertexesToNotifyOn.add(v);
579 serializer.addVertexToEdgeVertexes(relatedVertex);
582 Introspector existingObj = loader.introspectorFromName(obj.getDbName());
583 existingObj = this.getObjectFromDb(vertices, serializer, query, existingObj,
584 request.getUri(), 0, false, cleanUp);
585 String existingJson = existingObj.marshal(false);
588 if (request.getRawRequestContent().isPresent()) {
589 newJson = request.getRawRequestContent().get();
593 Object relationshipList = request.getIntrospector().getValue("relationship-list");
594 ObjectMapper mapper = new ObjectMapper();
596 JsonNode existingNode = mapper.readTree(existingJson);
597 JsonNode newNode = mapper.readTree(newJson);
598 JsonMergePatch patch = JsonMergePatch.fromJson(newNode);
599 JsonNode completed = patch.apply(existingNode);
600 String patched = mapper.writeValueAsString(completed);
601 Introspector patchedObj = loader.unmarshal(existingObj.getName(), patched);
602 if (relationshipList == null && patchedObj.hasProperty("relationship-list")) {
603 // if the caller didn't touch the relationship-list, we shouldn't either
604 patchedObj.setValue("relationship-list", null);
606 serializer.serializeToDb(patchedObj, v, query, uri.getRawPath(), requestContext);
608 mainVertexesToNotifyOn.add(v);
609 } catch (IOException | JsonPatchException e) {
610 throw new AAIException("AAI_3000", "could not perform patch operation");
614 String resourceVersion = params.getFirst(AAIProperties.RESOURCE_VERSION);
615 obj = serializer.getLatestVersionView(v, notificationDepth);
616 if (query.isDependent()) {
618 serializer.getRelatedObjects(queryEngine, v, obj, this.loader);
621 * Find all Delete-other-vertex vertices and create structure for notify
622 * findDeleatble also returns the startVertex v and we dont want to create
623 * duplicate notification events for the same
624 * So remove the startvertex first
627 List<Vertex> deletableVertices = dbEngine.getQueryEngine().findDeletable(v);
631 * I am assuming vertexId cant be null
633 deletableVertices.removeIf(s -> vId.equals(s.id()));
634 boolean isDelVerticesPresent = !deletableVertices.isEmpty();
635 Map<Vertex, Introspector> deleteObjects = new HashMap<>();
636 Map<String, URI> uriMap = new HashMap<>();
637 Map<String, HashMap<String, Introspector>> deleteRelatedObjects = new HashMap<>();
639 if (isDelVerticesPresent) {
640 deleteObjects = this.buildIntrospectorObjects(serializer, deletableVertices);
642 uriMap = this.buildURIMap(serializer, deleteObjects);
643 deleteRelatedObjects =
644 this.buildRelatedObjects(serializer, queryEngine, deleteObjects);
647 serializer.delete(v, deletableVertices, resourceVersion, enableResourceVersion);
648 status = Status.NO_CONTENT;
649 notification.createNotificationEvent(transactionId, sourceOfTruth, status, uri, obj,
650 relatedObjects, basePath);
653 * Notify delete-other-v candidates
656 if (isDelVerticesPresent) {
657 this.buildNotificationEvent(sourceOfTruth, status, transactionId, notification,
658 deleteObjects, uriMap, deleteRelatedObjects, basePath);
662 serializer.touchStandardVertexProperties(v, false);
663 Optional<Vertex> otherV = serializer.deleteEdge(obj, v);
665 status = Status.NO_CONTENT;
666 if (otherV.isPresent()) {
667 mainVertexesToNotifyOn.add(v);
668 serializer.addVertexToEdgeVertexes(otherV.get());
676 * temporarily adding vertex id to the headers
677 * to be able to use for testing the vertex id endpoint functionality
678 * since we presently have no other way of generating those id urls
680 if (response == null && v != null
681 && (method.equals(HttpMethod.PUT) || method.equals(HttpMethod.GET)
682 || method.equals(HttpMethod.MERGE_PATCH)
683 || method.equals(HttpMethod.GET_RELATIONSHIP))
686 String myvertid = v.id().toString();
687 if (this.isPaginated()) {
688 response = Response.status(status).header("vertex-id", myvertid)
689 .header("total-results", this.getTotalVertices())
690 .header("total-pages", this.getTotalPaginationBuckets()).entity(result)
691 .type(outputMediaType).build();
693 response = Response.status(status).header("vertex-id", myvertid).entity(result)
694 .type(outputMediaType).build();
696 } else if (response == null) {
697 response = Response.status(status).type(outputMediaType).build();
698 } // else, response already set to something
700 Pair<URI, Response> pairedResp = Pair.with(request.getUri(), response);
701 responses.add(pairedResp);
702 } catch (JanusGraphException e) {
703 this.dbEngine.rollback();
704 throw new AAIException("AAI_6134", e);
706 } catch (AAIException e) {
708 ArrayList<String> templateVars = new ArrayList<>();
709 templateVars.add(request.getMethod().toString()); // GET, PUT, etc
710 templateVars.add(request.getUri().getPath());
711 templateVars.addAll(e.getTemplateVars());
712 ErrorLogHelper.logException(e);
713 response = Response.status(e.getErrorObject().getHTTPResponseCode()).entity(ErrorLogHelper
714 .getRESTAPIErrorResponse(request.getHeaders().getAcceptableMediaTypes(), e, templateVars))
715 .type(outputMediaType)
717 Pair<URI, Response> pairedResp = Pair.with(request.getUri(), response);
718 responses.add(pairedResp);
719 } catch (Exception e) {
721 AAIException ex = new AAIException("AAI_4000", e);
722 ArrayList<String> templateVars = new ArrayList<>();
723 templateVars.add(request.getMethod().toString()); // GET, PUT, etc
724 templateVars.add(request.getUri().getPath());
725 ErrorLogHelper.logException(ex);
726 response = Response.status(ex.getErrorObject().getHTTPResponseCode()).entity(ErrorLogHelper
727 .getRESTAPIErrorResponse(request.getHeaders().getAcceptableMediaTypes(), ex, templateVars))
728 .type(outputMediaType)
730 Pair<URI, Response> pairedResp = Pair.with(request.getUri(), response);
731 responses.add(pairedResp);
734 if (response != null) {
735 metricLog.post(request, response);
741 generateEvents(sourceOfTruth, serializer, transactionId, queryEngine, mainVertexesToNotifyOn);
743 notification.clearEvents();
746 return Pair.with(success, responses);
750 * Generate notification events for the resulting db requests.
752 private void generateEvents(String sourceOfTruth, DBSerializer serializer, String transactionId, QueryEngine queryEngine, Set<Vertex> mainVertexesToNotifyOn) throws AAIException {
753 if (notificationDepth == AAIProperties.MINIMUM_DEPTH) {
754 serializer.getUpdatedVertexes().entrySet().stream().filter(Map.Entry::getValue).map(Map.Entry::getKey).forEach(mainVertexesToNotifyOn::add);
756 Set<Vertex> edgeVertexes = serializer.touchStandardVertexPropertiesForEdges().stream()
757 .filter(v -> !mainVertexesToNotifyOn.contains(v)).collect(Collectors.toSet());
759 createNotificationEvents(mainVertexesToNotifyOn, sourceOfTruth, serializer, transactionId, queryEngine, notificationDepth);
760 if("true".equals(AAIConfig.get("aai.notification.both.sides.enabled", "true"))){
761 createNotificationEvents(edgeVertexes, sourceOfTruth, serializer, transactionId, queryEngine, AAIProperties.MINIMUM_DEPTH);
763 } catch (UnsupportedEncodingException e) {
764 LOGGER.warn("Encountered exception generating events", e);
767 // Since @Autowired required is set to false, we need to do a null check
768 // for the existence of the validationService since its only enabled if profile is enabled
769 if(validationService != null){
770 validationService.validate(notification.getEvents());
772 notification.triggerEvents();
773 if (isDeltaEventsEnabled) {
775 DeltaEvents deltaEvents = new DeltaEvents(transactionId, sourceOfTruth, version.toString(), serializer.getObjectDeltas());
776 deltaEvents.triggerEvents();
777 } catch (Exception e) {
778 LOGGER.error("Error sending Delta Events", e);
784 * Generate notification events for provided set of vertexes at the specified depth
786 private void createNotificationEvents(Set<Vertex> vertexesToNotifyOn, String sourceOfTruth, DBSerializer serializer,
787 String transactionId, QueryEngine queryEngine, int eventDepth) throws AAIException, UnsupportedEncodingException {
788 for(Vertex vertex : vertexesToNotifyOn){
789 if (canGenerateEvent(vertex)) {
790 boolean isCurVertexNew = vertex.value(AAIProperties.CREATED_TS).equals(vertex.value(AAIProperties.LAST_MOD_TS));
791 Status curObjStatus = (isCurVertexNew) ? Status.CREATED : Status.OK;
793 Introspector curObj = serializer.getLatestVersionView(vertex, eventDepth);
794 String aaiUri = vertex.<String>property(AAIProperties.AAI_URI).value();
795 String uri = String.format("%s/%s%s", basePath, version, aaiUri);
796 HashMap<String, Introspector> curRelatedObjs = new HashMap<>();
797 if (!curObj.isTopLevel()) {
798 curRelatedObjs = serializer.getRelatedObjects(queryEngine, vertex, curObj, this.loader);
800 notification.createNotificationEvent(transactionId, sourceOfTruth, curObjStatus, URI.create(uri), curObj, curRelatedObjs, basePath);
806 * Verifies that vertex has needed properties to generate on
807 * @param vertex Vertex to be verified
808 * @return <code>true</code> if vertex has necessary properties and exists
810 private boolean canGenerateEvent(Vertex vertex) {
811 boolean canGenerate = true;
813 if(!vertex.property(AAIProperties.AAI_URI).isPresent()){
814 LOGGER.debug("Encountered an vertex {} with missing aai-uri", vertex.id());
816 } else if(!vertex.property(AAIProperties.CREATED_TS).isPresent() || !vertex.property(AAIProperties.LAST_MOD_TS).isPresent()){
817 LOGGER.debug("Encountered an vertex {} with missing timestamp", vertex.id());
820 } catch (IllegalStateException e) {
821 if (e.getMessage().contains(" was removed")) {
822 LOGGER.warn("Attempted to generate event for non existent vertex", e);
824 LOGGER.warn("Encountered exception generating events", e);
832 * Gets the media type.
834 * @param mediaTypeList the media type list
835 * @return the media type
837 private String getMediaType(List<MediaType> mediaTypeList) {
838 String mediaType = MediaType.APPLICATION_JSON; // json is the default
839 for (MediaType mt : mediaTypeList) {
840 if (MediaType.APPLICATION_XML_TYPE.isCompatible(mt)) {
841 mediaType = MediaType.APPLICATION_XML;
848 * Gets the object from db.
850 * @param serializer the serializer
851 * @param query the query
854 * @param depth the depth
855 * @param cleanUp the clean up
856 * @return the object from db
857 * @throws AAIException the AAI exception
858 * @throws IllegalAccessException the illegal access exception
859 * @throws IllegalArgumentException the illegal argument exception
860 * @throws InvocationTargetException the invocation target exception
861 * @throws SecurityException the security exception
862 * @throws InstantiationException the instantiation exception
863 * @throws NoSuchMethodException the no such method exception
864 * @throws UnsupportedEncodingException the unsupported encoding exception
865 * @throws MalformedURLException the malformed URL exception
866 * @throws AAIUnknownObjectException
867 * @throws URISyntaxException
869 private Introspector getObjectFromDb(List<Vertex> results, DBSerializer serializer, QueryParser query,
870 Introspector obj, URI uri, int depth, boolean nodeOnly, String cleanUp)
871 throws AAIException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
872 SecurityException, InstantiationException, NoSuchMethodException, UnsupportedEncodingException,
873 AAIUnknownObjectException, URISyntaxException {
876 if (results.isEmpty()) {
877 String msg = createNotFoundMessage(query.getResultType(), uri);
878 throw new AAIException("AAI_6114", msg);
881 return serializer.dbToObject(results, obj, depth, nodeOnly, cleanUp);
885 * Gets the object from db.
887 * @param serializer the serializer
888 * @param query the query
891 * @param depth the depth
892 * @param cleanUp the clean up
893 * @param isSkipRelatedTo include related to flag
894 * @return the object from db
895 * @throws AAIException the AAI exception
896 * @throws IllegalAccessException the illegal access exception
897 * @throws IllegalArgumentException the illegal argument exception
898 * @throws InvocationTargetException the invocation target exception
899 * @throws SecurityException the security exception
900 * @throws InstantiationException the instantiation exception
901 * @throws NoSuchMethodException the no such method exception
902 * @throws UnsupportedEncodingException the unsupported encoding exception
903 * @throws MalformedURLException the malformed URL exception
904 * @throws AAIUnknownObjectException
905 * @throws URISyntaxException
907 private Introspector getObjectFromDb(List<Vertex> results, DBSerializer serializer, QueryParser query,
908 Introspector obj, URI uri, int depth, boolean nodeOnly, String cleanUp, boolean isSkipRelatedTo)
909 throws AAIException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
910 SecurityException, InstantiationException, NoSuchMethodException, UnsupportedEncodingException,
911 AAIUnknownObjectException, URISyntaxException {
914 if (results.isEmpty()) {
915 String msg = createNotFoundMessage(query.getResultType(), uri);
916 throw new AAIException("AAI_6114", msg);
919 return serializer.dbToObject(results, obj, depth, nodeOnly, cleanUp, isSkipRelatedTo);
924 * Gets the object from db.
926 * @param serializer the serializer
927 * @param query the query
929 * @return the object from db
930 * @throws AAIException the AAI exception
931 * @throws IllegalAccessException the illegal access exception
932 * @throws IllegalArgumentException the illegal argument exception
933 * @throws InvocationTargetException the invocation target exception
934 * @throws SecurityException the security exception
935 * @throws InstantiationException the instantiation exception
936 * @throws NoSuchMethodException the no such method exception
937 * @throws UnsupportedEncodingException the unsupported encoding exception
938 * @throws MalformedURLException the malformed URL exception
939 * @throws AAIUnknownObjectException
940 * @throws URISyntaxException
942 private Introspector getRelationshipObjectFromDb(List<Vertex> results, DBSerializer serializer, QueryParser query,
943 URI uri, boolean isSkipRelatedTo) throws AAIException, IllegalArgumentException, SecurityException, UnsupportedEncodingException,
944 AAIUnknownObjectException {
947 if (results.isEmpty()) {
948 String msg = createNotFoundMessage(query.getResultType(), uri);
949 throw new AAIException("AAI_6114", msg);
952 if (results.size() > 1) {
953 throw new AAIException("AAI_6148", uri.getPath());
956 Vertex v = results.get(0);
957 return serializer.dbToRelationshipObject(v, isSkipRelatedTo);
961 * Creates the not found message.
963 * @param resultType the result type
967 private String createNotFoundMessage(String resultType, URI uri) {
968 return "No Node of type " + resultType + " found at: " + uri.getPath();
972 * Creates the not found message.
974 * @param resultType the result type
978 private String createRelationshipNotFoundMessage(String resultType, URI uri) {
979 return "No relationship found of type " + resultType + " at the given URI: " + uri.getPath()
980 + "/relationship-list";
986 * @param depthParam the depth param
988 * @throws AAIException the AAI exception
990 protected int setDepth(Introspector obj, String depthParam) throws AAIException {
991 int depth = AAIProperties.MAXIMUM_DEPTH;
993 String getAllRandomStr = AAIConfig.get("aai.rest.getall.depthparam", "");
994 if (getAllRandomStr != null && !getAllRandomStr.isEmpty() && getAllRandomStr.equals(depthParam)) {
998 if (depthParam == null) {
999 if (this.version.compareTo(schemaVersions.getDepthVersion()) >= 0) {
1003 if (!depthParam.isEmpty() && !"all".equals(depthParam)) {
1005 depth = Integer.parseInt(depthParam);
1006 } catch (Exception e) {
1007 throw new AAIException("AAI_4016");
1012 String maxDepth = obj.getMetadata(ObjectMetadata.MAXIMUM_DEPTH);
1014 int maximumDepth = AAIProperties.MAXIMUM_DEPTH;
1016 if (maxDepth != null) {
1018 maximumDepth = Integer.parseInt(maxDepth);
1019 } catch (Exception ex) {
1020 throw new AAIException("AAI_4018");
1024 if (depth > maximumDepth) {
1025 throw new AAIException("AAI_3303");
1031 private Map<Vertex, Introspector> buildIntrospectorObjects(DBSerializer serializer, Iterable<Vertex> vertices) {
1032 Map<Vertex, Introspector> deleteObjectMap = new HashMap<>();
1033 for (Vertex vertex : vertices) {
1035 Introspector deleteObj = serializer.getLatestVersionView(vertex, notificationDepth);
1036 deleteObjectMap.put(vertex, deleteObj);
1037 } catch (UnsupportedEncodingException | AAIException e) {
1038 LOGGER.warn("Unable to get Introspctor Objects, Just continue");
1043 return deleteObjectMap;
1047 private Map<String, URI> buildURIMap(DBSerializer serializer, Map<Vertex, Introspector> introSpector) {
1048 Map<String, URI> uriMap = new HashMap<>();
1049 for (Map.Entry<Vertex, Introspector> entry : introSpector.entrySet()) {
1052 uri = serializer.getURIForVertex(entry.getKey());
1053 if (null != entry.getValue()) {
1054 uriMap.put(entry.getValue().getObjectId(), uri);
1056 } catch (UnsupportedEncodingException e) {
1057 LOGGER.warn("Unable to get URIs, Just continue");
1066 private Map<String, HashMap<String, Introspector>> buildRelatedObjects(DBSerializer serializer,
1067 QueryEngine queryEngine, Map<Vertex, Introspector> introSpector) {
1069 Map<String, HashMap<String, Introspector>> relatedObjectsMap = new HashMap<>();
1070 for (Map.Entry<Vertex, Introspector> entry : introSpector.entrySet()) {
1072 HashMap<String, Introspector> relatedObjects =
1073 serializer.getRelatedObjects(queryEngine, entry.getKey(), entry.getValue(), this.loader);
1074 if (null != entry.getValue()) {
1075 relatedObjectsMap.put(entry.getValue().getObjectId(), relatedObjects);
1077 } catch (IllegalArgumentException | SecurityException
1078 | UnsupportedEncodingException | AAIException e) {
1079 LOGGER.warn("Unable to get realted Objects, Just continue");
1084 return relatedObjectsMap;
1088 private void buildNotificationEvent(String sourceOfTruth, Status status, String transactionId,
1089 UEBNotification notification, Map<Vertex, Introspector> deleteObjects, Map<String, URI> uriMap,
1090 Map<String, HashMap<String, Introspector>> deleteRelatedObjects, String basePath) {
1091 for (Map.Entry<Vertex, Introspector> entry : deleteObjects.entrySet()) {
1093 if (null != entry.getValue()) {
1094 String vertexObjectId = entry.getValue().getObjectId();
1096 if (uriMap.containsKey(vertexObjectId) && deleteRelatedObjects.containsKey(vertexObjectId)) {
1097 notification.createNotificationEvent(transactionId, sourceOfTruth, status,
1098 uriMap.get(vertexObjectId), entry.getValue(), deleteRelatedObjects.get(vertexObjectId),
1102 } catch (UnsupportedEncodingException | AAIException e) {
1104 LOGGER.warn("Error in sending notification");
1109 public void setPaginationParameters(String resultIndex, String resultSize) {
1110 if (resultIndex != null && !"-1".equals(resultIndex) && resultSize != null && !"-1".equals(resultSize)) {
1111 this.setPaginationIndex(Integer.parseInt(resultIndex));
1112 this.setPaginationBucket(Integer.parseInt(resultSize));
1116 public List<Object> getPaginatedVertexListForAggregateFormat(List<Object> aggregateVertexList) throws AAIException {
1117 List<Object> finalList = new Vector<>();
1118 if (this.isPaginated()) {
1119 if (aggregateVertexList != null && !aggregateVertexList.isEmpty()) {
1120 int listSize = aggregateVertexList.size();
1121 if (listSize == 1) {
1122 List<Object> vertexList = (List<Object>) aggregateVertexList.get(0);
1123 this.setTotalsForPaging(vertexList.size(), this.getPaginationBucket());
1124 int startIndex = (this.getPaginationIndex() - 1) * this.getPaginationBucket();
1125 int endIndex = Math.min((this.getPaginationBucket() * this.getPaginationIndex()), vertexList.size());
1126 if (startIndex > endIndex) {
1127 throw new AAIException("AAI_6150",
1128 " ResultIndex is not appropriate for the result set, Needs to be <= " + endIndex);
1130 finalList.add(new ArrayList<Object>());
1131 for (int i = startIndex; i < endIndex; i++) {
1132 ((ArrayList<Object>) finalList.get(0)).add(((ArrayList<Object>) aggregateVertexList.get(0)).get(i));
1138 // If the list size is greater than 1 or if pagination is not needed, return the original list.
1139 return aggregateVertexList;
1142 public List<Object> getPaginatedVertexList(List<Object> vertexList) throws AAIException {
1143 List<Object> vertices;
1144 if (this.isPaginated()) {
1145 this.setTotalsForPaging(vertexList.size(), this.getPaginationBucket());
1146 int startIndex = (this.getPaginationIndex() - 1) * this.getPaginationBucket();
1147 int endIndex = Math.min((this.getPaginationBucket() * this.getPaginationIndex()), vertexList.size());
1148 if (startIndex > endIndex) {
1149 throw new AAIException("AAI_6150",
1150 " ResultIndex is not appropriate for the result set, Needs to be <= " + endIndex);
1152 vertices = vertexList.subList(startIndex, endIndex);
1154 vertices = vertexList;