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;
113 private XmlFormatTransformer xmlFormatTransformer;
116 * Inject the validation service if the profile pre-valiation is enabled,
117 * Otherwise this variable will be set to null and thats why required=false
118 * so that it can continue even if pre validation isn't enabled
120 @Autowired(required = false)
121 private ValidationService validationService;
123 private UEBNotification notification;
125 private int notificationDepth;
128 * Instantiates a new http entry.
130 * @param modelType the model type
131 * @param queryStyle the query style
133 public HttpEntry(ModelType modelType, QueryStyle queryStyle) {
134 this.introspectorFactoryType = modelType;
135 this.queryStyle = queryStyle;
138 public HttpEntry setHttpEntryProperties(SchemaVersion version) {
139 this.version = version;
140 this.loader = loaderFactory.createLoaderForVersion(introspectorFactoryType, version);
141 this.dbEngine = new JanusGraphDBEngine(queryStyle, loader);
143 getDbEngine().startTransaction();
144 this.notification = new UEBNotification(loader, loaderFactory, schemaVersions);
145 if("true".equals(AAIConfig.get("aai.notification.depth.all.enabled", "true"))){
146 this.notificationDepth = AAIProperties.MAXIMUM_DEPTH;
148 this.notificationDepth = AAIProperties.MINIMUM_DEPTH;
153 public HttpEntry setHttpEntryProperties(SchemaVersion version, UEBNotification notification) {
154 this.version = version;
155 this.loader = loaderFactory.createLoaderForVersion(introspectorFactoryType, version);
156 this.dbEngine = new JanusGraphDBEngine(queryStyle, loader);
158 this.notification = notification;
160 if("true".equals(AAIConfig.get("aai.notification.depth.all.enabled", "true"))){
161 this.notificationDepth = AAIProperties.MAXIMUM_DEPTH;
163 this.notificationDepth = AAIProperties.MINIMUM_DEPTH;
165 // start transaction on creation
166 getDbEngine().startTransaction();
170 public HttpEntry setHttpEntryProperties(SchemaVersion version, UEBNotification notification, int notificationDepth) {
171 this.version = version;
172 this.loader = loaderFactory.createLoaderForVersion(introspectorFactoryType, version);
173 this.dbEngine = new JanusGraphDBEngine(queryStyle, loader);
175 this.notification = notification;
176 this.notificationDepth = notificationDepth;
177 // start transaction on creation
178 getDbEngine().startTransaction();
183 * Gets the introspector factory type.
185 * @return the introspector factory type
187 public ModelType getIntrospectorFactoryType() {
188 return introspectorFactoryType;
192 * Gets the query style.
194 * @return the query style
196 public QueryStyle getQueryStyle() {
203 * @return the version
205 public SchemaVersion getVersion() {
214 public Loader getLoader() {
219 * Gets the db engine.
221 * @return the db engine
223 public TransactionalGraphEngine getDbEngine() {
227 public Pair<Boolean, List<Pair<URI, Response>>> process(List<DBRequest> requests, String sourceOfTruth)
228 throws AAIException {
229 return this.process(requests, sourceOfTruth, true);
233 * Checks the pagination bucket and pagination index variables to determine whether or not the user
234 * requested paginated results
236 * @return a boolean true/false of whether the user requested paginated results
238 public boolean isPaginated() {
239 return this.paginationBucket > -1 && this.paginationIndex > -1;
243 * Returns the pagination size
245 * @return integer of the size of results to be returned when paginated
247 public int getPaginationBucket() {
248 return this.paginationBucket;
252 * Setter for the pagination bucket variable which stores in this object the size of results to return
256 public void setPaginationBucket(int pb) {
257 this.paginationBucket = pb;
261 * Getter to return the pagination index requested by the user when requesting paginated results
265 public int getPaginationIndex() {
266 return this.paginationIndex;
270 * Sets the pagination index that was passed in by the user, to determine which index or results to retrieve when
275 public void setPaginationIndex(int pi) {
279 this.paginationIndex = pi;
283 * Sets the total vertices variables and calculates the amount of pages based on size and total vertices
285 * @param totalVertices
286 * @param paginationBucketSize
288 public void setTotalsForPaging(int totalVertices, int paginationBucketSize) {
289 this.totalVertices = totalVertices;
290 // set total number of buckets equal to full pages
291 this.totalPaginationBuckets = totalVertices / paginationBucketSize;
292 // conditionally add a page for the remainder
293 if (totalVertices % paginationBucketSize > 0) {
294 this.totalPaginationBuckets++;
299 * @return the total amount of pages
301 public int getTotalPaginationBuckets() {
302 return this.totalPaginationBuckets;
307 * @return the total number of vertices when paginated
309 public int getTotalVertices() {
310 return this.totalVertices;
316 * @param requests the requests
317 * @param sourceOfTruth the source of truth
320 * @throws AAIException the AAI exception
322 public Pair<Boolean, List<Pair<URI, Response>>> process(List<DBRequest> requests, String sourceOfTruth,
323 boolean enableResourceVersion) throws AAIException {
325 DBSerializer serializer = new DBSerializer(version, dbEngine, introspectorFactoryType, sourceOfTruth, notificationDepth);
330 String transactionId = null;
332 Format format = null;
333 List<Pair<URI, Response>> responses = new ArrayList<>();
334 MultivaluedMap<String, String> params;
337 boolean success = true;
338 QueryEngine queryEngine = dbEngine.getQueryEngine();
339 Set<Vertex> mainVertexesToNotifyOn = new LinkedHashSet<>();
341 AaiDBMetricLog metricLog = new AaiDBMetricLog(AAIConstants.AAI_RESOURCES_MS);
343 String outputMediaType = null;
345 if(requests != null && !requests.isEmpty()){
346 HttpHeaders headers = requests.get(0).getHeaders();
347 outputMediaType = getMediaType(headers.getAcceptableMediaTypes());
350 for (DBRequest request : requests) {
352 Status status = Status.NOT_FOUND;
353 method = request.getMethod();
354 metricLog.pre(request);
358 obj = request.getIntrospector();
359 query = request.getParser();
360 transactionId = request.getTransactionId();
361 uriTemp = request.getUri().getRawPath().replaceFirst("^v\\d+/", "");
362 uri = UriBuilder.fromPath(uriTemp).build();
363 List<Vertex> vertTemp;
364 List<Vertex> vertices;
365 if (this.isPaginated()) {
366 vertTemp = query.getQueryBuilder().toList();
367 this.setTotalsForPaging(vertTemp.size(), this.paginationBucket);
368 vertices = vertTemp.subList(((this.paginationIndex - 1) * this.paginationBucket),
369 Math.min((this.paginationBucket * this.paginationIndex), vertTemp.size()));
371 vertices = query.getQueryBuilder().toList();
374 HttpHeaders headers = request.getHeaders();
375 outputMediaType = getMediaType(headers.getAcceptableMediaTypes());
376 String result = null;
377 params = request.getInfo().getQueryParameters(false);
378 depth = setDepth(obj, params.getFirst("depth"));
379 if (params.containsKey("format")) {
380 format = Format.getFormat(params.getFirst("format"));
382 String cleanUp = params.getFirst("cleanup");
383 String requestContext = "";
384 List<String> requestContextList = request.getHeaders().getRequestHeader("aai-request-context");
385 if (requestContextList != null) {
386 requestContext = requestContextList.get(0);
389 if (cleanUp == null) {
392 if (vertices.size() > 1 && processSingle
393 && !(method.equals(HttpMethod.GET) || method.equals(HttpMethod.GET_RELATIONSHIP))) {
394 if (method.equals(HttpMethod.DELETE)) {
396 throw new AAIException("AAI_6138");
398 throw new AAIException("AAI_6137");
401 if (method.equals(HttpMethod.PUT)) {
402 String resourceVersion = obj.getValue(AAIProperties.RESOURCE_VERSION);
403 if (vertices.isEmpty()) {
404 if (enableResourceVersion) {
405 serializer.verifyResourceVersion("create", query.getResultType(), "",
406 resourceVersion, obj.getURI());
410 if (enableResourceVersion) {
411 serializer.verifyResourceVersion("update", query.getResultType(),
412 vertices.get(0).<String>property(AAIProperties.RESOURCE_VERSION).orElse(null),
413 resourceVersion, obj.getURI());
418 if (vertices.isEmpty()) {
419 String msg = createNotFoundMessage(query.getResultType(), request.getUri());
420 throw new AAIException("AAI_6114", msg);
431 * This skip-related-to query parameter is used to determine if the relationships object will omit the related-to-property
432 * 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
433 * then behavior will be keep the related-to properties. By default, set to true.
434 * 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.
436 boolean isSkipRelatedTo = true;
437 if (params.containsKey("skip-related-to")) {
438 String skipRelatedTo = params.getFirst("skip-related-to");
439 isSkipRelatedTo = !(skipRelatedTo != null && skipRelatedTo.equals("false"));
441 // if skip-related-to param is missing, then default it to false;
442 isSkipRelatedTo = false;
445 HashMap<String, Introspector> relatedObjects = new HashMap<>();
446 String nodeOnly = params.getFirst("nodes-only");
447 boolean isNodeOnly = nodeOnly != null;
451 if (format == null) {
452 obj = this.getObjectFromDb(vertices, serializer, query, obj, request.getUri(),
453 depth, isNodeOnly, cleanUp, isSkipRelatedTo);
457 MarshallerProperties properties;
458 if (!request.getMarshallerProperties().isPresent()) {
459 properties = new MarshallerProperties.Builder(
460 org.onap.aai.restcore.MediaType.getEnum(outputMediaType)).build();
462 properties = request.getMarshallerProperties().get();
464 result = obj.marshal(properties);
468 new FormatFactory(loader, serializer, schemaVersions, basePath + "/");
469 Formatter formatter = ff.get(format, params);
470 result = formatter.output(vertices.stream().map(vertex -> (Object) vertex)
471 .collect(Collectors.toList())).toString();
473 if(outputMediaType == null){
474 outputMediaType = MediaType.APPLICATION_JSON;
477 if(MediaType.APPLICATION_XML_TYPE.isCompatible(MediaType.valueOf(outputMediaType))){
478 result = xmlFormatTransformer.transform(result);
484 case GET_RELATIONSHIP:
485 if (format == null) {
486 obj = this.getRelationshipObjectFromDb(vertices, serializer, query,
487 request.getInfo().getRequestUri(), isSkipRelatedTo);
491 MarshallerProperties properties;
492 if (!request.getMarshallerProperties().isPresent()) {
493 properties = new MarshallerProperties.Builder(
494 org.onap.aai.restcore.MediaType.getEnum(outputMediaType)).build();
496 properties = request.getMarshallerProperties().get();
498 result = obj.marshal(properties);
500 String msg = createRelationshipNotFoundMessage(query.getResultType(),
502 throw new AAIException("AAI_6149", msg);
506 new FormatFactory(loader, serializer, schemaVersions, basePath + "/");
507 Formatter formatter = ff.get(format, params);
508 result = formatter.output(vertices.stream().map(vertex -> (Object) vertex)
509 .collect(Collectors.toList())).toString();
511 if(outputMediaType == null){
512 outputMediaType = MediaType.APPLICATION_JSON;
515 if(MediaType.APPLICATION_XML_TYPE.isCompatible(MediaType.valueOf(outputMediaType))){
516 result = xmlFormatTransformer.transform(result);
523 v = serializer.createNewVertex(obj);
525 serializer.serializeToDb(obj, v, query, uri.getRawPath(), requestContext);
528 status = Status.CREATED;
531 mainVertexesToNotifyOn.add(v);
532 if (notificationDepth == AAIProperties.MINIMUM_DEPTH) {
533 Map<String, Pair<Introspector, LinkedHashMap<String,Introspector>>> allImpliedDeleteObjs = serializer.getImpliedDeleteUriObjectPair();
535 for(Map.Entry<String, Pair<Introspector, LinkedHashMap<String,Introspector>>> entry: allImpliedDeleteObjs.entrySet()){
536 // The format is purposefully %s/%s%s due to the fact
537 // that every aai-uri will have a slash at the beginning
538 // If that assumption isn't true, then its best to change this code
539 String curUri = String.format("%s/%s%s", basePath , version , entry.getKey());
540 Introspector curObj = entry.getValue().getValue0();
541 HashMap<String, Introspector> curObjRelated = entry.getValue().getValue1();
542 notification.createNotificationEvent(transactionId, sourceOfTruth, Status.NO_CONTENT, URI.create(curUri), curObj, curObjRelated, basePath);
548 serializer.touchStandardVertexProperties(v, false);
549 Vertex relatedVertex = serializer.createEdge(obj, v);
552 mainVertexesToNotifyOn.add(v);
553 serializer.addVertexToEdgeVertexes(relatedVertex);
556 Introspector existingObj = loader.introspectorFromName(obj.getDbName());
557 existingObj = this.getObjectFromDb(vertices, serializer, query, existingObj,
558 request.getUri(), 0, false, cleanUp);
559 String existingJson = existingObj.marshal(false);
562 if (request.getRawRequestContent().isPresent()) {
563 newJson = request.getRawRequestContent().get();
567 Object relationshipList = request.getIntrospector().getValue("relationship-list");
568 ObjectMapper mapper = new ObjectMapper();
570 JsonNode existingNode = mapper.readTree(existingJson);
571 JsonNode newNode = mapper.readTree(newJson);
572 JsonMergePatch patch = JsonMergePatch.fromJson(newNode);
573 JsonNode completed = patch.apply(existingNode);
574 String patched = mapper.writeValueAsString(completed);
575 Introspector patchedObj = loader.unmarshal(existingObj.getName(), patched);
576 if (relationshipList == null && patchedObj.hasProperty("relationship-list")) {
577 // if the caller didn't touch the relationship-list, we shouldn't either
578 patchedObj.setValue("relationship-list", null);
580 serializer.serializeToDb(patchedObj, v, query, uri.getRawPath(), requestContext);
582 mainVertexesToNotifyOn.add(v);
583 } catch (IOException | JsonPatchException e) {
584 throw new AAIException("AAI_3000", "could not perform patch operation");
588 String resourceVersion = params.getFirst(AAIProperties.RESOURCE_VERSION);
589 obj = serializer.getLatestVersionView(v, notificationDepth);
590 if (query.isDependent()) {
592 serializer.getRelatedObjects(queryEngine, v, obj, this.loader);
595 * Find all Delete-other-vertex vertices and create structure for notify
596 * findDeleatble also returns the startVertex v and we dont want to create
597 * duplicate notification events for the same
598 * So remove the startvertex first
601 List<Vertex> deletableVertices = dbEngine.getQueryEngine().findDeletable(v);
605 * I am assuming vertexId cant be null
607 deletableVertices.removeIf(s -> vId.equals(s.id()));
608 boolean isDelVerticesPresent = !deletableVertices.isEmpty();
609 Map<Vertex, Introspector> deleteObjects = new HashMap<>();
610 Map<String, URI> uriMap = new HashMap<>();
611 Map<String, HashMap<String, Introspector>> deleteRelatedObjects = new HashMap<>();
613 if (isDelVerticesPresent) {
614 deleteObjects = this.buildIntrospectorObjects(serializer, deletableVertices);
616 uriMap = this.buildURIMap(serializer, deleteObjects);
617 deleteRelatedObjects =
618 this.buildRelatedObjects(serializer, queryEngine, deleteObjects);
621 serializer.delete(v, deletableVertices, resourceVersion, enableResourceVersion);
622 status = Status.NO_CONTENT;
623 notification.createNotificationEvent(transactionId, sourceOfTruth, status, uri, obj,
624 relatedObjects, basePath);
627 * Notify delete-other-v candidates
630 if (isDelVerticesPresent) {
631 this.buildNotificationEvent(sourceOfTruth, status, transactionId, notification,
632 deleteObjects, uriMap, deleteRelatedObjects, basePath);
636 serializer.touchStandardVertexProperties(v, false);
637 Optional<Vertex> otherV = serializer.deleteEdge(obj, v);
639 status = Status.NO_CONTENT;
640 if (otherV.isPresent()) {
641 mainVertexesToNotifyOn.add(v);
642 serializer.addVertexToEdgeVertexes(otherV.get());
650 * temporarily adding vertex id to the headers
651 * to be able to use for testing the vertex id endpoint functionality
652 * since we presently have no other way of generating those id urls
654 if (response == null && v != null
655 && (method.equals(HttpMethod.PUT) || method.equals(HttpMethod.GET)
656 || method.equals(HttpMethod.MERGE_PATCH)
657 || method.equals(HttpMethod.GET_RELATIONSHIP))
660 String myvertid = v.id().toString();
661 if (this.isPaginated()) {
662 response = Response.status(status).header("vertex-id", myvertid)
663 .header("total-results", this.getTotalVertices())
664 .header("total-pages", this.getTotalPaginationBuckets()).entity(result)
665 .type(outputMediaType).build();
667 response = Response.status(status).header("vertex-id", myvertid).entity(result)
668 .type(outputMediaType).build();
670 } else if (response == null) {
671 response = Response.status(status).type(outputMediaType).build();
672 } // else, response already set to something
674 Pair<URI, Response> pairedResp = Pair.with(request.getUri(), response);
675 responses.add(pairedResp);
676 } catch (JanusGraphException e) {
677 this.dbEngine.rollback();
678 throw new AAIException("AAI_6134", e);
680 } catch (AAIException e) {
682 ArrayList<String> templateVars = new ArrayList<>();
683 templateVars.add(request.getMethod().toString()); // GET, PUT, etc
684 templateVars.add(request.getUri().getPath());
685 templateVars.addAll(e.getTemplateVars());
686 ErrorLogHelper.logException(e);
687 response = Response.status(e.getErrorObject().getHTTPResponseCode()).entity(ErrorLogHelper
688 .getRESTAPIErrorResponse(request.getHeaders().getAcceptableMediaTypes(), e, templateVars))
689 .type(outputMediaType)
691 Pair<URI, Response> pairedResp = Pair.with(request.getUri(), response);
692 responses.add(pairedResp);
693 } catch (Exception e) {
695 AAIException ex = new AAIException("AAI_4000", e);
696 ArrayList<String> templateVars = new ArrayList<>();
697 templateVars.add(request.getMethod().toString()); // GET, PUT, etc
698 templateVars.add(request.getUri().getPath());
699 ErrorLogHelper.logException(ex);
700 response = Response.status(ex.getErrorObject().getHTTPResponseCode()).entity(ErrorLogHelper
701 .getRESTAPIErrorResponse(request.getHeaders().getAcceptableMediaTypes(), ex, templateVars))
702 .type(outputMediaType)
704 Pair<URI, Response> pairedResp = Pair.with(request.getUri(), response);
705 responses.add(pairedResp);
708 if (response != null) {
709 metricLog.post(request, response);
715 generateEvents(sourceOfTruth, serializer, transactionId, queryEngine, mainVertexesToNotifyOn);
717 notification.clearEvents();
720 return Pair.with(success, responses);
724 * Generate notification events for the resulting db requests.
726 private void generateEvents(String sourceOfTruth, DBSerializer serializer, String transactionId, QueryEngine queryEngine, Set<Vertex> mainVertexesToNotifyOn) throws AAIException {
727 if (notificationDepth == AAIProperties.MINIMUM_DEPTH) {
728 serializer.getUpdatedVertexes().entrySet().stream().filter(Map.Entry::getValue).map(Map.Entry::getKey).forEach(mainVertexesToNotifyOn::add);
730 Set<Vertex> edgeVertexes = serializer.touchStandardVertexPropertiesForEdges().stream()
731 .filter(v -> !mainVertexesToNotifyOn.contains(v)).collect(Collectors.toSet());
733 createNotificationEvents(mainVertexesToNotifyOn, sourceOfTruth, serializer, transactionId, queryEngine, notificationDepth);
734 if("true".equals(AAIConfig.get("aai.notification.both.sides.enabled", "true"))){
735 createNotificationEvents(edgeVertexes, sourceOfTruth, serializer, transactionId, queryEngine, AAIProperties.MINIMUM_DEPTH);
737 } catch (UnsupportedEncodingException e) {
738 LOGGER.warn("Encountered exception generating events", e);
741 // Since @Autowired required is set to false, we need to do a null check
742 // for the existence of the validationService since its only enabled if profile is enabled
743 if(validationService != null){
744 validationService.validate(notification.getEvents());
746 notification.triggerEvents();
747 if (isDeltaEventsEnabled) {
749 DeltaEvents deltaEvents = new DeltaEvents(transactionId, sourceOfTruth, version.toString(), serializer.getObjectDeltas());
750 deltaEvents.triggerEvents();
751 } catch (Exception e) {
752 LOGGER.error("Error sending Delta Events", e);
758 * Generate notification events for provided set of vertexes at the specified depth
760 private void createNotificationEvents(Set<Vertex> vertexesToNotifyOn, String sourceOfTruth, DBSerializer serializer,
761 String transactionId, QueryEngine queryEngine, int eventDepth) throws AAIException, UnsupportedEncodingException {
762 for(Vertex vertex : vertexesToNotifyOn){
763 if (canGenerateEvent(vertex)) {
764 boolean isCurVertexNew = vertex.value(AAIProperties.CREATED_TS).equals(vertex.value(AAIProperties.LAST_MOD_TS));
765 Status curObjStatus = (isCurVertexNew) ? Status.CREATED : Status.OK;
767 Introspector curObj = serializer.getLatestVersionView(vertex, eventDepth);
768 String aaiUri = vertex.<String>property(AAIProperties.AAI_URI).value();
769 String uri = String.format("%s/%s%s", basePath, version, aaiUri);
770 HashMap<String, Introspector> curRelatedObjs = new HashMap<>();
771 if (!curObj.isTopLevel()) {
772 curRelatedObjs = serializer.getRelatedObjects(queryEngine, vertex, curObj, this.loader);
774 notification.createNotificationEvent(transactionId, sourceOfTruth, curObjStatus, URI.create(uri), curObj, curRelatedObjs, basePath);
780 * Verifies that vertex has needed properties to generate on
781 * @param vertex Vertex to be verified
782 * @return <code>true</code> if vertex has necessary properties and exists
784 private boolean canGenerateEvent(Vertex vertex) {
785 boolean canGenerate = true;
787 if(!vertex.property(AAIProperties.AAI_URI).isPresent()){
788 LOGGER.debug("Encountered an vertex {} with missing aai-uri", vertex.id());
790 } else if(!vertex.property(AAIProperties.CREATED_TS).isPresent() || !vertex.property(AAIProperties.LAST_MOD_TS).isPresent()){
791 LOGGER.debug("Encountered an vertex {} with missing timestamp", vertex.id());
794 } catch (IllegalStateException e) {
795 if (e.getMessage().contains(" was removed")) {
796 LOGGER.warn("Attempted to generate event for non existent vertex", e);
798 LOGGER.warn("Encountered exception generating events", e);
806 * Gets the media type.
808 * @param mediaTypeList the media type list
809 * @return the media type
811 private String getMediaType(List<MediaType> mediaTypeList) {
812 String mediaType = MediaType.APPLICATION_JSON; // json is the default
813 for (MediaType mt : mediaTypeList) {
814 if (MediaType.APPLICATION_XML_TYPE.isCompatible(mt)) {
815 mediaType = MediaType.APPLICATION_XML;
822 * Gets the object from db.
824 * @param serializer the serializer
825 * @param query the query
828 * @param depth the depth
829 * @param cleanUp the clean up
830 * @return the object from db
831 * @throws AAIException the AAI exception
832 * @throws IllegalAccessException the illegal access exception
833 * @throws IllegalArgumentException the illegal argument exception
834 * @throws InvocationTargetException the invocation target exception
835 * @throws SecurityException the security exception
836 * @throws InstantiationException the instantiation exception
837 * @throws NoSuchMethodException the no such method exception
838 * @throws UnsupportedEncodingException the unsupported encoding exception
839 * @throws MalformedURLException the malformed URL exception
840 * @throws AAIUnknownObjectException
841 * @throws URISyntaxException
843 private Introspector getObjectFromDb(List<Vertex> results, DBSerializer serializer, QueryParser query,
844 Introspector obj, URI uri, int depth, boolean nodeOnly, String cleanUp)
845 throws AAIException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
846 SecurityException, InstantiationException, NoSuchMethodException, UnsupportedEncodingException,
847 AAIUnknownObjectException, URISyntaxException {
850 if (results.isEmpty()) {
851 String msg = createNotFoundMessage(query.getResultType(), uri);
852 throw new AAIException("AAI_6114", msg);
855 return serializer.dbToObject(results, obj, depth, nodeOnly, cleanUp);
859 * Gets the object from db.
861 * @param serializer the serializer
862 * @param query the query
865 * @param depth the depth
866 * @param cleanUp the clean up
867 * @param isSkipRelatedTo include related to flag
868 * @return the object from db
869 * @throws AAIException the AAI exception
870 * @throws IllegalAccessException the illegal access exception
871 * @throws IllegalArgumentException the illegal argument exception
872 * @throws InvocationTargetException the invocation target exception
873 * @throws SecurityException the security exception
874 * @throws InstantiationException the instantiation exception
875 * @throws NoSuchMethodException the no such method exception
876 * @throws UnsupportedEncodingException the unsupported encoding exception
877 * @throws MalformedURLException the malformed URL exception
878 * @throws AAIUnknownObjectException
879 * @throws URISyntaxException
881 private Introspector getObjectFromDb(List<Vertex> results, DBSerializer serializer, QueryParser query,
882 Introspector obj, URI uri, int depth, boolean nodeOnly, String cleanUp, boolean isSkipRelatedTo)
883 throws AAIException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
884 SecurityException, InstantiationException, NoSuchMethodException, UnsupportedEncodingException,
885 AAIUnknownObjectException, URISyntaxException {
888 if (results.isEmpty()) {
889 String msg = createNotFoundMessage(query.getResultType(), uri);
890 throw new AAIException("AAI_6114", msg);
893 return serializer.dbToObject(results, obj, depth, nodeOnly, cleanUp, isSkipRelatedTo);
898 * Gets the object from db.
900 * @param serializer the serializer
901 * @param query the query
903 * @return the object from db
904 * @throws AAIException the AAI exception
905 * @throws IllegalAccessException the illegal access exception
906 * @throws IllegalArgumentException the illegal argument exception
907 * @throws InvocationTargetException the invocation target exception
908 * @throws SecurityException the security exception
909 * @throws InstantiationException the instantiation exception
910 * @throws NoSuchMethodException the no such method exception
911 * @throws UnsupportedEncodingException the unsupported encoding exception
912 * @throws MalformedURLException the malformed URL exception
913 * @throws AAIUnknownObjectException
914 * @throws URISyntaxException
916 private Introspector getRelationshipObjectFromDb(List<Vertex> results, DBSerializer serializer, QueryParser query,
917 URI uri, boolean isSkipRelatedTo) throws AAIException, IllegalArgumentException, SecurityException, UnsupportedEncodingException,
918 AAIUnknownObjectException {
921 if (results.isEmpty()) {
922 String msg = createNotFoundMessage(query.getResultType(), uri);
923 throw new AAIException("AAI_6114", msg);
926 if (results.size() > 1) {
927 throw new AAIException("AAI_6148", uri.getPath());
930 Vertex v = results.get(0);
931 return serializer.dbToRelationshipObject(v, isSkipRelatedTo);
935 * Creates the not found message.
937 * @param resultType the result type
941 private String createNotFoundMessage(String resultType, URI uri) {
942 return "No Node of type " + resultType + " found at: " + uri.getPath();
946 * Creates the not found message.
948 * @param resultType the result type
952 private String createRelationshipNotFoundMessage(String resultType, URI uri) {
953 return "No relationship found of type " + resultType + " at the given URI: " + uri.getPath()
954 + "/relationship-list";
960 * @param depthParam the depth param
962 * @throws AAIException the AAI exception
964 protected int setDepth(Introspector obj, String depthParam) throws AAIException {
965 int depth = AAIProperties.MAXIMUM_DEPTH;
967 String getAllRandomStr = AAIConfig.get("aai.rest.getall.depthparam", "");
968 if (getAllRandomStr != null && !getAllRandomStr.isEmpty() && getAllRandomStr.equals(depthParam)) {
972 if (depthParam == null) {
973 if (this.version.compareTo(schemaVersions.getDepthVersion()) >= 0) {
977 if (!depthParam.isEmpty() && !"all".equals(depthParam)) {
979 depth = Integer.parseInt(depthParam);
980 } catch (Exception e) {
981 throw new AAIException("AAI_4016");
986 String maxDepth = obj.getMetadata(ObjectMetadata.MAXIMUM_DEPTH);
988 int maximumDepth = AAIProperties.MAXIMUM_DEPTH;
990 if (maxDepth != null) {
992 maximumDepth = Integer.parseInt(maxDepth);
993 } catch (Exception ex) {
994 throw new AAIException("AAI_4018");
998 if (depth > maximumDepth) {
999 throw new AAIException("AAI_3303");
1005 private Map<Vertex, Introspector> buildIntrospectorObjects(DBSerializer serializer, Iterable<Vertex> vertices) {
1006 Map<Vertex, Introspector> deleteObjectMap = new HashMap<>();
1007 for (Vertex vertex : vertices) {
1009 Introspector deleteObj = serializer.getLatestVersionView(vertex, notificationDepth);
1010 deleteObjectMap.put(vertex, deleteObj);
1011 } catch (UnsupportedEncodingException | AAIException e) {
1012 LOGGER.warn("Unable to get Introspctor Objects, Just continue");
1017 return deleteObjectMap;
1021 private Map<String, URI> buildURIMap(DBSerializer serializer, Map<Vertex, Introspector> introSpector) {
1022 Map<String, URI> uriMap = new HashMap<>();
1023 for (Map.Entry<Vertex, Introspector> entry : introSpector.entrySet()) {
1026 uri = serializer.getURIForVertex(entry.getKey());
1027 if (null != entry.getValue()) {
1028 uriMap.put(entry.getValue().getObjectId(), uri);
1030 } catch (UnsupportedEncodingException e) {
1031 LOGGER.warn("Unable to get URIs, Just continue");
1040 private Map<String, HashMap<String, Introspector>> buildRelatedObjects(DBSerializer serializer,
1041 QueryEngine queryEngine, Map<Vertex, Introspector> introSpector) {
1043 Map<String, HashMap<String, Introspector>> relatedObjectsMap = new HashMap<>();
1044 for (Map.Entry<Vertex, Introspector> entry : introSpector.entrySet()) {
1046 HashMap<String, Introspector> relatedObjects =
1047 serializer.getRelatedObjects(queryEngine, entry.getKey(), entry.getValue(), this.loader);
1048 if (null != entry.getValue()) {
1049 relatedObjectsMap.put(entry.getValue().getObjectId(), relatedObjects);
1051 } catch (IllegalArgumentException | SecurityException
1052 | UnsupportedEncodingException | AAIException e) {
1053 LOGGER.warn("Unable to get realted Objects, Just continue");
1058 return relatedObjectsMap;
1062 private void buildNotificationEvent(String sourceOfTruth, Status status, String transactionId,
1063 UEBNotification notification, Map<Vertex, Introspector> deleteObjects, Map<String, URI> uriMap,
1064 Map<String, HashMap<String, Introspector>> deleteRelatedObjects, String basePath) {
1065 for (Map.Entry<Vertex, Introspector> entry : deleteObjects.entrySet()) {
1067 if (null != entry.getValue()) {
1068 String vertexObjectId = entry.getValue().getObjectId();
1070 if (uriMap.containsKey(vertexObjectId) && deleteRelatedObjects.containsKey(vertexObjectId)) {
1071 notification.createNotificationEvent(transactionId, sourceOfTruth, status,
1072 uriMap.get(vertexObjectId), entry.getValue(), deleteRelatedObjects.get(vertexObjectId),
1076 } catch (UnsupportedEncodingException | AAIException e) {
1078 LOGGER.warn("Error in sending notification");
1083 public void setPaginationParameters(String resultIndex, String resultSize) {
1084 if (resultIndex != null && !"-1".equals(resultIndex) && resultSize != null && !"-1".equals(resultSize)) {
1085 this.setPaginationIndex(Integer.parseInt(resultIndex));
1086 this.setPaginationBucket(Integer.parseInt(resultSize));
1090 public List<Object> getPaginatedVertexListForAggregateFormat(List<Object> aggregateVertexList) throws AAIException {
1091 List<Object> finalList = new Vector<>();
1092 if (this.isPaginated()) {
1093 if (aggregateVertexList != null && !aggregateVertexList.isEmpty()) {
1094 int listSize = aggregateVertexList.size();
1095 if (listSize == 1) {
1096 List<Object> vertexList = (List<Object>) aggregateVertexList.get(0);
1097 this.setTotalsForPaging(vertexList.size(), this.getPaginationBucket());
1098 int startIndex = (this.getPaginationIndex() - 1) * this.getPaginationBucket();
1099 int endIndex = Math.min((this.getPaginationBucket() * this.getPaginationIndex()), vertexList.size());
1100 if (startIndex > endIndex) {
1101 throw new AAIException("AAI_6150",
1102 " ResultIndex is not appropriate for the result set, Needs to be <= " + endIndex);
1104 finalList.add(new ArrayList<Object>());
1105 for (int i = startIndex; i < endIndex; i++) {
1106 ((ArrayList<Object>) finalList.get(0)).add(((ArrayList<Object>) aggregateVertexList.get(0)).get(i));
1112 // If the list size is greater than 1 or if pagination is not needed, return the original list.
1113 return aggregateVertexList;
1116 public List<Object> getPaginatedVertexList(List<Object> vertexList) throws AAIException {
1117 List<Object> vertices;
1118 if (this.isPaginated()) {
1119 this.setTotalsForPaging(vertexList.size(), this.getPaginationBucket());
1120 int startIndex = (this.getPaginationIndex() - 1) * this.getPaginationBucket();
1121 int endIndex = Math.min((this.getPaginationBucket() * this.getPaginationIndex()), vertexList.size());
1122 if (startIndex > endIndex) {
1123 throw new AAIException("AAI_6150",
1124 " ResultIndex is not appropriate for the result set, Needs to be <= " + endIndex);
1126 vertices = vertexList.subList(startIndex, endIndex);
1128 vertices = vertexList;