diff options
Diffstat (limited to 'aai-core/src/main')
10 files changed, 2481 insertions, 2094 deletions
diff --git a/aai-core/src/main/java/org/onap/aai/config/IntrospectionConfig.java b/aai-core/src/main/java/org/onap/aai/config/IntrospectionConfig.java index aa4ec1a1..e737f08d 100644 --- a/aai-core/src/main/java/org/onap/aai/config/IntrospectionConfig.java +++ b/aai-core/src/main/java/org/onap/aai/config/IntrospectionConfig.java @@ -21,14 +21,16 @@ */ package org.onap.aai.config; +import org.onap.aai.schema.enums.ObjectMetadata; import org.onap.aai.setup.SchemaVersion; import org.onap.aai.setup.SchemaVersions; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import java.util.Map; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; import org.onap.aai.introspection.LoaderFactory; import org.onap.aai.introspection.MoxyLoader; diff --git a/aai-core/src/main/java/org/onap/aai/introspection/Introspector.java b/aai-core/src/main/java/org/onap/aai/introspection/Introspector.java index c7520dd4..269d6330 100644 --- a/aai-core/src/main/java/org/onap/aai/introspection/Introspector.java +++ b/aai-core/src/main/java/org/onap/aai/introspection/Introspector.java @@ -36,6 +36,7 @@ import org.onap.aai.workarounds.NamingExceptions; import java.io.UnsupportedEncodingException; import java.lang.reflect.InvocationTargetException; import java.util.*; +import java.util.stream.Collectors; public abstract class Introspector implements Cloneable { @@ -57,25 +58,25 @@ public abstract class Introspector implements Cloneable { protected String convertPropertyName (String name) { return CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, name); } - + protected abstract Object get(String name); protected abstract void set(String name, Object value); /** - * + * * @param name the property name you'd like to retrieve the value for * @return the value of the property */ public <T> T getValue(String name) { String convertedName = convertPropertyName(name); Object result = null; - + if (this.hasProperty(name)) { result = this.get(convertedName); } else { /* property not found - slightly ambiguous */ return null; } - + Class<?> clazz = this.getClass(name); if (this.isListType(name) && result == null) { try { @@ -88,18 +89,18 @@ public abstract class Introspector implements Cloneable { return (T)result; } - + public Introspector getWrappedValue(String name) { String convertedName = convertPropertyName(name); Object value = null; - + if (this.hasProperty(name)) { value = this.get(convertedName); } else { /* property not found - slightly ambiguous */ return null; } - + Class<?> clazz = this.getClass(name); if (this.isListType(name) && value == null) { try { @@ -115,9 +116,9 @@ public abstract class Introspector implements Cloneable { //no value return null; } - + } - + public List<Introspector> getWrappedListValue(String name) { String convertedName = convertPropertyName(name); Object value = null; @@ -141,17 +142,17 @@ public abstract class Introspector implements Cloneable { LOGGER.warn(e.getMessage(),e); } } - + List<Object> valueList = (List<Object>)value; - + for (Object item : valueList) { resultList.add(IntrospectorFactory.newInstance(this.getModelType(), item)); } - + return resultList; - + } - + public Object castValueAccordingToSchema(String name, Object obj) { Object result = obj; Class<?> nameClass = this.getClass(name); @@ -159,11 +160,11 @@ public abstract class Introspector implements Cloneable { throw new IllegalArgumentException("property: " + name + " does not exist on " + this.getDbName()); } if (obj != null) { - + try { if (!obj.getClass().getName().equals(nameClass.getName())) { if (nameClass.isPrimitive()) { - nameClass = ClassUtils.primitiveToWrapper(nameClass); + nameClass = ClassUtils.primitiveToWrapper(nameClass); result = nameClass.getConstructor(String.class).newInstance(obj.toString()); } if (obj instanceof String) { @@ -180,35 +181,35 @@ public abstract class Introspector implements Cloneable { } return result; } - + public List<Object> castValueAccordingToSchema(String name, List<?> objs) { List<Object> result = new ArrayList<>(); - + for (Object item : objs) { result.add(this.castValueAccordingToSchema(name, item)); } - + return result; - + } /** - * + * * @param name the property name you'd like to set the value of * @param obj the value to be set * @return */ public void setValue(String name, Object obj) throws IllegalArgumentException { Object box = this.castValueAccordingToSchema(name, obj); - + name = convertPropertyName(name); this.set(name, box); } /** - * + * * @return a list of all the properties available on the object */ public abstract Set<String> getProperties(); - + public Set<String> getProperties(PropertyPredicate<Introspector, String> p) { final Set<String> temp = new LinkedHashSet<>(); this.getProperties().stream().filter(item -> { @@ -217,22 +218,30 @@ public abstract class Introspector implements Cloneable { temp.add(item); }); final Set<String> result = Collections.unmodifiableSet(temp); - + return result; - + } + + public Set<String> getSimpleProperties(PropertyPredicate<Introspector, String> p){ + return this.getProperties() + .stream() + .filter(item -> p.test(this, item)) + .filter(this::isSimpleType) + .collect(Collectors.toSet()); + } /** - * + * * @return a list of the required properties on the object */ public abstract Set<String> getRequiredProperties(); /** - * + * * @return a list of the properties that can be used to query the object in the db */ public abstract Set<String> getKeys(); /** - * + * * @return a list of the all key properties for this object */ public Set<String> getAllKeys() { @@ -274,7 +283,7 @@ public abstract class Introspector implements Cloneable { result = this.indexedProperties; return result; } - + public Set<String> getUniqueProperties() { Set<String> result = null; if (this.uniqueProperties == null) { @@ -292,7 +301,7 @@ public abstract class Introspector implements Cloneable { result = this.uniqueProperties; return result; } - + public Set<String> getDependentOn() { String dependentOn = this.getMetadata(ObjectMetadata.DEPENDENT_ON); if (dependentOn == null) { @@ -301,21 +310,21 @@ public abstract class Introspector implements Cloneable { return new LinkedHashSet<>(Arrays.asList(dependentOn.split(","))); } /** - * + * * @param name * @return the string name of the java class of the named property */ public String getType(String name) { Class<?> resultClass = this.getClass(name); String result = ""; - + if (resultClass != null) { result = resultClass.getName(); if (result.equals("java.util.ArrayList")) { result = "java.util.List"; } } - + return result; } /** @@ -327,33 +336,33 @@ public abstract class Introspector implements Cloneable { public String getGenericType(String name) { Class<?> resultClass = this.getGenericTypeClass(name); String result = ""; - + if (resultClass != null) { result = resultClass.getName(); } - + return result; } /** - * + * * @return the string name of the java class of the underlying object */ public abstract String getJavaClassName(); - + /** - * + * * @param name the property name * @return the Class object */ public abstract Class<?> getClass(String name); - + public abstract Class<?> getGenericTypeClass(String name); /** - * + * * @param name the property name * @return a new instance of the underlying type of this property - * @throws AAIUnknownObjectException + * @throws AAIUnknownObjectException */ public Object newInstanceOfProperty(String name) throws AAIUnknownObjectException { String type = this.getType(name); @@ -364,22 +373,22 @@ public abstract class Introspector implements Cloneable { String type = this.getGenericType(name); return loader.objectFromName(type); } - - + + public Introspector newIntrospectorInstanceOfProperty(String name) throws AAIUnknownObjectException { - + Introspector result = IntrospectorFactory.newInstance(this.getModelType(), this.newInstanceOfProperty(name)); - + return result; - + } - + public Introspector newIntrospectorInstanceOfNestedProperty(String name) throws AAIUnknownObjectException { - + Introspector result = IntrospectorFactory.newInstance(this.getModelType(), this.newInstanceOfNestedProperty(name)); - + return result; - + } /** * Is this type not a Java String or primitive @@ -388,56 +397,56 @@ public abstract class Introspector implements Cloneable { */ public boolean isComplexType(String name) { String result = this.getType(name); - + if (result.contains("aai") || result.equals("java.lang.Object")) { return true; } else { return false; } } - + public boolean isComplexGenericType(String name) { String result = this.getGenericType(name); - + if (result.contains("aai")) { return true; } else { return false; } } - + public boolean isSimpleType(String name) { return !(this.isComplexType(name) || this.isListType(name)); } - + public boolean isSimpleGenericType(String name) { return !this.isComplexGenericType(name); } public boolean isListType(String name) { String result = this.getType(name); - + if (result.contains("java.util.List")) { return true; } else { return false; } } - + public boolean isContainer() { Set<String> props = this.getProperties(); boolean result = false; if (props.size() == 1 && this.isListType(props.iterator().next())) { result = true; } - + return result; } - + public abstract String getChildName(); public String getChildDBName() { String result = this.getChildName(); - + result = namingException.getDBName(result); return result; } @@ -447,10 +456,10 @@ public abstract class Introspector implements Cloneable { String lowerHyphen = this.getName(); lowerHyphen = namingException.getDBName(lowerHyphen); - + return lowerHyphen; } - + public abstract ModelType getModelType(); public boolean hasChild(Introspector child) { @@ -474,7 +483,7 @@ public abstract class Introspector implements Cloneable { } return result; } - + public void setURIChain(String uri) { this.uriChain = uri; } @@ -488,21 +497,21 @@ public abstract class Introspector implements Cloneable { if (this.isContainer()) { result += "/" + this.getName(); } else { - + if (container != null) { result += "/" + container; } result += "/" + this.getDbName() + "/" + this.findKey(); - + if (namespace != null && !namespace.equals("")) { result = "/" + namespace + result; } } - + return result; } - + public String getGenericURI() { String result = ""; if (this.isContainer()) { @@ -513,10 +522,10 @@ public abstract class Introspector implements Cloneable { result += "/{" + this.getDbName() + "-" + key + "}"; } } - + return result; } - + public String getFullGenericURI() { String result = ""; String namespace = this.getMetadata(ObjectMetadata.NAMESPACE); @@ -537,33 +546,33 @@ public abstract class Introspector implements Cloneable { if (namespace != null && !namespace.equals("")) { result = "/" + namespace + result; } - + } - + return result; } public abstract String preProcessKey(String key); - + protected abstract String findKey() throws UnsupportedEncodingException; - + public abstract String marshal(MarshallerProperties properties); - + public abstract Object clone(); public abstract Object getUnderlyingObject(); - + public String marshal(boolean formatted) { MarshallerProperties properties = new MarshallerProperties.Builder(MediaType.APPLICATION_JSON_TYPE).formatted(formatted).build(); - + return marshal(properties); } public String makeSingular(String word) { - + String result = word; result = result.replaceAll("(?:([ho])es|s)$", ""); - + if (result.equals("ClassesOfService")) { result = "ClassOfService"; } else if (result.equals("CvlanTag")) { @@ -573,10 +582,10 @@ public abstract class Introspector implements Cloneable { } return result; } - + protected String makePlural(String word) { String result = word; - + if (result.equals("cvlan-tag-entry")) { return "cvlan-tags"; } else if (result.equals("class-of-service")) { @@ -590,7 +599,7 @@ public abstract class Introspector implements Cloneable { if (result.equals("classes-of-services")) { result = "classes-of-service"; }*/ - + return result; } @@ -599,21 +608,22 @@ public abstract class Introspector implements Cloneable { public Optional<String> getPropertyMetadata(String propName, PropertyMetadata metadataName) { final String resultValue = this.getPropertyMetadata(propName).getOrDefault(metadataName, ""); Optional<String> result = Optional.empty(); - + if (!resultValue.isEmpty()) { result = Optional.of(resultValue); } return result; - + } public abstract SchemaVersion getVersion(); public Loader getLoader() { return this.loader; } - + public boolean isTopLevel() { - + return this.getMetadata(ObjectMetadata.NAMESPACE) != null; } + } diff --git a/aai-core/src/main/java/org/onap/aai/introspection/JSONStrategy.java b/aai-core/src/main/java/org/onap/aai/introspection/JSONStrategy.java index b1447d8f..d54a9833 100644 --- a/aai-core/src/main/java/org/onap/aai/introspection/JSONStrategy.java +++ b/aai-core/src/main/java/org/onap/aai/introspection/JSONStrategy.java @@ -46,47 +46,47 @@ public class JSONStrategy extends Introspector { throw new IllegalArgumentException("This object has no named type."); } } - + protected JSONStrategy(Object o, String namedType) { super(o); json = (JSONObject)o; this.namedType = namedType; - + } - + @Override public boolean hasProperty(String name) { - //TODO + //TODO return true; } @Override public Object getValue(String name) { Object result = ""; result = json.get(name); - + return result; } @Override public void setValue(String name, Object obj) { json.put(name, obj); - + } @Override public Object getUnderlyingObject() { return this.json; } - + @Override public Set<String> getProperties() { Set<String> result = json.keySet(); return result; } - + @Override public Set<String> getRequiredProperties() { //unknowable - + return this.getProperties(); } @@ -109,11 +109,11 @@ public class JSONStrategy extends Introspector { if (resultClass != null) { result = resultClass.getName(); } - + if (result.equals("org.json.simple.JSONArray")) { result = "java.util.List"; } - + return result; } @@ -136,7 +136,7 @@ public class JSONStrategy extends Introspector { public Class<?> getClass(String name) { Class<?> result = null; result = json.get(name).getClass(); - + return result; } @@ -148,7 +148,7 @@ public class JSONStrategy extends Introspector { if (resultObject.getClass().getName().equals("org.json.simple.JSONArray")) { resultClass = ((List)resultObject).get(0).getClass(); } - + return resultClass; } @@ -169,43 +169,43 @@ public class JSONStrategy extends Introspector { return null; } } - + @Override public boolean isComplexType(String name) { String result = this.getType(name); - + if (result.contains("JSONObject")) { return true; } else { return false; } - + } - + @Override public boolean isComplexGenericType(String name) { String result = this.getGenericType(name); - + if (result.contains("JSONObject")) { return true; } else { return false; } - + } - + @Override public boolean isListType(String name) { String result = this.getType(name); - + if (result.contains("java.util.List")) { return true; } else { return false; } - + } - + @Override public boolean isContainer() { Set<String> props = this.getProperties(); @@ -213,66 +213,66 @@ public class JSONStrategy extends Introspector { if (props.size() == 1 && this.isListType(props.iterator().next())) { result = true; } - + return result; } @Override protected String findKey() { return ""; } - + @Override public String getName() { return this.namedType; } - + @Override public String getDbName() { return this.getName(); } - + @Override public String getURI() { - - // use a UUID for now + + // use a UUID for now return UUID.randomUUID().toString(); } - + @Override public String getGenericURI() { - + //there is none defined for this return ""; } - + @Override public String preProcessKey (String key) { - + // don't do anything with it return key; - + } - + @Override public String marshal(MarshallerProperties properties) { //TODO return null; } - + @Override public Object clone() { //TODO return null; } - + /*@Override public String findEdgeName(String parent, String child) { - + // Always has for now return "has"; - + }*/ - + @Override public ModelType getModelType() { return ModelType.JSON; @@ -341,7 +341,7 @@ public class JSONStrategy extends Introspector { @Override protected void set(String name, Object value) { // TODO Auto-generated method stub - + } @Override diff --git a/aai-core/src/main/java/org/onap/aai/introspection/Loader.java b/aai-core/src/main/java/org/onap/aai/introspection/Loader.java index 3a69e56e..cae4cbb7 100644 --- a/aai-core/src/main/java/org/onap/aai/introspection/Loader.java +++ b/aai-core/src/main/java/org/onap/aai/introspection/Loader.java @@ -25,12 +25,13 @@ import org.onap.aai.restcore.MediaType; import org.onap.aai.setup.SchemaVersion; import java.util.Map; +import java.util.Set; public abstract class Loader { private final SchemaVersion version; private final ModelType modelType; - + /** * Instantiates a new loader. * @@ -41,32 +42,32 @@ public abstract class Loader { this.version = version; this.modelType = modelType; } - + /** * Process. * * @param version the version */ protected abstract void process(SchemaVersion version); - + /** * Object from name. * * @param name the name * @return the object - * @throws AAIUnknownObjectException + * @throws AAIUnknownObjectException */ public abstract Object objectFromName(String name) throws AAIUnknownObjectException; - + /** * Introspector from name. * * @param name the name * @return the introspector - * @throws AAIUnknownObjectException + * @throws AAIUnknownObjectException */ public abstract Introspector introspectorFromName(String name) throws AAIUnknownObjectException; - + /** * Unmarshal. * @@ -76,7 +77,7 @@ public abstract class Loader { * @return the introspector */ public abstract Introspector unmarshal(String type, String json, MediaType mediaType) throws AAIUnmarshallingException; - + /** * Unmarshal. * @@ -88,7 +89,7 @@ public abstract class Loader { return unmarshal(type, json, MediaType.APPLICATION_JSON_TYPE); } - + /** * Gets the model type. * @@ -97,7 +98,7 @@ public abstract class Loader { public ModelType getModelType() { return this.modelType; } - + /** * Gets the version. * @@ -106,6 +107,8 @@ public abstract class Loader { public SchemaVersion getVersion() { return this.version; } - + public abstract Map<String, Introspector> getAllObjects(); + + public abstract Set<String> getNamedPropNodes(); } diff --git a/aai-core/src/main/java/org/onap/aai/introspection/MoxyLoader.java b/aai-core/src/main/java/org/onap/aai/introspection/MoxyLoader.java index 82504550..fa52d62f 100644 --- a/aai-core/src/main/java/org/onap/aai/introspection/MoxyLoader.java +++ b/aai-core/src/main/java/org/onap/aai/introspection/MoxyLoader.java @@ -34,6 +34,7 @@ import org.onap.aai.logging.ErrorLogHelper; import org.onap.aai.logging.LogFormatTools; import org.onap.aai.nodes.NodeIngestor; import org.onap.aai.restcore.MediaType; +import org.onap.aai.schema.enums.ObjectMetadata; import org.onap.aai.setup.SchemaVersion; import org.onap.aai.workarounds.NamingExceptions; import org.springframework.stereotype.Component; @@ -44,6 +45,7 @@ import java.io.*; import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; public class MoxyLoader extends Loader { @@ -52,9 +54,11 @@ public class MoxyLoader extends Loader { private Map<String, Introspector> allObjs = null; private Map<SchemaVersion, MoxyLoader> moxyLoaderFactory; - + private NodeIngestor nodeIngestor; + private Set<String> namedProps; + public MoxyLoader(SchemaVersion version, NodeIngestor nodeIngestor) { super(version, ModelType.MOXY); this.nodeIngestor = nodeIngestor; @@ -67,14 +71,14 @@ public class MoxyLoader extends Loader { } /** * {@inheritDoc} - * @throws AAIUnknownObjectException + * @throws AAIUnknownObjectException */ @Override public Introspector introspectorFromName(String name) throws AAIUnknownObjectException { return IntrospectorFactory.newInstance(ModelType.MOXY, objectFromName(name)); } - + /** * {@inheritDoc} */ @@ -93,7 +97,7 @@ public class MoxyLoader extends Loader { } else { upperCamel = CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, sanitizedName); } - + try { final DynamicEntity result = jaxbContext.newDynamicEntity(upperCamel); @@ -115,14 +119,14 @@ public class MoxyLoader extends Loader { * We need to have just same JaxbContext for each version */ jaxbContext = nodeIngestor.getContextForVersion(version); - + } /** * {@inheritDoc} */ @Override - public Introspector unmarshal(String type, String json, MediaType mediaType) throws AAIUnmarshallingException { + public Introspector unmarshal(String type, String json, MediaType mediaType) throws AAIUnmarshallingException { try { final Object clazz = objectFromName(type); final Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); @@ -143,7 +147,7 @@ public class MoxyLoader extends Loader { throw new AAIUnmarshallingException("Could not unmarshall: " + e.getMessage(), e); } } - + @Override public Map<String, Introspector> getAllObjects() { if (this.allObjs != null) { @@ -163,7 +167,7 @@ public class MoxyLoader extends Loader { return allObjs; } } - + private Set<String> objectsInVersion() { Set<String> result = new HashSet<>(); @@ -177,7 +181,21 @@ public class MoxyLoader extends Loader { //result.remove("EdgePropNames"); return result; } - + + @Override + public Set<String> getNamedPropNodes(){ + + if(namedProps == null){ + namedProps = getAllObjects() + .entrySet() + .stream() + .filter( + (entry) -> entry.getValue().getMetadata(ObjectMetadata.NAME_PROPS) != null + ).map(entry -> entry.getKey()).collect(Collectors.toSet()); + } + + return namedProps; + } public DynamicJAXBContext getJAXBContext() { return this.jaxbContext; } diff --git a/aai-core/src/main/java/org/onap/aai/introspection/MoxyStrategy.java b/aai-core/src/main/java/org/onap/aai/introspection/MoxyStrategy.java index ecf31253..f0d487e8 100644 --- a/aai-core/src/main/java/org/onap/aai/introspection/MoxyStrategy.java +++ b/aai-core/src/main/java/org/onap/aai/introspection/MoxyStrategy.java @@ -51,9 +51,10 @@ import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.util.*; import java.util.Map.Entry; +import java.util.stream.Collectors; public class MoxyStrategy extends Introspector { - + private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(MoxyStrategy.class); private DynamicEntity internalObject = null; private DynamicType internalType = null; @@ -67,9 +68,9 @@ public class MoxyStrategy extends Introspector { private Set<String> requiredProperties = null; private boolean isInitialized = false; - + private NodeIngestor nodeIngestor; - + protected MoxyStrategy(Object obj) { super(obj); /* must look up the correct jaxbcontext for this object */ @@ -81,19 +82,19 @@ public class MoxyStrategy extends Introspector { jaxbContext = nodeIngestor.getContextForVersion(version); String simpleName = internalObject.getClass().getName(); internalType = jaxbContext.getDynamicType(simpleName); - + cd = internalType.getDescriptor(); try { marshaller = jaxbContext.createMarshaller(); - + unmarshaller = jaxbContext.createUnmarshaller(); - + } catch (JAXBException e) { } } - + private void init() { isInitialized = true; @@ -104,13 +105,13 @@ public class MoxyStrategy extends Introspector { } props = Collections.unmodifiableSet(props); this.properties = props; - + Set<String> requiredProps = new LinkedHashSet<>(); requiredProps = new LinkedHashSet<>(); for (DatabaseMapping dm : cd.getMappings()) { - if (dm.getField() instanceof XMLField) { + if (dm.getField() instanceof XMLField) { XMLField x = (XMLField)dm.getField(); - if (x != null) { + if (x != null) { if (x.isRequired()) { requiredProps.add(this.removeXPathDescriptor(x.getName())); } @@ -119,25 +120,25 @@ public class MoxyStrategy extends Introspector { } requiredProps = Collections.unmodifiableSet(requiredProps); this.requiredProperties = requiredProps; - + Set<String> keys = new LinkedHashSet<>(); - + for (String name : internalType.getDescriptor().getPrimaryKeyFieldNames()) { keys.add(this.removeXPathDescriptor(name)); } keys = Collections.unmodifiableSet(keys); this.keys = keys; - - + + } - + @Override public boolean hasProperty(String name) { String convertedName = convertPropertyName(name); - return internalType.containsProperty(convertedName); + return internalType.containsProperty(convertedName); } - + @Override public Object get(String name) { return internalObject.get(name); @@ -145,7 +146,7 @@ public class MoxyStrategy extends Introspector { @Override public void set(String name, Object obj) throws IllegalArgumentException { - + internalObject.set(name, obj); } @@ -157,7 +158,7 @@ public class MoxyStrategy extends Introspector { } return this.properties; - + } @Override @@ -179,7 +180,7 @@ public class MoxyStrategy extends Introspector { return this.keys; } - + @Override public Map<PropertyMetadata, String> getPropertyMetadata(String prop) { String propName = this.convertPropertyName(prop); @@ -192,7 +193,7 @@ public class MoxyStrategy extends Introspector { PropertyMetadata.valueOf(CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, (String)entry.getKey())), (String)entry.getValue()); } } - + return result; } @@ -200,8 +201,8 @@ public class MoxyStrategy extends Introspector { public String getJavaClassName() { return internalObject.getClass().getName(); } - - + + @Override public Class<?> getClass(String name) { @@ -211,7 +212,7 @@ public class MoxyStrategy extends Introspector { if (internalType.getPropertyType(name) == null) { if (cd.getMappingForAttributeName(name) instanceof XMLCompositeDirectCollectionMapping) { resultClass = cd.getMappingForAttributeName(name).getContainerPolicy().getContainerClass(); - + } else if (cd.getMappingForAttributeName(name) instanceof XMLCompositeCollectionMapping) { resultClass = cd.getMappingForAttributeName(name).getContainerPolicy().getContainerClass(); } else { @@ -243,7 +244,7 @@ public class MoxyStrategy extends Introspector { resultClass = cd.getMappingForAttributeName(name).getReferenceDescriptor().getJavaClass(); } } - + return resultClass; } @@ -251,20 +252,20 @@ public class MoxyStrategy extends Introspector { public Object getUnderlyingObject() { return this.internalObject; } - + @Override public String getChildName() { - + String className = internalObject.getClass().getSimpleName(); String lowerHyphen = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, className); - + if (this.isContainer()) { lowerHyphen = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN,this.getGenericTypeClass(this.getProperties().iterator().next()).getSimpleName()); } - + return lowerHyphen; } - + @Override public String getName() { String className = internalObject.getClass().getSimpleName(); @@ -273,11 +274,11 @@ public class MoxyStrategy extends Introspector { if (this.isContainer()) { lowerHyphen = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN,this.getGenericTypeClass(this.getProperties().get(0)).getSimpleName()); }*/ - + return lowerHyphen; } - + @Override public String getObjectId() throws UnsupportedEncodingException { String result = ""; @@ -285,17 +286,17 @@ public class MoxyStrategy extends Introspector { if (this.isContainer()) { result += "/" + this.getName(); } else { - + if (container != null) { result += "/" + container; } result += "/" + this.getDbName() + "/" + this.findKey(); - + } - + return result; } - + @Override protected String findKey() throws UnsupportedEncodingException { Set<String> keys = null; @@ -305,10 +306,10 @@ public class MoxyStrategy extends Introspector { String value = UriUtils.encode(this.getValue(key).toString(), "UTF-8"); results.add(value); } - + return Joiner.on("/").join(results); } - + @Override public String preProcessKey (String key) { String result = ""; @@ -316,19 +317,19 @@ public class MoxyStrategy extends Introspector { String[] split = key.split("/"); int i = 0; for (i = split.length-1; i >= 0; i--) { - + if (jaxbContext.getDynamicType(split[i]) != null) { break; - + } - + } result = Joiner.on("/").join(Arrays.copyOfRange(split, 0, i)); - + return result; - + } - + @Override public String marshal(MarshallerProperties properties) { StringWriter result = new StringWriter(); @@ -339,7 +340,7 @@ public class MoxyStrategy extends Introspector { marshaller.setProperty(org.eclipse.persistence.jaxb.MarshallerProperties.JSON_WRAPPER_AS_ARRAY_NAME, properties.getWrapperAsArrayName()); marshaller.setProperty(org.eclipse.persistence.jaxb.MarshallerProperties.JSON_MARSHAL_EMPTY_COLLECTIONS, false); } - + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, properties.getFormatted()); marshaller.marshal(this.internalObject, result); } catch (JAXBException e) { @@ -348,7 +349,7 @@ public class MoxyStrategy extends Introspector { return result.toString(); } - + @Override public Object clone() { Object result = null; @@ -358,7 +359,7 @@ public class MoxyStrategy extends Introspector { unmarshaller.setProperty(UnmarshallerProperties.MEDIA_TYPE, "application/json"); unmarshaller.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT, false); unmarshaller.setProperty(UnmarshallerProperties.JSON_WRAPPER_AS_ARRAY_NAME, true); - + result = unmarshaller.unmarshal(new StreamSource(new StringReader(this.marshal(true))), this.internalObject.getClass()).getValue(); } catch (JAXBException e) { // TODO Auto-generated catch block @@ -371,21 +372,21 @@ public class MoxyStrategy extends Introspector { public ModelType getModelType() { return ModelType.MOXY; } - + private String removeXPathDescriptor(String name) { - + return name.replaceAll("/text\\(\\)", ""); } @Override public String getMetadata(ObjectMetadata name) { - + return (String)cd.getProperty(name.toString()); } @Override public SchemaVersion getVersion() { - + return this.version; } } diff --git a/aai-core/src/main/java/org/onap/aai/rest/db/HttpEntry.java b/aai-core/src/main/java/org/onap/aai/rest/db/HttpEntry.java index dd5d42c6..bd7f1b91 100644 --- a/aai-core/src/main/java/org/onap/aai/rest/db/HttpEntry.java +++ b/aai-core/src/main/java/org/onap/aai/rest/db/HttpEntry.java @@ -8,7 +8,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -50,6 +50,7 @@ import com.github.fge.jsonpatch.mergepatch.JsonMergePatch; import org.apache.commons.lang.StringUtils; import org.apache.tinkerpop.gremlin.structure.Graph; import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.VertexProperty; import org.janusgraph.core.JanusGraphException; import org.javatuples.Pair; import org.onap.aai.db.props.AAIProperties; @@ -67,6 +68,7 @@ import org.onap.aai.nodes.NodeIngestor; import org.onap.aai.parsers.query.QueryParser; import org.onap.aai.parsers.uri.URIToExtensionInformation; +import org.onap.aai.parsers.uri.URIToObject; import org.onap.aai.rest.ueb.UEBNotification; import org.onap.aai.restcore.HttpMethod; import org.onap.aai.schema.enums.ObjectMetadata; @@ -93,15 +95,15 @@ public class HttpEntry { private static final String TARGET_ENTITY = "DB"; private ModelType introspectorFactoryType; - + private QueryStyle queryStyle; - + private SchemaVersion version; - + private Loader loader; - + private TransactionalGraphEngine dbEngine; - + private boolean processSingle = true; private int paginationBucket = -1; @@ -148,7 +150,7 @@ public class HttpEntry { } - public HttpEntry setHttpEntryProperties(SchemaVersion version, DBConnectionType connectionType, UEBNotification notification){ + public HttpEntry setHttpEntryProperties(SchemaVersion version, DBConnectionType connectionType, UEBNotification notification){ this.version = version; this.loader = loaderFactory.createLoaderForVersion(introspectorFactoryType, version); this.dbEngine = new JanusGraphDBEngine( @@ -161,7 +163,7 @@ public class HttpEntry { getDbEngine().startTransaction(); return this; } - + /** * Gets the introspector factory type. @@ -316,7 +318,7 @@ public class HttpEntry { QueryEngine queryEngine = dbEngine.getQueryEngine(); int maxRetries = 10; int retry = 0; - + LoggingContext.save(); for (DBRequest request : requests) { response = null; @@ -358,7 +360,7 @@ public class HttpEntry { if (requestContextList != null) { requestContext = requestContextList.get(0); } - + if (cleanUp == null) { cleanUp = "false"; } @@ -426,7 +428,7 @@ public class HttpEntry { result = formatter.output(vertices.stream().map(vertex -> (Object) vertex).collect(Collectors.toList())).toString(); status = Status.OK; } - + break; case PUT: response = this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request, sourceOfTruth, version, loader, obj, uri, true); @@ -441,9 +443,9 @@ public class HttpEntry { } obj = serializer.getLatestVersionView(v); if (query.isDependent()) { - relatedObjects = this.getRelatedObjects(serializer, queryEngine, v); + relatedObjects = this.getRelatedObjects(serializer, queryEngine, v, obj, this.loader); } - LoggingContext.elapsedTime((long)serializer.getDBTimeMsecs() + + LoggingContext.elapsedTime((long)serializer.getDBTimeMsecs() + (long)queryEngine.getDBTimeMsecs(), TimeUnit.MILLISECONDS); LOGGER.info ("Completed "); LoggingContext.restoreIfPossible(); @@ -454,7 +456,7 @@ public class HttpEntry { serializer.touchStandardVertexProperties(v, false); this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request, sourceOfTruth, version, loader, obj, uri, true); serializer.createEdge(obj, v); - + LoggingContext.elapsedTime((long)serializer.getDBTimeMsecs(),TimeUnit.MILLISECONDS); LOGGER.info ("Completed"); LoggingContext.restoreIfPossible(); @@ -466,7 +468,7 @@ public class HttpEntry { existingObj = this.getObjectFromDb(vertices, serializer, query, existingObj, request.getUri(), 0, false, cleanUp); String existingJson = existingObj.marshal(false); String newJson; - + if (request.getRawRequestContent().isPresent()) { newJson = request.getRawRequestContent().get(); } else { @@ -489,15 +491,15 @@ public class HttpEntry { status = Status.OK; patchedObj = serializer.getLatestVersionView(v); if (query.isDependent()) { - relatedObjects = this.getRelatedObjects(serializer, queryEngine, v); + relatedObjects = this.getRelatedObjects(serializer, queryEngine, v, patchedObj, this.loader); } - LoggingContext.elapsedTime((long)serializer.getDBTimeMsecs() + + LoggingContext.elapsedTime((long)serializer.getDBTimeMsecs() + (long)queryEngine.getDBTimeMsecs(), TimeUnit.MILLISECONDS); LOGGER.info ("Completed"); LoggingContext.restoreIfPossible(); notification.createNotificationEvent(transactionId, sourceOfTruth, status, uri, patchedObj, relatedObjects, basePath); } catch (IOException | JsonPatchException e) { - + LOGGER.info ("Caught exception: " + e.getMessage()); LoggingContext.restoreIfPossible(); throw new AAIException("AAI_3000", "could not perform patch operation"); @@ -507,18 +509,18 @@ public class HttpEntry { String resourceVersion = params.getFirst("resource-version"); obj = serializer.getLatestVersionView(v); if (query.isDependent()) { - relatedObjects = this.getRelatedObjects(serializer, queryEngine, v); + relatedObjects = this.getRelatedObjects(serializer, queryEngine, v, obj, this.loader); } /* * Find all Delete-other-vertex vertices and create structure for notify - * findDeleatble also returns the startVertex v and we dont want to create + * findDeleatble also returns the startVertex v and we dont want to create * duplicate notification events for the same * So remove the startvertex first */ - + List<Vertex> deletableVertices = dbEngine.getQueryEngine().findDeletable(v); Long vId = (Long) v.id(); - + /* * I am assuming vertexId cant be null */ @@ -527,39 +529,39 @@ public class HttpEntry { Map<Vertex, Introspector> deleteObjects = new HashMap<>(); Map<String, URI> uriMap = new HashMap<>(); Map<String, HashMap<String, Introspector>> deleteRelatedObjects = new HashMap<>(); - + if(isDelVerticesPresent){ deleteObjects = this.buildIntrospectorObjects(serializer, deletableVertices); - + uriMap = this.buildURIMap(serializer, deleteObjects); deleteRelatedObjects = this.buildRelatedObjects(serializer, queryEngine, deleteObjects); } - + this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request, sourceOfTruth, version, loader, obj, uri, true); serializer.delete(v, deletableVertices, resourceVersion, enableResourceVersion); this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request, sourceOfTruth, version, loader, obj, uri, false); - - LoggingContext.elapsedTime((long)serializer.getDBTimeMsecs() + + + LoggingContext.elapsedTime((long)serializer.getDBTimeMsecs() + (long)queryEngine.getDBTimeMsecs(), TimeUnit.MILLISECONDS); LOGGER.info ("Completed"); LoggingContext.restoreIfPossible(); status = Status.NO_CONTENT; notification.createNotificationEvent(transactionId, sourceOfTruth, status, uri, obj, relatedObjects, basePath); - + /* * Notify delete-other-v candidates */ - + if(isDelVerticesPresent){ this.buildNotificationEvent(sourceOfTruth, status, transactionId, notification, deleteObjects, uriMap, deleteRelatedObjects, basePath); } - + break; case DELETE_EDGE: serializer.touchStandardVertexProperties(v, false); serializer.deleteEdge(obj, v); - + LoggingContext.elapsedTime((long)serializer.getDBTimeMsecs(),TimeUnit.MILLISECONDS); LOGGER.info ("Completed"); LoggingContext.restoreIfPossible(); @@ -569,8 +571,8 @@ public class HttpEntry { default: break; } - - + + /* temporarily adding vertex id to the headers * to be able to use for testing the vertex id endpoint functionality * since we presently have no other way of generating those id urls @@ -606,7 +608,7 @@ public class HttpEntry { break; } catch (JanusGraphException e) { this.dbEngine.rollback(); - + LOGGER.info ("Caught exception: " + e.getMessage()); LoggingContext.restoreIfPossible(); AAIException ex = new AAIException("AAI_6142", e); @@ -655,7 +657,7 @@ public class HttpEntry { return Pair.with(success, responses); } - + /** * Gets the media type. * @@ -663,15 +665,15 @@ public class HttpEntry { * @return the media type */ private String getMediaType(List <MediaType> mediaTypeList) { - String mediaType = MediaType.APPLICATION_JSON; // json is the default + String mediaType = MediaType.APPLICATION_JSON; // json is the default for (MediaType mt : mediaTypeList) { if (MediaType.APPLICATION_XML_TYPE.isCompatible(mt)) { mediaType = MediaType.APPLICATION_XML; - } + } } return mediaType; } - + /** * Gets the object from db. * @@ -691,19 +693,19 @@ public class HttpEntry { * @throws NoSuchMethodException the no such method exception * @throws UnsupportedEncodingException the unsupported encoding exception * @throws MalformedURLException the malformed URL exception - * @throws AAIUnknownObjectException - * @throws URISyntaxException + * @throws AAIUnknownObjectException + * @throws URISyntaxException */ private Introspector getObjectFromDb(List<Vertex> results, DBSerializer serializer, QueryParser query, Introspector obj, URI uri, int depth, boolean nodeOnly, String cleanUp) throws AAIException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, InstantiationException, NoSuchMethodException, UnsupportedEncodingException, AAIUnknownObjectException, URISyntaxException { - - //nothing found - if (results.isEmpty()) { - String msg = createNotFoundMessage(query.getResultType(), uri); + + //nothing found + if (results.isEmpty()) { + String msg = createNotFoundMessage(query.getResultType(), uri); throw new AAIException("AAI_6114", msg); - } + } + + return serializer.dbToObject(results, obj, depth, nodeOnly, cleanUp); - return serializer.dbToObject(results, obj, depth, nodeOnly, cleanUp); - } /** @@ -800,7 +802,7 @@ public class HttpEntry { return response; } - + /** * Creates the not found message. * @@ -809,12 +811,12 @@ public class HttpEntry { * @return the string */ private String createNotFoundMessage(String resultType, URI uri) { - - String msg = "No Node of type " + resultType + " found at: " + uri.getPath(); - return msg; + String msg = "No Node of type " + resultType + " found at: " + uri.getPath(); + + return msg; } - + /** * Sets the depth. * @@ -831,11 +833,11 @@ public class HttpEntry { return depth; } - if(depthParam == null){ + if(depthParam == null){ if(this.version.compareTo(schemaVersions.getDepthVersion()) >= 0){ depth = 0; } else { - depth = AAIProperties.MAXIMUM_DEPTH; + depth = AAIProperties.MAXIMUM_DEPTH; } } else { if (!depthParam.isEmpty() && !"all".equals(depthParam)){ @@ -847,16 +849,16 @@ public class HttpEntry { } } - String maxDepth = obj.getMetadata(ObjectMetadata.MAXIMUM_DEPTH); - + String maxDepth = obj.getMetadata(ObjectMetadata.MAXIMUM_DEPTH); + int maximumDepth = AAIProperties.MAXIMUM_DEPTH; if(maxDepth != null){ - try { - maximumDepth = Integer.parseInt(maxDepth); - } catch(Exception ex){ - throw new AAIException("AAI_4018"); - } + try { + maximumDepth = Integer.parseInt(maxDepth); + } catch(Exception ex){ + throw new AAIException("AAI_4018"); + } } if(depth > maximumDepth){ @@ -865,7 +867,7 @@ public class HttpEntry { return depth; } - + /** * Checks if is modification method. * @@ -874,31 +876,155 @@ public class HttpEntry { */ private boolean isModificationMethod(HttpMethod method) { boolean result = false; - + if (method.equals(HttpMethod.PUT) || method.equals(HttpMethod.PUT_EDGE) || method.equals(HttpMethod.DELETE_EDGE) || method.equals(HttpMethod.MERGE_PATCH)) { result = true; } - + return result; - + } - - private HashMap<String, Introspector> getRelatedObjects(DBSerializer serializer, QueryEngine queryEngine, Vertex v) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, InstantiationException, NoSuchMethodException, UnsupportedEncodingException, AAIException, URISyntaxException { + + /** + * Given an uri, introspector object and loader object + * it will check if the obj is top level object if it is, + * it will return immediately returning the uri passed in + * If it isn't, it will go through, get the uriTemplate + * from the introspector object and get the count of "/"s + * and remove that part of the uri using substring + * and keep doing that until the current object is top level + * Also added the max depth just so worst case scenario + * Then keep adding aai-uri to the list include the aai-uri passed in + * Convert that list into an array and return it + * <p> + * + * Example: + * + * <blockquote> + * aai-uri -> /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1/vservers/vserver/v1 + * + * Given the uriTemplate vserver -> /vservers/vserver/{vserver-id} + * it converts to /vservers/vserver + * + * lastIndexOf /vservers/vserver in /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1/vservers/vserver/v1 + * ^ + * | + * | + * lastIndexOf + * Use substring to get the string from 0 to that lastIndexOf + * aai-uri -> /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1 + * + * From this new aai-uri, generate a introspector from the URITOObject class + * and keep doing this until you + * + * </blockquote> + * + * @param aaiUri - aai-uri of the vertex representating the unique id of a given vertex + * @param obj - introspector object of the given starting vertex + * @param loader - Type of loader which will always be MoxyLoader to support model driven + * @return an array of strings which can be used to get the vertexes of parent and grand parents from a given vertex + * @throws UnsupportedEncodingException + * @throws AAIException + */ + String[] convertIntrospectorToUriList(String aaiUri, Introspector obj, Loader loader) throws UnsupportedEncodingException, AAIException { + + List<String> uriList = new ArrayList<>(); + String template = StringUtils.EMPTY; + String truncatedUri = aaiUri; + int depth = AAIProperties.MAXIMUM_DEPTH; + uriList.add(truncatedUri); + + while (depth >= 0 && !obj.isTopLevel()) { + template = obj.getMetadata(ObjectMetadata.URI_TEMPLATE); + + if(template == null){ + LOGGER.warn("Unable to find the uriTemplate for the object {}", obj.getDbName()); + return null; + } + + int templateCount = StringUtils.countMatches(template, "/"); + int truncatedUriCount = StringUtils.countMatches(truncatedUri, "/"); + + if(templateCount > truncatedUriCount){ + LOGGER.warn("Template uri {} contains more slashes than truncatedUri {}", template, truncatedUri); + return null; + } + + int cutIndex = StringUtils.ordinalIndexOf(truncatedUri, "/", truncatedUriCount-templateCount+1); + truncatedUri = StringUtils.substring(truncatedUri, 0, cutIndex); + uriList.add(truncatedUri); + obj = new URIToObject(loader, UriBuilder.fromPath(truncatedUri).build()).getEntity(); + depth--; + } + + return uriList.toArray(new String[uriList.size()]); + } + + /** + * + * @param serializer + * @param queryEngine + * @param v + * @param obj + * @param loader + * @return + * @throws IllegalAccessException + * @throws IllegalArgumentException + * @throws InvocationTargetException + * @throws SecurityException + * @throws InstantiationException + * @throws NoSuchMethodException + * @throws UnsupportedEncodingException + * @throws AAIException + * @throws URISyntaxException + */ + private HashMap<String, Introspector> getRelatedObjects(DBSerializer serializer, QueryEngine queryEngine, Vertex v, Introspector obj, Loader loader) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, InstantiationException, NoSuchMethodException, UnsupportedEncodingException, AAIException, URISyntaxException { + HashMap<String, Introspector> relatedVertices = new HashMap<>(); - List<Vertex> vertexChain = queryEngine.findParents(v); - for (Vertex vertex : vertexChain) { + VertexProperty aaiUriProperty = v.property(AAIProperties.AAI_URI); + + if(!aaiUriProperty.isPresent()){ + if(LOGGER.isDebugEnabled()){ + LOGGER.debug("For the given vertex {}, it seems aai-uri is not present so not getting related objects", v.id().toString()); + } else { + LOGGER.info("It seems aai-uri is not present in vertex, so not getting related objects, for more info enable debug log"); + } + return relatedVertices; + } + + String aaiUri = aaiUriProperty.value().toString(); + + if(!obj.isTopLevel()){ + String[] uriList = convertIntrospectorToUriList(aaiUri, obj, loader); + List<Vertex> vertexChain = null; + // If the uriList is null then there is something wrong with converting the uri + // into a list of aai-uris so falling back to the old mechanism for finding parents + if(uriList == null){ + LOGGER.info("Falling back to the old mechanism due to unable to convert aai-uri to list of uris but this is not optimal"); + vertexChain = queryEngine.findParents(v); + } else { + vertexChain = queryEngine.findParents(uriList); + } + for(Vertex vertex : vertexChain){ + try { + final Introspector vertexObj = serializer.getVertexProperties(vertex); + relatedVertices.put(vertexObj.getObjectId(), vertexObj); + } catch (AAIUnknownObjectException e) { + LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned"); + } + } + } else { try { - final Introspector vertexObj = serializer.getVertexProperties(vertex); + final Introspector vertexObj = serializer.getVertexProperties(v); relatedVertices.put(vertexObj.getObjectId(), vertexObj); } catch (AAIUnknownObjectException e) { LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned"); } - } - + return relatedVertices; } - + private Map<Vertex, Introspector> buildIntrospectorObjects(DBSerializer serializer, Iterable<Vertex> vertices) { Map<Vertex, Introspector> deleteObjectMap = new HashMap<>(); for (Vertex vertex : vertices) { @@ -944,7 +1070,7 @@ public class HttpEntry { for (Map.Entry<Vertex, Introspector> entry : introSpector.entrySet()) { try { HashMap<String, Introspector> relatedObjects = this.getRelatedObjects(serializer, queryEngine, - entry.getKey()); + entry.getKey(), entry.getValue(), this.loader); if (null != entry.getValue()) relatedObjectsMap.put(entry.getValue().getObjectId(), relatedObjects); } catch (IllegalAccessException | IllegalArgumentException @@ -959,7 +1085,7 @@ public class HttpEntry { return relatedObjectsMap; } - + private void buildNotificationEvent(String sourceOfTruth, Status status, String transactionId, UEBNotification notification, Map<Vertex, Introspector> deleteObjects, Map<String, URI> uriMap, Map<String, HashMap<String, Introspector>> deleteRelatedObjects, String basePath) { @@ -980,4 +1106,27 @@ public class HttpEntry { } } } + + public void setPaginationParameters(String resultIndex, String resultSize) { + if (resultIndex != null && resultIndex != "-1" && resultSize != null && resultSize != "-1") { + this.setPaginationIndex(Integer.parseInt(resultIndex)); + this.setPaginationBucket(Integer.parseInt(resultSize)); + } + } + + public List<Object> getPaginatedVertexList(List<Object> vertexList) throws AAIException{ + List<Object> vertices; + if(this.isPaginated()) { + this.setTotalsForPaging(vertexList.size(), this.getPaginationBucket()); + int startIndex = (this.getPaginationIndex() - 1) * this.getPaginationBucket(); + int endIndex = Math.min((this.getPaginationBucket() * this.getPaginationIndex()), vertexList.size()); + if(startIndex > endIndex){ + throw new AAIException("AAI_6150"," ResultIndex is not appropriate for the result set, Needs to be <= "+ endIndex); + } + vertices = vertexList.subList(startIndex, endIndex); + }else{ + vertices = vertexList; + } + return vertices; + } } diff --git a/aai-core/src/main/java/org/onap/aai/serialization/db/DBSerializer.java b/aai-core/src/main/java/org/onap/aai/serialization/db/DBSerializer.java index 7a2c447e..9d107b1f 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/db/DBSerializer.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/db/DBSerializer.java @@ -23,18 +23,21 @@ package org.onap.aai.serialization.db; import com.att.eelf.configuration.EELFLogger; import com.att.eelf.configuration.EELFManager; import com.google.common.base.CaseFormat; -import org.janusgraph.core.SchemaViolationException; +import com.google.common.collect.Multimap; import org.apache.commons.collections.IteratorUtils; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree; import org.apache.tinkerpop.gremlin.structure.*; -import org.javatuples.Pair; +import org.janusgraph.core.SchemaViolationException; import org.javatuples.Triplet; +import org.onap.aai.concurrent.AaiCallable; +import org.onap.aai.config.SpringContextAware; import org.onap.aai.db.props.AAIProperties; import org.onap.aai.edges.EdgeIngestor; import org.onap.aai.edges.EdgeRule; import org.onap.aai.edges.EdgeRuleQuery; +import org.onap.aai.edges.TypeAlphabetizer; import org.onap.aai.edges.enums.AAIDirection; import org.onap.aai.edges.enums.EdgeField; import org.onap.aai.edges.enums.EdgeProperty; @@ -57,6 +60,7 @@ import org.onap.aai.schema.enums.PropertyMetadata; import org.onap.aai.serialization.db.exceptions.MultipleEdgeRuleFoundException; import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException; import org.onap.aai.serialization.engines.TransactionalGraphEngine; +import org.onap.aai.serialization.engines.query.QueryEngine; import org.onap.aai.serialization.tinkerpop.TreeBackedVertex; import org.onap.aai.setup.SchemaVersion; import org.onap.aai.setup.SchemaVersions; @@ -64,8 +68,6 @@ import org.onap.aai.util.AAIConfig; import org.onap.aai.util.AAIConstants; import org.onap.aai.workarounds.NamingExceptions; import org.springframework.context.ApplicationContext; -import org.onap.aai.concurrent.AaiCallable; -import org.onap.aai.config.SpringContextAware; import javax.ws.rs.core.UriBuilder; import java.io.UnsupportedEncodingException; @@ -78,1790 +80,1956 @@ import java.util.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; +import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.onap.aai.serialization.engines.query.QueryEngine; +import java.util.stream.Collectors; public class DBSerializer { - - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(DBSerializer.class); - - private final TransactionalGraphEngine engine; - private final String sourceOfTruth; - private final ModelType introspectionType; - private final SchemaVersion version; - private final Loader latestLoader; - private EdgeSerializer edgeSer; - private EdgeIngestor edgeRules; - private final Loader loader; - private final String baseURL; - private double dbTimeMsecs = 0; - private long currentTimeMillis; - - private SchemaVersions schemaVersions; - /** - * Instantiates a new DB serializer. - * - * @param version the version - * @param engine the engine - * @param introspectionType the introspection type - * @param sourceOfTruth the source of truth - * @throws AAIException - */ - public DBSerializer(SchemaVersion version, TransactionalGraphEngine engine, ModelType introspectionType, String sourceOfTruth) throws AAIException { - this.engine = engine; - this.sourceOfTruth = sourceOfTruth; - this.introspectionType = introspectionType; - this.schemaVersions = SpringContextAware.getBean(SchemaVersions.class); - SchemaVersion LATEST = schemaVersions.getDefaultVersion(); - this.latestLoader = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, LATEST); - this.version = version; - this.loader = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version); - this.baseURL = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE); - this.currentTimeMillis = System.currentTimeMillis(); - initBeans(); - } - - private void initBeans() { - //TODO proper spring wiring, but that requires a lot of refactoring so for now we have this - ApplicationContext ctx = SpringContextAware.getApplicationContext(); - EdgeIngestor ei = ctx.getBean(EdgeIngestor.class); - setEdgeIngestor(ei); - EdgeSerializer es = ctx.getBean(EdgeSerializer.class); - setEdgeSerializer(es); - } - - private void backupESInit() { - setEdgeSerializer(new EdgeSerializer(this.edgeRules)); - } - - public void setEdgeSerializer(EdgeSerializer edgeSer) { - this.edgeSer = edgeSer; - } - - public EdgeSerializer getEdgeSeriailizer() { - return this.edgeSer; - } - - public void setEdgeIngestor(EdgeIngestor ei) { - this.edgeRules = ei; - } - - public EdgeIngestor getEdgeIngestor(){ - return this.edgeRules; - } - - /** - * Touch standard vertex properties. - * - * @param v the v - * @param isNewVertex the is new vertex - */ - public void touchStandardVertexProperties(Vertex v, boolean isNewVertex) { - String timeNowInSec = Long.toString(currentTimeMillis); - - if (isNewVertex) { - v.property(AAIProperties.SOURCE_OF_TRUTH, this.sourceOfTruth); - v.property(AAIProperties.CREATED_TS, timeNowInSec); - v.property(AAIProperties.AAI_UUID, UUID.randomUUID().toString()); - } - v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec); - v.property(AAIProperties.LAST_MOD_TS, timeNowInSec); - v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth); - - } - - private void touchStandardVertexProperties(String nodeType, Vertex v, boolean isNewVertex) { - - v.property(AAIProperties.NODE_TYPE, nodeType); - touchStandardVertexProperties(v, isNewVertex); - - } - - - - /** - * Creates the new vertex. - * - * @param wrappedObject the wrapped object - * @return the vertex - * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIException the AAI exception - */ - public Vertex createNewVertex(Introspector wrappedObject) { - Vertex v; - try { - StopWatch.conditionalStart(); - v = this.engine.tx().addVertex(); - touchStandardVertexProperties(wrappedObject.getDbName(), v, true); - } - finally { - dbTimeMsecs += StopWatch.stopIfStarted(); - } - return v; - } - - /** - * Trim class name. - * - * @param className the class name - * @return the string - */ - /* - * Removes the classpath from a class name - */ - public String trimClassName (String className) { - String returnValue = ""; - - if (className.lastIndexOf('.') == -1) { - return className; - } - returnValue = className.substring(className.lastIndexOf('.') + 1, className.length()); - - return returnValue; - } - - /** - * Serialize to db. - * - * @param obj the obj - * @param v the v - * @param uriQuery the uri query - * @param identifier the identifier - * @throws SecurityException the security exception - * @throws IllegalAccessException the illegal access exception - * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws InstantiationException the instantiation exception - * @throws InterruptedException the interrupted exception - * @throws NoSuchMethodException the no such method exception - * @throws AAIException the AAI exception - * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIUnknownObjectException - */ - public void serializeToDb(Introspector obj, Vertex v, QueryParser uriQuery, String identifier, String requestContext) throws AAIException, UnsupportedEncodingException { - StopWatch.conditionalStart(); - try { - if (uriQuery.isDependent()) { - //try to find the parent - List<Vertex> vertices = uriQuery.getQueryBuilder().getParentQuery().toList(); - if (!vertices.isEmpty()) { - Vertex parent = vertices.get(0); - this.reflectDependentVertex(parent, v, obj, requestContext); - } else { - dbTimeMsecs += StopWatch.stopIfStarted(); - throw new AAIException("AAI_6114", "No parent Node of type " + uriQuery.getParentResultType() + " for " + identifier); - } - } else { - serializeSingleVertex(v, obj, requestContext); - } - - } catch (SchemaViolationException e) { - dbTimeMsecs += StopWatch.stopIfStarted(); - throw new AAIException("AAI_6117", e); - } - dbTimeMsecs += StopWatch.stopIfStarted(); - } - - public void serializeSingleVertex(Vertex v, Introspector obj, String requestContext) throws UnsupportedEncodingException, AAIException { - StopWatch.conditionalStart(); - try { - boolean isTopLevel = obj.isTopLevel(); - if (isTopLevel) { - addUriIfNeeded(v, obj.getURI()); - } - - processObject(obj, v, requestContext); - if (!isTopLevel) { - URI uri = this.getURIForVertex(v); - URIParser parser = new URIParser(this.loader, uri); - if (parser.validate()) { - addUriIfNeeded(v, uri.toString()); - } - } - } catch (SchemaViolationException e) { - throw new AAIException("AAI_6117", e); - } - finally { - dbTimeMsecs += StopWatch.stopIfStarted(); - } - } - - private void addUriIfNeeded(Vertex v, String uri) { - VertexProperty<String> uriProp = v.property(AAIProperties.AAI_URI); - if (!uriProp.isPresent() || (uriProp.isPresent() && !uriProp.value().equals(uri))) { - v.property(AAIProperties.AAI_URI, uri); - } - } - - /** - * Process object. - * - * @param <T> the generic type - * @param obj the obj - * @param v the v - * @return the list - * @throws IllegalAccessException the illegal access exception - * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws InstantiationException the instantiation exception - * @throws NoSuchMethodException the no such method exception - * @throws SecurityException the security exception - * @throws AAIException the AAI exception - * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIUnknownObjectException - */ - /* - * Helper method for reflectToDb - * Handles all the property setting - */ - private <T> List<Vertex> processObject (Introspector obj, Vertex v, String requestContext) throws UnsupportedEncodingException, AAIException { - Set<String> properties = new LinkedHashSet<>(obj.getProperties()); - properties.remove(AAIProperties.RESOURCE_VERSION); - List<Vertex> dependentVertexes = new ArrayList<>(); - List<Vertex> processedVertexes = new ArrayList<>(); - boolean isComplexType = false; - boolean isListType = false; - if (!obj.isContainer()) { - this.touchStandardVertexProperties(obj.getDbName(), v, false); - } - this.executePreSideEffects(obj, v); - for (String property : properties) { - Object value = null; - final String propertyType; - propertyType = obj.getType(property); - isComplexType = obj.isComplexType(property); - isListType = obj.isListType(property); - value = obj.getValue(property); - - if (!(isComplexType || isListType)) { - boolean canModify = this.canModify(obj, property, requestContext); - - if (canModify) { - final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property); - String dbProperty = property; - if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) { - dbProperty = metadata.get(PropertyMetadata.DB_ALIAS); - } - if (metadata.containsKey(PropertyMetadata.DATA_LINK)) { - //data linked properties are ephemeral - //they are populated dynamically on GETs - continue; - } - if (value != null) { - if (!value.equals(v.property(dbProperty).orElse(null))) { - if (propertyType.toLowerCase().contains(".long")) { - v.property(dbProperty, new Integer(((Long)value).toString())); - } else { - v.property(dbProperty, value); - } - } - } else { - v.property(dbProperty).remove(); - } - } - } else if (isListType) { - List<Object> list = (List<Object>)value; - if (obj.isComplexGenericType(property)) { - if (list != null) { - for (Object o : list) { - Introspector child = IntrospectorFactory.newInstance(this.introspectionType, o); - child.setURIChain(obj.getURI()); - processedVertexes.add(reflectDependentVertex(v, child, requestContext)); - } - } - } else { - //simple list case - engine.setListProperty(v, property, list); - } - } else { - //method.getReturnType() is not 'simple' then create a vertex and edge recursively returning an edge back to this method - if (value != null) { //effectively ignore complex properties not included in the object we're processing - if (value.getClass().isArray()) { - - int length = Array.getLength(value); - for (int i = 0; i < length; i ++) { - Object arrayElement = Array.get(value, i); - Introspector child = IntrospectorFactory.newInstance(this.introspectionType, arrayElement); - child.setURIChain(obj.getURI()); - processedVertexes.add(reflectDependentVertex(v, child, requestContext)); - - } - } else if (!property.equals("relationship-list")) { - // container case - Introspector introspector = IntrospectorFactory.newInstance(this.introspectionType, value); - if (introspector.isContainer()) { - dependentVertexes.addAll(this.engine.getQueryEngine().findChildrenOfType(v, introspector.getChildDBName())); - introspector.setURIChain(obj.getURI()); - - processedVertexes.addAll(processObject(introspector, v, requestContext)); - - } else { - dependentVertexes.addAll(this.engine.getQueryEngine().findChildrenOfType(v, introspector.getDbName())); - processedVertexes.add(reflectDependentVertex(v, introspector, requestContext)); - - } - } else if (property.equals("relationship-list")) { - handleRelationships(obj, v); - } - } - } - } - this.writeThroughDefaults(v, obj); - /* handle those vertexes not touched */ - for (Vertex toBeRemoved : processedVertexes) { - dependentVertexes.remove(toBeRemoved); - } - this.deleteItemsWithTraversal(dependentVertexes); - - this.executePostSideEffects(obj, v); - return processedVertexes; - } - - /** - * Handle relationships. - * - * @param obj the obj - * @param vertex the vertex - * @throws SecurityException the security exception - * @throws IllegalAccessException the illegal access exception - * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIException the AAI exception - */ - /* - * Handles the explicit relationships defined for an obj - */ - private void handleRelationships(Introspector obj, Vertex vertex) throws UnsupportedEncodingException, AAIException { - - - - Introspector wrappedRl = obj.getWrappedValue("relationship-list"); - processRelationshipList(wrappedRl, vertex); - - - } - - - /** - * Process relationship list. - * - * @param wrapped the wrapped - * @param v the v - * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIException the AAI exception - */ - private void processRelationshipList(Introspector wrapped, Vertex v) throws UnsupportedEncodingException, AAIException { - - List<Object> relationships = (List<Object>)wrapped.getValue("relationship"); - - List<Triplet<Vertex, Vertex, String>> addEdges = new ArrayList<>(); - List<Edge> existingEdges = this.engine.getQueryEngine().findEdgesForVersion(v, wrapped.getLoader()); - - for (Object relationship : relationships) { - Edge e = null; - Vertex cousinVertex = null; - String label = null; - Introspector wrappedRel = IntrospectorFactory.newInstance(this.introspectionType, relationship); - QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(wrappedRel); - - if (wrappedRel.hasProperty("relationship-label")) { - label = wrappedRel.getValue("relationship-label"); - } - - List<Vertex> results = parser.getQueryBuilder().toList(); - if (results.isEmpty()) { - final AAIException ex = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri()); - ex.getTemplateVars().add(parser.getResultType()); - ex.getTemplateVars().add(parser.getUri().toString()); - throw ex; - } else { - //still an issue if there's more than one - cousinVertex = results.get(0); - } - - if (cousinVertex != null) { - String vType = (String)v.property(AAIProperties.NODE_TYPE).value(); - String cousinType = (String)cousinVertex.property(AAIProperties.NODE_TYPE).value(); - EdgeRuleQuery.Builder baseQ = new EdgeRuleQuery.Builder(vType, cousinType).label(label); - - - if (!edgeRules.hasRule(baseQ.build())) { - throw new AAIException("AAI_6120", "No EdgeRule found for passed nodeTypes: " + v.property(AAIProperties.NODE_TYPE).value().toString() + ", " - + cousinVertex.property(AAIProperties.NODE_TYPE).value().toString() + (label != null ? (" with label " + label):"") +"."); - } else if (edgeRules.hasRule(baseQ.edgeType(EdgeType.TREE).build()) && !edgeRules.hasRule(baseQ.edgeType(EdgeType.COUSIN).build())) { - throw new AAIException("AAI_6145"); - } - - e = this.getEdgeBetween(EdgeType.COUSIN, v, cousinVertex, label); - - if (e == null) { - addEdges.add(new Triplet<>(v, cousinVertex, label)); - } else { - existingEdges.remove(e); - } - } - } - - for (Edge edge : existingEdges) { - edge.remove(); - } - for (Triplet<Vertex, Vertex, String> triplet : addEdges) { - try { - edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), triplet.getValue0(), triplet.getValue1(), triplet.getValue2()); - } catch (NoEdgeRuleFoundException e) { - throw new AAIException("AAI_6129", e); - } - } - - } - - /** - * Write through defaults. - * - * @param v the v - * @param obj the obj - * @throws AAIUnknownObjectException - */ - private void writeThroughDefaults(Vertex v, Introspector obj) throws AAIUnknownObjectException { - Introspector latest = this.latestLoader.introspectorFromName(obj.getName()); - if (latest != null) { - Set<String> required = latest.getRequiredProperties(); - - for (String field : required) { - String defaultValue = null; - Object vertexProp = null; - defaultValue = latest.getPropertyMetadata(field).get(PropertyMetadata.DEFAULT_VALUE); - if (defaultValue != null) { - vertexProp = v.<Object>property(field).orElse(null); - if (vertexProp == null) { - v.property(field, defaultValue); - } - } - } - } - - } - - - /** - * Reflect dependent vertex. - * - * @param v the v - * @param dependentObj the dependent obj - * @return the vertex - * @throws IllegalAccessException the illegal access exception - * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws InstantiationException the instantiation exception - * @throws NoSuchMethodException the no such method exception - * @throws SecurityException the security exception - * @throws AAIException the AAI exception - * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIUnknownObjectException - */ - private Vertex reflectDependentVertex(Vertex v, Introspector dependentObj, String requestContext) throws AAIException, UnsupportedEncodingException { - - //QueryParser p = this.engine.getQueryBuilder().createQueryFromURI(obj.getURI()); - //List<Vertex> items = p.getQuery().toList(); - QueryBuilder<Vertex> query = this.engine.getQueryBuilder(v); - query.createEdgeTraversal(EdgeType.TREE, v, dependentObj); - query.createKeyQuery(dependentObj); - - List<Vertex> items = query.toList(); - - Vertex dependentVertex = null; - if (items.size() == 1) { - dependentVertex = items.get(0); - this.verifyResourceVersion("update", dependentObj.getDbName(), dependentVertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), (String)dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String)dependentObj.getURI()); - } else { - this.verifyResourceVersion("create", dependentObj.getDbName(), "", (String)dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String)dependentObj.getURI()); - dependentVertex = createNewVertex(dependentObj); - } - - return reflectDependentVertex(v, dependentVertex, dependentObj, requestContext); - - } - - /** - * Reflect dependent vertex. - * - * @param parent the parent - * @param child the child - * @param obj the obj - * @return the vertex - * @throws IllegalAccessException the illegal access exception - * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws InstantiationException the instantiation exception - * @throws NoSuchMethodException the no such method exception - * @throws SecurityException the security exception - * @throws AAIException the AAI exception - * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIUnknownObjectException - */ - private Vertex reflectDependentVertex(Vertex parent, Vertex child, Introspector obj, String requestContext) throws AAIException, UnsupportedEncodingException { - - String parentUri = parent.<String>property(AAIProperties.AAI_URI).orElse(null); - if (parentUri != null) { - String uri; - uri = obj.getURI(); - addUriIfNeeded(child, parentUri + uri); - } - processObject(obj, child, requestContext); - - Edge e; - e = this.getEdgeBetween(EdgeType.TREE, parent, child, null); - if (e == null) { - String canBeLinked = obj.getMetadata(ObjectMetadata.CAN_BE_LINKED); - if (canBeLinked != null && canBeLinked.equals("true")) { - Loader ldrForCntxt = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, getVerForContext(requestContext)); - boolean isFirst = !this.engine.getQueryBuilder(ldrForCntxt, parent).createEdgeTraversal(EdgeType.TREE, parent, obj).hasNext(); - if (isFirst) { - child.property(AAIProperties.LINKED, true); - } - } - edgeSer.addTreeEdge(this.engine.asAdmin().getTraversalSource(), parent, child); - } - return child; - - } - - private SchemaVersion getVerForContext(String requestContext) { - Pattern pattern = Pattern.compile("v[0-9]+"); - Matcher m = pattern.matcher(requestContext); - if (!m.find()) { - return this.version; - } else { - return new SchemaVersion(requestContext); - } - } - - /** - * Db to object. - * - * @param vertices the vertices - * @param obj the obj - * @param depth the depth - * @param cleanUp the clean up - * @return the introspector - * @throws AAIException the AAI exception - * @throws IllegalAccessException the illegal access exception - * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws SecurityException the security exception - * @throws InstantiationException the instantiation exception - * @throws NoSuchMethodException the no such method exception - * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws MalformedURLException the malformed URL exception - * @throws AAIUnknownObjectException - * @throws URISyntaxException - */ - public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly, String cleanUp) throws UnsupportedEncodingException, AAIException { - final int internalDepth; - if (depth == Integer.MAX_VALUE) { - internalDepth = depth--; - } else { - internalDepth = depth; - } - StopWatch.conditionalStart(); - if (vertices.size() > 1 && !obj.isContainer()) { - dbTimeMsecs += StopWatch.stopIfStarted(); - throw new AAIException("AAI_6136", "query object mismatch: this object cannot hold multiple items." + obj.getDbName()); - } else if (obj.isContainer()) { - final List getList; - String listProperty = null; - for (String property : obj.getProperties()) { - if (obj.isListType(property) && obj.isComplexGenericType(property)) { - listProperty = property; - break; - } - } - final String propertyName = listProperty; - getList = (List)obj.getValue(listProperty); - - /* This is an experimental multithreading experiment - * on get alls. - */ - ExecutorService pool = GetAllPool.getInstance().getPool(); - - List<Future<Object>> futures = new ArrayList<>(); - - QueryEngine tgEngine = this.engine.getQueryEngine(); - for (Vertex v : vertices) { - - AaiCallable<Object> task = new AaiCallable<Object>() { - @Override - public Object process() throws UnsupportedEncodingException, AAIException { - Set<Vertex> seen = new HashSet<>(); - Introspector childObject; - try { - childObject = obj.newIntrospectorInstanceOfNestedProperty(propertyName); - } catch (AAIUnknownObjectException e) { - throw e; - } - Tree<Element> tree = tgEngine.findSubGraph(v, internalDepth, nodeOnly); - TreeBackedVertex treeVertex = new TreeBackedVertex(v, tree); - try { - dbToObject(childObject, treeVertex, seen, internalDepth, nodeOnly, cleanUp); - } catch (UnsupportedEncodingException e) { - throw e; - } catch (AAIException e) { - throw e; - } - return childObject.getUnderlyingObject(); - //getList.add(childObject.getUnderlyingObject()); - } - }; - futures.add(pool.submit(task)); - } - - for (Future<Object> future : futures) { - try { - getList.add(future.get()); - } catch (ExecutionException e) { - dbTimeMsecs += StopWatch.stopIfStarted(); - throw new AAIException("AAI_4000", e); - } catch (InterruptedException e) { - dbTimeMsecs += StopWatch.stopIfStarted(); - throw new AAIException("AAI_4000", e); - } - } - } else if (vertices.size() == 1) { - Set<Vertex> seen = new HashSet<>(); - Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(vertices.get(0), depth, nodeOnly); - TreeBackedVertex treeVertex = new TreeBackedVertex(vertices.get(0), tree); - dbToObject(obj, treeVertex, seen, depth, nodeOnly, cleanUp); - } else { - //obj = null; - } - - dbTimeMsecs += StopWatch.stopIfStarted(); - return obj; - } - - /** - * Db to object. - * - * @param obj the obj - * @param v the v - * @param seen the seen - * @param depth the depth - * @param cleanUp the clean up - * @return the introspector - * @throws IllegalAccessException the illegal access exception - * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws SecurityException the security exception - * @throws InstantiationException the instantiation exception - * @throws NoSuchMethodException the no such method exception - * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIException the AAI exception - * @throws MalformedURLException the malformed URL exception - * @throws AAIUnknownObjectException - * @throws URISyntaxException - */ - private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly, String cleanUp) throws AAIException, UnsupportedEncodingException { - - if (depth < 0) { - return null; - } - depth--; - seen.add(v); - - boolean modified = false; - for (String property : obj.getProperties(PropertyPredicates.isVisible())) { - List<Object> getList = null; - Vertex[] vertices = null; - - if (!(obj.isComplexType(property) || obj.isListType(property))) { - this.copySimpleProperty(property, obj, v); - modified = true; - } else { - if (obj.isComplexType(property)) { - /* container case */ - - if (!property.equals("relationship-list") && depth >= 0) { - Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property); - Object result = dbToObject(argumentObject, v, seen, depth+1, nodeOnly, cleanUp); - if (result != null) { - obj.setValue(property, argumentObject.getUnderlyingObject()); - modified = true; - } - } else if (property.equals("relationship-list") && !nodeOnly){ - /* relationships need to be handled correctly */ - Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property); - relationshipList = createRelationshipList(v, relationshipList, cleanUp); - if (relationshipList != null) { - modified = true; - obj.setValue(property, relationshipList.getUnderlyingObject()); - modified = true; - } - - } - } else if (obj.isListType(property)) { - - if (property.equals("any")) { - continue; - } - String genericType = obj.getGenericTypeClass(property).getSimpleName(); - if (obj.isComplexGenericType(property) && depth >= 0) { - final String childDbName = convertFromCamelCase(genericType); - String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null); - EdgeRule rule; - - try { - rule = edgeRules.getRule(new EdgeRuleQuery.Builder(vType, childDbName).edgeType(EdgeType.TREE).build()); - } catch (EdgeRuleNotFoundException e) { - throw new NoEdgeRuleFoundException(e); - } catch (AmbiguousRuleChoiceException e) { - throw new MultipleEdgeRuleFoundException(e); - } - if (!rule.getContains().equals(AAIDirection.NONE.toString())) { - //vertices = this.queryEngine.findRelatedVertices(v, Direction.OUT, rule.getLabel(), childDbName); - Direction ruleDirection = rule.getDirection(); - Iterator<Vertex> itr = v.vertices(ruleDirection, rule.getLabel()); - List<Vertex> verticesList = (List<Vertex>)IteratorUtils.toList(itr); - itr = verticesList.stream().filter(item -> { - return item.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName); - }).iterator(); - if (itr.hasNext()) { - getList = (List<Object>)obj.getValue(property); - } - int processed = 0; - int removed = 0; - while (itr.hasNext()) { - Vertex childVertex = itr.next(); - if (!seen.contains(childVertex)) { - Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property); - - Object result = dbToObject(argumentObject, childVertex, seen, depth, nodeOnly, cleanUp); - if (result != null) { - getList.add(argumentObject.getUnderlyingObject()); - } - - processed++; - } else { - removed++; - LOGGER.warn("Cycle found while serializing vertex id={}", childVertex.id().toString()); - } - } - if (processed == 0) { - //vertices were all seen, reset the list - getList = null; - } - if (processed > 0) { - modified = true; - } - } - } else if (obj.isSimpleGenericType(property)) { - List<Object> temp = this.engine.getListProperty(v, property); - if (temp != null) { - getList = (List<Object>)obj.getValue(property); - getList.addAll(temp); - modified = true; - } - - } - - } - - } - } - - //no changes were made to this obj, discard the instance - if (!modified) { - return null; - } - this.enrichData(obj, v); - return obj; - - } - - - public Introspector getVertexProperties(Vertex v) throws AAIException, UnsupportedEncodingException { - String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null); - if (nodeType == null) { - throw new AAIException("AAI_6143"); - } - - Introspector obj = this.latestLoader.introspectorFromName(nodeType); - Set<Vertex> seen = new HashSet<>(); - int depth = 0; - String cleanUp = "false"; - boolean nodeOnly = true; - StopWatch.conditionalStart(); - this.dbToObject(obj, v, seen, depth, nodeOnly, cleanUp); - dbTimeMsecs += StopWatch.stopIfStarted(); - return obj; - - } - public Introspector getLatestVersionView(Vertex v) throws AAIException, UnsupportedEncodingException { - String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null); - if (nodeType == null) { - throw new AAIException("AAI_6143"); - } - Introspector obj = this.latestLoader.introspectorFromName(nodeType); - Set<Vertex> seen = new HashSet<>(); - int depth = AAIProperties.MAXIMUM_DEPTH; - String cleanUp = "false"; - boolean nodeOnly = false; - StopWatch.conditionalStart(); - Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(v, depth, nodeOnly); - TreeBackedVertex treeVertex = new TreeBackedVertex(v, tree); - this.dbToObject(obj, treeVertex, seen, depth, nodeOnly, cleanUp); - dbTimeMsecs += StopWatch.stopIfStarted(); - return obj; - } - /** - * Copy simple property. - * - * @param property the property - * @param obj the obj - * @param v the v - * @throws InstantiationException the instantiation exception - * @throws IllegalAccessException the illegal access exception - * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws NoSuchMethodException the no such method exception - * @throws SecurityException the security exception - */ - private void copySimpleProperty(String property, Introspector obj, Vertex v) { - - final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property); - String dbPropertyName = property; - - if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) { - dbPropertyName = metadata.get(PropertyMetadata.DB_ALIAS); - } - - - - final Object temp = v.<Object>property(dbPropertyName).orElse(null); - if (temp != null) { - obj.setValue(property, temp); - } - } - - /** - * Simple db to object. - * - * @param obj the obj - * @param v the v - * @throws InstantiationException the instantiation exception - * @throws IllegalAccessException the illegal access exception - * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws NoSuchMethodException the no such method exception - * @throws SecurityException the security exception - */ - private void simpleDbToObject (Introspector obj, Vertex v) { - for (String property : obj.getProperties()) { - - - if (!(obj.isComplexType(property) || obj.isListType(property))) { - this.copySimpleProperty(property, obj, v); - } - } - } - - /** - * Creates the relationship list. - * - * @param v the v - * @param obj the obj - * @param cleanUp the clean up - * @return the object - * @throws InstantiationException the instantiation exception - * @throws IllegalAccessException the illegal access exception - * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws NoSuchMethodException the no such method exception - * @throws SecurityException the security exception - * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIException the AAI exception - * @throws MalformedURLException the malformed URL exception - * @throws URISyntaxException - */ - private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp) throws UnsupportedEncodingException, AAIException { - - List<Vertex> cousins = this.engine.getQueryEngine().findCousinVertices(v); - - List<Object> relationshipObjList = obj.getValue("relationship"); - - for (Vertex cousin : cousins) { - if (obj.getVersion().compareTo(schemaVersions.getEdgeLabelVersion()) >= 0) { - List<Edge> edges = this.getEdgesBetween(EdgeType.COUSIN, v, cousin); - for (Edge e : edges) { - Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship"); - Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, e); - if (result != null) { - relationshipObjList.add(result); - } - } - } else { - Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship"); - Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, null); - if (result != null) { - relationshipObjList.add(result); - } - } - - } - - if (relationshipObjList.isEmpty()) { - return null; - } else { - return obj; - } - } - - /** - * Process edge relationship. - * - * @param relationshipObj the relationship obj - * @param edge the edge - * @param cleanUp the clean up - * @return the object - * @throws InstantiationException the instantiation exception - * @throws IllegalAccessException the illegal access exception - * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws NoSuchMethodException the no such method exception - * @throws SecurityException the security exception - * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIException the AAI exception - * @throws MalformedURLException the malformed URL exception - * @throws AAIUnknownObjectException - * @throws URISyntaxException - */ - private Object processEdgeRelationship(Introspector relationshipObj, Vertex cousin, String cleanUp, Edge edge) throws UnsupportedEncodingException, AAIUnknownObjectException { - - - //we must look up all parents in this case because we need to compute name-properties - //we cannot used the cached aaiUri to perform this action currently - Optional<Pair<Vertex, List<Introspector>>> tuple = this.getParents(relationshipObj.getLoader(), cousin, "true".equals(cleanUp)); - //damaged vertex found, ignore - if (!tuple.isPresent()) { - return null; - } - List<Introspector> list = tuple.get().getValue1(); - URI uri = this.getURIFromList(list); - - URIToRelationshipObject uriParser = null; - Introspector result = null; - try { - uriParser = new URIToRelationshipObject(relationshipObj.getLoader(), uri, this.baseURL); - result = uriParser.getResult(); - } catch (AAIException | URISyntaxException e) { - LOGGER.error("Error while processing edge relationship in version " + relationshipObj.getVersion() + " (bad vertex ID=" + tuple.get().getValue0().id().toString() + ": " - + e.getMessage() + " " + LogFormatTools.getStackTop(e)); - if ("true".equals(cleanUp)) { - this.deleteWithTraversal(tuple.get().getValue0()); - } - return null; - } - if (!list.isEmpty()) { - this.addRelatedToProperty(result, list.get(0)); - } - - if (edge != null && result.hasProperty("relationship-label")) { - result.setValue("relationship-label", edge.label()); - } - - return result.getUnderlyingObject(); - } - - /** - * Gets the URI for vertex. - * - * @param v the v - * @return the URI for vertex - * @throws InstantiationException the instantiation exception - * @throws IllegalAccessException the illegal access exception - * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws NoSuchMethodException the no such method exception - * @throws SecurityException the security exception - * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIUnknownObjectException - */ - public URI getURIForVertex(Vertex v) throws UnsupportedEncodingException { - - return getURIForVertex(v, false); - } - - public URI getURIForVertex(Vertex v, boolean overwrite) throws UnsupportedEncodingException { - URI uri = UriBuilder.fromPath("/unknown-uri").build(); - - String aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null); - - if (aaiUri != null && !overwrite) { - uri = UriBuilder.fromPath(aaiUri).build(); - } else { - StopWatch.conditionalStart(); - Optional<Pair<Vertex, List<Introspector>>> tuple = this.getParents(this.loader, v, false); - dbTimeMsecs += StopWatch.stopIfStarted(); - if (tuple.isPresent()) { - List<Introspector> list = tuple.get().getValue1(); - uri = this.getURIFromList(list); - } - - - } - return uri; - } - /** - * Gets the URI from list. - * - * @param list the list - * @return the URI from list - * @throws UnsupportedEncodingException the unsupported encoding exception - */ - private URI getURIFromList(List<Introspector> list) throws UnsupportedEncodingException { - String uri = ""; - StringBuilder sb = new StringBuilder(); - for (Introspector i : list) { - sb.insert(0, i.getURI()); - } - - uri = sb.toString(); - return UriBuilder.fromPath(uri).build(); - } - - /** - * Gets the parents. - * - * @param start the start - * @param removeDamaged the remove damaged - * @return the parents - * @throws InstantiationException the instantiation exception - * @throws IllegalAccessException the illegal access exception - * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws NoSuchMethodException the no such method exception - * @throws SecurityException the security exception - * @throws AAIUnknownObjectException - */ - private Optional<Pair<Vertex, List<Introspector>>> getParents(Loader loader, Vertex start, boolean removeDamaged) { - - List<Vertex> results = this.engine.getQueryEngine().findParents(start); - List<Introspector> objs = new ArrayList<>(); - boolean shortCircuit = false; - for (Vertex v : results) { - String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null); - Introspector obj = null; - //vertex on the other end of this edge is bad - if (nodeType == null) { - //log something here about what was found and that it was removed - ErrorLogHelper.logError("AAI-6143", "Found a damaged parent vertex " + v.id().toString()); - if (removeDamaged) { - this.deleteWithTraversal(v); - } - shortCircuit = true; - } else { - try { - obj = loader.introspectorFromName(nodeType); - } catch (AAIUnknownObjectException e) { - LOGGER.info("attempted to create node type " + nodeType + " but we do not understand it for version: " + loader.getVersion()); - obj = null; - } - } - - if (obj == null) { - //can't make a valid path because we don't understand this object - // don't include it - } else { - this.simpleDbToObject(obj, v); - objs.add(obj); - } - } - - //stop processing and don't return anything for this bad vertex - if (shortCircuit) { - return Optional.empty(); - } - - return Optional.of(new Pair<>(results.get(results.size()-1), objs)); - } - - /** - * Adds the r - * @throws AAIUnknownObjectException - * @throws IllegalArgumentException elated to property. - * - * @param relationship the relationship - * @param child the throws IllegalArgumentException, AAIUnknownObjectException child - */ - public void addRelatedToProperty(Introspector relationship, Introspector child) throws AAIUnknownObjectException { - String nameProps = child.getMetadata(ObjectMetadata.NAME_PROPS); - List<Introspector> relatedToProperties = new ArrayList<>(); - - if (nameProps != null) { - String[] props = nameProps.split(","); - for (String prop : props) { - Introspector relatedTo = relationship.newIntrospectorInstanceOfNestedProperty("related-to-property"); - relatedTo.setValue("property-key", child.getDbName() + "." + prop); - relatedTo.setValue("property-value", child.getValue(prop)); - relatedToProperties.add(relatedTo); - } - } - - if (!relatedToProperties.isEmpty()) { - List relatedToList = (List)relationship.getValue("related-to-property"); - for (Introspector obj : relatedToProperties) { - relatedToList.add(obj.getUnderlyingObject()); - } - } - - } - - /** - * Creates the edge. - * - * @param relationship the relationship - * @param inputVertex the input vertex - * @return true, if successful - * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIException the AAI exception - */ - public boolean createEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException { - - Vertex relatedVertex = null; - StopWatch.conditionalStart(); - QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship); - - String label = null; - if (relationship.hasProperty("relationship-label")) { - label = relationship.getValue("relationship-label"); - } - - List<Vertex> results = parser.getQueryBuilder().toList(); - if (results.isEmpty()) { - dbTimeMsecs += StopWatch.stopIfStarted(); - AAIException e = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri()); - e.getTemplateVars().add(parser.getResultType()); - e.getTemplateVars().add(parser.getUri().toString()); - throw e; - } else { - //still an issue if there's more than one - relatedVertex = results.get(0); - } - - if (relatedVertex != null) { - - Edge e; - try { - e = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label); - if (e == null) { - edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), inputVertex, relatedVertex, label); - } else { - //attempted to link two vertexes already linked - } - } finally { - dbTimeMsecs += StopWatch.stopIfStarted(); - } - } - - dbTimeMsecs += StopWatch.stopIfStarted(); - return true; - } - - /** - * Gets all the edges between of the type. - * - * @param aVertex the out vertex - * @param bVertex the in vertex - * @return the edges between - * @throws AAIException the AAI exception - * @throws NoEdgeRuleFoundException - */ - private List<Edge> getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex) { - - List<Edge> result = new ArrayList<>(); - - if (bVertex != null) { - GraphTraversal<Vertex, Edge> findEdgesBetween = null; - findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE(); - if (EdgeType.TREE.equals(type)) { - findEdgesBetween = findEdgesBetween - .not( - __.or( - __.has(EdgeProperty.CONTAINS.toString(), "NONE"), - __.has(EdgeField.PRIVATE.toString(), true) - ) - ); - } else { - findEdgesBetween = findEdgesBetween - .has(EdgeProperty.CONTAINS.toString(), "NONE") - .not( - __.has(EdgeField.PRIVATE.toString(), true) - ); - } - findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id())); - result = findEdgesBetween.toList(); - } - - return result; - } - /** - * Gets all the edges between the vertexes with the label and type. - * - * @param aVertex the out vertex - * @param bVertex the in vertex - * @param label - * @return the edges between - * @throws AAIException the AAI exception - */ - private List<Edge> getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException { - - List<Edge> result = new ArrayList<>(); - - if (bVertex != null) { - String aType = aVertex.<String>property(AAIProperties.NODE_TYPE).value(); - String bType = bVertex.<String>property(AAIProperties.NODE_TYPE).value(); - EdgeRuleQuery q = new EdgeRuleQuery.Builder(aType, bType).edgeType(type).label(label).build(); - EdgeRule rule; - try { - rule = edgeRules.getRule(q); - } catch (EdgeRuleNotFoundException e) { - throw new NoEdgeRuleFoundException(e); - } catch (AmbiguousRuleChoiceException e) { - throw new MultipleEdgeRuleFoundException(e); - } - List<Edge> edges = this.getEdgesBetween(type, aVertex, bVertex); - for (Edge edge : edges) { - if (edge.label().equals(rule.getLabel())) { - result.add(edge); - } - } - } - - return result; - } - - /** - * Gets the edge between with the label and edge type. - * - * @param aVertex the out vertex - * @param bVertex the in vertex - * @param label - * @return the edge between - * @throws AAIException the AAI exception - * @throws NoEdgeRuleFoundException - */ - public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException { - - StopWatch.conditionalStart(); - if (bVertex != null) { - - List<Edge> edges = this.getEdgesBetween(type, aVertex, bVertex, label); - - if (!edges.isEmpty()) { - dbTimeMsecs += StopWatch.stopIfStarted(); - return edges.get(0); - } - - } - dbTimeMsecs += StopWatch.stopIfStarted(); - return null; - } - public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException { - return this.getEdgeBetween(type, aVertex, bVertex, null); - } - - - /** - * Delete edge. - * - * @param relationship the relationship - * @param inputVertex the input vertex - * @return true, if successful - * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIException the AAI exception - */ - public boolean deleteEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException { - - Vertex relatedVertex = null; - StopWatch.conditionalStart(); - QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship); - - List<Vertex> results = parser.getQueryBuilder().toList(); - - String label = null; - if (relationship.hasProperty("relationship-label")) { - label = relationship.getValue("relationship-label"); - } - - if (results.isEmpty()) { - dbTimeMsecs += StopWatch.stopIfStarted(); - return false; - } - - relatedVertex = results.get(0); - Edge edge; - try { - edge = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label); - } catch (NoEdgeRuleFoundException e) { - dbTimeMsecs += StopWatch.stopIfStarted(); - throw new AAIException("AAI_6129", e); - } - if (edge != null) { - edge.remove(); - dbTimeMsecs += StopWatch.stopIfStarted(); - return true; - } else { - dbTimeMsecs += StopWatch.stopIfStarted(); - return false; - } - - } - - /** - * Delete items with traversal. - * - * @param vertexes the vertexes - * @throws IllegalStateException the illegal state exception - */ - public void deleteItemsWithTraversal(List<Vertex> vertexes) throws IllegalStateException { - - for (Vertex v : vertexes) { + + private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(DBSerializer.class); + + private final TransactionalGraphEngine engine; + private final String sourceOfTruth; + private final ModelType introspectionType; + private final SchemaVersion version; + private final Loader latestLoader; + private EdgeSerializer edgeSer; + private EdgeIngestor edgeRules; + private final Loader loader; + private final String baseURL; + private double dbTimeMsecs = 0; + private long currentTimeMillis; + + private SchemaVersions schemaVersions; + private Set<String> namedPropNodes; + /** + * Instantiates a new DB serializer. + * + * @param version the version + * @param engine the engine + * @param introspectionType the introspection type + * @param sourceOfTruth the source of truth + * @throws AAIException + */ + public DBSerializer(SchemaVersion version, TransactionalGraphEngine engine, ModelType introspectionType, String sourceOfTruth) throws AAIException { + this.engine = engine; + this.sourceOfTruth = sourceOfTruth; + this.introspectionType = introspectionType; + this.schemaVersions = SpringContextAware.getBean(SchemaVersions.class); + SchemaVersion LATEST = schemaVersions.getDefaultVersion(); + this.latestLoader = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, LATEST); + this.version = version; + this.loader = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version); + this.namedPropNodes = this.latestLoader.getNamedPropNodes(); + this.baseURL = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE); + this.currentTimeMillis = System.currentTimeMillis(); + initBeans(); + } + + private void initBeans() { + //TODO proper spring wiring, but that requires a lot of refactoring so for now we have this + ApplicationContext ctx = SpringContextAware.getApplicationContext(); + EdgeIngestor ei = ctx.getBean(EdgeIngestor.class); + setEdgeIngestor(ei); + EdgeSerializer es = ctx.getBean(EdgeSerializer.class); + setEdgeSerializer(es); + } + + private void backupESInit() { + setEdgeSerializer(new EdgeSerializer(this.edgeRules)); + } + + public void setEdgeSerializer(EdgeSerializer edgeSer) { + this.edgeSer = edgeSer; + } + + public EdgeSerializer getEdgeSeriailizer() { + return this.edgeSer; + } + + public void setEdgeIngestor(EdgeIngestor ei) { + this.edgeRules = ei; + } + + public EdgeIngestor getEdgeIngestor() { + return this.edgeRules; + } + + /** + * Touch standard vertex properties. + * + * @param v the v + * @param isNewVertex the is new vertex + */ + public void touchStandardVertexProperties(Vertex v, boolean isNewVertex) { + String timeNowInSec = Long.toString(currentTimeMillis); + + if (isNewVertex) { + v.property(AAIProperties.SOURCE_OF_TRUTH, this.sourceOfTruth); + v.property(AAIProperties.CREATED_TS, timeNowInSec); + v.property(AAIProperties.AAI_UUID, UUID.randomUUID().toString()); + } + v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec); + v.property(AAIProperties.LAST_MOD_TS, timeNowInSec); + v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth); + + } + + private void touchStandardVertexProperties(String nodeType, Vertex v, boolean isNewVertex) { + + v.property(AAIProperties.NODE_TYPE, nodeType); + touchStandardVertexProperties(v, isNewVertex); + + } + + + /** + * Creates the new vertex. + * + * @param wrappedObject the wrapped object + * @return the vertex + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIException the AAI exception + */ + public Vertex createNewVertex(Introspector wrappedObject) { + Vertex v; + try { + StopWatch.conditionalStart(); + v = this.engine.tx().addVertex(); + touchStandardVertexProperties(wrappedObject.getDbName(), v, true); + } finally { + dbTimeMsecs += StopWatch.stopIfStarted(); + } + return v; + } + + /** + * Trim class name. + * + * @param className the class name + * @return the string + */ + /* + * Removes the classpath from a class name + */ + public String trimClassName(String className) { + String returnValue = ""; + + if (className.lastIndexOf('.') == -1) { + return className; + } + returnValue = className.substring(className.lastIndexOf('.') + 1, className.length()); + + return returnValue; + } + + /** + * Serialize to db. + * + * @param obj the obj + * @param v the v + * @param uriQuery the uri query + * @param identifier the identifier + * @throws SecurityException the security exception + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws InstantiationException the instantiation exception + * @throws InterruptedException the interrupted exception + * @throws NoSuchMethodException the no such method exception + * @throws AAIException the AAI exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIUnknownObjectException + */ + public void serializeToDb(Introspector obj, Vertex v, QueryParser uriQuery, String identifier, String requestContext) throws AAIException, UnsupportedEncodingException { + StopWatch.conditionalStart(); + try { + if (uriQuery.isDependent()) { + //try to find the parent + List<Vertex> vertices = uriQuery.getQueryBuilder().getParentQuery().toList(); + if (!vertices.isEmpty()) { + Vertex parent = vertices.get(0); + this.reflectDependentVertex(parent, v, obj, requestContext); + } else { + dbTimeMsecs += StopWatch.stopIfStarted(); + throw new AAIException("AAI_6114", "No parent Node of type " + uriQuery.getParentResultType() + " for " + identifier); + } + } else { + serializeSingleVertex(v, obj, requestContext); + } + + } catch (SchemaViolationException e) { + dbTimeMsecs += StopWatch.stopIfStarted(); + throw new AAIException("AAI_6117", e); + } + dbTimeMsecs += StopWatch.stopIfStarted(); + } + + public void serializeSingleVertex(Vertex v, Introspector obj, String requestContext) throws UnsupportedEncodingException, AAIException { + StopWatch.conditionalStart(); + try { + boolean isTopLevel = obj.isTopLevel(); + if (isTopLevel) { + addUriIfNeeded(v, obj.getURI()); + } + + processObject(obj, v, requestContext); + if (!isTopLevel) { + URI uri = this.getURIForVertex(v); + URIParser parser = new URIParser(this.loader, uri); + if (parser.validate()) { + addUriIfNeeded(v, uri.toString()); + } + } + } catch (SchemaViolationException e) { + throw new AAIException("AAI_6117", e); + } finally { + dbTimeMsecs += StopWatch.stopIfStarted(); + } + } + + private void addUriIfNeeded(Vertex v, String uri) { + VertexProperty<String> uriProp = v.property(AAIProperties.AAI_URI); + if (!uriProp.isPresent() || (uriProp.isPresent() && !uriProp.value().equals(uri))) { + v.property(AAIProperties.AAI_URI, uri); + } + } + + /** + * Process object. + * + * @param <T> the generic type + * @param obj the obj + * @param v the v + * @return the list + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws InstantiationException the instantiation exception + * @throws NoSuchMethodException the no such method exception + * @throws SecurityException the security exception + * @throws AAIException the AAI exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIUnknownObjectException + */ + /* + * Helper method for reflectToDb + * Handles all the property setting + */ + private <T> List<Vertex> processObject(Introspector obj, Vertex v, String requestContext) throws UnsupportedEncodingException, AAIException { + Set<String> properties = new LinkedHashSet<>(obj.getProperties()); + properties.remove(AAIProperties.RESOURCE_VERSION); + List<Vertex> dependentVertexes = new ArrayList<>(); + List<Vertex> processedVertexes = new ArrayList<>(); + boolean isComplexType = false; + boolean isListType = false; + if (!obj.isContainer()) { + this.touchStandardVertexProperties(obj.getDbName(), v, false); + } + this.executePreSideEffects(obj, v); + for (String property : properties) { + Object value = null; + final String propertyType; + propertyType = obj.getType(property); + isComplexType = obj.isComplexType(property); + isListType = obj.isListType(property); + value = obj.getValue(property); + + if (!(isComplexType || isListType)) { + boolean canModify = this.canModify(obj, property, requestContext); + + if (canModify) { + final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property); + String dbProperty = property; + if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) { + dbProperty = metadata.get(PropertyMetadata.DB_ALIAS); + } + if (metadata.containsKey(PropertyMetadata.DATA_LINK)) { + //data linked properties are ephemeral + //they are populated dynamically on GETs + continue; + } + if (value != null) { + if (!value.equals(v.property(dbProperty).orElse(null))) { + if (propertyType.toLowerCase().contains(".long")) { + v.property(dbProperty, new Integer(((Long) value).toString())); + } else { + v.property(dbProperty, value); + } + } + } else { + v.property(dbProperty).remove(); + } + } + } else if (isListType) { + List<Object> list = (List<Object>) value; + if (obj.isComplexGenericType(property)) { + if (list != null) { + for (Object o : list) { + Introspector child = IntrospectorFactory.newInstance(this.introspectionType, o); + child.setURIChain(obj.getURI()); + processedVertexes.add(reflectDependentVertex(v, child, requestContext)); + } + } + } else { + //simple list case + engine.setListProperty(v, property, list); + } + } else { + //method.getReturnType() is not 'simple' then create a vertex and edge recursively returning an edge back to this method + if (value != null) { //effectively ignore complex properties not included in the object we're processing + if (value.getClass().isArray()) { + + int length = Array.getLength(value); + for (int i = 0; i < length; i++) { + Object arrayElement = Array.get(value, i); + Introspector child = IntrospectorFactory.newInstance(this.introspectionType, arrayElement); + child.setURIChain(obj.getURI()); + processedVertexes.add(reflectDependentVertex(v, child, requestContext)); + + } + } else if (!property.equals("relationship-list")) { + // container case + Introspector introspector = IntrospectorFactory.newInstance(this.introspectionType, value); + if (introspector.isContainer()) { + dependentVertexes.addAll(this.engine.getQueryEngine().findChildrenOfType(v, introspector.getChildDBName())); + introspector.setURIChain(obj.getURI()); + + processedVertexes.addAll(processObject(introspector, v, requestContext)); + + } else { + dependentVertexes.addAll(this.engine.getQueryEngine().findChildrenOfType(v, introspector.getDbName())); + processedVertexes.add(reflectDependentVertex(v, introspector, requestContext)); + + } + } else if (property.equals("relationship-list")) { + handleRelationships(obj, v); + } + } + } + } + this.writeThroughDefaults(v, obj); + /* handle those vertexes not touched */ + for (Vertex toBeRemoved : processedVertexes) { + dependentVertexes.remove(toBeRemoved); + } + this.deleteItemsWithTraversal(dependentVertexes); + + this.executePostSideEffects(obj, v); + return processedVertexes; + } + + /** + * Handle relationships. + * + * @param obj the obj + * @param vertex the vertex + * @throws SecurityException the security exception + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIException the AAI exception + */ + /* + * Handles the explicit relationships defined for an obj + */ + private void handleRelationships(Introspector obj, Vertex vertex) throws UnsupportedEncodingException, AAIException { + + + Introspector wrappedRl = obj.getWrappedValue("relationship-list"); + processRelationshipList(wrappedRl, vertex); + + + } + + + /** + * Process relationship list. + * + * @param wrapped the wrapped + * @param v the v + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIException the AAI exception + */ + private void processRelationshipList(Introspector wrapped, Vertex v) throws UnsupportedEncodingException, AAIException { + + List<Object> relationships = (List<Object>) wrapped.getValue("relationship"); + + List<Triplet<Vertex, Vertex, String>> addEdges = new ArrayList<>(); + List<Edge> existingEdges = this.engine.getQueryEngine().findEdgesForVersion(v, wrapped.getLoader()); + + for (Object relationship : relationships) { + Edge e = null; + Vertex cousinVertex = null; + String label = null; + Introspector wrappedRel = IntrospectorFactory.newInstance(this.introspectionType, relationship); + QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(wrappedRel); + + if (wrappedRel.hasProperty("relationship-label")) { + label = wrappedRel.getValue("relationship-label"); + } + + List<Vertex> results = parser.getQueryBuilder().toList(); + if (results.isEmpty()) { + final AAIException ex = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri()); + ex.getTemplateVars().add(parser.getResultType()); + ex.getTemplateVars().add(parser.getUri().toString()); + throw ex; + } else { + //still an issue if there's more than one + cousinVertex = results.get(0); + } + + if (cousinVertex != null) { + String vType = (String) v.property(AAIProperties.NODE_TYPE).value(); + String cousinType = (String) cousinVertex.property(AAIProperties.NODE_TYPE).value(); + EdgeRuleQuery.Builder baseQ = new EdgeRuleQuery.Builder(vType, cousinType).label(label); + + + if (!edgeRules.hasRule(baseQ.build())) { + throw new AAIException("AAI_6120", "No EdgeRule found for passed nodeTypes: " + v.property(AAIProperties.NODE_TYPE).value().toString() + ", " + + cousinVertex.property(AAIProperties.NODE_TYPE).value().toString() + (label != null ? (" with label " + label) : "") + "."); + } else if (edgeRules.hasRule(baseQ.edgeType(EdgeType.TREE).build()) && !edgeRules.hasRule(baseQ.edgeType(EdgeType.COUSIN).build())) { + throw new AAIException("AAI_6145"); + } + + e = this.getEdgeBetween(EdgeType.COUSIN, v, cousinVertex, label); + + if (e == null) { + addEdges.add(new Triplet<>(v, cousinVertex, label)); + } else { + existingEdges.remove(e); + } + } + } + + for (Edge edge : existingEdges) { + edge.remove(); + } + for (Triplet<Vertex, Vertex, String> triplet : addEdges) { + try { + edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), triplet.getValue0(), triplet.getValue1(), triplet.getValue2()); + } catch (NoEdgeRuleFoundException e) { + throw new AAIException("AAI_6129", e); + } + } + + } + + /** + * Write through defaults. + * + * @param v the v + * @param obj the obj + * @throws AAIUnknownObjectException + */ + private void writeThroughDefaults(Vertex v, Introspector obj) throws AAIUnknownObjectException { + Introspector latest = this.latestLoader.introspectorFromName(obj.getName()); + if (latest != null) { + Set<String> required = latest.getRequiredProperties(); + + for (String field : required) { + String defaultValue = null; + Object vertexProp = null; + defaultValue = latest.getPropertyMetadata(field).get(PropertyMetadata.DEFAULT_VALUE); + if (defaultValue != null) { + vertexProp = v.<Object>property(field).orElse(null); + if (vertexProp == null) { + v.property(field, defaultValue); + } + } + } + } + + } + + + /** + * Reflect dependent vertex. + * + * @param v the v + * @param dependentObj the dependent obj + * @return the vertex + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws InstantiationException the instantiation exception + * @throws NoSuchMethodException the no such method exception + * @throws SecurityException the security exception + * @throws AAIException the AAI exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIUnknownObjectException + */ + private Vertex reflectDependentVertex(Vertex v, Introspector dependentObj, String requestContext) throws AAIException, UnsupportedEncodingException { + + //QueryParser p = this.engine.getQueryBuilder().createQueryFromURI(obj.getURI()); + //List<Vertex> items = p.getQuery().toList(); + QueryBuilder<Vertex> query = this.engine.getQueryBuilder(v); + query.createEdgeTraversal(EdgeType.TREE, v, dependentObj); + query.createKeyQuery(dependentObj); + + List<Vertex> items = query.toList(); + + Vertex dependentVertex = null; + if (items.size() == 1) { + dependentVertex = items.get(0); + this.verifyResourceVersion("update", dependentObj.getDbName(), dependentVertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), (String) dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String) dependentObj.getURI()); + } else { + this.verifyResourceVersion("create", dependentObj.getDbName(), "", (String) dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String) dependentObj.getURI()); + dependentVertex = createNewVertex(dependentObj); + } + + return reflectDependentVertex(v, dependentVertex, dependentObj, requestContext); + + } + + /** + * Reflect dependent vertex. + * + * @param parent the parent + * @param child the child + * @param obj the obj + * @return the vertex + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws InstantiationException the instantiation exception + * @throws NoSuchMethodException the no such method exception + * @throws SecurityException the security exception + * @throws AAIException the AAI exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIUnknownObjectException + */ + private Vertex reflectDependentVertex(Vertex parent, Vertex child, Introspector obj, String requestContext) throws AAIException, UnsupportedEncodingException { + + String parentUri = parent.<String>property(AAIProperties.AAI_URI).orElse(null); + if (parentUri != null) { + String uri; + uri = obj.getURI(); + addUriIfNeeded(child, parentUri + uri); + } + processObject(obj, child, requestContext); + + Edge e; + e = this.getEdgeBetween(EdgeType.TREE, parent, child, null); + + if (e == null) { + String canBeLinked = obj.getMetadata(ObjectMetadata.CAN_BE_LINKED); + if (canBeLinked != null && canBeLinked.equals("true")) { + Loader ldrForCntxt = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, getVerForContext(requestContext)); + boolean isFirst = !this.engine.getQueryBuilder(ldrForCntxt, parent).createEdgeTraversal(EdgeType.TREE, parent, obj).hasNext(); + if (isFirst) { + child.property(AAIProperties.LINKED, true); + } + } + edgeSer.addTreeEdge(this.engine.asAdmin().getTraversalSource(), parent, child); + } + return child; + + } + + private SchemaVersion getVerForContext(String requestContext) { + Pattern pattern = Pattern.compile("v[0-9]+"); + Matcher m = pattern.matcher(requestContext); + if (!m.find()) { + return this.version; + } else { + return new SchemaVersion(requestContext); + } + } + + /** + * Db to object. + * + * @param vertices the vertices + * @param obj the obj + * @param depth the depth + * @param cleanUp the clean up + * @return the introspector + * @throws AAIException the AAI exception + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws SecurityException the security exception + * @throws InstantiationException the instantiation exception + * @throws NoSuchMethodException the no such method exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws MalformedURLException the malformed URL exception + * @throws AAIUnknownObjectException + * @throws URISyntaxException + */ + public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly, String cleanUp) throws UnsupportedEncodingException, AAIException { + final int internalDepth; + if (depth == Integer.MAX_VALUE) { + internalDepth = depth--; + } else { + internalDepth = depth; + } + StopWatch.conditionalStart(); + if (vertices.size() > 1 && !obj.isContainer()) { + dbTimeMsecs += StopWatch.stopIfStarted(); + throw new AAIException("AAI_6136", "query object mismatch: this object cannot hold multiple items." + obj.getDbName()); + } else if (obj.isContainer()) { + final List getList; + String listProperty = null; + for (String property : obj.getProperties()) { + if (obj.isListType(property) && obj.isComplexGenericType(property)) { + listProperty = property; + break; + } + } + final String propertyName = listProperty; + getList = (List) obj.getValue(listProperty); + + /* This is an experimental multithreading experiment + * on get alls. + */ + ExecutorService pool = GetAllPool.getInstance().getPool(); + + List<Future<Object>> futures = new ArrayList<>(); + + QueryEngine tgEngine = this.engine.getQueryEngine(); + for (Vertex v : vertices) { + + AaiCallable<Object> task = new AaiCallable<Object>() { + @Override + public Object process() throws UnsupportedEncodingException, AAIException { + Set<Vertex> seen = new HashSet<>(); + Introspector childObject; + try { + childObject = obj.newIntrospectorInstanceOfNestedProperty(propertyName); + } catch (AAIUnknownObjectException e) { + throw e; + } + try { + dbToObject(childObject, v, seen, internalDepth, nodeOnly, cleanUp); + } catch (UnsupportedEncodingException e) { + throw e; + } catch (AAIException e) { + throw e; + } + return childObject.getUnderlyingObject(); + //getList.add(childObject.getUnderlyingObject()); + } + }; + futures.add(pool.submit(task)); + } + + for (Future<Object> future : futures) { + try { + getList.add(future.get()); + } catch (ExecutionException e) { + dbTimeMsecs += StopWatch.stopIfStarted(); + throw new AAIException("AAI_4000", e); + } catch (InterruptedException e) { + dbTimeMsecs += StopWatch.stopIfStarted(); + throw new AAIException("AAI_4000", e); + } + } + } else if (vertices.size() == 1) { + Set<Vertex> seen = new HashSet<>(); + dbToObject(obj, vertices.get(0), seen, depth, nodeOnly, cleanUp); + } else { + //obj = null; + } + + dbTimeMsecs += StopWatch.stopIfStarted(); + return obj; + } + + /** + * Db to object. + * + * @param obj the obj + * @param v the v + * @param seen the seen + * @param depth the depth + * @param cleanUp the clean up + * @return the introspector + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws SecurityException the security exception + * @throws InstantiationException the instantiation exception + * @throws NoSuchMethodException the no such method exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIException the AAI exception + * @throws MalformedURLException the malformed URL exception + * @throws AAIUnknownObjectException + * @throws URISyntaxException + */ + private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly, String cleanUp) throws AAIException, UnsupportedEncodingException { + + if (depth < 0) { + return null; + } + depth--; + seen.add(v); + + boolean modified = false; + for (String property : obj.getProperties(PropertyPredicates.isVisible())) { + List<Object> getList = null; + Vertex[] vertices = null; + + if (!(obj.isComplexType(property) || obj.isListType(property))) { + this.copySimpleProperty(property, obj, v); + modified = true; + } else { + if (obj.isComplexType(property)) { + /* container case */ + + if (!property.equals("relationship-list") && depth >= 0) { + Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property); + Object result = dbToObject(argumentObject, v, seen, depth + 1, nodeOnly, cleanUp); + if (result != null) { + obj.setValue(property, argumentObject.getUnderlyingObject()); + modified = true; + } + } else if (property.equals("relationship-list") && !nodeOnly) { + /* relationships need to be handled correctly */ + Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property); + relationshipList = createRelationshipList(v, relationshipList, cleanUp); + if (relationshipList != null) { + modified = true; + obj.setValue(property, relationshipList.getUnderlyingObject()); + modified = true; + } + + } + } else if (obj.isListType(property)) { + + if (property.equals("any")) { + continue; + } + String genericType = obj.getGenericTypeClass(property).getSimpleName(); + if (obj.isComplexGenericType(property) && depth >= 0) { + final String childDbName = convertFromCamelCase(genericType); + String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null); + EdgeRule rule; + + try { + rule = edgeRules.getRule(new EdgeRuleQuery.Builder(vType, childDbName).edgeType(EdgeType.TREE).build()); + } catch (EdgeRuleNotFoundException e) { + throw new NoEdgeRuleFoundException(e); + } catch (AmbiguousRuleChoiceException e) { + throw new MultipleEdgeRuleFoundException(e); + } + if (!rule.getContains().equals(AAIDirection.NONE.toString())) { + //vertices = this.queryEngine.findRelatedVertices(v, Direction.OUT, rule.getLabel(), childDbName); + Direction ruleDirection = rule.getDirection(); + Iterator<Vertex> itr = v.vertices(ruleDirection, rule.getLabel()); + List<Vertex> verticesList = (List<Vertex>) IteratorUtils.toList(itr); + itr = verticesList.stream().filter(item -> { + return item.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName); + }).iterator(); + if (itr.hasNext()) { + getList = (List<Object>) obj.getValue(property); + } + int processed = 0; + int removed = 0; + while (itr.hasNext()) { + Vertex childVertex = itr.next(); + if (!seen.contains(childVertex)) { + Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property); + + Object result = dbToObject(argumentObject, childVertex, seen, depth, nodeOnly, cleanUp); + if (result != null) { + getList.add(argumentObject.getUnderlyingObject()); + } + + processed++; + } else { + removed++; + LOGGER.warn("Cycle found while serializing vertex id={}", childVertex.id().toString()); + } + } + if (processed == 0) { + //vertices were all seen, reset the list + getList = null; + } + if (processed > 0) { + modified = true; + } + } + } else if (obj.isSimpleGenericType(property)) { + List<Object> temp = this.engine.getListProperty(v, property); + if (temp != null) { + getList = (List<Object>) obj.getValue(property); + getList.addAll(temp); + modified = true; + } + + } + + } + + } + } + + //no changes were made to this obj, discard the instance + if (!modified) { + return null; + } + this.enrichData(obj, v); + return obj; + + } + + + public Introspector getVertexProperties(Vertex v) throws AAIException, UnsupportedEncodingException { + String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null); + if (nodeType == null) { + throw new AAIException("AAI_6143"); + } + + Introspector obj = this.latestLoader.introspectorFromName(nodeType); + Set<Vertex> seen = new HashSet<>(); + int depth = 0; + String cleanUp = "false"; + boolean nodeOnly = true; + StopWatch.conditionalStart(); + this.dbToObject(obj, v, seen, depth, nodeOnly, cleanUp); + dbTimeMsecs += StopWatch.stopIfStarted(); + return obj; + + } + + public Introspector getLatestVersionView(Vertex v) throws AAIException, UnsupportedEncodingException { + String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null); + if (nodeType == null) { + throw new AAIException("AAI_6143"); + } + Introspector obj = this.latestLoader.introspectorFromName(nodeType); + Set<Vertex> seen = new HashSet<>(); + int depth = AAIProperties.MAXIMUM_DEPTH; + String cleanUp = "false"; + boolean nodeOnly = false; + StopWatch.conditionalStart(); + Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(v, depth, nodeOnly); + TreeBackedVertex treeVertex = new TreeBackedVertex(v, tree); + this.dbToObject(obj, treeVertex, seen, depth, nodeOnly, cleanUp); + dbTimeMsecs += StopWatch.stopIfStarted(); + return obj; + } + + /** + * Copy simple property. + * + * @param property the property + * @param obj the obj + * @param v the v + * @throws InstantiationException the instantiation exception + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws NoSuchMethodException the no such method exception + * @throws SecurityException the security exception + */ + private void copySimpleProperty(String property, Introspector obj, Vertex v) { + + final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property); + String dbPropertyName = property; + + if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) { + dbPropertyName = metadata.get(PropertyMetadata.DB_ALIAS); + } + + + final Object temp = v.<Object>property(dbPropertyName).orElse(null); + if (temp != null) { + obj.setValue(property, temp); + } + } + + + /** + * Load the introspector from the hashmap for the given property key + * + * @param property - vertex property + * @param obj - introspector object representing the vertex + * @param hashMap - Containing a list of pre-fetched properties for a given vertex + */ + private void copySimplePropertyFromHashMap(String property, Introspector obj, Map<String, Object> hashMap){ + + final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property); + String dbPropertyName = property; + + if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) { + dbPropertyName = metadata.get(PropertyMetadata.DB_ALIAS); + } + + final Object temp = hashMap.getOrDefault(dbPropertyName, null); + + if (temp != null) { + obj.setValue(property, temp); + } + } + + /** + * Simple db to object. + * + * @param obj the obj + * @param v the v + * @throws InstantiationException the instantiation exception + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws NoSuchMethodException the no such method exception + * @throws SecurityException the security exception + */ + private void simpleDbToObject(Introspector obj, Vertex v) { + for(String key : obj.getProperties()){ + this.copySimpleProperty(key, obj, v); + } + } + + + public Map<String, Object> convertVertexToHashMap(Introspector obj, Vertex v){ + + long startTime = System.currentTimeMillis(); + + Set<String> simpleProperties = obj.getSimpleProperties(PropertyPredicates.isVisible()); + String[] simplePropsArray = new String[simpleProperties.size()]; + simplePropsArray = simpleProperties.toArray(simplePropsArray); + + Map<String, Object> simplePropsHashMap = new HashMap<>(simplePropsArray.length * 2); + + v.properties(simplePropsArray).forEachRemaining((vp) -> simplePropsHashMap.put(vp.key(), vp.value())); + + return simplePropsHashMap; + } + + /** + * Creates the relationship list. + * + * @param v the v + * @param obj the obj + * @param cleanUp the clean up + * @return the object + * @throws InstantiationException the instantiation exception + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws NoSuchMethodException the no such method exception + * @throws SecurityException the security exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIException the AAI exception + * @throws MalformedURLException the malformed URL exception + * @throws URISyntaxException + */ + private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp) throws UnsupportedEncodingException, AAIException { + + String[] cousinRules = new String[0]; + + try { + cousinRules = edgeRules.retrieveCachedCousinLabels(obj.getDbName()); + } catch (ExecutionException e) { + LOGGER.warn("Encountered an execution exception while retrieving labels for the node type {} using cached", obj.getDbName(), e); + } + + List<Vertex> cousins = null; + if(cousinRules != null && cousinRules.length != 0){ + cousins = this.engine.getQueryEngine().findCousinVertices(v, cousinRules); + } else { + cousins = this.engine.getQueryEngine().findCousinVertices(v); + } + + List<Object> relationshipObjList = obj.getValue("relationship"); + String aNodeType = v.property("aai-node-type").value().toString(); + + TypeAlphabetizer alphabetizer = new TypeAlphabetizer(); + + EdgeIngestor edgeIngestor = SpringContextAware.getBean(EdgeIngestor.class); + Set<String> keysWithMultipleLabels = edgeIngestor.getMultipleLabelKeys(); + + // For the given vertex, find all the cousins + // For each cousin retrieve the node type and then + // check if the version is greater than the edge label version + // meaning is the current version equal to greater than the version + // where we introduced the edge labels into the relationship payload + // If it is, then we check if the edge key there are multiple labels + // If there are multiple labels, then we need to go to the database + // to retrieve the labels between itself and cousin vertex + // If there is only single label between the edge a and b, then + // we can retrieve what that is without going to the database + // from using the edge rules json and get the edge rule out of it + EdgeRuleQuery.Builder queryBuilder = new EdgeRuleQuery.Builder(aNodeType); + for (Vertex cousin : cousins) { + VertexProperty vertexProperty = cousin.property("aai-node-type"); + String bNodeType = null; + if(vertexProperty.isPresent()){ + bNodeType = cousin.property("aai-node-type").value().toString(); + } else { + // If the vertex is missing the aai-node-type + // Then its either a bad vertex or its in the process + // of getting deleted so we should ignore these vertexes + if(LOGGER.isDebugEnabled()){ + LOGGER.debug("For the vertex {}, unable to retrieve the aai-node-type", v.id().toString()); + } else { + LOGGER.info("Unable to retrieve the aai-node-type for vertex, for more info enable debug log"); + } + continue; + } + if (obj.getVersion().compareTo(schemaVersions.getEdgeLabelVersion()) >= 0) { + String edgeKey = alphabetizer.buildAlphabetizedKey(aNodeType, bNodeType); + if(keysWithMultipleLabels.contains(edgeKey)){ + List<String> edgeLabels = this.getEdgeLabelsBetween(EdgeType.COUSIN, v, cousin); + for(String edgeLabel: edgeLabels){ + Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship"); + Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, edgeLabel); + if (result != null) { + relationshipObjList.add(result); + } + } + } else { + + EdgeRule edgeRule = null; + + // Create a query based on the a nodetype and b nodetype + // which is also a cousin edge and ensure the version + // is used properly so for example in order to be backwards + // compatible if we had allowed a edge between a and b + // in a previous release and we decided to remove it from + // the edge rules in the future we can display the edge + // only for the older apis and the new apis if the edge rule + // is removed will not be seen in the newer version of the API + + EdgeRuleQuery ruleQuery = queryBuilder + .to(bNodeType) + .edgeType(EdgeType.COUSIN) + .version(obj.getVersion()) + .build(); + + try { + edgeRule = edgeIngestor.getRule(ruleQuery); + } catch (EdgeRuleNotFoundException e) { + LOGGER.warn("Caught an edge rule not found exception for query {}, {}," + + " it could be the edge rule is no longer valid for the existing edge in db", + ruleQuery, LogFormatTools.getStackTop(e)); + continue; + } catch (AmbiguousRuleChoiceException e) { + LOGGER.error("Caught an ambiguous rule not found exception for query {}, {}", + ruleQuery, LogFormatTools.getStackTop(e)); + continue; + } + + Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship"); + Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp,edgeRule.getLabel()); + if (result != null) { + relationshipObjList.add(result); + } + } + } else { + Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship"); + Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, null); + if (result != null) { + relationshipObjList.add(result); + } + } + + } + + if (relationshipObjList.isEmpty()) { + return null; + } else { + return obj; + } + } + + /** + * Process edge relationship. + * + * @param relationshipObj the relationship obj + * @param edge the edge + * @param cleanUp the clean up + * @return the object + * @throws InstantiationException the instantiation exception + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws NoSuchMethodException the no such method exception + * @throws SecurityException the security exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIException the AAI exception + * @throws MalformedURLException the malformed URL exception + * @throws AAIUnknownObjectException + * @throws URISyntaxException + */ + private Object processEdgeRelationship(Introspector relationshipObj, Vertex cousin, String cleanUp, String edgeLabel) throws UnsupportedEncodingException, AAIUnknownObjectException { + + VertexProperty aaiUriProperty = cousin.property("aai-uri"); + + if(!aaiUriProperty.isPresent()){ + return null; + } + + URI uri = UriBuilder.fromUri(aaiUriProperty.value().toString()).build(); + + URIToRelationshipObject uriParser = null; + Introspector result = null; + try { + uriParser = new URIToRelationshipObject(relationshipObj.getLoader(), uri, this.baseURL); + result = uriParser.getResult(); + } catch (AAIException | URISyntaxException e) { + LOGGER.error("Error while processing edge relationship in version " + relationshipObj.getVersion() + " (bad vertex ID=" + ": " + + e.getMessage() + " " + LogFormatTools.getStackTop(e)); + return null; + } + + VertexProperty cousinVertexNodeType = cousin.property(AAIProperties.NODE_TYPE); + + if(cousinVertexNodeType.isPresent()){ + if(namedPropNodes.contains(cousinVertexNodeType.value().toString())){ + Introspector cousinObj = loader.introspectorFromName(cousinVertexNodeType.value().toString()); + this.simpleDbToObject(cousinObj, cousin); + this.addRelatedToProperty(result, cousinObj); + } + } + + if (edgeLabel != null && result.hasProperty("relationship-label")) { + result.setValue("relationship-label", edgeLabel); + } + + return result.getUnderlyingObject(); + } + + /** + * Gets the URI for vertex. + * + * @param v the v + * @return the URI for vertex + * @throws InstantiationException the instantiation exception + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws NoSuchMethodException the no such method exception + * @throws SecurityException the security exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIUnknownObjectException + */ + public URI getURIForVertex(Vertex v) throws UnsupportedEncodingException { + + return getURIForVertex(v, false); + } + + public URI getURIForVertex(Vertex v, boolean overwrite) throws UnsupportedEncodingException { + URI uri = UriBuilder.fromPath("/unknown-uri").build(); + + String aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null); + + if (aaiUri != null && !overwrite) { + uri = UriBuilder.fromPath(aaiUri).build(); + } + + return uri; + } + + /** + * Gets the URI from list. + * + * @param list the list + * @return the URI from list + * @throws UnsupportedEncodingException the unsupported encoding exception + */ + private URI getURIFromList(List<Introspector> list) throws UnsupportedEncodingException { + String uri = ""; + StringBuilder sb = new StringBuilder(); + for (Introspector i : list) { + sb.insert(0, i.getURI()); + } + + uri = sb.toString(); + return UriBuilder.fromPath(uri).build(); + } + + /** + * Adds the r + * + * @param relationship the relationship + * @param child the throws IllegalArgumentException, AAIUnknownObjectException child + * @throws AAIUnknownObjectException + * @throws IllegalArgumentException elated to property. + */ + public void addRelatedToProperty(Introspector relationship, Introspector child) throws AAIUnknownObjectException { + String nameProps = child.getMetadata(ObjectMetadata.NAME_PROPS); + List<Introspector> relatedToProperties = new ArrayList<>(); + + if (nameProps != null) { + String[] props = nameProps.split(","); + for (String prop : props) { + Introspector relatedTo = relationship.newIntrospectorInstanceOfNestedProperty("related-to-property"); + relatedTo.setValue("property-key", child.getDbName() + "." + prop); + relatedTo.setValue("property-value", child.getValue(prop)); + relatedToProperties.add(relatedTo); + } + } + + if (!relatedToProperties.isEmpty()) { + List relatedToList = (List) relationship.getValue("related-to-property"); + for (Introspector obj : relatedToProperties) { + relatedToList.add(obj.getUnderlyingObject()); + } + } + + } + + /** + * Creates the edge. + * + * @param relationship the relationship + * @param inputVertex the input vertex + * @return true, if successful + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIException the AAI exception + */ + public boolean createEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException { + + Vertex relatedVertex = null; + StopWatch.conditionalStart(); + QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship); + + String label = null; + if (relationship.hasProperty("relationship-label")) { + label = relationship.getValue("relationship-label"); + } + + List<Vertex> results = parser.getQueryBuilder().toList(); + if (results.isEmpty()) { + dbTimeMsecs += StopWatch.stopIfStarted(); + AAIException e = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri()); + e.getTemplateVars().add(parser.getResultType()); + e.getTemplateVars().add(parser.getUri().toString()); + throw e; + } else { + //still an issue if there's more than one + relatedVertex = results.get(0); + } + + if (relatedVertex != null) { + + Edge e; + try { + e = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label); + if (e == null) { + edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), inputVertex, relatedVertex, label); + } else { + //attempted to link two vertexes already linked + } + } finally { + dbTimeMsecs += StopWatch.stopIfStarted(); + } + } + + dbTimeMsecs += StopWatch.stopIfStarted(); + return true; + } + + /** + * Gets all the edges between of the type with the specified label. + * + * @param aVertex the out vertex + * @param bVertex the in vertex + * @return the edges between + * @throws AAIException the AAI exception + * @throws NoEdgeRuleFoundException + */ + private Edge getEdgeBetweenWithLabel(EdgeType type, Vertex aVertex, Vertex bVertex, EdgeRule edgeRule) { + + Edge result = null; + + if (bVertex != null) { + GraphTraversal<Vertex, Edge> findEdgesBetween = null; + if (EdgeType.TREE.equals(type)) { + GraphTraversal<Vertex,Vertex> findVertex = this.engine.asAdmin().getTraversalSource().V(bVertex); + if(edgeRule.getDirection().equals(Direction.IN)){ + findEdgesBetween = findVertex.outE(edgeRule.getLabel()) + .has(EdgeProperty.CONTAINS.toString(), edgeRule.getContains()) + .not( + __.has(EdgeField.PRIVATE.toString(), true) + ); + } else { + findEdgesBetween = findVertex.inE(edgeRule.getLabel()) + .has(EdgeProperty.CONTAINS.toString(), edgeRule.getContains()) + .not( + __.has(EdgeField.PRIVATE.toString(), true) + ); + } + findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(aVertex.id())).limit(1); + } else { + findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE(edgeRule.getLabel()); + findEdgesBetween = findEdgesBetween + .has(EdgeProperty.CONTAINS.toString(), "NONE") + .not( + __.has(EdgeField.PRIVATE.toString(), true) + ); + findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id())).limit(1); + } + List<Edge> list = findEdgesBetween.toList(); + if(!list.isEmpty()){ + result = list.get(0); + } + } + + return result; + } + + /** + * Gets all the edges between of the type. + * + * @param aVertex the out vertex + * @param bVertex the in vertex + * @return the edges between + * @throws AAIException the AAI exception + * @throws NoEdgeRuleFoundException + */ + private List<Edge> getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex) { + + List<Edge> result = new ArrayList<>(); + + if (bVertex != null) { + GraphTraversal<Vertex, Edge> findEdgesBetween = null; + findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE(); + if (EdgeType.TREE.equals(type)) { + findEdgesBetween = findEdgesBetween + .not( + __.or( + __.has(EdgeProperty.CONTAINS.toString(), "NONE"), + __.has(EdgeField.PRIVATE.toString(), true) + ) + ); + } else { + findEdgesBetween = findEdgesBetween + .has(EdgeProperty.CONTAINS.toString(), "NONE") + .not( + __.has(EdgeField.PRIVATE.toString(), true) + ); + } + findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id())); + result = findEdgesBetween.toList(); + } + + return result; + } + + /** + * Gets all the edges string between of the type. + * + * @param aVertex the out vertex + * @param bVertex the in vertex + * @return the edges between + * @throws AAIException the AAI exception + * @throws NoEdgeRuleFoundException + */ + private List<String> getEdgeLabelsBetween(EdgeType type, Vertex aVertex, Vertex bVertex) { + + List<String> result = new ArrayList<>(); + + if (bVertex != null) { + GraphTraversal<Vertex, Edge> findEdgesBetween = null; + findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE(); + if (EdgeType.TREE.equals(type)) { + findEdgesBetween = findEdgesBetween + .not( + __.or( + __.has(EdgeProperty.CONTAINS.toString(), "NONE"), + __.has(EdgeField.PRIVATE.toString(), true) + ) + ); + } else { + findEdgesBetween = findEdgesBetween + .has(EdgeProperty.CONTAINS.toString(), "NONE") + .not( + __.has(EdgeField.PRIVATE.toString(), true) + ); + } + findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id())); + result = findEdgesBetween.label().toList(); + } + return result; + } + + /** + * Gets all the edges string between of the type. + * + * @param aVertex the out vertex + * @param bVertex the in vertex + * @return the edges between + * @throws AAIException the AAI exception + * @throws NoEdgeRuleFoundException + */ + private Long getEdgeLabelsCount(Vertex aVertex, Vertex bVertex) { + + Long result = null; + + if (bVertex != null) { + GraphTraversal<Vertex, Edge> findEdgesBetween = null; + findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE(); + findEdgesBetween = findEdgesBetween + .has(EdgeProperty.CONTAINS.toString(), "NONE") + .not( + __.has(EdgeField.PRIVATE.toString(), true) + ); + findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id())); + result = findEdgesBetween.count().next(); + } + return result; + } + /** + * Gets all the edges between the vertexes with the label and type. + * + * @param aVertex the out vertex + * @param bVertex the in vertex + * @param label + * @return the edges between + * @throws AAIException the AAI exception + */ + private Edge getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException { + + Edge edge = null; + + if (bVertex != null) { + String aType = aVertex.<String>property(AAIProperties.NODE_TYPE).value(); + String bType = bVertex.<String>property(AAIProperties.NODE_TYPE).value(); + EdgeRuleQuery q = new EdgeRuleQuery.Builder(aType, bType).edgeType(type).label(label).build(); + EdgeRule rule; + try { + rule = edgeRules.getRule(q); + } catch (EdgeRuleNotFoundException e) { + throw new NoEdgeRuleFoundException(e); + } catch (AmbiguousRuleChoiceException e) { + throw new MultipleEdgeRuleFoundException(e); + } + edge = this.getEdgeBetweenWithLabel(type, aVertex, bVertex, rule); + } + + return edge; + } + + /** + * Gets the edge between with the label and edge type. + * + * @param aVertex the out vertex + * @param bVertex the in vertex + * @param label + * @return the edge between + * @throws AAIException the AAI exception + * @throws NoEdgeRuleFoundException + */ + public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException { + + StopWatch.conditionalStart(); + if (bVertex != null) { + + Edge edge = this.getEdgesBetween(type, aVertex, bVertex, label); + if (edge != null) { + dbTimeMsecs += StopWatch.stopIfStarted(); + return edge; + } + + } + dbTimeMsecs += StopWatch.stopIfStarted(); + return null; + } + + public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException { + return this.getEdgeBetween(type, aVertex, bVertex, null); + } + + + /** + * Delete edge. + * + * @param relationship the relationship + * @param inputVertex the input vertex + * @return true, if successful + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIException the AAI exception + */ + public boolean deleteEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException { + + Vertex relatedVertex = null; + StopWatch.conditionalStart(); + QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship); + + List<Vertex> results = parser.getQueryBuilder().toList(); + + String label = null; + if (relationship.hasProperty("relationship-label")) { + label = relationship.getValue("relationship-label"); + } + + if (results.isEmpty()) { + dbTimeMsecs += StopWatch.stopIfStarted(); + return false; + } + + relatedVertex = results.get(0); + Edge edge; + try { + edge = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label); + } catch (NoEdgeRuleFoundException e) { + dbTimeMsecs += StopWatch.stopIfStarted(); + throw new AAIException("AAI_6129", e); + } + if (edge != null) { + edge.remove(); + dbTimeMsecs += StopWatch.stopIfStarted(); + return true; + } else { + dbTimeMsecs += StopWatch.stopIfStarted(); + return false; + } + + } + + /** + * Delete items with traversal. + * + * @param vertexes the vertexes + * @throws IllegalStateException the illegal state exception + */ + public void deleteItemsWithTraversal(List<Vertex> vertexes) throws IllegalStateException { + + for (Vertex v : vertexes) { LOGGER.debug("About to delete the vertex with id: " + v.id()); - deleteWithTraversal(v); - } - - } - - /** - * Delete with traversal. - * - * @param startVertex the start vertex - */ - public void deleteWithTraversal(Vertex startVertex) { - StopWatch.conditionalStart(); - List<Vertex> results = this.engine.getQueryEngine().findDeletable(startVertex); - - for (Vertex v : results) { - LOGGER.warn("Removing vertex " + v.id().toString()); - - v.remove(); - } - dbTimeMsecs += StopWatch.stopIfStarted(); - } - - /** - * Delete. - * - * @param v the v - * @param resourceVersion the resource version - * @throws IllegalArgumentException the illegal argument exception - * @throws AAIException the AAI exception - * @throws InterruptedException the interrupted exception - */ - public void delete(Vertex v, List<Vertex> deletableVertices, String resourceVersion, boolean enableResourceVersion) throws IllegalArgumentException, AAIException { - - boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion); - /* - * The reason why I want to call PreventDeleteSemantics second time is to catch the prevent-deletes in a chain - * These are far-fewer than seeing a prevnt-delete on the vertex to be deleted - * So its better to make these in 2 steps - */ - if(result && !deletableVertices.isEmpty()){ - result = verifyPreventDeleteSemantics(deletableVertices); - } - if (result) { - - try { - deleteWithTraversal(v); - } catch (IllegalStateException e) { - throw new AAIException("AAI_6110", e); - } - - } - - } - - - /** - * Delete. - * - * @param v the v - * @param resourceVersion the resource version - * @throws IllegalArgumentException the illegal argument exception - * @throws AAIException the AAI exception - * @throws InterruptedException the interrupted exception - */ - public void delete(Vertex v, String resourceVersion, boolean enableResourceVersion) throws IllegalArgumentException, AAIException { - - boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion); - - if (result) { - - try { - deleteWithTraversal(v); - } catch (IllegalStateException e) { - throw new AAIException("AAI_6110", e); - } - - } - - } - /** - * Verify delete semantics. - * - * @param vertex the vertex - * @param resourceVersion the resource version - * @return true, if successful - * @throws AAIException the AAI exception - */ - private boolean verifyDeleteSemantics(Vertex vertex, String resourceVersion, boolean enableResourceVersion) throws AAIException { - boolean result = true; - String nodeType = ""; - String errorDetail = " unknown delete semantic found"; - String aaiExceptionCode = ""; - nodeType = vertex.<String>property(AAIProperties.NODE_TYPE).orElse(null); - if (enableResourceVersion && !this.verifyResourceVersion("delete", nodeType, vertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), resourceVersion, nodeType)) { - } - List<Vertex> vertices = new ArrayList<Vertex>(); - vertices.add(vertex); - result = verifyPreventDeleteSemantics(vertices); - - return result; - } - - /** - * Verify Prevent delete semantics. - * @param vertices the list of vertices - * @return true, if successful - * @throws AAIException the AAI exception - */ - private boolean verifyPreventDeleteSemantics(List<Vertex> vertices) throws AAIException { - boolean result = true; - String nodeType = ""; - String errorDetail = " unknown delete semantic found"; - String aaiExceptionCode = ""; - - StopWatch.conditionalStart(); - /* - * This takes in all the vertices in a cascade-delete-chain and checks if there is any edge with a "prevent-delete" condition - * If yes - that should prevent the deletion of the vertex - * Dedup makes sure we dont capture the prevent-delete vertices twice - * The prevent-delete vertices are stored so that the error message displays what prevents the delete - */ - - List<Object> preventDeleteVertices = this.engine.asAdmin().getReadOnlyTraversalSource().V(vertices). - union(__.inE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.IN.toString()).outV().values(AAIProperties.NODE_TYPE), - __.outE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.OUT.toString()).inV().values(AAIProperties.NODE_TYPE)) - .dedup().toList(); - - dbTimeMsecs += StopWatch.stopIfStarted(); - if (!preventDeleteVertices.isEmpty()) { - aaiExceptionCode = "AAI_6110"; - errorDetail = String.format("Object is being reference by additional objects preventing it from being deleted. Please clean up references from the following types %s", preventDeleteVertices); - result = false; - } - if (!result) { - throw new AAIException(aaiExceptionCode, errorDetail); - } - return result; - } - - /** - * Verify resource version. - * - * @param action the action - * @param nodeType the node type - * @param currentResourceVersion the current resource version - * @param resourceVersion the resource version - * @param uri the uri - * @return true, if successful - * @throws AAIException the AAI exception - */ - public boolean verifyResourceVersion(String action, String nodeType, String currentResourceVersion, String resourceVersion, String uri) throws AAIException { - String enabled = ""; - String errorDetail = ""; - String aaiExceptionCode = ""; - if (currentResourceVersion == null) { - currentResourceVersion = ""; - } - - if (resourceVersion == null) { - resourceVersion = ""; - } - try { - enabled = AAIConfig.get(AAIConstants.AAI_RESVERSION_ENABLEFLAG); - } catch (AAIException e) { - ErrorLogHelper.logException(e); - } - if (enabled.equals("true")) { - if (!currentResourceVersion.equals(resourceVersion)) { - if (action.equals("create") && !resourceVersion.equals("")) { - errorDetail = "resource-version passed for " + action + " of " + uri; - aaiExceptionCode = "AAI_6135"; - } else if (resourceVersion.equals("")) { - errorDetail = "resource-version not passed for " + action + " of " + uri; - aaiExceptionCode = "AAI_6130"; - } else { - errorDetail = "resource-version MISMATCH for " + action + " of " + uri; - aaiExceptionCode = "AAI_6131"; - } - - throw new AAIException(aaiExceptionCode, errorDetail); - - } - } - return true; - } - - /** - * Convert from camel case. - * - * @param name the name - * @return the string - */ - private String convertFromCamelCase (String name) { - String result = ""; - result = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, name); - - NamingExceptions exceptions = NamingExceptions.getInstance(); - result = exceptions.getDBName(result); - - return result; - } - - private boolean canModify(Introspector obj, String propName, String requestContext) { - final String readOnly = obj.getPropertyMetadata(propName).get(PropertyMetadata.READ_ONLY); - if (readOnly != null) { - final String[] items = readOnly.split(","); - for (String item : items) { - if (requestContext.equals(item)) { - return false; - } - } - } - return true; - } - - private void executePreSideEffects(Introspector obj, Vertex self) throws AAIException { - - SideEffectRunner runner = new SideEffectRunner - .Builder(this.engine, this).addSideEffect(DataCopy.class).addSideEffect(PrivateEdge.class).build(); - - runner.execute(obj, self); - } - - private void executePostSideEffects(Introspector obj, Vertex self) throws AAIException { - - SideEffectRunner runner = new SideEffectRunner - .Builder(this.engine, this).addSideEffect(DataLinkWriter.class).build(); - - runner.execute(obj, self); - } - - private void enrichData(Introspector obj, Vertex self) throws AAIException { - - SideEffectRunner runner = new SideEffectRunner - .Builder(this.engine, this).addSideEffect(DataLinkReader.class).build(); - - runner.execute(obj, self); - } - - public double getDBTimeMsecs() { - return (dbTimeMsecs); - } - - /** - * Db to object With Filters - * This is for a one-time run with Tenant Isloation to only filter relationships - * TODO: Chnage the original dbToObject to take filter parent/cousins - * - * @param obj the obj - * @param v the vertex from the graph - * @param depth the depth - * @param nodeOnly specify if to exclude relationships or not - * @param filterCousinNodes - * @return the introspector - * @throws AAIException the AAI exception - * @throws IllegalAccessException the illegal access exception - * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws SecurityException the security exception - * @throws InstantiationException the instantiation exception - * @throws NoSuchMethodException the no such method exception - * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws MalformedURLException the malformed URL exception - * @throws AAIUnknownObjectException - * @throws URISyntaxException - */ - //TODO - See if you can merge the 2 dbToObjectWithFilters - public Introspector dbToObjectWithFilters(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly, List<String> filterCousinNodes, List<String> filterParentNodes) throws AAIException, UnsupportedEncodingException { - String cleanUp = "false"; - if (depth < 0) { - return null; - } - depth--; - seen.add(v); - boolean modified = false; - for (String property : obj.getProperties(PropertyPredicates.isVisible())) { - List<Object> getList = null; - Vertex[] vertices = null; - - if (!(obj.isComplexType(property) || obj.isListType(property))) { - this.copySimpleProperty(property, obj, v); - modified = true; - } else { - if (obj.isComplexType(property)) { - /* container case */ - - if (!property.equals("relationship-list") && depth >= 0) { - Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property); - Object result = dbToObjectWithFilters(argumentObject, v, seen, depth+1, nodeOnly, filterCousinNodes, filterParentNodes); - if (result != null) { - obj.setValue(property, argumentObject.getUnderlyingObject()); - modified = true; - } - } else if (property.equals("relationship-list") && !nodeOnly){ - /* relationships need to be handled correctly */ - Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property); - relationshipList = createFilteredRelationshipList(v, relationshipList, cleanUp, filterCousinNodes); - if (relationshipList != null) { - modified = true; - obj.setValue(property, relationshipList.getUnderlyingObject()); - modified = true; - } - - } - } else if (obj.isListType(property)) { - - if (property.equals("any")) { - continue; - } - String genericType = obj.getGenericTypeClass(property).getSimpleName(); - if (obj.isComplexGenericType(property) && depth >= 0) { - final String childDbName = convertFromCamelCase(genericType); - String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null); - EdgeRule rule; - - boolean isthisParentRequired = filterParentNodes.parallelStream().anyMatch(childDbName::contains); - - EdgeRuleQuery q = new EdgeRuleQuery.Builder(vType, childDbName).edgeType(EdgeType.TREE).build(); - - try { - rule = edgeRules.getRule(q); - } catch (EdgeRuleNotFoundException e) { - throw new NoEdgeRuleFoundException(e); - } catch (AmbiguousRuleChoiceException e) { - throw new MultipleEdgeRuleFoundException(e); - } - if (!rule.getContains().equals(AAIDirection.NONE.toString()) && isthisParentRequired) { - //vertices = this.queryEngine.findRelatedVertices(v, Direction.OUT, rule.getLabel(), childDbName); - Direction ruleDirection = rule.getDirection(); - Iterator<Vertex> itr = v.vertices(ruleDirection, rule.getLabel()); - List<Vertex> verticesList = (List<Vertex>)IteratorUtils.toList(itr); - itr = verticesList.stream().filter(item -> { - return item.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName); - }).iterator(); - if (itr.hasNext()) { - getList = (List<Object>)obj.getValue(property); - } - int processed = 0; - int removed = 0; - while (itr.hasNext()) { - Vertex childVertex = itr.next(); - if (!seen.contains(childVertex)) { - Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property); - - Object result = dbToObjectWithFilters(argumentObject, childVertex, seen, depth, nodeOnly, filterCousinNodes, filterParentNodes); - if (result != null) { - getList.add(argumentObject.getUnderlyingObject()); - } - - processed++; - } else { - removed++; - LOGGER.warn("Cycle found while serializing vertex id={}", childVertex.id().toString()); - } - } - if (processed == 0) { - //vertices were all seen, reset the list - getList = null; - } - if (processed > 0) { - modified = true; - } - } - } else if (obj.isSimpleGenericType(property)) { - List<Object> temp = this.engine.getListProperty(v, property); - if (temp != null) { - getList = (List<Object>)obj.getValue(property); - getList.addAll(temp); - modified = true; - } - - } - - } - - } - } - - //no changes were made to this obj, discard the instance - if (!modified) { - return null; - } - this.enrichData(obj, v); - return obj; - - } - - /** - * Creates the relationship list with the filtered node types. - * - * @param v the v - * @param obj the obj - * @param cleanUp the clean up - * @return the object - * @throws InstantiationException the instantiation exception - * @throws IllegalAccessException the illegal access exception - * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws NoSuchMethodException the no such method exception - * @throws SecurityException the security exception - * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIException the AAI exception - * @throws MalformedURLException the malformed URL exception - * @throws URISyntaxException - */ - private Introspector createFilteredRelationshipList(Vertex v, Introspector obj, String cleanUp, List<String> filterNodes) throws UnsupportedEncodingException, AAIException { - List<Vertex> allCousins = this.engine.getQueryEngine().findCousinVertices(v); - - Iterator<Vertex> cousinVertices = allCousins.stream().filter(item -> { - String node = (String)item.property(AAIProperties.NODE_TYPE).orElse(""); - return filterNodes.parallelStream().anyMatch(node::contains); - }).iterator(); - - - List<Vertex> cousins = (List<Vertex>)IteratorUtils.toList(cousinVertices); - - //items.parallelStream().anyMatch(inputStr::contains) - List<Object> relationshipObjList = obj.getValue("relationship"); - for (Vertex cousin : cousins) { - - Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship"); - Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, null); - if (result != null) { - relationshipObjList.add(result); - } - - - } - - if (relationshipObjList.isEmpty()) { - return null; - } else { - return obj; - } - } + deleteWithTraversal(v); + } + + } + + /** + * Delete with traversal. + * + * @param startVertex the start vertex + */ + public void deleteWithTraversal(Vertex startVertex) { + StopWatch.conditionalStart(); + List<Vertex> results = this.engine.getQueryEngine().findDeletable(startVertex); + + for (Vertex v : results) { + LOGGER.warn("Removing vertex " + v.id().toString()); + + v.remove(); + } + dbTimeMsecs += StopWatch.stopIfStarted(); + } + + /** + * Delete. + * + * @param v the v + * @param resourceVersion the resource version + * @throws IllegalArgumentException the illegal argument exception + * @throws AAIException the AAI exception + * @throws InterruptedException the interrupted exception + */ + public void delete(Vertex v, List<Vertex> deletableVertices, String resourceVersion, boolean enableResourceVersion) throws IllegalArgumentException, AAIException { + + boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion); + /* + * The reason why I want to call PreventDeleteSemantics second time is to catch the prevent-deletes in a chain + * These are far-fewer than seeing a prevnt-delete on the vertex to be deleted + * So its better to make these in 2 steps + */ + if (result && !deletableVertices.isEmpty()) { + result = verifyPreventDeleteSemantics(deletableVertices); + } + if (result) { + + try { + deleteWithTraversal(v); + } catch (IllegalStateException e) { + throw new AAIException("AAI_6110", e); + } + + } + + } + + + /** + * Delete. + * + * @param v the v + * @param resourceVersion the resource version + * @throws IllegalArgumentException the illegal argument exception + * @throws AAIException the AAI exception + * @throws InterruptedException the interrupted exception + */ + public void delete(Vertex v, String resourceVersion, boolean enableResourceVersion) throws IllegalArgumentException, AAIException { + + boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion); + + if (result) { + + try { + deleteWithTraversal(v); + } catch (IllegalStateException e) { + throw new AAIException("AAI_6110", e); + } + + } + + } + + /** + * Verify delete semantics. + * + * @param vertex the vertex + * @param resourceVersion the resource version + * @return true, if successful + * @throws AAIException the AAI exception + */ + private boolean verifyDeleteSemantics(Vertex vertex, String resourceVersion, boolean enableResourceVersion) throws AAIException { + boolean result = true; + String nodeType = ""; + String errorDetail = " unknown delete semantic found"; + String aaiExceptionCode = ""; + nodeType = vertex.<String>property(AAIProperties.NODE_TYPE).orElse(null); + if (enableResourceVersion && !this.verifyResourceVersion("delete", nodeType, vertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), resourceVersion, nodeType)) { + } + List<Vertex> vertices = new ArrayList<Vertex>(); + vertices.add(vertex); + result = verifyPreventDeleteSemantics(vertices); + + return result; + } + + /** + * Verify Prevent delete semantics. + * + * @param vertices the list of vertices + * @return true, if successful + * @throws AAIException the AAI exception + */ + private boolean verifyPreventDeleteSemantics(List<Vertex> vertices) throws AAIException { + boolean result = true; + String nodeType = ""; + String errorDetail = " unknown delete semantic found"; + String aaiExceptionCode = ""; + + StopWatch.conditionalStart(); + /* + * This takes in all the vertices in a cascade-delete-chain and checks if there is any edge with a "prevent-delete" condition + * If yes - that should prevent the deletion of the vertex + * Dedup makes sure we dont capture the prevent-delete vertices twice + * The prevent-delete vertices are stored so that the error message displays what prevents the delete + */ + + List<Object> preventDeleteVertices = this.engine.asAdmin().getReadOnlyTraversalSource().V(vertices). + union(__.inE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.IN.toString()).outV().values(AAIProperties.NODE_TYPE), + __.outE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.OUT.toString()).inV().values(AAIProperties.NODE_TYPE)) + .dedup().toList(); + + dbTimeMsecs += StopWatch.stopIfStarted(); + if (!preventDeleteVertices.isEmpty()) { + aaiExceptionCode = "AAI_6110"; + errorDetail = String.format("Object is being reference by additional objects preventing it from being deleted. Please clean up references from the following types %s", preventDeleteVertices); + result = false; + } + if (!result) { + throw new AAIException(aaiExceptionCode, errorDetail); + } + return result; + } + + /** + * Verify resource version. + * + * @param action the action + * @param nodeType the node type + * @param currentResourceVersion the current resource version + * @param resourceVersion the resource version + * @param uri the uri + * @return true, if successful + * @throws AAIException the AAI exception + */ + public boolean verifyResourceVersion(String action, String nodeType, String currentResourceVersion, String resourceVersion, String uri) throws AAIException { + String enabled = ""; + String errorDetail = ""; + String aaiExceptionCode = ""; + if (currentResourceVersion == null) { + currentResourceVersion = ""; + } + + if (resourceVersion == null) { + resourceVersion = ""; + } + try { + enabled = AAIConfig.get(AAIConstants.AAI_RESVERSION_ENABLEFLAG); + } catch (AAIException e) { + ErrorLogHelper.logException(e); + } + if (enabled.equals("true")) { + if (!currentResourceVersion.equals(resourceVersion)) { + if (action.equals("create") && !resourceVersion.equals("")) { + errorDetail = "resource-version passed for " + action + " of " + uri; + aaiExceptionCode = "AAI_6135"; + } else if (resourceVersion.equals("")) { + errorDetail = "resource-version not passed for " + action + " of " + uri; + aaiExceptionCode = "AAI_6130"; + } else { + errorDetail = "resource-version MISMATCH for " + action + " of " + uri; + aaiExceptionCode = "AAI_6131"; + } + + throw new AAIException(aaiExceptionCode, errorDetail); + + } + } + return true; + } + + /** + * Convert from camel case. + * + * @param name the name + * @return the string + */ + private String convertFromCamelCase (String name) { + String result = ""; + result = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, name); + + NamingExceptions exceptions = NamingExceptions.getInstance(); + result = exceptions.getDBName(result); + + return result; + } + + private boolean canModify(Introspector obj, String propName, String requestContext) { + final String readOnly = obj.getPropertyMetadata(propName).get(PropertyMetadata.READ_ONLY); + if (readOnly != null) { + final String[] items = readOnly.split(","); + for (String item : items) { + if (requestContext.equals(item)) { + return false; + } + } + } + return true; + } + + private void executePreSideEffects(Introspector obj, Vertex self) throws AAIException { + + SideEffectRunner runner = new SideEffectRunner + .Builder(this.engine, this).addSideEffect(DataCopy.class).addSideEffect(PrivateEdge.class).build(); + + runner.execute(obj, self); + } + + private void executePostSideEffects(Introspector obj, Vertex self) throws AAIException { + + SideEffectRunner runner = new SideEffectRunner + .Builder(this.engine, this).addSideEffect(DataLinkWriter.class).build(); + + runner.execute(obj, self); + } + + private void enrichData(Introspector obj, Vertex self) throws AAIException { + + SideEffectRunner runner = new SideEffectRunner + .Builder(this.engine, this).addSideEffect(DataLinkReader.class).build(); + + runner.execute(obj, self); + } + + public double getDBTimeMsecs() { + return (dbTimeMsecs); + } + + /** + * Db to object With Filters + * This is for a one-time run with Tenant Isloation to only filter relationships + * TODO: Chnage the original dbToObject to take filter parent/cousins + * + * @param obj the obj + * @param v the vertex from the graph + * @param depth the depth + * @param nodeOnly specify if to exclude relationships or not + * @param filterCousinNodes + * @return the introspector + * @throws AAIException the AAI exception + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws SecurityException the security exception + * @throws InstantiationException the instantiation exception + * @throws NoSuchMethodException the no such method exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws MalformedURLException the malformed URL exception + * @throws AAIUnknownObjectException + * @throws URISyntaxException + */ + //TODO - See if you can merge the 2 dbToObjectWithFilters + public Introspector dbToObjectWithFilters(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly, List<String> filterCousinNodes, List<String> filterParentNodes) throws AAIException, UnsupportedEncodingException { + String cleanUp = "false"; + if (depth < 0) { + return null; + } + depth--; + seen.add(v); + boolean modified = false; + for (String property : obj.getProperties(PropertyPredicates.isVisible())) { + List<Object> getList = null; + Vertex[] vertices = null; + + if (!(obj.isComplexType(property) || obj.isListType(property))) { + this.copySimpleProperty(property, obj, v); + modified = true; + } else { + if (obj.isComplexType(property)) { + /* container case */ + + if (!property.equals("relationship-list") && depth >= 0) { + Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property); + Object result = dbToObjectWithFilters(argumentObject, v, seen, depth + 1, nodeOnly, filterCousinNodes, filterParentNodes); + if (result != null) { + obj.setValue(property, argumentObject.getUnderlyingObject()); + modified = true; + } + } else if (property.equals("relationship-list") && !nodeOnly) { + /* relationships need to be handled correctly */ + Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property); + relationshipList = createFilteredRelationshipList(v, relationshipList, cleanUp, filterCousinNodes); + if (relationshipList != null) { + modified = true; + obj.setValue(property, relationshipList.getUnderlyingObject()); + modified = true; + } + + } + } else if (obj.isListType(property)) { + + if (property.equals("any")) { + continue; + } + String genericType = obj.getGenericTypeClass(property).getSimpleName(); + if (obj.isComplexGenericType(property) && depth >= 0) { + final String childDbName = convertFromCamelCase(genericType); + String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null); + EdgeRule rule; + + boolean isthisParentRequired = filterParentNodes.parallelStream().anyMatch(childDbName::contains); + + EdgeRuleQuery q = new EdgeRuleQuery.Builder(vType, childDbName).edgeType(EdgeType.TREE).build(); + + try { + rule = edgeRules.getRule(q); + } catch (EdgeRuleNotFoundException e) { + throw new NoEdgeRuleFoundException(e); + } catch (AmbiguousRuleChoiceException e) { + throw new MultipleEdgeRuleFoundException(e); + } + if (!rule.getContains().equals(AAIDirection.NONE.toString()) && isthisParentRequired) { + //vertices = this.queryEngine.findRelatedVertices(v, Direction.OUT, rule.getLabel(), childDbName); + Direction ruleDirection = rule.getDirection(); + Iterator<Vertex> itr = v.vertices(ruleDirection, rule.getLabel()); + List<Vertex> verticesList = (List<Vertex>) IteratorUtils.toList(itr); + itr = verticesList.stream().filter(item -> { + return item.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName); + }).iterator(); + if (itr.hasNext()) { + getList = (List<Object>) obj.getValue(property); + } + int processed = 0; + int removed = 0; + while (itr.hasNext()) { + Vertex childVertex = itr.next(); + if (!seen.contains(childVertex)) { + Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property); + + Object result = dbToObjectWithFilters(argumentObject, childVertex, seen, depth, nodeOnly, filterCousinNodes, filterParentNodes); + if (result != null) { + getList.add(argumentObject.getUnderlyingObject()); + } + + processed++; + } else { + removed++; + LOGGER.warn("Cycle found while serializing vertex id={}", childVertex.id().toString()); + } + } + if (processed == 0) { + //vertices were all seen, reset the list + getList = null; + } + if (processed > 0) { + modified = true; + } + } + } else if (obj.isSimpleGenericType(property)) { + List<Object> temp = this.engine.getListProperty(v, property); + if (temp != null) { + getList = (List<Object>) obj.getValue(property); + getList.addAll(temp); + modified = true; + } + + } + + } + + } + } + + //no changes were made to this obj, discard the instance + if (!modified) { + return null; + } + this.enrichData(obj, v); + return obj; + + } + + /** + * Creates the relationship list with the filtered node types. + * + * @param v the v + * @param obj the obj + * @param cleanUp the clean up + * @return the object + * @throws InstantiationException the instantiation exception + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws NoSuchMethodException the no such method exception + * @throws SecurityException the security exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIException the AAI exception + * @throws MalformedURLException the malformed URL exception + * @throws URISyntaxException + */ + private Introspector createFilteredRelationshipList(Vertex v, Introspector obj, String cleanUp, List<String> filterNodes) throws UnsupportedEncodingException, AAIException { + List<Vertex> allCousins = this.engine.getQueryEngine().findCousinVertices(v); + + Iterator<Vertex> cousinVertices = allCousins.stream().filter(item -> { + String node = (String) item.property(AAIProperties.NODE_TYPE).orElse(""); + return filterNodes.parallelStream().anyMatch(node::contains); + }).iterator(); + + + List<Vertex> cousins = (List<Vertex>) IteratorUtils.toList(cousinVertices); + + //items.parallelStream().anyMatch(inputStr::contains) + List<Object> relationshipObjList = obj.getValue("relationship"); + for (Vertex cousin : cousins) { + + Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship"); + Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, null); + if (result != null) { + relationshipObjList.add(result); + } + + + } + + if (relationshipObjList.isEmpty()) { + return null; + } else { + return obj; + } + } } diff --git a/aai-core/src/main/java/org/onap/aai/serialization/engines/query/GraphTraversalQueryEngine.java b/aai-core/src/main/java/org/onap/aai/serialization/engines/query/GraphTraversalQueryEngine.java index 2158c894..d072db55 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/engines/query/GraphTraversalQueryEngine.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/engines/query/GraphTraversalQueryEngine.java @@ -30,6 +30,7 @@ import static org.onap.aai.edges.enums.EdgeProperty.DELETE_OTHER_V; import java.util.List; import java.util.Set; +import org.apache.tinkerpop.gremlin.process.traversal.Order; import org.apache.tinkerpop.gremlin.process.traversal.P; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; @@ -45,12 +46,12 @@ import org.onap.aai.logging.StopWatch; /* * This class needs some big explanation despite its compact size. - * This controls all the queries performed by the CRUD API in A&AI. + * This controls all the queries performed by the CRUD API in A&AI. * findParents, findChildren, and findDeletable require special attention * These methods use 'repeat'. You cannot use 'emit' with repeat currently * as it is extremely buggy as of tinkerpop-3.0.1-incubating. The way around * it (for now) is to sideEffect all the vertices we traverse into an ArrayList. - * + * */ public class GraphTraversalQueryEngine extends QueryEngine { @@ -70,6 +71,7 @@ public class GraphTraversalQueryEngine extends QueryEngine { public List<Vertex> findParents(Vertex start) { try { StopWatch.conditionalStart(); + @SuppressWarnings("unchecked") final GraphTraversal<Vertex, Vertex> pipe = this.g.V(start).emit(v -> true).repeat(__.union(__.inE().has(CONTAINS.toString(), OUT.toString()).outV(), __.outE().has(CONTAINS.toString(), IN.toString()).inV())); return pipe.toList(); @@ -79,19 +81,36 @@ public class GraphTraversalQueryEngine extends QueryEngine { } } + /** + * {@inheritDoc} + */ + @Override + public List<Vertex> findParents(String[] uris) { + try { + StopWatch.conditionalStart(); + final GraphTraversal<Vertex, Vertex> pipe = this.g.V() + .has(AAIProperties.AAI_URI, P.within(uris)) + .order().by(AAIProperties.AAI_URI, Order.decr); + return pipe.toList(); + } + finally { + dbTimeMsecs += StopWatch.stopIfStarted(); + } + } + /** * {@inheritDoc} */ @Override public List<Vertex> findAllChildren(Vertex start) { - + @SuppressWarnings("unchecked") GraphTraversal<Vertex, Vertex> pipe = this.g .V(start).emit(v -> true).repeat(__.union(__.outE().has(CONTAINS.toString(), OUT.toString()).inV(), __.inE().has(CONTAINS.toString(), IN.toString()).outV())); - + return pipe.toList(); - + } /** @@ -104,10 +123,10 @@ public class GraphTraversalQueryEngine extends QueryEngine { __.outE().has(CONTAINS.toString(), OUT.toString()).inV(), __.inE().has(CONTAINS.toString(), IN.toString()).outV() ).has(AAIProperties.NODE_TYPE, type).dedup(); - + return pipe.toList(); } - + /** * {@inheritDoc} */ @@ -118,7 +137,7 @@ public class GraphTraversalQueryEngine extends QueryEngine { __.outE().has(CONTAINS.toString(), OUT.toString()), __.inE().has(CONTAINS.toString(), IN.toString()) ).otherV().dedup(); - + return pipe.toList(); } @@ -135,7 +154,7 @@ public class GraphTraversalQueryEngine extends QueryEngine { __.inE().has(DELETE_OTHER_V.toString(), IN.toString()).outV() ) ).dedup(); - + return pipe.toList(); } @@ -158,11 +177,11 @@ public class GraphTraversalQueryEngine extends QueryEngine { default: break; } - + pipe.has(AAIProperties.NODE_TYPE, nodeType).dedup(); return pipe.toList(); } - + @Override public Tree<Element> findSubGraph(Vertex start, int iterations, boolean nodeOnly) { final GraphTraversal<Vertex, ?> t = this.g.V(start).emit(v -> true).times(iterations).repeat( @@ -204,13 +223,13 @@ public class GraphTraversalQueryEngine extends QueryEngine { __.has("private", true) ) .dedup(); - + return pipeline.toList(); } @Override - public List<Vertex> findCousinVertices(Vertex start) { + public List<Vertex> findCousinVertices(Vertex start, String... labels) { // Start at the given vertex // Do a union to copy the start vertex to be run against all // so for the start vertex it gets all of in edges that contains other v set to none @@ -223,8 +242,8 @@ public class GraphTraversalQueryEngine extends QueryEngine { GraphTraversal<Vertex, Vertex> pipeline = this.g .V(start) .union( - __.inE().has(CONTAINS.toString(), NONE.toString()), - __.outE().has(CONTAINS.toString(), NONE.toString()) + __.inE(labels).has(CONTAINS.toString(), NONE.toString()), + __.outE(labels).has(CONTAINS.toString(), NONE.toString()) ) .not( __.has(PRIVATE.toString(), true) @@ -234,7 +253,7 @@ public class GraphTraversalQueryEngine extends QueryEngine { return pipeline.toList(); } - + public double getDBTimeMsecs() { return (dbTimeMsecs); } diff --git a/aai-core/src/main/java/org/onap/aai/serialization/engines/query/QueryEngine.java b/aai-core/src/main/java/org/onap/aai/serialization/engines/query/QueryEngine.java index c2929468..110f8628 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/engines/query/QueryEngine.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/engines/query/QueryEngine.java @@ -42,16 +42,33 @@ public abstract class QueryEngine { public QueryEngine (GraphTraversalSource g) { this.g = g; } - + /** * Finds all the parents/grandparents/etc of the given start node. * * @param start - the start vertex whose parent chain you want * @return the list of start and start's parent, grandparent, etc, in - * order (ie {start, parent, grandparent, etc} + * order (ie {start, parent, grandparent, etc} */ public abstract List<Vertex> findParents(Vertex start); - + + /** + * Finds all the parents/grandparents/etc of the given start node. + * + * This method should be used in place of the #findParents(Vertex) + * as since the aai-uri is added the lookup for finding the parents + * using the given list of aai-uri will be much faster than using + * a traversal to follow a start vertex and keep repeating since + * as the number of different type of edges keeps growing that call + * will be more expensive than using the aai-uri's as they are fast lookup + * + * @param uris - list of the uris representing the aai-uris of + * parent, grandparent, etc + * @return the list of start and start's parent, grandparent, etc, in + * order (ie {start, parent, grandparent, etc} + */ + public abstract List<Vertex> findParents(String [] uris); + /** * Finds all children, grandchildren, etc of start * @@ -59,7 +76,7 @@ public abstract class QueryEngine { * @return the list of child/grandchild/etc vertices */ public abstract List<Vertex> findAllChildren(Vertex start); - + /** * Finds all immediate children of start (no grandchildren or so forth) of the given type * @param start - the start vertex @@ -67,14 +84,14 @@ public abstract class QueryEngine { * @return the list of immediate child vertices of given type */ public abstract List<Vertex> findChildrenOfType(Vertex start, String type); - + /** * Finds all immediate children of start (no grandchildren or so forth) * @param start - the start vertex * @return the list of immediate child vertices */ public abstract List<Vertex> findChildren(Vertex start); - + /** * Find all vertices that should be deleted in a cascade from a delete of start * @@ -82,34 +99,34 @@ public abstract class QueryEngine { * @return the list of vertices to be deleted when start is deleted */ public abstract List<Vertex> findDeletable(Vertex start); - + /** * Finds the subgraph under start, including cousins as well as start's children/grandchildren/etc. * More specifically, this includes start, all its descendants, start's cousins, and start's * descendants' cousins (but not any of the cousins' cousins or descendants), and the edges * connecting them. - * + * * @param start - the start vertex - * @return - Tree containing nodes and edges of the subgraph + * @return - Tree containing nodes and edges of the subgraph */ public Tree<Element> findSubGraph(Vertex start) { return findSubGraph(start, AAIProperties.MAXIMUM_DEPTH, false); } - + /** * Finds the subgraph under start, including cousins as well as start's children/grandchildren/etc. * More specifically, this includes start, all its descendants, start's cousins, and start's * descendants' cousins (but not any of the cousins' cousins or descendants), and the edges * connecting them. - * + * * @param start - the start vertex - * @param iterations - depth of the subgraph, this limits how many generations of + * @param iterations - depth of the subgraph, this limits how many generations of * descendants are included * @param nodeOnly - if true the subgraph will NOT include the cousins - * @return Tree containing nodes and edges of the subgraph + * @return Tree containing nodes and edges of the subgraph */ public abstract Tree<Element> findSubGraph(Vertex start, int iterations, boolean nodeOnly); - + /** * Find vertices of type nodeType related to start by edges of the given * direction and label. @@ -125,23 +142,23 @@ public abstract class QueryEngine { /** * Finds cousin edges connecting start to other vertices only of types defined in an old version. * The idea is that if a user is using an old version, they won't understand any new node types in - * subsequent versions. Thus, revealing edges to new types will cause problems. This methods + * subsequent versions. Thus, revealing edges to new types will cause problems. This methods * filters any such edges out. - * + * * @param start - the start vertex * @param loader - loader for retrieving the list of allowed node types for the desired version * (version is set when the loader was instantiated) * @return list of cousin edges between start and any node types understood by the version specified in loader */ public abstract List<Edge> findEdgesForVersion(Vertex start, Loader loader); - + /** - * Finds all cousins of start. - * + * Finds all cousins of start. + * * @param start - the start vertex * @return list of start's cousin vertices */ - public abstract List<Vertex> findCousinVertices(Vertex start); + public abstract List<Vertex> findCousinVertices(Vertex start, String... labels); public abstract double getDBTimeMsecs(); |