2 * ============LICENSE_START==========================================
4 * ===================================================================
5 * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6 * Copyright © 2017-2018 Amdocs
7 * ===================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 * ============LICENSE_END============================================
21 package org.onap.champ.service;
23 import org.onap.aai.champcore.ChampGraph;
24 import org.onap.aai.champcore.ChampTransaction;
25 import org.onap.aai.champcore.exceptions.ChampMarshallingException;
26 import org.onap.aai.champcore.exceptions.ChampObjectNotExistsException;
27 import org.onap.aai.champcore.exceptions.ChampRelationshipNotExistsException;
28 import org.onap.aai.champcore.exceptions.ChampSchemaViolationException;
29 import org.onap.aai.champcore.exceptions.ChampTransactionException;
30 import org.onap.aai.champcore.exceptions.ChampUnmarshallingException;
31 import org.onap.aai.champcore.model.ChampElement;
32 import org.onap.aai.champcore.model.ChampField;
33 import org.onap.aai.champcore.model.ChampObject;
34 import org.onap.aai.champcore.model.ChampObjectIndex;
35 import org.onap.aai.champcore.model.ChampRelationship;
36 import org.onap.aai.champcore.model.fluent.object.ObjectBuildOrPropertiesStep;
37 import org.onap.aai.cl.api.Logger;
38 import org.onap.aai.cl.eelf.LoggerFactory;
39 import org.onap.champ.entity.ChampBulkEdgeResponse;
40 import org.onap.champ.entity.ChampBulkOp;
41 import org.onap.champ.entity.ChampBulkPayload;
42 import org.onap.champ.entity.ChampBulkResponse;
43 import org.onap.champ.entity.ChampBulkVertexResponse;
44 import org.onap.champ.exception.ChampServiceException;
45 import org.onap.champ.service.logging.ChampMsgs;
46 import org.onap.champ.util.ChampProperties;
47 import org.onap.champ.util.ChampServiceConstants;
49 import java.io.PrintWriter;
50 import java.io.StringWriter;
51 import java.util.ArrayList;
52 import java.util.HashMap;
53 import java.util.HashSet;
54 import java.util.List;
56 import java.util.Optional;
57 import java.util.stream.Collectors;
58 import java.util.stream.Stream;
59 import javax.ws.rs.core.Response.Status;
61 public class ChampDataService {
62 private ChampUUIDService champUUIDService;
64 private ChampGraph graphImpl;
65 private ChampTransactionCache cache;
66 private boolean graphInitialized = false;
67 private static final String KEY_NAME = ChampProperties.get(ChampServiceConstants.CHAMP_KEY_NAME);
68 private static final String SOT_NAME = ChampProperties.get(ChampServiceConstants.CHAMP_SOT_NAME);
69 private static final String CREATED_TS_NAME = ChampProperties.get(ChampServiceConstants.CHAMP_CREATED_TS_NAME);
70 private static final String LAST_MOD_TS_NAME = ChampProperties.get(ChampServiceConstants.CHAMP_LAST_MOD_TS_NAME);
71 private Logger logger = LoggerFactory.getInstance().getLogger(ChampDataService.class);
74 public ChampDataService(ChampUUIDService champUUIDService, ChampGraph graphImpl, ChampTransactionCache cache) {
76 this.champUUIDService = champUUIDService;
77 this.graphImpl = graphImpl;
83 catch (Exception ex) {
84 // Swallow exception to prevent application from crashing. Connection will be retried when
85 // champ processes a new request.
86 StringWriter writer = new StringWriter();
87 PrintWriter printWriter = new PrintWriter(writer);
88 ex.printStackTrace(printWriter);
89 logger.error(ChampMsgs.CHAMP_DATA_SERVICE_ERROR, "Unable to initialize graph: " + ex.getLocalizedMessage());
90 logger.error(ChampMsgs.CHAMP_DATA_SERVICE_ERROR, writer.toString());
94 public ChampObject getObject(String id, Optional<ChampTransaction> transaction) throws ChampServiceException {
95 if (!graphInitialized) {
99 Optional<ChampObject> retrieved = Optional.empty();
101 retrieved = champUUIDService.getObjectbyUUID(id, transaction.orElse(null));
102 } catch (ChampUnmarshallingException | ChampTransactionException e) {
103 throw new ChampServiceException("Error: " + e.getMessage(), Status.INTERNAL_SERVER_ERROR);
105 if (retrieved.isPresent()) {
106 return (ChampObject) champUUIDService.populateUUIDKey(retrieved.get());
112 public ChampObject storeObject(ChampObject object, Optional<ChampTransaction> transaction)
113 throws ChampMarshallingException, ChampSchemaViolationException, ChampObjectNotExistsException,
114 ChampTransactionException, ChampServiceException {
115 if (!graphInitialized) {
119 if (object.getProperty(KEY_NAME).isPresent() || object.getKey().isPresent()) {
120 throw new ChampServiceException(KEY_NAME + " can't be updated", Status.BAD_REQUEST);
123 champUUIDService.populateUUIDProperty(object, java.util.UUID.randomUUID().toString());
124 addTimestamps(object, null);
125 ChampObject created = graphImpl.storeObject(object, transaction);
126 return (ChampObject) champUUIDService.populateUUIDKey(created);
129 public ChampObject replaceObject(ChampObject object, String objectId, Optional<ChampTransaction> transaction)
130 throws ChampServiceException, ChampUnmarshallingException, ChampTransactionException, ChampMarshallingException,
131 ChampSchemaViolationException, ChampObjectNotExistsException {
132 if (!graphInitialized) {
136 if (object.getKey().isPresent() && (!object.getKeyValue().equals(objectId))) {
137 throw new ChampServiceException("Object Id in the URI doesn't match the body.", Status.BAD_REQUEST);
140 if (object.getProperty(KEY_NAME).isPresent() && !object.getProperty(KEY_NAME).get().toString().equals(objectId)) {
141 throw new ChampServiceException(KEY_NAME + " can't be updated", Status.BAD_REQUEST);
144 Optional<ChampObject> retrieved = champUUIDService.getObjectbyUUID(objectId, transaction.orElse(null));
145 if (!retrieved.isPresent()) {
146 throw new ChampServiceException(objectId + " not found", Status.NOT_FOUND);
148 ObjectBuildOrPropertiesStep payloadBuilder = ChampObject.create().from(object).withKey(retrieved.get().getKey().get())
149 .withProperty(KEY_NAME, objectId);
150 if (retrieved.get().getProperty(SOT_NAME).isPresent()){
151 payloadBuilder = payloadBuilder.withProperty(SOT_NAME, retrieved.get().getProperty(SOT_NAME).get());
154 if (object.getProperty(CREATED_TS_NAME).isPresent() && retrieved.get().getProperty(CREATED_TS_NAME).isPresent()) {
155 // the timestamps in object are parsed as strings regardless of how the input json is. Convert retrieved to string for easy comparison
156 if (!retrieved.get().getProperty(CREATED_TS_NAME).get().toString().equals(object.getProperty(CREATED_TS_NAME).get())) {
157 throw new ChampServiceException(CREATED_TS_NAME + " can't be updated", Status.BAD_REQUEST);
161 if (object.getProperty(LAST_MOD_TS_NAME).isPresent() && retrieved.get().getProperty(LAST_MOD_TS_NAME).isPresent()) {
162 if (!retrieved.get().getProperty(LAST_MOD_TS_NAME).get().toString().equals(object.getProperty(LAST_MOD_TS_NAME).get())) {
163 throw new ChampServiceException(LAST_MOD_TS_NAME + " can't be updated", Status.BAD_REQUEST);
167 ChampObject payload = payloadBuilder.build();
168 addTimestamps(payload, (Long)retrieved.get().getProperty(CREATED_TS_NAME).orElse(null));
169 ChampObject updated = graphImpl.replaceObject(payload, transaction);
170 return (ChampObject) champUUIDService.populateUUIDKey(updated);
173 public void deleteObject(String objectId, Optional<ChampTransaction> transaction) throws ChampServiceException,
174 ChampObjectNotExistsException, ChampTransactionException, ChampUnmarshallingException {
175 if (!graphInitialized) {
179 Optional<ChampObject> retrieved = champUUIDService.getObjectbyUUID(objectId, transaction.orElse(null));
180 if (!retrieved.isPresent()) {
181 throw new ChampServiceException(objectId + " not found", Status.NOT_FOUND);
183 Stream<ChampRelationship> relationships = graphImpl.retrieveRelationships(retrieved.get(), transaction);
185 if (relationships.count() > 0) {
186 throw new ChampServiceException("Attempt to delete vertex with id " + objectId + " which has incident edges.",
189 graphImpl.deleteObject(retrieved.get().getKey().get(), transaction);
193 public ChampRelationship storeRelationship(ChampRelationship r, Optional<ChampTransaction> transaction)
194 throws ChampMarshallingException, ChampObjectNotExistsException, ChampSchemaViolationException,
195 ChampRelationshipNotExistsException, ChampUnmarshallingException, ChampTransactionException,
196 ChampServiceException {
197 if (!graphInitialized) {
201 if (r.getSource() == null || !r.getSource().getKey().isPresent() || r.getTarget() == null
202 || !r.getTarget().getKey().isPresent()) {
203 logger.error(ChampMsgs.CHAMP_DATA_SERVICE_ERROR, "Source/Target Object key must be provided");
204 throw new ChampServiceException("Source/Target Object key must be provided", Status.BAD_REQUEST);
207 if (r.getProperty(KEY_NAME).isPresent() || r.getKey().isPresent()) {
208 logger.error(ChampMsgs.CHAMP_DATA_SERVICE_ERROR, "key or " + KEY_NAME + " not allowed while creating new Objects");
209 throw new ChampServiceException("key or " + KEY_NAME + " not allowed while creating new Objects", Status.BAD_REQUEST);
213 Optional<ChampObject> source = champUUIDService.getObjectbyUUID(r.getSource().getKey().get().toString(),
214 transaction.orElse(null));
215 Optional<ChampObject> target = champUUIDService.getObjectbyUUID(r.getTarget().getKey().get().toString(),
216 transaction.orElse(null));
218 if (!source.isPresent() || !target.isPresent()) {
219 logger.error(ChampMsgs.CHAMP_DATA_SERVICE_ERROR, "Source/Target object not found");
220 throw new ChampServiceException("Source/Target object not found", Status.BAD_REQUEST);
223 champUUIDService.populateUUIDProperty(r, java.util.UUID.randomUUID().toString());
225 ChampRelationship payload = new ChampRelationship.Builder(source.get(), target.get(), r.getType())
226 .properties(r.getProperties()).build();
227 addTimestamps(payload, null);
228 ChampRelationship created = graphImpl.storeRelationship(payload, transaction);
229 return (ChampRelationship) champUUIDService.populateUUIDKey(created);
232 public ChampRelationship updateRelationship(ChampRelationship r, String rId, Optional<ChampTransaction> transaction)
233 throws ChampServiceException, ChampUnmarshallingException, ChampTransactionException, ChampMarshallingException,
234 ChampSchemaViolationException, ChampRelationshipNotExistsException {
235 if (!graphInitialized) {
239 if (r.getKey().isPresent() && (!r.getKeyValue().equals(rId))) {
241 throw new ChampServiceException("Relationship Id in the URI \"" + rId + "\" doesn't match the URI in the body"
242 + " \"" + r.getKeyValue() + "\"", Status.BAD_REQUEST);
246 if (r.getProperty(KEY_NAME).isPresent() && !r.getProperty(KEY_NAME).get().toString().equals(rId)) {
247 throw new ChampServiceException(KEY_NAME + " can't be updated", Status.BAD_REQUEST);
250 Optional<ChampRelationship> retrieved = champUUIDService.getRelationshipbyUUID(rId, transaction.orElse(null));
251 if (!retrieved.isPresent()) {
252 throw new ChampServiceException(rId + " not found", Status.NOT_FOUND);
254 // check if key is present or if it equals the key that is in the URI
255 if (r.getSource() == null || !r.getSource().getKey().isPresent() || r.getTarget() == null
256 || !r.getTarget().getKey().isPresent()) {
257 throw new ChampServiceException("Source/Target Object key must be provided", Status.BAD_REQUEST);
259 ChampObject source = retrieved.get().getSource();
260 ChampObject target = retrieved.get().getTarget();
262 if (!source.getProperty(KEY_NAME).get().toString().equals(r.getSource().getKey().get().toString())
263 || !target.getProperty(KEY_NAME).get().toString().equals(r.getTarget().getKey().get().toString())) {
264 throw new ChampServiceException("Source/Target cannot be updated", Status.BAD_REQUEST);
267 if (r.getProperty(CREATED_TS_NAME).isPresent() && retrieved.get().getProperty(CREATED_TS_NAME).isPresent()) {
268 if (!retrieved.get().getProperty(CREATED_TS_NAME).get().toString().equals(r.getProperty(CREATED_TS_NAME).get())) {
269 throw new ChampServiceException(CREATED_TS_NAME + " can't be updated", Status.BAD_REQUEST);
273 if (r.getProperty(LAST_MOD_TS_NAME).isPresent() && retrieved.get().getProperty(LAST_MOD_TS_NAME).isPresent()) {
274 if (!retrieved.get().getProperty(LAST_MOD_TS_NAME).get().toString().equals(r.getProperty(LAST_MOD_TS_NAME).get())) {
275 throw new ChampServiceException(LAST_MOD_TS_NAME + " can't be updated", Status.BAD_REQUEST);
279 ChampRelationship payload = new ChampRelationship.Builder(source, target, r.getType())
280 .key(retrieved.get().getKey().get()).properties(r.getProperties()).property(KEY_NAME, rId).build();
281 addTimestamps(payload, (Long)retrieved.get().getProperty(CREATED_TS_NAME).orElse(null));
282 ChampRelationship updated = graphImpl.replaceRelationship(payload, transaction);
283 return (ChampRelationship) champUUIDService.populateUUIDKey(updated);
286 public void deleteRelationship(String relationshipId, Optional<ChampTransaction> transaction)
287 throws ChampServiceException, ChampRelationshipNotExistsException, ChampTransactionException,
288 ChampUnmarshallingException {
289 if (!graphInitialized) {
293 Optional<ChampRelationship> retrieved = champUUIDService.getRelationshipbyUUID(relationshipId,
294 transaction.orElse(null));
295 if (!retrieved.isPresent()) {
296 throw new ChampServiceException(relationshipId + " not found", Status.NOT_FOUND);
299 graphImpl.deleteRelationship(retrieved.get(), transaction);
304 public List<ChampRelationship> getRelationshipsByObject(String objectId, Optional<ChampTransaction> transaction)
305 throws ChampServiceException {
306 if (!graphInitialized) {
311 Optional<ChampObject> retrievedObject = champUUIDService.getObjectbyUUID(objectId, transaction.orElse(null));
312 if (!retrievedObject.isPresent()) {
313 throw new ChampServiceException(objectId + " not found", Status.NOT_FOUND);
315 List<ChampRelationship> relations = new ArrayList<ChampRelationship>();
317 Stream<ChampRelationship> retrieved = graphImpl.retrieveRelationships(retrievedObject.get(), transaction);
318 relations = champUUIDService.populateUUIDKey(retrieved.collect(Collectors.toList()));
320 } catch (ChampObjectNotExistsException e) {
321 throw new ChampServiceException(" obj not found", Status.NOT_FOUND);
322 } catch (ChampUnmarshallingException | ChampTransactionException e) {
323 throw new ChampServiceException("Internal Error", Status.INTERNAL_SERVER_ERROR);
329 * Gets the ChampObjects that pass filter
330 * @param filter key/value pairs that must be present in the returned objects
331 * @param properties properties that will show up in the object
333 * @throws ChampServiceException
335 public List<ChampObject> queryObjects(Map<String, Object> filter, HashSet<String> properties) throws ChampServiceException {
336 if (!graphInitialized) {
341 Stream<ChampObject> retrieved = graphImpl.queryObjects(filter);
342 List<ChampObject> objects = champUUIDService.populateUUIDKey(retrieved.collect(Collectors.toList()));
344 if (!properties.contains("all")) {
345 for (ChampObject champObject : objects) {
346 champObject.dropProperties(properties);
351 } catch (ChampTransactionException e) {
352 throw new ChampServiceException("Internal Error", Status.INTERNAL_SERVER_ERROR);
356 public List<ChampRelationship> queryRelationships(Map<String, Object> filter) throws ChampServiceException {
357 if (!graphInitialized) {
362 List<ChampRelationship> relations = new ArrayList<ChampRelationship>();
363 Stream<ChampRelationship> retrieved;
365 retrieved = graphImpl.queryRelationships(filter);
367 relations = champUUIDService.populateUUIDKey(retrieved.collect(Collectors.toList()));
369 } catch (ChampTransactionException e) {
370 throw new ChampServiceException("Internal Error", Status.INTERNAL_SERVER_ERROR);
374 public ChampRelationship getRelationship(String id, Optional<ChampTransaction> transaction)
375 throws ChampServiceException {
376 if (!graphInitialized) {
380 Optional<ChampRelationship> retrieved = Optional.empty();
382 retrieved = champUUIDService.getRelationshipbyUUID(id, transaction.orElse(null));
383 } catch (ChampUnmarshallingException | ChampTransactionException e) {
384 throw new ChampServiceException("Error: " + e.getMessage(), Status.INTERNAL_SERVER_ERROR);
386 if (retrieved.isPresent()) {
387 return (ChampRelationship) champUUIDService.populateUUIDKey(retrieved.get());
393 public String openTransaction() {
394 if (!graphInitialized) {
398 ChampTransaction transaction = graphImpl.openTransaction();
399 String transacId = transaction.id();
400 cache.put(transacId, transaction);
405 public void commitTransaction(String tId) throws ChampServiceException, ChampTransactionException {
406 if (!graphInitialized) {
410 ChampTransaction transaction = cache.get(tId);
411 if (transaction == null) {
412 throw new ChampServiceException("Transaction Not found: " + tId, Status.NOT_FOUND);
414 graphImpl.commitTransaction(transaction);
415 cache.invalidate(tId);
416 cache.invalidate(transaction.id());
420 public void rollbackTransaction(String tId) throws ChampServiceException, ChampTransactionException {
421 if (!graphInitialized) {
425 ChampTransaction transaction = cache.get(tId);
426 if (transaction == null) {
427 throw new ChampServiceException("Transaction Not found: " + tId, Status.NOT_FOUND);
429 graphImpl.rollbackTransaction(transaction);
430 cache.invalidate(tId);
431 cache.invalidate(transaction.id());
435 public ChampTransaction getTransaction(String id) {
436 return cache.get(id);
439 public ChampBulkResponse processBulkRequest(ChampBulkPayload bulkPayload) throws ChampServiceException, ChampRelationshipNotExistsException, ChampTransactionException, ChampUnmarshallingException, ChampObjectNotExistsException, ChampMarshallingException, ChampSchemaViolationException {
440 if (!graphInitialized) {
444 // Open a transaction. If any operations fail, we want to rollback
445 ChampTransaction transaction = graphImpl.openTransaction();
446 if (transaction == null) {
447 throw new ChampServiceException("Unable to open transaction", Status.INTERNAL_SERVER_ERROR);
450 ChampBulkResponse responsePayload = new ChampBulkResponse();
451 Map<String,ChampObject> addedObjects = new HashMap<String,ChampObject>();
452 List<ChampBulkVertexResponse> addedObjectsResp = new ArrayList<ChampBulkVertexResponse>();
453 List<ChampBulkEdgeResponse> addedEdgesResp = new ArrayList<ChampBulkEdgeResponse>();
456 // 1. Process edge deletes
457 for (ChampBulkOp op : bulkPayload.getEdgeDeleteOps()) {
458 deleteRelationship(op.getId(), Optional.ofNullable(transaction));
461 // 2. Process vertex deletes
462 for (ChampBulkOp op : bulkPayload.getVertexDeleteOps()) {
463 deleteObject(op.getId(), Optional.ofNullable(transaction));
466 // 3. Add/modify vertexes
467 for (ChampBulkOp op : bulkPayload.getVertexAddModifyOps()) {
468 if (op.getOperation().equals(ChampBulkPayload.ADD_OP)) {
469 ChampObject addedObj = storeObject(op.toChampObject(), Optional.ofNullable(transaction));
470 addedObjects.put(op.getLabel(), addedObj);
471 addedObjectsResp.add(new ChampBulkVertexResponse(op.getLabel(), addedObj));
474 ChampObject addedObj = replaceObject(op.toChampObject(), op.getId(), Optional.ofNullable(transaction));
475 addedObjects.put(op.getLabel(), addedObj);
476 addedObjectsResp.add(new ChampBulkVertexResponse(op.getLabel(), addedObj));
480 // 4. Add/modify edges
481 for (ChampBulkOp op : bulkPayload.getEdgeAddModifyOps()) {
482 // If the edge references a newly added vertex, we need to replace the reference with the real ID
483 op.setSource(resolveVertex(op.getSource(), addedObjects));
484 op.setTarget(resolveVertex(op.getTarget(), addedObjects));
486 if (op.getOperation().equals(ChampBulkPayload.ADD_OP)) {
487 ChampRelationship addedRel = storeRelationship(op.toChampRelationship(), Optional.ofNullable(transaction));
488 addedEdgesResp.add(new ChampBulkEdgeResponse(op.getLabel(), addedRel));
491 ChampRelationship addedRel = updateRelationship(op.toChampRelationship(), op.getId(), Optional.ofNullable(transaction));
492 addedEdgesResp.add(new ChampBulkEdgeResponse(op.getLabel(), addedRel));
496 catch (Exception ex) {
497 // Rollback the transaction
498 graphImpl.rollbackTransaction(transaction);
502 // Commit transaction
503 graphImpl.commitTransaction(transaction);
505 responsePayload.setObjects(addedObjectsResp);
506 responsePayload.setRelationships(addedEdgesResp);
507 return responsePayload;
511 private String resolveVertex(String vertexId, Map<String, ChampObject> addedObjects) throws ChampServiceException {
512 if (vertexId.startsWith("$")) {
513 String key = vertexId.substring(1);
514 if (addedObjects.get(key) != null) {
515 return addedObjects.get(key).getKey().get().toString();
518 throw new ChampServiceException("Unable to resolve vertex " + key, Status.BAD_REQUEST);
524 private void addTimestamps(ChampElement e, Long oldCreated) {
525 Long timestamp = System.currentTimeMillis();
527 if (oldCreated == null) {
528 e.getProperties().put(CREATED_TS_NAME, timestamp);
530 e.getProperties().put(CREATED_TS_NAME, oldCreated);
533 e.getProperties().put(LAST_MOD_TS_NAME, timestamp);
536 private synchronized void initializeGraph() {
537 if (graphInitialized) {
541 graphImpl.createDefaultIndexes();
543 graphInitialized = true;