summaryrefslogtreecommitdiffstats
path: root/champ-lib/champ-titan/src
diff options
context:
space:
mode:
authormichaere <michaere@amdocs.com>2018-03-05 16:33:32 +0000
committermichaere <michaere@amdocs.com>2018-03-07 11:17:22 +0000
commitc74f7b13b573386e70c10721fc391624ee792ed6 (patch)
treeb44995474ff938b4b03c9b234f95b71bc75d6b79 /champ-lib/champ-titan/src
parent9fc28cff11a4b570618c0f533ce9de6209a5dd0c (diff)
Port champ-microservice project restructure
Includes project restructure and introduction of a parent pom. The original source folder and core functionality is now held within champ-lib, with champ-service forming the ajsc microservice from which it injects champ-lib core functionality. Issue-ID: AAI-813 Change-Id: I2ce0c4a70e485665276e7955572de23969deb706 Signed-off-by: michaere <michaere@amdocs.com>
Diffstat (limited to 'champ-lib/champ-titan/src')
-rw-r--r--champ-lib/champ-titan/src/main/java/org/onap/aai/champtitan/graph/impl/GraphSON.java66
-rw-r--r--champ-lib/champ-titan/src/main/java/org/onap/aai/champtitan/graph/impl/TitanChampGraphImpl.java474
-rw-r--r--champ-lib/champ-titan/src/main/java/org/onap/aai/champtitan/perf/ChampAPIPerformanceTest.java458
-rw-r--r--champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/concurrency/ConcurrencyTest.java33
-rw-r--r--champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampAPITest.java40
-rw-r--r--champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampObjectIndexTest.java48
-rw-r--r--champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampRelationshipIndexTest.java38
-rw-r--r--champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampSchemaTest.java37
-rw-r--r--champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampTransactionTest.java335
-rw-r--r--champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/TitanChampSetupTest.java74
10 files changed, 1603 insertions, 0 deletions
diff --git a/champ-lib/champ-titan/src/main/java/org/onap/aai/champtitan/graph/impl/GraphSON.java b/champ-lib/champ-titan/src/main/java/org/onap/aai/champtitan/graph/impl/GraphSON.java
new file mode 100644
index 0000000..e36bd53
--- /dev/null
+++ b/champ-lib/champ-titan/src/main/java/org/onap/aai/champtitan/graph/impl/GraphSON.java
@@ -0,0 +1,66 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * 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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.onap.aai.champtitan.graph.impl;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.thinkaurelius.titan.graphdb.tinkerpop.TitanIoRegistry;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONMapper;
+import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONWriter;
+import org.onap.aai.champcore.FormatMapper;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class GraphSON implements FormatMapper {
+ private final GraphSONMapper mapper;
+ private final GraphSONWriter writer;
+ protected JsonParser parser;
+
+ public GraphSON() {
+ this.mapper = GraphSONMapper.build().addRegistry(TitanIoRegistry.INSTANCE).create();
+ this.writer = GraphSONWriter.build().mapper(this.mapper).create();
+ this.parser = new JsonParser();
+ }
+
+ public JsonObject formatObject(Object v) {
+ OutputStream os = new ByteArrayOutputStream();
+ String result = "";
+
+ try {
+ this.writer.writeVertex(os, (Vertex)v, Direction.BOTH);
+ result = os.toString();
+ } catch (IOException var5) {
+ var5.printStackTrace();
+ }
+
+ return this.parser.parse(result).getAsJsonObject();
+ }
+
+ public int parallelThreshold() {
+ return 50;
+ }
+
+}
diff --git a/champ-lib/champ-titan/src/main/java/org/onap/aai/champtitan/graph/impl/TitanChampGraphImpl.java b/champ-lib/champ-titan/src/main/java/org/onap/aai/champtitan/graph/impl/TitanChampGraphImpl.java
new file mode 100644
index 0000000..56434be
--- /dev/null
+++ b/champ-lib/champ-titan/src/main/java/org/onap/aai/champtitan/graph/impl/TitanChampGraphImpl.java
@@ -0,0 +1,474 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * 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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.onap.aai.champtitan.graph.impl;
+
+import java.time.temporal.ChronoUnit;
+import java.util.*;
+import java.util.Map.Entry;
+import java.util.concurrent.ExecutionException;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.onap.aai.champcore.ChampCapabilities;
+import org.onap.aai.champcore.FormatMapper;
+import org.onap.aai.champcore.Formatter;
+import org.onap.aai.champcore.exceptions.ChampIndexNotExistsException;
+import org.onap.aai.champcore.exceptions.ChampSchemaViolationException;
+import org.onap.aai.champcore.graph.impl.AbstractTinkerpopChampGraph;
+import org.onap.aai.champcore.model.ChampCardinality;
+import org.onap.aai.champcore.model.ChampObject;
+import org.onap.aai.champcore.model.ChampObjectConstraint;
+import org.onap.aai.champcore.model.ChampObjectIndex;
+import org.onap.aai.champcore.model.ChampPropertyConstraint;
+import org.onap.aai.champcore.model.ChampRelationship;
+import org.onap.aai.champcore.model.ChampRelationshipConstraint;
+import org.onap.aai.champcore.model.ChampRelationshipIndex;
+import org.onap.aai.champcore.model.ChampSchema;
+import org.onap.aai.champcore.schema.ChampSchemaEnforcer;
+import org.onap.aai.champcore.schema.DefaultChampSchemaEnforcer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.thinkaurelius.titan.core.Cardinality;
+import com.thinkaurelius.titan.core.EdgeLabel;
+import com.thinkaurelius.titan.core.PropertyKey;
+import com.thinkaurelius.titan.core.SchemaViolationException;
+import com.thinkaurelius.titan.core.TitanEdge;
+import com.thinkaurelius.titan.core.TitanFactory;
+import com.thinkaurelius.titan.core.TitanGraph;
+import com.thinkaurelius.titan.core.TitanVertex;
+import com.thinkaurelius.titan.core.schema.SchemaAction;
+import com.thinkaurelius.titan.core.schema.SchemaStatus;
+import com.thinkaurelius.titan.core.schema.TitanGraphIndex;
+import com.thinkaurelius.titan.core.schema.TitanManagement;
+import com.thinkaurelius.titan.graphdb.database.management.ManagementSystem;
+
+public final class TitanChampGraphImpl extends AbstractTinkerpopChampGraph {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TitanChampGraphImpl.class);
+ private static final String TITAN_UNIQUE_SUFFIX = "graph.unique-instance-id-suffix";
+ private static final String TITAN_CASSANDRA_KEYSPACE = "storage.cassandra.keyspace";
+ private static final String TITAN_HBASE_TABLE = "storage.hbase.table";
+ private static final ChampSchemaEnforcer SCHEMA_ENFORCER = new DefaultChampSchemaEnforcer();
+ private static final int REGISTER_OBJECT_INDEX_TIMEOUT_SECS = 30;
+
+ private static final ChampCapabilities CAPABILITIES = new ChampCapabilities() {
+
+ @Override
+ public boolean canDeleteObjectIndices() {
+ return false;
+ }
+
+ @Override
+ public boolean canDeleteRelationshipIndices() {
+ return false;
+ }
+ };
+
+ private final TitanGraph graph;
+
+ public TitanChampGraphImpl(Builder builder) {
+ super(builder.graphConfiguration);
+ final TitanFactory.Builder titanGraphBuilder = TitanFactory.build();
+
+ for (Entry<String, Object> titanGraphProperty : builder.graphConfiguration.entrySet()) {
+ titanGraphBuilder.set(titanGraphProperty.getKey(), titanGraphProperty.getValue());
+ }
+
+ titanGraphBuilder.set(TITAN_UNIQUE_SUFFIX, ((short) new Random().nextInt(Short.MAX_VALUE)+""));
+
+ final Object storageBackend = builder.graphConfiguration.get("storage.backend");
+
+ if ("cassandra".equals(storageBackend) ||
+ "cassandrathrift".equals(storageBackend) ||
+ "astyanax".equals(storageBackend) ||
+ "embeddedcassandra".equals(storageBackend)) {
+ titanGraphBuilder.set(TITAN_CASSANDRA_KEYSPACE, builder.graphName);
+ } else if ("hbase".equals(storageBackend)) {
+ titanGraphBuilder.set(TITAN_HBASE_TABLE, builder.graphName);
+ } else if ("berkleyje".equals(storageBackend)) {
+ throw new RuntimeException("storage.backend=berkleyje cannot handle multiple graphs on a single DB, not usable");
+ } else if ("inmemory".equals(storageBackend)) {
+ } else {
+ throw new RuntimeException("Unknown storage.backend=" + storageBackend);
+ }
+
+ LOGGER.info("Instantiated data access layer for Titan graph data store with backend: " + storageBackend);
+
+ this.graph = titanGraphBuilder.open();
+ }
+
+ public static class Builder {
+ private final String graphName;
+
+ private final Map<String, Object> graphConfiguration = new HashMap<String, Object> ();
+
+ public Builder(String graphName) {
+ this.graphName = graphName;
+ }
+
+ public Builder(String graphName, Map<String, Object> properties) {
+ this.graphName = graphName;
+ properties(properties);
+ }
+
+ public Builder properties(Map<String, Object> properties) {
+ if (properties.containsKey(TITAN_CASSANDRA_KEYSPACE))
+ throw new IllegalArgumentException("Cannot use path " + TITAN_CASSANDRA_KEYSPACE
+ + " in initial configuration - this path is used"
+ + " to specify graph names");
+
+ this.graphConfiguration.putAll(properties);
+ return this;
+ }
+
+ public Builder property(String path, Object value) {
+ if (path.equals(TITAN_CASSANDRA_KEYSPACE))
+ throw new IllegalArgumentException("Cannot use path " + TITAN_CASSANDRA_KEYSPACE
+ + " in initial configuration - this path is used"
+ + " to specify graph names");
+ graphConfiguration.put(path, value);
+ return this;
+ }
+
+ public TitanChampGraphImpl build() {
+ return new TitanChampGraphImpl(this);
+ }
+ }
+
+ @Override
+ protected TitanGraph getGraph() {
+ return graph;
+ }
+
+ @Override
+ protected ChampSchemaEnforcer getSchemaEnforcer() {
+ return SCHEMA_ENFORCER;
+ }
+
+ public void executeStoreObjectIndex(ChampObjectIndex index) {
+ if (isShutdown()) throw new IllegalStateException("Cannot call storeObjectIndex() after shutdown has been initiated");
+
+ final TitanGraph graph = getGraph();
+ final TitanManagement createIndexMgmt = graph.openManagement();
+ final PropertyKey pk = createIndexMgmt.getOrCreatePropertyKey(index.getField().getName());
+
+ if (createIndexMgmt.getGraphIndex(index.getName()) != null) {
+ createIndexMgmt.rollback();
+ return; //Ignore, index already exists
+ }
+
+ createIndexMgmt.buildIndex(index.getName(), Vertex.class).addKey(pk).buildCompositeIndex();
+
+ createIndexMgmt.commit();
+ graph.tx().commit();
+
+ awaitIndexCreation(index.getName());
+ }
+
+ @Override
+ public Optional<ChampObjectIndex> retrieveObjectIndex(String indexName) {
+ if (isShutdown()) throw new IllegalStateException("Cannot call retrieveObjectIndex() after shutdown has been initiated");
+
+ final TitanManagement retrieveIndexMgmt = getGraph().openManagement();
+ final TitanGraphIndex index = retrieveIndexMgmt.getGraphIndex(indexName);
+
+ if (index == null) return Optional.empty();
+ if (index.getIndexedElement() != TitanVertex.class) return Optional.empty();
+
+ return Optional.of(ChampObjectIndex.create()
+ .ofName(indexName)
+ .onType(ChampObject.ReservedTypes.ANY.toString())
+ .forField(index.getFieldKeys()[0].name())
+ .build());
+ }
+
+ @Override
+ public Stream<ChampObjectIndex> retrieveObjectIndices() {
+ if (isShutdown()) throw new IllegalStateException("Cannot call retrieveObjectIndices() after shutdown has been initiated");
+
+ final TitanManagement createIndexMgmt = getGraph().openManagement();
+ final Iterator<TitanGraphIndex> indices = createIndexMgmt.getGraphIndexes(Vertex.class).iterator();
+
+ final Iterator<ChampObjectIndex> objIter = new Iterator<ChampObjectIndex> () {
+
+ private ChampObjectIndex next;
+
+ @Override
+ public boolean hasNext() {
+ if (indices.hasNext()) {
+ final TitanGraphIndex index = indices.next();
+
+ next = ChampObjectIndex.create()
+ .ofName(index.name())
+ .onType(ChampObject.ReservedTypes.ANY.toString())
+ .forField(index.getFieldKeys()[0].name())
+ .build();
+ return true;
+ }
+
+ next = null;
+ return false;
+ }
+
+ @Override
+ public ChampObjectIndex next() {
+ if (next == null) throw new NoSuchElementException();
+
+ return next;
+ }
+ };
+
+ return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
+ objIter, Spliterator.ORDERED | Spliterator.NONNULL), false);
+ }
+
+ public void executeDeleteObjectIndex(String indexName) throws ChampIndexNotExistsException {
+ if (isShutdown()) throw new IllegalStateException("Cannot call deleteObjectIndex() after shutdown has been initiated");
+
+ throw new UnsupportedOperationException("Cannot delete indices using the TitanChampImpl");
+ }
+
+ public void executeStoreRelationshipIndex(ChampRelationshipIndex index) {
+ if (isShutdown()) throw new IllegalStateException("Cannot call storeRelationshipIndex() after shutdown has been initiated");
+
+ final TitanGraph graph = getGraph();
+ final TitanManagement createIndexMgmt = graph.openManagement();
+ final PropertyKey pk = createIndexMgmt.getOrCreatePropertyKey(index.getField().getName());
+
+ if (createIndexMgmt.getGraphIndex(index.getName()) != null) return; //Ignore, index already exists
+ createIndexMgmt.buildIndex(index.getName(), Edge.class).addKey(pk).buildCompositeIndex();
+
+ createIndexMgmt.commit();
+ graph.tx().commit();
+
+ awaitIndexCreation(index.getName());
+ }
+
+ @Override
+ public Optional<ChampRelationshipIndex> retrieveRelationshipIndex(String indexName) {
+ if (isShutdown()) throw new IllegalStateException("Cannot call retrieveRelationshipIndex() after shutdown has been initiated");
+
+ final TitanManagement retrieveIndexMgmt = getGraph().openManagement();
+ final TitanGraphIndex index = retrieveIndexMgmt.getGraphIndex(indexName);
+
+ if (index == null) return Optional.empty();
+ if (index.getIndexedElement() != TitanEdge.class) return Optional.empty();
+
+ return Optional.of(ChampRelationshipIndex.create()
+ .ofName(indexName)
+ .onType(ChampObject.ReservedTypes.ANY.toString())
+ .forField(index.getFieldKeys()[0].name())
+ .build());
+ }
+
+ @Override
+ public Stream<ChampRelationshipIndex> retrieveRelationshipIndices() {
+ if (isShutdown()) throw new IllegalStateException("Cannot call retrieveRelationshipIndices() after shutdown has been initiated");
+
+ final TitanManagement createIndexMgmt = getGraph().openManagement();
+ final Iterator<TitanGraphIndex> indices = createIndexMgmt.getGraphIndexes(Edge.class).iterator();
+
+ final Iterator<ChampRelationshipIndex> objIter = new Iterator<ChampRelationshipIndex> () {
+
+ private ChampRelationshipIndex next;
+
+ @Override
+ public boolean hasNext() {
+ if (indices.hasNext()) {
+ final TitanGraphIndex index = indices.next();
+
+ next = ChampRelationshipIndex.create()
+ .ofName(index.name())
+ .onType(ChampRelationship.ReservedTypes.ANY.toString())
+ .forField(index.getFieldKeys()[0].name())
+ .build();
+ return true;
+ }
+
+ next = null;
+ return false;
+ }
+
+ @Override
+ public ChampRelationshipIndex next() {
+ if (next == null) throw new NoSuchElementException();
+
+ return next;
+ }
+ };
+
+ return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
+ objIter, Spliterator.ORDERED | Spliterator.NONNULL), false);
+ }
+
+ public void executeDeleteRelationshipIndex(String indexName) throws ChampIndexNotExistsException {
+ if (isShutdown()) throw new IllegalStateException("Cannot call deleteRelationshipIndex() after shutdown has been initiated");
+
+ throw new UnsupportedOperationException("Cannot delete indices using the TitanChampImpl");
+ }
+
+ private Cardinality getTitanCardinality(ChampCardinality cardinality) {
+ switch (cardinality) {
+ case LIST:
+ return Cardinality.LIST;
+ case SET:
+ return Cardinality.SET;
+ case SINGLE:
+ return Cardinality.SINGLE;
+ default:
+ throw new RuntimeException("Unknown ChampCardinality " + cardinality);
+ }
+ }
+
+ private void awaitIndexCreation(String indexName) {
+ //Wait for the index to become available
+ try {
+ if (ManagementSystem.awaitGraphIndexStatus(graph, indexName)
+ .status(SchemaStatus.ENABLED)
+ .timeout(1, ChronoUnit.SECONDS)
+ .call()
+ .getSucceeded()) {
+ return; //Empty graphs immediately ENABLE indices
+ }
+
+ if (!ManagementSystem.awaitGraphIndexStatus(graph, indexName)
+ .status(SchemaStatus.REGISTERED)
+ .timeout(REGISTER_OBJECT_INDEX_TIMEOUT_SECS, ChronoUnit.SECONDS)
+ .call()
+ .getSucceeded()) {
+ LOGGER.warn("Object index was created, but timed out while waiting for it to be registered");
+ return;
+ }
+ } catch (InterruptedException e) {
+ LOGGER.warn("Interrupted while waiting for object index creation status");
+ return;
+ }
+
+ //Reindex the existing data
+
+ try {
+ final TitanManagement updateIndexMgmt = graph.openManagement();
+ updateIndexMgmt.updateIndex(updateIndexMgmt.getGraphIndex(indexName),SchemaAction.REINDEX).get();
+ updateIndexMgmt.commit();
+ } catch (InterruptedException e) {
+ LOGGER.warn("Interrupted while reindexing for object index");
+ return;
+ } catch (ExecutionException e) {
+ LOGGER.warn("Exception occurred during reindexing procedure for creating object index " + indexName, e);
+ }
+
+ try {
+ ManagementSystem.awaitGraphIndexStatus(graph, indexName)
+ .status(SchemaStatus.ENABLED)
+ .timeout(10, ChronoUnit.MINUTES)
+ .call();
+ } catch (InterruptedException e) {
+ LOGGER.warn("Interrupted while waiting for index to transition to ENABLED state");
+ return;
+ }
+ }
+
+ @Override
+ public ChampCapabilities capabilities() {
+ return CAPABILITIES;
+ }
+
+ @Override
+ public void storeSchema(ChampSchema schema) throws ChampSchemaViolationException {
+ if (isShutdown()) throw new IllegalStateException("Cannot call storeSchema() after shutdown has been initiated");
+
+ final ChampSchema currentSchema = retrieveSchema();
+ final TitanManagement mgmt = getGraph().openManagement();
+
+ try {
+ for (ChampObjectConstraint objConstraint : schema.getObjectConstraints().values()) {
+ for (ChampPropertyConstraint propConstraint : objConstraint.getPropertyConstraints()) {
+ final Optional<ChampObjectConstraint> currentObjConstraint = currentSchema.getObjectConstraint(objConstraint.getType());
+
+ if (currentObjConstraint.isPresent()) {
+ final Optional<ChampPropertyConstraint> currentPropConstraint = currentObjConstraint.get().getPropertyConstraint(propConstraint.getField().getName());
+
+ if (currentPropConstraint.isPresent() && currentPropConstraint.get().compareTo(propConstraint) != 0) {
+ throw new ChampSchemaViolationException("Cannot update already existing property on object type " + objConstraint.getType() + ": " + propConstraint);
+ }
+ }
+
+ final String newPropertyKeyName = propConstraint.getField().getName();
+
+ if (mgmt.getPropertyKey(newPropertyKeyName) != null) continue; //Check Titan to see if another node created this property key
+
+ mgmt.makePropertyKey(newPropertyKeyName)
+ .dataType(propConstraint.getField().getJavaType())
+ .cardinality(getTitanCardinality(propConstraint.getCardinality()))
+ .make();
+ }
+ }
+
+ for (ChampRelationshipConstraint relConstraint : schema.getRelationshipConstraints().values()) {
+
+ final Optional<ChampRelationshipConstraint> currentRelConstraint = currentSchema.getRelationshipConstraint(relConstraint.getType());
+
+ for (ChampPropertyConstraint propConstraint : relConstraint.getPropertyConstraints()) {
+
+ if (currentRelConstraint.isPresent()) {
+ final Optional<ChampPropertyConstraint> currentPropConstraint = currentRelConstraint.get().getPropertyConstraint(propConstraint.getField().getName());
+
+ if (currentPropConstraint.isPresent() && currentPropConstraint.get().compareTo(propConstraint) != 0) {
+ throw new ChampSchemaViolationException("Cannot update already existing property on relationship type " + relConstraint.getType());
+ }
+ }
+
+ final String newPropertyKeyName = propConstraint.getField().getName();
+
+ if (mgmt.getPropertyKey(newPropertyKeyName) != null) continue; //Check Titan to see if another node created this property key
+
+ mgmt.makePropertyKey(newPropertyKeyName)
+ .dataType(propConstraint.getField().getJavaType())
+ .cardinality(getTitanCardinality(propConstraint.getCardinality()))
+ .make();
+ }
+
+ final EdgeLabel edgeLabel = mgmt.getEdgeLabel(relConstraint.getType());
+
+ if (edgeLabel != null) mgmt.makeEdgeLabel(relConstraint.getType())
+ .directed()
+ .make();
+ }
+
+ mgmt.commit();
+
+ super.storeSchema(schema);
+ } catch (SchemaViolationException | ChampSchemaViolationException e) {
+ mgmt.rollback();
+ throw new ChampSchemaViolationException(e);
+ }
+ }
+
+ @Override
+ public GraphTraversal<?, ?> hasLabel(GraphTraversal<?, ?> query, Object type) {
+ return query.hasLabel((String) type);
+ }
+}
diff --git a/champ-lib/champ-titan/src/main/java/org/onap/aai/champtitan/perf/ChampAPIPerformanceTest.java b/champ-lib/champ-titan/src/main/java/org/onap/aai/champtitan/perf/ChampAPIPerformanceTest.java
new file mode 100644
index 0000000..37ea4ff
--- /dev/null
+++ b/champ-lib/champ-titan/src/main/java/org/onap/aai/champtitan/perf/ChampAPIPerformanceTest.java
@@ -0,0 +1,458 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * 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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.onap.aai.champtitan.perf;
+
+import com.thinkaurelius.titan.core.TitanFactory;
+import com.thinkaurelius.titan.core.TitanFactory.Builder;
+import com.thinkaurelius.titan.core.TitanGraph;
+import com.thinkaurelius.titan.core.util.TitanCleanup;
+
+import org.onap.aai.champcore.ChampGraph;
+import org.onap.aai.champcore.exceptions.*;
+import org.onap.aai.champcore.graph.impl.InMemoryChampGraphImpl;
+import org.onap.aai.champcore.model.*;
+import org.onap.aai.champcore.schema.ChampSchemaEnforcer;
+import org.onap.aai.champtitan.graph.impl.TitanChampGraphImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+import java.util.Map.Entry;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class ChampAPIPerformanceTest {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ChampAPIPerformanceTest.class);
+
+ private static final int NUM_OBJECTS = 1000;
+ private static final int NUM_RELATIONSHIPS = 1000;
+ private static final String GRAPH_NAME = ChampAPIPerformanceTest.class.getSimpleName();
+
+ private static final String getGraphName() {
+ return GRAPH_NAME;
+ }
+
+ private static void cleanUp(String graphName, Map<String, String> settings) {
+ LOGGER.debug("Cleaning up graph {}", graphName);
+
+ try {
+ final Builder graphBuilder = TitanFactory.build();
+
+ for (Entry<String, String> setting : settings.entrySet()) {
+ graphBuilder.set(setting.getKey(), setting.getValue());
+ }
+
+ final String storageBackend = settings.getOrDefault("storage.backend", "inmemory");
+
+ if (storageBackend.equals("cassandra") ||
+ storageBackend.equals("cassandrathrift") ||
+ storageBackend.equals("astyanax") ||
+ storageBackend.equals("embeddedcassandra")) {
+ graphBuilder.set("storage.cassandra.keyspace", graphName);
+ } else if (storageBackend.equals("hbase")) {
+ graphBuilder.set("storage.hbase.table", graphName);
+ }
+
+ final TitanGraph graph = graphBuilder.open();
+
+ graph.close();
+ TitanCleanup.clear(graph);
+ } catch (IllegalArgumentException e) {
+ LOGGER.warn("Could not clean up graph - unable to instantiate");
+ }
+ }
+
+ public static void main(String[] args) {
+
+ if (args.length < 1 || !args[0].startsWith("--champcore.graph.type=")) {
+ throw new RuntimeException("Must provide --champcore.graph.type=" + " as first parameter");
+ }
+
+ final String graphType = args[0].split("=")[1];
+
+ final Map<String, String> settings = new HashMap<String, String> ();
+
+ for (int i = 1; i < args.length; i++) {
+ if (!args[i].startsWith("--")) {
+ throw new RuntimeException("Bad command line argument: " + args[i]);
+ }
+
+ final String[] keyValue = args[i].replaceFirst("--", "").split("=");
+
+ if (keyValue.length != 2) {
+ throw new RuntimeException("Bad command line argument: " + args[i]);
+ }
+
+ settings.put(keyValue[0], keyValue[1]);
+ }
+
+ LOGGER.info("Provided graph settings: " + settings);
+
+ if (graphType.equals("TITAN")) {
+ cleanUp(getGraphName(), settings);
+ }
+
+ LOGGER.info("Graph cleaned, instantiating ChampGraph");
+
+ final ChampGraph graph;
+
+ switch (graphType) {
+ case "IN_MEMORY":
+ final InMemoryChampGraphImpl.Builder inMemGraphBuilder = new InMemoryChampGraphImpl.Builder();
+
+ if (settings.containsKey("champcore.schema.enforcer")) {
+ final String schemaEnforcerClassStr = settings.get("champcore.schema.enforcer");
+
+ try {
+ final Class<?> schemaEnforcer = Class.forName(schemaEnforcerClassStr);
+
+ if (!schemaEnforcer.isAssignableFrom(ChampSchemaEnforcer.class)) {
+ throw new RuntimeException("Unknown ChampSchemaEnforcer " + schemaEnforcer);
+ }
+
+ inMemGraphBuilder.schemaEnforcer((ChampSchemaEnforcer) schemaEnforcer.newInstance());
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ } catch (InstantiationException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ graph = inMemGraphBuilder.build();
+ break;
+ case "TITAN":
+ final TitanChampGraphImpl.Builder graphBuilder = new TitanChampGraphImpl.Builder(getGraphName());
+
+ for (Entry<String, String> setting : settings.entrySet()) {
+ graphBuilder.property(setting.getKey(), setting.getValue());
+ }
+
+ graph = graphBuilder.build();
+ break;
+ default:
+ throw new RuntimeException("Unknown ChampGraph.Type " + graphType);
+ }
+
+ try {
+ if (graph.queryObjects(Collections.emptyMap(), Optional.empty()).limit(1).count() > 0) {
+ graph.shutdown();
+ throw new RuntimeException("Expected empty graph");
+ }
+ } catch (ChampTransactionException e) {
+ throw new RuntimeException("Transaction failure");
+ }
+
+ LOGGER.info("Graph instantiated, warming up JVM");
+ warmUp(graph);
+
+ LOGGER.info("Warm up complete, starting to record performance measurements");
+
+ LOGGER.info("Performance without indexing/schema");
+
+ storeObjects(graph, false);
+ storeRelationships(graph, false);
+ retrieveIndividualObjects(graph, false);
+ retrieveBulkRelationships(graph, false);
+ retrieveIndividualRelationships(graph, false);
+
+ LOGGER.info("Storing indices + schema");
+
+ storeIndices(graph, false);
+ storeSchema(graph, false);
+
+ LOGGER.info("Stored indices + schema");
+
+ LOGGER.info("Performance with indexing + schema");
+
+ storeObjects(graph, false);
+ storeRelationships(graph, false);
+ retrieveIndividualObjects(graph, false);
+ retrieveBulkRelationships(graph, false);
+ retrieveIndividualRelationships(graph, false);
+
+ LOGGER.info("Performance test complete, shutting down graph");
+
+ graph.shutdown();
+
+ LOGGER.info("Graph shutdown, JVM exiting");
+ }
+
+ private static void storeSchema(ChampGraph graph, boolean warmUp) {
+ try {
+ graph.storeSchema(
+ ChampSchema.create()
+ .withObjectConstraint()
+ .onType("foo")
+ .withPropertyConstraint()
+ .onField("fooObjectNumber")
+ .optional()
+ .build()
+ .build()
+ .withRelationshipConstraint()
+ .onType("bar")
+ .withPropertyConstraint()
+ .onField("barObjectNumber")
+ .ofType(ChampField.Type.INTEGER)
+ .optional()
+ .build()
+ .build()
+ .build()
+ );
+ } catch (ChampSchemaViolationException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ private static void storeIndices(ChampGraph graph, boolean warmUp) {
+ graph.storeObjectIndex(
+ ChampObjectIndex.create()
+ .ofName("objectNumberIndex")
+ .onType("foo")
+ .forField("objectNumber")
+ .build()
+ );
+
+ graph.storeRelationshipIndex(ChampRelationshipIndex.create()
+ .ofName("relationshipNumberIndex")
+ .onType("bazz")
+ .forField("relationshipNumber")
+ .build()
+ );
+ }
+
+ private static void warmUp(ChampGraph graph) {
+ storeObjects(graph, false);
+ storeRelationships(graph, false);
+ retrieveIndividualObjects(graph, false);
+ retrieveBulkRelationships(graph, false);
+ retrieveIndividualRelationships(graph, false);
+ }
+
+ private static void retrieveIndividualRelationships(ChampGraph graph, boolean warmUp) {
+ final double[] latencies = new double[NUM_RELATIONSHIPS];
+ final long totalStartTime = System.nanoTime();
+
+ for (int i = 0; i < NUM_RELATIONSHIPS; i++) {
+ final long startTime = System.nanoTime();
+
+ Stream<ChampRelationship> objects;
+ try {
+ objects = graph.queryRelationships(Collections.singletonMap("relationshipNumber", i), Optional.empty());
+ } catch (ChampTransactionException e) {
+ throw new RuntimeException(e);
+ }
+
+ objects.findFirst().get();
+ final double elapsedMs = (System.nanoTime() - startTime) / 1000.0 / 1000.0;
+ latencies[i] = elapsedMs;
+ }
+
+ final double totalElapsedTimeSecs = (System.nanoTime() - totalStartTime) / 1000.0 / 1000.0 / 1000.0;
+ LOGGER.info("Individually read " + NUM_RELATIONSHIPS + " relationships in " + totalElapsedTimeSecs + "s (" + NUM_RELATIONSHIPS / totalElapsedTimeSecs + " relationships/s)");
+
+ Arrays.sort(latencies);
+
+ if (!warmUp) {
+ LOGGER.info("Retrieve individual relationship latencies");
+ LOGGER.info("\t50th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.50)]);
+ LOGGER.info("\t75th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.75)]);
+ LOGGER.info("\t90th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.90)]);
+ LOGGER.info("\t99th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.99)]);
+ }
+ }
+
+ private static void retrieveIndividualObjects(ChampGraph graph, boolean warmUp) {
+
+ final double[] latencies = new double[NUM_OBJECTS];
+ final long totalStartTime = System.nanoTime();
+
+ for (int i = 0; i < NUM_OBJECTS; i++) {
+ final long startTime = System.nanoTime();
+ Stream<ChampObject> objects;
+ try {
+ objects = graph.queryObjects(Collections.singletonMap("objectNumber", i), Optional.empty());
+ } catch (ChampTransactionException e) {
+ throw new RuntimeException(e);
+ }
+
+ objects.findFirst().get();
+
+ final double elapsedMs = (System.nanoTime() - startTime) / 1000.0 / 1000.0;
+
+ latencies[i] = elapsedMs;
+ }
+
+ final double totalElapsedTimeSecs = (System.nanoTime() - totalStartTime) / 1000.0 / 1000.0 / 1000.0;
+
+ LOGGER.info("Individually read " + NUM_OBJECTS + " objects in " + totalElapsedTimeSecs + "s (" + NUM_OBJECTS / totalElapsedTimeSecs + " objects/s)");
+ Arrays.sort(latencies);
+
+ if (!warmUp) {
+ LOGGER.info("Retrieve individual object latencies");
+ LOGGER.info("\t50th percentile: " + latencies[(int) (NUM_OBJECTS * 0.50)]);
+ LOGGER.info("\t75th percentile: " + latencies[(int) (NUM_OBJECTS * 0.75)]);
+ LOGGER.info("\t90th percentile: " + latencies[(int) (NUM_OBJECTS * 0.90)]);
+ LOGGER.info("\t99th percentile: " + latencies[(int) (NUM_OBJECTS * 0.99)]);
+ }
+ }
+
+ private static List<ChampObject> retrieveBulkObjects(ChampGraph graph, boolean warmUp) {
+
+ final long startTime = System.nanoTime();
+ Stream<ChampObject> objects;
+ try {
+ objects = graph.queryObjects(
+ Collections.singletonMap(
+ ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_TYPE.toString(), "foo"
+ ), Optional.empty()
+ );
+ } catch (ChampTransactionException e) {
+ throw new RuntimeException(e);
+ }
+
+ final List<ChampObject> objectsAsList = objects.collect(Collectors.toList());
+ final double elapsedSecs = (System.nanoTime() - startTime) / 1000.0 / 1000.0 / 1000.0;
+
+ if (!warmUp) {
+ LOGGER.info("Bulk read " + objectsAsList.size() + " objects in " + elapsedSecs + "s (" + objectsAsList.size() / elapsedSecs + " objects/s)");
+ }
+
+ return objectsAsList;
+ }
+
+ private static List<ChampRelationship> retrieveBulkRelationships(ChampGraph graph, boolean warmUp) {
+ final long startTime = System.nanoTime();
+ Stream<ChampRelationship> relationships;
+ try {
+ relationships = graph.queryRelationships(
+ Collections.singletonMap(
+ ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString(), "bazz"
+ ), Optional.empty()
+ );
+ } catch (ChampTransactionException e) {
+ throw new RuntimeException(e);
+ }
+
+ final List<ChampRelationship> relationshipsAsList = relationships.collect(Collectors.toList());
+ final double elapsedSecs = (System.nanoTime() - startTime) / 1000.0 / 1000.0 / 1000.0;
+
+ if (!warmUp) {
+ LOGGER.info("Bulk read " + relationshipsAsList.size() + " relationships in " + elapsedSecs + "s (" + relationshipsAsList.size() / elapsedSecs + " relationships/s)");
+ }
+
+ return relationshipsAsList;
+ }
+
+ private static void storeObjects(ChampGraph graph, boolean warmUp) {
+ final double[] latencies = new double[NUM_OBJECTS];
+ final long totalStartTime = System.nanoTime();
+
+ for (int i = 0; i < NUM_OBJECTS; i++) {
+ try {
+ final long startTime = System.nanoTime();
+
+ graph.storeObject(
+ ChampObject.create()
+ .ofType("foo")
+ .withoutKey()
+ .withProperty("objectNumber", i)
+ .build(), Optional.empty()
+ );
+
+ final double elapsedMs = (System.nanoTime() - startTime) / 1000.0 / 1000.0;
+ latencies[i] = elapsedMs;
+ } catch (ChampMarshallingException e) {
+ throw new RuntimeException(e);
+ } catch (ChampSchemaViolationException e) {
+ //Ignore, no schema set
+ } catch (ChampObjectNotExistsException e) {
+ //Ignore, not an update
+ } catch (ChampTransactionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ final double totalElapsedTimeSecs = (System.nanoTime() - totalStartTime) / 1000.0 / 1000.0 / 1000.0;
+ LOGGER.info("Wrote " + NUM_OBJECTS + " objects in " + totalElapsedTimeSecs + "s (" + NUM_OBJECTS / totalElapsedTimeSecs + " objects/s)");
+
+ Arrays.sort(latencies);
+
+ if (!warmUp) {
+ LOGGER.info("Store object latencies");
+ LOGGER.info("\t50th percentile: " + latencies[(int) (NUM_OBJECTS * 0.50)]);
+ LOGGER.info("\t75th percentile: " + latencies[(int) (NUM_OBJECTS * 0.75)]);
+ LOGGER.info("\t90th percentile: " + latencies[(int) (NUM_OBJECTS * 0.90)]);
+ LOGGER.info("\t99th percentile: " + latencies[(int) (NUM_OBJECTS * 0.99)]);
+ }
+ }
+
+ private static void storeRelationships(ChampGraph graph, boolean warmUp) {
+ final List<ChampObject> objects = retrieveBulkObjects(graph, warmUp);
+ final double[] latencies = new double[NUM_RELATIONSHIPS];
+ final long totalStartTime = System.nanoTime();
+
+ for (int i = 0; i < NUM_RELATIONSHIPS; i++) {
+ try {
+ final long startTime = System.nanoTime();
+
+ graph.storeRelationship(
+ new ChampRelationship.Builder(
+ objects.get(i % objects.size()), objects.get((i + 1) % objects.size()), "bazz"
+ ).property("relationshipNumber", i)
+ .build()
+ , Optional.empty());
+
+ final double elapsedMs = (System.nanoTime() - startTime) / 1000.0 / 1000.0;
+
+ latencies[i] = elapsedMs;
+ } catch (ChampMarshallingException e) {
+ throw new RuntimeException(e);
+ } catch (ChampObjectNotExistsException e) {
+ throw new RuntimeException(e);
+ } catch (ChampSchemaViolationException e) {
+ throw new RuntimeException(e);
+ } catch (ChampRelationshipNotExistsException e) {
+ throw new RuntimeException(e);
+ } catch (ChampUnmarshallingException e) {
+ throw new RuntimeException(e);
+ } catch (ChampTransactionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ final double totalElapsedTimeSecs = (System.nanoTime() - totalStartTime) / 1000.0 / 1000.0 / 1000.0;
+ LOGGER.info("Wrote " + NUM_RELATIONSHIPS + " relationships in " + totalElapsedTimeSecs + "s (" + NUM_RELATIONSHIPS / totalElapsedTimeSecs + " relationships/s)");
+
+ Arrays.sort(latencies);
+
+ if (!warmUp) {
+ LOGGER.info("Store relationship latencies");
+ LOGGER.info("\t50th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.50)]);
+ LOGGER.info("\t75th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.75)]);
+ LOGGER.info("\t90th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.90)]);
+ LOGGER.info("\t99th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.99)]);
+ }
+ }
+}
diff --git a/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/concurrency/ConcurrencyTest.java b/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/concurrency/ConcurrencyTest.java
new file mode 100644
index 0000000..9e08f55
--- /dev/null
+++ b/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/concurrency/ConcurrencyTest.java
@@ -0,0 +1,33 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * 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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.onap.aai.champtitan.concurrency;
+
+import org.junit.Test;
+
+public class ConcurrencyTest {
+
+ @Test
+ public void runInMemoryConcurrentTest() {
+ org.onap.aai.champcore.concurrency.ConcurrencyTest baseTest = new org.onap.aai.champcore.concurrency.ConcurrencyTest();
+ baseTest.runConcurrentTest("TITAN");
+ }
+}
diff --git a/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampAPITest.java b/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampAPITest.java
new file mode 100644
index 0000000..8d4bc57
--- /dev/null
+++ b/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampAPITest.java
@@ -0,0 +1,40 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * 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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.onap.aai.champtitan.core;
+
+import org.junit.Test;
+import org.onap.aai.champtitan.graph.impl.TitanChampGraphImpl;
+
+public class ChampAPITest {
+ @Test
+ public void testChampGraphInstantiation() throws Exception {
+ TitanChampGraphImpl graph = new TitanChampGraphImpl.Builder("testGraph")
+ .property("storage.backend", "inmemory")
+ .build();
+
+ org.onap.aai.champcore.core.ChampAPITest baseTest = new org.onap.aai.champcore.core.ChampAPITest();
+
+ baseTest.testChampGraphInstantiation(graph);
+ }
+
+
+}
diff --git a/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampObjectIndexTest.java b/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampObjectIndexTest.java
new file mode 100644
index 0000000..bd9295f
--- /dev/null
+++ b/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampObjectIndexTest.java
@@ -0,0 +1,48 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * 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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.onap.aai.champtitan.core;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.onap.aai.champcore.exceptions.ChampIndexNotExistsException;
+import org.onap.aai.champcore.model.ChampCardinality;
+import org.onap.aai.champtitan.graph.impl.TitanChampGraphImpl;
+
+public class ChampObjectIndexTest {
+ @Rule
+ public final ExpectedException exception = ExpectedException.none();
+
+ @Test
+ public void testChampObjectIndexCrud() throws Exception {
+ TitanChampGraphImpl graph = new TitanChampGraphImpl.Builder("testGraph")
+ .property("storage.backend", "inmemory")
+ .build();
+
+ org.onap.aai.champcore.core.ChampObjectIndexTest.testChampObjectIndexCrud(graph);
+
+ graph.shutdown();
+
+ exception.expect(IllegalStateException.class);
+ graph.executeDeleteObjectIndex("any");
+ }
+}
diff --git a/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampRelationshipIndexTest.java b/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampRelationshipIndexTest.java
new file mode 100644
index 0000000..e515101
--- /dev/null
+++ b/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampRelationshipIndexTest.java
@@ -0,0 +1,38 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * 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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.onap.aai.champtitan.core;
+
+import org.junit.Test;
+import org.onap.aai.champtitan.graph.impl.TitanChampGraphImpl;
+
+public class ChampRelationshipIndexTest {
+ @Test
+ public void testChampRelationshipIndexCrud() {
+ TitanChampGraphImpl graph = new TitanChampGraphImpl.Builder("testGraph")
+ .property("storage.backend", "inmemory")
+ .build();
+ org.onap.aai.champcore.core.ChampRelationshipIndexTest baseTest = new org.onap.aai.champcore.core.ChampRelationshipIndexTest();
+ baseTest.testChampRelationshipIndexCrud(graph);
+
+ graph.shutdown();
+ }
+}
diff --git a/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampSchemaTest.java b/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampSchemaTest.java
new file mode 100644
index 0000000..ac29fc8
--- /dev/null
+++ b/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampSchemaTest.java
@@ -0,0 +1,37 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * 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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.onap.aai.champtitan.core;
+
+import org.junit.Test;
+import org.onap.aai.champtitan.graph.impl.TitanChampGraphImpl;
+
+public class ChampSchemaTest {
+ @Test
+ public void testChampSchemaCrud() {
+ TitanChampGraphImpl graph = new TitanChampGraphImpl.Builder("testGraph")
+ .property("storage.backend", "inmemory")
+ .build();
+ org.onap.aai.champcore.core.ChampSchemaTest.testChampSchemaCrud(graph);
+
+ graph.shutdown();
+ }
+}
diff --git a/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampTransactionTest.java b/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampTransactionTest.java
new file mode 100644
index 0000000..aafc053
--- /dev/null
+++ b/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampTransactionTest.java
@@ -0,0 +1,335 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * 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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.onap.aai.champtitan.core;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Optional;
+import java.util.concurrent.CountDownLatch;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.aai.champcore.ChampTransaction;
+import org.onap.aai.champcore.exceptions.ChampMarshallingException;
+import org.onap.aai.champcore.exceptions.ChampObjectNotExistsException;
+import org.onap.aai.champcore.exceptions.ChampRelationshipNotExistsException;
+import org.onap.aai.champcore.exceptions.ChampSchemaViolationException;
+import org.onap.aai.champcore.exceptions.ChampTransactionException;
+import org.onap.aai.champcore.exceptions.ChampUnmarshallingException;
+import org.onap.aai.champcore.model.ChampObject;
+import org.onap.aai.champcore.model.ChampRelationship;
+import org.onap.aai.champtitan.graph.impl.TitanChampGraphImpl;
+
+public class ChampTransactionTest {
+
+ public TitanChampGraphImpl graph = null;
+ public CountDownLatch latch = new CountDownLatch(2);
+ public ChampObject[] storedVertices = new ChampObject[2];
+
+
+ @Before
+ public void setup() {
+ graph = new TitanChampGraphImpl.Builder("TransactionTestGraph")
+ .property("storage.backend", "inmemory")
+ .build();
+ }
+
+
+ /**
+ * This test validates that multiple CRUD operations can be performed against vertices within
+ * the same transactional context and that, once the transaction is committed, the final
+ * state of the vertices reflects the sum of all of the operations performed within the
+ * transaction.
+ *
+ * @throws ChampMarshallingException
+ * @throws ChampSchemaViolationException
+ * @throws ChampObjectNotExistsException
+ * @throws ChampUnmarshallingException
+ * @throws ChampTransactionException
+ */
+ @Test
+ public void champObjectSingleTxTest() throws ChampMarshallingException, ChampSchemaViolationException, ChampObjectNotExistsException, ChampUnmarshallingException, ChampTransactionException {
+
+ ChampObject v1 = ChampObject.create()
+ .ofType("foo")
+ .withoutKey()
+ .withProperty("property1", "value1")
+ .withProperty("property2", "value2")
+ .build();
+
+ ChampObject v2 = ChampObject.create()
+ .ofType("foo")
+ .withoutKey()
+ .withProperty("property3", "value3")
+ .withProperty("property4", "value4")
+ .build();
+
+ ChampObject v3 = ChampObject.create()
+ .ofType("foo")
+ .withoutKey()
+ .withProperty("property5", "value5")
+ .withProperty("property6", "value6")
+ .build();
+
+ ChampObject v4 = ChampObject.create()
+ .ofType("foo")
+ .withoutKey()
+ .withProperty("property7", "value7")
+ .withProperty("property8", "value8")
+ .build();
+
+
+ // Open a transaction with the graph data store.
+ ChampTransaction tx = graph.openTransaction();
+
+ // Create all of our vertices.
+ ChampObject storedV1 = graph.storeObject(v1, Optional.of(tx));
+ ChampObject storedV2 = graph.storeObject(v2, Optional.of(tx));
+ ChampObject storedV3 = graph.storeObject(v3, Optional.of(tx));
+ ChampObject storedV4 = graph.storeObject(v4, Optional.of(tx));
+
+ // Now, within the same transactional context, do a replacement against one of the
+ // vertices that we just created.
+ ChampObject replacedV2 = graph.replaceObject(ChampObject.create()
+ .ofType("foo")
+ .withKey(storedV2.getKey().get())
+ .withProperty("replacedProperty3", "replacedValue3")
+ .withProperty("replacedProperty4", "replacedValue4")
+ .build(),
+ Optional.of(tx));
+
+ // Within the same transactional context, do an update against one of the vertices
+ // that we just created.
+ final ChampObject updatedV3 = graph.storeObject(ChampObject.create()
+ .from(storedV3)
+ .withKey(storedV3.getKey().get())
+ .withProperty("updatedProperty", "updatedValue")
+ .build(), Optional.of(tx));
+
+ // Within the same transactional context, delete one of the vertices that we just
+ // created.
+ graph.deleteObject(storedV4.getKey().get(), Optional.of(tx));
+
+ // Finally, commit our transaction.
+ tx.commit();
+
+ tx = graph.openTransaction();
+
+ Optional<ChampObject> retrievedV1 = graph.retrieveObject(storedV1.getKey().get(), Optional.of(tx));
+ assertTrue(retrievedV1.isPresent());
+ assertTrue(retrievedV1.get().getProperty("property1").get().equals("value1"));
+ assertTrue(retrievedV1.get().getProperty("property2").get().equals("value2"));
+
+
+ Optional<ChampObject> retrievedV2 = graph.retrieveObject(storedV2.getKey().get(), Optional.of(tx));
+ assertTrue(retrievedV2.isPresent());
+ assertTrue(retrievedV2.get().getProperty("replacedProperty3").get().equals("replacedValue3"));
+ assertTrue(retrievedV2.get().getProperty("replacedProperty4").get().equals("replacedValue4"));
+ assertFalse(retrievedV2.get().getProperty("value3").isPresent());
+ assertFalse(retrievedV2.get().getProperty("value4").isPresent());
+
+ Optional<ChampObject> retrievedV3 = graph.retrieveObject(storedV3.getKey().get(), Optional.of(tx));
+ assertTrue(retrievedV3.isPresent());
+ assertTrue(retrievedV3.get().getProperty("property5").get().equals("value5"));
+ assertTrue(retrievedV3.get().getProperty("property6").get().equals("value6"));
+ assertTrue(retrievedV3.get().getProperty("updatedProperty").get().equals("updatedValue"));
+
+
+ Optional<ChampObject> retrievedV4 = graph.retrieveObject(storedV4.getKey().get(), Optional.of(tx));
+ assertFalse("Deleted vertex should not be present in graph", retrievedV4.isPresent());
+
+ tx.commit();
+ }
+
+
+ /**
+ * This test validates that multiple threads can each open their own transactions with the
+ * graph data store, and that there is no leakage between each thread's transactions.
+ *
+ * @throws ChampMarshallingException
+ * @throws ChampSchemaViolationException
+ * @throws ChampObjectNotExistsException
+ * @throws ChampUnmarshallingException
+ * @throws ChampTransactionException
+ */
+ @Test
+ public void multipleTransactionTest() throws ChampMarshallingException, ChampSchemaViolationException, ChampObjectNotExistsException, ChampUnmarshallingException, ChampTransactionException {
+
+ ChampObject v1 = ChampObject.create()
+ .ofType("foo")
+ .withoutKey()
+ .withProperty("property1", "value1")
+ .withProperty("property2", "value2")
+ .build();
+
+ ChampObject v2 = ChampObject.create()
+ .ofType("bar")
+ .withoutKey()
+ .withProperty("property3", "value3")
+ .withProperty("property4", "value4")
+ .build();
+
+ // Instantiate and start our two transactional worker threads...
+ Thread thread1 = new Thread(new VertexWriter(v1, 0));
+ Thread thread2 = new Thread(new VertexWriter(v2, 1));
+ thread1.start();
+ thread2.start();
+
+ // and wait for the threads to complete.
+ try {
+ thread1.join();
+ thread2.join();
+
+ } catch (InterruptedException e) { }
+
+ // Now that all of our data has been committed, let's open a new transaction
+ // and verify that all of our vertices can be retrieved.
+ ChampTransaction tx3 = graph.openTransaction();
+
+ Optional<ChampObject> retrievedV1 = graph.retrieveObject(storedVertices[0].getKey().get(), Optional.of(tx3));
+ assertTrue(retrievedV1.isPresent());
+ Optional<ChampObject> retrievedV2 = graph.retrieveObject(storedVertices[1].getKey().get(), Optional.of(tx3));
+ assertTrue(retrievedV2.isPresent());
+
+ tx3.commit();
+ }
+
+
+ /**
+ * This method validates that edges can be successfully created within a single transaction.
+ *
+ * @throws ChampMarshallingException
+ * @throws ChampSchemaViolationException
+ * @throws ChampObjectNotExistsException
+ * @throws ChampUnmarshallingException
+ * @throws ChampRelationshipNotExistsException
+ * @throws ChampTransactionException
+ */
+ @Test
+ public void edgeTest() throws ChampMarshallingException, ChampSchemaViolationException, ChampObjectNotExistsException, ChampUnmarshallingException, ChampRelationshipNotExistsException, ChampTransactionException {
+
+ // Create the source and target vertices for our edge.
+ final ChampObject source = ChampObject.create()
+ .ofType("foo")
+ .withoutKey()
+ .withProperty("property1", "value1")
+ .build();
+
+ final ChampObject target = ChampObject.create()
+ .ofType("foo")
+ .withoutKey()
+ .build();
+
+ // Open a transaction with the graph data store.
+ ChampTransaction tx = graph.openTransaction();
+
+ // Now, create our vertices.
+ ChampObject storedSource = graph.storeObject(source, Optional.of(tx));
+ ChampObject storedTarget = graph.storeObject(target, Optional.of(tx));
+
+ // Create the edge between the vertices.
+ ChampRelationship relationship = new ChampRelationship.Builder(storedSource, storedTarget, "relationship")
+ .property("property-1", "value-1")
+ .property("property-2", 3)
+ .build();
+ ChampRelationship storedRelationship = graph.storeRelationship(relationship, Optional.of(tx));
+
+ // Validate that we can read back the edge within the transactional context.
+ Optional<ChampRelationship> retrievedRelationship = graph.retrieveRelationship(storedRelationship.getKey().get(), Optional.of(tx));
+ assertTrue("Failed to retrieve stored relationship", retrievedRelationship.isPresent());
+
+ // Commit our transaction.
+ graph.commitTransaction(tx);
+
+ // Now, open a new transaction.
+ tx = graph.openTransaction();
+
+ // Now, read back the edge that we just created again, validating that it was
+ // successfully committed to the graph.
+ retrievedRelationship = graph.retrieveRelationship(storedRelationship.getKey().get(), Optional.of(tx));
+ assertTrue("Failed to retrieve stored relationship", retrievedRelationship.isPresent());
+
+ graph.commitTransaction(tx);
+ }
+
+ private class VertexWriter implements Runnable {
+
+ ChampObject vertex;
+ int index;
+
+ public VertexWriter(ChampObject vertex, int index) {
+ this.vertex = vertex;
+ this.index = index;
+ }
+
+ public void run() {
+
+ ChampTransaction tx=null;
+ try {
+
+ // Open a threaded transaction to do some work in.
+ tx = graph.openTransaction();
+
+ // Now store one of our two vertices within the context of this transaction.
+ storedVertices[index] = graph.storeObject(vertex, Optional.of(tx));
+
+ // Use our latch to indicate that we are done creating vertices, and wait for
+ // the other thread to do the same.
+ latch.countDown();
+ latch.await();
+
+ // Validate that the vertex we created is visible to us in the graph, but the
+ // one that the other thread created is not.
+ Optional<ChampObject> retrievedV2 = graph.retrieveObject(storedVertices[index].getKey().get(), Optional.of(tx));
+ assertTrue(retrievedV2.isPresent());
+ Optional<ChampObject> retrievedV1 = graph.retrieveObject(storedVertices[(index+1)%2].getKey().get(), Optional.of(tx));
+ assertFalse(retrievedV1.isPresent());
+
+ } catch (InterruptedException |
+ ChampUnmarshallingException |
+ ChampMarshallingException |
+ ChampSchemaViolationException |
+ ChampObjectNotExistsException | ChampTransactionException e) {
+
+ fail("Thread failed to interact with graph due to " + e.getMessage());
+
+ } finally {
+
+ try {
+ Thread.sleep(index * 500);
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ // Now, commit our transaction and bail...
+ try {
+ graph.commitTransaction(tx);
+ } catch (ChampTransactionException e) {
+
+ }
+ }
+ }
+ };
+}
+
diff --git a/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/TitanChampSetupTest.java b/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/TitanChampSetupTest.java
new file mode 100644
index 0000000..bca61ec
--- /dev/null
+++ b/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/TitanChampSetupTest.java
@@ -0,0 +1,74 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * 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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.onap.aai.champtitan.core;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.onap.aai.champtitan.graph.impl.TitanChampGraphImpl;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class TitanChampSetupTest {
+ @Rule
+ public final ExpectedException exception = ExpectedException.none();
+
+ @Test
+ public void TitanSetupBadBackendTest() {
+ exception.expect(RuntimeException.class);
+ TitanChampGraphImpl graph = new TitanChampGraphImpl.Builder("testGraph")
+ .property("storage.backend", "bad-backend")
+ .build();
+ }
+
+ @Test
+ public void TitanSetupBerkleyBackendTest() {
+ exception.expect(RuntimeException.class);
+ Map<String, Object> propertiesMap = new HashMap<String, Object>();
+ propertiesMap.put("storage.backend", "berkleyje");
+ TitanChampGraphImpl graph = new TitanChampGraphImpl.Builder("testGraph")
+ .properties(propertiesMap)
+ .build();
+ }
+
+ @Test
+ public void TitanSetupBadPropertyTest() {
+ exception.expect(RuntimeException.class);
+ TitanChampGraphImpl graph = new TitanChampGraphImpl.Builder("testGraph")
+ .property("storage.backend", "in-memory")
+ .property("storage.cassandra.keyspace", "anything")
+ .build();
+ }
+
+ @Test
+ public void TitanSetupBadPropertiesTest() {
+ exception.expect(RuntimeException.class);
+ Map<String, Object> propertiesMap = new HashMap<String, Object>();
+ propertiesMap.put("storage.cassandra.keyspace", "anything");
+
+ TitanChampGraphImpl graph = new TitanChampGraphImpl.Builder("testGraph")
+ .property("storage.backend", "in-memory")
+ .properties(propertiesMap)
+ .build();
+ }
+}